From 0b1b39d70a8ce969b233c457f34bda1d20771083 Mon Sep 17 00:00:00 2001 From: Joonas Rikkonen Date: Mon, 29 Apr 2019 21:11:59 +0300 Subject: [PATCH] (caf7e6a2e) Replaced Concentus NuGet package with csproj (ensures correct System.Runtime references) --- .../BarotraumaClient/LinuxClient.csproj | 7 +- Barotrauma/BarotraumaClient/MacClient.csproj | 7 +- .../Source/Characters/Animation/Ragdoll.cs | 101 +- .../BarotraumaClient/WindowsClient.csproj | 7 +- Barotrauma/BarotraumaClient/app.config | 68 +- Barotrauma/BarotraumaClient/packages.config | 1 - .../BarotraumaShared/Source/Items/Item.cs | 4 - Barotrauma_Solution.sln | 127 + Libraries/Concentus/.gitignore | 240 ++ Libraries/Concentus/CSharp/Concentus.sln | 36 + .../Concentus/CSharp/Concentus/App.config | 6 + .../Concentus/Celt/AutocorrelationUnsafe.cs | 300 ++ .../Concentus/CSharp/Concentus/Celt/Bands.cs | 1714 +++++++++++ .../Concentus/CSharp/Concentus/Celt/CWRS.cs | 319 ++ .../CSharp/Concentus/Celt/CeltCommon.cs | 1406 +++++++++ .../CSharp/Concentus/Celt/CeltConstants.cs | 93 + .../CSharp/Concentus/Celt/CeltLPC.cs | 159 + .../CSharp/Concentus/Celt/CeltLPCUnsafe.cs | 163 + .../CSharp/Concentus/Celt/CeltPitchXCorr.cs | 154 + .../Concentus/Celt/CeltPitchXCorrUnsafe.cs | 255 ++ .../CSharp/Concentus/Celt/Enums/Spread.cs | 45 + .../CSharp/Concentus/Celt/Kernels.cs | 354 +++ .../CSharp/Concentus/Celt/KernelsUnsafe.cs | 352 +++ .../CSharp/Concentus/Celt/KissFFT.cs | 455 +++ .../CSharp/Concentus/Celt/KissFFTUnsafe.cs | 456 +++ .../CSharp/Concentus/Celt/Laplace.cs | 157 + .../Concentus/CSharp/Concentus/Celt/MDCT.cs | 370 +++ .../Concentus/CSharp/Concentus/Celt/Pitch.cs | 506 +++ .../CSharp/Concentus/Celt/QuantizeBands.cs | 524 ++++ .../Concentus/CSharp/Concentus/Celt/Rate.cs | 519 ++++ .../Concentus/Celt/Structs/AnalysisInfo.cs | 74 + .../Concentus/Celt/Structs/CELTDecoder.cs | 873 ++++++ .../CSharp/Concentus/Celt/Structs/CELTMode.cs | 118 + .../Concentus/Celt/Structs/CeltEncoder.cs | 1300 ++++++++ .../CSharp/Concentus/Celt/Structs/FFTState.cs | 54 + .../Concentus/Celt/Structs/MDCTLookup.cs | 59 + .../Concentus/Celt/Structs/PulseCache.cs | 59 + .../Concentus/CSharp/Concentus/Celt/Tables.cs | 1128 +++++++ .../Concentus/CSharp/Concentus/Celt/VQ.cs | 430 +++ .../Concentus/Common/Autocorrelation.cs | 284 ++ .../Concentus/Common/CPlusPlus/Arrays.cs | 221 ++ .../Concentus/Common/CPlusPlus/BoxedValue.cs | 97 + .../Concentus/Common/CPlusPlus/Pointer.cs | 563 ++++ .../CSharp/Concentus/Common/EntropyCoder.cs | 790 +++++ .../CSharp/Concentus/Common/Inlines.cs | 2714 +++++++++++++++++ .../CSharp/Concentus/Common/Resampler.cs | 1035 +++++++ .../CSharp/Concentus/Concentus.csproj | 182 ++ .../CSharp/Concentus/Opus/Analysis.cs | 590 ++++ .../CSharp/Concentus/Opus/CodecHelpers.cs | 712 +++++ .../CSharp/Concentus/Opus/Downmix.cs | 125 + .../Concentus/Opus/Enums/OpusApplication.cs | 57 + .../Concentus/Opus/Enums/OpusBandwidth.cs | 70 + .../Concentus/Opus/Enums/OpusControl.cs | 94 + .../CSharp/Concentus/Opus/Enums/OpusError.cs | 68 + .../Concentus/Opus/Enums/OpusFramesize.cs | 80 + .../CSharp/Concentus/Opus/Enums/OpusMode.cs | 45 + .../CSharp/Concentus/Opus/Enums/OpusSignal.cs | 52 + .../Concentus/Opus/MultiLayerPerceptron.cs | 111 + .../CSharp/Concentus/Opus/OpusCompare.cs | 340 +++ .../CSharp/Concentus/Opus/OpusConstants.cs | 66 + .../CSharp/Concentus/Opus/OpusException.cs | 46 + .../CSharp/Concentus/Opus/OpusMultistream.cs | 99 + .../Concentus/Opus/Structs/ChannelLayout.cs | 60 + .../CSharp/Concentus/Opus/Structs/MLP.cs | 52 + .../Concentus/Opus/Structs/OpusDecoder.cs | 933 ++++++ .../Concentus/Opus/Structs/OpusEncoder.cs | 2050 +++++++++++++ .../Concentus/Opus/Structs/OpusMSDecoder.cs | 438 +++ .../Concentus/Opus/Structs/OpusMSEncoder.cs | 1168 +++++++ .../Concentus/Opus/Structs/OpusPacketInfo.cs | 470 +++ .../Opus/Structs/OpusRepacketizer.cs | 573 ++++ .../Opus/Structs/StereoWidthState.cs | 61 + .../Opus/Structs/TonalityAnalysisState.cs | 140 + .../Concentus/Opus/Structs/VorbisLayout.cs | 69 + .../Concentus/CSharp/Concentus/Opus/Tables.cs | 298 ++ .../Concentus/CSharp/Concentus/Readme.txt | 52 + .../CSharp/Concentus/Silk/ApplySineWindow.cs | 118 + .../CSharp/Concentus/Silk/BWExpander.cs | 90 + .../CSharp/Concentus/Silk/BurgModified.cs | 326 ++ .../Concentus/Silk/BurgModifiedUnsafe.cs | 336 ++ .../Concentus/CSharp/Concentus/Silk/CNG.cs | 232 ++ .../CSharp/Concentus/Silk/CodeSigns.cs | 150 + .../CSharp/Concentus/Silk/CorrelateMatrix.cs | 186 ++ .../CSharp/Concentus/Silk/DecodeAPI.cs | 461 +++ .../CSharp/Concentus/Silk/DecodeCore.cs | 278 ++ .../CSharp/Concentus/Silk/DecodeIndices.cs | 179 ++ .../CSharp/Concentus/Silk/DecodeParameters.cs | 141 + .../CSharp/Concentus/Silk/DecodePitch.cs | 90 + .../CSharp/Concentus/Silk/DecodePulses.cs | 136 + .../CSharp/Concentus/Silk/EncodeAPI.cs | 740 +++++ .../CSharp/Concentus/Silk/EncodeIndices.cs | 223 ++ .../CSharp/Concentus/Silk/EncodePulses.cs | 278 ++ .../Concentus/Silk/Enums/DecoderAPIFlag.cs | 41 + .../CSharp/Concentus/Silk/Enums/SilkError.cs | 91 + .../CSharp/Concentus/Silk/Filters.cs | 714 +++++ .../CSharp/Concentus/Silk/FindLPC.cs | 184 ++ .../CSharp/Concentus/Silk/FindLTP.cs | 295 ++ .../CSharp/Concentus/Silk/FindPitchLags.cs | 167 + .../CSharp/Concentus/Silk/FindPredCoefs.cs | 171 ++ .../CSharp/Concentus/Silk/GainQuantization.cs | 187 ++ .../CSharp/Concentus/Silk/HPVariableCutoff.cs | 90 + .../Concentus/CSharp/Concentus/Silk/K2A.cs | 92 + .../Concentus/Silk/LPCInversePredGain.cs | 169 + .../Concentus/Silk/LTPAnalysisFilter.cs | 103 + .../CSharp/Concentus/Silk/LTPScaleControl.cs | 66 + .../CSharp/Concentus/Silk/LinearAlgebra.cs | 245 ++ .../Concentus/CSharp/Concentus/Silk/NLSF.cs | 1272 ++++++++ .../Concentus/Silk/NoiseShapeAnalysis.cs | 498 +++ .../Concentus/CSharp/Concentus/Silk/PLC.cs | 486 +++ .../Concentus/Silk/PitchAnalysisCore.cs | 792 +++++ .../CSharp/Concentus/Silk/ProcessGains.cs | 143 + .../CSharp/Concentus/Silk/QuantizeLTPGains.cs | 153 + .../Concentus/Silk/RegularizeCorrelations.cs | 61 + .../CSharp/Concentus/Silk/Resampler.cs | 744 +++++ .../CSharp/Concentus/Silk/ResidualEnergy.cs | 193 ++ .../Concentus/CSharp/Concentus/Silk/Schur.cs | 198 ++ .../CSharp/Concentus/Silk/ShellCoder.cs | 206 ++ .../CSharp/Concentus/Silk/Sigmoid.cs | 93 + .../CSharp/Concentus/Silk/SilkConstants.cs | 311 ++ .../Concentus/CSharp/Concentus/Silk/Sort.cs | 184 ++ .../Concentus/CSharp/Concentus/Silk/Stereo.cs | 543 ++++ .../CSharp/Concentus/Silk/Structs/CNGState.cs | 59 + .../Concentus/Silk/Structs/DecControlState.cs | 72 + .../Concentus/Silk/Structs/EncControlState.cs | 217 ++ .../Concentus/Silk/Structs/NLSFCodebook.cs | 108 + .../Concentus/Silk/Structs/PLCStruct.cs | 75 + .../Concentus/Silk/Structs/SideInfoIndices.cs | 88 + .../Silk/Structs/SilkChannelDecoder.cs | 359 +++ .../Silk/Structs/SilkChannelEncoder.cs | 1264 ++++++++ .../Concentus/Silk/Structs/SilkDecoder.cs | 70 + .../Silk/Structs/SilkDecoderControl.cs | 63 + .../Concentus/Silk/Structs/SilkEncoder.cs | 105 + .../Silk/Structs/SilkEncoderControl.cs | 105 + .../Concentus/Silk/Structs/SilkLPState.cs | 108 + .../Concentus/Silk/Structs/SilkNSQState.cs | 1742 +++++++++++ .../Silk/Structs/SilkPrefilterState.cs | 70 + .../Silk/Structs/SilkResamplerState.cs | 94 + .../Concentus/Silk/Structs/SilkShapeState.cs | 57 + .../Concentus/Silk/Structs/SilkVADState.cs | 108 + .../Silk/Structs/StereoDecodeState.cs | 52 + .../Silk/Structs/StereoEncodeState.cs | 71 + .../Concentus/Silk/Structs/TOCStruct.cs | 66 + .../CSharp/Concentus/Silk/SumSqrShift.cs | 179 ++ .../Concentus/CSharp/Concentus/Silk/Tables.cs | 1065 +++++++ .../CSharp/Concentus/Silk/TuningParameters.cs | 174 ++ .../CSharp/Concentus/Silk/VQ_WMat_EC.cs | 134 + .../Concentus/Silk/VoiceActivityDetection.cs | 409 +++ Libraries/Concentus/LICENSE | 30 + Libraries/Concentus/README.md | 25 + Libraries/Concentus/pom.xml | 11 + 149 files changed, 47171 insertions(+), 70 deletions(-) create mode 100644 Libraries/Concentus/.gitignore create mode 100644 Libraries/Concentus/CSharp/Concentus.sln create mode 100644 Libraries/Concentus/CSharp/Concentus/App.config create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/AutocorrelationUnsafe.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/Bands.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/CWRS.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/CeltCommon.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/CeltConstants.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/CeltLPC.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/CeltLPCUnsafe.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/CeltPitchXCorr.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/CeltPitchXCorrUnsafe.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/Enums/Spread.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/Kernels.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/KernelsUnsafe.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/KissFFT.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/KissFFTUnsafe.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/Laplace.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/MDCT.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/Pitch.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/QuantizeBands.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/Rate.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/Structs/AnalysisInfo.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/Structs/CELTDecoder.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/Structs/CELTMode.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/Structs/CeltEncoder.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/Structs/FFTState.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/Structs/MDCTLookup.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/Structs/PulseCache.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/Tables.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Celt/VQ.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Common/Autocorrelation.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Common/CPlusPlus/Arrays.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Common/CPlusPlus/BoxedValue.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Common/CPlusPlus/Pointer.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Common/EntropyCoder.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Common/Inlines.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Common/Resampler.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Concentus.csproj create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/Analysis.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/CodecHelpers.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/Downmix.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusApplication.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusBandwidth.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusControl.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusError.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusFramesize.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusMode.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusSignal.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/MultiLayerPerceptron.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/OpusCompare.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/OpusConstants.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/OpusException.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/OpusMultistream.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/Structs/ChannelLayout.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/Structs/MLP.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusDecoder.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusEncoder.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusMSDecoder.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusMSEncoder.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusPacketInfo.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusRepacketizer.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/Structs/StereoWidthState.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/Structs/TonalityAnalysisState.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/Structs/VorbisLayout.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Opus/Tables.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Readme.txt create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/ApplySineWindow.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/BWExpander.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/BurgModified.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/BurgModifiedUnsafe.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/CNG.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/CodeSigns.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/CorrelateMatrix.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/DecodeAPI.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/DecodeCore.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/DecodeIndices.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/DecodeParameters.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/DecodePitch.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/DecodePulses.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/EncodeAPI.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/EncodeIndices.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/EncodePulses.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Enums/DecoderAPIFlag.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Enums/SilkError.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Filters.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/FindLPC.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/FindLTP.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/FindPitchLags.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/FindPredCoefs.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/GainQuantization.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/HPVariableCutoff.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/K2A.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/LPCInversePredGain.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/LTPAnalysisFilter.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/LTPScaleControl.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/LinearAlgebra.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/NLSF.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/NoiseShapeAnalysis.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/PLC.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/PitchAnalysisCore.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/ProcessGains.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/QuantizeLTPGains.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/RegularizeCorrelations.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Resampler.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/ResidualEnergy.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Schur.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/ShellCoder.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Sigmoid.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/SilkConstants.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Sort.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Stereo.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Structs/CNGState.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Structs/DecControlState.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Structs/EncControlState.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Structs/NLSFCodebook.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Structs/PLCStruct.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Structs/SideInfoIndices.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkChannelDecoder.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkChannelEncoder.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkDecoder.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkDecoderControl.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkEncoder.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkEncoderControl.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkLPState.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkNSQState.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkPrefilterState.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkResamplerState.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkShapeState.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkVADState.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Structs/StereoDecodeState.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Structs/StereoEncodeState.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Structs/TOCStruct.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/SumSqrShift.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/Tables.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/TuningParameters.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/VQ_WMat_EC.cs create mode 100644 Libraries/Concentus/CSharp/Concentus/Silk/VoiceActivityDetection.cs create mode 100644 Libraries/Concentus/LICENSE create mode 100644 Libraries/Concentus/README.md create mode 100644 Libraries/Concentus/pom.xml diff --git a/Barotrauma/BarotraumaClient/LinuxClient.csproj b/Barotrauma/BarotraumaClient/LinuxClient.csproj index c74cd30c3..080c09481 100644 --- a/Barotrauma/BarotraumaClient/LinuxClient.csproj +++ b/Barotrauma/BarotraumaClient/LinuxClient.csproj @@ -60,9 +60,6 @@ - - ..\..\Libraries\NuGet\Concentus.1.1.7\lib\portable-net45+win+wpa81+wp80\Concentus.dll - ..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.1.1.12\lib\net45\GameAnalytics.Mono.dll @@ -132,6 +129,10 @@ + + {0e7fee6a-15e5-4a4e-943c-80276003478c} + Concentus + {3af0347c-5a9b-4421-868c-8ee3dbfaebc6} Facepunch.Steamworks diff --git a/Barotrauma/BarotraumaClient/MacClient.csproj b/Barotrauma/BarotraumaClient/MacClient.csproj index f9fdba9c8..99902b0ca 100644 --- a/Barotrauma/BarotraumaClient/MacClient.csproj +++ b/Barotrauma/BarotraumaClient/MacClient.csproj @@ -59,9 +59,6 @@ - - ..\..\Libraries\NuGet\Concentus.1.1.7\lib\portable-net45+win+wpa81+wp80\Concentus.dll - ..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.1.1.12\lib\net45\GameAnalytics.Mono.dll @@ -131,6 +128,10 @@ + + {0e7fee6a-15e5-4a4e-943c-80276003478c} + Concentus + {3af0347c-5a9b-4421-868c-8ee3dbfaebc6} Facepunch.Steamworks diff --git a/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs b/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs index bcf133b30..fa30d8a82 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs @@ -108,6 +108,61 @@ namespace Barotrauma } } + //unconscious/dead characters can't correct their position using AnimController movement + // -> we need to correct it manually + if (!character.AllowInput) + { + float mainLimbDistSqrd = Vector2.DistanceSquared(MainLimb.PullJointWorldAnchorA, Collider.SimPosition); + float mainLimbErrorTolerance = 0.1f; + //if the main limb is roughly at the correct position and the collider isn't moving (much at least), + //don't attempt to correct the position. + if (mainLimbDistSqrd > mainLimbErrorTolerance || Collider.LinearVelocity.LengthSquared() > 0.05f) + { + MainLimb.PullJointWorldAnchorB = Collider.SimPosition; + MainLimb.PullJointEnabled = true; + } + character.SelectedConstruction = character.MemState[0].SelectedItem; + } + + if (character.MemState[0].Animation == AnimController.Animation.CPR) + { + character.AnimController.Anim = AnimController.Animation.CPR; + } + else if (character.AnimController.Anim == AnimController.Animation.CPR) + { + character.AnimController.Anim = AnimController.Animation.None; + } + + Vector2 newVelocity = Collider.LinearVelocity; + Vector2 newPosition = Collider.SimPosition; + float newRotation = Collider.Rotation; + float newAngularVelocity = Collider.AngularVelocity; + Collider.CorrectPosition(character.MemState, out newPosition, out newVelocity, out newRotation, out newAngularVelocity); + + newVelocity = newVelocity.ClampLength(100.0f); + if (!MathUtils.IsValid(newVelocity)) { newVelocity = Vector2.Zero; } + overrideTargetMovement = newVelocity.LengthSquared() > 0.01f ? newVelocity : Vector2.Zero; + + Collider.LinearVelocity = newVelocity; + Collider.AngularVelocity = newAngularVelocity; + + float distSqrd = Vector2.DistanceSquared(newPosition, Collider.SimPosition); + float errorTolerance = character.AllowInput ? 0.01f : 0.2f; + if (distSqrd > errorTolerance) + { + if (distSqrd > 10.0f || !character.AllowInput) + { + Collider.TargetRotation = newRotation; + SetPosition(newPosition, lerp: distSqrd < 5.0f, ignorePlatforms: false); + } + else + { + Collider.TargetRotation = newRotation; + Collider.TargetPosition = newPosition; + Collider.MoveToTargetPosition(true); + } + } + //unconscious/dead characters can't correct their position using AnimController movement // -> we need to correct it manually if (!character.AllowInput) @@ -151,32 +206,34 @@ namespace Barotrauma } } - - if (character.MemLocalState.Count > 120) character.MemLocalState.RemoveRange(0, character.MemLocalState.Count - 120); - character.MemState.Clear(); + character.MemLocalState.Clear(); } - } - - partial void ImpactProjSpecific(float impact, Body body) - { - float volume = MathHelper.Clamp(impact - 3.0f, 0.5f, 1.0f); - - if (body.UserData is Limb limb && character.Stun <= 0f) + else { - if (impact > 3.0f) { PlayImpactSound(limb); } - } - else if (body.UserData is Limb || body == Collider.FarseerBody) - { - if (!character.IsRemotePlayer && impact > ImpactTolerance) + //remove states with a timestamp (there may still timestamp-based states + //in the list if the controlled character switches from timestamp-based interpolation to ID-based) + character.MemState.RemoveAll(m => m.Timestamp > 0.0f); + + for (int i = 0; i < character.MemLocalState.Count; i++) { - SoundPlayer.PlayDamageSound("LimbBlunt", strongestImpact, Collider); + if (character.Submarine == null) + { + //transform in-sub coordinates to outside coordinates + if (character.MemLocalState[i].Position.Y > lowestSubPos) + { + character.MemLocalState[i].TransformInToOutside(); + } + } + else if (currentHull?.Submarine != null) + { + //transform outside coordinates to in-sub coordinates + if (character.MemLocalState[i].Position.Y < lowestSubPos) + { + character.MemLocalState[i].TransformOutToInside(currentHull.Submarine); + } + } + } - } - if (Character.Controlled == character) - { - GameMain.GameScreen.Cam.Shake = Math.Min(Math.Max(strongestImpact, GameMain.GameScreen.Cam.Shake), 3.0f); - } - } if (character.MemState.Count < 1) return; diff --git a/Barotrauma/BarotraumaClient/WindowsClient.csproj b/Barotrauma/BarotraumaClient/WindowsClient.csproj index 7cf32db5d..131aa6e76 100644 --- a/Barotrauma/BarotraumaClient/WindowsClient.csproj +++ b/Barotrauma/BarotraumaClient/WindowsClient.csproj @@ -58,9 +58,6 @@ - - ..\..\Libraries\NuGet\Concentus.1.1.7\lib\portable-net45+win+wpa81+wp80\Concentus.dll - ..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.1.1.12\lib\net45\GameAnalytics.Mono.dll @@ -215,6 +212,10 @@ + + {0e7fee6a-15e5-4a4e-943c-80276003478c} + Concentus + {3af0347c-5a9b-4421-868c-8ee3dbfaebc6} Facepunch.Steamworks diff --git a/Barotrauma/BarotraumaClient/app.config b/Barotrauma/BarotraumaClient/app.config index 383108ffa..f067ae6ac 100644 --- a/Barotrauma/BarotraumaClient/app.config +++ b/Barotrauma/BarotraumaClient/app.config @@ -1,35 +1,35 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Barotrauma/BarotraumaClient/packages.config b/Barotrauma/BarotraumaClient/packages.config index 695edea35..d637bffbb 100644 --- a/Barotrauma/BarotraumaClient/packages.config +++ b/Barotrauma/BarotraumaClient/packages.config @@ -1,6 +1,5 @@  - diff --git a/Barotrauma/BarotraumaShared/Source/Items/Item.cs b/Barotrauma/BarotraumaShared/Source/Items/Item.cs index c33f7ea82..1622e76e8 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Item.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Item.cs @@ -1149,10 +1149,6 @@ namespace Barotrauma { body.SetTransform(body.SimPosition - Submarine.SimPosition, body.Rotation); } - else if (Submarine != null && prevSub != null && Submarine != prevSub) - { - body.SetTransform(body.SimPosition + prevSub.SimPosition - Submarine.SimPosition, body.Rotation); - } Vector2 displayPos = ConvertUnits.ToDisplayUnits(body.SimPosition); rect.X = (int)(displayPos.X - rect.Width / 2.0f); diff --git a/Barotrauma_Solution.sln b/Barotrauma_Solution.sln index c35da4735..94040636c 100644 --- a/Barotrauma_Solution.sln +++ b/Barotrauma_Solution.sln @@ -61,6 +61,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MacClient", "Barotrauma\Bar {85232B20-074D-4723-B0C6-91495391E448} = {85232B20-074D-4723-B0C6-91495391E448} EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Concentus", "Libraries\Concentus\CSharp\Concentus\Concentus.csproj", "{777A5414-CAE5-4011-96DF-C9661985917E}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution Barotrauma\BarotraumaClient\ClientCode.projitems*{008c0f83-e914-4966-9135-ea885059edd8}*SharedItemsImports = 4 @@ -81,12 +83,16 @@ Global Barotrauma\BarotraumaShared\SharedContent.projitems*{d7f9fdd3-af03-46ad-a2c2-f590899712b7}*SharedItemsImports = 4 EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 DebugLinux|Any CPU = DebugLinux|Any CPU DebugLinux|x64 = DebugLinux|x64 DebugMac|Any CPU = DebugMac|Any CPU DebugMac|x64 = DebugMac|x64 DebugWindows|Any CPU = DebugWindows|Any CPU DebugWindows|x64 = DebugWindows|x64 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 ReleaseLinux|Any CPU = ReleaseLinux|Any CPU ReleaseLinux|x64 = ReleaseLinux|x64 ReleaseMac|Any CPU = ReleaseMac|Any CPU @@ -95,6 +101,10 @@ Global ReleaseWindows|x64 = ReleaseWindows|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {008C0F83-E914-4966-9135-EA885059EDD8}.Debug|Any CPU.ActiveCfg = ReleaseWindows|x64 + {008C0F83-E914-4966-9135-EA885059EDD8}.Debug|Any CPU.Build.0 = ReleaseWindows|x64 + {008C0F83-E914-4966-9135-EA885059EDD8}.Debug|x64.ActiveCfg = DebugWindows|x64 + {008C0F83-E914-4966-9135-EA885059EDD8}.Debug|x64.Build.0 = DebugWindows|x64 {008C0F83-E914-4966-9135-EA885059EDD8}.DebugLinux|Any CPU.ActiveCfg = DebugWindows|x64 {008C0F83-E914-4966-9135-EA885059EDD8}.DebugLinux|Any CPU.Build.0 = DebugWindows|x64 {008C0F83-E914-4966-9135-EA885059EDD8}.DebugLinux|x64.ActiveCfg = DebugWindows|x64 @@ -104,6 +114,10 @@ Global {008C0F83-E914-4966-9135-EA885059EDD8}.DebugWindows|Any CPU.ActiveCfg = DebugWindows|x64 {008C0F83-E914-4966-9135-EA885059EDD8}.DebugWindows|x64.ActiveCfg = DebugWindows|x64 {008C0F83-E914-4966-9135-EA885059EDD8}.DebugWindows|x64.Build.0 = DebugWindows|x64 + {008C0F83-E914-4966-9135-EA885059EDD8}.Release|Any CPU.ActiveCfg = ReleaseWindows|x64 + {008C0F83-E914-4966-9135-EA885059EDD8}.Release|Any CPU.Build.0 = ReleaseWindows|x64 + {008C0F83-E914-4966-9135-EA885059EDD8}.Release|x64.ActiveCfg = ReleaseWindows|x64 + {008C0F83-E914-4966-9135-EA885059EDD8}.Release|x64.Build.0 = ReleaseWindows|x64 {008C0F83-E914-4966-9135-EA885059EDD8}.ReleaseLinux|Any CPU.ActiveCfg = DebugWindows|x64 {008C0F83-E914-4966-9135-EA885059EDD8}.ReleaseLinux|Any CPU.Build.0 = DebugWindows|x64 {008C0F83-E914-4966-9135-EA885059EDD8}.ReleaseLinux|x64.ActiveCfg = ReleaseWindows|x64 @@ -113,6 +127,10 @@ Global {008C0F83-E914-4966-9135-EA885059EDD8}.ReleaseWindows|Any CPU.ActiveCfg = ReleaseWindows|x64 {008C0F83-E914-4966-9135-EA885059EDD8}.ReleaseWindows|x64.ActiveCfg = ReleaseWindows|x64 {008C0F83-E914-4966-9135-EA885059EDD8}.ReleaseWindows|x64.Build.0 = ReleaseWindows|x64 + {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.Debug|x64.ActiveCfg = Debug|Any CPU + {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.Debug|x64.Build.0 = Debug|Any CPU {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.DebugLinux|Any CPU.ActiveCfg = Debug|Any CPU {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.DebugLinux|Any CPU.Build.0 = Debug|Any CPU {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.DebugLinux|x64.ActiveCfg = Debug|Any CPU @@ -125,6 +143,10 @@ Global {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.DebugWindows|Any CPU.Build.0 = Debug|Any CPU {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.DebugWindows|x64.ActiveCfg = Debug|Any CPU {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.DebugWindows|x64.Build.0 = Debug|Any CPU + {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.Release|Any CPU.Build.0 = Release|Any CPU + {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.Release|x64.ActiveCfg = Release|Any CPU + {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.Release|x64.Build.0 = Release|Any CPU {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.ReleaseLinux|Any CPU.ActiveCfg = Release|Any CPU {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.ReleaseLinux|Any CPU.Build.0 = Release|Any CPU {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.ReleaseLinux|x64.ActiveCfg = Release|Any CPU @@ -137,6 +159,10 @@ Global {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.ReleaseWindows|Any CPU.Build.0 = Release|Any CPU {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.ReleaseWindows|x64.ActiveCfg = Release|Any CPU {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.ReleaseWindows|x64.Build.0 = Release|Any CPU + {3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.Debug|x64.ActiveCfg = Debug|Any CPU + {3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.Debug|x64.Build.0 = Debug|Any CPU {3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.DebugLinux|Any CPU.ActiveCfg = Debug|Any CPU {3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.DebugLinux|Any CPU.Build.0 = Debug|Any CPU {3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.DebugLinux|x64.ActiveCfg = Debug|Any CPU @@ -149,6 +175,10 @@ Global {3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.DebugWindows|Any CPU.Build.0 = Debug|Any CPU {3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.DebugWindows|x64.ActiveCfg = Debug|Any CPU {3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.DebugWindows|x64.Build.0 = Debug|Any CPU + {3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.Release|Any CPU.Build.0 = Release|Any CPU + {3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.Release|x64.ActiveCfg = Release|Any CPU + {3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.Release|x64.Build.0 = Release|Any CPU {3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.ReleaseLinux|Any CPU.ActiveCfg = Release|Any CPU {3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.ReleaseLinux|Any CPU.Build.0 = Release|Any CPU {3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.ReleaseLinux|x64.ActiveCfg = Release|Any CPU @@ -161,6 +191,10 @@ Global {3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.ReleaseWindows|Any CPU.Build.0 = Release|Any CPU {3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.ReleaseWindows|x64.ActiveCfg = Release|Any CPU {3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.ReleaseWindows|x64.Build.0 = Release|Any CPU + {C293DB32-FA42-486D-B128-5A12522FAE4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C293DB32-FA42-486D-B128-5A12522FAE4E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C293DB32-FA42-486D-B128-5A12522FAE4E}.Debug|x64.ActiveCfg = Debug|Any CPU + {C293DB32-FA42-486D-B128-5A12522FAE4E}.Debug|x64.Build.0 = Debug|Any CPU {C293DB32-FA42-486D-B128-5A12522FAE4E}.DebugLinux|Any CPU.ActiveCfg = Debug|Any CPU {C293DB32-FA42-486D-B128-5A12522FAE4E}.DebugLinux|Any CPU.Build.0 = Debug|Any CPU {C293DB32-FA42-486D-B128-5A12522FAE4E}.DebugLinux|x64.ActiveCfg = Debug|Any CPU @@ -173,6 +207,10 @@ Global {C293DB32-FA42-486D-B128-5A12522FAE4E}.DebugWindows|Any CPU.Build.0 = Debug|Any CPU {C293DB32-FA42-486D-B128-5A12522FAE4E}.DebugWindows|x64.ActiveCfg = Debug|Any CPU {C293DB32-FA42-486D-B128-5A12522FAE4E}.DebugWindows|x64.Build.0 = Debug|Any CPU + {C293DB32-FA42-486D-B128-5A12522FAE4E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C293DB32-FA42-486D-B128-5A12522FAE4E}.Release|Any CPU.Build.0 = Release|Any CPU + {C293DB32-FA42-486D-B128-5A12522FAE4E}.Release|x64.ActiveCfg = Release|Any CPU + {C293DB32-FA42-486D-B128-5A12522FAE4E}.Release|x64.Build.0 = Release|Any CPU {C293DB32-FA42-486D-B128-5A12522FAE4E}.ReleaseLinux|Any CPU.ActiveCfg = Release|Any CPU {C293DB32-FA42-486D-B128-5A12522FAE4E}.ReleaseLinux|Any CPU.Build.0 = Release|Any CPU {C293DB32-FA42-486D-B128-5A12522FAE4E}.ReleaseLinux|x64.ActiveCfg = Release|Any CPU @@ -185,6 +223,10 @@ Global {C293DB32-FA42-486D-B128-5A12522FAE4E}.ReleaseWindows|Any CPU.Build.0 = Release|Any CPU {C293DB32-FA42-486D-B128-5A12522FAE4E}.ReleaseWindows|x64.ActiveCfg = Release|Any CPU {C293DB32-FA42-486D-B128-5A12522FAE4E}.ReleaseWindows|x64.Build.0 = Release|Any CPU + {85232B20-074D-4723-B0C6-91495391E448}.Debug|Any CPU.ActiveCfg = ReleaseWindows|x64 + {85232B20-074D-4723-B0C6-91495391E448}.Debug|Any CPU.Build.0 = ReleaseWindows|x64 + {85232B20-074D-4723-B0C6-91495391E448}.Debug|x64.ActiveCfg = DebugWindows|x64 + {85232B20-074D-4723-B0C6-91495391E448}.Debug|x64.Build.0 = DebugWindows|x64 {85232B20-074D-4723-B0C6-91495391E448}.DebugLinux|Any CPU.ActiveCfg = DebugLinux|x64 {85232B20-074D-4723-B0C6-91495391E448}.DebugLinux|Any CPU.Build.0 = DebugLinux|x64 {85232B20-074D-4723-B0C6-91495391E448}.DebugLinux|x64.ActiveCfg = DebugLinux|x64 @@ -195,6 +237,10 @@ Global {85232B20-074D-4723-B0C6-91495391E448}.DebugWindows|Any CPU.ActiveCfg = DebugWindows|x64 {85232B20-074D-4723-B0C6-91495391E448}.DebugWindows|x64.ActiveCfg = DebugWindows|x64 {85232B20-074D-4723-B0C6-91495391E448}.DebugWindows|x64.Build.0 = DebugWindows|x64 + {85232B20-074D-4723-B0C6-91495391E448}.Release|Any CPU.ActiveCfg = ReleaseWindows|x64 + {85232B20-074D-4723-B0C6-91495391E448}.Release|Any CPU.Build.0 = ReleaseWindows|x64 + {85232B20-074D-4723-B0C6-91495391E448}.Release|x64.ActiveCfg = ReleaseWindows|x64 + {85232B20-074D-4723-B0C6-91495391E448}.Release|x64.Build.0 = ReleaseWindows|x64 {85232B20-074D-4723-B0C6-91495391E448}.ReleaseLinux|Any CPU.ActiveCfg = ReleaseLinux|x64 {85232B20-074D-4723-B0C6-91495391E448}.ReleaseLinux|x64.ActiveCfg = ReleaseLinux|x64 {85232B20-074D-4723-B0C6-91495391E448}.ReleaseLinux|x64.Build.0 = ReleaseLinux|x64 @@ -204,6 +250,10 @@ Global {85232B20-074D-4723-B0C6-91495391E448}.ReleaseWindows|Any CPU.ActiveCfg = ReleaseWindows|x64 {85232B20-074D-4723-B0C6-91495391E448}.ReleaseWindows|x64.ActiveCfg = ReleaseWindows|x64 {85232B20-074D-4723-B0C6-91495391E448}.ReleaseWindows|x64.Build.0 = ReleaseWindows|x64 + {A4610E4C-DD34-428B-BABB-779CA0B5993A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A4610E4C-DD34-428B-BABB-779CA0B5993A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A4610E4C-DD34-428B-BABB-779CA0B5993A}.Debug|x64.ActiveCfg = Debug|Any CPU + {A4610E4C-DD34-428B-BABB-779CA0B5993A}.Debug|x64.Build.0 = Debug|Any CPU {A4610E4C-DD34-428B-BABB-779CA0B5993A}.DebugLinux|Any CPU.ActiveCfg = Debug|Any CPU {A4610E4C-DD34-428B-BABB-779CA0B5993A}.DebugLinux|Any CPU.Build.0 = Debug|Any CPU {A4610E4C-DD34-428B-BABB-779CA0B5993A}.DebugLinux|x64.ActiveCfg = Debug|Any CPU @@ -216,6 +266,10 @@ Global {A4610E4C-DD34-428B-BABB-779CA0B5993A}.DebugWindows|Any CPU.Build.0 = Debug|Any CPU {A4610E4C-DD34-428B-BABB-779CA0B5993A}.DebugWindows|x64.ActiveCfg = Debug|Any CPU {A4610E4C-DD34-428B-BABB-779CA0B5993A}.DebugWindows|x64.Build.0 = Debug|Any CPU + {A4610E4C-DD34-428B-BABB-779CA0B5993A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A4610E4C-DD34-428B-BABB-779CA0B5993A}.Release|Any CPU.Build.0 = Release|Any CPU + {A4610E4C-DD34-428B-BABB-779CA0B5993A}.Release|x64.ActiveCfg = Release|Any CPU + {A4610E4C-DD34-428B-BABB-779CA0B5993A}.Release|x64.Build.0 = Release|Any CPU {A4610E4C-DD34-428B-BABB-779CA0B5993A}.ReleaseLinux|Any CPU.ActiveCfg = Release|Any CPU {A4610E4C-DD34-428B-BABB-779CA0B5993A}.ReleaseLinux|Any CPU.Build.0 = Release|Any CPU {A4610E4C-DD34-428B-BABB-779CA0B5993A}.ReleaseLinux|x64.ActiveCfg = Release|Any CPU @@ -228,6 +282,10 @@ Global {A4610E4C-DD34-428B-BABB-779CA0B5993A}.ReleaseWindows|Any CPU.Build.0 = Release|Any CPU {A4610E4C-DD34-428B-BABB-779CA0B5993A}.ReleaseWindows|x64.ActiveCfg = Release|Any CPU {A4610E4C-DD34-428B-BABB-779CA0B5993A}.ReleaseWindows|x64.Build.0 = Release|Any CPU + {3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.Debug|x64.Build.0 = Debug|Any CPU {3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.DebugLinux|Any CPU.ActiveCfg = Debug|Any CPU {3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.DebugLinux|Any CPU.Build.0 = Debug|Any CPU {3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.DebugLinux|x64.ActiveCfg = Debug|Any CPU @@ -240,6 +298,10 @@ Global {3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.DebugWindows|Any CPU.Build.0 = Debug|Any CPU {3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.DebugWindows|x64.ActiveCfg = Debug|Any CPU {3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.DebugWindows|x64.Build.0 = Debug|Any CPU + {3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.Release|Any CPU.Build.0 = Release|Any CPU + {3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.Release|x64.ActiveCfg = Release|Any CPU + {3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.Release|x64.Build.0 = Release|Any CPU {3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.ReleaseLinux|Any CPU.ActiveCfg = Release|Any CPU {3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.ReleaseLinux|Any CPU.Build.0 = Release|Any CPU {3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.ReleaseLinux|x64.ActiveCfg = Release|Any CPU @@ -252,6 +314,10 @@ Global {3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.ReleaseWindows|Any CPU.Build.0 = Release|Any CPU {3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.ReleaseWindows|x64.ActiveCfg = Release|Any CPU {3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.ReleaseWindows|x64.Build.0 = Release|Any CPU + {0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.Debug|Any CPU.ActiveCfg = DebugWindows|Any CPU + {0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.Debug|Any CPU.Build.0 = DebugWindows|Any CPU + {0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.Debug|x64.ActiveCfg = DebugWindows|Any CPU + {0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.Debug|x64.Build.0 = DebugWindows|Any CPU {0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.DebugLinux|Any CPU.ActiveCfg = DebugLinux|Any CPU {0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.DebugLinux|Any CPU.Build.0 = DebugLinux|Any CPU {0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.DebugLinux|x64.ActiveCfg = DebugLinux|Any CPU @@ -263,6 +329,10 @@ Global {0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.DebugWindows|Any CPU.ActiveCfg = DebugWindows|Any CPU {0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.DebugWindows|Any CPU.Build.0 = DebugWindows|Any CPU {0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.DebugWindows|x64.ActiveCfg = DebugWindows|Any CPU + {0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.Release|Any CPU.ActiveCfg = ReleaseWindows|Any CPU + {0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.Release|Any CPU.Build.0 = ReleaseWindows|Any CPU + {0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.Release|x64.ActiveCfg = ReleaseWindows|Any CPU + {0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.Release|x64.Build.0 = ReleaseWindows|Any CPU {0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.ReleaseLinux|Any CPU.ActiveCfg = ReleaseLinux|Any CPU {0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.ReleaseLinux|Any CPU.Build.0 = ReleaseLinux|Any CPU {0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.ReleaseLinux|x64.ActiveCfg = ReleaseLinux|Any CPU @@ -274,6 +344,10 @@ Global {0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.ReleaseWindows|Any CPU.ActiveCfg = ReleaseWindows|Any CPU {0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.ReleaseWindows|Any CPU.Build.0 = ReleaseWindows|Any CPU {0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.ReleaseWindows|x64.ActiveCfg = ReleaseWindows|Any CPU + {830461AA-3E2E-4BDE-9B27-1B3280836521}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {830461AA-3E2E-4BDE-9B27-1B3280836521}.Debug|Any CPU.Build.0 = Debug|Any CPU + {830461AA-3E2E-4BDE-9B27-1B3280836521}.Debug|x64.ActiveCfg = Debug|Any CPU + {830461AA-3E2E-4BDE-9B27-1B3280836521}.Debug|x64.Build.0 = Debug|Any CPU {830461AA-3E2E-4BDE-9B27-1B3280836521}.DebugLinux|Any CPU.ActiveCfg = DebugLinux|Any CPU {830461AA-3E2E-4BDE-9B27-1B3280836521}.DebugLinux|Any CPU.Build.0 = DebugLinux|Any CPU {830461AA-3E2E-4BDE-9B27-1B3280836521}.DebugLinux|x64.ActiveCfg = DebugLinux|Any CPU @@ -284,6 +358,10 @@ Global {830461AA-3E2E-4BDE-9B27-1B3280836521}.DebugWindows|Any CPU.Build.0 = DebugWindows|Any CPU {830461AA-3E2E-4BDE-9B27-1B3280836521}.DebugWindows|x64.ActiveCfg = DebugWindows|Any CPU {830461AA-3E2E-4BDE-9B27-1B3280836521}.DebugWindows|x64.Build.0 = DebugWindows|Any CPU + {830461AA-3E2E-4BDE-9B27-1B3280836521}.Release|Any CPU.ActiveCfg = Release|Any CPU + {830461AA-3E2E-4BDE-9B27-1B3280836521}.Release|Any CPU.Build.0 = Release|Any CPU + {830461AA-3E2E-4BDE-9B27-1B3280836521}.Release|x64.ActiveCfg = Release|Any CPU + {830461AA-3E2E-4BDE-9B27-1B3280836521}.Release|x64.Build.0 = Release|Any CPU {830461AA-3E2E-4BDE-9B27-1B3280836521}.ReleaseLinux|Any CPU.ActiveCfg = ReleaseLinux|Any CPU {830461AA-3E2E-4BDE-9B27-1B3280836521}.ReleaseLinux|Any CPU.Build.0 = ReleaseLinux|Any CPU {830461AA-3E2E-4BDE-9B27-1B3280836521}.ReleaseLinux|x64.ActiveCfg = ReleaseLinux|Any CPU @@ -294,6 +372,10 @@ Global {830461AA-3E2E-4BDE-9B27-1B3280836521}.ReleaseWindows|Any CPU.Build.0 = ReleaseWindows|Any CPU {830461AA-3E2E-4BDE-9B27-1B3280836521}.ReleaseWindows|x64.ActiveCfg = ReleaseWindows|Any CPU {830461AA-3E2E-4BDE-9B27-1B3280836521}.ReleaseWindows|x64.Build.0 = ReleaseWindows|Any CPU + {D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.Debug|Any CPU.ActiveCfg = ReleaseLinux|x64 + {D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.Debug|Any CPU.Build.0 = ReleaseLinux|x64 + {D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.Debug|x64.ActiveCfg = DebugLinux|x64 + {D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.Debug|x64.Build.0 = DebugLinux|x64 {D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.DebugLinux|Any CPU.ActiveCfg = DebugLinux|x64 {D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.DebugLinux|x64.ActiveCfg = DebugLinux|x64 {D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.DebugLinux|x64.Build.0 = DebugLinux|x64 @@ -303,6 +385,10 @@ Global {D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.DebugWindows|Any CPU.ActiveCfg = DebugLinux|x64 {D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.DebugWindows|Any CPU.Build.0 = DebugLinux|x64 {D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.DebugWindows|x64.ActiveCfg = DebugLinux|x64 + {D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.Release|Any CPU.ActiveCfg = ReleaseLinux|x64 + {D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.Release|Any CPU.Build.0 = ReleaseLinux|x64 + {D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.Release|x64.ActiveCfg = ReleaseLinux|x64 + {D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.Release|x64.Build.0 = ReleaseLinux|x64 {D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.ReleaseLinux|Any CPU.ActiveCfg = ReleaseLinux|x64 {D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.ReleaseLinux|x64.ActiveCfg = ReleaseLinux|x64 {D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.ReleaseLinux|x64.Build.0 = ReleaseLinux|x64 @@ -312,6 +398,10 @@ Global {D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.ReleaseWindows|Any CPU.ActiveCfg = DebugLinux|x64 {D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.ReleaseWindows|Any CPU.Build.0 = DebugLinux|x64 {D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.ReleaseWindows|x64.ActiveCfg = ReleaseLinux|x64 + {CC996BB6-3781-4868-B996-07F9CDC936ED}.Debug|Any CPU.ActiveCfg = ReleaseMac|x64 + {CC996BB6-3781-4868-B996-07F9CDC936ED}.Debug|Any CPU.Build.0 = ReleaseMac|x64 + {CC996BB6-3781-4868-B996-07F9CDC936ED}.Debug|x64.ActiveCfg = DebugMac|x64 + {CC996BB6-3781-4868-B996-07F9CDC936ED}.Debug|x64.Build.0 = DebugMac|x64 {CC996BB6-3781-4868-B996-07F9CDC936ED}.DebugLinux|Any CPU.ActiveCfg = DebugMac|x64 {CC996BB6-3781-4868-B996-07F9CDC936ED}.DebugLinux|x64.ActiveCfg = DebugMac|x64 {CC996BB6-3781-4868-B996-07F9CDC936ED}.DebugMac|Any CPU.ActiveCfg = DebugMac|x64 @@ -321,6 +411,10 @@ Global {CC996BB6-3781-4868-B996-07F9CDC936ED}.DebugWindows|Any CPU.ActiveCfg = DebugMac|x64 {CC996BB6-3781-4868-B996-07F9CDC936ED}.DebugWindows|Any CPU.Build.0 = DebugMac|x64 {CC996BB6-3781-4868-B996-07F9CDC936ED}.DebugWindows|x64.ActiveCfg = DebugMac|x64 + {CC996BB6-3781-4868-B996-07F9CDC936ED}.Release|Any CPU.ActiveCfg = ReleaseMac|x64 + {CC996BB6-3781-4868-B996-07F9CDC936ED}.Release|Any CPU.Build.0 = ReleaseMac|x64 + {CC996BB6-3781-4868-B996-07F9CDC936ED}.Release|x64.ActiveCfg = ReleaseMac|x64 + {CC996BB6-3781-4868-B996-07F9CDC936ED}.Release|x64.Build.0 = ReleaseMac|x64 {CC996BB6-3781-4868-B996-07F9CDC936ED}.ReleaseLinux|Any CPU.ActiveCfg = ReleaseMac|x64 {CC996BB6-3781-4868-B996-07F9CDC936ED}.ReleaseLinux|x64.ActiveCfg = ReleaseMac|x64 {CC996BB6-3781-4868-B996-07F9CDC936ED}.ReleaseMac|Any CPU.ActiveCfg = DebugMac|x64 @@ -330,6 +424,38 @@ Global {CC996BB6-3781-4868-B996-07F9CDC936ED}.ReleaseWindows|Any CPU.ActiveCfg = DebugMac|x64 {CC996BB6-3781-4868-B996-07F9CDC936ED}.ReleaseWindows|Any CPU.Build.0 = DebugMac|x64 {CC996BB6-3781-4868-B996-07F9CDC936ED}.ReleaseWindows|x64.ActiveCfg = ReleaseMac|x64 + {777A5414-CAE5-4011-96DF-C9661985917E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.Debug|x64.ActiveCfg = Debug|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.Debug|x64.Build.0 = Debug|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.DebugLinux|Any CPU.ActiveCfg = Debug|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.DebugLinux|Any CPU.Build.0 = Debug|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.DebugLinux|x64.ActiveCfg = Debug|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.DebugLinux|x64.Build.0 = Debug|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.DebugMac|Any CPU.ActiveCfg = Debug|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.DebugMac|Any CPU.Build.0 = Debug|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.DebugMac|x64.ActiveCfg = Debug|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.DebugMac|x64.Build.0 = Debug|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.DebugWindows|Any CPU.ActiveCfg = Debug|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.DebugWindows|Any CPU.Build.0 = Debug|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.DebugWindows|x64.ActiveCfg = Debug|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.DebugWindows|x64.Build.0 = Debug|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.Release|Any CPU.Build.0 = Release|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.Release|x64.ActiveCfg = Release|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.Release|x64.Build.0 = Release|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseLinux|Any CPU.ActiveCfg = Release|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseLinux|Any CPU.Build.0 = Release|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseLinux|x64.ActiveCfg = Release|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseLinux|x64.Build.0 = Release|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseMac|Any CPU.ActiveCfg = Release|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseMac|Any CPU.Build.0 = Release|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseMac|x64.ActiveCfg = Release|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseMac|x64.Build.0 = Release|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseWindows|Any CPU.ActiveCfg = Release|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseWindows|Any CPU.Build.0 = Release|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseWindows|x64.ActiveCfg = Release|Any CPU + {777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseWindows|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -355,6 +481,7 @@ Global {D7F9FDD3-AF03-46AD-A2C2-F590899712B7} = {B2C129F2-8E5C-419A-98EB-161AA5B5FC71} {DBCF6FF0-3DE9-11E9-B3EF-63280FDBDA4A} = {F35DF9BF-0BED-4FEF-A51C-DD83C531882F} {CC996BB6-3781-4868-B996-07F9CDC936ED} = {DBCF6FF0-3DE9-11E9-B3EF-63280FDBDA4A} + {777A5414-CAE5-4011-96DF-C9661985917E} = {DE36F45F-F09E-4719-B953-00D148F7722A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {17032EAB-554B-4B44-A4F6-EFB177ACAB7A} diff --git a/Libraries/Concentus/.gitignore b/Libraries/Concentus/.gitignore new file mode 100644 index 000000000..629fe1100 --- /dev/null +++ b/Libraries/Concentus/.gitignore @@ -0,0 +1,240 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Microsoft Azure ApplicationInsights config file +ApplicationInsights.config + +# Windows Store app package directory +AppPackages/ +BundleArtifacts/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe + +# FAKE - F# Make +.fake/ +/Java/Concentus/target/ +/Java/ContentusTestConsole/ContentusTestConsole/target/ +/Java/ContentusTestConsole/ConcentusTestConsole/target/ +/Java/ConcentusTestConsole/target/ \ No newline at end of file diff --git a/Libraries/Concentus/CSharp/Concentus.sln b/Libraries/Concentus/CSharp/Concentus.sln new file mode 100644 index 000000000..575a10fcc --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus.sln @@ -0,0 +1,36 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27004.2009 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Concentus", "Concentus\Concentus.csproj", "{0E7FEE6A-15E5-4A4E-943C-80276003478C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0E7FEE6A-15E5-4A4E-943C-80276003478C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0E7FEE6A-15E5-4A4E-943C-80276003478C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0E7FEE6A-15E5-4A4E-943C-80276003478C}.Debug|x64.ActiveCfg = Debug|Any CPU + {0E7FEE6A-15E5-4A4E-943C-80276003478C}.Debug|x64.Build.0 = Debug|Any CPU + {0E7FEE6A-15E5-4A4E-943C-80276003478C}.Debug|x86.ActiveCfg = Debug|Any CPU + {0E7FEE6A-15E5-4A4E-943C-80276003478C}.Debug|x86.Build.0 = Debug|Any CPU + {0E7FEE6A-15E5-4A4E-943C-80276003478C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0E7FEE6A-15E5-4A4E-943C-80276003478C}.Release|Any CPU.Build.0 = Release|Any CPU + {0E7FEE6A-15E5-4A4E-943C-80276003478C}.Release|x64.ActiveCfg = Release|Any CPU + {0E7FEE6A-15E5-4A4E-943C-80276003478C}.Release|x64.Build.0 = Release|Any CPU + {0E7FEE6A-15E5-4A4E-943C-80276003478C}.Release|x86.ActiveCfg = Release|Any CPU + {0E7FEE6A-15E5-4A4E-943C-80276003478C}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4EE53631-CA03-4D29-A3A0-D17B4E57A175} + EndGlobalSection +EndGlobal diff --git a/Libraries/Concentus/CSharp/Concentus/App.config b/Libraries/Concentus/CSharp/Concentus/App.config new file mode 100644 index 000000000..fad249e40 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/AutocorrelationUnsafe.cs b/Libraries/Concentus/CSharp/Concentus/Celt/AutocorrelationUnsafe.cs new file mode 100644 index 000000000..22537f3b1 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/AutocorrelationUnsafe.cs @@ -0,0 +1,300 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#if UNSAFE + +namespace Concentus.Common +{ + using Concentus.Celt; + using Concentus.Common.CPlusPlus; + + internal static class Autocorrelation + { + /* Compute autocorrelation */ + internal static void silk_autocorr( + int[] results, /* O Result (length correlationCount) */ + BoxedValueInt scale, /* O Scaling of the correlation vector */ + short[] inputData, /* I Input data to correlate */ + int inputDataSize, /* I Length of input */ + int correlationCount /* I Number of correlation taps to compute */ + ) + { + int corrCount = Inlines.silk_min_int(inputDataSize, correlationCount); + scale.Val = Autocorrelation._celt_autocorr(inputData, results, corrCount - 1, inputDataSize); + } + + internal static unsafe int _celt_autocorr( + short[] x, /* in: [0...n-1] samples x */ + int[] ac, /* out: [0...lag-1] ac values */ + int lag, + int n + ) + { + int d; + int i, k; + int fastN = n - lag; + int shift; + short[] xx = new short[n]; + Inlines.OpusAssert(n > 0); + fixed (short* xptr_base = x, pxx = xx) + { + short* xptr = xptr_base; + shift = 0; + { + int ac0; + ac0 = 1 + (n << 7); + if ((n & 1) != 0) + { + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[0], xptr[0]), 9); + } + for (i = (n & 1); i < n; i += 2) + { + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i], xptr[i]), 9); + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i + 1], xptr[i + 1]), 9); + } + shift = Inlines.celt_ilog2(ac0) - 30 + 10; + shift = (shift) / 2; + if (shift > 0) + { + for (i = 0; i < n; i++) + { + xx[i] = (short)(Inlines.PSHR32(xptr[i], shift)); + } + xptr = pxx; + } + else + shift = 0; + } + CeltPitchXCorr.pitch_xcorr(xptr, xptr, ac, fastN, lag + 1); + for (k = 0; k <= lag; k++) + { + for (i = k + fastN, d = 0; i < n; i++) + d = Inlines.MAC16_16(d, xptr[i], xptr[i - k]); + ac[k] += d; + } + shift = 2 * shift; + if (shift <= 0) + ac[0] += Inlines.SHL32((int)1, -shift); + if (ac[0] < 268435456) + { + int shift2 = 29 - Inlines.EC_ILOG((uint)ac[0]); + for (i = 0; i <= lag; i++) + { + ac[i] = Inlines.SHL32(ac[i], shift2); + } + shift -= shift2; + } + else if (ac[0] >= 536870912) + { + int shift2 = 1; + if (ac[0] >= 1073741824) + shift2++; + for (i = 0; i <= lag; i++) + { + ac[i] = Inlines.SHR32(ac[i], shift2); + } + shift += shift2; + } + } + + return shift; + } + + internal static unsafe int _celt_autocorr( + int[] x, /* in: [0...n-1] samples x */ + int[] ac, /* out: [0...lag-1] ac values */ + int[] window, + int overlap, + int lag, + int n) + { + int d; + int i, k; + int fastN = n - lag; + int shift; + int[] xx = new int[n]; + + Inlines.OpusAssert(n > 0); + Inlines.OpusAssert(overlap >= 0); + + fixed (int* xptr_base = x, pxx = xx) + { + int* xptr = xptr_base; + + if (overlap == 0) + { + xptr = xptr_base; + } + else + { + for (i = 0; i < n; i++) + xx[i] = x[i]; + for (i = 0; i < overlap; i++) + { + xx[i] = Inlines.MULT16_16_Q15(x[i], window[i]); + xx[n - i - 1] = Inlines.MULT16_16_Q15(x[n - i - 1], window[i]); + } + xptr = pxx; + } + + shift = 0; + + int ac0; + ac0 = 1 + (n << 7); + if ((n & 1) != 0) + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[0], xptr[0]), 9); + + for (i = (n & 1); i < n; i += 2) + { + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i], xptr[i]), 9); + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i + 1], xptr[i + 1]), 9); + } + + shift = Inlines.celt_ilog2(ac0) - 30 + 10; + shift = (shift) / 2; + if (shift > 0) + { + for (i = 0; i < n; i++) + xx[i] = (Inlines.PSHR32(xptr[i], shift)); + xptr = pxx; + } + else + shift = 0; + + CeltPitchXCorr.pitch_xcorr(xptr, xptr, ac, fastN, lag + 1); + for (k = 0; k <= lag; k++) + { + for (i = k + fastN, d = 0; i < n; i++) + d = Inlines.MAC16_16(d, xptr[i], xptr[i - k]); + ac[k] += d; + } + + shift = 2 * shift; + if (shift <= 0) + ac[0] += Inlines.SHL32((int)1, -shift); + if (ac[0] < 268435456) + { + int shift2 = 29 - Inlines.EC_ILOG((uint)ac[0]); + for (i = 0; i <= lag; i++) + ac[i] = Inlines.SHL32(ac[i], shift2); + shift -= shift2; + } + else if (ac[0] >= 536870912) + { + int shift2 = 1; + if (ac[0] >= 1073741824) + shift2++; + for (i = 0; i <= lag; i++) + ac[i] = Inlines.SHR32(ac[i], shift2); + shift += shift2; + } + } + + return shift; + } + + private const int QC = 10; + private const int QS = 14; + + /* Autocorrelations for a warped frequency axis */ + internal static unsafe void silk_warped_autocorrelation( + int[] corr, /* O Result [order + 1] */ + BoxedValueInt scale, /* O Scaling of the correlation vector */ + short[] input, /* I Input data to correlate */ + int warping_Q16, /* I Warping coefficient */ + int length, /* I Length of input */ + int order /* I Correlation order (even) */ + ) + { + int n, i, lsh; + int tmp1_QS, tmp2_QS; + int[] state_QS = new int[order + 1];// = { 0 }; + long[] corr_QC = new long[order + 1];// = { 0 }; + + fixed (long* pcorr_QC = corr_QC) + { + fixed (int* pstate_QS = state_QS) + { + fixed (short* pinput = input) + { + /* Order must be even */ + Inlines.OpusAssert((order & 1) == 0); + Inlines.OpusAssert(2 * QS - QC >= 0); + + /* Loop over samples */ + for (n = 0; n < length; n++) + { + tmp1_QS = Inlines.silk_LSHIFT32((int)pinput[n], QS); + /* Loop over allpass sections */ + for (i = 0; i < order; i += 2) + { + /* Output of allpass section */ + int* pstate_QSi = pstate_QS + i; + tmp2_QS = Inlines.silk_SMLAWB(pstate_QSi[0], pstate_QSi[1] - tmp1_QS, warping_Q16); + pstate_QSi[0] = tmp1_QS; + pcorr_QC[i] += Inlines.silk_RSHIFT64(Inlines.silk_SMULL(tmp1_QS, *pstate_QS), 2 * QS - QC); + /* Output of allpass section */ + tmp1_QS = Inlines.silk_SMLAWB(pstate_QSi[1], pstate_QSi[2] - tmp2_QS, warping_Q16); + pstate_QSi[1] = tmp2_QS; + pcorr_QC[i + 1] += Inlines.silk_RSHIFT64(Inlines.silk_SMULL(tmp2_QS, *pstate_QS), 2 * QS - QC); + } + pstate_QS[order] = tmp1_QS; + pcorr_QC[order] += Inlines.silk_RSHIFT64(Inlines.silk_SMULL(tmp1_QS, *pstate_QS), 2 * QS - QC); + } + } + } + + lsh = Inlines.silk_CLZ64(*pcorr_QC) - 35; + lsh = Inlines.silk_LIMIT(lsh, -12 - QC, 30 - QC); + scale.Val = -(QC + lsh); + Inlines.OpusAssert(scale.Val >= -30 && scale.Val <= 12); + if (lsh >= 0) + { + for (i = 0; i < order + 1; i++) + { + corr[i] = (int)(Inlines.silk_LSHIFT64(pcorr_QC[i], lsh)); + } + } + else + { + for (i = 0; i < order + 1; i++) + { + corr[i] = (int)(Inlines.silk_RSHIFT64(corr_QC[i], -lsh)); + } + } + Inlines.OpusAssert(*pcorr_QC >= 0); /* If breaking, decrease QC*/ + } + } + } +} + +#endif \ No newline at end of file diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/Bands.cs b/Libraries/Concentus/CSharp/Concentus/Celt/Bands.cs new file mode 100644 index 000000000..2ab9b3679 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/Bands.cs @@ -0,0 +1,1714 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Celt +{ + using Concentus.Celt.Enums; + using Concentus.Celt.Structs; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using System; + using System.Diagnostics; + + internal static class Bands + { + internal static int hysteresis_decision( + int val, + int[] thresholds, + int[] hysteresis, + int N, + int prev) + { + int i; + for (i = 0; i < N; i++) + { + if (val < thresholds[i]) + break; + } + + if (i > prev && val < thresholds[prev] + hysteresis[prev]) + { + i = prev; + } + + if (i < prev && val > thresholds[prev - 1] - hysteresis[prev - 1]) + { + i = prev; + } + + return i; + } + + internal static uint celt_lcg_rand(uint seed) + { + return unchecked(1664525 * seed + 1013904223); + } + + /* This is a cos() approximation designed to be bit-exact on any platform. Bit exactness + with this approximation is important because it has an impact on the bit allocation */ + internal static int bitexact_cos(int x) + { + int tmp; + int x2; + tmp = (4096 + ((int)(x) * (x))) >> 13; + Inlines.OpusAssert(tmp <= 32767); + x2 = (tmp); + x2 = ((32767 - x2) + Inlines.FRAC_MUL16(x2, (-7651 + Inlines.FRAC_MUL16(x2, (8277 + Inlines.FRAC_MUL16(-626, x2)))))); + Inlines.OpusAssert(x2 <= 32766); + return (1 + x2); + } + + internal static int bitexact_log2tan(int isin, int icos) + { + int lc = Inlines.EC_ILOG((uint)icos); + int ls = Inlines.EC_ILOG((uint)isin); + icos <<= 15 - lc; + isin <<= 15 - ls; + return (ls - lc) * (1 << 11) + + Inlines.FRAC_MUL16(isin, Inlines.FRAC_MUL16(isin, -2597) + 7932) + - Inlines.FRAC_MUL16(icos, Inlines.FRAC_MUL16(icos, -2597) + 7932); + } + + /* Compute the amplitude (sqrt energy) in each of the bands */ + internal static void compute_band_energies(CeltMode m, int[][] X, int[][] bandE, int end, int C, int LM) + { + int i, c, N; + short[] eBands = m.eBands; + N = m.shortMdctSize << LM; + c = 0; + + do + { + for (i = 0; i < end; i++) + { + int j; + int maxval = 0; + int sum = 0; + maxval = Inlines.celt_maxabs32(X[c], (eBands[i] << LM), (eBands[i + 1] - eBands[i]) << LM); + if (maxval > 0) + { + int shift = Inlines.celt_ilog2(maxval) - 14 + (((m.logN[i] >> EntropyCoder.BITRES) + LM + 1) >> 1); + j = eBands[i] << LM; + if (shift > 0) + { + do + { + sum = Inlines.MAC16_16(sum, Inlines.EXTRACT16(Inlines.SHR32(X[c][j], shift)), + Inlines.EXTRACT16(Inlines.SHR32(X[c][j], shift))); + } while (++j < eBands[i + 1] << LM); + } + else { + do + { + sum = Inlines.MAC16_16(sum, Inlines.EXTRACT16(Inlines.SHL32(X[c][j], -shift)), + Inlines.EXTRACT16(Inlines.SHL32(X[c][j], -shift))); + } while (++j < eBands[i + 1] << LM); + } + /* We're adding one here to ensure the normalized band isn't larger than unity norm */ + bandE[c][i] = CeltConstants.EPSILON + Inlines.VSHR32(Inlines.celt_sqrt(sum), -shift); + } + else { + bandE[c][i] = CeltConstants.EPSILON; + } + /*printf ("%f ", bandE[i+c*m->nbEBands]);*/ + } + } while (++c < C); + } + + /* Normalise each band such that the energy is one. */ + internal static void normalise_bands(CeltMode m, int[][] freq, int[][] X, int[][] bandE, int end, int C, int M) + { + int i, c; + short[] eBands = m.eBands; + c = 0; + do + { + i = 0; + do + { + int g; + int j, shift; + int E; + shift = Inlines.celt_zlog2(bandE[c][i]) - 13; + E = Inlines.VSHR32(bandE[c][i], shift); + g = Inlines.EXTRACT16(Inlines.celt_rcp(Inlines.SHL32(E, 3))); + j = M * eBands[i]; do + { + X[c][j] = Inlines.MULT16_16_Q15(Inlines.VSHR32(freq[c][j], shift - 1), g); + } while (++j < M * eBands[i + 1]); + } while (++i < end); + } while (++c < C); + } + + /* De-normalise the energy to produce the synthesis from the unit-energy bands */ + internal static void denormalise_bands(CeltMode m, int[] X, + int[] freq, int freq_ptr, int[] bandLogE, int bandLogE_ptr, int start, + int end, int M, int downsample, int silence) + { + int i, N; + int bound; + int f; + int x; + short[] eBands = m.eBands; + N = M * m.shortMdctSize; + bound = M * eBands[end]; + if (downsample != 1) + bound = Inlines.IMIN(bound, N / downsample); + if (silence != 0) + { + bound = 0; + start = end = 0; + } + f = freq_ptr; + x = M * eBands[start]; + + for (i = 0; i < M * eBands[start]; i++) + { + freq[f++] = 0; + } + + for (i = start; i < end; i++) + { + int j, band_end; + int g; + int lg; + int shift; + + j = M * eBands[i]; + band_end = M * eBands[i + 1]; + lg = Inlines.ADD16(bandLogE[bandLogE_ptr + i], Inlines.SHL16(Tables.eMeans[i], 6)); + + /* Handle the integer part of the log energy */ + shift = 16 - (lg >> CeltConstants.DB_SHIFT); + if (shift > 31) + { + shift = 0; + g = 0; + } + else { + /* Handle the fractional part. */ + g = Inlines.celt_exp2_frac(lg & ((1 << CeltConstants.DB_SHIFT) - 1)); + } + /* Handle extreme gains with negative shift. */ + if (shift < 0) + { + /* For shift < -2 we'd be likely to overflow, so we're capping + the gain here. This shouldn't happen unless the bitstream is + already corrupted. */ + if (shift < -2) + { + g = 32767; + shift = -2; + } + do + { + freq[f] = Inlines.SHR32(Inlines.MULT16_16(X[x], g), -shift); + } while (++j < band_end); + } + else + { + do + { + freq[f++] = Inlines.SHR32(Inlines.MULT16_16(X[x++], g), shift); + } while (++j < band_end); + } + } + + Inlines.OpusAssert(start <= end); + Arrays.MemSetWithOffset(freq, 0, freq_ptr + bound, N - bound); + } + + /* This prevents energy collapse for transients with multiple short MDCTs */ + internal static void anti_collapse(CeltMode m, int[][] X_, byte[] collapse_masks, int LM, int C, int size, + int start, int end, int[] logE, int[] prev1logE, + int[] prev2logE, int[] pulses, uint seed) + { + int c, i, j, k; + for (i = start; i < end; i++) + { + int N0; + int thresh, sqrt_1; + int depth; + int shift; + int thresh32; + + N0 = m.eBands[i + 1] - m.eBands[i]; + /* depth in 1/8 bits */ + Inlines.OpusAssert(pulses[i] >= 0); + depth = Inlines.celt_udiv(1 + pulses[i], (m.eBands[i + 1] - m.eBands[i])) >> LM; + + thresh32 = Inlines.SHR32(Inlines.celt_exp2((0 - Inlines.SHL16((depth), 10 - EntropyCoder.BITRES))), 1); + thresh = (Inlines.MULT16_32_Q15(((short)(0.5 + (0.5f) * (((int)1) << (15))))/*Inlines.QCONST16(0.5f, 15)*/, Inlines.MIN32(32767, thresh32))); + { + int t; + t = N0 << LM; + shift = Inlines.celt_ilog2(t) >> 1; + t = Inlines.SHL32(t, (7 - shift) << 1); + sqrt_1 = Inlines.celt_rsqrt_norm(t); + } + + c = 0; do + { + int X; + int prev1; + int prev2; + int Ediff; + int r; + int renormalize = 0; + prev1 = prev1logE[c * m.nbEBands + i]; + prev2 = prev2logE[c * m.nbEBands + i]; + if (C == 1) + { + prev1 = Inlines.MAX16(prev1, prev1logE[m.nbEBands + i]); + prev2 = Inlines.MAX16(prev2, prev2logE[m.nbEBands + i]); + } + Ediff = Inlines.EXTEND32(logE[c * m.nbEBands + i]) - Inlines.EXTEND32(Inlines.MIN16(prev1, prev2)); + Ediff = Inlines.MAX32(0, Ediff); + + if (Ediff < 16384) + { + int r32 = Inlines.SHR32(Inlines.celt_exp2((short)(0 - Inlines.EXTRACT16(Ediff))), 1); + r = (2 * Inlines.MIN16(16383, (r32))); + } + else { + r = 0; + } + if (LM == 3) + r = Inlines.MULT16_16_Q14(23170, Inlines.MIN32(23169, r)); // opus bug: was MIN32 + r = Inlines.SHR16(Inlines.MIN16(thresh, r), 1); + r = (Inlines.SHR32(Inlines.MULT16_16_Q15(sqrt_1, r), shift)); + + X = m.eBands[i] << LM; + for (k = 0; k < 1 << LM; k++) + { + /* Detect collapse */ + if ((collapse_masks[i * C + c] & 1 << k) == 0) + { + /* Fill with noise */ + int Xk = X + k; + for (j = 0; j < N0; j++) + { + seed = celt_lcg_rand(seed); + X_[c][Xk + (j << LM)] = ((seed & 0x8000) != 0 ? r : 0 - r); + } + renormalize = 1; + } + } + /* We just added some energy, so we need to renormalise */ + if (renormalize != 0) + { + VQ.renormalise_vector(X_[c], X, N0 << LM, CeltConstants.Q15ONE); + } + } while (++c < C); + } + } + + internal static void intensity_stereo(CeltMode m, int[] X, int X_ptr, int[] Y, int Y_ptr, int[][] bandE, int bandID, int N) + { + int i = bandID; + int j; + int a1, a2; + int left, right; + int norm; + int shift = Inlines.celt_zlog2(Inlines.MAX32(bandE[0][i], bandE[1][i])) - 13; + left = Inlines.VSHR32(bandE[0][i], shift); + right = Inlines.VSHR32(bandE[1][i], shift); + norm = CeltConstants.EPSILON + Inlines.celt_sqrt(CeltConstants.EPSILON + Inlines.MULT16_16(left, left) + Inlines.MULT16_16(right, right)); + a1 = Inlines.DIV32_16(Inlines.SHL32(left, 14), norm); + a2 = Inlines.DIV32_16(Inlines.SHL32(right, 14), norm); + for (j = 0; j < N; j++) + { + int r, l; + l = X[X_ptr + j]; + r = Y[Y_ptr + j]; + X[X_ptr + j] = Inlines.EXTRACT16(Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(a1, l), a2, r), 14)); + /* Side is not encoded, no need to calculate */ + } + } + + static void stereo_split(int[] X, int X_ptr, int[] Y, int Y_ptr, int N) + { + int j; + for (j = 0; j < N; j++) + { + int r, l; + l = Inlines.MULT16_16(((short)(0.5 + (.70710678f) * (((int)1) << (15))))/*Inlines.QCONST16(.70710678f, 15)*/, X[X_ptr + j]); + r = Inlines.MULT16_16(((short)(0.5 + (.70710678f) * (((int)1) << (15))))/*Inlines.QCONST16(.70710678f, 15)*/, Y[Y_ptr + j]); + X[X_ptr + j] = Inlines.EXTRACT16(Inlines.SHR32(Inlines.ADD32(l, r), 15)); + Y[Y_ptr + j] = Inlines.EXTRACT16(Inlines.SHR32(Inlines.SUB32(r, l), 15)); + } + } + + static void stereo_merge(int[] X, int X_ptr, int[] Y, int Y_ptr, int mid, int N) + { + int j; + int xp, side; + int El, Er; + int mid2; + int kl, kr; + int t, lgain, rgain; + + /* Compute the norm of X+Y and X-Y as |X|^2 + |Y|^2 +/- sum(xy) */ +#if UNSAFE + unsafe + { + fixed (int* py_base = Y, px_base = X) + { + int* py = py_base + Y_ptr; + int* px = px_base + X_ptr; + Kernels.dual_inner_prod(py, px, py, N, out xp, out side); + } + } +#else + Kernels.dual_inner_prod(Y, Y_ptr, X, X_ptr, Y, Y_ptr, N, out xp, out side); +#endif + /* Compensating for the mid normalization */ + xp = Inlines.MULT16_32_Q15(mid, xp); + /* mid and side are in Q15, not Q14 like X and Y */ + mid2 = Inlines.SHR16(mid, 1); + El = Inlines.MULT16_16(mid2, mid2) + side - (2 * xp); + Er = Inlines.MULT16_16(mid2, mid2) + side + (2 * xp); + if (Er < ((int)(0.5 + (6e-4f) * (((int)1) << (28))))/*Inlines.QCONST32(6e-4f, 28)*/ || El < ((int)(0.5 + (6e-4f) * (((int)1) << (28))))/*Inlines.QCONST32(6e-4f, 28)*/) + { + Array.Copy(X, X_ptr, Y, Y_ptr, N); + return; + } + + kl = Inlines.celt_ilog2(El) >> 1; + kr = Inlines.celt_ilog2(Er) >> 1; + t = Inlines.VSHR32(El, (kl - 7) << 1); + lgain = Inlines.celt_rsqrt_norm(t); + t = Inlines.VSHR32(Er, (kr - 7) << 1); + rgain = Inlines.celt_rsqrt_norm(t); + + if (kl < 7) + kl = 7; + if (kr < 7) + kr = 7; + + for (j = 0; j < N; j++) + { + int r, l; + /* Apply mid scaling (side is already scaled) */ + l = Inlines.MULT16_16_P15(mid, X[X_ptr + j]); + r = Y[Y_ptr + j]; + X[X_ptr + j] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MULT16_16(lgain, Inlines.SUB16(l, r)), kl + 1)); + Y[Y_ptr + j] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MULT16_16(rgain, Inlines.ADD16(l, r)), kr + 1)); + } + } + + /* Decide whether we should spread the pulses in the current frame */ + internal static int spreading_decision(CeltMode m, int[][] X, ref int average, + int last_decision, ref int hf_average, ref int tapset_decision, int update_hf, + int end, int C, int M) + { + int i, c; + int sum = 0, nbBands = 0; + short[] eBands = m.eBands; + int decision; + int hf_sum = 0; + + Inlines.OpusAssert(end > 0); + + if (M * (eBands[end] - eBands[end - 1]) <= 8) + { + return Spread.SPREAD_NONE; + } + + c = 0; + + do + { + for (i = 0; i < end; i++) + { + int j, N, tmp = 0; + int[] tcount = { 0, 0, 0 }; + int[] x = X[c]; + int x_ptr = M * eBands[i]; + N = M * (eBands[i + 1] - eBands[i]); + if (N <= 8) + continue; + /* Compute rough CDF of |x[j]| */ + for (j = x_ptr; j < N + x_ptr; j++) + { + int x2N; /* Q13 */ + + x2N = Inlines.MULT16_16(Inlines.MULT16_16_Q15(x[j], x[j]), N); + if (x2N < ((short)(0.5 + (0.25f) * (((int)1) << (13))))/*Inlines.QCONST16(0.25f, 13)*/) + tcount[0]++; + if (x2N < ((short)(0.5 + (0.0625f) * (((int)1) << (13))))/*Inlines.QCONST16(0.0625f, 13)*/) + tcount[1]++; + if (x2N < ((short)(0.5 + (0.015625f) * (((int)1) << (13))))/*Inlines.QCONST16(0.015625f, 13)*/) + tcount[2]++; + } + + /* Only include four last bands (8 kHz and up) */ + if (i > m.nbEBands - 4) + { + hf_sum += Inlines.celt_udiv(32 * (tcount[1] + tcount[0]), N); + } + + tmp = (2 * tcount[2] >= N ? 1 : 0) + (2 * tcount[1] >= N ? 1 : 0) + (2 * tcount[0] >= N ? 1 : 0); + sum += tmp * 256; + nbBands++; + } + } while (++c < C); + + if (update_hf != 0) + { + if (hf_sum != 0) + { + hf_sum = Inlines.celt_udiv(hf_sum, C * (4 - m.nbEBands + end)); + } + + hf_average = (hf_average + hf_sum) >> 1; + hf_sum = hf_average; + + if (tapset_decision == 2) + { + hf_sum += 4; + } + else if (tapset_decision == 0) + { + hf_sum -= 4; + } + if (hf_sum > 22) + { + tapset_decision = 2; + } + else if (hf_sum > 18) + { + tapset_decision = 1; + } + else + { + tapset_decision = 0; + } + } + + Inlines.OpusAssert(nbBands > 0); /* end has to be non-zero */ + Inlines.OpusAssert(sum >= 0); + sum = Inlines.celt_udiv(sum, nbBands); + + /* Recursive averaging */ + sum = (sum + average) >> 1; + average = sum; + + /* Hysteresis */ + sum = (3 * sum + (((3 - last_decision) << 7) + 64) + 2) >> 2; + if (sum < 80) + { + decision = Spread.SPREAD_AGGRESSIVE; + } + else if (sum < 256) + { + decision = Spread.SPREAD_NORMAL; + } + else if (sum < 384) + { + decision = Spread.SPREAD_LIGHT; + } + else { + decision = Spread.SPREAD_NONE; + } +#if FUZZING + decision = new Random().Next() & 0x3; + tapset_decision.Val = new Random().Next() % 3; +#endif + return decision; + } + + internal static void deinterleave_hadamard(int[] X, int X_ptr, int N0, int stride, int hadamard) + { + int i, j; + int N; + N = N0 * stride; + int[] tmp = new int[N]; + + Inlines.OpusAssert(stride > 0); + if (hadamard != 0) + { + int ordery = (stride - 2); + + for (i = 0; i < stride; i++) + { + for (j = 0; j < N0; j++) + { + tmp[Tables.ordery_table[ordery + i] * N0 + j] = X[j * stride + i + X_ptr]; + } + } + } + else + { + for (i = 0; i < stride; i++) + { + for (j = 0; j < N0; j++) + { + tmp[i * N0 + j] = X[j * stride + i + X_ptr]; + } + } + } + + Array.Copy(tmp, 0, X, X_ptr, N); + } + + internal static void interleave_hadamard(int[] X, int X_ptr, int N0, int stride, int hadamard) + { + int i, j; + int N; + N = N0 * stride; + int[] tmp = new int[N]; + + if (hadamard != 0) + { + int ordery = (stride - 2); + for (i = 0; i < stride; i++) + { + for (j = 0; j < N0; j++) + { + tmp[j * stride + i] = X[Tables.ordery_table[ordery + i] * N0 + j + X_ptr]; + } + } + } + else + { + for (i = 0; i < stride; i++) + { + for (j = 0; j < N0; j++) + { + tmp[j * stride + i] = X[i * N0 + j + X_ptr]; + } + } + } + + Array.Copy(tmp, 0, X, X_ptr, N); + } + + internal static void haar1(int[] X, int X_ptr, int N0, int stride) + { + int i, j; + N0 >>= 1; + for (i = 0; i < stride; i++) + { + for (j = 0; j < N0; j++) + { + int tmpidx = X_ptr + i + (stride * 2 * j); + int tmp1, tmp2; + tmp1 = Inlines.MULT16_16(((short)(0.5 + (.70710678f) * (((int)1) << (15))))/*Inlines.QCONST16(.70710678f, 15)*/, X[tmpidx]); + tmp2 = Inlines.MULT16_16(((short)(0.5 + (.70710678f) * (((int)1) << (15))))/*Inlines.QCONST16(.70710678f, 15)*/, X[tmpidx + stride]); + X[tmpidx] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.ADD32(tmp1, tmp2), 15)); + X[tmpidx + stride] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.SUB32(tmp1, tmp2), 15)); + } + } + } + + internal static void haar1ZeroOffset(int[] X, int N0, int stride) + { + int i, j; + N0 >>= 1; + for (i = 0; i < stride; i++) + { + for (j = 0; j < N0; j++) + { + int tmpidx = i + (stride * 2 * j); + int tmp1, tmp2; + tmp1 = Inlines.MULT16_16(((short)(0.5 + (.70710678f) * (((int)1) << (15))))/*Inlines.QCONST16(.70710678f, 15)*/, X[tmpidx]); + tmp2 = Inlines.MULT16_16(((short)(0.5 + (.70710678f) * (((int)1) << (15))))/*Inlines.QCONST16(.70710678f, 15)*/, X[tmpidx + stride]); + X[tmpidx] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.ADD32(tmp1, tmp2), 15)); + X[tmpidx + stride] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.SUB32(tmp1, tmp2), 15)); + } + } + } + + internal static int compute_qn(int N, int b, int offset, int pulse_cap, int stereo) + { + short[] exp2_table8 = + {16384, 17866, 19483, 21247, 23170, 25267, 27554, 30048}; + int qn, qb; + int N2 = 2 * N - 1; + if (stereo != 0 && N == 2) + { + N2--; + } + + /* The upper limit ensures that in a stereo split with itheta==16384, we'll + always have enough bits left over to code at least one pulse in the + side; otherwise it would collapse, since it doesn't get folded. */ + + qb = Inlines.celt_sudiv(b + N2 * offset, N2); + qb = Inlines.IMIN(b - pulse_cap - (4 << EntropyCoder.BITRES), qb); + + qb = Inlines.IMIN(8 << EntropyCoder.BITRES, qb); + + if (qb < (1 << EntropyCoder.BITRES >> 1)) + { + qn = 1; + } + else { + qn = exp2_table8[qb & 0x7] >> (14 - (qb >> EntropyCoder.BITRES)); + qn = (qn + 1) >> 1 << 1; + } + Inlines.OpusAssert(qn <= 256); + return qn; + } + + public class band_ctx + { + public int encode; + public CeltMode m; + public int i; + public int intensity; + public int spread; + public int tf_change; + public EntropyCoder ec; + public int remaining_bits; + public int[][] bandE; + public uint seed; + }; + + public class split_ctx + { + public int inv; + public int imid; + public int iside; + public int delta; + public int itheta; + public int qalloc; + }; + + internal static void compute_theta(band_ctx ctx, split_ctx sctx, + int[] X, int X_ptr, int[] Y, int Y_ptr, int N, ref int b, int B, int B0, + int LM, + int stereo, ref int fill) + { + int qn; + int itheta = 0; + int delta; + int imid, iside; + int qalloc; + int pulse_cap; + int offset; + int tell; + int inv = 0; + int encode; + CeltMode m; + int i; + int intensity; + EntropyCoder ec; // porting note: pointer + int[][] bandE; + + encode = ctx.encode; + m = ctx.m; + i = ctx.i; + intensity = ctx.intensity; + ec = ctx.ec; + bandE = ctx.bandE; + + /* Decide on the resolution to give to the split parameter theta */ + pulse_cap = m.logN[i] + LM * (1 << EntropyCoder.BITRES); + offset = (pulse_cap >> 1) - (stereo != 0 && N == 2 ? CeltConstants.QTHETA_OFFSET_TWOPHASE : CeltConstants.QTHETA_OFFSET); + qn = compute_qn(N, b, offset, pulse_cap, stereo); + if (stereo != 0 && i >= intensity) + { + qn = 1; + } + + if (encode != 0) + { + /* theta is the atan() of the ratio between the (normalized) + side and mid. With just that parameter, we can re-scale both + mid and side because we know that 1) they have unit norm and + 2) they are orthogonal. */ + itheta = VQ.stereo_itheta(X, X_ptr, Y, Y_ptr, stereo, N); + } + + tell = (int)ec.tell_frac(); + + if (qn != 1) + { + if (encode != 0) + { + itheta = (itheta * qn + 8192) >> 14; + } + + /* Entropy coding of the angle. We use a uniform pdf for the + time split, a step for stereo, and a triangular one for the rest. */ + if (stereo != 0 && N > 2) + { + int p0 = 3; + int x = itheta; + int x0 = qn / 2; + uint ft = (uint)(p0 * (x0 + 1) + x0); + /* Use a probability of p0 up to itheta=8192 and then use 1 after */ + if (encode != 0) + { + ec.encode( + (uint)(x <= x0 ? + (p0 * x) : + ((x - 1 - x0) + (x0 + 1) * p0)), + (uint)(x <= x0 ? + (p0 * (x + 1)) : + ((x - x0) + (x0 + 1) * p0)), + ft); + } + else + { + int fs; + fs = (int)ec.decode(ft); + if (fs < (x0 + 1) * p0) + { + x = fs / p0; + } + else + { + x = x0 + 1 + (fs - (x0 + 1) * p0); + } + + ec.dec_update( + (uint)(x <= x0 ? + p0 * x : + (x - 1 - x0) + (x0 + 1) * p0), + (uint)(x <= x0 ? + p0 * (x + 1) : + (x - x0) + (x0 + 1) * p0), + ft); + itheta = x; + } + } + else if (B0 > 1 || stereo != 0) + { + /* Uniform pdf */ + if (encode != 0) + { + ec.enc_uint((uint)itheta, (uint)qn + 1); + } + else + { + itheta = (int)ec.dec_uint((uint)qn + 1); + } + } + else + { + int fs = 1, ft; + ft = ((qn >> 1) + 1) * ((qn >> 1) + 1); + if (encode != 0) + { + int fl; + + fs = itheta <= (qn >> 1) ? itheta + 1 : qn + 1 - itheta; + fl = itheta <= (qn >> 1) ? itheta * (itheta + 1) >> 1 : + ft - ((qn + 1 - itheta) * (qn + 2 - itheta) >> 1); + + ec.encode((uint)fl, (uint)(fl + fs), (uint)ft); + } + else + { + /* Triangular pdf */ + int fl = 0; + int fm; + fm = (int)ec.decode((uint)ft); + + if (fm < ((qn >> 1) * ((qn >> 1) + 1) >> 1)) + { + itheta = (int)(Inlines.isqrt32(8 * (uint)fm + 1) - 1) >> 1; + fs = itheta + 1; + fl = itheta * (itheta + 1) >> 1; + } + else + { + itheta = (int)(2 * (qn + 1) - Inlines.isqrt32(8 * (uint)(ft - fm - 1) + 1)) >> 1; + fs = qn + 1 - itheta; + fl = ft - ((qn + 1 - itheta) * (qn + 2 - itheta) >> 1); + } + + ec.dec_update((uint)fl, (uint)(fl + fs), (uint)ft); + } + } + Inlines.OpusAssert(itheta >= 0); + itheta = Inlines.celt_udiv(itheta * 16384, qn); + if (encode != 0 && stereo != 0) + { + if (itheta == 0) + { + intensity_stereo(m, X, X_ptr, Y, Y_ptr, bandE, i, N); + } + else + { + stereo_split(X, X_ptr, Y, Y_ptr, N); + } + } + } + else if (stereo != 0) + { + if (encode != 0) + { + inv = itheta > 8192 ? 1 : 0; + if (inv != 0) + { + int j; + for (j = 0; j < N; j++) + Y[Y_ptr + j] = (0 - Y[Y_ptr + j]); + } + intensity_stereo(m, X, X_ptr, Y, Y_ptr, bandE, i, N); + } + if (b > 2 << EntropyCoder.BITRES && ctx.remaining_bits > 2 << EntropyCoder.BITRES) + { + if (encode != 0) + { + ec.enc_bit_logp(inv, 2); + } + else + { + inv = ec.dec_bit_logp(2); + } + } + else + inv = 0; + itheta = 0; + } + qalloc = (int)ec.tell_frac() - tell; + b -= qalloc; + + if (itheta == 0) + { + imid = 32767; + iside = 0; + fill &= (1 << B) - 1; + delta = -16384; + } + else if (itheta == 16384) + { + imid = 0; + iside = 32767; + fill &= ((1 << B) - 1) << B; + delta = 16384; + } + else { + imid = bitexact_cos((short)itheta); + iside = bitexact_cos((short)(16384 - itheta)); + /* This is the mid vs side allocation that minimizes squared error + in that band. */ + delta = Inlines.FRAC_MUL16((N - 1) << 7, bitexact_log2tan(iside, imid)); + } + + sctx.inv = inv; + sctx.imid = imid; + sctx.iside = iside; + sctx.delta = delta; + sctx.itheta = itheta; + sctx.qalloc = qalloc; + } + + internal static uint quant_band_n1(band_ctx ctx, int[] X, int X_ptr, int[] Y, int Y_ptr, int b, + int[] lowband_out, int lowband_out_ptr) + { + int resynth = ctx.encode == 0 ? 1 : 0; + int c; + int stereo; + int[] x = X; + int x_ptr = X_ptr; + int encode; + EntropyCoder ec; // porting note: pointer + + encode = ctx.encode; + ec = ctx.ec; + + stereo = (Y != null) ? 1 : 0; + c = 0; + do + { + int sign = 0; + if (ctx.remaining_bits >= 1 << EntropyCoder.BITRES) + { + if (encode != 0) + { + sign = x[x_ptr] < 0 ? 1 : 0; + ec.enc_bits((uint)sign, 1); + } + else + { + sign = (int)ec.dec_bits(1); + } + ctx.remaining_bits -= 1 << EntropyCoder.BITRES; + b -= 1 << EntropyCoder.BITRES; + } + if (resynth != 0) + x[x_ptr] = sign != 0 ? 0 - CeltConstants.NORM_SCALING : CeltConstants.NORM_SCALING; + x = Y; + x_ptr = Y_ptr; + } while (++c < 1 + stereo); + if (lowband_out != null) + { + lowband_out[lowband_out_ptr] = Inlines.SHR16(X[X_ptr], 4); + } + + return 1; + } + + /* This function is responsible for encoding and decoding a mono partition. + It can split the band in two and transmit the energy difference with + the two half-bands. It can be called recursively so bands can end up being + split in 8 parts. */ + internal static uint quant_partition(band_ctx ctx, int[] X, int X_ptr, + int N, int b, int B, int[] lowband, int lowband_ptr, + int LM, + int gain, int fill) + { + int cache_ptr; + int q; + int curr_bits; + int imid = 0, iside = 0; + int B0 = B; + int mid = 0, side = 0; + uint cm = 0; + int resynth = (ctx.encode == 0) ? 1 : 0; + int Y = 0; + int encode; + CeltMode m; //porting note: pointer + int i; + int spread; + EntropyCoder ec; //porting note: pointer + + encode = ctx.encode; + m = ctx.m; + i = ctx.i; + spread = ctx.spread; + ec = ctx.ec; + byte[] cache = m.cache.bits; + /* If we need 1.5 more bits than we can produce, split the band in two. */ + cache_ptr = m.cache.index[(LM + 1) * m.nbEBands + i]; + if (LM != -1 && b > cache[cache_ptr + cache[cache_ptr]] + 12 && N > 2) + { + int mbits, sbits, delta; + int itheta; + int qalloc; + split_ctx sctx = new split_ctx(); + int next_lowband2 = 0; + int rebalance; + + N >>= 1; + Y = X_ptr + N; + LM -= 1; + if (B == 1) + { + fill = (fill & 1) | (fill << 1); + } + + B = (B + 1) >> 1; + + compute_theta(ctx, sctx, X, X_ptr, X, Y, N, ref b, B, B0, LM, 0, ref fill); + + imid = sctx.imid; + iside = sctx.iside; + delta = sctx.delta; + itheta = sctx.itheta; + qalloc = sctx.qalloc; + mid = (imid); + side = (iside); + + /* Give more bits to low-energy MDCTs than they would otherwise deserve */ + if (B0 > 1 && ((itheta & 0x3fff) != 0)) + { + if (itheta > 8192) + /* Rough approximation for pre-echo masking */ + delta -= delta >> (4 - LM); + else + /* Corresponds to a forward-masking slope of 1.5 dB per 10 ms */ + delta = Inlines.IMIN(0, delta + (N << EntropyCoder.BITRES >> (5 - LM))); + } + mbits = Inlines.IMAX(0, Inlines.IMIN(b, (b - delta) / 2)); + sbits = b - mbits; + ctx.remaining_bits -= qalloc; + + if (lowband != null) + { + next_lowband2 = (lowband_ptr + N); /* >32-bit split case */ + } + + rebalance = ctx.remaining_bits; + if (mbits >= sbits) + { + cm = quant_partition(ctx, X, X_ptr, N, mbits, B, + lowband, lowband_ptr, LM, + Inlines.MULT16_16_P15(gain, mid), fill); + rebalance = mbits - (rebalance - ctx.remaining_bits); + if (rebalance > 3 << EntropyCoder.BITRES && itheta != 0) + sbits += rebalance - (3 << EntropyCoder.BITRES); + cm |= quant_partition(ctx, X, Y, N, sbits, B, + lowband, next_lowband2, LM, + Inlines.MULT16_16_P15(gain, side), fill >> B) << (B0 >> 1); + } + else { + cm = quant_partition(ctx, X, Y, N, sbits, B, + lowband, next_lowband2, LM, + Inlines.MULT16_16_P15(gain, side), fill >> B) << (B0 >> 1); + rebalance = sbits - (rebalance - ctx.remaining_bits); + if (rebalance > 3 << EntropyCoder.BITRES && itheta != 16384) + mbits += rebalance - (3 << EntropyCoder.BITRES); + cm |= quant_partition(ctx, X, X_ptr, N, mbits, B, + lowband, lowband_ptr, LM, + Inlines.MULT16_16_P15(gain, mid), fill); + } + } + else { + /* This is the basic no-split case */ + q = Rate.bits2pulses(m, i, LM, b); + curr_bits = Rate.pulses2bits(m, i, LM, q); + ctx.remaining_bits -= curr_bits; + + /* Ensures we can never bust the budget */ + while (ctx.remaining_bits < 0 && q > 0) + { + ctx.remaining_bits += curr_bits; + q--; + curr_bits = Rate.pulses2bits(m, i, LM, q); + ctx.remaining_bits -= curr_bits; + } + + if (q != 0) + { + int K = Rate.get_pulses(q); + + /* Finally do the actual quantization */ + if (encode != 0) + { + cm = VQ.alg_quant(X, X_ptr, N, K, spread, B, ec); + } + else { + cm = VQ.alg_unquant(X, X_ptr, N, K, spread, B, ec, gain); + } + } + else + { + /* If there's no pulse, fill the band anyway */ + int j; + + if (resynth != 0) + { + uint cm_mask; + /* B can be as large as 16, so this shift might overflow an int on a + 16-bit platform; use a long to get defined behavior.*/ + cm_mask = (uint)(1UL << B) - 1; + fill &= (int)cm_mask; + + if (fill == 0) + { + Arrays.MemSetWithOffset(X, 0, X_ptr, N); + } + else + { + if (lowband == null) + { + /* Noise */ + for (j = 0; j < N; j++) + { + ctx.seed = celt_lcg_rand(ctx.seed); + X[X_ptr + j] = unchecked(unchecked((int)ctx.seed) >> 20); + } + cm = cm_mask; + } + else + { + /* Folded spectrum */ + for (j = 0; j < N; j++) + { + int tmp; + ctx.seed = celt_lcg_rand(ctx.seed); + /* About 48 dB below the "normal" folding level */ + tmp = ((short)(0.5 + (1.0f / 256) * (((int)1) << (10))))/*Inlines.QCONST16(1.0f / 256, 10)*/; + tmp = (((ctx.seed) & 0x8000) != 0 ? tmp : 0 - tmp); + X[X_ptr + j] = (lowband[lowband_ptr + j] + tmp); + } + cm = (uint)fill; + } + + VQ.renormalise_vector(X, X_ptr, N, gain); + } + } + } + } + + return cm; + } + + private static readonly byte[] bit_interleave_table = { 0, 1, 1, 1, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3 }; + + private static readonly byte[] bit_deinterleave_table ={ + 0x00,0x03,0x0C,0x0F,0x30,0x33,0x3C,0x3F, + 0xC0,0xC3,0xCC,0xCF,0xF0,0xF3,0xFC,0xFF + }; + + /* This function is responsible for encoding and decoding a band for the mono case. */ + internal static uint quant_band(band_ctx ctx, int[] X, int X_ptr, + int N, int b, int B, int[] lowband, int lowband_ptr, + int LM, int[] lowband_out, int lowband_out_ptr, + int gain, int[] lowband_scratch, int lowband_scratch_ptr, int fill) + { + int N0 = N; + int N_B = N; + int N_B0; + int B0 = B; + int time_divide = 0; + int recombine = 0; + int longBlocks; + uint cm = 0; + int resynth = ctx.encode == 0 ? 1 : 0; + int k; + int encode; + int tf_change; + + encode = ctx.encode; + tf_change = ctx.tf_change; + + longBlocks = B0 == 1 ? 1 : 0; + + N_B = Inlines.celt_udiv(N_B, B); + + /* Special case for one sample */ + if (N == 1) + { + return quant_band_n1(ctx, X, X_ptr, null, 0, b, lowband_out, lowband_out_ptr); + } + + if (tf_change > 0) + recombine = tf_change; + /* Band recombining to increase frequency resolution */ + + if (lowband_scratch != null && lowband != null && (recombine != 0 || ((N_B & 1) == 0 && tf_change < 0) || B0 > 1)) + { + Array.Copy(lowband, lowband_ptr, lowband_scratch, lowband_scratch_ptr, N); + lowband = lowband_scratch; + lowband_ptr = lowband_scratch_ptr; + } + + for (k = 0; k < recombine; k++) + { + if (encode != 0) + haar1(X, X_ptr, N >> k, 1 << k); + if (lowband != null) + haar1(lowband, lowband_ptr, N >> k, 1 << k); + fill = bit_interleave_table[fill & 0xF] | bit_interleave_table[fill >> 4] << 2; + } + B >>= recombine; + N_B <<= recombine; + + /* Increasing the time resolution */ + while ((N_B & 1) == 0 && tf_change < 0) + { + if (encode != 0) + haar1(X, X_ptr, N_B, B); + if (lowband != null) + haar1(lowband, lowband_ptr, N_B, B); + fill |= fill << B; + B <<= 1; + N_B >>= 1; + time_divide++; + tf_change++; + } + B0 = B; + N_B0 = N_B; + + /* Reorganize the samples in time order instead of frequency order */ + if (B0 > 1) + { + if (encode != 0) + deinterleave_hadamard(X, X_ptr, N_B >> recombine, B0 << recombine, longBlocks); + if (lowband != null) + deinterleave_hadamard(lowband, lowband_ptr, N_B >> recombine, B0 << recombine, longBlocks); + } + + cm = quant_partition(ctx, X, X_ptr, N, b, B, lowband, lowband_ptr, LM, gain, fill); + + /* This code is used by the decoder and by the resynthesis-enabled encoder */ + if (resynth != 0) + { + /* Undo the sample reorganization going from time order to frequency order */ + if (B0 > 1) + interleave_hadamard(X, X_ptr, N_B >> recombine, B0 << recombine, longBlocks); + + /* Undo time-freq changes that we did earlier */ + N_B = N_B0; + B = B0; + for (k = 0; k < time_divide; k++) + { + B >>= 1; + N_B <<= 1; + cm |= cm >> B; + haar1(X, X_ptr, N_B, B); + } + + for (k = 0; k < recombine; k++) + { + cm = bit_deinterleave_table[cm]; + haar1(X, X_ptr, N0 >> k, 1 << k); + } + B <<= recombine; + + /* Scale output for later folding */ + if (lowband_out != null) + { + int j; + int n; + n = (Inlines.celt_sqrt(Inlines.SHL32(N0, 22))); + for (j = 0; j < N0; j++) + lowband_out[lowband_out_ptr + j] = Inlines.MULT16_16_Q15(n, X[X_ptr + j]); + } + + cm = cm & (uint)((1 << B) - 1); + } + return cm; + } + + /* This function is responsible for encoding and decoding a band for the stereo case. */ + internal static uint quant_band_stereo(band_ctx ctx, int[] X, int X_ptr, int[] Y, int Y_ptr, + int N, int b, int B, int[] lowband, int lowband_ptr, + int LM, int[] lowband_out, int lowband_out_ptr, + int[] lowband_scratch, int lowband_scratch_ptr, int fill) + { + int imid = 0, iside = 0; + int inv = 0; + int mid = 0, side = 0; + uint cm = 0; + int resynth = ctx.encode == 0 ? 1 : 0; + int mbits, sbits, delta; + int itheta; + int qalloc; + split_ctx sctx = new split_ctx(); // porting note: stack var + int orig_fill; + int encode; + EntropyCoder ec; //porting note: pointer + + encode = ctx.encode; + ec = ctx.ec; + + /* Special case for one sample */ + if (N == 1) + { + return quant_band_n1(ctx, X, X_ptr, Y, Y_ptr, b, lowband_out, lowband_out_ptr); + } + + orig_fill = fill; + + compute_theta(ctx, sctx, X, X_ptr, Y, Y_ptr, N, ref b, B, B, LM, 1, ref fill); + + inv = sctx.inv; + imid = sctx.imid; + iside = sctx.iside; + delta = sctx.delta; + itheta = sctx.itheta; + qalloc = sctx.qalloc; + mid = (imid); + side = (iside); + + /* This is a special case for N=2 that only works for stereo and takes + advantage of the fact that mid and side are orthogonal to encode + the side with just one bit. */ + if (N == 2) + { + int c; + int sign = 0; + int[] x2, y2; + int x2_ptr, y2_ptr; + mbits = b; + sbits = 0; + /* Only need one bit for the side. */ + if (itheta != 0 && itheta != 16384) + sbits = 1 << EntropyCoder.BITRES; + mbits -= sbits; + c = itheta > 8192 ? 1 : 0; + ctx.remaining_bits -= qalloc + sbits; + if (c != 0) + { + x2 = Y; + x2_ptr = Y_ptr; + y2 = X; + y2_ptr = X_ptr; + } + else + { + x2 = X; + x2_ptr = X_ptr; + y2 = Y; + y2_ptr = Y_ptr; + } + + if (sbits != 0) + { + if (encode != 0) + { + /* Here we only need to encode a sign for the side. */ + sign = (x2[x2_ptr] * y2[Y_ptr + 1] - x2[x2_ptr + 1] * y2[Y_ptr] < 0) ? 1 : 0; + ec.enc_bits((uint)sign, 1); + } + else + { + sign = (int)ec.dec_bits(1); + } + } + sign = 1 - 2 * sign; + /* We use orig_fill here because we want to fold the side, but if + itheta==16384, we'll have cleared the low bits of fill. */ + cm = quant_band(ctx, x2, x2_ptr, N, mbits, B, lowband, lowband_ptr, + LM, lowband_out, lowband_out_ptr, CeltConstants.Q15ONE, lowband_scratch, lowband_scratch_ptr, orig_fill); + + /* We don't split N=2 bands, so cm is either 1 or 0 (for a fold-collapse), + and there's no need to worry about mixing with the other channel. */ + y2[Y_ptr] = ((0 - sign) * x2[x2_ptr + 1]); + y2[Y_ptr + 1] = (sign * x2[x2_ptr]); + if (resynth != 0) + { + int tmp; + X[X_ptr] = Inlines.MULT16_16_Q15(mid, X[X_ptr]); + X[X_ptr + 1] = Inlines.MULT16_16_Q15(mid, X[X_ptr + 1]); + Y[Y_ptr] = Inlines.MULT16_16_Q15(side, Y[Y_ptr]); + Y[Y_ptr + 1] = Inlines.MULT16_16_Q15(side, Y[Y_ptr + 1]); + tmp = X[X_ptr]; + X[X_ptr] = Inlines.SUB16(tmp, Y[Y_ptr]); + Y[Y_ptr] = Inlines.ADD16(tmp, Y[Y_ptr]); + tmp = X[X_ptr + 1]; + X[X_ptr + 1] = Inlines.SUB16(tmp, Y[Y_ptr + 1]); + Y[Y_ptr + 1] = Inlines.ADD16(tmp, Y[Y_ptr + 1]); + } + } + else + { + /* "Normal" split code */ + int rebalance; + + mbits = Inlines.IMAX(0, Inlines.IMIN(b, (b - delta) / 2)); + sbits = b - mbits; + ctx.remaining_bits -= qalloc; + + rebalance = ctx.remaining_bits; + if (mbits >= sbits) + { + /* In stereo mode, we do not apply a scaling to the mid because we need the normalized + mid for folding later. */ + cm = quant_band(ctx, X, X_ptr, N, mbits, B, + lowband, lowband_ptr, LM, lowband_out, lowband_out_ptr, + CeltConstants.Q15ONE, lowband_scratch, lowband_scratch_ptr, fill); + rebalance = mbits - (rebalance - ctx.remaining_bits); + if (rebalance > 3 << EntropyCoder.BITRES && itheta != 0) + sbits += rebalance - (3 << EntropyCoder.BITRES); + + /* For a stereo split, the high bits of fill are always zero, so no + folding will be done to the side. */ + cm |= quant_band(ctx, Y, Y_ptr, N, sbits, B, + null, 0, LM, null, 0, + side, null, 0, fill >> B); + } + else + { + /* For a stereo split, the high bits of fill are always zero, so no + folding will be done to the side. */ + cm = quant_band(ctx, Y, Y_ptr, N, sbits, B, + null, 0, LM, null, 0, + side, null, 0, fill >> B); + rebalance = sbits - (rebalance - ctx.remaining_bits); + if (rebalance > 3 << EntropyCoder.BITRES && itheta != 16384) + mbits += rebalance - (3 << EntropyCoder.BITRES); + /* In stereo mode, we do not apply a scaling to the mid because we need the normalized + mid for folding later. */ + cm |= quant_band(ctx, X, X_ptr, N, mbits, B, + lowband, lowband_ptr, LM, lowband_out, lowband_out_ptr, + CeltConstants.Q15ONE, lowband_scratch, lowband_scratch_ptr, fill); + } + } + + + /* This code is used by the decoder and by the resynthesis-enabled encoder */ + if (resynth != 0) + { + if (N != 2) + { + stereo_merge(X, X_ptr, Y, Y_ptr, mid, N); + } + if (inv != 0) + { + int j; + for (j = Y_ptr; j < N + Y_ptr; j++) + Y[j] = (short)(0 - Y[j]); + } + } + + return cm; + } + + + internal static void quant_all_bands(int encode, CeltMode m, int start, int end, + int[] X_, int[] Y_, byte[] collapse_masks, + int[][] bandE, int[] pulses, int shortBlocks, int spread, + int dual_stereo, int intensity, int[] tf_res, int total_bits, + int balance, EntropyCoder ec, int LM, int codedBands, + ref uint seed) + { + int i; + int remaining_bits; + short[] eBands = m.eBands; + int[] norm; + int norm2; + int[] lowband_scratch; + int lowband_scratch_ptr; + int B; + int M; + int lowband_offset; + int update_lowband = 1; + int C = Y_ != null ? 2 : 1; + int norm_offset; + int resynth = encode == 0 ? 1 : 0; + band_ctx ctx = new band_ctx(); // porting note: stack var + + M = 1 << LM; + B = (shortBlocks != 0) ? M : 1; + norm_offset = M * eBands[start]; + + /* No need to allocate norm for the last band because we don't need an + output in that band. */ + norm = new int[(C * (M * eBands[m.nbEBands - 1] - norm_offset))]; + norm2 = M * eBands[m.nbEBands - 1] - norm_offset; + + /* We can use the last band as scratch space because we don't need that + scratch space for the last band. */ + lowband_scratch = X_; + lowband_scratch_ptr = M * eBands[m.nbEBands - 1]; + + lowband_offset = 0; + ctx.bandE = bandE; + ctx.ec = ec; + ctx.encode = encode; + ctx.intensity = intensity; + ctx.m = m; + ctx.seed = seed; + ctx.spread = spread; + for (i = start; i < end; i++) + { + int tell; + int b; + int N; + int curr_balance; + int effective_lowband = -1; + int[] X, Y; + int X_ptr, Y_ptr; + Y_ptr = 0; + int tf_change = 0; + uint x_cm; + uint y_cm; + int last; + + ctx.i = i; + last = (i == end - 1) ? 1 : 0; + + X = X_; + X_ptr = (M * eBands[i]); + if (Y_ != null) + { + Y = Y_; + Y_ptr = (M * eBands[i]); + } + else + { + Y = null; + } + N = M * eBands[i + 1] - M * eBands[i]; + tell = (int)ec.tell_frac(); + + /* Compute how many bits we want to allocate to this band */ + if (i != start) + balance -= tell; + remaining_bits = total_bits - tell - 1; + ctx.remaining_bits = remaining_bits; + if (i <= codedBands - 1) + { + curr_balance = Inlines.celt_sudiv(balance, Inlines.IMIN(3, codedBands - i)); + b = Inlines.IMAX(0, Inlines.IMIN(16383, Inlines.IMIN(remaining_bits + 1, pulses[i] + curr_balance))); + } + else + { + b = 0; + } + + if (resynth != 0 && M * eBands[i] - N >= M * eBands[start] && (update_lowband != 0 || lowband_offset == 0)) + { + lowband_offset = i; + } + + tf_change = tf_res[i]; + ctx.tf_change = tf_change; + if (i >= m.effEBands) + { + X = norm; + X_ptr = 0; + if (Y_ != null) + { + Y = norm; + Y_ptr = 0; + } + lowband_scratch = null; + } + if (i == end - 1) + { + lowband_scratch = null; + } + + /* Get a conservative estimate of the collapse_mask's for the bands we're + going to be folding from. */ + if (lowband_offset != 0 && (spread != Spread.SPREAD_AGGRESSIVE || B > 1 || tf_change < 0)) + { + int fold_start; + int fold_end; + int fold_i; + /* This ensures we never repeat spectral content within one band */ + effective_lowband = Inlines.IMAX(0, M * eBands[lowband_offset] - norm_offset - N); + fold_start = lowband_offset; + while (M * eBands[--fold_start] > effective_lowband + norm_offset) ; + fold_end = lowband_offset - 1; + while (M * eBands[++fold_end] < effective_lowband + norm_offset + N) ; + x_cm = y_cm = 0; + fold_i = fold_start; do + { + x_cm |= collapse_masks[fold_i * C + 0]; + y_cm |= collapse_masks[fold_i * C + C - 1]; + } while (++fold_i < fold_end); + } + /* Otherwise, we'll be using the LCG to fold, so all blocks will (almost + always) be non-zero. */ + else + { + x_cm = y_cm = (uint)((1 << B) - 1); + } + + if (dual_stereo != 0 && i == intensity) + { + int j; + + /* Switch off dual stereo to do intensity. */ + dual_stereo = 0; + if (resynth != 0) + { + for (j = 0; j < M * eBands[i] - norm_offset; j++) + { + norm[j] = (Inlines.HALF32(norm[j] + norm[norm2 + j])); + } + } + } + if (dual_stereo != 0) + { + x_cm = quant_band(ctx, + X, + X_ptr, + N, + b / 2, + B, + effective_lowband != -1 ? norm : null, + effective_lowband, + LM, + last != 0 ? null : norm, + M * eBands[i] - norm_offset, + CeltConstants.Q15ONE, + lowband_scratch, + lowband_scratch_ptr, + (int)x_cm); + y_cm = quant_band( + ctx, + Y, + Y_ptr, + N, + b / 2, + B, + effective_lowband != -1 ? norm : null, + norm2 + effective_lowband, + LM, + last != 0 ? null : norm, + norm2 + (M * eBands[i] - norm_offset), + CeltConstants.Q15ONE, + lowband_scratch, + lowband_scratch_ptr, + (int)y_cm); + } + else + { + if (Y != null) + { + x_cm = quant_band_stereo( + ctx, + X, + X_ptr, + Y, + Y_ptr, + N, + b, + B, + effective_lowband != -1 ? norm : null, + effective_lowband, + LM, + last != 0 ? null : norm, + M * eBands[i] - norm_offset, + lowband_scratch, + lowband_scratch_ptr, + (int)(x_cm | y_cm)); + } + else + { + x_cm = quant_band( + ctx, + X, + X_ptr, + N, + b, + B, + effective_lowband != -1 ? norm : null, + effective_lowband, + LM, + last != 0 ? null : norm, + M * eBands[i] - norm_offset, + CeltConstants.Q15ONE, + lowband_scratch, + lowband_scratch_ptr, + (int)(x_cm | y_cm)); // opt: lots of pointers are created here too + } + y_cm = x_cm; + } + collapse_masks[i * C + 0] = (byte)(x_cm & 0xFF); + collapse_masks[i * C + C - 1] = (byte)(y_cm & 0xFF); + balance += pulses[i] + tell; + + /* Update the folding position only as long as we have 1 bit/sample depth. */ + update_lowband = (b > (N << EntropyCoder.BITRES)) ? 1 : 0; + } + + seed = ctx.seed; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/CWRS.cs b/Libraries/Concentus/CSharp/Concentus/Celt/CWRS.cs new file mode 100644 index 000000000..593eee75f --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/CWRS.cs @@ -0,0 +1,319 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Celt +{ + using Concentus.Celt.Enums; + using Concentus.Celt.Structs; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using System.Diagnostics; + + internal static class CWRS + { + /*Although derived separately, the pulse vector coding scheme is equivalent to + a Pyramid Vector Quantizer \cite{Fis86}. + Some additional notes about an early version appear at + http://people.xiph.org/~tterribe/notes/cwrs.html, but the codebook ordering + and the definitions of some terms have evolved since that was written. + + The conversion from a pulse vector to an integer index (encoding) and back + (decoding) is governed by two related functions, V(N,K) and U(N,K). + + V(N,K) = the number of combinations, with replacement, of N items, taken K + at a time, when a sign bit is added to each item taken at least once (i.e., + the number of N-dimensional unit pulse vectors with K pulses). + One way to compute this is via + V(N,K) = K>0 ? sum(k=1...K,2**k*choose(N,k)*choose(K-1,k-1)) : 1, + where choose() is the binomial function. + A table of values for N<10 and K<10 looks like: + V[10][10] = { + {1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {1, 2, 2, 2, 2, 2, 2, 2, 2, 2}, + {1, 4, 8, 12, 16, 20, 24, 28, 32, 36}, + {1, 6, 18, 38, 66, 102, 146, 198, 258, 326}, + {1, 8, 32, 88, 192, 360, 608, 952, 1408, 1992}, + {1, 10, 50, 170, 450, 1002, 1970, 3530, 5890, 9290}, + {1, 12, 72, 292, 912, 2364, 5336, 10836, 20256, 35436}, + {1, 14, 98, 462, 1666, 4942, 12642, 28814, 59906, 115598}, + {1, 16, 128, 688, 2816, 9424, 27008, 68464, 157184, 332688}, + {1, 18, 162, 978, 4482, 16722, 53154, 148626, 374274, 864146} + }; + + U(N,K) = the number of such combinations wherein N-1 objects are taken at + most K-1 at a time. + This is given by + U(N,K) = sum(k=0...K-1,V(N-1,k)) + = K>0 ? (V(N-1,K-1) + V(N,K-1))/2 : 0. + The latter expression also makes clear that U(N,K) is half the number of such + combinations wherein the first object is taken at least once. + Although it may not be clear from either of these definitions, U(N,K) is the + natural function to work with when enumerating the pulse vector codebooks, + not V(N,K). + U(N,K) is not well-defined for N=0, but with the extension + U(0,K) = K>0 ? 0 : 1, + the function becomes symmetric: U(N,K) = U(K,N), with a similar table: + U[10][10] = { + {1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {0, 1, 3, 5, 7, 9, 11, 13, 15, 17}, + {0, 1, 5, 13, 25, 41, 61, 85, 113, 145}, + {0, 1, 7, 25, 63, 129, 231, 377, 575, 833}, + {0, 1, 9, 41, 129, 321, 681, 1289, 2241, 3649}, + {0, 1, 11, 61, 231, 681, 1683, 3653, 7183, 13073}, + {0, 1, 13, 85, 377, 1289, 3653, 8989, 19825, 40081}, + {0, 1, 15, 113, 575, 2241, 7183, 19825, 48639, 108545}, + {0, 1, 17, 145, 833, 3649, 13073, 40081, 108545, 265729} + }; + + With this extension, V(N,K) may be written in terms of U(N,K): + V(N,K) = U(N,K) + U(N,K+1) + for all N>=0, K>=0. + Thus U(N,K+1) represents the number of combinations where the first element + is positive or zero, and U(N,K) represents the number of combinations where + it is negative. + With a large enough table of U(N,K) values, we could write O(N) encoding + and O(min(N*log(K),N+K)) decoding routines, but such a table would be + prohibitively large for small embedded devices (K may be as large as 32767 + for small N, and N may be as large as 200). + + Both functions obey the same recurrence relation: + V(N,K) = V(N-1,K) + V(N,K-1) + V(N-1,K-1), + U(N,K) = U(N-1,K) + U(N,K-1) + U(N-1,K-1), + for all N>0, K>0, with different initial conditions at N=0 or K=0. + This allows us to construct a row of one of the tables above given the + previous row or the next row. + Thus we can derive O(NK) encoding and decoding routines with O(K) memory + using only addition and subtraction. + + When encoding, we build up from the U(2,K) row and work our way forwards. + When decoding, we need to start at the U(N,K) row and work our way backwards, + which requires a means of computing U(N,K). + U(N,K) may be computed from two previous values with the same N: + U(N,K) = ((2*N-1)*U(N,K-1) - U(N,K-2))/(K-1) + U(N,K-2) + for all N>1, and since U(N,K) is symmetric, a similar relation holds for two + previous values with the same K: + U(N,K>1) = ((2*K-1)*U(N-1,K) - U(N-2,K))/(N-1) + U(N-2,K) + for all K>1. + This allows us to construct an arbitrary row of the U(N,K) table by starting + with the first two values, which are constants. + This saves roughly 2/3 the work in our O(NK) decoding routine, but costs O(K) + multiplications. + Similar relations can be derived for V(N,K), but are not used here. + + For N>0 and K>0, U(N,K) and V(N,K) take on the form of an (N-1)-degree + polynomial for fixed N. + The first few are + U(1,K) = 1, + U(2,K) = 2*K-1, + U(3,K) = (2*K-2)*K+1, + U(4,K) = (((4*K-6)*K+8)*K-3)/3, + U(5,K) = ((((2*K-4)*K+10)*K-8)*K+3)/3, + and + V(1,K) = 2, + V(2,K) = 4*K, + V(3,K) = 4*K*K+2, + V(4,K) = 8*(K*K+2)*K/3, + V(5,K) = ((4*K*K+20)*K*K+6)/3, + for all K>0. + This allows us to derive O(N) encoding and O(N*log(K)) decoding routines for + small N (and indeed decoding is also O(N) for N<3). + + @ARTICLE{Fis86, + author="Thomas R. Fischer", + title="A Pyramid Vector Quantizer", + journal="IEEE Transactions on Information Theory", + volume="IT-32", + number=4, + pages="568--583", + month=Jul, + year=1986 + }*/ + + internal static readonly uint[] CELT_PVQ_U_ROW = + { + 0,176,351,525,698,870,1041,1131,1178,1207,1226,1240,1248,1254,1257 + }; + + /*U(N,K) = U(K,N) := N>0?K>0?U(N-1,K)+U(N,K-1)+U(N-1,K-1):0:K>0?1:0*/ + private static uint CELT_PVQ_U(int _n, int _k) + { + return Tables.CELT_PVQ_U_DATA[CELT_PVQ_U_ROW[Inlines.IMIN(_n, _k)] + Inlines.IMAX(_n, _k)]; + } + + + /*V(N,K) := U(N,K)+U(N,K+1) = the number of PVQ codewords for a band of size N + with K pulses allocated to it.*/ + private static uint CELT_PVQ_V(int _n, int _k) + { + return (CELT_PVQ_U(_n, _k) + CELT_PVQ_U(_n, (_k) + 1)); + } + + internal static uint icwrs(int _n, int[] _y) + { + uint i; + int j; + int k; + Inlines.OpusAssert(_n >= 2); + j = _n - 1; + i = (_y[j] < 0) ? 1U : 0; + k = Inlines.abs(_y[j]); + do + { + j--; + i += CELT_PVQ_U(_n - j, k); + k += Inlines.abs(_y[j]); + if (_y[j] < 0) i += CELT_PVQ_U(_n - j, k + 1); + } + while (j > 0); + return i; + } + + internal static void encode_pulses(int[] _y, int _n, int _k, EntropyCoder _enc) + { + Inlines.OpusAssert(_k > 0); + _enc.enc_uint(icwrs(_n, _y), CELT_PVQ_V(_n, _k)); + } + + internal static int cwrsi(int _n, int _k, uint _i, int[] _y) + { + uint p; + int s; + int k0; + short val; + int yy = 0; + int y_ptr = 0; + Inlines.OpusAssert(_k > 0); + Inlines.OpusAssert(_n > 1); + + while (_n > 2) + { + uint q; + /*Lots of pulses case:*/ + if (_k >= _n) + { + uint row; + row = CELT_PVQ_U_ROW[_n]; + /*Are the pulses in this dimension negative?*/ + p = Tables.CELT_PVQ_U_DATA[row + _k + 1]; + s = 0 - (_i >= p ? 1 : 0); + _i -= (p & unchecked((uint)s)); + /*Count how many pulses were placed in this dimension.*/ + k0 = _k; + q = Tables.CELT_PVQ_U_DATA[row + _n]; + + if (q > _i) + { + Inlines.OpusAssert(p > q); + _k = _n; + + do + { + p = Tables.CELT_PVQ_U_DATA[CELT_PVQ_U_ROW[--_k] + _n]; + } while (p > _i); + } + else + { + for (p = Tables.CELT_PVQ_U_DATA[row + _k]; p > _i; p = Tables.CELT_PVQ_U_DATA[row + _k]) + { + _k--; + } + } + + _i -= p; + val = (short)((k0 - _k + s) ^ s); + _y[y_ptr++] = val; + yy = Inlines.MAC16_16(yy, val, val); + } + /*Lots of dimensions case:*/ + else + { + /*Are there any pulses in this dimension at all?*/ + p = Tables.CELT_PVQ_U_DATA[CELT_PVQ_U_ROW[_k] + _n]; + q = Tables.CELT_PVQ_U_DATA[CELT_PVQ_U_ROW[_k + 1] + _n]; + if (p <= _i && _i < q) + { + _i -= p; + _y[y_ptr++] = 0; + } + else + { + /*Are the pulses in this dimension negative?*/ + s = 0 - (_i >= q ? 1 : 0); + _i -= (q & unchecked((uint)s)); + /*Count how many pulses were placed in this dimension.*/ + k0 = _k; + do + { + p = Tables.CELT_PVQ_U_DATA[CELT_PVQ_U_ROW[--_k] + _n]; + } while (p > _i); + + _i -= p; + val = (short)((k0 - _k + s) ^ s); + _y[y_ptr++] = val; + yy = Inlines.MAC16_16(yy, val, val); + } + } + _n--; + } + + /*_n==2*/ + p = (uint)(2 * _k + 1); + s = 0 - (_i >= p ? 1 : 0); + _i -= (p & unchecked((uint)s)); + k0 = _k; + _k = (int)((_i + 1) >> 1); + if (_k != 0) + { + _i -= (2 * (uint)_k - 1); + } + + val = (short)((k0 - _k + s) ^ s); + _y[y_ptr++] = val; + yy = Inlines.MAC16_16(yy, val, val); + /*_n==1*/ + s = -(int)_i; + val = (short)((_k + s) ^ s); + _y[y_ptr] = val; + yy = Inlines.MAC16_16(yy, val, val); + return yy; + } + + internal static int decode_pulses(int[] _y, int _n, int _k, EntropyCoder _dec) + { + return cwrsi(_n, _k, _dec.dec_uint(CELT_PVQ_V(_n, _k)), _y); + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/CeltCommon.cs b/Libraries/Concentus/CSharp/Concentus/Celt/CeltCommon.cs new file mode 100644 index 000000000..1b1da8765 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/CeltCommon.cs @@ -0,0 +1,1406 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Celt +{ + using Concentus.Celt.Enums; + using Concentus.Celt.Structs; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Enums; + using System; + using System.Diagnostics; + + internal class CeltCommon + { + /* Table of 6*64/x, trained on real data to minimize the average error */ + private static readonly byte[] inv_table = { + 255,255,156,110, 86, 70, 59, 51, 45, 40, 37, 33, 31, 28, 26, 25, + 23, 22, 21, 20, 19, 18, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12, + 12, 12, 11, 11, 11, 10, 10, 10, 9, 9, 9, 9, 9, 9, 8, 8, + 8, 8, 8, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, + }; + + internal static int compute_vbr(CeltMode mode, AnalysisInfo analysis, int base_target, + int LM, int bitrate, int lastCodedBands, int C, int intensity, + int constrained_vbr, int stereo_saving, int tot_boost, + int tf_estimate, int pitch_change, int maxDepth, + OpusFramesize variable_duration, int lfe, int has_surround_mask, int surround_masking, + int temporal_vbr) + { + /* The target rate in 8th bits per frame */ + int target; + int coded_bins; + int coded_bands; + int tf_calibration; + int nbEBands; + short[] eBands; + + nbEBands = mode.nbEBands; + eBands = mode.eBands; + + coded_bands = lastCodedBands != 0 ? lastCodedBands : nbEBands; + coded_bins = eBands[coded_bands] << LM; + if (C == 2) + coded_bins += eBands[Inlines.IMIN(intensity, coded_bands)] << LM; + + target = base_target; + if (analysis.valid != 0 && analysis.activity < .4) + target -= (int)((coded_bins << EntropyCoder.BITRES) * (.4f - analysis.activity)); + + /* Stereo savings */ + if (C == 2) + { + int coded_stereo_bands; + int coded_stereo_dof; + int max_frac; + coded_stereo_bands = Inlines.IMIN(intensity, coded_bands); + coded_stereo_dof = (eBands[coded_stereo_bands] << LM) - coded_stereo_bands; + /* Maximum fraction of the bits we can save if the signal is mono. */ + max_frac = Inlines.DIV32_16(Inlines.MULT16_16(((short)(0.5 + (0.8f) * (((int)1) << (15))))/*Inlines.QCONST16(0.8f, 15)*/, coded_stereo_dof), coded_bins); + stereo_saving = Inlines.MIN16(stereo_saving, ((short)(0.5 + (1.0f) * (((int)1) << (8))))/*Inlines.QCONST16(1.0f, 8)*/); + /*printf("%d %d %d ", coded_stereo_dof, coded_bins, tot_boost);*/ + target -= (int)Inlines.MIN32(Inlines.MULT16_32_Q15(max_frac, target), + Inlines.SHR32(Inlines.MULT16_16(stereo_saving - ((short)(0.5 + (0.1f) * (((int)1) << (8))))/*Inlines.QCONST16(0.1f, 8)*/, (coded_stereo_dof << EntropyCoder.BITRES)), 8)); + } + /* Boost the rate according to dynalloc (minus the dynalloc average for calibration). */ + target += tot_boost - (16 << LM); + /* Apply transient boost, compensating for average boost. */ + tf_calibration = variable_duration == OpusFramesize.OPUS_FRAMESIZE_VARIABLE ? + ((short)(0.5 + (0.02f) * (((int)1) << (14))))/*Inlines.QCONST16(0.02f, 14)*/ : ((short)(0.5 + (0.04f) * (((int)1) << (14))))/*Inlines.QCONST16(0.04f, 14)*/; + target += (int)Inlines.SHL32(Inlines.MULT16_32_Q15(tf_estimate - tf_calibration, target), 1); + + /* Apply tonality boost */ + if (analysis.valid != 0 && lfe == 0) + { + int tonal_target; + float tonal; + + /* Tonality boost (compensating for the average). */ + tonal = Inlines.MAX16(0, analysis.tonality - .15f) - 0.09f; + tonal_target = target + (int)((coded_bins << EntropyCoder.BITRES) * 1.2f * tonal); + if (pitch_change != 0) + tonal_target += (int)((coded_bins << EntropyCoder.BITRES) * .8f); + target = tonal_target; + } + + if (has_surround_mask != 0 && lfe == 0) + { + int surround_target = target + (int)Inlines.SHR32(Inlines.MULT16_16(surround_masking, coded_bins << EntropyCoder.BITRES), CeltConstants.DB_SHIFT); + /*printf("%f %d %d %d %d %d %d ", surround_masking, coded_bins, st.end, st.intensity, surround_target, target, st.bitrate);*/ + target = Inlines.IMAX(target / 4, surround_target); + } + + { + int floor_depth; + int bins; + bins = eBands[nbEBands - 2] << LM; + /*floor_depth = Inlines.SHR32(Inlines.MULT16_16((C*bins<> 2); + target = Inlines.IMIN(target, floor_depth); + /*printf("%f %d\n", maxDepth, floor_depth);*/ + } + + if ((has_surround_mask == 0 || lfe != 0) && (constrained_vbr != 0 || bitrate < 64000)) + { + int rate_factor; + rate_factor = Inlines.MAX16(0, (bitrate - 32000)); + if (constrained_vbr != 0) + rate_factor = Inlines.MIN16(rate_factor, ((short)(0.5 + (0.67f) * (((int)1) << (15))))/*Inlines.QCONST16(0.67f, 15)*/); + target = base_target + (int)Inlines.MULT16_32_Q15(rate_factor, target - base_target); + } + + if (has_surround_mask == 0 && tf_estimate < ((short)(0.5 + (.2f) * (((int)1) << (14))))/*Inlines.QCONST16(.2f, 14)*/) + { + int amount; + int tvbr_factor; + amount = Inlines.MULT16_16_Q15(((short)(0.5 + (.0000031f) * (((int)1) << (30))))/*Inlines.QCONST16(.0000031f, 30)*/, Inlines.IMAX(0, Inlines.IMIN(32000, 96000 - bitrate))); + tvbr_factor = Inlines.SHR32(Inlines.MULT16_16(temporal_vbr, amount), CeltConstants.DB_SHIFT); + target += (int)Inlines.MULT16_32_Q15(tvbr_factor, target); + } + + /* Don't allow more than doubling the rate */ + target = Inlines.IMIN(2 * base_target, target); + + return target; + } + + internal static int transient_analysis(int[][] input, int len, int C, + out int tf_estimate, out int tf_chan) + { + int i; + int[] tmp; + int mem0, mem1; + int is_transient = 0; + int mask_metric = 0; + int c; + int tf_max; + int len2; + tf_chan = 0; + tmp = new int[len]; + + len2 = len / 2; + for (c = 0; c < C; c++) + { + int mean; + int unmask = 0; + int norm; + int maxE; + mem0 = 0; + mem1 = 0; + /* High-pass filter: (1 - 2*z^-1 + z^-2) / (1 - z^-1 + .5*z^-2) */ + for (i = 0; i < len; i++) + { + int x, y; + x = Inlines.SHR32(input[c][i], CeltConstants.SIG_SHIFT); + y = Inlines.ADD32(mem0, x); + mem0 = mem1 + y - Inlines.SHL32(x, 1); + mem1 = x - Inlines.SHR32(y, 1); + tmp[i] = Inlines.EXTRACT16(Inlines.SHR32(y, 2)); + /*printf("%f ", tmp[i]);*/ + } + /*printf("\n");*/ + /* First few samples are bad because we don't propagate the memory */ + Arrays.MemSetInt(tmp, 0, 12); + + /* Normalize tmp to max range */ + { + int shift = 0; + shift = 14 - Inlines.celt_ilog2(1 + Inlines.celt_maxabs32(tmp, 0, len)); + if (shift != 0) + { + for (i = 0; i < len; i++) + tmp[i] = Inlines.SHL16(tmp[i], shift); + } + } + + mean = 0; + mem0 = 0; + /* Grouping by two to reduce complexity */ + /* Forward pass to compute the post-echo threshold*/ + for (i = 0; i < len2; i++) + { + int x2 = (Inlines.PSHR32(Inlines.MULT16_16(tmp[2 * i], tmp[2 * i]) + Inlines.MULT16_16(tmp[2 * i + 1], tmp[2 * i + 1]), 16)); + mean += x2; + tmp[i] = (mem0 + Inlines.PSHR32(x2 - mem0, 4)); + mem0 = tmp[i]; + } + + mem0 = 0; + maxE = 0; + /* Backward pass to compute the pre-echo threshold */ + for (i = len2 - 1; i >= 0; i--) + { + tmp[i] = (mem0 + Inlines.PSHR32(tmp[i] - mem0, 3)); + mem0 = tmp[i]; + maxE = Inlines.MAX16(maxE, (mem0)); + } + /*for (i=0;i> 1))); + /* Inverse of the mean energy in Q15+6 */ + norm = Inlines.SHL32((len2), 6 + 14) / Inlines.ADD32(CeltConstants.EPSILON, Inlines.SHR32(mean, 1)); + /* Compute harmonic mean discarding the unreliable boundaries + The data is smooth, so we only take 1/4th of the samples */ + unmask = 0; + for (i = 12; i < len2 - 5; i += 4) + { + int id; + id = Inlines.MAX32(0, Inlines.MIN32(127, Inlines.MULT16_32_Q15((tmp[i] + CeltConstants.EPSILON), norm))); /* Do not round to nearest */ + unmask += inv_table[id]; + } + /*printf("%d\n", unmask);*/ + /* Normalize, compensate for the 1/4th of the sample and the factor of 6 in the inverse table */ + unmask = 64 * unmask * 4 / (6 * (len2 - 17)); + if (unmask > mask_metric) + { + tf_chan = c; + mask_metric = unmask; + } + } + is_transient = mask_metric > 200 ? 1 : 0; + + /* Arbitrary metric for VBR boost */ + tf_max = Inlines.MAX16(0, (Inlines.celt_sqrt(27 * mask_metric) - 42)); + /* *tf_estimate = 1 + Inlines.MIN16(1, sqrt(Inlines.MAX16(0, tf_max-30))/20); */ + tf_estimate = (Inlines.celt_sqrt(Inlines.MAX32(0, Inlines.SHL32(Inlines.MULT16_16(((short)(0.5 + (0.0069f) * (((int)1) << (14))))/*Inlines.QCONST16(0.0069f, 14)*/, Inlines.MIN16(163, tf_max)), 14) - ((int)(0.5 + (0.139f) * (((int)1) << (28))))/*Inlines.QCONST32(0.139f, 28)*/))); + /*printf("%d %f\n", tf_max, mask_metric);*/ + +#if FUZZING + is_transient = new Random().Next() & 0x1; +#endif + /*printf("%d %f %d\n", is_transient, (float)*tf_estimate, tf_max);*/ + return is_transient; + } + + /* Looks for sudden increases of energy to decide whether we need to patch + the transient decision */ + internal static int patch_transient_decision(int[][] newE, int[][] oldE, int nbEBands, + int start, int end, int C) + { + int i, c; + int mean_diff = 0; + int[] spread_old = new int[26]; + /* Apply an aggressive (-6 dB/Bark) spreading function to the old frame to + avoid false detection caused by irrelevant bands */ + if (C == 1) + { + spread_old[start] = oldE[0][start]; + for (i = start + 1; i < end; i++) + spread_old[i] = Inlines.MAX16((spread_old[i - 1] - ((short)(0.5 + (1.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.0f, CeltConstants.DB_SHIFT)*/), oldE[0][i]); + } + else { + spread_old[start] = Inlines.MAX16(oldE[0][start], oldE[1][start]); + for (i = start + 1; i < end; i++) + spread_old[i] = Inlines.MAX16((spread_old[i - 1] - ((short)(0.5 + (1.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.0f, CeltConstants.DB_SHIFT)*/), + Inlines.MAX16(oldE[0][i], oldE[1][i])); + } + for (i = end - 2; i >= start; i--) + spread_old[i] = Inlines.MAX16(spread_old[i], (spread_old[i + 1] - ((short)(0.5 + (1.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.0f, CeltConstants.DB_SHIFT)*/)); + /* Compute mean increase */ + c = 0; do + { + for (i = Inlines.IMAX(2, start); i < end - 1; i++) + { + int x1, x2; + x1 = Inlines.MAX16(0, newE[c][i]); + x2 = Inlines.MAX16(0, spread_old[i]); + mean_diff = Inlines.ADD32(mean_diff, (Inlines.MAX16(0, Inlines.SUB16(x1, x2)))); + } + } while (++c < C); + mean_diff = Inlines.DIV32(mean_diff, C * (end - 1 - Inlines.IMAX(2, start))); + /*printf("%f %f %d\n", mean_diff, max_diff, count);*/ + return (mean_diff > ((short)(0.5 + (1.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.0f, CeltConstants.DB_SHIFT)*/) ? 1 : 0; + } + + /** Apply window and compute the MDCT for all sub-frames and + all channels in a frame */ + internal static void compute_mdcts(CeltMode mode, int shortBlocks, int[][] input, + int[][] output, int C, int CC, int LM, int upsample) + { + int overlap = mode.overlap; + int N; + int B; + int shift; + int i, b, c; + if (shortBlocks != 0) + { + B = shortBlocks; + N = mode.shortMdctSize; + shift = mode.maxLM; + } + else { + B = 1; + N = mode.shortMdctSize << LM; + shift = mode.maxLM - LM; + } + c = 0; + do + { + for (b = 0; b < B; b++) + { + /* Interleaving the sub-frames while doing the MDCTs */ + MDCT.clt_mdct_forward( + mode.mdct, + input[c], + b * N, + output[c], + b, + mode.window, + overlap, + shift, + B); + } + } while (++c < CC); + + if (CC == 2 && C == 1) + { + for (i = 0; i < B * N; i++) + { + output[0][i] = Inlines.ADD32(Inlines.HALF32(output[0][i]), Inlines.HALF32(output[1][i])); + } + } + if (upsample != 1) + { + c = 0; + do + { + int bound = B * N / upsample; + for (i = 0; i < bound; i++) + output[c][i] *= upsample; + Arrays.MemSetWithOffset(output[c], 0, bound, B * N - bound); + } while (++c < C); + } + } + + internal static void celt_preemphasis(short[] pcmp, int pcmp_ptr, int[] inp, int inp_ptr, + int N, int CC, int upsample, int[] coef, ref int mem, int clip) + { + int i; + int coef0; + int m; + int Nu; + + coef0 = coef[0]; + m = mem; + + /* Fast path for the normal 48kHz case and no clipping */ + if (coef[1] == 0 && upsample == 1 && clip == 0) + { + for (i = 0; i < N; i++) + { + int x = pcmp[pcmp_ptr + (CC * i)]; + /* Apply pre-emphasis */ + inp[inp_ptr + i] = Inlines.SHL32(x, CeltConstants.SIG_SHIFT) - m; + m = Inlines.SHR32(Inlines.MULT16_16(coef0, x), 15 - CeltConstants.SIG_SHIFT); + } + mem = m; + return; + } + + Nu = N / upsample; + if (upsample != 1) + { + Arrays.MemSetWithOffset(inp, 0, inp_ptr, N); + } + for (i = 0; i < Nu; i++) + inp[inp_ptr + (i * upsample)] = pcmp[pcmp_ptr + (CC * i)]; + + + for (i = 0; i < N; i++) + { + int x; + x = (inp[inp_ptr + i]); + /* Apply pre-emphasis */ + inp[inp_ptr + i] = Inlines.SHL32(x, CeltConstants.SIG_SHIFT) - m; + m = Inlines.SHR32(Inlines.MULT16_16(coef0, x), 15 - CeltConstants.SIG_SHIFT); + } + + mem = m; + } + + internal static void celt_preemphasis(short[] pcmp, int[] inp, int inp_ptr, + int N, int CC, int upsample, int[] coef, BoxedValueInt mem, int clip) + { + int i; + int coef0; + int m; + int Nu; + + coef0 = coef[0]; + m = mem.Val; + + /* Fast path for the normal 48kHz case and no clipping */ + if (coef[1] == 0 && upsample == 1 && clip == 0) + { + for (i = 0; i < N; i++) + { + int x; + x = pcmp[CC * i]; + /* Apply pre-emphasis */ + inp[inp_ptr + i] = Inlines.SHL32(x, CeltConstants.SIG_SHIFT) - m; + m = Inlines.SHR32(Inlines.MULT16_16(coef0, x), 15 - CeltConstants.SIG_SHIFT); + } + mem.Val = m; + return; + } + + Nu = N / upsample; + if (upsample != 1) + { + Arrays.MemSetWithOffset(inp, 0, inp_ptr, N); + } + for (i = 0; i < Nu; i++) + inp[inp_ptr + (i * upsample)] = pcmp[CC * i]; + + + for (i = 0; i < N; i++) + { + int x; + x = (inp[inp_ptr + i]); + /* Apply pre-emphasis */ + inp[inp_ptr + i] = Inlines.SHL32(x, CeltConstants.SIG_SHIFT) - m; + m = Inlines.SHR32(Inlines.MULT16_16(coef0, x), 15 - CeltConstants.SIG_SHIFT); + } + + mem.Val = m; + } + + internal static int l1_metric(int[] tmp, int N, int LM, int bias) + { + int i; + int L1; + L1 = 0; + for (i = 0; i < N; i++) + { + L1 += Inlines.EXTEND32(Inlines.ABS32(tmp[i])); + } + + /* When in doubt, prefer good freq resolution */ + L1 = Inlines.MAC16_32_Q15(L1, (LM * bias), (L1)); + return L1; + + } + + internal static int tf_analysis(CeltMode m, int len, int isTransient, + int[] tf_res, int lambda, int[][] X, int N0, int LM, + out int tf_sum, int tf_estimate, int tf_chan) + { + int i; + int[] metric; + int cost0; + int cost1; + int[] path0; + int[] path1; + int[] tmp; + int[] tmp_1; + int sel; + int[] selcost = new int[2]; + int tf_select = 0; + int bias; + + + bias = Inlines.MULT16_16_Q14(((short)(0.5 + (.04f) * (((int)1) << (15))))/*Inlines.QCONST16(.04f, 15)*/, Inlines.MAX16((short)(0 - ((short)(0.5 + (.25f) * (((int)1) << (14))))/*Inlines.QCONST16(.25f, 14)*/), (((short)(0.5 + (.5f) * (((int)1) << (14))))/*Inlines.QCONST16(.5f, 14)*/ - tf_estimate))); + /*printf("%f ", bias);*/ + + metric = new int[len]; + tmp = new int[(m.eBands[len] - m.eBands[len - 1]) << LM]; + tmp_1 = new int[(m.eBands[len] - m.eBands[len - 1]) << LM]; + path0 = new int[len]; + path1 = new int[len]; + + tf_sum = 0; + for (i = 0; i < len; i++) + { + int k, N; + int narrow; + int L1, best_L1; + int best_level = 0; + N = (m.eBands[i + 1] - m.eBands[i]) << LM; + /* band is too narrow to be split down to LM=-1 */ + narrow = ((m.eBands[i + 1] - m.eBands[i]) == 1) ? 1 : 0; + Array.Copy(X[tf_chan], (m.eBands[i] << LM), tmp, 0, N); + /* Just add the right channel if we're in stereo */ + /*if (C==2) + for (j=0;j> LM, 1 << LM); + L1 = l1_metric(tmp_1, N, LM + 1, bias); + if (L1 < best_L1) + { + best_L1 = L1; + best_level = -1; + } + } + /*printf ("%f ", L1);*/ + for (k = 0; k < LM + (!(isTransient != 0 || narrow != 0) ? 1 : 0); k++) + { + int B; + + if (isTransient != 0) + B = (LM - k - 1); + else + B = k + 1; + + Bands.haar1ZeroOffset(tmp, N >> k, 1 << k); + + L1 = l1_metric(tmp, N, B, bias); + + if (L1 < best_L1) + { + best_L1 = L1; + best_level = k + 1; + } + } + /*printf ("%d ", isTransient ? LM-best_level : best_level);*/ + /* metric is in Q1 to be able to select the mid-point (-0.5) for narrower bands */ + if (isTransient != 0) + metric[i] = 2 * best_level; + else + metric[i] = -2 * best_level; + tf_sum += (isTransient != 0 ? LM : 0) - metric[i] / 2; + /* For bands that can't be split to -1, set the metric to the half-way point to avoid + biasing the decision */ + if (narrow != 0 && (metric[i] == 0 || metric[i] == -2 * LM)) + metric[i] -= 1; + /*printf("%d ", metric[i]);*/ + } + /*printf("\n");*/ + /* Search for the optimal tf resolution, including tf_select */ + tf_select = 0; + for (sel = 0; sel < 2; sel++) + { + cost0 = 0; + cost1 = isTransient != 0 ? 0 : lambda; + for (i = 1; i < len; i++) + { + int curr0, curr1; + curr0 = Inlines.IMIN(cost0, cost1 + lambda); + curr1 = Inlines.IMIN(cost0 + lambda, cost1); + cost0 = curr0 + Inlines.abs(metric[i] - 2 * Tables.tf_select_table[LM][4 * isTransient + 2 * sel + 0]); + cost1 = curr1 + Inlines.abs(metric[i] - 2 * Tables.tf_select_table[LM][4 * isTransient + 2 * sel + 1]); + } + cost0 = Inlines.IMIN(cost0, cost1); + selcost[sel] = cost0; + } + /* For now, we're conservative and only allow tf_select=1 for transients. + * If tests confirm it's useful for non-transients, we could allow it. */ + if (selcost[1] < selcost[0] && isTransient != 0) + tf_select = 1; + cost0 = 0; + cost1 = isTransient != 0 ? 0 : lambda; + /* Viterbi forward pass */ + for (i = 1; i < len; i++) + { + int curr0, curr1; + int from0, from1; + + from0 = cost0; + from1 = cost1 + lambda; + if (from0 < from1) + { + curr0 = from0; + path0[i] = 0; + } + else { + curr0 = from1; + path0[i] = 1; + } + + from0 = cost0 + lambda; + from1 = cost1; + if (from0 < from1) + { + curr1 = from0; + path1[i] = 0; + } + else { + curr1 = from1; + path1[i] = 1; + } + cost0 = curr0 + Inlines.abs(metric[i] - 2 * Tables.tf_select_table[LM][4 * isTransient + 2 * tf_select + 0]); + cost1 = curr1 + Inlines.abs(metric[i] - 2 * Tables.tf_select_table[LM][4 * isTransient + 2 * tf_select + 1]); + } + tf_res[len - 1] = cost0 < cost1 ? 0 : 1; + /* Viterbi backward pass to check the decisions */ + for (i = len - 2; i >= 0; i--) + { + if (tf_res[i + 1] == 1) + tf_res[i] = path1[i + 1]; + else + tf_res[i] = path0[i + 1]; + } + /*printf("%d %f\n", *tf_sum, tf_estimate);*/ + +#if FUZZING + Random rand = new Random(); + tf_select = rand.Next() & 0x1; + tf_res[0] = rand.Next() & 0x1; + for (i = 1; i < len; i++) + { + tf_res[i] = tf_res[i - 1] ^ ((rand.Next() & 0xF) == 0 ? 1 : 0); + } +#endif + return tf_select; + } + + internal static void tf_encode(int start, int end, int isTransient, int[] tf_res, int LM, int tf_select, EntropyCoder enc) + { + int curr, i; + int tf_select_rsv; + int tf_changed; + int logp; + uint budget; + uint tell; + budget = enc.storage * 8; + tell = (uint)enc.tell(); + logp = isTransient != 0 ? 2 : 4; + /* Reserve space to code the tf_select decision. */ + tf_select_rsv = (LM > 0 && tell + logp + 1 <= budget) ? 1 : 0; + budget -= (uint)tf_select_rsv; + curr = tf_changed = 0; + for (i = start; i < end; i++) + { + if (tell + logp <= budget) + { + enc.enc_bit_logp(tf_res[i] ^ curr, (uint)logp); + tell = (uint)enc.tell(); + curr = tf_res[i]; + tf_changed |= curr; + } + else + tf_res[i] = curr; + logp = isTransient != 0 ? 4 : 5; + } + /* Only code tf_select if it would actually make a difference. */ + if (tf_select_rsv != 0 && + Tables.tf_select_table[LM][4 * isTransient + 0 + tf_changed] != + Tables.tf_select_table[LM][4 * isTransient + 2 + tf_changed]) + enc.enc_bit_logp(tf_select, 1); + else + tf_select = 0; + for (i = start; i < end; i++) + tf_res[i] = Tables.tf_select_table[LM][4 * isTransient + 2 * tf_select + tf_res[i]]; + /*for(i=0;i Inlines.MULT16_32_Q15((m.eBands[13] << (LM + 1)), sumLR)) ? 1 : 0; + } + + internal static int median_of_5(int[] x, int x_ptr) + { + int t0, t1, t2, t3, t4; + t2 = x[x_ptr + 2]; + if (x[x_ptr] > x[x_ptr + 1]) + { + t0 = x[x_ptr + 1]; + t1 = x[x_ptr]; + } + else { + t0 = x[x_ptr]; + t1 = x[x_ptr + 1]; + } + if (x[x_ptr + 3] > x[x_ptr + 4]) + { + t3 = x[x_ptr + 4]; + t4 = x[x_ptr + 3]; + } + else { + t3 = x[x_ptr + 3]; + t4 = x[x_ptr + 4]; + } + if (t0 > t3) + { + // swap the pairs + int tmp = t3; + t3 = t0; + t0 = tmp; + tmp = t4; + t4 = t1; + t1 = tmp; + } + if (t2 > t1) + { + if (t1 < t3) + return Inlines.MIN16(t2, t3); + else + return Inlines.MIN16(t4, t1); + } + else { + if (t2 < t3) + return Inlines.MIN16(t1, t3); + else + return Inlines.MIN16(t2, t4); + } + } + + internal static int median_of_3(int[] x, int x_ptr) + { + int t0, t1, t2; + if (x[x_ptr] > x[x_ptr + 1]) + { + t0 = x[x_ptr + 1]; + t1 = x[x_ptr]; + } + else { + t0 = x[x_ptr]; + t1 = x[x_ptr + 1]; + } + t2 = x[x_ptr + 2]; + if (t1 < t2) + return t1; + else if (t0 < t2) + return t2; + else + return t0; + } + + internal static int dynalloc_analysis(int[][] bandLogE, int[][] bandLogE2, + int nbEBands, int start, int end, int C, int[] offsets, int lsb_depth, short[] logN, + int isTransient, int vbr, int constrained_vbr, short[] eBands, int LM, + int effectiveBytes, out int tot_boost_, int lfe, int[] surround_dynalloc) + { + int i, c; + int tot_boost = 0; + int maxDepth; + int[][] follower = Arrays.InitTwoDimensionalArray(2, nbEBands); + int[] noise_floor = new int[C * nbEBands]; // opt: partitioned array + + Arrays.MemSetInt(offsets, 0, nbEBands); + /* Dynamic allocation code */ + maxDepth = (0 - ((short)(0.5 + (31.9f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(31.9f, CeltConstants.DB_SHIFT)*/); + for (i = 0; i < end; i++) + { + /* Noise floor must take into account eMeans, the depth, the width of the bands + and the preemphasis filter (approx. square of bark band ID) */ + noise_floor[i] = (Inlines.MULT16_16(((short)(0.5 + (0.0625f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.0625f, CeltConstants.DB_SHIFT)*/, logN[i]) + + ((short)(0.5 + (.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/ + Inlines.SHL16((9 - lsb_depth), CeltConstants.DB_SHIFT) - Inlines.SHL16(Tables.eMeans[i], 6) + + Inlines.MULT16_16(((short)(0.5 + (0.0062f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.0062f, CeltConstants.DB_SHIFT)*/, (i + 5) * (i + 5))); + } + c = 0; do + { + for (i = 0; i < end; i++) + maxDepth = Inlines.MAX16(maxDepth, (bandLogE[c][i] - noise_floor[i])); + } while (++c < C); + /* Make sure that dynamic allocation can't make us bust the budget */ + if (effectiveBytes > 50 && LM >= 1 && lfe == 0) + { + int last = 0; + c = 0; do + { + int offset; + int tmp; + int[] f = follower[c]; + f[0] = bandLogE2[c][0]; + for (i = 1; i < end; i++) + { + /* The last band to be at least 3 dB higher than the previous one + is the last we'll consider. Otherwise, we run into problems on + bandlimited signals. */ + if (bandLogE2[c][i] > bandLogE2[c][i - 1] + ((short)(0.5 + (0.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.5f, CeltConstants.DB_SHIFT)*/) + last = i; + f[i] = Inlines.MIN16((f[i - 1] + ((short)(0.5 + (1.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.5f, CeltConstants.DB_SHIFT)*/), bandLogE2[c][i]); + } + for (i = last - 1; i >= 0; i--) + f[i] = Inlines.MIN16(f[i], Inlines.MIN16((f[i + 1] + ((short)(0.5 + (2.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(2.0f, CeltConstants.DB_SHIFT)*/), bandLogE2[c][i])); + + /* Combine with a median filter to avoid dynalloc triggering unnecessarily. + The "offset" value controls how conservative we are -- a higher offset + reduces the impact of the median filter and makes dynalloc use more bits. */ + offset = ((short)(0.5 + (1.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.0f, CeltConstants.DB_SHIFT)*/; + for (i = 2; i < end - 2; i++) + f[i] = Inlines.MAX16(f[i], median_of_5(bandLogE2[c], i - 2) - offset); + tmp = median_of_3(bandLogE2[c], 0) - offset; + f[0] = Inlines.MAX16(f[0], tmp); + f[1] = Inlines.MAX16(f[1], tmp); + tmp = median_of_3(bandLogE2[c], end - 3) - offset; + f[end - 2] = Inlines.MAX16(f[end - 2], tmp); + f[end - 1] = Inlines.MAX16(f[end - 1], tmp); + + for (i = 0; i < end; i++) + f[i] = Inlines.MAX16(f[i], noise_floor[i]); + } while (++c < C); + if (C == 2) + { + for (i = start; i < end; i++) + { + /* Consider 24 dB "cross-talk" */ + follower[1][i] = Inlines.MAX16(follower[1][i], follower[0][i] - ((short)(0.5 + (4.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(4.0f, CeltConstants.DB_SHIFT)*/); + follower[0][i] = Inlines.MAX16(follower[0][i], follower[1][i] - ((short)(0.5 + (4.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(4.0f, CeltConstants.DB_SHIFT)*/); + follower[0][i] = Inlines.HALF16(Inlines.MAX16(0, bandLogE[0][i] - follower[0][i]) + Inlines.MAX16(0, bandLogE[1][i] - follower[1][i])); + } + } + else { + for (i = start; i < end; i++) + { + follower[0][i] = Inlines.MAX16(0, bandLogE[0][i] - follower[0][i]); + } + } + for (i = start; i < end; i++) + follower[0][i] = Inlines.MAX16(follower[0][i], surround_dynalloc[i]); + /* For non-transient CBR/CVBR frames, halve the dynalloc contribution */ + if ((vbr == 0 || constrained_vbr != 0) && isTransient == 0) + { + for (i = start; i < end; i++) + follower[0][i] = Inlines.HALF16(follower[0][i]); + } + for (i = start; i < end; i++) + { + int width; + int boost; + int boost_bits; + + if (i < 8) + follower[0][i] *= 2; + if (i >= 12) + follower[0][i] = Inlines.HALF16(follower[0][i]); + follower[0][i] = Inlines.MIN16(follower[0][i], ((short)(0.5 + (4) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(4, CeltConstants.DB_SHIFT)*/); + + width = C * (eBands[i + 1] - eBands[i]) << LM; + if (width < 6) + { + boost = (int)Inlines.SHR32((follower[0][i]), CeltConstants.DB_SHIFT); + boost_bits = boost * width << EntropyCoder.BITRES; + } + else if (width > 48) + { + boost = (int)Inlines.SHR32((follower[0][i]) * 8, CeltConstants.DB_SHIFT); + boost_bits = (boost * width << EntropyCoder.BITRES) / 8; + } + else { + boost = (int)Inlines.SHR32((follower[0][i]) * width / 6, CeltConstants.DB_SHIFT); + boost_bits = boost * 6 << EntropyCoder.BITRES; + } + /* For CBR and non-transient CVBR frames, limit dynalloc to 1/4 of the bits */ + if ((vbr == 0 || (constrained_vbr != 0 && isTransient == 0)) + && (tot_boost + boost_bits) >> EntropyCoder.BITRES >> 3 > effectiveBytes / 4) + { + int cap = ((effectiveBytes / 4) << EntropyCoder.BITRES << 3); + offsets[i] = cap - tot_boost; + tot_boost = cap; + break; + } + else { + offsets[i] = boost; + tot_boost += boost_bits; + } + } + } + + tot_boost_ = tot_boost; + + return maxDepth; + } + + internal static void deemphasis(int[][] input, int[] input_ptrs, short[] pcm, int pcm_ptr, int N, int C, int downsample, int[] coef, + int[] mem, int accum) + { + int c; + int Nd; + int apply_downsampling = 0; + int coef0; + + /* shortcut version for common case */ + if (downsample == 1 && C == 2 && accum == 0) + { + deemphasis_stereo_simple(input, input_ptrs, pcm, pcm_ptr, N, coef[0], mem); + return; + } + + int[] scratch = new int[N]; + coef0 = coef[0]; + Nd = N / downsample; + c = 0; do + { + int j; + int x_ptr; + int y; + int m = mem[c]; + int[] x = input[c]; + x_ptr = input_ptrs[c]; + y = pcm_ptr + c; + if (downsample > 1) + { + /* Shortcut for the standard (non-custom modes) case */ + for (j = 0; j < N; j++) + { + int tmp = x[x_ptr + j] + m + CeltConstants.VERY_SMALL; + m = Inlines.MULT16_32_Q15(coef0, tmp); + scratch[j] = tmp; + } + apply_downsampling = 1; + } + else { + /* Shortcut for the standard (non-custom modes) case */ + if (accum != 0) // should never hit this branch? + { + for (j = 0; j < N; j++) + { + int tmp = x[x_ptr + j] + m + CeltConstants.VERY_SMALL; + m = Inlines.MULT16_32_Q15(coef0, tmp); + pcm[y + (j * C)] = Inlines.SAT16(Inlines.ADD32(pcm[y + (j * C)], Inlines.SIG2WORD16(tmp))); + } + } + else + { + for (j = 0; j < N; j++) + { + int tmp = unchecked(x[x_ptr + j] + m + CeltConstants.VERY_SMALL); // Opus bug: This can overflow. + if (x[x_ptr + j] > 0 && m > 0 && tmp < 0) // This is a hack to saturate to INT_MAXVALUE + { + tmp = int.MaxValue; + m = int.MaxValue; + } + else + { + m = Inlines.MULT16_32_Q15(coef0, tmp); + } + pcm[y + (j * C)] = Inlines.SIG2WORD16(tmp); + } + } + } + mem[c] = m; + + if (apply_downsampling != 0) + { + /* Perform down-sampling */ + { + for (j = 0; j < Nd; j++) + pcm[y + (j * C)] = Inlines.SIG2WORD16(scratch[j * downsample]); + } + } + } while (++c < C); + } + + /* Special case for stereo with no downsampling and no accumulation. This is + quite common and we can make it faster by processing both channels in the + same loop, reducing overhead due to the dependency loop in the IIR filter */ + internal static void deemphasis_stereo_simple(int[][] input, int[] input_ptrs, short[] pcm, int pcm_ptr, int N, int coef0, int[] mem) + { + int[] x0 = input[0]; + int[] x1 = input[1]; + int ip0 = input_ptrs[0]; + int ip1 = input_ptrs[1]; + int m0 = mem[0]; + int m1 = mem[1]; + for (int j = 0; j < N; j++) + { + int tmp0 = x0[ip0 + j] + m0; + int tmp1 = x1[ip1 + j] + m1; + m0 = Inlines.MULT16_32_Q15(coef0, tmp0); + m1 = Inlines.MULT16_32_Q15(coef0, tmp1); + pcm[pcm_ptr + (2 * j)] = Inlines.SIG2WORD16(tmp0); + pcm[pcm_ptr + (2 * j) + 1] = Inlines.SIG2WORD16(tmp1); + } + mem[0] = m0; + mem[1] = m1; + } + + internal static void celt_synthesis(CeltMode mode, int[][] X, int[][] out_syn, int[] out_syn_ptrs, + int[] oldBandE, int start, int effEnd, int C, int CC, + int isTransient, int LM, int downsample, + int silence) + { + int c, i; + int M; + int b; + int B; + int N, NB; + int shift; + int nbEBands; + int overlap; + int[] freq; + + overlap = mode.overlap; + nbEBands = mode.nbEBands; + N = mode.shortMdctSize << LM; + freq = new int[N]; /*< Interleaved signal MDCTs */ + M = 1 << LM; + + if (isTransient != 0) + { + B = M; + NB = mode.shortMdctSize; + shift = mode.maxLM; + } + else { + B = 1; + NB = mode.shortMdctSize << LM; + shift = mode.maxLM - LM; + } + + if (CC == 2 && C == 1) + { + /* Copying a mono streams to two channels */ + int freq2; + Bands.denormalise_bands(mode, X[0], freq, 0, oldBandE, 0, start, effEnd, M, + downsample, silence); + /* Store a temporary copy in the output buffer because the IMDCT destroys its input. */ + freq2 = out_syn_ptrs[1] + (overlap / 2); + Array.Copy(freq, 0, out_syn[1], freq2, N); + for (b = 0; b < B; b++) + MDCT.clt_mdct_backward(mode.mdct, out_syn[1], freq2 + b, out_syn[0], out_syn_ptrs[0] + (NB * b), mode.window, overlap, shift, B); + for (b = 0; b < B; b++) + MDCT.clt_mdct_backward(mode.mdct, freq, b, out_syn[1], out_syn_ptrs[1] + (NB * b), mode.window, overlap, shift, B); + } + else if (CC == 1 && C == 2) + { + /* Downmixing a stereo stream to mono */ + int freq2 = out_syn_ptrs[0] + (overlap / 2); + Bands.denormalise_bands(mode, X[0], freq, 0, oldBandE, 0, start, effEnd, M, + downsample, silence); + /* Use the output buffer as temp array before downmixing. */ + Bands.denormalise_bands(mode, X[1], out_syn[0], freq2, oldBandE, nbEBands, start, effEnd, M, + downsample, silence); + for (i = 0; i < N; i++) + freq[i] = Inlines.HALF32(Inlines.ADD32(freq[i], out_syn[0][freq2 + i])); + for (b = 0; b < B; b++) + MDCT.clt_mdct_backward(mode.mdct, freq, b, out_syn[0], out_syn_ptrs[0] + (NB * b), mode.window, overlap, shift, B); + } + else { + /* Normal case (mono or stereo) */ + c = 0; do + { + Bands.denormalise_bands(mode, X[c], freq, 0, oldBandE, c * nbEBands, start, effEnd, M, + downsample, silence); + for (b = 0; b < B; b++) + MDCT.clt_mdct_backward(mode.mdct, freq, b, out_syn[c], out_syn_ptrs[c] + (NB * b), mode.window, overlap, shift, B); + } while (++c < CC); + } + + } + + internal static void tf_decode(int start, int end, int isTransient, int[] tf_res, int LM, EntropyCoder dec) + { + int i, curr, tf_select; + int tf_select_rsv; + int tf_changed; + int logp; + uint budget; + uint tell; + + budget = dec.storage * 8; + tell = (uint)dec.tell(); + logp = isTransient != 0 ? 2 : 4; + tf_select_rsv = (LM > 0 && tell + logp + 1 <= budget) ? 1 : 0; + budget -= (uint)tf_select_rsv; + tf_changed = curr = 0; + for (i = start; i < end; i++) + { + if (tell + logp <= budget) + { + curr ^= dec.dec_bit_logp((uint)logp); + tell = (uint)dec.tell(); + tf_changed |= curr; + } + tf_res[i] = curr; + logp = isTransient != 0 ? 4 : 5; + } + tf_select = 0; + if (tf_select_rsv != 0 && + Tables.tf_select_table[LM][4 * isTransient + 0 + tf_changed] != + Tables.tf_select_table[LM][4 * isTransient + 2 + tf_changed]) + { + tf_select = dec.dec_bit_logp(1); + } + for (i = start; i < end; i++) + { + tf_res[i] = Tables.tf_select_table[LM][4 * isTransient + 2 * tf_select + tf_res[i]]; + } + } + + internal static int celt_plc_pitch_search(int[][] decode_mem, int C) + { + int pitch_index; + int[] lp_pitch_buf = new int[CeltConstants.DECODE_BUFFER_SIZE >> 1]; + Pitch.pitch_downsample(decode_mem, lp_pitch_buf, + CeltConstants.DECODE_BUFFER_SIZE, C); + Pitch.pitch_search(lp_pitch_buf, CeltConstants.PLC_PITCH_LAG_MAX >> 1, lp_pitch_buf, + CeltConstants.DECODE_BUFFER_SIZE - CeltConstants.PLC_PITCH_LAG_MAX, + CeltConstants.PLC_PITCH_LAG_MAX - CeltConstants.PLC_PITCH_LAG_MIN, out pitch_index); + pitch_index = CeltConstants.PLC_PITCH_LAG_MAX - pitch_index; + + return pitch_index; + } + + internal static int resampling_factor(int rate) + { + int ret; + switch (rate) + { + case 48000: + ret = 1; + break; + case 24000: + ret = 2; + break; + case 16000: + ret = 3; + break; + case 12000: + ret = 4; + break; + case 8000: + ret = 6; + break; + default: + Inlines.OpusAssert(false); + ret = 0; + break; + } + return ret; + } + + internal static void comb_filter_const(int[] y, int y_ptr, int[] x, int x_ptr, int T, int N, + int g10, int g11, int g12) + { + int x0, x1, x2, x3, x4; + int i; + int xpt = x_ptr - T; + x4 = x[xpt - 2]; + x3 = x[xpt - 1]; + x2 = x[xpt]; + x1 = x[xpt + 1]; + for (i = 0; i < N; i++) + { + x0 = x[xpt + i + 2]; + y[y_ptr + i] = x[x_ptr + i] + + Inlines.MULT16_32_Q15(g10, x2) + + Inlines.MULT16_32_Q15(g11, Inlines.ADD32(x1, x3)) + + Inlines.MULT16_32_Q15(g12, Inlines.ADD32(x0, x4)); + x4 = x3; + x3 = x2; + x2 = x1; + x1 = x0; + } + } + + private static readonly short[][] gains = { + new short[]{ ((short)(0.5 + (0.3066406250f) * (((int)1) << (15))))/*Inlines.QCONST16(0.3066406250f, 15)*/, ((short)(0.5 + (0.2170410156f) * (((int)1) << (15))))/*Inlines.QCONST16(0.2170410156f, 15)*/, ((short)(0.5 + (0.1296386719f) * (((int)1) << (15))))/*Inlines.QCONST16(0.1296386719f, 15)*/}, + new short[]{ ((short)(0.5 + (0.4638671875f) * (((int)1) << (15))))/*Inlines.QCONST16(0.4638671875f, 15)*/, ((short)(0.5 + (0.2680664062f) * (((int)1) << (15))))/*Inlines.QCONST16(0.2680664062f, 15)*/, ((short)(0.5 + (0.0f) * (((int)1) << (15))))/*Inlines.QCONST16(0.0f, 15)*/}, + new short[]{ ((short)(0.5 + (0.7998046875f) * (((int)1) << (15))))/*Inlines.QCONST16(0.7998046875f, 15)*/, ((short)(0.5 + (0.1000976562f) * (((int)1) << (15))))/*Inlines.QCONST16(0.1000976562f, 15)*/, ((short)(0.5 + (0.0f) * (((int)1) << (15))))/*Inlines.QCONST16(0.0f, 15)*/} + }; + + internal static void comb_filter(int[] y, int y_ptr, int[] x, int x_ptr, int T0, int T1, int N, + int g0, int g1, int tapset0, int tapset1, + int[] window, int overlap) + { + int i; + /* printf ("%d %d %f %f\n", T0, T1, g0, g1); */ + int g00, g01, g02, g10, g11, g12; + int x0, x1, x2, x3, x4; + + if (g0 == 0 && g1 == 0) + { + /* OPT: Happens to work without the OPUS_MOVE(), but only because the current encoder already copies x to y */ + if (x_ptr != y_ptr) + { + //x.MemMoveTo(y, N); + } + + return; + } + g00 = Inlines.MULT16_16_P15(g0, gains[tapset0][0]); + g01 = Inlines.MULT16_16_P15(g0, gains[tapset0][1]); + g02 = Inlines.MULT16_16_P15(g0, gains[tapset0][2]); + g10 = Inlines.MULT16_16_P15(g1, gains[tapset1][0]); + g11 = Inlines.MULT16_16_P15(g1, gains[tapset1][1]); + g12 = Inlines.MULT16_16_P15(g1, gains[tapset1][2]); + x1 = x[x_ptr - T1 + 1]; + x2 = x[x_ptr - T1]; + x3 = x[x_ptr - T1 - 1]; + x4 = x[x_ptr - T1 - 2]; + /* If the filter didn't change, we don't need the overlap */ + if (g0 == g1 && T0 == T1 && tapset0 == tapset1) + overlap = 0; + for (i = 0; i < overlap; i++) + { + int f; + x0 = x[x_ptr + i - T1 + 2]; + f = Inlines.MULT16_16_Q15(window[i], window[i]); + y[y_ptr + i] = x[x_ptr + i] + + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15((short)(CeltConstants.Q15ONE - f), g00), x[x_ptr + i - T0]) + + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15((short)(CeltConstants.Q15ONE - f), g01), Inlines.ADD32(x[x_ptr + i - T0 + 1], x[x_ptr + i - T0 - 1])) + + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15((short)(CeltConstants.Q15ONE - f), g02), Inlines.ADD32(x[x_ptr + i - T0 + 2], x[x_ptr + i - T0 - 2])) + + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15(f, g10), x2) + + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15(f, g11), Inlines.ADD32(x1, x3)) + + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15(f, g12), Inlines.ADD32(x0, x4)); + x4 = x3; + x3 = x2; + x2 = x1; + x1 = x0; + + } + if (g1 == 0) + { + /* OPT: Happens to work without the OPUS_MOVE(), but only because the current encoder already copies x to y */ + if (x_ptr != y_ptr) + { + //x.Point(overlap).MemMoveTo(y.Point(overlap), N - overlap); + } + return; + } + + /* Compute the part with the constant filter. */ + comb_filter_const(y, y_ptr + i, x, x_ptr + i, T1, N - i, g10, g11, g12); + } + + private static readonly sbyte[][] tf_select_table = { + new sbyte[]{0, -1, 0, -1, 0,-1, 0,-1}, + new sbyte[]{0, -1, 0, -2, 1, 0, 1,-1}, + new sbyte[]{0, -2, 0, -3, 2, 0, 1,-1}, + new sbyte[]{0, -2, 0, -3, 3, 0, 1,-1}, + }; + + internal static void init_caps(CeltMode m, int[] cap, int LM, int C) + { + int i; + for (i = 0; i < m.nbEBands; i++) + { + int N; + N = (m.eBands[i + 1] - m.eBands[i]) << LM; + cap[i] = (m.cache.caps[m.nbEBands * (2 * LM + C - 1) + i] + 64) * C * N >> 2; + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/CeltConstants.cs b/Libraries/Concentus/CSharp/Concentus/Celt/CeltConstants.cs new file mode 100644 index 000000000..b10fece24 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/CeltConstants.cs @@ -0,0 +1,93 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Celt +{ + internal static class CeltConstants + { + public const int Q15ONE = 32767; + + public const float CELT_SIG_SCALE = 32768.0f; + + public const int SIG_SHIFT = 12; + + public const int NORM_SCALING = 16384; + + public const int DB_SHIFT = 10; + + public const int EPSILON = 1; + public const int VERY_SMALL = 0; + public const short VERY_LARGE16 = ((short)32767); + public const short Q15_ONE = ((short)32767); + + public const int COMBFILTER_MAXPERIOD = 1024; + public const int COMBFILTER_MINPERIOD = 15; + + // from opus_decode.c + public const int DECODE_BUFFER_SIZE = 2048; + + // from modes.c + /* Alternate tuning (partially derived from Vorbis) */ + public const int BITALLOC_SIZE = 11; + public const int MAX_PERIOD = 1024; + + // from static_modes_float.h + public const int TOTAL_MODES = 1; + + + // from rate.h + public const int MAX_PSEUDO = 40; + public const int LOG_MAX_PSEUDO = 6; + + public const int CELT_MAX_PULSES = 128; + + public const int MAX_FINE_BITS = 8; + + public const int FINE_OFFSET = 21; + public const int QTHETA_OFFSET = 4; + public const int QTHETA_OFFSET_TWOPHASE = 16; + + /* The maximum pitch lag to allow in the pitch-based PLC. It's possible to save + CPU time in the PLC pitch search by making this smaller than MAX_PERIOD. The + current value corresponds to a pitch of 66.67 Hz. */ + public const int PLC_PITCH_LAG_MAX = 720; + + /* The minimum pitch lag to allow in the pitch-based PLC. This corresponds to a + pitch of 480 Hz. */ + public const int PLC_PITCH_LAG_MIN = 100; + + public const int LPC_ORDER = 24; + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/CeltLPC.cs b/Libraries/Concentus/CSharp/Concentus/Celt/CeltLPC.cs new file mode 100644 index 000000000..a0bb4370b --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/CeltLPC.cs @@ -0,0 +1,159 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#if !UNSAFE + +namespace Concentus.Celt +{ + using Concentus.Celt.Enums; + using Concentus.Celt.Structs; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using System.Diagnostics; + + internal static class CeltLPC + { + internal static void celt_lpc( + int[] _lpc, /* out: [0...p-1] LPC coefficients */ + int[] ac, /* in: [0...p] autocorrelation values */ + int p) + { + int i, j; + int r; + int error = ac[0]; + int[] lpc = new int[p]; + + //Arrays.MemSetInt(lpc, 0, p); strictly, this is not necessary since the runtime zeroes memory for us + + if (ac[0] != 0) + { + for (i = 0; i < p; i++) + { + /* Sum up this iteration's reflection coefficient */ + int rr = 0; + for (j = 0; j < i; j++) + rr += Inlines.MULT32_32_Q31(lpc[j], ac[i - j]); + rr += Inlines.SHR32(ac[i + 1], 3); + r = 0 - Inlines.frac_div32(Inlines.SHL32(rr, 3), error); + /* Update LPC coefficients and total error */ + lpc[i] = Inlines.SHR32(r, 3); + + for (j = 0; j < (i + 1) >> 1; j++) + { + int tmp1, tmp2; + tmp1 = lpc[j]; + tmp2 = lpc[i - 1 - j]; + lpc[j] = tmp1 + Inlines.MULT32_32_Q31(r, tmp2); + lpc[i - 1 - j] = tmp2 + Inlines.MULT32_32_Q31(r, tmp1); + } + + error = error - Inlines.MULT32_32_Q31(Inlines.MULT32_32_Q31(r, r), error); + + /* Bail out once we get 30 dB gain */ + if (error < Inlines.SHR32(ac[0], 10)) + { + break; + } + } + } + + for (i = 0; i < p; i++) + { + _lpc[i] = Inlines.ROUND16((lpc[i]), 16); + } + } + + internal static void celt_iir( + int[] _x, + int _x_ptr, + int[] den, + int[] _y, + int _y_ptr, + int N, + int ord, + int[] mem) + { + int i, j; + int[] rden = new int[ord]; + int[] y = new int[N + ord]; + Inlines.OpusAssert((ord & 3) == 0); + + for (i = 0; i < ord; i++) + rden[i] = den[ord - i - 1]; + for (i = 0; i < ord; i++) + y[i] = (0 - mem[ord - i - 1]); + for (; i < N + ord; i++) + y[i] = 0; + for (i = 0; i < N - 3; i += 4) + { + /* Unroll by 4 as if it were an FIR filter */ + int sum0 = _x[_x_ptr + i]; + int sum1 = _x[_x_ptr + i + 1]; + int sum2 = _x[_x_ptr + i + 2]; + int sum3 = _x[_x_ptr + i + 3]; + Kernels.xcorr_kernel(rden, y, i, ref sum0, ref sum1, ref sum2, ref sum3, ord); + + /* Patch up the result to compensate for the fact that this is an IIR */ + y[i + ord] = (0 - Inlines.ROUND16((sum0), CeltConstants.SIG_SHIFT)); + _y[_y_ptr + i] = sum0; + sum1 = Inlines.MAC16_16(sum1, y[i + ord], den[0]); + y[i + ord + 1] = (0 - Inlines.ROUND16((sum1), CeltConstants.SIG_SHIFT)); + _y[_y_ptr + i + 1] = sum1; + sum2 = Inlines.MAC16_16(sum2, y[i + ord + 1], den[0]); + sum2 = Inlines.MAC16_16(sum2, y[i + ord], den[1]); + y[i + ord + 2] = (0 - Inlines.ROUND16((sum2), CeltConstants.SIG_SHIFT)); + _y[_y_ptr + i + 2] = sum2; + + sum3 = Inlines.MAC16_16(sum3, y[i + ord + 2], den[0]); + sum3 = Inlines.MAC16_16(sum3, y[i + ord + 1], den[1]); + sum3 = Inlines.MAC16_16(sum3, y[i + ord], den[2]); + y[i + ord + 3] = (0 - Inlines.ROUND16((sum3), CeltConstants.SIG_SHIFT)); + _y[_y_ptr + i + 3] = sum3; + } + for (; i < N; i++) + { + int sum = _x[_x_ptr + i]; + for (j = 0; j < ord; j++) + sum -= Inlines.MULT16_16(rden[j], y[i + j]); + y[i + ord] = Inlines.ROUND16((sum), CeltConstants.SIG_SHIFT); + _y[_y_ptr + i] = sum; + } + for (i = 0; i < ord; i++) + mem[i] = (_y[_y_ptr + N - i - 1]); + } + } +} + +#endif \ No newline at end of file diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/CeltLPCUnsafe.cs b/Libraries/Concentus/CSharp/Concentus/Celt/CeltLPCUnsafe.cs new file mode 100644 index 000000000..f908523e6 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/CeltLPCUnsafe.cs @@ -0,0 +1,163 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#if UNSAFE + +namespace Concentus.Celt +{ + using Concentus.Celt.Enums; + using Concentus.Celt.Structs; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using System.Diagnostics; + + internal static class CeltLPC + { + internal static void celt_lpc( + int[] _lpc, /* out: [0...p-1] LPC coefficients */ + int[] ac, /* in: [0...p] autocorrelation values */ + int p) + { + int i, j; + int r; + int error = ac[0]; + int[] lpc = new int[p]; + + //Arrays.MemSetInt(lpc, 0, p); strictly, this is not necessary since the runtime zeroes memory for us + + if (ac[0] != 0) + { + for (i = 0; i < p; i++) + { + /* Sum up this iteration's reflection coefficient */ + int rr = 0; + for (j = 0; j < i; j++) + rr += Inlines.MULT32_32_Q31(lpc[j], ac[i - j]); + rr += Inlines.SHR32(ac[i + 1], 3); + r = 0 - Inlines.frac_div32(Inlines.SHL32(rr, 3), error); + /* Update LPC coefficients and total error */ + lpc[i] = Inlines.SHR32(r, 3); + + for (j = 0; j < (i + 1) >> 1; j++) + { + int tmp1, tmp2; + tmp1 = lpc[j]; + tmp2 = lpc[i - 1 - j]; + lpc[j] = tmp1 + Inlines.MULT32_32_Q31(r, tmp2); + lpc[i - 1 - j] = tmp2 + Inlines.MULT32_32_Q31(r, tmp1); + } + + error = error - Inlines.MULT32_32_Q31(Inlines.MULT32_32_Q31(r, r), error); + + /* Bail out once we get 30 dB gain */ + if (error < Inlines.SHR32(ac[0], 10)) + { + break; + } + } + } + + for (i = 0; i < p; i++) + { + _lpc[i] = Inlines.ROUND16((lpc[i]), 16); + } + } + + internal static unsafe void celt_iir( + int[] _x, + int _x_ptr, + int[] den, + int[] _y, + int _y_ptr, + int N, + int ord, + int[] mem) + { + int i, j; + int[] rden = new int[ord]; + int[] y = new int[N + ord]; + Inlines.OpusAssert((ord & 3) == 0); + + fixed (int* prden = rden, py_base = y) + { + for (i = 0; i < ord; i++) + rden[i] = den[ord - i - 1]; + for (i = 0; i < ord; i++) + y[i] = (0 - mem[ord - i - 1]); + for (; i < N + ord; i++) + y[i] = 0; + for (i = 0; i < N - 3; i += 4) + { + int* py = py_base + i; + /* Unroll by 4 as if it were an FIR filter */ + int sum0 = _x[_x_ptr + i]; + int sum1 = _x[_x_ptr + i + 1]; + int sum2 = _x[_x_ptr + i + 2]; + int sum3 = _x[_x_ptr + i + 3]; + Kernels.xcorr_kernel(prden, py, ref sum0, ref sum1, ref sum2, ref sum3, ord); + + /* Patch up the result to compensate for the fact that this is an IIR */ + y[i + ord] = (0 - Inlines.ROUND16((sum0), CeltConstants.SIG_SHIFT)); + _y[_y_ptr + i] = sum0; + sum1 = Inlines.MAC16_16(sum1, y[i + ord], den[0]); + y[i + ord + 1] = (0 - Inlines.ROUND16((sum1), CeltConstants.SIG_SHIFT)); + _y[_y_ptr + i + 1] = sum1; + sum2 = Inlines.MAC16_16(sum2, y[i + ord + 1], den[0]); + sum2 = Inlines.MAC16_16(sum2, y[i + ord], den[1]); + y[i + ord + 2] = (0 - Inlines.ROUND16((sum2), CeltConstants.SIG_SHIFT)); + _y[_y_ptr + i + 2] = sum2; + + sum3 = Inlines.MAC16_16(sum3, y[i + ord + 2], den[0]); + sum3 = Inlines.MAC16_16(sum3, y[i + ord + 1], den[1]); + sum3 = Inlines.MAC16_16(sum3, y[i + ord], den[2]); + y[i + ord + 3] = (0 - Inlines.ROUND16((sum3), CeltConstants.SIG_SHIFT)); + _y[_y_ptr + i + 3] = sum3; + } + for (; i < N; i++) + { + int sum = _x[_x_ptr + i]; + for (j = 0; j < ord; j++) + sum -= Inlines.MULT16_16(rden[j], y[i + j]); + y[i + ord] = Inlines.ROUND16((sum), CeltConstants.SIG_SHIFT); + _y[_y_ptr + i] = sum; + } + for (i = 0; i < ord; i++) + mem[i] = (_y[_y_ptr + N - i - 1]); + } + } + } +} + +#endif \ No newline at end of file diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/CeltPitchXCorr.cs b/Libraries/Concentus/CSharp/Concentus/Celt/CeltPitchXCorr.cs new file mode 100644 index 000000000..cbd3596d6 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/CeltPitchXCorr.cs @@ -0,0 +1,154 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#if !UNSAFE + +namespace Concentus.Celt +{ + using Concentus.Celt.Enums; + using Concentus.Celt.Structs; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using System.Diagnostics; + using System.Threading; + + internal static class CeltPitchXCorr + { + internal static int pitch_xcorr( + int[] _x, + int[] _y, + int[] xcorr, + int len, + int max_pitch) + { + int i; + int maxcorr = 1; + Inlines.OpusAssert(max_pitch > 0); + for (i = 0; i < max_pitch - 3; i += 4) + { + int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0; + Kernels.xcorr_kernel(_x, _y, i, ref sum0, ref sum1, ref sum2, ref sum3, len); + xcorr[i] = sum0; + xcorr[i + 1] = sum1; + xcorr[i + 2] = sum2; + xcorr[i + 3] = sum3; + sum0 = Inlines.MAX32(sum0, sum1); + sum2 = Inlines.MAX32(sum2, sum3); + sum0 = Inlines.MAX32(sum0, sum2); + maxcorr = Inlines.MAX32(maxcorr, sum0); + } + /* In case max_pitch isn't a multiple of 4, do non-unrolled version. */ + for (; i < max_pitch; i++) + { + int inner_sum = Kernels.celt_inner_prod(_x, 0, _y, i, len); + xcorr[i] = inner_sum; + maxcorr = Inlines.MAX32(maxcorr, inner_sum); + } + return maxcorr; + } + + internal static int pitch_xcorr( + short[] _x, + int _x_ptr, + short[] _y, + int _y_ptr, + int[] xcorr, + int len, + int max_pitch) + { + int i; + int maxcorr = 1; + Inlines.OpusAssert(max_pitch > 0); + for (i = 0; i < max_pitch - 3; i += 4) + { + int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0; + Kernels.xcorr_kernel(_x, _x_ptr, _y, _y_ptr + i, ref sum0, ref sum1, ref sum2, ref sum3, len); + + xcorr[i] = sum0; + xcorr[i + 1] = sum1; + xcorr[i + 2] = sum2; + xcorr[i + 3] = sum3; + sum0 = Inlines.MAX32(sum0, sum1); + sum2 = Inlines.MAX32(sum2, sum3); + sum0 = Inlines.MAX32(sum0, sum2); + maxcorr = Inlines.MAX32(maxcorr, sum0); + } + /* In case max_pitch isn't a multiple of 4, do non-unrolled version. */ + for (; i < max_pitch; i++) + { + int inner_sum = Kernels.celt_inner_prod(_x, _x_ptr, _y, _y_ptr + i, len); + xcorr[i] = inner_sum; + maxcorr = Inlines.MAX32(maxcorr, inner_sum); + } + return maxcorr; + } + + internal static int pitch_xcorr( + short[] _x, + short[] _y, + int[] xcorr, + int len, + int max_pitch) + { + int i; + int maxcorr = 1; + Inlines.OpusAssert(max_pitch > 0); + for (i = 0; i < max_pitch - 3; i += 4) + { + int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0; + Kernels.xcorr_kernel(_x, 0, _y, i, ref sum0, ref sum1, ref sum2, ref sum3, len); + + xcorr[i] = sum0; + xcorr[i + 1] = sum1; + xcorr[i + 2] = sum2; + xcorr[i + 3] = sum3; + sum0 = Inlines.MAX32(sum0, sum1); + sum2 = Inlines.MAX32(sum2, sum3); + sum0 = Inlines.MAX32(sum0, sum2); + maxcorr = Inlines.MAX32(maxcorr, sum0); + } + /* In case max_pitch isn't a multiple of 4, do non-unrolled version. */ + for (; i < max_pitch; i++) + { + int inner_sum = Kernels.celt_inner_prod(_x, _y, i, len); + xcorr[i] = inner_sum; + maxcorr = Inlines.MAX32(maxcorr, inner_sum); + } + return maxcorr; + } + } +} + +#endif \ No newline at end of file diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/CeltPitchXCorrUnsafe.cs b/Libraries/Concentus/CSharp/Concentus/Celt/CeltPitchXCorrUnsafe.cs new file mode 100644 index 000000000..800948778 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/CeltPitchXCorrUnsafe.cs @@ -0,0 +1,255 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#if UNSAFE + +namespace Concentus.Celt +{ + using Concentus.Celt.Enums; + using Concentus.Celt.Structs; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using System.Diagnostics; + using System.Threading; + + internal static class CeltPitchXCorr + { + internal static unsafe int pitch_xcorr( + int[] _x, + int[] _y, + int[] xcorr, + int len, + int max_pitch) + { + int i; + int maxcorr = 1; + Inlines.OpusAssert(max_pitch > 0); + fixed (int* py_base = _y, px = _x, px_base = _x) + { + for (i = 0; i < max_pitch - 3; i += 4) + { + int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0; + int* py = py_base + i; + Kernels.xcorr_kernel(px, py, ref sum0, ref sum1, ref sum2, ref sum3, len); + xcorr[i] = sum0; + xcorr[i + 1] = sum1; + xcorr[i + 2] = sum2; + xcorr[i + 3] = sum3; + sum0 = Inlines.MAX32(sum0, sum1); + sum2 = Inlines.MAX32(sum2, sum3); + sum0 = Inlines.MAX32(sum0, sum2); + maxcorr = Inlines.MAX32(maxcorr, sum0); + } + /* In case max_pitch isn't a multiple of 4, do non-unrolled version. */ + for (; i < max_pitch; i++) + { + int* py = py_base + i; + int inner_sum = Kernels.celt_inner_prod(px_base, py, len); + xcorr[i] = inner_sum; + maxcorr = Inlines.MAX32(maxcorr, inner_sum); + } + } + + return maxcorr; + } + + internal static unsafe int pitch_xcorr( + int* _x, + int* _y, + int[] xcorr, + int len, + int max_pitch) + { + int i; + int maxcorr = 1; + Inlines.OpusAssert(max_pitch > 0); + for (i = 0; i < max_pitch - 3; i += 4) + { + int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0; + int* py = _y + i; + Kernels.xcorr_kernel(_x, py, ref sum0, ref sum1, ref sum2, ref sum3, len); + xcorr[i] = sum0; + xcorr[i + 1] = sum1; + xcorr[i + 2] = sum2; + xcorr[i + 3] = sum3; + sum0 = Inlines.MAX32(sum0, sum1); + sum2 = Inlines.MAX32(sum2, sum3); + sum0 = Inlines.MAX32(sum0, sum2); + maxcorr = Inlines.MAX32(maxcorr, sum0); + } + /* In case max_pitch isn't a multiple of 4, do non-unrolled version. */ + for (; i < max_pitch; i++) + { + int* py = _y + i; + int inner_sum = Kernels.celt_inner_prod(_x, py, len); + xcorr[i] = inner_sum; + maxcorr = Inlines.MAX32(maxcorr, inner_sum); + } + + return maxcorr; + } + + internal static unsafe int pitch_xcorr( + short[] _x, + int _x_ptr, + short[] _y, + int _y_ptr, + int[] xcorr, + int len, + int max_pitch) + { + int i; + int maxcorr = 1; + Inlines.OpusAssert(max_pitch > 0); + fixed (int* pxcorr = xcorr) + { + fixed (short* px_base = _x, py_base = _y) + { + short* px = px_base + _x_ptr; + for (i = 0; i < max_pitch - 3; i += 4) + { + int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0; + short* py = py_base + _y_ptr + i; + Kernels.xcorr_kernel(px, py, ref sum0, ref sum1, ref sum2, ref sum3, len); + + int* pxcorr2 = pxcorr + i; + pxcorr2[0] = sum0; + pxcorr2[1] = sum1; + pxcorr2[2] = sum2; + pxcorr2[3] = sum3; + sum0 = Inlines.MAX32(sum0, sum1); + sum2 = Inlines.MAX32(sum2, sum3); + sum0 = Inlines.MAX32(sum0, sum2); + maxcorr = Inlines.MAX32(maxcorr, sum0); + } + /* In case max_pitch isn't a multiple of 4, do non-unrolled version. */ + for (; i < max_pitch; i++) + { + short* py = py_base + _y_ptr + i; + int inner_sum = Kernels.celt_inner_prod(px, py, len); + xcorr[i] = inner_sum; + maxcorr = Inlines.MAX32(maxcorr, inner_sum); + } + } + } + + return maxcorr; + } + + internal static unsafe int pitch_xcorr( + short[] _x, + short[] _y, + int[] xcorr, + int len, + int max_pitch) + { + int i; + int maxcorr = 1; + Inlines.OpusAssert(max_pitch > 0); + fixed (int* pxcorr_base = xcorr) + { + fixed (short* px = _x, py_base = _y) + { + for (i = 0; i < max_pitch - 3; i += 4) + { + int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0; + short* py = py_base + i; + Kernels.xcorr_kernel(px, py, ref sum0, ref sum1, ref sum2, ref sum3, len); + + int* pxcorr = pxcorr_base + i; + pxcorr[0] = sum0; + pxcorr[1] = sum1; + pxcorr[2] = sum2; + pxcorr[3] = sum3; + sum0 = Inlines.MAX32(sum0, sum1); + sum2 = Inlines.MAX32(sum2, sum3); + sum0 = Inlines.MAX32(sum0, sum2); + maxcorr = Inlines.MAX32(maxcorr, sum0); + } + /* In case max_pitch isn't a multiple of 4, do non-unrolled version. */ + for (; i < max_pitch; i++) + { + short* py = py_base + i; + int inner_sum = Kernels.celt_inner_prod(px, py, len); + xcorr[i] = inner_sum; + maxcorr = Inlines.MAX32(maxcorr, inner_sum); + } + return maxcorr; + } + } + } + + internal static unsafe int pitch_xcorr( + short* px, + short* py, + int[] xcorr, + int len, + int max_pitch) + { + int i; + int maxcorr = 1; + Inlines.OpusAssert(max_pitch > 0); + fixed (int* pxcorr_base = xcorr) + { + for (i = 0; i < max_pitch - 3; i += 4) + { + int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0; + short* py2 = py + i; + Kernels.xcorr_kernel(px, py2, ref sum0, ref sum1, ref sum2, ref sum3, len); + int* pxcorr = pxcorr_base + i; + pxcorr[0] = sum0; + pxcorr[1] = sum1; + pxcorr[2] = sum2; + pxcorr[3] = sum3; + sum0 = Inlines.MAX32(sum0, sum1); + sum2 = Inlines.MAX32(sum2, sum3); + sum0 = Inlines.MAX32(sum0, sum2); + maxcorr = Inlines.MAX32(maxcorr, sum0); + } + /* In case max_pitch isn't a multiple of 4, do non-unrolled version. */ + for (; i < max_pitch; i++) + { + short* py2 = py + i; + int inner_sum = Kernels.celt_inner_prod(px, py2, len); + xcorr[i] = inner_sum; + maxcorr = Inlines.MAX32(maxcorr, inner_sum); + } + return maxcorr; + } + } + } +} + +#endif \ No newline at end of file diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/Enums/Spread.cs b/Libraries/Concentus/CSharp/Concentus/Celt/Enums/Spread.cs new file mode 100644 index 000000000..bbed99342 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/Enums/Spread.cs @@ -0,0 +1,45 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Celt.Enums +{ + internal static class Spread + { + public const int SPREAD_NONE = 0; + public const int SPREAD_LIGHT = 1; + public const int SPREAD_NORMAL = 2; + public const int SPREAD_AGGRESSIVE = 3; + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/Kernels.cs b/Libraries/Concentus/CSharp/Concentus/Celt/Kernels.cs new file mode 100644 index 000000000..607d876fe --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/Kernels.cs @@ -0,0 +1,354 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#if !UNSAFE + +namespace Concentus.Celt +{ + using Concentus.Celt.Enums; + using Concentus.Celt.Structs; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using System.Diagnostics; + + internal static class Kernels + { + internal static void celt_fir( + short[] x, + int x_ptr, + short[] num, + short[] y, + int y_ptr, + int N, + int ord, + short[] mem + ) + { + int i, j; + short[] rnum = new short[ord]; + short[] local_x = new short[N + ord]; + + for (i = 0; i < ord; i++) + { + rnum[i] = num[ord - i - 1]; + } + + for (i = 0; i < ord; i++) + { + local_x[i] = mem[ord - i - 1]; + } + + for (i = 0; i < N; i++) + { + local_x[i + ord] = x[x_ptr + i]; + } + + for (i = 0; i < ord; i++) + { + mem[i] = x[x_ptr + N - i - 1]; + } + + for (i = 0; i < N - 3; i += 4) + { + int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0; + xcorr_kernel(rnum, 0, local_x, i, ref sum0, ref sum1, ref sum2, ref sum3, ord); + y[y_ptr + i] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i]), Inlines.PSHR32(sum0, CeltConstants.SIG_SHIFT)))); + y[y_ptr + i + 1] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 1]), Inlines.PSHR32(sum1, CeltConstants.SIG_SHIFT)))); + y[y_ptr + i + 2] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 2]), Inlines.PSHR32(sum2, CeltConstants.SIG_SHIFT)))); + y[y_ptr + i + 3] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 3]), Inlines.PSHR32(sum3, CeltConstants.SIG_SHIFT)))); + } + + for (; i < N; i++) + { + int sum = 0; + + for (j = 0; j < ord; j++) + { + sum = Inlines.MAC16_16(sum, rnum[j], local_x[i + j]); + } + + y[y_ptr + i] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i]), Inlines.PSHR32(sum, CeltConstants.SIG_SHIFT)))); + } + } + + internal static void celt_fir( + int[] x, + int x_ptr, + int[] num, + int num_ptr, + int[] y, + int y_ptr, + int N, + int ord, + int[] mem + ) + { + int i, j; + int[] rnum = new int[ord]; + int[] local_x = new int[N + ord]; + + for (i = 0; i < ord; i++) + { + rnum[i] = num[num_ptr + ord - i - 1]; + } + + for (i = 0; i < ord; i++) + { + local_x[i] = mem[ord - i - 1]; + } + + for (i = 0; i < N; i++) + { + local_x[i + ord] = x[x_ptr + i]; + } + + for (i = 0; i < ord; i++) + { + mem[i] = x[x_ptr + N - i - 1]; + } + + for (i = 0; i < N - 3; i += 4) + { + int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0; + xcorr_kernel(rnum, local_x, i, ref sum0, ref sum1, ref sum2, ref sum3, ord); + y[y_ptr + i] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i]), Inlines.PSHR32(sum0, CeltConstants.SIG_SHIFT)))); + y[y_ptr + i + 1] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 1]), Inlines.PSHR32(sum1, CeltConstants.SIG_SHIFT)))); + y[y_ptr + i + 2] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 2]), Inlines.PSHR32(sum2, CeltConstants.SIG_SHIFT)))); + y[y_ptr + i + 3] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 3]), Inlines.PSHR32(sum3, CeltConstants.SIG_SHIFT)))); + } + + for (; i < N; i++) + { + int sum = 0; + + for (j = 0; j < ord; j++) + { + sum = Inlines.MAC16_16(sum, rnum[j], local_x[i + j]); + } + + y[y_ptr + i] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i]), Inlines.PSHR32(sum, CeltConstants.SIG_SHIFT)))); + } + } + + /// + /// OPT: This is the kernel you really want to optimize. It gets used a lot by the prefilter and by the PLC. + /// + /// + /// + /// + /// + internal static void xcorr_kernel(short[] x, int x_ptr, short[] y, int y_ptr, ref int sum0, ref int sum1, ref int sum2, ref int sum3, int len) + { + int j; + short y_0, y_1, y_2, y_3; + Inlines.OpusAssert(len >= 3); + y_3 = 0; /* gcc doesn't realize that y_3 can't be used uninitialized */ + y_0 = y[y_ptr++]; + y_1 = y[y_ptr++]; + y_2 = y[y_ptr++]; + for (j = 0; j < len - 3; j += 4) + { + short tmp; + tmp = x[x_ptr++]; + y_3 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_0); + sum1 = Inlines.MAC16_16(sum1, tmp, y_1); + sum2 = Inlines.MAC16_16(sum2, tmp, y_2); + sum3 = Inlines.MAC16_16(sum3, tmp, y_3); + tmp = x[x_ptr++]; + y_0 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_1); + sum1 = Inlines.MAC16_16(sum1, tmp, y_2); + sum2 = Inlines.MAC16_16(sum2, tmp, y_3); + sum3 = Inlines.MAC16_16(sum3, tmp, y_0); + tmp = x[x_ptr++]; + y_1 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_2); + sum1 = Inlines.MAC16_16(sum1, tmp, y_3); + sum2 = Inlines.MAC16_16(sum2, tmp, y_0); + sum3 = Inlines.MAC16_16(sum3, tmp, y_1); + tmp = x[x_ptr++]; + y_2 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_3); + sum1 = Inlines.MAC16_16(sum1, tmp, y_0); + sum2 = Inlines.MAC16_16(sum2, tmp, y_1); + sum3 = Inlines.MAC16_16(sum3, tmp, y_2); + } + if (j++ < len) + { + short tmp; + tmp = x[x_ptr++]; + y_3 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_0); + sum1 = Inlines.MAC16_16(sum1, tmp, y_1); + sum2 = Inlines.MAC16_16(sum2, tmp, y_2); + sum3 = Inlines.MAC16_16(sum3, tmp, y_3); + } + if (j++ < len) + { + short tmp; + tmp = x[x_ptr++]; + y_0 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_1); + sum1 = Inlines.MAC16_16(sum1, tmp, y_2); + sum2 = Inlines.MAC16_16(sum2, tmp, y_3); + sum3 = Inlines.MAC16_16(sum3, tmp, y_0); + } + if (j < len) + { + short tmp; + tmp = x[x_ptr++]; + y_1 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_2); + sum1 = Inlines.MAC16_16(sum1, tmp, y_3); + sum2 = Inlines.MAC16_16(sum2, tmp, y_0); + sum3 = Inlines.MAC16_16(sum3, tmp, y_1); + } + } + + internal static void xcorr_kernel(int[] x, int[] y, int y_ptr, ref int sum0, ref int sum1, ref int sum2, ref int sum3, int len) + { + int j; + int y_0, y_1, y_2, y_3; + int x_ptr = 0; + Inlines.OpusAssert(len >= 3); + y_3 = 0; /* gcc doesn't realize that y_3 can't be used uninitialized */ + y_0 = y[y_ptr++]; + y_1 = y[y_ptr++]; + y_2 = y[y_ptr++]; + for (j = 0; j < len - 3; j += 4) + { + int tmp; + tmp = x[x_ptr++]; + y_3 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_0); + sum1 = Inlines.MAC16_16(sum1, tmp, y_1); + sum2 = Inlines.MAC16_16(sum2, tmp, y_2); + sum3 = Inlines.MAC16_16(sum3, tmp, y_3); + tmp = x[x_ptr++]; + y_0 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_1); + sum1 = Inlines.MAC16_16(sum1, tmp, y_2); + sum2 = Inlines.MAC16_16(sum2, tmp, y_3); + sum3 = Inlines.MAC16_16(sum3, tmp, y_0); + tmp = x[x_ptr++]; + y_1 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_2); + sum1 = Inlines.MAC16_16(sum1, tmp, y_3); + sum2 = Inlines.MAC16_16(sum2, tmp, y_0); + sum3 = Inlines.MAC16_16(sum3, tmp, y_1); + tmp = x[x_ptr++]; + y_2 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_3); + sum1 = Inlines.MAC16_16(sum1, tmp, y_0); + sum2 = Inlines.MAC16_16(sum2, tmp, y_1); + sum3 = Inlines.MAC16_16(sum3, tmp, y_2); + } + if (j++ < len) + { + int tmp; + tmp = x[x_ptr++]; + y_3 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_0); + sum1 = Inlines.MAC16_16(sum1, tmp, y_1); + sum2 = Inlines.MAC16_16(sum2, tmp, y_2); + sum3 = Inlines.MAC16_16(sum3, tmp, y_3); + } + if (j++ < len) + { + int tmp; + tmp = x[x_ptr++]; + y_0 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_1); + sum1 = Inlines.MAC16_16(sum1, tmp, y_2); + sum2 = Inlines.MAC16_16(sum2, tmp, y_3); + sum3 = Inlines.MAC16_16(sum3, tmp, y_0); + } + if (j < len) + { + int tmp; + tmp = x[x_ptr++]; + y_1 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_2); + sum1 = Inlines.MAC16_16(sum1, tmp, y_3); + sum2 = Inlines.MAC16_16(sum2, tmp, y_0); + sum3 = Inlines.MAC16_16(sum3, tmp, y_1); + } + } + + internal static int celt_inner_prod(short[] x, int x_ptr, short[] y, int y_ptr, int N) + { + int i; + int xy = 0; + for (i = 0; i < N; i++) + xy = Inlines.MAC16_16(xy, x[x_ptr + i], y[y_ptr + i]); + return xy; + } + + internal static int celt_inner_prod(short[] x, short[] y, int y_ptr, int N) + { + int i; + int xy = 0; + for (i = 0; i < N; i++) + xy = Inlines.MAC16_16(xy, x[i], y[y_ptr + i]); + return xy; + } + + internal static int celt_inner_prod(int[] x, int x_ptr, int[] y, int y_ptr, int N) + { + int i; + int xy = 0; + for (i = 0; i < N; i++) + xy = Inlines.MAC16_16(xy, x[x_ptr + i], y[y_ptr + i]); + return xy; + } + + internal static void dual_inner_prod(int[] x, int x_ptr, int[] y01,int y01_ptr, int[] y02, int y02_ptr, int N, out int xy1, out int xy2) + { + int i; + int xy01 = 0; + int xy02 = 0; + for (i = 0; i < N; i++) + { + xy01 = Inlines.MAC16_16(xy01, x[x_ptr + i], y01[y01_ptr + i]); + xy02 = Inlines.MAC16_16(xy02, x[x_ptr + i], y02[y02_ptr + i]); + } + xy1 = xy01; + xy2 = xy02; + } + } +} + +#endif \ No newline at end of file diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/KernelsUnsafe.cs b/Libraries/Concentus/CSharp/Concentus/Celt/KernelsUnsafe.cs new file mode 100644 index 000000000..0b8ef6b5d --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/KernelsUnsafe.cs @@ -0,0 +1,352 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#if UNSAFE + +namespace Concentus.Celt +{ + using Concentus.Celt.Enums; + using Concentus.Celt.Structs; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using System.Diagnostics; + + internal static class Kernels + { + internal static unsafe void celt_fir( + short* x, + short[] num, + short* y, + int N, + int ord, + short[] mem + ) + { + int i, j; + short[] rnum = new short[ord]; + short[] local_x = new short[N + ord]; + fixed (short* prnum = rnum, plocal_x = local_x) + { + for (i = 0; i < ord; i++) + { + prnum[i] = num[ord - i - 1]; + } + + for (i = 0; i < ord; i++) + { + plocal_x[i] = mem[ord - i - 1]; + } + + for (i = 0; i < N; i++) + { + plocal_x[i + ord] = x[i]; + } + + for (i = 0; i < ord; i++) + { + mem[i] = x[N - i - 1]; + } + + short* py = y; + for (i = 0; i < N - 3; i += 4) + { + int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0; + short* local_x2 = plocal_x + i; + xcorr_kernel(prnum, local_x2, ref sum0, ref sum1, ref sum2, ref sum3, ord); + py[0] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[i]), Inlines.PSHR32(sum0, CeltConstants.SIG_SHIFT)))); + py[1] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[i + 1]), Inlines.PSHR32(sum1, CeltConstants.SIG_SHIFT)))); + py[2] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[i + 2]), Inlines.PSHR32(sum2, CeltConstants.SIG_SHIFT)))); + py[3] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[i + 3]), Inlines.PSHR32(sum3, CeltConstants.SIG_SHIFT)))); + py += 4; + } + + for (; i < N; i++) + { + int sum = 0; + + for (j = 0; j < ord; j++) + { + sum = Inlines.MAC16_16(sum, prnum[j], plocal_x[i + j]); + } + + *py = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[i]), Inlines.PSHR32(sum, CeltConstants.SIG_SHIFT)))); + py++; + } + } + } + + internal static unsafe void celt_fir( + int* px, + int* pnum, + int* py, + int N, + int ord, + int[] mem + ) + { + int i, j; + int[] rnum = new int[ord]; + int[] local_x = new int[N + ord]; + fixed (int* prnum = rnum, plocal_x_base = local_x) + { + for (i = 0; i < ord; i++) + { + rnum[i] = pnum[ord - i - 1]; + } + + for (i = 0; i < ord; i++) + { + local_x[i] = mem[ord - i - 1]; + } + + for (i = 0; i < N; i++) + { + local_x[i + ord] = px[i]; + } + + for (i = 0; i < ord; i++) + { + mem[i] = px[N - i - 1]; + } + + int* px2 = px; + for (i = 0; i < N - 3; i += 4) + { + int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0; + int* plocal_x = plocal_x_base + i; + xcorr_kernel(prnum, plocal_x, ref sum0, ref sum1, ref sum2, ref sum3, ord); + py[0] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(px2[0]), Inlines.PSHR32(sum0, CeltConstants.SIG_SHIFT)))); + py[1] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(px2[1]), Inlines.PSHR32(sum1, CeltConstants.SIG_SHIFT)))); + py[2] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(px2[2]), Inlines.PSHR32(sum2, CeltConstants.SIG_SHIFT)))); + py[3] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(px2[3]), Inlines.PSHR32(sum3, CeltConstants.SIG_SHIFT)))); + py += 4; + px2 += 4; + } + + for (; i < N; i++) + { + int sum = 0; + + for (j = 0; j < ord; j++) + { + sum = Inlines.MAC16_16(sum, rnum[j], local_x[i + j]); + } + + *py = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(px[i]), Inlines.PSHR32(sum, CeltConstants.SIG_SHIFT)))); + py++; + } + } + } + + /// + /// OPT: This is the kernel you really want to optimize. It gets used a lot by the prefilter and by the PLC. + /// + /// + /// + /// + /// + internal unsafe static void xcorr_kernel(short* x, short* py, ref int sum0, ref int sum1, ref int sum2, ref int sum3, int len) + { + int j; + short y_0, y_1, y_2, y_3; + Inlines.OpusAssert(len >= 3); + y_3 = 0; /* gcc doesn't realize that y_3 can't be used uninitialized */ + y_0 = *py++; + y_1 = *py++; + y_2 = *py++; + for (j = 0; j < len - 3; j += 4) + { + short tmp; + tmp = *x++; + y_3 = *py++; + sum0 = Inlines.MAC16_16(sum0, tmp, y_0); + sum1 = Inlines.MAC16_16(sum1, tmp, y_1); + sum2 = Inlines.MAC16_16(sum2, tmp, y_2); + sum3 = Inlines.MAC16_16(sum3, tmp, y_3); + tmp = *x++; + y_0 = *py++; + sum0 = Inlines.MAC16_16(sum0, tmp, y_1); + sum1 = Inlines.MAC16_16(sum1, tmp, y_2); + sum2 = Inlines.MAC16_16(sum2, tmp, y_3); + sum3 = Inlines.MAC16_16(sum3, tmp, y_0); + tmp = *x++; + y_1 = *py++; + sum0 = Inlines.MAC16_16(sum0, tmp, y_2); + sum1 = Inlines.MAC16_16(sum1, tmp, y_3); + sum2 = Inlines.MAC16_16(sum2, tmp, y_0); + sum3 = Inlines.MAC16_16(sum3, tmp, y_1); + tmp = *x++; + y_2 = *py++; + sum0 = Inlines.MAC16_16(sum0, tmp, y_3); + sum1 = Inlines.MAC16_16(sum1, tmp, y_0); + sum2 = Inlines.MAC16_16(sum2, tmp, y_1); + sum3 = Inlines.MAC16_16(sum3, tmp, y_2); + } + if (j++ < len) + { + short tmp; + tmp = *x++; + y_3 = *py++; + sum0 = Inlines.MAC16_16(sum0, tmp, y_0); + sum1 = Inlines.MAC16_16(sum1, tmp, y_1); + sum2 = Inlines.MAC16_16(sum2, tmp, y_2); + sum3 = Inlines.MAC16_16(sum3, tmp, y_3); + } + if (j++ < len) + { + short tmp; + tmp = *x++; + y_0 = *py++; + sum0 = Inlines.MAC16_16(sum0, tmp, y_1); + sum1 = Inlines.MAC16_16(sum1, tmp, y_2); + sum2 = Inlines.MAC16_16(sum2, tmp, y_3); + sum3 = Inlines.MAC16_16(sum3, tmp, y_0); + } + if (j < len) + { + short tmp; + tmp = *x++; + y_1 = *py++; + sum0 = Inlines.MAC16_16(sum0, tmp, y_2); + sum1 = Inlines.MAC16_16(sum1, tmp, y_3); + sum2 = Inlines.MAC16_16(sum2, tmp, y_0); + sum3 = Inlines.MAC16_16(sum3, tmp, y_1); + } + } + + internal unsafe static void xcorr_kernel(int* px, int* py, ref int sum0, ref int sum1, ref int sum2, ref int sum3, int len) + { + int j; + int y_0, y_1, y_2, y_3; + Inlines.OpusAssert(len >= 3); + y_3 = 0; /* gcc doesn't realize that y_3 can't be used uninitialized */ + y_0 = *py++; + y_1 = *py++; + y_2 = *py++; + for (j = 0; j < len - 3; j += 4) + { + int tmp; + tmp = *px++; + y_3 = *py++; + sum0 = Inlines.MAC16_16(sum0, tmp, y_0); + sum1 = Inlines.MAC16_16(sum1, tmp, y_1); + sum2 = Inlines.MAC16_16(sum2, tmp, y_2); + sum3 = Inlines.MAC16_16(sum3, tmp, y_3); + tmp = *px++; + y_0 = *py++; + sum0 = Inlines.MAC16_16(sum0, tmp, y_1); + sum1 = Inlines.MAC16_16(sum1, tmp, y_2); + sum2 = Inlines.MAC16_16(sum2, tmp, y_3); + sum3 = Inlines.MAC16_16(sum3, tmp, y_0); + tmp = *px++; + y_1 = *py++; + sum0 = Inlines.MAC16_16(sum0, tmp, y_2); + sum1 = Inlines.MAC16_16(sum1, tmp, y_3); + sum2 = Inlines.MAC16_16(sum2, tmp, y_0); + sum3 = Inlines.MAC16_16(sum3, tmp, y_1); + tmp = *px++; + y_2 = *py++; + sum0 = Inlines.MAC16_16(sum0, tmp, y_3); + sum1 = Inlines.MAC16_16(sum1, tmp, y_0); + sum2 = Inlines.MAC16_16(sum2, tmp, y_1); + sum3 = Inlines.MAC16_16(sum3, tmp, y_2); + } + if (j++ < len) + { + int tmp; + tmp = *px++; + y_3 = *py++; + sum0 = Inlines.MAC16_16(sum0, tmp, y_0); + sum1 = Inlines.MAC16_16(sum1, tmp, y_1); + sum2 = Inlines.MAC16_16(sum2, tmp, y_2); + sum3 = Inlines.MAC16_16(sum3, tmp, y_3); + } + if (j++ < len) + { + int tmp; + tmp = *px++; + y_0 = *py++; + sum0 = Inlines.MAC16_16(sum0, tmp, y_1); + sum1 = Inlines.MAC16_16(sum1, tmp, y_2); + sum2 = Inlines.MAC16_16(sum2, tmp, y_3); + sum3 = Inlines.MAC16_16(sum3, tmp, y_0); + } + if (j < len) + { + int tmp; + tmp = *px++; + y_1 = *py++; + sum0 = Inlines.MAC16_16(sum0, tmp, y_2); + sum1 = Inlines.MAC16_16(sum1, tmp, y_3); + sum2 = Inlines.MAC16_16(sum2, tmp, y_0); + sum3 = Inlines.MAC16_16(sum3, tmp, y_1); + } + } + + internal static unsafe int celt_inner_prod(short* x, short* y, int N) + { + int i; + int xy = 0; + for (i = 0; i < N; i++) + xy = Inlines.MAC16_16(xy, x[i], y[i]); + return xy; + } + + internal static unsafe int celt_inner_prod(int* x, int* y, int N) + { + int i; + int xy = 0; + for (i = 0; i < N; i++) + xy = Inlines.MAC16_16(xy, x[i], y[i]); + return xy; + } + + internal static unsafe void dual_inner_prod(int* x, int* y01, int* y02, int N, out int xy1, out int xy2) + { + int i; + int xy01 = 0; + int xy02 = 0; + for (i = 0; i < N; i++) + { + xy01 = Inlines.MAC16_16(xy01, x[i], y01[i]); + xy02 = Inlines.MAC16_16(xy02, x[i], y02[i]); + } + xy1 = xy01; + xy2 = xy02; + } + } +} + +#endif \ No newline at end of file diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/KissFFT.cs b/Libraries/Concentus/CSharp/Concentus/Celt/KissFFT.cs new file mode 100644 index 000000000..151fc29a3 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/KissFFT.cs @@ -0,0 +1,455 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Copyright (c) 2003-2004, Mark Borgerding + Modified from KISS-FFT by Jean-Marc Valin + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* This code is originally from Mark Borgerding's KISS-FFT but has been + heavily modified to better suit Opus */ + +#if !UNSAFE + +namespace Concentus.Celt +{ + using Concentus.Celt.Enums; + using Concentus.Celt.Structs; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using System.Diagnostics; + using System.Threading; + internal static class KissFFT + { + //public const int SAMP_MAX = 2147483647; + //public const int SAMP_MIN = 0 - SAMP_MAX; + //public const int TWID_MAX = 32767; + //public const int TRIG_UPSCALE = 1; + + internal const int MAXFACTORS = 8; + + internal static int S_MUL(int a, int b) + { + return Inlines.MULT16_32_Q15(b, a); + } + + internal static int S_MUL(int a, short b) + { + return Inlines.MULT16_32_Q15(b, a); + } + + internal static int HALF_OF(int x) + { + return x >> 1; + } + + internal static void kf_bfly2(int[] Fout, int fout_ptr, int m, int N) + { + int Fout2; + int i; + { + short tw; + tw = ((short)(0.5 + (0.7071067812f) * (((int)1) << (15))))/*Inlines.QCONST16(0.7071067812f, 15)*/; + /* We know that m==4 here because the radix-2 is just after a radix-4 */ + Inlines.OpusAssert(m == 4); + for (i = 0; i < N; i++) + { + int t_r, t_i; + Fout2 = fout_ptr + 8; + t_r = Fout[Fout2 + 0]; + t_i = Fout[Fout2 + 1]; + Fout[Fout2 + 0] = Fout[fout_ptr + 0] - t_r; + Fout[Fout2 + 1] = Fout[fout_ptr + 1] - t_i; + Fout[fout_ptr + 0] += t_r; + Fout[fout_ptr + 1] += t_i; + + t_r = S_MUL(Fout[Fout2 + 2] + Fout[Fout2 + 3], tw); + t_i = S_MUL(Fout[Fout2 + 3] - Fout[Fout2 + 2], tw); + Fout[Fout2 + 2] = Fout[fout_ptr + 2] - t_r; + Fout[Fout2 + 3] = Fout[fout_ptr + 3] - t_i; + Fout[fout_ptr + 2] += t_r; + Fout[fout_ptr + 3] += t_i; + + t_r = Fout[Fout2 + 5]; + t_i = 0 - Fout[Fout2 + 4]; + Fout[Fout2 + 4] = Fout[fout_ptr + 4] - t_r; + Fout[Fout2 + 5] = Fout[fout_ptr + 5] - t_i; + Fout[fout_ptr + 4] += t_r; + Fout[fout_ptr + 5] += t_i; + + t_r = S_MUL(Fout[Fout2 + 7] - Fout[Fout2 + 6], tw); + t_i = S_MUL(0 - Fout[Fout2 + 7] - Fout[Fout2 + 6], tw); + Fout[Fout2 + 6] = Fout[fout_ptr + 6] - t_r; + Fout[Fout2 + 7] = Fout[fout_ptr + 7] - t_i; + Fout[fout_ptr + 6] += t_r; + Fout[fout_ptr + 7] += t_i; + + fout_ptr += 16; + } + } + } + + internal static void kf_bfly4( + int[] Fout, + int fout_ptr, + int fstride, + FFTState st, + int m, + int N, + int mm) + { + int i; + + if (m == 1) + { + /* Degenerate case where all the twiddles are 1. */ + int scratch0, scratch1, scratch2, scratch3; + for (i = 0; i < N; i++) + { + scratch0 = Fout[fout_ptr + 0] - Fout[fout_ptr + 4]; + scratch1 = Fout[fout_ptr + 1] - Fout[fout_ptr + 5]; + Fout[fout_ptr + 0] += Fout[fout_ptr + 4]; + Fout[fout_ptr + 1] += Fout[fout_ptr + 5]; + scratch2 = Fout[fout_ptr + 2] + Fout[fout_ptr + 6]; + scratch3 = Fout[fout_ptr + 3] + Fout[fout_ptr + 7]; + Fout[fout_ptr + 4] = Fout[fout_ptr + 0] - scratch2; + Fout[fout_ptr + 5] = Fout[fout_ptr + 1] - scratch3; + Fout[fout_ptr + 0] += scratch2; + Fout[fout_ptr + 1] += scratch3; + scratch2 = Fout[fout_ptr + 2] - Fout[fout_ptr + 6]; + scratch3 = Fout[fout_ptr + 3] - Fout[fout_ptr + 7]; + Fout[fout_ptr + 2] = scratch0 + scratch3; + Fout[fout_ptr + 3] = scratch1 - scratch2; + Fout[fout_ptr + 6] = scratch0 - scratch3; + Fout[fout_ptr + 7] = scratch1 + scratch2; + fout_ptr += 8; + } + } + else + { + int j; + int scratch0, scratch1, scratch2, scratch3, scratch4, scratch5, scratch6, scratch7, scratch8, scratch9, scratch10, scratch11; + int tw1, tw2, tw3; + int Fout_beg = fout_ptr; + for (i = 0; i < N; i++) + { + fout_ptr = Fout_beg + 2 * i * mm; + int m1 = fout_ptr + (2 * m); + int m2 = fout_ptr + (4 * m); + int m3 = fout_ptr + (6 * m); + tw3 = tw2 = tw1 = 0; + /* m is guaranteed to be a multiple of 4. */ + for (j = 0; j < m; j++) + { + scratch0 = (S_MUL(Fout[m1], st.twiddles[tw1 ]) - S_MUL(Fout[m1 + 1], st.twiddles[tw1 + 1])); + scratch1 = (S_MUL(Fout[m1], st.twiddles[tw1 + 1]) + S_MUL(Fout[m1 + 1], st.twiddles[tw1])); + scratch2 = (S_MUL(Fout[m2], st.twiddles[tw2 ]) - S_MUL(Fout[m2 + 1], st.twiddles[tw2 + 1])); + scratch3 = (S_MUL(Fout[m2], st.twiddles[tw2 + 1]) + S_MUL(Fout[m2 + 1], st.twiddles[tw2])); + scratch4 = (S_MUL(Fout[m3], st.twiddles[tw3 ]) - S_MUL(Fout[m3 + 1], st.twiddles[tw3 + 1])); + scratch5 = (S_MUL(Fout[m3], st.twiddles[tw3 + 1]) + S_MUL(Fout[m3 + 1], st.twiddles[tw3])); + scratch10 = Fout[fout_ptr] - scratch2; + scratch11 = Fout[fout_ptr + 1] - scratch3; + Fout[fout_ptr] += scratch2; + Fout[fout_ptr + 1] += scratch3; + scratch6 = scratch0 + scratch4; + scratch7 = scratch1 + scratch5; + scratch8 = scratch0 - scratch4; + scratch9 = scratch1 - scratch5; + Fout[m2] = Fout[fout_ptr] - scratch6; + Fout[m2 + 1] = Fout[fout_ptr + 1] - scratch7; + tw1 += fstride * 2; + tw2 += fstride * 4; + tw3 += fstride * 6; + Fout[fout_ptr] += scratch6; + Fout[fout_ptr + 1] += scratch7; + Fout[m1] = scratch10 + scratch9; + Fout[m1 + 1] = scratch11 - scratch8; + Fout[m3] = scratch10 - scratch9; + Fout[m3 + 1] = scratch11 + scratch8; + fout_ptr += 2; + m1 += 2; + m2 += 2; + m3 += 2; + } + } + } + } + + internal static void kf_bfly3( + int[] Fout, + int fout_ptr, + int fstride, + FFTState st, + int m, + int N, + int mm + ) + { + int i; + int k; + int m1 = 2 * m; + int m2 = 4 * m; + int tw1, tw2; + int scratch0, scratch1, scratch2, scratch3, scratch4, scratch5, scratch6, scratch7; + + int Fout_beg = fout_ptr; + + for (i = 0; i < N; i++) + { + fout_ptr = Fout_beg + 2 * i * mm; + tw1 = tw2 = 0; + /* For non-custom modes, m is guaranteed to be a multiple of 4. */ + k = m; + do + { + scratch2 = (S_MUL(Fout[fout_ptr + m1], st.twiddles[tw1]) - S_MUL(Fout[fout_ptr + m1 + 1], st.twiddles[tw1 + 1])); + scratch3 = (S_MUL(Fout[fout_ptr + m1], st.twiddles[tw1 + 1]) + S_MUL(Fout[fout_ptr + m1 + 1], st.twiddles[tw1])); + scratch4 = (S_MUL(Fout[fout_ptr + m2], st.twiddles[tw2]) - S_MUL(Fout[fout_ptr + m2 + 1], st.twiddles[tw2 + 1])); + scratch5 = (S_MUL(Fout[fout_ptr + m2], st.twiddles[tw2 + 1]) + S_MUL(Fout[fout_ptr + m2 + 1], st.twiddles[tw2])); + + scratch6 = scratch2 + scratch4; + scratch7 = scratch3 + scratch5; + scratch0 = scratch2 - scratch4; + scratch1 = scratch3 - scratch5; + + tw1 += fstride * 2; + tw2 += fstride * 4; + + Fout[fout_ptr + m1] = Fout[fout_ptr + 0] - HALF_OF(scratch6); + Fout[fout_ptr + m1 + 1] = Fout[fout_ptr + 1] - HALF_OF(scratch7); + + scratch0 = S_MUL(scratch0, -28378); + scratch1 = S_MUL(scratch1, -28378); + + Fout[fout_ptr + 0] += scratch6; + Fout[fout_ptr + 1] += scratch7; + + Fout[fout_ptr + m2] = Fout[fout_ptr + m1] + scratch1; + Fout[fout_ptr + m2 + 1] = Fout[fout_ptr + m1 + 1] - scratch0; + + Fout[fout_ptr + m1] -= scratch1; + Fout[fout_ptr + m1 + 1] += scratch0; + + fout_ptr += 2; + } while ((--k) != 0); + } + } + + internal static void kf_bfly5( + int[] Fout, + int fout_ptr, + int fstride, + FFTState st, + int m, + int N, + int mm + ) + { + int Fout0, Fout1, Fout2, Fout3, Fout4; + int i, u; + int scratch0, scratch1, scratch2, scratch3, scratch4, scratch5, + scratch6, scratch7, scratch8, scratch9, scratch10, scratch11, + scratch12,scratch13, scratch14, scratch15, scratch16, scratch17, + scratch18, scratch19, scratch20, scratch21, scratch22, scratch23, + scratch24, scratch25; + + int Fout_beg = fout_ptr; + + short ya_r = 10126; + short ya_i = -31164; + short yb_r = -26510; + short yb_i = -19261; + int tw1, tw2, tw3, tw4; + + for (i = 0; i < N; i++) + { + tw1 = tw2 = tw3 = tw4 = 0; + fout_ptr = Fout_beg + 2 * i * mm; + Fout0 = fout_ptr; + Fout1 = fout_ptr + (2 * m); + Fout2 = fout_ptr + (4 * m); + Fout3 = fout_ptr + (6 * m); + Fout4 = fout_ptr + (8 * m); + + /* For non-custom modes, m is guaranteed to be a multiple of 4. */ + for (u = 0; u < m; ++u) + { + scratch0 = Fout[Fout0 + 0]; + scratch1 = Fout[Fout0 + 1]; + + scratch2 = (S_MUL(Fout[Fout1 + 0], st.twiddles[tw1]) - S_MUL(Fout[Fout1 + 1], st.twiddles[tw1 + 1])); + scratch3 = (S_MUL(Fout[Fout1 + 0], st.twiddles[tw1 + 1]) + S_MUL(Fout[Fout1 + 1], st.twiddles[tw1])); + scratch4 = (S_MUL(Fout[Fout2 + 0], st.twiddles[tw2]) - S_MUL(Fout[Fout2 + 1], st.twiddles[tw2 + 1])); + scratch5 = (S_MUL(Fout[Fout2 + 0], st.twiddles[tw2 + 1]) + S_MUL(Fout[Fout2 + 1], st.twiddles[tw2])); + scratch6 = (S_MUL(Fout[Fout3 + 0], st.twiddles[tw3]) - S_MUL(Fout[Fout3 + 1], st.twiddles[tw3 + 1])); + scratch7 = (S_MUL(Fout[Fout3 + 0], st.twiddles[tw3 + 1]) + S_MUL(Fout[Fout3 + 1], st.twiddles[tw3])); + scratch8 = (S_MUL(Fout[Fout4 + 0], st.twiddles[tw4]) - S_MUL(Fout[Fout4 + 1], st.twiddles[tw4 + 1])); + scratch9 = (S_MUL(Fout[Fout4 + 0], st.twiddles[tw4 + 1]) + S_MUL(Fout[Fout4 + 1], st.twiddles[tw4])); + + tw1 += (2 * fstride); + tw2 += (4 * fstride); + tw3 += (6 * fstride); + tw4 += (8 * fstride); + + scratch14 = scratch2 + scratch8; + scratch15 = scratch3 + scratch9; + scratch20 = scratch2 - scratch8; + scratch21 = scratch3 - scratch9; + scratch16 = scratch4 + scratch6; + scratch17 = scratch5 + scratch7; + scratch18 = scratch4 - scratch6; + scratch19 = scratch5 - scratch7; + + Fout[Fout0 + 0] += scratch14 + scratch16; + Fout[Fout0 + 1] += scratch15 + scratch17; + + scratch10 = scratch0 + S_MUL(scratch14, ya_r) + S_MUL(scratch16, yb_r); + scratch11 = scratch1 + S_MUL(scratch15, ya_r) + S_MUL(scratch17, yb_r); + + scratch12 = S_MUL(scratch21, ya_i) + S_MUL(scratch19, yb_i); + scratch13 = 0 - S_MUL(scratch20, ya_i) - S_MUL(scratch18, yb_i); + + Fout[Fout1 + 0] = scratch10 - scratch12; + Fout[Fout1 + 1] = scratch11 - scratch13; + Fout[Fout4 + 0] = scratch10 + scratch12; + Fout[Fout4 + 1] = scratch11 + scratch13; + + scratch22 = scratch0 + S_MUL(scratch14, yb_r) + S_MUL(scratch16, ya_r); + scratch23 = scratch1 + S_MUL(scratch15, yb_r) + S_MUL(scratch17, ya_r); + scratch24 = 0 - S_MUL(scratch21, yb_i) + S_MUL(scratch19, ya_i); + scratch25 = S_MUL(scratch20, yb_i) - S_MUL(scratch18, ya_i); + + Fout[Fout2 + 0] = scratch22 + scratch24; + Fout[Fout2 + 1] = scratch23 + scratch25; + Fout[Fout3 + 0] = scratch22 - scratch24; + Fout[Fout3 + 1] = scratch23 - scratch25; + + Fout0 += 2; + Fout1 += 2; + Fout2 += 2; + Fout3 += 2; + Fout4 += 2; + } + } + } + + internal static void opus_fft_impl(FFTState st, int[] fout, int fout_ptr) + { + int m2, m; + int p; + int L; + int[] fstride = new int[MAXFACTORS]; + int i; + int shift; + + /* st.shift can be -1 */ + shift = st.shift > 0 ? st.shift : 0; + + fstride[0] = 1; + L = 0; + do + { + p = st.factors[2 * L]; + m = st.factors[2 * L + 1]; + fstride[L + 1] = fstride[L] * p; + L++; + } while (m != 1); + + m = st.factors[2 * L - 1]; + for (i = L - 1; i >= 0; i--) + { + if (i != 0) + m2 = st.factors[2 * i - 1]; + else + m2 = 1; + switch (st.factors[2 * i]) + { + case 2: + kf_bfly2(fout, fout_ptr, m, fstride[i]); + break; + case 4: + kf_bfly4(fout, fout_ptr, fstride[i] << shift, st, m, fstride[i], m2); + break; + case 3: + kf_bfly3(fout, fout_ptr, fstride[i] << shift, st, m, fstride[i], m2); + break; + case 5: + kf_bfly5(fout, fout_ptr, fstride[i] << shift, st, m, fstride[i], m2); + break; + } + m = m2; + } + } + + internal static void opus_fft(FFTState st, int[] fin, int[] fout) + { + int i; + /* Allows us to scale with MULT16_32_Q16() */ + int scale_shift = st.scale_shift - 1; + short scale = st.scale; + + Inlines.OpusAssert(fin != fout, "In-place FFT not supported"); + + /* Bit-reverse the input */ + for (i = 0; i < st.nfft; i++) + { + fout[(2 * st.bitrev[i])] = Inlines.SHR32(Inlines.MULT16_32_Q16(scale, fin[(2 * i)]), scale_shift); + fout[(2 * st.bitrev[i] + 1)] = Inlines.SHR32(Inlines.MULT16_32_Q16(scale, fin[(2 * i) + 1]), scale_shift); + } + + opus_fft_impl(st, fout, 0); + } + + + //internal static void opus_ifft(FFTState st, Pointer fin, Pointer fout) + //{ + // int i; + // Inlines.OpusAssert(fin != fout, "In-place iFFT not supported"); + + // /* Bit-reverse the input */ + // for (i = 0; i < st.nfft * 2; i++) + // { + // fout[st.bitrev[i]] = fin[i]; + // } + + // for (i = 1; i < st.nfft * 2; i += 2) + // { + // fout[i] = -fout[i]; + // } + + // opus_fft_impl(st, fout.Data, fout.Offset); + + // for (i = 1; i < st.nfft * 2; i += 2) + // fout[i] = -fout[i]; + //} + } +} + +#endif \ No newline at end of file diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/KissFFTUnsafe.cs b/Libraries/Concentus/CSharp/Concentus/Celt/KissFFTUnsafe.cs new file mode 100644 index 000000000..fd42796fe --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/KissFFTUnsafe.cs @@ -0,0 +1,456 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Copyright (c) 2003-2004, Mark Borgerding + Modified from KISS-FFT by Jean-Marc Valin + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* This code is originally from Mark Borgerding's KISS-FFT but has been + heavily modified to better suit Opus */ + +#if UNSAFE + +namespace Concentus.Celt +{ + using Concentus.Celt.Enums; + using Concentus.Celt.Structs; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using System.Diagnostics; + using System.Threading; + internal static class KissFFT + { + //public const int SAMP_MAX = 2147483647; + //public const int SAMP_MIN = 0 - SAMP_MAX; + //public const int TWID_MAX = 32767; + //public const int TRIG_UPSCALE = 1; + + internal const int MAXFACTORS = 8; + + internal static int S_MUL(int a, int b) + { + return Inlines.MULT16_32_Q15(b, a); + } + + internal static int S_MUL(int a, short b) + { + return Inlines.MULT16_32_Q15(b, a); + } + + internal static int HALF_OF(int x) + { + return x >> 1; + } + + internal static unsafe void kf_bfly2(int* Fout, int m, int N) + { + int* Fout2; + int i; + { + short tw; + tw = ((short)(0.5 + (0.7071067812f) * (((int)1) << (15))))/*Inlines.QCONST16(0.7071067812f, 15)*/; + /* We know that m==4 here because the radix-2 is just after a radix-4 */ + Inlines.OpusAssert(m == 4); + for (i = 0; i < N; i++) + { + int t_r, t_i; + Fout2 = Fout + 8; + t_r = *(Fout2); + t_i = *(Fout2 + 1); + *Fout2 = (*Fout) - t_r; + *(Fout2 + 1) = *(Fout + 1) - t_i; + *Fout += t_r; + *(Fout + 1) += t_i; + + t_r = S_MUL(*(Fout2 + 2) + *(Fout2 + 3), tw); + t_i = S_MUL(*(Fout2 + 3) - *(Fout2 + 2), tw); + *(Fout2 + 2) = *(Fout + 2) - t_r; + *(Fout2 + 3) = *(Fout + 3) - t_i; + *(Fout + 2) += t_r; + *(Fout + 3) += t_i; + + t_r = *(Fout2 + 5); + t_i = 0 - *(Fout2 + 4); + *(Fout2 + 4) = *(Fout + 4) - t_r; + *(Fout2 + 5) = *(Fout + 5) - t_i; + *(Fout + 4) += t_r; + *(Fout + 5) += t_i; + + t_r = S_MUL(*(Fout2 + 7) - *(Fout2 + 6), tw); + t_i = S_MUL(0 - *(Fout2 + 7) - *(Fout2 + 6), tw); + *(Fout2 + 6) = *(Fout + 6) - t_r; + *(Fout2 + 7) = *(Fout + 7) - t_i; + *(Fout + 6) += t_r; + *(Fout + 7) += t_i; + + Fout += 16; + } + } + } + + internal static unsafe void kf_bfly4( + int* Fout, + int fstride, + FFTState st, + int m, + int N, + int mm) + { + int i; + + if (m == 1) + { + /* Degenerate case where all the twiddles are 1. */ + int scratch0, scratch1, scratch2, scratch3; + for (i = 0; i < N; i++) + { + scratch0 = *(Fout) - *(Fout + 4); + scratch1 = *(Fout + 1) - *(Fout + 5); + *(Fout + 0) += *(Fout + 4); + *(Fout + 1) += *(Fout + 5); + scratch2 = *(Fout + 2) + *(Fout + 6); + scratch3 = *(Fout + 3) + *(Fout + 7); + *(Fout + 4) = *(Fout + 0) - scratch2; + *(Fout + 5) = *(Fout + 1) - scratch3; + *(Fout + 0) += scratch2; + *(Fout + 1) += scratch3; + scratch2 = *(Fout + 2) - *(Fout + 6); + scratch3 = *(Fout + 3) - *(Fout + 7); + *(Fout + 2) = scratch0 + scratch3; + *(Fout + 3) = scratch1 - scratch2; + *(Fout + 6) = scratch0 - scratch3; + *(Fout + 7) = scratch1 + scratch2; + Fout += 8; + } + } + else + { + int j; + int scratch0, scratch1, scratch2, scratch3, scratch4, scratch5, scratch6, scratch7, scratch8, scratch9, scratch10, scratch11; + int tw1, tw2, tw3; + int* Fout_beg = Fout; + for (i = 0; i < N; i++) + { + Fout = Fout_beg + 2 * i * mm; + int* m1 = Fout + (2 * m); + int* m2 = Fout + (4 * m); + int* m3 = Fout + (6 * m); + tw3 = tw2 = tw1 = 0; + /* m is guaranteed to be a multiple of 4. */ + for (j = 0; j < m; j++) + { + scratch0 = (S_MUL(*m1, st.twiddles[tw1]) - S_MUL(*(m1 + 1), st.twiddles[tw1 + 1])); + scratch1 = (S_MUL(*m1, st.twiddles[tw1 + 1]) + S_MUL(*(m1 + 1), st.twiddles[tw1])); + scratch2 = (S_MUL(*m2, st.twiddles[tw2]) - S_MUL(*(m2 + 1), st.twiddles[tw2 + 1])); + scratch3 = (S_MUL(*m2, st.twiddles[tw2 + 1]) + S_MUL(*(m2 + 1), st.twiddles[tw2])); + scratch4 = (S_MUL(*m3, st.twiddles[tw3]) - S_MUL(*(m3 + 1), st.twiddles[tw3 + 1])); + scratch5 = (S_MUL(*m3, st.twiddles[tw3 + 1]) + S_MUL(*(m3 + 1), st.twiddles[tw3])); + scratch10 = *(Fout) - scratch2; + scratch11 = *(Fout + 1) - scratch3; + *(Fout) += scratch2; + *(Fout + 1) += scratch3; + scratch6 = scratch0 + scratch4; + scratch7 = scratch1 + scratch5; + scratch8 = scratch0 - scratch4; + scratch9 = scratch1 - scratch5; + *m2 = *(Fout) - scratch6; + *(m2 + 1) = *(Fout + 1) - scratch7; + tw1 += fstride * 2; + tw2 += fstride * 4; + tw3 += fstride * 6; + *(Fout ) += scratch6; + *(Fout + 1) += scratch7; + *m1 = scratch10 + scratch9; + *(m1 + 1) = scratch11 - scratch8; + *m3 = scratch10 - scratch9; + *(m3 + 1) = scratch11 + scratch8; + Fout += 2; + m1 += 2; + m2 += 2; + m3 += 2; + } + } + } + } + + internal static unsafe void kf_bfly3( + int* Fout, + int fstride, + FFTState st, + int m, + int N, + int mm + ) + { + int i; + int k; + int m1 = 2 * m; + int m2 = 4 * m; + int tw1, tw2; + int scratch0, scratch1, scratch2, scratch3, scratch4, scratch5, scratch6, scratch7; + + int* Fout_beg = Fout; + + for (i = 0; i < N; i++) + { + Fout = Fout_beg + 2 * i * mm; + tw1 = tw2 = 0; + /* For non-custom modes, m is guaranteed to be a multiple of 4. */ + k = m; + do + { + scratch2 = (S_MUL(*(Fout + m1), st.twiddles[tw1]) - S_MUL(*(Fout + m1 + 1), st.twiddles[tw1 + 1])); + scratch3 = (S_MUL(*(Fout + m1), st.twiddles[tw1 + 1]) + S_MUL(*(Fout + m1 + 1), st.twiddles[tw1])); + scratch4 = (S_MUL(*(Fout + m2), st.twiddles[tw2]) - S_MUL(*(Fout + m2 + 1), st.twiddles[tw2 + 1])); + scratch5 = (S_MUL(*(Fout + m2), st.twiddles[tw2 + 1]) + S_MUL(*(Fout + m2 + 1), st.twiddles[tw2])); + + scratch6 = scratch2 + scratch4; + scratch7 = scratch3 + scratch5; + scratch0 = scratch2 - scratch4; + scratch1 = scratch3 - scratch5; + + tw1 += fstride * 2; + tw2 += fstride * 4; + + *(Fout + m1) = *(Fout) - HALF_OF(scratch6); + *(Fout + m1 + 1) = *(Fout + 1) - HALF_OF(scratch7); + + scratch0 = S_MUL(scratch0, -28378); + scratch1 = S_MUL(scratch1, -28378); + + *(Fout) += scratch6; + *(Fout + 1) += scratch7; + + *(Fout + m2) = *(Fout + m1) + scratch1; + *(Fout + m2 + 1) = *(Fout + m1 + 1) - scratch0; + + *(Fout + m1) -= scratch1; + *(Fout + m1 + 1) += scratch0; + + Fout += 2; + } while ((--k) != 0); + } + } + + internal static unsafe void kf_bfly5( + int* Fout, + int fstride, + FFTState st, + int m, + int N, + int mm + ) + { + int* Fout0, Fout1, Fout2, Fout3, Fout4; + int i, u; + int scratch0, scratch1, scratch2, scratch3, scratch4, scratch5, + scratch6, scratch7, scratch8, scratch9, scratch10, scratch11, + scratch12, scratch13, scratch14, scratch15, scratch16, scratch17, + scratch18, scratch19, scratch20, scratch21, scratch22, scratch23, + scratch24, scratch25; + + int* Fout_beg = Fout; + + short ya_r = 10126; + short ya_i = -31164; + short yb_r = -26510; + short yb_i = -19261; + int tw1, tw2, tw3, tw4; + + for (i = 0; i < N; i++) + { + tw1 = tw2 = tw3 = tw4 = 0; + Fout = Fout_beg + 2 * i * mm; + Fout0 = Fout; + Fout1 = Fout + (2 * m); + Fout2 = Fout + (4 * m); + Fout3 = Fout + (6 * m); + Fout4 = Fout + (8 * m); + + /* For non-custom modes, m is guaranteed to be a multiple of 4. */ + for (u = 0; u < m; ++u) + { + scratch0 = *(Fout0 + 0); + scratch1 = *(Fout0 + 1); + + scratch2 = (S_MUL(*(Fout1 + 0), st.twiddles[tw1]) - S_MUL(*(Fout1 + 1), st.twiddles[tw1 + 1])); + scratch3 = (S_MUL(*(Fout1 + 0), st.twiddles[tw1 + 1]) + S_MUL(*(Fout1 + 1), st.twiddles[tw1])); + scratch4 = (S_MUL(*(Fout2 + 0), st.twiddles[tw2]) - S_MUL(*(Fout2 + 1), st.twiddles[tw2 + 1])); + scratch5 = (S_MUL(*(Fout2 + 0), st.twiddles[tw2 + 1]) + S_MUL(*(Fout2 + 1), st.twiddles[tw2])); + scratch6 = (S_MUL(*(Fout3 + 0), st.twiddles[tw3]) - S_MUL(*(Fout3 + 1), st.twiddles[tw3 + 1])); + scratch7 = (S_MUL(*(Fout3 + 0), st.twiddles[tw3 + 1]) + S_MUL(*(Fout3 + 1), st.twiddles[tw3])); + scratch8 = (S_MUL(*(Fout4 + 0), st.twiddles[tw4]) - S_MUL(*(Fout4 + 1), st.twiddles[tw4 + 1])); + scratch9 = (S_MUL(*(Fout4 + 0), st.twiddles[tw4 + 1]) + S_MUL(*(Fout4 + 1), st.twiddles[tw4])); + + tw1 += (2 * fstride); + tw2 += (4 * fstride); + tw3 += (6 * fstride); + tw4 += (8 * fstride); + + scratch14 = scratch2 + scratch8; + scratch15 = scratch3 + scratch9; + scratch20 = scratch2 - scratch8; + scratch21 = scratch3 - scratch9; + scratch16 = scratch4 + scratch6; + scratch17 = scratch5 + scratch7; + scratch18 = scratch4 - scratch6; + scratch19 = scratch5 - scratch7; + + *(Fout0 + 0) += scratch14 + scratch16; + *(Fout0 + 1) += scratch15 + scratch17; + + scratch10 = scratch0 + S_MUL(scratch14, ya_r) + S_MUL(scratch16, yb_r); + scratch11 = scratch1 + S_MUL(scratch15, ya_r) + S_MUL(scratch17, yb_r); + + scratch12 = S_MUL(scratch21, ya_i) + S_MUL(scratch19, yb_i); + scratch13 = 0 - S_MUL(scratch20, ya_i) - S_MUL(scratch18, yb_i); + + *(Fout1 + 0) = scratch10 - scratch12; + *(Fout1 + 1) = scratch11 - scratch13; + *(Fout4 + 0) = scratch10 + scratch12; + *(Fout4 + 1) = scratch11 + scratch13; + + scratch22 = scratch0 + S_MUL(scratch14, yb_r) + S_MUL(scratch16, ya_r); + scratch23 = scratch1 + S_MUL(scratch15, yb_r) + S_MUL(scratch17, ya_r); + scratch24 = 0 - S_MUL(scratch21, yb_i) + S_MUL(scratch19, ya_i); + scratch25 = S_MUL(scratch20, yb_i) - S_MUL(scratch18, ya_i); + + *(Fout2 + 0) = scratch22 + scratch24; + *(Fout2 + 1) = scratch23 + scratch25; + *(Fout3 + 0) = scratch22 - scratch24; + *(Fout3 + 1) = scratch23 - scratch25; + + Fout0 += 2; + Fout1 += 2; + Fout2 += 2; + Fout3 += 2; + Fout4 += 2; + } + } + } + + internal static unsafe void opus_fft_impl(FFTState st, int[] fout, int fout_ptr) + { + int m2, m; + int p; + int L; + int[] fstride = new int[MAXFACTORS]; + int i; + int shift; + + /* st.shift can be -1 */ + shift = st.shift > 0 ? st.shift : 0; + + fstride[0] = 1; + L = 0; + do + { + p = st.factors[2 * L]; + m = st.factors[2 * L + 1]; + fstride[L + 1] = fstride[L] * p; + L++; + } while (m != 1); + + fixed (int* _fixed_fout = fout) + { + int* pfout = _fixed_fout + fout_ptr; + m = st.factors[2 * L - 1]; + for (i = L - 1; i >= 0; i--) + { + if (i != 0) + m2 = st.factors[2 * i - 1]; + else + m2 = 1; + switch (st.factors[2 * i]) + { + case 2: + kf_bfly2(pfout, m, fstride[i]); + break; + case 4: + kf_bfly4(pfout, fstride[i] << shift, st, m, fstride[i], m2); + break; + case 3: + kf_bfly3(pfout, fstride[i] << shift, st, m, fstride[i], m2); + break; + case 5: + kf_bfly5(pfout, fstride[i] << shift, st, m, fstride[i], m2); + break; + } + m = m2; + } + } + } + + internal static void opus_fft(FFTState st, int[] fin, int[] fout) + { + int i; + /* Allows us to scale with MULT16_32_Q16() */ + int scale_shift = st.scale_shift - 1; + short scale = st.scale; + + Inlines.OpusAssert(fin != fout, "In-place FFT not supported"); + + /* Bit-reverse the input */ + for (i = 0; i < st.nfft; i++) + { + fout[(2 * st.bitrev[i])] = Inlines.SHR32(Inlines.MULT16_32_Q16(scale, fin[(2 * i)]), scale_shift); + fout[(2 * st.bitrev[i] + 1)] = Inlines.SHR32(Inlines.MULT16_32_Q16(scale, fin[(2 * i) + 1]), scale_shift); + } + + opus_fft_impl(st, fout, 0); + } + + + //internal static void opus_ifft(FFTState st, Pointer fin, Pointer fout) + //{ + // int i; + // Inlines.OpusAssert(fin != fout, "In-place iFFT not supported"); + + // /* Bit-reverse the input */ + // for (i = 0; i < st.nfft * 2; i++) + // { + // fout[st.bitrev[i]] = fin[i]; + // } + + // for (i = 1; i < st.nfft * 2; i += 2) + // { + // fout[i] = -fout[i]; + // } + + // opus_fft_impl(st, fout.Data, fout.Offset); + + // for (i = 1; i < st.nfft * 2; i += 2) + // fout[i] = -fout[i]; + //} + } +} + +#endif \ No newline at end of file diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/Laplace.cs b/Libraries/Concentus/CSharp/Concentus/Celt/Laplace.cs new file mode 100644 index 000000000..f51e4d74c --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/Laplace.cs @@ -0,0 +1,157 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using Concentus.Celt.Enums; +using Concentus.Common; +using Concentus.Common.CPlusPlus; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; + + +namespace Concentus.Celt +{ + internal static class Laplace + { + /* The minimum probability of an energy delta (out of 32768). */ + private const int LAPLACE_LOG_MINP = 0; + private const uint LAPLACE_MINP = (1 << LAPLACE_LOG_MINP); + /* The minimum number of guaranteed representable energy deltas (in one + direction). */ + private const int LAPLACE_NMIN = 16; + + /* When called, decay is positive and at most 11456. */ + internal static uint ec_laplace_get_freq1(uint fs0, int decay) + { + uint ft; + ft = 32768 - LAPLACE_MINP * (2 * LAPLACE_NMIN) - fs0; + return (uint)((ft * (int)(16384 - decay)) >> 15); + } + + internal static void ec_laplace_encode(EntropyCoder enc, ref int value, uint fs, int decay) + { + uint fl; + int val = value; + fl = 0; + if (val != 0) + { + int s; + int i; + s = 0 - (val < 0 ? 1 : 0); + val = (val + s) ^ s; + fl = fs; + fs = ec_laplace_get_freq1(fs, decay); + + /* Search the decaying part of the PDF.*/ + for (i = 1; fs > 0 && i < val; i++) + { + fs *= 2; + fl += fs + 2 * LAPLACE_MINP; + fs = (uint)((fs * (int)decay) >> 15); + } + + /* Everything beyond that has probability LAPLACE_MINP. */ + if (fs == 0) + { + int di; + int ndi_max; + ndi_max = (int)(32768 - fl + LAPLACE_MINP - 1) >> LAPLACE_LOG_MINP; + ndi_max = (ndi_max - s) >> 1; + di = Inlines.IMIN(val - i, ndi_max - 1); + fl += (uint)(2 * di + 1 + s) * LAPLACE_MINP; + fs = Inlines.IMIN(LAPLACE_MINP, 32768 - fl); + value = (i + di + s) ^ s; + } + else + { + fs += LAPLACE_MINP; + fl += (uint)(fs & ~s); + } + Inlines.OpusAssert(fl + fs <= 32768); + Inlines.OpusAssert(fs > 0); + } + + enc.encode_bin(fl, fl + fs, 15); + } + + internal static int ec_laplace_decode(EntropyCoder dec, uint fs, int decay) + { + int val = 0; + uint fl; + uint fm; + fm = dec.decode_bin(15); + fl = 0; + + if (fm >= fs) + { + val++; + fl = fs; + fs = ec_laplace_get_freq1(fs, decay) + LAPLACE_MINP; + /* Search the decaying part of the PDF.*/ + while (fs > LAPLACE_MINP && fm >= fl + 2 * fs) + { + fs *= 2; + fl += fs; + fs = (uint)(((fs - 2 * LAPLACE_MINP) * (int)decay) >> 15); + fs += LAPLACE_MINP; + val++; + } + /* Everything beyond that has probability LAPLACE_MINP. */ + if (fs <= LAPLACE_MINP) + { + int di; + di = (int)(fm - fl) >> (LAPLACE_LOG_MINP + 1); + val += di; + fl += (uint)(2 * di * LAPLACE_MINP); + } + if (fm < fl + fs) + val = -val; + else + fl += fs; + } + + Inlines.OpusAssert(fl < 32768); + Inlines.OpusAssert(fs > 0); + Inlines.OpusAssert(fl <= fm); + Inlines.OpusAssert(fm < Inlines.IMIN(fl + fs, 32768)); + + dec.dec_update(fl, Inlines.IMIN(fl + fs, 32768), 32768); + return val; + } + + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/MDCT.cs b/Libraries/Concentus/CSharp/Concentus/Celt/MDCT.cs new file mode 100644 index 000000000..f41a52e9b --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/MDCT.cs @@ -0,0 +1,370 @@ +/* Copyright (c) 2003-2004, Mark Borgerding + Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Modified from KISS-FFT by Jean-Marc Valin + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Celt +{ + using Concentus.Celt.Enums; + using Concentus.Celt.Structs; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using System.Diagnostics; + + internal static class MDCT + { + +#if !UNSAFE + /* Forward MDCT trashes the input array */ + internal static void clt_mdct_forward(MDCTLookup l, int[] input, int input_ptr, int[] output, int output_ptr, + int[] window, int overlap, int shift, int stride) + { + int i; + int N, N2, N4; + int[] f; + int[] f2; + FFTState st = l.kfft[shift]; + short[] trig; + int trig_ptr = 0; + int scale; + + int scale_shift = st.scale_shift - 1; + scale = st.scale; + + N = l.n; + trig = l.trig; + for (i = 0; i < shift; i++) + { + N = N >> 1; + trig_ptr += N; + } + N2 = N >> 1; + N4 = N >> 2; + + f = new int[N2]; + f2 = new int[N4 * 2]; + + /* Consider the input to be composed of four blocks: [a, b, c, d] */ + /* Window, shuffle, fold */ + { + /* Temp pointers to make it really clear to the compiler what we're doing */ + int xp1 = input_ptr + (overlap >> 1); + int xp2 = input_ptr + N2 - 1 + (overlap >> 1); + int yp = 0; + int wp1 = (overlap >> 1); + int wp2 = ((overlap >> 1) - 1); + for (i = 0; i < ((overlap + 3) >> 2); i++) + { + /* Real part arranged as -d-cR, Imag part arranged as -b+aR*/ + f[yp++] = Inlines.MULT16_32_Q15(window[wp2], input[xp1 + N2]) + Inlines.MULT16_32_Q15(window[wp1], input[xp2]); + f[yp++] = Inlines.MULT16_32_Q15(window[wp1], input[xp1]) - Inlines.MULT16_32_Q15(window[wp2], input[xp2 - N2]); + xp1 += 2; + xp2 -= 2; + wp1 += 2; + wp2 -= 2; + } + wp1 = 0; + wp2 = (overlap - 1); + for (; i < N4 - ((overlap + 3) >> 2); i++) + { + /* Real part arranged as a-bR, Imag part arranged as -c-dR */ + f[yp++] = input[xp2]; + f[yp++] = input[xp1]; + xp1 += 2; + xp2 -= 2; + } + for (; i < N4; i++) + { + /* Real part arranged as a-bR, Imag part arranged as -c-dR */ + f[yp++] = Inlines.MULT16_32_Q15(window[wp2], input[xp2]) - Inlines.MULT16_32_Q15(window[wp1], input[xp1 - N2]); + f[yp++] = Inlines.MULT16_32_Q15(window[wp2], input[xp1]) + Inlines.MULT16_32_Q15(window[wp1], input[xp2 + N2]); + xp1 += 2; + xp2 -= 2; + wp1 += 2; + wp2 -= 2; + } + } + /* Pre-rotation */ + { + int yp = 0; + int t = trig_ptr; + for (i = 0; i < N4; i++) + { + short t0, t1; + int re, im, yr, yi; + t0 = trig[t + i]; + t1 = trig[t + N4 + i]; + re = f[yp++]; + im = f[yp++]; + yr = KissFFT.S_MUL(re, t0) - KissFFT.S_MUL(im, t1); + yi = KissFFT.S_MUL(im, t0) + KissFFT.S_MUL(re, t1); + f2[2 * st.bitrev[i]] = Inlines.PSHR32(Inlines.MULT16_32_Q16(scale, yr), scale_shift); + f2[2 * st.bitrev[i] + 1] = Inlines.PSHR32(Inlines.MULT16_32_Q16(scale, yi), scale_shift); + } + } + + /* N/4 complex FFT, does not downscale anymore */ + KissFFT.opus_fft_impl(st, f2, 0); + + /* Post-rotate */ + { + /* Temp pointers to make it really clear to the compiler what we're doing */ + int fp = 0; + int yp1 = output_ptr; + int yp2 = output_ptr + (stride * (N2 - 1)); + int t = trig_ptr; + for (i = 0; i < N4; i++) + { + int yr, yi; + yr = KissFFT.S_MUL(f2[fp + 1], trig[t + N4 + i]) - KissFFT.S_MUL(f2[fp], trig[t + i]); + yi = KissFFT.S_MUL(f2[fp], trig[t + N4 + i]) + KissFFT.S_MUL(f2[fp + 1], trig[t + i]); + output[yp1] = yr; + output[yp2] = yi; + fp += 2; + yp1 += (2 * stride); + yp2 -= (2 * stride); + } + } + } +#else + /* Forward MDCT trashes the input array */ + internal static unsafe void clt_mdct_forward(MDCTLookup l, int[] input, int input_ptr, int[] output, int output_ptr, + int[] window, int overlap, int shift, int stride) + { + int i; + int N, N2, N4; + int[] f; + int[] f2; + FFTState st = l.kfft[shift]; + int scale; + + int scale_shift = st.scale_shift - 1; + scale = st.scale; + + N = l.n; + fixed (short* ptrig_base = l.trig) + { + short* trig = ptrig_base; + for (i = 0; i < shift; i++) + { + N = N >> 1; + trig += N; + } + N2 = N >> 1; + N4 = N >> 2; + + f = new int[N2]; + f2 = new int[N4 * 2]; + fixed (int* pinput_base = input, pwindow = window, pf = f, pf2 = f2) + { + int* pinput = pinput_base + input_ptr; + /* Consider the input to be composed of four blocks: [a, b, c, d] */ + /* Window, shuffle, fold */ + { + /* Temp pointers to make it really clear to the compiler what we're doing */ + int* xp1 = pinput + (overlap >> 1); + int* xp2 = pinput + N2 - 1 + (overlap >> 1); + int* yp = pf; + int* wp1 = pwindow + (overlap >> 1); + int* wp2 = pwindow + ((overlap >> 1) - 1); + for (i = 0; i < ((overlap + 3) >> 2); i++) + { + /* Real part arranged as -d-cR, Imag part arranged as -b+aR*/ + *yp++ = Inlines.MULT16_32_Q15(*wp2, xp1[N2]) + Inlines.MULT16_32_Q15(*wp1, *xp2); + *yp++ = Inlines.MULT16_32_Q15(*wp1, *xp1) - Inlines.MULT16_32_Q15(*wp2, xp2[0 - N2]); + xp1 += 2; + xp2 -= 2; + wp1 += 2; + wp2 -= 2; + } + wp1 = pwindow; + wp2 = pwindow + (overlap - 1); + for (; i < N4 - ((overlap + 3) >> 2); i++) + { + /* Real part arranged as a-bR, Imag part arranged as -c-dR */ + *yp++ = *xp2; + *yp++ = *xp1; + xp1 += 2; + xp2 -= 2; + } + for (; i < N4; i++) + { + /* Real part arranged as a-bR, Imag part arranged as -c-dR */ + *yp++ = Inlines.MULT16_32_Q15(*wp2, *xp2) - Inlines.MULT16_32_Q15(*wp1, xp1[0 - N2]); + *yp++ = Inlines.MULT16_32_Q15(*wp2, *xp1) + Inlines.MULT16_32_Q15(*wp1, xp2[N2]); + xp1 += 2; + xp2 -= 2; + wp1 += 2; + wp2 -= 2; + } + } + /* Pre-rotation */ + { + int* yp = pf; + short* t = trig; + for (i = 0; i < N4; i++) + { + short t0, t1; + int re, im, yr, yi; + t0 = t[i]; + t1 = t[N4 + i]; + re = *yp++; + im = *yp++; + yr = KissFFT.S_MUL(re, t0) - KissFFT.S_MUL(im, t1); + yi = KissFFT.S_MUL(im, t0) + KissFFT.S_MUL(re, t1); + pf2[2 * st.bitrev[i]] = Inlines.PSHR32(Inlines.MULT16_32_Q16(scale, yr), scale_shift); + pf2[2 * st.bitrev[i] + 1] = Inlines.PSHR32(Inlines.MULT16_32_Q16(scale, yi), scale_shift); + } + } + + /* N/4 complex FFT, does not downscale anymore */ + KissFFT.opus_fft_impl(st, f2, 0); + + /* Post-rotate */ + fixed (int* poutput_base = output) + { + /* Temp pointers to make it really clear to the compiler what we're doing */ + int* fp = pf2; + int* yp1 = poutput_base + output_ptr; + int* yp2 = poutput_base + output_ptr + (stride * (N2 - 1)); + short* t = trig; + for (i = 0; i < N4; i++) + { + int yr, yi; + yr = KissFFT.S_MUL(fp[1], t[N4 + i]) - KissFFT.S_MUL(fp[0], t[i]); + yi = KissFFT.S_MUL(fp[0], t[N4 + i]) + KissFFT.S_MUL(fp[1], t[i]); + *yp1 = yr; + *yp2 = yi; + fp += 2; + yp1 += (2 * stride); + yp2 -= (2 * stride); + } + } + } + } + } +#endif + + internal static void clt_mdct_backward(MDCTLookup l, int[] input, int input_ptr, int[] output, int output_ptr, + int[] window, int overlap, int shift, int stride) + { + int i; + int N, N2, N4; + int trig = 0; + int xp1, xp2, yp, yp0, yp1; + + N = l.n; + for (i = 0; i < shift; i++) + { + N >>= 1; + trig += N; + } + N2 = N >> 1; + N4 = N >> 2; + + /* Pre-rotate */ + /* Temp pointers to make it really clear to the compiler what we're doing */ + xp2 = input_ptr + (stride * (N2 - 1)); + yp = output_ptr + (overlap >> 1); + short[] bitrev = l.kfft[shift].bitrev; + int bitrav_ptr = 0; + for (i = 0; i < N4; i++) + { + int rev = bitrev[bitrav_ptr++]; + int ypr = yp + 2 * rev; + /* We swap real and imag because we use an FFT instead of an IFFT. */ + output[ypr + 1] = KissFFT.S_MUL(input[xp2], l.trig[trig + i]) + KissFFT.S_MUL(input[input_ptr], l.trig[trig + N4 + i]); //yr + output[ypr] = KissFFT.S_MUL(input[input_ptr], l.trig[trig + i]) - KissFFT.S_MUL(input[xp2], l.trig[trig + N4 + i]); //yi + /* Storing the pre-rotation directly in the bitrev order. */ + input_ptr += (2 * stride); + xp2 -= (2 * stride); + } + + KissFFT.opus_fft_impl(l.kfft[shift], output, output_ptr + (overlap >> 1)); + + /* Post-rotate and de-shuffle from both ends of the buffer at once to make + it in-place. */ + yp0 = output_ptr + (overlap >> 1); + yp1 = output_ptr + (overlap >> 1) + N2 - 2; + int t = trig; + + /* Loop to (N4+1)>>1 to handle odd N4. When N4 is odd, the + middle pair will be computed twice. */ + int tN4m1 = t + N4 - 1; + int tN2m1 = t + N2 - 1; + for (i = 0; i < (N4 + 1) >> 1; i++) + { + int re, im, yr, yi; + short t0, t1; + /* We swap real and imag because we're using an FFT instead of an IFFT. */ + re = output[yp0 + 1]; + im = output[yp0]; + t0 = l.trig[t + i]; + t1 = l.trig[t + N4 + i]; + /* We'd scale up by 2 here, but instead it's done when mixing the windows */ + yr = KissFFT.S_MUL(re, t0) + KissFFT.S_MUL(im, t1); + yi = KissFFT.S_MUL(re, t1) - KissFFT.S_MUL(im, t0); + /* We swap real and imag because we're using an FFT instead of an IFFT. */ + re = output[yp1 + 1]; + im = output[yp1]; + output[yp0] = yr; + output[yp1 + 1] = yi; + t0 = l.trig[tN4m1 - i]; + t1 = l.trig[tN2m1 - i]; + /* We'd scale up by 2 here, but instead it's done when mixing the windows */ + yr = KissFFT.S_MUL(re, t0) + KissFFT.S_MUL(im, t1); + yi = KissFFT.S_MUL(re, t1) - KissFFT.S_MUL(im, t0); + output[yp1] = yr; + output[yp0 + 1] = yi; + yp0 += 2; + yp1 -= 2; + } + + /* Mirror on both sides for TDAC */ + xp1 = output_ptr + overlap - 1; + yp1 = output_ptr; + int wp1 = 0; + int wp2 = (overlap - 1); + + for (i = 0; i < overlap / 2; i++) + { + int x1 = output[xp1]; + int x2 = output[yp1]; + output[yp1++] = Inlines.MULT16_32_Q15(window[wp2], x2) - Inlines.MULT16_32_Q15(window[wp1], x1); + output[xp1--] = Inlines.MULT16_32_Q15(window[wp1], x2) + Inlines.MULT16_32_Q15(window[wp2], x1); + wp1++; + wp2--; + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/Pitch.cs b/Libraries/Concentus/CSharp/Concentus/Celt/Pitch.cs new file mode 100644 index 000000000..9407f6aea --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/Pitch.cs @@ -0,0 +1,506 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Celt +{ + using Concentus.Celt.Enums; + using Concentus.Celt.Structs; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using System.Diagnostics; + + internal static class Pitch + { + internal static void find_best_pitch(int[] xcorr, int[] y, int len, + int max_pitch, int[]best_pitch, + int yshift, int maxcorr + ) + { + int i, j; + int Syy = 1; + int best_num_0; + int best_num_1; + int best_den_0; + int best_den_1; + int xshift = Inlines.celt_ilog2(maxcorr) - 14; + + best_num_0 = -1; + best_num_1 = -1; + best_den_0 = 0; + best_den_1 = 0; + best_pitch[0] = 0; + best_pitch[1] = 1; + for (j = 0; j < len; j++) + Syy = Inlines.ADD32(Syy, Inlines.SHR32(Inlines.MULT16_16(y[j], y[j]), yshift)); + for (i = 0; i < max_pitch; i++) + { + if (xcorr[i] > 0) + { + int num; + int xcorr16; + xcorr16 = Inlines.EXTRACT16(Inlines.VSHR32(xcorr[i], xshift)); + num = Inlines.MULT16_16_Q15((xcorr16), (xcorr16)); + if (Inlines.MULT16_32_Q15(num, best_den_1) > Inlines.MULT16_32_Q15(best_num_1, Syy)) + { + if (Inlines.MULT16_32_Q15(num, best_den_0) > Inlines.MULT16_32_Q15(best_num_0, Syy)) + { + best_num_1 = best_num_0; + best_den_1 = best_den_0; + best_pitch[1] = best_pitch[0]; + best_num_0 = num; + best_den_0 = Syy; + best_pitch[0] = i; + } + else + { + best_num_1 = num; + best_den_1 = Syy; + best_pitch[1] = i; + } + } + } + + Syy += Inlines.SHR32(Inlines.MULT16_16(y[i + len], y[i + len]), yshift) - Inlines.SHR32(Inlines.MULT16_16(y[i], y[i]), yshift); + Syy = Inlines.MAX32(1, Syy); + } + } + + internal static void celt_fir5(int[] x, + int[] num, + int[] y, + int N, + int[] mem) + { + int i; + int num0, num1, num2, num3, num4; + int mem0, mem1, mem2, mem3, mem4; + num0 = num[0]; + num1 = num[1]; + num2 = num[2]; + num3 = num[3]; + num4 = num[4]; + mem0 = mem[0]; + mem1 = mem[1]; + mem2 = mem[2]; + mem3 = mem[3]; + mem4 = mem[4]; + for (i = 0; i < N; i++) + { + int sum = Inlines.SHL32(Inlines.EXTEND32(x[i]), CeltConstants.SIG_SHIFT); + sum = Inlines.MAC16_16(sum, num0, (mem0)); + sum = Inlines.MAC16_16(sum, num1, (mem1)); + sum = Inlines.MAC16_16(sum, num2, (mem2)); + sum = Inlines.MAC16_16(sum, num3, (mem3)); + sum = Inlines.MAC16_16(sum, num4, (mem4)); + mem4 = mem3; + mem3 = mem2; + mem2 = mem1; + mem1 = mem0; + mem0 = x[i]; + y[i] = Inlines.ROUND16(sum, CeltConstants.SIG_SHIFT); + } + mem[0] = (mem0); + mem[1] = (mem1); + mem[2] = (mem2); + mem[3] = (mem3); + mem[4] = (mem4); + } + + + internal static void pitch_downsample(int[][] x, int[] x_lp, int len, int C) + { + int i; + int[] ac = new int[5]; + int tmp = CeltConstants.Q15ONE; + int[] lpc = new int[4]; + int[] mem = new int[] { 0, 0, 0, 0, 0 }; + int[] lpc2 = new int[5]; + int c1 = ((short)(0.5 + (0.8f) * (((int)1) << (15))))/*Inlines.QCONST16(0.8f, 15)*/; + + int shift; + int maxabs = Inlines.celt_maxabs32(x[0], 0, len); + if (C == 2) + { + int maxabs_1 = Inlines.celt_maxabs32(x[1], 0, len); + maxabs = Inlines.MAX32(maxabs, maxabs_1); + } + if (maxabs < 1) + maxabs = 1; + shift = Inlines.celt_ilog2(maxabs) - 10; + if (shift < 0) + shift = 0; + if (C == 2) + shift++; + + int halflen = len >> 1; // cached for performance + for (i = 1; i < halflen; i++) + { + x_lp[i] = (Inlines.SHR32(Inlines.HALF32(Inlines.HALF32(x[0][(2 * i - 1)] + x[0][(2 * i + 1)]) + x[0][2 * i]), shift)); + } + + x_lp[0] = (Inlines.SHR32(Inlines.HALF32(Inlines.HALF32(x[0][1]) + x[0][0]), shift)); + + if (C == 2) + { + for (i = 1; i < halflen; i++) + x_lp[i] += (Inlines.SHR32(Inlines.HALF32(Inlines.HALF32(x[1][(2 * i - 1)] + x[1][(2 * i + 1)]) + x[1][2 * i]), shift)); + x_lp[0] += (Inlines.SHR32(Inlines.HALF32(Inlines.HALF32(x[1][1]) + x[1][0]), shift)); + } + + Autocorrelation._celt_autocorr(x_lp, ac, null, 0, 4, halflen); + + /* Noise floor -40 dB */ + ac[0] += Inlines.SHR32(ac[0], 13); + /* Lag windowing */ + for (i = 1; i <= 4; i++) + { + /*ac[i] *= exp(-.5*(2*M_PI*.002*i)*(2*M_PI*.002*i));*/ + ac[i] -= Inlines.MULT16_32_Q15((2 * i * i), ac[i]); + } + + CeltLPC.celt_lpc(lpc, ac, 4); + for (i = 0; i < 4; i++) + { + tmp = Inlines.MULT16_16_Q15(((short)(0.5 + (.9f) * (((int)1) << (15))))/*Inlines.QCONST16(.9f, 15)*/, tmp); + lpc[i] = Inlines.MULT16_16_Q15(lpc[i], tmp); + } + /* Add a zero */ + lpc2[0] = (lpc[0] + ((short)(0.5 + (0.8f) * (((int)1) << (CeltConstants.SIG_SHIFT))))/*Inlines.QCONST16(0.8f, CeltConstants.SIG_SHIFT)*/); + lpc2[1] = (lpc[1] + Inlines.MULT16_16_Q15(c1, lpc[0])); + lpc2[2] = (lpc[2] + Inlines.MULT16_16_Q15(c1, lpc[1])); + lpc2[3] = (lpc[3] + Inlines.MULT16_16_Q15(c1, lpc[2])); + lpc2[4] = Inlines.MULT16_16_Q15(c1, lpc[3]); + + celt_fir5(x_lp, lpc2, x_lp, halflen, mem); + } + + // Fixme: remove pointers and optimize + internal static void pitch_search(int[] x_lp, int x_lp_ptr, int[] y, + int len, int max_pitch, out int pitch) + { + int i, j; + int lag; + int[] best_pitch = new int[] { 0, 0 }; + int maxcorr; + int xmax, ymax; + int shift = 0; + int offset; + + Inlines.OpusAssert(len > 0); + Inlines.OpusAssert(max_pitch > 0); + lag = len + max_pitch; + + int[] x_lp4 = new int[len >> 2]; + int[] y_lp4 = new int[lag >> 2]; + int[] xcorr = new int[max_pitch >> 1]; + + /* Downsample by 2 again */ + for (j = 0; j < len >> 2; j++) + x_lp4[j] = x_lp[x_lp_ptr + (2 * j)]; + for (j = 0; j < lag >> 2; j++) + y_lp4[j] = y[2 * j]; + + xmax = Inlines.celt_maxabs32(x_lp4, 0, len >> 2); + ymax = Inlines.celt_maxabs32(y_lp4, 0, lag >> 2); + shift = Inlines.celt_ilog2(Inlines.MAX32(1, Inlines.MAX32(xmax, ymax))) - 11; + if (shift > 0) + { + for (j = 0; j < len >> 2; j++) + x_lp4[j] = Inlines.SHR16(x_lp4[j], shift); + for (j = 0; j < lag >> 2; j++) + y_lp4[j] = Inlines.SHR16(y_lp4[j], shift); + /* Use double the shift for a MAC */ + shift *= 2; + } + else { + shift = 0; + } + + /* Coarse search with 4x decimation */ + maxcorr = CeltPitchXCorr.pitch_xcorr(x_lp4, y_lp4, xcorr, len >> 2, max_pitch >> 2); + + find_best_pitch(xcorr, y_lp4, len >> 2, max_pitch >> 2, best_pitch, 0, maxcorr); + + /* Finer search with 2x decimation */ + maxcorr = 1; + for (i = 0; i < max_pitch >> 1; i++) + { + int sum; + xcorr[i] = 0; + if (Inlines.abs(i - 2 * best_pitch[0]) > 2 && Inlines.abs(i - 2 * best_pitch[1]) > 2) + { + continue; + } + sum = 0; + for (j = 0; j < len >> 1; j++) + sum += Inlines.SHR32(Inlines.MULT16_16(x_lp[x_lp_ptr + j], y[i + j]), shift); + + xcorr[i] = Inlines.MAX32(-1, sum); + maxcorr = Inlines.MAX32(maxcorr, sum); + } + find_best_pitch(xcorr, y, len >> 1, max_pitch >> 1, best_pitch, shift + 1, maxcorr); + + /* Refine by pseudo-interpolation */ + if (best_pitch[0] > 0 && best_pitch[0] < (max_pitch >> 1) - 1) + { + int a, b, c; + a = xcorr[best_pitch[0] - 1]; + b = xcorr[best_pitch[0]]; + c = xcorr[best_pitch[0] + 1]; + if ((c - a) > Inlines.MULT16_32_Q15(((short)(0.5 + (.7f) * (((int)1) << (15))))/*Inlines.QCONST16(.7f, 15)*/, b - a)) + { + offset = 1; + } + else if ((a - c) > Inlines.MULT16_32_Q15(((short)(0.5 + (.7f) * (((int)1) << (15))))/*Inlines.QCONST16(.7f, 15)*/, b - c)) + { + offset = -1; + } + else + { + offset = 0; + } + } + else + { + offset = 0; + } + + pitch = 2 * best_pitch[0] - offset; + } + + private static readonly int[] second_check = { 0, 0, 3, 2, 3, 2, 5, 2, 3, 2, 3, 2, 5, 2, 3, 2 }; + + internal static int remove_doubling(int[] x, int maxperiod, int minperiod, + int N, ref int T0_, int prev_period, int prev_gain) + { + int k, i, T, T0; + int g, g0; + int pg; + int yy, xx, xy, xy2; + int[] xcorr = new int[3]; + int best_xy, best_yy; + int offset; + int minperiod0 = minperiod; + maxperiod /= 2; + minperiod /= 2; + T0_ /= 2; + prev_period /= 2; + N /= 2; + int x_ptr = maxperiod; + if (T0_ >= maxperiod) + T0_ = maxperiod - 1; + + T = T0 = T0_; + int[] yy_lookup = new int[maxperiod + 1]; + +#if UNSAFE + unsafe + { + fixed (int* px_base = x) + { + int* px = px_base + x_ptr; + int* px2 = px_base + x_ptr - T0; + Kernels.dual_inner_prod(px, px, px2, N, out xx, out xy); + } + } +#else + Kernels.dual_inner_prod(x, x_ptr, x, x_ptr, x, x_ptr - T0, N, out xx, out xy); +#endif + + yy_lookup[0] = xx; + yy = xx; + for (i = 1; i <= maxperiod; i++) + { + int xi = x_ptr - i; + yy = yy + Inlines.MULT16_16(x[xi], x[xi]) - Inlines.MULT16_16(x[xi + N], x[xi + N]); + yy_lookup[i] = Inlines.MAX32(0, yy); + } + yy = yy_lookup[T0]; + best_xy = xy; + best_yy = yy; + + { + int x2y2; + int sh, t; + x2y2 = 1 + Inlines.HALF32(Inlines.MULT32_32_Q31(xx, yy)); + sh = Inlines.celt_ilog2(x2y2) >> 1; + t = Inlines.VSHR32(x2y2, 2 * (sh - 7)); + g = (Inlines.VSHR32(Inlines.MULT16_32_Q15(Inlines.celt_rsqrt_norm(t), xy), sh + 1)); + g0 = g; + } + + /* Look for any pitch at T/k */ + for (k = 2; k <= 15; k++) + { + int T1, T1b; + int g1; + int cont = 0; + int thresh; + T1 = Inlines.celt_udiv(2 * T0 + k, 2 * k); + if (T1 < minperiod) + { + break; + } + + /* Look for another strong correlation at T1b */ + if (k == 2) + { + if (T1 + T0 > maxperiod) + T1b = T0; + else + T1b = T0 + T1; + } + else + { + T1b = Inlines.celt_udiv(2 * second_check[k] * T0 + k, 2 * k); + } + +#if UNSAFE + unsafe + { + fixed (int* px_base = x) + { + int* px = px_base + x_ptr; + int* px2 = px_base + x_ptr - T1; + int* px3 = px_base + x_ptr - T1b; + Kernels.dual_inner_prod(px, px2, px3, N, out xy, out xy2); + } + } +#else + Kernels.dual_inner_prod(x, x_ptr, x, x_ptr - T1, x, x_ptr - T1b, N, out xy, out xy2); +#endif + + xy += xy2; + yy = yy_lookup[T1] + yy_lookup[T1b]; + + { + int x2y2; + int sh, t; + x2y2 = 1 + Inlines.MULT32_32_Q31(xx, yy); + sh = Inlines.celt_ilog2(x2y2) >> 1; + t = Inlines.VSHR32(x2y2, 2 * (sh - 7)); + g1 = (Inlines.VSHR32(Inlines.MULT16_32_Q15(Inlines.celt_rsqrt_norm(t), xy), sh + 1)); + } + + if (Inlines.abs(T1 - prev_period) <= 1) + cont = prev_gain; + else if (Inlines.abs(T1 - prev_period) <= 2 && 5 * k * k < T0) + { + cont = Inlines.HALF16(prev_gain); + } + else + { + cont = 0; + } + thresh = Inlines.MAX16(((short)(0.5 + (.3f) * (((int)1) << (15))))/*Inlines.QCONST16(.3f, 15)*/, (Inlines.MULT16_16_Q15(((short)(0.5 + (.7f) * (((int)1) << (15))))/*Inlines.QCONST16(.7f, 15)*/, g0) - cont)); + + /* Bias against very high pitch (very short period) to avoid false-positives + due to short-term correlation */ + if (T1 < 3 * minperiod) + { + thresh = Inlines.MAX16(((short)(0.5 + (.4f) * (((int)1) << (15))))/*Inlines.QCONST16(.4f, 15)*/, (Inlines.MULT16_16_Q15(((short)(0.5 + (.85f) * (((int)1) << (15))))/*Inlines.QCONST16(.85f, 15)*/, g0) - cont)); + } + else if (T1 < 2 * minperiod) + { + thresh = Inlines.MAX16(((short)(0.5 + (.5f) * (((int)1) << (15))))/*Inlines.QCONST16(.5f, 15)*/, (Inlines.MULT16_16_Q15(((short)(0.5 + (.9f) * (((int)1) << (15))))/*Inlines.QCONST16(.9f, 15)*/, g0) - cont)); + } + if (g1 > thresh) + { + best_xy = xy; + best_yy = yy; + T = T1; + g = g1; + } + } + + best_xy = Inlines.MAX32(0, best_xy); + if (best_yy <= best_xy) + { + pg = CeltConstants.Q15ONE; + } + else + { + pg = (Inlines.SHR32(Inlines.frac_div32(best_xy, best_yy + 1), 16)); + } + +#if UNSAFE + unsafe + { + fixed (int* px_base = x) + { + int* px = px_base + x_ptr; + for (k = 0; k < 3; k++) + { + xcorr[k] = Kernels.celt_inner_prod(px, px - (T + k - 1), N); + } + } + } +#else + for (k = 0; k < 3; k++) + { + xcorr[k] = Kernels.celt_inner_prod(x, x_ptr, x, x_ptr - (T + k - 1), N); + } +#endif + + if ((xcorr[2] - xcorr[0]) > Inlines.MULT16_32_Q15(((short)(0.5 + (.7f) * (((int)1) << (15))))/*Inlines.QCONST16(.7f, 15)*/, xcorr[1] - xcorr[0])) + { + offset = 1; + } + else if ((xcorr[0] - xcorr[2]) > Inlines.MULT16_32_Q15(((short)(0.5 + (.7f) * (((int)1) << (15))))/*Inlines.QCONST16(.7f, 15)*/, xcorr[1] - xcorr[2])) + { + offset = -1; + } + else + { + offset = 0; + } + + if (pg > g) + { + pg = g; + } + + T0_ = 2 * T + offset; + + if (T0_ < minperiod0) + { + T0_ = minperiod0; + } + + return pg; + } + + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/QuantizeBands.cs b/Libraries/Concentus/CSharp/Concentus/Celt/QuantizeBands.cs new file mode 100644 index 000000000..a9a08aec6 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/QuantizeBands.cs @@ -0,0 +1,524 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Celt +{ + using Concentus.Celt.Enums; + using Concentus.Celt.Structs; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using System; + using System.Diagnostics; + + internal static class QuantizeBands + { + /* prediction coefficients: 0.9, 0.8, 0.65, 0.5 */ + private static readonly int[] pred_coef = new int[]{ 29440, 26112, 21248, 16384 }; + private static readonly int[] beta_coef = new int[] { 30147, 22282, 12124, 6554 }; + private static readonly int beta_intra = 4915; + private static byte[] small_energy_icdf = { 2, 1, 0 }; + + internal static int loss_distortion(int[][] eBands, int[][] oldEBands, int start, int end, int len, int C) + { + int c, i; + int dist = 0; + c = 0; + do + { + for (i = start; i < end; i++) + { + int d = Inlines.SUB16(Inlines.SHR16(eBands[c][i], 3), Inlines.SHR16(oldEBands[c][i], 3)); + dist = Inlines.MAC16_16(dist, d, d); + } + } while (++c < C); + + return Inlines.MIN32(200, Inlines.SHR32(dist, 2 * CeltConstants.DB_SHIFT - 6)); + } + + internal static int quant_coarse_energy_impl(CeltMode m, int start, int end, + int[][] eBands, int[][] oldEBands, + int budget, int tell, + byte[] prob_model, int[][] error, EntropyCoder enc, + int C, int LM, int intra, int max_decay, int lfe) + { + int i, c; + int badness = 0; + int[] prev = { 0, 0 }; + int coef; + int beta; + + if (tell + 3 <= budget) + { + enc.enc_bit_logp(intra, 3); + } + + if (intra != 0) + { + coef = 0; + beta = beta_intra; + } + else { + beta = beta_coef[LM]; + coef = pred_coef[LM]; + } + + /* Encode at a fixed coarse resolution */ + for (i = start; i < end; i++) + { + c = 0; + do + { + int bits_left; + int qi, qi0; + int q; + int x; + int f, tmp; + int oldE; + int decay_bound; + x = eBands[c][i]; + oldE = Inlines.MAX16(-((short)(0.5 + (9.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(9.0f, CeltConstants.DB_SHIFT)*/, oldEBands[c][i]); + f = Inlines.SHL32(Inlines.EXTEND32(x), 7) - Inlines.PSHR32(Inlines.MULT16_16(coef, oldE), 8) - prev[c]; + /* Rounding to nearest integer here is really important! */ + qi = (f + ((int)(0.5 + (.5f) * (((int)1) << (CeltConstants.DB_SHIFT + 7))))/*Inlines.QCONST32(.5f, CeltConstants.DB_SHIFT + 7)*/) >> (CeltConstants.DB_SHIFT + 7); + decay_bound = Inlines.EXTRACT16(Inlines.MAX32(-((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/, + Inlines.SUB32((int)oldEBands[c][i], max_decay))); + /* Prevent the energy from going down too quickly (e.g. for bands + that have just one bin) */ + if (qi < 0 && x < decay_bound) + { + qi += (int)Inlines.SHR16(Inlines.SUB16(decay_bound, x), CeltConstants.DB_SHIFT); + if (qi > 0) + qi = 0; + } + qi0 = qi; + /* If we don't have enough bits to encode all the energy, just assume + something safe. */ + tell = enc.tell(); + bits_left = budget - tell - 3 * C * (end - i); + if (i != start && bits_left < 30) + { + if (bits_left < 24) + qi = Inlines.IMIN(1, qi); + if (bits_left < 16) + qi = Inlines.IMAX(-1, qi); + } + if (lfe != 0 && i >= 2) + qi = Inlines.IMIN(qi, 0); + if (budget - tell >= 15) + { + int pi; + pi = 2 * Inlines.IMIN(i, 20); + Laplace.ec_laplace_encode(enc, ref qi, (((uint)prob_model[pi]) << 7), ((int)prob_model[pi + 1]) << 6); + } + else if (budget - tell >= 2) + { + qi = Inlines.IMAX(-1, Inlines.IMIN(qi, 1)); + enc.enc_icdf(2 * qi ^ (0 - (qi < 0 ? 1 : 0)), small_energy_icdf, 2); + } + else if (budget - tell >= 1) + { + qi = Inlines.IMIN(0, qi); + enc.enc_bit_logp(-qi, 1); + } + else + qi = -1; + error[c][i] = (Inlines.PSHR32(f, 7) - Inlines.SHL16((qi), CeltConstants.DB_SHIFT)); + badness += Inlines.abs(qi0 - qi); + q = (int)Inlines.SHL32(qi, CeltConstants.DB_SHIFT); // opus bug: useless extend32 + + tmp = Inlines.PSHR32(Inlines.MULT16_16(coef, oldE), 8) + prev[c] + Inlines.SHL32(q, 7); + tmp = Inlines.MAX32(-((int)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT + 7))))/*Inlines.QCONST32(28.0f, CeltConstants.DB_SHIFT + 7)*/, tmp); + oldEBands[c][i] = (Inlines.PSHR32(tmp, 7)); + prev[c] = prev[c] + Inlines.SHL32(q, 7) - Inlines.MULT16_16(beta, Inlines.PSHR32(q, 8)); + } while (++c < C); + } + return lfe != 0 ? 0 : badness; + } + + internal static void quant_coarse_energy(CeltMode m, int start, int end, int effEnd, + int[][] eBands, int[][] oldEBands, uint budget, + int[][] error, EntropyCoder enc, int C, int LM, int nbAvailableBytes, + int force_intra, ref int delayedIntra, int two_pass, int loss_rate, int lfe) + { + int intra; + int max_decay; + int[][] oldEBands_intra; + int[][] error_intra; + EntropyCoder enc_start_state = new EntropyCoder(); // [porting note] stack variable + uint tell; + int badness1 = 0; + int intra_bias; + int new_distortion; + + + intra = (force_intra != 0 || (two_pass == 0 && delayedIntra > 2 * C * (end - start) && nbAvailableBytes > (end - start) * C)) ? 1 : 0; + intra_bias = (int)((budget * delayedIntra * loss_rate) / (C * 512)); + new_distortion = loss_distortion(eBands, oldEBands, start, effEnd, m.nbEBands, C); + + tell = (uint)enc.tell(); + if (tell + 3 > budget) + two_pass = intra = 0; + + max_decay = ((short)(0.5 + (16.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(16.0f, CeltConstants.DB_SHIFT)*/; + if (end - start > 10) + { + max_decay = (Inlines.MIN32(max_decay, Inlines.SHL32(nbAvailableBytes, CeltConstants.DB_SHIFT - 3))); // opus bug: useless extend32 + } + if (lfe != 0) + { + max_decay = ((short)(0.5 + (3.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(3.0f, CeltConstants.DB_SHIFT)*/; + } + enc_start_state.Assign(enc); + + oldEBands_intra = Arrays.InitTwoDimensionalArray(C, m.nbEBands); + error_intra = Arrays.InitTwoDimensionalArray(C, m.nbEBands); + Array.Copy(oldEBands[0], 0, oldEBands_intra[0], 0, m.nbEBands); + if (C == 2) + Array.Copy(oldEBands[1], 0, oldEBands_intra[1], 0, m.nbEBands); + + if (two_pass != 0 || intra != 0) + { + badness1 = quant_coarse_energy_impl(m, start, end, eBands, oldEBands_intra, (int)budget, + (int)tell, Tables.e_prob_model[LM][1], error_intra, enc, C, LM, 1, max_decay, lfe); + } + + if (intra == 0) + { + int intra_buf; + EntropyCoder enc_intra_state = new EntropyCoder(); // [porting note] stack variable + int tell_intra; + uint nstart_bytes; + uint nintra_bytes; + uint save_bytes; + int badness2; + byte[] intra_bits = null; + + tell_intra = (int)enc.tell_frac(); + + enc_intra_state.Assign(enc); + + nstart_bytes = enc_start_state.range_bytes(); + nintra_bytes = enc_intra_state.range_bytes(); + intra_buf = enc_intra_state.buf_ptr + (int)nstart_bytes; + save_bytes = nintra_bytes - nstart_bytes; + + if (save_bytes != 0) + { + intra_bits = new byte[(int)save_bytes]; + /* Copy bits from intra bit-stream */ + Array.Copy(enc_intra_state.buf, intra_buf, intra_bits, 0, (int)save_bytes); + } + + enc.Assign(enc_start_state); + + badness2 = quant_coarse_energy_impl(m, start, end, eBands, oldEBands, (int)budget, + (int)tell, Tables.e_prob_model[LM][intra], error, enc, C, LM, 0, max_decay, lfe); + + if (two_pass != 0 && (badness1 < badness2 || (badness1 == badness2 && ((int)enc.tell_frac()) + intra_bias > tell_intra))) + { + enc.Assign(enc_intra_state); + /* Copy intra bits to bit-stream */ + if (intra_bits != null) + { + Array.Copy(intra_bits, 0, enc_intra_state.buf, intra_buf, (int)(nintra_bytes - nstart_bytes)); + } + Array.Copy(oldEBands_intra[0], 0, oldEBands[0], 0, m.nbEBands); + Array.Copy(error_intra[0], 0, error[0], 0, m.nbEBands); + if (C == 2) + { + Array.Copy(oldEBands_intra[1], 0, oldEBands[1], 0, m.nbEBands); + Array.Copy(error_intra[1], 0, error[1], 0, m.nbEBands); + } + intra = 1; + } + } + else + { + Array.Copy(oldEBands_intra[0], 0, oldEBands[0], 0, m.nbEBands); + Array.Copy(error_intra[0], 0, error[0], 0, m.nbEBands); + if (C == 2) + { + Array.Copy(oldEBands_intra[1], 0, oldEBands[1], 0, m.nbEBands); + Array.Copy(error_intra[1], 0, error[1], 0, m.nbEBands); + } + } + + if (intra != 0) + { + delayedIntra = new_distortion; + } + else + { + delayedIntra = Inlines.ADD32(Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15(pred_coef[LM], pred_coef[LM]), delayedIntra), + new_distortion); + } + } + + internal static void quant_fine_energy(CeltMode m, int start, int end, int[][] oldEBands, int[][] error, int[] fine_quant, EntropyCoder enc, int C) + { + int i, c; + + /* Encode finer resolution */ + for (i = start; i < end; i++) + { + int frac = (1 << fine_quant[i]); + if (fine_quant[i] <= 0) + continue; + c = 0; + do + { + int q2; + int offset; + /* Has to be without rounding */ + q2 = (error[c][i] + ((short)(0.5 + (.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/) >> (CeltConstants.DB_SHIFT - fine_quant[i]); + if (q2 > frac - 1) + q2 = frac - 1; + if (q2 < 0) + q2 = 0; + enc.enc_bits((uint)q2, (uint)fine_quant[i]); + offset = Inlines.SUB16( + (Inlines.SHR32( + Inlines.SHL32(q2, CeltConstants.DB_SHIFT) + ((short)(0.5 + (.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/, + fine_quant[i])), + ((short)(0.5 + (.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/); + oldEBands[c][i] += offset; + error[c][i] -= offset; + } while (++c < C); + } + } + + internal static void quant_energy_finalise(CeltMode m, int start, int end, int[][] oldEBands, int[][] error, int[] fine_quant, int[] fine_priority, int bits_left, EntropyCoder enc, int C) + { + int i, prio, c; + + /* Use up the remaining bits */ + for (prio = 0; prio < 2; prio++) + { + for (i = start; i < end && bits_left >= C; i++) + { + if (fine_quant[i] >= CeltConstants.MAX_FINE_BITS || fine_priority[i] != prio) + { + continue; + } + + c = 0; + do + { + int q2; + int offset; + q2 = error[c][i] < 0 ? 0 : 1; + enc.enc_bits((uint)q2, 1); + offset = Inlines.SHR16((Inlines.SHL16((q2), CeltConstants.DB_SHIFT) - ((short)(0.5 + (.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/), fine_quant[i] + 1); + oldEBands[c][i] += offset; + bits_left--; + } while (++c < C); + } + } + } + + internal static void unquant_coarse_energy(CeltMode m, int start, int end, int[] oldEBands, int intra, EntropyCoder dec, int C, int LM) + { + byte[] prob_model = Tables.e_prob_model[LM][intra]; + int i, c; + int[] prev = { 0, 0 }; + int coef; + int beta; + int budget; + int tell; + + if (intra != 0) + { + coef = 0; + beta = beta_intra; + } + else { + beta = beta_coef[LM]; + coef = pred_coef[LM]; + } + + budget = (int)dec.storage * 8; + + /* Decode at a fixed coarse resolution */ + for (i = start; i < end; i++) + { + c = 0; + do + { + int qi; + int q; + int tmp; + /* It would be better to express this invariant as a + test on C at function entry, but that isn't enough + to make the static analyzer happy. */ + Inlines.OpusAssert(c < 2); + tell = dec.tell(); + if (budget - tell >= 15) + { + int pi; + pi = 2 * Inlines.IMIN(i, 20); + qi = Laplace.ec_laplace_decode(dec, + (uint)prob_model[pi] << 7, prob_model[pi + 1] << 6); + } + else if (budget - tell >= 2) + { + qi = dec.dec_icdf(small_energy_icdf, 2); + qi = (qi >> 1) ^ -(qi & 1); + } + else if (budget - tell >= 1) + { + qi = 0 - dec.dec_bit_logp(1); + } + else + { + qi = -1; + } + q = (int)Inlines.SHL32(qi, CeltConstants.DB_SHIFT); // opus bug: useless extend32 + + oldEBands[i + c * m.nbEBands] = Inlines.MAX16((0 - ((short)(0.5 + (9.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(9.0f, CeltConstants.DB_SHIFT)*/), oldEBands[i + c * m.nbEBands]); + tmp = Inlines.PSHR32(Inlines.MULT16_16(coef, oldEBands[i + c * m.nbEBands]), 8) + prev[c] + Inlines.SHL32(q, 7); + tmp = Inlines.MAX32(-((int)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT + 7))))/*Inlines.QCONST32(28.0f, CeltConstants.DB_SHIFT + 7)*/, tmp); + oldEBands[i + c * m.nbEBands] = (Inlines.PSHR32(tmp, 7)); + prev[c] = prev[c] + Inlines.SHL32(q, 7) - Inlines.MULT16_16(beta, Inlines.PSHR32(q, 8)); + } while (++c < C); + } + } + + internal static void unquant_fine_energy(CeltMode m, int start, int end, int[] oldEBands, int[] fine_quant, EntropyCoder dec, int C) + { + int i, c; + /* Decode finer resolution */ + for (i = start; i < end; i++) + { + if (fine_quant[i] <= 0) + continue; + c = 0; + do + { + int q2; + int offset; + q2 = (int)dec.dec_bits((uint)fine_quant[i]); + offset = Inlines.SUB16((Inlines.SHR32( + Inlines.SHL32(q2, CeltConstants.DB_SHIFT) + + ((short)(0.5 + (.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/, fine_quant[i])), + ((short)(0.5 + (.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/); // opus bug: unnecessary extend32 + oldEBands[i + c * m.nbEBands] += offset; + } while (++c < C); + } + } + + internal static void unquant_energy_finalise(CeltMode m, int start, int end, int[] oldEBands, int[] fine_quant, int[] fine_priority, int bits_left, EntropyCoder dec, int C) + { + int i, prio, c; + + /* Use up the remaining bits */ + for (prio = 0; prio < 2; prio++) + { + for (i = start; i < end && bits_left >= C; i++) + { + if (fine_quant[i] >= CeltConstants.MAX_FINE_BITS || fine_priority[i] != prio) + continue; + c = 0; + do + { + int q2; + int offset; + q2 = (int)dec.dec_bits(1); + offset = Inlines.SHR16((Inlines.SHL16((q2), CeltConstants.DB_SHIFT) - ((short)(0.5 + (.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/), fine_quant[i] + 1); + oldEBands[i + c * m.nbEBands] += offset; + bits_left--; + } while (++c < C); + } + } + } + + /// + /// non-pointer case + /// + /// + /// + /// + /// + /// + /// + internal static void amp2Log2(CeltMode m, int effEnd, int end, + int[][] bandE, int[][] bandLogE, int C) + { + int c, i; + c = 0; + do + { + for (i = 0; i < effEnd; i++) + { + bandLogE[c][i] = + (Inlines.celt_log2(Inlines.SHL32(bandE[c][i], 2)) + - Inlines.SHL16((int)Tables.eMeans[i], 6)); + } + for (i = effEnd; i < end; i++) + { + bandLogE[c][i] = (0 - ((short)(0.5 + (14.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(14.0f, CeltConstants.DB_SHIFT)*/); + } + } while (++c < C); + } + + /// + /// only needed in one place + /// + /// + /// + /// + /// + /// + /// + internal static void amp2Log2(CeltMode m, int effEnd, int end, + int[] bandE, int[] bandLogE, int bandLogE_ptr, int C) + { + int c, i; + c = 0; + do + { + for (i = 0; i < effEnd; i++) + { + bandLogE[bandLogE_ptr + (c * m.nbEBands) + i] = + (Inlines.celt_log2(Inlines.SHL32(bandE[i + c * m.nbEBands], 2)) + - Inlines.SHL16((int)Tables.eMeans[i], 6)); + } + for (i = effEnd; i < end; i++) + { + bandLogE[bandLogE_ptr + (c * m.nbEBands) + i] = (0 - ((short)(0.5 + (14.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(14.0f, CeltConstants.DB_SHIFT)*/); + } + } while (++c < C); + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/Rate.cs b/Libraries/Concentus/CSharp/Concentus/Celt/Rate.cs new file mode 100644 index 000000000..2115efac5 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/Rate.cs @@ -0,0 +1,519 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Celt +{ + using Concentus.Celt.Enums; + using Concentus.Celt.Structs; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using System.Diagnostics; + + internal static class Rate + { + private static readonly byte[] LOG2_FRAC_TABLE ={ + 0, + 8,13, + 16,19,21,23, + 24,26,27,28,29,30,31,32, + 32,33,34,34,35,36,36,37,37 + }; + + private const int ALLOC_STEPS = 6; + + internal static int get_pulses(int i) + { + return i < 8 ? i : (8 + (i & 7)) << ((i >> 3) - 1); + } + + internal static int bits2pulses(CeltMode m, int band, int LM, int bits) + { + int i; + int lo, hi; + + LM++; + byte[] cache = m.cache.bits; + int cache_ptr = m.cache.index[LM * m.nbEBands + band]; + + lo = 0; + hi = cache[cache_ptr]; + bits--; + for (i = 0; i < CeltConstants.LOG_MAX_PSEUDO; i++) + { + int mid = (lo + hi + 1) >> 1; + /* OPT: Make sure this is implemented with a conditional move */ + if ((int)cache[cache_ptr + mid] >= bits) + hi = mid; + else + lo = mid; + } + if (bits - (lo == 0 ? -1 : (int)cache[cache_ptr + lo]) <= (int)cache[cache_ptr + hi] - bits) + return lo; + else + return hi; + } + + internal static int pulses2bits(CeltMode m, int band, int LM, int pulses) + { + LM++; + return pulses == 0 ? 0 : m.cache.bits[m.cache.index[LM * m.nbEBands + band] + pulses] + 1; + } + + internal static int interp_bits2pulses(CeltMode m, int start, int end, int skip_start, + int[] bits1, int[] bits2, int[] thresh, int[] cap, int total, out int _balance, + int skip_rsv, ref int intensity, int intensity_rsv, ref int dual_stereo, int dual_stereo_rsv, int[] bits, + int[] ebits, int[] fine_priority, int C, int LM, EntropyCoder ec, int encode, int prev, int signalBandwidth) + { + int psum; + int lo, hi; + int i, j; + int logM; + int stereo; + int codedBands = -1; + int alloc_floor; + int left, percoeff; + int done; + int balance; + + + alloc_floor = C << EntropyCoder.BITRES; + stereo = C > 1 ? 1 : 0; + + logM = LM << EntropyCoder.BITRES; + lo = 0; + hi = 1 << ALLOC_STEPS; + for (i = 0; i < ALLOC_STEPS; i++) + { + int mid = (lo + hi) >> 1; + psum = 0; + done = 0; + for (j = end; j-- > start;) + { + int tmp = bits1[j] + (mid * (int)bits2[j] >> ALLOC_STEPS); + if (tmp >= thresh[j] || done != 0) + { + done = 1; + /* Don't allocate more than we can actually use */ + psum += Inlines.IMIN(tmp, cap[j]); + } + else + { + if (tmp >= alloc_floor) + psum += alloc_floor; + } + } + if (psum > total) + hi = mid; + else + lo = mid; + } + psum = 0; + /*printf ("interp bisection gave %d\n", lo);*/ + done = 0; + for (j = end; j-- > start;) + { + int tmp = bits1[j] + (lo * bits2[j] >> ALLOC_STEPS); + if (tmp < thresh[j] && done == 0) + { + if (tmp >= alloc_floor) + tmp = alloc_floor; + else + tmp = 0; + } + else + { + done = 1; + } + + /* Don't allocate more than we can actually use */ + tmp = Inlines.IMIN(tmp, cap[j]); + bits[j] = tmp; + psum += tmp; + } + + /* Decide which bands to skip, working backwards from the end. */ + for (codedBands = end; ; codedBands--) + { + int band_width; + int band_bits; + int rem; + j = codedBands - 1; + /* Never skip the first band, nor a band that has been boosted by + dynalloc. + In the first case, we'd be coding a bit to signal we're going to waste + all the other bits. + In the second case, we'd be coding a bit to redistribute all the bits + we just signaled should be cocentrated in this band. */ + if (j <= skip_start) + { + /* Give the bit we reserved to end skipping back. */ + total += skip_rsv; + break; + } + + /*Figure out how many left-over bits we would be adding to this band. + This can include bits we've stolen back from higher, skipped bands.*/ + left = total - psum; + percoeff = Inlines.celt_udiv(left, m.eBands[codedBands] - m.eBands[start]); + left -= (m.eBands[codedBands] - m.eBands[start]) * percoeff; + rem = Inlines.IMAX(left - (m.eBands[j] - m.eBands[start]), 0); + band_width = m.eBands[codedBands] - m.eBands[j]; + band_bits = (int)(bits[j] + percoeff * band_width + rem); + /*Only code a skip decision if we're above the threshold for this band. + Otherwise it is force-skipped. + This ensures that we have enough bits to code the skip flag.*/ + if (band_bits >= Inlines.IMAX(thresh[j], alloc_floor + (1 << EntropyCoder.BITRES))) + { + if (encode != 0) + { + /*This if() block is the only part of the allocation function that + is not a mandatory part of the bitstream: any bands we choose to + skip here must be explicitly signaled.*/ + /*Choose a threshold with some hysteresis to keep bands from + fluctuating in and out.*/ +#if FUZZING + if ((new Random().Next() & 0x1) == 0) +#else + if (codedBands <= start + 2 || (band_bits > ((j < prev ? 7 : 9) * band_width << LM << EntropyCoder.BITRES) >> 4 && j <= signalBandwidth)) +#endif + { + ec.enc_bit_logp(1, 1); + break; + } + ec.enc_bit_logp(0, 1); + } + else if (ec.dec_bit_logp(1) != 0) + { + break; + } + /*We used a bit to skip this band.*/ + psum += 1 << EntropyCoder.BITRES; + band_bits -= 1 << EntropyCoder.BITRES; + } + /*Reclaim the bits originally allocated to this band.*/ + psum -= bits[j] + intensity_rsv; + if (intensity_rsv > 0) + intensity_rsv = LOG2_FRAC_TABLE[j - start]; + psum += intensity_rsv; + if (band_bits >= alloc_floor) + { + /*If we have enough for a fine energy bit per channel, use it.*/ + psum += alloc_floor; + bits[j] = alloc_floor; + } + else { + /*Otherwise this band gets nothing at all.*/ + bits[j] = 0; + } + } + + Inlines.OpusAssert(codedBands > start); + /* Code the intensity and dual stereo parameters. */ + if (intensity_rsv > 0) + { + if (encode != 0) + { + intensity = Inlines.IMIN(intensity, codedBands); + ec.enc_uint((uint)(intensity - start), (uint)(codedBands + 1 - start)); + } + else + { + intensity = start + (int)ec.dec_uint((uint)(codedBands + 1 - start)); + } + } + else + { + intensity = 0; + } + + if (intensity <= start) + { + total += dual_stereo_rsv; + dual_stereo_rsv = 0; + } + if (dual_stereo_rsv > 0) + { + if (encode != 0) + { + ec.enc_bit_logp(dual_stereo, 1); + } + else + { + dual_stereo = ec.dec_bit_logp(1); + } + } + else + { + dual_stereo = 0; + } + + /* Allocate the remaining bits */ + left = total - psum; + percoeff = Inlines.celt_udiv(left, m.eBands[codedBands] - m.eBands[start]); + left -= (m.eBands[codedBands] - m.eBands[start]) * percoeff; + for (j = start; j < codedBands; j++) + bits[j] += ((int)percoeff * (m.eBands[j + 1] - m.eBands[j])); + for (j = start; j < codedBands; j++) + { + int tmp = (int)Inlines.IMIN(left, m.eBands[j + 1] - m.eBands[j]); + bits[j] += tmp; + left -= tmp; + } + /*for (j=0;j= 0); + N0 = m.eBands[j + 1] - m.eBands[j]; + N = N0 << LM; + bit = (int)bits[j] + balance; + + if (N > 1) + { + excess = Inlines.MAX32(bit - cap[j], 0); + bits[j] = bit - excess; + + /* Compensate for the extra DoF in stereo */ + den = (C * N + ((C == 2 && N > 2 && (dual_stereo == 0) && j < intensity) ? 1 : 0)); + + NClogN = den * (m.logN[j] + logM); + + /* Offset for the number of fine bits by log2(N)/2 + FINE_OFFSET + compared to their "fair share" of total/N */ + offset = (NClogN >> 1) - den * CeltConstants.FINE_OFFSET; + + /* N=2 is the only point that doesn't match the curve */ + if (N == 2) + offset += den << EntropyCoder.BITRES >> 2; + + /* Changing the offset for allocating the second and third + fine energy bit */ + if (bits[j] + offset < den * 2 << EntropyCoder.BITRES) + offset += NClogN >> 2; + else if (bits[j] + offset < den * 3 << EntropyCoder.BITRES) + offset += NClogN >> 3; + + /* Divide with rounding */ + ebits[j] = Inlines.IMAX(0, (bits[j] + offset + (den << (EntropyCoder.BITRES - 1)))); + ebits[j] = Inlines.celt_udiv(ebits[j], den) >> EntropyCoder.BITRES; + + /* Make sure not to bust */ + if (C * ebits[j] > (bits[j] >> EntropyCoder.BITRES)) + ebits[j] = bits[j] >> stereo >> EntropyCoder.BITRES; + + /* More than that is useless because that's about as far as PVQ can go */ + ebits[j] = Inlines.IMIN(ebits[j], CeltConstants.MAX_FINE_BITS); + + /* If we rounded down or capped this band, make it a candidate for the + final fine energy pass */ + fine_priority[j] = (ebits[j] * (den << EntropyCoder.BITRES) >= bits[j] + offset) ? 1 : 0; + + /* Remove the allocated fine bits; the rest are assigned to PVQ */ + bits[j] -= C * ebits[j] << EntropyCoder.BITRES; + + } + else { + /* For N=1, all bits go to fine energy except for a single sign bit */ + excess = Inlines.MAX32(0, bit - (C << EntropyCoder.BITRES)); + bits[j] = bit - excess; + ebits[j] = 0; + fine_priority[j] = 1; + } + + /* Fine energy can't take advantage of the re-balancing in + quant_all_bands(). + Instead, do the re-balancing here.*/ + if (excess > 0) + { + int extra_fine; + int extra_bits; + extra_fine = Inlines.IMIN(excess >> (stereo + EntropyCoder.BITRES), CeltConstants.MAX_FINE_BITS - ebits[j]); + ebits[j] += extra_fine; + extra_bits = extra_fine * C << EntropyCoder.BITRES; + fine_priority[j] = (extra_bits >= excess - balance) ? 1 : 0; + excess -= extra_bits; + } + balance = excess; + + Inlines.OpusAssert(bits[j] >= 0); + Inlines.OpusAssert(ebits[j] >= 0); + } + /* Save any remaining bits over the cap for the rebalancing in + quant_all_bands(). */ + _balance = balance; + + /* The skipped bands use all their bits for fine energy. */ + for (; j < end; j++) + { + ebits[j] = bits[j] >> stereo >> EntropyCoder.BITRES; + Inlines.OpusAssert(C * ebits[j] << EntropyCoder.BITRES == bits[j]); + bits[j] = 0; + fine_priority[j] = (ebits[j] < 1) ? 1 : 0; + } + + return codedBands; + } + + internal static int compute_allocation(CeltMode m, int start, int end, int[] offsets, int[] cap, int alloc_trim, ref int intensity, ref int dual_stereo, + int total, out int balance, int[] pulses, int[] ebits, int[] fine_priority, int C, int LM, EntropyCoder ec, int encode, int prev, int signalBandwidth) + { + int lo, hi, len, j; + int codedBands; + int skip_start; + int skip_rsv; + int intensity_rsv; + int dual_stereo_rsv; + + total = Inlines.IMAX(total, 0); + len = m.nbEBands; + skip_start = start; + /* Reserve a bit to signal the end of manually skipped bands. */ + skip_rsv = total >= 1 << EntropyCoder.BITRES ? 1 << EntropyCoder.BITRES : 0; + total -= skip_rsv; + /* Reserve bits for the intensity and dual stereo parameters. */ + intensity_rsv = dual_stereo_rsv = 0; + if (C == 2) + { + intensity_rsv = LOG2_FRAC_TABLE[end - start]; + if (intensity_rsv > total) + intensity_rsv = 0; + else + { + total -= intensity_rsv; + dual_stereo_rsv = total >= 1 << EntropyCoder.BITRES ? 1 << EntropyCoder.BITRES : 0; + total -= dual_stereo_rsv; + } + } + + int[] bits1 = new int[len]; + int[] bits2 = new int[len]; + int[] thresh = new int[len]; + int[] trim_offset = new int[len]; + + for (j = start; j < end; j++) + { + /* Below this threshold, we're sure not to allocate any PVQ bits */ + thresh[j] = Inlines.IMAX((C) << EntropyCoder.BITRES, (3 * (m.eBands[j + 1] - m.eBands[j]) << LM << EntropyCoder.BITRES) >> 4); + /* Tilt of the allocation curve */ + trim_offset[j] = C * (m.eBands[j + 1] - m.eBands[j]) * (alloc_trim - 5 - LM) * (end - j - 1) + * (1 << (LM + EntropyCoder.BITRES)) >> 6; + /* Giving less resolution to single-coefficient bands because they get + more benefit from having one coarse value per coefficient*/ + if ((m.eBands[j + 1] - m.eBands[j]) << LM == 1) + trim_offset[j] -= C << EntropyCoder.BITRES; + } + lo = 1; + hi = m.nbAllocVectors - 1; + do + { + int done = 0; + int psum = 0; + int mid = (lo + hi) >> 1; + for (j = end; j-- > start;) + { + int bitsj; + int N = m.eBands[j + 1] - m.eBands[j]; + bitsj = C * N * m.allocVectors[mid * len + j] << LM >> 2; + + if (bitsj > 0) + { + bitsj = Inlines.IMAX(0, bitsj + trim_offset[j]); + } + + bitsj += offsets[j]; + + if (bitsj >= thresh[j] || done != 0) + { + done = 1; + /* Don't allocate more than we can actually use */ + psum += Inlines.IMIN(bitsj, cap[j]); + } + else + { + if (bitsj >= C << EntropyCoder.BITRES) + { + psum += C << EntropyCoder.BITRES; + } + } + } + if (psum > total) + { + hi = mid - 1; + } + else + { + lo = mid + 1; + } + /*printf ("lo = %d, hi = %d\n", lo, hi);*/ + } while (lo <= hi); + + hi = lo--; + /*printf ("interp between %d and %d\n", lo, hi);*/ + + for (j = start; j < end; j++) + { + int bits1j, bits2j; + int N = m.eBands[j + 1] - m.eBands[j]; + bits1j = C * N * m.allocVectors[lo * len + j] << LM >> 2; + bits2j = hi >= m.nbAllocVectors ? + cap[j] : C * N * m.allocVectors[hi * len + j] << LM >> 2; + if (bits1j > 0) + bits1j = Inlines.IMAX(0, bits1j + trim_offset[j]); + if (bits2j > 0) + bits2j = Inlines.IMAX(0, bits2j + trim_offset[j]); + if (lo > 0) + bits1j += offsets[j]; + bits2j += offsets[j]; + if (offsets[j] > 0) + skip_start = j; + bits2j = Inlines.IMAX(0, bits2j - bits1j); + bits1[j] = bits1j; + bits2[j] = bits2j; + } + + codedBands = interp_bits2pulses(m, start, end, skip_start, bits1, bits2, thresh, cap, + total, out balance, skip_rsv, ref intensity, intensity_rsv, ref dual_stereo, dual_stereo_rsv, + pulses, ebits, fine_priority, C, LM, ec, encode, prev, signalBandwidth); + + return codedBands; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/Structs/AnalysisInfo.cs b/Libraries/Concentus/CSharp/Concentus/Celt/Structs/AnalysisInfo.cs new file mode 100644 index 000000000..09462a4f9 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/Structs/AnalysisInfo.cs @@ -0,0 +1,74 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Celt.Structs +{ + internal class AnalysisInfo + { + internal int valid = 0; + internal float tonality = 0; + internal float tonality_slope = 0; + internal float noisiness = 0; + internal float activity = 0; + internal float music_prob = 0; + internal int bandwidth = 0; + + internal AnalysisInfo() + { + } + + internal void Assign(AnalysisInfo other) + { + this.valid = other.valid; + this.tonality = other.tonality; + this.tonality_slope = other.tonality_slope; + this.noisiness = other.noisiness; + this.activity = other.activity; + this.music_prob = other.music_prob; + this.bandwidth = other.bandwidth; + } + + internal void Reset() + { + valid = 0; + tonality = 0; + tonality_slope = 0; + noisiness = 0; + activity = 0; + music_prob = 0; + bandwidth = 0; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/Structs/CELTDecoder.cs b/Libraries/Concentus/CSharp/Concentus/Celt/Structs/CELTDecoder.cs new file mode 100644 index 000000000..8a7788870 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/Structs/CELTDecoder.cs @@ -0,0 +1,873 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Celt.Structs +{ + using Concentus.Celt.Enums; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Enums; + using System; + + /// + /// Decoder state + /// + internal class CeltDecoder + { + internal CeltMode mode = null; + internal int overlap = 0; + internal int channels = 0; + internal int stream_channels = 0; + + internal int downsample = 0; + internal int start = 0; + internal int end = 0; + internal int signalling = 0; + + /* Everything beyond this point gets cleared on a reset */ + internal uint rng = 0; + internal int error = 0; + internal int last_pitch_index = 0; + internal int loss_count = 0; + internal int postfilter_period = 0; + internal int postfilter_period_old = 0; + internal int postfilter_gain = 0; + internal int postfilter_gain_old = 0; + internal int postfilter_tapset = 0; + internal int postfilter_tapset_old = 0; + + internal readonly int[] preemph_memD = new int[2]; + + /// + /// Scratch space used by the decoder. It is actually a variable-sized + /// field that resulted in a variable-sized struct. There are 6 distinct regions inside. + /// I have laid them out into separate variables here, + /// but these were the original definitions: + /// val32 decode_mem[], Size = channels*(DECODE_BUFFER_SIZE+mode.overlap) + /// val16 lpc[], Size = channels*LPC_ORDER + /// val16 oldEBands[], Size = 2*mode.nbEBands + /// val16 oldLogE[], Size = 2*mode.nbEBands + /// val16 oldLogE2[], Size = 2*mode.nbEBands + /// val16 backgroundLogE[], Size = 2*mode.nbEBands + /// + internal int[][] decode_mem = null; + internal int[][] lpc = null; // Porting note: Split two-part array into separate arrays (one per channel) + internal int[] oldEBands = null; + internal int[] oldLogE = null; + internal int[] oldLogE2 = null; + internal int[] backgroundLogE = null; + + private void Reset() + { + mode = null; + overlap = 0; + channels = 0; + stream_channels = 0; + downsample = 0; + start = 0; + end = 0; + signalling = 0; + PartialReset(); + } + + private void PartialReset() + { + rng = 0; + error = 0; + last_pitch_index = 0; + loss_count = 0; + postfilter_period = 0; + postfilter_period_old = 0; + postfilter_gain = 0; + postfilter_gain_old = 0; + postfilter_tapset = 0; + postfilter_tapset_old = 0; + Arrays.MemSetInt(preemph_memD, 0, 2); + decode_mem = null; + lpc = null; + oldEBands = null; + oldLogE = null; + oldLogE2 = null; + backgroundLogE = null; + } + + #region API functions + + internal void ResetState() + { + int i; + + this.PartialReset(); + + // We have to reconstitute the dynamic buffers here. fixme: this could be better implemented + this.decode_mem = new int[this.channels][]; + this.lpc = new int[this.channels][]; + for (int c = 0; c < this.channels; c++) + { + this.decode_mem[c] = new int[CeltConstants.DECODE_BUFFER_SIZE + this.mode.overlap]; + this.lpc[c] = new int[CeltConstants.LPC_ORDER]; + } + this.oldEBands = new int[2 * this.mode.nbEBands]; + this.oldLogE = new int[2 * this.mode.nbEBands]; + this.oldLogE2 = new int[2 * this.mode.nbEBands]; + this.backgroundLogE = new int[2 * this.mode.nbEBands]; + + for (i = 0; i < 2 * this.mode.nbEBands; i++) + this.oldLogE[i] = this.oldLogE2[i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/; + } + + internal int celt_decoder_init(int sampling_rate, int channels) + { + int ret; + ret = this.opus_custom_decoder_init(CeltMode.mode48000_960_120, channels); + if (ret != OpusError.OPUS_OK) + return ret; + this.downsample = CeltCommon.resampling_factor(sampling_rate); + if (this.downsample == 0) + return OpusError.OPUS_BAD_ARG; + else + return OpusError.OPUS_OK; + } + + private int opus_custom_decoder_init(CeltMode mode, int channels) + { + if (channels < 0 || channels > 2) + return OpusError.OPUS_BAD_ARG; + + if (this == null) + return OpusError.OPUS_ALLOC_FAIL; + + this.Reset(); + + this.mode = mode; + this.overlap = mode.overlap; + this.stream_channels = this.channels = channels; + + this.downsample = 1; + this.start = 0; + this.end = this.mode.effEBands; + this.signalling = 1; + + this.loss_count = 0; + + //this.decode_mem = new int[channels * (CeltConstants.DECODE_BUFFER_SIZE + mode.overlap)); + //this.lpc = new int[channels * CeltConstants.LPC_ORDER); + //this.oldEBands = new int[2 * mode.nbEBands); + //this.oldLogE = new int[2 * mode.nbEBands); + //this.oldLogE2 = new int[2 * mode.nbEBands); + //this.backgroundLogE = new int[2 * mode.nbEBands); + + this.ResetState(); + + return OpusError.OPUS_OK; + } + + internal void celt_decode_lost(int N, int LM) + { + int c; + int i; + int C = this.channels; + int[][] out_syn = new int[2][]; + int[] out_syn_ptrs = new int[2]; + CeltMode mode; + int nbEBands; + int overlap; + int noise_based; + short[] eBands; + + mode = this.mode; + nbEBands = mode.nbEBands; + overlap = mode.overlap; + eBands = mode.eBands; + + c = 0; do + { + out_syn[c] = this.decode_mem[c]; + out_syn_ptrs[c] = CeltConstants.DECODE_BUFFER_SIZE - N; + } while (++c < C); + + noise_based = (loss_count >= 5 || start != 0) ? 1 : 0; + if (noise_based != 0) + { + /* Noise-based PLC/CNG */ + int[][] X; + uint seed; + int end; + int effEnd; + int decay; + end = this.end; + effEnd = Inlines.IMAX(start, Inlines.IMIN(end, mode.effEBands)); + + X = Arrays.InitTwoDimensionalArray(C, N); /*< Interleaved normalised MDCTs */ + + /* Energy decay */ + decay = loss_count == 0 ? ((short)(0.5 + (1.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.5f, CeltConstants.DB_SHIFT)*/ : ((short)(0.5 + (0.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.5f, CeltConstants.DB_SHIFT)*/; + c = 0; do + { + for (i = start; i < end; i++) + this.oldEBands[c * nbEBands + i] = Inlines.MAX16(backgroundLogE[c * nbEBands + i], this.oldEBands[c * nbEBands + i] - decay); + } while (++c < C); + seed = this.rng; + for (c = 0; c < C; c++) + { + for (i = start; i < effEnd; i++) + { + int j; + int boffs; + int blen; + boffs = (eBands[i] << LM); + blen = (eBands[i + 1] - eBands[i]) << LM; + for (j = 0; j < blen; j++) + { + seed = Bands.celt_lcg_rand(seed); + X[c][boffs + j] = (unchecked((int)seed) >> 20); + } + + VQ.renormalise_vector(X[c], 0, blen, CeltConstants.Q15ONE); + } + } + this.rng = seed; + + c = 0; + do + { + Arrays.MemMoveInt(this.decode_mem[c], N, 0, CeltConstants.DECODE_BUFFER_SIZE - N + (overlap >> 1)); + } while (++c < C); + + CeltCommon.celt_synthesis(mode, X, out_syn, out_syn_ptrs, this.oldEBands, start, effEnd, C, C, 0, LM, this.downsample, 0); + } + else + { + /* Pitch-based PLC */ + int[] window; + int fade = CeltConstants.Q15ONE; + int pitch_index; + int[] etmp; + int[] exc; + + if (loss_count == 0) + { + this.last_pitch_index = pitch_index = CeltCommon.celt_plc_pitch_search(this.decode_mem, C); + } + else { + pitch_index = this.last_pitch_index; + fade = ((short)(0.5 + (.8f) * (((int)1) << (15))))/*Inlines.QCONST16(.8f, 15)*/; + } + + etmp = new int[overlap]; + exc = new int[CeltConstants.MAX_PERIOD]; + window = mode.window; + c = 0; do + { + int decay; + int attenuation; + int S1 = 0; + int[] buf; + int extrapolation_offset; + int extrapolation_len; + int exc_length; + int j; + + buf = this.decode_mem[c]; + for (i = 0; i < CeltConstants.MAX_PERIOD; i++) + { + exc[i] = Inlines.ROUND16(buf[CeltConstants.DECODE_BUFFER_SIZE - CeltConstants.MAX_PERIOD + i], CeltConstants.SIG_SHIFT); + } + + if (loss_count == 0) + { + int[] ac = new int[CeltConstants.LPC_ORDER + 1]; + /* Compute LPC coefficients for the last MAX_PERIOD samples before + the first loss so we can work in the excitation-filter domain. */ + Autocorrelation._celt_autocorr(exc, ac, window, overlap, + CeltConstants.LPC_ORDER, CeltConstants.MAX_PERIOD); + /* Add a noise floor of -40 dB. */ + ac[0] += Inlines.SHR32(ac[0], 13); + /* Use lag windowing to stabilize the Levinson-Durbin recursion. */ + for (i = 1; i <= CeltConstants.LPC_ORDER; i++) + { + /*ac[i] *= exp(-.5*(2*M_PI*.002*i)*(2*M_PI*.002*i));*/ + ac[i] -= Inlines.MULT16_32_Q15(2 * i * i, ac[i]); + } + CeltLPC.celt_lpc(this.lpc[c], ac, CeltConstants.LPC_ORDER); + } + /* We want the excitation for 2 pitch periods in order to look for a + decaying signal, but we can't get more than MAX_PERIOD. */ + exc_length = Inlines.IMIN(2 * pitch_index, CeltConstants.MAX_PERIOD); + /* Initialize the LPC history with the samples just before the start + of the region for which we're computing the excitation. */ + { + int[] lpc_mem = new int[CeltConstants.LPC_ORDER]; + for (i = 0; i < CeltConstants.LPC_ORDER; i++) + { + lpc_mem[i] = + Inlines.ROUND16(buf[CeltConstants.DECODE_BUFFER_SIZE - exc_length - 1 - i], CeltConstants.SIG_SHIFT); + } + + /* Compute the excitation for exc_length samples before the loss. */ +#if UNSAFE + unsafe + { + fixed (int* pexc_base = exc, lpc = this.lpc[c]) + { + int* pexc = pexc_base + (CeltConstants.MAX_PERIOD - exc_length); + Kernels.celt_fir(pexc, lpc, pexc, exc_length, CeltConstants.LPC_ORDER, lpc_mem); + } + } +#else + Kernels.celt_fir(exc, (CeltConstants.MAX_PERIOD - exc_length), this.lpc[c], 0, + exc, (CeltConstants.MAX_PERIOD - exc_length), exc_length, CeltConstants.LPC_ORDER, lpc_mem); +#endif + } + + /* Check if the waveform is decaying, and if so how fast. + We do this to avoid adding energy when concealing in a segment + with decaying energy. */ + { + int E1 = 1, E2 = 1; + int decay_length; + int shift = Inlines.IMAX(0, 2 * Inlines.celt_zlog2(Inlines.celt_maxabs16(exc, (CeltConstants.MAX_PERIOD - exc_length), exc_length)) - 20); + decay_length = exc_length >> 1; + for (i = 0; i < decay_length; i++) + { + int e; + e = exc[CeltConstants.MAX_PERIOD - decay_length + i]; + E1 += Inlines.SHR32(Inlines.MULT16_16(e, e), shift); + e = exc[CeltConstants.MAX_PERIOD - 2 * decay_length + i]; + E2 += Inlines.SHR32(Inlines.MULT16_16(e, e), shift); + } + E1 = Inlines.MIN32(E1, E2); + decay = Inlines.celt_sqrt(Inlines.frac_div32(Inlines.SHR32(E1, 1), E2)); + } + + /* Move the decoder memory one frame to the left to give us room to + add the data for the new frame. We ignore the overlap that extends + past the end of the buffer, because we aren't going to use it. */ + Arrays.MemMoveInt(buf, N, 0, CeltConstants.DECODE_BUFFER_SIZE - N); + + /* Extrapolate from the end of the excitation with a period of + "pitch_index", scaling down each period by an additional factor of + "decay". */ + extrapolation_offset = CeltConstants.MAX_PERIOD - pitch_index; + /* We need to extrapolate enough samples to cover a complete MDCT + window (including overlap/2 samples on both sides). */ + extrapolation_len = N + overlap; + /* We also apply fading if this is not the first loss. */ + attenuation = Inlines.MULT16_16_Q15(fade, decay); + for (i = j = 0; i < extrapolation_len; i++, j++) + { + int tmp; + if (j >= pitch_index) + { + j -= pitch_index; + attenuation = Inlines.MULT16_16_Q15(attenuation, decay); + } + buf[CeltConstants.DECODE_BUFFER_SIZE - N + i] = + Inlines.SHL32((Inlines.MULT16_16_Q15(attenuation, + exc[extrapolation_offset + j])), CeltConstants.SIG_SHIFT); + /* Compute the energy of the previously decoded signal whose + excitation we're copying. */ + tmp = Inlines.ROUND16( + buf[CeltConstants.DECODE_BUFFER_SIZE - CeltConstants.MAX_PERIOD - N + extrapolation_offset + j], + CeltConstants.SIG_SHIFT); + S1 += Inlines.SHR32(Inlines.MULT16_16(tmp, tmp), 8); + } + + { + int[] lpc_mem = new int[CeltConstants.LPC_ORDER]; + /* Copy the last decoded samples (prior to the overlap region) to + synthesis filter memory so we can have a continuous signal. */ + for (i = 0; i < CeltConstants.LPC_ORDER; i++) + lpc_mem[i] = Inlines.ROUND16(buf[CeltConstants.DECODE_BUFFER_SIZE - N - 1 - i], CeltConstants.SIG_SHIFT); + /* Apply the synthesis filter to convert the excitation back into + the signal domain. */ + CeltLPC.celt_iir(buf, CeltConstants.DECODE_BUFFER_SIZE - N, this.lpc[c], + buf, CeltConstants.DECODE_BUFFER_SIZE - N, extrapolation_len, CeltConstants.LPC_ORDER, + lpc_mem); + } + + /* Check if the synthesis energy is higher than expected, which can + happen with the signal changes during our window. If so, + attenuate. */ + { + int S2 = 0; + for (i = 0; i < extrapolation_len; i++) + { + int tmp = Inlines.ROUND16(buf[CeltConstants.DECODE_BUFFER_SIZE - N + i], CeltConstants.SIG_SHIFT); + S2 += Inlines.SHR32(Inlines.MULT16_16(tmp, tmp), 8); + } + /* This checks for an "explosion" in the synthesis. */ + if (!(S1 > Inlines.SHR32(S2, 2))) + { + for (i = 0; i < extrapolation_len; i++) + buf[CeltConstants.DECODE_BUFFER_SIZE - N + i] = 0; + } + else if (S1 < S2) + { + int ratio = Inlines.celt_sqrt(Inlines.frac_div32(Inlines.SHR32(S1, 1) + 1, S2 + 1)); + for (i = 0; i < overlap; i++) + { + int tmp_g = CeltConstants.Q15ONE + - Inlines.MULT16_16_Q15(window[i], CeltConstants.Q15ONE - ratio); + buf[CeltConstants.DECODE_BUFFER_SIZE - N + i] = + Inlines.MULT16_32_Q15(tmp_g, buf[CeltConstants.DECODE_BUFFER_SIZE - N + i]); + } + for (i = overlap; i < extrapolation_len; i++) + { + buf[CeltConstants.DECODE_BUFFER_SIZE - N + i] = + Inlines.MULT16_32_Q15(ratio, buf[CeltConstants.DECODE_BUFFER_SIZE - N + i]); + } + } + } + + /* Apply the pre-filter to the MDCT overlap for the next frame because + the post-filter will be re-applied in the decoder after the MDCT + overlap. */ + CeltCommon.comb_filter(etmp, 0, buf, CeltConstants.DECODE_BUFFER_SIZE, + this.postfilter_period, this.postfilter_period, overlap, + -this.postfilter_gain, -this.postfilter_gain, + this.postfilter_tapset, this.postfilter_tapset, null, 0); + + /* Simulate TDAC on the concealed audio so that it blends with the + MDCT of the next frame. */ + for (i = 0; i < overlap / 2; i++) + { + buf[CeltConstants.DECODE_BUFFER_SIZE + i] = + Inlines.MULT16_32_Q15(window[i], etmp[overlap - 1 - i]) + + Inlines.MULT16_32_Q15(window[overlap - i - 1], etmp[i]); + } + } while (++c < C); + } + + this.loss_count = loss_count + 1; + } + + internal int celt_decode_with_ec(byte[] data, int data_ptr, + int len, short[] pcm, int pcm_ptr, int frame_size, EntropyCoder dec, int accum) + { + int c, i, N; + int spread_decision; + int bits; + int[][] X; + int[] fine_quant; + int[] pulses; + int[] cap; + int[] offsets; + int[] fine_priority; + int[] tf_res; + byte[] collapse_masks; + int[][] out_syn = new int[2][]; + int[] out_syn_ptrs = new int[2]; + int[] oldBandE, oldLogE, oldLogE2, backgroundLogE; + + int shortBlocks; + int isTransient; + int intra_ener; + int CC = this.channels; + int LM, M; + int start; + int end; + int effEnd; + int codedBands; + int alloc_trim; + int postfilter_pitch; + int postfilter_gain; + int intensity = 0; + int dual_stereo = 0; + int total_bits; + int balance; + int tell; + int dynalloc_logp; + int postfilter_tapset; + int anti_collapse_rsv; + int anti_collapse_on = 0; + int silence; + int C = this.stream_channels; + CeltMode mode; // porting note: pointer + int nbEBands; + int overlap; + short[] eBands; + + mode = this.mode; + nbEBands = mode.nbEBands; + overlap = mode.overlap; + eBands = mode.eBands; + start = this.start; + end = this.end; + frame_size *= this.downsample; + + oldBandE = this.oldEBands; + oldLogE = this.oldLogE; + oldLogE2 = this.oldLogE2; + backgroundLogE = this.backgroundLogE; + + { + for (LM = 0; LM <= mode.maxLM; LM++) + if (mode.shortMdctSize << LM == frame_size) + break; + if (LM > mode.maxLM) + return OpusError.OPUS_BAD_ARG; + } + M = 1 << LM; + + if (len < 0 || len > 1275 || pcm == null) + return OpusError.OPUS_BAD_ARG; + + N = M * mode.shortMdctSize; + c = 0; do + { + out_syn[c] = this.decode_mem[c]; + out_syn_ptrs[c] = CeltConstants.DECODE_BUFFER_SIZE - N; + } while (++c < CC); + + effEnd = end; + if (effEnd > mode.effEBands) + effEnd = mode.effEBands; + + if (data == null || len <= 1) + { + this.celt_decode_lost(N, LM); + CeltCommon.deemphasis(out_syn, out_syn_ptrs, pcm, pcm_ptr, N, CC, this.downsample, mode.preemph, this.preemph_memD, accum); + + return frame_size / this.downsample; + } + + if (dec == null) + { + // If no entropy decoder was passed into this function, we need to create + // a new one here for local use only. It only exists in this function scope. + dec = new EntropyCoder(); + dec.dec_init(data, data_ptr, (uint)len); + } + + if (C == 1) + { + for (i = 0; i < nbEBands; i++) + oldBandE[i] = Inlines.MAX16(oldBandE[i], oldBandE[nbEBands + i]); + } + + total_bits = len * 8; + tell = dec.tell(); + + if (tell >= total_bits) + silence = 1; + else if (tell == 1) + silence = dec.dec_bit_logp(15); + else + silence = 0; + + if (silence != 0) + { + /* Pretend we've read all the remaining bits */ + tell = len * 8; + dec.nbits_total += tell - dec.tell(); + } + + postfilter_gain = 0; + postfilter_pitch = 0; + postfilter_tapset = 0; + if (start == 0 && tell + 16 <= total_bits) + { + if (dec.dec_bit_logp(1) != 0) + { + int qg, octave; + octave = (int)dec.dec_uint(6); + postfilter_pitch = (16 << octave) + (int)dec.dec_bits(4 + (uint)octave) - 1; + qg = (int)dec.dec_bits(3); + if (dec.tell() + 2 <= total_bits) + postfilter_tapset = dec.dec_icdf(Tables.tapset_icdf, 2); + postfilter_gain = ((short)(0.5 + (.09375f) * (((int)1) << (15))))/*Inlines.QCONST16(.09375f, 15)*/ * (qg + 1); + } + tell = dec.tell(); + } + + if (LM > 0 && tell + 3 <= total_bits) + { + isTransient = dec.dec_bit_logp(3); + tell = dec.tell(); + } + else + isTransient = 0; + + if (isTransient != 0) + shortBlocks = M; + else + shortBlocks = 0; + + /* Decode the global flags (first symbols in the stream) */ + intra_ener = tell + 3 <= total_bits ? dec.dec_bit_logp(3) : 0; + /* Get band energies */ + QuantizeBands.unquant_coarse_energy(mode, start, end, oldBandE, + intra_ener, dec, C, LM); + + tf_res = new int[nbEBands]; + CeltCommon.tf_decode(start, end, isTransient, tf_res, LM, dec); + + tell = dec.tell(); + spread_decision = Spread.SPREAD_NORMAL; + if (tell + 4 <= total_bits) + spread_decision = dec.dec_icdf(Tables.spread_icdf, 5); + + cap = new int[nbEBands]; + + CeltCommon.init_caps(mode, cap, LM, C); + + offsets = new int[nbEBands]; + + dynalloc_logp = 6; + total_bits <<= EntropyCoder.BITRES; + tell = (int)dec.tell_frac(); + for (i = start; i < end; i++) + { + int width, quanta; + int dynalloc_loop_logp; + int boost; + width = C * (eBands[i + 1] - eBands[i]) << LM; + /* quanta is 6 bits, but no more than 1 bit/sample + and no less than 1/8 bit/sample */ + quanta = Inlines.IMIN(width << EntropyCoder.BITRES, Inlines.IMAX(6 << EntropyCoder.BITRES, width)); + dynalloc_loop_logp = dynalloc_logp; + boost = 0; + while (tell + (dynalloc_loop_logp << EntropyCoder.BITRES) < total_bits && boost < cap[i]) + { + int flag; + flag = dec.dec_bit_logp((uint)dynalloc_loop_logp); + tell = (int)dec.tell_frac(); + if (flag == 0) + break; + boost += quanta; + total_bits -= quanta; + dynalloc_loop_logp = 1; + } + offsets[i] = boost; + /* Making dynalloc more likely */ + if (boost > 0) + dynalloc_logp = Inlines.IMAX(2, dynalloc_logp - 1); + } + + fine_quant = new int[nbEBands]; + alloc_trim = tell + (6 << EntropyCoder.BITRES) <= total_bits ? + dec.dec_icdf(Tables.trim_icdf, 7) : 5; + + bits = (((int)len * 8) << EntropyCoder.BITRES) - (int)dec.tell_frac() - 1; + anti_collapse_rsv = isTransient != 0 && LM >= 2 && bits >= ((LM + 2) << EntropyCoder.BITRES) ? (1 << EntropyCoder.BITRES) : 0; + bits -= anti_collapse_rsv; + + pulses = new int[nbEBands]; + fine_priority = new int[nbEBands]; + + codedBands = Rate.compute_allocation(mode, start, end, offsets, cap, + alloc_trim, ref intensity, ref dual_stereo, bits, out balance, pulses, + fine_quant, fine_priority, C, LM, dec, 0, 0, 0); + + QuantizeBands.unquant_fine_energy(mode, start, end, oldBandE, fine_quant, dec, C); + + c = 0; + do + { + Arrays.MemMoveInt(decode_mem[c], N, 0, CeltConstants.DECODE_BUFFER_SIZE - N + overlap / 2); + } while (++c < CC); + + /* Decode fixed codebook */ + collapse_masks = new byte[C * nbEBands]; + + X = Arrays.InitTwoDimensionalArray(C, N); /*< Interleaved normalised MDCTs */ + + Bands.quant_all_bands(0, mode, start, end, X[0], C == 2 ? X[1] : null, collapse_masks, + null, pulses, shortBlocks, spread_decision, dual_stereo, intensity, tf_res, + len * (8 << EntropyCoder.BITRES) - anti_collapse_rsv, balance, dec, LM, codedBands, ref this.rng); + + if (anti_collapse_rsv > 0) + { + anti_collapse_on = (int)dec.dec_bits(1); + } + + QuantizeBands.unquant_energy_finalise(mode, start, end, oldBandE, + fine_quant, fine_priority, len * 8 - dec.tell(), dec, C); + + if (anti_collapse_on != 0) + Bands.anti_collapse(mode, X, collapse_masks, LM, C, N, + start, end, oldBandE, oldLogE, oldLogE2, pulses, this.rng); + + if (silence != 0) + { + for (i = 0; i < C * nbEBands; i++) + oldBandE[i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/; + } + + CeltCommon.celt_synthesis(mode, X, out_syn, out_syn_ptrs, oldBandE, start, effEnd, + C, CC, isTransient, LM, this.downsample, silence); + + c = 0; do + { + this.postfilter_period = Inlines.IMAX(this.postfilter_period, CeltConstants.COMBFILTER_MINPERIOD); + this.postfilter_period_old = Inlines.IMAX(this.postfilter_period_old, CeltConstants.COMBFILTER_MINPERIOD); + CeltCommon.comb_filter(out_syn[c], out_syn_ptrs[c], out_syn[c], out_syn_ptrs[c], this.postfilter_period_old, this.postfilter_period, mode.shortMdctSize, + this.postfilter_gain_old, this.postfilter_gain, this.postfilter_tapset_old, this.postfilter_tapset, + mode.window, overlap); + if (LM != 0) + { + CeltCommon.comb_filter( + out_syn[c], out_syn_ptrs[c] + (mode.shortMdctSize), + out_syn[c], out_syn_ptrs[c] + (mode.shortMdctSize), + this.postfilter_period, postfilter_pitch, N - mode.shortMdctSize, + this.postfilter_gain, postfilter_gain, this.postfilter_tapset, postfilter_tapset, + mode.window, overlap); + } + + } while (++c < CC); + this.postfilter_period_old = this.postfilter_period; + this.postfilter_gain_old = this.postfilter_gain; + this.postfilter_tapset_old = this.postfilter_tapset; + this.postfilter_period = postfilter_pitch; + this.postfilter_gain = postfilter_gain; + this.postfilter_tapset = postfilter_tapset; + if (LM != 0) + { + this.postfilter_period_old = this.postfilter_period; + this.postfilter_gain_old = this.postfilter_gain; + this.postfilter_tapset_old = this.postfilter_tapset; + } + + if (C == 1) + { + Array.Copy(oldBandE, 0, oldBandE, nbEBands, nbEBands); + } + + /* In case start or end were to change */ + if (isTransient == 0) + { + int max_background_increase; + Array.Copy(oldLogE, oldLogE2, 2 * nbEBands); + Array.Copy(oldBandE, oldLogE, 2 * nbEBands); + /* In normal circumstances, we only allow the noise floor to increase by + up to 2.4 dB/second, but when we're in DTX, we allow up to 6 dB + increase for each update.*/ + if (this.loss_count < 10) + max_background_increase = M * ((short)(0.5 + (0.001f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.001f, CeltConstants.DB_SHIFT)*/; + else + max_background_increase = ((short)(0.5 + (1.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.0f, CeltConstants.DB_SHIFT)*/; + for (i = 0; i < 2 * nbEBands; i++) + backgroundLogE[i] = Inlines.MIN16(backgroundLogE[i] + max_background_increase, oldBandE[i]); + } + else { + for (i = 0; i < 2 * nbEBands; i++) + oldLogE[i] = Inlines.MIN16(oldLogE[i], oldBandE[i]); + } + c = 0; do + { + for (i = 0; i < start; i++) + { + oldBandE[c * nbEBands + i] = 0; + oldLogE[c * nbEBands + i] = oldLogE2[c * nbEBands + i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/; + } + for (i = end; i < nbEBands; i++) + { + oldBandE[c * nbEBands + i] = 0; + oldLogE[c * nbEBands + i] = oldLogE2[c * nbEBands + i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/; + } + } while (++c < 2); + this.rng = dec.rng; + + CeltCommon.deemphasis(out_syn, out_syn_ptrs, pcm, pcm_ptr, N, CC, this.downsample, mode.preemph, this.preemph_memD, accum); + this.loss_count = 0; + + if (dec.tell() > 8 * len) + return OpusError.OPUS_INTERNAL_ERROR; + if (dec.get_error() != 0) + this.error = 1; + return frame_size / this.downsample; + } + +#endregion + +#region Getters and Setters + + internal void SetStartBand(int value) + { + if (value < 0 || value >= this.mode.nbEBands) + throw new ArgumentException("Start band above max number of ebands (or negative)"); + this.start = value; + } + + internal void SetEndBand(int value) + { + if (value < 1 || value > this.mode.nbEBands) + throw new ArgumentException("End band above max number of ebands (or less than 1)"); + this.end = value; + } + + internal void SetChannels(int value) + { + if (value < 1 || value > 2) + throw new ArgumentException("Channel count must be 1 or 2"); + this.stream_channels = value; + } + + internal int GetAndClearError() + { + int returnVal = this.error; + this.error = 0; + return returnVal; + } + + public int GetLookahead() + { + return this.overlap / this.downsample; + } + + public int GetPitch() + { + return this.postfilter_period; + } + + public CeltMode GetMode() + { + return this.mode; + } + + public void SetSignalling(int value) + { + this.signalling = value; + } + + public uint GetFinalRange() + { + return this.rng; + } + +#endregion + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/Structs/CELTMode.cs b/Libraries/Concentus/CSharp/Concentus/Celt/Structs/CELTMode.cs new file mode 100644 index 000000000..d1d60a29d --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/Structs/CELTMode.cs @@ -0,0 +1,118 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Celt.Structs +{ + using Concentus.Celt.Enums; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Enums; + using System; + + internal class CeltMode + { + internal int Fs = 0; + internal int overlap = 0; + + internal int nbEBands = 0; + internal int effEBands = 0; + internal int[] preemph = { 0, 0, 0, 0 }; + + /// + /// Definition for each "pseudo-critical band" + /// + internal short[] eBands = null; + + internal int maxLM = 0; + internal int nbShortMdcts = 0; + internal int shortMdctSize = 0; + + /// + /// Number of lines in allocVectors + /// + internal int nbAllocVectors = 0; + + /// + /// Number of bits in each band for several rates + /// + internal byte[] allocVectors = null; + internal short[] logN = null; + + internal int[] window = null; + internal MDCTLookup mdct = new MDCTLookup(); + internal PulseCache cache = new PulseCache(); + + private CeltMode() + { + } + + internal static readonly CeltMode mode48000_960_120 = new CeltMode + { + Fs = 48000, + overlap = 120, + nbEBands = 21, + effEBands = 21, + preemph = new int[] { 27853, 0, 4096, 8192 }, + eBands = Tables.eband5ms, + maxLM = 3, + nbShortMdcts = 8, + shortMdctSize = 120, + nbAllocVectors = 11, + allocVectors = Tables.band_allocation, + logN = Tables.logN400, + window = Tables.window120, + mdct = new MDCTLookup() + { + n = 1920, + maxshift = 3, + kfft = new FFTState[] + { + Tables.fft_state48000_960_0, + Tables.fft_state48000_960_1, + Tables.fft_state48000_960_2, + Tables.fft_state48000_960_3, + }, + trig = Tables.mdct_twiddles960 + }, + cache = new PulseCache() + { + size = 392, + index = Tables.cache_index50, + bits = Tables.cache_bits50, + caps = Tables.cache_caps50, + } + }; + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/Structs/CeltEncoder.cs b/Libraries/Concentus/CSharp/Concentus/Celt/Structs/CeltEncoder.cs new file mode 100644 index 000000000..a6f4d3d8c --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/Structs/CeltEncoder.cs @@ -0,0 +1,1300 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Celt.Structs +{ + using Concentus.Celt.Enums; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Enums; + using System; + + internal class CeltEncoder + { + #region Encoder state + internal CeltMode mode = null; /*< Mode used by the encoder. Without custom modes, this always refers to the same predefined struct */ + internal int channels = 0; + internal int stream_channels = 0; + + internal int force_intra = 0; + internal int clip = 0; + internal int disable_pf = 0; + internal int complexity = 0; + internal int upsample = 0; + internal int start = 0; + internal int end = 0; + + internal int bitrate = 0; + internal int vbr = 0; + internal int signalling = 0; + + /* If zero, VBR can do whatever it likes with the rate */ + internal int constrained_vbr = 0; + internal int loss_rate = 0; + internal int lsb_depth = 0; + internal OpusFramesize variable_duration = 0; + internal int lfe = 0; + + /* Everything beyond this point gets cleared on a reset */ + + internal uint rng = 0; + internal int spread_decision = 0; + internal int delayedIntra = 0; + internal int tonal_average = 0; + internal int lastCodedBands = 0; + internal int hf_average = 0; + internal int tapset_decision = 0; + + internal int prefilter_period = 0; + internal int prefilter_gain = 0; + internal int prefilter_tapset = 0; + internal int consec_transient = 0; + internal AnalysisInfo analysis = new AnalysisInfo(); + + internal readonly int[] preemph_memE = new int[2]; + internal readonly int[] preemph_memD = new int[2]; + + /* VBR-related parameters */ + internal int vbr_reservoir = 0; + internal int vbr_drift = 0; + internal int vbr_offset = 0; + internal int vbr_count = 0; + internal int overlap_max = 0; + internal int stereo_saving = 0; + internal int intensity = 0; + internal int[] energy_mask = null; + internal int spec_avg = 0; + + /// + /// The original C++ defined in_mem as a single float[1] which was the "caboose" + /// to the overall encoder struct, containing 5 separate variable-sized buffer + /// spaces of heterogeneous datatypes. I have laid them out into separate variables here, + /// but these were the original definitions: + /// val32 in_mem[], Size = channels*mode.overlap + /// val32 prefilter_mem[], Size = channels*COMBFILTER_MAXPERIOD + /// val16 oldBandE[], Size = channels*mode.nbEBands + /// val16 oldLogE[], Size = channels*mode.nbEBands + /// val16 oldLogE2[], Size = channels*mode.nbEBands + /// + internal int[][] in_mem = null; + internal int[][] prefilter_mem = null; + internal int[][] oldBandE = null; + internal int[][] oldLogE = null; + internal int[][] oldLogE2 = null; + + private void Reset() + { + mode = null; + channels = 0; + stream_channels = 0; + force_intra = 0; + clip = 0; + disable_pf = 0; + complexity = 0; + upsample = 0; + start = 0; + end = 0; + bitrate = 0; + vbr = 0; + signalling = 0; + constrained_vbr = 0; + loss_rate = 0; + lsb_depth = 0; + variable_duration = 0; + lfe = 0; + PartialReset(); + } + + private void PartialReset() + { + rng = 0; + spread_decision = 0; + delayedIntra = 0; + tonal_average = 0; + lastCodedBands = 0; + hf_average = 0; + tapset_decision = 0; + prefilter_period = 0; + prefilter_gain = 0; + prefilter_tapset = 0; + consec_transient = 0; + analysis.Reset(); + preemph_memE[0] = 0; + preemph_memE[1] = 0; + preemph_memD[0] = 0; + preemph_memD[1] = 0; + vbr_reservoir = 0; + vbr_drift = 0; + vbr_offset = 0; + vbr_count = 0; + overlap_max = 0; + stereo_saving = 0; + intensity = 0; + energy_mask = null; + spec_avg = 0; + in_mem = null; + prefilter_mem = null; + oldBandE = null; + oldLogE = null; + oldLogE2 = null; + } + +#endregion + +#region API functions + + internal void ResetState() + { + int i; + + this.PartialReset(); + + // We have to reconstitute the dynamic buffers here. + this.in_mem = Arrays.InitTwoDimensionalArray(this.channels, this.mode.overlap); + this.prefilter_mem = Arrays.InitTwoDimensionalArray(this.channels, CeltConstants.COMBFILTER_MAXPERIOD); + this.oldBandE = Arrays.InitTwoDimensionalArray(this.channels, this.mode.nbEBands); + this.oldLogE = Arrays.InitTwoDimensionalArray(this.channels, this.mode.nbEBands); + this.oldLogE2 = Arrays.InitTwoDimensionalArray(this.channels, this.mode.nbEBands); + + for (i = 0; i < this.mode.nbEBands; i++) + { + this.oldLogE[0][i] = this.oldLogE2[0][i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/; + } + if (this.channels == 2) + { + for (i = 0; i < this.mode.nbEBands; i++) + { + this.oldLogE[1][i] = this.oldLogE2[1][i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/; + } + } + this.vbr_offset = 0; + this.delayedIntra = 1; + this.spread_decision = Spread.SPREAD_NORMAL; + this.tonal_average = 256; + this.hf_average = 0; + this.tapset_decision = 0; + } + + internal int opus_custom_encoder_init_arch(CeltMode mode, + int channels) + { + if (channels < 0 || channels > 2) + return OpusError.OPUS_BAD_ARG; + + if (this == null || mode == null) + return OpusError.OPUS_ALLOC_FAIL; + + this.Reset(); + + this.mode = mode; + this.stream_channels = this.channels = channels; + + this.upsample = 1; + this.start = 0; + this.end = this.mode.effEBands; + this.signalling = 1; + + this.constrained_vbr = 1; + this.clip = 1; + + this.bitrate = OpusConstants.OPUS_BITRATE_MAX; + this.vbr = 0; + this.force_intra = 0; + this.complexity = 5; + this.lsb_depth = 24; + + //this.in_mem = new int[channels * mode.overlap); + //this.prefilter_mem = new int[channels * CeltConstants.COMBFILTER_MAXPERIOD); + //this.oldBandE = new int[channels * mode.nbEBands); + //this.oldLogE = new int[channels * mode.nbEBands); + //this.oldLogE2 = new int[channels * mode.nbEBands); + + this.ResetState(); + + return OpusError.OPUS_OK; + } + + internal int celt_encoder_init(int sampling_rate, int channels) + { + int ret; + ret = this.opus_custom_encoder_init_arch(CeltMode.mode48000_960_120, channels); + if (ret != OpusError.OPUS_OK) + return ret; + this.upsample = CeltCommon.resampling_factor(sampling_rate); + return OpusError.OPUS_OK; + } + + internal int run_prefilter(int[][] input, int[][] prefilter_mem, int CC, int N, + int prefilter_tapset, out int pitch, out int gain, out int qgain, int enabled, int nbAvailableBytes) + { + int c; + int[][] pre = new int[CC][]; + CeltMode mode; // [porting note] pointer + int pitch_index; + int gain1; + int pf_threshold; + int pf_on; + int qg; + int overlap; + + mode = this.mode; + overlap = mode.overlap; + for (int z = 0; z < CC; z++) + { + pre[z] = new int[N + CeltConstants.COMBFILTER_MAXPERIOD]; + } + + c = 0; + do + { + Array.Copy(prefilter_mem[c], 0, pre[c], 0, CeltConstants.COMBFILTER_MAXPERIOD); + Array.Copy(input[c], overlap, pre[c], CeltConstants.COMBFILTER_MAXPERIOD, N); + } while (++c < CC); + + if (enabled != 0) + { + int[] pitch_buf = new int[(CeltConstants.COMBFILTER_MAXPERIOD + N) >> 1]; + + Concentus.Celt.Pitch.pitch_downsample(pre, pitch_buf, CeltConstants.COMBFILTER_MAXPERIOD + N, CC); + /* Don't search for the fir last 1.5 octave of the range because + there's too many false-positives due to short-term correlation */ + Concentus.Celt.Pitch.pitch_search(pitch_buf, CeltConstants.COMBFILTER_MAXPERIOD >> 1, pitch_buf, N, + CeltConstants.COMBFILTER_MAXPERIOD - 3 * CeltConstants.COMBFILTER_MINPERIOD, out pitch_index); + pitch_index = CeltConstants.COMBFILTER_MAXPERIOD - pitch_index; + gain1 = Concentus.Celt.Pitch.remove_doubling(pitch_buf, CeltConstants.COMBFILTER_MAXPERIOD, CeltConstants.COMBFILTER_MINPERIOD, + N, ref pitch_index, this.prefilter_period, this.prefilter_gain); + if (pitch_index > CeltConstants.COMBFILTER_MAXPERIOD - 2) + pitch_index = CeltConstants.COMBFILTER_MAXPERIOD - 2; + gain1 = Inlines.MULT16_16_Q15(((short)(0.5 + (.7f) * (((int)1) << (15))))/*Inlines.QCONST16(.7f, 15)*/, gain1); + /*printf("%d %d %f %f\n", pitch_change, pitch_index, gain1, st.analysis.tonality);*/ + if (this.loss_rate > 2) + gain1 = Inlines.HALF32(gain1); + if (this.loss_rate > 4) + gain1 = Inlines.HALF32(gain1); + if (this.loss_rate > 8) + gain1 = 0; + } + else { + gain1 = 0; + pitch_index = CeltConstants.COMBFILTER_MINPERIOD; + } + + /* Gain threshold for enabling the prefilter/postfilter */ + pf_threshold = ((short)(0.5 + (.2f) * (((int)1) << (15))))/*Inlines.QCONST16(.2f, 15)*/; + + /* Adjusting the threshold based on rate and continuity */ + if (Inlines.abs(pitch_index - this.prefilter_period) * 10 > pitch_index) + pf_threshold += ((short)(0.5 + (.2f) * (((int)1) << (15))))/*Inlines.QCONST16(.2f, 15)*/; + if (nbAvailableBytes < 25) + pf_threshold += ((short)(0.5 + (.1f) * (((int)1) << (15))))/*Inlines.QCONST16(.1f, 15)*/; + if (nbAvailableBytes < 35) + pf_threshold += ((short)(0.5 + (.1f) * (((int)1) << (15))))/*Inlines.QCONST16(.1f, 15)*/; + if (this.prefilter_gain > ((short)(0.5 + (.4f) * (((int)1) << (15))))/*Inlines.QCONST16(.4f, 15)*/) + pf_threshold -= ((short)(0.5 + (.1f) * (((int)1) << (15))))/*Inlines.QCONST16(.1f, 15)*/; + if (this.prefilter_gain > ((short)(0.5 + (.55f) * (((int)1) << (15))))/*Inlines.QCONST16(.55f, 15)*/) + pf_threshold -= ((short)(0.5 + (.1f) * (((int)1) << (15))))/*Inlines.QCONST16(.1f, 15)*/; + + /* Hard threshold at 0.2 */ + pf_threshold = Inlines.MAX16(pf_threshold, ((short)(0.5 + (.2f) * (((int)1) << (15))))/*Inlines.QCONST16(.2f, 15)*/); + + if (gain1 < pf_threshold) + { + gain1 = 0; + pf_on = 0; + qg = 0; + } + else + { + /*This block is not gated by a total bits check only because + of the nbAvailableBytes check above.*/ + if (Inlines.ABS32(gain1 - this.prefilter_gain) < ((short)(0.5 + (.1f) * (((int)1) << (15))))/*Inlines.QCONST16(.1f, 15)*/) + gain1 = this.prefilter_gain; + + qg = ((gain1 + 1536) >> 10) / 3 - 1; + qg = Inlines.IMAX(0, Inlines.IMIN(7, qg)); + gain1 = ((short)(0.5 + (0.09375f) * (((int)1) << (15))))/*Inlines.QCONST16(0.09375f, 15)*/ * (qg + 1); + pf_on = 1; + } + /*printf("%d %f\n", pitch_index, gain1);*/ + + c = 0; + do + { + int offset = mode.shortMdctSize - overlap; + this.prefilter_period = Inlines.IMAX(this.prefilter_period, CeltConstants.COMBFILTER_MINPERIOD); + Array.Copy(this.in_mem[c], 0, input[c], 0, overlap); + if (offset != 0) + { + CeltCommon.comb_filter(input[c], (overlap), pre[c], (CeltConstants.COMBFILTER_MAXPERIOD), + this.prefilter_period, this.prefilter_period, offset, -this.prefilter_gain, -this.prefilter_gain, + this.prefilter_tapset, this.prefilter_tapset, null, 0); // opt: lots of pointer allocations here + } + + CeltCommon.comb_filter(input[c], (overlap + offset), pre[c], (CeltConstants.COMBFILTER_MAXPERIOD + offset), + this.prefilter_period, pitch_index, N - offset, -this.prefilter_gain, -gain1, + this.prefilter_tapset, prefilter_tapset, mode.window, overlap); + Array.Copy(input[c], N, this.in_mem[c], 0, overlap); + + if (N > CeltConstants.COMBFILTER_MAXPERIOD) + { + Array.Copy(pre[c], N, prefilter_mem[c], 0, CeltConstants.COMBFILTER_MAXPERIOD); + } + else + { + Arrays.MemMove(prefilter_mem[c], N, 0, CeltConstants.COMBFILTER_MAXPERIOD - N); + Array.Copy(pre[c], CeltConstants.COMBFILTER_MAXPERIOD, prefilter_mem[c], CeltConstants.COMBFILTER_MAXPERIOD - N, N); + } + } while (++c < CC); + + + gain = gain1; + pitch = pitch_index; + qgain = qg; + return pf_on; + } + + + internal int celt_encode_with_ec(short[] pcm, int pcm_ptr, int frame_size, byte[] compressed, int compressed_ptr, int nbCompressedBytes, EntropyCoder enc) + { + int i, c, N; + int bits; + int[][] input; + int[][] freq; + int[][] X; + int[][] bandE; + int[][] bandLogE; + int[][] bandLogE2; + int[] fine_quant; + int[][] error; + int[] pulses; + int[] cap; + int[] offsets; + int[] fine_priority; + int[] tf_res; + byte[] collapse_masks; + int shortBlocks = 0; + int isTransient = 0; + int CC = this.channels; + int C = this.stream_channels; + int LM, M; + int tf_select; + int nbFilledBytes, nbAvailableBytes; + int start; + int end; + int effEnd; + int codedBands; + int tf_sum; + int alloc_trim; + int pitch_index = CeltConstants.COMBFILTER_MINPERIOD; + int gain1 = 0; + int dual_stereo = 0; + int effectiveBytes; + int dynalloc_logp; + int vbr_rate; + int total_bits; + int total_boost; + int balance; + int tell; + int prefilter_tapset = 0; + int pf_on; + int anti_collapse_rsv; + int anti_collapse_on = 0; + int silence = 0; + int tf_chan = 0; + int tf_estimate; + int pitch_change = 0; + int tot_boost; + int sample_max; + int maxDepth; + CeltMode mode; + int nbEBands; + int overlap; + short[] eBands; + int secondMdct; + int signalBandwidth; + int transient_got_disabled = 0; + int surround_masking = 0; + int temporal_vbr = 0; + int surround_trim = 0; + int equiv_rate = 510000; + int[] surround_dynalloc; + + mode = this.mode; + nbEBands = mode.nbEBands; + overlap = mode.overlap; + eBands = mode.eBands; + start = this.start; + end = this.end; + tf_estimate = 0; + if (nbCompressedBytes < 2 || pcm == null) + { + return OpusError.OPUS_BAD_ARG; + } + + frame_size *= this.upsample; + for (LM = 0; LM <= mode.maxLM; LM++) + if (mode.shortMdctSize << LM == frame_size) + break; + if (LM > mode.maxLM) + { + return OpusError.OPUS_BAD_ARG; + } + M = 1 << LM; + N = M * mode.shortMdctSize; + + if (enc == null) + { + tell = 1; + nbFilledBytes = 0; + } + else { + tell = enc.tell(); + nbFilledBytes = (tell + 4) >> 3; + } + + Inlines.OpusAssert(this.signalling == 0); + + /* Can't produce more than 1275 output bytes */ + nbCompressedBytes = Inlines.IMIN(nbCompressedBytes, 1275); + nbAvailableBytes = nbCompressedBytes - nbFilledBytes; + + if (this.vbr != 0 && this.bitrate != OpusConstants.OPUS_BITRATE_MAX) + { + int den = mode.Fs >> EntropyCoder.BITRES; + vbr_rate = (this.bitrate * frame_size + (den >> 1)) / den; + effectiveBytes = vbr_rate >> (3 + EntropyCoder.BITRES); + } + else { + int tmp; + vbr_rate = 0; + tmp = this.bitrate * frame_size; + if (tell > 1) + tmp += tell; + if (this.bitrate != OpusConstants.OPUS_BITRATE_MAX) + nbCompressedBytes = Inlines.IMAX(2, Inlines.IMIN(nbCompressedBytes, + (tmp + 4 * mode.Fs) / (8 * mode.Fs) - (this.signalling != 0 ? 1 : 0))); + effectiveBytes = nbCompressedBytes; + } + if (this.bitrate != OpusConstants.OPUS_BITRATE_MAX) + equiv_rate = this.bitrate - (40 * C + 20) * ((400 >> LM) - 50); + + if (enc == null) + { + enc = new EntropyCoder(); + enc.enc_init(compressed, compressed_ptr, (uint)nbCompressedBytes); + } + + if (vbr_rate > 0) + { + /* Computes the max bit-rate allowed in VBR mode to avoid violating the + target rate and buffering. + We must do this up front so that bust-prevention logic triggers + correctly if we don't have enough bits. */ + if (this.constrained_vbr != 0) + { + int vbr_bound; + int max_allowed; + /* We could use any multiple of vbr_rate as bound (depending on the + delay). + This is clamped to ensure we use at least two bytes if the encoder + was entirely empty, but to allow 0 in hybrid mode. */ + vbr_bound = vbr_rate; + max_allowed = Inlines.IMIN(Inlines.IMAX(tell == 1 ? 2 : 0, + (vbr_rate + vbr_bound - this.vbr_reservoir) >> (EntropyCoder.BITRES + 3)), + nbAvailableBytes); + if (max_allowed < nbAvailableBytes) + { + nbCompressedBytes = nbFilledBytes + max_allowed; + nbAvailableBytes = max_allowed; + enc.enc_shrink((uint)nbCompressedBytes); + } + } + } + total_bits = nbCompressedBytes * 8; + + effEnd = end; + if (effEnd > mode.effEBands) + effEnd = mode.effEBands; + + input = Arrays.InitTwoDimensionalArray(CC, N + overlap); + + sample_max = Inlines.MAX32(this.overlap_max, Inlines.celt_maxabs32(pcm, pcm_ptr, C * (N - overlap) / this.upsample)); + this.overlap_max = Inlines.celt_maxabs32(pcm, pcm_ptr + (C * (N - overlap) / this.upsample), C * overlap / this.upsample); + sample_max = Inlines.MAX32(sample_max, this.overlap_max); + silence = (sample_max == 0) ? 1 : 0; +#if FUZZING + if ((new Random().Next() & 0x3F) == 0) + silence = 1; +#endif + if (tell == 1) + enc.enc_bit_logp(silence, 15); + else + silence = 0; + if (silence != 0) + { + /*In VBR mode there is no need to send more than the minimum. */ + if (vbr_rate > 0) + { + effectiveBytes = nbCompressedBytes = Inlines.IMIN(nbCompressedBytes, nbFilledBytes + 2); + total_bits = nbCompressedBytes * 8; + nbAvailableBytes = 2; + enc.enc_shrink((uint)nbCompressedBytes); + } + /* Pretend we've filled all the remaining bits with zeros + (that's what the initialiser did anyway) */ + tell = nbCompressedBytes * 8; + enc.nbits_total += tell - enc.tell(); + } + c = 0; + do + { + int need_clip = 0; + CeltCommon.celt_preemphasis(pcm, pcm_ptr + c, input[c], overlap, N, CC, this.upsample, + mode.preemph, ref this.preemph_memE[c], need_clip); + } while (++c < CC); + + /* Find pitch period and gain */ + { + int enabled; + int qg; + enabled = (((this.lfe != 0 && nbAvailableBytes > 3) || nbAvailableBytes > 12 * C) && start == 0 && silence == 0 && this.disable_pf == 0 + && this.complexity >= 5 && !(this.consec_transient != 0 && LM != 3 && this.variable_duration == OpusFramesize.OPUS_FRAMESIZE_VARIABLE)) ? 1 : 0; + + prefilter_tapset = this.tapset_decision; + pf_on = this.run_prefilter(input, this.prefilter_mem, CC, N, prefilter_tapset, out pitch_index, out gain1, out qg, enabled, nbAvailableBytes); + + if ((gain1 > ((short)(0.5 + (.4f) * (((int)1) << (15))))/*Inlines.QCONST16(.4f, 15)*/ || this.prefilter_gain > ((short)(0.5 + (.4f) * (((int)1) << (15))))/*Inlines.QCONST16(.4f, 15)*/) && (this.analysis.valid == 0 || this.analysis.tonality > .3) + && (pitch_index > 1.26 * this.prefilter_period || pitch_index < .79 * this.prefilter_period)) + pitch_change = 1; + if (pf_on == 0) + { + if (start == 0 && tell + 16 <= total_bits) + enc.enc_bit_logp(0, 1); + } + else { + /*This block is not gated by a total bits check only because + of the nbAvailableBytes check above.*/ + int octave; + enc.enc_bit_logp(1, 1); + pitch_index += 1; + octave = Inlines.EC_ILOG((uint)pitch_index) - 5; + enc.enc_uint((uint)octave, 6); + enc.enc_bits((uint)(pitch_index - (16 << octave)), (uint)(4 + octave)); + pitch_index -= 1; + enc.enc_bits((uint)qg, 3); + enc.enc_icdf(prefilter_tapset, Tables.tapset_icdf, 2); + } + } + + isTransient = 0; + shortBlocks = 0; + if (this.complexity >= 1 && this.lfe == 0) + { + isTransient = CeltCommon.transient_analysis(input, N + overlap, CC, + out tf_estimate, out tf_chan); + } + + if (LM > 0 && enc.tell() + 3 <= total_bits) + { + if (isTransient != 0) + shortBlocks = M; + } + else { + isTransient = 0; + transient_got_disabled = 1; + } + + freq = Arrays.InitTwoDimensionalArray(CC, N); /*< Interleaved signal MDCTs */ + bandE = Arrays.InitTwoDimensionalArray(CC, nbEBands); + bandLogE = Arrays.InitTwoDimensionalArray(CC, nbEBands); + + secondMdct = (shortBlocks != 0 && this.complexity >= 8) ? 1 : 0; + bandLogE2 = Arrays.InitTwoDimensionalArray(CC, nbEBands); + //Arrays.MemSetInt(bandLogE2, 0, C * nbEBands); // not explicitly needed + if (secondMdct != 0) + { + CeltCommon.compute_mdcts(mode, 0, input, freq, C, CC, LM, this.upsample); + Bands.compute_band_energies(mode, freq, bandE, effEnd, C, LM); + QuantizeBands.amp2Log2(mode, effEnd, end, bandE, bandLogE2, C); + for (i = 0; i < nbEBands; i++) + { + bandLogE2[0][i] += Inlines.HALF16(Inlines.SHL16(LM, CeltConstants.DB_SHIFT)); + } + if (C == 2) + { + for (i = 0; i < nbEBands; i++) + { + bandLogE2[1][i] += Inlines.HALF16(Inlines.SHL16(LM, CeltConstants.DB_SHIFT)); + } + } + } + + CeltCommon.compute_mdcts(mode, shortBlocks, input, freq, C, CC, LM, this.upsample); + if (CC == 2 && C == 1) + tf_chan = 0; + Bands.compute_band_energies(mode, freq, bandE, effEnd, C, LM); + + if (this.lfe != 0) + { + for (i = 2; i < end; i++) + { + bandE[0][i] = Inlines.IMIN(bandE[0][i], Inlines.MULT16_32_Q15(((short)(0.5 + (1e-4f) * (((int)1) << (15))))/*Inlines.QCONST16(1e-4f, 15)*/, bandE[0][0])); + bandE[0][i] = Inlines.MAX32(bandE[0][i], CeltConstants.EPSILON); + } + } + + QuantizeBands.amp2Log2(mode, effEnd, end, bandE, bandLogE, C); + + surround_dynalloc = new int[C * nbEBands]; + //Arrays.MemSetInt(surround_dynalloc, 0, end); // not strictly needed + /* This computes how much masking takes place between surround channels */ + if (start == 0 && this.energy_mask != null && this.lfe == 0) + { + int mask_end; + int midband; + int count_dynalloc; + int mask_avg = 0; + int diff = 0; + int count = 0; + mask_end = Inlines.IMAX(2, this.lastCodedBands); + for (c = 0; c < C; c++) + { + for (i = 0; i < mask_end; i++) + { + int mask; + mask = Inlines.MAX16(Inlines.MIN16(this.energy_mask[nbEBands * c + i], + ((short)(0.5 + (.25f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.25f, CeltConstants.DB_SHIFT)*/), -((short)(0.5 + (2.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(2.0f, CeltConstants.DB_SHIFT)*/); + if (mask > 0) + mask = Inlines.HALF16(mask); + mask_avg += Inlines.MULT16_16(mask, eBands[i + 1] - eBands[i]); + count += eBands[i + 1] - eBands[i]; + diff += Inlines.MULT16_16(mask, 1 + 2 * i - mask_end); + } + } + Inlines.OpusAssert(count > 0); + mask_avg = Inlines.DIV32_16(mask_avg, count); + mask_avg += ((short)(0.5 + (.2f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.2f, CeltConstants.DB_SHIFT)*/; + diff = diff * 6 / (C * (mask_end - 1) * (mask_end + 1) * mask_end); + /* Again, being conservative */ + diff = Inlines.HALF32(diff); + diff = Inlines.MAX32(Inlines.MIN32(diff, ((int)(0.5 + (.031f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST32(.031f, CeltConstants.DB_SHIFT)*/), 0 - ((int)(0.5 + (.031f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST32(.031f, CeltConstants.DB_SHIFT)*/); + /* Find the band that's in the middle of the coded spectrum */ + for (midband = 0; eBands[midband + 1] < eBands[mask_end] / 2; midband++) ; + count_dynalloc = 0; + for (i = 0; i < mask_end; i++) + { + int lin; + int unmask; + lin = mask_avg + diff * (i - midband); + if (C == 2) + unmask = Inlines.MAX16(this.energy_mask[i], this.energy_mask[nbEBands + i]); + else + unmask = this.energy_mask[i]; + unmask = Inlines.MIN16(unmask, ((short)(0.5 + (.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.0f, CeltConstants.DB_SHIFT)*/); + unmask -= lin; + if (unmask > ((short)(0.5 + (.25f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.25f, CeltConstants.DB_SHIFT)*/) + { + surround_dynalloc[i] = unmask - ((short)(0.5 + (.25f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.25f, CeltConstants.DB_SHIFT)*/; + count_dynalloc++; + } + } + if (count_dynalloc >= 3) + { + /* If we need dynalloc in many bands, it's probably because our + initial masking rate was too low. */ + mask_avg += ((short)(0.5 + (.25f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.25f, CeltConstants.DB_SHIFT)*/; + if (mask_avg > 0) + { + /* Something went really wrong in the original calculations, + disabling masking. */ + mask_avg = 0; + diff = 0; + Arrays.MemSetInt(surround_dynalloc, 0, mask_end); + } + else { + for (i = 0; i < mask_end; i++) + surround_dynalloc[i] = Inlines.MAX16(0, surround_dynalloc[i] - ((short)(0.5 + (.25f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.25f, CeltConstants.DB_SHIFT)*/); + } + } + mask_avg += ((short)(0.5 + (.2f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.2f, CeltConstants.DB_SHIFT)*/; + /* Convert to 1/64th units used for the trim */ + surround_trim = 64 * diff; + /*printf("%d %d ", mask_avg, surround_trim);*/ + surround_masking = mask_avg; + } + /* Temporal VBR (but not for LFE) */ + if (this.lfe == 0) + { + int follow = -((short)(0.5 + (10.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(10.0f, CeltConstants.DB_SHIFT)*/; + int frame_avg = 0; + int offset = shortBlocks != 0 ? Inlines.HALF16(Inlines.SHL16(LM, CeltConstants.DB_SHIFT)) : 0; + for (i = start; i < end; i++) + { + follow = Inlines.MAX16(follow - ((short)(0.5 + (1.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.0f, CeltConstants.DB_SHIFT)*/, bandLogE[0][i] - offset); + if (C == 2) + follow = Inlines.MAX16(follow, bandLogE[1][i] - offset); + frame_avg += follow; + } + frame_avg /= (end - start); + temporal_vbr = Inlines.SUB16(frame_avg, this.spec_avg); + temporal_vbr = Inlines.MIN16(((short)(0.5 + (3.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(3.0f, CeltConstants.DB_SHIFT)*/, Inlines.MAX16(-((short)(0.5 + (1.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.5f, CeltConstants.DB_SHIFT)*/, temporal_vbr)); + this.spec_avg += (short)(Inlines.MULT16_16_Q15(((short)(0.5 + (.02f) * (((int)1) << (15))))/*Inlines.QCONST16(.02f, 15)*/, temporal_vbr)); + } + /*for (i=0;i<21;i++) + printf("%f ", bandLogE[i]); + printf("\n");*/ + + if (secondMdct == 0) + { + Array.Copy(bandLogE[0], bandLogE2[0], nbEBands); + if (C == 2) + Array.Copy(bandLogE[1], bandLogE2[1], nbEBands); + } + + /* Last chance to catch any transient we might have missed in the + time-domain analysis */ + if (LM > 0 && enc.tell() + 3 <= total_bits && isTransient == 0 && this.complexity >= 5 && this.lfe == 0) + { + if (CeltCommon.patch_transient_decision(bandLogE, this.oldBandE, nbEBands, start, end, C) != 0) + { + isTransient = 1; + shortBlocks = M; + CeltCommon.compute_mdcts(mode, shortBlocks, input, freq, C, CC, LM, this.upsample); + Bands.compute_band_energies(mode, freq, bandE, effEnd, C, LM); + QuantizeBands.amp2Log2(mode, effEnd, end, bandE, bandLogE, C); + /* Compensate for the scaling of short vs long mdcts */ + for (i = 0; i < nbEBands; i++) + bandLogE2[0][i] += Inlines.HALF16(Inlines.SHL16(LM, CeltConstants.DB_SHIFT)); + if (C == 2) + { + for (i = 0; i < nbEBands; i++) + bandLogE2[1][i] += Inlines.HALF16(Inlines.SHL16(LM, CeltConstants.DB_SHIFT)); + } + tf_estimate = ((short)(0.5 + (.2f) * (((int)1) << (14))))/*Inlines.QCONST16(.2f, 14)*/; + } + } + + if (LM > 0 && enc.tell() + 3 <= total_bits) + enc.enc_bit_logp(isTransient, 3); + + X = Arrays.InitTwoDimensionalArray(C, N); /*< Interleaved normalised MDCTs */ + + /* Band normalisation */ + Bands.normalise_bands(mode, freq, X, bandE, effEnd, C, M); + + tf_res = new int[nbEBands]; + /* Disable variable tf resolution for hybrid and at very low bitrate */ + if (effectiveBytes >= 15 * C && start == 0 && this.complexity >= 2 && this.lfe == 0) + { + int lambda; + if (effectiveBytes < 40) + lambda = 12; + else if (effectiveBytes < 60) + lambda = 6; + else if (effectiveBytes < 100) + lambda = 4; + else + lambda = 3; + lambda *= 2; + tf_select = CeltCommon.tf_analysis(mode, effEnd, isTransient, tf_res, lambda, X, N, LM, out tf_sum, tf_estimate, tf_chan); + + for (i = effEnd; i < end; i++) + tf_res[i] = tf_res[effEnd - 1]; + } + else { + tf_sum = 0; + for (i = 0; i < end; i++) + tf_res[i] = isTransient; + tf_select = 0; + } + + error = Arrays.InitTwoDimensionalArray(C, nbEBands); + QuantizeBands.quant_coarse_energy(mode, start, end, effEnd, bandLogE, + this.oldBandE, (uint)total_bits, error, enc, + C, LM, nbAvailableBytes, this.force_intra, + ref this.delayedIntra, this.complexity >= 4 ? 1 : 0, this.loss_rate, this.lfe); + + CeltCommon.tf_encode(start, end, isTransient, tf_res, LM, tf_select, enc); + + if (enc.tell() + 4 <= total_bits) + { + if (this.lfe != 0) + { + this.tapset_decision = 0; + this.spread_decision = Spread.SPREAD_NORMAL; + } + else if (shortBlocks != 0 || this.complexity < 3 || nbAvailableBytes < 10 * C || start != 0) + { + if (this.complexity == 0) + this.spread_decision = Spread.SPREAD_NONE; + else + this.spread_decision = Spread.SPREAD_NORMAL; + } + else + { + this.spread_decision = Bands.spreading_decision(mode, X, + ref this.tonal_average, this.spread_decision, ref this.hf_average, + ref this.tapset_decision, (pf_on != 0 && shortBlocks == 0) ? 1 : 0, effEnd, C, M); + + /*printf("%d %d\n", st.tapset_decision, st.spread_decision);*/ + /*printf("%f %d %f %d\n\n", st.analysis.tonality, st.spread_decision, st.analysis.tonality_slope, st.tapset_decision);*/ + } + enc.enc_icdf(this.spread_decision, Tables.spread_icdf, 5); + } + + offsets = new int[nbEBands]; + + maxDepth = CeltCommon.dynalloc_analysis(bandLogE, bandLogE2, nbEBands, start, end, C, offsets, + this.lsb_depth, mode.logN, isTransient, this.vbr, this.constrained_vbr, + eBands, LM, effectiveBytes, out tot_boost, this.lfe, surround_dynalloc); + + /* For LFE, everything interesting is in the first band */ + if (this.lfe != 0) + offsets[0] = Inlines.IMIN(8, effectiveBytes / 3); + cap = new int[nbEBands]; + CeltCommon.init_caps(mode, cap, LM, C); + + dynalloc_logp = 6; + total_bits <<= EntropyCoder.BITRES; + total_boost = 0; + tell = (int)enc.tell_frac(); + for (i = start; i < end; i++) + { + int width, quanta; + int dynalloc_loop_logp; + int boost; + int j; + width = C * (eBands[i + 1] - eBands[i]) << LM; + /* quanta is 6 bits, but no more than 1 bit/sample + and no less than 1/8 bit/sample */ + quanta = Inlines.IMIN(width << EntropyCoder.BITRES, Inlines.IMAX(6 << EntropyCoder.BITRES, width)); + dynalloc_loop_logp = dynalloc_logp; + boost = 0; + for (j = 0; tell + (dynalloc_loop_logp << EntropyCoder.BITRES) < total_bits - total_boost + && boost < cap[i]; j++) + { + int flag; + flag = j < offsets[i] ? 1 : 0; + enc.enc_bit_logp(flag, (uint)dynalloc_loop_logp); + tell = (int)enc.tell_frac(); + if (flag == 0) + break; + boost += quanta; + total_boost += quanta; + dynalloc_loop_logp = 1; + } + /* Making dynalloc more likely */ + if (j != 0) + dynalloc_logp = Inlines.IMAX(2, dynalloc_logp - 1); + offsets[i] = boost; + } + + if (C == 2) + { + /* Always use MS for 2.5 ms frames until we can do a better analysis */ + if (LM != 0) + dual_stereo = CeltCommon.stereo_analysis(mode, X, LM); + + this.intensity = Bands.hysteresis_decision((int)(equiv_rate / 1000), + Tables.intensity_thresholds, Tables.intensity_histeresis, 21, this.intensity); + this.intensity = Inlines.IMIN(end, Inlines.IMAX(start, this.intensity)); + } + + alloc_trim = 5; + if (tell + (6 << EntropyCoder.BITRES) <= total_bits - total_boost) + { + if (this.lfe != 0) + { + alloc_trim = 5; + } + else + { + alloc_trim = CeltCommon.alloc_trim_analysis(mode, X, bandLogE, + end, LM, C, this.analysis, ref this.stereo_saving, tf_estimate, + this.intensity, surround_trim); + } + enc.enc_icdf(alloc_trim, Tables.trim_icdf, 7); + tell = (int)enc.tell_frac(); + } + + /* Variable bitrate */ + if (vbr_rate > 0) + { + int alpha; + int delta; + /* The target rate in 8th bits per frame */ + int target, base_target; + int min_allowed; + int lm_diff = mode.maxLM - LM; + + /* Don't attempt to use more than 510 kb/s, even for frames smaller than 20 ms. + The CELT allocator will just not be able to use more than that anyway. */ + nbCompressedBytes = Inlines.IMIN(nbCompressedBytes, 1275 >> (3 - LM)); + base_target = vbr_rate - ((40 * C + 20) << EntropyCoder.BITRES); + + if (this.constrained_vbr != 0) + base_target += (this.vbr_offset >> lm_diff); + + target = CeltCommon.compute_vbr(mode, this.analysis, base_target, LM, equiv_rate, + this.lastCodedBands, C, this.intensity, this.constrained_vbr, + this.stereo_saving, tot_boost, tf_estimate, pitch_change, maxDepth, + this.variable_duration, this.lfe, this.energy_mask != null ? 1 : 0, surround_masking, + temporal_vbr); + + /* The current offset is removed from the target and the space used + so far is added*/ + target = target + tell; + /* In VBR mode the frame size must not be reduced so much that it would + result in the encoder running out of bits. + The margin of 2 bytes ensures that none of the bust-prevention logic + in the decoder will have triggered so far. */ + min_allowed = ((tell + total_boost + (1 << (EntropyCoder.BITRES + 3)) - 1) >> (EntropyCoder.BITRES + 3)) + 2 - nbFilledBytes; + + nbAvailableBytes = (target + (1 << (EntropyCoder.BITRES + 2))) >> (EntropyCoder.BITRES + 3); + nbAvailableBytes = Inlines.IMAX(min_allowed, nbAvailableBytes); + nbAvailableBytes = Inlines.IMIN(nbCompressedBytes, nbAvailableBytes + nbFilledBytes) - nbFilledBytes; + + /* By how much did we "miss" the target on that frame */ + delta = target - vbr_rate; + + target = nbAvailableBytes << (EntropyCoder.BITRES + 3); + + /*If the frame is silent we don't adjust our drift, otherwise + the encoder will shoot to very high rates after hitting a + span of silence, but we do allow the EntropyCoder.BITRES to refill. + This means that we'll undershoot our target in CVBR/VBR modes + on files with lots of silence. */ + if (silence != 0) + { + nbAvailableBytes = 2; + target = 2 * 8 << EntropyCoder.BITRES; + delta = 0; + } + + if (this.vbr_count < 970) + { + this.vbr_count++; + alpha = Inlines.celt_rcp(Inlines.SHL32((this.vbr_count + 20), 16)); + } + else + alpha = ((short)(0.5 + (.001f) * (((int)1) << (15))))/*Inlines.QCONST16(.001f, 15)*/; + /* How many bits have we used in excess of what we're allowed */ + if (this.constrained_vbr != 0) + this.vbr_reservoir += target - vbr_rate; + /*printf ("%d\n", st.vbr_reservoir);*/ + + /* Compute the offset we need to apply in order to reach the target */ + if (this.constrained_vbr != 0) + { + this.vbr_drift += (int)Inlines.MULT16_32_Q15(alpha, (delta * (1 << lm_diff)) - this.vbr_offset - this.vbr_drift); + this.vbr_offset = -this.vbr_drift; + } + /*printf ("%d\n", st.vbr_drift);*/ + + if (this.constrained_vbr != 0 && this.vbr_reservoir < 0) + { + /* We're under the min value -- increase rate */ + int adjust = (-this.vbr_reservoir) / (8 << EntropyCoder.BITRES); + /* Unless we're just coding silence */ + nbAvailableBytes += silence != 0 ? 0 : adjust; + this.vbr_reservoir = 0; + /*printf ("+%d\n", adjust);*/ + } + nbCompressedBytes = Inlines.IMIN(nbCompressedBytes, nbAvailableBytes + nbFilledBytes); + /*printf("%d\n", nbCompressedBytes*50*8);*/ + /* This moves the raw bits to take into account the new compressed size */ + enc.enc_shrink((uint)nbCompressedBytes); + } + + /* Bit allocation */ + fine_quant = new int[nbEBands]; + pulses = new int[nbEBands]; + fine_priority = new int[nbEBands]; + + /* bits = packet size - where we are - safety*/ + bits = (((int)nbCompressedBytes * 8) << EntropyCoder.BITRES) - (int)enc.tell_frac() - 1; + anti_collapse_rsv = isTransient != 0 && LM >= 2 && bits >= ((LM + 2) << EntropyCoder.BITRES) ? (1 << EntropyCoder.BITRES) : 0; + bits -= anti_collapse_rsv; + signalBandwidth = end - 1; + + if (this.analysis.valid != 0) + { + int min_bandwidth; + if (equiv_rate < (int)32000 * C) + min_bandwidth = 13; + else if (equiv_rate < (int)48000 * C) + min_bandwidth = 16; + else if (equiv_rate < (int)60000 * C) + min_bandwidth = 18; + else if (equiv_rate < (int)80000 * C) + min_bandwidth = 19; + else + min_bandwidth = 20; + signalBandwidth = Inlines.IMAX(this.analysis.bandwidth, min_bandwidth); + } + + if (this.lfe != 0) + { + signalBandwidth = 1; + } + + codedBands = Rate.compute_allocation(mode, start, end, offsets, cap, + alloc_trim, ref this.intensity, ref dual_stereo, bits, out balance, pulses, + fine_quant, fine_priority, C, LM, enc, 1, this.lastCodedBands, signalBandwidth); + + if (this.lastCodedBands != 0) + this.lastCodedBands = Inlines.IMIN(this.lastCodedBands + 1, Inlines.IMAX(this.lastCodedBands - 1, codedBands)); + else + this.lastCodedBands = codedBands; + + QuantizeBands.quant_fine_energy(mode, start, end, this.oldBandE, error, fine_quant, enc, C); + + /* Residual quantisation */ + collapse_masks = new byte[C * nbEBands]; + Bands.quant_all_bands(1, mode, start, end, X[0], C == 2 ? X[1] : null, collapse_masks, + bandE, pulses, shortBlocks, this.spread_decision, + dual_stereo, this.intensity, tf_res, nbCompressedBytes * (8 << EntropyCoder.BITRES) - anti_collapse_rsv, + balance, enc, LM, codedBands, ref this.rng); + + if (anti_collapse_rsv > 0) + { + anti_collapse_on = (this.consec_transient < 2) ? 1 : 0; +#if FUZZING + anti_collapse_on = new Random().Next() & 0x1; +#endif + enc.enc_bits((uint)anti_collapse_on, 1); + } + + QuantizeBands.quant_energy_finalise(mode, start, end, this.oldBandE, error, fine_quant, fine_priority, nbCompressedBytes * 8 - (int)enc.tell(), enc, C); + + if (silence != 0) + { + for (i = 0; i < nbEBands; i++) + { + this.oldBandE[0][i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/; + } + if (C == 2) + { + for (i = 0; i < nbEBands; i++) + { + this.oldBandE[1][i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/; + } + } + } + + this.prefilter_period = pitch_index; + this.prefilter_gain = gain1; + this.prefilter_tapset = prefilter_tapset; + + if (CC == 2 && C == 1) + { + Array.Copy(oldBandE[0], 0, oldBandE[1], 0, nbEBands); + } + + if (isTransient == 0) + { + Array.Copy(oldLogE[0], 0, oldLogE2[0], 0, nbEBands); + Array.Copy(oldBandE[0], 0, oldLogE[0], 0, nbEBands); + if (CC == 2) + { + Array.Copy(oldLogE[1], 0, oldLogE2[1], 0, nbEBands); + Array.Copy(oldBandE[1], 0, oldLogE[1], 0, nbEBands); + } + } + else + { + for (i = 0; i < nbEBands; i++) + { + oldLogE[0][i] = Inlines.MIN16(oldLogE[0][i], oldBandE[0][i]); + } + if (CC == 2) + { + for (i = 0; i < nbEBands; i++) + { + oldLogE[1][i] = Inlines.MIN16(oldLogE[1][i], oldBandE[1][i]); + } + } + } + + /* In case start or end were to change */ + c = 0; + do + { + for (i = 0; i < start; i++) + { + oldBandE[c][i] = 0; + oldLogE[c][i] = oldLogE2[c][i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/; + } + for (i = end; i < nbEBands; i++) + { + oldBandE[c][i] = 0; + oldLogE[c][i] = oldLogE2[c][i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/; + } + } while (++c < CC); + + if (isTransient != 0 || transient_got_disabled != 0) + this.consec_transient++; + else + this.consec_transient = 0; + this.rng = enc.rng; + + /* If there's any room left (can only happen for very high rates), + it's already filled with zeros */ + enc.enc_done(); + + + if (enc.get_error() != 0) + return OpusError.OPUS_INTERNAL_ERROR; + else + return nbCompressedBytes; + } + +#endregion + +#region Getters and Setters + + internal void SetComplexity(int value) + { + if (value < 0 || value > 10) + throw new ArgumentException("Complexity must be between 0 and 10 inclusive"); + this.complexity = value; + } + + internal void SetStartBand(int value) + { + if (value < 0 || value >= this.mode.nbEBands) + throw new ArgumentException("Start band above max number of ebands (or negative)"); + this.start = value; + } + + internal void SetEndBand(int value) + { + if (value < 1 || value > this.mode.nbEBands) + throw new ArgumentException("End band above max number of ebands (or less than 1)"); + this.end = value; + } + + internal void SetPacketLossPercent(int value) + { + if (value < 0 || value > 100) + throw new ArgumentException("Packet loss must be between 0 and 100"); + this.loss_rate = value; + } + + internal void SetPrediction(int value) + { + if (value < 0 || value > 2) + throw new ArgumentException("CELT prediction mode must be 0, 1, or 2"); + this.disable_pf = (value <= 1) ? 1 : 0; + this.force_intra = (value == 0) ? 1 : 0; + } + + internal void SetVBRConstraint(bool value) + { + this.constrained_vbr = value ? 1 : 0; + } + + internal void SetVBR(bool value) + { + this.vbr = value ? 1 : 0; + } + + internal void SetBitrate(int value) + { + if (value <= 500 && value != OpusConstants.OPUS_BITRATE_MAX) + throw new ArgumentException("Bitrate out of range"); + value = Inlines.IMIN(value, 260000 * this.channels); + this.bitrate = value; + } + + internal void SetChannels(int value) + { + if (value < 1 || value > 2) + throw new ArgumentException("Channel count must be 1 or 2"); + this.stream_channels = value; + } + + internal void SetLSBDepth(int value) + { + if (value < 8 || value > 24) + throw new ArgumentException("Bit depth must be between 8 and 24"); + this.lsb_depth = value; + } + + internal int GetLSBDepth() + { + return this.lsb_depth; + } + + internal void SetExpertFrameDuration(OpusFramesize value) + { + this.variable_duration = value; + } + + internal void SetSignalling(int value) + { + this.signalling = value; + } + + internal void SetAnalysis(AnalysisInfo value) + { + if (value == null) + throw new ArgumentNullException("AnalysisInfo"); + this.analysis.Assign(value); + } + + internal CeltMode GetMode() + { + return this.mode; + } + + internal uint GetFinalRange() + { + return this.rng; + } + + internal void SetLFE(int value) + { + this.lfe = value; + } + + internal void SetEnergyMask(int[] value) + { + this.energy_mask = value; + } + +#endregion + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/Structs/FFTState.cs b/Libraries/Concentus/CSharp/Concentus/Celt/Structs/FFTState.cs new file mode 100644 index 000000000..1f1b795b9 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/Structs/FFTState.cs @@ -0,0 +1,54 @@ +/* Copyright (c) 2003-2004, Mark Borgerding + Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Modified from KISS-FFT by Jean-Marc Valin + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Celt.Structs +{ + using Concentus.Celt.Enums; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Enums; + using System; + + internal class FFTState + { + internal int nfft = 0; + internal short scale = 0; + internal int scale_shift = 0; + internal int shift = 0; + internal short[] factors = new short[2 * KissFFT.MAXFACTORS]; + internal short[] bitrev = null; + internal short[] twiddles = null; + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/Structs/MDCTLookup.cs b/Libraries/Concentus/CSharp/Concentus/Celt/Structs/MDCTLookup.cs new file mode 100644 index 000000000..9322ccbb8 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/Structs/MDCTLookup.cs @@ -0,0 +1,59 @@ +/* Copyright (c) 2003-2004, Mark Borgerding + Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Modified from KISS-FFT by Jean-Marc Valin + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Celt.Structs +{ + using Concentus.Celt.Enums; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Enums; + using System; + + internal class MDCTLookup + { + internal int n = 0; + + internal int maxshift = 0; + + // [porting note] these are pointers to static states defined in tables.cs + internal FFTState[] kfft = new FFTState[4]; + + internal short[] trig = null; + + internal MDCTLookup() + { + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/Structs/PulseCache.cs b/Libraries/Concentus/CSharp/Concentus/Celt/Structs/PulseCache.cs new file mode 100644 index 000000000..2713fc9b0 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/Structs/PulseCache.cs @@ -0,0 +1,59 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Celt.Structs +{ + using Concentus.Celt.Enums; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Enums; + using System; + + internal class PulseCache + { + internal int size = 0; + internal short[] index = null; + internal byte[] bits = null; + internal byte[] caps = null; + + internal void Reset() + { + size = 0; + index = null; + bits = null; + caps = null; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/Tables.cs b/Libraries/Concentus/CSharp/Concentus/Celt/Tables.cs new file mode 100644 index 000000000..a53d6835e --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/Tables.cs @@ -0,0 +1,1128 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Celt +{ + using Concentus.Celt.Enums; + using Concentus.Celt.Structs; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using System.Diagnostics; + + internal static class Tables + { + /* Mean energy in each band quantized in Q4 */ + internal static readonly byte[] eMeans/*[25]*/ = { + 103,100, 92, 85, 81, + 77, 72, 70, 78, 75, + 73, 71, 78, 74, 69, + 72, 70, 74, 76, 71, + 60, 60, 60, 60, 60 + }; + + /* Indexing table for converting from natural Hadamard to ordery Hadamard + This is essentially a bit-reversed Gray, on top of which we've added + an inversion of the order because we want the DC at the end rather than + the beginning. The lines are for N=2, 4, 8, 16 */ + internal static readonly int[] ordery_table = { + 1, 0, + 3, 0, 2, 1, + 7, 0, 4, 3, 6, 1, 5, 2, + 15, 0, 8, 7, 12, 3, 11, 4, 14, 1, 9, 6, 13, 2, 10, 5, + }; + + internal static readonly short[] eband5ms = { + /*0 200 400 600 800 1k 1.2 1.4 1.6 2k 2.4 2.8 3.2 4k 4.8 5.6 6.8 8k 9.6 12k 15.6 */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 34, 40, 48, 60, 78, 100 + }; + + /*Parameters of the Laplace-like probability models used for the coarse energy. + There is one pair of parameters for each frame size, prediction type + (inter/intra), and band number. + The first number of each pair is the probability of 0, and the second is the + decay rate, both in Q8 precision.*/ + internal static readonly byte[][][] e_prob_model = { + /*120 sample frames.*/ + new byte[][]{ + /*Inter*/ + new byte[]{ + 72, 127, 65, 129, 66, 128, 65, 128, 64, 128, 62, 128, 64, 128, + 64, 128, 92, 78, 92, 79, 92, 78, 90, 79, 116, 41, 115, 40, + 114, 40, 132, 26, 132, 26, 145, 17, 161, 12, 176, 10, 177, 11 + }, + /*Intra*/ + new byte[]{ + 24, 179, 48, 138, 54, 135, 54, 132, 53, 134, 56, 133, 55, 132, + 55, 132, 61, 114, 70, 96, 74, 88, 75, 88, 87, 74, 89, 66, + 91, 67, 100, 59, 108, 50, 120, 40, 122, 37, 97, 43, 78, 50 + } + }, + /*240 sample frames.*/ + new byte[][]{ + /*Inter*/ + new byte[]{ + 83, 78, 84, 81, 88, 75, 86, 74, 87, 71, 90, 73, 93, 74, + 93, 74, 109, 40, 114, 36, 117, 34, 117, 34, 143, 17, 145, 18, + 146, 19, 162, 12, 165, 10, 178, 7, 189, 6, 190, 8, 177, 9 + }, + /*Intra*/ + new byte[]{ + 23, 178, 54, 115, 63, 102, 66, 98, 69, 99, 74, 89, 71, 91, + 73, 91, 78, 89, 86, 80, 92, 66, 93, 64, 102, 59, 103, 60, + 104, 60, 117, 52, 123, 44, 138, 35, 133, 31, 97, 38, 77, 45 + } + }, + /*480 sample frames.*/ + new byte[][]{ + /*Inter*/ + new byte[]{ + 61, 90, 93, 60, 105, 42, 107, 41, 110, 45, 116, 38, 113, 38, + 112, 38, 124, 26, 132, 27, 136, 19, 140, 20, 155, 14, 159, 16, + 158, 18, 170, 13, 177, 10, 187, 8, 192, 6, 175, 9, 159, 10 + }, + /*Intra*/ + new byte[]{ + 21, 178, 59, 110, 71, 86, 75, 85, 84, 83, 91, 66, 88, 73, + 87, 72, 92, 75, 98, 72, 105, 58, 107, 54, 115, 52, 114, 55, + 112, 56, 129, 51, 132, 40, 150, 33, 140, 29, 98, 35, 77, 42 + } + }, + /*960 sample frames.*/ + new byte[][]{ + /*Inter*/ + new byte[]{ + 42, 121, 96, 66, 108, 43, 111, 40, 117, 44, 123, 32, 120, 36, + 119, 33, 127, 33, 134, 34, 139, 21, 147, 23, 152, 20, 158, 25, + 154, 26, 166, 21, 173, 16, 184, 13, 184, 10, 150, 13, 139, 15 + }, + /*Intra*/ + new byte[]{ + 22, 178, 63, 114, 74, 82, 84, 83, 92, 82, 103, 62, 96, 72, + 96, 67, 101, 73, 107, 72, 113, 55, 118, 52, 125, 52, 118, 52, + 117, 55, 135, 49, 137, 39, 157, 32, 145, 29, 97, 33, 77, 40 + } + } + }; + + internal static readonly sbyte[][] tf_select_table = { + new sbyte[] { 0, -1, 0, -1, 0,-1, 0,-1}, + new sbyte[] { 0, -1, 0, -2, 1, 0, 1,-1}, + new sbyte[] { 0, -2, 0, -3, 2, 0, 1,-1}, + new sbyte[] { 0, -2, 0, -3, 3, 0, 1,-1}, + }; + + internal static readonly byte[] trim_icdf = { 126, 124, 119, 109, 87, 41, 19, 9, 4, 2, 0 }; + /* Probs: NONE: 21.875%, LIGHT: 6.25%, NORMAL: 65.625%, AGGRESSIVE: 6.25% */ + internal static readonly byte[] spread_icdf = { 25, 23, 2, 0 }; + + internal static readonly byte[] tapset_icdf = { 2, 1, 0 }; + + /* Bit allocation table in units of 1/32 bit/sample (0.1875 dB SNR) */ + internal static readonly byte[] band_allocation = { + /*0 200 400 600 800 1k 1.2 1.4 1.6 2k 2.4 2.8 3.2 4k 4.8 5.6 6.8 8k 9.6 12k 15.6 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 90, 80, 75, 69, 63, 56, 49, 40, 34, 29, 20, 18, 10, 0, 0, 0, 0, 0, 0, 0, 0, + 110,100, 90, 84, 78, 71, 65, 58, 51, 45, 39, 32, 26, 20, 12, 0, 0, 0, 0, 0, 0, + 118,110,103, 93, 86, 80, 75, 70, 65, 59, 53, 47, 40, 31, 23, 15, 4, 0, 0, 0, 0, + 126,119,112,104, 95, 89, 83, 78, 72, 66, 60, 54, 47, 39, 32, 25, 17, 12, 1, 0, 0, + 134,127,120,114,103, 97, 91, 85, 78, 72, 66, 60, 54, 47, 41, 35, 29, 23, 16, 10, 1, + 144,137,130,124,113,107,101, 95, 88, 82, 76, 70, 64, 57, 51, 45, 39, 33, 26, 15, 1, + 152,145,138,132,123,117,111,105, 98, 92, 86, 80, 74, 67, 61, 55, 49, 43, 36, 20, 1, + 162,155,148,142,133,127,121,115,108,102, 96, 90, 84, 77, 71, 65, 59, 53, 46, 30, 1, + 172,165,158,152,143,137,131,125,118,112,106,100, 94, 87, 81, 75, 69, 63, 56, 45, 20, + 200,200,200,200,200,200,200,200,198,193,188,183,178,173,168,163,158,153,148,129,104, + }; + + /*For each V(N,K) supported, we will access element U(min(N,K+1),max(N,K+1)). + Thus, the number of entries in row I is the larger of the maximum number of + pulses we will ever allocate for a given N=I (K=128, or however many fit in + 32 bits, whichever is smaller), plus one, and the maximum N for which + K=I-1 pulses fit in 32 bits. + The largest band size in an Opus Custom mode is 208. + Otherwise, we can limit things to the set of N which can be achieved by + splitting a band from a standard Opus mode: 176, 144, 96, 88, 72, 64, 48, + 44, 36, 32, 24, 22, 18, 16, 8, 4, 2).*/ + internal static readonly uint[] CELT_PVQ_U_DATA ={ + /*N=0, K=0...176:*/ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /*N=1, K=1...176:*/ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + /*N=2, K=2...176:*/ + 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, + 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, + 81, 83, 85, 87, 89, 91, 93, 95, 97, 99, 101, 103, 105, 107, 109, 111, 113, + 115, 117, 119, 121, 123, 125, 127, 129, 131, 133, 135, 137, 139, 141, 143, + 145, 147, 149, 151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171, 173, + 175, 177, 179, 181, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203, + 205, 207, 209, 211, 213, 215, 217, 219, 221, 223, 225, 227, 229, 231, 233, + 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255, 257, 259, 261, 263, + 265, 267, 269, 271, 273, 275, 277, 279, 281, 283, 285, 287, 289, 291, 293, + 295, 297, 299, 301, 303, 305, 307, 309, 311, 313, 315, 317, 319, 321, 323, + 325, 327, 329, 331, 333, 335, 337, 339, 341, 343, 345, 347, 349, 351, + /*N=3, K=3...176:*/ + 13, 25, 41, 61, 85, 113, 145, 181, 221, 265, 313, 365, 421, 481, 545, 613, + 685, 761, 841, 925, 1013, 1105, 1201, 1301, 1405, 1513, 1625, 1741, 1861, + 1985, 2113, 2245, 2381, 2521, 2665, 2813, 2965, 3121, 3281, 3445, 3613, 3785, + 3961, 4141, 4325, 4513, 4705, 4901, 5101, 5305, 5513, 5725, 5941, 6161, 6385, + 6613, 6845, 7081, 7321, 7565, 7813, 8065, 8321, 8581, 8845, 9113, 9385, 9661, + 9941, 10225, 10513, 10805, 11101, 11401, 11705, 12013, 12325, 12641, 12961, + 13285, 13613, 13945, 14281, 14621, 14965, 15313, 15665, 16021, 16381, 16745, + 17113, 17485, 17861, 18241, 18625, 19013, 19405, 19801, 20201, 20605, 21013, + 21425, 21841, 22261, 22685, 23113, 23545, 23981, 24421, 24865, 25313, 25765, + 26221, 26681, 27145, 27613, 28085, 28561, 29041, 29525, 30013, 30505, 31001, + 31501, 32005, 32513, 33025, 33541, 34061, 34585, 35113, 35645, 36181, 36721, + 37265, 37813, 38365, 38921, 39481, 40045, 40613, 41185, 41761, 42341, 42925, + 43513, 44105, 44701, 45301, 45905, 46513, 47125, 47741, 48361, 48985, 49613, + 50245, 50881, 51521, 52165, 52813, 53465, 54121, 54781, 55445, 56113, 56785, + 57461, 58141, 58825, 59513, 60205, 60901, 61601, + /*N=4, K=4...176:*/ + 63, 129, 231, 377, 575, 833, 1159, 1561, 2047, 2625, 3303, 4089, 4991, 6017, + 7175, 8473, 9919, 11521, 13287, 15225, 17343, 19649, 22151, 24857, 27775, + 30913, 34279, 37881, 41727, 45825, 50183, 54809, 59711, 64897, 70375, 76153, + 82239, 88641, 95367, 102425, 109823, 117569, 125671, 134137, 142975, 152193, + 161799, 171801, 182207, 193025, 204263, 215929, 228031, 240577, 253575, + 267033, 280959, 295361, 310247, 325625, 341503, 357889, 374791, 392217, + 410175, 428673, 447719, 467321, 487487, 508225, 529543, 551449, 573951, + 597057, 620775, 645113, 670079, 695681, 721927, 748825, 776383, 804609, + 833511, 863097, 893375, 924353, 956039, 988441, 1021567, 1055425, 1090023, + 1125369, 1161471, 1198337, 1235975, 1274393, 1313599, 1353601, 1394407, + 1436025, 1478463, 1521729, 1565831, 1610777, 1656575, 1703233, 1750759, + 1799161, 1848447, 1898625, 1949703, 2001689, 2054591, 2108417, 2163175, + 2218873, 2275519, 2333121, 2391687, 2451225, 2511743, 2573249, 2635751, + 2699257, 2763775, 2829313, 2895879, 2963481, 3032127, 3101825, 3172583, + 3244409, 3317311, 3391297, 3466375, 3542553, 3619839, 3698241, 3777767, + 3858425, 3940223, 4023169, 4107271, 4192537, 4278975, 4366593, 4455399, + 4545401, 4636607, 4729025, 4822663, 4917529, 5013631, 5110977, 5209575, + 5309433, 5410559, 5512961, 5616647, 5721625, 5827903, 5935489, 6044391, + 6154617, 6266175, 6379073, 6493319, 6608921, 6725887, 6844225, 6963943, + 7085049, 7207551, + /*N=5, K=5...176:*/ + 321, 681, 1289, 2241, 3649, 5641, 8361, 11969, 16641, 22569, 29961, 39041, + 50049, 63241, 78889, 97281, 118721, 143529, 172041, 204609, 241601, 283401, + 330409, 383041, 441729, 506921, 579081, 658689, 746241, 842249, 947241, + 1061761, 1186369, 1321641, 1468169, 1626561, 1797441, 1981449, 2179241, + 2391489, 2618881, 2862121, 3121929, 3399041, 3694209, 4008201, 4341801, + 4695809, 5071041, 5468329, 5888521, 6332481, 6801089, 7295241, 7815849, + 8363841, 8940161, 9545769, 10181641, 10848769, 11548161, 12280841, 13047849, + 13850241, 14689089, 15565481, 16480521, 17435329, 18431041, 19468809, + 20549801, 21675201, 22846209, 24064041, 25329929, 26645121, 28010881, + 29428489, 30899241, 32424449, 34005441, 35643561, 37340169, 39096641, + 40914369, 42794761, 44739241, 46749249, 48826241, 50971689, 53187081, + 55473921, 57833729, 60268041, 62778409, 65366401, 68033601, 70781609, + 73612041, 76526529, 79526721, 82614281, 85790889, 89058241, 92418049, + 95872041, 99421961, 103069569, 106816641, 110664969, 114616361, 118672641, + 122835649, 127107241, 131489289, 135983681, 140592321, 145317129, 150160041, + 155123009, 160208001, 165417001, 170752009, 176215041, 181808129, 187533321, + 193392681, 199388289, 205522241, 211796649, 218213641, 224775361, 231483969, + 238341641, 245350569, 252512961, 259831041, 267307049, 274943241, 282741889, + 290705281, 298835721, 307135529, 315607041, 324252609, 333074601, 342075401, + 351257409, 360623041, 370174729, 379914921, 389846081, 399970689, 410291241, + 420810249, 431530241, 442453761, 453583369, 464921641, 476471169, 488234561, + 500214441, 512413449, 524834241, 537479489, 550351881, 563454121, 576788929, + 590359041, 604167209, 618216201, 632508801, + /*N=6, K=6...96:*/ + 1683, 3653, 7183, 13073, 22363, 36365, 56695, 85305, 124515, 177045, 246047, + 335137, 448427, 590557, 766727, 982729, 1244979, 1560549, 1937199, 2383409, + 2908411, 3522221, 4235671, 5060441, 6009091, 7095093, 8332863, 9737793, + 11326283, 13115773, 15124775, 17372905, 19880915, 22670725, 25765455, + 29189457, 32968347, 37129037, 41699767, 46710137, 52191139, 58175189, + 64696159, 71789409, 79491819, 87841821, 96879431, 106646281, 117185651, + 128542501, 140763503, 153897073, 167993403, 183104493, 199284183, 216588185, + 235074115, 254801525, 275831935, 298228865, 322057867, 347386557, 374284647, + 402823977, 433078547, 465124549, 499040399, 534906769, 572806619, 612825229, + 655050231, 699571641, 746481891, 795875861, 847850911, 902506913, 959946283, + 1020274013, 1083597703, 1150027593, 1219676595, 1292660325, 1369097135, + 1449108145, 1532817275, 1620351277, 1711839767, 1807415257, 1907213187, + 2011371957, 2120032959, + /*N=7, K=7...54*/ + 8989, 19825, 40081, 75517, 134245, 227305, 369305, 579125, 880685, 1303777, + 1884961, 2668525, 3707509, 5064793, 6814249, 9041957, 11847485, 15345233, + 19665841, 24957661, 31388293, 39146185, 48442297, 59511829, 72616013, + 88043969, 106114625, 127178701, 151620757, 179861305, 212358985, 249612805, + 292164445, 340600625, 395555537, 457713341, 527810725, 606639529, 695049433, + 793950709, 904317037, 1027188385, 1163673953, 1314955181, 1482288821, + 1667010073, 1870535785, 2094367717, + /*N=8, K=8...37*/ + 48639, 108545, 224143, 433905, 795455, 1392065, 2340495, 3800305, 5984767, + 9173505, 13726991, 20103025, 28875327, 40754369, 56610575, 77500017, + 104692735, 139703809, 184327311, 240673265, 311207743, 398796225, 506750351, + 638878193, 799538175, 993696769, 1226990095, 1505789553, 1837271615, + 2229491905U, + /*N=9, K=9...28:*/ + 265729, 598417, 1256465, 2485825, 4673345, 8405905, 14546705, 24331777, + 39490049, 62390545, 96220561, 145198913, 214828609, 312193553, 446304145, + 628496897, 872893441, 1196924561, 1621925137, 2173806145U, + /*N=10, K=10...24:*/ + 1462563, 3317445, 7059735, 14218905, 27298155, 50250765, 89129247, 152951073, + 254831667, 413442773, 654862247, 1014889769, 1541911931, 2300409629U, + 3375210671U, + /*N=11, K=11...19:*/ + 8097453, 18474633, 39753273, 81270333, 158819253, 298199265, 540279585, + 948062325, 1616336765, + /*N=12, K=12...18:*/ + 45046719, 103274625, 224298231, 464387817, 921406335, 1759885185, + 3248227095U, + /*N=13, K=13...16:*/ + 251595969, 579168825, 1267854873, 2653649025U, + /*N=14, K=14:*/ + 1409933619 + }; + + internal static readonly int[] window120 = { + 2, 20, 55, 108, 178, + 266, 372, 494, 635, 792, + 966, 1157, 1365, 1590, 1831, + 2089, 2362, 2651, 2956, 3276, + 3611, 3961, 4325, 4703, 5094, + 5499, 5916, 6346, 6788, 7241, + 7705, 8179, 8663, 9156, 9657, + 10167, 10684, 11207, 11736, 12271, + 12810, 13353, 13899, 14447, 14997, + 15547, 16098, 16648, 17197, 17744, + 18287, 18827, 19363, 19893, 20418, + 20936, 21447, 21950, 22445, 22931, + 23407, 23874, 24330, 24774, 25208, + 25629, 26039, 26435, 26819, 27190, + 27548, 27893, 28224, 28541, 28845, + 29135, 29411, 29674, 29924, 30160, + 30384, 30594, 30792, 30977, 31151, + 31313, 31463, 31602, 31731, 31849, + 31958, 32057, 32148, 32229, 32303, + 32370, 32429, 32481, 32528, 32568, + 32604, 32634, 32661, 32683, 32701, + 32717, 32729, 32740, 32748, 32754, + 32758, 32762, 32764, 32766, 32767, + 32767, 32767, 32767, 32767, 32767, + }; + + internal static readonly short[] logN400 = + { 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 16, 16, 16, 21, 21, 24, 29, 34, 36, }; + + internal static readonly short[] cache_index50 = { + -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 41, 41, 41, + 82, 82, 123, 164, 200, 222, 0, 0, 0, 0, 0, 0, 0, 0, 41, + 41, 41, 41, 123, 123, 123, 164, 164, 240, 266, 283, 295, 41, 41, 41, + 41, 41, 41, 41, 41, 123, 123, 123, 123, 240, 240, 240, 266, 266, 305, + 318, 328, 336, 123, 123, 123, 123, 123, 123, 123, 123, 240, 240, 240, 240, + 305, 305, 305, 318, 318, 343, 351, 358, 364, 240, 240, 240, 240, 240, 240, + 240, 240, 305, 305, 305, 305, 343, 343, 343, 351, 351, 370, 376, 382, 387, + }; + + internal static readonly byte[] cache_bits50 = { +40, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 40, 15, 23, 28, + 31, 34, 36, 38, 39, 41, 42, 43, 44, 45, 46, 47, 47, 49, 50, + 51, 52, 53, 54, 55, 55, 57, 58, 59, 60, 61, 62, 63, 63, 65, + 66, 67, 68, 69, 70, 71, 71, 40, 20, 33, 41, 48, 53, 57, 61, + 64, 66, 69, 71, 73, 75, 76, 78, 80, 82, 85, 87, 89, 91, 92, + 94, 96, 98, 101, 103, 105, 107, 108, 110, 112, 114, 117, 119, 121, 123, + 124, 126, 128, 40, 23, 39, 51, 60, 67, 73, 79, 83, 87, 91, 94, + 97, 100, 102, 105, 107, 111, 115, 118, 121, 124, 126, 129, 131, 135, 139, + 142, 145, 148, 150, 153, 155, 159, 163, 166, 169, 172, 174, 177, 179, 35, + 28, 49, 65, 78, 89, 99, 107, 114, 120, 126, 132, 136, 141, 145, 149, + 153, 159, 165, 171, 176, 180, 185, 189, 192, 199, 205, 211, 216, 220, 225, + 229, 232, 239, 245, 251, 21, 33, 58, 79, 97, 112, 125, 137, 148, 157, + 166, 174, 182, 189, 195, 201, 207, 217, 227, 235, 243, 251, 17, 35, 63, + 86, 106, 123, 139, 152, 165, 177, 187, 197, 206, 214, 222, 230, 237, 250, + 25, 31, 55, 75, 91, 105, 117, 128, 138, 146, 154, 161, 168, 174, 180, + 185, 190, 200, 208, 215, 222, 229, 235, 240, 245, 255, 16, 36, 65, 89, + 110, 128, 144, 159, 173, 185, 196, 207, 217, 226, 234, 242, 250, 11, 41, + 74, 103, 128, 151, 172, 191, 209, 225, 241, 255, 9, 43, 79, 110, 138, + 163, 186, 207, 227, 246, 12, 39, 71, 99, 123, 144, 164, 182, 198, 214, + 228, 241, 253, 9, 44, 81, 113, 142, 168, 192, 214, 235, 255, 7, 49, + 90, 127, 160, 191, 220, 247, 6, 51, 95, 134, 170, 203, 234, 7, 47, + 87, 123, 155, 184, 212, 237, 6, 52, 97, 137, 174, 208, 240, 5, 57, + 106, 151, 192, 231, 5, 59, 111, 158, 202, 243, 5, 55, 103, 147, 187, + 224, 5, 60, 113, 161, 206, 248, 4, 65, 122, 175, 224, 4, 67, 127, + 182, 234, }; + + internal static readonly byte[] cache_caps50 = { + 224, 224, 224, 224, 224, 224, 224, 224, 160, 160, 160, 160, 185, 185, 185, + 178, 178, 168, 134, 61, 37, 224, 224, 224, 224, 224, 224, 224, 224, 240, + 240, 240, 240, 207, 207, 207, 198, 198, 183, 144, 66, 40, 160, 160, 160, + 160, 160, 160, 160, 160, 185, 185, 185, 185, 193, 193, 193, 183, 183, 172, + 138, 64, 38, 240, 240, 240, 240, 240, 240, 240, 240, 207, 207, 207, 207, + 204, 204, 204, 193, 193, 180, 143, 66, 40, 185, 185, 185, 185, 185, 185, + 185, 185, 193, 193, 193, 193, 193, 193, 193, 183, 183, 172, 138, 65, 39, + 207, 207, 207, 207, 207, 207, 207, 207, 204, 204, 204, 204, 201, 201, 201, + 188, 188, 176, 141, 66, 40, 193, 193, 193, 193, 193, 193, 193, 193, 193, + 193, 193, 193, 194, 194, 194, 184, 184, 173, 139, 65, 39, 204, 204, 204, + 204, 204, 204, 204, 204, 201, 201, 201, 201, 198, 198, 198, 187, 187, 175, + 140, 66, 40, }; + + internal static readonly short[] fft_twiddles48000_960 = + new short[] { + 32767, 0, 32766, -429, + 32757, -858, 32743, -1287, + 32724, -1715, 32698, -2143, + 32667, -2570, 32631, -2998, + 32588, -3425, 32541, -3851, + 32488, -4277, 32429, -4701, + 32364, -5125, 32295, -5548, + 32219, -5971, 32138, -6393, + 32051, -6813, 31960, -7231, + 31863, -7650, 31760, -8067, + 31652, -8481, 31539, -8895, + 31419, -9306, 31294, -9716, + 31165, -10126, 31030, -10532, + 30889, -10937, 30743, -11340, + 30592, -11741, 30436, -12141, + 30274, -12540, 30107, -12935, + 29936, -13328, 29758, -13718, + 29577, -14107, 29390, -14493, + 29197, -14875, 29000, -15257, + 28797, -15635, 28590, -16010, + 28379, -16384, 28162, -16753, + 27940, -17119, 27714, -17484, + 27482, -17845, 27246, -18205, + 27006, -18560, 26760, -18911, + 26510, -19260, 26257, -19606, + 25997, -19947, 25734, -20286, + 25466, -20621, 25194, -20952, + 24918, -21281, 24637, -21605, + 24353, -21926, 24063, -22242, + 23770, -22555, 23473, -22865, + 23171, -23171, 22866, -23472, + 22557, -23769, 22244, -24063, + 21927, -24352, 21606, -24636, + 21282, -24917, 20954, -25194, + 20622, -25465, 20288, -25733, + 19949, -25997, 19607, -26255, + 19261, -26509, 18914, -26760, + 18561, -27004, 18205, -27246, + 17846, -27481, 17485, -27713, + 17122, -27940, 16755, -28162, + 16385, -28378, 16012, -28590, + 15636, -28797, 15258, -28999, + 14878, -29197, 14494, -29389, + 14108, -29576, 13720, -29757, + 13329, -29934, 12937, -30107, + 12540, -30274, 12142, -30435, + 11744, -30592, 11342, -30743, + 10939, -30889, 10534, -31030, + 10127, -31164, 9718, -31294, + 9307, -31418, 8895, -31537, + 8482, -31652, 8067, -31759, + 7650, -31862, 7233, -31960, + 6815, -32051, 6393, -32138, + 5973, -32219, 5549, -32294, + 5127, -32364, 4703, -32429, + 4278, -32487, 3852, -32541, + 3426, -32588, 2999, -32630, + 2572, -32667, 2144, -32698, + 1716, -32724, 1287, -32742, + 860, -32757, 430, -32766, + 0, -32767, -429, -32766, + -858, -32757, -1287, -32743, + -1715, -32724, -2143, -32698, + -2570, -32667, -2998, -32631, + -3425, -32588, -3851, -32541, + -4277, -32488, -4701, -32429, + -5125, -32364, -5548, -32295, + -5971, -32219, -6393, -32138, + -6813, -32051, -7231, -31960, + -7650, -31863, -8067, -31760, + -8481, -31652, -8895, -31539, + -9306, -31419, -9716, -31294, + -10126, -31165, -10532, -31030, + -10937, -30889, -11340, -30743, + -11741, -30592, -12141, -30436, + -12540, -30274, -12935, -30107, + -13328, -29936, -13718, -29758, + -14107, -29577, -14493, -29390, + -14875, -29197, -15257, -29000, + -15635, -28797, -16010, -28590, + -16384, -28379, -16753, -28162, + -17119, -27940, -17484, -27714, + -17845, -27482, -18205, -27246, + -18560, -27006, -18911, -26760, + -19260, -26510, -19606, -26257, + -19947, -25997, -20286, -25734, + -20621, -25466, -20952, -25194, + -21281, -24918, -21605, -24637, + -21926, -24353, -22242, -24063, + -22555, -23770, -22865, -23473, + -23171, -23171, -23472, -22866, + -23769, -22557, -24063, -22244, + -24352, -21927, -24636, -21606, + -24917, -21282, -25194, -20954, + -25465, -20622, -25733, -20288, + -25997, -19949, -26255, -19607, + -26509, -19261, -26760, -18914, + -27004, -18561, -27246, -18205, + -27481, -17846, -27713, -17485, + -27940, -17122, -28162, -16755, + -28378, -16385, -28590, -16012, + -28797, -15636, -28999, -15258, + -29197, -14878, -29389, -14494, + -29576, -14108, -29757, -13720, + -29934, -13329, -30107, -12937, + -30274, -12540, -30435, -12142, + -30592, -11744, -30743, -11342, + -30889, -10939, -31030, -10534, + -31164, -10127, -31294, -9718, + -31418, -9307, -31537, -8895, + -31652, -8482, -31759, -8067, + -31862, -7650, -31960, -7233, + -32051, -6815, -32138, -6393, + -32219, -5973, -32294, -5549, + -32364, -5127, -32429, -4703, + -32487, -4278, -32541, -3852, + -32588, -3426, -32630, -2999, + -32667, -2572, -32698, -2144, + -32724, -1716, -32742, -1287, + -32757, -860, -32766, -430, + -32767, 0, -32766, 429, + -32757, 858, -32743, 1287, + -32724, 1715, -32698, 2143, + -32667, 2570, -32631, 2998, + -32588, 3425, -32541, 3851, + -32488, 4277, -32429, 4701, + -32364, 5125, -32295, 5548, + -32219, 5971, -32138, 6393, + -32051, 6813, -31960, 7231, + -31863, 7650, -31760, 8067, + -31652, 8481, -31539, 8895, + -31419, 9306, -31294, 9716, + -31165, 10126, -31030, 10532, + -30889, 10937, -30743, 11340, + -30592, 11741, -30436, 12141, + -30274, 12540, -30107, 12935, + -29936, 13328, -29758, 13718, + -29577, 14107, -29390, 14493, + -29197, 14875, -29000, 15257, + -28797, 15635, -28590, 16010, + -28379, 16384, -28162, 16753, + -27940, 17119, -27714, 17484, + -27482, 17845, -27246, 18205, + -27006, 18560, -26760, 18911, + -26510, 19260, -26257, 19606, + -25997, 19947, -25734, 20286, + -25466, 20621, -25194, 20952, + -24918, 21281, -24637, 21605, + -24353, 21926, -24063, 22242, + -23770, 22555, -23473, 22865, + -23171, 23171, -22866, 23472, + -22557, 23769, -22244, 24063, + -21927, 24352, -21606, 24636, + -21282, 24917, -20954, 25194, + -20622, 25465, -20288, 25733, + -19949, 25997, -19607, 26255, + -19261, 26509, -18914, 26760, + -18561, 27004, -18205, 27246, + -17846, 27481, -17485, 27713, + -17122, 27940, -16755, 28162, + -16385, 28378, -16012, 28590, + -15636, 28797, -15258, 28999, + -14878, 29197, -14494, 29389, + -14108, 29576, -13720, 29757, + -13329, 29934, -12937, 30107, + -12540, 30274, -12142, 30435, + -11744, 30592, -11342, 30743, + -10939, 30889, -10534, 31030, + -10127, 31164, -9718, 31294, + -9307, 31418, -8895, 31537, + -8482, 31652, -8067, 31759, + -7650, 31862, -7233, 31960, + -6815, 32051, -6393, 32138, + -5973, 32219, -5549, 32294, + -5127, 32364, -4703, 32429, + -4278, 32487, -3852, 32541, + -3426, 32588, -2999, 32630, + -2572, 32667, -2144, 32698, + -1716, 32724, -1287, 32742, + -860, 32757, -430, 32766, + 0, 32767, 429, 32766, + 858, 32757, 1287, 32743, + 1715, 32724, 2143, 32698, + 2570, 32667, 2998, 32631, + 3425, 32588, 3851, 32541, + 4277, 32488, 4701, 32429, + 5125, 32364, 5548, 32295, + 5971, 32219, 6393, 32138, + 6813, 32051, 7231, 31960, + 7650, 31863, 8067, 31760, + 8481, 31652, 8895, 31539, + 9306, 31419, 9716, 31294, + 10126, 31165, 10532, 31030, + 10937, 30889, 11340, 30743, + 11741, 30592, 12141, 30436, + 12540, 30274, 12935, 30107, + 13328, 29936, 13718, 29758, + 14107, 29577, 14493, 29390, + 14875, 29197, 15257, 29000, + 15635, 28797, 16010, 28590, + 16384, 28379, 16753, 28162, + 17119, 27940, 17484, 27714, + 17845, 27482, 18205, 27246, + 18560, 27006, 18911, 26760, + 19260, 26510, 19606, 26257, + 19947, 25997, 20286, 25734, + 20621, 25466, 20952, 25194, + 21281, 24918, 21605, 24637, + 21926, 24353, 22242, 24063, + 22555, 23770, 22865, 23473, + 23171, 23171, 23472, 22866, + 23769, 22557, 24063, 22244, + 24352, 21927, 24636, 21606, + 24917, 21282, 25194, 20954, + 25465, 20622, 25733, 20288, + 25997, 19949, 26255, 19607, + 26509, 19261, 26760, 18914, + 27004, 18561, 27246, 18205, + 27481, 17846, 27713, 17485, + 27940, 17122, 28162, 16755, + 28378, 16385, 28590, 16012, + 28797, 15636, 28999, 15258, + 29197, 14878, 29389, 14494, + 29576, 14108, 29757, 13720, + 29934, 13329, 30107, 12937, + 30274, 12540, 30435, 12142, + 30592, 11744, 30743, 11342, + 30889, 10939, 31030, 10534, + 31164, 10127, 31294, 9718, + 31418, 9307, 31537, 8895, + 31652, 8482, 31759, 8067, + 31862, 7650, 31960, 7233, + 32051, 6815, 32138, 6393, + 32219, 5973, 32294, 5549, + 32364, 5127, 32429, 4703, + 32487, 4278, 32541, 3852, + 32588, 3426, 32630, 2999, + 32667, 2572, 32698, 2144, + 32724, 1716, 32742, 1287, + 32757, 860, 32766, 430 + }; + + internal static readonly short[] fft_bitrev480 = { + 0, 96, 192, 288, 384, 32, 128, 224, 320, 416, 64, 160, 256, 352, 448, + 8, 104, 200, 296, 392, 40, 136, 232, 328, 424, 72, 168, 264, 360, 456, + 16, 112, 208, 304, 400, 48, 144, 240, 336, 432, 80, 176, 272, 368, 464, + 24, 120, 216, 312, 408, 56, 152, 248, 344, 440, 88, 184, 280, 376, 472, + 4, 100, 196, 292, 388, 36, 132, 228, 324, 420, 68, 164, 260, 356, 452, + 12, 108, 204, 300, 396, 44, 140, 236, 332, 428, 76, 172, 268, 364, 460, + 20, 116, 212, 308, 404, 52, 148, 244, 340, 436, 84, 180, 276, 372, 468, + 28, 124, 220, 316, 412, 60, 156, 252, 348, 444, 92, 188, 284, 380, 476, + 1, 97, 193, 289, 385, 33, 129, 225, 321, 417, 65, 161, 257, 353, 449, + 9, 105, 201, 297, 393, 41, 137, 233, 329, 425, 73, 169, 265, 361, 457, + 17, 113, 209, 305, 401, 49, 145, 241, 337, 433, 81, 177, 273, 369, 465, + 25, 121, 217, 313, 409, 57, 153, 249, 345, 441, 89, 185, 281, 377, 473, + 5, 101, 197, 293, 389, 37, 133, 229, 325, 421, 69, 165, 261, 357, 453, + 13, 109, 205, 301, 397, 45, 141, 237, 333, 429, 77, 173, 269, 365, 461, + 21, 117, 213, 309, 405, 53, 149, 245, 341, 437, 85, 181, 277, 373, 469, + 29, 125, 221, 317, 413, 61, 157, 253, 349, 445, 93, 189, 285, 381, 477, + 2, 98, 194, 290, 386, 34, 130, 226, 322, 418, 66, 162, 258, 354, 450, + 10, 106, 202, 298, 394, 42, 138, 234, 330, 426, 74, 170, 266, 362, 458, + 18, 114, 210, 306, 402, 50, 146, 242, 338, 434, 82, 178, 274, 370, 466, + 26, 122, 218, 314, 410, 58, 154, 250, 346, 442, 90, 186, 282, 378, 474, + 6, 102, 198, 294, 390, 38, 134, 230, 326, 422, 70, 166, 262, 358, 454, + 14, 110, 206, 302, 398, 46, 142, 238, 334, 430, 78, 174, 270, 366, 462, + 22, 118, 214, 310, 406, 54, 150, 246, 342, 438, 86, 182, 278, 374, 470, + 30, 126, 222, 318, 414, 62, 158, 254, 350, 446, 94, 190, 286, 382, 478, + 3, 99, 195, 291, 387, 35, 131, 227, 323, 419, 67, 163, 259, 355, 451, + 11, 107, 203, 299, 395, 43, 139, 235, 331, 427, 75, 171, 267, 363, 459, + 19, 115, 211, 307, 403, 51, 147, 243, 339, 435, 83, 179, 275, 371, 467, + 27, 123, 219, 315, 411, 59, 155, 251, 347, 443, 91, 187, 283, 379, 475, + 7, 103, 199, 295, 391, 39, 135, 231, 327, 423, 71, 167, 263, 359, 455, + 15, 111, 207, 303, 399, 47, 143, 239, 335, 431, 79, 175, 271, 367, 463, + 23, 119, 215, 311, 407, 55, 151, 247, 343, 439, 87, 183, 279, 375, 471, + 31, 127, 223, 319, 415, 63, 159, 255, 351, 447, 95, 191, 287, 383, 479, + }; + + internal static readonly short[] fft_bitrev240 = { + 0, 48, 96, 144, 192, 16, 64, 112, 160, 208, 32, 80, 128, 176, 224, + 4, 52, 100, 148, 196, 20, 68, 116, 164, 212, 36, 84, 132, 180, 228, + 8, 56, 104, 152, 200, 24, 72, 120, 168, 216, 40, 88, 136, 184, 232, + 12, 60, 108, 156, 204, 28, 76, 124, 172, 220, 44, 92, 140, 188, 236, + 1, 49, 97, 145, 193, 17, 65, 113, 161, 209, 33, 81, 129, 177, 225, + 5, 53, 101, 149, 197, 21, 69, 117, 165, 213, 37, 85, 133, 181, 229, + 9, 57, 105, 153, 201, 25, 73, 121, 169, 217, 41, 89, 137, 185, 233, + 13, 61, 109, 157, 205, 29, 77, 125, 173, 221, 45, 93, 141, 189, 237, + 2, 50, 98, 146, 194, 18, 66, 114, 162, 210, 34, 82, 130, 178, 226, + 6, 54, 102, 150, 198, 22, 70, 118, 166, 214, 38, 86, 134, 182, 230, + 10, 58, 106, 154, 202, 26, 74, 122, 170, 218, 42, 90, 138, 186, 234, + 14, 62, 110, 158, 206, 30, 78, 126, 174, 222, 46, 94, 142, 190, 238, + 3, 51, 99, 147, 195, 19, 67, 115, 163, 211, 35, 83, 131, 179, 227, + 7, 55, 103, 151, 199, 23, 71, 119, 167, 215, 39, 87, 135, 183, 231, + 11, 59, 107, 155, 203, 27, 75, 123, 171, 219, 43, 91, 139, 187, 235, + 15, 63, 111, 159, 207, 31, 79, 127, 175, 223, 47, 95, 143, 191, 239, + }; + + internal static readonly short[] fft_bitrev120 = { + 0, 24, 48, 72, 96, 8, 32, 56, 80, 104, 16, 40, 64, 88, 112, + 4, 28, 52, 76, 100, 12, 36, 60, 84, 108, 20, 44, 68, 92, 116, + 1, 25, 49, 73, 97, 9, 33, 57, 81, 105, 17, 41, 65, 89, 113, + 5, 29, 53, 77, 101, 13, 37, 61, 85, 109, 21, 45, 69, 93, 117, + 2, 26, 50, 74, 98, 10, 34, 58, 82, 106, 18, 42, 66, 90, 114, + 6, 30, 54, 78, 102, 14, 38, 62, 86, 110, 22, 46, 70, 94, 118, + 3, 27, 51, 75, 99, 11, 35, 59, 83, 107, 19, 43, 67, 91, 115, + 7, 31, 55, 79, 103, 15, 39, 63, 87, 111, 23, 47, 71, 95, 119, + }; + + internal static readonly short[] fft_bitrev60 = { + 0, 12, 24, 36, 48, 4, 16, 28, 40, 52, 8, 20, 32, 44, 56, + 1, 13, 25, 37, 49, 5, 17, 29, 41, 53, 9, 21, 33, 45, 57, + 2, 14, 26, 38, 50, 6, 18, 30, 42, 54, 10, 22, 34, 46, 58, + 3, 15, 27, 39, 51, 7, 19, 31, 43, 55, 11, 23, 35, 47, 59, + }; + + + internal static readonly FFTState fft_state48000_960_0 = new FFTState() + { + nfft = 480, + scale = 17476, + scale_shift = 8, + shift = -1, + factors = new short[] { 5, 96, 3, 32, 4, 8, 2, 4, 4, 1, 0, 0, 0, 0, 0, 0, }, + bitrev = fft_bitrev480, + twiddles = fft_twiddles48000_960 + }; + + internal static readonly FFTState fft_state48000_960_1 = new FFTState() + { + nfft = 240, + scale = 17476, + scale_shift = 7, + shift = 1, + factors = new short[] { 5, 48, 3, 16, 4, 4, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, }, + bitrev = fft_bitrev240, + twiddles = fft_twiddles48000_960 + }; + + internal static readonly FFTState fft_state48000_960_2 = new FFTState() + { + nfft = 120, + scale = 17476, + scale_shift = 6, + shift = 2, + factors = new short[] { 5, 24, 3, 8, 2, 4, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, }, + bitrev = fft_bitrev120, + twiddles = fft_twiddles48000_960 + }; + + internal static readonly FFTState fft_state48000_960_3 = new FFTState() + { + nfft = 60, + scale = 17476, + scale_shift = 5, + shift = 3, + factors = new short[] { 5, 12, 3, 4, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + bitrev = fft_bitrev60, + twiddles = fft_twiddles48000_960 + }; + + internal static readonly short[] mdct_twiddles960 /*1800*/ = { + 32767, 32767, 32767, 32766, 32765, + 32763, 32761, 32759, 32756, 32753, + 32750, 32746, 32742, 32738, 32733, + 32728, 32722, 32717, 32710, 32704, + 32697, 32690, 32682, 32674, 32666, + 32657, 32648, 32639, 32629, 32619, + 32609, 32598, 32587, 32576, 32564, + 32552, 32539, 32526, 32513, 32500, + 32486, 32472, 32457, 32442, 32427, + 32411, 32395, 32379, 32362, 32345, + 32328, 32310, 32292, 32274, 32255, + 32236, 32217, 32197, 32177, 32157, + 32136, 32115, 32093, 32071, 32049, + 32027, 32004, 31981, 31957, 31933, + 31909, 31884, 31859, 31834, 31809, + 31783, 31756, 31730, 31703, 31676, + 31648, 31620, 31592, 31563, 31534, + 31505, 31475, 31445, 31415, 31384, + 31353, 31322, 31290, 31258, 31226, + 31193, 31160, 31127, 31093, 31059, + 31025, 30990, 30955, 30920, 30884, + 30848, 30812, 30775, 30738, 30701, + 30663, 30625, 30587, 30548, 30509, + 30470, 30430, 30390, 30350, 30309, + 30269, 30227, 30186, 30144, 30102, + 30059, 30016, 29973, 29930, 29886, + 29842, 29797, 29752, 29707, 29662, + 29616, 29570, 29524, 29477, 29430, + 29383, 29335, 29287, 29239, 29190, + 29142, 29092, 29043, 28993, 28943, + 28892, 28842, 28791, 28739, 28688, + 28636, 28583, 28531, 28478, 28425, + 28371, 28317, 28263, 28209, 28154, + 28099, 28044, 27988, 27932, 27876, + 27820, 27763, 27706, 27648, 27591, + 27533, 27474, 27416, 27357, 27298, + 27238, 27178, 27118, 27058, 26997, + 26936, 26875, 26814, 26752, 26690, + 26628, 26565, 26502, 26439, 26375, + 26312, 26247, 26183, 26119, 26054, + 25988, 25923, 25857, 25791, 25725, + 25658, 25592, 25524, 25457, 25389, + 25322, 25253, 25185, 25116, 25047, + 24978, 24908, 24838, 24768, 24698, + 24627, 24557, 24485, 24414, 24342, + 24270, 24198, 24126, 24053, 23980, + 23907, 23834, 23760, 23686, 23612, + 23537, 23462, 23387, 23312, 23237, + 23161, 23085, 23009, 22932, 22856, + 22779, 22701, 22624, 22546, 22468, + 22390, 22312, 22233, 22154, 22075, + 21996, 21916, 21836, 21756, 21676, + 21595, 21515, 21434, 21352, 21271, + 21189, 21107, 21025, 20943, 20860, + 20777, 20694, 20611, 20528, 20444, + 20360, 20276, 20192, 20107, 20022, + 19937, 19852, 19767, 19681, 19595, + 19509, 19423, 19336, 19250, 19163, + 19076, 18988, 18901, 18813, 18725, + 18637, 18549, 18460, 18372, 18283, + 18194, 18104, 18015, 17925, 17835, + 17745, 17655, 17565, 17474, 17383, + 17292, 17201, 17110, 17018, 16927, + 16835, 16743, 16650, 16558, 16465, + 16372, 16279, 16186, 16093, 15999, + 15906, 15812, 15718, 15624, 15529, + 15435, 15340, 15245, 15150, 15055, + 14960, 14864, 14769, 14673, 14577, + 14481, 14385, 14288, 14192, 14095, + 13998, 13901, 13804, 13706, 13609, + 13511, 13414, 13316, 13218, 13119, + 13021, 12923, 12824, 12725, 12626, + 12527, 12428, 12329, 12230, 12130, + 12030, 11930, 11831, 11730, 11630, + 11530, 11430, 11329, 11228, 11128, + 11027, 10926, 10824, 10723, 10622, + 10520, 10419, 10317, 10215, 10113, + 10011, 9909, 9807, 9704, 9602, + 9499, 9397, 9294, 9191, 9088, + 8985, 8882, 8778, 8675, 8572, + 8468, 8364, 8261, 8157, 8053, + 7949, 7845, 7741, 7637, 7532, + 7428, 7323, 7219, 7114, 7009, + 6905, 6800, 6695, 6590, 6485, + 6380, 6274, 6169, 6064, 5958, + 5853, 5747, 5642, 5536, 5430, + 5325, 5219, 5113, 5007, 4901, + 4795, 4689, 4583, 4476, 4370, + 4264, 4157, 4051, 3945, 3838, + 3732, 3625, 3518, 3412, 3305, + 3198, 3092, 2985, 2878, 2771, + 2664, 2558, 2451, 2344, 2237, + 2130, 2023, 1916, 1809, 1702, + 1594, 1487, 1380, 1273, 1166, + 1059, 952, 844, 737, 630, + 523, 416, 308, 201, 94, + -13, -121, -228, -335, -442, + -550, -657, -764, -871, -978, + -1086, -1193, -1300, -1407, -1514, + -1621, -1728, -1835, -1942, -2049, + -2157, -2263, -2370, -2477, -2584, + -2691, -2798, -2905, -3012, -3118, + -3225, -3332, -3439, -3545, -3652, + -3758, -3865, -3971, -4078, -4184, + -4290, -4397, -4503, -4609, -4715, + -4821, -4927, -5033, -5139, -5245, + -5351, -5457, -5562, -5668, -5774, + -5879, -5985, -6090, -6195, -6301, + -6406, -6511, -6616, -6721, -6826, + -6931, -7036, -7140, -7245, -7349, + -7454, -7558, -7663, -7767, -7871, + -7975, -8079, -8183, -8287, -8390, + -8494, -8597, -8701, -8804, -8907, + -9011, -9114, -9217, -9319, -9422, + -9525, -9627, -9730, -9832, -9934, + -10037, -10139, -10241, -10342, -10444, + -10546, -10647, -10748, -10850, -10951, + -11052, -11153, -11253, -11354, -11455, + -11555, -11655, -11756, -11856, -11955, + -12055, -12155, -12254, -12354, -12453, + -12552, -12651, -12750, -12849, -12947, + -13046, -13144, -13242, -13340, -13438, + -13536, -13633, -13731, -13828, -13925, + -14022, -14119, -14216, -14312, -14409, + -14505, -14601, -14697, -14793, -14888, + -14984, -15079, -15174, -15269, -15364, + -15459, -15553, -15647, -15741, -15835, + -15929, -16023, -16116, -16210, -16303, + -16396, -16488, -16581, -16673, -16766, + -16858, -16949, -17041, -17133, -17224, + -17315, -17406, -17497, -17587, -17678, + -17768, -17858, -17948, -18037, -18127, + -18216, -18305, -18394, -18483, -18571, + -18659, -18747, -18835, -18923, -19010, + -19098, -19185, -19271, -19358, -19444, + -19531, -19617, -19702, -19788, -19873, + -19959, -20043, -20128, -20213, -20297, + -20381, -20465, -20549, -20632, -20715, + -20798, -20881, -20963, -21046, -21128, + -21210, -21291, -21373, -21454, -21535, + -21616, -21696, -21776, -21856, -21936, + -22016, -22095, -22174, -22253, -22331, + -22410, -22488, -22566, -22643, -22721, + -22798, -22875, -22951, -23028, -23104, + -23180, -23256, -23331, -23406, -23481, + -23556, -23630, -23704, -23778, -23852, + -23925, -23998, -24071, -24144, -24216, + -24288, -24360, -24432, -24503, -24574, + -24645, -24716, -24786, -24856, -24926, + -24995, -25064, -25133, -25202, -25270, + -25339, -25406, -25474, -25541, -25608, + -25675, -25742, -25808, -25874, -25939, + -26005, -26070, -26135, -26199, -26264, + -26327, -26391, -26455, -26518, -26581, + -26643, -26705, -26767, -26829, -26891, + -26952, -27013, -27073, -27133, -27193, + -27253, -27312, -27372, -27430, -27489, + -27547, -27605, -27663, -27720, -27777, + -27834, -27890, -27946, -28002, -28058, + -28113, -28168, -28223, -28277, -28331, + -28385, -28438, -28491, -28544, -28596, + -28649, -28701, -28752, -28803, -28854, + -28905, -28955, -29006, -29055, -29105, + -29154, -29203, -29251, -29299, -29347, + -29395, -29442, -29489, -29535, -29582, + -29628, -29673, -29719, -29764, -29808, + -29853, -29897, -29941, -29984, -30027, + -30070, -30112, -30154, -30196, -30238, + -30279, -30320, -30360, -30400, -30440, + -30480, -30519, -30558, -30596, -30635, + -30672, -30710, -30747, -30784, -30821, + -30857, -30893, -30929, -30964, -30999, + -31033, -31068, -31102, -31135, -31168, + -31201, -31234, -31266, -31298, -31330, + -31361, -31392, -31422, -31453, -31483, + -31512, -31541, -31570, -31599, -31627, + -31655, -31682, -31710, -31737, -31763, + -31789, -31815, -31841, -31866, -31891, + -31915, -31939, -31963, -31986, -32010, + -32032, -32055, -32077, -32099, -32120, + -32141, -32162, -32182, -32202, -32222, + -32241, -32260, -32279, -32297, -32315, + -32333, -32350, -32367, -32383, -32399, + -32415, -32431, -32446, -32461, -32475, + -32489, -32503, -32517, -32530, -32542, + -32555, -32567, -32579, -32590, -32601, + -32612, -32622, -32632, -32641, -32651, + -32659, -32668, -32676, -32684, -32692, + -32699, -32706, -32712, -32718, -32724, + -32729, -32734, -32739, -32743, -32747, + -32751, -32754, -32757, -32760, -32762, + -32764, -32765, -32767, -32767, -32767, + 32767, 32767, 32765, 32761, 32756, + 32750, 32742, 32732, 32722, 32710, + 32696, 32681, 32665, 32647, 32628, + 32608, 32586, 32562, 32538, 32512, + 32484, 32455, 32425, 32393, 32360, + 32326, 32290, 32253, 32214, 32174, + 32133, 32090, 32046, 32001, 31954, + 31906, 31856, 31805, 31753, 31700, + 31645, 31588, 31530, 31471, 31411, + 31349, 31286, 31222, 31156, 31089, + 31020, 30951, 30880, 30807, 30733, + 30658, 30582, 30504, 30425, 30345, + 30263, 30181, 30096, 30011, 29924, + 29836, 29747, 29656, 29564, 29471, + 29377, 29281, 29184, 29086, 28987, + 28886, 28784, 28681, 28577, 28471, + 28365, 28257, 28147, 28037, 27925, + 27812, 27698, 27583, 27467, 27349, + 27231, 27111, 26990, 26868, 26744, + 26620, 26494, 26367, 26239, 26110, + 25980, 25849, 25717, 25583, 25449, + 25313, 25176, 25038, 24900, 24760, + 24619, 24477, 24333, 24189, 24044, + 23898, 23751, 23602, 23453, 23303, + 23152, 22999, 22846, 22692, 22537, + 22380, 22223, 22065, 21906, 21746, + 21585, 21423, 21261, 21097, 20933, + 20767, 20601, 20434, 20265, 20096, + 19927, 19756, 19584, 19412, 19239, + 19065, 18890, 18714, 18538, 18361, + 18183, 18004, 17824, 17644, 17463, + 17281, 17098, 16915, 16731, 16546, + 16361, 16175, 15988, 15800, 15612, + 15423, 15234, 15043, 14852, 14661, + 14469, 14276, 14083, 13889, 13694, + 13499, 13303, 13107, 12910, 12713, + 12515, 12317, 12118, 11918, 11718, + 11517, 11316, 11115, 10913, 10710, + 10508, 10304, 10100, 9896, 9691, + 9486, 9281, 9075, 8869, 8662, + 8455, 8248, 8040, 7832, 7623, + 7415, 7206, 6996, 6787, 6577, + 6366, 6156, 5945, 5734, 5523, + 5311, 5100, 4888, 4675, 4463, + 4251, 4038, 3825, 3612, 3399, + 3185, 2972, 2758, 2544, 2330, + 2116, 1902, 1688, 1474, 1260, + 1045, 831, 617, 402, 188, + -27, -241, -456, -670, -885, + -1099, -1313, -1528, -1742, -1956, + -2170, -2384, -2598, -2811, -3025, + -3239, -3452, -3665, -3878, -4091, + -4304, -4516, -4728, -4941, -5153, + -5364, -5576, -5787, -5998, -6209, + -6419, -6629, -6839, -7049, -7258, + -7467, -7676, -7884, -8092, -8300, + -8507, -8714, -8920, -9127, -9332, + -9538, -9743, -9947, -10151, -10355, + -10558, -10761, -10963, -11165, -11367, + -11568, -11768, -11968, -12167, -12366, + -12565, -12762, -12960, -13156, -13352, + -13548, -13743, -13937, -14131, -14324, + -14517, -14709, -14900, -15091, -15281, + -15470, -15659, -15847, -16035, -16221, + -16407, -16593, -16777, -16961, -17144, + -17326, -17508, -17689, -17869, -18049, + -18227, -18405, -18582, -18758, -18934, + -19108, -19282, -19455, -19627, -19799, + -19969, -20139, -20308, -20475, -20642, + -20809, -20974, -21138, -21301, -21464, + -21626, -21786, -21946, -22105, -22263, + -22420, -22575, -22730, -22884, -23037, + -23189, -23340, -23490, -23640, -23788, + -23935, -24080, -24225, -24369, -24512, + -24654, -24795, -24934, -25073, -25211, + -25347, -25482, -25617, -25750, -25882, + -26013, -26143, -26272, -26399, -26526, + -26651, -26775, -26898, -27020, -27141, + -27260, -27379, -27496, -27612, -27727, + -27841, -27953, -28065, -28175, -28284, + -28391, -28498, -28603, -28707, -28810, + -28911, -29012, -29111, -29209, -29305, + -29401, -29495, -29587, -29679, -29769, + -29858, -29946, -30032, -30118, -30201, + -30284, -30365, -30445, -30524, -30601, + -30677, -30752, -30825, -30897, -30968, + -31038, -31106, -31172, -31238, -31302, + -31365, -31426, -31486, -31545, -31602, + -31658, -31713, -31766, -31818, -31869, + -31918, -31966, -32012, -32058, -32101, + -32144, -32185, -32224, -32262, -32299, + -32335, -32369, -32401, -32433, -32463, + -32491, -32518, -32544, -32568, -32591, + -32613, -32633, -32652, -32669, -32685, + -32700, -32713, -32724, -32735, -32744, + -32751, -32757, -32762, -32766, -32767, + 32767, 32764, 32755, 32741, 32720, + 32694, 32663, 32626, 32583, 32535, + 32481, 32421, 32356, 32286, 32209, + 32128, 32041, 31948, 31850, 31747, + 31638, 31523, 31403, 31278, 31148, + 31012, 30871, 30724, 30572, 30415, + 30253, 30086, 29913, 29736, 29553, + 29365, 29172, 28974, 28771, 28564, + 28351, 28134, 27911, 27684, 27452, + 27216, 26975, 26729, 26478, 26223, + 25964, 25700, 25432, 25159, 24882, + 24601, 24315, 24026, 23732, 23434, + 23133, 22827, 22517, 22204, 21886, + 21565, 21240, 20912, 20580, 20244, + 19905, 19563, 19217, 18868, 18516, + 18160, 17802, 17440, 17075, 16708, + 16338, 15964, 15588, 15210, 14829, + 14445, 14059, 13670, 13279, 12886, + 12490, 12093, 11693, 11291, 10888, + 10482, 10075, 9666, 9255, 8843, + 8429, 8014, 7597, 7180, 6760, + 6340, 5919, 5496, 5073, 4649, + 4224, 3798, 3372, 2945, 2517, + 2090, 1661, 1233, 804, 375, + -54, -483, -911, -1340, -1768, + -2197, -2624, -3052, -3479, -3905, + -4330, -4755, -5179, -5602, -6024, + -6445, -6865, -7284, -7702, -8118, + -8533, -8946, -9358, -9768, -10177, + -10584, -10989, -11392, -11793, -12192, + -12589, -12984, -13377, -13767, -14155, + -14541, -14924, -15305, -15683, -16058, + -16430, -16800, -17167, -17531, -17892, + -18249, -18604, -18956, -19304, -19649, + -19990, -20329, -20663, -20994, -21322, + -21646, -21966, -22282, -22595, -22904, + -23208, -23509, -23806, -24099, -24387, + -24672, -24952, -25228, -25499, -25766, + -26029, -26288, -26541, -26791, -27035, + -27275, -27511, -27741, -27967, -28188, + -28405, -28616, -28823, -29024, -29221, + -29412, -29599, -29780, -29957, -30128, + -30294, -30455, -30611, -30761, -30906, + -31046, -31181, -31310, -31434, -31552, + -31665, -31773, -31875, -31972, -32063, + -32149, -32229, -32304, -32373, -32437, + -32495, -32547, -32594, -32635, -32671, + -32701, -32726, -32745, -32758, -32766, + 32767, 32754, 32717, 32658, 32577, + 32473, 32348, 32200, 32029, 31837, + 31624, 31388, 31131, 30853, 30553, + 30232, 29891, 29530, 29148, 28746, + 28324, 27883, 27423, 26944, 26447, + 25931, 25398, 24847, 24279, 23695, + 23095, 22478, 21846, 21199, 20538, + 19863, 19174, 18472, 17757, 17030, + 16291, 15541, 14781, 14010, 13230, + 12441, 11643, 10837, 10024, 9204, + 8377, 7545, 6708, 5866, 5020, + 4171, 3319, 2464, 1608, 751, + -107, -965, -1822, -2678, -3532, + -4383, -5232, -6077, -6918, -7754, + -8585, -9409, -10228, -11039, -11843, + -12639, -13426, -14204, -14972, -15730, + -16477, -17213, -17937, -18648, -19347, + -20033, -20705, -21363, -22006, -22634, + -23246, -23843, -24423, -24986, -25533, + -26062, -26573, -27066, -27540, -27995, + -28431, -28848, -29245, -29622, -29979, + -30315, -30630, -30924, -31197, -31449, + -31679, -31887, -32074, -32239, -32381, + -32501, -32600, -32675, -32729, -32759, + }; + + internal static readonly int[] intensity_thresholds = + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 off*/ + { 1, 2, 3, 4, 5, 6, 7, 8,16,24,36,44,50,56,62,67,72,79,88,106,134}; + internal static readonly int[] intensity_histeresis = + { 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 5, 6, 8, 8}; + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Celt/VQ.cs b/Libraries/Concentus/CSharp/Concentus/Celt/VQ.cs new file mode 100644 index 000000000..c4ca1dffb --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Celt/VQ.cs @@ -0,0 +1,430 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Celt +{ + using Concentus.Celt.Enums; + using Concentus.Celt.Structs; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using System.Diagnostics; + + internal static class VQ + { + internal static void exp_rotation1(int[] X, int X_ptr, int len, int stride, int c, int s) + { + int i; + int ms; + int Xptr; + Xptr = X_ptr; + ms = Inlines.NEG16(s); + for (i = 0; i < len - stride; i++) + { + int x1, x2; + x1 = X[Xptr]; + x2 = X[Xptr + stride]; + X[Xptr + stride] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MAC16_16(Inlines.MULT16_16(c, x2), s, x1), 15)); + X[Xptr] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MAC16_16(Inlines.MULT16_16(c, x1), ms, x2), 15)); + Xptr++; + } + Xptr = X_ptr + (len - 2 * stride - 1); + for (i = len - 2 * stride - 1; i >= 0; i--) + { + int x1, x2; + x1 = X[Xptr]; + x2 = X[Xptr + stride]; + X[Xptr + stride] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MAC16_16(Inlines.MULT16_16(c, x2), s, x1), 15)); + X[Xptr] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MAC16_16(Inlines.MULT16_16(c, x1), ms, x2), 15)); + Xptr--; + } + } + + private static int[] SPREAD_FACTOR = { 15, 10, 5 }; + + internal static void exp_rotation(int[] X, int X_ptr, int len, int dir, int stride, int K, int spread) + { + int i; + int c, s; + int gain, theta; + int stride2 = 0; + int factor; + + if (2 * K >= len || spread == Spread.SPREAD_NONE) + { + return; + } + + factor = SPREAD_FACTOR[spread - 1]; + + gain = (Inlines.celt_div((int)Inlines.MULT16_16(CeltConstants.Q15_ONE, len), (int)(len + factor * K))); + theta = Inlines.HALF16(Inlines.MULT16_16_Q15(gain, gain)); + + c = Inlines.celt_cos_norm(Inlines.EXTEND32(theta)); + s = Inlines.celt_cos_norm(Inlines.EXTEND32(Inlines.SUB16(CeltConstants.Q15ONE, theta))); /* sin(theta) */ + + if (len >= 8 * stride) + { + stride2 = 1; + /* This is just a simple (equivalent) way of computing sqrt(len/stride) with rounding. + It's basically incrementing long as (stride2+0.5)^2 < len/stride. */ + while ((stride2 * stride2 + stride2) * stride + (stride >> 2) < len) + { + stride2++; + } + } + + /*NOTE: As a minor optimization, we could be passing around log2(B), not B, for both this and for + extract_collapse_mask().*/ + len = Inlines.celt_udiv(len, stride); + for (i = 0; i < stride; i++) + { + if (dir < 0) + { + if (stride2 != 0) + { + exp_rotation1(X, X_ptr + (i * len), len, stride2, s, c); + } + + exp_rotation1(X, X_ptr + (i * len), len, 1, c, s); + } + else + { + exp_rotation1(X, X_ptr + (i * len), len, 1, c, (short)(0 - s)); + + if (stride2 != 0) + { + exp_rotation1(X, X_ptr + (i * len), len, stride2, s, (short)(0 - c)); + } + } + } + } + + /** Takes the pitch vector and the decoded residual vector, computes the gain + that will give ||p+g*y||=1 and mixes the residual with the pitch. */ + internal static void normalise_residual(int[] iy, int[] X, int X_ptr, + int N, int Ryy, int gain) + { + int i; + int k; + int t; + int g; + + k = Inlines.celt_ilog2(Ryy) >> 1; + t = Inlines.VSHR32(Ryy, 2 * (k - 7)); + g = Inlines.MULT16_16_P15(Inlines.celt_rsqrt_norm(t), gain); + + i = 0; + do + X[X_ptr + i] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MULT16_16(g, iy[i]), k + 1)); + while (++i < N); + } + + internal static uint extract_collapse_mask(int[] iy, int N, int B) + { + uint collapse_mask; + int N0; + int i; + if (B <= 1) + return 1; + /*NOTE: As a minor optimization, we could be passing around log2(B), not B, for both this and for + exp_rotation().*/ + N0 = Inlines.celt_udiv(N, B); + collapse_mask = 0; + i = 0; + do + { + int j; + uint tmp = 0; + j = 0; + do + { + tmp |= unchecked((uint)iy[i * N0 + j]); + } while (++j < N0); + + collapse_mask |= (tmp != 0 ? 1U : 0) << i; + } while (++i < B); + + return collapse_mask; + } + + internal static uint alg_quant(int[] X, int X_ptr, int N, int K, int spread, int B, EntropyCoder enc + ) + { + int[] y = new int[N]; + int[] iy = new int[N]; + int[] signx = new int[N]; + int i, j; + int s; + int pulsesLeft; + int sum; + int xy; + int yy; + uint collapse_mask; + + Inlines.OpusAssert(K > 0, "alg_quant() needs at least one pulse"); + Inlines.OpusAssert(N > 1, "alg_quant() needs at least two dimensions"); + + exp_rotation(X, X_ptr, N, 1, B, K, spread); + + /* Get rid of the sign */ + sum = 0; + j = 0; + do + { + int xpj = X_ptr + j; + + /* OPT: Make sure the following two lines result in conditional moves + rather than branches. */ + signx[j] = X[xpj] > 0 ? 1 : -1; + X[xpj] = Inlines.ABS16(X[xpj]); + + iy[j] = 0; + y[j] = 0; + } while (++j < N); + + xy = yy = 0; + + pulsesLeft = K; + + /* Do a pre-search by projecting on the pyramid */ + if (K > (N >> 1)) + { + int rcp; + j = 0; do + { + sum += X[X_ptr + j]; + } while (++j < N); + + /* If X is too small, just replace it with a pulse at 0 */ + /* Prevents infinities and NaNs from causing too many pulses + to be allocated. 64 is an approximation of infinity here. */ + if (sum <= K) + { + X[X_ptr] = ((short)(0.5 + (1.0f) * (((int)1) << (14))))/*Inlines.QCONST16(1.0f, 14)*/; + j = X_ptr + 1; + do + { + X[j] = 0; + } while (++j < N + X_ptr); + + sum = ((short)(0.5 + (1.0f) * (((int)1) << (14))))/*Inlines.QCONST16(1.0f, 14)*/; + } + + rcp = Inlines.EXTRACT16(Inlines.MULT16_32_Q16((K - 1), Inlines.celt_rcp(sum))); + j = 0; + + do + { + /* It's really important to round *towards zero* here */ + iy[j] = Inlines.MULT16_16_Q15(X[X_ptr + j], rcp); + y[j] = (int)iy[j]; + yy = (Inlines.MAC16_16(yy, y[j], y[j])); + xy = Inlines.MAC16_16(xy, X[X_ptr + j], y[j]); + y[j] *= 2; + pulsesLeft -= iy[j]; + } while (++j < N); + } + + Inlines.OpusAssert(pulsesLeft >= 1, "Allocated too many pulses in the quick pass"); + + /* This should never happen, but just in case it does (e.g. on silence) + we fill the first bin with pulses. */ + if (pulsesLeft > N + 3) + { + int tmp = (int)pulsesLeft; + yy = (Inlines.MAC16_16(yy, tmp, tmp)); + yy = (Inlines.MAC16_16(yy, tmp, y[0])); + iy[0] += pulsesLeft; + pulsesLeft = 0; + } + + s = 1; + for (i = 0; i < pulsesLeft; i++) + { + int best_id; + int best_num = 0 - CeltConstants.VERY_LARGE16; + int best_den = 0; + int rshift = 1 + Inlines.celt_ilog2(K - pulsesLeft + i + 1); + best_id = 0; + /* The squared magnitude term gets added anyway, so we might as well + add it outside the loop */ + yy = Inlines.ADD16(yy, 1); // opus bug - was add32 + j = 0; + do + { + int Rxy, Ryy; + /* Temporary sums of the new pulse(s) */ + Rxy = Inlines.EXTRACT16(Inlines.SHR32(Inlines.ADD32(xy, Inlines.EXTEND32(X[X_ptr + j])), rshift)); + /* We're multiplying y[j] by two so we don't have to do it here */ + Ryy = Inlines.ADD16(yy, y[j]); + + /* Approximate score: we maximise Rxy/sqrt(Ryy) (we're guaranteed that + Rxy is positive because the sign is pre-computed) */ + Rxy = Inlines.MULT16_16_Q15(Rxy, Rxy); + /* The idea is to check for num/den >= best_num/best_den, but that way + we can do it without any division */ + /* OPT: Make sure to use conditional moves here */ + if (Inlines.MULT16_16(best_den, Rxy) > Inlines.MULT16_16(Ryy, best_num)) + { + best_den = Ryy; + best_num = Rxy; + best_id = j; + } + } while (++j < N); + + /* Updating the sums of the new pulse(s) */ + xy = Inlines.ADD32(xy, Inlines.EXTEND32(X[X_ptr + best_id])); + /* We're multiplying y[j] by two so we don't have to do it here */ + yy = Inlines.ADD16(yy, y[best_id]); + + /* Only now that we've made the final choice, update y/iy */ + /* Multiplying y[j] by 2 so we don't have to do it everywhere else */ + y[best_id] = (y[best_id] + (2 * s)); + iy[best_id]++; + } + + /* Put the original sign back */ + j = 0; + do + { + X[X_ptr + j] = (Inlines.MULT16_16(signx[j], X[X_ptr + j])); + /* OPT: Make sure your compiler uses a conditional move here rather than + a branch. */ + iy[j] = signx[j] < 0 ? -iy[j] : iy[j]; + } while (++j < N); + + CWRS.encode_pulses(iy, N, K, enc); + + collapse_mask = extract_collapse_mask(iy, N, B); + + return collapse_mask; + } + + /** Decode pulse vector and combine the result with the pitch vector to produce + the final normalised signal in the current band. */ + internal static uint alg_unquant(int[] X, int X_ptr, int N, int K, int spread, int B, + EntropyCoder dec, int gain) + { + int Ryy; + uint collapse_mask; + int[] iy = new int[N]; + Inlines.OpusAssert(K > 0, "alg_unquant() needs at least one pulse"); + Inlines.OpusAssert(N > 1, "alg_unquant() needs at least two dimensions"); + Ryy = CWRS.decode_pulses(iy, N, K, dec); + normalise_residual(iy, X, X_ptr, N, Ryy, gain); + exp_rotation(X, X_ptr, N, -1, B, K, spread); + collapse_mask = extract_collapse_mask(iy, N, B); + + return collapse_mask; + } + + internal static void renormalise_vector(int[] X, int X_ptr, int N, int gain) + { + int i; + int k; + int E; + int g; + int t; + int xptr; +#if UNSAFE + unsafe + { + fixed (int* px_base = X) + { + int* px = px_base + X_ptr; + E = CeltConstants.EPSILON + Kernels.celt_inner_prod(px, px, N); + } + } +#else + E = CeltConstants.EPSILON + Kernels.celt_inner_prod(X, X_ptr, X, X_ptr, N); +#endif + k = Inlines.celt_ilog2(E) >> 1; + t = Inlines.VSHR32(E, 2 * (k - 7)); + g = Inlines.MULT16_16_P15(Inlines.celt_rsqrt_norm(t), gain); + + xptr = X_ptr; + for (i = 0; i < N; i++) + { + X[xptr] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MULT16_16(g, X[xptr]), k + 1)); + xptr++; + } + /*return celt_sqrt(E);*/ + } + + internal static int stereo_itheta(int[] X, int X_ptr, int[] Y, int Y_ptr, int stereo, int N) + { + int i; + int itheta; + int mid, side; + int Emid, Eside; + + Emid = Eside = CeltConstants.EPSILON; + if (stereo != 0) + { + for (i = 0; i < N; i++) + { + int m, s; + m = Inlines.ADD16(Inlines.SHR16(X[X_ptr + i], 1), Inlines.SHR16(Y[Y_ptr + i], 1)); + s = Inlines.SUB16(Inlines.SHR16(X[X_ptr + i], 1), Inlines.SHR16(Y[Y_ptr + i], 1)); + Emid = Inlines.MAC16_16(Emid, m, m); + Eside = Inlines.MAC16_16(Eside, s, s); + } + } + else { +#if UNSAFE + unsafe + { + fixed (int* px_base = X, py_base = Y) + { + int* px = px_base + X_ptr; + int* py = py_base + Y_ptr; + Emid += Kernels.celt_inner_prod(px, px, N); + Eside += Kernels.celt_inner_prod(py, py, N); + } + } +#else + Emid += Kernels.celt_inner_prod(X, X_ptr, X, X_ptr, N); + Eside += Kernels.celt_inner_prod(Y, Y_ptr, Y, Y_ptr, N); +#endif + } + mid = (Inlines.celt_sqrt(Emid)); + side = (Inlines.celt_sqrt(Eside)); + /* 0.63662 = 2/pi */ + itheta = Inlines.MULT16_16_Q15(((short)(0.5 + (0.63662f) * (((int)1) << (15))))/*Inlines.QCONST16(0.63662f, 15)*/, Inlines.celt_atan2p(side, mid)); + + return itheta; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Common/Autocorrelation.cs b/Libraries/Concentus/CSharp/Concentus/Common/Autocorrelation.cs new file mode 100644 index 000000000..5e09536c8 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Common/Autocorrelation.cs @@ -0,0 +1,284 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#if !UNSAFE + +namespace Concentus.Common +{ + using Concentus.Celt; + using Concentus.Common.CPlusPlus; + + internal static class Autocorrelation + { + /* Compute autocorrelation */ + internal static void silk_autocorr( + int[] results, /* O Result (length correlationCount) */ + BoxedValueInt scale, /* O Scaling of the correlation vector */ + short[] inputData, /* I Input data to correlate */ + int inputDataSize, /* I Length of input */ + int correlationCount /* I Number of correlation taps to compute */ + ) + { + int corrCount = Inlines.silk_min_int(inputDataSize, correlationCount); + scale.Val = Autocorrelation._celt_autocorr(inputData, results, corrCount - 1, inputDataSize); + } + + internal static int _celt_autocorr( + short[] x, /* in: [0...n-1] samples x */ + int[] ac, /* out: [0...lag-1] ac values */ + int lag, + int n + ) + { + int d; + int i, k; + int fastN = n - lag; + int shift; + short[] xptr; + short[] xx = new short[n]; + Inlines.OpusAssert(n > 0); + xptr = x; + + shift = 0; + { + int ac0; + ac0 = 1 + (n << 7); + if ((n & 1) != 0) + { + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[0], xptr[0]), 9); + } + for (i = (n & 1); i < n; i += 2) + { + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i], xptr[i]), 9); + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i + 1], xptr[i + 1]), 9); + } + shift = Inlines.celt_ilog2(ac0) - 30 + 10; + shift = (shift) / 2; + if (shift > 0) + { + for (i = 0; i < n; i++) + { + xx[i] = (short)(Inlines.PSHR32(xptr[i], shift)); + } + xptr = xx; + } + else + shift = 0; + } + CeltPitchXCorr.pitch_xcorr(xptr, xptr, ac, fastN, lag + 1); + for (k = 0; k <= lag; k++) + { + for (i = k + fastN, d = 0; i < n; i++) + d = Inlines.MAC16_16(d, xptr[i], xptr[i - k]); + ac[k] += d; + } + shift = 2 * shift; + if (shift <= 0) + ac[0] += Inlines.SHL32((int)1, -shift); + if (ac[0] < 268435456) + { + int shift2 = 29 - Inlines.EC_ILOG((uint)ac[0]); + for (i = 0; i <= lag; i++) + { + ac[i] = Inlines.SHL32(ac[i], shift2); + } + shift -= shift2; + } + else if (ac[0] >= 536870912) + { + int shift2 = 1; + if (ac[0] >= 1073741824) + shift2++; + for (i = 0; i <= lag; i++) + { + ac[i] = Inlines.SHR32(ac[i], shift2); + } + shift += shift2; + } + + return shift; + } + + internal static int _celt_autocorr( + int[] x, /* in: [0...n-1] samples x */ + int[] ac, /* out: [0...lag-1] ac values */ + int[] window, + int overlap, + int lag, + int n) + { + int d; + int i, k; + int fastN = n - lag; + int shift; + int[] xptr; + int[] xx = new int[n]; + + Inlines.OpusAssert(n > 0); + Inlines.OpusAssert(overlap >= 0); + + if (overlap == 0) + { + xptr = x; + } + else + { + for (i = 0; i < n; i++) + xx[i] = x[i]; + for (i = 0; i < overlap; i++) + { + xx[i] = Inlines.MULT16_16_Q15(x[i], window[i]); + xx[n - i - 1] = Inlines.MULT16_16_Q15(x[n - i - 1], window[i]); + } + xptr = xx; + } + + shift = 0; + + int ac0; + ac0 = 1 + (n << 7); + if ((n & 1) != 0) + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[0], xptr[0]), 9); + + for (i = (n & 1); i < n; i += 2) + { + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i], xptr[i]), 9); + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i + 1], xptr[i + 1]), 9); + } + + shift = Inlines.celt_ilog2(ac0) - 30 + 10; + shift = (shift) / 2; + if (shift > 0) + { + for (i = 0; i < n; i++) + xx[i] = (Inlines.PSHR32(xptr[i], shift)); + xptr = xx; + } + else + shift = 0; + + CeltPitchXCorr.pitch_xcorr(xptr, xptr, ac, fastN, lag + 1); + for (k = 0; k <= lag; k++) + { + for (i = k + fastN, d = 0; i < n; i++) + d = Inlines.MAC16_16(d, xptr[i], xptr[i - k]); + ac[k] += d; + } + + shift = 2 * shift; + if (shift <= 0) + ac[0] += Inlines.SHL32((int)1, -shift); + if (ac[0] < 268435456) + { + int shift2 = 29 - Inlines.EC_ILOG((uint)ac[0]); + for (i = 0; i <= lag; i++) + ac[i] = Inlines.SHL32(ac[i], shift2); + shift -= shift2; + } + else if (ac[0] >= 536870912) + { + int shift2 = 1; + if (ac[0] >= 1073741824) + shift2++; + for (i = 0; i <= lag; i++) + ac[i] = Inlines.SHR32(ac[i], shift2); + shift += shift2; + } + + return shift; + } + + private const int QC = 10; + private const int QS = 14; + + /* Autocorrelations for a warped frequency axis */ + internal static void silk_warped_autocorrelation( + int[] corr, /* O Result [order + 1] */ + BoxedValueInt scale, /* O Scaling of the correlation vector */ + short[] input, /* I Input data to correlate */ + int warping_Q16, /* I Warping coefficient */ + int length, /* I Length of input */ + int order /* I Correlation order (even) */ + ) + { + int n, i, lsh; + int tmp1_QS, tmp2_QS; + int[] state_QS = new int[order + 1];// = { 0 }; + long[] corr_QC = new long[order + 1];// = { 0 }; + + /* Order must be even */ + Inlines.OpusAssert((order & 1) == 0); + Inlines.OpusAssert(2 * QS - QC >= 0); + + /* Loop over samples */ + for (n = 0; n < length; n++) + { + tmp1_QS = Inlines.silk_LSHIFT32((int)input[n], QS); + /* Loop over allpass sections */ + for (i = 0; i < order; i += 2) + { + /* Output of allpass section */ + tmp2_QS = Inlines.silk_SMLAWB(state_QS[i], state_QS[i + 1] - tmp1_QS, warping_Q16); + state_QS[i] = tmp1_QS; + corr_QC[i] += Inlines.silk_RSHIFT64(Inlines.silk_SMULL(tmp1_QS, state_QS[0]), 2 * QS - QC); + /* Output of allpass section */ + tmp1_QS = Inlines.silk_SMLAWB(state_QS[i + 1], state_QS[i + 2] - tmp2_QS, warping_Q16); + state_QS[i + 1] = tmp2_QS; + corr_QC[i + 1] += Inlines.silk_RSHIFT64(Inlines.silk_SMULL(tmp2_QS, state_QS[0]), 2 * QS - QC); + } + state_QS[order] = tmp1_QS; + corr_QC[order] += Inlines.silk_RSHIFT64(Inlines.silk_SMULL(tmp1_QS, state_QS[0]), 2 * QS - QC); + } + + lsh = Inlines.silk_CLZ64(corr_QC[0]) - 35; + lsh = Inlines.silk_LIMIT(lsh, -12 - QC, 30 - QC); + scale.Val = -(QC + lsh); + Inlines.OpusAssert(scale.Val >= -30 && scale.Val <= 12); + if (lsh >= 0) + { + for (i = 0; i < order + 1; i++) + { + corr[i] = (int)(Inlines.silk_LSHIFT64(corr_QC[i], lsh)); + } + } + else { + for (i = 0; i < order + 1; i++) + { + corr[i] = (int)(Inlines.silk_RSHIFT64(corr_QC[i], -lsh)); + } + } + Inlines.OpusAssert(corr_QC[0] >= 0); /* If breaking, decrease QC*/ + } + } +} + +#endif diff --git a/Libraries/Concentus/CSharp/Concentus/Common/CPlusPlus/Arrays.cs b/Libraries/Concentus/CSharp/Concentus/Common/CPlusPlus/Arrays.cs new file mode 100644 index 000000000..5ab1ab7d7 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Common/CPlusPlus/Arrays.cs @@ -0,0 +1,221 @@ +/* Copyright (c) 2016 Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Common.CPlusPlus +{ + using System; + + internal static class Arrays + { + internal static T[][] InitTwoDimensionalArray(int x, int y) + { + T[][] returnVal = new T[x][]; + for (int c = 0; c < x; c++) + { + returnVal[c] = new T[y]; + } + return returnVal; + } + + internal static Pointer> InitTwoDimensionalArrayPointer(int x, int y) + { + Pointer> returnVal = Pointer.Malloc>(x); + for (int c = 0; c < x; c++) + { + returnVal[c] = Pointer.Malloc(y); + } + return returnVal; + } + + internal static T[][][] InitThreeDimensionalArray(int x, int y, int z) + { + T[][][] returnVal = new T[x][][]; + for (int c = 0; c < x; c++) + { + returnVal[c] = new T[y][]; + for (int a = 0; a < y; a++) + { + returnVal[c][a] = new T[z]; + } + } + return returnVal; + } + + //FIXME: For the most part this method is used to zero-out arrays, which is usually already done by the runtime. + + internal static void MemSetByte(byte[] array, byte value) + { + for (int c = 0; c < array.Length; c++) + { + array[c] = value; + } + } + + internal static void MemSetInt(int[] array, int value, int length) + { + for (int c = 0; c < length; c++) + { + array[c] = value; + } + } + + internal static void MemSetShort(short[] array, short value, int length) + { + for (int c = 0; c < length; c++) + { + array[c] = value; + } + } + + internal static void MemSetFloat(float[] array, float value, int length) + { + for (int c = 0; c < length; c++) + { + array[c] = value; + } + } + + internal static void MemSetSbyte(sbyte[] array, sbyte value, int length) + { + for (int c = 0; c < length; c++) + { + array[c] = value; + } + } + + internal static void MemSetWithOffset(T[] array, T value, int offset, int length) + { + for (int c = offset; c < offset + length; c++) + { + array[c] = value; + } + } + + internal static void MemMove(T[] array, int src_idx, int dst_idx, int length) + { + if (src_idx == dst_idx || length == 0) + return; + + // Do regions overlap? + if (src_idx + length > dst_idx || dst_idx + length > src_idx) + { + // Take extra precautions + if (dst_idx < src_idx) + { + // Copy forwards + for (int c = 0; c < length; c++) + { + array[c + dst_idx] = array[c + src_idx]; + } + } + else + { + // Copy backwards + for (int c = length - 1; c >= 0; c--) + { + array[c + dst_idx] = array[c + src_idx]; + } + } + } + else + { + // Memory regions cannot overlap; just do a fast copy + Array.Copy(array, src_idx, array, dst_idx, length); + } + } + + internal static void MemMoveInt(int[] array, int src_idx, int dst_idx, int length) + { + if (src_idx == dst_idx || length == 0) + return; + + // Do regions overlap? + if (src_idx + length > dst_idx || dst_idx + length > src_idx) + { + // Take extra precautions + if (dst_idx < src_idx) + { + // Copy forwards + for (int c = 0; c < length; c++) + { + array[c + dst_idx] = array[c + src_idx]; + } + } + else + { + // Copy backwards + for (int c = length - 1; c >= 0; c--) + { + array[c + dst_idx] = array[c + src_idx]; + } + } + } + else + { + // Memory regions cannot overlap; just do a fast copy + Array.Copy(array, src_idx, array, dst_idx, length); + } + } + + internal static void MemMoveShort(short[] array, int src_idx, int dst_idx, int length) + { + if (src_idx == dst_idx || length == 0) + return; + + // Do regions overlap? + if (src_idx + length > dst_idx || dst_idx + length > src_idx) + { + // Take extra precautions + if (dst_idx < src_idx) + { + // Copy forwards + for (int c = 0; c < length; c++) + { + array[c + dst_idx] = array[c + src_idx]; + } + } + else + { + // Copy backwards + for (int c = length - 1; c >= 0; c--) + { + array[c + dst_idx] = array[c + src_idx]; + } + } + } + else + { + // Memory regions cannot overlap; just do a fast copy + Array.Copy(array, src_idx, array, dst_idx, length); + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Common/CPlusPlus/BoxedValue.cs b/Libraries/Concentus/CSharp/Concentus/Common/CPlusPlus/BoxedValue.cs new file mode 100644 index 000000000..30a3ae1d8 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Common/CPlusPlus/BoxedValue.cs @@ -0,0 +1,97 @@ +/* Copyright (c) 2016 Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Common.CPlusPlus +{ + public class BoxedValueInt + { + public int Val; + + public BoxedValueInt(int v = 0) + { + Val = v; + } + + public override string ToString() + { + return Val.ToString(); + } + } + + public class BoxedValueShort + { + public short Val; + + public BoxedValueShort(short v = 0) + { + Val = v; + } + + public override string ToString() + { + return Val.ToString(); + } + } + + public class BoxedValueSbyte + { + public sbyte Val; + + public BoxedValueSbyte(sbyte v = 0) + { + Val = v; + } + + public override string ToString() + { + return Val.ToString(); + } + } + + /// + /// For performance reasons, do not use this generic class if possible + /// + /// + public class BoxedValue + { + public T Val; + + public BoxedValue(T v = default(T)) + { + Val = v; + } + + public override string ToString() + { + return Val == null ? "null" : Val.ToString(); + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Common/CPlusPlus/Pointer.cs b/Libraries/Concentus/CSharp/Concentus/Common/CPlusPlus/Pointer.cs new file mode 100644 index 000000000..0ad894a64 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Common/CPlusPlus/Pointer.cs @@ -0,0 +1,563 @@ +/* Copyright (c) 2016 Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Common.CPlusPlus +{ + using System; + using System.Diagnostics; + using System.Runtime.CompilerServices; + using System.Text; + + /// + /// This simulates a C++ style pointer as far as can be implemented in C#. It represents a handle + /// to an array of objects, along with a base offset that represents the address. + /// When you are programming in debug mode, this class also enforces memory boundaries, + /// tracks uninitialized values, and also records all statistics of accesses to its base array. + /// + /// + public class Pointer + { + private const bool CHECK_UNINIT_MEM = false; + +#if DEBUG && !NET35 + private class Statistics + { + public Statistics(int baseOffset) + { + this.baseOffset = baseOffset; + } + + public int baseOffset; + public int minReadIndex = int.MaxValue; + public int maxReadIndex = int.MinValue; + public int minWriteIndex = int.MaxValue; + public int maxWriteIndex = int.MinValue; + + public Tuple ReadRange + { + get + { + if (minReadIndex == int.MaxValue || maxReadIndex == int.MinValue) + return null; + return new Tuple(minReadIndex - baseOffset, maxReadIndex - baseOffset); + } + } + + public Tuple WriteRange + { + get + { + if (minWriteIndex == int.MaxValue || maxWriteIndex == int.MinValue) + return null; + return new Tuple(minWriteIndex - baseOffset, maxWriteIndex - baseOffset); + } + } + } + private bool[] _initialized; + private Statistics _statistics; + private int _length; +#endif + + private T[] _array; + private int _offset; + + public Pointer(int capacity) + { + _array = new T[capacity]; + _offset = 0; +#if DEBUG && !NET35 + _length = capacity; + _statistics = new Statistics(0); + _initialized = new bool[capacity]; + for (int c = 0; c < capacity; c++) + { + _initialized[c] = false; + } +#endif + } + + public Pointer(T[] buffer) + { + _array = buffer; + _offset = 0; +#if DEBUG && !NET35 + _length = buffer.Length; + _statistics = new Statistics(0); + _initialized = new bool[buffer.Length]; + for (int c = 0; c < buffer.Length; c++) + { + _initialized[c] = true; + } +#endif + } + + public Pointer(T[] buffer, int absoluteOffset) + { + _array = buffer; + _offset = absoluteOffset; +#if DEBUG && !NET35 + _length = buffer.Length - absoluteOffset; + //Inlines.OpusAssert(_length >= 0, "Attempted to point past the end of an array"); + _statistics = new Statistics(absoluteOffset); + _initialized = new bool[buffer.Length]; + for (int c = 0; c < buffer.Length; c++) + { + _initialized[c] = true; + } +#endif + } + +#if DEBUG && !NET35 + private Pointer(T[] buffer, int absoluteOffset, Statistics statistics, bool[] initializedStatus) + { + _array = buffer; + _offset = absoluteOffset; + _length = buffer.Length - absoluteOffset; + //Inlines.OpusAssert(_length >= 0, "Attempted to point past the end of an array"); + _statistics = statistics; + _initialized = initializedStatus; + } + + public Tuple ReadRange + { + get + { + return _statistics.ReadRange; + } + } + + public Tuple WriteRange + { + get + { + return _statistics.WriteRange; + } + } + + public int Length + { + get + { + return _length; + } + } +#endif + + public int Offset + { + get + { + return _offset; + } + } + + // This should only be temporary while I migrate from pointers to arrays + public T[] Data + { + get + { + return _array; + } + } + + public T this[int index] + { + get + { +#if DEBUG && !NET35 +#pragma warning disable 162 + if (CHECK_UNINIT_MEM) Inlines.OpusAssert(_initialized[index + _offset], "Attempted to read from uninitialized memory!"); +#pragma warning restore 162 + // Inlines.OpusAssert(index < _length, "Attempted to read past the end of an array!"); + _statistics.maxReadIndex = Math.Max(_statistics.maxReadIndex, index + _offset); + _statistics.minReadIndex = Math.Min(_statistics.minReadIndex, index + _offset); +#endif + return _array[index + _offset]; + } + + set + { +#if DEBUG && !NET35 + // Inlines.OpusAssert(index < _length, "Attempted to write past the end of an array!"); + _statistics.maxWriteIndex = Math.Max(_statistics.maxWriteIndex, index + _offset); + _statistics.minWriteIndex = Math.Min(_statistics.minWriteIndex, index + _offset); + _initialized[index + _offset] = true; +#endif + _array[index + _offset] = value; + } + } + + public T this[uint index] + { + get + { + return this[(int)index]; + } + + set + { + this[(int)index] = value; + } + } + + /// + /// Returns the value currently under the pointer, and returns a new pointer with +1 offset. + /// This method is not very efficient because it creates new pointers; this is because we must preserve + /// the pass-by-value nature of C++ pointers when they are used as arguments to functions + /// + /// + public Pointer Iterate(out T returnVal) + { + returnVal = _array[_offset]; + return Point(1); + } + +#if DEBUG && !NET35 + public Pointer Point(int relativeOffset) + { + if (relativeOffset == 0) return this; + return new Pointer(_array, _offset + relativeOffset, _statistics, _initialized); + } + + public Pointer Point(uint relativeOffset) + { + if (relativeOffset == 0) return this; + return new Pointer(_array, _offset + (int)relativeOffset, _statistics, _initialized); + } +#else + public Pointer Point(int relativeOffset) + { + if (relativeOffset == 0) return this; + return new Pointer(_array, _offset + relativeOffset); + } + + public Pointer Point(uint relativeOffset) + { + if (relativeOffset == 0) return this; + return new Pointer(_array, _offset + (int)relativeOffset); + } +#endif + + private static string invert_endianness(string hexstring) + { + StringBuilder b = new StringBuilder(hexstring.Length); + for (int c = 0; c < hexstring.Length / 2; c++) + { + b.Append(hexstring.Substring(hexstring.Length - ((c + 1) * 2), 2)); + } + return b.ToString(); + } + + private static void PrintMemCopy(E[] source, int sourceOffset, int length) + { + if (typeof(E) == typeof(int) || typeof(E) == typeof(uint)) + { + Debug.WriteLine(string.Format("memcpy of {0} bytes", length * 4)); + string buf = string.Empty; + for (int c = 0; c < length; c++) + { + buf += invert_endianness(string.Format("{0:x8}", source[c + sourceOffset])); + } + Debug.WriteLine(buf); + } + else if (typeof(E) == typeof(short) || typeof(E) == typeof(ushort)) + { + Debug.WriteLine(string.Format("memcpy of {0} bytes", length * 2)); + string buf = string.Empty; + for (int c = 0; c < length; c++) + { + buf += invert_endianness(string.Format("{0:x4}", source[c + sourceOffset])); + } + Debug.WriteLine(buf); + } + else if (typeof(E) == typeof(byte) || typeof(E) == typeof(sbyte)) + { + Debug.WriteLine(string.Format("memcpy of {0} bytes", length)); + string buf = string.Empty; + for (int c = 0; c < length; c++) + { + buf += invert_endianness(string.Format("{0:x2}", source[c + sourceOffset])); + } + Debug.WriteLine(buf); + } + } + + /// + /// Copies the contents of this pointer, starting at its current address, into the space of another pointer. + /// !!! IMPORTANT !!! REMEMBER THAT C++ memcpy is (DEST, SOURCE, LENGTH) !!!! + /// IN C# IT IS (SOURCE, DEST, LENGTH). DON'T GET SCOOPED LIKE I DID + /// + /// + /// +#if DEBUG + public void MemCopyTo(Pointer destination, int length, bool debug = false) + { + Inlines.OpusAssert(length >= 0, "Cannot memcopy() with a negative length!"); + if (debug) + PrintMemCopy(_array, _offset, length); + + for (int c = 0; c < length; c++) + { + destination[c] = _array[c + _offset]; + } + } +#else + public void MemCopyTo(Pointer destination, int length) + { + if (destination is Pointer) + { + // Use the fast way if we have access to the base array + Array.Copy(_array, _offset, ((Pointer)destination)._array, destination.Offset, length); + } + else + { + // Otherwise do it the slow way + for (int c = 0; c < length; c++) + { + destination[c] = _array[c + _offset]; + } + } + } +#endif + + /// + /// Copies the contents of this pointer, starting at its current address, into an array. + /// !!! IMPORTANT !!! REMEMBER THAT C++ memcpy is (DEST, SOURCE, LENGTH) !!!! + /// + /// + /// +#if DEBUG + public void MemCopyTo(T[] destination, int destOffset, int length) + { + Inlines.OpusAssert(length >= 0, "Cannot memcopy() with a negative length!"); + //PrintMemCopy(_array, _offset, length); + for (int c = 0; c < length; c++) + { + destination[c + destOffset] = _array[c + _offset]; + } + } +#else + public void MemCopyTo(T[] destination, int offset, int length) + { + // Use the fast way if we have access to the base array + Array.Copy(_array, _offset, destination, offset, length); + } +#endif + + /// + /// Loads N values from a source array into this pointer's space + /// + /// +#if DEBUG && !NET35 + public void MemCopyFrom(T[] source, int sourceOffset, int length) + { + Inlines.OpusAssert(length >= 0, "Cannot memcopy() with a negative length!"); + //PrintMemCopy(source, sourceOffset, length); + for (int c = 0; c < length; c++) + { + _array[c + _offset] = source[c + sourceOffset]; + _initialized[c + _offset] = true; + } + } +#else + public void MemCopyFrom(T[] source, int sourceOffset, int length) + { + Array.Copy(source, sourceOffset, _array, _offset, length); + } +#endif + + /// + /// Assigns a certain value to a range of spaces in this array + /// + /// The value to set + /// The number of values to write + public void MemSet(T value, int length) + { +#if DEBUG + Inlines.OpusAssert(length >= 0, "Cannot memset() with a negative length!"); +#endif + MemSet(value, (uint)length); + } + + /// + /// Assigns a certain value to a range of spaces in this array + /// + /// The value to set + /// The number of values to write + public void MemSet(T value, uint length) + { + for (int c = _offset; c < _offset + length; c++) + { + _array[c] = value; +#if DEBUG && !NET35 + _initialized[c] = true; +#endif + } + } + + public void MemMoveTo(Pointer other, int length) + { + if (_array == other._array) + { + // Pointers refer to the same array, perform a move + //if (debug) + // PrintMemCopy(_array, _offset, length); + MemMove(other.Offset - Offset, length); + } + else + { + // Pointers refer to different arrays (if you end up here you probably wanted to just to MemCopy()) + // Debug.WriteLine("Unnecessary memmove detected"); + MemCopyTo(other, length); + } + } + + /// + /// Moves regions of memory within the bounds of this pointer's array. + /// Extra checks are done to ensure that the data is not corrupted if the copy + /// regions overlap + /// + /// The offset to send this pointer's data to + /// The number of values to copy +#if DEBUG && !NET35 + public void MemMove(int move_dist, int length) + { + Inlines.OpusAssert(length >= 0, "Cannot memmove() with a negative length!"); + if (move_dist == 0 || length == 0) + return; + + // Do regions overlap? + if ((move_dist > 0 && move_dist < length) || (move_dist < 0 && 0 - move_dist > length)) + { + // Take extra precautions + if (move_dist < 0) + { + // Copy forwards + for (int c = 0; c < length; c++) + { + _array[c + _offset + move_dist] = _array[c + _offset]; + _initialized[c + _offset + move_dist] = true; + } + } + else + { + // Copy backwards + for (int c = length - 1; c >= 0; c--) + { + _array[c + _offset + move_dist] = _array[c + _offset]; + _initialized[c + _offset + move_dist] = true; + } + } + } + else + { + for (int c = 0; c < length; c++) + { + _array[c + _offset + move_dist] = _array[c + _offset]; + _initialized[c + _offset + move_dist] = true; + } + } + } +#else + public void MemMove(int move_dist, int length) + { + Arrays.MemMove(_array, _offset, _offset + move_dist, length); + } +#endif + + /*/// + /// Simulates pointer zooming: newPtr = &ptr[offset]. + /// Returns a pointer that is offset from this one within the same buffer. + /// + /// + /// + /// + internal static Pointer operator +(Pointer arg, int offset) + { + return new Pointer(arg._array, arg._offset + offset); + }*/ + + public override bool Equals(object obj) + { + if (obj == null || GetType() != obj.GetType()) + { + return false; + } + Pointer other = (Pointer)obj; + return other._offset == _offset && + other._array == _array; + } + + public override int GetHashCode() + { + return _array.GetHashCode() + _offset.GetHashCode(); + } + } + + /// + /// This is a helper class which contains static methods that involve pointers + /// + public static class Pointer + { + /// + /// Allocates a new array and returns a pointer to it + /// + /// + /// + /// + public static Pointer Malloc(int capacity) + { + //this returns a pointer inside of a random field, to make sure offset indexing works properly + //E[] field = new E[capacity * 2]; + //return new Pointer(field, new Random().Next(0, capacity - 1)); + return new Pointer(capacity); + } + + /// + /// Creates a pointer to an existing array + /// + /// + /// + /// + /// + public static Pointer GetPointer(this E[] memory, int offset = 0) + { + if (memory == null) + return null; + //if (Debugger.IsAttached && offset == memory.Length / 2) + //{ + // // This may be a partitioned array. Signal the debugger + // Debugger.Break(); + //} + return new Pointer(memory, offset); + } + } +} \ No newline at end of file diff --git a/Libraries/Concentus/CSharp/Concentus/Common/EntropyCoder.cs b/Libraries/Concentus/CSharp/Concentus/Common/EntropyCoder.cs new file mode 100644 index 000000000..4c64abf3b --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Common/EntropyCoder.cs @@ -0,0 +1,790 @@ +/* Copyright (c) 2001-2011 Timothy B. Terriberry + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Common +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using System.Diagnostics; + + /*A range decoder. + This is an entropy decoder based upon \cite{Mar79}, which is itself a + rediscovery of the FIFO arithmetic code introduced by \cite{Pas76}. + It is very similar to arithmetic encoding, except that encoding is done with + digits in any base, instead of with bits, and so it is faster when using + larger bases (i.e.: a byte). + The author claims an average waste of $\frac{1}{2}\log_b(2b)$ bits, where $b$ + is the base, longer than the theoretical optimum, but to my knowledge there + is no published justification for this claim. + This only seems true when using near-infinite precision arithmetic so that + the process is carried out with no rounding errors. + + An excellent description of implementation details is available at + http://www.arturocampos.com/ac_range.html + A recent work \cite{MNW98} which proposes several changes to arithmetic + encoding for efficiency actually re-discovers many of the principles + behind range encoding, and presents a good theoretical analysis of them. + + End of stream is handled by writing out the smallest number of bits that + ensures that the stream will be correctly decoded regardless of the value of + any subsequent bits. + ec_tell() can be used to determine how many bits were needed to decode + all the symbols thus far; other data can be packed in the remaining bits of + the input buffer. + @PHDTHESIS{Pas76, + author="Richard Clark Pasco", + title="Source coding algorithms for fast data compression", + school="Dept. of Electrical Engineering, Stanford University", + address="Stanford, CA", + month=May, + year=1976 + } + @INPROCEEDINGS{Mar79, + author="Martin, G.N.N.", + title="Range encoding: an algorithm for removing redundancy from a digitised + message", + booktitle="Video & Data Recording Conference", + year=1979, + address="Southampton", + month=Jul + } + @ARTICLE{MNW98, + author="Alistair Moffat and Radford Neal and Ian H. Witten", + title="Arithmetic Coding Revisited", + journal="{ACM} Transactions on Information Systems", + year=1998, + volume=16, + number=3, + pages="256--294", + month=Jul, + URL="http://www.stanford.edu/class/ee398a/handouts/papers/Moffat98ArithmCoding.pdf" + }*/ + internal class EntropyCoder + { + private const int EC_WINDOW_SIZE = ((int)sizeof(uint) * 8); + + ///*The number of bits to use for the range-coded part of uint integers.*/ + private const int EC_UINT_BITS = 8; + + ///*The resolution of fractional-precision bit usage measurements, i.e., + // 3 => 1/8th bits.*/ + public const int BITRES = 3; + + /*The number of bits to output at a time.*/ + private const int EC_SYM_BITS = (8); + + /*The total number of bits in each of the state registers.*/ + private const int EC_CODE_BITS = (32); + + /*The maximum symbol value.*/ + private const uint EC_SYM_MAX = ((1U << EC_SYM_BITS) - 1); + + /*Bits to shift by to move a symbol into the high-order position.*/ + private const uint EC_CODE_SHIFT = (EC_CODE_BITS - EC_SYM_BITS - 1); + + /*Carry bit of the high-order range symbol.*/ + private const uint EC_CODE_TOP = ((1U) << (EC_CODE_BITS - 1)); + + /*Low-order bit of the high-order range symbol.*/ + private const uint EC_CODE_BOT = (EC_CODE_TOP >> EC_SYM_BITS); + + /*The number of bits available for the last, partial symbol in the code field.*/ + private const int EC_CODE_EXTRA = ((EC_CODE_BITS - 2) % EC_SYM_BITS + 1); + + //////////////// Coder State //////////////////// + + /*POINTER to Buffered input/output.*/ + public byte[] buf; + public int buf_ptr; + + /*The size of the buffer.*/ + public uint storage; + + /*The offset at which the last byte containing raw bits was read/written.*/ + public uint end_offs; + + /*Bits that will be read from/written at the end.*/ + public uint end_window; + + /*Number of valid bits in end_window.*/ + public int nend_bits; + + /*The total number of whole bits read/written. + This does not include partial bits currently in the range coder.*/ + public int nbits_total; + + /*The offset at which the next range coder byte will be read/written.*/ + public uint offs; + + /*The number of values in the current range.*/ + public uint rng; + + /*In the decoder: the difference between the top of the current range and + the input value, minus one. + In the encoder: the low end of the current range.*/ + public uint val; + + /*In the decoder: the saved normalization factor from ec_decode(). + In the encoder: the number of oustanding carry propagating symbols.*/ + public uint ext; + + /*A buffered input/output symbol, awaiting carry propagation.*/ + public int rem; + + /*Nonzero if an error occurred.*/ + public int error; + + public EntropyCoder() + { + Reset(); + } + + public void Reset() + { + buf = null; + buf_ptr = 0; + storage = 0; + end_offs = 0; + end_window = 0; + nend_bits = 0; + offs = 0; + rng = 0; + val = 0; + ext = 0; + rem = 0; + error = 0; + } + + public void Assign(EntropyCoder other) + { + this.buf = other.buf; + this.buf_ptr = other.buf_ptr; + this.storage = other.storage; + this.end_offs = other.end_offs; + this.end_window = other.end_window; + this.nend_bits = other.nend_bits; + this.nbits_total = other.nbits_total; + this.offs = other.offs; + this.rng = other.rng; + this.val = other.val; + this.ext = other.ext; + this.rem = other.rem; + this.error = other.error; + } + + internal int read_byte() + { + return this.offs < this.storage ? this.buf[buf_ptr + this.offs++] : 0; + } + + internal int read_byte_from_end() + { + return this.end_offs < this.storage ? + this.buf[buf_ptr + (this.storage - ++(this.end_offs))] : 0; + } + + internal int write_byte(uint _value) + { + if (this.offs + this.end_offs >= this.storage) + { + return -1; + } + this.buf[buf_ptr + this.offs++] = (byte)_value; + return 0; + } + + internal int write_byte_at_end(uint _value) + { + if (this.offs + this.end_offs >= this.storage) + { + return -1; + } + + this.buf[buf_ptr + (this.storage - ++(this.end_offs))] = (byte)_value; + return 0; + } + + /// + /// Normalizes the contents of val and rng so that rng lies entirely in the high-order symbol. + /// + internal void dec_normalize() + { + /*If the range is too small, rescale it and input some bits.*/ + while (this.rng <= EC_CODE_BOT) + { + int sym; + this.nbits_total += EC_SYM_BITS; + this.rng <<= EC_SYM_BITS; + + /*Use up the remaining bits from our last symbol.*/ + sym = this.rem; + + /*Read the next value from the input.*/ + this.rem = read_byte(); + + /*Take the rest of the bits we need from this new symbol.*/ + sym = (sym << EC_SYM_BITS | this.rem) >> (EC_SYM_BITS - EC_CODE_EXTRA); + + /*And subtract them from val, capped to be less than EC_CODE_TOP.*/ + this.val = (uint)((this.val << EC_SYM_BITS) + (EC_SYM_MAX & ~sym)) & (EC_CODE_TOP - 1); + } + } + + internal void dec_init(byte[] _buf, int _buf_ptr, uint _storage) + { + this.buf = _buf; + this.buf_ptr = _buf_ptr; + this.storage = _storage; + this.end_offs = 0; + this.end_window = 0; + this.nend_bits = 0; + /*This is the offset from which ec_tell() will subtract partial bits. + The final value after the ec_dec_normalize() call will be the same as in + the encoder, but we have to compensate for the bits that are added there.*/ + this.nbits_total = EC_CODE_BITS + 1 + - ((EC_CODE_BITS - EC_CODE_EXTRA) / EC_SYM_BITS) * EC_SYM_BITS; + this.offs = 0; + this.rng = 1U << EC_CODE_EXTRA; + this.rem = read_byte(); + this.val = this.rng - 1 - (uint)(this.rem >> (EC_SYM_BITS - EC_CODE_EXTRA)); + this.error = 0; + /*Normalize the interval.*/ + dec_normalize(); + } + + internal uint decode(uint _ft) + { + uint s; + this.ext = this.rng / _ft; + s = (uint)(this.val / this.ext); + return _ft - Inlines.EC_MINI(s + 1, _ft); + } + + internal uint decode_bin(uint _bits) + { + uint s; + this.ext = this.rng >> (int)_bits; + s = (uint)(this.val / this.ext); + return (1U << (int)_bits) - Inlines.EC_MINI(s + 1U, 1U << (int)_bits); + } + + internal void dec_update(uint _fl, uint _fh, uint _ft) + { + uint s; + s = this.ext * (_ft - _fh); + this.val -= s; + this.rng = _fl > 0 ? this.ext * (_fh - _fl) : this.rng - s; + dec_normalize(); + } + + /// + /// The probability of having a "one" is 1/(1<<_logp). + /// + /// + /// + internal int dec_bit_logp(uint _logp) + { + uint r; + uint d; + uint s; + int ret; + r = this.rng; + d = this.val; + s = r >> (int)_logp; + ret = d < s ? 1 : 0; + if (ret == 0) this.val = d - s; + this.rng = ret != 0 ? s : r - s; + dec_normalize(); + return ret; + } + + internal int dec_icdf(byte[] _icdf, uint _ftb) + { + uint r; + uint d; + uint s; + uint t; + int ret; + s = this.rng; + d = this.val; + r = s >> (int)_ftb; + ret = -1; + do + { + t = s; + s = r * _icdf[++ret]; + } + while (d < s); + this.val = d - s; + this.rng = t - s; + dec_normalize(); + return ret; + } + + internal int dec_icdf(byte[] _icdf, int _icdf_offset, uint _ftb) + { + uint r; + uint d; + uint s; + uint t; + int ret; + s = this.rng; + d = this.val; + r = s >> (int)_ftb; + ret = _icdf_offset - 1; + do + { + t = s; + s = r * _icdf[++ret]; + } + while (d < s); + this.val = d - s; + this.rng = t - s; + dec_normalize(); + return ret - _icdf_offset; + } + + internal uint dec_uint(uint _ft) + { + uint ft; + uint s; + int ftb; + /*In order to optimize EC_ILOG(), it is undefined for the value 0.*/ + Inlines.OpusAssert(_ft > 1); + _ft--; + ftb = Inlines.EC_ILOG(_ft); + if (ftb > EC_UINT_BITS) + { + uint t; + ftb -= EC_UINT_BITS; + ft = (uint)(_ft >> ftb) + 1; + s = decode(ft); + dec_update(s, s + 1, ft); + t = (uint)s << ftb | dec_bits((uint)ftb); + if (t <= _ft) return t; + this.error = 1; + return _ft; + } + else { + _ft++; + s = decode((uint)_ft); + dec_update(s, s + 1, (uint)_ft); + return s; + } + } + + internal uint dec_bits(uint _bits) + { + uint window; + int available; + uint ret; + window = this.end_window; + available = this.nend_bits; + if ((uint)available < _bits) + { + do + { + window |= (uint)read_byte_from_end() << available; + available += EC_SYM_BITS; + } + while (available <= EC_WINDOW_SIZE - EC_SYM_BITS); + } + ret = (uint)window & (((uint)1 << (int)_bits) - 1U); + window = window >> (int)_bits; + available = available - (int)_bits; + this.end_window = window; + this.nend_bits = available; + this.nbits_total = this.nbits_total + (int)_bits; + return ret; + } + + /// + /// Outputs a symbol, with a carry bit. + /// If there is a potential to propagate a carry over several symbols, they are + /// buffered until it can be determined whether or not an actual carry will + /// occur. + /// If the counter for the buffered symbols overflows, then the stream becomes + /// undecodable. + /// This gives a theoretical limit of a few billion symbols in a single packet on + /// 32-bit systems. + /// The alternative is to truncate the range in order to force a carry, but + /// requires similar carry tracking in the decoder, needlessly slowing it down. + /// + /// + internal void enc_carry_out(int _c) + { + if (_c != EC_SYM_MAX) + { + /*No further carry propagation possible, flush buffer.*/ + int carry; + carry = _c >> EC_SYM_BITS; + + /*Don't output a byte on the first write. + This compare should be taken care of by branch-prediction thereafter.*/ + if (this.rem >= 0) + { + this.error |= write_byte((uint)(this.rem + carry)); + } + + if (this.ext > 0) + { + uint sym; + sym = (EC_SYM_MAX + (uint)carry) & EC_SYM_MAX; + do this.error |= write_byte(sym); + while (--(this.ext) > 0); + } + + this.rem = (int)((uint)_c & EC_SYM_MAX); + } + else + { + this.ext++; + } + } + + internal void enc_normalize() + { + /*If the range is too small, output some bits and rescale it.*/ + while (this.rng <= EC_CODE_BOT) + { + enc_carry_out((int)(this.val >> (int)EC_CODE_SHIFT)); + /*Move the next-to-high-order symbol into the high-order position.*/ + this.val = (this.val << EC_SYM_BITS) & (EC_CODE_TOP - 1); + this.rng = this.rng << EC_SYM_BITS; + this.nbits_total += EC_SYM_BITS; + } + } + + internal void enc_init(byte[] _buf, int buf_ptr, uint _size) + { + this.buf = _buf; + this.buf_ptr = buf_ptr; + this.end_offs = 0; + this.end_window = 0; + this.nend_bits = 0; + /*This is the offset from which ec_tell() will subtract partial bits.*/ + this.nbits_total = EC_CODE_BITS + 1; + this.offs = 0; + this.rng = EC_CODE_TOP; + this.rem = -1; + this.val = 0; + this.ext = 0; + this.storage = _size; + this.error = 0; + } + + internal void encode(uint _fl, uint _fh, uint _ft) + { + uint r; + r = this.rng / _ft; + if (_fl > 0) + { + this.val += this.rng - (r * (_ft - _fl)); + this.rng = (r * (_fh - _fl)); + } + else + { + this.rng -= (r * (_ft - _fh)); + } + + enc_normalize(); + } + + internal void encode_bin(uint _fl, uint _fh, uint _bits) + { + uint r; + r = this.rng >> (int)_bits; + if (_fl > 0) + { + this.val += this.rng - (r * ((1U << (int)_bits) - _fl)); + this.rng = (r * (_fh - _fl)); + } + else this.rng -= (r * ((1U << (int)_bits) - _fh)); + enc_normalize(); + } + + /*The probability of having a "one" is 1/(1<<_logp).*/ + internal void enc_bit_logp(int _val, uint _logp) + { + uint r; + uint s; + uint l; + r = this.rng; + l = this.val; + s = r >> (int)_logp; + r -= s; + if (_val != 0) + { + this.val = l + r; + } + + this.rng = _val != 0 ? s : r; + enc_normalize(); + } + + internal void enc_icdf(int _s, byte[] _icdf, uint _ftb) + { + uint r; + r = this.rng >> (int)_ftb; + if (_s > 0) + { + this.val += this.rng - (r * _icdf[_s - 1]); + this.rng = (r * (uint)(_icdf[_s - 1] - _icdf[_s])); + } + else + { + this.rng -= (r * _icdf[_s]); + } + enc_normalize(); + } + + internal void enc_icdf(int _s, byte[] _icdf, int icdf_ptr, uint _ftb) + { + uint r; + r = this.rng >> (int)_ftb; + if (_s > 0) + { + this.val += this.rng - (r * _icdf[icdf_ptr + _s - 1]); + this.rng = (r * (uint)(_icdf[icdf_ptr + _s - 1] - _icdf[icdf_ptr + _s])); + } + else + { + this.rng -= (r * _icdf[icdf_ptr + _s]); + } + enc_normalize(); + } + + internal void enc_uint(uint _fl, uint _ft) + { + uint ft; + uint fl; + int ftb; + /*In order to optimize EC_ILOG(), it is undefined for the value 0.*/ + Inlines.OpusAssert(_ft > 1); + _ft--; + ftb = Inlines.EC_ILOG(_ft); + if (ftb > EC_UINT_BITS) + { + ftb -= EC_UINT_BITS; + ft = (_ft >> ftb) + 1; + fl = (uint)(_fl >> ftb); + encode(fl, fl + 1, ft); + enc_bits(_fl & (((uint)1 << ftb) - 1U), (uint)ftb); + } + else encode(_fl, _fl + 1, _ft + 1); + } + + internal void enc_bits(uint _fl, uint _bits) + { + uint window; + int used; + window = this.end_window; + used = this.nend_bits; + Inlines.OpusAssert(_bits > 0); + + if (used + _bits > EC_WINDOW_SIZE) + { + do + { + this.error |= write_byte_at_end((uint)window & EC_SYM_MAX); + window >>= EC_SYM_BITS; + used -= EC_SYM_BITS; + } + while (used >= EC_SYM_BITS); + } + + window |= (uint)_fl << used; + used += (int)_bits; + this.end_window = window; + this.nend_bits = used; + this.nbits_total += (int)_bits; + } + + internal void enc_patch_initial_bits(uint _val, uint _nbits) + { + int shift; + uint mask; + Inlines.OpusAssert(_nbits <= EC_SYM_BITS); + shift = EC_SYM_BITS - (int)_nbits; + mask = ((1U << (int)_nbits) - 1) << shift; + + if (this.offs > 0) + { + /*The first byte has been finalized.*/ + this.buf[buf_ptr] = (byte)((this.buf[buf_ptr] & ~mask) | _val << shift); + } + else if (this.rem >= 0) + { + /*The first byte is still awaiting carry propagation.*/ + this.rem = (int)(((uint)this.rem & ~mask) | _val) << shift; + } + else if (this.rng <= (EC_CODE_TOP >> (int)_nbits)) + { + /*The renormalization loop has never been run.*/ + this.val = (this.val & ~((uint)mask << (int)EC_CODE_SHIFT)) | + (uint)_val << (int)(EC_CODE_SHIFT + shift); + } + else + { + /*The encoder hasn't even encoded _nbits of data yet.*/ + this.error = -1; + } + } + + internal void enc_shrink(uint _size) + { + Inlines.OpusAssert(this.offs + this.end_offs <= _size); + //(memmove(this.buf + _size - this.end_offs, this.buf + this.storage - this.end_offs, this.end_offs * sizeof(*(dst)))) + Arrays.MemMove(this.buf, buf_ptr + (int)_size - (int)this.end_offs, buf_ptr + (int)this.storage - (int)this.end_offs, (int)this.end_offs); + this.storage = _size; + } + + internal uint range_bytes() + { + return this.offs; + } + + internal int get_error() + { + return this.error; + } + + /// + /// Returns the number of bits "used" by the encoded or decoded symbols so far. + /// This same number can be computed in either the encoder or the decoder, and is + /// suitable for making coding decisions. + /// This will always be slightly larger than the exact value (e.g., all + /// rounding error is in the positive direction). + /// + /// The number of bits. + internal int tell() + { + int returnVal = this.nbits_total - Inlines.EC_ILOG(this.rng); + return returnVal; + } + + private static readonly uint[] correction = {35733, 38967, 42495, 46340, 50535, 55109, 60097, 65535}; + + /// + /// This is a faster version of ec_tell_frac() that takes advantage + /// of the low(1/8 bit) resolution to use just a linear function + /// followed by a lookup to determine the exact transition thresholds. + /// + /// + internal uint tell_frac() + { + int nbits; + int r; + int l; + uint b; + nbits = this.nbits_total << EntropyCoder.BITRES; + l = Inlines.EC_ILOG(this.rng); + r = (int)(this.rng >> (l - 16)); + b = (uint)((r >> 12) - 8); + b += (r > correction[b] ? 1u : 0); + l = (int)((l << 3) + b); + return (uint)(nbits - l); + } + + internal void enc_done() + { + uint window; + int used; + uint msk; + uint end; + int l; + /*We output the minimum number of bits that ensures that the symbols encoded + thus far will be decoded correctly regardless of the bits that follow.*/ + l = EC_CODE_BITS - Inlines.EC_ILOG(this.rng); + msk = (EC_CODE_TOP - 1) >> l; + end = (this.val + msk) & ~msk; + + if ((end | msk) >= this.val + this.rng) + { + l++; + msk >>= 1; + end = (this.val + msk) & ~msk; + } + + while (l > 0) + { + enc_carry_out((int)(end >> (int)EC_CODE_SHIFT)); + end = (end << EC_SYM_BITS) & (EC_CODE_TOP - 1); + l -= EC_SYM_BITS; + } + + /*If we have a buffered byte flush it into the output buffer.*/ + if (this.rem >= 0 || this.ext > 0) + { + enc_carry_out(0); + } + + /*If we have buffered extra bits, flush them as well.*/ + window = this.end_window; + used = this.nend_bits; + + while (used >= EC_SYM_BITS) + { + this.error |= write_byte_at_end((uint)window & EC_SYM_MAX); + window >>= EC_SYM_BITS; + used -= EC_SYM_BITS; + } + + /*Clear any excess space and add any remaining extra bits to the last byte.*/ + if (this.error == 0) + { + Arrays.MemSetWithOffset(this.buf, 0, buf_ptr + (int)this.offs, (int)this.storage - (int)this.offs - (int)this.end_offs); + if (used > 0) + { + /*If there's no range coder data at all, give up.*/ + if (this.end_offs >= this.storage) + { + this.error = -1; + } + else + { + l = -l; + /*If we've busted, don't add too many extra bits to the last byte; it + would corrupt the range coder data, and that's more important.*/ + if (this.offs + this.end_offs >= this.storage && l < used) + { + window = window & ((1U << l) - 1); + this.error = -1; + } + + this.buf[buf_ptr + this.storage - this.end_offs - 1] |= (byte)window; + } + } + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Common/Inlines.cs b/Libraries/Concentus/CSharp/Concentus/Common/Inlines.cs new file mode 100644 index 000000000..014ee6d15 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Common/Inlines.cs @@ -0,0 +1,2714 @@ +/* Copyright (c) 2006-2011 Skype Limited. + Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Common +{ + using Concentus.Celt; + using Concentus.Common.CPlusPlus; + using System; + using System.Diagnostics; + using System.Runtime.CompilerServices; + + internal static class Inlines + { +#if NET35 + private const MethodImplOptions INLINE_ATTR = MethodImplOptions.PreserveSig; +#else + private const MethodImplOptions INLINE_ATTR = MethodImplOptions.AggressiveInlining; +#endif + + [Conditional("DEBUG")] + public static void OpusAssert(bool condition, string message = "Unknown error") + { +#if DEBUG_MACROS + if (!condition) throw new ArithmeticException("Debug macro failed validation"); +#endif + Debug.Assert(condition, message); + } + +#region CELT + + // CELT-SPECIFIC INLINES + + // /** Multiply a 16-bit signed value by a 16-bit unsigned value. The result is a 32-bit signed value */ + //#define MULT16_16SU(a,b) ((opus_val32)(opus_val16)(a)*(opus_val32)(opus_uint16)(b)) + [MethodImpl(INLINE_ATTR)] + public static int MULT16_16SU(int a, int b) + { + return ((int)(short)(a) * (int)(ushort)(b)); + } + + [MethodImpl(INLINE_ATTR)] + public static int MULT16_16SU(short a, ushort b) + { + return ((int)(short)(a) * (int)(ushort)(b)); + } + + [MethodImpl(INLINE_ATTR)] + public static int MULT16_16SU(int a, uint b) + { + return ((a) * (int)(b)); + } + + // /** 16x32 multiplication, followed by a 16-bit shift right. Results fits in 32 bits */ + //#define MULT16_32_Q16(a,b) ADD32(MULT16_16((a),SHR((b),16)), SHR(MULT16_16SU((a),((b)&0x0000ffff)),16)) + [MethodImpl(INLINE_ATTR)] + public static int MULT16_32_Q16(short a, int b) + { + return ADD32(MULT16_16((a), SHR((b), 16)), SHR(MULT16_16SU((a), ((b) & 0x0000ffff)), 16)); + } + + [MethodImpl(INLINE_ATTR)] + public static int MULT16_32_Q16(int a, int b) + { + return ADD32(MULT16_16((a), SHR((b), 16)), SHR(MULT16_16SU((a), ((b) & 0x0000ffff)), 16)); + } + + // /** 16x32 multiplication, followed by a 16-bit shift right (round-to-nearest). Results fits in 32 bits */ + //#define MULT16_32_P16(a,b) ADD32(MULT16_16((a),SHR((b),16)), PSHR(MULT16_16SU((a),((b)&0x0000ffff)),16)) + [MethodImpl(INLINE_ATTR)] + public static int MULT16_32_P16(short a, int b) + { + return ADD32(MULT16_16((a), SHR((b), 16)), PSHR(MULT16_16SU((a), ((b) & 0x0000ffff)), 16)); + } + + [MethodImpl(INLINE_ATTR)] + public static int MULT16_32_P16(int a, int b) + { + return ADD32(MULT16_16((a), SHR((b), 16)), PSHR(MULT16_16SU((a), ((b) & 0x0000ffff)), 16)); + } + + // /** 16x32 multiplication, followed by a 15-bit shift right. Results fits in 32 bits */ + [MethodImpl(INLINE_ATTR)] + public static int MULT16_32_Q15(short a, int b) + { + return ((a * (b >> 16)) << 1) + ((a * (b & 0xFFFF)) >> 15); + //return ADD32(SHL(MULT16_16((a), SHR((b), 16)), 1), SHR(MULT16_16SU((a), (ushort)((b) & 0x0000ffff)), 15)); + } + + [MethodImpl(INLINE_ATTR)] + public static int MULT16_32_Q15(int a, int b) + { + return ((a * (b >> 16)) << 1) + ((a * (b & 0xFFFF)) >> 15); + //return ADD32(SHL(MULT16_16((a), SHR((b), 16)), 1), SHR(MULT16_16SU((a), (uint)((b) & 0x0000ffff)), 15)); + } + + // /** 32x32 multiplication, followed by a 31-bit shift right. Results fits in 32 bits */ + //#define MULT32_32_Q31(a,b) ADD32(ADD32(SHL(MULT16_16(SHR((a),16),SHR((b),16)),1), SHR(MULT16_16SU(SHR((a),16),((b)&0x0000ffff)),15)), SHR(MULT16_16SU(SHR((b),16),((a)&0x0000ffff)),15)) + [MethodImpl(INLINE_ATTR)] + public static int MULT32_32_Q31(int a, int b) + { + return ADD32(ADD32(SHL(MULT16_16(SHR((a), 16), SHR((b), 16)), 1), SHR(MULT16_16SU(SHR((a), 16), ((b) & 0x0000ffff)), 15)), SHR(MULT16_16SU(SHR((b), 16), ((a) & 0x0000ffff)), 15)); + } + + // "Compile-time" (not really) conversion of float constant to 16-bit value + [MethodImpl(INLINE_ATTR)] + public static short QCONST16(float x, int bits) + { + return ((short)(0.5 + (x) * (((int)1) << (bits)))); + } + + // "Compile-time" (not really) conversion of float constant to 32-bit value + [MethodImpl(INLINE_ATTR)] + public static int QCONST32(float x, int bits) + { + return ((int)(0.5 + (x) * (((int)1) << (bits)))); + } + + // /** Negate a 16-bit value */ + [MethodImpl(INLINE_ATTR)] + public static short NEG16(short x) + { + return (short)(0 - x); + } + + [MethodImpl(INLINE_ATTR)] + public static int NEG16(int x) + { + return 0 - x; + } + + // /** Negate a 32-bit value */ + [MethodImpl(INLINE_ATTR)] + public static int NEG32(int x) + { + return 0 - x; + } + + // /** Change a 32-bit value into a 16-bit value. The value is assumed to fit in 16-bit, otherwise the result is undefined */ + [MethodImpl(INLINE_ATTR)] + public static short EXTRACT16(int x) + { + return unchecked((short)x); + } + + // /** Change a 16-bit value into a 32-bit value */ + [MethodImpl(INLINE_ATTR)] + public static int EXTEND32(short x) + { + return (int)x; + } + + [MethodImpl(INLINE_ATTR)] + public static int EXTEND32(int x) + { + return x; + } + + // /** Arithmetic shift-right of a 16-bit value */ + [MethodImpl(INLINE_ATTR)] + public static short SHR16(short a, int shift) + { + return (short)((a) >> (shift)); + } + + [MethodImpl(INLINE_ATTR)] + public static int SHR16(int a, int shift) + { + return ((a) >> (shift)); + } + + // /** Arithmetic shift-left of a 16-bit value */ + [MethodImpl(INLINE_ATTR)] + public static short SHL16(short a, int shift) + { + return unchecked((short)(unchecked((ushort)a) << shift)); + } + + [MethodImpl(INLINE_ATTR)] + public static int SHL16(int a, int shift) + { + return unchecked(((int)(unchecked((unchecked((uint)(a)) << (shift)))))); + } + + // /** Arithmetic shift-right of a 32-bit value */ + [MethodImpl(INLINE_ATTR)] + public static int SHR32(int a, int shift) + { + return a >> shift; + } + + // /** Arithmetic shift-left of a 32-bit value */ + [MethodImpl(INLINE_ATTR)] + public static int SHL32(int a, int shift) + { + return unchecked(((int)(unchecked((unchecked((uint)(a)) << (shift)))))); + } + + // /** 32-bit arithmetic shift right with rounding-to-nearest instead of rounding down */ + [MethodImpl(INLINE_ATTR)] + public static int PSHR32(int a, int shift) + { + return (SHR32((a) + ((EXTEND32(1) << ((shift)) >> 1)), shift)); + } + + [MethodImpl(INLINE_ATTR)] + public static short PSHR16(short a, int shift) + { + return SHR16((short)(a + (1 << (shift) >> 1)), shift); + } + + [MethodImpl(INLINE_ATTR)] + public static int PSHR16(int a, int shift) + { + return SHR32((a + (1 << (shift) >> 1)), shift); + } + + // /** 32-bit arithmetic shift right where the argument can be negative */ + [MethodImpl(INLINE_ATTR)] + public static int VSHR32(int a, int shift) + { + return (((shift) > 0) ? SHR32(a, shift) : SHL32(a, -(shift))); + } + + // /** "RAW" macros, should not be used outside of this header file */ + [MethodImpl(INLINE_ATTR)] + private static int SHR(int a, int shift) + { + return ((a) >> (shift)); + } + + [MethodImpl(INLINE_ATTR)] + private static int SHL(int a, int shift) + { + return SHL32(a, shift); + } + + [MethodImpl(INLINE_ATTR)] + private static int SHR(short a, int shift) + { + return ((a) >> (shift)); + } + + [MethodImpl(INLINE_ATTR)] + private static int SHL(short a, int shift) + { + return SHL32(a, shift); + } + + [MethodImpl(INLINE_ATTR)] + private static int PSHR(int a, int shift) + { + return (SHR((a) + ((EXTEND32(1) << ((shift)) >> 1)), shift)); + } + + [MethodImpl(INLINE_ATTR)] + public static int SATURATE(int x, int a) + { + return (((x) > (a) ? (a) : (x) < -(a) ? -(a) : (x))); + } + + [MethodImpl(INLINE_ATTR)] + public static short SATURATE16(int x) + { + return (EXTRACT16((x) > 32767 ? 32767 : (x) < -32768 ? -32768 : (x))); + } + + // /** Shift by a and round-to-neareast 32-bit value. Result is a 16-bit value */ + [MethodImpl(INLINE_ATTR)] + public static short ROUND16(short x, short a) + { + return (EXTRACT16(PSHR32((x), (a)))); + } + + [MethodImpl(INLINE_ATTR)] + public static int ROUND16(int x, int a) + { + return PSHR32((x), (a)); + } + + [MethodImpl(INLINE_ATTR)] + public static int PDIV32(int a, int b) + { + return a / b; + } + + // /** Divide by two */ + // fixme: can this be optimized? + [MethodImpl(INLINE_ATTR)] + public static short HALF16(short x) + { + return (SHR16(x, 1)); + } + + [MethodImpl(INLINE_ATTR)] + public static int HALF16(int x) + { + return (SHR32(x, 1)); + } + + [MethodImpl(INLINE_ATTR)] + public static int HALF32(int x) + { + return (SHR32(x, 1)); + } + + // /** Add two 16-bit values */ + [MethodImpl(INLINE_ATTR)] + public static short ADD16(short a, short b) + { + return ((short)((short)(a) + (short)(b))); + } + + [MethodImpl(INLINE_ATTR)] + public static int ADD16(int a, int b) + { + return (a + b); + } + + // /** Subtract two 16-bit values */ + [MethodImpl(INLINE_ATTR)] + public static short SUB16(short a, short b) + { + return ((short)((short)(a) - (short)(b))); + } + + [MethodImpl(INLINE_ATTR)] + public static int SUB16(int a, int b) + { + return (a - b); + } + + // /** Add two 32-bit values */ + [MethodImpl(INLINE_ATTR)] + public static int ADD32(int a, int b) + { + return ((int)(a) + (int)(b)); + } + + // /** Subtract two 32-bit values */ + [MethodImpl(INLINE_ATTR)] + public static int SUB32(int a, int b) + { + return ((int)(a) - (int)(b)); + } + + // /** 16x16 multiplication where the result fits in 16 bits */ + //#define MULT16_16_16(a,b) ((((opus_val16)(a))*((opus_val16)(b)))) + [MethodImpl(INLINE_ATTR)] + public static short MULT16_16_16(short a, short b) + { + return (short)(((((short)(a)) * ((short)(b))))); + } + + [MethodImpl(INLINE_ATTR)] + public static int MULT16_16_16(int a, int b) + { + return (a * b); + } + + // /* (opus_val32)(opus_val16) gives TI compiler a hint that it's 16x16->32 multiply */ + // /** 16x16 multiplication where the result fits in 32 bits */ + //#define MULT16_16(a,b) (((opus_val32)(opus_val16)(a))*((opus_val32)(opus_val16)(b))) + [MethodImpl(INLINE_ATTR)] + public static int MULT16_16(int a, int b) + { + return a * b; + } + + [MethodImpl(INLINE_ATTR)] + public static int MULT16_16(short a, short b) + { + return a * b; + } + + // /** 16x16 multiply-add where the result fits in 32 bits */ + //#define MAC16_16(c,a,b) (ADD32((c),MULT16_16((a),(b)))) + [MethodImpl(INLINE_ATTR)] + public static int MAC16_16(short c, short a, short b) + { + return c + (a * b); + } + + [MethodImpl(INLINE_ATTR)] + public static int MAC16_16(int c, short a, short b) + { + return c + (a * b); + } + + [MethodImpl(INLINE_ATTR)] + public static int MAC16_16(int c, int a, int b) + { + return c + (a * b); + } + + // /** 16x32 multiply, followed by a 15-bit shift right and 32-bit add. + // b must fit in 31 bits. + // Result fits in 32 bits. */ + //#define MAC16_32_Q15(c,a,b) ADD32((c),ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15))) + [MethodImpl(INLINE_ATTR)] + public static int MAC16_32_Q15(int c, short a, short b) + { + return ADD32((c), ADD32(MULT16_16((a), SHR((b), 15)), SHR(MULT16_16((a), ((b) & 0x00007fff)), 15))); + } + + [MethodImpl(INLINE_ATTR)] + public static int MAC16_32_Q15(int c, int a, int b) + { + return ADD32((c), ADD32(MULT16_16((a), SHR((b), 15)), SHR(MULT16_16((a), ((b) & 0x00007fff)), 15))); + } + + // /** 16x32 multiplication, followed by a 16-bit shift right and 32-bit add. + // Results fits in 32 bits */ + //#define MAC16_32_Q16(c,a,b) ADD32((c),ADD32(MULT16_16((a),SHR((b),16)), SHR(MULT16_16SU((a),((b)&0x0000ffff)),16))) + [MethodImpl(INLINE_ATTR)] + public static int MAC16_32_Q16(int c, short a, short b) + { + return ADD32((c), ADD32(MULT16_16((a), SHR((b), 16)), SHR(MULT16_16SU((a), ((b) & 0x0000ffff)), 16))); + } + + [MethodImpl(INLINE_ATTR)] + public static int MAC16_32_Q16(int c, int a, int b) + { + return ADD32((c), ADD32(MULT16_16((a), SHR((b), 16)), SHR(MULT16_16SU((a), ((b) & 0x0000ffff)), 16))); + } + + //#define MULT16_16_Q11_32(a,b) (SHR(MULT16_16((a),(b)),11)) + [MethodImpl(INLINE_ATTR)] + public static int MULT16_16_Q11_32(short a, short b) + { + return (SHR(MULT16_16((a), (b)), 11)); + } + + [MethodImpl(INLINE_ATTR)] + public static int MULT16_16_Q11_32(int a, int b) + { + return (SHR(MULT16_16((a), (b)), 11)); + } + + //#define MULT16_16_Q11(a,b) (SHR(MULT16_16((a),(b)),11)) + [MethodImpl(INLINE_ATTR)] + public static short MULT16_16_Q11(short a, short b) + { + return (short)((SHR(MULT16_16((a), (b)), 11))); + } + + [MethodImpl(INLINE_ATTR)] + public static int MULT16_16_Q11(int a, int b) + { + return (SHR(MULT16_16((a), (b)), 11)); + } + + //#define MULT16_16_Q13(a,b) (SHR(MULT16_16((a),(b)),13)) + [MethodImpl(INLINE_ATTR)] + public static short MULT16_16_Q13(short a, short b) + { + return (short)((SHR(MULT16_16((a), (b)), 13))); + } + + [MethodImpl(INLINE_ATTR)] + public static int MULT16_16_Q13(int a, int b) + { + return (SHR(MULT16_16((a), (b)), 13)); + } + + //#define MULT16_16_Q14(a,b) (SHR(MULT16_16((a),(b)),14)) + [MethodImpl(INLINE_ATTR)] + public static short MULT16_16_Q14(short a, short b) + { + return (short)((SHR(MULT16_16((a), (b)), 14))); + } + + [MethodImpl(INLINE_ATTR)] + public static int MULT16_16_Q14(int a, int b) + { + return (SHR(MULT16_16((a), (b)), 14)); + } + + //#define MULT16_16_Q15(a,b) (SHR(MULT16_16((a),(b)),15)) + [MethodImpl(INLINE_ATTR)] + public static short MULT16_16_Q15(short a, short b) + { + return (short)((SHR(MULT16_16((a), (b)), 15))); + } + + [MethodImpl(INLINE_ATTR)] + public static int MULT16_16_Q15(int a, int b) + { + return (SHR(MULT16_16((a), (b)), 15)); + } + + //#define MULT16_16_P13(a,b) (SHR(ADD32(4096,MULT16_16((a),(b))),13)) + [MethodImpl(INLINE_ATTR)] + public static short MULT16_16_P13(short a, short b) + { + return (short)((SHR(ADD32(4096, MULT16_16((a), (b))), 13))); + } + + [MethodImpl(INLINE_ATTR)] + public static int MULT16_16_P13(int a, int b) + { + return (SHR(ADD32(4096, MULT16_16((a), (b))), 13)); + } + + //#define MULT16_16_P14(a,b) (SHR(ADD32(8192,MULT16_16((a),(b))),14)) + [MethodImpl(INLINE_ATTR)] + public static short MULT16_16_P14(short a, short b) + { + return (short)((SHR(ADD32(8192, MULT16_16((a), (b))), 14))); + } + + [MethodImpl(INLINE_ATTR)] + public static int MULT16_16_P14(int a, int b) + { + return (SHR(ADD32(8192, MULT16_16((a), (b))), 14)); + } + + //#define MULT16_16_P15(a,b) (SHR(ADD32(16384,MULT16_16((a),(b))),15)) + [MethodImpl(INLINE_ATTR)] + public static short MULT16_16_P15(short a, short b) + { + return (short)((SHR(ADD32(16384, MULT16_16((a), (b))), 15))); + } + + [MethodImpl(INLINE_ATTR)] + public static int MULT16_16_P15(int a, int b) + { + return (SHR(ADD32(16384, MULT16_16((a), (b))), 15)); + } + + // /** Divide a 32-bit value by a 16-bit value. Result fits in 16 bits */ + //#define DIV32_16(a,b) ((opus_val16)(((opus_val32)(a))/((opus_val16)(b)))) + [MethodImpl(INLINE_ATTR)] + public static short DIV32_16(int a, short b) + { + return (short)(((short)(((int)(a)) / ((short)(b))))); + } + + [MethodImpl(INLINE_ATTR)] + public static int DIV32_16(int a, int b) + { + return a / b; + } + + // /** Divide a 32-bit value by a 32-bit value. Result fits in 32 bits */ + //#define DIV32(a,b) (((opus_val32)(a))/((opus_val32)(b))) + [MethodImpl(INLINE_ATTR)] + public static int DIV32(int a, int b) + { + return a / b; + } + + // identical to silk_SAT16 - saturate operation + [MethodImpl(INLINE_ATTR)] + public static short SAT16(int x) + { + return (short)(x > 32767 ? 32767 : x < -32768 ? -32768 : (short)x); + } + + [MethodImpl(INLINE_ATTR)] + public static short SIG2WORD16(int x) + { + x = PSHR32(x, 12); + x = MAX32(x, -32768); + x = MIN32(x, 32767); + return EXTRACT16(x); + } + + [MethodImpl(INLINE_ATTR)] + public static short MIN(short a, short b) + { + return ((a) < (b) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static short MAX(short a, short b) + { + return ((a) > (b) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static short MIN16(short a, short b) + { + return ((a) < (b) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static short MAX16(short a, short b) + { + return ((a) > (b) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static int MIN16(int a, int b) + { + return ((a) < (b) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static int MAX16(int a, int b) + { + return ((a) > (b) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static float MIN16(float a, float b) + { + return ((a) < (b) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static float MAX16(float a, float b) + { + return ((a) > (b) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static int MIN(int a, int b) + { + return ((a) < (b) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static int MAX(int a, int b) + { + return ((a) > (b) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static int IMIN(int a, int b) + { + return ((a) < (b) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static uint IMIN(uint a, uint b) + { + return ((a) < (b) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static int IMAX(int a, int b) + { + return ((a) > (b) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static int MIN32(int a, int b) + { + return ((a) < (b) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static int MAX32(int a, int b) + { + return ((a) > (b) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static float MIN32(float a, float b) + { + return ((a) < (b) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static float MAX32(float a, float b) + { + return ((a) > (b) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static int ABS16(int x) + { + return ((x) < 0 ? (-(x)) : (x)); + } + + [MethodImpl(INLINE_ATTR)] + public static float ABS16(float x) + { + return ((x) < 0 ? (-(x)) : (x)); + } + + [MethodImpl(INLINE_ATTR)] + public static short ABS16(short x) + { + return (short)(((x) < 0 ? (-(x)) : (x))); + } + + [MethodImpl(INLINE_ATTR)] + public static int ABS32(int x) + { + return ((x) < 0 ? (-(x)) : (x)); + } + + [MethodImpl(INLINE_ATTR)] + public static uint celt_udiv(uint n, uint d) + { + Inlines.OpusAssert(d > 0); + return n / d; + } + + [MethodImpl(INLINE_ATTR)] + public static int celt_udiv(int n, int d) + { + Inlines.OpusAssert(d > 0); + return n / d; + } + + [MethodImpl(INLINE_ATTR)] + public static int celt_sudiv(int n, int d) + { + Inlines.OpusAssert(d > 0); + return n / d; + } + + //#define celt_div(a,b) MULT32_32_Q31((opus_val32)(a),celt_rcp(b)) + [MethodImpl(INLINE_ATTR)] + public static int celt_div(int a, int b) + { + return MULT32_32_Q31((int)(a), celt_rcp(b)); + } + + /** Integer log in base2. Undefined for zero and negative numbers */ + [MethodImpl(INLINE_ATTR)] + public static int celt_ilog2(int x) + { + Inlines.OpusAssert(x > 0, "celt_ilog2() only defined for strictly positive numbers"); +#if DEBUG_MACROS + if (x <= 0) + throw new ArgumentException("celt_ilog2() only defined for strictly positive numbers"); +#endif + return (EC_ILOG((uint)x) - 1); + } + + /** Integer log in base2. Defined for zero, but not for negative numbers */ + [MethodImpl(INLINE_ATTR)] + public static int celt_zlog2(int x) + { + return x <= 0 ? 0 : celt_ilog2(x); + } + + [MethodImpl(INLINE_ATTR)] + public static int celt_maxabs16(int[] x, int x_ptr, int len) + { + int i; + int maxval = 0; + int minval = 0; + for (i = x_ptr; i < len + x_ptr; i++) + { + maxval = MAX32(maxval, x[i]); + minval = MIN32(minval, x[i]); + } + return MAX32(EXTEND32(maxval), -EXTEND32(minval)); + } + + [MethodImpl(INLINE_ATTR)] + public static int celt_maxabs32(int[] x, int x_ptr, int len) + { + int i; + int maxval = 0; + int minval = 0; + for (i = x_ptr; i < x_ptr + len; i++) + { + maxval = MAX32(maxval, x[i]); + minval = MIN32(minval, x[i]); + } + return MAX32(maxval, 0 - minval); + } + + [MethodImpl(INLINE_ATTR)] + public static short celt_maxabs32(short[] x, int x_ptr, int len) + { + int i; + short maxval = 0; + short minval = 0; + for (i = x_ptr; i < x_ptr + len; i++) + { + maxval = MAX16(maxval, x[i]); + minval = MIN16(minval, x[i]); + } + return MAX(maxval, (short)(0 - minval)); + } + + /// + /// Multiplies two 16-bit fractional values. Bit-exactness of this macro is important + /// + /// + /// + /// + [MethodImpl(INLINE_ATTR)] + public static int FRAC_MUL16(int a, int b) + { + return ((16384 + ((int)((short)a * (short)b))) >> 15); + } + + /// + /// Compute floor(sqrt(_val)) with exact arithmetic. + /// This has been tested on all possible 32-bit inputs. + /// + /// + /// + [MethodImpl(INLINE_ATTR)] + public static uint isqrt32(uint _val) + { +#if PARITY + uint b; + uint g; + int bshift; + /*Uses the second method from + http://www.azillionmonkeys.com/qed/sqroot.html + The main idea is to search for the largest binary digit b such that + (g+b)*(g+b) <= _val, and add it to the solution g.*/ + g = 0; + bshift = (EC_ILOG(_val) - 1) >> 1; + b = 1U << bshift; + do + { + uint t; + t = (((uint)g << 1) + b) << bshift; + if (t <= _val) + { + g += b; + _val -= t; + } + b >>= 1; + bshift--; + } + while (bshift >= 0); + return g; +#else + // This is 100x faster + return (uint)Math.Sqrt(_val); +#endif + } + + private static readonly short[] sqrt_C = { 23175, 11561, -3011, 1699, -664 }; + + /** Sqrt approximation (QX input, QX/2 output) */ + [MethodImpl(INLINE_ATTR)] + public static int celt_sqrt(int x) + { +#if PARITY + int k; + short n; + int rt; + + if (x == 0) + return 0; + else if (x >= 1073741824) + return 32767; + k = (celt_ilog2(x) >> 1) - 7; + x = VSHR32(x, 2 * k); + n = (short)(x - 32768); + rt = ADD16(sqrt_C[0], MULT16_16_Q15(n, ADD16(sqrt_C[1], MULT16_16_Q15(n, ADD16(sqrt_C[2], + MULT16_16_Q15(n, ADD16(sqrt_C[3], MULT16_16_Q15(n, (sqrt_C[4]))))))))); + rt = VSHR32(rt, 7 - k); + return rt; +#else + // This is 100x faster + return (int)Math.Sqrt(x); +#endif + } + + /** Reciprocal approximation (Q15 input, Q16 output) */ + [MethodImpl(INLINE_ATTR)] + public static int celt_rcp(int x) + { +#if PARITY + int i; + int n; + int r; + Inlines.OpusAssert(x > 0, "celt_rcp() only defined for positive values"); + i = celt_ilog2(x); + /* n is Q15 with range [0,1). */ + n = VSHR32(x, i - 15) - 32768; + /* Start with a linear approximation: + r = 1.8823529411764706-0.9411764705882353*n. + The coefficients and the result are Q14 in the range [15420,30840].*/ + r = ADD16(30840, MULT16_16_Q15(-15420, n)); + /* Perform two Newton iterations: + r -= r*((r*n)-1.Q15) + = r*((r*n)+(r-1.Q15)). */ + r = SUB16(r, MULT16_16_Q15(r, + ADD16(MULT16_16_Q15(r, n), ADD16(r, -32768)))); + /* We subtract an extra 1 in the second iteration to avoid overflow; it also + neatly compensates for truncation error in the rest of the process. */ + r = SUB16(r, ADD16(1, MULT16_16_Q15(r, + ADD16(MULT16_16_Q15(r, n), ADD16(r, -32768))))); + /* r is now the Q15 solution to 2/(n+1), with a maximum relative error + of 7.05346E-5, a (relative) RMSE of 2.14418E-5, and a peak absolute + error of 1.24665/32768. */ + return VSHR32(EXTEND32(r), i - 16); +#else + // 50x faster + return (int)(((float)(1 << 16) * (float)(1 << 15)) / ((float)x)); +#endif + } + + /** Reciprocal sqrt approximation in the range [0.25,1) (Q16 in, Q14 out) */ + [MethodImpl(INLINE_ATTR)] + public static int celt_rsqrt_norm(int x) + { + int n; + int r; + int r2; + int y; + /* Range of n is [-16384,32767] ([-0.5,1) in Q15). */ + n = x - 32768; + /* Get a rough initial guess for the root. + The optimal minimax quadratic approximation (using relative error) is + r = 1.437799046117536+n*(-0.823394375837328+n*0.4096419668459485). + Coefficients here, and the final result r, are Q14.*/ + r = ADD16(23557, MULT16_16_Q15(n, ADD16(-13490, MULT16_16_Q15(n, 6713)))); + /* We want y = x*r*r-1 in Q15, but x is 32-bit Q16 and r is Q14. + We can compute the result from n and r using Q15 multiplies with some + adjustment, carefully done to avoid overflow. + Range of y is [-1564,1594]. */ + r2 = MULT16_16_Q15(r, r); + y = SHL16(SUB16(ADD16(MULT16_16_Q15(r2, n), r2), 16384), 1); + /* Apply a 2nd-order Householder iteration: r += r*y*(y*0.375-0.5). + This yields the Q14 reciprocal square root of the Q16 x, with a maximum + relative error of 1.04956E-4, a (relative) RMSE of 2.80979E-5, and a + peak absolute error of 2.26591/16384. */ + return ADD16(r, MULT16_16_Q15(r, MULT16_16_Q15(y, + SUB16(MULT16_16_Q15(y, 12288), 16384)))); + } + + [MethodImpl(INLINE_ATTR)] + public static int frac_div32(int a, int b) + { + int rcp; + int result, rem; + int shift = celt_ilog2(b) - 29; + a = VSHR32(a, shift); + b = VSHR32(b, shift); + /* 16-bit reciprocal */ + rcp = ROUND16(celt_rcp(ROUND16(b, 16)), 3); + result = MULT16_32_Q15(rcp, a); + rem = PSHR32(a, 2) - MULT32_32_Q31(result, b); + result = ADD32(result, SHL32(MULT16_32_Q15(rcp, rem), 2)); + if (result >= 536870912) /* 2^29 */ + return 2147483647; /* 2^31 - 1 */ + else if (result <= -536870912) /* -2^29 */ + return -2147483647; /* -2^31 */ + else + return SHL32(result, 2); + } + + private const short log2_C0 = -6801 + (1 << (3)); + + /** Base-2 logarithm approximation (log2(x)). (Q14 input, Q10 output) */ + [MethodImpl(INLINE_ATTR)] + public static int celt_log2(int x) + { +#if PARITY + int i; + int n, frac; + /* -0.41509302963303146, 0.9609890551383969, -0.31836011537636605, + 0.15530808010959576, -0.08556153059057618 */ + if (x == 0) + return -32767; + i = celt_ilog2(x); + n = VSHR32(x, i - 15) - 32768 - 16384; + frac = ADD16(log2_C0, MULT16_16_Q15(n, ADD16(15746, MULT16_16_Q15(n, ADD16(-5217, MULT16_16_Q15(n, ADD16(2545, MULT16_16_Q15(n, -1401)))))))); + return SHL16((short)(i - 13), 10) + SHR16(frac, 4); +#else + return (int)((float)(1 << 10) * (float)Math.Log10(x / (float)(1 << 14)) / (float)Math.Log10(2)); +#endif + } + + [MethodImpl(INLINE_ATTR)] + public static int celt_exp2_frac(int x) + { + int frac; + frac = SHL16(x, 4); + return ADD16(16383, MULT16_16_Q15(frac, ADD16(22804, MULT16_16_Q15(frac, ADD16(14819, MULT16_16_Q15(10204, frac)))))); + } + + /** Base-2 exponential approximation (2^x). (Q10 input, Q16 output) */ + [MethodImpl(INLINE_ATTR)] + public static int celt_exp2(int x) + { + int integer; + int frac; + integer = SHR16(x, 10); + if (integer > 14) + return 0x7f000000; + else if (integer < -15) + return 0; + frac = (short)(celt_exp2_frac((short)(x - SHL16((short)(integer), 10)))); + return VSHR32(EXTEND32(frac), -integer - 2); + } + + /* Atan approximation using a 4th order polynomial. Input is in Q15 format + and normalized by pi/4. Output is in Q15 format */ + [MethodImpl(INLINE_ATTR)] + public static int celt_atan01(int x) + { + return MULT16_16_P15(x, ADD32(32767, MULT16_16_P15(x, ADD32(-21, MULT16_16_P15(x, ADD32(-11943, MULT16_16_P15(4936, x))))))); + } + + /* atan2() approximation valid for positive input values */ + [MethodImpl(INLINE_ATTR)] + public static int celt_atan2p(int y, int x) + { + if (y < x) + { + int arg; + arg = celt_div(SHL32(EXTEND32(y), 15), x); + if (arg >= 32767) + arg = 32767; + return SHR32(celt_atan01(EXTRACT16(arg)), 1); + } + else { + int arg; + arg = celt_div(SHL32(EXTEND32(x), 15), y); + if (arg >= 32767) + arg = 32767; + return 25736 - SHR16(celt_atan01(EXTRACT16(arg)), 1); + } + } + + [MethodImpl(INLINE_ATTR)] + public static int celt_cos_norm(int x) + { + x = x & 0x0001ffff; + if (x > SHL32(EXTEND32(1), 16)) + x = SUB32(SHL32(EXTEND32(1), 17), x); + if ((x & 0x00007fff) != 0) + { + if (x < SHL32(EXTEND32(1), 15)) + { + return _celt_cos_pi_2(EXTRACT16(x)); + } + else { + return NEG32(_celt_cos_pi_2(EXTRACT16(65536 - x))); // opus bug: should be neg32? + } + } + else { + if ((x & 0x0000ffff) != 0) + return 0; + else if ((x & 0x0001ffff) != 0) + return -32767; + else + return 32767; + } + } + + [MethodImpl(INLINE_ATTR)] + public static int _celt_cos_pi_2(int x) + { + int x2; + + x2 = MULT16_16_P15(x, x); + return ADD32(1, MIN32(32766, ADD32(SUB16(32767, x2), MULT16_16_P15(x2, ADD32(-7651, MULT16_16_P15(x2, ADD32(8277, MULT16_16_P15(-626, x2)))))))); + } + + [MethodImpl(INLINE_ATTR)] + public static short FLOAT2INT16(float x) + { + x = x * CeltConstants.CELT_SIG_SCALE; + if (x < short.MinValue) + x = short.MinValue; + if (x > short.MaxValue) + x = short.MaxValue; + return (short)x; + } + +#endregion + +#region SILK + + // SILK-SPECIFIC INLINES + + /// + /// Rotate a32 right by 'rot' bits. Negative rot values result in rotating + /// left. Output is 32bit int. + /// + /// + /// + /// + [MethodImpl(INLINE_ATTR)] + public static int silk_ROR32(int a32, int rot) + { + return unchecked((int)silk_ROR32(unchecked((uint)a32), rot)); + } + + /// + /// Rotate a32 right by 'rot' bits. Negative rot values result in rotating + /// left. Output is 32bit uint. + /// + /// + /// + /// + [MethodImpl(INLINE_ATTR)] + public static uint silk_ROR32(uint a32, int rot) + { + int m = (0 - rot); + if (rot == 0) + { + return a32; + } + else if (rot < 0) + { + return ((a32 << m) | (a32 >> (32 - m))); + } + else { + return ((a32 << (32 - rot)) | (a32 >> rot)); + } + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_MUL(int a32, int b32) + { + int ret = a32 * b32; +#if DEBUG_MACROS + long ret64 = (long)a32 * (long)b32; + Inlines.OpusAssert((long)ret == ret64); +#endif + return ret; + } + + [MethodImpl(INLINE_ATTR)] + public static uint silk_MUL_uint(uint a32, uint b32) + { + uint ret = a32 * b32; + Inlines.OpusAssert((ulong)ret == (ulong)a32 * (ulong)b32); + return ret; + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_MLA(int a32, int b32, int c32) + { + int ret = silk_ADD32((a32), ((b32) * (c32))); + Inlines.OpusAssert((long)ret == (long)a32 + (long)b32 * (long)c32); + return ret; + } + + + [MethodImpl(INLINE_ATTR)] + public static int silk_MLA_uint(uint a32, uint b32, uint c32) + { + uint ret = silk_ADD32((a32), ((b32) * (c32))); + Inlines.OpusAssert((long)ret == (long)a32 + (long)b32 * (long)c32); + return (int)ret; + } + + /// + /// ((a32 >> 16) * (b32 >> 16)) + /// + /// + /// + /// + + [MethodImpl(INLINE_ATTR)] + public static int silk_SMULTT(int a32, int b32) + { + return ((a32 >> 16) * (b32 >> 16)); + } + + + [MethodImpl(INLINE_ATTR)] + public static int silk_SMLATT(int a32, int b32, int c32) + { + return silk_ADD32((a32), ((b32) >> 16) * ((c32) >> 16)); + } + + + [MethodImpl(INLINE_ATTR)] + public static long silk_SMLALBB(long a64, short b16, short c16) + { + return silk_ADD64((a64), (long)((int)(b16) * (int)(c16))); + } + + + [MethodImpl(INLINE_ATTR)] + public static long silk_SMULL(int a32, int b32) + { + return (long)a32 * (long)b32; + } + + /// + /// Adds two signed 32-bit values in a way that can overflow, while not relying on undefined behaviour + /// (just standard two's complement implementation-specific behaviour) + /// + /// + /// + /// + [MethodImpl(INLINE_ATTR)] + public static int silk_ADD32_ovflw(int a, int b) + { + return unchecked((int)((uint)a + (uint)b)); + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_ADD32_ovflw(uint a, uint b) + { + return unchecked((int)(a + b)); + } + + /// + /// Subtracts two signed 32-bit values in a way that can overflow, while not relying on undefined behaviour + /// (just standard two's complement implementation-specific behaviour) + /// + /// + /// + /// + [MethodImpl(INLINE_ATTR)] + public static int silk_SUB32_ovflw(int a, int b) + { + return unchecked((int)((uint)a - (uint)b)); + } + + /// + /// Multiply-accumulate macros that allow overflow in the addition (ie, no asserts in debug mode) + /// + /// + /// + /// + /// + [MethodImpl(INLINE_ATTR)] + public static int silk_MLA_ovflw(int a32, int b32, int c32) + { + return unchecked(silk_ADD32_ovflw((uint)(a32), (uint)(b32) * (uint)(c32))); + } + + + [MethodImpl(INLINE_ATTR)] + public static int silk_SMLABB_ovflw(int a32, int b32, int c32) + { + return unchecked((silk_ADD32_ovflw((a32), ((int)((short)(b32))) * (int)((short)(c32))))); + } + + + [MethodImpl(INLINE_ATTR)] + public static int silk_SMULBB(int a32, int b32) + { + return ((int)unchecked((short)a32) * (int)unchecked((short)b32)); + } + + /// + /// (a32 * (int)((short)(b32))) >> 16 output have to be 32bit int + /// + /// + /// + /// + + [MethodImpl(INLINE_ATTR)] + public static int silk_SMULWB(int a32, int b32) + { +#if DEBUG_MACROS + int ret; + ret = ((a32 >> 16) * (int)((short)b32) + (((a32 & 0x0000FFFF) * (int)((short)b32)) >> 16)); + if ((long)ret != ((long)a32 * (short)b32) >> 16) + { + Inlines.OpusAssert(false); + } + return ret; +#else + return unchecked((int)(unchecked(unchecked(a32 * (long)(unchecked((short)b32))) >> 16))); +#endif + } + + + [MethodImpl(INLINE_ATTR)] + public static int silk_SMLABB(int a32, int b32, int c32) + { + return ((a32) + ((int)unchecked((short)b32)) * (int)unchecked((short)c32)); + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_DIV32_16(int a32, int b32) + { +#if DEBUG_MACROS + bool fail = false; + fail |= b32 == 0; + fail |= b32 > short.MaxValue; + fail |= b32 < short.MinValue; + Inlines.OpusAssert(!fail); +#endif + return a32 / b32; + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_DIV32(int a32, int b32) + { + return a32 / b32; + } + + + [MethodImpl(INLINE_ATTR)] + public static short silk_ADD16(short a, short b) + { + short ret = (short)(a + b); +#if DEBUG_MACROS + if (ret != silk_ADD_SAT16(a, b)) + { + Inlines.OpusAssert(false); + } +#endif + return ret; + } + + + [MethodImpl(INLINE_ATTR)] + public static int silk_ADD32(int a, int b) + { + int ret = a + b; +#if DEBUG_MACROS + if (ret != silk_ADD_SAT32(a, b)) + { + Inlines.OpusAssert(false); + } +#endif + return ret; + } + + [MethodImpl(INLINE_ATTR)] + public static uint silk_ADD32(uint a, uint b) + { + uint ret = a + b; + return ret; + } + + [MethodImpl(INLINE_ATTR)] + public static long silk_ADD64(long a, long b) + { + long ret = a + b; + Inlines.OpusAssert(ret == silk_ADD_SAT64(a, b)); + return ret; + } + + + [MethodImpl(INLINE_ATTR)] + public static short silk_SUB16(short a, short b) + { + short ret = (short)(a - b); + Inlines.OpusAssert(ret == silk_SUB_SAT16(a, b)); + return ret; + } + + + [MethodImpl(INLINE_ATTR)] + public static int silk_SUB32(int a, int b) + { + int ret = a - b; + Inlines.OpusAssert(ret == silk_SUB_SAT32(a, b)); + return ret; + } + + + [MethodImpl(INLINE_ATTR)] + public static long silk_SUB64(long a, long b) + { + long ret = a - b; + Inlines.OpusAssert(ret == silk_SUB_SAT64(a, b)); + return ret; + } + + + [MethodImpl(INLINE_ATTR)] + public static int silk_SAT8(int a) + { + return a > byte.MaxValue ? byte.MaxValue : ((a) < byte.MinValue ? byte.MinValue : (a)); + } + + + [MethodImpl(INLINE_ATTR)] + public static int silk_SAT16(int a) + { + return a > short.MaxValue ? short.MaxValue : ((a) < short.MinValue ? short.MinValue : (a)); + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_SAT32(long a) + { + return a > int.MaxValue ? int.MaxValue : ((a) < int.MinValue ? int.MinValue : (int)(a)); + } + + /// + /// ////////////////// + /// + /// + /// + /// + [MethodImpl(INLINE_ATTR)] + public static short silk_ADD_SAT16(short a16, short b16) + { + short res = (short)silk_SAT16(silk_ADD32((int)(a16), (b16))); + Inlines.OpusAssert(res == silk_SAT16((int)a16 + (int)b16)); + return res; + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_ADD_SAT32(int a32, int b32) + { + int res = (unchecked(((uint)(a32) + (uint)(b32)) & 0x80000000) == 0 ? + ((((a32) & (b32)) & 0x80000000) != 0 ? int.MinValue : (a32) + (b32)) : + ((((a32) | (b32)) & 0x80000000) == 0 ? int.MaxValue : (a32) + (b32))); + Inlines.OpusAssert(res == silk_SAT32((long)a32 + (long)b32)); + return res; + } + + [MethodImpl(INLINE_ATTR)] + public static long silk_ADD_SAT64(long a64, long b64) + { + long res; + res = (unchecked((ulong)(a64 + b64) & 0x8000000000000000UL) == 0 ? + (unchecked((ulong)(a64 & b64) & 0x8000000000000000UL) != 0 ? long.MinValue : a64 + b64) : + (unchecked((ulong)(a64 | b64) & 0x8000000000000000UL) == 0 ? long.MaxValue : a64 + b64)); +#if DEBUG_MACROS + bool fail = false; + if (res != a64 + b64) + { + /* Check that we saturated to the correct extreme value */ + if (!((res == long.MaxValue && ((a64 >> 1) + (b64 >> 1) > (long.MaxValue >> 3))) || + (res == long.MinValue && ((a64 >> 1) + (b64 >> 1) < (long.MinValue >> 3))))) + { + fail = true; + } + } + else + { + /* Saturation not necessary */ + fail = res != a64 + b64; + } + Inlines.OpusAssert(!fail); +#endif + return res; + } + + [MethodImpl(INLINE_ATTR)] + public static short silk_SUB_SAT16(short a16, short b16) + { + short res = (short)silk_SAT16(silk_SUB32((int)(a16), (b16))); + Inlines.OpusAssert(res == silk_SAT16((int)a16 - (int)b16)); + return res; + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_SUB_SAT32(int a32, int b32) + { + int res = (unchecked(((uint)(a32) - (uint)(b32)) & 0x80000000) == 0 ? + (((a32) & ((b32) ^ 0x80000000) & 0x80000000) != 0 ? int.MinValue : (a32) - (b32)) : + ((((a32) ^ 0x80000000) & (b32) & 0x80000000) != 0 ? int.MaxValue : (a32) - (b32))); + Inlines.OpusAssert(res == silk_SAT32((long)a32 - (long)b32)); + return res; + } + + [MethodImpl(INLINE_ATTR)] + public static long silk_SUB_SAT64(long a64, long b64) + { + long res; + res = (unchecked((ulong)((a64) - (b64)) & 0x8000000000000000UL) == 0 ? + (((ulong)(a64) & ((ulong)(b64) ^ 0x8000000000000000UL) & 0x8000000000000000UL) != 0 ? long.MinValue : (a64) - (b64)) : + ((((ulong)(a64) ^ 0x8000000000000000UL) & (ulong)(b64) & 0x8000000000000000UL) != 0 ? long.MaxValue : (a64) - (b64))); +#if DEBUG_MACROS + bool fail = false; + if (res != a64 - b64) + { + /* Check that we saturated to the correct extreme value */ + if (!((res == long.MaxValue && ((a64 >> 1) + (b64 >> 1) > (long.MaxValue >> 3))) || + (res == long.MinValue && ((a64 >> 1) + (b64 >> 1) < (long.MinValue >> 3))))) + { + fail = true; + } + } + else + { + /* Saturation not necessary */ + fail = res != a64 - b64; + } + Inlines.OpusAssert(!fail); +#endif + return res; + } + + //* Saturation for positive input values */ + //#define silk_POS_SAT32(a) ((a) > int_MAX ? int_MAX : (a)) + + /// + /// Add with saturation for positive input values + /// + /// + /// + /// + [MethodImpl(INLINE_ATTR)] + public static sbyte silk_ADD_POS_SAT8(sbyte a, sbyte b) + { + return (sbyte)((((a + b) & 0x80) != 0) ? sbyte.MaxValue : (a + b)); + } + + /// + /// Add with saturation for positive input values + /// + /// + /// + /// + [MethodImpl(INLINE_ATTR)] + public static short silk_ADD_POS_SAT16(short a, short b) + { + return (short)(unchecked(((a + b) & 0x8000) != 0) ? short.MaxValue : (a + b)); + } + + /// + /// Add with saturation for positive input values + /// + /// + /// + /// + [MethodImpl(INLINE_ATTR)] + public static int silk_ADD_POS_SAT32(int a, int b) + { + return (unchecked(((a + b) & 0x80000000) != 0) ? int.MaxValue : (a + b)); + } + + /// + /// Add with saturation for positive input values + /// + /// + /// + /// + [MethodImpl(INLINE_ATTR)] + public static long silk_ADD_POS_SAT64(long a, long b) + { + return ((unchecked((ulong)(a + b) & 0x8000000000000000L) != 0) ? long.MaxValue : (a + b)); + } + + [MethodImpl(INLINE_ATTR)] + public static sbyte silk_LSHIFT8(sbyte a, int shift) + { + sbyte ret = (sbyte)(a << shift); +#if DEBUG_MACROS + bool fail = false; + fail |= shift < 0; + fail |= shift >= 8; + fail |= (long)ret != ((long)a) << shift; + Inlines.OpusAssert(!fail); +#endif + return ret; + } + + [MethodImpl(INLINE_ATTR)] + public static short silk_LSHIFT16(short a, int shift) + { + short ret = (short)(a << shift); +#if DEBUG_MACROS + bool fail = false; + fail |= shift < 0; + fail |= shift >= 16; + fail |= (long)ret != ((long)a) << shift; + Inlines.OpusAssert(!fail); +#endif + return ret; + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_LSHIFT32(int a, int shift) + { + int ret = a << shift; +#if DEBUG_MACROS + bool fail = false; + fail |= shift < 0; + fail |= shift >= 32; + fail |= (long)ret != ((long)a) << shift; + Inlines.OpusAssert(!fail); +#endif + return ret; + } + + [MethodImpl(INLINE_ATTR)] + public static long silk_LSHIFT64(long a, int shift) + { + long ret = a << shift; +#if DEBUG_MACROS + bool fail = false; + fail |= shift < 0; + fail |= shift >= 64; + fail |= (ret >> shift) != ((long)a); + Inlines.OpusAssert(!fail); +#endif + return ret; + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_LSHIFT(int a, int shift) + { + int ret = a << shift; +#if DEBUG_MACROS + bool fail = false; + fail |= shift < 0; + fail |= shift >= 32; + fail |= (long)ret != ((long)a) << shift; + Inlines.OpusAssert(!fail); +#endif + return ret; + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_LSHIFT_ovflw(int a, int shift) + { +#if DEBUG_MACROS + if ((shift < 0) || (shift >= 32)) /* no check for overflow */ + { + Inlines.OpusAssert(false); + } +#endif + return a << shift; + } + + [MethodImpl(INLINE_ATTR)] + public static uint silk_LSHIFT_uint(uint a, int shift) + { + uint ret = a << shift; +#if DEBUG_MACROS + if ((shift < 0) || ((long)ret != ((long)a) << shift)) + { + Inlines.OpusAssert(false); + } +#endif + return ret; + } + + /// + /// saturates before shifting + /// + /// + /// + /// + [MethodImpl(INLINE_ATTR)] + public static int silk_LSHIFT_SAT32(int a, int shift) + { + return (silk_LSHIFT32(silk_LIMIT((a), silk_RSHIFT32(int.MinValue, (shift)), silk_RSHIFT32(int.MaxValue, (shift))), (shift))); + } + + [MethodImpl(INLINE_ATTR)] + public static sbyte silk_RSHIFT8(sbyte a, int shift) + { +#if DEBUG_MACROS + if ((shift < 0) || (shift >= 8)) + { + Inlines.OpusAssert(false); + } +#endif + return (sbyte)(a >> shift); + } + + [MethodImpl(INLINE_ATTR)] + public static short silk_RSHIFT16(short a, int shift) + { +#if DEBUG_MACROS + if ((shift < 0) || (shift >= 16)) + { + Inlines.OpusAssert(false); + } +#endif + return (short)(a >> shift); + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_RSHIFT32(int a, int shift) + { +#if DEBUG_MACROS + if ((shift < 0) || (shift >= 32)) + { + Inlines.OpusAssert(false); + } +#endif + return a >> shift; + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_RSHIFT(int a, int shift) + { +#if DEBUG_MACROS + if ((shift < 0) || (shift >= 32)) + { + Inlines.OpusAssert(false); + } +#endif + return a >> shift; + } + + [MethodImpl(INLINE_ATTR)] + public static long silk_RSHIFT64(long a, int shift) + { +#if DEBUG_MACROS + if ((shift < 0) || (shift >= 64)) + { + Inlines.OpusAssert(false); + } +#endif + return a >> shift; + } + + [MethodImpl(INLINE_ATTR)] + public static uint silk_RSHIFT_uint(uint a, int shift) + { +#if DEBUG_MACROS + if ((shift < 0) || (shift > 32)) + { + Inlines.OpusAssert(false); + } +#endif + return a >> shift; + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_ADD_LSHIFT(int a, int b, int shift) + { + int ret = a + (b << shift); +#if DEBUG_MACROS + if ((shift < 0) || (shift > 31) || ((long)ret != (long)a + (((long)b) << shift))) + { + Inlines.OpusAssert(false); + } +#endif + return ret; /* shift >= 0 */ + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_ADD_LSHIFT32(int a, int b, int shift) + { + int ret = a + (b << shift); +#if DEBUG_MACROS + if ((shift < 0) || (shift > 31) || ((long)ret != (long)a + (((long)b) << shift))) + { + Inlines.OpusAssert(false); + } +#endif + return ret; /* shift >= 0 */ + } + + [MethodImpl(INLINE_ATTR)] + public static uint silk_ADD_LSHIFT_uint(uint a, uint b, int shift) + { + uint ret; + ret = a + (b << shift); +#if DEBUG_MACROS + if ((shift < 0) || (shift > 32) || ((long)ret != (long)a + (((long)b) << shift))) + { + Inlines.OpusAssert(false); + } +#endif + return ret; /* shift >= 0 */ + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_ADD_RSHIFT(int a, int b, int shift) + { + int ret = a + (b >> shift); +#if DEBUG_MACROS + if ((shift < 0) || (shift > 31) || ((long)ret != (long)a + (((long)b) >> shift))) + { + Inlines.OpusAssert(false); + } +#endif + return ret; /* shift > 0 */ + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_ADD_RSHIFT32(int a, int b, int shift) + { + int ret = a + (b >> shift); +#if DEBUG_MACROS + if ((shift < 0) || (shift > 31) || ((long)ret != (long)a + (((long)b) >> shift))) + { + Inlines.OpusAssert(false); + } +#endif + return ret; /* shift > 0 */ + } + + [MethodImpl(INLINE_ATTR)] + public static uint silk_ADD_RSHIFT_uint(uint a, uint b, int shift) + { + uint ret; + ret = a + (b >> shift); +#if DEBUG_MACROS + if ((shift < 0) || (shift > 32) || ((long)ret != (long)a + (((long)b) >> shift))) + { + Inlines.OpusAssert(false); + } +#endif + return ret; /* shift > 0 */ + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_SUB_LSHIFT32(int a, int b, int shift) + { + int ret; + ret = a - (b << shift); +#if DEBUG_MACROS + if ((shift < 0) || (shift > 31) || ((long)ret != (long)a - (((long)b) << shift))) + { + Inlines.OpusAssert(false); + } +#endif + return ret; /* shift >= 0 */ + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_SUB_RSHIFT32(int a, int b, int shift) + { + int ret; + ret = a - (b >> shift); +#if DEBUG_MACROS + if ((shift < 0) || (shift > 31) || ((long)ret != (long)a - (((long)b) >> shift))) + { + Inlines.OpusAssert(false); + } +#endif + return ret; /* shift > 0 */ + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_RSHIFT_ROUND(int a, int shift) + { + int ret; + ret = shift == 1 ? (a >> 1) + (a & 1) : ((a >> (shift - 1)) + 1) >> 1; +#if DEBUG_MACROS + /* the marco definition can't handle a shift of zero */ + if ((shift <= 0) || (shift > 31) || ((long)ret != ((long)a + ((long)1 << (shift - 1))) >> shift)) + { + Inlines.OpusAssert(false); + } +#endif + return ret; + } + + [MethodImpl(INLINE_ATTR)] + public static long silk_RSHIFT_ROUND64(long a, int shift) + { + long ret; +#if DEBUG_MACROS + /* the macro definition can't handle a shift of zero */ + if ((shift <= 0) || (shift >= 64)) + { + Inlines.OpusAssert(false); + } +#endif + ret = shift == 1 ? (a >> 1) + (a & 1) : ((a >> (shift - 1)) + 1) >> 1; + return ret; + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_min(int a, int b) + { + return ((a) < (b)) ? (a) : (b); + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_max(int a, int b) + { + return ((a) > (b)) ? (a) : (b); + } + + [MethodImpl(INLINE_ATTR)] + public static float silk_min(float a, float b) + { + return ((a) < (b)) ? (a) : (b); + } + + [MethodImpl(INLINE_ATTR)] + public static float silk_max(float a, float b) + { + return ((a) > (b)) ? (a) : (b); + } + + /// + /// Macro to convert floating-point constants to fixed-point by applying a scalar factor + /// Because of limitations of the C# JIT, this macro is actually evaluated at runtime and therefore should not be used if you want to maximize performance + /// + [MethodImpl(INLINE_ATTR)] + public static int SILK_CONST(float number, int scale) + { + return ((int)((number) * ((long)1 << (scale)) + 0.5)); + } + + /* silk_min() versions with typecast in the function call */ + [MethodImpl(INLINE_ATTR)] + public static int silk_min_int(int a, int b) + { + return (((a) < (b)) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static short silk_min_16(short a, short b) + { + return (((a) < (b)) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_min_32(int a, int b) + { + return (((a) < (b)) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static long silk_min_64(long a, long b) + { + return (((a) < (b)) ? (a) : (b)); + } + + /* silk_min() versions with typecast in the function call */ + [MethodImpl(INLINE_ATTR)] + public static int silk_max_int(int a, int b) + { + return (((a) > (b)) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static short silk_max_16(short a, short b) + { + return (((a) > (b)) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_max_32(int a, int b) + { + return (((a) > (b)) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static long silk_max_64(long a, long b) + { + return (((a) > (b)) ? (a) : (b)); + } + + [MethodImpl(INLINE_ATTR)] + public static float silk_LIMIT(float a, float limit1, float limit2) + { + return ((limit1) > (limit2) ? ((a) > (limit1) ? (limit1) : ((a) < (limit2) ? (limit2) : (a))) : ((a) > (limit2) ? (limit2) : ((a) < (limit1) ? (limit1) : (a)))); + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_LIMIT(int a, int limit1, int limit2) + { + return silk_LIMIT_32(a, limit1, limit2); + } + + + [MethodImpl(INLINE_ATTR)] + public static int silk_LIMIT_int(int a, int limit1, int limit2) + { + return silk_LIMIT_32(a, limit1, limit2); + } + + + [MethodImpl(INLINE_ATTR)] + public static short silk_LIMIT_16(short a, short limit1, short limit2) + { + return ((limit1) > (limit2) ? ((a) > (limit1) ? (limit1) : ((a) < (limit2) ? (limit2) : (a))) : ((a) > (limit2) ? (limit2) : ((a) < (limit1) ? (limit1) : (a)))); + } + + + [MethodImpl(INLINE_ATTR)] + public static int silk_LIMIT_32(int a, int limit1, int limit2) + { + return ((limit1) > (limit2) ? ((a) > (limit1) ? (limit1) : ((a) < (limit2) ? (limit2) : (a))) : ((a) > (limit2) ? (limit2) : ((a) < (limit1) ? (limit1) : (a)))); + } + + + [MethodImpl(INLINE_ATTR)] + public static int silk_abs(int a) + { + // Be careful, silk_abs returns wrong when input equals to silk_intXX_MIN + return ((a) > 0) ? (a) : -(a); + } + + + [MethodImpl(INLINE_ATTR)] + public static int silk_abs_int16(int a) + { + return (a ^ (a >> 15)) - (a >> 15); + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_abs_int32(int a) + { + return (a ^ (a >> 31)) - (a >> 31); + } + + [MethodImpl(INLINE_ATTR)] + public static long silk_abs_int64(long a) + { + return ((a) > 0) ? (a) : -(a); + } + + [MethodImpl(INLINE_ATTR)] + public static long silk_sign(int a) + { + return (a) > 0 ? 1 : ((a) < 0 ? -1 : 0); + } + + /// + /// PSEUDO-RANDOM GENERATOR + /// Make sure to store the result as the seed for the next call (also in between + /// frames), otherwise result won't be random at all. When only using some of the + /// bits, take the most significant bits by right-shifting. + /// + [MethodImpl(INLINE_ATTR)] + public static int silk_RAND(int seed) + { + return silk_MLA_ovflw(907633515, seed, 196314165); + } + + /// + /// silk_SMMUL: Signed top word multiply. + /// + /// + /// + /// + [MethodImpl(INLINE_ATTR)] + public static int silk_SMMUL(int a32, int b32) + { + return (int)silk_RSHIFT64(silk_SMULL((a32), (b32)), 32); + } + + /* a32 + (b32 * (c32 >> 16)) >> 16 */ + [MethodImpl(INLINE_ATTR)] + public static int silk_SMLAWT(int a32, int b32, int c32) + { + int ret = a32 + ((b32 >> 16) * (c32 >> 16)) + (((b32 & 0x0000FFFF) * ((c32 >> 16)) >> 16)); +#if DEBUG_MACROS + if ((long)ret != (long)a32 + (((long)b32 * (c32 >> 16)) >> 16)) + { + Inlines.OpusAssert(false); + } +#endif + return ret; + } + + /// + /// Divide two int32 values and return result as int32 in a given Q-domain + /// + /// I numerator (Q0) + /// I denominator (Q0) + /// I Q-domain of result (>= 0) + /// O returns a good approximation of "(a32 << Qres) / b32" + [MethodImpl(INLINE_ATTR)] + public static int silk_DIV32_varQ(int a32, int b32, int Qres) + { + int a_headrm, b_headrm, lshift; + int b32_inv, a32_nrm, b32_nrm, result; + + Inlines.OpusAssert(b32 != 0); + Inlines.OpusAssert(Qres >= 0); + + /* Compute number of bits head room and normalize inputs */ + a_headrm = silk_CLZ32(silk_abs(a32)) - 1; + a32_nrm = silk_LSHIFT(a32, a_headrm); /* Q: a_headrm */ + b_headrm = silk_CLZ32(silk_abs(b32)) - 1; + b32_nrm = silk_LSHIFT(b32, b_headrm); /* Q: b_headrm */ + + /* Inverse of b32, with 14 bits of precision */ + b32_inv = silk_DIV32_16(int.MaxValue >> 2, silk_RSHIFT(b32_nrm, 16)); /* Q: 29 + 16 - b_headrm */ + + /* First approximation */ + result = silk_SMULWB(a32_nrm, b32_inv); /* Q: 29 + a_headrm - b_headrm */ + + /* Compute residual by subtracting product of denominator and first approximation */ + /* It's OK to overflow because the final value of a32_nrm should always be small */ + a32_nrm = silk_SUB32_ovflw(a32_nrm, silk_LSHIFT_ovflw(silk_SMMUL(b32_nrm, result), 3)); /* Q: a_headrm */ + + /* Refinement */ + result = silk_SMLAWB(result, a32_nrm, b32_inv); /* Q: 29 + a_headrm - b_headrm */ + + /* Convert to Qres domain */ + lshift = 29 + a_headrm - b_headrm - Qres; + if (lshift < 0) + { + return silk_LSHIFT_SAT32(result, -lshift); + } + else + { + if (lshift < 32) + { + return silk_RSHIFT(result, lshift); + } + else + { + /* Avoid undefined result */ + return 0; + } + } + } + + /// + /// Invert int32 value and return result as int32 in a given Q-domain + /// + /// I denominator (Q0) + /// I Q-domain of result (> 0) + /// a good approximation of "(1 << Qres) / b32" + [MethodImpl(INLINE_ATTR)] + public static int silk_INVERSE32_varQ(int b32, int Qres) + { + int b_headrm, lshift; + int b32_inv, b32_nrm, err_Q32, result; + + Inlines.OpusAssert(b32 != 0); + Inlines.OpusAssert(Qres > 0); + + /* Compute number of bits head room and normalize input */ + b_headrm = silk_CLZ32(silk_abs(b32)) - 1; + b32_nrm = silk_LSHIFT(b32, b_headrm); /* Q: b_headrm */ + + /* Inverse of b32, with 14 bits of precision */ + b32_inv = silk_DIV32_16(int.MaxValue >> 2, (short)(silk_RSHIFT(b32_nrm, 16))); /* Q: 29 + 16 - b_headrm */ + + /* First approximation */ + result = silk_LSHIFT(b32_inv, 16); /* Q: 61 - b_headrm */ + + /* Compute residual by subtracting product of denominator and first approximation from one */ + err_Q32 = silk_LSHIFT(((int)1 << 29) - silk_SMULWB(b32_nrm, b32_inv), 3); /* Q32 */ + + /* Refinement */ + result = silk_SMLAWW(result, err_Q32, b32_inv); /* Q: 61 - b_headrm */ + + /* Convert to Qres domain */ + lshift = 61 - b_headrm - Qres; + if (lshift <= 0) + { + return silk_LSHIFT_SAT32(result, -lshift); + } + else + { + if (lshift < 32) + { + return silk_RSHIFT(result, lshift); + } + else + { + /* Avoid undefined result */ + return 0; + } + } + } + + //////////////////////// from macros.h ///////////////////////////////////////////// + + /// + /// a32 + (b32 * (int)((short)(c32))) >> 16 output have to be 32bit int + /// + // fixme: This method should be as optimized as possible + [MethodImpl(INLINE_ATTR)] + public static int silk_SMLAWB(int a32, int b32, int c32) + { + //return (int)(a32 + ((b32 * (long)((short)c32)) >> 16)); + int ret; + ret = a32 + silk_SMULWB(b32, c32); +#if DEBUG_MACROS + if (silk_ADD32(a32, silk_SMULWB(b32, c32)) != silk_ADD_SAT32(a32, silk_SMULWB(b32, c32))) + { + Inlines.OpusAssert(false); + } +#endif + return ret; + } + + ///* (a32 * (b32 >> 16)) >> 16 */ + [MethodImpl(INLINE_ATTR)] + public static int silk_SMULWT(int a32, int b32) + { + return (((a32) >> 16) * ((b32) >> 16) + ((((a32) & 0x0000FFFF) * ((b32) >> 16)) >> 16)); + } + + ///* (int)((short)(a32)) * (b32 >> 16) */ + [MethodImpl(INLINE_ATTR)] + public static int silk_SMULBT(int a32, int b32) + { + return ((int)((short)(a32)) * ((b32) >> 16)); + } + + ///* a32 + (int)((short)(b32)) * (c32 >> 16) */ + [MethodImpl(INLINE_ATTR)] + public static int silk_SMLABT(int a32, int b32, int c32) + { + return ((a32) + ((int)((short)(b32))) * ((c32) >> 16)); + } + + ///* a64 + (b32 * c32) */ + [MethodImpl(INLINE_ATTR)] + public static long silk_SMLAL(long a64, int b32, int c32) + { + return (silk_ADD64((a64), ((long)(b32) * (long)(c32)))); + } + + [MethodImpl(INLINE_ATTR)] + public static void MatrixSet(T[] Matrix_base_adr, int Matrix_ptr, int row, int column, int N, T value) + { + Matrix_base_adr[Matrix_ptr + (row * N) + column] = value; + } + + [MethodImpl(INLINE_ATTR)] + public static int MatrixGetPointer(int row, int column, int N) + { + return (row * N) + column; + } + + [MethodImpl(INLINE_ATTR)] + public static T MatrixGet(T[] Matrix_base_adr, int row, int column, int N) + { + return Matrix_base_adr[((row) * (N)) + (column)]; + } + + public static T MatrixGet(T[] Matrix_base_adr, int matrix_ptr, int row, int column, int N) + { + return Matrix_base_adr[matrix_ptr + (row * N) + column]; + } + + [MethodImpl(INLINE_ATTR)] + public static void MatrixSet(T[] Matrix_base_adr, int row, int column, int N, T value) + { + Matrix_base_adr[((row) * (N)) + (column)] = value; + } + + /// + /// (a32 * b32) >> 16 + /// + [MethodImpl(INLINE_ATTR)] + public static int silk_SMULWW(int a32, int b32) + { +#if DEBUG_MACROS + int ret, tmp1, tmp2; + long ret64; + bool fail = false; + + ret = silk_SMULWB(a32, b32); + tmp1 = silk_RSHIFT_ROUND(b32, 16); + tmp2 = silk_MUL(a32, tmp1); + + fail |= (long)tmp2 != (long)a32 * (long)tmp1; + + tmp1 = ret; + ret = silk_ADD32(tmp1, tmp2); + fail |= silk_ADD32(tmp1, tmp2) != silk_ADD_SAT32(tmp1, tmp2); + + ret64 = silk_RSHIFT64(silk_SMULL(a32, b32), 16); + fail |= (long)ret != ret64; + + if (fail) + { + Inlines.OpusAssert(false); + } + + return ret; +#else + //return CHOP32(((long)(a32) * (b32)) >> 16); + return silk_MLA(silk_SMULWB((a32), (b32)), (a32), silk_RSHIFT_ROUND((b32), 16)); +#endif + } + + /// + /// a32 + ((b32 * c32) >> 16) + /// + [MethodImpl(INLINE_ATTR)] + public static int silk_SMLAWW(int a32, int b32, int c32) + { +#if DEBUG_MACROS + int ret, tmp; + + tmp = silk_SMULWW(b32, c32); + ret = silk_ADD32(a32, tmp); + if (ret != silk_ADD_SAT32(a32, tmp)) + { + Inlines.OpusAssert(false); + } + return ret; +#else + //return CHOP32(((a32) + (((long)(b32) * (c32)) >> 16))); + return silk_MLA(silk_SMLAWB((a32), (b32), (c32)), (b32), silk_RSHIFT_ROUND((c32), 16)); +#endif + } + + /* count leading zeros of opus_int64 */ + [MethodImpl(INLINE_ATTR)] + public static int silk_CLZ64(long input) + { + int in_upper; + + in_upper = (int)silk_RSHIFT64(input, 32); + if (in_upper == 0) + { + /* Search in the lower 32 bits */ + return 32 + silk_CLZ32(unchecked((int)input)); + } + else { + /* Search in the upper 32 bits */ + return silk_CLZ32(in_upper); + } + } + + [MethodImpl(INLINE_ATTR)] + public static int silk_CLZ32(int in32) + { + return in32 == 0 ? 32 : 32 - EC_ILOG(unchecked((uint)in32)); + } + + /// + /// Get number of leading zeros and fractional part (the bits right after the leading one) + /// + /// input + /// number of leading zeros + /// the 7 bits right after the leading one + [MethodImpl(INLINE_ATTR)] + public static void silk_CLZ_FRAC(int input, out int lz, out int frac_Q7) + { + int lzeros = silk_CLZ32(input); + + lz = lzeros; + frac_Q7 = silk_ROR32(input, 24 - lzeros) & 0x7f; + } + + /// + /// Approximation of square root. + /// Accuracy: +/- 10% for output values > 15 + /// +/- 2.5% for output values > 120 + /// + /// + /// + [MethodImpl(INLINE_ATTR)] + public static int silk_SQRT_APPROX(int x) + { +#if PARITY + int y, lz, frac_Q7; + + if (x <= 0) + { + return 0; + } + + silk_CLZ_FRAC(x, out lz, out frac_Q7); + + if ((lz & 1) != 0) + { + y = 32768; + } + else { + y = 46214; // 46214 = sqrt(2) * 32768 + } + + // get scaling right + y >>= silk_RSHIFT(lz, 1); + + // increment using fractional part of input + y = silk_SMLAWB(y, y, silk_SMULBB(213, frac_Q7)); + + return y; +#else + // This is 10x faster + return (int)(Math.Sqrt(x)); +#endif + } + + [MethodImpl(INLINE_ATTR)] + public static int MUL32_FRAC_Q(int a32, int b32, int Q) + { + return ((int)(silk_RSHIFT_ROUND64(silk_SMULL(a32, b32), Q))); + } + + /// + /// Approximation of 128 * log2() (very close inverse of silk_log2lin()) + /// Convert input to a log scale + /// + /// (I) input in linear scale + /// + [MethodImpl(INLINE_ATTR)] + public static int silk_lin2log(int inLin) + { + int lz, frac_Q7; + + silk_CLZ_FRAC(inLin, out lz, out frac_Q7); + + // Piece-wise parabolic approximation + return silk_LSHIFT(31 - lz, 7) + silk_SMLAWB(frac_Q7, silk_MUL(frac_Q7, 128 - frac_Q7), 179); + } + + /// + /// Approximation of 2^() (very close inverse of silk_lin2log()) + /// Convert input to a linear scale + /// + /// input on log scale + /// Linearized value + [MethodImpl(INLINE_ATTR)] + public static int silk_log2lin(int inLog_Q7) + { + int output, frac_Q7; + + if (inLog_Q7 < 0) + { + return 0; + } + else if (inLog_Q7 >= 3967) + { + return int.MaxValue; + } + + output = silk_LSHIFT(1, silk_RSHIFT(inLog_Q7, 7)); + frac_Q7 = inLog_Q7 & 0x7F; + + if (inLog_Q7 < 2048) + { + /* Piece-wise parabolic approximation */ + output = silk_ADD_RSHIFT32(output, silk_MUL(output, silk_SMLAWB(frac_Q7, silk_SMULBB(frac_Q7, 128 - frac_Q7), -174)), 7); + } + else + { + /* Piece-wise parabolic approximation */ + output = silk_MLA(output, silk_RSHIFT(output, 7), silk_SMLAWB(frac_Q7, silk_SMULBB(frac_Q7, 128 - frac_Q7), -174)); + } + + return output; + } + + /// + /// Interpolate two vectors + /// + /// (O) interpolated vector [MAX_LPC_ORDER] + /// (I) first vector [MAX_LPC_ORDER] + /// (I) second vector [MAX_LPC_ORDER] + /// (I) interp. factor, weight on 2nd vector + /// (I) number of parameters + [MethodImpl(INLINE_ATTR)] + public static void silk_interpolate( + short[] xi, + short[] x0, + short[] x1, + int ifact_Q2, + int d) + { + int i; + + Inlines.OpusAssert(ifact_Q2 >= 0); + Inlines.OpusAssert(ifact_Q2 <= 4); + + for (i = 0; i < d; i++) + { + xi[i] = (short)silk_ADD_RSHIFT(x0[i], silk_SMULBB(x1[i] - x0[i], ifact_Q2), 2); + } + } + + /// + /// Inner product with bit-shift + /// + /// I input vector 1 + /// I input vector 2 + /// I number of bits to shift + /// I vector lengths + /// + [MethodImpl(INLINE_ATTR)] + public static int silk_inner_prod_aligned_scale( + short[] inVec1, + short[] inVec2, + int scale, + int len) + { + int i, sum = 0; + for (i = 0; i < len; i++) + { + sum = silk_ADD_RSHIFT32(sum, silk_SMULBB(inVec1[i], inVec2[i]), scale); + } + + return sum; + } + + /* Copy and multiply a vector by a constant */ + [MethodImpl(INLINE_ATTR)] + public static void silk_scale_copy_vector16( + short[] data_out, + int data_out_ptr, + short[] data_in, + int data_in_ptr, + int gain_Q16, /* I Gain in Q16 */ + int dataSize /* I Length */ + ) + { + for (int i = 0; i < dataSize; i++) + { + data_out[data_out_ptr + i] = (short)(silk_SMULWB(gain_Q16, data_in[data_in_ptr + i])); + } + } + + /* Multiply a vector by a constant */ + [MethodImpl(INLINE_ATTR)] + public static void silk_scale_vector32_Q26_lshift_18( + int[] data1, /* I/O Q0/Q18 */ + int data1_ptr, + int gain_Q26, /* I Q26 */ + int dataSize /* I length */ + ) + { + for (int i = data1_ptr; i < data1_ptr + dataSize; i++) + { + data1[i] = (int)(silk_RSHIFT64(silk_SMULL(data1[i], gain_Q26), 8)); /* OUTPUT: Q18 */ + } + } + + /* sum = for(i=0;i + /// returns the value that has fewer higher-order bits, ignoring sign bit (? I think?) + /// + /// + /// + /// + [MethodImpl(INLINE_ATTR)] + public static uint EC_MINI(uint a, uint b) + { + return unchecked(a + ((b - a) & ((b < a) ? 0xFFFFFFFFU : 0))); + } + + /// + /// Counts leading zeroes + /// + /// + /// + [MethodImpl(INLINE_ATTR)] + public static int EC_CLZ(uint x) + { + if (x == 0) + return 0; + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + uint y = x - ((x >> 1) & 0x55555555); + y = (((y >> 2) & 0x33333333) + (y & 0x33333333)); + y = (((y >> 4) + y) & 0x0f0f0f0f); + y += (y >> 8); + y += (y >> 16); + y = (y & 0x0000003f); + return (int)(1 - y); + } + + //public static int clz_fast(uint x) + //{ + // x |= (x >> 1); + // x |= (x >> 2); + // x |= (x >> 4); + // x |= (x >> 8); + // x |= (x >> 16); + // uint y = x - ((x >> 1) & 0x55555555); + // y = (((y >> 2) & 0x33333333) + (y & 0x33333333)); + // y = (((y >> 4) + y) & 0x0f0f0f0f); + // y += (y >> 8); + // y += (y >> 16); + // y = (y & 0x0000003f); + // return (int)(32 - y); + //} + + /// + /// returns inverse base-2 log of a value + /// + /// + /// + [MethodImpl(INLINE_ATTR)] + public static int EC_ILOG(uint x) + { +#if PARITY + if(x == 0) + return 1; + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + uint y = x - ((x >> 1) & 0x55555555); + y = (((y >> 2) & 0x33333333) + (y & 0x33333333)); + y = (((y >> 4) + y) & 0x0f0f0f0f); + y += (y >> 8); + y += (y >> 16); + y = (y & 0x0000003f); + return (int)y; +#else + // On a Pentium M, this branchless version tested as the fastest on + // 1,000,000,000 random 32-bit integers, edging out a similar version with + // branches, and a 256-entry LUT version. + int ret; + int m; + ret = x == 0 ? 0 : 1; + m = ((x & 0xFFFF0000U) == 0 ? 0 : 1) << 4; + x >>= m; + ret |= m; + m = ((x & 0xFF00U) == 0 ? 0 : 1) << 3; + x >>= m; + ret |= m; + m = ((x & 0xF0U) == 0 ? 0 : 1) << 2; + x >>= m; + ret |= m; + m = ((x & 0xCU) == 0 ? 0 : 1) << 1; + x >>= m; + ret |= m; + ret += (x & 0x2U) == 0 ? 0 : 1; + return ret; +#endif + } + +#endregion + +#region C++ Math + + [MethodImpl(INLINE_ATTR)] + public static int abs(int a) + { + if (a < 0) + return 0 - a; + return a; + } + +#endregion + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Common/Resampler.cs b/Libraries/Concentus/CSharp/Concentus/Common/Resampler.cs new file mode 100644 index 000000000..2e0d5254b --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Common/Resampler.cs @@ -0,0 +1,1035 @@ +/* Copyright (C) 2007-2008 Jean-Marc Valin + Copyright (C) 2008 Thorvald Natvig + Ported to C# by Logan Stromberg + + File: Resampler.cs + Arbitrary resampling code + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + The design goals of this code are: + - Very fast algorithm + - SIMD-friendly algorithm + - Low memory requirement + - Good *perceptual* quality (and not best SNR) + + Warning: This resampler is relatively new. Although I think I got rid of + all the major bugs and I don't expect the API to change anymore, there + may be something I've missed. So use with caution. + + This algorithm is based on this original resampling algorithm: + Smith, Julius O. Digital Audio Resampling Home Page + Center for Computer Research in Music and Acoustics (CCRMA), + Stanford University, 2007. + Web published at http://www-ccrma.stanford.edu/~jos/resample/. + + There is one main difference, though. This resampler uses cubic + interpolation instead of linear interpolation in the above paper. This + makes the table much smaller and makes it possible to compute that table + on a per-stream basis. In turn, being able to tweak the table for each + stream makes it possible to both reduce complexity on simple ratios + (e.g. 2/3), and get rid of the rounding operations in the inner loop. + The latter both reduces CPU time and makes the algorithm more SIMD-friendly. +*/ + +using Concentus.Celt; +using Concentus.Common.CPlusPlus; +using System; + +namespace Concentus.Common +{ + /// + /// Arbitrary-rate audio resampler originally implemented for the Speex codec. + /// + public class SpeexResampler + { + private const int FIXED_STACK_ALLOC = 8192; + + #region Encoder state + + private int in_rate = 0; + private int out_rate = 0; + private int num_rate = 0; + private int den_rate = 0; + + private int quality = 0; + private int nb_channels = 0; + private int filt_len = 0; + private int mem_alloc_size = 0; + private int buffer_size = 0; + private int int_advance = 0; + private int frac_advance = 0; + private float cutoff = 0; + private int oversample = 0; + private int initialised = 0; + private int started = 0; + + /* These are per-channel */ + private int[] last_sample = null; + private int[] samp_frac_num = null; + private int[] magic_samples = null; + + private short[] mem = null; + private short[] sinc_table = null; + private int sinc_table_length = 0; + private resampler_basic_func resampler_ptr = null; + + int in_stride = 0; + int out_stride = 0; + + #endregion + + #region Helper classes and tables + + private class FuncDef + { + public FuncDef(double[] t, int os) + { + table = t; + oversample = os; + } + + public double[] table; + public int oversample; + + public static readonly double[] kaiser12_table/*[68]*/ = { + 0.99859849, 1.00000000, 0.99859849, 0.99440475, 0.98745105, 0.97779076, + 0.96549770, 0.95066529, 0.93340547, 0.91384741, 0.89213598, 0.86843014, + 0.84290116, 0.81573067, 0.78710866, 0.75723148, 0.72629970, 0.69451601, + 0.66208321, 0.62920216, 0.59606986, 0.56287762, 0.52980938, 0.49704014, + 0.46473455, 0.43304576, 0.40211431, 0.37206735, 0.34301800, 0.31506490, + 0.28829195, 0.26276832, 0.23854851, 0.21567274, 0.19416736, 0.17404546, + 0.15530766, 0.13794294, 0.12192957, 0.10723616, 0.09382272, 0.08164178, + 0.07063950, 0.06075685, 0.05193064, 0.04409466, 0.03718069, 0.03111947, + 0.02584161, 0.02127838, 0.01736250, 0.01402878, 0.01121463, 0.00886058, + 0.00691064, 0.00531256, 0.00401805, 0.00298291, 0.00216702, 0.00153438, + 0.00105297, 0.00069463, 0.00043489, 0.00025272, 0.00013031, 0.0000527734, + 0.00001000, 0.00000000}; + /* + static double kaiser12_table[36] = { + 0.99440475, 1.00000000, 0.99440475, 0.97779076, 0.95066529, 0.91384741, + 0.86843014, 0.81573067, 0.75723148, 0.69451601, 0.62920216, 0.56287762, + 0.49704014, 0.43304576, 0.37206735, 0.31506490, 0.26276832, 0.21567274, + 0.17404546, 0.13794294, 0.10723616, 0.08164178, 0.06075685, 0.04409466, + 0.03111947, 0.02127838, 0.01402878, 0.00886058, 0.00531256, 0.00298291, + 0.00153438, 0.00069463, 0.00025272, 0.0000527734, 0.00000500, 0.00000000}; + */ + public static readonly double[] kaiser10_table/*[36]*/ = { + 0.99537781, 1.00000000, 0.99537781, 0.98162644, 0.95908712, 0.92831446, + 0.89005583, 0.84522401, 0.79486424, 0.74011713, 0.68217934, 0.62226347, + 0.56155915, 0.50119680, 0.44221549, 0.38553619, 0.33194107, 0.28205962, + 0.23636152, 0.19515633, 0.15859932, 0.12670280, 0.09935205, 0.07632451, + 0.05731132, 0.04193980, 0.02979584, 0.02044510, 0.01345224, 0.00839739, + 0.00488951, 0.00257636, 0.00115101, 0.00035515, 0.00000000, 0.00000000}; + + public static readonly double[] kaiser8_table/*[36]*/ = { + 0.99635258, 1.00000000, 0.99635258, 0.98548012, 0.96759014, 0.94302200, + 0.91223751, 0.87580811, 0.83439927, 0.78875245, 0.73966538, 0.68797126, + 0.63451750, 0.58014482, 0.52566725, 0.47185369, 0.41941150, 0.36897272, + 0.32108304, 0.27619388, 0.23465776, 0.19672670, 0.16255380, 0.13219758, + 0.10562887, 0.08273982, 0.06335451, 0.04724088, 0.03412321, 0.02369490, + 0.01563093, 0.00959968, 0.00527363, 0.00233883, 0.00050000, 0.00000000}; + + public static readonly double[] kaiser6_table/*[36]*/ = { + 0.99733006, 1.00000000, 0.99733006, 0.98935595, 0.97618418, 0.95799003, + 0.93501423, 0.90755855, 0.87598009, 0.84068475, 0.80211977, 0.76076565, + 0.71712752, 0.67172623, 0.62508937, 0.57774224, 0.53019925, 0.48295561, + 0.43647969, 0.39120616, 0.34752997, 0.30580127, 0.26632152, 0.22934058, + 0.19505503, 0.16360756, 0.13508755, 0.10953262, 0.08693120, 0.06722600, + 0.05031820, 0.03607231, 0.02432151, 0.01487334, 0.00752000, 0.00000000}; + }; + + private class QualityMapping + { + public int base_length = 0; + public int oversample = 0; + public float downsample_bandwidth = 0; + public float upsample_bandwidth = 0; + public FuncDef window_func = null; + + private QualityMapping(int bl, int os, float dsb, float usb, FuncDef wf) + { + base_length = bl; + oversample = os; + downsample_bandwidth = dsb; + upsample_bandwidth = usb; + window_func = wf; + } + + /* This table maps conversion quality to private parameters. There are two + reasons that explain why the up-sampling bandwidth is larger than the + down-sampling bandwidth: + 1) When up-sampling, we can assume that the spectrum is already attenuated + close to the Nyquist rate (from an A/D or a previous resampling filter) + 2) Any aliasing that occurs very close to the Nyquist rate will be masked + by the sinusoids/noise just below the Nyquist rate (guaranteed only for + up-sampling). + */ + public static readonly QualityMapping[] quality_map = { + new QualityMapping( 8, 4, 0.830f, 0.860f, new FuncDef(FuncDef.kaiser6_table, 32) ), /* Q0 */ + new QualityMapping( 16, 4, 0.850f, 0.880f, new FuncDef(FuncDef.kaiser6_table, 32) ), /* Q1 */ + new QualityMapping( 32, 4, 0.882f, 0.910f, new FuncDef(FuncDef.kaiser6_table, 32) ), /* Q2 */ /* 82.3% cutoff ( ~60 dB stop) 6 */ + new QualityMapping( 48, 8, 0.895f, 0.917f, new FuncDef(FuncDef.kaiser8_table, 32) ), /* Q3 */ /* 84.9% cutoff ( ~80 dB stop) 8 */ + new QualityMapping( 64, 8, 0.921f, 0.940f, new FuncDef(FuncDef.kaiser8_table, 32) ), /* Q4 */ /* 88.7% cutoff ( ~80 dB stop) 8 */ + new QualityMapping( 80, 16, 0.922f, 0.940f, new FuncDef(FuncDef.kaiser10_table, 32)), /* Q5 */ /* 89.1% cutoff (~100 dB stop) 10 */ + new QualityMapping( 96, 16, 0.940f, 0.945f, new FuncDef(FuncDef.kaiser10_table, 32)), /* Q6 */ /* 91.5% cutoff (~100 dB stop) 10 */ + new QualityMapping(128, 16, 0.950f, 0.950f, new FuncDef(FuncDef.kaiser10_table, 32)), /* Q7 */ /* 93.1% cutoff (~100 dB stop) 10 */ + new QualityMapping(160, 16, 0.960f, 0.960f, new FuncDef(FuncDef.kaiser10_table, 32)), /* Q8 */ /* 94.5% cutoff (~100 dB stop) 10 */ + new QualityMapping(192, 32, 0.968f, 0.968f, new FuncDef(FuncDef.kaiser12_table, 64)), /* Q9 */ /* 95.5% cutoff (~100 dB stop) 10 */ + new QualityMapping(256, 32, 0.975f, 0.975f, new FuncDef(FuncDef.kaiser12_table, 64)), /* Q10 */ /* 96.6% cutoff (~100 dB stop) 10 */ + }; + } + + #endregion + + #region Private code + + /// + /// typedef int (* resampler_basic_func)(SpeexResamplerState*, int , Pointer<short>, int *, Pointer<short>, Pointer<int>); + /// + private delegate int resampler_basic_func(int channel_index, short[] input, int input_ptr, ref int in_len, short[] output, int output_ptr, ref int out_len); + + private static short WORD2INT(float x) + { + return x < short.MinValue ? short.MinValue : (x > short.MaxValue ? short.MaxValue : (short)x); + } + + /*8,24,40,56,80,104,128,160,200,256,320*/ + private static double compute_func(float x, FuncDef func) + { + float y, frac; + double interp0, interp1, interp2, interp3; + int ind; + y = x * func.oversample; + ind = (int)Math.Floor(y); + frac = (y - ind); + /* CSE with handle the repeated powers */ + interp3 = -0.1666666667 * frac + 0.1666666667 * (frac * frac * frac); + interp2 = frac + 0.5 * (frac * frac) - 0.5 * (frac * frac * frac); + /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/ + interp0 = -0.3333333333 * frac + 0.5 * (frac * frac) - 0.1666666667 * (frac * frac * frac); + /* Just to make sure we don't have rounding problems */ + interp1 = 1.0f - interp3 - interp2 - interp0; + + /*sum = frac*accum[1] + (1-frac)*accum[2];*/ + return interp0 * func.table[ind] + interp1 * func.table[ind + 1] + interp2 * func.table[ind + 2] + interp3 * func.table[ind + 3]; + } + + /* The slow way of computing a sinc for the table. Should improve that some day */ + private static short sinc(float cutoff, float x, int N, FuncDef window_func) + { + /*fprintf (stderr, "%f ", x);*/ + float xx = x * cutoff; + if (Math.Abs(x) < 1e-6f) + return WORD2INT(32768.0f * cutoff); + else if (Math.Abs(x) > .5f * N) + return 0; + /*FIXME: Can it really be any slower than this? */ + return WORD2INT(32768.0f * cutoff * (float)Math.Sin((float)Math.PI * xx) / ((float)Math.PI * xx) * (float)compute_func(Math.Abs(2.0f * x / N), window_func)); + } + + private static void cubic_coef(short x, out short interp0, out short interp1, out short interp2, out short interp3) + { + /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation + but I know it's MMSE-optimal on a sinc */ + short x2, x3; + x2 = Inlines.MULT16_16_P15(x, x); + x3 = Inlines.MULT16_16_P15(x, x2); + interp0 = (short)Inlines.PSHR32(Inlines.MULT16_16(Inlines.QCONST16(-0.16667f, 15), x) + Inlines.MULT16_16(Inlines.QCONST16(0.16667f, 15), x3), 15); + interp1 = Inlines.EXTRACT16(Inlines.EXTEND32(x) + Inlines.SHR32(Inlines.SUB32(Inlines.EXTEND32(x2), Inlines.EXTEND32(x3)), 1)); + interp3 = (short)Inlines.PSHR32(Inlines.MULT16_16(Inlines.QCONST16(-0.33333f, 15), x) + Inlines.MULT16_16(Inlines.QCONST16(.5f, 15), x2) - Inlines.MULT16_16(Inlines.QCONST16(0.16667f, 15), x3), 15); + /* Just to make sure we don't have rounding problems */ + interp2 = (short)(CeltConstants.Q15_ONE - interp0 - interp1 - interp3); + if (interp2 < 32767) + interp2 += 1; + } + + private int resampler_basic_direct_single(int channel_index, short[] input, int input_ptr, ref int in_len, short[] output, int output_ptr, ref int out_len) + { + int N = this.filt_len; + int out_sample = 0; + int last_sample = this.last_sample[channel_index]; + int samp_frac_num = this.samp_frac_num[channel_index]; + + int sum; + + while (!(last_sample >= in_len || out_sample >= out_len)) + { + int sinct = (int)samp_frac_num * N; + int iptr = input_ptr + last_sample; + + int j; + sum = 0; + for (j = 0; j < N; j++) + { + sum += Inlines.MULT16_16(this.sinc_table[sinct + j], input[iptr + j]); + } + + output[output_ptr + (this.out_stride * out_sample++)] = Inlines.SATURATE16(Inlines.PSHR32(sum, 15)); + last_sample += this.int_advance; + samp_frac_num += (int)this.frac_advance; + if (samp_frac_num >= this.den_rate) + { + samp_frac_num -= this.den_rate; + last_sample++; + } + } + + this.last_sample[channel_index] = last_sample; + this.samp_frac_num[channel_index] = samp_frac_num; + return out_sample; + } + + private int resampler_basic_interpolate_single(int channel_index, short[] input, int input_ptr, ref int in_len, short[] output, int output_ptr, ref int out_len) + { + int N = this.filt_len; + int out_sample = 0; + int last_sample = this.last_sample[channel_index]; + int samp_frac_num = this.samp_frac_num[channel_index]; + int sum; + + while (!(last_sample >= in_len || out_sample >= out_len)) + { + int iptr = input_ptr + last_sample; + + int offset = samp_frac_num * this.oversample / this.den_rate; + short frac = (short)Inlines.PDIV32(Inlines.SHL32((samp_frac_num * this.oversample) % this.den_rate, 15), this.den_rate); + short interp0, interp1, interp2, interp3; + + int j; + int accum0 = 0; + int accum1 = 0; + int accum2 = 0; + int accum3 = 0; + + for (j = 0; j < N; j++) + { + short curr_in = input[iptr + j]; + accum0 += Inlines.MULT16_16(curr_in, this.sinc_table[4 + (j + 1) * (int)this.oversample - offset - 2]); + accum1 += Inlines.MULT16_16(curr_in, this.sinc_table[4 + (j + 1) * (int)this.oversample - offset - 1]); + accum2 += Inlines.MULT16_16(curr_in, this.sinc_table[4 + (j + 1) * (int)this.oversample - offset]); + accum3 += Inlines.MULT16_16(curr_in, this.sinc_table[4 + (j + 1) * (int)this.oversample - offset + 1]); + } + + cubic_coef(frac, out interp0, out interp1, out interp2, out interp3); + sum = Inlines.MULT16_32_Q15(interp0, Inlines.SHR32(accum0, 1)) + + Inlines.MULT16_32_Q15(interp1, Inlines.SHR32(accum1, 1)) + + Inlines.MULT16_32_Q15(interp2, Inlines.SHR32(accum2, 1)) + + Inlines.MULT16_32_Q15(interp3, Inlines.SHR32(accum3, 1)); + + output[output_ptr + (out_stride * out_sample++)] = Inlines.SATURATE16(Inlines.PSHR32(sum, 14)); + last_sample += int_advance; + samp_frac_num += (int)frac_advance; + if (samp_frac_num >= den_rate) + { + samp_frac_num -= den_rate; + last_sample++; + } + } + + this.last_sample[channel_index] = last_sample; + this.samp_frac_num[channel_index] = samp_frac_num; + return out_sample; + } + + private void update_filter() + { + int old_length; + + old_length = this.filt_len; + this.oversample = QualityMapping.quality_map[this.quality].oversample; + this.filt_len = QualityMapping.quality_map[this.quality].base_length; + + if (this.num_rate > this.den_rate) + { + /* down-sampling */ + this.cutoff = QualityMapping.quality_map[this.quality].downsample_bandwidth * this.den_rate / this.num_rate; + /* FIXME: divide the numerator and denominator by a certain amount if they're too large */ + this.filt_len = this.filt_len * this.num_rate / this.den_rate; + /* Round up to make sure we have a multiple of 8 */ + this.filt_len = ((this.filt_len - 1) & (~0x7)) + 8; + if (2 * this.den_rate < this.num_rate) + this.oversample >>= 1; + if (4 * this.den_rate < this.num_rate) + this.oversample >>= 1; + if (8 * this.den_rate < this.num_rate) + this.oversample >>= 1; + if (16 * this.den_rate < this.num_rate) + this.oversample >>= 1; + if (this.oversample < 1) + this.oversample = 1; + } + else { + /* up-sampling */ + this.cutoff = QualityMapping.quality_map[this.quality].upsample_bandwidth; + } + + if (this.den_rate <= 16 * (this.oversample + 8)) + { + int i; + if (this.sinc_table == null) + this.sinc_table = new short[this.filt_len * this.den_rate]; + else if (this.sinc_table_length < this.filt_len * this.den_rate) + { + this.sinc_table = new short[this.filt_len * this.den_rate]; + this.sinc_table_length = this.filt_len * this.den_rate; + } + for (i = 0; i < this.den_rate; i++) + { + int j; + for (j = 0; j < this.filt_len; j++) + { + this.sinc_table[i * this.filt_len + j] = sinc(this.cutoff, ((j - (int)this.filt_len / 2 + 1) - ((float)i) / this.den_rate), this.filt_len, QualityMapping.quality_map[this.quality].window_func); + } + } + this.resampler_ptr = resampler_basic_direct_single; + /*fprintf (stderr, "resampler uses direct sinc table and normalised cutoff %f\n", cutoff);*/ + } + else { + int i; + if (this.sinc_table == null) + this.sinc_table = new short[this.filt_len * this.oversample + 8]; + else if (this.sinc_table_length < this.filt_len * this.oversample + 8) + { + this.sinc_table = new short[this.filt_len * this.oversample + 8]; + this.sinc_table_length = this.filt_len * this.oversample + 8; + } + for (i = -4; i < (int)(this.oversample * this.filt_len + 4); i++) + this.sinc_table[i + 4] = sinc(this.cutoff, (i / (float)this.oversample - this.filt_len / 2), this.filt_len, QualityMapping.quality_map[this.quality].window_func); + this.resampler_ptr = resampler_basic_interpolate_single; + /*fprintf (stderr, "resampler uses interpolated sinc table and normalised cutoff %f\n", cutoff);*/ + } + this.int_advance = this.num_rate / this.den_rate; + this.frac_advance = this.num_rate % this.den_rate; + + + /* Here's the place where we update the filter memory to take into account + the change in filter length. It's probably the messiest part of the code + due to handling of lots of corner cases. */ + if (this.mem == null) + { + int i; + this.mem_alloc_size = this.filt_len - 1 + this.buffer_size; + this.mem = new short[this.nb_channels * this.mem_alloc_size]; + for (i = 0; i < this.nb_channels * this.mem_alloc_size; i++) + this.mem[i] = 0; + /*speex_warning("init filter");*/ + } + else if (this.started == 0) + { + int i; + this.mem_alloc_size = this.filt_len - 1 + this.buffer_size; + this.mem = new short[this.nb_channels * this.mem_alloc_size]; + for (i = 0; i < this.nb_channels * this.mem_alloc_size; i++) + this.mem[i] = 0; + /*speex_warning("reinit filter");*/ + } + else if (this.filt_len > old_length) + { + int i; + /* Increase the filter length */ + /*speex_warning("increase filter size");*/ + int old_alloc_size = this.mem_alloc_size; + if ((this.filt_len - 1 + this.buffer_size) > this.mem_alloc_size) + { + this.mem_alloc_size = this.filt_len - 1 + this.buffer_size; + this.mem = new short[this.nb_channels * this.mem_alloc_size]; + } + for (i = this.nb_channels - 1; i >= 0; i--) + { + int j; + int olen = old_length; + /*if (st.magic_samples[i])*/ + { + /* Try and remove the magic samples as if nothing had happened */ + + /* FIXME: This is wrong but for now we need it to avoid going over the array bounds */ + olen = old_length + 2 * this.magic_samples[i]; + for (j = old_length - 2 + this.magic_samples[i]; j >= 0; j--) + this.mem[i * this.mem_alloc_size + j + this.magic_samples[i]] = this.mem[i * old_alloc_size + j]; + for (j = 0; j < this.magic_samples[i]; j++) + this.mem[i * this.mem_alloc_size + j] = 0; + this.magic_samples[i] = 0; + } + if (this.filt_len > olen) + { + /* If the new filter length is still bigger than the "augmented" length */ + /* Copy data going backward */ + for (j = 0; j < olen - 1; j++) + this.mem[i * this.mem_alloc_size + (this.filt_len - 2 - j)] = this.mem[i * this.mem_alloc_size + (olen - 2 - j)]; + /* Then put zeros for lack of anything better */ + for (; j < this.filt_len - 1; j++) + this.mem[i * this.mem_alloc_size + (this.filt_len - 2 - j)] = 0; + /* Adjust last_sample */ + this.last_sample[i] += (this.filt_len - olen) / 2; + } + else { + /* Put back some of the magic! */ + this.magic_samples[i] = (olen - this.filt_len) / 2; + for (j = 0; j < this.filt_len - 1 + this.magic_samples[i]; j++) + this.mem[i * this.mem_alloc_size + j] = this.mem[i * this.mem_alloc_size + j + this.magic_samples[i]]; + } + } + } + else if (this.filt_len < old_length) + { + int i; + /* Reduce filter length, this a bit tricky. We need to store some of the memory as "magic" + samples so they can be used directly as input the next time(s) */ + for (i = 0; i < this.nb_channels; i++) + { + int j; + int old_magic = this.magic_samples[i]; + this.magic_samples[i] = (old_length - this.filt_len) / 2; + /* We must copy some of the memory that's no longer used */ + /* Copy data going backward */ + for (j = 0; j < this.filt_len - 1 + this.magic_samples[i] + old_magic; j++) + this.mem[i * this.mem_alloc_size + j] = this.mem[i * this.mem_alloc_size + j + this.magic_samples[i]]; + this.magic_samples[i] += old_magic; + } + } + } + + private void speex_resampler_process_native(int channel_index, ref int in_len, short[] output, int output_ptr, ref int out_len) + { + int j = 0; + int N = this.filt_len; + int out_sample = 0; + int mem_ptr = channel_index * this.mem_alloc_size; + int ilen; + + this.started = 1; + + /* Call the right resampler through the function ptr */ + out_sample = this.resampler_ptr(channel_index, this.mem, mem_ptr, ref in_len, output, output_ptr, ref out_len); + + if (this.last_sample[channel_index] < (int)in_len) + in_len = this.last_sample[channel_index]; + out_len = out_sample; + this.last_sample[channel_index] -= in_len; + + ilen = in_len; + + for (j = mem_ptr; j < N - 1 + mem_ptr; ++j) + this.mem[j] = this.mem[j + ilen]; + } + + private int speex_resampler_magic(int channel_index, short[] output, ref int output_ptr, int out_len) + { + int tmp_in_len = this.magic_samples[channel_index]; + int mem_ptr = channel_index * this.mem_alloc_size; + int N = this.filt_len; + + this.speex_resampler_process_native(channel_index, ref tmp_in_len, output, output_ptr, ref out_len); + + this.magic_samples[channel_index] -= tmp_in_len; + + /* If we couldn't process all "magic" input samples, save the rest for next time */ + if (this.magic_samples[channel_index] != 0) + { + int i; + for (i = mem_ptr; i < this.magic_samples[channel_index] + mem_ptr; i++) + this.mem[N - 1 + i] = this.mem[N - 1 + i + tmp_in_len]; + } + output_ptr += out_len * this.out_stride; + return out_len; + } + + #endregion + + #region Public API + + /// + /// Create a new resampler with integer input and output rates (in hertz). + /// + /// The number of channels to be processed + /// Input sampling rate, in hertz + /// Output sampling rate, in hertz + /// Resampling quality, from 0 to 10 + public SpeexResampler(int nb_channels, int in_rate, int out_rate, int quality) : this(nb_channels, in_rate, out_rate, in_rate, out_rate, quality) + { + } + + /// + /// Create a new resampler with fractional input/output rates. The sampling + /// rate ratio is an arbitrary rational number with both the numerator and + /// denominator being 32-bit integers. + /// + /// The number of channels to be processed + /// Numerator of sampling rate ratio + /// Denominator of sampling rate ratio + /// Input sample rate rounded to the nearest integer (in hz) + /// Output sample rate rounded to the nearest integer (in hz) + /// Resampling quality, from 0 to 10 + /// A newly created restampler + public SpeexResampler(int nb_channels, int ratio_num, int ratio_den, int in_rate, int out_rate, int quality) + { + int i; + if (quality > 10 || quality < 0) + { + throw new ArgumentException("Quality must be between 0 and 10"); + } + this.initialised = 0; + this.started = 0; + this.in_rate = 0; + this.out_rate = 0; + this.num_rate = 0; + this.den_rate = 0; + this.quality = -1; + this.sinc_table_length = 0; + this.mem_alloc_size = 0; + this.filt_len = 0; + this.mem = null; + this.resampler_ptr = null; + this.cutoff = 1.0f; + this.nb_channels = nb_channels; + this.in_stride = 1; + this.out_stride = 1; + this.buffer_size = 160; + + /* Per channel data */ + this.last_sample = new int[nb_channels]; + this.magic_samples = new int[nb_channels]; + this.samp_frac_num = new int[nb_channels]; + for (i = 0; i < nb_channels; i++) + { + this.last_sample[i] = 0; + this.magic_samples[i] = 0; + this.samp_frac_num[i] = 0; + } + + this.Quality = quality; + this.SetRateFraction(ratio_num, ratio_den, in_rate, out_rate); + + this.update_filter(); + + this.initialised = 1; + } + + public static SpeexResampler Create(int nb_channels, int in_rate, int out_rate, int quality) + { + return new SpeexResampler(nb_channels, in_rate, out_rate, in_rate, out_rate, quality); + } + + /// + /// DEPRECATED. Use the regular constructor instead. + /// + public static SpeexResampler Create(int nb_channels, int ratio_num, int ratio_den, int in_rate, int out_rate, int quality) + { + return new SpeexResampler(nb_channels, ratio_num, ratio_den, in_rate, out_rate, quality); + } + + /// + /// Resample an int array. The input and output buffers must *not* overlap + /// + /// The index of the channel to process (for multichannel input, 0 otherwise) + /// Input buffer + /// Offset to start from when reading input + /// Number of input samples in the input buffer. After this function returns, this value + /// will be set to the number of input samples actually processed + /// Output buffer + /// Offset to start from when writing output + /// Size of the output buffer. After this function returns, this value will be set to the number + /// of output samples actually produced + public void Process(int channel_index, short[] input, int input_ptr, ref int in_len, short[] output, int output_ptr, ref int out_len) + { + int j; + int ilen = in_len; + int olen = out_len; + int x = channel_index * this.mem_alloc_size; + int filt_offs = this.filt_len - 1; + int xlen = this.mem_alloc_size - filt_offs; + int istride = this.in_stride; + + if (this.magic_samples[channel_index] != 0) + { + + olen -= this.speex_resampler_magic(channel_index, output, ref output_ptr, olen); + + } + if (this.magic_samples[channel_index] == 0) + { + while (ilen != 0 && olen != 0) + { + int ichunk = (ilen > xlen) ? xlen : ilen; + int ochunk = olen; + + if (input != null) + { + for (j = 0; j < ichunk; ++j) + this.mem[x + j + filt_offs] = input[input_ptr + j * istride]; + } + else { + for (j = 0; j < ichunk; ++j) + + this.mem[x + j + filt_offs] = 0; + } + this.speex_resampler_process_native(channel_index, ref ichunk, output, output_ptr, ref ochunk); + ilen -= ichunk; + olen -= ochunk; + output_ptr += ochunk * this.out_stride; + if (input != null) + + input_ptr += ichunk * istride; + } + } + in_len -= ilen; + out_len -= olen; + } + + /// + /// Resample a float array array. The input and output buffers must *not* overlap + /// + /// The index of the channel to process (for multichannel input, 0 otherwise) + /// Input buffer + /// Offset to start from when reading input + /// Number of input samples in the input buffer. After this function returns, this value + /// will be set to the number of input samples actually processed + /// Output buffer + /// Offset to start from when writing output + /// Size of the output buffer. After this function returns, this value will be set to the number + /// of output samples actually produced + public void Process(int channel_index, float[] input, int input_ptr, ref int in_len, float[] output, int output_ptr, ref int out_len) + { + int j; + int istride_save = this.in_stride; + int ostride_save = this.out_stride; + int ilen = in_len; + int olen = out_len; + int x = channel_index * this.mem_alloc_size; + int xlen = this.mem_alloc_size - (this.filt_len - 1); + int ylen = (olen < FIXED_STACK_ALLOC) ? olen : FIXED_STACK_ALLOC; + short[] ystack = new short[ylen]; + + this.out_stride = 1; + + while (ilen != 0 && olen != 0) + { + + int y = 0; + int ichunk = (ilen > xlen) ? xlen : ilen; + int ochunk = (olen > ylen) ? ylen : olen; + int omagic = 0; + + if (this.magic_samples[channel_index] != 0) + { + + omagic = this.speex_resampler_magic(channel_index, ystack, ref y, ochunk); + + ochunk -= omagic; + olen -= omagic; + } + if (this.magic_samples[channel_index] == 0) + { + if (input != null) + { + for (j = 0; j < ichunk; ++j) + this.mem[x + j + this.filt_len - 1] = WORD2INT(input[input_ptr + j * istride_save]); + } + else { + for (j = 0; j < ichunk; ++j) + this.mem[x + j + this.filt_len - 1] = 0; + } + + this.speex_resampler_process_native(channel_index, ref ichunk, ystack, y, ref ochunk); + } + else { + ichunk = 0; + ochunk = 0; + } + + for (j = 0; j < ochunk + omagic; ++j) + output[output_ptr + j * ostride_save] = ystack[j]; + + ilen -= ichunk; + olen -= ochunk; + output_ptr += ((ochunk + omagic) * ostride_save); + if (input != null) + input_ptr += ichunk * istride_save; + } + this.out_stride = ostride_save; + in_len -= ilen; + out_len -= olen; + } + + /// + /// Resamples an interleaved int array. The stride is automatically determined by the number of channels of the resampler. + /// + /// Input buffer + /// Offset to start from when reading input + /// The number of samples *PER-CHANNEL* in the input buffer. After this function returns, this + /// value will be set to the number of input samples actually processed + /// Output buffer + /// Offset to start from when writing output + /// The size of the output buffer in samples-per-channel. After this function returns, this value + /// will be set to the number of samples per channel actually produced + public void ProcessInterleaved(float[] input, int input_ptr, ref int in_len, float[] output, int output_ptr, ref int out_len) + { + int i; + int istride_save, ostride_save; + int bak_out_len = out_len; + int bak_in_len = in_len; + istride_save = this.in_stride; + ostride_save = this.out_stride; + this.in_stride = this.out_stride = this.nb_channels; + for (i = 0; i < this.nb_channels; i++) + { + out_len = bak_out_len; + in_len = bak_in_len; + if (input != null) + this.Process(i, input, input_ptr + i, ref in_len, output, output_ptr + i, ref out_len); + else + this.Process(i, null, 0, ref in_len, output, output_ptr + i, ref out_len); + } + this.in_stride = istride_save; + this.out_stride = ostride_save; + } + + /// + /// Resamples an interleaved float array. The stride is automatically determined by the number of channels of the resampler. + /// + /// Input buffer + /// Offset to start from when reading input + /// The number of samples *PER-CHANNEL* in the input buffer. After this function returns, this + /// value will be set to the number of input samples actually processed + /// Output buffer + /// Offset to start from when writing output + /// The size of the output buffer in samples-per-channel. After this function returns, this value + /// will be set to the number of samples per channel actually produced + public void ProcessInterleaved(short[] input, int input_ptr, ref int in_len, short[] output, int output_ptr, ref int out_len) + { + int i; + int istride_save, ostride_save; + int bak_out_len = out_len; + int bak_in_len = in_len; + istride_save = this.in_stride; + ostride_save = this.out_stride; + this.in_stride = this.out_stride = this.nb_channels; + for (i = 0; i < this.nb_channels; i++) + { + out_len = bak_out_len; + in_len = bak_in_len; + if (input != null) + this.Process(i, input, input_ptr + i, ref in_len, output, output_ptr + i, ref out_len); + else + this.Process(i, null, 0, ref in_len, output, output_ptr + i, ref out_len); + } + this.in_stride = istride_save; + this.out_stride = ostride_save; + } + + /// + /// Make sure that the first samples to go out of the resamplers don't have + /// leading zeros. This is only useful before starting to use a newly created + /// resampler. It is recommended to use that when resampling an audio file, as + /// it will generate a file with the same length.For real-time processing, + /// it is probably easier not to use this call (so that the output duration + /// is the same for the first frame). + /// + public void SkipZeroes() + { + int i; + for (i = 0; i < this.nb_channels; i++) + this.last_sample[i] = this.filt_len / 2; + } + + /// + /// Clears the resampler buffers so a new (unrelated) stream can be processed. + /// + public void ResetMem() + { + int i; + for (i = 0; i < this.nb_channels; i++) + { + this.last_sample[i] = 0; + this.magic_samples[i] = 0; + this.samp_frac_num[i] = 0; + } + for (i = 0; i < this.nb_channels * (this.filt_len - 1); i++) + this.mem[i] = 0; + } + + #endregion + + #region Getters and Setters + + /// + /// Sets the input and output rates + /// + /// Input sampling rate, in hertz + /// Output sampling rate, in hertz + public void SetRates(int in_rate, int out_rate) + { + this.SetRateFraction(in_rate, out_rate, in_rate, out_rate); + } + + /// + /// Get the current input/output sampling rates (integer value). + /// + /// (Output) Sampling rate of input + /// (Output) Sampling rate of output + public void GetRates(out int in_rate, out int out_rate) + { + in_rate = this.in_rate; + out_rate = this.out_rate; + } + + /// + /// Sets the input/output sampling rates and resampling ration (fractional values in Hz supported) + /// + /// Numerator of the sampling rate ratio + /// Denominator of the sampling rate ratio + /// Input sampling rate rounded to the nearest integer (in Hz) + /// Output sampling rate rounded to the nearest integer (in Hz) + public void SetRateFraction(int ratio_num, int ratio_den, int in_rate, int out_rate) + { + int fact; + int old_den; + int i; + if (this.in_rate == in_rate && this.out_rate == out_rate && this.num_rate == ratio_num && this.den_rate == ratio_den) + return; + + old_den = this.den_rate; + this.in_rate = in_rate; + this.out_rate = out_rate; + this.num_rate = ratio_num; + this.den_rate = ratio_den; + /* FIXME: This is terribly inefficient, but who cares (at least for now)? */ + for (fact = 2; fact <= Inlines.IMIN(this.num_rate, this.den_rate); fact++) + { + while ((this.num_rate % fact == 0) && (this.den_rate % fact == 0)) + { + this.num_rate /= fact; + this.den_rate /= fact; + } + } + + if (old_den > 0) + { + for (i = 0; i < this.nb_channels; i++) + { + this.samp_frac_num[i] = this.samp_frac_num[i] * this.den_rate / old_den; + /* Safety net */ + if (this.samp_frac_num[i] >= this.den_rate) + this.samp_frac_num[i] = this.den_rate - 1; + } + } + + if (this.initialised != 0) + this.update_filter(); + } + + /// + /// Gets the current resampling ratio. This will be reduced to the least common denominator + /// + /// (Output) numerator of the sampling rate ratio + /// (Output) denominator of the sampling rate ratio + public void GetRateFraction(out int ratio_num, out int ratio_den) + { + ratio_num = this.num_rate; + ratio_den = this.den_rate; + } + + /// + /// Gets or sets the resampling quality between 0 and 10, where 0 has poor + /// quality and 10 has very high quality. + /// + public int Quality + { + get + { + return this.quality; + } + set + { + if (value > 10 || value < 0) + throw new ArgumentException("Quality must be between 0 and 10"); + if (this.quality == value) + return; + this.quality = value; + if (this.initialised != 0) + this.update_filter(); + } + } + + /// + /// Gets or sets the input stride + /// + public int InputStride + { + get + { + return this.in_stride; + } + set + { + this.in_stride = value; + } + } + + /// + /// Gets or sets the output stride + /// + public int OutputStride + { + get + { + return this.out_stride; + } + set + { + this.out_stride = value; + } + } + + /// + /// Get the latency introduced by the resampler measured in input samples. + /// + public int InputLatency + { + get + { + return this.filt_len / 2; + } + } + + /// + /// Gets the latency introduced by the resampler measured in output samples. + /// + public int OutputLatency + { + get + { + return ((this.filt_len / 2) * this.den_rate + (this.num_rate >> 1)) / this.num_rate; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/Libraries/Concentus/CSharp/Concentus/Concentus.csproj b/Libraries/Concentus/CSharp/Concentus/Concentus.csproj new file mode 100644 index 000000000..1cf63c7b8 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Concentus.csproj @@ -0,0 +1,182 @@ + + + + + Debug + AnyCPU + {0E7FEE6A-15E5-4A4E-943C-80276003478C} + Library + Concentus + Concentus + v4.5 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/Analysis.cs b/Libraries/Concentus/CSharp/Concentus/Opus/Analysis.cs new file mode 100644 index 000000000..66e100067 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/Analysis.cs @@ -0,0 +1,590 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using Concentus.Celt; +using Concentus.Celt.Structs; +using Concentus.Common; +using Concentus.Common.CPlusPlus; +using Concentus; +using Concentus.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Concentus +{ + internal static class Analysis + { + private const double M_PI = 3.141592653; + private const float cA = 0.43157974f; + private const float cB = 0.67848403f; + private const float cC = 0.08595542f; + private const float cE = ((float)M_PI / 2); + + private const int NB_TONAL_SKIP_BANDS = 9; + + internal static float fast_atan2f(float y, float x) + { + float x2, y2; + /* Should avoid underflow on the values we'll get */ + if (Inlines.ABS16(x) + Inlines.ABS16(y) < 1e-9f) + { + x *= 1e12f; + y *= 1e12f; + } + x2 = x * x; + y2 = y * y; + if (x2 < y2) + { + float den = (y2 + cB * x2) * (y2 + cC * x2); + if (den != 0) + return -x * y * (y2 + cA * x2) / den + (y < 0 ? -cE : cE); + else + return (y < 0 ? -cE : cE); + } + else { + float den = (x2 + cB * y2) * (x2 + cC * y2); + if (den != 0) + return x * y * (x2 + cA * y2) / den + (y < 0 ? -cE : cE) - (x * y < 0 ? -cE : cE); + else + return (y < 0 ? -cE : cE) - (x * y < 0 ? -cE : cE); + } + } + + internal static void tonality_analysis_init(TonalityAnalysisState tonal) + { + tonal.Reset(); + } + + internal static void tonality_get_info(TonalityAnalysisState tonal, AnalysisInfo info_out, int len) + { + int pos; + int curr_lookahead; + float psum; + int i; + + pos = tonal.read_pos; + curr_lookahead = tonal.write_pos - tonal.read_pos; + if (curr_lookahead < 0) + curr_lookahead += OpusConstants.DETECT_SIZE; + + if (len > 480 && pos != tonal.write_pos) + { + pos++; + if (pos == OpusConstants.DETECT_SIZE) + pos = 0; + } + if (pos == tonal.write_pos) + pos--; + if (pos < 0) + pos = OpusConstants.DETECT_SIZE - 1; + + info_out.Assign(tonal.info[pos]); + tonal.read_subframe += len / 120; + while (tonal.read_subframe >= 4) + { + tonal.read_subframe -= 4; + tonal.read_pos++; + } + if (tonal.read_pos >= OpusConstants.DETECT_SIZE) + tonal.read_pos -= OpusConstants.DETECT_SIZE; + + /* Compensate for the delay in the features themselves. + FIXME: Need a better estimate the 10 I just made up */ + curr_lookahead = Inlines.IMAX(curr_lookahead - 10, 0); + + psum = 0; + /* Summing the probability of transition patterns that involve music at + time (DETECT_SIZE-curr_lookahead-1) */ + for (i = 0; i < OpusConstants.DETECT_SIZE - curr_lookahead; i++) + psum += tonal.pmusic[i]; + for (; i < OpusConstants.DETECT_SIZE; i++) + psum += tonal.pspeech[i]; + psum = psum * tonal.music_confidence + (1 - psum) * tonal.speech_confidence; + /*printf("%f %f %f\n", psum, info_out.music_prob, info_out.tonality);*/ + + info_out.music_prob = psum; + } + + /// + /// + /// + /// The type of signal being handled (either short or float) - changes based on which API is used + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal static void tonality_analysis(TonalityAnalysisState tonal, CeltMode celt_mode, T[] x, int x_ptr, int len, int offset, int c1, int c2, int C, int lsb_depth, Downmix.downmix_func downmix) + { + int i, b; + FFTState kfft; + int[] input; + int[] output; + int N = 480, N2 = 240; + float[] A = tonal.angle; + float[] dA = tonal.d_angle; + float[] d2A = tonal.d2_angle; + float[] tonality; + float[] noisiness; + float[] band_tonality = new float[OpusConstants.NB_TBANDS]; + float[] logE = new float[OpusConstants.NB_TBANDS]; + float[] BFCC = new float[8]; + float[] features = new float[25]; + float frame_tonality; + float max_frame_tonality; + /*float tw_sum=0;*/ + float frame_noisiness; + float pi4 = (float)(M_PI * M_PI * M_PI * M_PI); + float slope = 0; + float frame_stationarity; + float relativeE; + float[] frame_probs = new float[2]; + float alpha, alphaE, alphaE2; + float frame_loudness; + float bandwidth_mask; + int bandwidth = 0; + float maxE = 0; + float noise_floor; + int remaining; + AnalysisInfo info; //[porting note] pointer + + tonal.last_transition++; + alpha = 1.0f / Inlines.IMIN(20, 1 + tonal.count); + alphaE = 1.0f / Inlines.IMIN(50, 1 + tonal.count); + alphaE2 = 1.0f / Inlines.IMIN(1000, 1 + tonal.count); + + if (tonal.count < 4) + tonal.music_prob = 0.5f; + kfft = celt_mode.mdct.kfft[0]; + if (tonal.count == 0) + tonal.mem_fill = 240; + + downmix(x, x_ptr, tonal.inmem, tonal.mem_fill, Inlines.IMIN(len, OpusConstants.ANALYSIS_BUF_SIZE - tonal.mem_fill), offset, c1, c2, C); + + if (tonal.mem_fill + len < OpusConstants.ANALYSIS_BUF_SIZE) + { + tonal.mem_fill += len; + /* Don't have enough to update the analysis */ + return; + } + + info = tonal.info[tonal.write_pos++]; + if (tonal.write_pos >= OpusConstants.DETECT_SIZE) + tonal.write_pos -= OpusConstants.DETECT_SIZE; + + input = new int[960]; + output = new int[960]; + tonality = new float[240]; + noisiness = new float[240]; + for (i = 0; i < N2; i++) + { + float w = Tables.analysis_window[i]; + input[2 * i] = (int)(w * tonal.inmem[i]); + input[2 * i + 1] = (int)(w * tonal.inmem[N2 + i]); + input[(2 * (N - i - 1))] = (int)(w * tonal.inmem[N - i - 1]); + input[(2 * (N - i - 1)) + 1] = (int)(w * tonal.inmem[N + N2 - i - 1]); + } + Arrays.MemMoveInt(tonal.inmem, OpusConstants.ANALYSIS_BUF_SIZE - 240, 0, 240); + + remaining = len - (OpusConstants.ANALYSIS_BUF_SIZE - tonal.mem_fill); + downmix(x, x_ptr, tonal.inmem, 240, remaining, offset + OpusConstants.ANALYSIS_BUF_SIZE - tonal.mem_fill, c1, c2, C); + tonal.mem_fill = 240 + remaining; + + KissFFT.opus_fft(kfft, input, output); + + for (i = 1; i < N2; i++) + { + float X1r, X2r, X1i, X2i; + float angle, d_angle, d2_angle; + float angle2, d_angle2, d2_angle2; + float mod1, mod2, avg_mod; + X1r = (float)output[2 * i] + output[2 * (N - i)]; + X1i = (float)output[(2 * i) + 1] - output[2 * (N - i) + 1]; + X2r = (float)output[(2 * i) + 1] + output[2 * (N - i) + 1]; + X2i = (float)output[2 * (N - i)] - output[2 * i]; + + angle = (float)(.5f / M_PI) * fast_atan2f(X1i, X1r); + d_angle = angle - A[i]; + d2_angle = d_angle - dA[i]; + + angle2 = (float)(.5f / M_PI) * fast_atan2f(X2i, X2r); + d_angle2 = angle2 - angle; + d2_angle2 = d_angle2 - d_angle; + + mod1 = d2_angle - (float)Math.Floor(0.5f + d2_angle); + noisiness[i] = Inlines.ABS16(mod1); + mod1 *= mod1; + mod1 *= mod1; + + mod2 = d2_angle2 - (float)Math.Floor(0.5f + d2_angle2); + noisiness[i] += Inlines.ABS16(mod2); + mod2 *= mod2; + mod2 *= mod2; + + avg_mod = .25f * (d2A[i] + 2.0f * mod1 + mod2); + tonality[i] = 1.0f / (1.0f + 40.0f * 16.0f * pi4 * avg_mod) - .015f; + + A[i] = angle2; + dA[i] = d_angle2; + d2A[i] = mod2; + } + + frame_tonality = 0; + max_frame_tonality = 0; + /*tw_sum = 0;*/ + info.activity = 0; + frame_noisiness = 0; + frame_stationarity = 0; + if (tonal.count == 0) + { + for (b = 0; b < OpusConstants.NB_TBANDS; b++) + { + tonal.lowE[b] = 1e10f; + tonal.highE[b] = -1e10f; + } + } + relativeE = 0; + frame_loudness = 0; + for (b = 0; b < OpusConstants.NB_TBANDS; b++) + { + float E = 0, tE = 0, nE = 0; + float L1, L2; + float stationarity; + for (i = Tables.tbands[b]; i < Tables.tbands[b + 1]; i++) + { + float binE = output[2 * i] * (float)output[2 * i] + output[2 * (N - i)] * (float)output[2 * (N - i)] + + output[2 * i + 1] * (float)output[2 * i + 1] + output[2 * (N - i) + 1] * (float)output[2 * (N - i) + 1]; + /* FIXME: It's probably best to change the BFCC filter initial state instead */ + binE *= 5.55e-17f; + E += binE; + tE += binE * tonality[i]; + nE += binE * 2.0f * (.5f - noisiness[i]); + } + + tonal.E[tonal.E_count][b] = E; + frame_noisiness += nE / (1e-15f + E); + + frame_loudness += (float)Math.Sqrt(E + 1e-10f); + logE[b] = (float)Math.Log(E + 1e-10f); + tonal.lowE[b] = Inlines.MIN32(logE[b], tonal.lowE[b] + 0.01f); + tonal.highE[b] = Inlines.MAX32(logE[b], tonal.highE[b] - 0.1f); + if (tonal.highE[b] < tonal.lowE[b] + 1.0f) + { + tonal.highE[b] += 0.5f; + tonal.lowE[b] -= 0.5f; + } + relativeE += (logE[b] - tonal.lowE[b]) / (1e-15f + tonal.highE[b] - tonal.lowE[b]); + + L1 = L2 = 0; + for (i = 0; i < OpusConstants.NB_FRAMES; i++) + { + L1 += (float)Math.Sqrt(tonal.E[i][b]); + L2 += tonal.E[i][b]; + } + + stationarity = Inlines.MIN16(0.99f, L1 / (float)Math.Sqrt(1e-15 + OpusConstants.NB_FRAMES * L2)); + stationarity *= stationarity; + stationarity *= stationarity; + frame_stationarity += stationarity; + /*band_tonality[b] = tE/(1e-15+E)*/ + band_tonality[b] = Inlines.MAX16(tE / (1e-15f + E), stationarity * tonal.prev_band_tonality[b]); + frame_tonality += band_tonality[b]; + if (b >= OpusConstants.NB_TBANDS - OpusConstants.NB_TONAL_SKIP_BANDS) + frame_tonality -= band_tonality[b - OpusConstants.NB_TBANDS + OpusConstants.NB_TONAL_SKIP_BANDS]; + max_frame_tonality = Inlines.MAX16(max_frame_tonality, (1.0f + .03f * (b - OpusConstants.NB_TBANDS)) * frame_tonality); + slope += band_tonality[b] * (b - 8); + tonal.prev_band_tonality[b] = band_tonality[b]; + } + + bandwidth_mask = 0; + bandwidth = 0; + maxE = 0; + noise_floor = 5.7e-4f / (1 << (Inlines.IMAX(0, lsb_depth - 8))); + noise_floor *= 1 << (15 + CeltConstants.SIG_SHIFT); + noise_floor *= noise_floor; + for (b = 0; b < OpusConstants.NB_TOT_BANDS; b++) + { + float E = 0; + int band_start, band_end; + /* Keep a margin of 300 Hz for aliasing */ + band_start = Tables.extra_bands[b]; + band_end = Tables.extra_bands[b + 1]; + for (i = band_start; i < band_end; i++) + { + float binE = output[2 * i] * (float)output[2 * i] + output[2 * (N - i)] * (float)output[2 * (N - i)] + + output[2 * i + 1] * (float)output[2 * i + 1] + output[2 * (N - i) + 1] * (float)output[2 * (N - i) + 1]; + E += binE; + } + maxE = Inlines.MAX32(maxE, E); + tonal.meanE[b] = Inlines.MAX32((1 - alphaE2) * tonal.meanE[b], E); + E = Inlines.MAX32(E, tonal.meanE[b]); + /* Use a simple follower with 13 dB/Bark slope for spreading function */ + bandwidth_mask = Inlines.MAX32(.05f * bandwidth_mask, E); + /* Consider the band "active" only if all these conditions are met: + 1) less than 10 dB below the simple follower + 2) less than 90 dB below the peak band (maximal masking possible considering + both the ATH and the loudness-dependent slope of the spreading function) + 3) above the PCM quantization noise floor + */ + if (E > .1 * bandwidth_mask && E * 1e9f > maxE && E > noise_floor * (band_end - band_start)) + bandwidth = b; + } + if (tonal.count <= 2) + bandwidth = 20; + frame_loudness = 20 * (float)Math.Log10(frame_loudness); + tonal.Etracker = Inlines.MAX32(tonal.Etracker - .03f, frame_loudness); + tonal.lowECount *= (1 - alphaE); + if (frame_loudness < tonal.Etracker - 30) + tonal.lowECount += alphaE; + + for (i = 0; i < 8; i++) + { + float sum = 0; + for (b = 0; b < 16; b++) + sum += Tables.dct_table[i * 16 + b] * logE[b]; + BFCC[i] = sum; + } + + frame_stationarity /= OpusConstants.NB_TBANDS; + relativeE /= OpusConstants.NB_TBANDS; + if (tonal.count < 10) + relativeE = 0.5f; + frame_noisiness /= OpusConstants.NB_TBANDS; + info.activity = frame_noisiness + (1 - frame_noisiness) * relativeE; + frame_tonality = (max_frame_tonality / (OpusConstants.NB_TBANDS - OpusConstants.NB_TONAL_SKIP_BANDS)); + frame_tonality = Inlines.MAX16(frame_tonality, tonal.prev_tonality * .8f); + tonal.prev_tonality = frame_tonality; + + slope /= 8 * 8; + info.tonality_slope = slope; + + tonal.E_count = (tonal.E_count + 1) % OpusConstants.NB_FRAMES; + tonal.count++; + info.tonality = frame_tonality; + + for (i = 0; i < 4; i++) + features[i] = -0.12299f * (BFCC[i] + tonal.mem[i + 24]) + 0.49195f * (tonal.mem[i] + tonal.mem[i + 16]) + 0.69693f * tonal.mem[i + 8] - 1.4349f * tonal.cmean[i]; + + for (i = 0; i < 4; i++) + tonal.cmean[i] = (1 - alpha) * tonal.cmean[i] + alpha * BFCC[i]; + + for (i = 0; i < 4; i++) + features[4 + i] = 0.63246f * (BFCC[i] - tonal.mem[i + 24]) + 0.31623f * (tonal.mem[i] - tonal.mem[i + 16]); + for (i = 0; i < 3; i++) + features[8 + i] = 0.53452f * (BFCC[i] + tonal.mem[i + 24]) - 0.26726f * (tonal.mem[i] + tonal.mem[i + 16]) - 0.53452f * tonal.mem[i + 8]; + + if (tonal.count > 5) + { + for (i = 0; i < 9; i++) + tonal.std[i] = (1 - alpha) * tonal.std[i] + alpha * features[i] * features[i]; + } + + for (i = 0; i < 8; i++) + { + tonal.mem[i + 24] = tonal.mem[i + 16]; + tonal.mem[i + 16] = tonal.mem[i + 8]; + tonal.mem[i + 8] = tonal.mem[i]; + tonal.mem[i] = BFCC[i]; + } + for (i = 0; i < 9; i++) + features[11 + i] = (float)Math.Sqrt(tonal.std[i]); + features[20] = info.tonality; + features[21] = info.activity; + features[22] = frame_stationarity; + features[23] = info.tonality_slope; + features[24] = tonal.lowECount; + + mlp.mlp_process(Tables.net, features, frame_probs); + frame_probs[0] = .5f * (frame_probs[0] + 1); + /* Curve fitting between the MLP probability and the actual probability */ + frame_probs[0] = .01f + 1.21f * frame_probs[0] * frame_probs[0] - .23f * (float)Math.Pow(frame_probs[0], 10); + /* Probability of active audio (as opposed to silence) */ + frame_probs[1] = .5f * frame_probs[1] + .5f; + /* Consider that silence has a 50-50 probability. */ + frame_probs[0] = frame_probs[1] * frame_probs[0] + (1 - frame_probs[1]) * .5f; + + /*printf("%f %f ", frame_probs[0], frame_probs[1]);*/ + { + /* Probability of state transition */ + float tau; + /* Represents independence of the MLP probabilities, where + beta=1 means fully independent. */ + float beta; + /* Denormalized probability of speech (p0) and music (p1) after update */ + float p0, p1; + /* Probabilities for "all speech" and "all music" */ + float s0, m0; + /* Probability sum for renormalisation */ + float psum; + /* Instantaneous probability of speech and music, with beta pre-applied. */ + float speech0; + float music0; + + /* One transition every 3 minutes of active audio */ + tau = .00005f * frame_probs[1]; + beta = .05f; + //if (1) + { + /* Adapt beta based on how "unexpected" the new prob is */ + float p, q; + p = Inlines.MAX16(.05f, Inlines.MIN16(.95f, frame_probs[0])); + q = Inlines.MAX16(.05f, Inlines.MIN16(.95f, tonal.music_prob)); + beta = .01f + .05f * Inlines.ABS16(p - q) / (p * (1 - q) + q * (1 - p)); + } + /* p0 and p1 are the probabilities of speech and music at this frame + using only information from previous frame and applying the + state transition model */ + p0 = (1 - tonal.music_prob) * (1 - tau) + tonal.music_prob * tau; + p1 = tonal.music_prob * (1 - tau) + (1 - tonal.music_prob) * tau; + /* We apply the current probability with exponent beta to work around + the fact that the probability estimates aren't independent. */ + p0 *= (float)Math.Pow(1 - frame_probs[0], beta); + p1 *= (float)Math.Pow(frame_probs[0], beta); + /* Normalise the probabilities to get the Marokv probability of music. */ + tonal.music_prob = p1 / (p0 + p1); + info.music_prob = tonal.music_prob; + + /* This chunk of code deals with delayed decision. */ + psum = 1e-20f; + /* Instantaneous probability of speech and music, with beta pre-applied. */ + speech0 = (float)Math.Pow(1 - frame_probs[0], beta); + music0 = (float)Math.Pow(frame_probs[0], beta); + if (tonal.count == 1) + { + tonal.pspeech[0] = 0.5f; + tonal.pmusic[0] = 0.5f; + } + /* Updated probability of having only speech (s0) or only music (m0), + before considering the new observation. */ + s0 = tonal.pspeech[0] + tonal.pspeech[1]; + m0 = tonal.pmusic[0] + tonal.pmusic[1]; + /* Updates s0 and m0 with instantaneous probability. */ + tonal.pspeech[0] = s0 * (1 - tau) * speech0; + tonal.pmusic[0] = m0 * (1 - tau) * music0; + /* Propagate the transition probabilities */ + for (i = 1; i < OpusConstants.DETECT_SIZE - 1; i++) + { + tonal.pspeech[i] = tonal.pspeech[i + 1] * speech0; + tonal.pmusic[i] = tonal.pmusic[i + 1] * music0; + } + /* Probability that the latest frame is speech, when all the previous ones were music. */ + tonal.pspeech[OpusConstants.DETECT_SIZE - 1] = m0 * tau * speech0; + /* Probability that the latest frame is music, when all the previous ones were speech. */ + tonal.pmusic[OpusConstants.DETECT_SIZE - 1] = s0 * tau * music0; + + /* Renormalise probabilities to 1 */ + for (i = 0; i < OpusConstants.DETECT_SIZE; i++) + psum += tonal.pspeech[i] + tonal.pmusic[i]; + psum = 1.0f / psum; + for (i = 0; i < OpusConstants.DETECT_SIZE; i++) + { + tonal.pspeech[i] *= psum; + tonal.pmusic[i] *= psum; + } + psum = tonal.pmusic[0]; + for (i = 1; i < OpusConstants.DETECT_SIZE; i++) + psum += tonal.pspeech[i]; + + /* Estimate our confidence in the speech/music decisions */ + if (frame_probs[1] > .75) + { + if (tonal.music_prob > .9) + { + float adapt; + adapt = 1.0f / (++tonal.music_confidence_count); + tonal.music_confidence_count = Inlines.IMIN(tonal.music_confidence_count, 500); + tonal.music_confidence += adapt * Inlines.MAX16(-.2f, frame_probs[0] - tonal.music_confidence); + } + if (tonal.music_prob < .1) + { + float adapt; + adapt = 1.0f / (++tonal.speech_confidence_count); + tonal.speech_confidence_count = Inlines.IMIN(tonal.speech_confidence_count, 500); + tonal.speech_confidence += adapt * Inlines.MIN16(.2f, frame_probs[0] - tonal.speech_confidence); + } + } + else { + if (tonal.music_confidence_count == 0) + tonal.music_confidence = .9f; + if (tonal.speech_confidence_count == 0) + tonal.speech_confidence = .1f; + } + } + if (tonal.last_music != ((tonal.music_prob > .5f) ? 1 : 0)) + tonal.last_transition = 0; + tonal.last_music = (tonal.music_prob > .5f) ? 1 : 0; + + info.bandwidth = bandwidth; + info.noisiness = frame_noisiness; + info.valid = 1; + } + + internal static void run_analysis(TonalityAnalysisState analysis, CeltMode celt_mode, T[] analysis_pcm, int analysis_pcm_ptr, + int analysis_frame_size, int frame_size, int c1, int c2, int C, int Fs, + int lsb_depth, Downmix.downmix_func downmix, AnalysisInfo analysis_info) + { + int offset; + int pcm_len; + + if (analysis_pcm != null) + { + /* Avoid overflow/wrap-around of the analysis buffer */ + analysis_frame_size = Inlines.IMIN((OpusConstants.DETECT_SIZE - 5) * Fs / 100, analysis_frame_size); + + pcm_len = analysis_frame_size - analysis.analysis_offset; + offset = analysis.analysis_offset; + do + { + tonality_analysis(analysis, celt_mode, analysis_pcm, analysis_pcm_ptr, Inlines.IMIN(480, pcm_len), offset, c1, c2, C, lsb_depth, downmix); + offset += 480; + pcm_len -= 480; + } while (pcm_len > 0); + analysis.analysis_offset = analysis_frame_size; + + analysis.analysis_offset -= frame_size; + } + + analysis_info.valid = 0; + tonality_get_info(analysis, analysis_info, frame_size); + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/CodecHelpers.cs b/Libraries/Concentus/CSharp/Concentus/Opus/CodecHelpers.cs new file mode 100644 index 000000000..fcb0b988c --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/CodecHelpers.cs @@ -0,0 +1,712 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using Concentus.Celt; +using Concentus.Celt.Structs; +using Concentus.Common; +using Concentus.Common.CPlusPlus; +using Concentus; +using Concentus.Enums; +using Concentus.Silk; +using Concentus.Silk.Structs; +using Concentus.Structs; +using System; + +namespace Concentus +{ + public static class CodecHelpers + { + internal static byte gen_toc(OpusMode mode, int framerate, OpusBandwidth bandwidth, int channels) + { + int period; + byte toc; + period = 0; + while (framerate < 400) + { + framerate <<= 1; + period++; + } + if (mode == OpusMode.MODE_SILK_ONLY) + { + toc = (byte)((bandwidth - OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND) << 5); + toc |= (byte)((period - 2) << 3); + } + else if (mode == OpusMode.MODE_CELT_ONLY) + { + int tmp = bandwidth - OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND; + if (tmp < 0) + tmp = 0; + toc = 0x80; + toc |= (byte)(tmp << 5); + toc |= (byte)(period << 3); + } + else /* Hybrid */ + { + toc = 0x60; + toc |= (byte)((bandwidth - OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND) << 4); + toc |= (byte)((period - 2) << 3); + } + toc |= (byte)((channels == 2 ? 1 : 0) << 2); + return toc; + } + + internal static void hp_cutoff(short[] input, int input_ptr, int cutoff_Hz, short[] output, int output_ptr, int[] hp_mem, int len, int channels, int Fs) + { + int[] B_Q28 = new int[3]; + int[] A_Q28 = new int[2]; + int Fc_Q19, r_Q28, r_Q22; + + Inlines.OpusAssert(cutoff_Hz <= int.MaxValue / ((int)((1.5f * 3.14159f / 1000) * ((long)1 << (19)) + 0.5))/*Inlines.SILK_CONST(1.5f * 3.14159f / 1000, 19)*/); + Fc_Q19 = Inlines.silk_DIV32_16(Inlines.silk_SMULBB(((int)((1.5f * 3.14159f / 1000) * ((long)1 << (19)) + 0.5))/*Inlines.SILK_CONST(1.5f * 3.14159f / 1000, 19)*/, cutoff_Hz), Fs / 1000); + Inlines.OpusAssert(Fc_Q19 > 0 && Fc_Q19 < 32768); + + r_Q28 = ((int)((1.0f) * ((long)1 << (28)) + 0.5))/*Inlines.SILK_CONST(1.0f, 28)*/ - Inlines.silk_MUL(((int)((0.92f) * ((long)1 << (9)) + 0.5))/*Inlines.SILK_CONST(0.92f, 9)*/, Fc_Q19); + + /* b = r * [ 1; -2; 1 ]; */ + /* a = [ 1; -2 * r * ( 1 - 0.5 * Fc^2 ); r^2 ]; */ + B_Q28[0] = r_Q28; + B_Q28[1] = Inlines.silk_LSHIFT(-r_Q28, 1); + B_Q28[2] = r_Q28; + + /* -r * ( 2 - Fc * Fc ); */ + r_Q22 = Inlines.silk_RSHIFT(r_Q28, 6); + A_Q28[0] = Inlines.silk_SMULWW(r_Q22, Inlines.silk_SMULWW(Fc_Q19, Fc_Q19) - ((int)((2.0f) * ((long)1 << (22)) + 0.5))/*Inlines.SILK_CONST(2.0f, 22)*/); + A_Q28[1] = Inlines.silk_SMULWW(r_Q22, r_Q22); + + Filters.silk_biquad_alt(input, input_ptr, B_Q28, A_Q28, hp_mem, 0, output, output_ptr, len, channels); + if (channels == 2) + { + Filters.silk_biquad_alt(input, input_ptr + 1, B_Q28, A_Q28, hp_mem, 2, output, output_ptr + 1, len, channels); + } + } + + internal static void dc_reject(short[] input, int input_ptr, int cutoff_Hz, short[] output, int output_ptr, int[] hp_mem, int len, int channels, int Fs) + { + int c, i; + int shift; + + /* Approximates -round(log2(4.*cutoff_Hz/Fs)) */ + shift = Inlines.celt_ilog2(Fs / (cutoff_Hz * 3)); + for (c = 0; c < channels; c++) + { + for (i = 0; i < len; i++) + { + int x, tmp, y; + x = Inlines.SHL32(Inlines.EXTEND32(input[channels * i + c + input_ptr]), 15); + /* First stage */ + tmp = x - hp_mem[2 * c]; + hp_mem[2 * c] = hp_mem[2 * c] + Inlines.PSHR32(x - hp_mem[2 * c], shift); + /* Second stage */ + y = tmp - hp_mem[2 * c + 1]; + hp_mem[2 * c + 1] = hp_mem[2 * c + 1] + Inlines.PSHR32(tmp - hp_mem[2 * c + 1], shift); + output[channels * i + c + output_ptr] = Inlines.EXTRACT16(Inlines.SATURATE(Inlines.PSHR32(y, 15), 32767)); + } + } + } + + internal static void stereo_fade( + short[] pcm_buf, + int g1, + int g2, + int overlap48, + int frame_size, + int channels, + int[] window, + int Fs) + { + int i; + int overlap; + int inc; + inc = 48000 / Fs; + overlap = overlap48 / inc; + g1 = CeltConstants.Q15ONE - g1; + g2 = CeltConstants.Q15ONE - g2; + for (i = 0; i < overlap; i++) + { + int diff; + int g, w; + w = Inlines.MULT16_16_Q15(window[i * inc], window[i * inc]); + g = Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(w, g2), + CeltConstants.Q15ONE - w, g1), 15); + diff = Inlines.EXTRACT16(Inlines.HALF32((int)pcm_buf[i * channels] - (int)pcm_buf[i * channels + 1])); + diff = Inlines.MULT16_16_Q15(g, diff); + pcm_buf[i * channels] = (short)(pcm_buf[i * channels] - diff); + pcm_buf[i * channels + 1] = (short)(pcm_buf[i * channels + 1] + diff); + } + for (; i < frame_size; i++) + { + int diff; + diff = Inlines.EXTRACT16(Inlines.HALF32((int)pcm_buf[i * channels] - (int)pcm_buf[i * channels + 1])); + diff = Inlines.MULT16_16_Q15(g2, diff); + pcm_buf[i * channels] = (short)(pcm_buf[i * channels] - diff); + pcm_buf[i * channels + 1] = (short)(pcm_buf[i * channels + 1] + diff); + } + } + + internal static void gain_fade(short[] buffer, int buf_ptr, int g1, int g2, + int overlap48, int frame_size, int channels, int[] window, int Fs) + { + int i; + int inc; + int overlap; + int c; + inc = 48000 / Fs; + overlap = overlap48 / inc; + if (channels == 1) + { + for (i = 0; i < overlap; i++) + { + int g, w; + w = Inlines.MULT16_16_Q15(window[i * inc], window[i * inc]); + g = Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(w, g2), + CeltConstants.Q15ONE - w, g1), 15); + buffer[buf_ptr + i] = (short)Inlines.MULT16_16_Q15(g, buffer[buf_ptr + i]); + } + } + else { + for (i = 0; i < overlap; i++) + { + int g, w; + w = Inlines.MULT16_16_Q15(window[i * inc], window[i * inc]); + g = Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(w, g2), + CeltConstants.Q15ONE - w, g1), 15); + buffer[buf_ptr + i * 2] = (short)Inlines.MULT16_16_Q15(g, buffer[buf_ptr + i * 2]); + buffer[buf_ptr + i * 2 + 1] = (short)Inlines.MULT16_16_Q15(g, buffer[buf_ptr + i * 2 + 1]); + } + } + c = 0; do + { + for (i = overlap; i < frame_size; i++) + { + buffer[buf_ptr + i * channels + c] = (short)Inlines.MULT16_16_Q15(g2, buffer[buf_ptr + i * channels + c]); + } + } + while (++c < channels); + } + + /* Don't use more than 60 ms for the frame size analysis */ + private const int MAX_DYNAMIC_FRAMESIZE = 24; + + /* Estimates how much the bitrate will be boosted based on the sub-frame energy */ + internal static float transient_boost(float[] E, int E_ptr, float[] E_1, int LM, int maxM) + { + int i; + int M; + float sumE = 0, sumE_1 = 0; + float metric; + + M = Inlines.IMIN(maxM, (1 << LM) + 1); + for (i = E_ptr; i < M + E_ptr; i++) + { + sumE += E[i]; + sumE_1 += E_1[i]; + } + metric = sumE * sumE_1 / (M * M); + /*if (LM==3) + printf("%f\n", metric);*/ + /*return metric>10 ? 1 : 0;*/ + /*return Inlines.MAX16(0,1-exp(-.25*(metric-2.)));*/ + return Inlines.MIN16(1, (float)Math.Sqrt(Inlines.MAX16(0, .05f * (metric - 2)))); + } + + /* Viterbi decoding trying to find the best frame size combination using look-ahead + + State numbering: + 0: unused + 1: 2.5 ms + 2: 5 ms (#1) + 3: 5 ms (#2) + 4: 10 ms (#1) + 5: 10 ms (#2) + 6: 10 ms (#3) + 7: 10 ms (#4) + 8: 20 ms (#1) + 9: 20 ms (#2) + 10: 20 ms (#3) + 11: 20 ms (#4) + 12: 20 ms (#5) + 13: 20 ms (#6) + 14: 20 ms (#7) + 15: 20 ms (#8) + */ + internal static int transient_viterbi(float[] E, float[] E_1, int N, int frame_cost, int rate) + { + int i; + float[][] cost = Arrays.InitTwoDimensionalArray(MAX_DYNAMIC_FRAMESIZE, 16); + int[][] states = Arrays.InitTwoDimensionalArray(MAX_DYNAMIC_FRAMESIZE, 16); + float best_cost; + int best_state; + float factor; + /* Take into account that we damp VBR in the 32 kb/s to 64 kb/s range. */ + if (rate < 80) + factor = 0; + else if (rate > 160) + factor = 1; + else + factor = (rate - 80.0f) / 80.0f; + /* Makes variable framesize less aggressive at lower bitrates, but I can't + find any valid theoretical justification for this (other than it seems + to help) */ + for (i = 0; i < 16; i++) + { + /* Impossible state */ + states[0][i] = -1; + cost[0][i] = 1e10f; + } + for (i = 0; i < 4; i++) + { + cost[0][1 << i] = (frame_cost + rate * (1 << i)) * (1 + factor * transient_boost(E, 0, E_1, i, N + 1)); + states[0][1 << i] = i; + } + for (i = 1; i < N; i++) + { + int j; + + /* Follow continuations */ + for (j = 2; j < 16; j++) + { + cost[i][j] = cost[i - 1][j - 1]; + states[i][j] = j - 1; + } + + /* New frames */ + for (j = 0; j < 4; j++) + { + int k; + float min_cost; + float curr_cost; + states[i][1 << j] = 1; + min_cost = cost[i - 1][1]; + for (k = 1; k < 4; k++) + { + float tmp = cost[i - 1][(1 << (k + 1)) - 1]; + if (tmp < min_cost) + { + states[i][1 << j] = (1 << (k + 1)) - 1; + min_cost = tmp; + } + } + curr_cost = (frame_cost + rate * (1 << j)) * (1 + factor * transient_boost(E, i, E_1, j, N - i + 1)); + cost[i][1 << j] = min_cost; + /* If part of the frame is outside the analysis window, only count part of the cost */ + if (N - i < (1 << j)) + cost[i][1 << j] += curr_cost * (float)(N - i) / (1 << j); + else + cost[i][1 << j] += curr_cost; + } + } + + best_state = 1; + best_cost = cost[N - 1][1]; + /* Find best end state (doesn't force a frame to end at N-1) */ + for (i = 2; i < 16; i++) + { + if (cost[N - 1][i] < best_cost) + { + best_cost = cost[N - 1][i]; + best_state = i; + } + } + + /* Follow transitions back */ + for (i = N - 1; i >= 0; i--) + { + /*printf("%d ", best_state);*/ + best_state = states[i][best_state]; + } + /*printf("%d\n", best_state);*/ + return best_state; + } + + internal static int optimize_framesize(T[] x, int x_ptr, int len, int C, int Fs, + int bitrate, int tonality, float[] mem, int buffering, + Downmix.downmix_func downmix) + { + int N; + int i; + float[] e = new float[MAX_DYNAMIC_FRAMESIZE + 4]; + float[] e_1 = new float[MAX_DYNAMIC_FRAMESIZE + 3]; + int memx; + int bestLM = 0; + int subframe; + int pos; + int offset; + int[] sub; + + subframe = Fs / 400; + sub = new int[subframe]; + e[0] = mem[0]; + e_1[0] = 1.0f / (CeltConstants.EPSILON + mem[0]); + if (buffering != 0) + { + /* Consider the CELT delay when not in restricted-lowdelay */ + /* We assume the buffering is between 2.5 and 5 ms */ + offset = 2 * subframe - buffering; + Inlines.OpusAssert(offset >= 0 && offset <= subframe); + len -= offset; + e[1] = mem[1]; + e_1[1] = 1.0f / (CeltConstants.EPSILON + mem[1]); + e[2] = mem[2]; + e_1[2] = 1.0f / (CeltConstants.EPSILON + mem[2]); + pos = 3; + } + else { + pos = 1; + offset = 0; + } + N = Inlines.IMIN(len / subframe, MAX_DYNAMIC_FRAMESIZE); + /* Just silencing a warning, it's really initialized later */ + memx = 0; + for (i = 0; i < N; i++) + { + float tmp; + int tmpx; + int j; + tmp = CeltConstants.EPSILON; + + downmix(x, x_ptr, sub, 0, subframe, i * subframe + offset, 0, -2, C); + if (i == 0) + memx = sub[0]; + for (j = 0; j < subframe; j++) + { + tmpx = sub[j]; + tmp += (tmpx - memx) * (float)(tmpx - memx); + memx = tmpx; + } + e[i + pos] = tmp; + e_1[i + pos] = 1.0f / tmp; + } + /* Hack to get 20 ms working with APPLICATION_AUDIO + The real problem is that the corresponding memory needs to use 1.5 ms + from this frame and 1 ms from the next frame */ + e[i + pos] = e[i + pos - 1]; + if (buffering != 0) + N = Inlines.IMIN(MAX_DYNAMIC_FRAMESIZE, N + 2); + bestLM = transient_viterbi(e, e_1, N, (int)((1.0f + .5f * tonality) * (60 * C + 40)), bitrate / 400); + mem[0] = e[1 << bestLM]; + if (buffering != 0) + { + mem[1] = e[(1 << bestLM) + 1]; + mem[2] = e[(1 << bestLM) + 2]; + } + return bestLM; + } + + internal static int frame_size_select(int frame_size, OpusFramesize variable_duration, int Fs) + { + int new_size; + if (frame_size < Fs / 400) + return -1; + if (variable_duration == OpusFramesize.OPUS_FRAMESIZE_ARG) + new_size = frame_size; + else if (variable_duration == OpusFramesize.OPUS_FRAMESIZE_VARIABLE) + new_size = Fs / 50; + else if (variable_duration >= OpusFramesize.OPUS_FRAMESIZE_2_5_MS && variable_duration <= OpusFramesize.OPUS_FRAMESIZE_60_MS) + new_size = Inlines.IMIN(3 * Fs / 50, (Fs / 400) << (variable_duration - OpusFramesize.OPUS_FRAMESIZE_2_5_MS)); + else + return -1; + if (new_size > frame_size) + return -1; + if (400 * new_size != Fs && 200 * new_size != Fs && 100 * new_size != Fs && + 50 * new_size != Fs && 25 * new_size != Fs && 50 * new_size != 3 * Fs) + return -1; + return new_size; + } + + internal static int compute_frame_size(T[] analysis_pcm, int analysis_pcm_ptr, int frame_size, + OpusFramesize variable_duration, int C, int Fs, int bitrate_bps, + int delay_compensation, Downmix.downmix_func downmix, float[] subframe_mem, bool analysis_enabled + ) + { + + if (analysis_enabled && variable_duration == OpusFramesize.OPUS_FRAMESIZE_VARIABLE && frame_size >= Fs / 200) + { + int LM = 3; + LM = optimize_framesize(analysis_pcm, analysis_pcm_ptr, frame_size, C, Fs, bitrate_bps, + 0, subframe_mem, delay_compensation, downmix); + while ((Fs / 400 << LM) > frame_size) + LM--; + frame_size = (Fs / 400 << LM); + } + else + { + frame_size = frame_size_select(frame_size, variable_duration, Fs); + } + + if (frame_size < 0) + return -1; + return frame_size; + } + + internal static int compute_stereo_width(short[] pcm, int pcm_ptr, int frame_size, int Fs, StereoWidthState mem) + { + int corr; + int ldiff; + int width; + int xx, xy, yy; + int sqrt_xx, sqrt_yy; + int qrrt_xx, qrrt_yy; + int frame_rate; + int i; + int short_alpha; + + frame_rate = Fs / frame_size; + short_alpha = CeltConstants.Q15ONE - (25 * CeltConstants.Q15ONE / Inlines.IMAX(50, frame_rate)); + xx = xy = yy = 0; + for (i = 0; i < frame_size - 3; i += 4) + { + int pxx = 0; + int pxy = 0; + int pyy = 0; + int x, y; + int p2i = pcm_ptr + (2 * i); + x = pcm[p2i]; + y = pcm[p2i + 1]; + pxx = Inlines.SHR32(Inlines.MULT16_16(x, x), 2); + pxy = Inlines.SHR32(Inlines.MULT16_16(x, y), 2); + pyy = Inlines.SHR32(Inlines.MULT16_16(y, y), 2); + x = pcm[p2i + 2]; + y = pcm[p2i + 3]; + pxx += Inlines.SHR32(Inlines.MULT16_16(x, x), 2); + pxy += Inlines.SHR32(Inlines.MULT16_16(x, y), 2); + pyy += Inlines.SHR32(Inlines.MULT16_16(y, y), 2); + x = pcm[p2i + 4]; + y = pcm[p2i + 5]; + pxx += Inlines.SHR32(Inlines.MULT16_16(x, x), 2); + pxy += Inlines.SHR32(Inlines.MULT16_16(x, y), 2); + pyy += Inlines.SHR32(Inlines.MULT16_16(y, y), 2); + x = pcm[p2i + 6]; + y = pcm[p2i + 7]; + pxx += Inlines.SHR32(Inlines.MULT16_16(x, x), 2); + pxy += Inlines.SHR32(Inlines.MULT16_16(x, y), 2); + pyy += Inlines.SHR32(Inlines.MULT16_16(y, y), 2); + + xx += Inlines.SHR32(pxx, 10); + xy += Inlines.SHR32(pxy, 10); + yy += Inlines.SHR32(pyy, 10); + } + + mem.XX += Inlines.MULT16_32_Q15(short_alpha, xx - mem.XX); + mem.XY += Inlines.MULT16_32_Q15(short_alpha, xy - mem.XY); + mem.YY += Inlines.MULT16_32_Q15(short_alpha, yy - mem.YY); + mem.XX = Inlines.MAX32(0, mem.XX); + mem.XY = Inlines.MAX32(0, mem.XY); + mem.YY = Inlines.MAX32(0, mem.YY); + if (Inlines.MAX32(mem.XX, mem.YY) > ((short)(0.5 + (8e-4f) * (((int)1) << (18))))/*Inlines.QCONST16(8e-4f, 18)*/) + { + sqrt_xx = Inlines.celt_sqrt(mem.XX); + sqrt_yy = Inlines.celt_sqrt(mem.YY); + qrrt_xx = Inlines.celt_sqrt(sqrt_xx); + qrrt_yy = Inlines.celt_sqrt(sqrt_yy); + /* Inter-channel correlation */ + mem.XY = Inlines.MIN32(mem.XY, sqrt_xx * sqrt_yy); + corr = Inlines.SHR32(Inlines.frac_div32(mem.XY, CeltConstants.EPSILON + Inlines.MULT16_16(sqrt_xx, sqrt_yy)), 16); + /* Approximate loudness difference */ + ldiff = CeltConstants.Q15ONE * Inlines.ABS16(qrrt_xx - qrrt_yy) / (CeltConstants.EPSILON + qrrt_xx + qrrt_yy); + width = Inlines.MULT16_16_Q15(Inlines.celt_sqrt(((int)(0.5 + (1.0f) * (((int)1) << (30))))/*Inlines.QCONST32(1.0f, 30)*/ - Inlines.MULT16_16(corr, corr)), ldiff); + /* Smoothing over one second */ + mem.smoothed_width += (width - mem.smoothed_width) / frame_rate; + /* Peak follower */ + mem.max_follower = Inlines.MAX16(mem.max_follower - ((short)(0.5 + (.02f) * (((int)1) << (15))))/*Inlines.QCONST16(.02f, 15)*/ / frame_rate, mem.smoothed_width); + } + else { + width = 0; + corr = CeltConstants.Q15ONE; + ldiff = 0; + } + /*printf("%f %f %f %f %f ", corr/(float)1.0f, ldiff/(float)1.0f, width/(float)1.0f, mem.smoothed_width/(float)1.0f, mem.max_follower/(float)1.0f);*/ + return Inlines.EXTRACT16(Inlines.MIN32(CeltConstants.Q15ONE, 20 * mem.max_follower)); + } + + internal static void smooth_fade(short[] in1, int in1_ptr, short[] in2, int in2_ptr, + short[] output, int output_ptr, int overlap, int channels, + int[] window, int Fs) + { + int i, c; + int inc = 48000 / Fs; + for (c = 0; c < channels; c++) + { + for (i = 0; i < overlap; i++) + { + int w = Inlines.MULT16_16_Q15(window[i * inc], window[i * inc]); + output[output_ptr + (i * channels) + c] = (short)(Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(w, in2[in2_ptr + (i * channels) + c]), + CeltConstants.Q15ONE - w, in1[in1_ptr + (i * channels) + c]), 15)); + } + } + } + + //internal static void opus_pcm_soft_clip(Pointer _x, int N, int C, Pointer declip_mem) + //{ + // int c; + // int i; + // Pointer x; + + // if (C < 1 || N < 1 || _x == null || declip_mem == null) return; + + // /* First thing: saturate everything to +/- 2 which is the highest level our + // non-linearity can handle. At the point where the signal reaches +/-2, + // the derivative will be zero anyway, so this doesn't introduce any + // discontinuity in the derivative. */ + // for (i = 0; i < N * C; i++) + // _x[i] = Inlines.MAX16(-2.0f, Inlines.MIN16(2.0f, _x[i])); + // for (c = 0; c < C; c++) + // { + // float a; + // float x0; + // int curr; + + // x = _x.Point(c); + // a = declip_mem[c]; + // /* Continue applying the non-linearity from the previous frame to avoid + // any discontinuity. */ + // for (i = 0; i < N; i++) + // { + // if (x[i * C] * a >= 0) + // break; + // x[i * C] = x[i * C] + a * x[i * C] * x[i * C]; + // } + + // curr = 0; + // x0 = x[0]; + + // while (true) + // { + // int start, end; + // float maxval; + // int special = 0; + // int peak_pos; + // for (i = curr; i < N; i++) + // { + // if (x[i * C] > 1 || x[i * C] < -1) + // break; + // } + // if (i == N) + // { + // a = 0; + // break; + // } + // peak_pos = i; + // start = end = i; + // maxval = Inlines.ABS16(x[i * C]); + // /* Look for first zero crossing before clipping */ + // while (start > 0 && x[i * C] * x[(start - 1) * C] >= 0) + // start--; + // /* Look for first zero crossing after clipping */ + // while (end < N && x[i * C] * x[end * C] >= 0) + // { + // /* Look for other peaks until the next zero-crossing. */ + // if (Inlines.ABS16(x[end * C]) > maxval) + // { + // maxval = Inlines.ABS16(x[end * C]); + // peak_pos = end; + // } + // end++; + // } + // /* Detect the special case where we clip before the first zero crossing */ + // special = (start == 0 && x[i * C] * x[0] >= 0) ? 1 : 0; + + // /* Compute a such that maxval + a*maxval^2 = 1 */ + // a = (maxval - 1) / (maxval * maxval); + // if (x[i * C] > 0) + // a = -a; + // /* Apply soft clipping */ + // for (i = start; i < end; i++) + // x[i * C] = x[i * C] + a * x[i * C] * x[i * C]; + + // if (special != 0 && peak_pos >= 2) + // { + // /* Add a linear ramp from the first sample to the signal peak. + // This avoids a discontinuity at the beginning of the frame. */ + // float delta; + // float offset = x0 - x[0]; + // delta = offset / peak_pos; + // for (i = curr; i < peak_pos; i++) + // { + // offset -= delta; + // x[i * C] += offset; + // x[i * C] = Inlines.MAX16(-1.0f, Inlines.MIN16(1.0f, x[i * C])); + // } + // } + // curr = end; + // if (curr == N) + // { + // break; + // } + // } + + // declip_mem[c] = a; + // } + //} + + + internal static string opus_strerror(int error) + { + string[] error_strings = { + "success", + "invalid argument", + "buffer too small", + "internal error", + "corrupted stream", + "request not implemented", + "invalid state", + "memory allocation failed" + }; + if (error > 0 || error < -7) + return "unknown error"; + else + return error_strings[-error]; + } + + /// + /// Returns the version number of this library + /// + /// + public static string GetVersionString() + { + return "Concentus 1.1.6" +#if DEBUG + + "-debug" +#endif +#if FUZZING + + "-fuzzing" +#endif +#if PARITY +#else + + "-nonparity" +#endif + ; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/Downmix.cs b/Libraries/Concentus/CSharp/Concentus/Opus/Downmix.cs new file mode 100644 index 000000000..a27459570 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/Downmix.cs @@ -0,0 +1,125 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using Concentus.Celt; +using Concentus.Common; +using Concentus.Common.CPlusPlus; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + + +namespace Concentus +{ + internal static class Downmix + { + /// + /// + /// + /// The type of signal being handled (either short or float) + /// + /// + /// + /// + /// + /// + /// + public delegate void downmix_func(T[] _x, int x_ptr, int[] sub, int sub_ptr, int subframe, int offset, int c1, int c2, int C); + + internal static void downmix_float(float[] x, int x_ptr, int[] sub, int sub_ptr, int subframe, int offset, int c1, int c2, int C) + { + int scale; + int j; + int c1x = c1 + x_ptr; + for (j = 0; j < subframe; j++) + sub[sub_ptr + j] = Inlines.FLOAT2INT16(x[(j + offset) * C + c1x]); + if (c2 > -1) + { + int c2x = c2 + x_ptr; + for (j = 0; j < subframe; j++) + sub[sub_ptr + j] += Inlines.FLOAT2INT16(x[(j + offset) * C + c2x]); + } + else if (c2 == -2) + { + int c; + int cx; + for (c = 1; c < C; c++) + { + cx = c + x_ptr; + for (j = 0; j < subframe; j++) + sub[sub_ptr + j] += Inlines.FLOAT2INT16(x[(j + offset) * C + cx]); + } + } + scale = (1 << CeltConstants.SIG_SHIFT); + if (C == -2) + scale /= C; + else + scale /= 2; + for (j = 0; j < subframe; j++) + sub[sub_ptr + j] *= scale; + } + + internal static void downmix_int(short[] x, int x_ptr, int[] sub, int sub_ptr, int subframe, int offset, int c1, int c2, int C) + { + int scale; + int j; + for (j = 0; j < subframe; j++) + sub[j + sub_ptr] = x[(j + offset) * C + c1]; + if (c2 > -1) + { + for (j = 0; j < subframe; j++) + sub[j + sub_ptr] += x[(j + offset) * C + c2]; + } + else if (c2 == -2) + { + int c; + for (c = 1; c < C; c++) + { + for (j = 0; j < subframe; j++) + sub[j + sub_ptr] += x[(j + offset) * C + c]; + } + } + scale = (1 << CeltConstants.SIG_SHIFT); + if (C == -2) + scale /= C; + else + scale /= 2; + for (j = 0; j < subframe; j++) + sub[j + sub_ptr] *= scale; + } + + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusApplication.cs b/Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusApplication.cs new file mode 100644 index 000000000..dc99d593f --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusApplication.cs @@ -0,0 +1,57 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Enums +{ + public enum OpusApplication + { + OPUS_APPLICATION_UNIMPLEMENTED = 0, + + /// + /// Best for most VoIP/videoconference applications where listening quality and intelligibility matter most + /// + OPUS_APPLICATION_VOIP = 2048, + + /// + /// Best for broadcast/high-fidelity application where the decoded audio should be as close as possible to the input + /// + OPUS_APPLICATION_AUDIO = 2049, + + /// + /// Only use when lowest-achievable latency is what matters most. Voice-optimized modes cannot be used. + /// + OPUS_APPLICATION_RESTRICTED_LOWDELAY = 2051 + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusBandwidth.cs b/Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusBandwidth.cs new file mode 100644 index 000000000..381bb6da3 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusBandwidth.cs @@ -0,0 +1,70 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Enums +{ + public enum OpusBandwidth + { + OPUS_BANDWIDTH_AUTO = -1000, + OPUS_BANDWIDTH_NARROWBAND = 1101, + OPUS_BANDWIDTH_MEDIUMBAND = 1102, + OPUS_BANDWIDTH_WIDEBAND = 1103, + OPUS_BANDWIDTH_SUPERWIDEBAND = 1104, + OPUS_BANDWIDTH_FULLBAND = 1105 + } + + // FIXME: We should remove all cases where bandwidth is cast to int, it's.....improper + internal static class OpusBandwidthHelpers + { + internal static int GetOrdinal(OpusBandwidth bw) + { + return (int)bw - (int)OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND; + } + + internal static OpusBandwidth MIN(OpusBandwidth a, OpusBandwidth b) + { + if ((int)a < (int)b) + return a; + return b; + } + + internal static OpusBandwidth MAX(OpusBandwidth a, OpusBandwidth b) + { + if ((int)a > (int)b) + return a; + return b; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusControl.cs b/Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusControl.cs new file mode 100644 index 000000000..f5644ef39 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusControl.cs @@ -0,0 +1,94 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Enums +{ + public static class OpusControl + { + /** These are the actual Encoder CTL ID numbers. + * They should not be used directly by applications. + * In general, SETs should be even and GETs should be odd.*/ + public const int OPUS_SET_APPLICATION_REQUEST = 4000; + public const int OPUS_GET_APPLICATION_REQUEST = 4001; + public const int OPUS_SET_BITRATE_REQUEST = 4002; + public const int OPUS_GET_BITRATE_REQUEST = 4003; + public const int OPUS_SET_MAX_BANDWIDTH_REQUEST = 4004; + public const int OPUS_GET_MAX_BANDWIDTH_REQUEST = 4005; + public const int OPUS_SET_VBR_REQUEST = 4006; + public const int OPUS_GET_VBR_REQUEST = 4007; + public const int OPUS_SET_BANDWIDTH_REQUEST = 4008; + public const int OPUS_GET_BANDWIDTH_REQUEST = 4009; + public const int OPUS_SET_COMPLEXITY_REQUEST = 4010; + public const int OPUS_GET_COMPLEXITY_REQUEST = 4011; + public const int OPUS_SET_INBAND_FEC_REQUEST = 4012; + public const int OPUS_GET_INBAND_FEC_REQUEST = 4013; + public const int OPUS_SET_PACKET_LOSS_PERC_REQUEST = 4014; + public const int OPUS_GET_PACKET_LOSS_PERC_REQUEST = 4015; + public const int OPUS_SET_DTX_REQUEST = 4016; + public const int OPUS_GET_DTX_REQUEST = 4017; + public const int OPUS_SET_VBR_CONSTRAINT_REQUEST = 4020; + public const int OPUS_GET_VBR_CONSTRAINT_REQUEST = 4021; + public const int OPUS_SET_FORCE_CHANNELS_REQUEST = 4022; + public const int OPUS_GET_FORCE_CHANNELS_REQUEST = 4023; + public const int OPUS_SET_SIGNAL_REQUEST = 4024; + public const int OPUS_GET_SIGNAL_REQUEST = 4025; + public const int OPUS_GET_LOOKAHEAD_REQUEST = 4027; + /* public const int OPUS_RESET_STATE 4028 */ + public const int OPUS_GET_SAMPLE_RATE_REQUEST = 4029; + public const int OPUS_GET_FINAL_RANGE_REQUEST = 4031; + public const int OPUS_GET_PITCH_REQUEST = 4033; + public const int OPUS_SET_GAIN_REQUEST = 4034; + public const int OPUS_GET_GAIN_REQUEST = 4045; + public const int OPUS_SET_LSB_DEPTH_REQUEST = 4036; + public const int OPUS_GET_LSB_DEPTH_REQUEST = 4037; + public const int OPUS_GET_LAST_PACKET_DURATION_REQUEST = 4039; + public const int OPUS_SET_EXPERT_FRAME_DURATION_REQUEST = 4040; + public const int OPUS_GET_EXPERT_FRAME_DURATION_REQUEST = 4041; + public const int OPUS_SET_PREDICTION_DISABLED_REQUEST = 4042; + public const int OPUS_GET_PREDICTION_DISABLED_REQUEST = 4043; + + /// + /// Resets the codec state to be equivalent to a freshly initialized state. + /// This should be called when switching streams in order to prevent + /// the back to back decoding from giving different results from + /// one at a time decoding. + /// + public const int OPUS_RESET_STATE = 4028; + + public const int OPUS_SET_VOICE_RATIO_REQUEST = 11018; + public const int OPUS_GET_VOICE_RATIO_REQUEST = 11019; + public const int OPUS_SET_FORCE_MODE_REQUEST = 11002; + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusError.cs b/Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusError.cs new file mode 100644 index 000000000..004617446 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusError.cs @@ -0,0 +1,68 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Enums +{ + /// + /// Note that since most API-level errors are detected and thrown as + /// OpusExceptions, direct use of this class is not usually needed + /// + public static class OpusError + { + /** No error*/ + public const int OPUS_OK = 0; + + /** One or more invalid/out of range arguments*/ + public const int OPUS_BAD_ARG = -1; + + /** Not enough bytes allocated in the buffer*/ + public const int OPUS_BUFFER_TOO_SMALL = -2; + + /** An public error was detected*/ + public const int OPUS_INTERNAL_ERROR = -3; + + /** The compressed data passed is corrupted*/ + public const int OPUS_INVALID_PACKET = -4; + + /** Invalid/unsupported request number*/ + public const int OPUS_UNIMPLEMENTED = -5; + + /** An encoder or decoder structure is invalid or already freed*/ + public const int OPUS_INVALID_STATE = -6; + + /** Memory allocation has failed*/ + public const int OPUS_ALLOC_FAIL = -7; + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusFramesize.cs b/Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusFramesize.cs new file mode 100644 index 000000000..abdb96b61 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusFramesize.cs @@ -0,0 +1,80 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Enums +{ + public enum OpusFramesize + { + /// + /// Select frame size from the argument (default) + /// + OPUS_FRAMESIZE_ARG = 5000, + + /// + /// Use 2.5 ms frames + /// + OPUS_FRAMESIZE_2_5_MS = 5001, + + /// + /// Use 5 ms frames + /// + OPUS_FRAMESIZE_5_MS = 5002, + + /// + /// Use 10 ms frames + /// + OPUS_FRAMESIZE_10_MS = 5003, + + /// + /// Use 20 ms frames + /// + OPUS_FRAMESIZE_20_MS = 5004, + + /// + /// Use 40 ms frames + /// + OPUS_FRAMESIZE_40_MS = 5005, + + /// + /// Use 60 ms frames + /// + OPUS_FRAMESIZE_60_MS = 5006, + + /// + /// Do not use - not fully implemented. Optimize the frame size dynamically. + /// + OPUS_FRAMESIZE_VARIABLE = 5010 + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusMode.cs b/Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusMode.cs new file mode 100644 index 000000000..3f29eb7ad --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusMode.cs @@ -0,0 +1,45 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Enums +{ + public enum OpusMode + { + MODE_AUTO = -1000, + MODE_SILK_ONLY = 1000, + MODE_HYBRID = 1001, + MODE_CELT_ONLY = 1002 + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusSignal.cs b/Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusSignal.cs new file mode 100644 index 000000000..c8ccd6df6 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/Enums/OpusSignal.cs @@ -0,0 +1,52 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Enums +{ + public enum OpusSignal + { + OPUS_SIGNAL_AUTO = -1000, + + /// + /// Signal being encoded is voice + /// + OPUS_SIGNAL_VOICE = 3001, + + /// + /// Signal being encoded is music + /// + OPUS_SIGNAL_MUSIC = 3002 + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/MultiLayerPerceptron.cs b/Libraries/Concentus/CSharp/Concentus/Opus/MultiLayerPerceptron.cs new file mode 100644 index 000000000..11c065f88 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/MultiLayerPerceptron.cs @@ -0,0 +1,111 @@ +/* Copyright (c) 2008-2011 Octasic Inc. + Originally written by Jean-Marc Valin + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using Concentus.Common; +using Concentus.Common.CPlusPlus; +using Concentus.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + + +namespace Concentus +{ + /// + /// multi-layer perceptron processor + /// + internal static class mlp + { + private const int MAX_NEURONS = 100; + + internal static float tansig_approx(float x) + { + int i; + float y, dy; + float sign = 1; + /* Tests are reversed to catch NaNs */ + if (!(x < 8)) + return 1; + if (!(x > -8)) + return -1; + if (x < 0) + { + x = -x; + sign = -1; + } + i = (int)Math.Floor(.5f + 25 * x); + x -= .04f * i; + y = Tables.tansig_table[i]; + dy = 1 - y * y; + y = y + x * dy * (1 - y * x); + return sign * y; + } + + internal static void mlp_process(MLP m, float[] input, float[] output) + { + int j; + float[] hidden = new float[MAX_NEURONS]; + float[] W = m.weights; + int W_ptr = 0; + + /* Copy to tmp_in */ + + for (j = 0; j < m.topo[1]; j++) + { + int k; + float sum = W[W_ptr]; + W_ptr++; + for (k = 0; k < m.topo[0]; k++) + { + sum = sum + input[k] * W[W_ptr]; + W_ptr++; + } + hidden[j] = tansig_approx(sum); + } + + for (j = 0; j < m.topo[2]; j++) + { + int k; + float sum = W[W_ptr]; + W_ptr++; + for (k = 0; k < m.topo[1]; k++) + { + sum = sum + hidden[k] * W[W_ptr]; + W_ptr++; + } + output[j] = tansig_approx(sum); + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/OpusCompare.cs b/Libraries/Concentus/CSharp/Concentus/Opus/OpusCompare.cs new file mode 100644 index 000000000..d43f8edb4 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/OpusCompare.cs @@ -0,0 +1,340 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using Concentus.Common.CPlusPlus; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; + + +namespace Concentus +{ + internal static class OpusCompare + { + private const int NBANDS = 21; + private const int NFREQS = 240; + private const int TEST_WIN_SIZE = 480; + private const int TEST_WIN_STEP = 120; + + /*Bands on which we compute the pseudo-NMR (Bark-derived CELT bands).*/ + private static readonly int[] BANDS/*[NBANDS + 1]*/ ={ + 0,2,4,6,8,10,12,14,16,20,24,28,32,40,48,56,68,80,96,120,156,200 + }; + + private static void band_energy(Pointer _out, Pointer _ps, + Pointer _bands, int _nbands, + Pointer _in, int _nchannels, int _nframes, int _window_sz, + int _step, int _downsample) + { + Pointer window; + Pointer x; + Pointer c; + Pointer s; + int xi; + int xj; + int ps_sz; + window = Pointer.Malloc((3 + _nchannels) * _window_sz); + c = window.Point(_window_sz); + s = c.Point(_window_sz); + x = s.Point(_window_sz); + ps_sz = _window_sz / 2; + for (xj = 0; xj < _window_sz; xj++) + { + window[xj] = (float)(0.5 - 0.5 * Math.Cos((2 * Math.PI / (_window_sz - 1)) * xj)); + } + for (xj = 0; xj < _window_sz; xj++) + { + c[xj] = (float)Math.Cos((2 * Math.PI / _window_sz) * xj); + } + for (xj = 0; xj < _window_sz; xj++) + { + s[xj] = (float)Math.Sin((2 * Math.PI / _window_sz) * xj); + } + for (xi = 0; xi < _nframes; xi++) + { + int ci; + int xk; + int bi; + for (ci = 0; ci < _nchannels; ci++) + { + for (xk = 0; xk < _window_sz; xk++) + { + x[ci * _window_sz + xk] = window[xk] * _in[(xi * _step + xk) * _nchannels + ci]; + } + } + for (bi = xj = 0; bi < _nbands; bi++) + { + float[] p = { 0, 0 }; + for (; xj < _bands[bi + 1]; xj++) + { + for (ci = 0; ci < _nchannels; ci++) + { + float re; + float im; + int ti; + ti = 0; + re = im = 0; + for (xk = 0; xk < _window_sz; xk++) + { + re += c[ti] * x[ci * _window_sz + xk]; + im -= s[ti] * x[ci * _window_sz + xk]; + ti += xj; + if (ti >= _window_sz) ti -= _window_sz; + } + re *= _downsample; + im *= _downsample; + _ps[(xi * ps_sz + xj) * _nchannels + ci] = re * re + im * im + 100000; + p[ci] += _ps[(xi * ps_sz + xj) * _nchannels + ci]; + } + } + if (_out != null) + { + _out[(xi * _nbands + bi) * _nchannels] = p[0] / (_bands[bi + 1] - _bands[bi]); + if (_nchannels == 2) + { + _out[(xi * _nbands + bi) * _nchannels + 1] = p[1] / (_bands[bi + 1] - _bands[bi]); + } + } + } + } + } + + internal static float compare(float[] x, float[] y, int nchannels, int rate = 48000) + { + Pointer xb; + Pointer X; + Pointer Y; + double err; + float Q; + int xlength = x.Length; + int ylength = y.Length; + int nframes; + int xi; + int ci; + int xj; + int bi; + int downsample; + int ybands; + int yfreqs; + int max_compare; + ybands = NBANDS; + yfreqs = NFREQS; + if (rate != 8000 && rate != 12000 && rate != 16000 && rate != 24000 && rate != 48000) + { + throw new ArgumentException("Sampling rate must be 8000, 12000, 16000, 24000, or 48000\n"); + } + if (rate != 48000) + { + downsample = 48000 / rate; + switch (rate) + { + case 8000: ybands = 13; break; + case 12000: ybands = 15; break; + case 16000: ybands = 17; break; + case 24000: ybands = 19; break; + } + yfreqs = NFREQS / downsample; + } + else + { + downsample = 1; + } + + if (xlength != ylength * downsample) + { + throw new ArgumentException("Sample counts do not match"); + } + + if (xlength < TEST_WIN_SIZE) + { + throw new ArgumentException("Insufficient sample data"); + } + + nframes = (xlength - TEST_WIN_SIZE + TEST_WIN_STEP) / TEST_WIN_STEP; + xb = Pointer.Malloc(nframes * NBANDS * nchannels); + X = Pointer.Malloc(nframes * NFREQS * nchannels); + Y = Pointer.Malloc(nframes * yfreqs * nchannels); + /*Compute the per-band spectral energy of the original signal + and the error.*/ + band_energy(xb, X, BANDS.GetPointer(), NBANDS, x.GetPointer(), nchannels, nframes, + TEST_WIN_SIZE, TEST_WIN_STEP, 1); + band_energy(null, Y, BANDS.GetPointer(), ybands, y.GetPointer(), nchannels, nframes, + TEST_WIN_SIZE / downsample, TEST_WIN_STEP / downsample, downsample); + for (xi = 0; xi < nframes; xi++) + { + /*Frequency masking (low to high): 10 dB/Bark slope.*/ + for (bi = 1; bi < NBANDS; bi++) + { + for (ci = 0; ci < nchannels; ci++) + { + xb[(xi * NBANDS + bi) * nchannels + ci] += + 0.1F * xb[(xi * NBANDS + bi - 1) * nchannels + ci]; + } + } + /*Frequency masking (high to low): 15 dB/Bark slope.*/ + for (bi = NBANDS - 1; bi-- > 0;) + { + for (ci = 0; ci < nchannels; ci++) + { + xb[(xi * NBANDS + bi) * nchannels + ci] += + 0.03F * xb[(xi * NBANDS + bi + 1) * nchannels + ci]; + } + } + if (xi > 0) + { + /*Temporal masking: -3 dB/2.5ms slope.*/ + for (bi = 0; bi < NBANDS; bi++) + { + for (ci = 0; ci < nchannels; ci++) + { + xb[(xi * NBANDS + bi) * nchannels + ci] += + 0.5F * xb[((xi - 1) * NBANDS + bi) * nchannels + ci]; + } + } + } + /* Allowing some cross-talk */ + if (nchannels == 2) + { + for (bi = 0; bi < NBANDS; bi++) + { + float l, r; + l = xb[(xi * NBANDS + bi) * nchannels + 0]; + r = xb[(xi * NBANDS + bi) * nchannels + 1]; + xb[(xi * NBANDS + bi) * nchannels + 0] += 0.01F * r; + xb[(xi * NBANDS + bi) * nchannels + 1] += 0.01F * l; + } + } + + /* Apply masking */ + for (bi = 0; bi < ybands; bi++) + { + for (xj = BANDS[bi]; xj < BANDS[bi + 1]; xj++) + { + for (ci = 0; ci < nchannels; ci++) + { + X[(xi * NFREQS + xj) * nchannels + ci] += + 0.1F * xb[(xi * NBANDS + bi) * nchannels + ci]; + Y[(xi * yfreqs + xj) * nchannels + ci] += + 0.1F * xb[(xi * NBANDS + bi) * nchannels + ci]; + } + } + } + } + + /* Average of consecutive frames to make comparison slightly less sensitive */ + for (bi = 0; bi < ybands; bi++) + { + for (xj = BANDS[bi]; xj < BANDS[bi + 1]; xj++) + { + for (ci = 0; ci < nchannels; ci++) + { + float xtmp; + float ytmp; + xtmp = X[xj * nchannels + ci]; + ytmp = Y[xj * nchannels + ci]; + for (xi = 1; xi < nframes; xi++) + { + float xtmp2; + float ytmp2; + xtmp2 = X[(xi * NFREQS + xj) * nchannels + ci]; + ytmp2 = Y[(xi * yfreqs + xj) * nchannels + ci]; + X[(xi * NFREQS + xj) * nchannels + ci] += xtmp; + Y[(xi * yfreqs + xj) * nchannels + ci] += ytmp; + xtmp = xtmp2; + ytmp = ytmp2; + } + } + } + } + + /*If working at a lower sampling rate, don't take into account the last + 300 Hz to allow for different transition bands. + For 12 kHz, we don't skip anything, because the last band already skips + 400 Hz.*/ + if (rate == 48000) max_compare = BANDS[NBANDS]; + else if (rate == 12000) max_compare = BANDS[ybands]; + else max_compare = BANDS[ybands] - 3; + err = 0; + for (xi = 0; xi < nframes; xi++) + { + double Ef; + Ef = 0; + for (bi = 0; bi < ybands; bi++) + { + double Eb; + Eb = 0; + for (xj = BANDS[bi]; xj < BANDS[bi + 1] && xj < max_compare; xj++) + { + for (ci = 0; ci < nchannels; ci++) + { + float re; + float im; + re = Y[(xi * yfreqs + xj) * nchannels + ci] / X[(xi * NFREQS + xj) * nchannels + ci]; + im = re - (float)Math.Log(re) - 1; + /*Make comparison less sensitive around the SILK/CELT cross-over to + allow for mode freedom in the filters.*/ + if (xj >= 79 && xj <= 81) im *= 0.1F; + if (xj == 80) im *= 0.1F; + Eb += im; + } + } + Eb /= (BANDS[bi + 1] - BANDS[bi]) * nchannels; + Ef += Eb * Eb; + } + /*Using a fixed normalization value means we're willing to accept slightly + lower quality for lower sampling rates.*/ + Ef /= NBANDS; + Ef *= Ef; + err += Ef * Ef; + } + err = Math.Pow(err / nframes, 1.0 / 16); + Q = (float)(100 * (1 - 0.5 * Math.Log(1 + err) / Math.Log(1.13))); + + if (Q < 0) + { + Debug.WriteLine("Test vector FAILS"); + Debug.WriteLine(string.Format("Internal weighted error is {0}", err)); + } + else { + Debug.WriteLine("Test vector PASSES"); + Debug.WriteLine(string.Format("Opus quality metric: {0} (internal weighted error is {1})", Q, err)); + } + + return Q; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/OpusConstants.cs b/Libraries/Concentus/CSharp/Concentus/Opus/OpusConstants.cs new file mode 100644 index 000000000..70b8cd9dd --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/OpusConstants.cs @@ -0,0 +1,66 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + + +namespace Concentus +{ + public static class OpusConstants + { + /// + /// Auto/default setting + /// + public const int OPUS_AUTO = -1000; + + /// + /// Maximum bitrate + /// + public const int OPUS_BITRATE_MAX = -1; + + // from analysis.c + public const int NB_FRAMES = 8; + public const int NB_TBANDS = 18; + public const int NB_TOT_BANDS = 21; + public const int NB_TONAL_SKIP_BANDS = 9; + public const int ANALYSIS_BUF_SIZE = 720; /* 15 ms at 48 kHz */ + public const int DETECT_SIZE = 200; + + public const int MAX_ENCODER_BUFFER = 480; + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/OpusException.cs b/Libraries/Concentus/CSharp/Concentus/Opus/OpusException.cs new file mode 100644 index 000000000..3a8c71610 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/OpusException.cs @@ -0,0 +1,46 @@ +/* Copyright (c) 2016 Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + + +namespace Concentus +{ + public class OpusException : Exception + { + public OpusException() : base() { } + public OpusException(string message) : base(message) { } + public OpusException(string message, int opus_error_code) : base(message + ": " + CodecHelpers.opus_strerror(opus_error_code)) { } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/OpusMultistream.cs b/Libraries/Concentus/CSharp/Concentus/Opus/OpusMultistream.cs new file mode 100644 index 000000000..fdd243b70 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/OpusMultistream.cs @@ -0,0 +1,99 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using Concentus.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + + +namespace Concentus +{ + internal static class OpusMultistream + { + internal static int validate_layout(ChannelLayout layout) + { + int i, max_channel; + + max_channel = layout.nb_streams + layout.nb_coupled_streams; + if (max_channel > 255) + return 0; + for (i = 0; i < layout.nb_channels; i++) + { + if (layout.mapping[i] >= max_channel && layout.mapping[i] != 255) + return 0; + } + return 1; + } + + + internal static int get_left_channel(ChannelLayout layout, int stream_id, int prev) + { + int i; + i = (prev < 0) ? 0 : prev + 1; + for (; i < layout.nb_channels; i++) + { + if (layout.mapping[i] == stream_id * 2) + return i; + } + return -1; + } + + internal static int get_right_channel(ChannelLayout layout, int stream_id, int prev) + { + int i; + i = (prev < 0) ? 0 : prev + 1; + for (; i < layout.nb_channels; i++) + { + if (layout.mapping[i] == stream_id * 2 + 1) + return i; + } + return -1; + } + + internal static int get_mono_channel(ChannelLayout layout, int stream_id, int prev) + { + int i; + i = (prev < 0) ? 0 : prev + 1; + for (; i < layout.nb_channels; i++) + { + if (layout.mapping[i] == stream_id + layout.nb_coupled_streams) + return i; + } + return -1; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/Structs/ChannelLayout.cs b/Libraries/Concentus/CSharp/Concentus/Opus/Structs/ChannelLayout.cs new file mode 100644 index 000000000..233aaccb9 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/Structs/ChannelLayout.cs @@ -0,0 +1,60 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using Concentus.Common.CPlusPlus; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + + +namespace Concentus.Structs +{ + internal class ChannelLayout + { + internal int nb_channels; + internal int nb_streams; + internal int nb_coupled_streams; + internal readonly byte[] mapping = new byte[256]; + + internal void Reset() + { + nb_channels = 0; + nb_streams = 0; + nb_coupled_streams = 0; + Arrays.MemSetByte(mapping, 0); + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/Structs/MLP.cs b/Libraries/Concentus/CSharp/Concentus/Opus/Structs/MLP.cs new file mode 100644 index 000000000..0901116cf --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/Structs/MLP.cs @@ -0,0 +1,52 @@ +/* Copyright (c) 2008-2011 Octasic Inc. + Originally written by Jean-Marc Valin + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using Concentus.Common.CPlusPlus; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + + +namespace Concentus.Structs +{ + /// + /// state object for multi-layer perceptron + /// + internal class MLP + { + internal int layers; + internal int[] topo; + internal float[] weights; + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusDecoder.cs b/Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusDecoder.cs new file mode 100644 index 000000000..18fff6501 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusDecoder.cs @@ -0,0 +1,933 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using Concentus.Celt; +using Concentus.Celt.Structs; +using Concentus.Common; +using Concentus.Common.CPlusPlus; +using Concentus.Enums; +using Concentus.Silk; +using Concentus.Silk.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + + +namespace Concentus.Structs +{ + /// + /// The Opus decoder structure. + /// + /// Opus is a stateful codec with overlapping blocks and as a result Opus + /// packets are not coded independently of each other. Packets must be + /// passed into the decoder serially and in the correct order for a correct + /// decode. Lost packets can be replaced with loss concealment by calling + /// the decoder with a null reference and zero length for the missing packet. + /// + /// A single codec state may only be accessed from a single thread at + /// a time and any required locking must be performed by the caller. Separate + /// streams must be decoded with separate decoder states and can be decoded + /// in parallel. + /// + public class OpusDecoder + { + internal int channels; + internal int Fs; /** Sampling rate (at the API level) */ + internal readonly DecControlState DecControl = new DecControlState(); + internal int decode_gain; + + /* Everything beyond this point gets cleared on a reset */ + internal int stream_channels; + internal OpusBandwidth bandwidth; + internal OpusMode mode; + internal OpusMode prev_mode; + internal int frame_size; + internal int prev_redundancy; + internal int last_packet_duration; + internal uint rangeFinal; + internal SilkDecoder SilkDecoder = new SilkDecoder(); + internal CeltDecoder Celt_Decoder = new CeltDecoder(); + + // Used by multistream decoder + internal OpusDecoder() { } + + internal void Reset() + { + channels = 0; + Fs = 0; /* Sampling rate (at the API level) */ + DecControl.Reset(); + decode_gain = 0; + PartialReset(); + } + + /// + /// OPUS_DECODER_RESET_START + /// + internal void PartialReset() + { + stream_channels = 0; + bandwidth = 0; + mode = 0; + prev_mode = 0; + frame_size = 0; + prev_redundancy = 0; + last_packet_duration = 0; + rangeFinal = 0; + // fixme: do these get reset here? I don't think they do because init_celt and init_silk should both call RESET_STATE on their respective states + //SilkDecoder.Reset(); + //CeltDecoder.Reset(); + } + + /// + /// Deprecated. Just use the regular constructor + /// + public static OpusDecoder Create(int Fs, int channels) + { + return new OpusDecoder(Fs, channels); + } + + /// + /// Allocates and initializes a decoder state. + /// Internally Opus stores data at 48000 Hz, so that should be the default + /// value for Fs. However, the decoder can efficiently decode to buffers + /// at 8, 12, 16, and 24 kHz so if for some reason the caller cannot use + /// data at the full sample rate, or knows the compressed data doesn't + /// use the full frequency range, it can request decoding at a reduced + /// rate. Likewise, the decoder is capable of filling in either mono or + /// interleaved stereo pcm buffers, at the caller's request. + /// + /// Sample rate to decode at (Hz). This must be one of 8000, 12000, 16000, 24000, or 48000. + /// Number of channels (1 or 2) to decode + /// The created encoder + public OpusDecoder(int Fs, int channels) + { + int ret; + if ((Fs != 48000 && Fs != 24000 && Fs != 16000 && Fs != 12000 && Fs != 8000)) + { + throw new ArgumentException("Sample rate is invalid (must be 8/12/16/24/48 Khz)"); + } + if (channels != 1 && channels != 2) + { + throw new ArgumentException("Number of channels must be 1 or 2"); + } + + ret = this.opus_decoder_init(Fs, channels); + if (ret != OpusError.OPUS_OK) + { + if (ret == OpusError.OPUS_BAD_ARG) + throw new ArgumentException("OPUS_BAD_ARG when creating decoder"); + throw new OpusException("Error while initializing decoder", ret); + } + } + + /** Initializes a previously allocated decoder state. + * The state must be at least the size returned by opus_decoder_get_size(). + * This is intended for applications which use their own allocator instead of malloc. @see opus_decoder_create,opus_decoder_get_size + * To reset a previously initialized state, use the #OPUS_RESET_STATE CTL. + * @param [in] st OpusDecoder*: Decoder state. + * @param [in] Fs opus_int32: Sampling rate to decode to (Hz). + * This must be one of 8000, 12000, 16000, + * 24000, or 48000. + * @param [in] channels int: Number of channels (1 or 2) to decode + * @retval #OPUS_OK Success or @ref opus_errorcodes + */ + internal int opus_decoder_init(int Fs, int channels) + { + SilkDecoder silk_dec; + CeltDecoder celt_dec; + int ret; + + if ((Fs != 48000 && Fs != 24000 && Fs != 16000 && Fs != 12000 && Fs != 8000) + || (channels != 1 && channels != 2)) + return OpusError.OPUS_BAD_ARG; + this.Reset(); + + /* Initialize SILK encoder */ + silk_dec = this.SilkDecoder; + celt_dec = this.Celt_Decoder; + this.stream_channels = this.channels = channels; + + this.Fs = Fs; + this.DecControl.API_sampleRate = this.Fs; + this.DecControl.nChannelsAPI = this.channels; + + /* Reset decoder */ + ret = DecodeAPI.silk_InitDecoder(silk_dec); + if (ret != 0) return OpusError.OPUS_INTERNAL_ERROR; + + /* Initialize CELT decoder */ + ret = celt_dec.celt_decoder_init(Fs, channels); + if (ret != OpusError.OPUS_OK) + return OpusError.OPUS_INTERNAL_ERROR; + + celt_dec.SetSignalling(0); + + this.prev_mode = 0; + this.frame_size = Fs / 400; + return OpusError.OPUS_OK; + } + + private static readonly byte[] SILENCE = { 0xFF, 0xFF }; + + internal int opus_decode_frame(byte[] data, int data_ptr, + int len, short[] pcm, int pcm_ptr, int frame_size, int decode_fec) + { + SilkDecoder silk_dec; + CeltDecoder celt_dec; + int i, silk_ret = 0, celt_ret = 0; + EntropyCoder dec = new EntropyCoder(); // porting note: stack var + int silk_frame_size; + int pcm_silk_size; + short[] pcm_silk; + int pcm_transition_silk_size; + short[] pcm_transition_silk; + int pcm_transition_celt_size; + short[] pcm_transition_celt; + short[] pcm_transition = null; + int redundant_audio_size; + short[] redundant_audio; + + int audiosize; + OpusMode mode; + int transition = 0; + int start_band; + int redundancy = 0; + int redundancy_bytes = 0; + int celt_to_silk = 0; + int c; + int F2_5, F5, F10, F20; + int[] window; + uint redundant_rng = 0; + int celt_accum; + + silk_dec = this.SilkDecoder; + celt_dec = this.Celt_Decoder; + F20 = this.Fs / 50; + F10 = F20 >> 1; + F5 = F10 >> 1; + F2_5 = F5 >> 1; + if (frame_size < F2_5) + { + + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + /* Limit frame_size to avoid excessive stack allocations. */ + frame_size = Inlines.IMIN(frame_size, this.Fs / 25 * 3); + /* Payloads of 1 (2 including ToC) or 0 trigger the PLC/DTX */ + if (len <= 1) + { + data = null; + /* In that case, don't conceal more than what the ToC says */ + frame_size = Inlines.IMIN(frame_size, this.frame_size); + } + if (data != null) + { + audiosize = this.frame_size; + mode = this.mode; + dec.dec_init(data, data_ptr, (uint)len); + } + else { + audiosize = frame_size; + mode = this.prev_mode; + + if (mode == 0) + { + /* If we haven't got any packet yet, all we can do is return zeros */ + for (i = pcm_ptr; i < pcm_ptr + (audiosize * this.channels); i++) + pcm[i] = 0; + + return audiosize; + } + + /* Avoids trying to run the PLC on sizes other than 2.5 (CELT), 5 (CELT), + 10, or 20 (e.g. 12.5 or 30 ms). */ + if (audiosize > F20) + { + do + { + int ret = opus_decode_frame(null, 0, 0, pcm, pcm_ptr, Inlines.IMIN(audiosize, F20), 0); + if (ret < 0) + { + + return ret; + } + pcm_ptr += ret * this.channels; + audiosize -= ret; + } while (audiosize > 0); + + return frame_size; + } + else if (audiosize < F20) + { + if (audiosize > F10) + audiosize = F10; + else if (mode != OpusMode.MODE_SILK_ONLY && audiosize > F5 && audiosize < F10) + audiosize = F5; + } + } + + /* In fixed-point, we can tell CELT to do the accumulation on top of the + SILK PCM buffer. This saves some stack space. */ + celt_accum = ((mode != OpusMode.MODE_CELT_ONLY) && (frame_size >= F10)) ? 1 : 0; + + pcm_transition_silk_size = 0; + pcm_transition_celt_size = 0; + if (data != null && this.prev_mode > 0 && ( + (mode == OpusMode.MODE_CELT_ONLY && this.prev_mode != OpusMode.MODE_CELT_ONLY && (this.prev_redundancy == 0)) + || (mode != OpusMode.MODE_CELT_ONLY && this.prev_mode == OpusMode.MODE_CELT_ONLY)) + ) + { + transition = 1; + /* Decide where to allocate the stack memory for pcm_transition */ + if (mode == OpusMode.MODE_CELT_ONLY) + pcm_transition_celt_size = F5 * this.channels; + else + pcm_transition_silk_size = F5 * this.channels; + } + pcm_transition_celt = new short[pcm_transition_celt_size]; + if (transition != 0 && mode == OpusMode.MODE_CELT_ONLY) + { + pcm_transition = pcm_transition_celt; + opus_decode_frame(null, 0, 0, pcm_transition, 0, Inlines.IMIN(F5, audiosize), 0); + } + if (audiosize > frame_size) + { + /*fprintf(stderr, "PCM buffer too small: %d vs %d (mode = %d)\n", audiosize, frame_size, mode);*/ + + return OpusError.OPUS_BAD_ARG; + } + else { + frame_size = audiosize; + } + + /* Don't allocate any memory when in CELT-only mode */ + pcm_silk_size = (mode != OpusMode.MODE_CELT_ONLY && (celt_accum == 0)) ? Inlines.IMAX(F10, frame_size) * this.channels : 0; + pcm_silk = new short[pcm_silk_size]; + + /* SILK processing */ + if (mode != OpusMode.MODE_CELT_ONLY) + { + int lost_flag, decoded_samples; + short[] pcm_ptr2; + int pcm_ptr2_ptr = 0; + + if (celt_accum != 0) + { + pcm_ptr2 = pcm; + pcm_ptr2_ptr = pcm_ptr; + } + else + { + pcm_ptr2 = pcm_silk; + pcm_ptr2_ptr = 0; + } + + if (this.prev_mode == OpusMode.MODE_CELT_ONLY) + DecodeAPI.silk_InitDecoder(silk_dec); + + /* The SILK PLC cannot produce frames of less than 10 ms */ + this.DecControl.payloadSize_ms = Inlines.IMAX(10, 1000 * audiosize / this.Fs); + + if (data != null) + { + this.DecControl.nChannelsInternal = this.stream_channels; + if (mode == OpusMode.MODE_SILK_ONLY) + { + if (this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND) + { + this.DecControl.internalSampleRate = 8000; + } + else if (this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) + { + this.DecControl.internalSampleRate = 12000; + } + else if (this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND) + { + this.DecControl.internalSampleRate = 16000; + } + else { + this.DecControl.internalSampleRate = 16000; + Inlines.OpusAssert(false); + } + } + else { + /* Hybrid mode */ + this.DecControl.internalSampleRate = 16000; + } + } + + lost_flag = data == null ? 1 : 2 * decode_fec; + decoded_samples = 0; + do + { + /* Call SILK decoder */ + int first_frame = (decoded_samples == 0) ? 1 : 0; + silk_ret = DecodeAPI.silk_Decode(silk_dec, this.DecControl, + lost_flag, first_frame, dec, pcm_ptr2, pcm_ptr2_ptr, out silk_frame_size); + if (silk_ret != 0) + { + if (lost_flag != 0) + { + /* PLC failure should not be fatal */ + silk_frame_size = frame_size; + Arrays.MemSetWithOffset(pcm_ptr2, 0, pcm_ptr2_ptr, frame_size * this.channels); + } + else { + + return OpusError.OPUS_INTERNAL_ERROR; + } + } + pcm_ptr2_ptr += (silk_frame_size * this.channels); + decoded_samples += silk_frame_size; + } while (decoded_samples < frame_size); + } + + start_band = 0; + if (decode_fec == 0 && mode != OpusMode.MODE_CELT_ONLY && data != null + && dec.tell() + 17 + 20 * (this.mode == OpusMode.MODE_HYBRID ? 1 : 0) <= 8 * len) + { + /* Check if we have a redundant 0-8 kHz band */ + if (mode == OpusMode.MODE_HYBRID) + redundancy = dec.dec_bit_logp(12); + else + redundancy = 1; + if (redundancy != 0) + { + celt_to_silk = dec.dec_bit_logp(1); + /* redundancy_bytes will be at least two, in the non-hybrid + case due to the ec_tell() check above */ + redundancy_bytes = mode == OpusMode.MODE_HYBRID ? + (int)dec.dec_uint(256) + 2 : + len - ((dec.tell() + 7) >> 3); + len -= redundancy_bytes; + /* This is a sanity check. It should never happen for a valid + packet, so the exact behaviour is not normative. */ + if (len * 8 < dec.tell()) + { + len = 0; + redundancy_bytes = 0; + redundancy = 0; + } + /* Shrink decoder because of raw bits */ + dec.storage = (uint)(dec.storage - redundancy_bytes); + } + } + if (mode != OpusMode.MODE_CELT_ONLY) + start_band = 17; + + { + int endband = 21; + + switch (this.bandwidth) + { + case OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND: + endband = 13; + break; + case OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND: + case OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND: + endband = 17; + break; + case OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND: + endband = 19; + break; + case OpusBandwidth.OPUS_BANDWIDTH_FULLBAND: + endband = 21; + break; + } + celt_dec.SetEndBand(endband); + celt_dec.SetChannels(this.stream_channels); + } + + if (redundancy != 0) + { + transition = 0; + pcm_transition_silk_size = 0; + } + + pcm_transition_silk = new short[pcm_transition_silk_size]; + + if (transition != 0 && mode != OpusMode.MODE_CELT_ONLY) + { + pcm_transition = pcm_transition_silk; + opus_decode_frame(null, 0, 0, pcm_transition, 0, Inlines.IMIN(F5, audiosize), 0); + } + + /* Only allocation memory for redundancy if/when needed */ + redundant_audio_size = redundancy != 0 ? F5 * this.channels : 0; + redundant_audio = new short[redundant_audio_size]; + + /* 5 ms redundant frame for CELT->SILK*/ + if (redundancy != 0 && celt_to_silk != 0) + { + celt_dec.SetStartBand(0); + celt_dec.celt_decode_with_ec(data, (data_ptr + len), redundancy_bytes, + redundant_audio, 0, F5, null, 0); + redundant_rng = celt_dec.GetFinalRange(); + } + + /* MUST be after PLC */ + celt_dec.SetStartBand(start_band); + + if (mode != OpusMode.MODE_SILK_ONLY) + { + int celt_frame_size = Inlines.IMIN(F20, frame_size); + /* Make sure to discard any previous CELT state */ + if (mode != this.prev_mode && this.prev_mode > 0 && this.prev_redundancy == 0) + celt_dec.ResetState(); + /* Decode CELT */ + celt_ret = celt_dec.celt_decode_with_ec(decode_fec != 0 ? null : data, data_ptr, + len, pcm, pcm_ptr, celt_frame_size, dec, celt_accum); + } + else + { + if (celt_accum == 0) + { + for (i = pcm_ptr; i < (frame_size * this.channels) + pcm_ptr; i++) + pcm[i] = 0; + } + /* For hybrid -> SILK transitions, we let the CELT MDCT + do a fade-out by decoding a silence frame */ + if (this.prev_mode == OpusMode.MODE_HYBRID && !(redundancy != 0 && celt_to_silk != 0 && this.prev_redundancy != 0)) + { + celt_dec.SetStartBand(0); + celt_dec.celt_decode_with_ec(SILENCE, 0, 2, pcm, pcm_ptr, F2_5, null, celt_accum); + } + } + + if (mode != OpusMode.MODE_CELT_ONLY && celt_accum == 0) + { + for (i = 0; i < frame_size * this.channels; i++) + pcm[pcm_ptr + i] = Inlines.SAT16(Inlines.ADD32(pcm[pcm_ptr + i], pcm_silk[i])); + } + + window = celt_dec.GetMode().window; + + /* 5 ms redundant frame for SILK->CELT */ + if (redundancy != 0 && celt_to_silk == 0) + { + celt_dec.ResetState(); + celt_dec.SetStartBand(0); + + celt_dec.celt_decode_with_ec(data, data_ptr + len, redundancy_bytes, redundant_audio, 0, F5, null, 0); + redundant_rng = celt_dec.GetFinalRange(); + CodecHelpers.smooth_fade(pcm, pcm_ptr + this.channels * (frame_size - F2_5), redundant_audio, this.channels * F2_5, + pcm, (pcm_ptr + this.channels * (frame_size - F2_5)), F2_5, this.channels, window, this.Fs); + } + if (redundancy != 0 && celt_to_silk != 0) + { + for (c = 0; c < this.channels; c++) + { + for (i = 0; i < F2_5; i++) + pcm[this.channels * i + c + pcm_ptr] = redundant_audio[this.channels * i + c]; + } + CodecHelpers.smooth_fade(redundant_audio,(this.channels * F2_5), pcm,(pcm_ptr + (this.channels * F2_5)), + pcm, (pcm_ptr + (this.channels * F2_5)), F2_5, this.channels, window, this.Fs); + } + if (transition != 0) + { + if (audiosize >= F5) + { + for (i = 0; i < this.channels * F2_5; i++) + pcm[i] = pcm_transition[i]; + CodecHelpers.smooth_fade(pcm_transition, (this.channels * F2_5), pcm, (pcm_ptr + (this.channels * F2_5)), + pcm, (pcm_ptr + (this.channels * F2_5)), F2_5, + this.channels, window, this.Fs); + } + else { + /* Not enough time to do a clean transition, but we do it anyway + This will not preserve amplitude perfectly and may introduce + a bit of temporal aliasing, but it shouldn't be too bad and + that's pretty much the best we can do. In any case, generating this + transition is pretty silly in the first place */ + CodecHelpers.smooth_fade(pcm_transition, 0, pcm, pcm_ptr, + pcm, pcm_ptr, F2_5, + this.channels, window, this.Fs); + } + } + + if (this.decode_gain != 0) + { + int gain; + gain = Inlines.celt_exp2(Inlines.MULT16_16_P15(((short)(0.5 + (6.48814081e-4f) * (((int)1) << (25))))/*Inlines.QCONST16(6.48814081e-4f, 25)*/, this.decode_gain)); + for (i = pcm_ptr; i < pcm_ptr + (frame_size * this.channels); i++) + { + int x; + x = Inlines.MULT16_32_P16(pcm[i], gain); + pcm[i] = (short)Inlines.SATURATE(x, 32767); + } + } + + if (len <= 1) + this.rangeFinal = 0; + else + this.rangeFinal = dec.rng ^ redundant_rng; + + this.prev_mode = mode; + this.prev_redundancy = (redundancy != 0 && celt_to_silk == 0) ? 1 : 0; + + return celt_ret < 0 ? celt_ret : audiosize; + } + + internal int opus_decode_native(byte[] data, int data_ptr, + int len, short[] pcm_out, int pcm_out_ptr, int frame_size, int decode_fec, + int self_delimited, out int packet_offset, int soft_clip) + { + int i, nb_samples; + int count, offset; + byte toc; + int packet_frame_size, packet_stream_channels; + packet_offset = 0; + OpusBandwidth packet_bandwidth; + OpusMode packet_mode; + /* 48 x 2.5 ms = 120 ms */ + // fixme: make sure these values can fit in an int16 + short[] size = new short[48]; + if (decode_fec < 0 || decode_fec > 1) + return OpusError.OPUS_BAD_ARG; + /* For FEC/PLC, frame_size has to be to have a multiple of 2.5 ms */ + if ((decode_fec != 0 || len == 0 || data == null) && frame_size % (this.Fs / 400) != 0) + return OpusError.OPUS_BAD_ARG; + if (len == 0 || data == null) + { + int pcm_count = 0; + do + { + int ret; + ret = opus_decode_frame(null, 0, 0, pcm_out, pcm_out_ptr + (pcm_count * this.channels), frame_size - pcm_count, 0); + if (ret < 0) + return ret; + pcm_count += ret; + } while (pcm_count < frame_size); + Inlines.OpusAssert(pcm_count == frame_size); + this.last_packet_duration = pcm_count; + return pcm_count; + } + else if (len < 0) + return OpusError.OPUS_BAD_ARG; + + packet_mode = OpusPacketInfo.GetEncoderMode(data, data_ptr); + packet_bandwidth = OpusPacketInfo.GetBandwidth(data, data_ptr); + packet_frame_size = OpusPacketInfo.GetNumSamplesPerFrame(data, data_ptr, this.Fs); + packet_stream_channels = OpusPacketInfo.GetNumEncodedChannels(data, data_ptr); + + count = OpusPacketInfo.opus_packet_parse_impl(data, data_ptr, len, self_delimited, out toc, null, null, 0, + size, 0, out offset, out packet_offset); + + if (count < 0) + return count; + + data_ptr += offset; + + if (decode_fec != 0) + { + int dummy; + int duration_copy; + int ret; + /* If no FEC can be present, run the PLC (recursive call) */ + if (frame_size < packet_frame_size || packet_mode == OpusMode.MODE_CELT_ONLY || this.mode == OpusMode.MODE_CELT_ONLY) + return opus_decode_native(null, 0, 0, pcm_out, pcm_out_ptr, frame_size, 0, 0, out dummy, soft_clip); + /* Otherwise, run the PLC on everything except the size for which we might have FEC */ + duration_copy = this.last_packet_duration; + if (frame_size - packet_frame_size != 0) + { + ret = opus_decode_native(null, 0, 0, pcm_out, pcm_out_ptr, frame_size - packet_frame_size, 0, 0, out dummy, soft_clip); + if (ret < 0) + { + this.last_packet_duration = duration_copy; + return ret; + } + Inlines.OpusAssert(ret == frame_size - packet_frame_size); + } + /* Complete with FEC */ + this.mode = packet_mode; + this.bandwidth = packet_bandwidth; + this.frame_size = packet_frame_size; + this.stream_channels = packet_stream_channels; + ret = opus_decode_frame(data, data_ptr, size[0], pcm_out, pcm_out_ptr + (this.channels * (frame_size - packet_frame_size)), + packet_frame_size, 1); + if (ret < 0) + return ret; + else { + this.last_packet_duration = frame_size; + return frame_size; + } + } + + if (count * packet_frame_size > frame_size) + return OpusError.OPUS_BUFFER_TOO_SMALL; + + /* Update the state as the last step to avoid updating it on an invalid packet */ + this.mode = packet_mode; + this.bandwidth = packet_bandwidth; + this.frame_size = packet_frame_size; + this.stream_channels = packet_stream_channels; + + nb_samples = 0; + for (i = 0; i < count; i++) + { + int ret; + ret = opus_decode_frame(data, data_ptr, size[i], pcm_out, pcm_out_ptr + (nb_samples * this.channels), frame_size - nb_samples, 0); + if (ret < 0) + return ret; + Inlines.OpusAssert(ret == packet_frame_size); + data_ptr += size[i]; + nb_samples += ret; + } + this.last_packet_duration = nb_samples; + + return nb_samples; + } + + /// + /// Decodes an Opus packet. + /// + /// The input payload. This may be NULL if that previous packet was lost in transit (when PLC is enabled) + /// The offset to use when reading the input payload. Usually 0 + /// The number of bytes in the payload + /// A buffer to put the output PCM. The output size is (# of samples) * (# of channels). + /// You can use the OpusPacketInfo helpers to get a hint of the frame size before you decode the packet if you need + /// exact sizing. Otherwise, the minimum safe buffer size is 5760 samples + /// The offset to use when writing to the output buffer + /// The number of samples (per channel) of available space in the output PCM buf. + /// If this is less than the maximum packet duration (120ms; 5760 for 48khz), this function will + /// not be capable of decoding some packets. In the case of PLC (data == NULL) or FEC (decode_fec == true), + /// then frame_size needs to be exactly the duration of the audio that is missing, otherwise the decoder will + /// not be in an optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size *must* + /// be a multiple of 10 ms. + /// Indicates that we want to recreate the PREVIOUS (lost) packet using FEC data from THIS packet. Using this packet + /// recovery scheme, you will actually decode this packet twice, first with decode_fec TRUE and then again with FALSE. If FEC data is not + /// available in this packet, the decoder will simply generate a best-effort recreation of the lost packet. + /// The number of decoded samples + public int Decode(byte[] in_data, int in_data_offset, + int len, short[] out_pcm, int out_pcm_offset, int frame_size, bool decode_fec = false) + { + if (frame_size <= 0) + { + throw new ArgumentException("Frame size must be > 0"); + } + + try + { + int dummy; + int ret = opus_decode_native(in_data, in_data_offset, len, out_pcm, out_pcm_offset, frame_size, decode_fec ? 1 : 0, 0, out dummy, 0); + + if (ret < 0) + { + // An error happened; report it + if (ret == OpusError.OPUS_BAD_ARG) + throw new ArgumentException("OPUS_BAD_ARG while decoding"); + throw new OpusException("An error occurred during decoding", ret); + } + + return ret; + } + catch (ArgumentException e) + { + throw new OpusException("Internal error during decoding: " + e.Message); + } + } + + /// + /// Decodes an Opus packet, putting the output data into a floating-point buffer. + /// + /// The input payload. This may be NULL if that previous packet was lost in transit (when PLC is enabled) + /// The offset to use when reading the input payload. Usually 0 + /// The number of bytes in the payload + /// A buffer to put the output PCM. The output size is (# of samples) * (# of channels). + /// You can use the OpusPacketInfo helpers to get a hint of the frame size before you decode the packet if you need + /// exact sizing. Otherwise, the minimum safe buffer size is 5760 samples + /// The offset to use when writing to the output buffer + /// The number of samples (per channel) of available space in the output PCM buf. + /// If this is less than the maximum packet duration (120ms; 5760 for 48khz), this function will + /// not be capable of decoding some packets. In the case of PLC (data == NULL) or FEC (decode_fec == true), + /// then frame_size needs to be exactly the duration of the audio that is missing, otherwise the decoder will + /// not be in an optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size *must* + /// be a multiple of 10 ms. + /// Indicates that we want to recreate the PREVIOUS (lost) packet using FEC data from THIS packet. Using this packet + /// recovery scheme, you will actually decode this packet twice, first with decode_fec TRUE and then again with FALSE. If FEC data is not + /// available in this packet, the decoder will simply generate a best-effort recreation of the lost packet. In that case, + /// the length of frame_size must be EXACTLY the length of the audio that was lost, or else the decoder will be in an inconsistent state. + /// The number of decoded samples (per channel) + public int Decode(byte[] in_data, int in_data_offset, + int len, float[] out_pcm, int out_pcm_offset, int frame_size, bool decode_fec = false) + { + short[] output; + int ret, i; + int nb_samples; + + if (frame_size <= 0) + { + throw new ArgumentException("Frame size must be > 0"); + } + if (in_data != null && len > 0 && !decode_fec) + { + nb_samples = OpusPacketInfo.GetNumSamples(this, in_data, in_data_offset, len); + if (nb_samples > 0) + frame_size = Inlines.IMIN(frame_size, nb_samples); + else + throw new OpusException("An invalid packet was provided (unable to parse # of samples)"); + } + output = new short[frame_size * this.channels]; + + try + { + int dummy; + ret = opus_decode_native(in_data, in_data_offset, len, output, 0, frame_size, decode_fec ? 1 : 0, 0, out dummy, 0); + + if (ret < 0) + { + // An error happened; report it + if (ret == OpusError.OPUS_BAD_ARG) + throw new ArgumentException("OPUS_BAD_ARG when decoding"); + throw new OpusException("An error occurred during decoding", ret); + } + + if (ret > 0) + { + for (i = 0; i < ret * this.channels; i++) + out_pcm[out_pcm_offset + i] = (1.0f / 32768.0f) * (output[i]); + } + + return ret; + } + catch (ArgumentException e) + { + throw new OpusException("Internal error during decoding: " + e.Message); + } + } + + /// + /// Gets the encoded bandwidth of the last packet decoded. This may be lower than the actual decoding sample rate, + /// and is only an indicator of the encoded audio's quality + /// + public OpusBandwidth Bandwidth + { + get + { + return bandwidth; + } + } + + public uint FinalRange + { + get + { + return rangeFinal; + } + } + + /// + /// Gets the sample rate that this decoder decodes to. Always constant for the lifetime of the decoder + /// + public int SampleRate + { + get + { + return Fs; + } + } + + /// + /// Gets the number of channels that this decoder decodes to. Always constant for the lifetime of the decoder. + /// + public int NumChannels + { + get + { + return channels; + } + } + + /// + /// Gets the last estimated pitch value of the decoded audio + /// + public int Pitch + { + get + { + if (prev_mode == OpusMode.MODE_CELT_ONLY) + { + return Celt_Decoder.GetPitch(); + } + else + return DecControl.prevPitchLag; + } + } + + /// + /// Gets or sets the gain (Q8) to use in decoding + /// + public int Gain + { + get + { + return decode_gain; + } + set + { + if (value < -32768 || value > 32767) + { + throw new ArgumentException("Gain must be within the range of a signed int16"); + } + + decode_gain = value; + } + } + + /// + /// Gets the duration of the last packet, in PCM samples per channel + /// + public int LastPacketDuration + { + get + { + return last_packet_duration; + } + } + + /// + /// Resets all buffers and prepares this decoder to process a fresh (unrelated) stream + /// + public void ResetState() + { + PartialReset(); + Celt_Decoder.ResetState(); + DecodeAPI.silk_InitDecoder(SilkDecoder); + stream_channels = channels; + frame_size = Fs / 400; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusEncoder.cs b/Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusEncoder.cs new file mode 100644 index 000000000..41365fe87 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusEncoder.cs @@ -0,0 +1,2050 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using Concentus.Celt; +using Concentus.Celt.Structs; +using Concentus.Common; +using Concentus.Common.CPlusPlus; +using Concentus; +using Concentus.Enums; +using Concentus.Silk; +using Concentus.Silk.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using System.Runtime.CompilerServices; + +namespace Concentus.Structs +{ + /// + /// The Opus encoder structure + /// + public class OpusEncoder + { + #region Encoder state + + internal readonly EncControlState silk_mode = new EncControlState(); + internal OpusApplication application; + internal int channels; + internal int delay_compensation; + internal int force_channels; + internal OpusSignal signal_type; + internal OpusBandwidth user_bandwidth; + internal OpusBandwidth max_bandwidth; + internal OpusMode user_forced_mode; + internal int voice_ratio; + internal int Fs; + internal int use_vbr; + internal int vbr_constraint; + internal OpusFramesize variable_duration; + internal int bitrate_bps; + internal int user_bitrate_bps; + internal int lsb_depth; + internal int encoder_buffer; + internal int lfe; + internal readonly TonalityAnalysisState analysis = new TonalityAnalysisState(); + + // partial reset happens below this line + internal int stream_channels; + internal short hybrid_stereo_width_Q14; + internal int variable_HP_smth2_Q15; + internal int prev_HB_gain; + internal readonly int[] hp_mem = new int[4]; + internal OpusMode mode; + internal OpusMode prev_mode; + internal int prev_channels; + internal int prev_framesize; + internal OpusBandwidth bandwidth; + internal int silk_bw_switch; + /* Sampling rate (at the API level) */ + internal int first; + internal int[] energy_masking; + internal readonly StereoWidthState width_mem = new StereoWidthState(); + internal readonly short[] delay_buffer = new short[OpusConstants.MAX_ENCODER_BUFFER * 2]; + internal OpusBandwidth detected_bandwidth; + internal uint rangeFinal; + + // Constant quality encoding parameter (new feature not in the main libopus) + private int? _vqLevel; + + // [Porting Note] There were originally "cabooses" that were tacked onto the end + // of the struct without being explicitly included (since they have a variable size). + // Here they are just included as an intrinsic variable. + internal readonly SilkEncoder SilkEncoder = new SilkEncoder(); + internal readonly CeltEncoder Celt_Encoder = new CeltEncoder(); + + // used by multichannel structs + internal OpusEncoder() { } + + internal void Reset() + { + silk_mode.Reset(); + application = 0; + channels = 0; + delay_compensation = 0; + force_channels = 0; + signal_type = 0; + user_bandwidth = 0; + max_bandwidth = 0; + user_forced_mode = 0; + voice_ratio = 0; + Fs = 0; + use_vbr = 0; + vbr_constraint = 0; + variable_duration = 0; + bitrate_bps = 0; + user_bitrate_bps = 0; + lsb_depth = 0; + encoder_buffer = 0; + lfe = 0; + analysis.Reset(); + PartialReset(); + } + + /// + /// OPUS_ENCODER_RESET_START + /// + internal void PartialReset() + { + stream_channels = 0; + hybrid_stereo_width_Q14 = 0; + variable_HP_smth2_Q15 = 0; + prev_HB_gain = 0; + Arrays.MemSetInt(hp_mem, 0, 4); + mode = 0; + prev_mode = 0; + prev_channels = 0; + prev_framesize = 0; + bandwidth = 0; + silk_bw_switch = 0; + first = 0; + energy_masking = null; + width_mem.Reset(); + Arrays.MemSetShort(delay_buffer, 0, OpusConstants.MAX_ENCODER_BUFFER * 2); + detected_bandwidth = 0; + rangeFinal = 0; + //SilkEncoder.Reset(); + //CeltEncoder.Reset(); + } + + public void ResetState() + { + EncControlState dummy = new EncControlState(); + analysis.Reset(); + PartialReset(); + + Celt_Encoder.ResetState(); + EncodeAPI.silk_InitEncoder(SilkEncoder, dummy); + stream_channels = channels; + hybrid_stereo_width_Q14 = 1 << 14; + prev_HB_gain = CeltConstants.Q15ONE; + first = 1; + mode = OpusMode.MODE_HYBRID; + bandwidth = OpusBandwidth.OPUS_BANDWIDTH_FULLBAND; + variable_HP_smth2_Q15 = Inlines.silk_LSHIFT(Inlines.silk_lin2log(TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ), 8); + } + + #endregion + + #region Encoder API functions + + /// + /// Deprecated. Just use the regular constructor + /// + public static OpusEncoder Create(int Fs, int channels, OpusApplication application) + { + return new OpusEncoder(Fs, channels, application); + } + + /// + /// Allocates and initializes an encoder state. + /// Note that regardless of the sampling rate and number channels selected, the Opus encoder + /// can switch to a lower audio bandwidth or number of channels if the bitrate + /// selected is too low. This also means that it is safe to always use 48 kHz stereo input + /// and let the encoder optimize the encoding. The decoder will not be constrained later on + /// by the mode that you select here for the encoder. + /// + /// Sampling rate of input signal (Hz). This must be one of 8000, 12000, 16000, 24000, or 48000. + /// Number of channels (1 or 2) in input signal + /// There are three coding modes: + /// + /// OPUS_APPLICATION_VOIP gives best quality at a given bitrate for voice + /// signals.It enhances the input signal by high-pass filtering and + /// emphasizing formants and harmonics.Optionally it includes in-band + /// forward error correction to protect against packet loss.Use this + /// mode for typical VoIP applications.Because of the enhancement, + /// even at high bitrates the output may sound different from the input. + /// + /// OPUS_APPLICATION_AUDIO gives best quality at a given bitrate for most + /// non-voice signals like music. Use this mode for music and mixed + /// (music/voice) content, broadcast, and applications requiring less + /// than 15 ms of coding delay. + /// + /// OPUS_APPLICATION_RESTRICTED_LOWDELAY configures low-delay mode that + /// disables the speech-optimized mode in exchange for slightly reduced delay. + /// This mode can only be set on an newly initialized or freshly reset encoder + /// because it changes the codec delay. + /// The created encoder + public OpusEncoder(int Fs, int channels, OpusApplication application) + { + int ret; + if ((Fs != 48000 && Fs != 24000 && Fs != 16000 && Fs != 12000 && Fs != 8000)) + { + throw new ArgumentException("Sample rate is invalid (must be 8/12/16/24/48 Khz)"); + } + if (channels != 1 && channels != 2) + { + throw new ArgumentException("Number of channels must be 1 or 2"); + } + + ret = this.opus_init_encoder(Fs, channels, application); + if (ret != OpusError.OPUS_OK) + { + if (ret == OpusError.OPUS_BAD_ARG) + throw new ArgumentException("OPUS_BAD_ARG when creating encoder"); + throw new OpusException("Error while initializing encoder", ret); + } + } + + internal int opus_init_encoder(int Fs, int channels, OpusApplication application) + { + SilkEncoder silk_enc; + CeltEncoder celt_enc; + int err; + int ret; + + if ((Fs != 48000 && Fs != 24000 && Fs != 16000 && Fs != 12000 && Fs != 8000) || (channels != 1 && channels != 2) || + application == OpusApplication.OPUS_APPLICATION_UNIMPLEMENTED) + return OpusError.OPUS_BAD_ARG; + + this.Reset(); + /* Create SILK encoder */ + silk_enc = this.SilkEncoder; + celt_enc = this.Celt_Encoder; + + this.stream_channels = this.channels = channels; + + this.Fs = Fs; + + ret = EncodeAPI.silk_InitEncoder(silk_enc, this.silk_mode); + if (ret != 0) return OpusError.OPUS_INTERNAL_ERROR; + + /* default SILK parameters */ + this.silk_mode.nChannelsAPI = channels; + this.silk_mode.nChannelsInternal = channels; + this.silk_mode.API_sampleRate = this.Fs; + this.silk_mode.maxInternalSampleRate = 16000; + this.silk_mode.minInternalSampleRate = 8000; + this.silk_mode.desiredInternalSampleRate = 16000; + this.silk_mode.payloadSize_ms = 20; + this.silk_mode.bitRate = 25000; + this.silk_mode.packetLossPercentage = 0; + this.silk_mode.complexity = 9; + this.silk_mode.useInBandFEC = 0; + this.silk_mode.useDTX = 0; + this.silk_mode.useCBR = 0; + this.silk_mode.reducedDependency = 0; + + /* Create CELT encoder */ + /* Initialize CELT encoder */ + err = celt_enc.celt_encoder_init(Fs, channels); + if (err != OpusError.OPUS_OK) return OpusError.OPUS_INTERNAL_ERROR; + + celt_enc.SetSignalling(0); + celt_enc.SetComplexity(this.silk_mode.complexity); + + this.use_vbr = 1; + /* Makes constrained VBR the default (safer for real-time use) */ + this.vbr_constraint = 1; + this.user_bitrate_bps = OpusConstants.OPUS_AUTO; + this.bitrate_bps = 3000 + Fs * channels; + this.application = application; + this.signal_type = OpusSignal.OPUS_SIGNAL_AUTO; + this.user_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_AUTO; + this.max_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_FULLBAND; + this.force_channels = OpusConstants.OPUS_AUTO; + this.user_forced_mode = OpusMode.MODE_AUTO; + this.voice_ratio = -1; + this.encoder_buffer = this.Fs / 100; + this.lsb_depth = 24; + this.variable_duration = OpusFramesize.OPUS_FRAMESIZE_ARG; + + /* Delay compensation of 4 ms (2.5 ms for SILK's extra look-ahead + + 1.5 ms for SILK resamplers and stereo prediction) */ + this.delay_compensation = this.Fs / 250; + + this.hybrid_stereo_width_Q14 = 1 << 14; + this.prev_HB_gain = CeltConstants.Q15ONE; + this.variable_HP_smth2_Q15 = Inlines.silk_LSHIFT(Inlines.silk_lin2log(TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ), 8); + this.first = 1; + this.mode = OpusMode.MODE_HYBRID; + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_FULLBAND; + + Analysis.tonality_analysis_init(this.analysis); + + return OpusError.OPUS_OK; + } + + + internal int user_bitrate_to_bitrate(int user_bitrate, int frame_size, int max_data_bytes) + { + if (frame_size == 0) + { + frame_size = this.Fs / 400; + } + if (user_bitrate == OpusConstants.OPUS_AUTO) + return 60 * this.Fs / frame_size + this.Fs * this.channels; + else if (user_bitrate == OpusConstants.OPUS_BITRATE_MAX) + return max_data_bytes * 8 * this.Fs / frame_size; + else + return user_bitrate; + } + + /// + /// + /// + /// The storage type of analysis_pcm, either short or float + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal int opus_encode_native(short[] pcm, int pcm_ptr, int frame_size, + byte[] data, int data_ptr, int out_data_bytes, int lsb_depth, + T[] analysis_pcm, int analysis_pcm_ptr, int analysis_size, int c1, int c2, + int analysis_channels, Downmix.downmix_func downmix, int float_api) + { + SilkEncoder silk_enc; + CeltEncoder celt_enc; + int i; + int ret = 0; + int nBytes; + EntropyCoder enc = new EntropyCoder(); // porting note: stack var + int bytes_target; + int prefill = 0; + int start_band = 0; + int redundancy = 0; + int redundancy_bytes = 0; /* Number of bytes to use for redundancy frame */ + int celt_to_silk = 0; + short[] pcm_buf; + int nb_compr_bytes; + int to_celt = 0; + uint redundant_rng = 0; + int cutoff_Hz, hp_freq_smth1; + int voice_est; /* Probability of voice in Q7 */ + int equiv_rate; + int delay_compensation; + int frame_rate; + int max_rate; /* Max bitrate we're allowed to use */ + OpusBandwidth curr_bandwidth; + int HB_gain; + int max_data_bytes; /* Max number of bytes we're allowed to use */ + int total_buffer; + int stereo_width; + CeltMode celt_mode; + AnalysisInfo analysis_info = new AnalysisInfo(); // porting note: stack var + int analysis_read_pos_bak = -1; + int analysis_read_subframe_bak = -1; + short[] tmp_prefill; + + max_data_bytes = Inlines.IMIN(1276, out_data_bytes); + + this.rangeFinal = 0; + if ((this.variable_duration == 0 && 400 * frame_size != this.Fs && 200 * frame_size != this.Fs && 100 * frame_size != this.Fs && + 50 * frame_size != this.Fs && 25 * frame_size != this.Fs && 50 * frame_size != 3 * this.Fs) + || (400 * frame_size < this.Fs) + || max_data_bytes <= 0 + ) + { + return OpusError.OPUS_BAD_ARG; + } + + silk_enc = this.SilkEncoder; + celt_enc = this.Celt_Encoder; + if (this.application == OpusApplication.OPUS_APPLICATION_RESTRICTED_LOWDELAY) + delay_compensation = 0; + else + delay_compensation = this.delay_compensation; + + lsb_depth = Inlines.IMIN(lsb_depth, this.lsb_depth); + celt_mode = celt_enc.GetMode(); + this.voice_ratio = -1; + + if (this.analysis.enabled) + { + analysis_info.valid = 0; + if ((_vqLevel.HasValue || this.silk_mode.complexity >= 7) && this.Fs == 48000) + { + analysis_read_pos_bak = this.analysis.read_pos; + analysis_read_subframe_bak = this.analysis.read_subframe; + Analysis.run_analysis(this.analysis, + celt_mode, + analysis_pcm != null ? analysis_pcm : null, + analysis_pcm_ptr, + analysis_size, + frame_size, + c1, + c2, + analysis_channels, + this.Fs, + lsb_depth, + downmix, + analysis_info); + } + + this.detected_bandwidth = 0; + if (analysis_info.valid != 0) + { + int analysis_bandwidth; + if (this.signal_type == OpusSignal.OPUS_SIGNAL_AUTO) + this.voice_ratio = (int)Math.Floor(.5f + 100 * (1 - analysis_info.music_prob)); + + analysis_bandwidth = analysis_info.bandwidth; + if (analysis_bandwidth <= 12) + this.detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND; + else if (analysis_bandwidth <= 14) + this.detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND; + else if (analysis_bandwidth <= 16) + this.detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND; + else if (analysis_bandwidth <= 18) + this.detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND; + else + this.detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_FULLBAND; + + // If we are in constant-quality encode mode, use the analysis results to set the variable bitrate + if (_vqLevel.HasValue) + { + this.user_bitrate_bps = GetVariableQualityBitrate(_vqLevel.Value, analysis.music_prob); + } + } + } + + if (this.channels == 2 && this.force_channels != 1) + stereo_width = CodecHelpers.compute_stereo_width(pcm, pcm_ptr, frame_size, this.Fs, this.width_mem); + else + stereo_width = 0; + total_buffer = delay_compensation; + this.bitrate_bps = user_bitrate_to_bitrate(this.user_bitrate_bps, frame_size, max_data_bytes); + + frame_rate = this.Fs / frame_size; + if (this.use_vbr == 0) + { + int cbrBytes; + /* Multiply by 3 to make sure the division is exact. */ + int frame_rate3 = 3 * this.Fs / frame_size; + /* We need to make sure that "int" values always fit in 16 bits. */ + cbrBytes = Inlines.IMIN((3 * this.bitrate_bps / 8 + frame_rate3 / 2) / frame_rate3, max_data_bytes); + this.bitrate_bps = cbrBytes * (int)frame_rate3 * 8 / 3; + max_data_bytes = cbrBytes; + } + if (max_data_bytes < 3 || this.bitrate_bps < 3 * frame_rate * 8 + || (frame_rate < 50 && (max_data_bytes * frame_rate < 300 || this.bitrate_bps < 2400))) + { + /*If the space is too low to do something useful, emit 'PLC' frames.*/ + OpusMode tocmode = this.mode; + OpusBandwidth bw = this.bandwidth == 0 ? OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND : this.bandwidth; + if (tocmode == 0) + tocmode = OpusMode.MODE_SILK_ONLY; + if (frame_rate > 100) + tocmode = OpusMode.MODE_CELT_ONLY; + if (frame_rate < 50) + tocmode = OpusMode.MODE_SILK_ONLY; + if (tocmode == OpusMode.MODE_SILK_ONLY && bw > OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND) + bw = OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND; + else if (tocmode == OpusMode.MODE_CELT_ONLY && bw == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) + bw = OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND; + else if (tocmode == OpusMode.MODE_HYBRID && bw <= OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND) + bw = OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND; + data[data_ptr] = CodecHelpers.gen_toc(tocmode, frame_rate, bw, this.stream_channels); + ret = 1; + if (this.use_vbr == 0) + { + ret = OpusRepacketizer.PadPacket(data, data_ptr, ret, max_data_bytes); + if (ret == OpusError.OPUS_OK) + ret = max_data_bytes; + } + return ret; + } + max_rate = frame_rate * max_data_bytes * 8; + + /* Equivalent 20-ms rate for mode/channel/bandwidth decisions */ + equiv_rate = this.bitrate_bps - (40 * this.channels + 20) * (this.Fs / frame_size - 50); + + if (this.signal_type == OpusSignal.OPUS_SIGNAL_VOICE) + voice_est = 127; + else if (this.signal_type == OpusSignal.OPUS_SIGNAL_MUSIC) + voice_est = 0; + else if (this.voice_ratio >= 0) + { + voice_est = this.voice_ratio * 327 >> 8; + /* For AUDIO, never be more than 90% confident of having speech */ + if (this.application == OpusApplication.OPUS_APPLICATION_AUDIO) + voice_est = Inlines.IMIN(voice_est, 115); + } + else if (this.application == OpusApplication.OPUS_APPLICATION_VOIP) + voice_est = 115; + else + voice_est = 48; + + if (this.force_channels != OpusConstants.OPUS_AUTO && this.channels == 2) + { + this.stream_channels = this.force_channels; + } + else { +#if FUZZING + /* Random mono/stereo decision */ + if (st.channels == 2 && (new Random().Next() & 0x1F) == 0) + st.stream_channels = 3 - st.stream_channels; +#else + /* Rate-dependent mono-stereo decision */ + if (this.channels == 2) + { + int stereo_threshold; + stereo_threshold = Tables.stereo_music_threshold + ((voice_est * voice_est * (Tables.stereo_voice_threshold - Tables.stereo_music_threshold)) >> 14); + if (this.stream_channels == 2) + stereo_threshold -= 1000; + else + stereo_threshold += 1000; + this.stream_channels = (equiv_rate > stereo_threshold) ? 2 : 1; + } + else { + this.stream_channels = this.channels; + } +#endif + } + equiv_rate = this.bitrate_bps - (40 * this.stream_channels + 20) * (this.Fs / frame_size - 50); + + /* Mode selection depending on application and signal type */ + if (this.application == OpusApplication.OPUS_APPLICATION_RESTRICTED_LOWDELAY) + { + this.mode = OpusMode.MODE_CELT_ONLY; + } + else if (this.user_forced_mode == OpusMode.MODE_AUTO) + { +#if FUZZING + /* Random mode switching */ + if ((new Random().Next() & 0xF) == 0) + { + if ((new Random().Next() & 0x1) == 0) + st.mode = OpusMode.MODE_CELT_ONLY; + else + st.mode = OpusMode.MODE_SILK_ONLY; + } + else { + if (st.prev_mode == OpusMode.MODE_CELT_ONLY) + st.mode = OpusMode.MODE_CELT_ONLY; + else + st.mode = OpusMode.MODE_SILK_ONLY; + } +#else + int mode_voice, mode_music; + int threshold; + + /* Interpolate based on stereo width */ + mode_voice = (int)(Inlines.MULT16_32_Q15(CeltConstants.Q15ONE - stereo_width, Tables.mode_thresholds[0][0]) + + Inlines.MULT16_32_Q15(stereo_width, Tables.mode_thresholds[1][0])); + mode_music = (int)(Inlines.MULT16_32_Q15(CeltConstants.Q15ONE - stereo_width, Tables.mode_thresholds[1][1]) + + Inlines.MULT16_32_Q15(stereo_width, Tables.mode_thresholds[1][1])); + /* Interpolate based on speech/music probability */ + threshold = mode_music + ((voice_est * voice_est * (mode_voice - mode_music)) >> 14); + /* Bias towards SILK for VoIP because of some useful features */ + if (this.application == OpusApplication.OPUS_APPLICATION_VOIP) + threshold += 8000; + + /*printf("%f %d\n", stereo_width/(float)1.0f, threshold);*/ + /* Hysteresis */ + if (this.prev_mode == OpusMode.MODE_CELT_ONLY) + threshold -= 4000; + else if (this.prev_mode > 0) + threshold += 4000; + + this.mode = (equiv_rate >= threshold) ? OpusMode.MODE_CELT_ONLY : OpusMode.MODE_SILK_ONLY; + + /* When FEC is enabled and there's enough packet loss, use SILK */ + if (this.silk_mode.useInBandFEC != 0 && this.silk_mode.packetLossPercentage > (128 - voice_est) >> 4) + this.mode = OpusMode.MODE_SILK_ONLY; + /* When encoding voice and DTX is enabled, set the encoder to SILK mode (at least for now) */ + if (this.silk_mode.useDTX != 0 && voice_est > 100) + this.mode = OpusMode.MODE_SILK_ONLY; +#endif + } + else { + this.mode = this.user_forced_mode; + } + + /* Override the chosen mode to make sure we meet the requested frame size */ + if (this.mode != OpusMode.MODE_CELT_ONLY && frame_size < this.Fs / 100) + this.mode = OpusMode.MODE_CELT_ONLY; + if (this.lfe != 0) + this.mode = OpusMode.MODE_CELT_ONLY; + /* If max_data_bytes represents less than 8 kb/s, switch to CELT-only mode */ + if (max_data_bytes < (frame_rate > 50 ? 12000 : 8000) * frame_size / (this.Fs * 8)) + this.mode = OpusMode.MODE_CELT_ONLY; + + if (this.stream_channels == 1 && this.prev_channels == 2 && this.silk_mode.toMono == 0 + && this.mode != OpusMode.MODE_CELT_ONLY && this.prev_mode != OpusMode.MODE_CELT_ONLY) + { + /* Delay stereo.mono transition by two frames so that SILK can do a smooth downmix */ + this.silk_mode.toMono = 1; + this.stream_channels = 2; + } + else { + this.silk_mode.toMono = 0; + } + + if (this.prev_mode > 0 && + ((this.mode != OpusMode.MODE_CELT_ONLY && this.prev_mode == OpusMode.MODE_CELT_ONLY) || + (this.mode == OpusMode.MODE_CELT_ONLY && this.prev_mode != OpusMode.MODE_CELT_ONLY))) + { + redundancy = 1; + celt_to_silk = (this.mode != OpusMode.MODE_CELT_ONLY) ? 1 : 0; + if (celt_to_silk == 0) + { + /* Switch to SILK/hybrid if frame size is 10 ms or more*/ + if (frame_size >= this.Fs / 100) + { + this.mode = this.prev_mode; + to_celt = 1; + } + else { + redundancy = 0; + } + } + } + /* For the first frame at a new SILK bandwidth */ + if (this.silk_bw_switch != 0) + { + redundancy = 1; + celt_to_silk = 1; + this.silk_bw_switch = 0; + prefill = 1; + } + + if (redundancy != 0) + { + /* Fair share of the max size allowed */ + redundancy_bytes = Inlines.IMIN(257, max_data_bytes * (int)(this.Fs / 200) / (frame_size + this.Fs / 200)); + /* For VBR, target the actual bitrate (subject to the limit above) */ + if (this.use_vbr != 0) + redundancy_bytes = Inlines.IMIN(redundancy_bytes, this.bitrate_bps / 1600); + } + + if (this.mode != OpusMode.MODE_CELT_ONLY && this.prev_mode == OpusMode.MODE_CELT_ONLY) + { + EncControlState dummy = new EncControlState(); + EncodeAPI.silk_InitEncoder(silk_enc, dummy); + prefill = 1; + } + + /* Automatic (rate-dependent) bandwidth selection */ + if (this.mode == OpusMode.MODE_CELT_ONLY || this.first != 0 || this.silk_mode.allowBandwidthSwitch != 0) + { + int[] voice_bandwidth_thresholds; + int[] music_bandwidth_thresholds; + int[] bandwidth_thresholds = new int[8]; + OpusBandwidth bandwidth = OpusBandwidth.OPUS_BANDWIDTH_FULLBAND; + int equiv_rate2; + + equiv_rate2 = equiv_rate; + if (this.mode != OpusMode.MODE_CELT_ONLY) + { + /* Adjust the threshold +/- 10% depending on complexity */ + equiv_rate2 = equiv_rate2 * (45 + this.silk_mode.complexity) / 50; + /* CBR is less efficient by ~1 kb/s */ + if (this.use_vbr == 0) + equiv_rate2 -= 1000; + } + if (this.channels == 2 && this.force_channels != 1) + { + voice_bandwidth_thresholds = Tables.stereo_voice_bandwidth_thresholds; + music_bandwidth_thresholds = Tables.stereo_music_bandwidth_thresholds; + } + else { + voice_bandwidth_thresholds = Tables.mono_voice_bandwidth_thresholds; + music_bandwidth_thresholds = Tables.mono_music_bandwidth_thresholds; + } + /* Interpolate bandwidth thresholds depending on voice estimation */ + for (i = 0; i < 8; i++) + { + bandwidth_thresholds[i] = music_bandwidth_thresholds[i] + + ((voice_est * voice_est * (voice_bandwidth_thresholds[i] - music_bandwidth_thresholds[i])) >> 14); + } + do + { + int threshold, hysteresis; + threshold = bandwidth_thresholds[2 * (bandwidth - OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND)]; + hysteresis = bandwidth_thresholds[2 * (bandwidth - OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) + 1]; + if (this.first == 0) + { + if (this.bandwidth >= bandwidth) + threshold -= hysteresis; + else + threshold += hysteresis; + } + if (equiv_rate2 >= threshold) + break; + } while (--bandwidth > OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND); + this.bandwidth = bandwidth; + /* Prevents any transition to SWB/FB until the SILK layer has fully + switched to WB mode and turned the variable LP filter off */ + if (this.first == 0 && this.mode != OpusMode.MODE_CELT_ONLY && this.silk_mode.inWBmodeWithoutVariableLP == 0 && this.bandwidth > OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND) + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND; + } + + if (this.bandwidth > this.max_bandwidth) + this.bandwidth = this.max_bandwidth; + + if (this.user_bandwidth != OpusBandwidth.OPUS_BANDWIDTH_AUTO) + this.bandwidth = this.user_bandwidth; + + /* This prevents us from using hybrid at unsafe CBR/max rates */ + if (this.mode != OpusMode.MODE_CELT_ONLY && max_rate < 15000) + { + this.bandwidth = OpusBandwidthHelpers.MIN(this.bandwidth, OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND); + } + + /* Prevents Opus from wasting bits on frequencies that are above + the Nyquist rate of the input signal */ + if (this.Fs <= 24000 && this.bandwidth > OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND) + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND; + if (this.Fs <= 16000 && this.bandwidth > OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND) + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND; + if (this.Fs <= 12000 && this.bandwidth > OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND; + if (this.Fs <= 8000 && this.bandwidth > OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND) + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND; + /* Use detected bandwidth to reduce the encoded bandwidth. */ + if (this.detected_bandwidth != 0 && this.user_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_AUTO) + { + OpusBandwidth min_detected_bandwidth; + /* Makes bandwidth detection more conservative just in case the detector + gets it wrong when we could have coded a high bandwidth transparently. + When operating in SILK/hybrid mode, we don't go below wideband to avoid + more complicated switches that require redundancy. */ + if (equiv_rate <= 18000 * this.stream_channels && this.mode == OpusMode.MODE_CELT_ONLY) + min_detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND; + else if (equiv_rate <= 24000 * this.stream_channels && this.mode == OpusMode.MODE_CELT_ONLY) + min_detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND; + else if (equiv_rate <= 30000 * this.stream_channels) + min_detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND; + else if (equiv_rate <= 44000 * this.stream_channels) + min_detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND; + else + min_detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_FULLBAND; + + this.detected_bandwidth = OpusBandwidthHelpers.MAX(this.detected_bandwidth, min_detected_bandwidth); + this.bandwidth = OpusBandwidthHelpers.MIN(this.bandwidth, this.detected_bandwidth); + } + celt_enc.SetLSBDepth(lsb_depth); + + /* CELT mode doesn't support mediumband, use wideband instead */ + if (this.mode == OpusMode.MODE_CELT_ONLY && this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND; + if (this.lfe != 0) + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND; + + /* Can't support higher than wideband for >20 ms frames */ + if (frame_size > this.Fs / 50 && (this.mode == OpusMode.MODE_CELT_ONLY || this.bandwidth > OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND)) + { + byte[] tmp_data; + int nb_frames; + OpusBandwidth bak_bandwidth; + int bak_channels, bak_to_mono; + OpusMode bak_mode; + OpusRepacketizer rp; + int bytes_per_frame; + int repacketize_len; + + if (this.analysis.enabled && analysis_read_pos_bak != -1) + { + this.analysis.read_pos = analysis_read_pos_bak; + this.analysis.read_subframe = analysis_read_subframe_bak; + } + + nb_frames = frame_size > this.Fs / 25 ? 3 : 2; + bytes_per_frame = Inlines.IMIN(1276, (out_data_bytes - 3) / nb_frames); + + tmp_data = new byte[nb_frames * bytes_per_frame]; + + rp = new OpusRepacketizer(); + + bak_mode = this.user_forced_mode; + bak_bandwidth = this.user_bandwidth; + bak_channels = this.force_channels; + + this.user_forced_mode = this.mode; + this.user_bandwidth = this.bandwidth; + this.force_channels = this.stream_channels; + bak_to_mono = this.silk_mode.toMono; + + if (bak_to_mono != 0) + this.force_channels = 1; + else + this.prev_channels = this.stream_channels; + for (i = 0; i < nb_frames; i++) + { + int tmp_len; + this.silk_mode.toMono = 0; + /* When switching from SILK/Hybrid to CELT, only ask for a switch at the last frame */ + if (to_celt != 0 && i == nb_frames - 1) + this.user_forced_mode = OpusMode.MODE_CELT_ONLY; + tmp_len = opus_encode_native(pcm, pcm_ptr + (i * (this.channels * this.Fs / 50)), this.Fs / 50, + tmp_data, i * bytes_per_frame, bytes_per_frame, lsb_depth, + null, 0, 0, c1, c2, analysis_channels, downmix, float_api); + if (tmp_len < 0) + { + + return OpusError.OPUS_INTERNAL_ERROR; + } + ret = rp.AddPacket(tmp_data, i * bytes_per_frame, tmp_len); + if (ret < 0) + { + + return OpusError.OPUS_INTERNAL_ERROR; + } + } + if (this.use_vbr != 0) + repacketize_len = out_data_bytes; + else + repacketize_len = Inlines.IMIN(3 * this.bitrate_bps / (3 * 8 * 50 / nb_frames), out_data_bytes); + ret = rp.opus_repacketizer_out_range_impl(0, nb_frames, data, data_ptr, repacketize_len, 0, (this.use_vbr == 0) ? 1 : 0); + if (ret < 0) + { + return OpusError.OPUS_INTERNAL_ERROR; + } + this.user_forced_mode = bak_mode; + this.user_bandwidth = bak_bandwidth; + this.force_channels = bak_channels; + this.silk_mode.toMono = bak_to_mono; + + return ret; + } + curr_bandwidth = this.bandwidth; + + /* Chooses the appropriate mode for speech + *NEVER* switch to/from CELT-only mode here as this will invalidate some assumptions */ + if (this.mode == OpusMode.MODE_SILK_ONLY && curr_bandwidth > OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND) + this.mode = OpusMode.MODE_HYBRID; + if (this.mode == OpusMode.MODE_HYBRID && curr_bandwidth <= OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND) + this.mode = OpusMode.MODE_SILK_ONLY; + + /* printf("%d %d %d %d\n", st.bitrate_bps, st.stream_channels, st.mode, curr_bandwidth); */ + bytes_target = Inlines.IMIN(max_data_bytes - redundancy_bytes, this.bitrate_bps * frame_size / (this.Fs * 8)) - 1; + + data_ptr += 1; + + enc.enc_init(data, data_ptr, (uint)(max_data_bytes - 1)); + + pcm_buf = new short[(total_buffer + frame_size) * this.channels]; + Array.Copy(this.delay_buffer, ((this.encoder_buffer - total_buffer) * this.channels), pcm_buf, 0, total_buffer * this.channels); + + if (this.mode == OpusMode.MODE_CELT_ONLY) + hp_freq_smth1 = Inlines.silk_LSHIFT(Inlines.silk_lin2log(TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ), 8); + else + hp_freq_smth1 = silk_enc.state_Fxx[0].variable_HP_smth1_Q15; + + this.variable_HP_smth2_Q15 = Inlines.silk_SMLAWB(this.variable_HP_smth2_Q15, + hp_freq_smth1 - this.variable_HP_smth2_Q15, ((int)((TuningParameters.VARIABLE_HP_SMTH_COEF2) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.VARIABLE_HP_SMTH_COEF2, 16)*/); + + /* convert from log scale to Hertz */ + cutoff_Hz = Inlines.silk_log2lin(Inlines.silk_RSHIFT(this.variable_HP_smth2_Q15, 8)); + + if (this.application == OpusApplication.OPUS_APPLICATION_VOIP) + { + CodecHelpers.hp_cutoff(pcm, pcm_ptr, cutoff_Hz, pcm_buf, (total_buffer * this.channels), this.hp_mem, frame_size, this.channels, this.Fs); + } + else { + CodecHelpers.dc_reject(pcm, pcm_ptr, 3, pcm_buf, total_buffer * this.channels, this.hp_mem, frame_size, this.channels, this.Fs); + } + + /* SILK processing */ + HB_gain = CeltConstants.Q15ONE; + if (this.mode != OpusMode.MODE_CELT_ONLY) + { + int total_bitRate, celt_rate; + short[] pcm_silk = new short[this.channels * frame_size]; + + /* Distribute bits between SILK and CELT */ + total_bitRate = 8 * bytes_target * frame_rate; + if (this.mode == OpusMode.MODE_HYBRID) + { + int HB_gain_ref; + /* Base rate for SILK */ + this.silk_mode.bitRate = this.stream_channels * (5000 + ((this.Fs == 100 * frame_size) ? 1000 : 0)); + if (curr_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND) + { + /* SILK gets 2/3 of the remaining bits */ + this.silk_mode.bitRate += (total_bitRate - this.silk_mode.bitRate) * 2 / 3; + } + else { /* FULLBAND */ + /* SILK gets 3/5 of the remaining bits */ + this.silk_mode.bitRate += (total_bitRate - this.silk_mode.bitRate) * 3 / 5; + } + /* Don't let SILK use more than 80% */ + if (this.silk_mode.bitRate > total_bitRate * 4 / 5) + { + this.silk_mode.bitRate = total_bitRate * 4 / 5; + } + if (this.energy_masking == null) + { + /* Increasingly attenuate high band when it gets allocated fewer bits */ + celt_rate = total_bitRate - this.silk_mode.bitRate; + HB_gain_ref = (curr_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND) ? 3000 : 3600; + HB_gain = Inlines.SHL32((int)celt_rate, 9) / Inlines.SHR32((int)celt_rate + this.stream_channels * HB_gain_ref, 6); + HB_gain = HB_gain < CeltConstants.Q15ONE * 6 / 7 ? HB_gain + CeltConstants.Q15ONE / 7 : CeltConstants.Q15ONE; + } + } + else { + /* SILK gets all bits */ + this.silk_mode.bitRate = total_bitRate; + } + + /* Surround masking for SILK */ + if (this.energy_masking != null && this.use_vbr != 0 && this.lfe == 0) + { + int mask_sum = 0; + int masking_depth; + int rate_offset; + int c; + int end = 17; + short srate = 16000; + if (this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND) + { + end = 13; + srate = 8000; + } + else if (this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) + { + end = 15; + srate = 12000; + } + for (c = 0; c < this.channels; c++) + { + for (i = 0; i < end; i++) + { + int mask; + mask = Inlines.MAX16(Inlines.MIN16(this.energy_masking[21 * c + i], + ((short)(0.5 + (.5f) * (((int)1) << (10))))/*Inlines.QCONST16(.5f, 10)*/), -((short)(0.5 + (2.0f) * (((int)1) << (10))))/*Inlines.QCONST16(2.0f, 10)*/); + if (mask > 0) + mask = Inlines.HALF16(mask); + mask_sum += mask; + } + } + /* Conservative rate reduction, we cut the masking in half */ + masking_depth = mask_sum / end * this.channels; + masking_depth += ((short)(0.5 + (.2f) * (((int)1) << (10))))/*Inlines.QCONST16(.2f, 10)*/; + rate_offset = (int)Inlines.PSHR32(Inlines.MULT16_16(srate, masking_depth), 10); + rate_offset = Inlines.MAX32(rate_offset, -2 * this.silk_mode.bitRate / 3); + /* Split the rate change between the SILK and CELT part for hybrid. */ + if (this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND || this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_FULLBAND) + this.silk_mode.bitRate += 3 * rate_offset / 5; + else + this.silk_mode.bitRate += rate_offset; + bytes_target += rate_offset * frame_size / (8 * this.Fs); + } + + this.silk_mode.payloadSize_ms = 1000 * frame_size / this.Fs; + this.silk_mode.nChannelsAPI = this.channels; + this.silk_mode.nChannelsInternal = this.stream_channels; + if (curr_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND) + { + this.silk_mode.desiredInternalSampleRate = 8000; + } + else if (curr_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) + { + this.silk_mode.desiredInternalSampleRate = 12000; + } + else { + Inlines.OpusAssert(this.mode == OpusMode.MODE_HYBRID || curr_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND); + this.silk_mode.desiredInternalSampleRate = 16000; + } + if (this.mode == OpusMode.MODE_HYBRID) + { + /* Don't allow bandwidth reduction at lowest bitrates in hybrid mode */ + this.silk_mode.minInternalSampleRate = 16000; + } + else { + this.silk_mode.minInternalSampleRate = 8000; + } + + if (this.mode == OpusMode.MODE_SILK_ONLY) + { + int effective_max_rate = max_rate; + this.silk_mode.maxInternalSampleRate = 16000; + if (frame_rate > 50) + effective_max_rate = effective_max_rate * 2 / 3; + if (effective_max_rate < 13000) + { + this.silk_mode.maxInternalSampleRate = 12000; + this.silk_mode.desiredInternalSampleRate = Inlines.IMIN(12000, this.silk_mode.desiredInternalSampleRate); + } + if (effective_max_rate < 9600) + { + this.silk_mode.maxInternalSampleRate = 8000; + this.silk_mode.desiredInternalSampleRate = Inlines.IMIN(8000, this.silk_mode.desiredInternalSampleRate); + } + } + else { + this.silk_mode.maxInternalSampleRate = 16000; + } + + this.silk_mode.useCBR = this.use_vbr == 0 ? 1 : 0; + + /* Call SILK encoder for the low band */ + nBytes = Inlines.IMIN(1275, max_data_bytes - 1 - redundancy_bytes); + + this.silk_mode.maxBits = nBytes * 8; + /* Only allow up to 90% of the bits for hybrid mode*/ + if (this.mode == OpusMode.MODE_HYBRID) + this.silk_mode.maxBits = (int)this.silk_mode.maxBits * 9 / 10; + if (this.silk_mode.useCBR != 0) + { + this.silk_mode.maxBits = (this.silk_mode.bitRate * frame_size / (this.Fs * 8)) * 8; + /* Reduce the initial target to make it easier to reach the CBR rate */ + this.silk_mode.bitRate = Inlines.IMAX(1, this.silk_mode.bitRate - 2000); + } + + if (prefill != 0) + { + BoxedValueInt zero = new BoxedValueInt(0); + int prefill_offset; + + /* Use a smooth onset for the SILK prefill to avoid the encoder trying to encode + a discontinuity. The exact location is what we need to avoid leaving any "gap" + in the audio when mixing with the redundant CELT frame. Here we can afford to + overwrite st.delay_buffer because the only thing that uses it before it gets + rewritten is tmp_prefill[] and even then only the part after the ramp really + gets used (rather than sent to the encoder and discarded) */ + prefill_offset = this.channels * (this.encoder_buffer - this.delay_compensation - this.Fs / 400); + CodecHelpers.gain_fade(this.delay_buffer, prefill_offset, + 0, CeltConstants.Q15ONE, celt_mode.overlap, this.Fs / 400, this.channels, celt_mode.window, this.Fs); + Arrays.MemSetShort(this.delay_buffer, 0, prefill_offset); + Array.Copy(this.delay_buffer, 0, pcm_silk, 0, this.encoder_buffer * this.channels); + + EncodeAPI.silk_Encode(silk_enc, this.silk_mode, pcm_silk, this.encoder_buffer, null, zero, 1); + } + + Array.Copy(pcm_buf, total_buffer * this.channels, pcm_silk, 0, frame_size * this.channels); + + BoxedValueInt boxed_silkBytes = new BoxedValueInt(nBytes); + ret = EncodeAPI.silk_Encode(silk_enc, this.silk_mode, pcm_silk, frame_size, enc, boxed_silkBytes, 0); + nBytes = boxed_silkBytes.Val; + + if (ret != 0) + { + /*fprintf (stderr, "SILK encode error: %d\n", ret);*/ + /* Handle error */ + + return OpusError.OPUS_INTERNAL_ERROR; + } + if (nBytes == 0) + { + this.rangeFinal = 0; + data[data_ptr - 1] = CodecHelpers.gen_toc(this.mode, this.Fs / frame_size, curr_bandwidth, this.stream_channels); + + return 1; + } + /* Extract SILK public bandwidth for signaling in first byte */ + if (this.mode == OpusMode.MODE_SILK_ONLY) + { + if (this.silk_mode.internalSampleRate == 8000) + { + curr_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND; + } + else if (this.silk_mode.internalSampleRate == 12000) + { + curr_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND; + } + else if (this.silk_mode.internalSampleRate == 16000) + { + curr_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND; + } + } + else + { + Inlines.OpusAssert(this.silk_mode.internalSampleRate == 16000); + } + + this.silk_mode.opusCanSwitch = this.silk_mode.switchReady; + if (this.silk_mode.opusCanSwitch != 0) + { + redundancy = 1; + celt_to_silk = 0; + this.silk_bw_switch = 1; + } + } + + /* CELT processing */ + { + int endband = 21; + + switch (curr_bandwidth) + { + case OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND: + endband = 13; + break; + case OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND: + case OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND: + endband = 17; + break; + case OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND: + endband = 19; + break; + case OpusBandwidth.OPUS_BANDWIDTH_FULLBAND: + endband = 21; + break; + } + celt_enc.SetEndBand(endband); + celt_enc.SetChannels(this.stream_channels); + } + celt_enc.SetBitrate(OpusConstants.OPUS_BITRATE_MAX); + if (this.mode != OpusMode.MODE_SILK_ONLY) + { + int celt_pred = 2; + celt_enc.SetVBR(false); + /* We may still decide to disable prediction later */ + if (this.silk_mode.reducedDependency != 0) + celt_pred = 0; + celt_enc.SetPrediction(celt_pred); + + if (this.mode == OpusMode.MODE_HYBRID) + { + int len; + + len = (enc.tell() + 7) >> 3; + if (redundancy != 0) + len += this.mode == OpusMode.MODE_HYBRID ? 3 : 1; + if (this.use_vbr != 0) + { + nb_compr_bytes = len + bytes_target - (this.silk_mode.bitRate * frame_size) / (8 * this.Fs); + } + else { + /* check if SILK used up too much */ + nb_compr_bytes = len > bytes_target ? len : bytes_target; + } + } + else { + if (this.use_vbr != 0) + { + int bonus = 0; + if (this.analysis.enabled && this.variable_duration == OpusFramesize.OPUS_FRAMESIZE_VARIABLE && frame_size != this.Fs / 50) + { + bonus = (60 * this.stream_channels + 40) * (this.Fs / frame_size - 50); + if (analysis_info.valid != 0) + bonus = (int)(bonus * (1.0f + .5f * analysis_info.tonality)); + } + celt_enc.SetVBR(true); + celt_enc.SetVBRConstraint(this.vbr_constraint != 0); + celt_enc.SetBitrate(this.bitrate_bps + bonus); + nb_compr_bytes = max_data_bytes - 1 - redundancy_bytes; + } + else { + nb_compr_bytes = bytes_target; + } + } + + } + else { + nb_compr_bytes = 0; + } + + + tmp_prefill = new short[this.channels * this.Fs / 400]; + if (this.mode != OpusMode.MODE_SILK_ONLY && this.mode != this.prev_mode && this.prev_mode > 0) + { + Array.Copy(this.delay_buffer, ((this.encoder_buffer - total_buffer - this.Fs / 400) * this.channels), tmp_prefill, 0, this.channels * this.Fs / 400); + } + + if (this.channels * (this.encoder_buffer - (frame_size + total_buffer)) > 0) + { + Arrays.MemMoveShort(this.delay_buffer, this.channels * frame_size, 0, this.channels * (this.encoder_buffer - frame_size - total_buffer)); + Array.Copy(pcm_buf, 0, this.delay_buffer, (this.channels * (this.encoder_buffer - frame_size - total_buffer)), (frame_size + total_buffer) * this.channels); + } + else + { + Array.Copy(pcm_buf, (frame_size + total_buffer - this.encoder_buffer) * this.channels, this.delay_buffer, 0, this.encoder_buffer * this.channels); + } + + /* gain_fade() and stereo_fade() need to be after the buffer copying + because we don't want any of this to affect the SILK part */ + if (this.prev_HB_gain < CeltConstants.Q15ONE || HB_gain < CeltConstants.Q15ONE) + { + CodecHelpers.gain_fade(pcm_buf, 0, + this.prev_HB_gain, HB_gain, celt_mode.overlap, frame_size, this.channels, celt_mode.window, this.Fs); + } + + this.prev_HB_gain = HB_gain; + if (this.mode != OpusMode.MODE_HYBRID || this.stream_channels == 1) + this.silk_mode.stereoWidth_Q14 = Inlines.IMIN((1 << 14), 2 * Inlines.IMAX(0, equiv_rate - 30000)); + if (this.energy_masking == null && this.channels == 2) + { + /* Apply stereo width reduction (at low bitrates) */ + if (this.hybrid_stereo_width_Q14 < (1 << 14) || this.silk_mode.stereoWidth_Q14 < (1 << 14)) + { + int g1, g2; + g1 = this.hybrid_stereo_width_Q14; + g2 = (int)(this.silk_mode.stereoWidth_Q14); + g1 = g1 == 16384 ? CeltConstants.Q15ONE : Inlines.SHL16(g1, 1); + g2 = g2 == 16384 ? CeltConstants.Q15ONE : Inlines.SHL16(g2, 1); + CodecHelpers.stereo_fade(pcm_buf, g1, g2, celt_mode.overlap, + frame_size, this.channels, celt_mode.window, this.Fs); + this.hybrid_stereo_width_Q14 = (short)(this.silk_mode.stereoWidth_Q14); + } + } + + if (this.mode != OpusMode.MODE_CELT_ONLY && enc.tell() + 17 + 20 * ((this.mode == OpusMode.MODE_HYBRID) ? 1 : 0) <= 8 * (max_data_bytes - 1)) + { + /* For SILK mode, the redundancy is inferred from the length */ + if (this.mode == OpusMode.MODE_HYBRID && (redundancy != 0 || enc.tell() + 37 <= 8 * nb_compr_bytes)) + enc.enc_bit_logp(redundancy, 12); + if (redundancy != 0) + { + int max_redundancy; + enc.enc_bit_logp(celt_to_silk, 1); + if (this.mode == OpusMode.MODE_HYBRID) + max_redundancy = (max_data_bytes - 1) - nb_compr_bytes; + else + max_redundancy = (max_data_bytes - 1) - ((enc.tell() + 7) >> 3); + /* Target the same bit-rate for redundancy as for the rest, + up to a max of 257 bytes */ + redundancy_bytes = Inlines.IMIN(max_redundancy, this.bitrate_bps / 1600); + redundancy_bytes = Inlines.IMIN(257, Inlines.IMAX(2, redundancy_bytes)); + if (this.mode == OpusMode.MODE_HYBRID) + enc.enc_uint((uint)(redundancy_bytes - 2), 256); + } + } + else { + redundancy = 0; + } + + if (redundancy == 0) + { + this.silk_bw_switch = 0; + redundancy_bytes = 0; + } + if (this.mode != OpusMode.MODE_CELT_ONLY) start_band = 17; + + if (this.mode == OpusMode.MODE_SILK_ONLY) + { + ret = (enc.tell() + 7) >> 3; + enc.enc_done(); + nb_compr_bytes = ret; + } + else { + nb_compr_bytes = Inlines.IMIN((max_data_bytes - 1) - redundancy_bytes, nb_compr_bytes); + enc.enc_shrink((uint)nb_compr_bytes); + } + + if (this.analysis.enabled && redundancy != 0 || this.mode != OpusMode.MODE_SILK_ONLY) + { + celt_enc.SetAnalysis(analysis_info); + } + /* 5 ms redundant frame for CELT->SILK */ + if (redundancy != 0 && celt_to_silk != 0) + { + int err; + celt_enc.SetStartBand(0); + celt_enc.SetVBR(false); + err = celt_enc.celt_encode_with_ec(pcm_buf, 0, this.Fs / 200, data, data_ptr + nb_compr_bytes, redundancy_bytes, null); + if (err < 0) + { + return OpusError.OPUS_INTERNAL_ERROR; + } + redundant_rng = celt_enc.GetFinalRange(); + celt_enc.ResetState(); + } + + celt_enc.SetStartBand(start_band); + + if (this.mode != OpusMode.MODE_SILK_ONLY) + { + if (this.mode != this.prev_mode && this.prev_mode > 0) + { + byte[] dummy = new byte[2]; + celt_enc.ResetState(); + + /* Prefilling */ + celt_enc.celt_encode_with_ec(tmp_prefill, 0, this.Fs / 400, dummy, 0, 2, null); + celt_enc.SetPrediction(0); + } + /* If false, we already busted the budget and we'll end up with a "PLC packet" */ + if (enc.tell() <= 8 * nb_compr_bytes) + { + ret = celt_enc.celt_encode_with_ec(pcm_buf, 0, frame_size, null, 0, nb_compr_bytes, enc); + if (ret < 0) + { + return OpusError.OPUS_INTERNAL_ERROR; + } + } + } + + /* 5 ms redundant frame for SILK->CELT */ + if (redundancy != 0 && celt_to_silk == 0) + { + int err; + byte[] dummy = new byte[2]; + int N2, N4; + N2 = this.Fs / 200; + N4 = this.Fs / 400; + + celt_enc.ResetState(); + celt_enc.SetStartBand(0); + celt_enc.SetPrediction(0); + + /* NOTE: We could speed this up slightly (at the expense of code size) by just adding a function that prefills the buffer */ + celt_enc.celt_encode_with_ec(pcm_buf, (this.channels * (frame_size - N2 - N4)), N4, dummy, 0, 2, null); + + err = celt_enc.celt_encode_with_ec(pcm_buf, (this.channels * (frame_size - N2)), N2, data, data_ptr + nb_compr_bytes, redundancy_bytes, null); + if (err < 0) + { + return OpusError.OPUS_INTERNAL_ERROR; + } + redundant_rng = celt_enc.GetFinalRange(); + } + + /* Signalling the mode in the first byte */ + data_ptr -= 1; + data[data_ptr] = CodecHelpers.gen_toc(this.mode, this.Fs / frame_size, curr_bandwidth, this.stream_channels); + + this.rangeFinal = enc.rng ^ redundant_rng; + + if (to_celt != 0) + this.prev_mode = OpusMode.MODE_CELT_ONLY; + else + this.prev_mode = this.mode; + this.prev_channels = this.stream_channels; + this.prev_framesize = frame_size; + + this.first = 0; + + /* In the unlikely case that the SILK encoder busted its target, tell + the decoder to call the PLC */ + if (enc.tell() > (max_data_bytes - 1) * 8) + { + if (max_data_bytes < 2) + { + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + data[data_ptr + 1] = 0; + ret = 1; + this.rangeFinal = 0; + } + else if (this.mode == OpusMode.MODE_SILK_ONLY && redundancy == 0) + { + /*When in LPC only mode it's perfectly + reasonable to strip off trailing zero bytes as + the required range decoder behavior is to + fill these in. This can't be done when the MDCT + modes are used because the decoder needs to know + the actual length for allocation purposes.*/ + while (ret > 2 && data[data_ptr + ret] == 0) ret--; + } + /* Count ToC and redundancy */ + ret += 1 + redundancy_bytes; + if (this.use_vbr == 0) + { + if (OpusRepacketizer.PadPacket(data, data_ptr, ret, max_data_bytes) != OpusError.OPUS_OK) + { + return OpusError.OPUS_INTERNAL_ERROR; + } + ret = max_data_bytes; + } + + return ret; + } + + // Bitrate table used for constant quality encoding + // Column 1 is bitrate for 100% speech, column 2 is 100% music + private static readonly int[][] vqTable = new int[][] + { + new int[] { 7000, 16000 }, // VQ 0 + new int[] { 10000, 24000 }, // VQ 1 + new int[] { 13000, 32000 }, // VQ 2 + new int[] { 17000, 48000 }, // VQ 3 + new int[] { 20000, 64000 }, // VQ 4 + new int[] { 24000, 80000 }, // VQ 5 + new int[] { 28000, 96000 }, // VQ 6 + new int[] { 32000, 112000 }, // VQ 7 + new int[] { 38000, 128000 }, // VQ 8 + new int[] { 48000, 192000 }, // VQ 9 + new int[] { 64000, 256000 }, // VQ 10 + }; + + private static int GetVariableQualityBitrate(int vqLevel, float music_prob) + { + float low = vqTable[vqLevel][0]; + float high = vqTable[vqLevel][1]; + // Use a simple square root curve to cause the VQ to favor higher bitrates unless speech is very confident + float curvedQual = (float)Math.Sqrt(music_prob); + return (int)((high * curvedQual) + (low * (1 - curvedQual))); + } + + /// + /// Encodes an Opus frame. + /// + /// Input signal (Interleaved if stereo). Length should be at least frame_size * channels + /// Offset to use when reading the in_pcm buffer + /// The number of samples per channel in the inpus signal. + /// The frame size must be a valid Opus framesize for the given sample rate. + /// For example, at 48Khz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10ms + /// (480 samples at 48Khz) will prevent the encoder from using FEC, DTX, or hybrid modes. + /// Destination buffer for the output payload. This must contain at least max_data_bytes + /// The offset to use when writing to the output data buffer + /// The maximum amount of space allocated for the output payload. This may be used to impose + /// an upper limit on the instant bitrate, but should not be used as the only bitrate control (use the Bitrate parameter for that) + /// The length of the encoded packet, in bytes. This value will always be less than or equal to 1275, the maximum Opus packet size. + public int Encode(short[] in_pcm, int pcm_offset, int frame_size, + byte[] out_data, int out_data_offset, int max_data_bytes) + { + // Check that the caller is telling the truth about its input buffers + if (out_data_offset + max_data_bytes > out_data.Length) + { + throw new ArgumentException(string.Format( + "Output buffer is too small: Stated size is {0} bytes, actual size is {1} bytes", + max_data_bytes, out_data.Length - out_data_offset)); + } + + int delay_compensation; + if (this.application == OpusApplication.OPUS_APPLICATION_RESTRICTED_LOWDELAY) + delay_compensation = 0; + else + delay_compensation = this.delay_compensation; + + int internal_frame_size = CodecHelpers.compute_frame_size(in_pcm, pcm_offset, frame_size, + this.variable_duration, this.channels, this.Fs, this.bitrate_bps, + delay_compensation, Downmix.downmix_int, this.analysis.subframe_mem, this.analysis.enabled); + + // Check that input pcm length is >= frame_size + if (pcm_offset + internal_frame_size > in_pcm.Length) + { + throw new ArgumentException(string.Format( + "Not enough samples provided in input signal: Expected {0} samples, found {1}", + internal_frame_size, in_pcm.Length - pcm_offset)); + } + + try + { + int ret = opus_encode_native(in_pcm, pcm_offset, internal_frame_size, out_data, out_data_offset, max_data_bytes, 16, + in_pcm, pcm_offset, frame_size, 0, -2, this.channels, Downmix.downmix_int, 0); + + if (ret < 0) + { + // An error happened; report it + if (ret == OpusError.OPUS_BAD_ARG) + throw new ArgumentException("OPUS_BAD_ARG while encoding"); + throw new OpusException("An error occurred during encoding", ret); + } + + return ret; + } + catch (OutOfMemoryException e) + { + throw new OpusException("Internal error during encoding: " + e.Message); + } + } + + /// + /// Encodes an Opus frame using floating point input. + /// + /// Input signal in float format (Interleaved if stereo). Length should be at least frame_size * channels. + /// Value should be normalized to the +/- 1.0 range. Samples with a range beyond +/-1.0 will be clipped. + /// Offset to use when reading the in_pcm buffer + /// The number of samples per channel in the inpus signal. + /// The frame size must be a valid Opus framesize for the given sample rate. + /// For example, at 48Khz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10ms + /// (480 samples at 48Khz) will prevent the encoder from using FEC, DTX, or hybrid modes. + /// Destination buffer for the output payload. This must contain at least max_data_bytes + /// The offset to use when writing to the output data buffer + /// The maximum amount of space allocated for the output payload. This may be used to impose + /// an upper limit on the instant bitrate, but should not be used as the only bitrate control (use the Bitrate parameter for that) + /// The length of the encoded packet, in bytes. This value will always be less than or equal to 1275, the maximum Opus packet size. + public int Encode(float[] in_pcm, int pcm_offset, int frame_size, + byte[] out_data, int out_data_offset, int max_data_bytes) + { + // Check that the caller is telling the truth about its input buffers + if (out_data_offset + max_data_bytes > out_data.Length) + { + throw new ArgumentException(string.Format( + "Output buffer is too small: Stated size is {0} bytes, actual size is {1} bytes", + max_data_bytes, out_data.Length - out_data_offset)); + } + + int i, ret; + int internal_frame_size; + int delay_compensation; + short[] input; + + if (this.application == OpusApplication.OPUS_APPLICATION_RESTRICTED_LOWDELAY) + delay_compensation = 0; + else + delay_compensation = this.delay_compensation; + + internal_frame_size = CodecHelpers.compute_frame_size(in_pcm, pcm_offset, frame_size, + this.variable_duration, this.channels, this.Fs, this.bitrate_bps, + delay_compensation, Downmix.downmix_float, this.analysis.subframe_mem, this.analysis.enabled); + + // Check that input pcm length is >= frame_size + if (pcm_offset + internal_frame_size > in_pcm.Length) + { + throw new ArgumentException(string.Format( + "Not enough samples provided in input signal: Expected {0} samples, found {1}", + internal_frame_size, in_pcm.Length - pcm_offset)); + } + + input = new short[internal_frame_size * this.channels]; + + for (i = 0; i < internal_frame_size * this.channels; i++) + input[i] = Inlines.FLOAT2INT16(in_pcm[pcm_offset + i]); + + try + { + ret = opus_encode_native(input, 0, internal_frame_size, out_data, out_data_offset, max_data_bytes, 16, + in_pcm, pcm_offset, frame_size, 0, -2, this.channels, Downmix.downmix_float, 1); + + if (ret < 0) + { + // An error happened; report it + if (ret == OpusError.OPUS_BAD_ARG) + throw new ArgumentException("OPUS_BAD_ARG while decoding"); + throw new OpusException("An error occurred during encoding", ret); + } + + return ret; + } + catch (OutOfMemoryException e) + { + throw new OpusException("Internal error during encoding: " + e.Message); + } + } + + #endregion + + #region Getters and Setters + + /// + /// Gets or sets the application (or signal type) of the input signal. This hints + /// to the encoder what type of details we want to preserve in the encoding. + /// This cannot be changed after the encoder has started + /// + public OpusApplication Application + { + get + { + return application; + } + set + { + if (first == 0 && application != value) + { + throw new ArgumentException("Application cannot be changed after encoding has started"); + } + + application = value; + } + } + + /// + /// Gets or sets the bitrate for encoder, in bits per second. Valid bitrates are between 6K (6144) and 510K (522240) + /// + public int Bitrate + { + get + { + return user_bitrate_to_bitrate(this.user_bitrate_bps, prev_framesize, 1276); + } + set + { + if (ConstantQuality.HasValue) + { + throw new ArgumentException("Bitrate is read-only while the ConstantQuality parameter is set"); + } + + if (value != OpusConstants.OPUS_AUTO && value != OpusConstants.OPUS_BITRATE_MAX) + { + if (value <= 0) + throw new ArgumentException("Bitrate must be positive"); + else if (value <= 500) + value = 500; + else if (value > (int)300000 * channels) + value = (int)300000 * channels; + } + + user_bitrate_bps = value; + } + } + + /// + /// Gets or sets the maximum number of channels to be encoded. This can be used to force a downmix from stereo to mono if stereo + /// separation is not important + /// + public int ForceChannels + { + get + { + return force_channels; + } + set + { + if ((value < 1 || value > channels) && value != OpusConstants.OPUS_AUTO) + { + throw new ArgumentException("Force channels must be <= num. of channels"); + } + + force_channels = value; + } + } + + /// + /// Gets or sets the maximum bandwidth to be used by the encoder. This can be used if + /// high-frequency audio is not important to your application (e.g. telephony) + /// + public OpusBandwidth MaxBandwidth + { + get + { + return max_bandwidth; + } + set + { + max_bandwidth = value; + if (max_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND) + { + silk_mode.maxInternalSampleRate = 8000; + } + else if (max_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) + { + silk_mode.maxInternalSampleRate = 12000; + } + else { + silk_mode.maxInternalSampleRate = 16000; + } + } + } + + /// + /// Gets or sets the "preferred" encoded bandwidth. This does not affect the sample rate of the input audio, + /// only the encoding cutoffs + /// + public OpusBandwidth Bandwidth + { + get + { + return bandwidth; + } + set + { + user_bandwidth = value; + if (user_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND) + { + silk_mode.maxInternalSampleRate = 8000; + } + else if (user_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) + { + silk_mode.maxInternalSampleRate = 12000; + } + else { + silk_mode.maxInternalSampleRate = 16000; + } + } + } + + /// + /// Gets or sets a flag to enable Discontinuous Transmission mode. This mode is only available in the SILK encoder + /// (Bitrate < 40Kbit/s and/or ForceMode == SILK). When enabled, the encoder detects silence and background noise + /// and reduces the number of output packets, with up to 600ms in between separate packet transmissions. + /// + public bool UseDTX + { + get + { + return silk_mode.useDTX != 0; + } + set + { + silk_mode.useDTX = value ? 1 : 0; + } + } + + /// + /// Gets or sets the encoder complexity, between 0 and 10 + /// + public int Complexity + { + get + { + return silk_mode.complexity; + } + set + { + if (value < 0 || value > 10) + { + throw new ArgumentException("Complexity must be between 0 and 10"); + } + silk_mode.complexity = value; + Celt_Encoder.SetComplexity(value); + } + } + + /// + /// Gets or sets a flag to enable Forward Error Correction. This mode is only available in the SILK encoder + /// (Bitrate < 40Kbit/s and/or ForceMode == SILK). When enabled, lost packets can be partially recovered + /// by decoding data stored in the following packet. + /// + public bool UseInbandFEC + { + get + { + return silk_mode.useInBandFEC != 0; + } + set + { + silk_mode.useInBandFEC = value ? 1 : 0; + } + } + + /// + /// Gets or sets the expected amount of packet loss in the transmission medium, from 0 to 100. + /// Only applies if UseInbandFEC is also enabled, and the encoder is in SILK mode. + /// + public int PacketLossPercent + { + get + { + return silk_mode.packetLossPercentage; + } + set + { + if (value < 0 || value > 100) + { + throw new ArgumentException("Packet loss must be between 0 and 100"); + } + silk_mode.packetLossPercentage = value; + Celt_Encoder.SetPacketLossPercent(value); + } + } + + /// + /// Gets or sets a flag to enable Variable Bitrate encoding. This is recommended as it generally improves audio quality + /// with little impact on average bitrate + /// + public bool UseVBR + { + get + { + return use_vbr != 0; + } + set + { + use_vbr = value ? 1 : 0; + silk_mode.useCBR = value ? 0 : 1; + } + } + + // + // Gets or sets the "voice ratio". This is not implemented, but the idea is to hint the amount of voice vs. music in the input signal + // + //public int VoiceRatio + //{ + // get + // { + // return voice_ratio; + // } + // set + // { + // if (value < -1 || value > 100) + // { + // throw new ArgumentException("Voice ratio must be between -1 and 100"); + // } + + // voice_ratio = value; + // } + //} + + /// + /// Gets or sets a flag to enable constrained VBR. This only applies when the encoder is in CELT mode (i.e. high bitrates) + /// + public bool UseConstrainedVBR + { + get + { + return vbr_constraint != 0; + } + set + { + vbr_constraint = value ? 1 : 0; + } + } + + /// + /// Gets or sets a hint to the encoder for what type of audio is being processed, voice or music + /// + public OpusSignal SignalType + { + get + { + return signal_type; + } + set + { + signal_type = value; + } + } + + /// + /// Gets the number of samples of audio that are being stored in a buffer and are therefore contributing to latency. + /// + public int Lookahead + { + get + { + int returnVal = Fs / 400; + if (application != OpusApplication.OPUS_APPLICATION_RESTRICTED_LOWDELAY) + returnVal += delay_compensation; + + return returnVal; + } + } + + /// + /// Gets the encoder's input sample rate. This is fixed for the lifetime of the encoder. + /// + public int SampleRate + { + get + { + return Fs; + } + } + + /// + /// Gets the number of channels that this encoder expects in its input. Always constant for the lifetime of the decoder. + /// + public int NumChannels + { + get + { + return channels; + } + } + + /// + /// Returns the final range of the entropy coder + /// + public uint FinalRange + { + get + { + return rangeFinal; + } + } + + /// + /// Gets or sets the bit resolution of the input audio signal. Though the encoder always uses 16-bit internally, this can help + /// it make better decisions about bandwidth and cutoff values + /// + public int LSBDepth + { + get + { + return lsb_depth; + } + set + { + if (value < 8 || value > 24) + { + throw new ArgumentException("LSB depth must be between 8 and 24"); + } + + lsb_depth = value; + } + } + + /// + /// Gets or sets a fixed length for each encoded frame. Typically, the encoder just chooses a frame duration based on the input length + /// and the current internal mode. This can be used to enforce an exact length if it is required by your application (e.g. monotonous transmission) + /// + public OpusFramesize ExpertFrameDuration + { + get + { + return variable_duration; + + } + set + { + variable_duration = value; + Celt_Encoder.SetExpertFrameDuration(value); + } + } + + /// + /// Gets or sets a user-forced mode for the encoder. There are three modes, SILK, HYBRID, and CELT. Silk can only encode below 40Kbit/s and is best suited + /// for speech. Silk also has modes such as FEC which may be desirable. Celt sounds better at higher bandwidth and is comparable to AAC. It also performs somewhat faster. + /// Hybrid is used to create a smooth transition between the two modes. Note that this value may not always be honored due to other factors such + /// as frame size and bitrate. + /// + public OpusMode ForceMode + { + get + { + return user_forced_mode; + } + set + { + user_forced_mode = value; + } + } + + /// + /// Gets or sets a value indicating that this stream is a low-frequency channel. This is used when encoding 5.1 surround audio. + /// + public bool IsLFE + { + get + { + return lfe != 0; + } + set + { + lfe = value ? 1 : 0; + Celt_Encoder.SetLFE(value ? 1 : 0); + } + } + + /// + /// Gets or sets a flag to disable prediction, which does... something with the SILK codec + /// + public bool PredictionDisabled + { + get + { + return silk_mode.reducedDependency != 0; + } + set + { + silk_mode.reducedDependency = value ? 1 : 0; + } + } + + /// + /// Gets or sets a value indicating whether neural net analysis functions should be enabled, increasing encode quality + /// at the expense of speed. + /// + public bool EnableAnalysis + { + get + { + return analysis.enabled; + } + set + { + if (!value && _vqLevel.HasValue) + { + throw new ArgumentException("You cannot disable analysis while also specifying a ConstantQuality parameter"); + } + if (value && this.Fs != 48000) + { + throw new ArgumentException("EnableAnalysis only works if the encoder is in 48000Khz mode"); + } + + analysis.enabled = value; + } + } + + /// + /// EXPERIMENTAL!!! Gets or sets the constant quality encoding parameter. This is a new feature intended to approximate + /// "Constant Quality VBR" that other codecs such as MP3Lame provide, to let you encode mixed speech and music + /// (such as a podcast) in the same Opus stream without changing encoder params. + /// The quality is range from 0 (lowest) to 10 (highest). A setting of "null" means to use the regular Opus bitrate modes. + /// + public int? ConstantQuality + { + get + { + return _vqLevel; + } + + set + { + if (value.HasValue && (value.Value < 0 || value.Value > 10)) + { + throw new ArgumentException("Constant quality VBR level must be either null (disabled) or between 0 and 10, inclusive."); + } + if (value.HasValue && this.Fs != 48000) + { + throw new ArgumentException("ConstantQuality only works if the encoder is in 48000Khz mode"); + } + + EnableAnalysis = true; + _vqLevel = value; + } + } + + /// + /// EXPERIMENTAL. Returns the probability that the current signal is music, according to the built-in analysis. + /// Only meaningful if EnableAnalysis is true and quality is above 7 or so + /// + public float MusicProbability + { + get + { + return analysis.music_prob; + } + } + + internal void SetEnergyMask(int[] value) + { + energy_masking = value; + Celt_Encoder.SetEnergyMask(value); + } + + internal CeltMode GetCeltMode() + { + return Celt_Encoder.GetMode(); + } + + #endregion + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusMSDecoder.cs b/Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusMSDecoder.cs new file mode 100644 index 000000000..51a9199f2 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusMSDecoder.cs @@ -0,0 +1,438 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using Concentus.Common; +using Concentus.Common.CPlusPlus; +using Concentus.Enums; +using Concentus.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + + +namespace Concentus.Structs +{ + public class OpusMSDecoder + { + internal ChannelLayout layout = new ChannelLayout(); + internal OpusDecoder[] decoders = null; + + private OpusMSDecoder(int nb_streams, int nb_coupled_streams) + { + decoders = new OpusDecoder[nb_streams]; + for (int c = 0; c < nb_streams; c++) + decoders[c] = new OpusDecoder(); + } + + #region API functions + + internal int opus_multistream_decoder_init( + int Fs, + int channels, + int streams, + int coupled_streams, + byte[] mapping +) + { + int i, ret; + int decoder_ptr = 0; + + if ((channels > 255) || (channels < 1) || (coupled_streams > streams) || + (streams < 1) || (coupled_streams < 0) || (streams > 255 - coupled_streams)) + throw new ArgumentException("Invalid channel or coupled stream count"); + + this.layout.nb_channels = channels; + this.layout.nb_streams = streams; + this.layout.nb_coupled_streams = coupled_streams; + + for (i = 0; i < this.layout.nb_channels; i++) + this.layout.mapping[i] = mapping[i]; + if (OpusMultistream.validate_layout(this.layout) == 0) + throw new ArgumentException("Invalid surround channel layout"); + + for (i = 0; i < this.layout.nb_coupled_streams; i++) + { + ret = this.decoders[decoder_ptr].opus_decoder_init(Fs, 2); + if (ret != OpusError.OPUS_OK) return ret; + decoder_ptr++; + } + for (; i < this.layout.nb_streams; i++) + { + ret = this.decoders[decoder_ptr].opus_decoder_init(Fs, 1); + if (ret != OpusError.OPUS_OK) return ret; + decoder_ptr++; + } + return OpusError.OPUS_OK; + } + + /// + /// Creates a new multichannel decoder + /// + /// + /// + /// + /// + /// A mapping family (just use { 0, 1, 255 }) + /// + public OpusMSDecoder( + int Fs, + int channels, + int streams, + int coupled_streams, + byte[] mapping) : this(streams, coupled_streams) + { + int ret; + if ((channels > 255) || (channels < 1) || (coupled_streams > streams) || + (streams < 1) || (coupled_streams < 0) || (streams > 255 - coupled_streams)) + { + throw new ArgumentException("Invalid channel / stream configuration"); + } + + ret = this.opus_multistream_decoder_init(Fs, channels, streams, coupled_streams, mapping); + if (ret != OpusError.OPUS_OK) + { + if (ret == OpusError.OPUS_BAD_ARG) + throw new ArgumentException("Bad argument while creating MS decoder"); + throw new OpusException("Could not create MS decoder", ret); + } + } + + internal delegate void opus_copy_channel_out_func( + T[] dst, + int dst_ptr, + int dst_stride, + int dst_channel, + short[] src, + int src_ptr, + int src_stride, + int frame_size + ); + + internal static int opus_multistream_packet_validate(byte[] data, int data_ptr, + int len, int nb_streams, int Fs) + { + int s; + int count; + byte toc; + short[] size = new short[48]; + int samples = 0; + int packet_offset; + int dummy; + + for (s = 0; s < nb_streams; s++) + { + int tmp_samples; + if (len <= 0) + return OpusError.OPUS_INVALID_PACKET; + + count = OpusPacketInfo.opus_packet_parse_impl(data, data_ptr, len, (s != nb_streams - 1) ? 1 : 0, out toc, null, null, 0, + size, 0, out dummy, out packet_offset); + if (count < 0) + return count; + + tmp_samples = OpusPacketInfo.GetNumSamples(data, data_ptr, packet_offset, Fs); + if (s != 0 && samples != tmp_samples) + return OpusError.OPUS_INVALID_PACKET; + samples = tmp_samples; + data_ptr += packet_offset; + len -= packet_offset; + } + + return samples; + } + + internal int opus_multistream_decode_native( + byte[] data, + int data_ptr, + int len, + T[] pcm, + int pcm_ptr, + opus_copy_channel_out_func copy_channel_out, + int frame_size, + int decode_fec, + int soft_clip +) + { + int Fs; + int s, c; + int decoder_ptr; + int do_plc = 0; + short[] buf; + + /* Limit frame_size to avoid excessive stack allocations. */ + Fs = this.SampleRate; + frame_size = Inlines.IMIN(frame_size, Fs / 25 * 3); + buf = new short[2 * frame_size]; + decoder_ptr = 0; + + if (len == 0) + do_plc = 1; + if (len < 0) + { + return OpusError.OPUS_BAD_ARG; + } + if (do_plc == 0 && len < 2 * this.layout.nb_streams - 1) + { + return OpusError.OPUS_INVALID_PACKET; + } + if (do_plc == 0) + { + int ret = opus_multistream_packet_validate(data, data_ptr, len, this.layout.nb_streams, Fs); + if (ret < 0) + { + return ret; + } + else if (ret > frame_size) + { + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + } + for (s = 0; s < this.layout.nb_streams; s++) + { + OpusDecoder dec; + int ret; + + dec = this.decoders[decoder_ptr++]; + + if (do_plc == 0 && len <= 0) + { + return OpusError.OPUS_INTERNAL_ERROR; + } + int packet_offset; + ret = dec.opus_decode_native( + data, data_ptr, len, buf, 0, frame_size, decode_fec, + (s != this.layout.nb_streams - 1) ? 1 : 0, out packet_offset, soft_clip); + data_ptr += packet_offset; + len -= packet_offset; + if (ret <= 0) + { + return ret; + } + frame_size = ret; + if (s < this.layout.nb_coupled_streams) + { + int chan, prev; + prev = -1; + /* Copy "left" audio to the channel(s) where it belongs */ + while ((chan = OpusMultistream.get_left_channel(this.layout, s, prev)) != -1) + { + copy_channel_out(pcm, pcm_ptr, this.layout.nb_channels, chan, + buf, 0, 2, frame_size); + prev = chan; + } + prev = -1; + /* Copy "right" audio to the channel(s) where it belongs */ + while ((chan = OpusMultistream.get_right_channel(this.layout, s, prev)) != -1) + { + copy_channel_out(pcm, pcm_ptr, this.layout.nb_channels, chan, + buf, 1, 2, frame_size); + prev = chan; + } + } + else { + int chan, prev; + prev = -1; + /* Copy audio to the channel(s) where it belongs */ + while ((chan = OpusMultistream.get_mono_channel(this.layout, s, prev)) != -1) + { + copy_channel_out(pcm, pcm_ptr, this.layout.nb_channels, chan, + buf, 0, 1, frame_size); + prev = chan; + } + } + } + /* Handle muted channels */ + for (c = 0; c < this.layout.nb_channels; c++) + { + if (this.layout.mapping[c] == 255) + { + copy_channel_out(pcm, pcm_ptr, this.layout.nb_channels, c, + null, 0, 0, frame_size); + } + } + + return frame_size; + } + + internal static void opus_copy_channel_out_float( + float[] dst, + int dst_ptr, + int dst_stride, + int dst_channel, + short[] src, + int src_ptr, + int src_stride, + int frame_size + ) + { + int i; + if (src != null) + { + for (i = 0; i < frame_size; i++) + dst[i * dst_stride + dst_channel + dst_ptr] = (1 / 32768.0f) * src[i * src_stride + src_ptr]; + } + else + { + for (i = 0; i < frame_size; i++) + dst[i * dst_stride + dst_channel + dst_ptr] = 0; + } + } + + internal static void opus_copy_channel_out_short( + short[] dst, + int dst_ptr, + int dst_stride, + int dst_channel, + short[] src, + int src_ptr, + int src_stride, + int frame_size + ) + { + int i; + if (src != null) + { + for (i = 0; i < frame_size; i++) + dst[i * dst_stride + dst_channel + dst_ptr] = src[i * src_stride + src_ptr]; + } + else + { + for (i = 0; i < frame_size; i++) + dst[i * dst_stride + dst_channel + dst_ptr] = 0; + } + } + + public int DecodeMultistream( + byte[] data, + int data_offset, + int len, + short[] out_pcm, + int out_pcm_offset, + int frame_size, + int decode_fec + ) + { + return opus_multistream_decode_native(data, data_offset, len, + out_pcm, out_pcm_offset, opus_copy_channel_out_short, frame_size, decode_fec, 0); + } + + public int DecodeMultistream(byte[] data, int data_offset, + int len, float[] out_pcm, int out_pcm_offset, int frame_size, int decode_fec) + { + return opus_multistream_decode_native(data, data_offset, len, + out_pcm, out_pcm_offset, opus_copy_channel_out_float, frame_size, decode_fec, 0); + } + + #endregion + + #region Getters and setters + + public OpusBandwidth Bandwidth + { + get + { + if (decoders == null || decoders.Length == 0) + throw new InvalidOperationException("Decoder not initialized"); + return decoders[0].Bandwidth; + } + } + + public int SampleRate + { + get + { + if (decoders == null || decoders.Length == 0) + throw new InvalidOperationException("Decoder not initialized"); + return decoders[0].SampleRate; + } + } + + public int Gain + { + get + { + if (decoders == null || decoders.Length == 0) + return OpusError.OPUS_INVALID_STATE; + return decoders[0].Gain; + } + set + { + for (int s = 0; s < layout.nb_streams; s++) + { + decoders[s].Gain = value; + } + } + } + + public int LastPacketDuration + { + get + { + if (decoders == null || decoders.Length == 0) + return OpusError.OPUS_INVALID_STATE; + return decoders[0].LastPacketDuration; + } + } + + public uint FinalRange + { + get + { + uint value = 0; + for (int s = 0; s < layout.nb_streams; s++) + { + value ^= decoders[s].FinalRange; + } + return value; + } + } + + public void ResetState() + { + for (int s = 0; s < layout.nb_streams; s++) + { + decoders[s].ResetState(); + } + } + + public OpusDecoder GetMultistreamDecoderState(int streamId) + { + return decoders[streamId]; + } + + #endregion + } +} \ No newline at end of file diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusMSEncoder.cs b/Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusMSEncoder.cs new file mode 100644 index 000000000..ab5f51588 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusMSEncoder.cs @@ -0,0 +1,1168 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using Concentus.Celt; +using Concentus.Celt.Structs; +using Concentus.Common; +using Concentus.Common.CPlusPlus; +using Concentus.Enums; +using Concentus.Structs; +using System; + +namespace Concentus.Structs +{ + public class OpusMSEncoder + { + internal readonly ChannelLayout layout = new ChannelLayout(); + internal int lfe_stream = 0; + internal OpusApplication application = OpusApplication.OPUS_APPLICATION_AUDIO; + internal OpusFramesize variable_duration = 0; + internal int surround = 0; + internal int bitrate_bps = 0; + internal readonly float[] subframe_mem = new float[3]; + internal readonly OpusEncoder[] encoders = null; + internal readonly int[] window_mem = null; + internal readonly int[] preemph_mem = null; + + private OpusMSEncoder(int nb_streams, int nb_coupled_streams) + { + if (nb_streams < 1 || nb_coupled_streams > nb_streams || nb_coupled_streams < 0) + throw new ArgumentException("Invalid channel count in MS encoder"); + + encoders = new OpusEncoder[nb_streams]; + for (int c = 0; c < nb_streams; c++) + encoders[c] = new OpusEncoder(); + // fixme is this nb_streams or nb_channels? + window_mem = new int[nb_streams * 120]; + preemph_mem = new int[nb_streams]; + } + + public void ResetState() + { + int s; + subframe_mem[0] = subframe_mem[1] = subframe_mem[2] = 0; + if (surround != 0) + { + Arrays.MemSetInt(preemph_mem, 0, layout.nb_channels); + Arrays.MemSetInt(window_mem, 0, layout.nb_channels * 120); + } + int encoder_ptr = 0; + for (s = 0; s < layout.nb_streams; s++) + { + OpusEncoder enc = encoders[encoder_ptr++]; + enc.ResetState(); + } + } + + #region Encoder API functions + + internal delegate void opus_copy_channel_in_func( + short[] dst, int dst_ptr, int dst_stride, T[] src, int src_ptr, int src_stride, int src_channel, int frame_size); + + internal static int validate_encoder_layout(ChannelLayout layout) + { + int s; + for (s = 0; s < layout.nb_streams; s++) + { + if (s < layout.nb_coupled_streams) + { + if (OpusMultistream.get_left_channel(layout, s, -1) == -1) + return 0; + if (OpusMultistream.get_right_channel(layout, s, -1) == -1) + return 0; + } + else { + if (OpusMultistream.get_mono_channel(layout, s, -1) == -1) + return 0; + } + } + return 1; + } + + internal static void channel_pos(int channels, int[] pos/*[8]*/) + { + /* Position in the mix: 0 don't mix, 1: left, 2: center, 3:right */ + if (channels == 4) + { + pos[0] = 1; + pos[1] = 3; + pos[2] = 1; + pos[3] = 3; + } + else if (channels == 3 || channels == 5 || channels == 6) + { + pos[0] = 1; + pos[1] = 2; + pos[2] = 3; + pos[3] = 1; + pos[4] = 3; + pos[5] = 0; + } + else if (channels == 7) + { + pos[0] = 1; + pos[1] = 2; + pos[2] = 3; + pos[3] = 1; + pos[4] = 3; + pos[5] = 2; + pos[6] = 0; + } + else if (channels == 8) + { + pos[0] = 1; + pos[1] = 2; + pos[2] = 3; + pos[3] = 1; + pos[4] = 3; + pos[5] = 1; + pos[6] = 3; + pos[7] = 0; + } + } + + private static readonly int[] diff_table/*[17]*/ = { + ((short)(0.5 + (0.5000000f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.5000000f, CeltConstants.DB_SHIFT)*/, ((short)(0.5 + (0.2924813f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.2924813f, CeltConstants.DB_SHIFT)*/, ((short)(0.5 + (0.1609640f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.1609640f, CeltConstants.DB_SHIFT)*/, ((short)(0.5 + (0.0849625f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.0849625f, CeltConstants.DB_SHIFT)*/, + ((short)(0.5 + (0.0437314f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.0437314f, CeltConstants.DB_SHIFT)*/, ((short)(0.5 + (0.0221971f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.0221971f, CeltConstants.DB_SHIFT)*/, ((short)(0.5 + (0.0111839f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.0111839f, CeltConstants.DB_SHIFT)*/, ((short)(0.5 + (0.0056136f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.0056136f, CeltConstants.DB_SHIFT)*/, + ((short)(0.5 + (0.0028123f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.0028123f, CeltConstants.DB_SHIFT)*/ + }; + + /* Computes a rough approximation of log2(2^a + 2^b) */ + internal static int logSum(int a, int b) + { + int max; + int diff; + int frac; + + int low; + if (a > b) + { + max = a; + diff = Inlines.SUB32(Inlines.EXTEND32(a), Inlines.EXTEND32(b)); + } + else { + max = b; + diff = Inlines.SUB32(Inlines.EXTEND32(b), Inlines.EXTEND32(a)); + } + if (!(diff < ((short)(0.5 + (8.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(8.0f, CeltConstants.DB_SHIFT)*/)) /* inverted to catch NaNs */ + return max; + low = Inlines.SHR32(diff, CeltConstants.DB_SHIFT - 1); + frac = Inlines.SHL16(diff - Inlines.SHL16(low, CeltConstants.DB_SHIFT - 1), 16 - CeltConstants.DB_SHIFT); + return max + diff_table[low] + Inlines.MULT16_16_Q15(frac, Inlines.SUB16(diff_table[low + 1], diff_table[low])); + } + + // fixme: test the perf of this alternate implementation + //int logSum(int a, int b) + //{ + // return log2(pow(4, a) + pow(4, b)) / 2; + //} + + internal static void surround_analysis(CeltMode celt_mode, T[] pcm, int pcm_ptr, + int[] bandLogE, int[] mem, int[] preemph_mem, + int len, int overlap, int channels, int rate, opus_copy_channel_in_func copy_channel_in + ) + { + int c; + int i; + int LM; + int[] pos = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int upsample; + int frame_size; + int channel_offset; + int[][] bandE = Arrays.InitTwoDimensionalArray(1, 21); + int[][] maskLogE = Arrays.InitTwoDimensionalArray(3, 21); + int[] input; + short[] x; + int[][] freq; + + upsample = CeltCommon.resampling_factor(rate); + frame_size = len * upsample; + + for (LM = 0; LM < celt_mode.maxLM; LM++) + if (celt_mode.shortMdctSize << LM == frame_size) + break; + + input = new int[frame_size + overlap]; + x = new short[len]; + freq = Arrays.InitTwoDimensionalArray(1, frame_size); + + channel_pos(channels, pos); + + for (c = 0; c < 3; c++) + for (i = 0; i < 21; i++) + maskLogE[c][i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/; + + for (c = 0; c < channels; c++) + { + Array.Copy(mem, c * overlap, input, 0, overlap); + copy_channel_in(x, 0 , 1, pcm, pcm_ptr, channels, c, len); + BoxedValueInt boxed_preemph = new BoxedValueInt(preemph_mem[c]); + CeltCommon.celt_preemphasis(x, input, overlap, frame_size, 1, upsample, celt_mode.preemph, boxed_preemph, 0); + preemph_mem[c] = boxed_preemph.Val; + + MDCT.clt_mdct_forward( + celt_mode.mdct, + input, + 0, + freq[0], + 0, + celt_mode.window, + overlap, + celt_mode.maxLM - LM, + 1); + if (upsample != 1) + { + int bound = len; + for (i = 0; i < bound; i++) + freq[0][i] *= upsample; + for (; i < frame_size; i++) + freq[0][i] = 0; + } + + Bands.compute_band_energies(celt_mode, freq, bandE, 21, 1, LM); + QuantizeBands.amp2Log2(celt_mode, 21, 21, bandE[0], bandLogE, 21 * c, 1); + /* Apply spreading function with -6 dB/band going up and -12 dB/band going down. */ + for (i = 1; i < 21; i++) + bandLogE[21 * c + i] = Inlines.MAX16(bandLogE[21 * c + i], bandLogE[21 * c + i - 1] - ((short)(0.5 + (1.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.0f, CeltConstants.DB_SHIFT)*/); + for (i = 19; i >= 0; i--) + bandLogE[21 * c + i] = Inlines.MAX16(bandLogE[21 * c + i], bandLogE[21 * c + i + 1] - ((short)(0.5 + (2.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(2.0f, CeltConstants.DB_SHIFT)*/); + if (pos[c] == 1) + { + for (i = 0; i < 21; i++) + maskLogE[0][i] = logSum(maskLogE[0][i], bandLogE[21 * c + i]); + } + else if (pos[c] == 3) + { + for (i = 0; i < 21; i++) + maskLogE[2][i] = logSum(maskLogE[2][i], bandLogE[21 * c + i]); + } + else if (pos[c] == 2) + { + for (i = 0; i < 21; i++) + { + maskLogE[0][i] = logSum(maskLogE[0][i], bandLogE[21 * c + i] - ((short)(0.5 + (.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/); + maskLogE[2][i] = logSum(maskLogE[2][i], bandLogE[21 * c + i] - ((short)(0.5 + (.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/); + } + } + Array.Copy(input, frame_size, mem, c * overlap, overlap); + } + for (i = 0; i < 21; i++) + maskLogE[1][i] = Inlines.MIN32(maskLogE[0][i], maskLogE[2][i]); + channel_offset = Inlines.HALF16(Inlines.celt_log2(((int)(0.5 + (2.0f) * (((int)1) << (14))))/*Inlines.QCONST32(2.0f, 14)*/ / (channels - 1))); + for (c = 0; c < 3; c++) + for (i = 0; i < 21; i++) + maskLogE[c][i] += channel_offset; + + for (c = 0; c < channels; c++) + { + int[] mask; + if (pos[c] != 0) + { + mask = maskLogE[pos[c] - 1]; + for (i = 0; i < 21; i++) + bandLogE[21 * c + i] = bandLogE[21 * c + i] - mask[i]; + } + else { + for (i = 0; i < 21; i++) + bandLogE[21 * c + i] = 0; + } + } + } + + internal int opus_multistream_encoder_init( + int Fs, + int channels, + int streams, + int coupled_streams, + byte[] mapping, + OpusApplication application, + int surround + ) + { + int i, ret; + int encoder_ptr; + + if ((channels > 255) || (channels < 1) || (coupled_streams > streams) || + (streams < 1) || (coupled_streams < 0) || (streams > 255 - coupled_streams)) + return OpusError.OPUS_BAD_ARG; + + this.layout.nb_channels = channels; + this.layout.nb_streams = streams; + this.layout.nb_coupled_streams = coupled_streams; + this.subframe_mem[0] = this.subframe_mem[1] = this.subframe_mem[2] = 0; + if (surround == 0) + this.lfe_stream = -1; + this.bitrate_bps = OpusConstants.OPUS_AUTO; + this.application = application; + this.variable_duration = OpusFramesize.OPUS_FRAMESIZE_ARG; + for (i = 0; i < this.layout.nb_channels; i++) + this.layout.mapping[i] = mapping[i]; + if (OpusMultistream.validate_layout(this.layout) == 0 || validate_encoder_layout(this.layout) == 0) + return OpusError.OPUS_BAD_ARG; + + encoder_ptr = 0; + + for (i = 0; i < this.layout.nb_coupled_streams; i++) + { + ret = this.encoders[encoder_ptr].opus_init_encoder(Fs, 2, application); + if (ret != OpusError.OPUS_OK) return ret; + if (i == this.lfe_stream) + this.encoders[encoder_ptr].IsLFE = true; + encoder_ptr += 1; + } + for (; i < this.layout.nb_streams; i++) + { + ret = this.encoders[encoder_ptr].opus_init_encoder(Fs, 1, application); + if (i == this.lfe_stream) + this.encoders[encoder_ptr].IsLFE = true; + if (ret != OpusError.OPUS_OK) return ret; + encoder_ptr += 1; + } + if (surround != 0) + { + Arrays.MemSetInt(this.preemph_mem, 0, channels); + Arrays.MemSetInt(this.window_mem, 0, channels * 120); + } + this.surround = surround; + return OpusError.OPUS_OK; + } + + internal int opus_multistream_surround_encoder_init( + int Fs, + int channels, + int mapping_family, + out int streams, + out int coupled_streams, + byte[] mapping, + OpusApplication application + ) + { + streams = 0; + coupled_streams = 0; + if ((channels > 255) || (channels < 1)) + return OpusError.OPUS_BAD_ARG; + this.lfe_stream = -1; + if (mapping_family == 0) + { + if (channels == 1) + { + streams = 1; + coupled_streams = 0; + mapping[0] = 0; + } + else if (channels == 2) + { + streams = 1; + coupled_streams = 1; + mapping[0] = 0; + mapping[1] = 1; + } + else + return OpusError.OPUS_UNIMPLEMENTED; + } + else if (mapping_family == 1 && channels <= 8 && channels >= 1) + { + int i; + streams = VorbisLayout.vorbis_mappings[channels - 1].nb_streams; + coupled_streams = VorbisLayout.vorbis_mappings[channels - 1].nb_coupled_streams; + for (i = 0; i < channels; i++) + mapping[i] = VorbisLayout.vorbis_mappings[channels - 1].mapping[i]; + if (channels >= 6) + this.lfe_stream = streams - 1; + } + else if (mapping_family == 255) + { + byte i; + streams = channels; + coupled_streams = 0; + for (i = 0; i < channels; i++) + mapping[i] = i; + } + else + return OpusError.OPUS_UNIMPLEMENTED; + return opus_multistream_encoder_init(Fs, channels, streams, coupled_streams, + mapping, application, (channels > 2 && mapping_family == 1) ? 1 : 0); + } + + /// + /// Creates a new multichannel Opus encoder using the "old API". + /// + /// The sample rate of the input signal + /// The number of channels to encode (1 - 255) + /// The number of streams to encode + /// The number of coupled streams + /// A raw mapping between input and output channels + /// The application to use for the encoder + public static OpusMSEncoder Create( + int Fs, + int channels, + int streams, + int coupled_streams, + byte[] mapping, + OpusApplication application + ) + { + int ret; + if ((channels > 255) || (channels < 1) || (coupled_streams > streams) || + (streams < 1) || (coupled_streams < 0) || (streams > 255 - coupled_streams)) + { + throw new ArgumentException("Invalid channel / stream configuration"); + } + OpusMSEncoder st = new OpusMSEncoder(streams, coupled_streams); + ret = st.opus_multistream_encoder_init(Fs, channels, streams, coupled_streams, mapping, application, 0); + if (ret != OpusError.OPUS_OK) + { + if (ret == OpusError.OPUS_BAD_ARG) + throw new ArgumentException("OPUS_BAD_ARG when creating MS encoder"); + throw new OpusException("Could not create MS encoder", ret); + } + return st; + } + + internal static void GetStreamCount(int channels, int mapping_family, BoxedValueInt nb_streams, BoxedValueInt nb_coupled_streams) + { + if (mapping_family == 0) + { + if (channels == 1) + { + nb_streams.Val = 1; + nb_coupled_streams.Val = 0; + } + else if (channels == 2) + { + nb_streams.Val = 1; + nb_coupled_streams.Val = 1; + } + else + throw new ArgumentException("More than 2 channels requires custom mappings"); + } + else if (mapping_family == 1 && channels <= 8 && channels >= 1) + { + nb_streams.Val = VorbisLayout.vorbis_mappings[channels - 1].nb_streams; + nb_coupled_streams.Val = VorbisLayout.vorbis_mappings[channels - 1].nb_coupled_streams; + } + else if (mapping_family == 255) + { + nb_streams.Val = channels; + nb_coupled_streams.Val = 0; + } + else + throw new ArgumentException("Invalid mapping family"); + } + + /// + /// Creates a multichannel Opus encoder using the "new API". This constructor allows you to use predefined Vorbis channel mappings, or specify your own. + /// + /// The samples rate of the input + /// The total number of channels to encode (1 - 255) + /// The mapping family to use. 0 = mono/stereo, 1 = use Vorbis mappings, 255 = use raw channel mapping + /// The number of streams to encode + /// The number of coupled streams + /// A raw mapping of input/output channels + /// The application to use for the encoders + public static OpusMSEncoder CreateSurround( + int Fs, + int channels, + int mapping_family, + out int streams, + out int coupled_streams, + byte[] mapping, + OpusApplication application + ) + { + int ret; + OpusMSEncoder st; + if ((channels > 255) || (channels < 1) || application == OpusApplication.OPUS_APPLICATION_UNIMPLEMENTED) + { + throw new ArgumentException("Invalid channel count or application"); + } + BoxedValueInt nb_streams = new BoxedValueInt(); + BoxedValueInt nb_coupled_streams = new BoxedValueInt(); + GetStreamCount(channels, mapping_family, nb_streams, nb_coupled_streams); + + st = new OpusMSEncoder(nb_streams.Val, nb_coupled_streams.Val); + ret = st.opus_multistream_surround_encoder_init(Fs, channels, mapping_family, out streams, out coupled_streams, mapping, application); + if (ret != OpusError.OPUS_OK) + { + if (ret == OpusError.OPUS_BAD_ARG) + throw new ArgumentException("Bad argument passed to CreateSurround"); + throw new OpusException("Could not create multistream encoder", ret); + } + return st; + } + + internal int surround_rate_allocation( + int[] out_rates, + int frame_size + ) + { + int i; + int channel_rate; + int Fs; + OpusEncoder ptr; + int stream_offset; + int lfe_offset; + int coupled_ratio; /* Q8 */ + int lfe_ratio; /* Q8 */ + int rate_sum = 0; + + ptr = this.encoders[0]; + Fs = ptr.SampleRate; + + if (this.bitrate_bps > this.layout.nb_channels * 40000) + stream_offset = 20000; + else + stream_offset = this.bitrate_bps / this.layout.nb_channels / 2; + stream_offset += 60 * (Fs / frame_size - 50); + /* We start by giving each stream (coupled or uncoupled) the same bitrate. + This models the main saving of coupled channels over uncoupled. */ + /* The LFE stream is an exception to the above and gets fewer bits. */ + lfe_offset = 3500 + 60 * (Fs / frame_size - 50); + /* Coupled streams get twice the mono rate after the first 20 kb/s. */ + coupled_ratio = 512; + /* Should depend on the bitrate, for now we assume LFE gets 1/8 the bits of mono */ + lfe_ratio = 32; + + /* Compute bitrate allocation between streams */ + if (this.bitrate_bps == OpusConstants.OPUS_AUTO) + { + channel_rate = Fs + 60 * Fs / frame_size; + } + else if (this.bitrate_bps == OpusConstants.OPUS_BITRATE_MAX) + { + channel_rate = 300000; + } + else { + int nb_lfe; + int nb_uncoupled; + int nb_coupled; + int total; + nb_lfe = (this.lfe_stream != -1) ? 1 : 0; + nb_coupled = this.layout.nb_coupled_streams; + nb_uncoupled = this.layout.nb_streams - nb_coupled - nb_lfe; + total = (nb_uncoupled << 8) /* mono */ + + coupled_ratio * nb_coupled /* stereo */ + + nb_lfe * lfe_ratio; + channel_rate = 256 * (this.bitrate_bps - lfe_offset * nb_lfe - stream_offset * (nb_coupled + nb_uncoupled)) / total; + } + + for (i = 0; i < this.layout.nb_streams; i++) + { + if (i < this.layout.nb_coupled_streams) + out_rates[i] = stream_offset + (channel_rate * coupled_ratio >> 8); + else if (i != this.lfe_stream) + out_rates[i] = stream_offset + channel_rate; + else + out_rates[i] = lfe_offset + (channel_rate * lfe_ratio >> 8); + out_rates[i] = Inlines.IMAX(out_rates[i], 500); + rate_sum += out_rates[i]; + } + return rate_sum; + } + + /* Max size in case the encoder decides to return three frames */ + private const int MS_FRAME_TMP = (3 * 1275 + 7); + + internal int opus_multistream_encode_native + ( + opus_copy_channel_in_func copy_channel_in, + T[] pcm, + int pcm_ptr, + int analysis_frame_size, + byte[] data, + int data_ptr, + int max_data_bytes, + int lsb_depth, + Downmix.downmix_func downmix, + int float_api + ) + { + int Fs; + int s; + int encoder_ptr; + int tot_size; + short[] buf; + int[] bandSMR; + byte[] tmp_data = new byte[MS_FRAME_TMP]; + OpusRepacketizer rp = new OpusRepacketizer(); + int vbr; + CeltMode celt_mode; + int[] bitrates = new int[256]; + int[] bandLogE = new int[42]; + int[] mem = null; + int[] preemph_mem = null; + int frame_size; + int rate_sum; + int smallest_packet; + + if (this.surround != 0) + { + preemph_mem = this.preemph_mem; + mem = this.window_mem; + } + + encoder_ptr = 0; + Fs = this.encoders[encoder_ptr].SampleRate; + vbr = this.encoders[encoder_ptr].UseVBR ? 1 : 0; + celt_mode = this.encoders[encoder_ptr].GetCeltMode(); + + { + int delay_compensation; + int channels; + + channels = this.layout.nb_streams + this.layout.nb_coupled_streams; + delay_compensation = this.encoders[encoder_ptr].Lookahead; + delay_compensation -= Fs / 400; + frame_size = CodecHelpers.compute_frame_size(pcm, pcm_ptr, analysis_frame_size, + this.variable_duration, channels, Fs, this.bitrate_bps, + delay_compensation, downmix, this.subframe_mem, this.encoders[encoder_ptr].analysis.enabled); + } + + if (400 * frame_size < Fs) + { + return OpusError.OPUS_BAD_ARG; + } + /* Validate frame_size before using it to allocate stack space. + This mirrors the checks in opus_encode[_float](). */ + if (400 * frame_size != Fs && 200 * frame_size != Fs && + 100 * frame_size != Fs && 50 * frame_size != Fs && + 25 * frame_size != Fs && 50 * frame_size != 3 * Fs) + { + return OpusError.OPUS_BAD_ARG; + } + + /* Smallest packet the encoder can produce. */ + smallest_packet = this.layout.nb_streams * 2 - 1; + if (max_data_bytes < smallest_packet) + { + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + buf = new short[2 * frame_size]; + + bandSMR = new int[21 * this.layout.nb_channels]; + if (this.surround != 0) + { + surround_analysis(celt_mode, pcm, pcm_ptr, bandSMR, mem, preemph_mem, frame_size, 120, this.layout.nb_channels, Fs, copy_channel_in); + } + + /* Compute bitrate allocation between streams (this could be a lot better) */ + rate_sum = surround_rate_allocation(bitrates, frame_size); + + if (vbr == 0) + { + if (this.bitrate_bps == OpusConstants.OPUS_AUTO) + { + max_data_bytes = Inlines.IMIN(max_data_bytes, 3 * rate_sum / (3 * 8 * Fs / frame_size)); + } + else if (this.bitrate_bps != OpusConstants.OPUS_BITRATE_MAX) + { + max_data_bytes = Inlines.IMIN(max_data_bytes, Inlines.IMAX(smallest_packet, + 3 * this.bitrate_bps / (3 * 8 * Fs / frame_size))); + } + } + + for (s = 0; s < this.layout.nb_streams; s++) + { + OpusEncoder enc = this.encoders[encoder_ptr]; + encoder_ptr += 1; + enc.Bitrate = (bitrates[s]); + if (this.surround != 0) + { + int equiv_rate; + equiv_rate = this.bitrate_bps; + if (frame_size * 50 < Fs) + equiv_rate -= 60 * (Fs / frame_size - 50) * this.layout.nb_channels; + if (equiv_rate > 10000 * this.layout.nb_channels) + enc.Bandwidth = (OpusBandwidth.OPUS_BANDWIDTH_FULLBAND); + else if (equiv_rate > 7000 * this.layout.nb_channels) + enc.Bandwidth = (OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND); + else if (equiv_rate > 5000 * this.layout.nb_channels) + enc.Bandwidth= (OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND); + else + enc.Bandwidth = (OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND); + if (s < this.layout.nb_coupled_streams) + { + /* To preserve the spatial image, force stereo CELT on coupled streams */ + enc.ForceMode = (OpusMode.MODE_CELT_ONLY); + enc.ForceChannels = (2); + } + } + } + + encoder_ptr = 0; + /* Counting ToC */ + tot_size = 0; + for (s = 0; s < this.layout.nb_streams; s++) + { + OpusEncoder enc; + int len; + int curr_max; + int c1, c2; + + rp.Reset(); + enc = this.encoders[encoder_ptr]; + if (s < this.layout.nb_coupled_streams) + { + int i; + int left, right; + left = OpusMultistream.get_left_channel(this.layout, s, -1); + right = OpusMultistream.get_right_channel(this.layout, s, -1); + copy_channel_in(buf, 0, 2, + pcm, pcm_ptr, this.layout.nb_channels, left, frame_size); + copy_channel_in(buf, 1, 2, + pcm, pcm_ptr, this.layout.nb_channels, right, frame_size); + encoder_ptr += 1; + if (this.surround != 0) + { + for (i = 0; i < 21; i++) + { + bandLogE[i] = bandSMR[21 * left + i]; + bandLogE[21 + i] = bandSMR[21 * right + i]; + } + } + c1 = left; + c2 = right; + } + else { + int i; + int chan = OpusMultistream.get_mono_channel(this.layout, s, -1); + copy_channel_in(buf, 0, 1, + pcm, pcm_ptr, this.layout.nb_channels, chan, frame_size); + encoder_ptr += 1; + if (this.surround != 0) + { + for (i = 0; i < 21; i++) + bandLogE[i] = bandSMR[21 * chan + i]; + } + c1 = chan; + c2 = -1; + } + if (this.surround != 0) + enc.SetEnergyMask(bandLogE); + + /* number of bytes left (+Toc) */ + curr_max = max_data_bytes - tot_size; + /* Reserve one byte for the last stream and two for the others */ + curr_max -= Inlines.IMAX(0, 2 * (this.layout.nb_streams - s - 1) - 1); + curr_max = Inlines.IMIN(curr_max, MS_FRAME_TMP); + /* Repacketizer will add one or two bytes for self-delimited frames */ + if (s != this.layout.nb_streams - 1) curr_max -= curr_max > 253 ? 2 : 1; + if (vbr == 0 && s == this.layout.nb_streams - 1) + enc.Bitrate = (curr_max * (8 * Fs / frame_size)); + len = enc.opus_encode_native(buf, 0, frame_size, tmp_data, 0, curr_max, lsb_depth, + pcm, pcm_ptr, analysis_frame_size, c1, c2, this.layout.nb_channels, downmix, float_api); + if (len < 0) + { + return len; + } + /* We need to use the repacketizer to add the self-delimiting lengths + while taking into account the fact that the encoder can now return + more than one frame at a time (e.g. 60 ms CELT-only) */ + rp.AddPacket(tmp_data, 0, len); + len = rp.opus_repacketizer_out_range_impl(0, rp.GetNumFrames(), + data, data_ptr, max_data_bytes - tot_size, (s != this.layout.nb_streams - 1) ? 1 : 0, (vbr == 0 && s == this.layout.nb_streams - 1) ? 1 : 0); + data_ptr += len; + tot_size += len; + } + + return tot_size; + } + + internal static void opus_copy_channel_in_float( + short[] dst, + int dst_ptr, + int dst_stride, + float[] src, + int src_ptr, + int src_stride, + int src_channel, + int frame_size + ) + { + int i; + for (i = 0; i < frame_size; i++) + dst[dst_ptr + i * dst_stride] = Inlines.FLOAT2INT16(src[i * src_stride + src_channel + src_ptr]); + } + + internal static void opus_copy_channel_in_short( + short[] dst, + int dst_ptr, + int dst_stride, + short[] src, + int src_ptr, + int src_stride, + int src_channel, + int frame_size + ) + { + int i; + for (i = 0; i < frame_size; i++) + dst[dst_ptr + i * dst_stride] = src[i * src_stride + src_channel + src_ptr]; + } + + public int EncodeMultistream( + short[] pcm, + int pcm_offset, + int frame_size, + byte[] outputBuffer, + int outputBuffer_offset, + int max_data_bytes + ) + { + // todo: catch error codes here + return opus_multistream_encode_native(opus_copy_channel_in_short, + pcm, pcm_offset, frame_size, outputBuffer, outputBuffer_offset, max_data_bytes, 16, Downmix.downmix_int, 0); + } + + public int EncodeMultistream( + float[] pcm, + int pcm_offset, + int frame_size, + byte[] outputBuffer, + int outputBuffer_offset, + int max_data_bytes + ) + { + // todo: catch error codes here + return opus_multistream_encode_native(opus_copy_channel_in_float, + pcm, pcm_offset, frame_size, outputBuffer, outputBuffer_offset, max_data_bytes, 16, Downmix.downmix_float, 1); + } + + #endregion + + #region Getters and Setters + + public int Bitrate + { + get + { + int s; + int value = 0; + int encoder_ptr = 0; + for (s = 0; s < layout.nb_streams; s++) + { + OpusEncoder enc = encoders[encoder_ptr++]; + value += enc.Bitrate; + } + return value; + } + set + { + if (value < 0 && value != OpusConstants.OPUS_AUTO && value != OpusConstants.OPUS_BITRATE_MAX) + { + throw new ArgumentException("Invalid bitrate"); + } + bitrate_bps = value; + } + } + + public OpusApplication Application + { + get + { + return encoders[0].Application; + } + set + { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) + { + encoders[encoder_ptr].Application = (value); + } + } + } + + public int ForceChannels + { + get + { + return encoders[0].ForceChannels; + } + set + { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) + { + encoders[encoder_ptr].ForceChannels = (value); + } + } + } + + public OpusBandwidth MaxBandwidth + { + get + { + return encoders[0].MaxBandwidth; + } + set + { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) + { + encoders[encoder_ptr].MaxBandwidth = (value); + } + } + } + + public OpusBandwidth Bandwidth + { + get + { + return encoders[0].Bandwidth; + } + set + { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) + { + encoders[encoder_ptr].Bandwidth = (value); + } + } + } + + public bool UseDTX + { + get + { + return encoders[0].UseDTX; + } + set + { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) + { + encoders[encoder_ptr].UseDTX = (value); + } + } + } + + public int Complexity + { + get + { + return encoders[0].Complexity; + } + set + { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) + { + encoders[encoder_ptr].Complexity = (value); + } + } + } + + public OpusMode ForceMode + { + get + { + return encoders[0].ForceMode; + } + set + { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) + { + encoders[encoder_ptr].ForceMode = (value); + } + } + } + + public bool UseInbandFEC + { + get + { + return encoders[0].UseInbandFEC; + } + set + { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) + { + encoders[encoder_ptr].UseInbandFEC = (value); + } + } + } + + public int PacketLossPercent + { + get + { + return encoders[0].PacketLossPercent; + } + set + { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) + { + encoders[encoder_ptr].PacketLossPercent = (value); + } + } + } + + public bool UseVBR + { + get + { + return encoders[0].UseVBR; + } + set + { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) + { + encoders[encoder_ptr].UseVBR = value; + } + } + } + + public bool UseConstrainedVBR + { + get + { + return encoders[0].UseConstrainedVBR; + } + set + { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) + { + encoders[encoder_ptr].UseConstrainedVBR = (value); + } + } + } + + //public int VoiceRatio + //{ + // get + // { + // return encoders[0].VoiceRatio; + // } + // set + // { + // for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) + // { + // encoders[encoder_ptr].VoiceRatio = (value); + // } + // } + //} + + public OpusSignal SignalType + { + get + { + return encoders[0].SignalType; + } + set + { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) + { + encoders[encoder_ptr].SignalType = (value); + } + } + } + + public int Lookahead + { + get + { + return encoders[0].Lookahead; + } + } + + public int SampleRate + { + get + { + return encoders[0].SampleRate; + } + } + + public uint FinalRange + { + get + { + int s; + uint value = 0; + int encoder_ptr = 0; + for (s = 0; s < layout.nb_streams; s++) + { + value ^= encoders[encoder_ptr++].FinalRange; + } + return value; + } + } + + public int LSBDepth + { + get + { + return encoders[0].LSBDepth; + } + set + { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) + { + encoders[encoder_ptr].LSBDepth = (value); + } + } + } + + public bool PredictionDisabled + { + get + { + return encoders[0].PredictionDisabled; + } + set + { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) + { + encoders[encoder_ptr].PredictionDisabled = (value); + } + } + } + + public OpusFramesize ExpertFrameDuration + { + get + { + return variable_duration; + } + set + { + variable_duration = value; + } + } + + public OpusEncoder GetMultistreamEncoderState(int streamId) + { + if (streamId >= layout.nb_streams) + throw new ArgumentException("Requested stream doesn't exist"); + return encoders[streamId]; + } + + #endregion + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusPacketInfo.cs b/Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusPacketInfo.cs new file mode 100644 index 000000000..dec76bbe6 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusPacketInfo.cs @@ -0,0 +1,470 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using Concentus.Common; +using Concentus.Common.CPlusPlus; +using Concentus.Enums; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + + +namespace Concentus.Structs +{ + public class OpusPacketInfo + { + /// + /// The Table of Contents byte for this packet. Contains info about modes, frame length, etc. + /// + public readonly byte TOCByte; + + /// + /// The list of subframes in this packet + /// + public readonly IList Frames; + + /// + /// The index of the start of the payload within the packet + /// + public readonly int PayloadOffset; + + private OpusPacketInfo(byte toc, IList frames, int payloadOffset) + { + TOCByte = toc; + Frames = frames; + PayloadOffset = payloadOffset; + } + + /// + /// Parse an opus packet into a packetinfo object containing one or more frames. + /// Opus_decode will perform this operation internally so most applications do + /// not need to use this function. + /// + /// The packet data to be parsed + /// The index of the beginning of the packet in the data array (usually 0) + /// The packet's length + /// A parsed packet info struct + public static OpusPacketInfo ParseOpusPacket(byte[] packet, int packet_offset, int len) + { + // Find the number of frames first + int numFrames = GetNumFrames(packet, packet_offset, len); + + int payload_offset; + byte out_toc; + byte[][] frames = new byte[numFrames][]; + int[] frames_ptrs = new int[numFrames]; + short[] size = new short[numFrames]; + int packetOffset; + int error = opus_packet_parse_impl(packet, packet_offset, len, 0, out out_toc, frames, frames_ptrs, 0, size, 0, out payload_offset, out packetOffset); + if (error < 0) + { + throw new OpusException("An error occurred while parsing the packet", error); + } + + IList copiedFrames = new List(); + + for (int c = 0; c < numFrames; c++) + { + byte[] nextFrame = new byte[size[c]]; + Array.Copy(frames[c], frames_ptrs[c], nextFrame, 0, nextFrame.Length); + copiedFrames.Add(nextFrame); + } + + return new OpusPacketInfo(out_toc, copiedFrames, payload_offset); + } + + /// + /// Gets the number of samples per frame from an Opus packet. + /// + /// Opus packet. This must contain at least one byte of data + /// Sampling rate in Hz. This must be a multiple of 400, or inaccurate results will be returned. + /// Number of samples per frame + public static int GetNumSamplesPerFrame(byte[] packet, int packet_offset, int Fs) + { + int audiosize; + if ((packet[packet_offset] & 0x80) != 0) + { + audiosize = ((packet[packet_offset] >> 3) & 0x3); + audiosize = (Fs << audiosize) / 400; + } + else if ((packet[packet_offset] & 0x60) == 0x60) + { + audiosize = ((packet[packet_offset] & 0x08) != 0) ? Fs / 50 : Fs / 100; + } + else + { + audiosize = ((packet[packet_offset] >> 3) & 0x3); + if (audiosize == 3) + audiosize = Fs * 60 / 1000; + else + audiosize = (Fs << audiosize) / 100; + } + return audiosize; + } + + /// + /// Gets the encoded bandwidth of an Opus packet. Note that you are not forced to decode at this bandwidth + /// + /// An Opus packet (must be at least 1 byte). + /// An OpusBandwidth value + public static OpusBandwidth GetBandwidth(byte[] packet, int packet_offset) + { + OpusBandwidth bandwidth; + if ((packet[packet_offset] & 0x80) != 0) + { + bandwidth = OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND + ((packet[packet_offset] >> 5) & 0x3); + if (bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) + bandwidth = OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND; + } + else if ((packet[packet_offset] & 0x60) == 0x60) + { + bandwidth = ((packet[packet_offset] & 0x10) != 0) ? OpusBandwidth.OPUS_BANDWIDTH_FULLBAND : + OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND; + } + else { + bandwidth = OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND + ((packet[packet_offset] >> 5) & 0x3); + } + return bandwidth; + } + + /// + /// Gets the number of encoded channels of an Opus packet. Note that you are not forced to decode with this channel count. + /// + /// An opus packet (must be at least 1 byte) + /// The number of channels + public static int GetNumEncodedChannels(byte[] packet, int packet_offset) + { + return ((packet[packet_offset] & 0x4) != 0) ? 2 : 1; + } + + /// + /// Gets the number of frames in an Opus packet. + /// + /// An Opus packet + /// The packet's length (must be at least 1) + /// The number of frames in the packet + public static int GetNumFrames(byte[] packet, int packet_offset, int len) + { + int count; + if (len < 1) + return OpusError.OPUS_BAD_ARG; + count = packet[packet_offset] & 0x3; + if (count == 0) + return 1; + else if (count != 3) + return 2; + else if (len < 2) + return OpusError.OPUS_INVALID_PACKET; + else + return packet[packet_offset + 1] & 0x3F; + } + + /// + /// Gets the number of samples of an Opus packet. + /// + /// An Opus packet + /// The packet's length + /// The decoder's sampling rate in Hz. This must be a multiple of 400 + /// The size of the PCM samples that this packet will be decoded to at the specified sample rate + public static int GetNumSamples(byte[] packet, int packet_offset, int len, + int Fs) + { + int samples; + int count = GetNumFrames(packet, packet_offset, len); + + if (count < 0) + return count; + + samples = count * GetNumSamplesPerFrame(packet, packet_offset, Fs); + /* Can't have more than 120 ms */ + if (samples * 25 > Fs * 3) + return OpusError.OPUS_INVALID_PACKET; + else + return samples; + } + + /// + /// Gets the number of samples of an Opus packet. + /// + /// Your current decoder state + /// An Opus packet + /// The packet's length + /// The size of the PCM samples that this packet will be decoded to by the specified decoder + public static int GetNumSamples(OpusDecoder dec, + byte[] packet, int packet_offset, int len) + { + return GetNumSamples(packet, packet_offset, len, dec.Fs); + } + + /// + /// Gets the mode that was used to encode this packet. + /// Normally there is nothing you can really do with this, other than debugging. + /// + /// An Opus packet + /// The OpusMode used by the encoder + public static OpusMode GetEncoderMode(byte[] packet, int packet_offset) + { + OpusMode mode; + if ((packet[packet_offset] & 0x80) != 0) + { + mode = OpusMode.MODE_CELT_ONLY; + } + else if ((packet[packet_offset] & 0x60) == 0x60) + { + mode = OpusMode.MODE_HYBRID; + } + else { + mode = OpusMode.MODE_SILK_ONLY; + } + return mode; + } + + internal static int encode_size(int size, byte[] data, int data_ptr) + { + if (size < 252) + { + data[data_ptr] = (byte)size; + return 1; + } + else { + data[data_ptr] = (byte)(252 + (size & 0x3)); + data[data_ptr + 1] = (byte)((size - (int)data[data_ptr]) >> 2); + return 2; + } + } + + internal static int parse_size(byte[] data, int data_ptr, int len, BoxedValueShort size) + { + if (len < 1) + { + size.Val = -1; + return -1; + } + else if (data[data_ptr] < 252) + { + size.Val = data[data_ptr]; + return 1; + } + else if (len < 2) + { + size.Val = -1; + return -1; + } + else { + size.Val = (short)(4 * data[data_ptr + 1] + data[data_ptr]); + return 2; + } + } + + internal static int opus_packet_parse_impl(byte[] data, int data_ptr, int len, + int self_delimited, out byte out_toc, + byte[][] frames, int[] frames_ptrs, int frames_ptr, short[] sizes, int sizes_ptr, + out int payload_offset, out int packet_offset) + { + int i, bytes; + int count; + int cbr; + byte ch, toc; + int framesize; + int last_size; + int pad = 0; + int data0 = data_ptr; + out_toc = 0; + payload_offset = 0; + packet_offset = 0; + + if (sizes == null || len < 0) + return OpusError.OPUS_BAD_ARG; + if (len == 0) + return OpusError.OPUS_INVALID_PACKET; + + framesize = GetNumSamplesPerFrame(data, data_ptr, 48000); + + cbr = 0; + toc = data[data_ptr++]; + len--; + last_size = len; + switch (toc & 0x3) + { + /* One frame */ + case 0: + count = 1; + break; + /* Two CBR frames */ + case 1: + count = 2; + cbr = 1; + if (self_delimited == 0) + { + if ((len & 0x1) != 0) + return OpusError.OPUS_INVALID_PACKET; + last_size = len / 2; + /* If last_size doesn't fit in size[0], we'll catch it later */ + sizes[sizes_ptr] = (short)last_size; + } + break; + /* Two VBR frames */ + case 2: + count = 2; + BoxedValueShort boxed_size = new BoxedValueShort(sizes[sizes_ptr]); + bytes = parse_size(data, data_ptr, len, boxed_size); + sizes[sizes_ptr] = boxed_size.Val; + len -= bytes; + if (sizes[sizes_ptr] < 0 || sizes[sizes_ptr] > len) + return OpusError.OPUS_INVALID_PACKET; + data_ptr += bytes; + last_size = len - sizes[sizes_ptr]; + break; + /* Multiple CBR/VBR frames (from 0 to 120 ms) */ + default: /*case 3:*/ + if (len < 1) + return OpusError.OPUS_INVALID_PACKET; + /* Number of frames encoded in bits 0 to 5 */ + ch = data[data_ptr++]; + count = ch & 0x3F; + if (count <= 0 || framesize * count > 5760) + return OpusError.OPUS_INVALID_PACKET; + len--; + /* Padding flag is bit 6 */ + if ((ch & 0x40) != 0) + { + int p; + do + { + int tmp; + if (len <= 0) + return OpusError.OPUS_INVALID_PACKET; + p = data[data_ptr++]; + len--; + tmp = p == 255 ? 254 : p; + len -= tmp; + pad += tmp; + } while (p == 255); + } + if (len < 0) + return OpusError.OPUS_INVALID_PACKET; + /* VBR flag is bit 7 */ + cbr = (ch & 0x80) != 0 ? 0 : 1; + if (cbr == 0) + { + /* VBR case */ + last_size = len; + for (i = 0; i < count - 1; i++) + { + boxed_size = new BoxedValueShort(sizes[sizes_ptr + i]); + bytes = parse_size(data, data_ptr, len, boxed_size); + sizes[sizes_ptr + i] = boxed_size.Val; + len -= bytes; + if (sizes[sizes_ptr + i] < 0 || sizes[sizes_ptr + i] > len) + return OpusError.OPUS_INVALID_PACKET; + data_ptr += bytes; + last_size -= bytes + sizes[sizes_ptr + i]; + } + if (last_size < 0) + return OpusError.OPUS_INVALID_PACKET; + } + else if (self_delimited == 0) + { + /* CBR case */ + last_size = len / count; + if (last_size * count != len) + return OpusError.OPUS_INVALID_PACKET; + for (i = 0; i < count - 1; i++) + sizes[sizes_ptr + i] = (short)last_size; + } + break; + } + + /* Self-delimited framing has an extra size for the last frame. */ + if (self_delimited != 0) + { + BoxedValueShort boxed_size = new BoxedValueShort(sizes[sizes_ptr + count - 1]); + bytes = parse_size(data, data_ptr, len, boxed_size); + sizes[sizes_ptr + count - 1] = boxed_size.Val; + len -= bytes; + if (sizes[sizes_ptr + count - 1] < 0 || sizes[sizes_ptr + count - 1] > len) + return OpusError.OPUS_INVALID_PACKET; + data_ptr += bytes; + /* For CBR packets, apply the size to all the frames. */ + if (cbr != 0) + { + if (sizes[sizes_ptr + count - 1] * count > len) + return OpusError.OPUS_INVALID_PACKET; + for (i = 0; i < count - 1; i++) + sizes[sizes_ptr + i] = sizes[sizes_ptr + count - 1]; + } + else if (bytes + sizes[sizes_ptr + count - 1] > last_size) + return OpusError.OPUS_INVALID_PACKET; + } + else + { + /* Because it's not encoded explicitly, it's possible the size of the + last packet (or all the packets, for the CBR case) is larger than + 1275. Reject them here.*/ + if (last_size > 1275) + return OpusError.OPUS_INVALID_PACKET; + sizes[sizes_ptr + count - 1] = (short)last_size; + } + + payload_offset = (int)(data_ptr - data0); + + for (i = 0; i < count; i++) + { + if (frames != null) + frames[frames_ptr + i] = data; + if (frames_ptrs != null) + frames_ptrs[frames_ptr + i] = data_ptr; + data_ptr += sizes[sizes_ptr + i]; + } + + packet_offset = pad + (int)(data_ptr - data0); + + out_toc = toc; + + return count; + } + + // used internally + //internal static int opus_packet_parse(byte[] data, int data_ptr, int len, + // out byte out_toc, Pointer> frames, + // short[] size, int size_ptr, out int payload_offset) + //{ + // int dummy; + // return OpusPacketInfo.opus_packet_parse_impl(data, data_ptr, len, 0, out out_toc, + // frames, size, size_ptr, out payload_offset, out dummy); + //} + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusRepacketizer.cs b/Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusRepacketizer.cs new file mode 100644 index 000000000..60013572a --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/Structs/OpusRepacketizer.cs @@ -0,0 +1,573 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using Concentus.Common; +using Concentus.Common.CPlusPlus; +using Concentus.Enums; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + + +namespace Concentus.Structs +{ + public class OpusRepacketizer + { + internal byte toc = 0; + internal int nb_frames = 0; + internal readonly byte[][] frames = new byte[48][]; + internal readonly int[] frames_ptrs = new int[48]; + internal readonly short[] len = new short[48]; + internal int framesize = 0; + + /** (Re)initializes a previously allocated repacketizer state. + * The state must be at least the size returned by opus_repacketizer_get_size(). + * This can be used for applications which use their own allocator instead of + * malloc(). + * It must also be called to reset the queue of packets waiting to be + * repacketized, which is necessary if the maximum packet duration of 120 ms + * is reached or if you wish to submit packets with a different Opus + * configuration (coding mode, audio bandwidth, frame size, or channel count). + * Failure to do so will prevent a new packet from being added with + * opus_repacketizer_cat(). + * @see opus_repacketizer_create + * @see opus_repacketizer_get_size + * @see opus_repacketizer_cat + * @param rp OpusRepacketizer*: The repacketizer state to + * (re)initialize. + */ + public void Reset() + { + this.nb_frames = 0; + } + + /// + /// Creates a new repacketizer + /// + public OpusRepacketizer() + { + this.Reset(); + } + + internal int opus_repacketizer_cat_impl(byte[] data, int data_ptr, int len, int self_delimited) + { + byte dummy_toc; + int dummy_offset; + int curr_nb_frames, ret; + /* Set of check ToC */ + if (len < 1) + { + return OpusError.OPUS_INVALID_PACKET; + } + + if (this.nb_frames == 0) + { + this.toc = data[data_ptr]; + this.framesize = OpusPacketInfo.GetNumSamplesPerFrame(data, data_ptr, 8000); + } + else if ((this.toc & 0xFC) != (data[data_ptr] & 0xFC)) + { + /*fprintf(stderr, "toc mismatch: 0x%x vs 0x%x\n", rp.toc, data[0]);*/ + return OpusError.OPUS_INVALID_PACKET; + } + curr_nb_frames = OpusPacketInfo.GetNumFrames(data, data_ptr, len); + if (curr_nb_frames < 1) + return OpusError.OPUS_INVALID_PACKET; + + /* Check the 120 ms maximum packet size */ + if ((curr_nb_frames + this.nb_frames) * this.framesize > 960) + { + return OpusError.OPUS_INVALID_PACKET; + } + + ret = OpusPacketInfo.opus_packet_parse_impl(data, data_ptr, len, self_delimited, out dummy_toc, this.frames, this.frames_ptrs, this.nb_frames, this.len, this.nb_frames, out dummy_offset, out dummy_offset); + if (ret < 1) return ret; + + this.nb_frames += curr_nb_frames; + return OpusError.OPUS_OK; + } + + /** opus_repacketizer_cat. Add a packet to the current repacketizer state. + * This packet must match the configuration of any packets already submitted + * for repacketization since the last call to opus_repacketizer_init(). + * This means that it must have the same coding mode, audio bandwidth, frame + * size, and channel count. + * This can be checked in advance by examining the top 6 bits of the first + * byte of the packet, and ensuring they match the top 6 bits of the first + * byte of any previously submitted packet. + * The total duration of audio in the repacketizer state also must not exceed + * 120 ms, the maximum duration of a single packet, after adding this packet. + * + * The contents of the current repacketizer state can be extracted into new + * packets using opus_repacketizer_out() or opus_repacketizer_out_range(). + * + * In order to add a packet with a different configuration or to add more + * audio beyond 120 ms, you must clear the repacketizer state by calling + * opus_repacketizer_init(). + * If a packet is too large to add to the current repacketizer state, no part + * of it is added, even if it contains multiple frames, some of which might + * fit. + * If you wish to be able to add parts of such packets, you should first use + * another repacketizer to split the packet into pieces and add them + * individually. + * @see opus_repacketizer_out_range + * @see opus_repacketizer_out + * @see opus_repacketizer_init + * @param rp OpusRepacketizer*: The repacketizer state to which to + * add the packet. + * @param[in] data const unsigned char*: The packet data. + * The application must ensure + * this pointer remains valid + * until the next call to + * opus_repacketizer_init() or + * opus_repacketizer_destroy(). + * @param len opus_int32: The number of bytes in the packet data. + * @returns An error code indicating whether or not the operation succeeded. + * @retval #OPUS_OK The packet's contents have been added to the repacketizer + * state. + * @retval #OPUS_INVALID_PACKET The packet did not have a valid TOC sequence, + * the packet's TOC sequence was not compatible + * with previously submitted packets (because + * the coding mode, audio bandwidth, frame size, + * or channel count did not match), or adding + * this packet would increase the total amount of + * audio stored in the repacketizer state to more + * than 120 ms. + */ + public int AddPacket(byte[] data, int data_offset, int len) + { + return opus_repacketizer_cat_impl(data, data_offset, len, 0); + } + + /** Return the total number of frames contained in packet data submitted to + * the repacketizer state so far via opus_repacketizer_cat() since the last + * call to opus_repacketizer_init() or opus_repacketizer_create(). + * This defines the valid range of packets that can be extracted with + * opus_repacketizer_out_range() or opus_repacketizer_out(). + * @param rp OpusRepacketizer*: The repacketizer state containing the + * frames. + * @returns The total number of frames contained in the packet data submitted + * to the repacketizer state. + */ + public int GetNumFrames() + { + return this.nb_frames; + } + + internal int opus_repacketizer_out_range_impl(int begin, int end, + byte[] data, int data_ptr, int maxlen, int self_delimited, int pad) + { + int i, count; + int tot_size; + int ptr; + + if (begin < 0 || begin >= end || end > this.nb_frames) + { + /*fprintf(stderr, "%d %d %d\n", begin, end, rp.nb_frames);*/ + return OpusError.OPUS_BAD_ARG; + } + count = end - begin; + + if (self_delimited != 0) + tot_size = 1 + (this.len[count - 1] >= 252 ? 1 : 0); + else + tot_size = 0; + + ptr = data_ptr; + if (count == 1) + { + /* Code 0 */ + tot_size += this.len[0] + 1; + if (tot_size > maxlen) + return OpusError.OPUS_BUFFER_TOO_SMALL; + data[ptr++] = (byte)(this.toc & 0xFC); + } + else if (count == 2) + { + if (this.len[1] == this.len[0]) + { + /* Code 1 */ + tot_size += 2 * this.len[0] + 1; + if (tot_size > maxlen) + return OpusError.OPUS_BUFFER_TOO_SMALL; + data[ptr++] = (byte)((this.toc & 0xFC) | 0x1); + } + else { + /* Code 2 */ + tot_size += this.len[0] + this.len[1] + 2 + (this.len[0] >= 252 ? 1 : 0); + if (tot_size > maxlen) + return OpusError.OPUS_BUFFER_TOO_SMALL; + data[ptr++] = (byte)((this.toc & 0xFC) | 0x2); + ptr += OpusPacketInfo.encode_size(this.len[0], data, ptr); + } + } + if (count > 2 || (pad != 0 && tot_size < maxlen)) + { + /* Code 3 */ + int vbr; + int pad_amount = 0; + + /* Restart the process for the padding case */ + ptr = data_ptr; + if (self_delimited != 0) + tot_size = 1 + (this.len[count - 1] >= 252 ? 1 : 0); + else + tot_size = 0; + vbr = 0; + for (i = 1; i < count; i++) + { + if (this.len[i] != this.len[0]) + { + vbr = 1; + break; + } + } + if (vbr != 0) + { + tot_size += 2; + for (i = 0; i < count - 1; i++) + tot_size += 1 + (this.len[i] >= 252 ? 1 : 0) + this.len[i]; + tot_size += this.len[count - 1]; + + if (tot_size > maxlen) + return OpusError.OPUS_BUFFER_TOO_SMALL; + data[ptr++] = (byte)((this.toc & 0xFC) | 0x3); + data[ptr++] = (byte)(count | 0x80); + } + else + { + tot_size += count * this.len[0] + 2; + if (tot_size > maxlen) + return OpusError.OPUS_BUFFER_TOO_SMALL; + data[ptr++] = (byte)((this.toc & 0xFC) | 0x3); + data[ptr++] = (byte)(count); + } + + pad_amount = pad != 0 ? (maxlen - tot_size) : 0; + + if (pad_amount != 0) + { + int nb_255s; + data[data_ptr + 1] |= 0x40; + nb_255s = (pad_amount - 1) / 255; + for (i = 0; i < nb_255s; i++) + { + data[ptr++] = 255; + } + + data[ptr++] = (byte)(pad_amount - 255 * nb_255s - 1); + tot_size += pad_amount; + } + + if (vbr != 0) + { + for (i = 0; i < count - 1; i++) + ptr += (OpusPacketInfo.encode_size(this.len[i], data, ptr)); + } + } + + if (self_delimited != 0) + { + int sdlen = OpusPacketInfo.encode_size(this.len[count - 1], data, ptr); + ptr += (sdlen); + } + + /* Copy the actual data */ + for (i = begin; i < count + begin; i++) + { + + if (this.frames[i] == data) + { + /* Using OPUS_MOVE() instead of OPUS_COPY() in case we're doing in-place + padding from opus_packet_pad or opus_packet_unpad(). */ + Arrays.MemMove(data, frames_ptrs[i], ptr, this.len[i]); + } + else + { + Array.Copy(this.frames[i], frames_ptrs[i], data, ptr, this.len[i]); + } + ptr += this.len[i]; + } + + if (pad != 0) + { + /* Fill padding with zeros. */ + //Arrays.MemSetWithOffset(ptr.Data, 0, ptr.Offset, data.Offset + maxlen - ptr.Offset); + // FIXME why did they not just use a MemSet(0) here? + while (ptr < data_ptr + maxlen) + { + data[ptr++] = 0; + } + } + + return tot_size; + } + + /** Construct a new packet from data previously submitted to the repacketizer + * state via opus_repacketizer_cat(). + * @param rp OpusRepacketizer*: The repacketizer state from which to + * construct the new packet. + * @param begin int: The index of the first frame in the current + * repacketizer state to include in the output. + * @param end int: One past the index of the last frame in the + * current repacketizer state to include in the + * output. + * @param[out] data const unsigned char*: The buffer in which to + * store the output packet. + * @param maxlen opus_int32: The maximum number of bytes to store in + * the output buffer. In order to guarantee + * success, this should be at least + * 1276 for a single frame, + * or for multiple frames, + * 1277*(end-begin). + * However, 1*(end-begin) plus + * the size of all packet data submitted to + * the repacketizer since the last call to + * opus_repacketizer_init() or + * opus_repacketizer_create() is also + * sufficient, and possibly much smaller. + * @returns The total size of the output packet on success, or an error code + * on failure. + * @retval #OPUS_BAD_ARG [begin,end) was an invalid range of + * frames (begin < 0, begin >= end, or end > + * opus_repacketizer_get_nb_frames()). + * @retval #OPUS_BUFFER_TOO_SMALL \a maxlen was insufficient to contain the + * complete output packet. + */ + public int CreatePacket(int begin, int end, byte[] data, int data_offset, int maxlen) + { + return opus_repacketizer_out_range_impl(begin, end, data, data_offset, maxlen, 0, 0); + } + + /** Construct a new packet from data previously submitted to the repacketizer + * state via opus_repacketizer_cat(). + * This is a convenience routine that returns all the data submitted so far + * in a single packet. + * It is equivalent to calling + * @code + * opus_repacketizer_out_range(rp, 0, opus_repacketizer_get_nb_frames(rp), + * data, maxlen) + * @endcode + * @param rp OpusRepacketizer*: The repacketizer state from which to + * construct the new packet. + * @param[out] data const unsigned char*: The buffer in which to + * store the output packet. + * @param maxlen opus_int32: The maximum number of bytes to store in + * the output buffer. In order to guarantee + * success, this should be at least + * 1277*opus_repacketizer_get_nb_frames(rp). + * However, + * 1*opus_repacketizer_get_nb_frames(rp) + * plus the size of all packet data + * submitted to the repacketizer since the + * last call to opus_repacketizer_init() or + * opus_repacketizer_create() is also + * sufficient, and possibly much smaller. + * @returns The total size of the output packet on success, or an error code + * on failure. + * @retval #OPUS_BUFFER_TOO_SMALL \a maxlen was insufficient to contain the + * complete output packet. + */ + public int CreatePacket(byte[] data, int data_offset, int maxlen) + { + return opus_repacketizer_out_range_impl(0, this.nb_frames, data, data_offset, maxlen, 0, 0); + } + + /** Pads a given Opus packet to a larger size (possibly changing the TOC sequence). + * @param[in,out] data const unsigned char*: The buffer containing the + * packet to pad. + * @param len opus_int32: The size of the packet. + * This must be at least 1. + * @param new_len opus_int32: The desired size of the packet after padding. + * This must be at least as large as len. + * @returns an error code + * @retval #OPUS_OK \a on success. + * @retval #OPUS_BAD_ARG \a len was less than 1 or new_len was less than len. + * @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet. + */ + public static int PadPacket(byte[] data, int data_offset, int len, int new_len) + { + OpusRepacketizer rp = new OpusRepacketizer(); + int ret; + if (len < 1) + return OpusError.OPUS_BAD_ARG; + if (len == new_len) + return OpusError.OPUS_OK; + else if (len > new_len) + return OpusError.OPUS_BAD_ARG; + rp.Reset(); + /* Moving payload to the end of the packet so we can do in-place padding */ + Arrays.MemMove(data, data_offset, data_offset + new_len - len, len); + //data.MemMoveTo(data.Point(new_len - len), len); + rp.AddPacket(data, data_offset + new_len - len, len); + ret = rp.opus_repacketizer_out_range_impl(0, rp.nb_frames, data, data_offset, new_len, 0, 1); + if (ret > 0) + return OpusError.OPUS_OK; + else + return ret; + } + + /** Remove all padding from a given Opus packet and rewrite the TOC sequence to + * minimize space usage. + * @param[in,out] data const unsigned char*: The buffer containing the + * packet to strip. + * @param len opus_int32: The size of the packet. + * This must be at least 1. + * @returns The new size of the output packet on success, or an error code + * on failure. + * @retval #OPUS_BAD_ARG \a len was less than 1. + * @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet. + */ + public static int UnpadPacket(byte[] data, int data_offset, int len) + { + int ret; + if (len < 1) + return OpusError.OPUS_BAD_ARG; + + OpusRepacketizer rp = new OpusRepacketizer(); + rp.Reset(); + ret = rp.AddPacket(data, data_offset, len); + if (ret < 0) + return ret; + ret = rp.opus_repacketizer_out_range_impl(0, rp.nb_frames, data, data_offset, len, 0, 0); + Inlines.OpusAssert(ret > 0 && ret <= len); + + return ret; + } + + /** Pads a given Opus multi-stream packet to a larger size (possibly changing the TOC sequence). + * @param[in,out] data const unsigned char*: The buffer containing the + * packet to pad. + * @param len opus_int32: The size of the packet. + * This must be at least 1. + * @param new_len opus_int32: The desired size of the packet after padding. + * This must be at least 1. + * @param nb_streams opus_int32: The number of streams (not channels) in the packet. + * This must be at least as large as len. + * @returns an error code + * @retval #OPUS_OK \a on success. + * @retval #OPUS_BAD_ARG \a len was less than 1. + * @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet. + */ + public static int PadMultistreamPacket(byte[] data, int data_offset, int len, int new_len, int nb_streams) + { + int s; + int count; + byte dummy_toc; + short[] size = new short[48]; + int packet_offset; + int dummy_offset; + int amount; + + if (len < 1) + return OpusError.OPUS_BAD_ARG; + if (len == new_len) + return OpusError.OPUS_OK; + else if (len > new_len) + return OpusError.OPUS_BAD_ARG; + amount = new_len - len; + /* Seek to last stream */ + for (s = 0; s < nb_streams - 1; s++) + { + if (len <= 0) + return OpusError.OPUS_INVALID_PACKET; + count = OpusPacketInfo.opus_packet_parse_impl(data, data_offset, len, 1, out dummy_toc, null, null, 0, + size, 0, out dummy_offset, out packet_offset); + if (count < 0) + return count; + data_offset += packet_offset; + len -= packet_offset; + } + return PadPacket(data, data_offset, len, len + amount); + } + + // FIXME THIS METHOD FAILS IN TEST_OPUS_ENCODE + /** Remove all padding from a given Opus multi-stream packet and rewrite the TOC sequence to + * minimize space usage. + * @param[in,out] data const unsigned char*: The buffer containing the + * packet to strip. + * @param len opus_int32: The size of the packet. + * This must be at least 1. + * @param nb_streams opus_int32: The number of streams (not channels) in the packet. + * This must be at least 1. + * @returns The new size of the output packet on success, or an error code + * on failure. + * @retval #OPUS_BAD_ARG \a len was less than 1 or new_len was less than len. + * @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet. + */ + public static int UnpadMultistreamPacket(byte[] data, int data_offset, int len, int nb_streams) + { + int s; + byte dummy_toc; + short[] size = new short[48]; + int packet_offset; + int dummy_offset; + OpusRepacketizer rp = new OpusRepacketizer(); + int dst; + int dst_len; + + if (len < 1) + return OpusError.OPUS_BAD_ARG; + dst = data_offset; + dst_len = 0; + /* Unpad all frames */ + for (s = 0; s < nb_streams; s++) + { + int ret; + int self_delimited = ((s != nb_streams) ? 1 : 0) - 1; + if (len <= 0) + return OpusError.OPUS_INVALID_PACKET; + rp.Reset(); + ret = OpusPacketInfo.opus_packet_parse_impl(data, data_offset, len, self_delimited, out dummy_toc, null, null, 0, + size, 0, out dummy_offset, out packet_offset); + if (ret < 0) + return ret; + ret = rp.opus_repacketizer_cat_impl(data, data_offset, packet_offset, self_delimited); + if (ret < 0) + return ret; + ret = rp.opus_repacketizer_out_range_impl(0, rp.nb_frames, data, dst, len, self_delimited, 0); + if (ret < 0) + return ret; + else + dst_len += ret; + dst += ret; + data_offset += packet_offset; + len -= packet_offset; + } + return dst_len; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/Structs/StereoWidthState.cs b/Libraries/Concentus/CSharp/Concentus/Opus/Structs/StereoWidthState.cs new file mode 100644 index 000000000..eec1c12a0 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/Structs/StereoWidthState.cs @@ -0,0 +1,61 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + + +namespace Concentus.Structs +{ + internal class StereoWidthState + { + internal int XX; + internal int XY; + internal int YY; + internal int smoothed_width; + internal int max_follower; + + internal void Reset() + { + XX = 0; + XY = 0; + YY = 0; + smoothed_width = 0; + max_follower = 0; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/Structs/TonalityAnalysisState.cs b/Libraries/Concentus/CSharp/Concentus/Opus/Structs/TonalityAnalysisState.cs new file mode 100644 index 000000000..22b3613eb --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/Structs/TonalityAnalysisState.cs @@ -0,0 +1,140 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using Concentus.Celt.Structs; +using Concentus.Common; +using Concentus.Common.CPlusPlus; +using Concentus; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + + +namespace Concentus.Structs +{ + internal class TonalityAnalysisState + { + internal bool enabled = false; + internal readonly float[] angle = new float[240]; + internal readonly float[] d_angle = new float[240]; + internal readonly float[] d2_angle = new float[240]; + internal readonly int[] inmem = new int[OpusConstants.ANALYSIS_BUF_SIZE]; + internal int mem_fill; /* number of usable samples in the buffer */ + internal readonly float[] prev_band_tonality = new float[OpusConstants.NB_TBANDS]; + internal float prev_tonality; + internal readonly float[][] E = Arrays.InitTwoDimensionalArray(OpusConstants.NB_FRAMES, OpusConstants.NB_TBANDS); + internal readonly float[] lowE = new float[OpusConstants.NB_TBANDS]; + internal readonly float[] highE = new float[OpusConstants.NB_TBANDS]; + internal readonly float[] meanE = new float[OpusConstants.NB_TOT_BANDS]; + internal readonly float[] mem = new float[32]; + internal readonly float[] cmean = new float[8]; + internal readonly float[] std = new float[9]; + internal float music_prob; + internal float Etracker; + internal float lowECount; + internal int E_count; + internal int last_music; + internal int last_transition; + internal int count; + internal readonly float[] subframe_mem = new float[3]; + internal int analysis_offset; + /** Probability of having speech for time i to DETECT_SIZE-1 (and music before). + pspeech[0] is the probability that all frames in the window are speech. */ + internal readonly float[] pspeech = new float[OpusConstants.DETECT_SIZE]; + /** Probability of having music for time i to DETECT_SIZE-1 (and speech before). + pmusic[0] is the probability that all frames in the window are music. */ + internal readonly float[] pmusic = new float[OpusConstants.DETECT_SIZE]; + internal float speech_confidence; + internal float music_confidence; + internal int speech_confidence_count; + internal int music_confidence_count; + internal int write_pos; + internal int read_pos; + internal int read_subframe; + internal readonly AnalysisInfo[] info = new AnalysisInfo[OpusConstants.DETECT_SIZE]; + + internal TonalityAnalysisState() + { + for (int c = 0; c < OpusConstants.DETECT_SIZE; c++) + { + info[c] = new AnalysisInfo(); + } + } + + internal void Reset() + { + Arrays.MemSetFloat(angle,0, 240); + Arrays.MemSetFloat(d_angle,0, 240); + Arrays.MemSetFloat(d2_angle,0, 240); + Arrays.MemSetInt(inmem, 0, OpusConstants.ANALYSIS_BUF_SIZE); + mem_fill = 0; + Arrays.MemSetFloat(prev_band_tonality,0, OpusConstants.NB_TBANDS); + prev_tonality = 0; + for (int c = 0; c < OpusConstants.NB_FRAMES; c++) + { + Arrays.MemSetFloat(E[c], 0, OpusConstants.NB_TBANDS); + } + Arrays.MemSetFloat(lowE,0, OpusConstants.NB_TBANDS); + Arrays.MemSetFloat(highE,0, OpusConstants.NB_TBANDS); + Arrays.MemSetFloat(meanE,0, OpusConstants.NB_TOT_BANDS); + Arrays.MemSetFloat(mem,0, 32); + Arrays.MemSetFloat(cmean,0, 8); + Arrays.MemSetFloat(std,0, 9); + music_prob = 0; + Etracker = 0; + lowECount = 0; + E_count = 0; + last_music = 0; + last_transition = 0; + count = 0; + Arrays.MemSetFloat(subframe_mem,0, 3); + analysis_offset = 0; + Arrays.MemSetFloat(pspeech,0, OpusConstants.DETECT_SIZE); + Arrays.MemSetFloat(pmusic,0, OpusConstants.DETECT_SIZE); + speech_confidence = 0; + music_confidence = 0; + speech_confidence_count = 0; + music_confidence_count = 0; + write_pos = 0; + read_pos = 0; + read_subframe = 0; + for (int c = 0; c < OpusConstants.DETECT_SIZE; c++) + { + info[c].Reset(); + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/Structs/VorbisLayout.cs b/Libraries/Concentus/CSharp/Concentus/Opus/Structs/VorbisLayout.cs new file mode 100644 index 000000000..d42c8ae17 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/Structs/VorbisLayout.cs @@ -0,0 +1,69 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + + +namespace Concentus.Structs +{ + internal class VorbisLayout + { + internal VorbisLayout(int streams, int coupled_streams, byte[] map) + { + nb_streams = streams; + nb_coupled_streams = coupled_streams; + mapping = map; + } + + internal int nb_streams; + internal int nb_coupled_streams; + internal byte[] mapping; + + /* Index is nb_channel-1*/ + internal static readonly VorbisLayout[] vorbis_mappings = { + new VorbisLayout(1, 0, new byte[] {0}), /* 1: mono */ + new VorbisLayout(1, 1, new byte[] {0, 1}), /* 2: stereo */ + new VorbisLayout(2, 1, new byte[] {0, 2, 1}), /* 3: 1-d surround */ + new VorbisLayout(2, 2, new byte[] {0, 1, 2, 3}), /* 4: quadraphonic surround */ + new VorbisLayout(3, 2, new byte[] {0, 4, 1, 2, 3}), /* 5: 5-channel surround */ + new VorbisLayout(4, 2, new byte[] {0, 4, 1, 2, 3, 5}), /* 6: 5.1 surround */ + new VorbisLayout(4, 3, new byte[] {0, 4, 1, 2, 3, 5, 6}), /* 7: 6.1 surround */ + new VorbisLayout(5, 3, new byte[] {0, 6, 1, 2, 3, 4, 5, 7}), /* 8: 7.1 surround */ + }; + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Opus/Tables.cs b/Libraries/Concentus/CSharp/Concentus/Opus/Tables.cs new file mode 100644 index 000000000..da40214fa --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Opus/Tables.cs @@ -0,0 +1,298 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using Concentus.Common.CPlusPlus; +using Concentus.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + + +namespace Concentus +{ + internal static class Tables + { + internal static readonly float[] dct_table = { + 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, + 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, + 0.351851f, 0.338330f, 0.311806f, 0.273300f, 0.224292f, 0.166664f, 0.102631f, 0.034654f, + -0.034654f,-0.102631f,-0.166664f,-0.224292f,-0.273300f,-0.311806f,-0.338330f,-0.351851f, + 0.346760f, 0.293969f, 0.196424f, 0.068975f,-0.068975f,-0.196424f,-0.293969f,-0.346760f, + -0.346760f,-0.293969f,-0.196424f,-0.068975f, 0.068975f, 0.196424f, 0.293969f, 0.346760f, + 0.338330f, 0.224292f, 0.034654f,-0.166664f,-0.311806f,-0.351851f,-0.273300f,-0.102631f, + 0.102631f, 0.273300f, 0.351851f, 0.311806f, 0.166664f,-0.034654f,-0.224292f,-0.338330f, + 0.326641f, 0.135299f,-0.135299f,-0.326641f,-0.326641f,-0.135299f, 0.135299f, 0.326641f, + 0.326641f, 0.135299f,-0.135299f,-0.326641f,-0.326641f,-0.135299f, 0.135299f, 0.326641f, + 0.311806f, 0.034654f,-0.273300f,-0.338330f,-0.102631f, 0.224292f, 0.351851f, 0.166664f, + -0.166664f,-0.351851f,-0.224292f, 0.102631f, 0.338330f, 0.273300f,-0.034654f,-0.311806f, + 0.293969f,-0.068975f,-0.346760f,-0.196424f, 0.196424f, 0.346760f, 0.068975f,-0.293969f, + -0.293969f, 0.068975f, 0.346760f, 0.196424f,-0.196424f,-0.346760f,-0.068975f, 0.293969f, + 0.273300f,-0.166664f,-0.338330f, 0.034654f, 0.351851f, 0.102631f,-0.311806f,-0.224292f, + 0.224292f, 0.311806f,-0.102631f,-0.351851f,-0.034654f, 0.338330f, 0.166664f,-0.273300f, + }; + + internal static readonly float[] analysis_window = { + 0.000043f, 0.000171f, 0.000385f, 0.000685f, 0.001071f, 0.001541f, 0.002098f, 0.002739f, + 0.003466f, 0.004278f, 0.005174f, 0.006156f, 0.007222f, 0.008373f, 0.009607f, 0.010926f, + 0.012329f, 0.013815f, 0.015385f, 0.017037f, 0.018772f, 0.020590f, 0.022490f, 0.024472f, + 0.026535f, 0.028679f, 0.030904f, 0.033210f, 0.035595f, 0.038060f, 0.040604f, 0.043227f, + 0.045928f, 0.048707f, 0.051564f, 0.054497f, 0.057506f, 0.060591f, 0.063752f, 0.066987f, + 0.070297f, 0.073680f, 0.077136f, 0.080665f, 0.084265f, 0.087937f, 0.091679f, 0.095492f, + 0.099373f, 0.103323f, 0.107342f, 0.111427f, 0.115579f, 0.119797f, 0.124080f, 0.128428f, + 0.132839f, 0.137313f, 0.141849f, 0.146447f, 0.151105f, 0.155823f, 0.160600f, 0.165435f, + 0.170327f, 0.175276f, 0.180280f, 0.185340f, 0.190453f, 0.195619f, 0.200838f, 0.206107f, + 0.211427f, 0.216797f, 0.222215f, 0.227680f, 0.233193f, 0.238751f, 0.244353f, 0.250000f, + 0.255689f, 0.261421f, 0.267193f, 0.273005f, 0.278856f, 0.284744f, 0.290670f, 0.296632f, + 0.302628f, 0.308658f, 0.314721f, 0.320816f, 0.326941f, 0.333097f, 0.339280f, 0.345492f, + 0.351729f, 0.357992f, 0.364280f, 0.370590f, 0.376923f, 0.383277f, 0.389651f, 0.396044f, + 0.402455f, 0.408882f, 0.415325f, 0.421783f, 0.428254f, 0.434737f, 0.441231f, 0.447736f, + 0.454249f, 0.460770f, 0.467298f, 0.473832f, 0.480370f, 0.486912f, 0.493455f, 0.500000f, + 0.506545f, 0.513088f, 0.519630f, 0.526168f, 0.532702f, 0.539230f, 0.545751f, 0.552264f, + 0.558769f, 0.565263f, 0.571746f, 0.578217f, 0.584675f, 0.591118f, 0.597545f, 0.603956f, + 0.610349f, 0.616723f, 0.623077f, 0.629410f, 0.635720f, 0.642008f, 0.648271f, 0.654508f, + 0.660720f, 0.666903f, 0.673059f, 0.679184f, 0.685279f, 0.691342f, 0.697372f, 0.703368f, + 0.709330f, 0.715256f, 0.721144f, 0.726995f, 0.732807f, 0.738579f, 0.744311f, 0.750000f, + 0.755647f, 0.761249f, 0.766807f, 0.772320f, 0.777785f, 0.783203f, 0.788573f, 0.793893f, + 0.799162f, 0.804381f, 0.809547f, 0.814660f, 0.819720f, 0.824724f, 0.829673f, 0.834565f, + 0.839400f, 0.844177f, 0.848895f, 0.853553f, 0.858151f, 0.862687f, 0.867161f, 0.871572f, + 0.875920f, 0.880203f, 0.884421f, 0.888573f, 0.892658f, 0.896677f, 0.900627f, 0.904508f, + 0.908321f, 0.912063f, 0.915735f, 0.919335f, 0.922864f, 0.926320f, 0.929703f, 0.933013f, + 0.936248f, 0.939409f, 0.942494f, 0.945503f, 0.948436f, 0.951293f, 0.954072f, 0.956773f, + 0.959396f, 0.961940f, 0.964405f, 0.966790f, 0.969096f, 0.971321f, 0.973465f, 0.975528f, + 0.977510f, 0.979410f, 0.981228f, 0.982963f, 0.984615f, 0.986185f, 0.987671f, 0.989074f, + 0.990393f, 0.991627f, 0.992778f, 0.993844f, 0.994826f, 0.995722f, 0.996534f, 0.997261f, + 0.997902f, 0.998459f, 0.998929f, 0.999315f, 0.999615f, 0.999829f, 0.999957f, 1.000000f, + }; + + internal static readonly int[] tbands/*[NB_TBANDS + 1]*/ = { + 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 68, 80, 96, 120 + }; + + internal static readonly int[] extra_bands/*[NB_TOT_BANDS + 1]*/ = { + 1, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 68, 80, 96, 120, 160, 200 + }; + + /*static const float tweight[NB_TBANDS+1] = { + .3, .4, .5, .6, .7, .8, .9, 1., 1., 1., 1., 1., 1., 1., .8, .7, .6, .5 + };*/ + + /* RMS error was 0.138320, seed was 1361535663 */ + + internal static readonly float[] weights/*[422]*/ = { + /* hidden layer */ + -0.0941125f, -0.302976f, -0.603555f, -0.19393f, -0.185983f, + -0.601617f, -0.0465317f, -0.114563f, -0.103599f, -0.618938f, + -0.317859f, -0.169949f, -0.0702885f, 0.148065f, 0.409524f, + 0.548432f, 0.367649f, -0.494393f, 0.764306f, -1.83957f, + 0.170849f, 12.786f, -1.08848f, -1.27284f, -16.2606f, + 24.1773f, -5.57454f, -0.17276f, -0.163388f, -0.224421f, + -0.0948944f, -0.0728695f, -0.26557f, -0.100283f, -0.0515459f, + -0.146142f, -0.120674f, -0.180655f, 0.12857f, 0.442138f, + -0.493735f, 0.167767f, 0.206699f, -0.197567f, 0.417999f, + 1.50364f, -0.773341f, -10.0401f, 0.401872f, 2.97966f, + 15.2165f, -1.88905f, -1.19254f, 0.0285397f, -0.00405139f, + 0.0707565f, 0.00825699f, -0.0927269f, -0.010393f, -0.00428882f, + -0.00489743f, -0.0709731f, -0.00255992f, 0.0395619f, 0.226424f, + 0.0325231f, 0.162175f, -0.100118f, 0.485789f, 0.12697f, + 0.285937f, 0.0155637f, 0.10546f, 3.05558f, 1.15059f, + -1.00904f, -1.83088f, 3.31766f, -3.42516f, -0.119135f, + -0.0405654f, 0.00690068f, 0.0179877f, -0.0382487f, 0.00597941f, + -0.0183611f, 0.00190395f, -0.144322f, -0.0435671f, 0.000990594f, + 0.221087f, 0.142405f, 0.484066f, 0.404395f, 0.511955f, + -0.237255f, 0.241742f, 0.35045f, -0.699428f, 10.3993f, + 2.6507f, -2.43459f, -4.18838f, 1.05928f, 1.71067f, + 0.00667811f, -0.0721335f, -0.0397346f, 0.0362704f, -0.11496f, + -0.0235776f, 0.0082161f, -0.0141741f, -0.0329699f, -0.0354253f, + 0.00277404f, -0.290654f, -1.14767f, -0.319157f, -0.686544f, + 0.36897f, 0.478899f, 0.182579f, -0.411069f, 0.881104f, + -4.60683f, 1.4697f, 0.335845f, -1.81905f, -30.1699f, + 5.55225f, 0.0019508f, -0.123576f, -0.0727332f, -0.0641597f, + -0.0534458f, -0.108166f, -0.0937368f, -0.0697883f, -0.0275475f, + -0.192309f, -0.110074f, 0.285375f, -0.405597f, 0.0926724f, + -0.287881f, -0.851193f, -0.099493f, -0.233764f, -1.2852f, + 1.13611f, 3.12168f, -0.0699f, -1.86216f, 2.65292f, + -7.31036f, 2.44776f, -0.00111802f, -0.0632786f, -0.0376296f, + -0.149851f, 0.142963f, 0.184368f, 0.123433f, 0.0756158f, + 0.117312f, 0.0933395f, 0.0692163f, 0.0842592f, 0.0704683f, + 0.0589963f, 0.0942205f, -0.448862f, 0.0262677f, 0.270352f, + -0.262317f, 0.172586f, 2.00227f, -0.159216f, 0.038422f, + 10.2073f, 4.15536f, -2.3407f, -0.0550265f, 0.00964792f, + -0.141336f, 0.0274501f, 0.0343921f, -0.0487428f, 0.0950172f, + -0.00775017f, -0.0372492f, -0.00548121f, -0.0663695f, 0.0960506f, + -0.200008f, -0.0412827f, 0.58728f, 0.0515787f, 0.337254f, + 0.855024f, 0.668371f, -0.114904f, -3.62962f, -0.467477f, + -0.215472f, 2.61537f, 0.406117f, -1.36373f, 0.0425394f, + 0.12208f, 0.0934502f, 0.123055f, 0.0340935f, -0.142466f, + 0.035037f, -0.0490666f, 0.0733208f, 0.0576672f, 0.123984f, + -0.0517194f, -0.253018f, 0.590565f, 0.145849f, 0.315185f, + 0.221534f, -0.149081f, 0.216161f, -0.349575f, 24.5664f, + -0.994196f, 0.614289f, -18.7905f, -2.83277f, -0.716801f, + -0.347201f, 0.479515f, -0.246027f, 0.0758683f, 0.137293f, + -0.17781f, 0.118751f, -0.00108329f, -0.237334f, 0.355732f, + -0.12991f, -0.0547627f, -0.318576f, -0.325524f, 0.180494f, + -0.0625604f, 0.141219f, 0.344064f, 0.37658f, -0.591772f, + 5.8427f, -0.38075f, 0.221894f, -1.41934f, -1.87943e+06f, + 1.34114f, 0.0283355f, -0.0447856f, -0.0211466f, -0.0256927f, + 0.0139618f, 0.0207934f, -0.0107666f, 0.0110969f, 0.0586069f, + -0.0253545f, -0.0328433f, 0.11872f, -0.216943f, 0.145748f, + 0.119808f, -0.0915211f, -0.120647f, -0.0787719f, -0.143644f, + -0.595116f, -1.152f, -1.25335f, -1.17092f, 4.34023f, + -975268.0f, -1.37033f, -0.0401123f, 0.210602f, -0.136656f, + 0.135962f, -0.0523293f, 0.0444604f, 0.0143928f, 0.00412666f, + -0.0193003f, 0.218452f, -0.110204f, -2.02563f, 0.918238f, + -2.45362f, 1.19542f, -0.061362f, -1.92243f, 0.308111f, + 0.49764f, 0.912356f, 0.209272f, -2.34525f, 2.19326f, + -6.47121f, 1.69771f, -0.725123f, 0.0118929f, 0.0377944f, + 0.0554003f, 0.0226452f, -0.0704421f, -0.0300309f, 0.0122978f, + -0.0041782f, -0.0686612f, 0.0313115f, 0.039111f, 0.364111f, + -0.0945548f, 0.0229876f, -0.17414f, 0.329795f, 0.114714f, + 0.30022f, 0.106997f, 0.132355f, 5.79932f, 0.908058f, + -0.905324f, -3.3561f, 0.190647f, 0.184211f, -0.673648f, + 0.231807f, -0.0586222f, 0.230752f, -0.438277f, 0.245857f, + -0.17215f, 0.0876383f, -0.720512f, 0.162515f, 0.0170571f, + 0.101781f, 0.388477f, 1.32931f, 1.08548f, -0.936301f, + -2.36958f, -6.71988f, -3.44376f, 2.13818f, 14.2318f, + 4.91459f, -3.09052f, -9.69191f, -0.768234f, 1.79604f, + 0.0549653f, 0.163399f, 0.0797025f, 0.0343933f, -0.0555876f, + -0.00505673f, 0.0187258f, 0.0326628f, 0.0231486f, 0.15573f, + 0.0476223f, -0.254824f, 1.60155f, -0.801221f, 2.55496f, + 0.737629f, -1.36249f, -0.695463f, -2.44301f, -1.73188f, + 3.95279f, 1.89068f, 0.486087f, -11.3343f, 3.9416e+06f, + + /* output layer */ + -0.381439f, 0.12115f, -0.906927f, 2.93878f, 1.6388f, + 0.882811f, 0.874344f, 1.21726f, -0.874545f, 0.321706f, + 0.785055f, 0.946558f, -0.575066f, -3.46553f, 0.884905f, + 0.0924047f, -9.90712f, 0.391338f, 0.160103f, -2.04954f, + 4.1455f, 0.0684029f, -0.144761f, -0.285282f, 0.379244f, + -1.1584f, -0.0277241f, -9.85f, -4.82386f, 3.71333f, + 3.87308f, 3.52558f}; + + internal static readonly int[] topo = { 25, 15, 2 }; + + // fixme: move this into an MLP class singleton or something? + internal static readonly MLP net = new MLP() + { + layers = 3, + topo = topo, + weights = weights + }; + + internal static readonly float[] tansig_table/*[201]*/ = { + 0.000000f, 0.039979f, 0.079830f, 0.119427f, 0.158649f, + 0.197375f, 0.235496f, 0.272905f, 0.309507f, 0.345214f, + 0.379949f, 0.413644f, 0.446244f, 0.477700f, 0.507977f, + 0.537050f, 0.564900f, 0.591519f, 0.616909f, 0.641077f, + 0.664037f, 0.685809f, 0.706419f, 0.725897f, 0.744277f, + 0.761594f, 0.777888f, 0.793199f, 0.807569f, 0.821040f, + 0.833655f, 0.845456f, 0.856485f, 0.866784f, 0.876393f, + 0.885352f, 0.893698f, 0.901468f, 0.908698f, 0.915420f, + 0.921669f, 0.927473f, 0.932862f, 0.937863f, 0.942503f, + 0.946806f, 0.950795f, 0.954492f, 0.957917f, 0.961090f, + 0.964028f, 0.966747f, 0.969265f, 0.971594f, 0.973749f, + 0.975743f, 0.977587f, 0.979293f, 0.980869f, 0.982327f, + 0.983675f, 0.984921f, 0.986072f, 0.987136f, 0.988119f, + 0.989027f, 0.989867f, 0.990642f, 0.991359f, 0.992020f, + 0.992631f, 0.993196f, 0.993718f, 0.994199f, 0.994644f, + 0.995055f, 0.995434f, 0.995784f, 0.996108f, 0.996407f, + 0.996682f, 0.996937f, 0.997172f, 0.997389f, 0.997590f, + 0.997775f, 0.997946f, 0.998104f, 0.998249f, 0.998384f, + 0.998508f, 0.998623f, 0.998728f, 0.998826f, 0.998916f, + 0.999000f, 0.999076f, 0.999147f, 0.999213f, 0.999273f, + 0.999329f, 0.999381f, 0.999428f, 0.999472f, 0.999513f, + 0.999550f, 0.999585f, 0.999617f, 0.999646f, 0.999673f, + 0.999699f, 0.999722f, 0.999743f, 0.999763f, 0.999781f, + 0.999798f, 0.999813f, 0.999828f, 0.999841f, 0.999853f, + 0.999865f, 0.999875f, 0.999885f, 0.999893f, 0.999902f, + 0.999909f, 0.999916f, 0.999923f, 0.999929f, 0.999934f, + 0.999939f, 0.999944f, 0.999948f, 0.999952f, 0.999956f, + 0.999959f, 0.999962f, 0.999965f, 0.999968f, 0.999970f, + 0.999973f, 0.999975f, 0.999977f, 0.999978f, 0.999980f, + 0.999982f, 0.999983f, 0.999984f, 0.999986f, 0.999987f, + 0.999988f, 0.999989f, 0.999990f, 0.999990f, 0.999991f, + 0.999992f, 0.999992f, 0.999993f, 0.999994f, 0.999994f, + 0.999994f, 0.999995f, 0.999995f, 0.999996f, 0.999996f, + 0.999996f, 0.999997f, 0.999997f, 0.999997f, 0.999997f, + 0.999997f, 0.999998f, 0.999998f, 0.999998f, 0.999998f, + 0.999998f, 0.999998f, 0.999999f, 0.999999f, 0.999999f, + 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, + 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, + 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, + 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, + 1.000000f, + }; + + // from opus_encoder.c + /* Transition tables for the voice and music. First column is the + middle (memoriless) threshold. The second column is the hysteresis + (difference with the middle) */ + internal static readonly int[] mono_voice_bandwidth_thresholds = { + 11000, 1000, /* NB<->MB */ + 14000, 1000, /* MB<->WB */ + 17000, 1000, /* WB<->SWB */ + 21000, 2000, /* SWB<->FB */ + }; + internal static readonly int[] mono_music_bandwidth_thresholds = { + 12000, 1000, /* NB<->MB */ + 15000, 1000, /* MB<->WB */ + 18000, 2000, /* WB<->SWB */ + 22000, 2000, /* SWB<->FB */ + }; + internal static readonly int[] stereo_voice_bandwidth_thresholds = { + 11000, 1000, /* NB<->MB */ + 14000, 1000, /* MB<->WB */ + 21000, 2000, /* WB<->SWB */ + 28000, 2000, /* SWB<->FB */ + }; + internal static readonly int[] stereo_music_bandwidth_thresholds = { + 12000, 1000, /* NB<->MB */ + 18000, 2000, /* MB<->WB */ + 21000, 2000, /* WB<->SWB */ + 30000, 2000, /* SWB<->FB */ + }; + + /* Threshold bit-rates for switching between mono and stereo */ + public const int stereo_voice_threshold = 30000; + public const int stereo_music_threshold = 30000; + + /* Threshold bit-rate for switching between SILK/hybrid and CELT-only */ + internal static readonly int[][] mode_thresholds = { + /* voice */ /* music */ + new int[]{ 64000, 16000}, /* mono */ + new int[]{ 36000, 16000}, /* stereo */ + }; + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Readme.txt b/Libraries/Concentus/CSharp/Concentus/Readme.txt new file mode 100644 index 000000000..59e2da2fc --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Readme.txt @@ -0,0 +1,52 @@ +Hi there, welcome to the Concentus package. You're about to ask me for sample code, so I'll get straight to it: + +If you're already using something like P/Opus then your code probably looks like this: + + [DllImport(OPUS_TARGET_DLL, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr opus_encoder_create(int Fs, int channels, int application, out IntPtr error); + + [DllImport(OPUS_TARGET_DLL, CallingConvention = CallingConvention.Cdecl)] + private static extern int opus_encode(IntPtr st, byte[] pcm, int frame_size, IntPtr data, int max_data_bytes); + + // Initialize + IntPtr error; + IntPtr _encoder = opus_encoder_create(48000, 1, OPUS_APPLICATION_AUDIO, out error); + opus_encoder_ctl(_encoder, OPUS_SET_BITRATE_REQUEST, 12000); + + // Encoding loop + byte[] inputAudioSamplesInterleaved; // 16-bit pcm data interleaved into a byte array + byte[] outputBuffer[1000]; + int frameSize = 960; + + unsafe + { + fixed (byte* benc = outputBuffer) + { + IntPtr encodedPtr = new IntPtr(benc); + int thisPacketSizeOrSometimesAnErrorCode = opus_encode(_encoder, inputAudioSamplesInterleaved, frameSize, encodedPtr, outputBuffer.Length); + } + } + +Here is what you can replace it with: + + // Initialize + OpusEncoder encoder = OpusEncoder.Create(48000, 1, OpusApplication.OPUS_APPLICATION_AUDIO); + encoder.Bitrate = 12000; + + // Encoding loop + short[] inputAudioSamples + byte[] outputBuffer[1000]; + int frameSize = 960; + + int thisPacketSize = encoder.Encode(inputAudioSamples, 0, frameSize, outputBuffer, 0, outputBuffer.Length); // this throws OpusException on a failure, rather than returning a negative number + +And here is the decoder path: + + OpusDecoder decoder = OpusDecoder.Create(48000, 1); + + // Decoding loop + byte[] compressedPacket; + int frameSize = 960; // must be same as framesize used in input, you can use OpusPacketInfo.GetNumSamples() to determine this dynamically + short[] outputBuffer = new short[frameSize]; + + int thisFrameSize = _decoder.Decode(compressedPacket, 0, compressedPacket.Length, outputBuffer, 0, frameSize, false); diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/ApplySineWindow.cs b/Libraries/Concentus/CSharp/Concentus/Silk/ApplySineWindow.cs new file mode 100644 index 000000000..1c96c6cfb --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/ApplySineWindow.cs @@ -0,0 +1,118 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + + internal static class ApplySineWindow + { + /* Apply sine window to signal vector. */ + /* Window types: */ + /* 1 . sine window from 0 to pi/2 */ + /* 2 . sine window from pi/2 to pi */ + /* Every other sample is linearly interpolated, for speed. */ + /* Window length must be between 16 and 120 (incl) and a multiple of 4. */ + + /* Matlab code for table: + for k=16:9*4:16+2*9*4, fprintf(' %7.d,', -round(65536*pi ./ (k:4:k+8*4))); fprintf('\n'); end + */ + private static readonly short[] freq_table_Q16 = { + 12111, 9804, 8235, 7100, 6239, 5565, 5022, 4575, 4202, + 3885, 3612, 3375, 3167, 2984, 2820, 2674, 2542, 2422, + 2313, 2214, 2123, 2038, 1961, 1889, 1822, 1760, 1702, +}; + + internal static void silk_apply_sine_window( + short[] px_win, /* O Pointer to windowed signal */ + int px_win_ptr, + short[] px, /* I Pointer to input signal */ + int px_ptr, + int win_type, /* I Selects a window type */ + int length /* I Window length, multiple of 4 */ + ) + { + int k, f_Q16, c_Q16; + int S0_Q16, S1_Q16; + + Inlines.OpusAssert(win_type == 1 || win_type == 2); + + /* Length must be in a range from 16 to 120 and a multiple of 4 */ + Inlines.OpusAssert(length >= 16 && length <= 120); + Inlines.OpusAssert((length & 3) == 0); + + /* Frequency */ + k = (length >> 2) - 4; + Inlines.OpusAssert(k >= 0 && k <= 26); + f_Q16 = (int)freq_table_Q16[k]; + + /* Factor used for cosine approximation */ + c_Q16 = Inlines.silk_SMULWB((int)f_Q16, -f_Q16); + Inlines.OpusAssert(c_Q16 >= -32768); + + /* initialize state */ + if (win_type == 1) + { + /* start from 0 */ + S0_Q16 = 0; + /* approximation of sin(f) */ + S1_Q16 = f_Q16 + Inlines.silk_RSHIFT(length, 3); + } + else { + /* start from 1 */ + S0_Q16 = ((int)1 << 16); + /* approximation of cos(f) */ + S1_Q16 = ((int)1 << 16) + Inlines.silk_RSHIFT(c_Q16, 1) + Inlines.silk_RSHIFT(length, 4); + } + + /* Uses the recursive equation: sin(n*f) = 2 * cos(f) * sin((n-1)*f) - sin((n-2)*f) */ + /* 4 samples at a time */ + for (k = 0; k < length; k += 4) + { + int pxwk = px_win_ptr + k; + int pxk = px_ptr + k; + px_win[pxwk] = (short)Inlines.silk_SMULWB(Inlines.silk_RSHIFT(S0_Q16 + S1_Q16, 1), px[pxk]); + px_win[pxwk + 1] = (short)Inlines.silk_SMULWB(S1_Q16, px[pxk + 1]); + S0_Q16 = Inlines.silk_SMULWB(S1_Q16, c_Q16) + Inlines.silk_LSHIFT(S1_Q16, 1) - S0_Q16 + 1; + S0_Q16 = Inlines.silk_min(S0_Q16, ((int)1 << 16)); + + px_win[pxwk + 2] = (short)Inlines.silk_SMULWB(Inlines.silk_RSHIFT(S0_Q16 + S1_Q16, 1), px[pxk + 2]); + px_win[pxwk + 3] = (short)Inlines.silk_SMULWB(S0_Q16, px[pxk + 3]); + S1_Q16 = Inlines.silk_SMULWB(S0_Q16, c_Q16) + Inlines.silk_LSHIFT(S0_Q16, 1) - S1_Q16; + S1_Q16 = Inlines.silk_min(S1_Q16, ((int)1 << 16)); + } + } + } +} \ No newline at end of file diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/BWExpander.cs b/Libraries/Concentus/CSharp/Concentus/Silk/BWExpander.cs new file mode 100644 index 000000000..905793a28 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/BWExpander.cs @@ -0,0 +1,90 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal static class BWExpander + { + /// + /// Chirp (bw expand) LP AR filter (Fixed point implementation) + /// + /// I/O AR filter to be expanded (without leading 1) + /// I length of ar + /// I chirp factor (typically in range (0..1) ) + internal static void silk_bwexpander_32( + int[] ar, /* I/O AR filter to be expanded (without leading 1) */ + int d, /* I Length of ar */ + int chirp_Q16 /* I Chirp factor in Q16 */ +) + { + int i; + int chirp_minus_one_Q16 = chirp_Q16 - 65536; + + for (i = 0; i < d - 1; i++) + { + ar[i] = Inlines.silk_SMULWW(chirp_Q16, ar[i]); + chirp_Q16 += Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, chirp_minus_one_Q16), 16); + } + ar[d - 1] = Inlines.silk_SMULWW(chirp_Q16, ar[d - 1]); + } + + /// + /// Chirp (bw expand) LP AR filter (Fixed point implementation) + /// + /// I/O AR filter to be expanded (without leading 1) + /// I length of ar + /// I chirp factor (typically in range (0..1) ) + internal static void silk_bwexpander( + short[] ar, + int d, + int chirp_Q16) + { + int i; + int chirp_minus_one_Q16 = chirp_Q16 - 65536; + + /* NB: Dont use silk_SMULWB, instead of silk_RSHIFT_ROUND( silk_MUL(), 16 ), below. */ + /* Bias in silk_SMULWB can lead to unstable filters */ + for (i = 0; i < d - 1; i++) + { + ar[i] = (short)Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, ar[i]), 16); + chirp_Q16 += Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, chirp_minus_one_Q16), 16); + } + ar[d - 1] = (short)Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, ar[d - 1]), 16); + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/BurgModified.cs b/Libraries/Concentus/CSharp/Concentus/Silk/BurgModified.cs new file mode 100644 index 000000000..95773c19a --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/BurgModified.cs @@ -0,0 +1,326 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#if !UNSAFE + +namespace Concentus.Silk +{ + using Concentus.Celt; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System; + using System.Diagnostics; + + internal static class BurgModified + { + /* subfr_length * nb_subfr = ( 0.005 * 16000 + 16 ) * 4 = 384 */ + private const int MAX_FRAME_SIZE = 384; + private const int QA = 25; + private const int N_BITS_HEAD_ROOM = 2; + private const int MIN_RSHIFTS = -16; + private const int MAX_RSHIFTS = (32 - QA); + + /* Compute reflection coefficients from input signal */ + internal static void silk_burg_modified( + BoxedValueInt res_nrg, /* O Residual energy */ + BoxedValueInt res_nrg_Q, /* O Residual energy Q value */ + int[] A_Q16, /* O Prediction coefficients (length order) */ + short[] x, /* I Input signal, length: nb_subfr * ( D + subfr_length ) */ + int x_ptr, + int minInvGain_Q30, /* I Inverse of max prediction gain */ + int subfr_length, /* I Input signal subframe length (incl. D preceding samples) */ + int nb_subfr, /* I Number of subframes stacked in x */ + int D /* I Order */ + ) + { + int k, n, s, lz, rshifts, reached_max_gain; + int C0, num, nrg, rc_Q31, invGain_Q30, Atmp_QA, Atmp1, tmp1, tmp2, x1, x2; + int x_offset; + int[] C_first_row = new int[SilkConstants.SILK_MAX_ORDER_LPC]; + int[] C_last_row = new int[SilkConstants.SILK_MAX_ORDER_LPC]; + int[] Af_QA = new int[SilkConstants.SILK_MAX_ORDER_LPC]; + int[] CAf = new int[SilkConstants.SILK_MAX_ORDER_LPC + 1]; + int[] CAb = new int[SilkConstants.SILK_MAX_ORDER_LPC + 1]; + int[] xcorr = new int[SilkConstants.SILK_MAX_ORDER_LPC]; + long C0_64; + + Inlines.OpusAssert(subfr_length * nb_subfr <= MAX_FRAME_SIZE); + + /* Compute autocorrelations, added over subframes */ + C0_64 = Inlines.silk_inner_prod16_aligned_64(x, x_ptr, x, x_ptr, subfr_length * nb_subfr); + lz = Inlines.silk_CLZ64(C0_64); + rshifts = 32 + 1 + N_BITS_HEAD_ROOM - lz; + if (rshifts > MAX_RSHIFTS) rshifts = MAX_RSHIFTS; + if (rshifts < MIN_RSHIFTS) rshifts = MIN_RSHIFTS; + + if (rshifts > 0) + { + C0 = (int)Inlines.silk_RSHIFT64(C0_64, rshifts); + } + else { + C0 = Inlines.silk_LSHIFT32((int)C0_64, -rshifts); + } + + CAb[0] = CAf[0] = C0 + Inlines.silk_SMMUL(((int)((TuningParameters.FIND_LPC_COND_FAC) * ((long)1 << (32)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LPC_COND_FAC, 32)*/, C0) + 1; /* Q(-rshifts) */ + Arrays.MemSetInt(C_first_row, 0, SilkConstants.SILK_MAX_ORDER_LPC); + if (rshifts > 0) + { + for (s = 0; s < nb_subfr; s++) + { + x_offset = x_ptr + s * subfr_length; + for (n = 1; n < D + 1; n++) + { + C_first_row[n - 1] += (int)Inlines.silk_RSHIFT64( + Inlines.silk_inner_prod16_aligned_64(x, x_offset, x, x_offset + n, subfr_length - n), rshifts); + } + } + } + else { + for (s = 0; s < nb_subfr; s++) + { + int i; + int d; + x_offset = x_ptr + s * subfr_length; + CeltPitchXCorr.pitch_xcorr(x, x_offset, x, x_offset + 1, xcorr, subfr_length - D, D); + for (n = 1; n < D + 1; n++) + { + for (i = n + subfr_length - D, d = 0; i < subfr_length; i++) + d = Inlines.MAC16_16(d, x[x_offset + i], x[x_offset + i - n]); + xcorr[n - 1] += d; + } + for (n = 1; n < D + 1; n++) + { + C_first_row[n - 1] += Inlines.silk_LSHIFT32(xcorr[n - 1], -rshifts); + } + } + } + Array.Copy(C_first_row, C_last_row, SilkConstants.SILK_MAX_ORDER_LPC); + + /* Initialize */ + CAb[0] = CAf[0] = C0 + Inlines.silk_SMMUL(((int)((TuningParameters.FIND_LPC_COND_FAC) * ((long)1 << (32)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LPC_COND_FAC, 32)*/, C0) + 1; /* Q(-rshifts) */ + + invGain_Q30 = (int)1 << 30; + reached_max_gain = 0; + for (n = 0; n < D; n++) + { + /* Update first row of correlation matrix (without first element) */ + /* Update last row of correlation matrix (without last element, stored in reversed order) */ + /* Update C * Af */ + /* Update C * flipud(Af) (stored in reversed order) */ + if (rshifts > -2) + { + for (s = 0; s < nb_subfr; s++) + { + x_offset = x_ptr + s * subfr_length; + x1 = -Inlines.silk_LSHIFT32((int)x[x_offset + n], 16 - rshifts); /* Q(16-rshifts) */ + x2 = -Inlines.silk_LSHIFT32((int)x[x_offset + subfr_length - n - 1], 16 - rshifts); /* Q(16-rshifts) */ + tmp1 = Inlines.silk_LSHIFT32((int)x[x_offset + n], QA - 16); /* Q(QA-16) */ + tmp2 = Inlines.silk_LSHIFT32((int)x[x_offset + subfr_length - n - 1], QA - 16); /* Q(QA-16) */ + for (k = 0; k < n; k++) + { + C_first_row[k] = Inlines.silk_SMLAWB(C_first_row[k], x1, x[x_offset + n - k - 1]); /* Q( -rshifts ) */ + C_last_row[k] = Inlines.silk_SMLAWB(C_last_row[k], x2, x[x_offset + subfr_length - n + k]); /* Q( -rshifts ) */ + Atmp_QA = Af_QA[k]; + tmp1 = Inlines.silk_SMLAWB(tmp1, Atmp_QA, x[x_offset + n - k - 1]); /* Q(QA-16) */ + tmp2 = Inlines.silk_SMLAWB(tmp2, Atmp_QA, x[x_offset + subfr_length - n + k]); /* Q(QA-16) */ + } + tmp1 = Inlines.silk_LSHIFT32(-tmp1, 32 - QA - rshifts); /* Q(16-rshifts) */ + tmp2 = Inlines.silk_LSHIFT32(-tmp2, 32 - QA - rshifts); /* Q(16-rshifts) */ + for (k = 0; k <= n; k++) + { + CAf[k] = Inlines.silk_SMLAWB(CAf[k], tmp1, x[x_offset + n - k]); /* Q( -rshift ) */ + CAb[k] = Inlines.silk_SMLAWB(CAb[k], tmp2, x[x_offset + subfr_length - n + k - 1]); /* Q( -rshift ) */ + } + } + } + else { + for (s = 0; s < nb_subfr; s++) + { + x_offset = x_ptr + s * subfr_length; + x1 = -Inlines.silk_LSHIFT32((int)x[x_offset + n], -rshifts); /* Q( -rshifts ) */ + x2 = -Inlines.silk_LSHIFT32((int)x[x_offset + subfr_length - n - 1], -rshifts); /* Q( -rshifts ) */ + tmp1 = Inlines.silk_LSHIFT32((int)x[x_offset + n], 17); /* Q17 */ + tmp2 = Inlines.silk_LSHIFT32((int)x[x_offset + subfr_length - n - 1], 17); /* Q17 */ + for (k = 0; k < n; k++) + { + C_first_row[k] = Inlines.silk_MLA(C_first_row[k], x1, x[x_offset + n - k - 1]); /* Q( -rshifts ) */ + C_last_row[k] = Inlines.silk_MLA(C_last_row[k], x2, x[x_offset + subfr_length - n + k]); /* Q( -rshifts ) */ + Atmp1 = Inlines.silk_RSHIFT_ROUND(Af_QA[k], QA - 17); /* Q17 */ + tmp1 = Inlines.silk_MLA(tmp1, x[x_offset + n - k - 1], Atmp1); /* Q17 */ + tmp2 = Inlines.silk_MLA(tmp2, x[x_offset + subfr_length - n + k], Atmp1); /* Q17 */ + } + tmp1 = -tmp1; /* Q17 */ + tmp2 = -tmp2; /* Q17 */ + for (k = 0; k <= n; k++) + { + CAf[k] = Inlines.silk_SMLAWW(CAf[k], tmp1, + Inlines.silk_LSHIFT32((int)x[x_offset + n - k], -rshifts - 1)); /* Q( -rshift ) */ + CAb[k] = Inlines.silk_SMLAWW(CAb[k], tmp2, + Inlines.silk_LSHIFT32((int)x[x_offset + subfr_length - n + k - 1], -rshifts - 1)); /* Q( -rshift ) */ + } + } + } + + /* Calculate nominator and denominator for the next order reflection (parcor) coefficient */ + tmp1 = C_first_row[n]; /* Q( -rshifts ) */ + tmp2 = C_last_row[n]; /* Q( -rshifts ) */ + num = 0; /* Q( -rshifts ) */ + nrg = Inlines.silk_ADD32(CAb[0], CAf[0]); /* Q( 1-rshifts ) */ + for (k = 0; k < n; k++) + { + Atmp_QA = Af_QA[k]; + lz = Inlines.silk_CLZ32(Inlines.silk_abs(Atmp_QA)) - 1; + lz = Inlines.silk_min(32 - QA, lz); + Atmp1 = Inlines.silk_LSHIFT32(Atmp_QA, lz); /* Q( QA + lz ) */ + + tmp1 = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(C_last_row[n - k - 1], Atmp1), 32 - QA - lz); /* Q( -rshifts ) */ + tmp2 = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(C_first_row[n - k - 1], Atmp1), 32 - QA - lz); /* Q( -rshifts ) */ + num = Inlines.silk_ADD_LSHIFT32(num, Inlines.silk_SMMUL(CAb[n - k], Atmp1), 32 - QA - lz); /* Q( -rshifts ) */ + nrg = Inlines.silk_ADD_LSHIFT32(nrg, Inlines.silk_SMMUL(Inlines.silk_ADD32(CAb[k + 1], CAf[k + 1]), + Atmp1), 32 - QA - lz); /* Q( 1-rshifts ) */ + } + CAf[n + 1] = tmp1; /* Q( -rshifts ) */ + CAb[n + 1] = tmp2; /* Q( -rshifts ) */ + num = Inlines.silk_ADD32(num, tmp2); /* Q( -rshifts ) */ + num = Inlines.silk_LSHIFT32(-num, 1); /* Q( 1-rshifts ) */ + + /* Calculate the next order reflection (parcor) coefficient */ + if (Inlines.silk_abs(num) < nrg) + { + rc_Q31 = Inlines.silk_DIV32_varQ(num, nrg, 31); + } + else { + rc_Q31 = (num > 0) ? int.MaxValue : int.MinValue; + } + + /* Update inverse prediction gain */ + tmp1 = ((int)1 << 30) - Inlines.silk_SMMUL(rc_Q31, rc_Q31); + tmp1 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, tmp1), 2); + if (tmp1 <= minInvGain_Q30) + { + /* Max prediction gain exceeded; set reflection coefficient such that max prediction gain is exactly hit */ + tmp2 = ((int)1 << 30) - Inlines.silk_DIV32_varQ(minInvGain_Q30, invGain_Q30, 30); /* Q30 */ + rc_Q31 = Inlines.silk_SQRT_APPROX(tmp2); /* Q15 */ + /* Newton-Raphson iteration */ + rc_Q31 = Inlines.silk_RSHIFT32(rc_Q31 + Inlines.silk_DIV32(tmp2, rc_Q31), 1); /* Q15 */ + rc_Q31 = Inlines.silk_LSHIFT32(rc_Q31, 16); /* Q31 */ + if (num < 0) + { + /* Ensure adjusted reflection coefficients has the original sign */ + rc_Q31 = -rc_Q31; + } + invGain_Q30 = minInvGain_Q30; + reached_max_gain = 1; + } + else { + invGain_Q30 = tmp1; + } + + /* Update the AR coefficients */ + for (k = 0; k < (n + 1) >> 1; k++) + { + tmp1 = Af_QA[k]; /* QA */ + tmp2 = Af_QA[n - k - 1]; /* QA */ + Af_QA[k] = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(tmp2, rc_Q31), 1); /* QA */ + Af_QA[n - k - 1] = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(tmp1, rc_Q31), 1); /* QA */ + } + Af_QA[n] = Inlines.silk_RSHIFT32(rc_Q31, 31 - QA); /* QA */ + + if (reached_max_gain != 0) + { + /* Reached max prediction gain; set remaining coefficients to zero and exit loop */ + for (k = n + 1; k < D; k++) + { + Af_QA[k] = 0; + } + break; + } + + /* Update C * Af and C * Ab */ + for (k = 0; k <= n + 1; k++) + { + tmp1 = CAf[k]; /* Q( -rshifts ) */ + tmp2 = CAb[n - k + 1]; /* Q( -rshifts ) */ + CAf[k] = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(tmp2, rc_Q31), 1); /* Q( -rshifts ) */ + CAb[n - k + 1] = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(tmp1, rc_Q31), 1); /* Q( -rshifts ) */ + } + } + + if (reached_max_gain != 0) + { + for (k = 0; k < D; k++) + { + /* Scale coefficients */ + A_Q16[k] = -Inlines.silk_RSHIFT_ROUND(Af_QA[k], QA - 16); + } + /* Subtract energy of preceding samples from C0 */ + if (rshifts > 0) + { + for (s = 0; s < nb_subfr; s++) + { + x_offset = x_ptr + s * subfr_length; + C0 -= (int)Inlines.silk_RSHIFT64(Inlines.silk_inner_prod16_aligned_64(x, x_offset, x, x_offset, D), rshifts); + } + } + else { + for (s = 0; s < nb_subfr; s++) + { + x_offset = x_ptr + s * subfr_length; + C0 -= Inlines.silk_LSHIFT32(Inlines.silk_inner_prod_self(x, x_offset, D), -rshifts); + } + } + /* Approximate residual energy */ + res_nrg.Val = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, C0), 2); + res_nrg_Q.Val = 0 - rshifts; + } + else { + /* Return residual energy */ + nrg = CAf[0]; /* Q( -rshifts ) */ + tmp1 = (int)1 << 16; /* Q16 */ + for (k = 0; k < D; k++) + { + Atmp1 = Inlines.silk_RSHIFT_ROUND(Af_QA[k], QA - 16); /* Q16 */ + nrg = Inlines.silk_SMLAWW(nrg, CAf[k + 1], Atmp1); /* Q( -rshifts ) */ + tmp1 = Inlines.silk_SMLAWW(tmp1, Atmp1, Atmp1); /* Q16 */ + A_Q16[k] = -Atmp1; + } + res_nrg.Val = Inlines.silk_SMLAWW(nrg, Inlines.silk_SMMUL(((int)((TuningParameters.FIND_LPC_COND_FAC) * ((long)1 << (32)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LPC_COND_FAC, 32)*/, C0), -tmp1);/* Q( -rshifts ) */ + res_nrg_Q.Val = -rshifts; + } + } + + } +} + +#endif \ No newline at end of file diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/BurgModifiedUnsafe.cs b/Libraries/Concentus/CSharp/Concentus/Silk/BurgModifiedUnsafe.cs new file mode 100644 index 000000000..e4cc4926d --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/BurgModifiedUnsafe.cs @@ -0,0 +1,336 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#if UNSAFE + +namespace Concentus.Silk +{ + using Concentus.Celt; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System; + using System.Diagnostics; + + internal static class BurgModified + { + /* subfr_length * nb_subfr = ( 0.005 * 16000 + 16 ) * 4 = 384 */ + private const int MAX_FRAME_SIZE = 384; + private const int QA = 25; + private const int N_BITS_HEAD_ROOM = 2; + private const int MIN_RSHIFTS = -16; + private const int MAX_RSHIFTS = (32 - QA); + + /* Compute reflection coefficients from input signal */ + internal static unsafe void silk_burg_modified( + BoxedValueInt res_nrg, /* O Residual energy */ + BoxedValueInt res_nrg_Q, /* O Residual energy Q value */ + int[] A_Q16, /* O Prediction coefficients (length order) */ + short[] x, /* I Input signal, length: nb_subfr * ( D + subfr_length ) */ + int x_ptr, + int minInvGain_Q30, /* I Inverse of max prediction gain */ + int subfr_length, /* I Input signal subframe length (incl. D preceding samples) */ + int nb_subfr, /* I Number of subframes stacked in x */ + int D /* I Order */ + ) + { + int k, n, s, lz, rshifts, reached_max_gain; + int C0, num, nrg, rc_Q31, invGain_Q30, Atmp_QA, Atmp1, tmp1, tmp2, x1, x2; + int x_offset; + int[] C_first_row = new int[SilkConstants.SILK_MAX_ORDER_LPC]; + int[] C_last_row = new int[SilkConstants.SILK_MAX_ORDER_LPC]; + int[] Af_QA = new int[SilkConstants.SILK_MAX_ORDER_LPC]; + int[] CAf = new int[SilkConstants.SILK_MAX_ORDER_LPC + 1]; + int[] CAb = new int[SilkConstants.SILK_MAX_ORDER_LPC + 1]; + int[] xcorr = new int[SilkConstants.SILK_MAX_ORDER_LPC]; + long C0_64; + + Inlines.OpusAssert(subfr_length * nb_subfr <= MAX_FRAME_SIZE); + + fixed (short* px_base = x) + { + short* px = px_base + x_ptr; + /* Compute autocorrelations, added over subframes */ + C0_64 = Inlines.silk_inner_prod16_aligned_64(x, x_ptr, x, x_ptr, subfr_length * nb_subfr); + lz = Inlines.silk_CLZ64(C0_64); + rshifts = 32 + 1 + N_BITS_HEAD_ROOM - lz; + if (rshifts > MAX_RSHIFTS) rshifts = MAX_RSHIFTS; + if (rshifts < MIN_RSHIFTS) rshifts = MIN_RSHIFTS; + + if (rshifts > 0) + { + C0 = (int)Inlines.silk_RSHIFT64(C0_64, rshifts); + } + else + { + C0 = Inlines.silk_LSHIFT32((int)C0_64, -rshifts); + } + + CAb[0] = CAf[0] = C0 + Inlines.silk_SMMUL(((int)((TuningParameters.FIND_LPC_COND_FAC) * ((long)1 << (32)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LPC_COND_FAC, 32)*/, C0) + 1; /* Q(-rshifts) */ + Arrays.MemSetInt(C_first_row, 0, SilkConstants.SILK_MAX_ORDER_LPC); + if (rshifts > 0) + { + for (s = 0; s < nb_subfr; s++) + { + short* px2 = px + s * subfr_length; + for (n = 1; n < D + 1; n++) + { + C_first_row[n - 1] += (int)Inlines.silk_RSHIFT64( + Inlines.silk_inner_prod16_aligned_64(px2, px2 + n, subfr_length - n), rshifts); + } + } + } + else + { + for (s = 0; s < nb_subfr; s++) + { + int i; + int d; + x_offset = x_ptr + s * subfr_length; + CeltPitchXCorr.pitch_xcorr(x, x_offset, x, x_offset + 1, xcorr, subfr_length - D, D); + for (n = 1; n < D + 1; n++) + { + for (i = n + subfr_length - D, d = 0; i < subfr_length; i++) + d = Inlines.MAC16_16(d, x[x_offset + i], x[x_offset + i - n]); + xcorr[n - 1] += d; + } + for (n = 1; n < D + 1; n++) + { + C_first_row[n - 1] += Inlines.silk_LSHIFT32(xcorr[n - 1], -rshifts); + } + } + } + Array.Copy(C_first_row, C_last_row, SilkConstants.SILK_MAX_ORDER_LPC); + + /* Initialize */ + CAb[0] = CAf[0] = C0 + Inlines.silk_SMMUL(((int)((TuningParameters.FIND_LPC_COND_FAC) * ((long)1 << (32)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LPC_COND_FAC, 32)*/, C0) + 1; /* Q(-rshifts) */ + + invGain_Q30 = (int)1 << 30; + reached_max_gain = 0; + for (n = 0; n < D; n++) + { + /* Update first row of correlation matrix (without first element) */ + /* Update last row of correlation matrix (without last element, stored in reversed order) */ + /* Update C * Af */ + /* Update C * flipud(Af) (stored in reversed order) */ + if (rshifts > -2) + { + for (s = 0; s < nb_subfr; s++) + { + short* px2 = px + s * subfr_length; + x1 = -Inlines.silk_LSHIFT32((int)px2[n], 16 - rshifts); /* Q(16-rshifts) */ + x2 = -Inlines.silk_LSHIFT32((int)px2[subfr_length - n - 1], 16 - rshifts); /* Q(16-rshifts) */ + tmp1 = Inlines.silk_LSHIFT32((int)px2[n], QA - 16); /* Q(QA-16) */ + tmp2 = Inlines.silk_LSHIFT32((int)px2[subfr_length - n - 1], QA - 16); /* Q(QA-16) */ + for (k = 0; k < n; k++) + { + C_first_row[k] = Inlines.silk_SMLAWB(C_first_row[k], x1, px2[n - k - 1]); /* Q( -rshifts ) */ + C_last_row[k] = Inlines.silk_SMLAWB(C_last_row[k], x2, px2[subfr_length - n + k]); /* Q( -rshifts ) */ + Atmp_QA = Af_QA[k]; + tmp1 = Inlines.silk_SMLAWB(tmp1, Atmp_QA, px2[n - k - 1]); /* Q(QA-16) */ + tmp2 = Inlines.silk_SMLAWB(tmp2, Atmp_QA, px2[subfr_length - n + k]); /* Q(QA-16) */ + } + tmp1 = Inlines.silk_LSHIFT32(-tmp1, 32 - QA - rshifts); /* Q(16-rshifts) */ + tmp2 = Inlines.silk_LSHIFT32(-tmp2, 32 - QA - rshifts); /* Q(16-rshifts) */ + for (k = 0; k <= n; k++) + { + CAf[k] = Inlines.silk_SMLAWB(CAf[k], tmp1, px2[n - k]); /* Q( -rshift ) */ + CAb[k] = Inlines.silk_SMLAWB(CAb[k], tmp2, px2[subfr_length - n + k - 1]); /* Q( -rshift ) */ + } + } + } + else + { + for (s = 0; s < nb_subfr; s++) + { + short* px2 = px + s * subfr_length; + x1 = -Inlines.silk_LSHIFT32((int)px2[n], -rshifts); /* Q( -rshifts ) */ + x2 = -Inlines.silk_LSHIFT32((int)px2[subfr_length - n - 1], -rshifts); /* Q( -rshifts ) */ + tmp1 = Inlines.silk_LSHIFT32((int)px2[n], 17); /* Q17 */ + tmp2 = Inlines.silk_LSHIFT32((int)px2[subfr_length - n - 1], 17); /* Q17 */ + for (k = 0; k < n; k++) + { + C_first_row[k] = Inlines.silk_MLA(C_first_row[k], x1, px2[n - k - 1]); /* Q( -rshifts ) */ + C_last_row[k] = Inlines.silk_MLA(C_last_row[k], x2, px2[subfr_length - n + k]); /* Q( -rshifts ) */ + Atmp1 = Inlines.silk_RSHIFT_ROUND(Af_QA[k], QA - 17); /* Q17 */ + tmp1 = Inlines.silk_MLA(tmp1, px2[n - k - 1], Atmp1); /* Q17 */ + tmp2 = Inlines.silk_MLA(tmp2, px2[subfr_length - n + k], Atmp1); /* Q17 */ + } + tmp1 = -tmp1; /* Q17 */ + tmp2 = -tmp2; /* Q17 */ + for (k = 0; k <= n; k++) + { + CAf[k] = Inlines.silk_SMLAWW(CAf[k], tmp1, + Inlines.silk_LSHIFT32((int)px2[n - k], -rshifts - 1)); /* Q( -rshift ) */ + CAb[k] = Inlines.silk_SMLAWW(CAb[k], tmp2, + Inlines.silk_LSHIFT32((int)px2[subfr_length - n + k - 1], -rshifts - 1)); /* Q( -rshift ) */ + } + } + } + + /* Calculate nominator and denominator for the next order reflection (parcor) coefficient */ + tmp1 = C_first_row[n]; /* Q( -rshifts ) */ + tmp2 = C_last_row[n]; /* Q( -rshifts ) */ + num = 0; /* Q( -rshifts ) */ + nrg = Inlines.silk_ADD32(CAb[0], CAf[0]); /* Q( 1-rshifts ) */ + for (k = 0; k < n; k++) + { + Atmp_QA = Af_QA[k]; + lz = Inlines.silk_CLZ32(Inlines.silk_abs(Atmp_QA)) - 1; + lz = Inlines.silk_min(32 - QA, lz); + Atmp1 = Inlines.silk_LSHIFT32(Atmp_QA, lz); /* Q( QA + lz ) */ + + tmp1 = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(C_last_row[n - k - 1], Atmp1), 32 - QA - lz); /* Q( -rshifts ) */ + tmp2 = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(C_first_row[n - k - 1], Atmp1), 32 - QA - lz); /* Q( -rshifts ) */ + num = Inlines.silk_ADD_LSHIFT32(num, Inlines.silk_SMMUL(CAb[n - k], Atmp1), 32 - QA - lz); /* Q( -rshifts ) */ + nrg = Inlines.silk_ADD_LSHIFT32(nrg, Inlines.silk_SMMUL(Inlines.silk_ADD32(CAb[k + 1], CAf[k + 1]), + Atmp1), 32 - QA - lz); /* Q( 1-rshifts ) */ + } + CAf[n + 1] = tmp1; /* Q( -rshifts ) */ + CAb[n + 1] = tmp2; /* Q( -rshifts ) */ + num = Inlines.silk_ADD32(num, tmp2); /* Q( -rshifts ) */ + num = Inlines.silk_LSHIFT32(-num, 1); /* Q( 1-rshifts ) */ + + /* Calculate the next order reflection (parcor) coefficient */ + if (Inlines.silk_abs(num) < nrg) + { + rc_Q31 = Inlines.silk_DIV32_varQ(num, nrg, 31); + } + else + { + rc_Q31 = (num > 0) ? int.MaxValue : int.MinValue; + } + + /* Update inverse prediction gain */ + tmp1 = ((int)1 << 30) - Inlines.silk_SMMUL(rc_Q31, rc_Q31); + tmp1 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, tmp1), 2); + if (tmp1 <= minInvGain_Q30) + { + /* Max prediction gain exceeded; set reflection coefficient such that max prediction gain is exactly hit */ + tmp2 = ((int)1 << 30) - Inlines.silk_DIV32_varQ(minInvGain_Q30, invGain_Q30, 30); /* Q30 */ + rc_Q31 = Inlines.silk_SQRT_APPROX(tmp2); /* Q15 */ + /* Newton-Raphson iteration */ + rc_Q31 = Inlines.silk_RSHIFT32(rc_Q31 + Inlines.silk_DIV32(tmp2, rc_Q31), 1); /* Q15 */ + rc_Q31 = Inlines.silk_LSHIFT32(rc_Q31, 16); /* Q31 */ + if (num < 0) + { + /* Ensure adjusted reflection coefficients has the original sign */ + rc_Q31 = -rc_Q31; + } + invGain_Q30 = minInvGain_Q30; + reached_max_gain = 1; + } + else + { + invGain_Q30 = tmp1; + } + + /* Update the AR coefficients */ + for (k = 0; k < (n + 1) >> 1; k++) + { + tmp1 = Af_QA[k]; /* QA */ + tmp2 = Af_QA[n - k - 1]; /* QA */ + Af_QA[k] = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(tmp2, rc_Q31), 1); /* QA */ + Af_QA[n - k - 1] = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(tmp1, rc_Q31), 1); /* QA */ + } + Af_QA[n] = Inlines.silk_RSHIFT32(rc_Q31, 31 - QA); /* QA */ + + if (reached_max_gain != 0) + { + /* Reached max prediction gain; set remaining coefficients to zero and exit loop */ + for (k = n + 1; k < D; k++) + { + Af_QA[k] = 0; + } + break; + } + + /* Update C * Af and C * Ab */ + for (k = 0; k <= n + 1; k++) + { + tmp1 = CAf[k]; /* Q( -rshifts ) */ + tmp2 = CAb[n - k + 1]; /* Q( -rshifts ) */ + CAf[k] = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(tmp2, rc_Q31), 1); /* Q( -rshifts ) */ + CAb[n - k + 1] = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(tmp1, rc_Q31), 1); /* Q( -rshifts ) */ + } + } + + if (reached_max_gain != 0) + { + for (k = 0; k < D; k++) + { + /* Scale coefficients */ + A_Q16[k] = -Inlines.silk_RSHIFT_ROUND(Af_QA[k], QA - 16); + } + /* Subtract energy of preceding samples from C0 */ + if (rshifts > 0) + { + for (s = 0; s < nb_subfr; s++) + { + x_offset = x_ptr + s * subfr_length; + C0 -= (int)Inlines.silk_RSHIFT64(Inlines.silk_inner_prod16_aligned_64(x, x_offset, x, x_offset, D), rshifts); + } + } + else + { + for (s = 0; s < nb_subfr; s++) + { + x_offset = x_ptr + s * subfr_length; + C0 -= Inlines.silk_LSHIFT32(Inlines.silk_inner_prod_self(x, x_offset, D), -rshifts); + } + } + /* Approximate residual energy */ + res_nrg.Val = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, C0), 2); + res_nrg_Q.Val = 0 - rshifts; + } + else + { + /* Return residual energy */ + nrg = CAf[0]; /* Q( -rshifts ) */ + tmp1 = (int)1 << 16; /* Q16 */ + for (k = 0; k < D; k++) + { + Atmp1 = Inlines.silk_RSHIFT_ROUND(Af_QA[k], QA - 16); /* Q16 */ + nrg = Inlines.silk_SMLAWW(nrg, CAf[k + 1], Atmp1); /* Q( -rshifts ) */ + tmp1 = Inlines.silk_SMLAWW(tmp1, Atmp1, Atmp1); /* Q16 */ + A_Q16[k] = -Atmp1; + } + res_nrg.Val = Inlines.silk_SMLAWW(nrg, Inlines.silk_SMMUL(((int)((TuningParameters.FIND_LPC_COND_FAC) * ((long)1 << (32)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LPC_COND_FAC, 32)*/, C0), -tmp1);/* Q( -rshifts ) */ + res_nrg_Q.Val = -rshifts; + } + } + } + } +} + +#endif \ No newline at end of file diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/CNG.cs b/Libraries/Concentus/CSharp/Concentus/Silk/CNG.cs new file mode 100644 index 000000000..d735d9adf --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/CNG.cs @@ -0,0 +1,232 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System; + using System.Diagnostics; + + /// + /// Comfort noise generation and estimation + /// + internal static class CNG + { + /// + /// Generates excitation for CNG LPC synthesis + /// + /// O CNG excitation signal Q10 + /// I Random samples buffer Q10 + /// I Gain to apply + /// I Length + /// I/O Seed to random index generator + internal static void silk_CNG_exc( + int[] exc_Q10, + int exc_Q10_ptr, + int[] exc_buf_Q14, + int Gain_Q16, + int length, + ref int rand_seed) + { + int seed; + int i, idx, exc_mask; + + exc_mask = SilkConstants.CNG_BUF_MASK_MAX; + + while (exc_mask > length) + { + exc_mask = Inlines.silk_RSHIFT(exc_mask, 1); + } + + seed = rand_seed; + for (i = exc_Q10_ptr; i < exc_Q10_ptr + length; i++) + { + seed = Inlines.silk_RAND(seed); + idx = (int)(Inlines.silk_RSHIFT(seed, 24) & exc_mask); + Inlines.OpusAssert(idx >= 0); + Inlines.OpusAssert(idx <= SilkConstants.CNG_BUF_MASK_MAX); + exc_Q10[i] = (short)Inlines.silk_SAT16(Inlines.silk_SMULWW(exc_buf_Q14[idx], Gain_Q16 >> 4)); + } + + rand_seed = seed; + } + + /// + /// Resets CNG state + /// + /// I/O Decoder state + internal static void silk_CNG_Reset(SilkChannelDecoder psDec) + { + int i, NLSF_step_Q15, NLSF_acc_Q15; + + NLSF_step_Q15 = Inlines.silk_DIV32_16(short.MaxValue, (short)(psDec.LPC_order + 1)); + NLSF_acc_Q15 = 0; + for (i = 0; i < psDec.LPC_order; i++) + { + NLSF_acc_Q15 += NLSF_step_Q15; + psDec.sCNG.CNG_smth_NLSF_Q15[i] = (short)(NLSF_acc_Q15); + } + psDec.sCNG.CNG_smth_Gain_Q16 = 0; + psDec.sCNG.rand_seed = 3176576; + } + + /// + /// Updates CNG estimate, and applies the CNG when packet was lost + /// + /// I/O Decoder state + /// I/O Decoder control + /// I/O Signal + /// I Length of residual + internal static void silk_CNG( + SilkChannelDecoder psDec, + SilkDecoderControl psDecCtrl, + short[] frame, + int frame_ptr, + int length) + { + int i, subfr; + int sum_Q6, max_Gain_Q16, gain_Q16; + short[] A_Q12 = new short[psDec.LPC_order]; + CNGState psCNG = psDec.sCNG; + + if (psDec.fs_kHz != psCNG.fs_kHz) + { + /* Reset state */ + silk_CNG_Reset(psDec); + + psCNG.fs_kHz = psDec.fs_kHz; + } + + if (psDec.lossCnt == 0 && psDec.prevSignalType == SilkConstants.TYPE_NO_VOICE_ACTIVITY) + { + /* Update CNG parameters */ + + /* Smoothing of LSF's */ + for (i = 0; i < psDec.LPC_order; i++) + { + psCNG.CNG_smth_NLSF_Q15[i] += (short)(Inlines.silk_SMULWB((int)psDec.prevNLSF_Q15[i] - (int)psCNG.CNG_smth_NLSF_Q15[i], SilkConstants.CNG_NLSF_SMTH_Q16)); + } + + /* Find the subframe with the highest gain */ + max_Gain_Q16 = 0; + subfr = 0; + for (i = 0; i < psDec.nb_subfr; i++) + { + if (psDecCtrl.Gains_Q16[i] > max_Gain_Q16) + { + max_Gain_Q16 = psDecCtrl.Gains_Q16[i]; + subfr = i; + } + } + + /* Update CNG excitation buffer with excitation from this subframe */ + Arrays.MemMoveInt(psCNG.CNG_exc_buf_Q14, 0, psDec.subfr_length, (psDec.nb_subfr - 1) * psDec.subfr_length); + + /* Smooth gains */ + for (i = 0; i < psDec.nb_subfr; i++) + { + psCNG.CNG_smth_Gain_Q16 += Inlines.silk_SMULWB(psDecCtrl.Gains_Q16[i] - psCNG.CNG_smth_Gain_Q16, SilkConstants.CNG_GAIN_SMTH_Q16); + } + } + + /* Add CNG when packet is lost or during DTX */ + if (psDec.lossCnt != 0) + { + int[] CNG_sig_Q10 = new int[length + SilkConstants.MAX_LPC_ORDER]; + + /* Generate CNG excitation */ + gain_Q16 = Inlines.silk_SMULWW(psDec.sPLC.randScale_Q14, psDec.sPLC.prevGain_Q16[1]); + if (gain_Q16 >= (1 << 21) || psCNG.CNG_smth_Gain_Q16 > (1 << 23)) + { + gain_Q16 = Inlines.silk_SMULTT(gain_Q16, gain_Q16); + gain_Q16 = Inlines.silk_SUB_LSHIFT32(Inlines.silk_SMULTT(psCNG.CNG_smth_Gain_Q16, psCNG.CNG_smth_Gain_Q16), gain_Q16, 5); + gain_Q16 = Inlines.silk_LSHIFT32(Inlines.silk_SQRT_APPROX(gain_Q16), 16); + } + else + { + gain_Q16 = Inlines.silk_SMULWW(gain_Q16, gain_Q16); + gain_Q16 = Inlines.silk_SUB_LSHIFT32(Inlines.silk_SMULWW(psCNG.CNG_smth_Gain_Q16, psCNG.CNG_smth_Gain_Q16), gain_Q16, 5); + gain_Q16 = Inlines.silk_LSHIFT32(Inlines.silk_SQRT_APPROX(gain_Q16), 8); + } + silk_CNG_exc(CNG_sig_Q10, SilkConstants.MAX_LPC_ORDER, psCNG.CNG_exc_buf_Q14, gain_Q16, length, ref psCNG.rand_seed); + + /* Convert CNG NLSF to filter representation */ + NLSF.silk_NLSF2A(A_Q12, psCNG.CNG_smth_NLSF_Q15, psDec.LPC_order); + + /* Generate CNG signal, by synthesis filtering */ + Array.Copy(psCNG.CNG_synth_state, CNG_sig_Q10, SilkConstants.MAX_LPC_ORDER); + + for (i = 0; i < length; i++) + { + int lpci = SilkConstants.MAX_LPC_ORDER + i; + Inlines.OpusAssert(psDec.LPC_order == 10 || psDec.LPC_order == 16); + /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */ + sum_Q6 = Inlines.silk_RSHIFT(psDec.LPC_order, 1); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 1], A_Q12[0]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 2], A_Q12[1]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 3], A_Q12[2]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 4], A_Q12[3]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 5], A_Q12[4]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 6], A_Q12[5]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 7], A_Q12[6]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 8], A_Q12[7]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 9], A_Q12[8]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 10], A_Q12[9]); + + if (psDec.LPC_order == 16) + { + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 11], A_Q12[10]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 12], A_Q12[11]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 13], A_Q12[12]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 14], A_Q12[13]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 15], A_Q12[14]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 16], A_Q12[15]); + } + + /* Update states */ + CNG_sig_Q10[lpci] = Inlines.silk_ADD_LSHIFT(CNG_sig_Q10[lpci], sum_Q6, 4); + + frame[frame_ptr + i] = Inlines.silk_ADD_SAT16(frame[frame_ptr + i], (short)(Inlines.silk_RSHIFT_ROUND(CNG_sig_Q10[lpci], 10))); + } + + Array.Copy(CNG_sig_Q10, length, psCNG.CNG_synth_state, 0, SilkConstants.MAX_LPC_ORDER); + } + else + { + Arrays.MemSetInt(psCNG.CNG_synth_state, 0, psDec.LPC_order); + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/CodeSigns.cs b/Libraries/Concentus/CSharp/Concentus/Silk/CodeSigns.cs new file mode 100644 index 000000000..5a16217e6 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/CodeSigns.cs @@ -0,0 +1,150 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal static class CodeSigns + { + private static int silk_enc_map(int a) + { + return (Inlines.silk_RSHIFT((a), 15) + 1); + } + + private static int silk_dec_map(int a) + { + return (Inlines.silk_LSHIFT((a), 1) - 1); + } + + /// + /// Encodes signs of excitation + /// + /// I/O Compressor data structure + /// I pulse signal + /// I length of input + /// I Signal type + /// I Quantization offset type + /// I Sum of absolute pulses per block [MAX_NB_SHELL_BLOCKS] + internal static void silk_encode_signs( + EntropyCoder psRangeEnc, + sbyte[] pulses, + int length, + int signalType, + int quantOffsetType, + int[] sum_pulses) + { + int i, j, p; + byte[] icdf = new byte[2]; + int q_ptr; + byte[] sign_icdf = Tables.silk_sign_iCDF; + int icdf_ptr; + + icdf[1] = 0; + q_ptr = 0; + i = Inlines.silk_SMULBB(7, Inlines.silk_ADD_LSHIFT(quantOffsetType, signalType, 1)); + icdf_ptr = i; + length = Inlines.silk_RSHIFT(length + (SilkConstants.SHELL_CODEC_FRAME_LENGTH / 2), SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH); + for (i = 0; i < length; i++) + { + p = sum_pulses[i]; + if (p > 0) + { + icdf[0] = sign_icdf[icdf_ptr + Inlines.silk_min(p & 0x1F, 6)]; + for (j = q_ptr; j < q_ptr + SilkConstants.SHELL_CODEC_FRAME_LENGTH; j++) + { + if (pulses[j] != 0) + { + psRangeEnc.enc_icdf( silk_enc_map(pulses[j]), icdf, 8); + } + } + } + + q_ptr += SilkConstants.SHELL_CODEC_FRAME_LENGTH; + } + } + + /// + /// Decodes signs of excitation + /// + /// I/O Compressor data structure + /// I/O pulse signal + /// I length of input + /// I Signal type + /// I Quantization offset type + /// I Sum of absolute pulses per block [MAX_NB_SHELL_BLOCKS] + internal static void silk_decode_signs( + EntropyCoder psRangeDec, + short[] pulses, + int length, + int signalType, + int quantOffsetType, + int[] sum_pulses) + { + int i, j, p; + byte[] icdf = new byte[2]; + int q_ptr; + byte[] icdf_table = Tables.silk_sign_iCDF; + int icdf_ptr; + + icdf[1] = 0; + q_ptr = 0; + i = Inlines.silk_SMULBB(7, Inlines.silk_ADD_LSHIFT(quantOffsetType, signalType, 1)); + icdf_ptr = i; + length = Inlines.silk_RSHIFT(length + SilkConstants.SHELL_CODEC_FRAME_LENGTH / 2, SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH); + + for (i = 0; i < length; i++) + { + p = sum_pulses[i]; + + if (p > 0) + { + icdf[0] = icdf_table[icdf_ptr + Inlines.silk_min(p & 0x1F, 6)]; + for (j = 0; j < SilkConstants.SHELL_CODEC_FRAME_LENGTH; j++) + { + if (pulses[q_ptr + j] > 0) + { + /* attach sign */ + pulses[q_ptr + j] *= (short)(silk_dec_map(psRangeDec.dec_icdf(icdf, 8))); + } + } + } + + q_ptr += SilkConstants.SHELL_CODEC_FRAME_LENGTH; + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/CorrelateMatrix.cs b/Libraries/Concentus/CSharp/Concentus/Silk/CorrelateMatrix.cs new file mode 100644 index 000000000..4e3a15825 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/CorrelateMatrix.cs @@ -0,0 +1,186 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + /********************************************************************** + * Correlation Matrix Computations for LS estimate. + **********************************************************************/ + internal static class CorrelateMatrix + { + /* Calculates correlation vector X'*t */ + internal static void silk_corrVector( + short[] x, /* I x vector [L + order - 1] used to form data matrix X */ + int x_ptr, + short[] t, /* I Target vector [L] */ + int t_ptr, + int L, /* I Length of vectors */ + int order, /* I Max lag for correlation */ + int[] Xt, /* O Pointer to X'*t correlation vector [order] */ + int rshifts /* I Right shifts of correlations */ + ) + { + int lag, i; + int ptr1; + int ptr2; + int inner_prod; + + ptr1 = x_ptr + order - 1; /* Points to first sample of column 0 of X: X[:,0] */ + ptr2 = t_ptr; + /* Calculate X'*t */ + if (rshifts > 0) + { + /* Right shifting used */ + for (lag = 0; lag < order; lag++) + { + inner_prod = 0; + for (i = 0; i < L; i++) + { + inner_prod += Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + i], t[ptr2 + i]), rshifts); + } + Xt[lag] = inner_prod; /* X[:,lag]'*t */ + ptr1--; /* Go to next column of X */ + } + } + else { + Inlines.OpusAssert(rshifts == 0); + for (lag = 0; lag < order; lag++) + { + Xt[lag] = Inlines.silk_inner_prod(x, ptr1, t, ptr2, L); /* X[:,lag]'*t */ + ptr1--; /* Go to next column of X */ + } + } + } + + /* Calculates correlation matrix X'*X */ + internal static void silk_corrMatrix( + short[] x, /* I x vector [L + order - 1] used to form data matrix X */ + int x_ptr, + int L, /* I Length of vectors */ + int order, /* I Max lag for correlation */ + int head_room, /* I Desired headroom */ + int[] XX, /* O Pointer to X'*X correlation matrix [ order x order ] */ + int XX_ptr, + BoxedValueInt rshifts /* I/O Right shifts of correlations */ + ) + { + int i, j, lag, head_room_rshifts; + int energy, rshifts_local; + int ptr1, ptr2; + + /* Calculate energy to find shift used to fit in 32 bits */ + SumSqrShift.silk_sum_sqr_shift(out energy, out rshifts_local, x, x_ptr, L + order - 1); + /* Add shifts to get the desired head room */ + head_room_rshifts = Inlines.silk_max(head_room - Inlines.silk_CLZ32(energy), 0); + + energy = Inlines.silk_RSHIFT32(energy, head_room_rshifts); + rshifts_local += head_room_rshifts; + + /* Calculate energy of first column (0) of X: X[:,0]'*X[:,0] */ + /* Remove contribution of first order - 1 samples */ + for (i = x_ptr; i < x_ptr + order - 1; i++) + { + energy -= Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[i], x[i]), rshifts_local); + } + if (rshifts_local < rshifts.Val) + { + /* Adjust energy */ + energy = Inlines.silk_RSHIFT32(energy, rshifts.Val - rshifts_local); + rshifts_local = rshifts.Val; + } + + /* Calculate energy of remaining columns of X: X[:,j]'*X[:,j] */ + /* Fill out the diagonal of the correlation matrix */ + Inlines.MatrixSet(XX, XX_ptr, 0, 0, order, energy); + ptr1 = x_ptr + order - 1; /* First sample of column 0 of X */ + for (j = 1; j < order; j++) + { + energy = Inlines.silk_SUB32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + L - j], x[ptr1 + L - j]), rshifts_local)); + energy = Inlines.silk_ADD32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 - j], x[ptr1 - j]), rshifts_local)); + Inlines.MatrixSet(XX, XX_ptr, j, j, order, energy); + } + + ptr2 = x_ptr + order - 2; /* First sample of column 1 of X */ + /* Calculate the remaining elements of the correlation matrix */ + if (rshifts_local > 0) + { + /* Right shifting used */ + for (lag = 1; lag < order; lag++) + { + /* Inner product of column 0 and column lag: X[:,0]'*X[:,lag] */ + energy = 0; + for (i = 0; i < L; i++) + { + energy += Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + i], x[ptr2 + i]), rshifts_local); + } + /* Calculate remaining off diagonal: X[:,j]'*X[:,j + lag] */ + Inlines.MatrixSet(XX, XX_ptr, lag, 0, order, energy); + Inlines.MatrixSet(XX, XX_ptr, 0, lag, order, energy); + for (j = 1; j < (order - lag); j++) + { + energy = Inlines.silk_SUB32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + L - j], x[ptr2 + L - j]), rshifts_local)); + energy = Inlines.silk_ADD32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 - j], x[ptr2 - j]), rshifts_local)); + Inlines.MatrixSet(XX, XX_ptr, lag + j, j, order, energy); + Inlines.MatrixSet(XX, XX_ptr, j, lag + j, order, energy); + } + ptr2--; /* Update pointer to first sample of next column (lag) in X */ + } + } + else { + for (lag = 1; lag < order; lag++) + { + /* Inner product of column 0 and column lag: X[:,0]'*X[:,lag] */ + energy = Inlines.silk_inner_prod(x, ptr1, x, ptr2, L); + Inlines.MatrixSet(XX, XX_ptr, lag, 0, order,energy); + Inlines.MatrixSet(XX, XX_ptr, 0, lag, order, energy); + /* Calculate remaining off diagonal: X[:,j]'*X[:,j + lag] */ + for (j = 1; j < (order - lag); j++) + { + energy = Inlines.silk_SUB32(energy, Inlines.silk_SMULBB(x[ptr1 + L - j], x[ptr2 + L - j])); + energy = Inlines.silk_SMLABB(energy, x[ptr1 - j], x[ptr2 - j]); + Inlines.MatrixSet(XX, XX_ptr, lag + j, j, order, energy); + Inlines.MatrixSet(XX, XX_ptr, j, lag + j, order, energy); + } + ptr2--;/* Update pointer to first sample of next column (lag) in X */ + } + } + rshifts.Val = rshifts_local; + } + + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/DecodeAPI.cs b/Libraries/Concentus/CSharp/Concentus/Silk/DecodeAPI.cs new file mode 100644 index 000000000..494806a72 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/DecodeAPI.cs @@ -0,0 +1,461 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System; + using System.Diagnostics; + + internal static class DecodeAPI + { + /// + /// Reset decoder state + /// + /// I/O Stat + /// Returns error code + internal static int silk_InitDecoder(SilkDecoder decState) + { + /* Reset decoder */ + decState.Reset(); + + int n, ret = SilkError.SILK_NO_ERROR; + SilkChannelDecoder[] channel_states = decState.channel_state; + + for (n = 0; n < SilkConstants.DECODER_NUM_CHANNELS; n++) + { + ret = channel_states[n].silk_init_decoder(); + } + + decState.sStereo.Reset(); + + /* Not strictly needed, but it's cleaner that way */ + decState.prev_decode_only_middle = 0; + + return ret; + } + + /* Decode a frame */ + internal static int silk_Decode( /* O Returns error code */ + SilkDecoder psDec, /* I/O State */ + DecControlState decControl, /* I/O Control Structure */ + int lostFlag, /* I 0: no loss, 1 loss, 2 decode fec */ + int newPacketFlag, /* I Indicates first decoder call for this packet */ + EntropyCoder psRangeDec, /* I/O Compressor data structure */ + short[] samplesOut, /* O Decoded output speech vector */ + int samplesOut_ptr, + out int nSamplesOut /* O Number of samples decoded */ + ) + { + int i, n, decode_only_middle = 0, ret = SilkError.SILK_NO_ERROR; + int LBRR_symbol; + BoxedValueInt nSamplesOutDec = new BoxedValueInt(); + short[] samplesOut_tmp; + int[] samplesOut_tmp_ptrs = new int[2]; + short[] samplesOut1_tmp_storage1; + short[] samplesOut1_tmp_storage2; + short[] samplesOut2_tmp; + int[] MS_pred_Q13 = new int[] { 0, 0 }; + short[] resample_out; + int resample_out_ptr; + SilkChannelDecoder[] channel_state = psDec.channel_state; + int has_side; + int stereo_to_mono; + int delay_stack_alloc; + nSamplesOut = 0; + + Inlines.OpusAssert(decControl.nChannelsInternal == 1 || decControl.nChannelsInternal == 2); + + /**********************************/ + /* Test if first frame in payload */ + /**********************************/ + if (newPacketFlag != 0) + { + for (n = 0; n < decControl.nChannelsInternal; n++) + { + channel_state[n].nFramesDecoded = 0; /* Used to count frames in packet */ + } + } + + /* If Mono . Stereo transition in bitstream: init state of second channel */ + if (decControl.nChannelsInternal > psDec.nChannelsInternal) + { + ret += channel_state[1].silk_init_decoder(); + } + + stereo_to_mono = (decControl.nChannelsInternal == 1 && psDec.nChannelsInternal == 2 && + (decControl.internalSampleRate == 1000 * channel_state[0].fs_kHz)) ? 1 : 0; + + if (channel_state[0].nFramesDecoded == 0) + { + for (n = 0; n < decControl.nChannelsInternal; n++) + { + int fs_kHz_dec; + if (decControl.payloadSize_ms == 0) + { + /* Assuming packet loss, use 10 ms */ + channel_state[n].nFramesPerPacket = 1; + channel_state[n].nb_subfr = 2; + } + else if (decControl.payloadSize_ms == 10) + { + channel_state[n].nFramesPerPacket = 1; + channel_state[n].nb_subfr = 2; + } + else if (decControl.payloadSize_ms == 20) + { + channel_state[n].nFramesPerPacket = 1; + channel_state[n].nb_subfr = 4; + } + else if (decControl.payloadSize_ms == 40) + { + channel_state[n].nFramesPerPacket = 2; + channel_state[n].nb_subfr = 4; + } + else if (decControl.payloadSize_ms == 60) + { + channel_state[n].nFramesPerPacket = 3; + channel_state[n].nb_subfr = 4; + } + else { + Inlines.OpusAssert(false); + return SilkError.SILK_DEC_INVALID_FRAME_SIZE; + } + fs_kHz_dec = (decControl.internalSampleRate >> 10) + 1; + if (fs_kHz_dec != 8 && fs_kHz_dec != 12 && fs_kHz_dec != 16) + { + Inlines.OpusAssert(false); + return SilkError.SILK_DEC_INVALID_SAMPLING_FREQUENCY; + } + ret += channel_state[n].silk_decoder_set_fs(fs_kHz_dec, decControl.API_sampleRate); + } + } + + if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 2 && (psDec.nChannelsAPI == 1 || psDec.nChannelsInternal == 1)) + { + Arrays.MemSetShort(psDec.sStereo.pred_prev_Q13, 0, 2); + Arrays.MemSetShort(psDec.sStereo.sSide, 0, 2); + channel_state[1].resampler_state.Assign(channel_state[0].resampler_state); + } + psDec.nChannelsAPI = decControl.nChannelsAPI; + psDec.nChannelsInternal = decControl.nChannelsInternal; + + if (decControl.API_sampleRate > (int)SilkConstants.MAX_API_FS_KHZ * 1000 || decControl.API_sampleRate < 8000) + { + ret = SilkError.SILK_DEC_INVALID_SAMPLING_FREQUENCY; + return (ret); + } + + if (lostFlag != DecoderAPIFlag.FLAG_PACKET_LOST && channel_state[0].nFramesDecoded == 0) + { + /* First decoder call for this payload */ + /* Decode VAD flags and LBRR flag */ + for (n = 0; n < decControl.nChannelsInternal; n++) + { + for (i = 0; i < channel_state[n].nFramesPerPacket; i++) + { + channel_state[n].VAD_flags[i] = psRangeDec.dec_bit_logp(1); + } + channel_state[n].LBRR_flag = psRangeDec.dec_bit_logp(1); + } + /* Decode LBRR flags */ + for (n = 0; n < decControl.nChannelsInternal; n++) + { + Arrays.MemSetInt(channel_state[n].LBRR_flags, 0, SilkConstants.MAX_FRAMES_PER_PACKET); + if (channel_state[n].LBRR_flag != 0) + { + if (channel_state[n].nFramesPerPacket == 1) + { + channel_state[n].LBRR_flags[0] = 1; + } + else { + LBRR_symbol = psRangeDec.dec_icdf(Tables.silk_LBRR_flags_iCDF_ptr[channel_state[n].nFramesPerPacket - 2], 8) + 1; + for (i = 0; i < channel_state[n].nFramesPerPacket; i++) + { + channel_state[n].LBRR_flags[i] = Inlines.silk_RSHIFT(LBRR_symbol, i) & 1; + } + } + } + } + + if (lostFlag == DecoderAPIFlag.FLAG_DECODE_NORMAL) + { + /* Regular decoding: skip all LBRR data */ + for (i = 0; i < channel_state[0].nFramesPerPacket; i++) + { + for (n = 0; n < decControl.nChannelsInternal; n++) + { + if (channel_state[n].LBRR_flags[i] != 0) + { + short[] pulses = new short[SilkConstants.MAX_FRAME_LENGTH]; + int condCoding; + + if (decControl.nChannelsInternal == 2 && n == 0) + { + Stereo.silk_stereo_decode_pred(psRangeDec, MS_pred_Q13); + if (channel_state[1].LBRR_flags[i] == 0) + { + BoxedValueInt decodeOnlyMiddleBoxed = new BoxedValueInt(decode_only_middle); + Stereo.silk_stereo_decode_mid_only(psRangeDec, decodeOnlyMiddleBoxed); + decode_only_middle = decodeOnlyMiddleBoxed.Val; + } + } + /* Use conditional coding if previous frame available */ + if (i > 0 && (channel_state[n].LBRR_flags[i - 1] != 0)) + { + condCoding = SilkConstants.CODE_CONDITIONALLY; + } + else + { + condCoding = SilkConstants.CODE_INDEPENDENTLY; + } + DecodeIndices.silk_decode_indices(channel_state[n], psRangeDec, i, 1, condCoding); + DecodePulses.silk_decode_pulses(psRangeDec, pulses, channel_state[n].indices.signalType, + channel_state[n].indices.quantOffsetType, channel_state[n].frame_length); + } + } + } + } + } + + /* Get MS predictor index */ + if (decControl.nChannelsInternal == 2) + { + if (lostFlag == DecoderAPIFlag.FLAG_DECODE_NORMAL || + (lostFlag == DecoderAPIFlag.FLAG_DECODE_LBRR && channel_state[0].LBRR_flags[channel_state[0].nFramesDecoded] == 1)) + { + Stereo.silk_stereo_decode_pred(psRangeDec, MS_pred_Q13); + /* For LBRR data, decode mid-only flag only if side-channel's LBRR flag is false */ + if ((lostFlag == DecoderAPIFlag.FLAG_DECODE_NORMAL && channel_state[1].VAD_flags[channel_state[0].nFramesDecoded] == 0) || + (lostFlag == DecoderAPIFlag.FLAG_DECODE_LBRR && channel_state[1].LBRR_flags[channel_state[0].nFramesDecoded] == 0)) + { + BoxedValueInt decodeOnlyMiddleBoxed = new BoxedValueInt(decode_only_middle); + Stereo.silk_stereo_decode_mid_only(psRangeDec, decodeOnlyMiddleBoxed); + decode_only_middle = decodeOnlyMiddleBoxed.Val; + } + else + { + decode_only_middle = 0; + } + } + else + { + for (n = 0; n < 2; n++) + { + MS_pred_Q13[n] = psDec.sStereo.pred_prev_Q13[n]; + } + } + } + + /* Reset side channel decoder prediction memory for first frame with side coding */ + if (decControl.nChannelsInternal == 2 && decode_only_middle == 0 && psDec.prev_decode_only_middle == 1) + { + Arrays.MemSetShort(psDec.channel_state[1].outBuf, 0, SilkConstants.MAX_FRAME_LENGTH + 2 * SilkConstants.MAX_SUB_FRAME_LENGTH); + Arrays.MemSetInt(psDec.channel_state[1].sLPC_Q14_buf, 0, SilkConstants.MAX_LPC_ORDER); + psDec.channel_state[1].lagPrev = 100; + psDec.channel_state[1].LastGainIndex = 10; + psDec.channel_state[1].prevSignalType = SilkConstants.TYPE_NO_VOICE_ACTIVITY; + psDec.channel_state[1].first_frame_after_reset = 1; + } + + /* Check if the temp buffer fits into the output PCM buffer. If it fits, + we can delay allocating the temp buffer until after the SILK peak stack + usage. We need to use a < and not a <= because of the two extra samples. */ + delay_stack_alloc = (decControl.internalSampleRate * decControl.nChannelsInternal + < decControl.API_sampleRate * decControl.nChannelsAPI) ? 1 : 0; + + if (delay_stack_alloc != 0) + { + samplesOut_tmp = samplesOut; + samplesOut_tmp_ptrs[0] = samplesOut_ptr; + samplesOut_tmp_ptrs[1] = samplesOut_ptr + channel_state[0].frame_length + 2; + } + else + { + samplesOut1_tmp_storage1 = new short[decControl.nChannelsInternal * (channel_state[0].frame_length + 2)]; + samplesOut_tmp = samplesOut1_tmp_storage1; + samplesOut_tmp_ptrs[0] = 0; + samplesOut_tmp_ptrs[1] = channel_state[0].frame_length + 2; + } + + if (lostFlag == DecoderAPIFlag.FLAG_DECODE_NORMAL) + { + has_side = (decode_only_middle == 0) ? 1 : 0; + } + else + { + has_side = (psDec.prev_decode_only_middle == 0 + || (decControl.nChannelsInternal == 2 && + lostFlag == DecoderAPIFlag.FLAG_DECODE_LBRR && + channel_state[1].LBRR_flags[channel_state[1].nFramesDecoded] == 1)) ? 1 : 0; + } + /* Call decoder for one frame */ + for (n = 0; n < decControl.nChannelsInternal; n++) + { + if (n == 0 || (has_side != 0)) + { + int FrameIndex; + int condCoding; + + FrameIndex = channel_state[0].nFramesDecoded - n; + /* Use independent coding if no previous frame available */ + if (FrameIndex <= 0) + { + condCoding = SilkConstants.CODE_INDEPENDENTLY; + } + else if (lostFlag == DecoderAPIFlag.FLAG_DECODE_LBRR) + { + condCoding = (channel_state[n].LBRR_flags[FrameIndex - 1] != 0) ? SilkConstants.CODE_CONDITIONALLY : SilkConstants.CODE_INDEPENDENTLY; + } + else if (n > 0 && (psDec.prev_decode_only_middle != 0)) + { + /* If we skipped a side frame in this packet, we don't + need LTP scaling; the LTP state is well-defined. */ + condCoding = SilkConstants.CODE_INDEPENDENTLY_NO_LTP_SCALING; + } + else + { + condCoding = SilkConstants.CODE_CONDITIONALLY; + } + ret += channel_state[n].silk_decode_frame(psRangeDec, samplesOut_tmp, samplesOut_tmp_ptrs[n] + 2, nSamplesOutDec, lostFlag, condCoding); + } + else + { + Arrays.MemSetWithOffset(samplesOut_tmp, 0, samplesOut_tmp_ptrs[n] + 2, nSamplesOutDec.Val); + } + channel_state[n].nFramesDecoded++; + } + + if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 2) + { + /* Convert Mid/Side to Left/Right */ + Stereo.silk_stereo_MS_to_LR(psDec.sStereo, samplesOut_tmp, samplesOut_tmp_ptrs[0], samplesOut_tmp, samplesOut_tmp_ptrs[1], MS_pred_Q13, channel_state[0].fs_kHz, nSamplesOutDec.Val); + } + else + { + /* Buffering */ + Array.Copy(psDec.sStereo.sMid, 0, samplesOut_tmp, samplesOut_tmp_ptrs[0], 2); + Array.Copy(samplesOut_tmp, samplesOut_tmp_ptrs[0] + nSamplesOutDec.Val, psDec.sStereo.sMid, 0, 2); + } + + /* Number of output samples */ + nSamplesOut = Inlines.silk_DIV32(nSamplesOutDec.Val * decControl.API_sampleRate, Inlines.silk_SMULBB(channel_state[0].fs_kHz, 1000)); + + /* Set up pointers to temp buffers */ + if (decControl.nChannelsAPI == 2) + { + samplesOut2_tmp = new short[nSamplesOut]; + resample_out = samplesOut2_tmp; + resample_out_ptr = 0; + } + else { + resample_out = samplesOut; + resample_out_ptr = samplesOut_ptr; + } + + if (delay_stack_alloc != 0) + { + samplesOut1_tmp_storage2 = new short[decControl.nChannelsInternal * (channel_state[0].frame_length + 2)]; + Array.Copy(samplesOut, samplesOut_ptr, samplesOut1_tmp_storage2, 0, decControl.nChannelsInternal * (channel_state[0].frame_length + 2)); + samplesOut_tmp = samplesOut1_tmp_storage2; + samplesOut_tmp_ptrs[0] = 0; + samplesOut_tmp_ptrs[1] = channel_state[0].frame_length + 2; + } + for (n = 0; n < Inlines.silk_min(decControl.nChannelsAPI, decControl.nChannelsInternal); n++) + { + + /* Resample decoded signal to API_sampleRate */ + ret += Resampler.silk_resampler(channel_state[n].resampler_state, resample_out, resample_out_ptr, samplesOut_tmp, samplesOut_tmp_ptrs[n] + 1, nSamplesOutDec.Val); + + /* Interleave if stereo output and stereo stream */ + if (decControl.nChannelsAPI == 2) + { + int nptr = samplesOut_ptr + n; + for (i = 0; i < nSamplesOut; i++) + { + samplesOut[nptr + 2 * i] = resample_out[resample_out_ptr + i]; + } + } + } + + /* Create two channel output from mono stream */ + if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 1) + { + if (stereo_to_mono != 0) + { + /* Resample right channel for newly collapsed stereo just in case + we weren't doing collapsing when switching to mono */ + ret += Resampler.silk_resampler(channel_state[1].resampler_state, resample_out, resample_out_ptr, samplesOut_tmp, samplesOut_tmp_ptrs[0] + 1, nSamplesOutDec.Val); + + for (i = 0; i < nSamplesOut; i++) + { + samplesOut[samplesOut_ptr + 1 + 2 * i] = resample_out[resample_out_ptr + i]; + } + } + else { + for (i = 0; i < nSamplesOut; i++) + { + samplesOut[samplesOut_ptr + 1 + 2 * i] = samplesOut[samplesOut_ptr + 2 * i]; + } + } + } + + /* Export pitch lag, measured at 48 kHz sampling rate */ + if (channel_state[0].prevSignalType == SilkConstants.TYPE_VOICED) + { + int[] mult_tab = { 6, 4, 3 }; + decControl.prevPitchLag = channel_state[0].lagPrev * mult_tab[(channel_state[0].fs_kHz - 8) >> 2]; + } + else + { + decControl.prevPitchLag = 0; + } + + if (lostFlag == DecoderAPIFlag.FLAG_PACKET_LOST) + { + /* On packet loss, remove the gain clamping to prevent having the energy "bounce back" + if we lose packets when the energy is going down */ + for (i = 0; i < psDec.nChannelsInternal; i++) + psDec.channel_state[i].LastGainIndex = 10; + } + else + { + psDec.prev_decode_only_middle = decode_only_middle; + } + + return ret; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/DecodeCore.cs b/Libraries/Concentus/CSharp/Concentus/Silk/DecodeCore.cs new file mode 100644 index 000000000..bca7d5a11 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/DecodeCore.cs @@ -0,0 +1,278 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System; + using System.Diagnostics; + + internal static class DecodeCore + { + /**********************************************************/ + /* Core decoder. Performs inverse NSQ operation LTP + LPC */ + /**********************************************************/ + internal static void silk_decode_core( + SilkChannelDecoder psDec, /* I/O Decoder state */ + SilkDecoderControl psDecCtrl, /* I Decoder control */ + short[] xq, /* O Decoded speech */ + int xq_ptr, + short[] pulses /* I Pulse signal [MAX_FRAME_LENGTH] */ + ) + { + int i, k, lag = 0, start_idx, sLTP_buf_idx, NLSF_interpolation_flag, signalType; + short[] A_Q12; + short[] B_Q14 = psDecCtrl.LTPCoef_Q14; + int B_Q14_ptr; + int pxq; + short[] sLTP; + int[] sLTP_Q15; + int LTP_pred_Q13, LPC_pred_Q10, Gain_Q10, inv_gain_Q31, gain_adj_Q16, rand_seed, offset_Q10; + int pred_lag_ptr; + int pexc_Q14; + int[] pres_Q14; + int pres_Q14_ptr; + int[] res_Q14; + int[] sLPC_Q14; + + Inlines.OpusAssert(psDec.prev_gain_Q16 != 0); + + sLTP= new short[psDec.ltp_mem_length]; + sLTP_Q15 = new int[psDec.ltp_mem_length + psDec.frame_length]; + res_Q14 = new int[psDec.subfr_length]; + sLPC_Q14 = new int[psDec.subfr_length + SilkConstants.MAX_LPC_ORDER]; + + offset_Q10 = Tables.silk_Quantization_Offsets_Q10[psDec.indices.signalType >> 1][psDec.indices.quantOffsetType]; + + if (psDec.indices.NLSFInterpCoef_Q2 < 1 << 2) + { + NLSF_interpolation_flag = 1; + } + else { + NLSF_interpolation_flag = 0; + } + + /* Decode excitation */ + rand_seed = psDec.indices.Seed; + for (i = 0; i < psDec.frame_length; i++) + { + rand_seed = Inlines.silk_RAND(rand_seed); + psDec.exc_Q14[i] = Inlines.silk_LSHIFT((int)pulses[i], 14); + if (psDec.exc_Q14[i] > 0) + { + psDec.exc_Q14[i] -= SilkConstants.QUANT_LEVEL_ADJUST_Q10 << 4; + } + else + if (psDec.exc_Q14[i] < 0) + { + psDec.exc_Q14[i] += SilkConstants.QUANT_LEVEL_ADJUST_Q10 << 4; + } + psDec.exc_Q14[i] += offset_Q10 << 4; + if (rand_seed < 0) + { + psDec.exc_Q14[i] = -psDec.exc_Q14[i]; + } + + rand_seed = Inlines.silk_ADD32_ovflw(rand_seed, pulses[i]); + } + + /* Copy LPC state */ + Array.Copy(psDec.sLPC_Q14_buf, sLPC_Q14, SilkConstants.MAX_LPC_ORDER); + + pexc_Q14 = 0; + pxq = xq_ptr; + sLTP_buf_idx = psDec.ltp_mem_length; + /* Loop over subframes */ + for (k = 0; k < psDec.nb_subfr; k++) + { + pres_Q14 = res_Q14; + pres_Q14_ptr = 0; + A_Q12 = psDecCtrl.PredCoef_Q12[k >> 1]; + B_Q14_ptr = k * SilkConstants.LTP_ORDER; + signalType = psDec.indices.signalType; + + Gain_Q10 = Inlines.silk_RSHIFT(psDecCtrl.Gains_Q16[k], 6); + inv_gain_Q31 = Inlines.silk_INVERSE32_varQ(psDecCtrl.Gains_Q16[k], 47); + + /* Calculate gain adjustment factor */ + if (psDecCtrl.Gains_Q16[k] != psDec.prev_gain_Q16) + { + gain_adj_Q16 = Inlines.silk_DIV32_varQ(psDec.prev_gain_Q16, psDecCtrl.Gains_Q16[k], 16); + + /* Scale short term state */ + for (i = 0; i < SilkConstants.MAX_LPC_ORDER; i++) + { + sLPC_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, sLPC_Q14[i]); + } + } + else { + gain_adj_Q16 = (int)1 << 16; + } + + /* Save inv_gain */ + Inlines.OpusAssert(inv_gain_Q31 != 0); + psDec.prev_gain_Q16 = psDecCtrl.Gains_Q16[k]; + + /* Avoid abrupt transition from voiced PLC to unvoiced normal decoding */ + if (psDec.lossCnt != 0 && psDec.prevSignalType == SilkConstants.TYPE_VOICED && + psDec.indices.signalType != SilkConstants.TYPE_VOICED && k < SilkConstants.MAX_NB_SUBFR / 2) + { + + Arrays.MemSetWithOffset(B_Q14, 0, B_Q14_ptr, SilkConstants.LTP_ORDER); + B_Q14[B_Q14_ptr + (SilkConstants.LTP_ORDER / 2)] = (short)(((int)((0.25f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(0.25f, 14)*/); + + signalType = SilkConstants.TYPE_VOICED; + psDecCtrl.pitchL[k] = psDec.lagPrev; + } + + if (signalType == SilkConstants.TYPE_VOICED) + { + /* Voiced */ + lag = psDecCtrl.pitchL[k]; + + /* Re-whitening */ + if (k == 0 || (k == 2 && (NLSF_interpolation_flag != 0))) + { + /* Rewhiten with new A coefs */ + start_idx = psDec.ltp_mem_length - lag - psDec.LPC_order - SilkConstants.LTP_ORDER / 2; + Inlines.OpusAssert(start_idx > 0); + + if (k == 2) + { + Array.Copy(xq, xq_ptr, psDec.outBuf, psDec.ltp_mem_length, 2 * psDec.subfr_length); + } + + Filters.silk_LPC_analysis_filter(sLTP, start_idx, psDec.outBuf, (start_idx + k * psDec.subfr_length), + A_Q12, 0, psDec.ltp_mem_length - start_idx, psDec.LPC_order); + + /* After rewhitening the LTP state is unscaled */ + if (k == 0) + { + /* Do LTP downscaling to reduce inter-packet dependency */ + inv_gain_Q31 = Inlines.silk_LSHIFT(Inlines.silk_SMULWB(inv_gain_Q31, psDecCtrl.LTP_scale_Q14), 2); + } + for (i = 0; i < lag + SilkConstants.LTP_ORDER / 2; i++) + { + sLTP_Q15[sLTP_buf_idx - i - 1] = Inlines.silk_SMULWB(inv_gain_Q31, sLTP[psDec.ltp_mem_length - i - 1]); + } + } + else { + /* Update LTP state when Gain changes */ + if (gain_adj_Q16 != (int)1 << 16) + { + for (i = 0; i < lag + SilkConstants.LTP_ORDER / 2; i++) + { + sLTP_Q15[sLTP_buf_idx - i - 1] = Inlines.silk_SMULWW(gain_adj_Q16, sLTP_Q15[sLTP_buf_idx - i - 1]); + } + } + } + } + + /* Long-term prediction */ + if (signalType == SilkConstants.TYPE_VOICED) + { + /* Set up pointer */ + pred_lag_ptr = sLTP_buf_idx - lag + SilkConstants.LTP_ORDER / 2; + for (i = 0; i < psDec.subfr_length; i++) + { + /* Unrolled loop */ + /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */ + LTP_pred_Q13 = 2; + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr], B_Q14[B_Q14_ptr]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 1], B_Q14[B_Q14_ptr + 1]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 2], B_Q14[B_Q14_ptr + 2]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 3], B_Q14[B_Q14_ptr + 3]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 4], B_Q14[B_Q14_ptr + 4]); + pred_lag_ptr += 1; + + /* Generate LPC excitation */ + pres_Q14[pres_Q14_ptr + i] = Inlines.silk_ADD_LSHIFT32(psDec.exc_Q14[pexc_Q14 + i], LTP_pred_Q13, 1); + + /* Update states */ + sLTP_Q15[sLTP_buf_idx] = Inlines.silk_LSHIFT(pres_Q14[pres_Q14_ptr + i], 1); + sLTP_buf_idx++; + } + } + else { + pres_Q14 = psDec.exc_Q14; + pres_Q14_ptr = pexc_Q14; + } + + for (i = 0; i < psDec.subfr_length; i++) + { + /* Short-term prediction */ + Inlines.OpusAssert(psDec.LPC_order == 10 || psDec.LPC_order == 16); + /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */ + LPC_pred_Q10 = Inlines.silk_RSHIFT(psDec.LPC_order, 1); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 1], A_Q12[0]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 2], A_Q12[1]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 3], A_Q12[2]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 4], A_Q12[3]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 5], A_Q12[4]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 6], A_Q12[5]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 7], A_Q12[6]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 8], A_Q12[7]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 9], A_Q12[8]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 10], A_Q12[9]); + if (psDec.LPC_order == 16) + { + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 11], A_Q12[10]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 12], A_Q12[11]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 13], A_Q12[12]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 14], A_Q12[13]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 15], A_Q12[14]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 16], A_Q12[15]); + } + + /* Add prediction to LPC excitation */ + sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i] = Inlines.silk_ADD_LSHIFT32(pres_Q14[pres_Q14_ptr + i], LPC_pred_Q10, 4); + + /* Scale with gain */ + xq[pxq + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWW(sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i], Gain_Q10), 8)); + } + + /* DEBUG_STORE_DATA( dec.pcm, pxq, psDec.subfr_length * sizeof( short ) ) */ + + /* Update LPC filter state */ + Array.Copy(sLPC_Q14, psDec.subfr_length, sLPC_Q14, 0, SilkConstants.MAX_LPC_ORDER); + pexc_Q14 += psDec.subfr_length; + pxq += psDec.subfr_length; + } + + /* Save LPC state */ + Array.Copy(sLPC_Q14, 0, psDec.sLPC_Q14_buf, 0, SilkConstants.MAX_LPC_ORDER); + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/DecodeIndices.cs b/Libraries/Concentus/CSharp/Concentus/Silk/DecodeIndices.cs new file mode 100644 index 000000000..b4a797479 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/DecodeIndices.cs @@ -0,0 +1,179 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal static class DecodeIndices + { + /* Decode side-information parameters from payload */ + internal static void silk_decode_indices( + SilkChannelDecoder psDec, /* I/O State */ + EntropyCoder psRangeDec, /* I/O Compressor data structure */ + int FrameIndex, /* I Frame number */ + int decode_LBRR, /* I Flag indicating LBRR data is being decoded */ + int condCoding /* I The type of conditional coding to use */ + ) + { + int i, k, Ix; + int decode_absolute_lagIndex, delta_lagIndex; + short[] ec_ix = new short[psDec.LPC_order]; + byte[] pred_Q8 = new byte[psDec.LPC_order]; + + /*******************************************/ + /* Decode signal type and quantizer offset */ + /*******************************************/ + if (decode_LBRR != 0 || psDec.VAD_flags[FrameIndex] != 0) + { + Ix = psRangeDec.dec_icdf(Tables.silk_type_offset_VAD_iCDF, 8) + 2; + } + else { + Ix = psRangeDec.dec_icdf(Tables.silk_type_offset_no_VAD_iCDF, 8); + } + psDec.indices.signalType = (sbyte)Inlines.silk_RSHIFT(Ix, 1); + psDec.indices.quantOffsetType = (sbyte)(Ix & 1); + + /****************/ + /* Decode gains */ + /****************/ + /* First subframe */ + if (condCoding == SilkConstants.CODE_CONDITIONALLY) + { + /* Conditional coding */ + psDec.indices.GainsIndices[0] = (sbyte)psRangeDec.dec_icdf(Tables.silk_delta_gain_iCDF, 8); + } + else { + /* Independent coding, in two stages: MSB bits followed by 3 LSBs */ + psDec.indices.GainsIndices[0] = (sbyte)Inlines.silk_LSHIFT(psRangeDec.dec_icdf(Tables.silk_gain_iCDF[psDec.indices.signalType], 8), 3); + psDec.indices.GainsIndices[0] += (sbyte)psRangeDec.dec_icdf(Tables.silk_uniform8_iCDF, 8); + } + + /* Remaining subframes */ + for (i = 1; i < psDec.nb_subfr; i++) + { + psDec.indices.GainsIndices[i] = (sbyte)psRangeDec.dec_icdf(Tables.silk_delta_gain_iCDF, 8); + } + + /**********************/ + /* Decode LSF Indices */ + /**********************/ + psDec.indices.NLSFIndices[0] = (sbyte)psRangeDec.dec_icdf(psDec.psNLSF_CB.CB1_iCDF, (psDec.indices.signalType >> 1) * psDec.psNLSF_CB.nVectors, 8); + NLSF.silk_NLSF_unpack(ec_ix, pred_Q8, psDec.psNLSF_CB, psDec.indices.NLSFIndices[0]); + Inlines.OpusAssert(psDec.psNLSF_CB.order == psDec.LPC_order); + for (i = 0; i < psDec.psNLSF_CB.order; i++) + { + Ix = psRangeDec.dec_icdf(psDec.psNLSF_CB.ec_iCDF, (ec_ix[i]), 8); + if (Ix == 0) + { + Ix -= psRangeDec.dec_icdf(Tables.silk_NLSF_EXT_iCDF, 8); + } + else if (Ix == 2 * SilkConstants.NLSF_QUANT_MAX_AMPLITUDE) + { + Ix += psRangeDec.dec_icdf(Tables.silk_NLSF_EXT_iCDF, 8); + } + psDec.indices.NLSFIndices[i + 1] = (sbyte)(Ix - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE); + } + + /* Decode LSF interpolation factor */ + if (psDec.nb_subfr == SilkConstants.MAX_NB_SUBFR) + { + psDec.indices.NLSFInterpCoef_Q2 = (sbyte)psRangeDec.dec_icdf(Tables.silk_NLSF_interpolation_factor_iCDF, 8); + } + else { + psDec.indices.NLSFInterpCoef_Q2 = 4; + } + + if (psDec.indices.signalType == SilkConstants.TYPE_VOICED) + { + /*********************/ + /* Decode pitch lags */ + /*********************/ + /* Get lag index */ + decode_absolute_lagIndex = 1; + if (condCoding == SilkConstants.CODE_CONDITIONALLY && psDec.ec_prevSignalType == SilkConstants.TYPE_VOICED) + { + /* Decode Delta index */ + delta_lagIndex = (short)psRangeDec.dec_icdf(Tables.silk_pitch_delta_iCDF, 8); + if (delta_lagIndex > 0) + { + delta_lagIndex = delta_lagIndex - 9; + psDec.indices.lagIndex = (short)(psDec.ec_prevLagIndex + delta_lagIndex); + decode_absolute_lagIndex = 0; + } + } + if (decode_absolute_lagIndex != 0) + { + /* Absolute decoding */ + psDec.indices.lagIndex = (short)(psRangeDec.dec_icdf(Tables.silk_pitch_lag_iCDF, 8) * Inlines.silk_RSHIFT(psDec.fs_kHz, 1)); + psDec.indices.lagIndex += (short)psRangeDec.dec_icdf(psDec.pitch_lag_low_bits_iCDF, 8); + } + psDec.ec_prevLagIndex = psDec.indices.lagIndex; + + /* Get countour index */ + psDec.indices.contourIndex = (sbyte)psRangeDec.dec_icdf(psDec.pitch_contour_iCDF, 8); + + /********************/ + /* Decode LTP gains */ + /********************/ + /* Decode PERIndex value */ + psDec.indices.PERIndex = (sbyte)psRangeDec.dec_icdf(Tables.silk_LTP_per_index_iCDF, 8); + + for (k = 0; k < psDec.nb_subfr; k++) + { + psDec.indices.LTPIndex[k] = (sbyte)psRangeDec.dec_icdf(Tables.silk_LTP_gain_iCDF_ptrs[psDec.indices.PERIndex], 8); + } + + /**********************/ + /* Decode LTP scaling */ + /**********************/ + if (condCoding == SilkConstants.CODE_INDEPENDENTLY) + { + psDec.indices.LTP_scaleIndex = (sbyte)psRangeDec.dec_icdf(Tables.silk_LTPscale_iCDF, 8); + } + else { + psDec.indices.LTP_scaleIndex = 0; + } + } + psDec.ec_prevSignalType = psDec.indices.signalType; + + /***************/ + /* Decode seed */ + /***************/ + psDec.indices.Seed = (sbyte)psRangeDec.dec_icdf(Tables.silk_uniform4_iCDF, 8); + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/DecodeParameters.cs b/Libraries/Concentus/CSharp/Concentus/Silk/DecodeParameters.cs new file mode 100644 index 000000000..4689ce227 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/DecodeParameters.cs @@ -0,0 +1,141 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System; + using System.Diagnostics; + + internal class DecodeParameters + { + /* Decode parameters from payload */ + internal static void silk_decode_parameters( + SilkChannelDecoder psDec, /* I/O State */ + SilkDecoderControl psDecCtrl, /* I/O Decoder control */ + int condCoding /* I The type of conditional coding to use */ + ) + { + int i, k, Ix; + short[] pNLSF_Q15 = new short[psDec.LPC_order]; + short[] pNLSF0_Q15 = new short[psDec.LPC_order]; + sbyte[][] cbk_ptr_Q7; + + /* Dequant Gains */ + BoxedValueSbyte boxedLastGainIndex = new BoxedValueSbyte(psDec.LastGainIndex); + GainQuantization.silk_gains_dequant(psDecCtrl.Gains_Q16, psDec.indices.GainsIndices, + boxedLastGainIndex, condCoding == SilkConstants.CODE_CONDITIONALLY ? 1 : 0, psDec.nb_subfr); + psDec.LastGainIndex = boxedLastGainIndex.Val; + + /****************/ + /* Decode NLSFs */ + /****************/ + NLSF.silk_NLSF_decode(pNLSF_Q15, psDec.indices.NLSFIndices, psDec.psNLSF_CB); + + /* Convert NLSF parameters to AR prediction filter coefficients */ + NLSF.silk_NLSF2A(psDecCtrl.PredCoef_Q12[1], pNLSF_Q15, psDec.LPC_order); + + /* If just reset, e.g., because internal Fs changed, do not allow interpolation */ + /* improves the case of packet loss in the first frame after a switch */ + if (psDec.first_frame_after_reset == 1) + { + psDec.indices.NLSFInterpCoef_Q2 = 4; + } + + if (psDec.indices.NLSFInterpCoef_Q2 < 4) + { + /* Calculation of the interpolated NLSF0 vector from the interpolation factor, */ + /* the previous NLSF1, and the current NLSF1 */ + for (i = 0; i < psDec.LPC_order; i++) + { + pNLSF0_Q15[i] = (short)(psDec.prevNLSF_Q15[i] + Inlines.silk_RSHIFT(Inlines.silk_MUL(psDec.indices.NLSFInterpCoef_Q2, + pNLSF_Q15[i] - psDec.prevNLSF_Q15[i]), 2)); + } + + /* Convert NLSF parameters to AR prediction filter coefficients */ + NLSF.silk_NLSF2A(psDecCtrl.PredCoef_Q12[0], pNLSF0_Q15, psDec.LPC_order); + } + else + { + /* Copy LPC coefficients for first half from second half */ + Array.Copy(psDecCtrl.PredCoef_Q12[1], psDecCtrl.PredCoef_Q12[0], psDec.LPC_order); + } + + Array.Copy(pNLSF_Q15, psDec.prevNLSF_Q15, psDec.LPC_order); + + /* After a packet loss do BWE of LPC coefs */ + if (psDec.lossCnt != 0) + { + BWExpander.silk_bwexpander(psDecCtrl.PredCoef_Q12[0], psDec.LPC_order, SilkConstants.BWE_AFTER_LOSS_Q16); + BWExpander.silk_bwexpander(psDecCtrl.PredCoef_Q12[1], psDec.LPC_order, SilkConstants.BWE_AFTER_LOSS_Q16); + } + + if (psDec.indices.signalType == SilkConstants.TYPE_VOICED) + { + /*********************/ + /* Decode pitch lags */ + /*********************/ + + /* Decode pitch values */ + DecodePitch.silk_decode_pitch(psDec.indices.lagIndex, psDec.indices.contourIndex, psDecCtrl.pitchL, psDec.fs_kHz, psDec.nb_subfr); + + /* Decode Codebook Index */ + cbk_ptr_Q7 = Tables.silk_LTP_vq_ptrs_Q7[psDec.indices.PERIndex]; /* set pointer to start of codebook */ + + for (k = 0; k < psDec.nb_subfr; k++) + { + Ix = psDec.indices.LTPIndex[k]; + for (i = 0; i < SilkConstants.LTP_ORDER; i++) + { + psDecCtrl.LTPCoef_Q14[k * SilkConstants.LTP_ORDER + i] = (short)(Inlines.silk_LSHIFT(cbk_ptr_Q7[Ix][i], 7)); + } + } + + /**********************/ + /* Decode LTP scaling */ + /**********************/ + Ix = psDec.indices.LTP_scaleIndex; + psDecCtrl.LTP_scale_Q14 = Tables.silk_LTPScales_table_Q14[Ix]; + } + else + { + Arrays.MemSetInt(psDecCtrl.pitchL, 0, psDec.nb_subfr); + Arrays.MemSetShort(psDecCtrl.LTPCoef_Q14, 0, SilkConstants.LTP_ORDER * psDec.nb_subfr); + psDec.indices.PERIndex = 0; + psDecCtrl.LTP_scale_Q14 = 0; + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/DecodePitch.cs b/Libraries/Concentus/CSharp/Concentus/Silk/DecodePitch.cs new file mode 100644 index 000000000..82334ca68 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/DecodePitch.cs @@ -0,0 +1,90 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal static class DecodePitch + { + internal static void silk_decode_pitch( + short lagIndex, /* I */ + sbyte contourIndex, /* O */ + int[] pitch_lags, /* O 4 pitch values */ + int Fs_kHz, /* I sampling frequency (kHz) */ + int nb_subfr /* I number of sub frames */ + ) + { + int lag, k, min_lag, max_lag; + sbyte[][] Lag_CB_ptr; + + if (Fs_kHz == 8) + { + if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR) + { + Lag_CB_ptr = Tables.silk_CB_lags_stage2; + } + else + { + Inlines.OpusAssert(nb_subfr == SilkConstants.PE_MAX_NB_SUBFR >> 1); + Lag_CB_ptr = Tables.silk_CB_lags_stage2_10_ms; + } + } + else + { + if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR) + { + Lag_CB_ptr = Tables.silk_CB_lags_stage3; + } + else + { + Inlines.OpusAssert(nb_subfr == SilkConstants.PE_MAX_NB_SUBFR >> 1); + Lag_CB_ptr = Tables.silk_CB_lags_stage3_10_ms; + } + } + + min_lag = Inlines.silk_SMULBB(SilkConstants.PE_MIN_LAG_MS, Fs_kHz); + max_lag = Inlines.silk_SMULBB(SilkConstants.PE_MAX_LAG_MS, Fs_kHz); + lag = min_lag + lagIndex; + + for (k = 0; k < nb_subfr; k++) + { + pitch_lags[k] = lag + Lag_CB_ptr[k][contourIndex]; + pitch_lags[k] = Inlines.silk_LIMIT(pitch_lags[k], min_lag, max_lag); + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/DecodePulses.cs b/Libraries/Concentus/CSharp/Concentus/Silk/DecodePulses.cs new file mode 100644 index 000000000..346df54b1 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/DecodePulses.cs @@ -0,0 +1,136 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal static class DecodePulses + { + /*********************************************/ + /* Decode quantization indices of excitation */ + /*********************************************/ + internal static void silk_decode_pulses( + EntropyCoder psRangeDec, /* I/O Compressor data structure */ + short[] pulses, /* O Excitation signal */ + int signalType, /* I Sigtype */ + int quantOffsetType, /* I quantOffsetType */ + int frame_length /* I Frame length */ + ) + { + int i, j, k, iter, abs_q, nLS, RateLevelIndex; + int[] sum_pulses = new int[SilkConstants.MAX_NB_SHELL_BLOCKS]; + int[] nLshifts = new int[SilkConstants.MAX_NB_SHELL_BLOCKS]; + int pulses_ptr; + + /*********************/ + /* Decode rate level */ + /*********************/ + RateLevelIndex = psRangeDec.dec_icdf(Tables.silk_rate_levels_iCDF[signalType >> 1], 8); + + /* Calculate number of shell blocks */ + Inlines.OpusAssert(1 << SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH == SilkConstants.SHELL_CODEC_FRAME_LENGTH); + iter = Inlines.silk_RSHIFT(frame_length, SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH); + if (iter * SilkConstants.SHELL_CODEC_FRAME_LENGTH < frame_length) + { + Inlines.OpusAssert(frame_length == 12 * 10); /* Make sure only happens for 10 ms @ 12 kHz */ + iter++; + } + + /***************************************************/ + /* Sum-Weighted-Pulses Decoding */ + /***************************************************/ + for (i = 0; i < iter; i++) + { + nLshifts[i] = 0; + sum_pulses[i] = psRangeDec.dec_icdf(Tables.silk_pulses_per_block_iCDF[RateLevelIndex], 8); + + /* LSB indication */ + while (sum_pulses[i] == SilkConstants.SILK_MAX_PULSES + 1) + { + nLshifts[i]++; + /* When we've already got 10 LSBs, we shift the table to not allow (SILK_MAX_PULSES + 1) */ + sum_pulses[i] = psRangeDec.dec_icdf( + Tables.silk_pulses_per_block_iCDF[SilkConstants.N_RATE_LEVELS - 1], (nLshifts[i] == 10 ? 1 : 0), 8); + } + } + + /***************************************************/ + /* Shell decoding */ + /***************************************************/ + for (i = 0; i < iter; i++) + { + if (sum_pulses[i] > 0) + { + ShellCoder.silk_shell_decoder(pulses, Inlines.silk_SMULBB(i, SilkConstants.SHELL_CODEC_FRAME_LENGTH), psRangeDec, sum_pulses[i]); + } + else + { + Arrays.MemSetWithOffset(pulses, 0, Inlines.silk_SMULBB(i, SilkConstants.SHELL_CODEC_FRAME_LENGTH), SilkConstants.SHELL_CODEC_FRAME_LENGTH); + } + } + + /***************************************************/ + /* LSB Decoding */ + /***************************************************/ + for (i = 0; i < iter; i++) + { + if (nLshifts[i] > 0) + { + nLS = nLshifts[i]; + pulses_ptr = Inlines.silk_SMULBB(i, SilkConstants.SHELL_CODEC_FRAME_LENGTH); + for (k = 0; k < SilkConstants.SHELL_CODEC_FRAME_LENGTH; k++) + { + abs_q = pulses[pulses_ptr + k]; + for (j = 0; j < nLS; j++) + { + abs_q = Inlines.silk_LSHIFT(abs_q, 1); + abs_q += psRangeDec.dec_icdf(Tables.silk_lsb_iCDF, 8); + } + pulses[pulses_ptr + k] = (short)(abs_q); + } + /* Mark the number of pulses non-zero for sign decoding. */ + sum_pulses[i] |= nLS << 5; + } + } + + /****************************************/ + /* Decode and add signs to pulse signal */ + /****************************************/ + CodeSigns.silk_decode_signs(psRangeDec, pulses, frame_length, signalType, quantOffsetType, sum_pulses); + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/EncodeAPI.cs b/Libraries/Concentus/CSharp/Concentus/Silk/EncodeAPI.cs new file mode 100644 index 000000000..73824994d --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/EncodeAPI.cs @@ -0,0 +1,740 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System; + using System.Diagnostics; + + internal static class EncodeAPI + { + /// + /// Init or Reset encoder + /// + /// I/O State + /// O Encoder Status + /// O Returns error code + internal static int silk_InitEncoder(SilkEncoder encState, EncControlState encStatus) + { + int ret = SilkError.SILK_NO_ERROR; + + /* Reset encoder */ + encState.Reset(); + + for (int n = 0; n < SilkConstants.ENCODER_NUM_CHANNELS; n++) + { + ret += SilkEncoder.silk_init_encoder(encState.state_Fxx[n]); + Inlines.OpusAssert(ret == SilkError.SILK_NO_ERROR); + } + + encState.nChannelsAPI = 1; + encState.nChannelsInternal = 1; + + /* Read control structure */ + ret += silk_QueryEncoder(encState, encStatus); + Inlines.OpusAssert(ret == SilkError.SILK_NO_ERROR); + + return ret; + } + + /// + /// Read control structure from encode + /// + /// I State + /// O Encoder Status + /// Returns error code + internal static int silk_QueryEncoder(SilkEncoder encState, EncControlState encStatus) + { + int ret = SilkError.SILK_NO_ERROR; + SilkChannelEncoder state_Fxx = encState.state_Fxx[0]; + + encStatus.Reset(); + + encStatus.nChannelsAPI = encState.nChannelsAPI; + encStatus.nChannelsInternal = encState.nChannelsInternal; + encStatus.API_sampleRate = state_Fxx.API_fs_Hz; + encStatus.maxInternalSampleRate = state_Fxx.maxInternal_fs_Hz; + encStatus.minInternalSampleRate = state_Fxx.minInternal_fs_Hz; + encStatus.desiredInternalSampleRate = state_Fxx.desiredInternal_fs_Hz; + encStatus.payloadSize_ms = state_Fxx.PacketSize_ms; + encStatus.bitRate = state_Fxx.TargetRate_bps; + encStatus.packetLossPercentage = state_Fxx.PacketLoss_perc; + encStatus.complexity = state_Fxx.Complexity; + encStatus.useInBandFEC = state_Fxx.useInBandFEC; + encStatus.useDTX = state_Fxx.useDTX; + encStatus.useCBR = state_Fxx.useCBR; + encStatus.internalSampleRate = Inlines.silk_SMULBB(state_Fxx.fs_kHz, 1000); + encStatus.allowBandwidthSwitch = state_Fxx.allow_bandwidth_switch; + encStatus.inWBmodeWithoutVariableLP = (state_Fxx.fs_kHz == 16 && state_Fxx.sLP.mode == 0) ? 1 : 0; + + return ret; + } + + /// + /// Encode frame with Silk + /// Note: if prefillFlag is set, the input must contain 10 ms of audio, irrespective of what + /// encControl.payloadSize_ms is set to + /// + /// I/O State + /// I Control status + /// I Speech sample input vector + /// I Number of samples in input vector + /// I/O Compressor data structure + /// I/O Number of bytes in payload (input: Max bytes) + /// I Flag to indicate prefilling buffers no coding + /// error code + internal static int silk_Encode( + SilkEncoder psEnc, + EncControlState encControl, + short[] samplesIn, + int nSamplesIn, + EntropyCoder psRangeEnc, + BoxedValueInt nBytesOut, + int prefillFlag) + { + int ret = SilkError.SILK_NO_ERROR; + int n, i, nBits, flags, tmp_payloadSize_ms = 0, tmp_complexity = 0; + int nSamplesToBuffer, nSamplesToBufferMax, nBlocksOf10ms; + int nSamplesFromInput = 0, nSamplesFromInputMax; + int speech_act_thr_for_switch_Q8; + int TargetRate_bps, channelRate_bps, LBRR_symbol, sum; + int[] MStargetRates_bps = new int[2]; + short[] buf; + int transition, curr_block, tot_blocks; + nBytesOut.Val = 0; + + if (encControl.reducedDependency != 0) + { + psEnc.state_Fxx[0].first_frame_after_reset = 1; + psEnc.state_Fxx[1].first_frame_after_reset = 1; + } + psEnc.state_Fxx[0].nFramesEncoded = psEnc.state_Fxx[1].nFramesEncoded = 0; + + /* Check values in encoder control structure */ + ret += encControl.check_control_input(); + if (ret != SilkError.SILK_NO_ERROR) + { + Inlines.OpusAssert(false); + return ret; + } + + encControl.switchReady = 0; + + if (encControl.nChannelsInternal > psEnc.nChannelsInternal) + { + /* Mono . Stereo transition: init state of second channel and stereo state */ + ret += SilkEncoder.silk_init_encoder(psEnc.state_Fxx[1]); + + Arrays.MemSetShort(psEnc.sStereo.pred_prev_Q13, 0, 2); + Arrays.MemSetShort(psEnc.sStereo.sSide, 0, 2); + psEnc.sStereo.mid_side_amp_Q0[0] = 0; + psEnc.sStereo.mid_side_amp_Q0[1] = 1; + psEnc.sStereo.mid_side_amp_Q0[2] = 0; + psEnc.sStereo.mid_side_amp_Q0[3] = 1; + psEnc.sStereo.width_prev_Q14 = 0; + psEnc.sStereo.smth_width_Q14 = (short)(((int)((1.0f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(1.0f, 14)*/); + if (psEnc.nChannelsAPI == 2) + { + psEnc.state_Fxx[1].resampler_state.Assign(psEnc.state_Fxx[0].resampler_state); + Array.Copy(psEnc.state_Fxx[0].In_HP_State, psEnc.state_Fxx[1].In_HP_State, 2); + } + } + + transition = ((encControl.payloadSize_ms != psEnc.state_Fxx[0].PacketSize_ms) || (psEnc.nChannelsInternal != encControl.nChannelsInternal)) ? 1 : 0; + + psEnc.nChannelsAPI = encControl.nChannelsAPI; + psEnc.nChannelsInternal = encControl.nChannelsInternal; + + nBlocksOf10ms = Inlines.silk_DIV32(100 * nSamplesIn, encControl.API_sampleRate); + tot_blocks = (nBlocksOf10ms > 1) ? nBlocksOf10ms >> 1 : 1; + curr_block = 0; + if (prefillFlag != 0) + { + /* Only accept input length of 10 ms */ + if (nBlocksOf10ms != 1) + { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES; + } + /* Reset Encoder */ + for (n = 0; n < encControl.nChannelsInternal; n++) + { + ret += SilkEncoder.silk_init_encoder(psEnc.state_Fxx[n]); + Inlines.OpusAssert(ret == SilkError.SILK_NO_ERROR); + } + tmp_payloadSize_ms = encControl.payloadSize_ms; + encControl.payloadSize_ms = 10; + tmp_complexity = encControl.complexity; + encControl.complexity = 0; + for (n = 0; n < encControl.nChannelsInternal; n++) + { + psEnc.state_Fxx[n].controlled_since_last_payload = 0; + psEnc.state_Fxx[n].prefillFlag = 1; + } + } + else + { + /* Only accept input lengths that are a multiple of 10 ms */ + if (nBlocksOf10ms * encControl.API_sampleRate != 100 * nSamplesIn || nSamplesIn < 0) + { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES; + } + /* Make sure no more than one packet can be produced */ + if (1000 * (int)nSamplesIn > encControl.payloadSize_ms * encControl.API_sampleRate) + { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES; + } + } + + TargetRate_bps = Inlines.silk_RSHIFT32(encControl.bitRate, encControl.nChannelsInternal - 1); + + for (n = 0; n < encControl.nChannelsInternal; n++) + { + /* Force the side channel to the same rate as the mid */ + int force_fs_kHz = (n == 1) ? psEnc.state_Fxx[0].fs_kHz : 0; + ret += psEnc.state_Fxx[n].silk_control_encoder(encControl, TargetRate_bps, psEnc.allowBandwidthSwitch, n, force_fs_kHz); + + if (ret != SilkError.SILK_NO_ERROR) + { + Inlines.OpusAssert(false); + return ret; + } + + if (psEnc.state_Fxx[n].first_frame_after_reset != 0 || transition != 0) + { + for (i = 0; i < psEnc.state_Fxx[0].nFramesPerPacket; i++) + { + psEnc.state_Fxx[n].LBRR_flags[i] = 0; + } + } + + psEnc.state_Fxx[n].inDTX = psEnc.state_Fxx[n].useDTX; + } + + Inlines.OpusAssert(encControl.nChannelsInternal == 1 || psEnc.state_Fxx[0].fs_kHz == psEnc.state_Fxx[1].fs_kHz); + + /* Input buffering/resampling and encoding */ + nSamplesToBufferMax = 10 * nBlocksOf10ms * psEnc.state_Fxx[0].fs_kHz; + nSamplesFromInputMax = + Inlines.silk_DIV32_16(nSamplesToBufferMax * + psEnc.state_Fxx[0].API_fs_Hz, + (short)(psEnc.state_Fxx[0].fs_kHz * 1000)); + + buf = new short[nSamplesFromInputMax]; + + int samplesIn_ptr = 0; + while (true) + { + nSamplesToBuffer = psEnc.state_Fxx[0].frame_length - psEnc.state_Fxx[0].inputBufIx; + nSamplesToBuffer = Inlines.silk_min(nSamplesToBuffer, nSamplesToBufferMax); + nSamplesFromInput = Inlines.silk_DIV32_16(nSamplesToBuffer * psEnc.state_Fxx[0].API_fs_Hz, psEnc.state_Fxx[0].fs_kHz * 1000); + + /* Resample and write to buffer */ + if (encControl.nChannelsAPI == 2 && encControl.nChannelsInternal == 2) + { + int id = psEnc.state_Fxx[0].nFramesEncoded; + for (n = 0; n < nSamplesFromInput; n++) + { + buf[n] = samplesIn[samplesIn_ptr + (2 * n)]; + } + + /* Making sure to start both resamplers from the same state when switching from mono to stereo */ + if (psEnc.nPrevChannelsInternal == 1 && id == 0) + { + //silk_memcpy(&psEnc.state_Fxx[1].resampler_state, &psEnc.state_Fxx[0].resampler_state, sizeof(psEnc.state_Fxx[1].resampler_state)); + psEnc.state_Fxx[1].resampler_state.Assign(psEnc.state_Fxx[0].resampler_state); + } + + ret += Resampler.silk_resampler( + psEnc.state_Fxx[0].resampler_state, + psEnc.state_Fxx[0].inputBuf, + psEnc.state_Fxx[0].inputBufIx + 2, + buf, + 0, + nSamplesFromInput); + + psEnc.state_Fxx[0].inputBufIx += nSamplesToBuffer; + + nSamplesToBuffer = psEnc.state_Fxx[1].frame_length - psEnc.state_Fxx[1].inputBufIx; + nSamplesToBuffer = Inlines.silk_min(nSamplesToBuffer, 10 * nBlocksOf10ms * psEnc.state_Fxx[1].fs_kHz); + for (n = 0; n < nSamplesFromInput; n++) + { + buf[n] = samplesIn[samplesIn_ptr + (2 * n) + 1]; + } + ret += Resampler.silk_resampler( + psEnc.state_Fxx[1].resampler_state, + psEnc.state_Fxx[1].inputBuf, + psEnc.state_Fxx[1].inputBufIx + 2, + buf, + 0, + nSamplesFromInput); + + psEnc.state_Fxx[1].inputBufIx += nSamplesToBuffer; + } + else if (encControl.nChannelsAPI == 2 && encControl.nChannelsInternal == 1) + { + /* Combine left and right channels before resampling */ + for (n = 0; n < nSamplesFromInput; n++) + { + sum = samplesIn[samplesIn_ptr + (2 * n)] + samplesIn[samplesIn_ptr + (2 * n) + 1]; + buf[n] = (short)Inlines.silk_RSHIFT_ROUND(sum, 1); + } + + ret += Resampler.silk_resampler( + psEnc.state_Fxx[0].resampler_state, + psEnc.state_Fxx[0].inputBuf, + psEnc.state_Fxx[0].inputBufIx + 2, + buf, + 0, + nSamplesFromInput); + + /* On the first mono frame, average the results for the two resampler states */ + if (psEnc.nPrevChannelsInternal == 2 && psEnc.state_Fxx[0].nFramesEncoded == 0) + { + ret += Resampler.silk_resampler( + psEnc.state_Fxx[1].resampler_state, + psEnc.state_Fxx[1].inputBuf, + psEnc.state_Fxx[1].inputBufIx + 2, + buf, + 0, + nSamplesFromInput); + + for (n = 0; n < psEnc.state_Fxx[0].frame_length; n++) + { + psEnc.state_Fxx[0].inputBuf[psEnc.state_Fxx[0].inputBufIx + n + 2] = + (short)(Inlines.silk_RSHIFT(psEnc.state_Fxx[0].inputBuf[psEnc.state_Fxx[0].inputBufIx + n + 2] + + psEnc.state_Fxx[1].inputBuf[psEnc.state_Fxx[1].inputBufIx + n + 2], 1)); + } + } + + psEnc.state_Fxx[0].inputBufIx += nSamplesToBuffer; + } + else + { + Inlines.OpusAssert(encControl.nChannelsAPI == 1 && encControl.nChannelsInternal == 1); + Array.Copy(samplesIn, samplesIn_ptr, buf, 0, nSamplesFromInput); + ret += Resampler.silk_resampler( + psEnc.state_Fxx[0].resampler_state, + psEnc.state_Fxx[0].inputBuf, + psEnc.state_Fxx[0].inputBufIx + 2, + buf, + 0, + nSamplesFromInput); + + psEnc.state_Fxx[0].inputBufIx += nSamplesToBuffer; + } + + samplesIn_ptr += (nSamplesFromInput * encControl.nChannelsAPI); + nSamplesIn -= nSamplesFromInput; + + /* Default */ + psEnc.allowBandwidthSwitch = 0; + + /* Silk encoder */ + if (psEnc.state_Fxx[0].inputBufIx >= psEnc.state_Fxx[0].frame_length) + { + /* Enough data in input buffer, so encode */ + Inlines.OpusAssert(psEnc.state_Fxx[0].inputBufIx == psEnc.state_Fxx[0].frame_length); + Inlines.OpusAssert(encControl.nChannelsInternal == 1 || psEnc.state_Fxx[1].inputBufIx == psEnc.state_Fxx[1].frame_length); + + /* Deal with LBRR data */ + if (psEnc.state_Fxx[0].nFramesEncoded == 0 && prefillFlag == 0) + { + /* Create space at start of payload for VAD and FEC flags */ + byte[] iCDF = { 0, 0 }; + iCDF[0] = (byte)(256 - Inlines.silk_RSHIFT(256, (psEnc.state_Fxx[0].nFramesPerPacket + 1) * encControl.nChannelsInternal)); + psRangeEnc.enc_icdf(0, iCDF, 8); + + /* Encode any LBRR data from previous packet */ + /* Encode LBRR flags */ + for (n = 0; n < encControl.nChannelsInternal; n++) + { + LBRR_symbol = 0; + for (i = 0; i < psEnc.state_Fxx[n].nFramesPerPacket; i++) + { + LBRR_symbol |= Inlines.silk_LSHIFT(psEnc.state_Fxx[n].LBRR_flags[i], i); + } + + psEnc.state_Fxx[n].LBRR_flag = (sbyte)(LBRR_symbol > 0 ? 1 : 0); + if (LBRR_symbol != 0 && psEnc.state_Fxx[n].nFramesPerPacket > 1) + { + psRangeEnc.enc_icdf( LBRR_symbol - 1, Tables.silk_LBRR_flags_iCDF_ptr[psEnc.state_Fxx[n].nFramesPerPacket - 2], 8); + } + } + + /* Code LBRR indices and excitation signals */ + for (i = 0; i < psEnc.state_Fxx[0].nFramesPerPacket; i++) + { + for (n = 0; n < encControl.nChannelsInternal; n++) + { + if (psEnc.state_Fxx[n].LBRR_flags[i] != 0) + { + int condCoding; + + if (encControl.nChannelsInternal == 2 && n == 0) + { + Stereo.silk_stereo_encode_pred(psRangeEnc, psEnc.sStereo.predIx[i]); + /* For LBRR data there's no need to code the mid-only flag if the side-channel LBRR flag is set */ + if (psEnc.state_Fxx[1].LBRR_flags[i] == 0) + { + Stereo.silk_stereo_encode_mid_only(psRangeEnc, psEnc.sStereo.mid_only_flags[i]); + } + } + + /* Use conditional coding if previous frame available */ + if (i > 0 && psEnc.state_Fxx[n].LBRR_flags[i - 1] != 0) + { + condCoding = SilkConstants.CODE_CONDITIONALLY; + } + else + { + condCoding = SilkConstants.CODE_INDEPENDENTLY; + } + + EncodeIndices.silk_encode_indices(psEnc.state_Fxx[n], psRangeEnc, i, 1, condCoding); + EncodePulses.silk_encode_pulses(psRangeEnc, psEnc.state_Fxx[n].indices_LBRR[i].signalType, psEnc.state_Fxx[n].indices_LBRR[i].quantOffsetType, + psEnc.state_Fxx[n].pulses_LBRR[i], psEnc.state_Fxx[n].frame_length); + } + } + } + + /* Reset LBRR flags */ + for (n = 0; n < encControl.nChannelsInternal; n++) + { + Arrays.MemSetInt(psEnc.state_Fxx[n].LBRR_flags, 0, SilkConstants.MAX_FRAMES_PER_PACKET); + } + + psEnc.nBitsUsedLBRR = psRangeEnc.tell(); + } + + HPVariableCutoff.silk_HP_variable_cutoff(psEnc.state_Fxx); + + /* Total target bits for packet */ + nBits = Inlines.silk_DIV32_16(Inlines.silk_MUL(encControl.bitRate, encControl.payloadSize_ms), 1000); + + /* Subtract bits used for LBRR */ + if (prefillFlag == 0) + { + nBits -= psEnc.nBitsUsedLBRR; + } + + /* Divide by number of uncoded frames left in packet */ + nBits = Inlines.silk_DIV32_16(nBits, psEnc.state_Fxx[0].nFramesPerPacket); + + /* Convert to bits/second */ + if (encControl.payloadSize_ms == 10) + { + TargetRate_bps = Inlines.silk_SMULBB(nBits, 100); + } + else + { + TargetRate_bps = Inlines.silk_SMULBB(nBits, 50); + } + + /* Subtract fraction of bits in excess of target in previous frames and packets */ + TargetRate_bps -= Inlines.silk_DIV32_16(Inlines.silk_MUL(psEnc.nBitsExceeded, 1000), TuningParameters.BITRESERVOIR_DECAY_TIME_MS); + + if (prefillFlag == 0 && psEnc.state_Fxx[0].nFramesEncoded > 0) + { + /* Compare actual vs target bits so far in this packet */ + int bitsBalance = psRangeEnc.tell() - psEnc.nBitsUsedLBRR - nBits * psEnc.state_Fxx[0].nFramesEncoded; + TargetRate_bps -= Inlines.silk_DIV32_16(Inlines.silk_MUL(bitsBalance, 1000), TuningParameters.BITRESERVOIR_DECAY_TIME_MS); + } + + /* Never exceed input bitrate */ + TargetRate_bps = Inlines.silk_LIMIT(TargetRate_bps, encControl.bitRate, 5000); + + /* Convert Left/Right to Mid/Side */ + if (encControl.nChannelsInternal == 2) + { + BoxedValueSbyte midOnlyFlagBoxed = new BoxedValueSbyte(psEnc.sStereo.mid_only_flags[psEnc.state_Fxx[0].nFramesEncoded]); + Stereo.silk_stereo_LR_to_MS(psEnc.sStereo, + psEnc.state_Fxx[0].inputBuf, + 2, + psEnc.state_Fxx[1].inputBuf, + 2, + psEnc.sStereo.predIx[psEnc.state_Fxx[0].nFramesEncoded], + midOnlyFlagBoxed, + MStargetRates_bps, + TargetRate_bps, + psEnc.state_Fxx[0].speech_activity_Q8, + encControl.toMono, + psEnc.state_Fxx[0].fs_kHz, + psEnc.state_Fxx[0].frame_length); + + psEnc.sStereo.mid_only_flags[psEnc.state_Fxx[0].nFramesEncoded] = midOnlyFlagBoxed.Val; + + if (midOnlyFlagBoxed.Val == 0) + { + /* Reset side channel encoder memory for first frame with side coding */ + if (psEnc.prev_decode_only_middle == 1) + { + psEnc.state_Fxx[1].sShape.Reset(); + psEnc.state_Fxx[1].sPrefilt.Reset(); + psEnc.state_Fxx[1].sNSQ.Reset(); + Arrays.MemSetShort(psEnc.state_Fxx[1].prev_NLSFq_Q15, 0, SilkConstants.MAX_LPC_ORDER); + Arrays.MemSetInt(psEnc.state_Fxx[1].sLP.In_LP_State, 0, 2); + + psEnc.state_Fxx[1].prevLag = 100; + psEnc.state_Fxx[1].sNSQ.lagPrev = 100; + psEnc.state_Fxx[1].sShape.LastGainIndex = 10; + psEnc.state_Fxx[1].prevSignalType = SilkConstants.TYPE_NO_VOICE_ACTIVITY; + psEnc.state_Fxx[1].sNSQ.prev_gain_Q16 = 65536; + psEnc.state_Fxx[1].first_frame_after_reset = 1; + } + + psEnc.state_Fxx[1].silk_encode_do_VAD(); + } + else + { + psEnc.state_Fxx[1].VAD_flags[psEnc.state_Fxx[0].nFramesEncoded] = 0; + } + + if (prefillFlag == 0) + { + Stereo.silk_stereo_encode_pred(psRangeEnc, psEnc.sStereo.predIx[psEnc.state_Fxx[0].nFramesEncoded]); + if (psEnc.state_Fxx[1].VAD_flags[psEnc.state_Fxx[0].nFramesEncoded] == 0) + { + Stereo.silk_stereo_encode_mid_only(psRangeEnc, psEnc.sStereo.mid_only_flags[psEnc.state_Fxx[0].nFramesEncoded]); + } + } + } + else + { + /* Buffering */ + Array.Copy(psEnc.sStereo.sMid, psEnc.state_Fxx[0].inputBuf, 2); + Array.Copy(psEnc.state_Fxx[0].inputBuf, psEnc.state_Fxx[0].frame_length, psEnc.sStereo.sMid, 0, 2); + } + + psEnc.state_Fxx[0].silk_encode_do_VAD(); + + /* Encode */ + for (n = 0; n < encControl.nChannelsInternal; n++) + { + int maxBits, useCBR; + + /* Handling rate constraints */ + maxBits = encControl.maxBits; + if (tot_blocks == 2 && curr_block == 0) + { + maxBits = maxBits * 3 / 5; + } + + else if (tot_blocks == 3) + { + if (curr_block == 0) + { + maxBits = maxBits * 2 / 5; + } + else if (curr_block == 1) + { + maxBits = maxBits * 3 / 4; + } + } + + useCBR = (encControl.useCBR != 0 && curr_block == tot_blocks - 1) ? 1 : 0; + + if (encControl.nChannelsInternal == 1) + { + channelRate_bps = TargetRate_bps; + } + else + { + channelRate_bps = MStargetRates_bps[n]; + if (n == 0 && MStargetRates_bps[1] > 0) + { + useCBR = 0; + /* Give mid up to 1/2 of the max bits for that frame */ + maxBits -= encControl.maxBits / (tot_blocks * 2); + } + } + + if (channelRate_bps > 0) + { + int condCoding; + + psEnc.state_Fxx[n].silk_control_SNR(channelRate_bps); + + /* Use independent coding if no previous frame available */ + if (psEnc.state_Fxx[0].nFramesEncoded - n <= 0) + { + condCoding = SilkConstants.CODE_INDEPENDENTLY; + } + else if (n > 0 && psEnc.prev_decode_only_middle != 0) + { + /* If we skipped a side frame in this packet, we don't + need LTP scaling; the LTP state is well-defined. */ + condCoding = SilkConstants.CODE_INDEPENDENTLY_NO_LTP_SCALING; + } + else + { + condCoding = SilkConstants.CODE_CONDITIONALLY; + } + + ret += psEnc.state_Fxx[n].silk_encode_frame(nBytesOut, psRangeEnc, condCoding, maxBits, useCBR); + Inlines.OpusAssert(ret == SilkError.SILK_NO_ERROR); + } + + psEnc.state_Fxx[n].controlled_since_last_payload = 0; + psEnc.state_Fxx[n].inputBufIx = 0; + psEnc.state_Fxx[n].nFramesEncoded++; + } + + psEnc.prev_decode_only_middle = psEnc.sStereo.mid_only_flags[psEnc.state_Fxx[0].nFramesEncoded - 1]; + + /* Insert VAD and FEC flags at beginning of bitstream */ + if (nBytesOut.Val > 0 && psEnc.state_Fxx[0].nFramesEncoded == psEnc.state_Fxx[0].nFramesPerPacket) + { + flags = 0; + for (n = 0; n < encControl.nChannelsInternal; n++) + { + for (i = 0; i < psEnc.state_Fxx[n].nFramesPerPacket; i++) + { + flags = Inlines.silk_LSHIFT(flags, 1); + flags |= (int)psEnc.state_Fxx[n].VAD_flags[i]; + } + flags = Inlines.silk_LSHIFT(flags, 1); + flags |= (int)psEnc.state_Fxx[n].LBRR_flag; + } + + if (prefillFlag == 0) + { + psRangeEnc.enc_patch_initial_bits((uint)flags, (uint)((psEnc.state_Fxx[0].nFramesPerPacket + 1) * encControl.nChannelsInternal)); + } + + /* Return zero bytes if all channels DTXed */ + if (psEnc.state_Fxx[0].inDTX != 0 && (encControl.nChannelsInternal == 1 || psEnc.state_Fxx[1].inDTX != 0)) + { + nBytesOut.Val = 0; + } + + psEnc.nBitsExceeded += nBytesOut.Val * 8; + psEnc.nBitsExceeded -= Inlines.silk_DIV32_16(Inlines.silk_MUL(encControl.bitRate, encControl.payloadSize_ms), 1000); + psEnc.nBitsExceeded = Inlines.silk_LIMIT(psEnc.nBitsExceeded, 0, 10000); + + /* Update flag indicating if bandwidth switching is allowed */ + speech_act_thr_for_switch_Q8 = Inlines.silk_SMLAWB(((int)((TuningParameters.SPEECH_ACTIVITY_DTX_THRES) * ((long)1 << (8)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SPEECH_ACTIVITY_DTX_THRES, 8)*/, + ((int)(((1 - TuningParameters.SPEECH_ACTIVITY_DTX_THRES) / TuningParameters.MAX_BANDWIDTH_SWITCH_DELAY_MS) * ((long)1 << (16 + 8)) + 0.5))/*Inlines.SILK_CONST((1 - TuningParameters.SPEECH_ACTIVITY_DTX_THRES) / TuningParameters.MAX_BANDWIDTH_SWITCH_DELAY_MS, 16 + 8)*/, + psEnc.timeSinceSwitchAllowed_ms); + if (psEnc.state_Fxx[0].speech_activity_Q8 < speech_act_thr_for_switch_Q8) + { + psEnc.allowBandwidthSwitch = 1; + psEnc.timeSinceSwitchAllowed_ms = 0; + } + else + { + psEnc.allowBandwidthSwitch = 0; + psEnc.timeSinceSwitchAllowed_ms += encControl.payloadSize_ms; + } + } + + if (nSamplesIn == 0) + { + break; + } + } + else + { + break; + } + + curr_block++; + } + + psEnc.nPrevChannelsInternal = encControl.nChannelsInternal; + + encControl.allowBandwidthSwitch = psEnc.allowBandwidthSwitch; + encControl.inWBmodeWithoutVariableLP = (psEnc.state_Fxx[0].fs_kHz == 16 && psEnc.state_Fxx[0].sLP.mode == 0) ? 1 : 0; + encControl.internalSampleRate = Inlines.silk_SMULBB(psEnc.state_Fxx[0].fs_kHz, 1000); + encControl.stereoWidth_Q14 = encControl.toMono != 0 ? 0 : psEnc.sStereo.smth_width_Q14; + + if (prefillFlag != 0) + { + encControl.payloadSize_ms = tmp_payloadSize_ms; + encControl.complexity = tmp_complexity; + + for (n = 0; n < encControl.nChannelsInternal; n++) + { + psEnc.state_Fxx[n].controlled_since_last_payload = 0; + psEnc.state_Fxx[n].prefillFlag = 0; + } + } + + return ret; + } + } +} + +//────────────────────▄▄▄▄▄▄▄▄▄▄──────────────────── +//─────────────▄▄▄▄████▓▓▓▓▓▓▓▓▓██▄▄──────────────── +//─────────▄▄██▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒░▀▀▄───────────── +//────────▀▀▀▀▀█████▓▓▓▓▒▒▒▒▒▒▒▒▒▒░░░░░▀▄─────────── +//────────────▄▄█▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░▀▄───────── +//─────▄▀▀▄─▄█▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░█▄▀▀▄───── +//────█░░░▄█▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░█░░░░░█──── +//───█░░░█▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░▄▀░░░▄░░█─── +//──█░░░█▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░█░░░░░█░░█── +//──█░░█▓▒▒▄▄▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░██░░░░░░█░█── +//─█░░█▓▄▄▀█▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░▄▄▀█░░░░▄▀█░░░░░░█░░█─ +//─█░██▀░▄▀▒▒▒▒▒▒▒▒▒▒▒▒▄▄▄▄▀▀▀░▄▀░░░▄▀░░░░░░░░░█░░█─ +//─██▀░░█▒▒▒▒▒▒▒▒▒▄▄▀▀▀░░░░░░▄▀░░▄▄▀░░░░░░░░░░░░░░█─ +//──█░░█▒▒▒▒▒▒▒▄▀▀░░░░░░░░░░▀▀▀▀▀░░░░░░░░░░▄▄░░░░░█─ +//───██▒▒▒▒▒▒▄▀██▄▄░░░░░█░░░░░░░░░░░░░▄▄▄█▀░░░░░░█── +//────█▒▒▒▒▒█─▓██████▄▄█░░░█░░░░▄▄▄████▓─█▀▀░░░░█─── +//───█▒▒▒▒▄█─▓█──████▓─█░░░░████████──█▓─█▄░░░▄▀──── +//───█▒▒▄▀█──▓█▒▓████▓─█░░░░█─▓█████▓▒█▓─█░░█▀────── +//──█▒▄▀█░█──▓███─███▓─█░░░░█─▓████─██▓──█░█──────── +//──█▀──█░█──▓▓██████▓─█░░░░█─▓██████▓▓──█░█──────── +//──────█░░█──▓▓████▓─█░░░░░░█─▓████▓▓──█░░█──────── +//──────█░░▀▄───▓▓▓▓──█░░░░░░█──▓▓▓▓───▄▀░░█──────── +//───────█░░▀▄───────█░░░░░░░░█───────▄▀░░█───────── +//───────█░░░░▀▄▄▄▄▄▀░░▄▀▀▀▀▄░░▀▄▄▄▄▄▀░░░░█───────── +//────────█░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░█────────── +//─────────█░░░░░░░░░░▀▄░░░░▄▀░░░░░░░░░░█─────────── +//──────────▀▄░░░░░░░░░░░░░░░░░░░░░░░░▄▀──────────── +//────────────▀▄░░░░░░▀▄░░░░▄▀░░░░░░▄▀────────────── +//──────────────▀▄▄░░░░░▀▀▀▀░░░░░▄▄▀──────────────── +//─────────────────▀▀▄▄▄▄▄▄▄▄▄▄▀▀─────────────────── +//────────────────────────────────────────────────── +//───────█───█───█─████──███──███──█───█─████─█───── +//──────█─█──█───█─█────█────█───█─██─██─█────█───── +//─────█▄▄▄█─█─█─█─██────██──█───█─█─█─█─██───█───── +//─────█───█──█─█──█───────█─█───█─█───█─█────────── +//─────█───█──█─█──████─███───███──█───█─████─█───── +//────────────────────────────────────────────────── +//────────────────────────────────────────────────── \ No newline at end of file diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/EncodeIndices.cs b/Libraries/Concentus/CSharp/Concentus/Silk/EncodeIndices.cs new file mode 100644 index 000000000..90ae83a09 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/EncodeIndices.cs @@ -0,0 +1,223 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal static class EncodeIndices + { + /// + /// Encode side-information parameters to payload + /// + /// I/O Encoder state + /// I/O Compressor data structure + /// I Frame number + /// I Flag indicating LBRR data is being encoded + /// I The type of conditional coding to use + internal static void silk_encode_indices( + SilkChannelEncoder psEncC, + EntropyCoder psRangeEnc, + int FrameIndex, + int encode_LBRR, + int condCoding) + { + int i, k, typeOffset; + int encode_absolute_lagIndex, delta_lagIndex; + short[] ec_ix = new short[SilkConstants.MAX_LPC_ORDER]; + byte[] pred_Q8 = new byte[SilkConstants.MAX_LPC_ORDER]; + SideInfoIndices psIndices; + + if (encode_LBRR != 0) + { + psIndices = psEncC.indices_LBRR[FrameIndex]; + } + else { + psIndices = psEncC.indices; + } + + /*******************************************/ + /* Encode signal type and quantizer offset */ + /*******************************************/ + typeOffset = 2 * psIndices.signalType + psIndices.quantOffsetType; + Inlines.OpusAssert(typeOffset >= 0 && typeOffset < 6); + Inlines.OpusAssert(encode_LBRR == 0 || typeOffset >= 2); + if (encode_LBRR != 0 || typeOffset >= 2) + { + psRangeEnc.enc_icdf( typeOffset - 2, Tables.silk_type_offset_VAD_iCDF, 8); + } + else + { + psRangeEnc.enc_icdf( typeOffset, Tables.silk_type_offset_no_VAD_iCDF, 8); + } + + /****************/ + /* Encode gains */ + /****************/ + /* first subframe */ + if (condCoding == SilkConstants.CODE_CONDITIONALLY) + { + /* conditional coding */ + Inlines.OpusAssert(psIndices.GainsIndices[0] >= 0 && psIndices.GainsIndices[0] < SilkConstants.MAX_DELTA_GAIN_QUANT - SilkConstants.MIN_DELTA_GAIN_QUANT + 1); + psRangeEnc.enc_icdf( psIndices.GainsIndices[0], Tables.silk_delta_gain_iCDF, 8); + } + else + { + /* independent coding, in two stages: MSB bits followed by 3 LSBs */ + Inlines.OpusAssert(psIndices.GainsIndices[0] >= 0 && psIndices.GainsIndices[0] < SilkConstants.N_LEVELS_QGAIN); + psRangeEnc.enc_icdf( Inlines.silk_RSHIFT(psIndices.GainsIndices[0], 3), Tables.silk_gain_iCDF[psIndices.signalType], 8); + psRangeEnc.enc_icdf( psIndices.GainsIndices[0] & 7, Tables.silk_uniform8_iCDF, 8); + } + + /* remaining subframes */ + for (i = 1; i < psEncC.nb_subfr; i++) + { + Inlines.OpusAssert(psIndices.GainsIndices[i] >= 0 && psIndices.GainsIndices[i] < SilkConstants.MAX_DELTA_GAIN_QUANT - SilkConstants.MIN_DELTA_GAIN_QUANT + 1); + psRangeEnc.enc_icdf( psIndices.GainsIndices[i], Tables.silk_delta_gain_iCDF, 8); + } + + /****************/ + /* Encode NLSFs */ + /****************/ + psRangeEnc.enc_icdf( psIndices.NLSFIndices[0], psEncC.psNLSF_CB.CB1_iCDF, ((psIndices.signalType >> 1) * psEncC.psNLSF_CB.nVectors), 8); + NLSF.silk_NLSF_unpack(ec_ix, pred_Q8, psEncC.psNLSF_CB, psIndices.NLSFIndices[0]); + Inlines.OpusAssert(psEncC.psNLSF_CB.order == psEncC.predictLPCOrder); + + for (i = 0; i < psEncC.psNLSF_CB.order; i++) + { + if (psIndices.NLSFIndices[i + 1] >= SilkConstants.NLSF_QUANT_MAX_AMPLITUDE) + { + psRangeEnc.enc_icdf( 2 * SilkConstants.NLSF_QUANT_MAX_AMPLITUDE, psEncC.psNLSF_CB.ec_iCDF, (ec_ix[i]), 8); + psRangeEnc.enc_icdf( psIndices.NLSFIndices[i + 1] - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE, Tables.silk_NLSF_EXT_iCDF, 8); + } + else if (psIndices.NLSFIndices[i + 1] <= 0 - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE) + { + psRangeEnc.enc_icdf( 0, psEncC.psNLSF_CB.ec_iCDF, ec_ix[i], 8); + psRangeEnc.enc_icdf( -psIndices.NLSFIndices[i + 1] - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE, Tables.silk_NLSF_EXT_iCDF, 8); + } + else + { + psRangeEnc.enc_icdf( psIndices.NLSFIndices[i + 1] + SilkConstants.NLSF_QUANT_MAX_AMPLITUDE, psEncC.psNLSF_CB.ec_iCDF, ec_ix[i], 8); + } + } + + /* Encode NLSF interpolation factor */ + if (psEncC.nb_subfr == SilkConstants.MAX_NB_SUBFR) + { + Inlines.OpusAssert(psIndices.NLSFInterpCoef_Q2 >= 0 && psIndices.NLSFInterpCoef_Q2 < 5); + psRangeEnc.enc_icdf( psIndices.NLSFInterpCoef_Q2, Tables.silk_NLSF_interpolation_factor_iCDF, 8); + } + + if (psIndices.signalType == SilkConstants.TYPE_VOICED) + { + /*********************/ + /* Encode pitch lags */ + /*********************/ + /* lag index */ + encode_absolute_lagIndex = 1; + if (condCoding == SilkConstants.CODE_CONDITIONALLY && psEncC.ec_prevSignalType == SilkConstants.TYPE_VOICED) + { + /* Delta Encoding */ + delta_lagIndex = psIndices.lagIndex - psEncC.ec_prevLagIndex; + + if (delta_lagIndex < -8 || delta_lagIndex > 11) + { + delta_lagIndex = 0; + } + else + { + delta_lagIndex = delta_lagIndex + 9; + encode_absolute_lagIndex = 0; /* Only use delta */ + } + + Inlines.OpusAssert(delta_lagIndex >= 0 && delta_lagIndex < 21); + psRangeEnc.enc_icdf( delta_lagIndex, Tables.silk_pitch_delta_iCDF, 8); + } + + if (encode_absolute_lagIndex != 0) + { + /* Absolute encoding */ + int pitch_high_bits, pitch_low_bits; + pitch_high_bits = Inlines.silk_DIV32_16(psIndices.lagIndex, Inlines.silk_RSHIFT(psEncC.fs_kHz, 1)); + pitch_low_bits = psIndices.lagIndex - Inlines.silk_SMULBB(pitch_high_bits, Inlines.silk_RSHIFT(psEncC.fs_kHz, 1)); + Inlines.OpusAssert(pitch_low_bits < psEncC.fs_kHz / 2); + Inlines.OpusAssert(pitch_high_bits < 32); + psRangeEnc.enc_icdf( pitch_high_bits, Tables.silk_pitch_lag_iCDF, 8); + psRangeEnc.enc_icdf( pitch_low_bits, psEncC.pitch_lag_low_bits_iCDF, 8); + } + psEncC.ec_prevLagIndex = psIndices.lagIndex; + + /* Countour index */ + Inlines.OpusAssert(psIndices.contourIndex >= 0); + Inlines.OpusAssert((psIndices.contourIndex < 34 && psEncC.fs_kHz > 8 && psEncC.nb_subfr == 4) || (psIndices.contourIndex < 11 && psEncC.fs_kHz == 8 && psEncC.nb_subfr == 4) || (psIndices.contourIndex < 12 && psEncC.fs_kHz > 8 && psEncC.nb_subfr == 2) || (psIndices.contourIndex < 3 && psEncC.fs_kHz == 8 && psEncC.nb_subfr == 2)); + psRangeEnc.enc_icdf( psIndices.contourIndex, psEncC.pitch_contour_iCDF, 8); + + /********************/ + /* Encode LTP gains */ + /********************/ + /* PERIndex value */ + Inlines.OpusAssert(psIndices.PERIndex >= 0 && psIndices.PERIndex < 3); + psRangeEnc.enc_icdf( psIndices.PERIndex, Tables.silk_LTP_per_index_iCDF, 8); + + /* Codebook Indices */ + for (k = 0; k < psEncC.nb_subfr; k++) + { + Inlines.OpusAssert(psIndices.LTPIndex[k] >= 0 && psIndices.LTPIndex[k] < (8 << psIndices.PERIndex)); + psRangeEnc.enc_icdf( psIndices.LTPIndex[k], Tables.silk_LTP_gain_iCDF_ptrs[psIndices.PERIndex], 8); + } + + /**********************/ + /* Encode LTP scaling */ + /**********************/ + if (condCoding == SilkConstants.CODE_INDEPENDENTLY) + { + Inlines.OpusAssert(psIndices.LTP_scaleIndex >= 0 && psIndices.LTP_scaleIndex < 3); + psRangeEnc.enc_icdf( psIndices.LTP_scaleIndex, Tables.silk_LTPscale_iCDF, 8); + } + + Inlines.OpusAssert(condCoding == 0 || psIndices.LTP_scaleIndex == 0); + } + + psEncC.ec_prevSignalType = psIndices.signalType; + + /***************/ + /* Encode seed */ + /***************/ + Inlines.OpusAssert(psIndices.Seed >= 0 && psIndices.Seed < 4); + psRangeEnc.enc_icdf( psIndices.Seed, Tables.silk_uniform4_iCDF, 8); + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/EncodePulses.cs b/Libraries/Concentus/CSharp/Concentus/Silk/EncodePulses.cs new file mode 100644 index 000000000..266abe99e --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/EncodePulses.cs @@ -0,0 +1,278 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal static class EncodePulses + { + /// + /// + /// + /// (O) + /// (I) + /// I max value for sum of pulses + /// I number of output values + /// return ok + internal static int combine_and_check( + int[] pulses_comb, + int pulses_comb_ptr, + int[] pulses_in, + int pulses_in_ptr, + int max_pulses, + int len) + { + for (int k = 0; k < len; k++) + { + int k2p = 2 * k + pulses_in_ptr; + int sum = pulses_in[k2p] + pulses_in[k2p + 1]; + if (sum > max_pulses) + { + return 1; + } + pulses_comb[pulses_comb_ptr + k] = sum; + } + return 0; + } + + /// + /// + /// + /// (O) + /// (I) + /// I max value for sum of pulses + /// I number of output values + /// return ok + internal static int combine_and_check( + int[] pulses_comb, + int[] pulses_in, + int max_pulses, + int len) + { + for (int k = 0; k < len; k++) + { + int sum = pulses_in[2 * k] + pulses_in[2 * k + 1]; + if (sum > max_pulses) + { + return 1; + } + pulses_comb[k] = sum; + } + return 0; + } + + /// + /// Encode quantization indices of excitation + /// + /// I/O compressor data structure + /// I Signal type + /// I quantOffsetType + /// I quantization indices + /// I Frame length + internal static void silk_encode_pulses( + EntropyCoder psRangeEnc, + int signalType, + int quantOffsetType, + sbyte[] pulses, + int frame_length) + { + int i, k, j, iter, bit, nLS, scale_down, RateLevelIndex = 0; + int abs_q, minSumBits_Q5, sumBits_Q5; + int[] abs_pulses; + int[] sum_pulses; + int[] nRshifts; + int[] pulses_comb = new int[8]; + int abs_pulses_ptr; + int pulses_ptr; + byte[] nBits_ptr; + + Arrays.MemSetInt(pulses_comb, 0, 8); + + /****************************/ + /* Prepare for shell coding */ + /****************************/ + /* Calculate number of shell blocks */ + Inlines.OpusAssert(1 << SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH == SilkConstants.SHELL_CODEC_FRAME_LENGTH); + iter = Inlines.silk_RSHIFT(frame_length, SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH); + if (iter * SilkConstants.SHELL_CODEC_FRAME_LENGTH < frame_length) + { + Inlines.OpusAssert(frame_length == 12 * 10); /* Make sure only happens for 10 ms @ 12 kHz */ + iter++; + Arrays.MemSetWithOffset(pulses, 0, frame_length, SilkConstants.SHELL_CODEC_FRAME_LENGTH); + } + + /* Take the absolute value of the pulses */ + abs_pulses = new int[iter * SilkConstants.SHELL_CODEC_FRAME_LENGTH]; + Inlines.OpusAssert((SilkConstants.SHELL_CODEC_FRAME_LENGTH & 3) == 0); + + // unrolled loop + for (i = 0; i < iter * SilkConstants.SHELL_CODEC_FRAME_LENGTH; i += 4) + { + abs_pulses[i + 0] = (int)Inlines.silk_abs(pulses[i + 0]); + abs_pulses[i + 1] = (int)Inlines.silk_abs(pulses[i + 1]); + abs_pulses[i + 2] = (int)Inlines.silk_abs(pulses[i + 2]); + abs_pulses[i + 3] = (int)Inlines.silk_abs(pulses[i + 3]); + } + + /* Calc sum pulses per shell code frame */ + sum_pulses = new int[iter]; + nRshifts = new int[iter]; + abs_pulses_ptr = 0; + for (i = 0; i < iter; i++) + { + nRshifts[i] = 0; + + while (true) + { + /* 1+1 . 2 */ + scale_down = combine_and_check(pulses_comb, 0, abs_pulses, abs_pulses_ptr, Tables.silk_max_pulses_table[0], 8); + /* 2+2 . 4 */ + scale_down += combine_and_check(pulses_comb, pulses_comb, Tables.silk_max_pulses_table[1], 4); + /* 4+4 . 8 */ + scale_down += combine_and_check(pulses_comb, pulses_comb, Tables.silk_max_pulses_table[2], 2); + /* 8+8 . 16 */ + scale_down += combine_and_check(sum_pulses, i, pulses_comb, 0, Tables.silk_max_pulses_table[3], 1); + + if (scale_down != 0) + { + /* We need to downscale the quantization signal */ + nRshifts[i]++; + for (k = abs_pulses_ptr; k < abs_pulses_ptr + SilkConstants.SHELL_CODEC_FRAME_LENGTH; k++) + { + abs_pulses[k] = Inlines.silk_RSHIFT(abs_pulses[k], 1); + } + } + else + { + /* Jump out of while(1) loop and go to next shell coding frame */ + break; + } + } + + abs_pulses_ptr += SilkConstants.SHELL_CODEC_FRAME_LENGTH; + } + + /**************/ + /* Rate level */ + /**************/ + /* find rate level that leads to fewest bits for coding of pulses per block info */ + minSumBits_Q5 = int.MaxValue; + for (k = 0; k < SilkConstants.N_RATE_LEVELS - 1; k++) + { + nBits_ptr = Tables.silk_pulses_per_block_BITS_Q5[k]; + sumBits_Q5 = Tables.silk_rate_levels_BITS_Q5[signalType >> 1][k]; + for (i = 0; i < iter; i++) + { + if (nRshifts[i] > 0) + { + sumBits_Q5 += nBits_ptr[SilkConstants.SILK_MAX_PULSES + 1]; + } + else { + sumBits_Q5 += nBits_ptr[sum_pulses[i]]; + } + } + if (sumBits_Q5 < minSumBits_Q5) + { + minSumBits_Q5 = sumBits_Q5; + RateLevelIndex = k; + } + } + + psRangeEnc.enc_icdf( RateLevelIndex, Tables.silk_rate_levels_iCDF[signalType >> 1], 8); + + /***************************************************/ + /* Sum-Weighted-Pulses Encoding */ + /***************************************************/ + for (i = 0; i < iter; i++) + { + if (nRshifts[i] == 0) + { + psRangeEnc.enc_icdf( sum_pulses[i], Tables.silk_pulses_per_block_iCDF[RateLevelIndex], 8); + } + else + { + psRangeEnc.enc_icdf( SilkConstants.SILK_MAX_PULSES + 1, Tables.silk_pulses_per_block_iCDF[RateLevelIndex], 8); + for (k = 0; k < nRshifts[i] - 1; k++) + { + psRangeEnc.enc_icdf( SilkConstants.SILK_MAX_PULSES + 1, Tables.silk_pulses_per_block_iCDF[SilkConstants.N_RATE_LEVELS - 1], 8); + } + + psRangeEnc.enc_icdf( sum_pulses[i], Tables.silk_pulses_per_block_iCDF[SilkConstants.N_RATE_LEVELS - 1], 8); + } + } + + /******************/ + /* Shell Encoding */ + /******************/ + for (i = 0; i < iter; i++) + { + if (sum_pulses[i] > 0) + { + ShellCoder.silk_shell_encoder(psRangeEnc, abs_pulses, i * SilkConstants.SHELL_CODEC_FRAME_LENGTH); + } + } + + /****************/ + /* LSB Encoding */ + /****************/ + for (i = 0; i < iter; i++) + { + if (nRshifts[i] > 0) + { + pulses_ptr = i * SilkConstants.SHELL_CODEC_FRAME_LENGTH; + nLS = nRshifts[i] - 1; + for (k = 0; k < SilkConstants.SHELL_CODEC_FRAME_LENGTH; k++) + { + abs_q = (sbyte)Inlines.silk_abs(pulses[pulses_ptr + k]); + for (j = nLS; j > 0; j--) + { + bit = Inlines.silk_RSHIFT(abs_q, j) & 1; + psRangeEnc.enc_icdf( bit, Tables.silk_lsb_iCDF, 8); + } + bit = abs_q & 1; + psRangeEnc.enc_icdf( bit, Tables.silk_lsb_iCDF, 8); + } + } + } + + /****************/ + /* Encode signs */ + /****************/ + CodeSigns.silk_encode_signs(psRangeEnc, pulses, frame_length, signalType, quantOffsetType, sum_pulses); + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Enums/DecoderAPIFlag.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Enums/DecoderAPIFlag.cs new file mode 100644 index 000000000..f30825774 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Enums/DecoderAPIFlag.cs @@ -0,0 +1,41 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk.Enums +{ + internal static class DecoderAPIFlag + { + public const int FLAG_DECODE_NORMAL = 0; + public const int FLAG_PACKET_LOST = 1; + public const int FLAG_DECODE_LBRR = 2; + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Enums/SilkError.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Enums/SilkError.cs new file mode 100644 index 000000000..d2403996a --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Enums/SilkError.cs @@ -0,0 +1,91 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk.Enums +{ + /// + /// Represents error messages from a silk encoder/decoder + /// + internal static class SilkError + { + internal static int SILK_NO_ERROR = 0; + + // Encoder error messages + + /* Input length is not a multiple of 10 ms, or length is longer than the packet length */ + internal static int SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES = -101; + + /* Sampling frequency not 8000, 12000 or 16000 Hertz */ + internal static int SILK_ENC_FS_NOT_SUPPORTED = -102; + + /* Packet size not 10, 20, 40, or 60 ms */ + internal static int SILK_ENC_PACKET_SIZE_NOT_SUPPORTED = -103; + + /* Allocated payload buffer too short */ + internal static int SILK_ENC_PAYLOAD_BUF_TOO_SHORT = -104; + + /* Loss rate not between 0 and 100 percent */ + internal static int SILK_ENC_INVALID_LOSS_RATE = -105; + + /* Complexity setting not valid, use 0...10 */ + internal static int SILK_ENC_INVALID_COMPLEXITY_SETTING = -106; + + /* Inband FEC setting not valid, use 0 or 1 */ + internal static int SILK_ENC_INVALID_INBAND_FEC_SETTING = -107; + + /* DTX setting not valid, use 0 or 1 */ + internal static int SILK_ENC_INVALID_DTX_SETTING = -108; + + /* CBR setting not valid, use 0 or 1 */ + internal static int SILK_ENC_INVALID_CBR_SETTING = -109; + + /* Internal encoder error */ + internal static int SILK_ENC_INTERNAL_ERROR = -110; + + /* Internal encoder error */ + internal static int SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR = -111; + + // Decoder error messages + + /* Output sampling frequency lower than internal decoded sampling frequency */ + internal static int SILK_DEC_INVALID_SAMPLING_FREQUENCY = -200; + + /* Payload size exceeded the maximum allowed 1024 bytes */ + internal static int SILK_DEC_PAYLOAD_TOO_LARGE = -201; + + /* Payload has bit errors */ + internal static int SILK_DEC_PAYLOAD_ERROR = -202; + + /* Payload has bit errors */ + internal static int SILK_DEC_INVALID_FRAME_SIZE = -203; + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Filters.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Filters.cs new file mode 100644 index 000000000..cc92a4ab1 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Filters.cs @@ -0,0 +1,714 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Celt; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System; + using System.Diagnostics; + + internal static class Filters + { + internal static void silk_warped_LPC_analysis_filter( + int[] state, /* I/O State [order + 1] */ + int[] res_Q2, /* O Residual signal [length] */ + short[] coef_Q13, /* I Coefficients [order] */ + int coef_Q13_ptr, + short[] input, /* I Input signal [length] */ + int input_ptr, + short lambda_Q16, /* I Warping factor */ + int length, /* I Length of input signal */ + int order /* I Filter order (even) */ + ) + { + int n, i; + int acc_Q11, tmp1, tmp2; + + /* Order must be even */ + Inlines.OpusAssert((order & 1) == 0); + + for (n = 0; n < length; n++) + { + /* Output of lowpass section */ + tmp2 = Inlines.silk_SMLAWB(state[0], state[1], lambda_Q16); + state[0] = Inlines.silk_LSHIFT(input[input_ptr + n], 14); + /* Output of allpass section */ + tmp1 = Inlines.silk_SMLAWB(state[1], state[2] - tmp2, lambda_Q16); + state[1] = tmp2; + acc_Q11 = Inlines.silk_RSHIFT(order, 1); + acc_Q11 = Inlines.silk_SMLAWB(acc_Q11, tmp2, coef_Q13[coef_Q13_ptr]); + /* Loop over allpass sections */ + for (i = 2; i < order; i += 2) + { + /* Output of allpass section */ + tmp2 = Inlines.silk_SMLAWB(state[i], state[i + 1] - tmp1, lambda_Q16); + state[i] = tmp1; + acc_Q11 = Inlines.silk_SMLAWB(acc_Q11, tmp1, coef_Q13[coef_Q13_ptr + i - 1]); + /* Output of allpass section */ + tmp1 = Inlines.silk_SMLAWB(state[i + 1], state[i + 2] - tmp2, lambda_Q16); + state[i + 1] = tmp2; + acc_Q11 = Inlines.silk_SMLAWB(acc_Q11, tmp2, coef_Q13[coef_Q13_ptr + i]); + } + state[order] = tmp1; + acc_Q11 = Inlines.silk_SMLAWB(acc_Q11, tmp1, coef_Q13[coef_Q13_ptr + order - 1]); + res_Q2[n] = Inlines.silk_LSHIFT((int)input[input_ptr + n], 2) - Inlines.silk_RSHIFT_ROUND(acc_Q11, 9); + } + } + + internal static void silk_prefilter( + SilkChannelEncoder psEnc, /* I/O Encoder state */ + SilkEncoderControl psEncCtrl, /* I Encoder control */ + int[] xw_Q3, /* O Weighted signal */ + short[] x, /* I Speech signal */ + int x_ptr +) + { + SilkPrefilterState P = psEnc.sPrefilt; + int j, k, lag; + int tmp_32; + int AR1_shp_Q13; + int px; + int pxw_Q3; + int HarmShapeGain_Q12, Tilt_Q14; + int HarmShapeFIRPacked_Q12, LF_shp_Q14; + int[] x_filt_Q12; + int[] st_res_Q2; + short[] B_Q10 = new short[2]; + + /* Set up pointers */ + px = x_ptr; + pxw_Q3 = 0; + lag = P.lagPrev; + x_filt_Q12 = new int[psEnc.subfr_length]; + st_res_Q2 = new int[psEnc.subfr_length]; + for (k = 0; k < psEnc.nb_subfr; k++) + { + /* Update Variables that change per sub frame */ + if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED) + { + lag = psEncCtrl.pitchL[k]; + } + + /* Noise shape parameters */ + HarmShapeGain_Q12 = Inlines.silk_SMULWB((int)psEncCtrl.HarmShapeGain_Q14[k], 16384 - psEncCtrl.HarmBoost_Q14[k]); + Inlines.OpusAssert(HarmShapeGain_Q12 >= 0); + HarmShapeFIRPacked_Q12 = Inlines.silk_RSHIFT(HarmShapeGain_Q12, 2); + HarmShapeFIRPacked_Q12 |= Inlines.silk_LSHIFT((int)Inlines.silk_RSHIFT(HarmShapeGain_Q12, 1), 16); + Tilt_Q14 = psEncCtrl.Tilt_Q14[k]; + LF_shp_Q14 = psEncCtrl.LF_shp_Q14[k]; + AR1_shp_Q13 = k * SilkConstants.MAX_SHAPE_LPC_ORDER; + + /* Short term FIR filtering*/ + silk_warped_LPC_analysis_filter(P.sAR_shp, st_res_Q2, psEncCtrl.AR1_Q13, AR1_shp_Q13, x, px, + (short)(psEnc.warping_Q16), psEnc.subfr_length, psEnc.shapingLPCOrder); + + /* Reduce (mainly) low frequencies during harmonic emphasis */ + B_Q10[0] = (short)(Inlines.silk_RSHIFT_ROUND(psEncCtrl.GainsPre_Q14[k], 4)); + tmp_32 = Inlines.silk_SMLABB(((int)((TuningParameters.INPUT_TILT) * ((long)1 << (26)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.INPUT_TILT, 26)*/, psEncCtrl.HarmBoost_Q14[k], HarmShapeGain_Q12); /* Q26 */ + tmp_32 = Inlines.silk_SMLABB(tmp_32, psEncCtrl.coding_quality_Q14, ((int)((TuningParameters.HIGH_RATE_INPUT_TILT) * ((long)1 << (12)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HIGH_RATE_INPUT_TILT, 12)*/); /* Q26 */ + tmp_32 = Inlines.silk_SMULWB(tmp_32, -psEncCtrl.GainsPre_Q14[k]); /* Q24 */ + tmp_32 = Inlines.silk_RSHIFT_ROUND(tmp_32, 14); /* Q10 */ + B_Q10[1] = (short)(Inlines.silk_SAT16(tmp_32)); + x_filt_Q12[0] = Inlines.silk_MLA(Inlines.silk_MUL(st_res_Q2[0], B_Q10[0]), P.sHarmHP_Q2, B_Q10[1]); + for (j = 1; j < psEnc.subfr_length; j++) + { + x_filt_Q12[j] = Inlines.silk_MLA(Inlines.silk_MUL(st_res_Q2[j], B_Q10[0]), st_res_Q2[j - 1], B_Q10[1]); + } + P.sHarmHP_Q2 = st_res_Q2[psEnc.subfr_length - 1]; + + silk_prefilt(P, x_filt_Q12, xw_Q3, pxw_Q3, HarmShapeFIRPacked_Q12, Tilt_Q14, LF_shp_Q14, lag, psEnc.subfr_length); + + px += psEnc.subfr_length; + pxw_Q3 += psEnc.subfr_length; + } + + P.lagPrev = psEncCtrl.pitchL[psEnc.nb_subfr - 1]; + + } + + /* Prefilter for finding Quantizer input signal */ + static void silk_prefilt( + SilkPrefilterState P, /* I/O state */ + int[] st_res_Q12, /* I short term residual signal */ + int[] xw_Q3, /* O prefiltered signal */ + int xw_Q3_ptr, + int HarmShapeFIRPacked_Q12, /* I Harmonic shaping coeficients */ + int Tilt_Q14, /* I Tilt shaping coeficient */ + int LF_shp_Q14, /* I Low-frequancy shaping coeficients */ + int lag, /* I Lag for harmonic shaping */ + int length /* I Length of signals */ + ) + { + int i, idx, LTP_shp_buf_idx; + int n_LTP_Q12, n_Tilt_Q10, n_LF_Q10; + int sLF_MA_shp_Q12, sLF_AR_shp_Q12; + short[] LTP_shp_buf; + + /* To speed up use temp variables instead of using the struct */ + LTP_shp_buf = P.sLTP_shp; + LTP_shp_buf_idx = P.sLTP_shp_buf_idx; + sLF_AR_shp_Q12 = P.sLF_AR_shp_Q12; + sLF_MA_shp_Q12 = P.sLF_MA_shp_Q12; + + for (i = 0; i < length; i++) + { + if (lag > 0) + { + /* unrolled loop */ + Inlines.OpusAssert(SilkConstants.HARM_SHAPE_FIR_TAPS == 3); + idx = lag + LTP_shp_buf_idx; + n_LTP_Q12 = Inlines.silk_SMULBB(LTP_shp_buf[(idx - SilkConstants.HARM_SHAPE_FIR_TAPS / 2 - 1) & SilkConstants.LTP_MASK], HarmShapeFIRPacked_Q12); + n_LTP_Q12 = Inlines.silk_SMLABT(n_LTP_Q12, LTP_shp_buf[(idx - SilkConstants.HARM_SHAPE_FIR_TAPS / 2) & SilkConstants.LTP_MASK], HarmShapeFIRPacked_Q12); + n_LTP_Q12 = Inlines.silk_SMLABB(n_LTP_Q12, LTP_shp_buf[(idx - SilkConstants.HARM_SHAPE_FIR_TAPS / 2 + 1) & SilkConstants.LTP_MASK], HarmShapeFIRPacked_Q12); + } + else { + n_LTP_Q12 = 0; + } + + n_Tilt_Q10 = Inlines.silk_SMULWB(sLF_AR_shp_Q12, Tilt_Q14); + n_LF_Q10 = Inlines.silk_SMLAWB(Inlines.silk_SMULWT(sLF_AR_shp_Q12, LF_shp_Q14), sLF_MA_shp_Q12, LF_shp_Q14); + + sLF_AR_shp_Q12 = Inlines.silk_SUB32(st_res_Q12[i], Inlines.silk_LSHIFT(n_Tilt_Q10, 2)); + sLF_MA_shp_Q12 = Inlines.silk_SUB32(sLF_AR_shp_Q12, Inlines.silk_LSHIFT(n_LF_Q10, 2)); + + LTP_shp_buf_idx = (LTP_shp_buf_idx - 1) & SilkConstants.LTP_MASK; + LTP_shp_buf[LTP_shp_buf_idx] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(sLF_MA_shp_Q12, 12)); + + xw_Q3[xw_Q3_ptr + i] = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SUB32(sLF_MA_shp_Q12, n_LTP_Q12), 9); + } + + /* Copy temp variable back to state */ + P.sLF_AR_shp_Q12 = sLF_AR_shp_Q12; + P.sLF_MA_shp_Q12 = sLF_MA_shp_Q12; + P.sLTP_shp_buf_idx = LTP_shp_buf_idx; + } + +#if !UNSAFE + + /// + /// Second order ARMA filter, alternative implementation + /// + /// I input signal + /// I MA coefficients [3] + /// I AR coefficients [2] + /// I/O State vector [2] + /// O output signal + /// I signal length (must be even) + /// I Operate on interleaved signal if > 1 + internal static void silk_biquad_alt( + short[] input, + int input_ptr, + int[] B_Q28, + int[] A_Q28, + int[] S, + short[] output, + int output_ptr, + int len, + int stride) + { + /* DIRECT FORM II TRANSPOSED (uses 2 element state vector) */ + int k; + int inval, A0_U_Q28, A0_L_Q28, A1_U_Q28, A1_L_Q28, out32_Q14; + + /* Negate A_Q28 values and split in two parts */ + A0_L_Q28 = (-A_Q28[0]) & 0x00003FFF; /* lower part */ + A0_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[0], 14); /* upper part */ + A1_L_Q28 = (-A_Q28[1]) & 0x00003FFF; /* lower part */ + A1_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[1], 14); /* upper part */ + + for (k = 0; k < len; k++) + { + /* S[ 0 ], S[ 1 ]: Q12 */ + inval = input[input_ptr + k * stride]; + out32_Q14 = Inlines.silk_LSHIFT(Inlines.silk_SMLAWB(S[0], B_Q28[0], inval), 2); + + S[0] = S[1] + Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A0_L_Q28), 14); + S[0] = Inlines.silk_SMLAWB(S[0], out32_Q14, A0_U_Q28); + S[0] = Inlines.silk_SMLAWB(S[0], B_Q28[1], inval); + + S[1] = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A1_L_Q28), 14); + S[1] = Inlines.silk_SMLAWB(S[1], out32_Q14, A1_U_Q28); + S[1] = Inlines.silk_SMLAWB(S[1], B_Q28[2], inval); + + /* Scale back to Q0 and saturate */ + output[output_ptr + k * stride] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT(out32_Q14 + (1 << 14) - 1, 14)); + } + } + + internal static void silk_biquad_alt( + short[] input, + int input_ptr, + int[] B_Q28, + int[] A_Q28, + int[] S, + int S_ptr, + short[] output, + int output_ptr, + int len, + int stride) + { + /* DIRECT FORM II TRANSPOSED (uses 2 element state vector) */ + int k; + int inval, A0_U_Q28, A0_L_Q28, A1_U_Q28, A1_L_Q28, out32_Q14; + + /* Negate A_Q28 values and split in two parts */ + A0_L_Q28 = (-A_Q28[0]) & 0x00003FFF; /* lower part */ + A0_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[0], 14); /* upper part */ + A1_L_Q28 = (-A_Q28[1]) & 0x00003FFF; /* lower part */ + A1_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[1], 14); /* upper part */ + + for (k = 0; k < len; k++) + { + int s1 = S_ptr + 1; + /* S[ 0 ], S[ 1 ]: Q12 */ + inval = input[input_ptr + k * stride]; + out32_Q14 = Inlines.silk_LSHIFT(Inlines.silk_SMLAWB(S[S_ptr], B_Q28[0], inval), 2); + + S[S_ptr] = S[s1] + Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A0_L_Q28), 14); + S[S_ptr] = Inlines.silk_SMLAWB(S[S_ptr], out32_Q14, A0_U_Q28); + S[S_ptr] = Inlines.silk_SMLAWB(S[S_ptr], B_Q28[1], inval); + + S[s1] = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A1_L_Q28), 14); + S[s1] = Inlines.silk_SMLAWB(S[s1], out32_Q14, A1_U_Q28); + S[s1] = Inlines.silk_SMLAWB(S[s1], B_Q28[2], inval); + + /* Scale back to Q0 and saturate */ + output[output_ptr + k * stride] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT(out32_Q14 + (1 << 14) - 1, 14)); + } + } + +#else + /// + /// Second order ARMA filter, alternative implementation + /// + /// I input signal + /// I MA coefficients [3] + /// I AR coefficients [2] + /// I/O State vector [2] + /// O output signal + /// I signal length (must be even) + /// I Operate on interleaved signal if > 1 + internal static unsafe void silk_biquad_alt( + short[] input, + int input_ptr, + int[] B_Q28, + int[] A_Q28, + int[] S, + int S_ptr, + short[] output, + int output_ptr, + int len, + int stride) + { + /* DIRECT FORM II TRANSPOSED (uses 2 element state vector) */ + int k; + int inval, A0_U_Q28, A0_L_Q28, A1_U_Q28, A1_L_Q28, out32_Q14; + + /* Negate A_Q28 values and split in two parts */ + A0_L_Q28 = (-A_Q28[0]) & 0x00003FFF; /* lower part */ + A0_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[0], 14); /* upper part */ + A1_L_Q28 = (-A_Q28[1]) & 0x00003FFF; /* lower part */ + A1_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[1], 14); /* upper part */ + + fixed (short* pinput_base = input, poutput_base = output) + { + fixed (int* pS_base = S, pBQ28 = B_Q28) + { + int* pS = pS_base + S_ptr; + short* pinput = pinput_base + input_ptr; + short* poutput = poutput_base + output_ptr; + for (k = 0; k < len; k++) + { + /* S[ 0 ], S[ 1 ]: Q12 */ + inval = pinput[k * stride]; + out32_Q14 = Inlines.silk_LSHIFT(Inlines.silk_SMLAWB(pS[0], pBQ28[0], inval), 2); + + pS[0] = pS[1] + Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A0_L_Q28), 14); + pS[0] = Inlines.silk_SMLAWB(pS[0], out32_Q14, A0_U_Q28); + pS[0] = Inlines.silk_SMLAWB(pS[0], pBQ28[1], inval); + + pS[1] = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A1_L_Q28), 14); + pS[1] = Inlines.silk_SMLAWB(pS[1], out32_Q14, A1_U_Q28); + pS[1] = Inlines.silk_SMLAWB(pS[1], pBQ28[2], inval); + + /* Scale back to Q0 and saturate */ + poutput[k * stride] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT(out32_Q14 + (1 << 14) - 1, 14)); + } + } + } + } + + internal static unsafe void silk_biquad_alt( + short[] input, + int input_ptr, + int[] B_Q28, + int[] A_Q28, + int[] S, + short[] output, + int output_ptr, + int len, + int stride) + { + silk_biquad_alt(input, input_ptr, B_Q28, A_Q28, S, 0, output, output_ptr, len, stride); + } +#endif + + /* Coefficients for 2-band filter bank based on first-order allpass filters */ + private readonly static short A_fb1_20 = 5394 << 1; + private readonly static short A_fb1_21 = -24290; /* (opus_int16)(20623 << 1) */ + + /// + /// Split signal into two decimated bands using first-order allpass filters + /// + /// I Input signal [N] + /// I/O State vector [2] + /// O Low band [N/2] + /// O High band [N/2] + /// I Number of input samples + internal static void silk_ana_filt_bank_1( + short[] input, + int input_ptr, + int[] S, + short[] outL, + short[] outH, + int outH_ptr, + int N) + { + int k, N2 = Inlines.silk_RSHIFT(N, 1); + int in32, X, Y, out_1, out_2; + + /* Internal variables and state are in Q10 format */ + for (k = 0; k < N2; k++) + { + /* Convert to Q10 */ + in32 = Inlines.silk_LSHIFT((int)input[input_ptr + 2 * k], 10); + + /* All-pass section for even input sample */ + Y = Inlines.silk_SUB32(in32, S[0]); + X = Inlines.silk_SMLAWB(Y, Y, A_fb1_21); + out_1 = Inlines.silk_ADD32(S[0], X); + S[0] = Inlines.silk_ADD32(in32, X); + + /* Convert to Q10 */ + in32 = Inlines.silk_LSHIFT((int)input[input_ptr + 2 * k + 1], 10); + + /* All-pass section for odd input sample, and add to output of previous section */ + Y = Inlines.silk_SUB32(in32, S[1]); + X = Inlines.silk_SMULWB(Y, A_fb1_20); + out_2 = Inlines.silk_ADD32(S[1], X); + S[1] = Inlines.silk_ADD32(in32, X); + + /* Add/subtract, convert back to int16 and store to output */ + outL[k] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_ADD32(out_2, out_1), 11)); + outH[outH_ptr + k] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SUB32(out_2, out_1), 11)); + } + } + + /// + /// Chirp (bandwidth expand) LP AR filter + /// + /// I/O AR filter to be expanded (without leading 1) + /// I Length of ar + /// I Chirp factor in Q16 + internal static void silk_bwexpander_32(int[] ar, int d, int chirp_Q16) + { + int i; + int chirp_minus_one_Q16 = chirp_Q16 - 65536; + + for (i = 0; i < d - 1; i++) + { + ar[i] = Inlines.silk_SMULWW(chirp_Q16, ar[i]); + chirp_Q16 += Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, chirp_minus_one_Q16), 16); + } + + ar[d - 1] = Inlines.silk_SMULWW(chirp_Q16, ar[d - 1]); + } + + /// + /// Elliptic/Cauer filters designed with 0.1 dB passband ripple, + /// 80 dB minimum stopband attenuation, and + /// [0.95 : 0.15 : 0.35] normalized cut off frequencies. + /// Helper function, interpolates the filter taps + /// + /// order [TRANSITION_NB] + /// order [TRANSITION_NA] + /// + /// + internal static void silk_LP_interpolate_filter_taps( + int[] B_Q28, + int[] A_Q28, + int ind, + int fac_Q16) + { + int nb, na; + + if (ind < SilkConstants.TRANSITION_INT_NUM - 1) + { + if (fac_Q16 > 0) + { + if (fac_Q16 < 32768) + { + /* fac_Q16 is in range of a 16-bit int */ + /* Piece-wise linear interpolation of B and A */ + for (nb = 0; nb < SilkConstants.TRANSITION_NB; nb++) + { + B_Q28[nb] = Inlines.silk_SMLAWB( + Tables.silk_Transition_LP_B_Q28[ind][nb], + Tables.silk_Transition_LP_B_Q28[ind + 1][nb] - + Tables.silk_Transition_LP_B_Q28[ind][nb], + fac_Q16); + } + + for (na = 0; na < SilkConstants.TRANSITION_NA; na++) + { + A_Q28[na] = Inlines.silk_SMLAWB( + Tables.silk_Transition_LP_A_Q28[ind][na], + Tables.silk_Transition_LP_A_Q28[ind + 1][na] - + Tables.silk_Transition_LP_A_Q28[ind][na], + fac_Q16); + } + } + else + { + /* ( fac_Q16 - ( 1 << 16 ) ) is in range of a 16-bit int */ + Inlines.OpusAssert(fac_Q16 - (1 << 16) == Inlines.silk_SAT16(fac_Q16 - (1 << 16))); + + /* Piece-wise linear interpolation of B and A */ + + for (nb = 0; nb < SilkConstants.TRANSITION_NB; nb++) + { + B_Q28[nb] = Inlines.silk_SMLAWB( + Tables.silk_Transition_LP_B_Q28[ind + 1][nb], + Tables.silk_Transition_LP_B_Q28[ind + 1][nb] - + Tables.silk_Transition_LP_B_Q28[ind][nb], + fac_Q16 - ((int)1 << 16)); + } + + for (na = 0; na < SilkConstants.TRANSITION_NA; na++) + { + A_Q28[na] = Inlines.silk_SMLAWB( + Tables.silk_Transition_LP_A_Q28[ind + 1][na], + Tables.silk_Transition_LP_A_Q28[ind + 1][na] - + Tables.silk_Transition_LP_A_Q28[ind][na], + fac_Q16 - ((int)1 << 16)); + } + } + } + else + { + Array.Copy(Tables.silk_Transition_LP_B_Q28[ind], 0, B_Q28, 0, SilkConstants.TRANSITION_NB); + Array.Copy(Tables.silk_Transition_LP_A_Q28[ind], 0, A_Q28, 0, SilkConstants.TRANSITION_NA); + } + } + else + { + Array.Copy(Tables.silk_Transition_LP_B_Q28[SilkConstants.TRANSITION_INT_NUM - 1], 0, B_Q28, 0, SilkConstants.TRANSITION_NB); + Array.Copy(Tables.silk_Transition_LP_A_Q28[SilkConstants.TRANSITION_INT_NUM - 1], 0, A_Q28, 0, SilkConstants.TRANSITION_NA); + } + } + + /// + /// LPC analysis filter + /// NB! State is kept internally and the + /// filter always starts with zero state + /// first d output samples are set to zero + /// + /// O Output signal + /// I Input signal + /// I MA prediction coefficients, Q12 [order] + /// I Signal length + /// I Filter order + internal static void silk_LPC_analysis_filter( + short[] output, + int output_ptr, + short[] input, + int input_ptr, + short[] B, + int B_ptr, + int len, + int d) + { + int j; + + short[] mem = new short[SilkConstants.SILK_MAX_ORDER_LPC]; + short[] num = new short[SilkConstants.SILK_MAX_ORDER_LPC]; + + Inlines.OpusAssert(d >= 6); + Inlines.OpusAssert((d & 1) == 0); + Inlines.OpusAssert(d <= len); + + Inlines.OpusAssert(d <= SilkConstants.SILK_MAX_ORDER_LPC); + for (j = 0; j < d; j++) + { + num[j] = (short)(0 - B[B_ptr + j]); + } + for (j = 0; j < d; j++) + { + mem[j] = input[input_ptr + d - j - 1]; + } +#if UNSAFE + unsafe + { + fixed (short* pinput_base = input, poutput_base = output) + { + short* pinput = pinput_base + input_ptr + d; + short* poutput = poutput_base + output_ptr + d; + Kernels.celt_fir(pinput, num, poutput, len - d, d, mem); + } + } +#else + Kernels.celt_fir(input, input_ptr + d, num, output, output_ptr + d, len - d, d, mem); +#endif + for (j = output_ptr; j < output_ptr + d; j++) + { + output[j] = 0; + } + } + + private const int QA = 24; + private static readonly int A_LIMIT = ((int)((0.99975f) * ((long)1 << (QA)) + 0.5))/*Inlines.SILK_CONST(0.99975f, QA)*/; + + /// + /// Compute inverse of LPC prediction gain, and + /// test if LPC coefficients are stable (all poles within unit circle) + /// + /// Prediction coefficients, order [2][SILK_MAX_ORDER_LPC] + /// Prediction order + /// inverse prediction gain in energy domain, Q30 + internal static int LPC_inverse_pred_gain_QA( + int[][] A_QA, + int order) + { + int k, n, mult2Q; + int invGain_Q30, rc_Q31, rc_mult1_Q30, rc_mult2, tmp_QA; + int[] Aold_QA, Anew_QA; + + Anew_QA = A_QA[order & 1]; + + invGain_Q30 = (int)1 << 30; + for (k = order - 1; k > 0; k--) + { + /* Check for stability */ + if ((Anew_QA[k] > A_LIMIT) || (Anew_QA[k] < -A_LIMIT)) + { + return 0; + } + + /* Set RC equal to negated AR coef */ + rc_Q31 = 0 - Inlines.silk_LSHIFT(Anew_QA[k], 31 - QA); + + /* rc_mult1_Q30 range: [ 1 : 2^30 ] */ + rc_mult1_Q30 = ((int)1 << 30) - Inlines.silk_SMMUL(rc_Q31, rc_Q31); + Inlines.OpusAssert(rc_mult1_Q30 > (1 << 15)); /* reduce A_LIMIT if fails */ + Inlines.OpusAssert(rc_mult1_Q30 <= (1 << 30)); + + /* rc_mult2 range: [ 2^30 : silk_int32_MAX ] */ + mult2Q = 32 - Inlines.silk_CLZ32(Inlines.silk_abs(rc_mult1_Q30)); + rc_mult2 = Inlines.silk_INVERSE32_varQ(rc_mult1_Q30, mult2Q + 30); + + /* Update inverse gain */ + /* invGain_Q30 range: [ 0 : 2^30 ] */ + invGain_Q30 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, rc_mult1_Q30), 2); + Inlines.OpusAssert(invGain_Q30 >= 0); + Inlines.OpusAssert(invGain_Q30 <= (1 << 30)); + + /* Swap pointers */ + Aold_QA = Anew_QA; + Anew_QA = A_QA[k & 1]; + + /* Update AR coefficient */ + for (n = 0; n < k; n++) + { + tmp_QA = Aold_QA[n] - Inlines.MUL32_FRAC_Q(Aold_QA[k - n - 1], rc_Q31, 31); + Anew_QA[n] = Inlines.MUL32_FRAC_Q(tmp_QA, rc_mult2, mult2Q); + } + } + + /* Check for stability */ + if ((Anew_QA[0] > A_LIMIT) || (Anew_QA[0] < -A_LIMIT)) + { + return 0; + } + + /* Set RC equal to negated AR coef */ + rc_Q31 = 0 - Inlines.silk_LSHIFT(Anew_QA[0], 31 - QA); + + /* Range: [ 1 : 2^30 ] */ + rc_mult1_Q30 = ((int)1 << 30) - Inlines.silk_SMMUL(rc_Q31, rc_Q31); + + /* Update inverse gain */ + /* Range: [ 0 : 2^30 ] */ + invGain_Q30 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, rc_mult1_Q30), 2); + Inlines.OpusAssert(invGain_Q30 >= 0); + Inlines.OpusAssert(invGain_Q30 <= 1 << 30); + + return invGain_Q30; + } + + /// + /// For input in Q12 domain + /// + /// Prediction coefficients, Q12 [order] + /// I Prediction order + /// inverse prediction gain in energy domain, Q30 + internal static int silk_LPC_inverse_pred_gain(short[] A_Q12, int order) + { + int k; + int[][] Atmp_QA = new int[2][]; + Atmp_QA[0] = new int[order]; + Atmp_QA[1] = new int[order]; + int[] Anew_QA; + int DC_resp = 0; + + Anew_QA = Atmp_QA[order & 1]; + + /* Increase Q domain of the AR coefficients */ + for (k = 0; k < order; k++) + { + DC_resp += (int)A_Q12[k]; + Anew_QA[k] = Inlines.silk_LSHIFT32((int)A_Q12[k], QA - 12); + } + + /* If the DC is unstable, we don't even need to do the full calculations */ + if (DC_resp >= 4096) + { + return 0; + } + + return LPC_inverse_pred_gain_QA(Atmp_QA, order); + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/FindLPC.cs b/Libraries/Concentus/CSharp/Concentus/Silk/FindLPC.cs new file mode 100644 index 000000000..74839d368 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/FindLPC.cs @@ -0,0 +1,184 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal static class FindLPC + { + /* Finds LPC vector from correlations, and converts to NLSF */ + internal static void silk_find_LPC( + SilkChannelEncoder psEncC, /* I/O Encoder state */ + short[] NLSF_Q15, /* O NLSFs */ + short[] x, /* I Input signal */ + int minInvGain_Q30 /* I Inverse of max prediction gain */ + ) + { + int k, subfr_length; + int[] a_Q16 = new int[SilkConstants.MAX_LPC_ORDER]; + int isInterpLower, shift; + int res_nrg0, res_nrg1; + int rshift0, rshift1; + BoxedValueInt scratch_box1 = new BoxedValueInt(); + BoxedValueInt scratch_box2 = new BoxedValueInt(); + + /* Used only for LSF interpolation */ + int[] a_tmp_Q16 = new int[SilkConstants.MAX_LPC_ORDER]; + int res_nrg_interp, res_nrg, res_tmp_nrg; + int res_nrg_interp_Q, res_nrg_Q, res_tmp_nrg_Q; + short[] a_tmp_Q12 = new short[SilkConstants.MAX_LPC_ORDER]; + short[] NLSF0_Q15 = new short[SilkConstants.MAX_LPC_ORDER]; + + subfr_length = psEncC.subfr_length + psEncC.predictLPCOrder; + + /* Default: no interpolation */ + psEncC.indices.NLSFInterpCoef_Q2 = 4; + + /* Burg AR analysis for the full frame */ + BurgModified.silk_burg_modified(scratch_box1, scratch_box2, a_Q16, x, 0, minInvGain_Q30, subfr_length, psEncC.nb_subfr, psEncC.predictLPCOrder); + res_nrg = scratch_box1.Val; + res_nrg_Q = scratch_box2.Val; + + if (psEncC.useInterpolatedNLSFs != 0 && psEncC.first_frame_after_reset == 0 && psEncC.nb_subfr == SilkConstants.MAX_NB_SUBFR) + { + short[] LPC_res; + + /* Optimal solution for last 10 ms */ + BurgModified.silk_burg_modified(scratch_box1, scratch_box2, a_tmp_Q16, x, (2 * subfr_length), minInvGain_Q30, subfr_length, 2, psEncC.predictLPCOrder); + res_tmp_nrg = scratch_box1.Val; + res_tmp_nrg_Q = scratch_box2.Val; + + /* subtract residual energy here, as that's easier than adding it to the */ + /* residual energy of the first 10 ms in each iteration of the search below */ + shift = res_tmp_nrg_Q - res_nrg_Q; + if (shift >= 0) + { + if (shift < 32) + { + res_nrg = res_nrg - Inlines.silk_RSHIFT(res_tmp_nrg, shift); + } + } + else { + Inlines.OpusAssert(shift > -32); + res_nrg = Inlines.silk_RSHIFT(res_nrg, -shift) - res_tmp_nrg; + res_nrg_Q = res_tmp_nrg_Q; + } + + /* Convert to NLSFs */ + NLSF.silk_A2NLSF(NLSF_Q15, a_tmp_Q16, psEncC.predictLPCOrder); + + LPC_res = new short[2 * subfr_length]; + + /* Search over interpolation indices to find the one with lowest residual energy */ + for (k = 3; k >= 0; k--) + { + /* Interpolate NLSFs for first half */ + Inlines.silk_interpolate(NLSF0_Q15, psEncC.prev_NLSFq_Q15, NLSF_Q15, k, psEncC.predictLPCOrder); + + /* Convert to LPC for residual energy evaluation */ + NLSF.silk_NLSF2A(a_tmp_Q12, NLSF0_Q15, psEncC.predictLPCOrder); + + /* Calculate residual energy with NLSF interpolation */ + Filters.silk_LPC_analysis_filter(LPC_res, 0, x, 0, a_tmp_Q12, 0, 2 * subfr_length, psEncC.predictLPCOrder); + + SumSqrShift.silk_sum_sqr_shift(out res_nrg0, out rshift0, LPC_res, psEncC.predictLPCOrder, subfr_length - psEncC.predictLPCOrder); + + SumSqrShift.silk_sum_sqr_shift(out res_nrg1, out rshift1, LPC_res, psEncC.predictLPCOrder + subfr_length, subfr_length - psEncC.predictLPCOrder); + + /* Add subframe energies from first half frame */ + shift = rshift0 - rshift1; + if (shift >= 0) + { + res_nrg1 = Inlines.silk_RSHIFT(res_nrg1, shift); + res_nrg_interp_Q = -rshift0; + } + else { + res_nrg0 = Inlines.silk_RSHIFT(res_nrg0, -shift); + res_nrg_interp_Q = -rshift1; + } + res_nrg_interp = Inlines.silk_ADD32(res_nrg0, res_nrg1); + + /* Compare with first half energy without NLSF interpolation, or best interpolated value so far */ + shift = res_nrg_interp_Q - res_nrg_Q; + if (shift >= 0) + { + if (Inlines.silk_RSHIFT(res_nrg_interp, shift) < res_nrg) + { + isInterpLower = (true ? 1 : 0); + } + else { + isInterpLower = (false ? 1 : 0); + } + } + else { + if (-shift < 32) + { + if (res_nrg_interp < Inlines.silk_RSHIFT(res_nrg, -shift)) + { + isInterpLower = (true ? 1 : 0); + } + else { + isInterpLower = (false ? 1 : 0); + } + } + else { + isInterpLower = (false ? 1 : 0); + } + } + + /* Determine whether current interpolated NLSFs are best so far */ + if (isInterpLower == (true ? 1 : 0)) + { + /* Interpolation has lower residual energy */ + res_nrg = res_nrg_interp; + res_nrg_Q = res_nrg_interp_Q; + psEncC.indices.NLSFInterpCoef_Q2 = (sbyte)k; + } + } + } + + if (psEncC.indices.NLSFInterpCoef_Q2 == 4) + { + /* NLSF interpolation is currently inactive, calculate NLSFs from full frame AR coefficients */ + NLSF.silk_A2NLSF(NLSF_Q15, a_Q16, psEncC.predictLPCOrder); + } + + Inlines.OpusAssert(psEncC.indices.NLSFInterpCoef_Q2 == 4 || (psEncC.useInterpolatedNLSFs != 0 && psEncC.first_frame_after_reset == 0 && psEncC.nb_subfr == SilkConstants.MAX_NB_SUBFR)); + + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/FindLTP.cs b/Libraries/Concentus/CSharp/Concentus/Silk/FindLTP.cs new file mode 100644 index 000000000..fee993b14 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/FindLTP.cs @@ -0,0 +1,295 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal static class FindLTP + { + /* Head room for correlations */ + private const int LTP_CORRS_HEAD_ROOM = 2; + + /// + /// Finds linear prediction coeffecients and weights + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal static void silk_find_LTP( + short[] b_Q14, /* O LTP coefs [SilkConstants.MAX_NB_SUBFR * SilkConstants.LTP_ORDER] */ + int[] WLTP, /* O Weight for LTP quantization [SilkConstants.MAX_NB_SUBFR * SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER] */ + BoxedValueInt LTPredCodGain_Q7, /* O LTP coding gain */ + short[] r_lpc, /* I residual signal after LPC signal + state for first 10 ms */ + int[] lag, /* I LTP lags [SilkConstants.MAX_NB_SUBFR] */ + int[] Wght_Q15, /* I weights [SilkConstants.MAX_NB_SUBFR] */ + int subfr_length, /* I subframe length */ + int nb_subfr, /* I number of subframes */ + int mem_offset, /* I number of samples in LTP memory */ + int[] corr_rshifts /* O right shifts applied to correlations [SilkConstants.MAX_NB_SUBFR] */ + ) + { + int i, k, lshift; + int r_ptr; + int lag_ptr; + int b_Q14_ptr; + + int regu; + int WLTP_ptr; + int[] b_Q16 = new int[SilkConstants.LTP_ORDER]; + int[] delta_b_Q14 = new int[SilkConstants.LTP_ORDER]; + int[] d_Q14 = new int[SilkConstants.MAX_NB_SUBFR]; + int[] nrg = new int[SilkConstants.MAX_NB_SUBFR]; + int g_Q26; + int[] w = new int[SilkConstants.MAX_NB_SUBFR]; + int WLTP_max, max_abs_d_Q14, max_w_bits; + + int temp32, denom32; + int extra_shifts; + int rr_shifts, maxRshifts, maxRshifts_wxtra, LZs; + int LPC_res_nrg, LPC_LTP_res_nrg, div_Q16; + int[] Rr = new int[SilkConstants.LTP_ORDER]; + int[] rr = new int[SilkConstants.MAX_NB_SUBFR]; + int wd, m_Q12; + + b_Q14_ptr = 0; + WLTP_ptr = 0; + r_ptr = mem_offset; + for (k = 0; k < nb_subfr; k++) + { + lag_ptr = r_ptr - (lag[k] + SilkConstants.LTP_ORDER / 2); + + SumSqrShift.silk_sum_sqr_shift(out rr[k], out rr_shifts, r_lpc, r_ptr, subfr_length); /* rr[ k ] in Q( -rr_shifts ) */ + + /* Assure headroom */ + LZs = Inlines.silk_CLZ32(rr[k]); + if (LZs < LTP_CORRS_HEAD_ROOM) + { + rr[k] = Inlines.silk_RSHIFT_ROUND(rr[k], LTP_CORRS_HEAD_ROOM - LZs); + rr_shifts += (LTP_CORRS_HEAD_ROOM - LZs); + } + corr_rshifts[k] = rr_shifts; + BoxedValueInt boxed_shifts = new BoxedValueInt(corr_rshifts[k]); + CorrelateMatrix.silk_corrMatrix(r_lpc, lag_ptr, subfr_length, SilkConstants.LTP_ORDER, LTP_CORRS_HEAD_ROOM, WLTP, WLTP_ptr, boxed_shifts); /* WLTP_ptr in Q( -corr_rshifts[ k ] ) */ + corr_rshifts[k] = boxed_shifts.Val; + + /* The correlation vector always has lower max abs value than rr and/or RR so head room is assured */ + CorrelateMatrix.silk_corrVector(r_lpc, lag_ptr, r_lpc, r_ptr, subfr_length, SilkConstants.LTP_ORDER, Rr, corr_rshifts[k]); /* Rr_ptr in Q( -corr_rshifts[ k ] ) */ + if (corr_rshifts[k] > rr_shifts) + { + rr[k] = Inlines.silk_RSHIFT(rr[k], corr_rshifts[k] - rr_shifts); /* rr[ k ] in Q( -corr_rshifts[ k ] ) */ + } + Inlines.OpusAssert(rr[k] >= 0); + + regu = 1; + regu = Inlines.silk_SMLAWB(regu, rr[k], ((int)((TuningParameters.LTP_DAMPING / 3) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LTP_DAMPING / 3, 16)*/); + regu = Inlines.silk_SMLAWB(regu, Inlines.MatrixGet(WLTP, WLTP_ptr, 0, 0, SilkConstants.LTP_ORDER), ((int)((TuningParameters.LTP_DAMPING / 3) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LTP_DAMPING / 3, 16)*/); + regu = Inlines.silk_SMLAWB(regu, Inlines.MatrixGet(WLTP, WLTP_ptr, SilkConstants.LTP_ORDER - 1, SilkConstants.LTP_ORDER - 1, SilkConstants.LTP_ORDER), ((int)((TuningParameters.LTP_DAMPING / 3) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LTP_DAMPING / 3, 16)*/); + RegularizeCorrelations.silk_regularize_correlations(WLTP, WLTP_ptr, rr, k, regu, SilkConstants.LTP_ORDER); + + LinearAlgebra.silk_solve_LDL(WLTP, WLTP_ptr, SilkConstants.LTP_ORDER, Rr, b_Q16); /* WLTP_ptr and Rr_ptr both in Q(-corr_rshifts[k]) */ + + /* Limit and store in Q14 */ + silk_fit_LTP(b_Q16, b_Q14, b_Q14_ptr); + + /* Calculate residual energy */ + nrg[k] = ResidualEnergy.silk_residual_energy16_covar(b_Q14, b_Q14_ptr, WLTP, WLTP_ptr, Rr, rr[k], SilkConstants.LTP_ORDER, 14); /* nrg in Q( -corr_rshifts[ k ] ) */ + + /* temp = Wght[ k ] / ( nrg[ k ] * Wght[ k ] + 0.01f * subfr_length ); */ + extra_shifts = Inlines.silk_min_int(corr_rshifts[k], LTP_CORRS_HEAD_ROOM); + denom32 = Inlines.silk_LSHIFT_SAT32(Inlines.silk_SMULWB(nrg[k], Wght_Q15[k]), 1 + extra_shifts) + /* Q( -corr_rshifts[ k ] + extra_shifts ) */ + Inlines.silk_RSHIFT(Inlines.silk_SMULWB((int)subfr_length, 655), corr_rshifts[k] - extra_shifts); /* Q( -corr_rshifts[ k ] + extra_shifts ) */ + denom32 = Inlines.silk_max(denom32, 1); + Inlines.OpusAssert(((long)Wght_Q15[k] << 16) < int.MaxValue); /* Wght always < 0.5 in Q0 */ + temp32 = Inlines.silk_DIV32(Inlines.silk_LSHIFT((int)Wght_Q15[k], 16), denom32); /* Q( 15 + 16 + corr_rshifts[k] - extra_shifts ) */ + temp32 = Inlines.silk_RSHIFT(temp32, 31 + corr_rshifts[k] - extra_shifts - 26); /* Q26 */ + + /* Limit temp such that the below scaling never wraps around */ + WLTP_max = 0; + for (i = WLTP_ptr; i < WLTP_ptr + (SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER); i++) + { + WLTP_max = Inlines.silk_max(WLTP[i], WLTP_max); + } + lshift = Inlines.silk_CLZ32(WLTP_max) - 1 - 3; /* keep 3 bits free for vq_nearest_neighbor */ + Inlines.OpusAssert(26 - 18 + lshift >= 0); + if (26 - 18 + lshift < 31) + { + temp32 = Inlines.silk_min_32(temp32, Inlines.silk_LSHIFT((int)1, 26 - 18 + lshift)); + } + + Inlines.silk_scale_vector32_Q26_lshift_18(WLTP, WLTP_ptr, temp32, SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER); /* WLTP_ptr in Q( 18 - corr_rshifts[ k ] ) */ + + w[k] = Inlines.MatrixGet(WLTP, WLTP_ptr, SilkConstants.LTP_ORDER / 2, SilkConstants.LTP_ORDER / 2, SilkConstants.LTP_ORDER); /* w in Q( 18 - corr_rshifts[ k ] ) */ + Inlines.OpusAssert(w[k] >= 0); + + r_ptr += subfr_length; + b_Q14_ptr += SilkConstants.LTP_ORDER; + WLTP_ptr += (SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER); + } + + maxRshifts = 0; + for (k = 0; k < nb_subfr; k++) + { + maxRshifts = Inlines.silk_max_int(corr_rshifts[k], maxRshifts); + } + + /* Compute LTP coding gain */ + if (LTPredCodGain_Q7 != null) + { + LPC_LTP_res_nrg = 0; + LPC_res_nrg = 0; + Inlines.OpusAssert(LTP_CORRS_HEAD_ROOM >= 2); /* Check that no overflow will happen when adding */ + for (k = 0; k < nb_subfr; k++) + { + LPC_res_nrg = Inlines.silk_ADD32(LPC_res_nrg, Inlines.silk_RSHIFT(Inlines.silk_ADD32(Inlines.silk_SMULWB(rr[k], Wght_Q15[k]), 1), 1 + (maxRshifts - corr_rshifts[k]))); /* Q( -maxRshifts ) */ + LPC_LTP_res_nrg = Inlines.silk_ADD32(LPC_LTP_res_nrg, Inlines.silk_RSHIFT(Inlines.silk_ADD32(Inlines.silk_SMULWB(nrg[k], Wght_Q15[k]), 1), 1 + (maxRshifts - corr_rshifts[k]))); /* Q( -maxRshifts ) */ + } + LPC_LTP_res_nrg = Inlines.silk_max(LPC_LTP_res_nrg, 1); /* avoid division by zero */ + + div_Q16 = Inlines.silk_DIV32_varQ(LPC_res_nrg, LPC_LTP_res_nrg, 16); + LTPredCodGain_Q7.Val = (int)Inlines.silk_SMULBB(3, Inlines.silk_lin2log(div_Q16) - (16 << 7)); + + Inlines.OpusAssert(LTPredCodGain_Q7.Val == (int)Inlines.silk_SAT16(Inlines.silk_MUL(3, Inlines.silk_lin2log(div_Q16) - (16 << 7)))); + } + + /* smoothing */ + /* d = sum( B, 1 ); */ + b_Q14_ptr = 0; + for (k = 0; k < nb_subfr; k++) + { + d_Q14[k] = 0; + for (i = b_Q14_ptr; i < b_Q14_ptr + SilkConstants.LTP_ORDER; i++) + { + d_Q14[k] += b_Q14[i]; + } + b_Q14_ptr += SilkConstants.LTP_ORDER; + } + + /* m = ( w * d' ) / ( sum( w ) + 1e-3 ); */ + + /* Find maximum absolute value of d_Q14 and the bits used by w in Q0 */ + max_abs_d_Q14 = 0; + max_w_bits = 0; + for (k = 0; k < nb_subfr; k++) + { + max_abs_d_Q14 = Inlines.silk_max_32(max_abs_d_Q14, Inlines.silk_abs(d_Q14[k])); + /* w[ k ] is in Q( 18 - corr_rshifts[ k ] ) */ + /* Find bits needed in Q( 18 - maxRshifts ) */ + max_w_bits = Inlines.silk_max_32(max_w_bits, 32 - Inlines.silk_CLZ32(w[k]) + corr_rshifts[k] - maxRshifts); + } + + /* max_abs_d_Q14 = (5 << 15); worst case, i.e. SilkConstants.LTP_ORDER * -silk_int16_MIN */ + Inlines.OpusAssert(max_abs_d_Q14 <= (5 << 15)); + + /* How many bits is needed for w*d' in Q( 18 - maxRshifts ) in the worst case, of all d_Q14's being equal to max_abs_d_Q14 */ + extra_shifts = max_w_bits + 32 - Inlines.silk_CLZ32(max_abs_d_Q14) - 14; + + /* Subtract what we got available; bits in output var plus maxRshifts */ + extra_shifts -= (32 - 1 - 2 + maxRshifts); /* Keep sign bit free as well as 2 bits for accumulation */ + extra_shifts = Inlines.silk_max_int(extra_shifts, 0); + + maxRshifts_wxtra = maxRshifts + extra_shifts; + + temp32 = Inlines.silk_RSHIFT(262, maxRshifts + extra_shifts) + 1; /* 1e-3f in Q( 18 - (maxRshifts + extra_shifts) ) */ + wd = 0; + for (k = 0; k < nb_subfr; k++) + { + /* w has at least 2 bits of headroom so no overflow should happen */ + temp32 = Inlines.silk_ADD32(temp32, Inlines.silk_RSHIFT(w[k], maxRshifts_wxtra - corr_rshifts[k])); /* Q( 18 - maxRshifts_wxtra ) */ + wd = Inlines.silk_ADD32(wd, Inlines.silk_LSHIFT(Inlines.silk_SMULWW(Inlines.silk_RSHIFT(w[k], maxRshifts_wxtra - corr_rshifts[k]), d_Q14[k]), 2)); /* Q( 18 - maxRshifts_wxtra ) */ + } + m_Q12 = Inlines.silk_DIV32_varQ(wd, temp32, 12); + + b_Q14_ptr = 0; + for (k = 0; k < nb_subfr; k++) + { + /* w[ k ] from Q( 18 - corr_rshifts[ k ] ) to Q( 16 ) */ + if (2 - corr_rshifts[k] > 0) + { + temp32 = Inlines.silk_RSHIFT(w[k], 2 - corr_rshifts[k]); + } + else { + temp32 = Inlines.silk_LSHIFT_SAT32(w[k], corr_rshifts[k] - 2); + } + + g_Q26 = Inlines.silk_MUL( + Inlines.silk_DIV32( + ((int)((TuningParameters.LTP_SMOOTHING) * ((long)1 << (26)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LTP_SMOOTHING, 26)*/, + Inlines.silk_RSHIFT(((int)((TuningParameters.LTP_SMOOTHING) * ((long)1 << (26)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LTP_SMOOTHING, 26)*/, 10) + temp32), /* Q10 */ + Inlines.silk_LSHIFT_SAT32(Inlines.silk_SUB_SAT32((int)m_Q12, Inlines.silk_RSHIFT(d_Q14[k], 2)), 4)); /* Q16 */ + + temp32 = 0; + for (i = 0; i < SilkConstants.LTP_ORDER; i++) + { + delta_b_Q14[i] = Inlines.silk_max_16(b_Q14[b_Q14_ptr + i], 1638); /* 1638_Q14 = 0.1_Q0 */ + temp32 += delta_b_Q14[i]; /* Q14 */ + } + temp32 = Inlines.silk_DIV32(g_Q26, temp32); /* Q14 . Q12 */ + for (i = 0; i < SilkConstants.LTP_ORDER; i++) + { + b_Q14[b_Q14_ptr + i] = (short)(Inlines.silk_LIMIT_32((int)b_Q14[b_Q14_ptr + i] + Inlines.silk_SMULWB(Inlines.silk_LSHIFT_SAT32(temp32, 4), delta_b_Q14[i]), -16000, 28000)); + } + b_Q14_ptr += SilkConstants.LTP_ORDER; + } + } + + /// + /// + /// + /// [SilkConstants.LTP_ORDER] + /// [SilkConstants.LTP_ORDER] + /// + internal static void silk_fit_LTP( + int[] LTP_coefs_Q16, + short[] LTP_coefs_Q14, + int LTP_coefs_Q14_ptr) + { + int i; + + for (i = 0; i < SilkConstants.LTP_ORDER; i++) + { + LTP_coefs_Q14[LTP_coefs_Q14_ptr + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(LTP_coefs_Q16[i], 2)); + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/FindPitchLags.cs b/Libraries/Concentus/CSharp/Concentus/Silk/FindPitchLags.cs new file mode 100644 index 000000000..3101129db --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/FindPitchLags.cs @@ -0,0 +1,167 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System; + using System.Diagnostics; + + internal static class FindPitchLags + { + /* Find pitch lags */ + internal static void silk_find_pitch_lags( + SilkChannelEncoder psEnc, /* I/O encoder state */ + SilkEncoderControl psEncCtrl, /* I/O encoder control */ + short[] res, /* O residual */ + short[] x, /* I Speech signal */ + int x_ptr + ) + { + int buf_len, i, scale; + int thrhld_Q13, res_nrg; + int x_buf, x_buf_ptr; + short[] Wsig; + int Wsig_ptr; + int[] auto_corr = new int[SilkConstants.MAX_FIND_PITCH_LPC_ORDER + 1]; + short[] rc_Q15 = new short[SilkConstants.MAX_FIND_PITCH_LPC_ORDER]; + int[] A_Q24 = new int[SilkConstants.MAX_FIND_PITCH_LPC_ORDER]; + short[] A_Q12 = new short[SilkConstants.MAX_FIND_PITCH_LPC_ORDER]; + + + /******************************************/ + /* Set up buffer lengths etc based on Fs */ + /******************************************/ + buf_len = psEnc.la_pitch + psEnc.frame_length + psEnc.ltp_mem_length; + + /* Safety check */ + Inlines.OpusAssert(buf_len >= psEnc.pitch_LPC_win_length); + + x_buf = x_ptr - psEnc.ltp_mem_length; + + /*************************************/ + /* Estimate LPC AR coefficients */ + /*************************************/ + + /* Calculate windowed signal */ + + Wsig = new short[psEnc.pitch_LPC_win_length]; + + /* First LA_LTP samples */ + x_buf_ptr = x_buf + buf_len - psEnc.pitch_LPC_win_length; + Wsig_ptr = 0; + ApplySineWindow.silk_apply_sine_window(Wsig, Wsig_ptr, x, x_buf_ptr, 1, psEnc.la_pitch); + + /* Middle un - windowed samples */ + Wsig_ptr += psEnc.la_pitch; + x_buf_ptr += psEnc.la_pitch; + Array.Copy(x, x_buf_ptr, Wsig, Wsig_ptr, (psEnc.pitch_LPC_win_length - Inlines.silk_LSHIFT(psEnc.la_pitch, 1))); + + /* Last LA_LTP samples */ + Wsig_ptr += psEnc.pitch_LPC_win_length - Inlines.silk_LSHIFT(psEnc.la_pitch, 1); + x_buf_ptr += psEnc.pitch_LPC_win_length - Inlines.silk_LSHIFT(psEnc.la_pitch, 1); + ApplySineWindow.silk_apply_sine_window(Wsig, Wsig_ptr, x, x_buf_ptr, 2, psEnc.la_pitch); + + /* Calculate autocorrelation sequence */ + BoxedValueInt boxed_scale = new BoxedValueInt(); + Autocorrelation.silk_autocorr(auto_corr, boxed_scale, Wsig, psEnc.pitch_LPC_win_length, psEnc.pitchEstimationLPCOrder + 1); + scale = boxed_scale.Val; + + /* Add white noise, as fraction of energy */ + auto_corr[0] = Inlines.silk_SMLAWB(auto_corr[0], auto_corr[0], ((int)((TuningParameters.FIND_PITCH_WHITE_NOISE_FRACTION) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_PITCH_WHITE_NOISE_FRACTION, 16)*/) + 1; + + /* Calculate the reflection coefficients using schur */ + res_nrg = Schur.silk_schur(rc_Q15, auto_corr, psEnc.pitchEstimationLPCOrder); + + /* Prediction gain */ + psEncCtrl.predGain_Q16 = Inlines.silk_DIV32_varQ(auto_corr[0], Inlines.silk_max_int(res_nrg, 1), 16); + + /* Convert reflection coefficients to prediction coefficients */ + K2A.silk_k2a(A_Q24, rc_Q15, psEnc.pitchEstimationLPCOrder); + + /* Convert From 32 bit Q24 to 16 bit Q12 coefs */ + for (i = 0; i < psEnc.pitchEstimationLPCOrder; i++) + { + A_Q12[i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT(A_Q24[i], 12)); + } + + /* Do BWE */ + BWExpander.silk_bwexpander(A_Q12, psEnc.pitchEstimationLPCOrder, ((int)((TuningParameters.FIND_PITCH_BANDWIDTH_EXPANSION) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_PITCH_BANDWIDTH_EXPANSION, 16)*/); + + /*****************************************/ + /* LPC analysis filtering */ + /*****************************************/ + Filters.silk_LPC_analysis_filter(res, 0, x, x_buf, A_Q12, 0, buf_len, psEnc.pitchEstimationLPCOrder); + + if (psEnc.indices.signalType != SilkConstants.TYPE_NO_VOICE_ACTIVITY && psEnc.first_frame_after_reset == 0) + { + /* Threshold for pitch estimator */ + thrhld_Q13 = ((int)((0.6f) * ((long)1 << (13)) + 0.5))/*Inlines.SILK_CONST(0.6f, 13)*/; + thrhld_Q13 = Inlines.silk_SMLABB(thrhld_Q13, ((int)((-0.004f) * ((long)1 << (13)) + 0.5))/*Inlines.SILK_CONST(-0.004f, 13)*/, psEnc.pitchEstimationLPCOrder); + thrhld_Q13 = Inlines.silk_SMLAWB(thrhld_Q13, ((int)((-0.1f) * ((long)1 << (21)) + 0.5))/*Inlines.SILK_CONST(-0.1f, 21)*/, psEnc.speech_activity_Q8); + thrhld_Q13 = Inlines.silk_SMLABB(thrhld_Q13, ((int)((-0.15f) * ((long)1 << (13)) + 0.5))/*Inlines.SILK_CONST(-0.15f, 13)*/, Inlines.silk_RSHIFT(psEnc.prevSignalType, 1)); + thrhld_Q13 = Inlines.silk_SMLAWB(thrhld_Q13, ((int)((-0.1f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(-0.1f, 14)*/, psEnc.input_tilt_Q15); + thrhld_Q13 = Inlines.silk_SAT16(thrhld_Q13); + + /*****************************************/ + /* Call pitch estimator */ + /*****************************************/ + BoxedValueShort boxed_lagIndex = new BoxedValueShort(psEnc.indices.lagIndex); + BoxedValueSbyte boxed_contourIndex = new BoxedValueSbyte(psEnc.indices.contourIndex); + BoxedValueInt boxed_LTPcorr = new BoxedValueInt(psEnc.LTPCorr_Q15); + if (PitchAnalysisCore.silk_pitch_analysis_core(res, psEncCtrl.pitchL, boxed_lagIndex, boxed_contourIndex, + boxed_LTPcorr, psEnc.prevLag, psEnc.pitchEstimationThreshold_Q16, + (int)thrhld_Q13, psEnc.fs_kHz, psEnc.pitchEstimationComplexity, psEnc.nb_subfr) == 0) + { + psEnc.indices.signalType = SilkConstants.TYPE_VOICED; + } + else { + psEnc.indices.signalType = SilkConstants.TYPE_UNVOICED; + } + + psEnc.indices.lagIndex = boxed_lagIndex.Val; + psEnc.indices.contourIndex = boxed_contourIndex.Val; + psEnc.LTPCorr_Q15 = boxed_LTPcorr.Val; + } + else { + Arrays.MemSetInt(psEncCtrl.pitchL, 0, SilkConstants.MAX_NB_SUBFR); + psEnc.indices.lagIndex = 0; + psEnc.indices.contourIndex = 0; + psEnc.LTPCorr_Q15 = 0; + } + + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/FindPredCoefs.cs b/Libraries/Concentus/CSharp/Concentus/Silk/FindPredCoefs.cs new file mode 100644 index 000000000..87bb503e4 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/FindPredCoefs.cs @@ -0,0 +1,171 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System; + using System.Diagnostics; + + internal static class FindPredCoefs + { + internal static void silk_find_pred_coefs( + SilkChannelEncoder psEnc, /* I/O encoder state */ + SilkEncoderControl psEncCtrl, /* I/O encoder control */ + short[] res_pitch, /* I Residual from pitch analysis */ + short[] x, /* I Speech signal */ + int x_ptr, + int condCoding /* I The type of conditional coding to use */ + ) + { + int i; + int[] invGains_Q16 = new int[SilkConstants.MAX_NB_SUBFR]; + int[] local_gains = new int[SilkConstants.MAX_NB_SUBFR]; + int[] Wght_Q15 = new int[SilkConstants.MAX_NB_SUBFR]; + short[] NLSF_Q15 = new short[SilkConstants.MAX_LPC_ORDER]; + int x_ptr2; + int x_pre_ptr; + short[] LPC_in_pre; + int tmp, min_gain_Q16, minInvGain_Q30; + int[] LTP_corrs_rshift = new int[SilkConstants.MAX_NB_SUBFR]; + + /* weighting for weighted least squares */ + min_gain_Q16 = int.MaxValue >> 6; + for (i = 0; i < psEnc.nb_subfr; i++) + { + min_gain_Q16 = Inlines.silk_min(min_gain_Q16, psEncCtrl.Gains_Q16[i]); + } + for (i = 0; i < psEnc.nb_subfr; i++) + { + /* Divide to Q16 */ + Inlines.OpusAssert(psEncCtrl.Gains_Q16[i] > 0); + /* Invert and normalize gains, and ensure that maximum invGains_Q16 is within range of a 16 bit int */ + invGains_Q16[i] = Inlines.silk_DIV32_varQ(min_gain_Q16, psEncCtrl.Gains_Q16[i], 16 - 2); + + /* Ensure Wght_Q15 a minimum value 1 */ + invGains_Q16[i] = Inlines.silk_max(invGains_Q16[i], 363); + + /* Square the inverted gains */ + Inlines.OpusAssert(invGains_Q16[i] == Inlines.silk_SAT16(invGains_Q16[i])); + tmp = Inlines.silk_SMULWB(invGains_Q16[i], invGains_Q16[i]); + Wght_Q15[i] = Inlines.silk_RSHIFT(tmp, 1); + + /* Invert the inverted and normalized gains */ + local_gains[i] = Inlines.silk_DIV32(((int)1 << 16), invGains_Q16[i]); + } + + LPC_in_pre = new short[psEnc.nb_subfr * psEnc.predictLPCOrder + psEnc.frame_length]; + if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED) + { + int[] WLTP; + + /**********/ + /* VOICED */ + /**********/ + Inlines.OpusAssert(psEnc.ltp_mem_length - psEnc.predictLPCOrder >= psEncCtrl.pitchL[0] + SilkConstants.LTP_ORDER / 2); + + WLTP = new int[psEnc.nb_subfr * SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER]; + + /* LTP analysis */ + BoxedValueInt boxed_codgain = new BoxedValueInt(psEncCtrl.LTPredCodGain_Q7); + FindLTP.silk_find_LTP(psEncCtrl.LTPCoef_Q14, WLTP, boxed_codgain, + res_pitch, psEncCtrl.pitchL, Wght_Q15, psEnc.subfr_length, + psEnc.nb_subfr, psEnc.ltp_mem_length, LTP_corrs_rshift); + psEncCtrl.LTPredCodGain_Q7 = boxed_codgain.Val; + + /* Quantize LTP gain parameters */ + BoxedValueSbyte boxed_periodicity = new BoxedValueSbyte(psEnc.indices.PERIndex); + BoxedValueInt boxed_gain = new BoxedValueInt(psEnc.sum_log_gain_Q7); + QuantizeLTPGains.silk_quant_LTP_gains(psEncCtrl.LTPCoef_Q14, psEnc.indices.LTPIndex, boxed_periodicity, + boxed_gain, WLTP, psEnc.mu_LTP_Q9, psEnc.LTPQuantLowComplexity, psEnc.nb_subfr + ); + psEnc.indices.PERIndex = boxed_periodicity.Val; + psEnc.sum_log_gain_Q7 = boxed_gain.Val; + + /* Control LTP scaling */ + LTPScaleControl.silk_LTP_scale_ctrl(psEnc, psEncCtrl, condCoding); + + /* Create LTP residual */ + LTPAnalysisFilter.silk_LTP_analysis_filter(LPC_in_pre, x, x_ptr - psEnc.predictLPCOrder, psEncCtrl.LTPCoef_Q14, + psEncCtrl.pitchL, invGains_Q16, psEnc.subfr_length, psEnc.nb_subfr, psEnc.predictLPCOrder); + + } + else { + /************/ + /* UNVOICED */ + /************/ + /* Create signal with prepended subframes, scaled by inverse gains */ + x_ptr2 = x_ptr - psEnc.predictLPCOrder; + x_pre_ptr = 0; + for (i = 0; i < psEnc.nb_subfr; i++) + { + Inlines.silk_scale_copy_vector16(LPC_in_pre, x_pre_ptr, x, x_ptr2, invGains_Q16[i], + psEnc.subfr_length + psEnc.predictLPCOrder); + x_pre_ptr += psEnc.subfr_length + psEnc.predictLPCOrder; + x_ptr2 += psEnc.subfr_length; + } + + Arrays.MemSetShort(psEncCtrl.LTPCoef_Q14, 0, psEnc.nb_subfr * SilkConstants.LTP_ORDER); + psEncCtrl.LTPredCodGain_Q7 = 0; + psEnc.sum_log_gain_Q7 = 0; + } + + /* Limit on total predictive coding gain */ + if (psEnc.first_frame_after_reset != 0) + { + minInvGain_Q30 = ((int)((1.0f / SilkConstants.MAX_PREDICTION_POWER_GAIN_AFTER_RESET) * ((long)1 << (30)) + 0.5))/*Inlines.SILK_CONST(1.0f / SilkConstants.MAX_PREDICTION_POWER_GAIN_AFTER_RESET, 30)*/; + } + else { + minInvGain_Q30 = Inlines.silk_log2lin(Inlines.silk_SMLAWB(16 << 7, (int)psEncCtrl.LTPredCodGain_Q7, ((int)((1.0f / 3f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f / 3f, 16)*/)); /* Q16 */ + minInvGain_Q30 = Inlines.silk_DIV32_varQ(minInvGain_Q30, + Inlines.silk_SMULWW(((int)((SilkConstants.MAX_PREDICTION_POWER_GAIN) * ((long)1 << (0)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.MAX_PREDICTION_POWER_GAIN, 0)*/, + Inlines.silk_SMLAWB(((int)((0.25f) * ((long)1 << (18)) + 0.5))/*Inlines.SILK_CONST(0.25f, 18)*/, ((int)((0.75f) * ((long)1 << (18)) + 0.5))/*Inlines.SILK_CONST(0.75f, 18)*/, psEncCtrl.coding_quality_Q14)), 14); + } + + /* LPC_in_pre contains the LTP-filtered input for voiced, and the unfiltered input for unvoiced */ + FindLPC.silk_find_LPC(psEnc, NLSF_Q15, LPC_in_pre, minInvGain_Q30); + + /* Quantize LSFs */ + NLSF.silk_process_NLSFs(psEnc, psEncCtrl.PredCoef_Q12, NLSF_Q15, psEnc.prev_NLSFq_Q15); + + /* Calculate residual energy using quantized LPC coefficients */ + ResidualEnergy.silk_residual_energy(psEncCtrl.ResNrg, psEncCtrl.ResNrgQ, LPC_in_pre, psEncCtrl.PredCoef_Q12, local_gains, + psEnc.subfr_length, psEnc.nb_subfr, psEnc.predictLPCOrder); + + /* Copy to prediction struct for use in next frame for interpolation */ + Array.Copy(NLSF_Q15, psEnc.prev_NLSFq_Q15, SilkConstants.MAX_LPC_ORDER); + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/GainQuantization.cs b/Libraries/Concentus/CSharp/Concentus/Silk/GainQuantization.cs new file mode 100644 index 000000000..7f892d59e --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/GainQuantization.cs @@ -0,0 +1,187 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal static class GainQuantization + { + private static readonly int OFFSET = ((SilkConstants.MIN_QGAIN_DB * 128) / 6 + 16 * 128); + private static readonly int SCALE_Q16 = ((65536 * (SilkConstants.N_LEVELS_QGAIN - 1)) / (((SilkConstants.MAX_QGAIN_DB - SilkConstants.MIN_QGAIN_DB) * 128) / 6)); + private static readonly int INV_SCALE_Q16 = ((65536 * (((SilkConstants.MAX_QGAIN_DB - SilkConstants.MIN_QGAIN_DB) * 128) / 6)) / (SilkConstants.N_LEVELS_QGAIN - 1)); + + /// + /// Gain scalar quantization with hysteresis, uniform on log scale + /// + /// O gain indices [MAX_NB_SUBFR] + /// I/O gains (quantized out) [MAX_NB_SUBFR] + /// I/O last index in previous frame. [Porting note] original implementation passed this as an int8* + /// I first gain is delta coded if 1 + /// I number of subframes + internal static void silk_gains_quant( + sbyte[] ind, + int[] gain_Q16, + BoxedValueSbyte prev_ind, + int conditional, + int nb_subfr) + { + int k, double_step_size_threshold; + + for (k = 0; k < nb_subfr; k++) + { + // Debug.WriteLine("2a 0x{0:x}", (uint)gain_Q16[k]); + /* Convert to log scale, scale, floor() */ + ind[k] = (sbyte)(Inlines.silk_SMULWB(SCALE_Q16, Inlines.silk_lin2log(gain_Q16[k]) - OFFSET)); + + /* Round towards previous quantized gain (hysteresis) */ + if (ind[k] < prev_ind.Val) + { + ind[k]++; + } + + ind[k] = (sbyte)(Inlines.silk_LIMIT_int(ind[k], 0, SilkConstants.N_LEVELS_QGAIN - 1)); + + /* Compute delta indices and limit */ + if (k == 0 && conditional == 0) + { + /* Full index */ + ind[k] = (sbyte)(Inlines.silk_LIMIT_int(ind[k], prev_ind.Val + SilkConstants.MIN_DELTA_GAIN_QUANT, SilkConstants.N_LEVELS_QGAIN - 1)); + prev_ind.Val = ind[k]; + } + else + { + /* Delta index */ + ind[k] = (sbyte)(ind[k] - prev_ind.Val); + + /* Double the quantization step size for large gain increases, so that the max gain level can be reached */ + double_step_size_threshold = 2 * SilkConstants.MAX_DELTA_GAIN_QUANT - SilkConstants.N_LEVELS_QGAIN + prev_ind.Val; + if (ind[k] > double_step_size_threshold) + { + ind[k] = (sbyte)(double_step_size_threshold + Inlines.silk_RSHIFT(ind[k] - double_step_size_threshold + 1, 1)); + } + + ind[k] = (sbyte)(Inlines.silk_LIMIT_int(ind[k], SilkConstants.MIN_DELTA_GAIN_QUANT, SilkConstants.MAX_DELTA_GAIN_QUANT)); + + /* Accumulate deltas */ + if (ind[k] > double_step_size_threshold) + { + prev_ind.Val += (sbyte)(Inlines.silk_LSHIFT(ind[k], 1) - double_step_size_threshold); + } + else + { + prev_ind.Val += ind[k]; + } + + /* Shift to make non-negative */ + ind[k] -= SilkConstants.MIN_DELTA_GAIN_QUANT; + // Debug.WriteLine("2b 0x{0:x}", (uint)ind[k]); + } + + /* Scale and convert to linear scale */ + gain_Q16[k] = Inlines.silk_log2lin(Inlines.silk_min_32(Inlines.silk_SMULWB(INV_SCALE_Q16, prev_ind.Val) + OFFSET, 3967)); /* 3967 = 31 in Q7 */ + } + } + + /// + /// Gains scalar dequantization, uniform on log scale + /// + /// O quantized gains [MAX_NB_SUBFR] + /// I gain indices [MAX_NB_SUBFR] + /// I/O last index in previous frame [Porting note] original implementation passed this as an int8* + /// I first gain is delta coded if 1 + /// I number of subframes + internal static void silk_gains_dequant( + int[] gain_Q16, + sbyte[] ind, + BoxedValueSbyte prev_ind, + int conditional, + int nb_subfr) + { + int k, ind_tmp, double_step_size_threshold; + + for (k = 0; k < nb_subfr; k++) + { + if (k == 0 && conditional == 0) + { + /* Gain index is not allowed to go down more than 16 steps (~21.8 dB) */ + prev_ind.Val = (sbyte)(Inlines.silk_max_int(ind[k], prev_ind.Val - 16)); + } + else + { + /* Delta index */ + ind_tmp = ind[k] + SilkConstants.MIN_DELTA_GAIN_QUANT; + + /* Accumulate deltas */ + double_step_size_threshold = 2 * SilkConstants.MAX_DELTA_GAIN_QUANT - SilkConstants.N_LEVELS_QGAIN + prev_ind.Val; + if (ind_tmp > double_step_size_threshold) + { + prev_ind.Val += (sbyte)(Inlines.silk_LSHIFT(ind_tmp, 1) - double_step_size_threshold); + } + else + { + prev_ind.Val += (sbyte)(ind_tmp); + } + } + + prev_ind.Val = (sbyte)(Inlines.silk_LIMIT_int(prev_ind.Val, 0, SilkConstants.N_LEVELS_QGAIN - 1)); + + /* Scale and convert to linear scale */ + gain_Q16[k] = Inlines.silk_log2lin(Inlines.silk_min_32(Inlines.silk_SMULWB(INV_SCALE_Q16, prev_ind.Val) + OFFSET, 3967)); /* 3967 = 31 in Q7 */ + } + } + + /// + /// Compute unique identifier of gain indices vector + /// + /// I gain indices [MAX_NB_SUBFR] + /// I number of subframes + /// unique identifier of gains + internal static int silk_gains_ID(sbyte[] ind, int nb_subfr) + { + int k; + int gainsID; + + gainsID = 0; + for (k = 0; k < nb_subfr; k++) + { + gainsID = Inlines.silk_ADD_LSHIFT32(ind[k], gainsID, 8); + } + + return gainsID; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/HPVariableCutoff.cs b/Libraries/Concentus/CSharp/Concentus/Silk/HPVariableCutoff.cs new file mode 100644 index 000000000..06dc4af73 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/HPVariableCutoff.cs @@ -0,0 +1,90 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal static class HPVariableCutoff + { + /// + /// High-pass filter with cutoff frequency adaptation based on pitch lag statistics + /// + /// I/O Encoder states + internal static void silk_HP_variable_cutoff(SilkChannelEncoder[] state_Fxx) + { + int quality_Q15; + int pitch_freq_Hz_Q16, pitch_freq_log_Q7, delta_freq_Q7; + SilkChannelEncoder psEncC1 = state_Fxx[0]; + + /* Adaptive cutoff frequency: estimate low end of pitch frequency range */ + if (psEncC1.prevSignalType == SilkConstants.TYPE_VOICED) + { + /* difference, in log domain */ + pitch_freq_Hz_Q16 = Inlines.silk_DIV32_16(Inlines.silk_LSHIFT(Inlines.silk_MUL(psEncC1.fs_kHz, 1000), 16), psEncC1.prevLag); + pitch_freq_log_Q7 = Inlines.silk_lin2log(pitch_freq_Hz_Q16) - (16 << 7); + + /* adjustment based on quality */ + quality_Q15 = psEncC1.input_quality_bands_Q15[0]; + pitch_freq_log_Q7 = Inlines.silk_SMLAWB(pitch_freq_log_Q7, Inlines.silk_SMULWB(Inlines.silk_LSHIFT(-quality_Q15, 2), quality_Q15), + pitch_freq_log_Q7 - (Inlines.silk_lin2log(((int)((TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ, 16)*/) - (16 << 7))); + + /* delta_freq = pitch_freq_log - psEnc.variable_HP_smth1; */ + delta_freq_Q7 = pitch_freq_log_Q7 - Inlines.silk_RSHIFT(psEncC1.variable_HP_smth1_Q15, 8); + if (delta_freq_Q7 < 0) + { + /* less smoothing for decreasing pitch frequency, to track something close to the minimum */ + delta_freq_Q7 = Inlines.silk_MUL(delta_freq_Q7, 3); + } + + /* limit delta, to reduce impact of outliers in pitch estimation */ + delta_freq_Q7 = Inlines.silk_LIMIT_32( + delta_freq_Q7, + 0 - ((int)((TuningParameters.VARIABLE_HP_MAX_DELTA_FREQ) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.VARIABLE_HP_MAX_DELTA_FREQ, 7)*/, + ((int)((TuningParameters.VARIABLE_HP_MAX_DELTA_FREQ) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.VARIABLE_HP_MAX_DELTA_FREQ, 7)*/); + + /* update smoother */ + psEncC1.variable_HP_smth1_Q15 = Inlines.silk_SMLAWB(psEncC1.variable_HP_smth1_Q15, + Inlines.silk_SMULBB(psEncC1.speech_activity_Q8, delta_freq_Q7), ((int)((TuningParameters.VARIABLE_HP_SMTH_COEF1) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.VARIABLE_HP_SMTH_COEF1, 16)*/); + + /* limit frequency range */ + psEncC1.variable_HP_smth1_Q15 = Inlines.silk_LIMIT_32(psEncC1.variable_HP_smth1_Q15, + Inlines.silk_LSHIFT(Inlines.silk_lin2log(TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ), 8), + Inlines.silk_LSHIFT(Inlines.silk_lin2log(TuningParameters.VARIABLE_HP_MAX_CUTOFF_HZ), 8)); + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/K2A.cs b/Libraries/Concentus/CSharp/Concentus/Silk/K2A.cs new file mode 100644 index 000000000..9377986eb --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/K2A.cs @@ -0,0 +1,92 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal static class K2A + { + /* Step up function, converts reflection coefficients to prediction coefficients */ + internal static void silk_k2a( + int[] A_Q24, /* O Prediction coefficients [order] Q24 */ + short[] rc_Q15, /* I Reflection coefficients [order] Q15 */ + int order /* I Prediction order */ + ) + { + int k, n; + int[] Atmp = new int[SilkConstants.SILK_MAX_ORDER_LPC]; + + for (k = 0; k < order; k++) + { + for (n = 0; n < k; n++) + { + Atmp[n] = A_Q24[n]; + } + for (n = 0; n < k; n++) + { + A_Q24[n] = Inlines.silk_SMLAWB(A_Q24[n], Inlines.silk_LSHIFT(Atmp[k - n - 1], 1), rc_Q15[k]); + } + A_Q24[k] = 0 - Inlines.silk_LSHIFT((int)rc_Q15[k], 9); + } + } + + /* Step up function, converts reflection coefficients to prediction coefficients */ + internal static void silk_k2a_Q16( + int[] A_Q24, /* O Prediction coefficients [order] Q24 */ + int[] rc_Q16, /* I Reflection coefficients [order] Q16 */ + int order /* I Prediction order */ + ) + { + int k, n; + int[] Atmp = new int[SilkConstants.SILK_MAX_ORDER_LPC]; + + for (k = 0; k < order; k++) + { + for (n = 0; n < k; n++) + { + Atmp[n] = A_Q24[n]; + } + for (n = 0; n < k; n++) + { + A_Q24[n] = Inlines.silk_SMLAWW(A_Q24[n], Atmp[k - n - 1], rc_Q16[k]); + } + A_Q24[k] = 0 - Inlines.silk_LSHIFT(rc_Q16[k], 8); + } + } + + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/LPCInversePredGain.cs b/Libraries/Concentus/CSharp/Concentus/Silk/LPCInversePredGain.cs new file mode 100644 index 000000000..cc7f50557 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/LPCInversePredGain.cs @@ -0,0 +1,169 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal static class LPCInversePredGain + { + private const float RC_THRESHOLD = 0.9999f; + + private const int QA = 24; + private static readonly int A_LIMIT = ((int)((0.99975f) * ((long)1 << (QA)) + 0.5))/*Inlines.SILK_CONST(0.99975f, QA)*/; + + /* Compute inverse of LPC prediction gain, and */ + /* test if LPC coefficients are stable (all poles within unit circle) */ + internal static int LPC_inverse_pred_gain_QA( /* O Returns inverse prediction gain in energy domain, Q30 */ + int[][] A_QA, /* I Prediction coefficients [ 2 ][SILK_MAX_ORDER_LPC] */ + int order /* I Prediction order */ +) + { + int k, n, mult2Q; + int invGain_Q30, rc_Q31, rc_mult1_Q30, rc_mult2, tmp_QA; + int[] Aold_QA; + int[] Anew_QA; + + Anew_QA = A_QA[order & 1]; + + invGain_Q30 = (int)1 << 30; + for (k = order - 1; k > 0; k--) + { + /* Check for stability */ + if ((Anew_QA[k] > A_LIMIT) || (Anew_QA[k] < -A_LIMIT)) + { + return 0; + } + + /* Set RC equal to negated AR coef */ + rc_Q31 = 0 - Inlines.silk_LSHIFT(Anew_QA[k], 31 - QA); + + /* rc_mult1_Q30 range: [ 1 : 2^30 ] */ + rc_mult1_Q30 = ((int)1 << 30) - Inlines.silk_SMMUL(rc_Q31, rc_Q31); + Inlines.OpusAssert(rc_mult1_Q30 > (1 << 15)); /* reduce A_LIMIT if fails */ + Inlines.OpusAssert(rc_mult1_Q30 <= (1 << 30)); + + /* rc_mult2 range: [ 2^30 : silk_int32_MAX ] */ + mult2Q = 32 - Inlines.silk_CLZ32(Inlines.silk_abs(rc_mult1_Q30)); + rc_mult2 = Inlines.silk_INVERSE32_varQ(rc_mult1_Q30, mult2Q + 30); + + /* Update inverse gain */ + /* invGain_Q30 range: [ 0 : 2^30 ] */ + invGain_Q30 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, rc_mult1_Q30), 2); + Inlines.OpusAssert(invGain_Q30 >= 0); + Inlines.OpusAssert(invGain_Q30 <= (1 << 30)); + + /* Swap pointers */ + Aold_QA = Anew_QA; + Anew_QA = A_QA[k & 1]; + + /* Update AR coefficient */ + for (n = 0; n < k; n++) + { + tmp_QA = Aold_QA[n] - Inlines.MUL32_FRAC_Q(Aold_QA[k - n - 1], rc_Q31, 31); + Anew_QA[n] = Inlines.MUL32_FRAC_Q(tmp_QA, rc_mult2, mult2Q); + } + } + + /* Check for stability */ + if ((Anew_QA[0] > A_LIMIT) || (Anew_QA[0] < -A_LIMIT)) + { + return 0; + } + + /* Set RC equal to negated AR coef */ + rc_Q31 = 0 - Inlines.silk_LSHIFT(Anew_QA[0], 31 - QA); + + /* Range: [ 1 : 2^30 ] */ + rc_mult1_Q30 = ((int)1 << 30) - Inlines.silk_SMMUL(rc_Q31, rc_Q31); + + /* Update inverse gain */ + /* Range: [ 0 : 2^30 ] */ + invGain_Q30 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, rc_mult1_Q30), 2); + Inlines.OpusAssert(invGain_Q30 >= 0); + Inlines.OpusAssert(invGain_Q30 <= 1 << 30); + + return invGain_Q30; + } + + /* For input in Q12 domain */ + internal static int silk_LPC_inverse_pred_gain( /* O Returns inverse prediction gain in energy domain, Q30 */ + short[] A_Q12, /* I Prediction coefficients, Q12 [order] */ + int order /* I Prediction order */ + ) + { + int k; + int[][] Atmp_QA = Arrays.InitTwoDimensionalArray(2, SilkConstants.SILK_MAX_ORDER_LPC); + int[] Anew_QA; + int DC_resp = 0; + + Anew_QA = Atmp_QA[order & 1]; + + /* Increase Q domain of the AR coefficients */ + for (k = 0; k < order; k++) + { + DC_resp += (int)A_Q12[k]; + Anew_QA[k] = Inlines.silk_LSHIFT32((int)A_Q12[k], QA - 12); + } + /* If the DC is unstable, we don't even need to do the full calculations */ + if (DC_resp >= 4096) + { + return 0; + } + return LPC_inverse_pred_gain_QA(Atmp_QA, order); + } + + internal static int silk_LPC_inverse_pred_gain_Q24( /* O Returns inverse prediction gain in energy domain, Q30 */ + int[] A_Q24, /* I Prediction coefficients [order] */ + int order /* I Prediction order */ + ) + { + int k; + int[][] Atmp_QA = Arrays.InitTwoDimensionalArray(2, SilkConstants.SILK_MAX_ORDER_LPC); + int[] Anew_QA; + + Anew_QA = Atmp_QA[order & 1]; + + /* Increase Q domain of the AR coefficients */ + for (k = 0; k < order; k++) + { + Anew_QA[k] = Inlines.silk_RSHIFT32(A_Q24[k], 24 - QA); + } + + return LPC_inverse_pred_gain_QA(Atmp_QA, order); + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/LTPAnalysisFilter.cs b/Libraries/Concentus/CSharp/Concentus/Silk/LTPAnalysisFilter.cs new file mode 100644 index 000000000..a672f69d2 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/LTPAnalysisFilter.cs @@ -0,0 +1,103 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal static class LTPAnalysisFilter + { + internal static void silk_LTP_analysis_filter( + short[] LTP_res, /* O LTP residual signal of length SilkConstants.MAX_NB_SUBFR * ( pre_length + subfr_length ) */ + short[] x, /* I Pointer to input signal with at least max( pitchL ) preceding samples */ + int x_ptr, + short[] LTPCoef_Q14,/* I LTP_ORDER LTP coefficients for each MAX_NB_SUBFR subframe [SilkConstants.LTP_ORDER * SilkConstants.MAX_NB_SUBFR] */ + int[] pitchL, /* I Pitch lag, one for each subframe [SilkConstants.MAX_NB_SUBFR] */ + int[] invGains_Q16, /* I Inverse quantization gains, one for each subframe [SilkConstants.MAX_NB_SUBFR] */ + int subfr_length, /* I Length of each subframe */ + int nb_subfr, /* I Number of subframes */ + int pre_length /* I Length of the preceding samples starting at &x[0] for each subframe */ +) + { + int x_ptr2, x_lag_ptr; + short[] Btmp_Q14 = new short[SilkConstants.LTP_ORDER]; + int LTP_res_ptr; + int k, i; + int LTP_est; + + x_ptr2 = x_ptr; + LTP_res_ptr = 0; + for (k = 0; k < nb_subfr; k++) + { + x_lag_ptr = x_ptr2 - pitchL[k]; + + Btmp_Q14[0] = LTPCoef_Q14[k * SilkConstants.LTP_ORDER]; + Btmp_Q14[1] = LTPCoef_Q14[k * SilkConstants.LTP_ORDER + 1]; + Btmp_Q14[2] = LTPCoef_Q14[k * SilkConstants.LTP_ORDER + 2]; + Btmp_Q14[3] = LTPCoef_Q14[k * SilkConstants.LTP_ORDER + 3]; + Btmp_Q14[4] = LTPCoef_Q14[k * SilkConstants.LTP_ORDER + 4]; + + /* LTP analysis FIR filter */ + for (i = 0; i < subfr_length + pre_length; i++) + { + int LTP_res_ptri = LTP_res_ptr + i; + LTP_res[LTP_res_ptri] = x[x_ptr2 + i]; + + /* Long-term prediction */ + LTP_est = Inlines.silk_SMULBB(x[x_lag_ptr + SilkConstants.LTP_ORDER / 2], Btmp_Q14[0]); + LTP_est = Inlines.silk_SMLABB_ovflw(LTP_est, x[x_lag_ptr + 1], Btmp_Q14[1]); + LTP_est = Inlines.silk_SMLABB_ovflw(LTP_est, x[x_lag_ptr], Btmp_Q14[2]); + LTP_est = Inlines.silk_SMLABB_ovflw(LTP_est, x[x_lag_ptr - 1], Btmp_Q14[3]); + LTP_est = Inlines.silk_SMLABB_ovflw(LTP_est, x[x_lag_ptr - 2], Btmp_Q14[4]); + + LTP_est = Inlines.silk_RSHIFT_ROUND(LTP_est, 14); /* round and . Q0*/ + + /* Subtract long-term prediction */ + LTP_res[LTP_res_ptri] = (short)Inlines.silk_SAT16((int)x[x_ptr2 + i] - LTP_est); + + /* Scale residual */ + LTP_res[LTP_res_ptri] = (short)(Inlines.silk_SMULWB(invGains_Q16[k], LTP_res[LTP_res_ptri])); + + x_lag_ptr++; + } + + /* Update pointers */ + LTP_res_ptr += subfr_length + pre_length; + x_ptr2 += subfr_length; + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/LTPScaleControl.cs b/Libraries/Concentus/CSharp/Concentus/Silk/LTPScaleControl.cs new file mode 100644 index 000000000..9b7da9de8 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/LTPScaleControl.cs @@ -0,0 +1,66 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal static class LTPScaleControl + { + /* Calculation of LTP state scaling */ + internal static void silk_LTP_scale_ctrl( + SilkChannelEncoder psEnc, /* I/O encoder state */ + SilkEncoderControl psEncCtrl, /* I/O encoder control */ + int condCoding /* I The type of conditional coding to use */ + ) + { + int round_loss; + + if (condCoding == SilkConstants.CODE_INDEPENDENTLY) + { + /* Only scale if first frame in packet */ + round_loss = psEnc.PacketLoss_perc + psEnc.nFramesPerPacket; + psEnc.indices.LTP_scaleIndex = (sbyte)Inlines.silk_LIMIT( + Inlines.silk_SMULWB(Inlines.silk_SMULBB(round_loss, psEncCtrl.LTPredCodGain_Q7), ((int)((0.1f) * ((long)1 << (9)) + 0.5))/*Inlines.SILK_CONST(0.1f, 9)*/), 0, 2); + } + else { + /* Default is minimum scaling */ + psEnc.indices.LTP_scaleIndex = 0; + } + psEncCtrl.LTP_scale_Q14 = Tables.silk_LTPScales_table_Q14[psEnc.indices.LTP_scaleIndex]; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/LinearAlgebra.cs b/Libraries/Concentus/CSharp/Concentus/Silk/LinearAlgebra.cs new file mode 100644 index 000000000..a80be01a5 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/LinearAlgebra.cs @@ -0,0 +1,245 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal static class LinearAlgebra + { + /* Solves Ax = b, assuming A is symmetric */ + internal static void silk_solve_LDL( + int[] A, /* I Pointer to symetric square matrix A */ + int A_ptr, + int M, /* I Size of matrix */ + int[] b, /* I Pointer to b vector */ + int[] x_Q16 /* O Pointer to x solution vector */ + ) + { + Inlines.OpusAssert(M <= SilkConstants.MAX_MATRIX_SIZE); + int[] L_Q16 = new int[M * M]; + int[] Y = new int[SilkConstants.MAX_MATRIX_SIZE]; + + // [Porting note] This is an interleaved array. Formerly it was an array of data structures laid out thus: + //private struct inv_D_t + //{ + // int Q36_part; + // int Q48_part; + //} + int[] inv_D = new int[SilkConstants.MAX_MATRIX_SIZE * 2]; + + /*************************************************** + Factorize A by LDL such that A = L*D*L', + where L is lower triangular with ones on diagonal + ****************************************************/ + silk_LDL_factorize(A, A_ptr, M, L_Q16, inv_D); + + /**************************************************** + * substitute D*L'*x = Y. ie: + L*D*L'*x = b => L*Y = b <=> Y = inv(L)*b + ******************************************************/ + silk_LS_SolveFirst(L_Q16, M, b, Y); + + /**************************************************** + D*L'*x = Y <=> L'*x = inv(D)*Y, because D is + diagonal just multiply with 1/d_i + ****************************************************/ + silk_LS_divide_Q16(Y, inv_D, M); + + /**************************************************** + x = inv(L') * inv(D) * Y + *****************************************************/ + silk_LS_SolveLast(L_Q16, M, Y, x_Q16); + + } + + /* Factorize square matrix A into LDL form */ + private static void silk_LDL_factorize( + int[] A, /* I/O Pointer to Symetric Square Matrix */ + int A_ptr, + int M, /* I Size of Matrix */ + int[] L_Q16, /* I/O Pointer to Square Upper triangular Matrix */ + int[] inv_D /* I/O Pointer to vector holding inverted diagonal elements of D */ + ) + { + int i, j, k, status, loop_count; + int[] scratch1; + int scratch1_ptr; + int[] scratch2; + int scratch2_ptr; + int diag_min_value, tmp_32, err; + int[] v_Q0 = new int[M]; /*SilkConstants.MAX_MATRIX_SIZE*/ + int[] D_Q0 = new int[M]; /*SilkConstants.MAX_MATRIX_SIZE*/ + int one_div_diag_Q36, one_div_diag_Q40, one_div_diag_Q48; + + Inlines.OpusAssert(M <= SilkConstants.MAX_MATRIX_SIZE); + + status = 1; + diag_min_value = Inlines.silk_max_32(Inlines.silk_SMMUL(Inlines.silk_ADD_SAT32(A[A_ptr], A[A_ptr + Inlines.silk_SMULBB(M, M) - 1]), ((int)((TuningParameters.FIND_LTP_COND_FAC) * ((long)1 << (31)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LTP_COND_FAC, 31)*/), 1 << 9); + for (loop_count = 0; loop_count < M && status == 1; loop_count++) + { + status = 0; + for (j = 0; j < M; j++) + { + scratch1 = L_Q16; + scratch1_ptr = Inlines.MatrixGetPointer(j, 0, M); + tmp_32 = 0; + for (i = 0; i < j; i++) + { + v_Q0[i] = Inlines.silk_SMULWW(D_Q0[i], scratch1[scratch1_ptr + i]); /* Q0 */ + tmp_32 = Inlines.silk_SMLAWW(tmp_32, v_Q0[i], scratch1[scratch1_ptr + i]); /* Q0 */ + } + tmp_32 = Inlines.silk_SUB32(Inlines.MatrixGet(A, A_ptr, j, j, M), tmp_32); + + if (tmp_32 < diag_min_value) + { + tmp_32 = Inlines.silk_SUB32(Inlines.silk_SMULBB(loop_count + 1, diag_min_value), tmp_32); + /* Matrix not positive semi-definite, or ill conditioned */ + for (i = 0; i < M; i++) + { + Inlines.MatrixSet(A, A_ptr, i, i, M, Inlines.silk_ADD32(Inlines.MatrixGet(A, A_ptr, i, i, M), tmp_32)); + } + status = 1; + break; + } + D_Q0[j] = tmp_32; /* always < max(Correlation) */ + + /* two-step division */ + one_div_diag_Q36 = Inlines.silk_INVERSE32_varQ(tmp_32, 36); /* Q36 */ + one_div_diag_Q40 = Inlines.silk_LSHIFT(one_div_diag_Q36, 4); /* Q40 */ + err = Inlines.silk_SUB32((int)1 << 24, Inlines.silk_SMULWW(tmp_32, one_div_diag_Q40)); /* Q24 */ + one_div_diag_Q48 = Inlines.silk_SMULWW(err, one_div_diag_Q40); /* Q48 */ + + /* Save 1/Ds */ + inv_D[(j * 2) + 0] = one_div_diag_Q36; + inv_D[(j * 2) + 1] = one_div_diag_Q48; + + Inlines.MatrixSet(L_Q16, j, j, M, 65536); /* 1.0 in Q16 */ + scratch1 = A; + scratch1_ptr = Inlines.MatrixGetPointer(j, 0, M) + A_ptr; + scratch2 = L_Q16; + scratch2_ptr = Inlines.MatrixGetPointer(j + 1, 0, M); + for (i = j + 1; i < M; i++) + { + tmp_32 = 0; + for (k = 0; k < j; k++) + { + tmp_32 = Inlines.silk_SMLAWW(tmp_32, v_Q0[k], scratch2[scratch2_ptr + k]); /* Q0 */ + } + tmp_32 = Inlines.silk_SUB32(scratch1[scratch1_ptr + i], tmp_32); /* always < max(Correlation) */ + + /* tmp_32 / D_Q0[j] : Divide to Q16 */ + Inlines.MatrixSet(L_Q16, i, j, M, Inlines.silk_ADD32(Inlines.silk_SMMUL(tmp_32, one_div_diag_Q48), + Inlines.silk_RSHIFT(Inlines.silk_SMULWW(tmp_32, one_div_diag_Q36), 4))); + + /* go to next column */ + scratch2_ptr += M; + } + } + } + + Inlines.OpusAssert(status == 0); + } + + private static void silk_LS_divide_Q16( + int[] T, /* I/O Numenator vector */ + int[] inv_D, /* I 1 / D vector */ + int M /* I dimension */ + ) + { + int i; + int tmp_32; + int one_div_diag_Q36, one_div_diag_Q48; + + for (i = 0; i < M; i++) + { + one_div_diag_Q36 = inv_D[(i * 2) + 0]; + one_div_diag_Q48 = inv_D[(i * 2) + 1]; + + tmp_32 = T[i]; + T[i] = Inlines.silk_ADD32(Inlines.silk_SMMUL(tmp_32, one_div_diag_Q48), Inlines.silk_RSHIFT(Inlines.silk_SMULWW(tmp_32, one_div_diag_Q36), 4)); + } + } + + /* Solve Lx = b, when L is lower triangular and has ones on the diagonal */ + private static void silk_LS_SolveFirst( + int[] L_Q16, /* I Pointer to Lower Triangular Matrix */ + int M, /* I Dim of Matrix equation */ + int[] b, /* I b Vector */ + int[] x_Q16 /* O x Vector */ + ) + { + int i, j; + int ptr32; + int tmp_32; + + for (i = 0; i < M; i++) + { + ptr32 = Inlines.MatrixGetPointer(i, 0, M); + tmp_32 = 0; + for (j = 0; j < i; j++) + { + tmp_32 = Inlines.silk_SMLAWW(tmp_32, L_Q16[ptr32 + j], x_Q16[j]); + } + x_Q16[i] = Inlines.silk_SUB32(b[i], tmp_32); + } + } + + /* Solve L^t*x = b, where L is lower triangular with ones on the diagonal */ + private static void silk_LS_SolveLast( + int[] L_Q16, /* I Pointer to Lower Triangular Matrix */ + int M, /* I Dim of Matrix equation */ + int[] b, /* I b Vector */ + int[] x_Q16 /* O x Vector */ + ) + { + int i, j; + int ptr32; + int tmp_32; + + for (i = M - 1; i >= 0; i--) + { + ptr32 = Inlines.MatrixGetPointer(0, i, M); + tmp_32 = 0; + for (j = M - 1; j > i; j--) + { + tmp_32 = Inlines.silk_SMLAWW(tmp_32, L_Q16[ptr32 + Inlines.silk_SMULBB(j, M)], x_Q16[j]); + } + x_Q16[i] = Inlines.silk_SUB32(b[i], tmp_32); + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/NLSF.cs b/Libraries/Concentus/CSharp/Concentus/Silk/NLSF.cs new file mode 100644 index 000000000..5c0568302 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/NLSF.cs @@ -0,0 +1,1272 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System; + using System.Diagnostics; + + /// + /// Normalized line spectrum frequency processor + /// + internal static class NLSF + { + private const int MAX_STABILIZE_LOOPS = 20; + + private const int QA = 16; + + /// + /// Number of binary divisions, when not in low complexity mode + /// + private const int BIN_DIV_STEPS_A2NLSF = 3; /* must be no higher than 16 - log2( LSF_COS_TAB_SZ ) */ + + private const int MAX_ITERATIONS_A2NLSF = 30; + + /// + /// Compute quantization errors for an LPC_order element input vector for a VQ codebook + /// + /// (O) Quantization errors [K] + /// (I) Input vectors to be quantized [LPC_order] + /// (I) Codebook vectors [K*LPC_order] + /// (I) Number of codebook vectors + /// (I) Number of LPCs + internal static void silk_NLSF_VQ(int[] err_Q26, short[] in_Q15, byte[] pCB_Q8, int K, int LPC_order) + { + int diff_Q15, sum_error_Q30, sum_error_Q26; + int pCB_idx = 0; + + Inlines.OpusAssert(err_Q26 != null); + Inlines.OpusAssert(LPC_order <= 16); + Inlines.OpusAssert((LPC_order & 1) == 0); + + // Loop over codebook + for (int i = 0; i < K; i++) + { + sum_error_Q26 = 0; + + for (int m = 0; m < LPC_order; m += 2) + { + // Compute weighted squared quantization error for index m + diff_Q15 = Inlines.silk_SUB_LSHIFT32(in_Q15[m], pCB_Q8[pCB_idx++], 7); // range: [ -32767 : 32767 ] + sum_error_Q30 = Inlines.silk_SMULBB(diff_Q15, diff_Q15); + + // Compute weighted squared quantization error for index m + 1 + diff_Q15 = Inlines.silk_SUB_LSHIFT32(in_Q15[m + 1], pCB_Q8[pCB_idx++], 7); // range: [ -32767 : 32767 ] + sum_error_Q30 = Inlines.silk_SMLABB(sum_error_Q30, diff_Q15, diff_Q15); + + sum_error_Q26 = Inlines.silk_ADD_RSHIFT32(sum_error_Q26, sum_error_Q30, 4); + + Inlines.OpusAssert(sum_error_Q26 >= 0); + Inlines.OpusAssert(sum_error_Q30 >= 0); + } + + err_Q26[i] = sum_error_Q26; + } + } + + /// + /// Laroia low complexity NLSF weights + /// + /// (O) Pointer to input vector weights [D] + /// (I) Pointer to input vector [D] + /// (I) Input vector dimension (even) + internal static void silk_NLSF_VQ_weights_laroia(short[] pNLSFW_Q_OUT, short[] pNLSF_Q15, int D) + { + int k; + int tmp1_int, tmp2_int; + + Inlines.OpusAssert(pNLSFW_Q_OUT != null); + Inlines.OpusAssert(D > 0); + Inlines.OpusAssert((D & 1) == 0); + + // First value + tmp1_int = Inlines.silk_max_int(pNLSF_Q15[0], 1); + tmp1_int = Inlines.silk_DIV32((int)1 << (15 + SilkConstants.NLSF_W_Q), tmp1_int); + tmp2_int = Inlines.silk_max_int(pNLSF_Q15[1] - pNLSF_Q15[0], 1); + tmp2_int = Inlines.silk_DIV32((int)1 << (15 + SilkConstants.NLSF_W_Q), tmp2_int); + pNLSFW_Q_OUT[0] = (short)Inlines.silk_min_int(tmp1_int + tmp2_int, short.MaxValue); + + Inlines.OpusAssert(pNLSFW_Q_OUT[0] > 0); + + // Main loop + for (k = 1; k < D - 1; k += 2) + { + tmp1_int = Inlines.silk_max_int(pNLSF_Q15[k + 1] - pNLSF_Q15[k], 1); + tmp1_int = Inlines.silk_DIV32((int)1 << (15 + SilkConstants.NLSF_W_Q), tmp1_int); + pNLSFW_Q_OUT[k] = (short)Inlines.silk_min_int(tmp1_int + tmp2_int, short.MaxValue); + Inlines.OpusAssert(pNLSFW_Q_OUT[k] > 0); + + tmp2_int = Inlines.silk_max_int(pNLSF_Q15[k + 2] - pNLSF_Q15[k + 1], 1); + tmp2_int = Inlines.silk_DIV32((int)1 << (15 + SilkConstants.NLSF_W_Q), tmp2_int); + pNLSFW_Q_OUT[k + 1] = (short)Inlines.silk_min_int(tmp1_int + tmp2_int, short.MaxValue); + Inlines.OpusAssert(pNLSFW_Q_OUT[k + 1] > 0); + } + + // Last value + tmp1_int = Inlines.silk_max_int((1 << 15) - pNLSF_Q15[D - 1], 1); + tmp1_int = Inlines.silk_DIV32((int)1 << (15 + SilkConstants.NLSF_W_Q), tmp1_int); + pNLSFW_Q_OUT[D - 1] = (short)Inlines.silk_min_int(tmp1_int + tmp2_int, short.MaxValue); + + Inlines.OpusAssert(pNLSFW_Q_OUT[D - 1] > 0); + } + + /// + /// Returns RD value in Q30 + /// + /// (O) Output [ order ] + /// (I) Quantization indices [ order ] + /// (I) Backward predictor coefs [ order ] + /// (I) Quantization step size + /// (I) Number of input values + internal static void silk_NLSF_residual_dequant( + short[] x_Q10, + sbyte[] indices, + int indices_ptr, + byte[] pred_coef_Q8, + int quant_step_size_Q16, + short order) + { + int i, pred_Q10; + short out_Q10; + + out_Q10 = 0; + for (i = order - 1; i >= 0; i--) + { + pred_Q10 = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(out_Q10, (short)pred_coef_Q8[i]), 8); + out_Q10 = Inlines.silk_LSHIFT16((short)indices[indices_ptr + i], 10); + if (out_Q10 > 0) + { + out_Q10 = Inlines.silk_SUB16(out_Q10, (short)(((int)((SilkConstants.NLSF_QUANT_LEVEL_ADJ) * ((long)1 << (10)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.NLSF_QUANT_LEVEL_ADJ, 10)*/)); + } + else if (out_Q10 < 0) + { + out_Q10 = Inlines.silk_ADD16(out_Q10, (short)(((int)((SilkConstants.NLSF_QUANT_LEVEL_ADJ) * ((long)1 << (10)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.NLSF_QUANT_LEVEL_ADJ, 10)*/)); + } + out_Q10 = (short)(Inlines.silk_SMLAWB(pred_Q10, (int)out_Q10, quant_step_size_Q16)); + x_Q10[i] = out_Q10; + } + } + + /// + /// Unpack predictor values and indices for entropy coding tables + /// + /// (O) Indices to entropy tables [ LPC_ORDER ] + /// (O) LSF predictor [ LPC_ORDER ] + /// (I) Codebook object + /// (I) Index of vector in first LSF codebook + internal static void silk_NLSF_unpack(short[] ec_ix, byte[] pred_Q8, NLSFCodebook psNLSF_CB, int CB1_index) + { + int i; + byte entry; + byte[] ec_sel = psNLSF_CB.ec_sel; + int ec_sel_ptr = CB1_index * psNLSF_CB.order / 2; + + for (i = 0; i < psNLSF_CB.order; i += 2) + { + entry = ec_sel[ec_sel_ptr]; + ec_sel_ptr++; + ec_ix[i] = (short)(Inlines.silk_SMULBB(Inlines.silk_RSHIFT(entry, 1) & 7, 2 * SilkConstants.NLSF_QUANT_MAX_AMPLITUDE + 1)); + pred_Q8[i] = psNLSF_CB.pred_Q8[i + (entry & 1) * (psNLSF_CB.order - 1)]; + ec_ix[i + 1] = (short)(Inlines.silk_SMULBB(Inlines.silk_RSHIFT(entry, 5) & 7, 2 * SilkConstants.NLSF_QUANT_MAX_AMPLITUDE + 1)); + pred_Q8[i + 1] = psNLSF_CB.pred_Q8[i + (Inlines.silk_RSHIFT(entry, 4) & 1) * (psNLSF_CB.order - 1) + 1]; + } + } + + /// + /// NLSF stabilizer, for a single input data vector + /// + /// (I/O) Unstable/stabilized normalized LSF vector in Q15 [L] + /// (I) Min distance vector, NDeltaMin_Q15[L] must be >= 1 [L+1] + /// (I) Number of NLSF parameters in the input vector + internal static void silk_NLSF_stabilize(short[] NLSF_Q15, short[] NDeltaMin_Q15, int L) + { + int i, I = 0, k, loops; + short center_freq_Q15; + int diff_Q15, min_diff_Q15, min_center_Q15, max_center_Q15; + + // This is necessary to ensure an output within range of a short + Inlines.OpusAssert(NDeltaMin_Q15[L] >= 1); + + for (loops = 0; loops < MAX_STABILIZE_LOOPS; loops++) + { + /**************************/ + /* Find smallest distance */ + /**************************/ + // First element + min_diff_Q15 = NLSF_Q15[0] - NDeltaMin_Q15[0]; + I = 0; + + // Middle elements + for (i = 1; i <= L - 1; i++) + { + diff_Q15 = NLSF_Q15[i] - (NLSF_Q15[i - 1] + NDeltaMin_Q15[i]); + if (diff_Q15 < min_diff_Q15) + { + min_diff_Q15 = diff_Q15; + I = i; + } + } + + // Last element + diff_Q15 = (1 << 15) - (NLSF_Q15[L - 1] + NDeltaMin_Q15[L]); + if (diff_Q15 < min_diff_Q15) + { + min_diff_Q15 = diff_Q15; + I = L; + } + + /***************************************************/ + /* Now check if the smallest distance non-negative */ + /***************************************************/ + if (min_diff_Q15 >= 0) + { + return; + } + + if (I == 0) + { + // Move away from lower limit + NLSF_Q15[0] = NDeltaMin_Q15[0]; + } + else if (I == L) + { + // Move away from higher limit + NLSF_Q15[L - 1] = (short)((1 << 15) - NDeltaMin_Q15[L]); + } + else + { + // Find the lower extreme for the location of the current center frequency + min_center_Q15 = 0; + for (k = 0; k < I; k++) + { + min_center_Q15 += NDeltaMin_Q15[k]; + } + + min_center_Q15 += Inlines.silk_RSHIFT(NDeltaMin_Q15[I], 1); + + // Find the upper extreme for the location of the current center frequency + max_center_Q15 = 1 << 15; + for (k = L; k > I; k--) + { + max_center_Q15 -= NDeltaMin_Q15[k]; + } + + max_center_Q15 -= Inlines.silk_RSHIFT(NDeltaMin_Q15[I], 1); + + // Move apart, sorted by value, keeping the same center frequency + center_freq_Q15 = (short)(Inlines.silk_LIMIT_32(Inlines.silk_RSHIFT_ROUND((int)NLSF_Q15[I - 1] + (int)NLSF_Q15[I], 1), + min_center_Q15, max_center_Q15)); + NLSF_Q15[I - 1] = (short)(center_freq_Q15 - Inlines.silk_RSHIFT(NDeltaMin_Q15[I], 1)); + NLSF_Q15[I] = (short)(NLSF_Q15[I - 1] + NDeltaMin_Q15[I]); + } + } + + // Safe and simple fall back method, which is less ideal than the above + if (loops == MAX_STABILIZE_LOOPS) + { + Sort.silk_insertion_sort_increasing_all_values_int16(NLSF_Q15, L); + + // First NLSF should be no less than NDeltaMin[0] + NLSF_Q15[0] = (short)(Inlines.silk_max_int(NLSF_Q15[0], NDeltaMin_Q15[0])); + + // Keep delta_min distance between the NLSFs + for (i = 1; i < L; i++) + { + NLSF_Q15[i] = (short)(Inlines.silk_max_int(NLSF_Q15[i], NLSF_Q15[i - 1] + NDeltaMin_Q15[i])); + } + + // Last NLSF should be no higher than 1 - NDeltaMin[L] + NLSF_Q15[L - 1] = (short)(Inlines.silk_min_int(NLSF_Q15[L - 1], (1 << 15) - NDeltaMin_Q15[L])); + + // Keep NDeltaMin distance between the NLSFs + for (i = L - 2; i >= 0; i--) + { + NLSF_Q15[i] = (short)(Inlines.silk_min_int(NLSF_Q15[i], NLSF_Q15[i + 1] - NDeltaMin_Q15[i + 1])); + } + } + } + + /// + /// NLSF vector decoder + /// + /// (O) Quantized NLSF vector [ LPC_ORDER ] + /// (I) Codebook path vector [ LPC_ORDER + 1 ] + /// (I) Codebook object + internal static void silk_NLSF_decode(short[] pNLSF_Q15, sbyte[] NLSFIndices, NLSFCodebook psNLSF_CB) + { + int i; + byte[] pred_Q8 = new byte[psNLSF_CB.order]; + short[] ec_ix = new short[psNLSF_CB.order]; + short[] res_Q10 = new short[psNLSF_CB.order]; + short[] W_tmp_QW = new short[psNLSF_CB.order]; + int W_tmp_Q9, NLSF_Q15_tmp; + + // Decode first stage + byte[] pCB = psNLSF_CB.CB1_NLSF_Q8; + int pCB_element = NLSFIndices[0] * psNLSF_CB.order; + + for (i = 0; i < psNLSF_CB.order; i++) + { + pNLSF_Q15[i] = Inlines.silk_LSHIFT16((short)pCB[pCB_element + i], 7); + } + + // Unpack entropy table indices and predictor for current CB1 index + silk_NLSF_unpack(ec_ix, pred_Q8, psNLSF_CB, NLSFIndices[0]); + + // Predictive residual dequantizer + silk_NLSF_residual_dequant(res_Q10, + NLSFIndices, + 1, + pred_Q8, + psNLSF_CB.quantStepSize_Q16, + psNLSF_CB.order); + + // Weights from codebook vector + silk_NLSF_VQ_weights_laroia(W_tmp_QW, pNLSF_Q15, psNLSF_CB.order); + + // Apply inverse square-rooted weights and add to output + for (i = 0; i < psNLSF_CB.order; i++) + { + W_tmp_Q9 = Inlines.silk_SQRT_APPROX(Inlines.silk_LSHIFT((int)W_tmp_QW[i], 18 - SilkConstants.NLSF_W_Q)); + NLSF_Q15_tmp = Inlines.silk_ADD32(pNLSF_Q15[i], Inlines.silk_DIV32_16(Inlines.silk_LSHIFT((int)res_Q10[i], 14), (short)(W_tmp_Q9))); + pNLSF_Q15[i] = (short)(Inlines.silk_LIMIT(NLSF_Q15_tmp, 0, 32767)); + } + + // NLSF stabilization + silk_NLSF_stabilize(pNLSF_Q15, psNLSF_CB.deltaMin_Q15, psNLSF_CB.order); + } + + /// + /// Delayed-decision quantizer for NLSF residuals + /// + /// (O) Quantization indices [ order ] + /// (O) Input [ order ] + /// (I) Weights [ order ] + /// (I) Backward predictor coefs [ order ] + /// (I) Indices to entropy coding tables [ order ] + /// (I) Rates [] + /// (I) Quantization step size + /// (I) Inverse quantization step size + /// (I) R/D tradeoff + /// (I) Number of input values + /// RD value in Q25 + /// Fixme: Optimize this method! + internal static int silk_NLSF_del_dec_quant( + sbyte[] indices, + short[] x_Q10, + short[] w_Q5, + byte[] pred_coef_Q8, + short[] ec_ix, + byte[] ec_rates_Q5, + int quant_step_size_Q16, + short inv_quant_step_size_Q6, + int mu_Q20, + short order) + { + int i, j, nStates, ind_tmp, ind_min_max, ind_max_min, in_Q10, res_Q10; + int pred_Q10, diff_Q10, out0_Q10, out1_Q10, rate0_Q5, rate1_Q5; + int RD_tmp_Q25, min_Q25, min_max_Q25, max_min_Q25, pred_coef_Q16; + int[] ind_sort = new int[SilkConstants.NLSF_QUANT_DEL_DEC_STATES]; + sbyte[][] ind = new sbyte[SilkConstants.NLSF_QUANT_DEL_DEC_STATES][]; + for (i = 0; i < SilkConstants.NLSF_QUANT_DEL_DEC_STATES; i++) + { + ind[i] = new sbyte[SilkConstants.MAX_LPC_ORDER]; + } + + short[] prev_out_Q10 = new short[2 * SilkConstants.NLSF_QUANT_DEL_DEC_STATES]; + int[] RD_Q25 = new int[2 * SilkConstants.NLSF_QUANT_DEL_DEC_STATES]; + int[] RD_min_Q25 = new int[SilkConstants.NLSF_QUANT_DEL_DEC_STATES]; + int[] RD_max_Q25 = new int[SilkConstants.NLSF_QUANT_DEL_DEC_STATES]; + int rates_Q5; + + int[] out0_Q10_table = new int[2 * SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT]; + int[] out1_Q10_table = new int[2 * SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT]; + + for (i = 0 - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT; i <= SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT - 1; i++) + { + out0_Q10 = Inlines.silk_LSHIFT(i, 10); + out1_Q10 = Inlines.silk_ADD16((short)(out0_Q10), 1024); + + if (i > 0) + { + out0_Q10 = Inlines.silk_SUB16((short)(out0_Q10), (short)(((int)((SilkConstants.NLSF_QUANT_LEVEL_ADJ) * ((long)1 << (10)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.NLSF_QUANT_LEVEL_ADJ, 10)*/)); + out1_Q10 = Inlines.silk_SUB16((short)(out1_Q10), (short)(((int)((SilkConstants.NLSF_QUANT_LEVEL_ADJ) * ((long)1 << (10)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.NLSF_QUANT_LEVEL_ADJ, 10)*/)); + } + else if (i == 0) + { + out1_Q10 = Inlines.silk_SUB16((short)(out1_Q10), (short)(((int)((SilkConstants.NLSF_QUANT_LEVEL_ADJ) * ((long)1 << (10)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.NLSF_QUANT_LEVEL_ADJ, 10)*/)); + } + else if (i == -1) + { + out0_Q10 = Inlines.silk_ADD16((short)(out0_Q10), (short)(((int)((SilkConstants.NLSF_QUANT_LEVEL_ADJ) * ((long)1 << (10)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.NLSF_QUANT_LEVEL_ADJ, 10)*/)); + } + else + { + out0_Q10 = Inlines.silk_ADD16((short)(out0_Q10), (short)(((int)((SilkConstants.NLSF_QUANT_LEVEL_ADJ) * ((long)1 << (10)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.NLSF_QUANT_LEVEL_ADJ, 10)*/)); + out1_Q10 = Inlines.silk_ADD16((short)(out1_Q10), (short)(((int)((SilkConstants.NLSF_QUANT_LEVEL_ADJ) * ((long)1 << (10)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.NLSF_QUANT_LEVEL_ADJ, 10)*/)); + } + + out0_Q10_table[i + SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT] = Inlines.silk_SMULWB((int)out0_Q10, quant_step_size_Q16); + out1_Q10_table[i + SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT] = Inlines.silk_SMULWB((int)out1_Q10, quant_step_size_Q16); + } + + Inlines.OpusAssert((SilkConstants.NLSF_QUANT_DEL_DEC_STATES & (SilkConstants.NLSF_QUANT_DEL_DEC_STATES - 1)) == 0); // must be power of two + + nStates = 1; + RD_Q25[0] = 0; + prev_out_Q10[0] = 0; + + for (i = order - 1; ; i--) + { + pred_coef_Q16 = Inlines.silk_LSHIFT((int)pred_coef_Q8[i], 8); + in_Q10 = x_Q10[i]; + + for (j = 0; j < nStates; j++) + { + pred_Q10 = Inlines.silk_SMULWB(pred_coef_Q16, prev_out_Q10[j]); + res_Q10 = Inlines.silk_SUB16((short)(in_Q10), (short)(pred_Q10)); + ind_tmp = Inlines.silk_SMULWB((int)inv_quant_step_size_Q6, res_Q10); + ind_tmp = Inlines.silk_LIMIT(ind_tmp, 0 - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT, SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT - 1); + ind[j][i] = (sbyte)ind_tmp; + rates_Q5 = ec_ix[i] + ind_tmp; + + // compute outputs for ind_tmp and ind_tmp + 1 + out0_Q10 = out0_Q10_table[ind_tmp + SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT]; + out1_Q10 = out1_Q10_table[ind_tmp + SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT]; + + out0_Q10 = Inlines.silk_ADD16((short)(out0_Q10), (short)(pred_Q10)); + out1_Q10 = Inlines.silk_ADD16((short)(out1_Q10), (short)(pred_Q10)); + prev_out_Q10[j] = (short)(out0_Q10); + prev_out_Q10[j + nStates] = (short)(out1_Q10); + + // compute RD for ind_tmp and ind_tmp + 1 + if (ind_tmp + 1 >= SilkConstants.NLSF_QUANT_MAX_AMPLITUDE) + { + if (ind_tmp + 1 == SilkConstants.NLSF_QUANT_MAX_AMPLITUDE) + { + rate0_Q5 = ec_rates_Q5[rates_Q5 + SilkConstants.NLSF_QUANT_MAX_AMPLITUDE]; + rate1_Q5 = 280; + } + else + { + rate0_Q5 = Inlines.silk_SMLABB(280 - (43 * SilkConstants.NLSF_QUANT_MAX_AMPLITUDE), 43, ind_tmp); + rate1_Q5 = Inlines.silk_ADD16((short)(rate0_Q5), 43); + } + } + else if (ind_tmp <= 0 - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE) + { + if (ind_tmp == 0 - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE) + { + rate0_Q5 = 280; + rate1_Q5 = ec_rates_Q5[rates_Q5 + 1 + SilkConstants.NLSF_QUANT_MAX_AMPLITUDE]; + } + else + { + rate0_Q5 = Inlines.silk_SMLABB(280 - 43 * SilkConstants.NLSF_QUANT_MAX_AMPLITUDE, -43, ind_tmp); + rate1_Q5 = Inlines.silk_SUB16((short)(rate0_Q5), 43); + } + } + else + { + rate0_Q5 = ec_rates_Q5[rates_Q5 + SilkConstants.NLSF_QUANT_MAX_AMPLITUDE]; + rate1_Q5 = ec_rates_Q5[rates_Q5 + 1 + SilkConstants.NLSF_QUANT_MAX_AMPLITUDE]; + } + + RD_tmp_Q25 = RD_Q25[j]; + diff_Q10 = Inlines.silk_SUB16((short)(in_Q10), (short)(out0_Q10)); + RD_Q25[j] = Inlines.silk_SMLABB(Inlines.silk_MLA(RD_tmp_Q25, Inlines.silk_SMULBB(diff_Q10, diff_Q10), w_Q5[i]), mu_Q20, rate0_Q5); + diff_Q10 = Inlines.silk_SUB16((short)(in_Q10), (short)(out1_Q10)); + RD_Q25[j + nStates] = Inlines.silk_SMLABB(Inlines.silk_MLA(RD_tmp_Q25, Inlines.silk_SMULBB(diff_Q10, diff_Q10), w_Q5[i]), mu_Q20, rate1_Q5); + } + + if (nStates <= (SilkConstants.NLSF_QUANT_DEL_DEC_STATES >> 1)) + { + // double number of states and copy + for (j = 0; j < nStates; j++) + { + ind[j + nStates][i] = (sbyte)(ind[j][i] + 1); + } + nStates = Inlines.silk_LSHIFT(nStates, 1); + + for (j = nStates; j < SilkConstants.NLSF_QUANT_DEL_DEC_STATES; j++) + { + ind[j][i] = ind[j - nStates][i]; + } + } + else if (i > 0) + { + // sort lower and upper half of RD_Q25, pairwise + for (j = 0; j < SilkConstants.NLSF_QUANT_DEL_DEC_STATES; j++) + { + if (RD_Q25[j] > RD_Q25[j + SilkConstants.NLSF_QUANT_DEL_DEC_STATES]) + { + RD_max_Q25[j] = RD_Q25[j]; + RD_min_Q25[j] = RD_Q25[j + SilkConstants.NLSF_QUANT_DEL_DEC_STATES]; + RD_Q25[j] = RD_min_Q25[j]; + RD_Q25[j + SilkConstants.NLSF_QUANT_DEL_DEC_STATES] = RD_max_Q25[j]; + + // swap prev_out values + out0_Q10 = prev_out_Q10[j]; + prev_out_Q10[j] = prev_out_Q10[j + SilkConstants.NLSF_QUANT_DEL_DEC_STATES]; + prev_out_Q10[j + SilkConstants.NLSF_QUANT_DEL_DEC_STATES] = (short)(out0_Q10); + ind_sort[j] = j + SilkConstants.NLSF_QUANT_DEL_DEC_STATES; + } + else + { + RD_min_Q25[j] = RD_Q25[j]; + RD_max_Q25[j] = RD_Q25[j + SilkConstants.NLSF_QUANT_DEL_DEC_STATES]; + ind_sort[j] = j; + } + } + + // compare the highest RD values of the winning half with the lowest one in the losing half, and copy if necessary + // afterwards ind_sort[] will contain the indices of the NLSF_QUANT_DEL_DEC_STATES winning RD values + while (true) + { + min_max_Q25 = int.MaxValue; + max_min_Q25 = 0; + ind_min_max = 0; + ind_max_min = 0; + + for (j = 0; j < SilkConstants.NLSF_QUANT_DEL_DEC_STATES; j++) + { + if (min_max_Q25 > RD_max_Q25[j]) + { + min_max_Q25 = RD_max_Q25[j]; + ind_min_max = j; + } + if (max_min_Q25 < RD_min_Q25[j]) + { + max_min_Q25 = RD_min_Q25[j]; + ind_max_min = j; + } + } + + if (min_max_Q25 >= max_min_Q25) + { + break; + } + + // copy ind_min_max to ind_max_min + ind_sort[ind_max_min] = ind_sort[ind_min_max] ^ SilkConstants.NLSF_QUANT_DEL_DEC_STATES; + RD_Q25[ind_max_min] = RD_Q25[ind_min_max + SilkConstants.NLSF_QUANT_DEL_DEC_STATES]; + prev_out_Q10[ind_max_min] = prev_out_Q10[ind_min_max + SilkConstants.NLSF_QUANT_DEL_DEC_STATES]; + RD_min_Q25[ind_max_min] = 0; + RD_max_Q25[ind_min_max] = int.MaxValue; + Buffer.BlockCopy(ind[ind_min_max], 0, ind[ind_max_min], 0, order * sizeof(sbyte)); + } + + // increment index if it comes from the upper half + for (j = 0; j < SilkConstants.NLSF_QUANT_DEL_DEC_STATES; j++) + { + var x = (sbyte)Inlines.silk_RSHIFT(ind_sort[j], SilkConstants.NLSF_QUANT_DEL_DEC_STATES_LOG2); + ind[j][i] += x; + } + } + else + { + // i == 0 + break; + } + } + + // last sample: find winner, copy indices and return RD value + ind_tmp = 0; + min_Q25 = int.MaxValue; + for (j = 0; j < 2 * SilkConstants.NLSF_QUANT_DEL_DEC_STATES; j++) + { + if (min_Q25 > RD_Q25[j]) + { + min_Q25 = RD_Q25[j]; + ind_tmp = j; + } + } + + for (j = 0; j < order; j++) + { + indices[j] = ind[ind_tmp & (SilkConstants.NLSF_QUANT_DEL_DEC_STATES - 1)][j]; + Inlines.OpusAssert(indices[j] >= 0 - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT); + Inlines.OpusAssert(indices[j] <= SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT); + } + + indices[0] = (sbyte)(indices[0] + Inlines.silk_RSHIFT(ind_tmp, SilkConstants.NLSF_QUANT_DEL_DEC_STATES_LOG2)); + Inlines.OpusAssert(indices[0] <= SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT); + Inlines.OpusAssert(min_Q25 >= 0); + return min_Q25; + } + + /// + /// NLSF vector encoder + /// + /// (I) Codebook path vector [ LPC_ORDER + 1 ] + /// (I/O) Quantized NLSF vector [ LPC_ORDER ] + /// (I) Codebook object + /// (I) NLSF weight vector [ LPC_ORDER ] + /// (I) Rate weight for the RD optimization + /// (I) Max survivors after first stage + /// (I) Signal type: 0/1/2 + /// RD value in Q25 + internal static int silk_NLSF_encode( + sbyte[] NLSFIndices, + short[] pNLSF_Q15, + NLSFCodebook psNLSF_CB, + short[] pW_QW, + int NLSF_mu_Q20, + int nSurvivors, + int signalType) + { + int i, s, ind1, prob_Q8, bits_q7; + int W_tmp_Q9; + int[] err_Q26; + int[] RD_Q25; + int[] tempIndices1; + sbyte[][] tempIndices2; + short[] res_Q15 = new short[psNLSF_CB.order]; + short[] res_Q10 = new short[psNLSF_CB.order]; + short[] NLSF_tmp_Q15 = new short[psNLSF_CB.order]; + short[] W_tmp_QW = new short[psNLSF_CB.order]; + short[] W_adj_Q5 = new short[psNLSF_CB.order]; + byte[] pred_Q8 = new byte[psNLSF_CB.order]; + short[] ec_ix = new short[psNLSF_CB.order]; + byte[] pCB = psNLSF_CB.CB1_NLSF_Q8; + int iCDF_ptr; + int pCB_element; + + Inlines.OpusAssert(nSurvivors <= SilkConstants.NLSF_VQ_MAX_SURVIVORS); + Inlines.OpusAssert(signalType >= 0 && signalType <= 2); + Inlines.OpusAssert(NLSF_mu_Q20 <= 32767 && NLSF_mu_Q20 >= 0); + + // NLSF stabilization + silk_NLSF_stabilize(pNLSF_Q15, psNLSF_CB.deltaMin_Q15, psNLSF_CB.order); + + // First stage: VQ + err_Q26 = new int[psNLSF_CB.nVectors]; + silk_NLSF_VQ(err_Q26, pNLSF_Q15, psNLSF_CB.CB1_NLSF_Q8, psNLSF_CB.nVectors, psNLSF_CB.order); + + // Sort the quantization errors + tempIndices1 = new int[nSurvivors]; + Sort.silk_insertion_sort_increasing(err_Q26, tempIndices1, psNLSF_CB.nVectors, nSurvivors); + + RD_Q25 = new int[nSurvivors]; + tempIndices2 = Arrays.InitTwoDimensionalArray(nSurvivors, SilkConstants.MAX_LPC_ORDER); + + + // Loop over survivors + for (s = 0; s < nSurvivors; s++) + { + ind1 = tempIndices1[s]; + + // Residual after first stage + + pCB_element = ind1 * psNLSF_CB.order; // opt: potential 1:2 partitioned buffer + for (i = 0; i < psNLSF_CB.order; i++) + { + NLSF_tmp_Q15[i] = Inlines.silk_LSHIFT16((short)pCB[pCB_element + i], 7); + res_Q15[i] = (short)(pNLSF_Q15[i] - NLSF_tmp_Q15[i]); + } + + // Weights from codebook vector + silk_NLSF_VQ_weights_laroia(W_tmp_QW, NLSF_tmp_Q15, psNLSF_CB.order); + + // Apply square-rooted weights + for (i = 0; i < psNLSF_CB.order; i++) + { + W_tmp_Q9 = Inlines.silk_SQRT_APPROX(Inlines.silk_LSHIFT((int)W_tmp_QW[i], 18 - SilkConstants.NLSF_W_Q)); + res_Q10[i] = (short)Inlines.silk_RSHIFT(Inlines.silk_SMULBB(res_Q15[i], W_tmp_Q9), 14); + } + + // Modify input weights accordingly + for (i = 0; i < psNLSF_CB.order; i++) + { + W_adj_Q5[i] = (short)(Inlines.silk_DIV32_16(Inlines.silk_LSHIFT((int)pW_QW[i], 5), W_tmp_QW[i])); + } + + // Unpack entropy table indices and predictor for current CB1 index + silk_NLSF_unpack(ec_ix, pred_Q8, psNLSF_CB, ind1); + + // Trellis quantizer + RD_Q25[s] = silk_NLSF_del_dec_quant( + tempIndices2[s], + res_Q10, + W_adj_Q5, + pred_Q8, + ec_ix, + psNLSF_CB.ec_Rates_Q5, + psNLSF_CB.quantStepSize_Q16, + psNLSF_CB.invQuantStepSize_Q6, + NLSF_mu_Q20, + psNLSF_CB.order); + + // Add rate for first stage + iCDF_ptr = (signalType >> 1) * psNLSF_CB.nVectors; + + if (ind1 == 0) + { + prob_Q8 = 256 - psNLSF_CB.CB1_iCDF[iCDF_ptr + ind1]; + } + else + { + prob_Q8 = psNLSF_CB.CB1_iCDF[iCDF_ptr + ind1 - 1] - psNLSF_CB.CB1_iCDF[iCDF_ptr + ind1]; + } + + bits_q7 = (8 << 7) - Inlines.silk_lin2log(prob_Q8); + RD_Q25[s] = Inlines.silk_SMLABB(RD_Q25[s], bits_q7, Inlines.silk_RSHIFT(NLSF_mu_Q20, 2)); + } + + // Find the lowest rate-distortion error + int[] bestIndex = new int[1]; + Sort.silk_insertion_sort_increasing(RD_Q25, bestIndex, nSurvivors, 1); + + NLSFIndices[0] = (sbyte)tempIndices1[bestIndex[0]]; + Array.Copy(tempIndices2[bestIndex[0]], 0, NLSFIndices, 1, psNLSF_CB.order); + + // Decode + silk_NLSF_decode(pNLSF_Q15, NLSFIndices, psNLSF_CB); + + return RD_Q25[0]; + } + + /// + /// helper function for NLSF2A(..) + /// + /// (O) intermediate polynomial, QA [dd+1] + /// (I) vector of interleaved 2*cos(LSFs), QA [d] + /// (I) polynomial order (= 1/2 * filter order) + internal static void silk_NLSF2A_find_poly( + int[] o, + int[] cLSF, + int cLSF_ptr, + int dd) + { + int k, n, ftmp; + + o[0] = Inlines.silk_LSHIFT(1, QA); + o[1] = 0 - cLSF[cLSF_ptr]; + for (k = 1; k < dd; k++) + { + ftmp = cLSF[cLSF_ptr + (2 * k)]; /* QA*/ + o[k + 1] = Inlines.silk_LSHIFT(o[k - 1], 1) - (int)Inlines.silk_RSHIFT_ROUND64(Inlines.silk_SMULL(ftmp, o[k]), QA); + for (n = k; n > 1; n--) + { + o[n] += o[n - 2] - (int)Inlines.silk_RSHIFT_ROUND64(Inlines.silk_SMULL(ftmp, o[n - 1]), QA); + } + o[1] -= ftmp; + } + } + + /* This ordering was found to maximize quality. It improves numerical accuracy of + silk_NLSF2A_find_poly() compared to "standard" ordering. */ + private static readonly byte[] ordering16 = { 0, 15, 8, 7, 4, 11, 12, 3, 2, 13, 10, 5, 6, 9, 14, 1 }; + private static readonly byte[] ordering10 = { 0, 9, 6, 3, 4, 5, 8, 1, 2, 7 }; + + + /// + /// compute whitening filter coefficients from normalized line spectral frequencies + /// + /// (O) monic whitening filter coefficients in Q12, [ d ] + /// (I) normalized line spectral frequencies in Q15, [ d ] + /// (I) filter order (should be even) + internal static void silk_NLSF2A( + short[] a_Q12, + short[] NLSF, + int d) + { + + byte[] ordering; + int k, i, dd; + int[] cos_LSF_QA = new int[d]; + int[] P = new int[d / 2 + 1]; + int[] Q = new int[d / 2 + 1]; + int[] a32_QA1 = new int[d]; + + int Ptmp, Qtmp, f_int, f_frac, cos_val, delta; + int maxabs, absval, idx = 0, sc_Q16; + + Inlines.OpusAssert (SilkConstants.LSF_COS_TAB_SZ == 128); + Inlines.OpusAssert (d == 10 || d == 16); + + /* convert LSFs to 2*cos(LSF), using piecewise linear curve from table */ + ordering = d == 16 ? ordering16 : ordering10; + + for (k = 0; k < d; k++) + { + Inlines.OpusAssert(NLSF[k] >= 0); + + /* f_int on a scale 0-127 (rounded down) */ + f_int = Inlines.silk_RSHIFT(NLSF[k], 15 - 7); + + /* f_frac, range: 0..255 */ + f_frac = NLSF[k] - Inlines.silk_LSHIFT(f_int, 15 - 7); + + Inlines.OpusAssert(f_int >= 0); + Inlines.OpusAssert(f_int < SilkConstants.LSF_COS_TAB_SZ); + + /* Read start and end value from table */ + cos_val = Tables.silk_LSFCosTab_Q12[f_int]; /* Q12 */ + delta = Tables.silk_LSFCosTab_Q12[f_int + 1] - cos_val; /* Q12, with a range of 0..200 */ + + /* Linear interpolation */ + cos_LSF_QA[ordering[k]] = Inlines.silk_RSHIFT_ROUND(Inlines.silk_LSHIFT(cos_val, 8) + Inlines.silk_MUL(delta, f_frac), 20 - QA); /* QA */ + } + + dd = Inlines.silk_RSHIFT(d, 1); + + /* generate even and odd polynomials using convolution */ + silk_NLSF2A_find_poly(P, cos_LSF_QA, 0, dd); + silk_NLSF2A_find_poly(Q, cos_LSF_QA, 1, dd); + + /* convert even and odd polynomials to opus_int32 Q12 filter coefs */ + for (k = 0; k < dd; k++) + { + Ptmp = P[k + 1] + P[k]; + Qtmp = Q[k + 1] - Q[k]; + + /* the Ptmp and Qtmp values at this stage need to fit in int32 */ + a32_QA1[k] = -Qtmp - Ptmp; /* QA+1 */ + a32_QA1[d - k - 1] = Qtmp - Ptmp; /* QA+1 */ + } + + /* Limit the maximum absolute value of the prediction coefficients, so that they'll fit in int16 */ + for (i = 0; i < 10; i++) + { + /* Find maximum absolute value and its index */ + maxabs = 0; + for (k = 0; k < d; k++) + { + absval = Inlines.silk_abs(a32_QA1[k]); + if (absval > maxabs) + { + maxabs = absval; + idx = k; + } + } + + maxabs = Inlines.silk_RSHIFT_ROUND(maxabs, QA + 1 - 12); /* QA+1 . Q12 */ + + if (maxabs > short.MaxValue) + { + /* Reduce magnitude of prediction coefficients */ + maxabs = Inlines.silk_min(maxabs, 163838); /* ( silk_int32_MAX >> 14 ) + silk_int16_MAX = 163838 */ + sc_Q16 = ((int)((0.999f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.999f, 16)*/ - Inlines.silk_DIV32(Inlines.silk_LSHIFT(maxabs - short.MaxValue, 14), + Inlines.silk_RSHIFT32(Inlines.silk_MUL(maxabs, idx + 1), 2)); + Filters.silk_bwexpander_32(a32_QA1, d, sc_Q16); + } + else + { + break; + } + } + + if (i == 10) + { + /* Reached the last iteration, clip the coefficients */ + for (k = 0; k < d; k++) + { + a_Q12[k] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(a32_QA1[k], QA + 1 - 12)); /* QA+1 . Q12 */ + a32_QA1[k] = Inlines.silk_LSHIFT((int)a_Q12[k], QA + 1 - 12); + } + } + else + { + for (k = 0; k < d; k++) + { + a_Q12[k] = (short)Inlines.silk_RSHIFT_ROUND(a32_QA1[k], QA + 1 - 12); /* QA+1 . Q12 */ + } + } + + for (i = 0; i < SilkConstants.MAX_LPC_STABILIZE_ITERATIONS; i++) + { + if (Filters.silk_LPC_inverse_pred_gain(a_Q12, d) < ((int)((1.0f / SilkConstants.MAX_PREDICTION_POWER_GAIN) * ((long)1 << (30)) + 0.5))/*Inlines.SILK_CONST(1.0f / SilkConstants.MAX_PREDICTION_POWER_GAIN, 30)*/) + { + /* Prediction coefficients are (too close to) unstable; apply bandwidth expansion */ + /* on the unscaled coefficients, convert to Q12 and measure again */ + Filters.silk_bwexpander_32(a32_QA1, d, 65536 - Inlines.silk_LSHIFT(2, i)); + + for (k = 0; k < d; k++) + { + a_Q12[k] = (short)Inlines.silk_RSHIFT_ROUND(a32_QA1[k], QA + 1 - 12); /* QA+1 . Q12 */ + } + } + else + { + break; + } + } + } + + /// + /// Helper function for A2NLSF(..) Transforms polynomials from cos(n*f) to cos(f)^n + /// + /// (I/O) Polynomial + /// (I) Polynomial order (= filter order / 2 ) + internal static void silk_A2NLSF_trans_poly(int[] p, int dd) + { + int k, n; + + for (k = 2; k <= dd; k++) + { + for (n = dd; n > k; n--) + { + p[n - 2] -= p[n]; + } + p[k - 2] -= Inlines.silk_LSHIFT(p[k], 1); + } + } + + /// + /// Helper function for A2NLSF(..) Polynomial evaluation + /// + /// (I) Polynomial, Q16 + /// (I) Evaluation point, Q12 + /// (I) Order + /// the polynomial evaluation, in Q16 + internal static int silk_A2NLSF_eval_poly(int[] p, int x, int dd) + { + int n; + int x_Q16, y32; + + y32 = p[dd]; /* Q16 */ + x_Q16 = Inlines.silk_LSHIFT(x, 4); + + if (8 == dd) + { + y32 = Inlines.silk_SMLAWW(p[7], y32, x_Q16); + y32 = Inlines.silk_SMLAWW(p[6], y32, x_Q16); + y32 = Inlines.silk_SMLAWW(p[5], y32, x_Q16); + y32 = Inlines.silk_SMLAWW(p[4], y32, x_Q16); + y32 = Inlines.silk_SMLAWW(p[3], y32, x_Q16); + y32 = Inlines.silk_SMLAWW(p[2], y32, x_Q16); + y32 = Inlines.silk_SMLAWW(p[1], y32, x_Q16); + y32 = Inlines.silk_SMLAWW(p[0], y32, x_Q16); + } + else + { + for (n = dd - 1; n >= 0; n--) + { + y32 = Inlines.silk_SMLAWW(p[n], y32, x_Q16); /* Q16 */ + } + } + + return y32; + } + + internal static void silk_A2NLSF_init( + int[] a_Q16, + int[] P, + int[] Q, + int dd) + { + int k; + + /* Convert filter coefs to even and odd polynomials */ + P[dd] = Inlines.silk_LSHIFT(1, 16); + Q[dd] = Inlines.silk_LSHIFT(1, 16); + for (k = 0; k < dd; k++) + { + P[k] = -a_Q16[dd - k - 1] - a_Q16[dd + k]; /* Q16 */ + Q[k] = -a_Q16[dd - k - 1] + a_Q16[dd + k]; /* Q16 */ + } + + /* Divide out zeros as we have that for even filter orders, */ + /* z = 1 is always a root in Q, and */ + /* z = -1 is always a root in P */ + for (k = dd; k > 0; k--) + { + P[k - 1] -= P[k]; + Q[k - 1] += Q[k]; + } + + /* Transform polynomials from cos(n*f) to cos(f)^n */ + silk_A2NLSF_trans_poly(P, dd); + silk_A2NLSF_trans_poly(Q, dd); + } + + /// + /// Compute Normalized Line Spectral Frequencies (NLSFs) from whitening filter coefficients + /// If not all roots are found, the a_Q16 coefficients are bandwidth expanded until convergence. + /// + /// (O) Normalized Line Spectral Frequencies in Q15 (0..2^15-1) [d] + /// (I/O) Monic whitening filter coefficients in Q16 [d] + /// (I) Filter order (must be even) + internal static void silk_A2NLSF(short[] NLSF, int[] a_Q16, int d) + { + int i, k, m, dd, root_ix, ffrac; + int xlo, xhi, xmid; + int ylo, yhi, ymid, thr; + int nom, den; + int[] P = new int[SilkConstants.SILK_MAX_ORDER_LPC / 2 + 1]; + int[] Q = new int[SilkConstants.SILK_MAX_ORDER_LPC / 2 + 1]; + int[][] PQ = new int[2][]; + int[] p; + + /* Store pointers to array */ + PQ[0] = P; + PQ[1] = Q; + + dd = Inlines.silk_RSHIFT(d, 1); + + silk_A2NLSF_init(a_Q16, P, Q, dd); + + /* Find roots, alternating between P and Q */ + p = P; /* Pointer to polynomial */ + + xlo = Tables.silk_LSFCosTab_Q12[0]; /* Q12*/ + ylo = silk_A2NLSF_eval_poly(p, xlo, dd); + + if (ylo < 0) + { + /* Set the first NLSF to zero and move on to the next */ + NLSF[0] = 0; + p = Q; /* Pointer to polynomial */ + ylo = silk_A2NLSF_eval_poly(p, xlo, dd); + root_ix = 1; /* Index of current root */ + } + else { + root_ix = 0; /* Index of current root */ + } + k = 1; /* Loop counter */ + i = 0; /* Counter for bandwidth expansions applied */ + thr = 0; + while (true) + { + /* Evaluate polynomial */ + xhi = Tables.silk_LSFCosTab_Q12[k]; /* Q12 */ + yhi = silk_A2NLSF_eval_poly(p, xhi, dd); + + /* Detect zero crossing */ + if ((ylo <= 0 && yhi >= thr) || (ylo >= 0 && yhi <= -thr)) + { + if (yhi == 0) + { + /* If the root lies exactly at the end of the current */ + /* interval, look for the next root in the next interval */ + thr = 1; + } + else { + thr = 0; + } + /* Binary division */ + ffrac = -256; + for (m = 0; m < BIN_DIV_STEPS_A2NLSF; m++) + { + /* Evaluate polynomial */ + xmid = Inlines.silk_RSHIFT_ROUND(xlo + xhi, 1); + ymid = silk_A2NLSF_eval_poly(p, xmid, dd); + + /* Detect zero crossing */ + if ((ylo <= 0 && ymid >= 0) || (ylo >= 0 && ymid <= 0)) + { + /* Reduce frequency */ + xhi = xmid; + yhi = ymid; + } + else { + /* Increase frequency */ + xlo = xmid; + ylo = ymid; + ffrac = Inlines.silk_ADD_RSHIFT(ffrac, 128, m); + } + } + + /* Interpolate */ + if (Inlines.silk_abs(ylo) < 65536) + { + /* Avoid dividing by zero */ + den = ylo - yhi; + nom = Inlines.silk_LSHIFT(ylo, 8 - BIN_DIV_STEPS_A2NLSF) + Inlines.silk_RSHIFT(den, 1); + if (den != 0) + { + ffrac += Inlines.silk_DIV32(nom, den); + } + } + else + { + /* No risk of dividing by zero because abs(ylo - yhi) >= abs(ylo) >= 65536 */ + ffrac += Inlines.silk_DIV32(ylo, Inlines.silk_RSHIFT(ylo - yhi, 8 - BIN_DIV_STEPS_A2NLSF)); + } + NLSF[root_ix] = (short)Inlines.silk_min_32(Inlines.silk_LSHIFT((int)k, 8) + ffrac, short.MaxValue); + + Inlines.OpusAssert(NLSF[root_ix] >= 0); + + root_ix++; /* Next root */ + if (root_ix >= d) + { + /* Found all roots */ + break; + } + + /* Alternate pointer to polynomial */ + p = PQ[root_ix & 1]; + + /* Evaluate polynomial */ + xlo = Tables.silk_LSFCosTab_Q12[k - 1]; /* Q12*/ + ylo = Inlines.silk_LSHIFT(1 - (root_ix & 2), 12); + } + else + { + /* Increment loop counter */ + k++; + xlo = xhi; + ylo = yhi; + thr = 0; + + if (k > SilkConstants.LSF_COS_TAB_SZ) + { + i++; + if (i > MAX_ITERATIONS_A2NLSF) + { + /* Set NLSFs to white spectrum and exit */ + NLSF[0] = (short)Inlines.silk_DIV32_16(1 << 15, (short)(d + 1)); + for (k = 1; k < d; k++) + { + NLSF[k] = (short)Inlines.silk_SMULBB(k + 1, NLSF[0]); + } + return; + } + + /* Error: Apply progressively more bandwidth expansion and run again */ + Filters.silk_bwexpander_32(a_Q16, d, 65536 - Inlines.silk_SMULBB(10 + i, i)); /* 10_Q16 = 0.00015*/ + + silk_A2NLSF_init(a_Q16, P, Q, dd); + p = P; /* Pointer to polynomial */ + xlo = Tables.silk_LSFCosTab_Q12[0]; /* Q12*/ + ylo = silk_A2NLSF_eval_poly(p, xlo, dd); + if (ylo < 0) + { + /* Set the first NLSF to zero and move on to the next */ + NLSF[0] = 0; + p = Q; /* Pointer to polynomial */ + ylo = silk_A2NLSF_eval_poly(p, xlo, dd); + root_ix = 1; /* Index of current root */ + } + else { + root_ix = 0; /* Index of current root */ + } + k = 1; /* Reset loop counter */ + } + } + } + } + + /// + /// Limit, stabilize, convert and quantize NLSFs + /// + /// I/O Encoder state + /// O Prediction coefficients [ 2 ][MAX_LPC_ORDER] + /// I/O Normalized LSFs (quant out) (0 - (2^15-1)) [MAX_LPC_ORDER] + /// I Previous Normalized LSFs (0 - (2^15-1)) [MAX_LPC_ORDER] + internal static void silk_process_NLSFs( + SilkChannelEncoder psEncC, + short[][] PredCoef_Q12, + short[] pNLSF_Q15, + short[] prev_NLSFq_Q15) + { + int i; + bool doInterpolate; + int NLSF_mu_Q20; + int i_sqr_Q15; + short[] pNLSF0_temp_Q15 = new short[SilkConstants.MAX_LPC_ORDER]; + short[] pNLSFW_QW = new short[SilkConstants.MAX_LPC_ORDER]; + short[] pNLSFW0_temp_QW = new short[SilkConstants.MAX_LPC_ORDER]; + + Inlines.OpusAssert(psEncC.speech_activity_Q8 >= 0); + Inlines.OpusAssert(psEncC.speech_activity_Q8 <= ((int)((1.0f) * ((long)1 << (8)) + 0.5))/*Inlines.SILK_CONST(1.0f, 8)*/); + Inlines.OpusAssert(psEncC.useInterpolatedNLSFs == 1 || psEncC.indices.NLSFInterpCoef_Q2 == (1 << 2)); + + /***********************/ + /* Calculate mu values */ + /***********************/ + /* NLSF_mu = 0.003 - 0.0015 * psEnc.speech_activity; */ + NLSF_mu_Q20 = Inlines.silk_SMLAWB(((int)((0.003f) * ((long)1 << (20)) + 0.5))/*Inlines.SILK_CONST(0.003f, 20)*/, ((int)((-0.001f) * ((long)1 << (28)) + 0.5))/*Inlines.SILK_CONST(-0.001f, 28)*/, psEncC.speech_activity_Q8); + if (psEncC.nb_subfr == 2) + { + /* Multiply by 1.5 for 10 ms packets */ + NLSF_mu_Q20 = Inlines.silk_ADD_RSHIFT(NLSF_mu_Q20, NLSF_mu_Q20, 1); + } + + Inlines.OpusAssert(NLSF_mu_Q20 > 0); + Inlines.OpusAssert(NLSF_mu_Q20 <= ((int)((0.005f) * ((long)1 << (20)) + 0.5))/*Inlines.SILK_CONST(0.005f, 20)*/); + + /* Calculate NLSF weights */ + silk_NLSF_VQ_weights_laroia(pNLSFW_QW, pNLSF_Q15, psEncC.predictLPCOrder); + + /* Update NLSF weights for interpolated NLSFs */ + doInterpolate = (psEncC.useInterpolatedNLSFs == 1) && (psEncC.indices.NLSFInterpCoef_Q2 < 4); + if (doInterpolate) + { + /* Calculate the interpolated NLSF vector for the first half */ + Inlines.silk_interpolate(pNLSF0_temp_Q15, prev_NLSFq_Q15, pNLSF_Q15, + psEncC.indices.NLSFInterpCoef_Q2, psEncC.predictLPCOrder); + + /* Calculate first half NLSF weights for the interpolated NLSFs */ + silk_NLSF_VQ_weights_laroia(pNLSFW0_temp_QW, pNLSF0_temp_Q15, psEncC.predictLPCOrder); + + /* Update NLSF weights with contribution from first half */ + i_sqr_Q15 = Inlines.silk_LSHIFT(Inlines.silk_SMULBB(psEncC.indices.NLSFInterpCoef_Q2, psEncC.indices.NLSFInterpCoef_Q2), 11); + + for (i = 0; i < psEncC.predictLPCOrder; i++) + { + pNLSFW_QW[i] = (short)(Inlines.silk_SMLAWB(Inlines.silk_RSHIFT(pNLSFW_QW[i], 1), (int)pNLSFW0_temp_QW[i], i_sqr_Q15)); + Inlines.OpusAssert(pNLSFW_QW[i] >= 1); + } + } + + ////////////////////////////////////////////////////////////////////////// + + silk_NLSF_encode(psEncC.indices.NLSFIndices, pNLSF_Q15, psEncC.psNLSF_CB, pNLSFW_QW, + NLSF_mu_Q20, psEncC.NLSF_MSVQ_Survivors, psEncC.indices.signalType); + + /* Convert quantized NLSFs back to LPC coefficients */ + silk_NLSF2A(PredCoef_Q12[1], pNLSF_Q15, psEncC.predictLPCOrder); + + if (doInterpolate) + { + /* Calculate the interpolated, quantized LSF vector for the first half */ + Inlines.silk_interpolate(pNLSF0_temp_Q15, prev_NLSFq_Q15, pNLSF_Q15, + psEncC.indices.NLSFInterpCoef_Q2, psEncC.predictLPCOrder); + + /* Convert back to LPC coefficients */ + silk_NLSF2A(PredCoef_Q12[0], pNLSF0_temp_Q15, psEncC.predictLPCOrder); + + } + else + { + /* Copy LPC coefficients for first half from second half */ + Array.Copy(PredCoef_Q12[1], 0, PredCoef_Q12[0], 0, psEncC.predictLPCOrder); + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/NoiseShapeAnalysis.cs b/Libraries/Concentus/CSharp/Concentus/Silk/NoiseShapeAnalysis.cs new file mode 100644 index 000000000..cddfd0e11 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/NoiseShapeAnalysis.cs @@ -0,0 +1,498 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System; + using System.Diagnostics; + + internal static class NoiseShapeAnalysis + { + /* Compute gain to make warped filter coefficients have a zero mean log frequency response on a */ + /* non-warped frequency scale. (So that it can be implemented with a minimum-phase monic filter.) */ + /* Note: A monic filter is one with the first coefficient equal to 1.0. In Silk we omit the first */ + /* coefficient in an array of coefficients, for monic filters. */ + internal static int warped_gain( /* gain in Q16*/ + int[] coefs_Q24, + int lambda_Q16, + int order + ) + { + int i; + int gain_Q24; + + lambda_Q16 = -lambda_Q16; + gain_Q24 = coefs_Q24[order - 1]; + for (i = order - 2; i >= 0; i--) + { + gain_Q24 = Inlines.silk_SMLAWB(coefs_Q24[i], gain_Q24, lambda_Q16); + } + gain_Q24 = Inlines.silk_SMLAWB(((int)((1.0f) * ((long)1 << (24)) + 0.5))/*Inlines.SILK_CONST(1.0f, 24)*/, gain_Q24, -lambda_Q16); + return Inlines.silk_INVERSE32_varQ(gain_Q24, 40); + } + + /* Convert warped filter coefficients to monic pseudo-warped coefficients and limit maximum */ + /* amplitude of monic warped coefficients by using bandwidth expansion on the true coefficients */ + internal static void limit_warped_coefs( + int[] coefs_syn_Q24, + int[] coefs_ana_Q24, + int lambda_Q16, + int limit_Q24, + int order + ) + { + int i, iter, ind = 0; + int tmp, maxabs_Q24, chirp_Q16, gain_syn_Q16, gain_ana_Q16; + int nom_Q16, den_Q24; + + /* Convert to monic coefficients */ + lambda_Q16 = -lambda_Q16; + for (i = order - 1; i > 0; i--) + { + coefs_syn_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_syn_Q24[i - 1], coefs_syn_Q24[i], lambda_Q16); + coefs_ana_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_ana_Q24[i - 1], coefs_ana_Q24[i], lambda_Q16); + } + lambda_Q16 = -lambda_Q16; + nom_Q16 = Inlines.silk_SMLAWB(((int)((1.0f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/, -(int)lambda_Q16, lambda_Q16); + den_Q24 = Inlines.silk_SMLAWB(((int)((1.0f) * ((long)1 << (24)) + 0.5))/*Inlines.SILK_CONST(1.0f, 24)*/, coefs_syn_Q24[0], lambda_Q16); + gain_syn_Q16 = Inlines.silk_DIV32_varQ(nom_Q16, den_Q24, 24); + den_Q24 = Inlines.silk_SMLAWB(((int)((1.0f) * ((long)1 << (24)) + 0.5))/*Inlines.SILK_CONST(1.0f, 24)*/, coefs_ana_Q24[0], lambda_Q16); + gain_ana_Q16 = Inlines.silk_DIV32_varQ(nom_Q16, den_Q24, 24); + for (i = 0; i < order; i++) + { + coefs_syn_Q24[i] = Inlines.silk_SMULWW(gain_syn_Q16, coefs_syn_Q24[i]); + coefs_ana_Q24[i] = Inlines.silk_SMULWW(gain_ana_Q16, coefs_ana_Q24[i]); + } + + for (iter = 0; iter < 10; iter++) + { + /* Find maximum absolute value */ + maxabs_Q24 = -1; + for (i = 0; i < order; i++) + { + tmp = Inlines.silk_max(Inlines.silk_abs_int32(coefs_syn_Q24[i]), Inlines.silk_abs_int32(coefs_ana_Q24[i])); + if (tmp > maxabs_Q24) + { + maxabs_Q24 = tmp; + ind = i; + } + } + if (maxabs_Q24 <= limit_Q24) + { + /* Coefficients are within range - done */ + return; + } + + /* Convert back to true warped coefficients */ + for (i = 1; i < order; i++) + { + coefs_syn_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_syn_Q24[i - 1], coefs_syn_Q24[i], lambda_Q16); + coefs_ana_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_ana_Q24[i - 1], coefs_ana_Q24[i], lambda_Q16); + } + gain_syn_Q16 = Inlines.silk_INVERSE32_varQ(gain_syn_Q16, 32); + gain_ana_Q16 = Inlines.silk_INVERSE32_varQ(gain_ana_Q16, 32); + for (i = 0; i < order; i++) + { + coefs_syn_Q24[i] = Inlines.silk_SMULWW(gain_syn_Q16, coefs_syn_Q24[i]); + coefs_ana_Q24[i] = Inlines.silk_SMULWW(gain_ana_Q16, coefs_ana_Q24[i]); + } + + /* Apply bandwidth expansion */ + chirp_Q16 = ((int)((0.99f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.99f, 16)*/ - Inlines.silk_DIV32_varQ( + Inlines.silk_SMULWB(maxabs_Q24 - limit_Q24, Inlines.silk_SMLABB(((int)((0.8f) * ((long)1 << (10)) + 0.5))/*Inlines.SILK_CONST(0.8f, 10)*/, ((int)((0.1f) * ((long)1 << (10)) + 0.5))/*Inlines.SILK_CONST(0.1f, 10)*/, iter)), + Inlines.silk_MUL(maxabs_Q24, ind + 1), 22); + BWExpander.silk_bwexpander_32(coefs_syn_Q24, order, chirp_Q16); + BWExpander.silk_bwexpander_32(coefs_ana_Q24, order, chirp_Q16); + + /* Convert to monic warped coefficients */ + lambda_Q16 = -lambda_Q16; + for (i = order - 1; i > 0; i--) + { + coefs_syn_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_syn_Q24[i - 1], coefs_syn_Q24[i], lambda_Q16); + coefs_ana_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_ana_Q24[i - 1], coefs_ana_Q24[i], lambda_Q16); + } + lambda_Q16 = -lambda_Q16; + nom_Q16 = Inlines.silk_SMLAWB(((int)((1.0f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/, -(int)lambda_Q16, lambda_Q16); + den_Q24 = Inlines.silk_SMLAWB(((int)((1.0f) * ((long)1 << (24)) + 0.5))/*Inlines.SILK_CONST(1.0f, 24)*/, coefs_syn_Q24[0], lambda_Q16); + gain_syn_Q16 = Inlines.silk_DIV32_varQ(nom_Q16, den_Q24, 24); + den_Q24 = Inlines.silk_SMLAWB(((int)((1.0f) * ((long)1 << (24)) + 0.5))/*Inlines.SILK_CONST(1.0f, 24)*/, coefs_ana_Q24[0], lambda_Q16); + gain_ana_Q16 = Inlines.silk_DIV32_varQ(nom_Q16, den_Q24, 24); + for (i = 0; i < order; i++) + { + coefs_syn_Q24[i] = Inlines.silk_SMULWW(gain_syn_Q16, coefs_syn_Q24[i]); + coefs_ana_Q24[i] = Inlines.silk_SMULWW(gain_ana_Q16, coefs_ana_Q24[i]); + } + } + Inlines.OpusAssert(false); + } + + /**************************************************************/ + /* Compute noise shaping coefficients and initial gain values */ + /**************************************************************/ + internal static void silk_noise_shape_analysis( + SilkChannelEncoder psEnc, /* I/O Encoder state FIX */ + SilkEncoderControl psEncCtrl, /* I/O Encoder control FIX */ + short[] pitch_res, /* I LPC residual from pitch analysis */ + int pitch_res_ptr, + short[] x, /* I Input signal [ frame_length + la_shape ] */ + int x_ptr + ) + { + SilkShapeState psShapeSt = psEnc.sShape; + int k, i, nSamples, Qnrg, b_Q14, warping_Q16, scale = 0; + int SNR_adj_dB_Q7, HarmBoost_Q16, HarmShapeGain_Q16, Tilt_Q16, tmp32; + int nrg, pre_nrg_Q30, log_energy_Q7, log_energy_prev_Q7, energy_variation_Q7; + int delta_Q16, BWExp1_Q16, BWExp2_Q16, gain_mult_Q16, gain_add_Q16, strength_Q16, b_Q8; + int[] auto_corr = new int[SilkConstants.MAX_SHAPE_LPC_ORDER + 1]; + int[] refl_coef_Q16 = new int[SilkConstants.MAX_SHAPE_LPC_ORDER]; + int[] AR1_Q24 = new int[SilkConstants.MAX_SHAPE_LPC_ORDER]; + int[] AR2_Q24 = new int[SilkConstants.MAX_SHAPE_LPC_ORDER]; + short[] x_windowed; + int pitch_res_ptr2; + int x_ptr2; + + /* Point to start of first LPC analysis block */ + x_ptr2 = x_ptr - psEnc.la_shape; + + /****************/ + /* GAIN CONTROL */ + /****************/ + SNR_adj_dB_Q7 = psEnc.SNR_dB_Q7; + + /* Input quality is the average of the quality in the lowest two VAD bands */ + psEncCtrl.input_quality_Q14 = (int)Inlines.silk_RSHIFT((int)psEnc.input_quality_bands_Q15[0] + + psEnc.input_quality_bands_Q15[1], 2); + + /* Coding quality level, between 0.0_Q0 and 1.0_Q0, but in Q14 */ + psEncCtrl.coding_quality_Q14 = Inlines.silk_RSHIFT(Sigmoid.silk_sigm_Q15(Inlines.silk_RSHIFT_ROUND(SNR_adj_dB_Q7 - + ((int)((20.0f) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(20.0f, 7)*/, 4)), 1); + + /* Reduce coding SNR during low speech activity */ + if (psEnc.useCBR == 0) + { + b_Q8 = ((int)((1.0f) * ((long)1 << (8)) + 0.5))/*Inlines.SILK_CONST(1.0f, 8)*/ - psEnc.speech_activity_Q8; + b_Q8 = Inlines.silk_SMULWB(Inlines.silk_LSHIFT(b_Q8, 8), b_Q8); + SNR_adj_dB_Q7 = Inlines.silk_SMLAWB(SNR_adj_dB_Q7, + Inlines.silk_SMULBB(((int)((0 - TuningParameters.BG_SNR_DECR_dB) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(0 - TuningParameters.BG_SNR_DECR_dB, 7)*/ >> (4 + 1), b_Q8), /* Q11*/ + Inlines.silk_SMULWB(((int)((1.0f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(1.0f, 14)*/ + psEncCtrl.input_quality_Q14, psEncCtrl.coding_quality_Q14)); /* Q12*/ + } + + if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED) + { + /* Reduce gains for periodic signals */ + SNR_adj_dB_Q7 = Inlines.silk_SMLAWB(SNR_adj_dB_Q7, ((int)((TuningParameters.HARM_SNR_INCR_dB) * ((long)1 << (8)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HARM_SNR_INCR_dB, 8)*/, psEnc.LTPCorr_Q15); + } + else { + /* For unvoiced signals and low-quality input, adjust the quality slower than SNR_dB setting */ + SNR_adj_dB_Q7 = Inlines.silk_SMLAWB(SNR_adj_dB_Q7, + Inlines.silk_SMLAWB(((int)((6.0f) * ((long)1 << (9)) + 0.5))/*Inlines.SILK_CONST(6.0f, 9)*/, -((int)((0.4f) * ((long)1 << (18)) + 0.5))/*Inlines.SILK_CONST(0.4f, 18)*/, psEnc.SNR_dB_Q7), + ((int)((1.0f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(1.0f, 14)*/ - psEncCtrl.input_quality_Q14); + } + + /*************************/ + /* SPARSENESS PROCESSING */ + /*************************/ + /* Set quantizer offset */ + if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED) + { + /* Initially set to 0; may be overruled in process_gains(..) */ + psEnc.indices.quantOffsetType = 0; + psEncCtrl.sparseness_Q8 = 0; + } + else { + /* Sparseness measure, based on relative fluctuations of energy per 2 milliseconds */ + nSamples = Inlines.silk_LSHIFT(psEnc.fs_kHz, 1); + energy_variation_Q7 = 0; + log_energy_prev_Q7 = 0; + pitch_res_ptr2 = pitch_res_ptr; + for (k = 0; k < Inlines.silk_SMULBB(SilkConstants.SUB_FRAME_LENGTH_MS, psEnc.nb_subfr) / 2; k++) + { + SumSqrShift.silk_sum_sqr_shift(out nrg, out scale, pitch_res, pitch_res_ptr2, nSamples); + nrg += Inlines.silk_RSHIFT(nSamples, scale); /* Q(-scale)*/ + + log_energy_Q7 = Inlines.silk_lin2log(nrg); + if (k > 0) + { + energy_variation_Q7 += Inlines.silk_abs(log_energy_Q7 - log_energy_prev_Q7); + } + log_energy_prev_Q7 = log_energy_Q7; + pitch_res_ptr2 += nSamples; + } + + psEncCtrl.sparseness_Q8 = Inlines.silk_RSHIFT(Sigmoid.silk_sigm_Q15(Inlines.silk_SMULWB(energy_variation_Q7 - + ((int)((5.0f) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(5.0f, 7)*/, ((int)((0.1f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.1f, 16)*/)), 7); + + /* Set quantization offset depending on sparseness measure */ + if (psEncCtrl.sparseness_Q8 > ((int)((TuningParameters.SPARSENESS_THRESHOLD_QNT_OFFSET) * ((long)1 << (8)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SPARSENESS_THRESHOLD_QNT_OFFSET, 8)*/) + { + psEnc.indices.quantOffsetType = 0; + } + else { + psEnc.indices.quantOffsetType = 1; + } + + /* Increase coding SNR for sparse signals */ + SNR_adj_dB_Q7 = Inlines.silk_SMLAWB(SNR_adj_dB_Q7, ((int)((TuningParameters.SPARSE_SNR_INCR_dB) * ((long)1 << (15)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SPARSE_SNR_INCR_dB, 15)*/, psEncCtrl.sparseness_Q8 - ((int)((0.5f) * ((long)1 << (8)) + 0.5))/*Inlines.SILK_CONST(0.5f, 8)*/); + } + + /*******************************/ + /* Control bandwidth expansion */ + /*******************************/ + /* More BWE for signals with high prediction gain */ + strength_Q16 = Inlines.silk_SMULWB(psEncCtrl.predGain_Q16, ((int)((TuningParameters.FIND_PITCH_WHITE_NOISE_FRACTION) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_PITCH_WHITE_NOISE_FRACTION, 16)*/); + BWExp1_Q16 = BWExp2_Q16 = Inlines.silk_DIV32_varQ(((int)((TuningParameters.BANDWIDTH_EXPANSION) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.BANDWIDTH_EXPANSION, 16)*/, + Inlines.silk_SMLAWW(((int)((1.0f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/, strength_Q16, strength_Q16), 16); + delta_Q16 = Inlines.silk_SMULWB(((int)((1.0f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/ - Inlines.silk_SMULBB(3, psEncCtrl.coding_quality_Q14), + ((int)((TuningParameters.LOW_RATE_BANDWIDTH_EXPANSION_DELTA) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LOW_RATE_BANDWIDTH_EXPANSION_DELTA, 16)*/); + BWExp1_Q16 = Inlines.silk_SUB32(BWExp1_Q16, delta_Q16); + BWExp2_Q16 = Inlines.silk_ADD32(BWExp2_Q16, delta_Q16); + /* BWExp1 will be applied after BWExp2, so make it relative */ + BWExp1_Q16 = Inlines.silk_DIV32_16(Inlines.silk_LSHIFT(BWExp1_Q16, 14), Inlines.silk_RSHIFT(BWExp2_Q16, 2)); + + if (psEnc.warping_Q16 > 0) + { + /* Slightly more warping in analysis will move quantization noise up in frequency, where it's better masked */ + warping_Q16 = Inlines.silk_SMLAWB(psEnc.warping_Q16, (int)psEncCtrl.coding_quality_Q14, ((int)((0.01f) * ((long)1 << (18)) + 0.5))/*Inlines.SILK_CONST(0.01f, 18)*/); + } + else { + warping_Q16 = 0; + } + + /********************************************/ + /* Compute noise shaping AR coefs and gains */ + /********************************************/ + x_windowed = new short[psEnc.shapeWinLength]; + for (k = 0; k < psEnc.nb_subfr; k++) + { + /* Apply window: sine slope followed by flat part followed by cosine slope */ + int shift, slope_part, flat_part; + flat_part = psEnc.fs_kHz * 3; + slope_part = Inlines.silk_RSHIFT(psEnc.shapeWinLength - flat_part, 1); + + ApplySineWindow.silk_apply_sine_window(x_windowed, 0, x, x_ptr2, 1, slope_part); + shift = slope_part; + Array.Copy(x, x_ptr2 + shift, x_windowed, shift, flat_part); + shift += flat_part; + ApplySineWindow.silk_apply_sine_window(x_windowed, shift, x, x_ptr2 + shift, 2, slope_part); + + /* Update pointer: next LPC analysis block */ + x_ptr2 += psEnc.subfr_length; + BoxedValueInt scale_boxed = new BoxedValueInt(scale); + if (psEnc.warping_Q16 > 0) + { + /* Calculate warped auto correlation */ + Autocorrelation.silk_warped_autocorrelation(auto_corr, scale_boxed, x_windowed, warping_Q16, psEnc.shapeWinLength, psEnc.shapingLPCOrder); + } + else { + /* Calculate regular auto correlation */ + Autocorrelation.silk_autocorr(auto_corr, scale_boxed, x_windowed, psEnc.shapeWinLength, psEnc.shapingLPCOrder + 1); + } + scale = scale_boxed.Val; + + /* Add white noise, as a fraction of energy */ + auto_corr[0] = Inlines.silk_ADD32(auto_corr[0], Inlines.silk_max_32(Inlines.silk_SMULWB(Inlines.silk_RSHIFT(auto_corr[0], 4), + ((int)((TuningParameters.SHAPE_WHITE_NOISE_FRACTION) * ((long)1 << (20)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SHAPE_WHITE_NOISE_FRACTION, 20)*/), 1)); + + /* Calculate the reflection coefficients using schur */ + nrg = Schur.silk_schur64(refl_coef_Q16, auto_corr, psEnc.shapingLPCOrder); + Inlines.OpusAssert(nrg >= 0); + + /* Convert reflection coefficients to prediction coefficients */ + K2A.silk_k2a_Q16(AR2_Q24, refl_coef_Q16, psEnc.shapingLPCOrder); + + Qnrg = -scale; /* range: -12...30*/ + Inlines.OpusAssert(Qnrg >= -12); + Inlines.OpusAssert(Qnrg <= 30); + + /* Make sure that Qnrg is an even number */ + if ((Qnrg & 1) != 0) + { + Qnrg -= 1; + nrg >>= 1; + } + + tmp32 = Inlines.silk_SQRT_APPROX(nrg); + Qnrg >>= 1; /* range: -6...15*/ + + psEncCtrl.Gains_Q16[k] = Inlines.silk_LSHIFT_SAT32(tmp32, 16 - Qnrg); + + if (psEnc.warping_Q16 > 0) + { + /* Adjust gain for warping */ + gain_mult_Q16 = warped_gain(AR2_Q24, warping_Q16, psEnc.shapingLPCOrder); + Inlines.OpusAssert(psEncCtrl.Gains_Q16[k] >= 0); + if (Inlines.silk_SMULWW(Inlines.silk_RSHIFT_ROUND(psEncCtrl.Gains_Q16[k], 1), gain_mult_Q16) >= (int.MaxValue >> 1)) + { + psEncCtrl.Gains_Q16[k] = int.MaxValue; + } + else { + psEncCtrl.Gains_Q16[k] = Inlines.silk_SMULWW(psEncCtrl.Gains_Q16[k], gain_mult_Q16); + } + } + + /* Bandwidth expansion for synthesis filter shaping */ + BWExpander.silk_bwexpander_32(AR2_Q24, psEnc.shapingLPCOrder, BWExp2_Q16); + + /* Compute noise shaping filter coefficients */ + Array.Copy(AR2_Q24, AR1_Q24, psEnc.shapingLPCOrder); + + /* Bandwidth expansion for analysis filter shaping */ + Inlines.OpusAssert(BWExp1_Q16 <= ((int)((1.0f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/); + BWExpander.silk_bwexpander_32(AR1_Q24, psEnc.shapingLPCOrder, BWExp1_Q16); + + /* Ratio of prediction gains, in energy domain */ + pre_nrg_Q30 = LPCInversePredGain.silk_LPC_inverse_pred_gain_Q24(AR2_Q24, psEnc.shapingLPCOrder); + nrg = LPCInversePredGain.silk_LPC_inverse_pred_gain_Q24(AR1_Q24, psEnc.shapingLPCOrder); + + /*psEncCtrl.GainsPre[ k ] = 1.0f - 0.7f * ( 1.0f - pre_nrg / nrg ) = 0.3f + 0.7f * pre_nrg / nrg;*/ + pre_nrg_Q30 = Inlines.silk_LSHIFT32(Inlines.silk_SMULWB(pre_nrg_Q30, ((int)((0.7f) * ((long)1 << (15)) + 0.5))/*Inlines.SILK_CONST(0.7f, 15)*/), 1); + psEncCtrl.GainsPre_Q14[k] = (int)((int)((0.3f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(0.3f, 14)*/ + Inlines.silk_DIV32_varQ(pre_nrg_Q30, nrg, 14); + + /* Convert to monic warped prediction coefficients and limit absolute values */ + limit_warped_coefs(AR2_Q24, AR1_Q24, warping_Q16, ((int)((3.999f) * ((long)1 << (24)) + 0.5))/*Inlines.SILK_CONST(3.999f, 24)*/, psEnc.shapingLPCOrder); + + /* Convert from Q24 to Q13 and store in int16 */ + for (i = 0; i < psEnc.shapingLPCOrder; i++) + { + psEncCtrl.AR1_Q13[k * SilkConstants.MAX_SHAPE_LPC_ORDER + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(AR1_Q24[i], 11)); + psEncCtrl.AR2_Q13[k * SilkConstants.MAX_SHAPE_LPC_ORDER + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(AR2_Q24[i], 11)); + } + } + + /*****************/ + /* Gain tweaking */ + /*****************/ + /* Increase gains during low speech activity and put lower limit on gains */ + gain_mult_Q16 = Inlines.silk_log2lin(-Inlines.silk_SMLAWB(-((int)((16.0f) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(16.0f, 7)*/, SNR_adj_dB_Q7, ((int)((0.16f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.16f, 16)*/)); + gain_add_Q16 = Inlines.silk_log2lin(Inlines.silk_SMLAWB(((int)((16.0f) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(16.0f, 7)*/, ((int)((SilkConstants.MIN_QGAIN_DB) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.MIN_QGAIN_DB, 7)*/, ((int)((0.16f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.16f, 16)*/)); + Inlines.OpusAssert(gain_mult_Q16 > 0); + for (k = 0; k < psEnc.nb_subfr; k++) + { + psEncCtrl.Gains_Q16[k] = Inlines.silk_SMULWW(psEncCtrl.Gains_Q16[k], gain_mult_Q16); + Inlines.OpusAssert(psEncCtrl.Gains_Q16[k] >= 0); + psEncCtrl.Gains_Q16[k] = Inlines.silk_ADD_POS_SAT32(psEncCtrl.Gains_Q16[k], gain_add_Q16); + } + + gain_mult_Q16 = ((int)((1.0f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/ + Inlines.silk_RSHIFT_ROUND(Inlines.silk_MLA(((int)((TuningParameters.INPUT_TILT) * ((long)1 << (26)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.INPUT_TILT, 26)*/, + psEncCtrl.coding_quality_Q14, ((int)((TuningParameters.HIGH_RATE_INPUT_TILT) * ((long)1 << (12)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HIGH_RATE_INPUT_TILT, 12)*/), 10); + for (k = 0; k < psEnc.nb_subfr; k++) + { + psEncCtrl.GainsPre_Q14[k] = Inlines.silk_SMULWB(gain_mult_Q16, psEncCtrl.GainsPre_Q14[k]); + } + + /************************************************/ + /* Control low-frequency shaping and noise tilt */ + /************************************************/ + /* Less low frequency shaping for noisy inputs */ + strength_Q16 = Inlines.silk_MUL(((int)((TuningParameters.LOW_FREQ_SHAPING) * ((long)1 << (4)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LOW_FREQ_SHAPING, 4)*/, Inlines.silk_SMLAWB(((int)((1.0f) * ((long)1 << (12)) + 0.5))/*Inlines.SILK_CONST(1.0f, 12)*/, + ((int)((TuningParameters.LOW_QUALITY_LOW_FREQ_SHAPING_DECR) * ((long)1 << (13)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LOW_QUALITY_LOW_FREQ_SHAPING_DECR, 13)*/, psEnc.input_quality_bands_Q15[0] - ((int)((1.0f) * ((long)1 << (15)) + 0.5))/*Inlines.SILK_CONST(1.0f, 15)*/)); + strength_Q16 = Inlines.silk_RSHIFT(Inlines.silk_MUL(strength_Q16, psEnc.speech_activity_Q8), 8); + if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED) + { + /* Reduce low frequencies quantization noise for periodic signals, depending on pitch lag */ + /*f = 400; freqz([1, -0.98 + 2e-4 * f], [1, -0.97 + 7e-4 * f], 2^12, Fs); axis([0, 1000, -10, 1])*/ + int fs_kHz_inv = Inlines.silk_DIV32_16(((int)((0.2f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(0.2f, 14)*/, psEnc.fs_kHz); + for (k = 0; k < psEnc.nb_subfr; k++) + { + b_Q14 = fs_kHz_inv + Inlines.silk_DIV32_16(((int)((3.0f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(3.0f, 14)*/, psEncCtrl.pitchL[k]); + /* Pack two coefficients in one int32 */ + psEncCtrl.LF_shp_Q14[k] = Inlines.silk_LSHIFT(((int)((1.0f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(1.0f, 14)*/ - b_Q14 - Inlines.silk_SMULWB(strength_Q16, b_Q14), 16); + psEncCtrl.LF_shp_Q14[k] |= (b_Q14 - ((int)((1.0f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(1.0f, 14)*/) & 0xFFFF; // opus bug: again, cast to ushort was done here where bitwise masking was intended + } + Inlines.OpusAssert(((int)((TuningParameters.HARM_HP_NOISE_COEF) * ((long)1 << (24)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HARM_HP_NOISE_COEF, 24)*/ < ((int)((0.5f) * ((long)1 << (24)) + 0.5))/*Inlines.SILK_CONST(0.5f, 24)*/); /* Guarantees that second argument to SMULWB() is within range of an short*/ + Tilt_Q16 = -((int)((TuningParameters.HP_NOISE_COEF) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HP_NOISE_COEF, 16)*/ - + Inlines.silk_SMULWB(((int)((1.0f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/ - ((int)((TuningParameters.HP_NOISE_COEF) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HP_NOISE_COEF, 16)*/, + Inlines.silk_SMULWB(((int)((TuningParameters.HARM_HP_NOISE_COEF) * ((long)1 << (24)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HARM_HP_NOISE_COEF, 24)*/, psEnc.speech_activity_Q8)); + } + else { + b_Q14 = Inlines.silk_DIV32_16(21299, psEnc.fs_kHz); /* 1.3_Q0 = 21299_Q14*/ + /* Pack two coefficients in one int32 */ + psEncCtrl.LF_shp_Q14[0] = Inlines.silk_LSHIFT(((int)((1.0f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(1.0f, 14)*/ - b_Q14 - + Inlines.silk_SMULWB(strength_Q16, Inlines.silk_SMULWB(((int)((0.6f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.6f, 16)*/, b_Q14)), 16); + psEncCtrl.LF_shp_Q14[0] |= (b_Q14 - ((int)((1.0f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(1.0f, 14)*/) & 0xFFFF; // opus bug: cast to ushort is better expressed as a bitwise operator, otherwise runtime analysis might flag it as an overflow error + for (k = 1; k < psEnc.nb_subfr; k++) + { + psEncCtrl.LF_shp_Q14[k] = psEncCtrl.LF_shp_Q14[0]; + } + Tilt_Q16 = -((int)((TuningParameters.HP_NOISE_COEF) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HP_NOISE_COEF, 16)*/; + } + + /****************************/ + /* HARMONIC SHAPING CONTROL */ + /****************************/ + /* Control boosting of harmonic frequencies */ + HarmBoost_Q16 = Inlines.silk_SMULWB(Inlines.silk_SMULWB(((int)((1.0f) * ((long)1 << (17)) + 0.5))/*Inlines.SILK_CONST(1.0f, 17)*/ - Inlines.silk_LSHIFT(psEncCtrl.coding_quality_Q14, 3), + psEnc.LTPCorr_Q15), ((int)((TuningParameters.LOW_RATE_HARMONIC_BOOST) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LOW_RATE_HARMONIC_BOOST, 16)*/); + + /* More harmonic boost for noisy input signals */ + HarmBoost_Q16 = Inlines.silk_SMLAWB(HarmBoost_Q16, + ((int)((1.0f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/ - Inlines.silk_LSHIFT(psEncCtrl.input_quality_Q14, 2), ((int)((TuningParameters.LOW_INPUT_QUALITY_HARMONIC_BOOST) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LOW_INPUT_QUALITY_HARMONIC_BOOST, 16)*/); + + if (SilkConstants.USE_HARM_SHAPING != 0 && psEnc.indices.signalType == SilkConstants.TYPE_VOICED) + { + /* More harmonic noise shaping for high bitrates or noisy input */ + HarmShapeGain_Q16 = Inlines.silk_SMLAWB(((int)((TuningParameters.HARMONIC_SHAPING) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HARMONIC_SHAPING, 16)*/, + ((int)((1.0f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/ - Inlines.silk_SMULWB(((int)((1.0f) * ((long)1 << (18)) + 0.5))/*Inlines.SILK_CONST(1.0f, 18)*/ - Inlines.silk_LSHIFT(psEncCtrl.coding_quality_Q14, 4), + psEncCtrl.input_quality_Q14), ((int)((TuningParameters.HIGH_RATE_OR_LOW_QUALITY_HARMONIC_SHAPING) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HIGH_RATE_OR_LOW_QUALITY_HARMONIC_SHAPING, 16)*/); + + /* Less harmonic noise shaping for less periodic signals */ + HarmShapeGain_Q16 = Inlines.silk_SMULWB(Inlines.silk_LSHIFT(HarmShapeGain_Q16, 1), + Inlines.silk_SQRT_APPROX(Inlines.silk_LSHIFT(psEnc.LTPCorr_Q15, 15))); + } + else { + HarmShapeGain_Q16 = 0; + } + + /*************************/ + /* Smooth over subframes */ + /*************************/ + for (k = 0; k < SilkConstants.MAX_NB_SUBFR; k++) + { + psShapeSt.HarmBoost_smth_Q16 = + Inlines.silk_SMLAWB(psShapeSt.HarmBoost_smth_Q16, HarmBoost_Q16 - psShapeSt.HarmBoost_smth_Q16, ((int)((TuningParameters.SUBFR_SMTH_COEF) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SUBFR_SMTH_COEF, 16)*/); + psShapeSt.HarmShapeGain_smth_Q16 = + Inlines.silk_SMLAWB(psShapeSt.HarmShapeGain_smth_Q16, HarmShapeGain_Q16 - psShapeSt.HarmShapeGain_smth_Q16, ((int)((TuningParameters.SUBFR_SMTH_COEF) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SUBFR_SMTH_COEF, 16)*/); + psShapeSt.Tilt_smth_Q16 = + Inlines.silk_SMLAWB(psShapeSt.Tilt_smth_Q16, Tilt_Q16 - psShapeSt.Tilt_smth_Q16, ((int)((TuningParameters.SUBFR_SMTH_COEF) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SUBFR_SMTH_COEF, 16)*/); + + psEncCtrl.HarmBoost_Q14[k] = (int)Inlines.silk_RSHIFT_ROUND(psShapeSt.HarmBoost_smth_Q16, 2); + psEncCtrl.HarmShapeGain_Q14[k] = (int)Inlines.silk_RSHIFT_ROUND(psShapeSt.HarmShapeGain_smth_Q16, 2); + psEncCtrl.Tilt_Q14[k] = (int)Inlines.silk_RSHIFT_ROUND(psShapeSt.Tilt_smth_Q16, 2); + } + + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/PLC.cs b/Libraries/Concentus/CSharp/Concentus/Silk/PLC.cs new file mode 100644 index 000000000..f89c3dc77 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/PLC.cs @@ -0,0 +1,486 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System; + using System.Diagnostics; + + /// + /// Routines for managing packet loss concealment + /// + internal static class PLC + { + private const int NB_ATT = 2; + private static readonly short[] HARM_ATT_Q15 = { 32440, 31130 }; /* 0.99, 0.95 */ + private static readonly short[] PLC_RAND_ATTENUATE_V_Q15 = { 31130, 26214 }; /* 0.95, 0.8 */ + private static readonly short[] PLC_RAND_ATTENUATE_UV_Q15 = { 32440, 29491 }; /* 0.99, 0.9 */ + + internal static void silk_PLC_Reset( + SilkChannelDecoder psDec /* I/O Decoder state */ + ) + { + psDec.sPLC.pitchL_Q8 = Inlines.silk_LSHIFT(psDec.frame_length, 8 - 1); + psDec.sPLC.prevGain_Q16[0] = ((int)((1) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1, 16)*/; + psDec.sPLC.prevGain_Q16[1] = ((int)((1) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1, 16)*/; + psDec.sPLC.subfr_length = 20; + psDec.sPLC.nb_subfr = 2; + } + + internal static void silk_PLC( + SilkChannelDecoder psDec, /* I/O Decoder state */ + SilkDecoderControl psDecCtrl, /* I/O Decoder control */ + short[] frame, /* I/O signal */ + int frame_ptr, + int lost /* I Loss flag */ + ) + { + /* PLC control function */ + if (psDec.fs_kHz != psDec.sPLC.fs_kHz) + { + silk_PLC_Reset(psDec); + psDec.sPLC.fs_kHz = psDec.fs_kHz; + } + + if (lost != 0) + { + /****************************/ + /* Generate Signal */ + /****************************/ + silk_PLC_conceal(psDec, psDecCtrl, frame, frame_ptr); + + psDec.lossCnt++; + } + else { + /****************************/ + /* Update state */ + /****************************/ + silk_PLC_update(psDec, psDecCtrl); + } + } + + /**************************************************/ + /* Update state of PLC */ + /**************************************************/ + internal static void silk_PLC_update( + SilkChannelDecoder psDec, /* I/O Decoder state */ + SilkDecoderControl psDecCtrl /* I/O Decoder control */ + ) + { + int LTP_Gain_Q14, temp_LTP_Gain_Q14; + int i, j; + PLCStruct psPLC = psDec.sPLC; // [porting note] pointer on the stack + + /* Update parameters used in case of packet loss */ + psDec.prevSignalType = psDec.indices.signalType; + LTP_Gain_Q14 = 0; + if (psDec.indices.signalType == SilkConstants.TYPE_VOICED) + { + /* Find the parameters for the last subframe which contains a pitch pulse */ + for (j = 0; j * psDec.subfr_length < psDecCtrl.pitchL[psDec.nb_subfr - 1]; j++) + { + if (j == psDec.nb_subfr) + { + break; + } + temp_LTP_Gain_Q14 = 0; + for (i = 0; i < SilkConstants.LTP_ORDER; i++) + { + temp_LTP_Gain_Q14 += psDecCtrl.LTPCoef_Q14[(psDec.nb_subfr - 1 - j) * SilkConstants.LTP_ORDER + i]; + } + if (temp_LTP_Gain_Q14 > LTP_Gain_Q14) + { + LTP_Gain_Q14 = temp_LTP_Gain_Q14; + + Array.Copy(psDecCtrl.LTPCoef_Q14, Inlines.silk_SMULBB(psDec.nb_subfr - 1 - j, SilkConstants.LTP_ORDER), psPLC.LTPCoef_Q14, 0, SilkConstants.LTP_ORDER); + + psPLC.pitchL_Q8 = Inlines.silk_LSHIFT(psDecCtrl.pitchL[psDec.nb_subfr - 1 - j], 8); + } + } + + Arrays.MemSetShort(psPLC.LTPCoef_Q14, 0, SilkConstants.LTP_ORDER); + psPLC.LTPCoef_Q14[SilkConstants.LTP_ORDER / 2] = (short)(LTP_Gain_Q14); + + /* Limit LT coefs */ + if (LTP_Gain_Q14 < SilkConstants.V_PITCH_GAIN_START_MIN_Q14) + { + int scale_Q10; + int tmp; + + tmp = Inlines.silk_LSHIFT(SilkConstants.V_PITCH_GAIN_START_MIN_Q14, 10); + scale_Q10 = Inlines.silk_DIV32(tmp, Inlines.silk_max(LTP_Gain_Q14, 1)); + for (i = 0; i < SilkConstants.LTP_ORDER; i++) + { + psPLC.LTPCoef_Q14[i] = (short)(Inlines.silk_RSHIFT(Inlines.silk_SMULBB(psPLC.LTPCoef_Q14[i], scale_Q10), 10)); + } + } + else if (LTP_Gain_Q14 > SilkConstants.V_PITCH_GAIN_START_MAX_Q14) + { + int scale_Q14; + int tmp; + + tmp = Inlines.silk_LSHIFT(SilkConstants.V_PITCH_GAIN_START_MAX_Q14, 14); + scale_Q14 = Inlines.silk_DIV32(tmp, Inlines.silk_max(LTP_Gain_Q14, 1)); + for (i = 0; i < SilkConstants.LTP_ORDER; i++) + { + psPLC.LTPCoef_Q14[i] = (short)(Inlines.silk_RSHIFT(Inlines.silk_SMULBB(psPLC.LTPCoef_Q14[i], scale_Q14), 14)); + } + } + } + else { + psPLC.pitchL_Q8 = Inlines.silk_LSHIFT(Inlines.silk_SMULBB(psDec.fs_kHz, 18), 8); + Arrays.MemSetShort(psPLC.LTPCoef_Q14, 0, SilkConstants.LTP_ORDER); + } + + /* Save LPC coeficients */ + Array.Copy(psDecCtrl.PredCoef_Q12[1], psPLC.prevLPC_Q12, psDec.LPC_order); + psPLC.prevLTP_scale_Q14 = (short)(psDecCtrl.LTP_scale_Q14); + + /* Save last two gains */ + Array.Copy(psDecCtrl.Gains_Q16, psDec.nb_subfr - 2, psPLC.prevGain_Q16, 0, 2); + + psPLC.subfr_length = psDec.subfr_length; + psPLC.nb_subfr = psDec.nb_subfr; + } + + /// + /// + /// + /// O + /// O + /// O + /// O + /// I + /// I + /// I + /// I + internal static void silk_PLC_energy( + out int energy1, + out int shift1, + out int energy2, + out int shift2, + int[] exc_Q14, + int[] prevGain_Q10, + int subfr_length, + int nb_subfr) + { + int i, k; + int exc_buf_ptr = 0; + short[] exc_buf = new short[2 * subfr_length]; + + /* Find random noise component */ + /* Scale previous excitation signal */ + for (k = 0; k < 2; k++) + { + for (i = 0; i < subfr_length; i++) + { + exc_buf[exc_buf_ptr + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT( + Inlines.silk_SMULWW(exc_Q14[i + (k + nb_subfr - 2) * subfr_length], prevGain_Q10[k]), 8)); + } + exc_buf_ptr += subfr_length; + } + + /* Find the subframe with lowest energy of the last two and use that as random noise generator */ + SumSqrShift.silk_sum_sqr_shift(out energy1, out shift1, exc_buf, subfr_length); + SumSqrShift.silk_sum_sqr_shift(out energy2, out shift2, exc_buf, subfr_length, subfr_length); + } + + internal static void silk_PLC_conceal( + SilkChannelDecoder psDec, /* I/O Decoder state */ + SilkDecoderControl psDecCtrl, /* I/O Decoder control */ + short[] frame, /* O LPC residual signal */ + int frame_ptr + ) + { + int i, j, k; + int lag, idx, sLTP_buf_idx; + int rand_seed, harm_Gain_Q15, rand_Gain_Q15, inv_gain_Q30; + int energy1, energy2, shift1, shift2; + int rand_ptr; + int pred_lag_ptr; + int LPC_pred_Q10, LTP_pred_Q12; + short rand_scale_Q14; + short[] B_Q14; + int sLPC_Q14_ptr; + short[] sLTP = new short[psDec.ltp_mem_length]; + int[] sLTP_Q14 = new int[psDec.ltp_mem_length + psDec.frame_length]; + PLCStruct psPLC = psDec.sPLC; + int[] prevGain_Q10 = new int[2]; + + prevGain_Q10[0] = Inlines.silk_RSHIFT(psPLC.prevGain_Q16[0], 6); + prevGain_Q10[1] = Inlines.silk_RSHIFT(psPLC.prevGain_Q16[1], 6); + + if (psDec.first_frame_after_reset != 0) + { + Arrays.MemSetShort(psPLC.prevLPC_Q12, 0, SilkConstants.MAX_LPC_ORDER); + } + + silk_PLC_energy(out energy1, out shift1, out energy2, out shift2, psDec.exc_Q14, prevGain_Q10, psDec.subfr_length, psDec.nb_subfr); + + if (Inlines.silk_RSHIFT(energy1, shift2) < Inlines.silk_RSHIFT(energy2, shift1)) + { + /* First sub-frame has lowest energy */ + rand_ptr = Inlines.silk_max_int(0, (psPLC.nb_subfr - 1) * psPLC.subfr_length - SilkConstants.RAND_BUF_SIZE); + } + else { + /* Second sub-frame has lowest energy */ + rand_ptr = Inlines.silk_max_int(0, psPLC.nb_subfr * psPLC.subfr_length - SilkConstants.RAND_BUF_SIZE); + } + + /* Set up Gain to random noise component */ + B_Q14 = psPLC.LTPCoef_Q14; + rand_scale_Q14 = psPLC.randScale_Q14; + + /* Set up attenuation gains */ + harm_Gain_Q15 = HARM_ATT_Q15[Inlines.silk_min_int(NB_ATT - 1, psDec.lossCnt)]; + if (psDec.prevSignalType == SilkConstants.TYPE_VOICED) + { + rand_Gain_Q15 = PLC_RAND_ATTENUATE_V_Q15[Inlines.silk_min_int(NB_ATT - 1, psDec.lossCnt)]; + } + else { + rand_Gain_Q15 = PLC_RAND_ATTENUATE_UV_Q15[Inlines.silk_min_int(NB_ATT - 1, psDec.lossCnt)]; + } + + /* LPC concealment. Apply BWE to previous LPC */ + BWExpander.silk_bwexpander(psPLC.prevLPC_Q12, psDec.LPC_order, ((int)((SilkConstants.BWE_COEF) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.BWE_COEF, 16)*/); + + /* First Lost frame */ + if (psDec.lossCnt == 0) + { + rand_scale_Q14 = 1 << 14; + + /* Reduce random noise Gain for voiced frames */ + if (psDec.prevSignalType == SilkConstants.TYPE_VOICED) + { + for (i = 0; i < SilkConstants.LTP_ORDER; i++) + { + rand_scale_Q14 -= B_Q14[i]; + } + rand_scale_Q14 = Inlines.silk_max_16(3277, rand_scale_Q14); /* 0.2 */ + rand_scale_Q14 = (short)Inlines.silk_RSHIFT(Inlines.silk_SMULBB(rand_scale_Q14, psPLC.prevLTP_scale_Q14), 14); + } + else + { + /* Reduce random noise for unvoiced frames with high LPC gain */ + int invGain_Q30, down_scale_Q30; + + invGain_Q30 = LPCInversePredGain.silk_LPC_inverse_pred_gain(psPLC.prevLPC_Q12, psDec.LPC_order); + + down_scale_Q30 = Inlines.silk_min_32(Inlines.silk_RSHIFT((int)1 << 30, SilkConstants.LOG2_INV_LPC_GAIN_HIGH_THRES), invGain_Q30); + down_scale_Q30 = Inlines.silk_max_32(Inlines.silk_RSHIFT((int)1 << 30, SilkConstants.LOG2_INV_LPC_GAIN_LOW_THRES), down_scale_Q30); + down_scale_Q30 = Inlines.silk_LSHIFT(down_scale_Q30, SilkConstants.LOG2_INV_LPC_GAIN_HIGH_THRES); + + rand_Gain_Q15 = Inlines.silk_RSHIFT(Inlines.silk_SMULWB(down_scale_Q30, rand_Gain_Q15), 14); + } + } + + rand_seed = psPLC.rand_seed; + lag = Inlines.silk_RSHIFT_ROUND(psPLC.pitchL_Q8, 8); + sLTP_buf_idx = psDec.ltp_mem_length; + + /* Rewhiten LTP state */ + idx = psDec.ltp_mem_length - lag - psDec.LPC_order - SilkConstants.LTP_ORDER / 2; + Inlines.OpusAssert(idx > 0); + Filters.silk_LPC_analysis_filter(sLTP, idx, psDec.outBuf, idx, psPLC.prevLPC_Q12, 0, psDec.ltp_mem_length - idx, psDec.LPC_order); + /* Scale LTP state */ + inv_gain_Q30 = Inlines.silk_INVERSE32_varQ(psPLC.prevGain_Q16[1], 46); + inv_gain_Q30 = Inlines.silk_min(inv_gain_Q30, int.MaxValue >> 1); + for (i = idx + psDec.LPC_order; i < psDec.ltp_mem_length; i++) + { + sLTP_Q14[i] = Inlines.silk_SMULWB(inv_gain_Q30, sLTP[i]); + } + + /***************************/ + /* LTP synthesis filtering */ + /***************************/ + for (k = 0; k < psDec.nb_subfr; k++) + { + /* Set up pointer */ + pred_lag_ptr = sLTP_buf_idx - lag + SilkConstants.LTP_ORDER / 2; + for (i = 0; i < psDec.subfr_length; i++) + { + /* Unrolled loop */ + /* Avoids introducing a bias because Inlines.silk_SMLAWB() always rounds to -inf */ + LTP_pred_Q12 = 2; + LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr], B_Q14[0]); + LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr - 1], B_Q14[1]); + LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr - 2], B_Q14[2]); + LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr - 3], B_Q14[3]); + LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr - 4], B_Q14[4]); + pred_lag_ptr++; + + /* Generate LPC excitation */ + rand_seed = Inlines.silk_RAND(rand_seed); + idx = Inlines.silk_RSHIFT(rand_seed, 25) & SilkConstants.RAND_BUF_MASK; + sLTP_Q14[sLTP_buf_idx] = Inlines.silk_LSHIFT32(Inlines.silk_SMLAWB(LTP_pred_Q12, psDec.exc_Q14[rand_ptr + idx], rand_scale_Q14), 2); + sLTP_buf_idx++; + } + + /* Gradually reduce LTP gain */ + for (j = 0; j < SilkConstants.LTP_ORDER; j++) + { + B_Q14[j] = (short)(Inlines.silk_RSHIFT(Inlines.silk_SMULBB(harm_Gain_Q15, B_Q14[j]), 15)); + } + /* Gradually reduce excitation gain */ + rand_scale_Q14 = (short)(Inlines.silk_RSHIFT(Inlines.silk_SMULBB(rand_scale_Q14, rand_Gain_Q15), 15)); + + /* Slowly increase pitch lag */ + psPLC.pitchL_Q8 = Inlines.silk_SMLAWB(psPLC.pitchL_Q8, psPLC.pitchL_Q8, SilkConstants.PITCH_DRIFT_FAC_Q16); + psPLC.pitchL_Q8 = Inlines.silk_min_32(psPLC.pitchL_Q8, Inlines.silk_LSHIFT(Inlines.silk_SMULBB(SilkConstants.MAX_PITCH_LAG_MS, psDec.fs_kHz), 8)); + lag = Inlines.silk_RSHIFT_ROUND(psPLC.pitchL_Q8, 8); + } + + /***************************/ + /* LPC synthesis filtering */ + /***************************/ + sLPC_Q14_ptr = psDec.ltp_mem_length - SilkConstants.MAX_LPC_ORDER; + + /* Copy LPC state */ + Array.Copy(psDec.sLPC_Q14_buf, 0, sLTP_Q14, sLPC_Q14_ptr, SilkConstants.MAX_LPC_ORDER); + + Inlines.OpusAssert(psDec.LPC_order >= 10); /* check that unrolling works */ + for (i = 0; i < psDec.frame_length; i++) + { + /* partly unrolled */ + int sLPCmaxi = sLPC_Q14_ptr + SilkConstants.MAX_LPC_ORDER + i; + /* Avoids introducing a bias because Inlines.silk_SMLAWB() always rounds to -inf */ + LPC_pred_Q10 = Inlines.silk_RSHIFT(psDec.LPC_order, 1); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 1], psPLC.prevLPC_Q12[0]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 2], psPLC.prevLPC_Q12[1]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 3], psPLC.prevLPC_Q12[2]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 4], psPLC.prevLPC_Q12[3]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 5], psPLC.prevLPC_Q12[4]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 6], psPLC.prevLPC_Q12[5]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 7], psPLC.prevLPC_Q12[6]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 8], psPLC.prevLPC_Q12[7]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 9], psPLC.prevLPC_Q12[8]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 10], psPLC.prevLPC_Q12[9]); + for (j = 10; j < psDec.LPC_order; j++) + { + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - j - 1], psPLC.prevLPC_Q12[j]); + } + + /* Add prediction to LPC excitation */ + sLTP_Q14[sLPCmaxi] = Inlines.silk_ADD_LSHIFT32(sLTP_Q14[sLPCmaxi], LPC_pred_Q10, 4); + + /* Scale with Gain */ + frame[frame_ptr + i] = (short)Inlines.silk_SAT16(Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWW(sLTP_Q14[sLPCmaxi], prevGain_Q10[1]), 8))); + } + + /* Save LPC state */ + Array.Copy(sLTP_Q14, sLPC_Q14_ptr + psDec.frame_length, psDec.sLPC_Q14_buf, 0, SilkConstants.MAX_LPC_ORDER); + + /**************************************/ + /* Update states */ + /**************************************/ + psPLC.rand_seed = rand_seed; + psPLC.randScale_Q14 = rand_scale_Q14; + for (i = 0; i < SilkConstants.MAX_NB_SUBFR; i++) + { + psDecCtrl.pitchL[i] = lag; + } + } + + /* Glues concealed frames with new good received frames */ + internal static void silk_PLC_glue_frames( + SilkChannelDecoder psDec, /* I/O decoder state */ + short[] frame, /* I/O signal */ + int frame_ptr, + int length /* I length of signal */ + ) + { + int i; + int energy_shift, energy; + PLCStruct psPLC = psDec.sPLC; + + if (psDec.lossCnt != 0) + { + /* Calculate energy in concealed residual */ + SumSqrShift.silk_sum_sqr_shift(out psPLC.conc_energy, out psPLC.conc_energy_shift, frame, frame_ptr, length); + + psPLC.last_frame_lost = 1; + } + else + { + if (psDec.sPLC.last_frame_lost != 0) + { + /* Calculate residual in decoded signal if last frame was lost */ + SumSqrShift.silk_sum_sqr_shift(out energy, out energy_shift, frame, frame_ptr, length); + + /* Normalize energies */ + if (energy_shift > psPLC.conc_energy_shift) + { + psPLC.conc_energy = Inlines.silk_RSHIFT(psPLC.conc_energy, energy_shift - psPLC.conc_energy_shift); + } + else if (energy_shift < psPLC.conc_energy_shift) + { + energy = Inlines.silk_RSHIFT(energy, psPLC.conc_energy_shift - energy_shift); + } + + /* Fade in the energy difference */ + if (energy > psPLC.conc_energy) + { + int frac_Q24, LZ; + int gain_Q16, slope_Q16; + + LZ = Inlines.silk_CLZ32(psPLC.conc_energy); + LZ = LZ - 1; + psPLC.conc_energy = Inlines.silk_LSHIFT(psPLC.conc_energy, LZ); + energy = Inlines.silk_RSHIFT(energy, Inlines.silk_max_32(24 - LZ, 0)); + + frac_Q24 = Inlines.silk_DIV32(psPLC.conc_energy, Inlines.silk_max(energy, 1)); + + gain_Q16 = Inlines.silk_LSHIFT(Inlines.silk_SQRT_APPROX(frac_Q24), 4); + slope_Q16 = Inlines.silk_DIV32_16(((int)1 << 16) - gain_Q16, length); + /* Make slope 4x steeper to avoid missing onsets after DTX */ + slope_Q16 = Inlines.silk_LSHIFT(slope_Q16, 2); + + for (i = frame_ptr; i < frame_ptr + length; i++) + { + frame[i] = (short)(Inlines.silk_SMULWB(gain_Q16, frame[i])); + gain_Q16 += slope_Q16; + if (gain_Q16 > (int)1 << 16) + { + break; + } + } + } + } + psPLC.last_frame_lost = 0; + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/PitchAnalysisCore.cs b/Libraries/Concentus/CSharp/Concentus/Silk/PitchAnalysisCore.cs new file mode 100644 index 000000000..6ce467ebf --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/PitchAnalysisCore.cs @@ -0,0 +1,792 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Celt; + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System; + using System.Diagnostics; + + internal static class PitchAnalysisCore + { + private const int SCRATCH_SIZE = 22; + private const int SF_LENGTH_4KHZ = (SilkConstants.PE_SUBFR_LENGTH_MS * 4); + private const int SF_LENGTH_8KHZ = (SilkConstants.PE_SUBFR_LENGTH_MS * 8); + private const int MIN_LAG_4KHZ = (SilkConstants.PE_MIN_LAG_MS * 4); + private const int MIN_LAG_8KHZ = (SilkConstants.PE_MIN_LAG_MS * 8); + private const int MAX_LAG_4KHZ = (SilkConstants.PE_MAX_LAG_MS * 4); + private const int MAX_LAG_8KHZ = (SilkConstants.PE_MAX_LAG_MS * 8 - 1); + private const int CSTRIDE_4KHZ = (MAX_LAG_4KHZ + 1 - MIN_LAG_4KHZ); + private const int CSTRIDE_8KHZ = (MAX_LAG_8KHZ + 3 - (MIN_LAG_8KHZ - 2)); + private const int D_COMP_MIN = (MIN_LAG_8KHZ - 3); + private const int D_COMP_MAX = (MAX_LAG_8KHZ + 4); + private const int D_COMP_STRIDE = (D_COMP_MAX - D_COMP_MIN); + + // typedef int silk_pe_stage3_vals[SilkConstants.PE_NB_STAGE3_LAGS]; + // fixme can I linearize this? + private class silk_pe_stage3_vals + { + public readonly int[] Values = new int[SilkConstants.PE_NB_STAGE3_LAGS]; + } + + /*************************************************************/ + /* FIXED POINT CORE PITCH ANALYSIS FUNCTION */ + /*************************************************************/ + internal static int silk_pitch_analysis_core( /* O Voicing estimate: 0 voiced, 1 unvoiced */ + short[] frame, /* I Signal of length PE_FRAME_LENGTH_MS*Fs_kHz */ + int[] pitch_out, /* O 4 pitch lag values */ + BoxedValueShort lagIndex, /* O Lag Index */ + BoxedValueSbyte contourIndex, /* O Pitch contour Index */ + BoxedValueInt LTPCorr_Q15, /* I/O Normalized correlation; input: value from previous frame */ + int prevLag, /* I Last lag of previous frame; set to zero is unvoiced */ + int search_thres1_Q16, /* I First stage threshold for lag candidates 0 - 1 */ + int search_thres2_Q13, /* I Final threshold for lag candidates 0 - 1 */ + int Fs_kHz, /* I Sample frequency (kHz) */ + int complexity, /* I Complexity setting, 0-2, where 2 is highest */ + int nb_subfr /* I number of 5 ms subframes */ + ) + { + short[] frame_8kHz; + short[] frame_4kHz; + int[] filt_state = new int[6]; + short[] input_frame_ptr; + int i, k, d, j; + short[] C; + int[] xcorr32; + short[] basis; + int basis_ptr; + short[] target; + int target_ptr; + int cross_corr, normalizer, energy, shift, energy_basis, energy_target; + int Cmax, length_d_srch, length_d_comp; + int[] d_srch = new int[SilkConstants.PE_D_SRCH_LENGTH]; + short[] d_comp; + int sum, threshold, lag_counter; + int CBimax, CBimax_new, CBimax_old, lag, start_lag, end_lag, lag_new; + int CCmax, CCmax_b, CCmax_new_b, CCmax_new; + int[] CC = new int[SilkConstants.PE_NB_CBKS_STAGE2_EXT]; + silk_pe_stage3_vals[] energies_st3; + silk_pe_stage3_vals[] cross_corr_st3; + int frame_length, frame_length_8kHz, frame_length_4kHz; + int sf_length; + int min_lag; + int max_lag; + int contour_bias_Q15, diff; + int nb_cbk_search; + int delta_lag_log2_sqr_Q7, lag_log2_Q7, prevLag_log2_Q7, prev_lag_bias_Q13; + sbyte[][] Lag_CB_ptr; + + /* Check for valid sampling frequency */ + Inlines.OpusAssert(Fs_kHz == 8 || Fs_kHz == 12 || Fs_kHz == 16); + + /* Check for valid complexity setting */ + Inlines.OpusAssert(complexity >= SilkConstants.SILK_PE_MIN_COMPLEX); + Inlines.OpusAssert(complexity <= SilkConstants.SILK_PE_MAX_COMPLEX); + + Inlines.OpusAssert(search_thres1_Q16 >= 0 && search_thres1_Q16 <= (1 << 16)); + Inlines.OpusAssert(search_thres2_Q13 >= 0 && search_thres2_Q13 <= (1 << 13)); + + /* Set up frame lengths max / min lag for the sampling frequency */ + frame_length = (SilkConstants.PE_LTP_MEM_LENGTH_MS + nb_subfr * SilkConstants.PE_SUBFR_LENGTH_MS) * Fs_kHz; + frame_length_4kHz = (SilkConstants.PE_LTP_MEM_LENGTH_MS + nb_subfr * SilkConstants.PE_SUBFR_LENGTH_MS) * 4; + frame_length_8kHz = (SilkConstants.PE_LTP_MEM_LENGTH_MS + nb_subfr * SilkConstants.PE_SUBFR_LENGTH_MS) * 8; + sf_length = SilkConstants.PE_SUBFR_LENGTH_MS * Fs_kHz; + min_lag = SilkConstants.PE_MIN_LAG_MS * Fs_kHz; + max_lag = SilkConstants.PE_MAX_LAG_MS * Fs_kHz - 1; + + /* Resample from input sampled at Fs_kHz to 8 kHz */ + frame_8kHz = new short[frame_length_8kHz]; + if (Fs_kHz == 16) + { + Arrays.MemSetInt(filt_state, 0, 2); + Resampler.silk_resampler_down2(filt_state, frame_8kHz, frame, frame_length); + } + else if (Fs_kHz == 12) + { + Arrays.MemSetInt(filt_state, 0, 6); + Resampler.silk_resampler_down2_3(filt_state, frame_8kHz, frame, frame_length); + } + else { + Inlines.OpusAssert(Fs_kHz == 8); + Array.Copy(frame, frame_8kHz, frame_length_8kHz); + } + + /* Decimate again to 4 kHz */ + Arrays.MemSetInt(filt_state, 0, 2); /* Set state to zero */ + frame_4kHz = new short[frame_length_4kHz]; + Resampler.silk_resampler_down2(filt_state, frame_4kHz, frame_8kHz, frame_length_8kHz); + + /* Low-pass filter */ + for (i = frame_length_4kHz - 1; i > 0; i--) + { + frame_4kHz[i] = Inlines.silk_ADD_SAT16(frame_4kHz[i], frame_4kHz[i - 1]); + } + + /******************************************************************************* + ** Scale 4 kHz signal down to prevent correlations measures from overflowing + ** find scaling as max scaling for each 8kHz(?) subframe + *******************************************************************************/ + + /* Inner product is calculated with different lengths, so scale for the worst case */ + SumSqrShift.silk_sum_sqr_shift(out energy, out shift, frame_4kHz, frame_length_4kHz); + if (shift > 0) + { + shift = Inlines.silk_RSHIFT(shift, 1); + for (i = 0; i < frame_length_4kHz; i++) + { + frame_4kHz[i] = Inlines.silk_RSHIFT16(frame_4kHz[i], shift); + } + } + + /****************************************************************************** + * FIRST STAGE, operating in 4 khz + ******************************************************************************/ + C = new short[nb_subfr * CSTRIDE_8KHZ]; + xcorr32 = new int[MAX_LAG_4KHZ - MIN_LAG_4KHZ + 1]; + Arrays.MemSetShort(C, 0, (nb_subfr >> 1) * CSTRIDE_4KHZ); + target = frame_4kHz; + target_ptr = Inlines.silk_LSHIFT(SF_LENGTH_4KHZ, 2); + for (k = 0; k < nb_subfr >> 1; k++) + { + basis = target; + basis_ptr = target_ptr - MIN_LAG_4KHZ; + + CeltPitchXCorr.pitch_xcorr(target, target_ptr, target, target_ptr - MAX_LAG_4KHZ, xcorr32, SF_LENGTH_8KHZ, MAX_LAG_4KHZ - MIN_LAG_4KHZ + 1); + + /* Calculate first vector products before loop */ + cross_corr = xcorr32[MAX_LAG_4KHZ - MIN_LAG_4KHZ]; + normalizer = Inlines.silk_inner_prod_self(target, target_ptr, SF_LENGTH_8KHZ); + normalizer = Inlines.silk_ADD32(normalizer, Inlines.silk_inner_prod_self(basis, basis_ptr, SF_LENGTH_8KHZ)); + normalizer = Inlines.silk_ADD32(normalizer, Inlines.silk_SMULBB(SF_LENGTH_8KHZ, 4000)); + + Inlines.MatrixSet(C, k, 0, CSTRIDE_4KHZ, + (short)Inlines.silk_DIV32_varQ(cross_corr, normalizer, 13 + 1)); /* Q13 */ + + /* From now on normalizer is computed recursively */ + for (d = MIN_LAG_4KHZ + 1; d <= MAX_LAG_4KHZ; d++) + { + basis_ptr--; + + cross_corr = xcorr32[MAX_LAG_4KHZ - d]; + + /* Add contribution of new sample and remove contribution from oldest sample */ + normalizer = Inlines.silk_ADD32(normalizer, + Inlines.silk_SMULBB(basis[basis_ptr], basis[basis_ptr]) - + Inlines.silk_SMULBB(basis[basis_ptr + SF_LENGTH_8KHZ], basis[basis_ptr + SF_LENGTH_8KHZ])); + + Inlines.MatrixSet(C, k, d - MIN_LAG_4KHZ, CSTRIDE_4KHZ, + (short)Inlines.silk_DIV32_varQ(cross_corr, normalizer, 13 + 1)); /* Q13 */ + } + /* Update target pointer */ + target_ptr += SF_LENGTH_8KHZ; + } + + /* Combine two subframes into single correlation measure and apply short-lag bias */ + if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR) + { + for (i = MAX_LAG_4KHZ; i >= MIN_LAG_4KHZ; i--) + { + sum = (int)Inlines.MatrixGet(C, 0, i - MIN_LAG_4KHZ, CSTRIDE_4KHZ) + + (int)Inlines.MatrixGet(C, 1, i - MIN_LAG_4KHZ, CSTRIDE_4KHZ); /* Q14 */ + sum = Inlines.silk_SMLAWB(sum, sum, Inlines.silk_LSHIFT(-i, 4)); /* Q14 */ + C[i - MIN_LAG_4KHZ] = (short)sum; /* Q14 */ + } + } + else { + /* Only short-lag bias */ + for (i = MAX_LAG_4KHZ; i >= MIN_LAG_4KHZ; i--) + { + sum = Inlines.silk_LSHIFT((int)C[i - MIN_LAG_4KHZ], 1); /* Q14 */ + sum = Inlines.silk_SMLAWB(sum, sum, Inlines.silk_LSHIFT(-i, 4)); /* Q14 */ + C[i - MIN_LAG_4KHZ] = (short)sum; /* Q14 */ + } + } + + /* Sort */ + length_d_srch = Inlines.silk_ADD_LSHIFT32(4, complexity, 1); + Inlines.OpusAssert(3 * length_d_srch <= SilkConstants.PE_D_SRCH_LENGTH); + Sort.silk_insertion_sort_decreasing_int16(C, d_srch, CSTRIDE_4KHZ, length_d_srch); + + /* Escape if correlation is very low already here */ + Cmax = (int)C[0]; /* Q14 */ + if (Cmax < ((int)((0.2f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(0.2f, 14)*/) + { + Arrays.MemSetInt(pitch_out, 0, nb_subfr); + LTPCorr_Q15.Val = 0; + lagIndex.Val = 0; + contourIndex.Val = 0; + + return 1; + } + + threshold = Inlines.silk_SMULWB(search_thres1_Q16, Cmax); + for (i = 0; i < length_d_srch; i++) + { + /* Convert to 8 kHz indices for the sorted correlation that exceeds the threshold */ + if (C[i] > threshold) + { + d_srch[i] = Inlines.silk_LSHIFT(d_srch[i] + MIN_LAG_4KHZ, 1); + } + else { + length_d_srch = i; + break; + } + } + Inlines.OpusAssert(length_d_srch > 0); + + d_comp = new short[D_COMP_STRIDE]; + for (i = D_COMP_MIN; i < D_COMP_MAX; i++) + { + d_comp[i - D_COMP_MIN] = 0; + } + for (i = 0; i < length_d_srch; i++) + { + d_comp[d_srch[i] - D_COMP_MIN] = 1; + } + + /* Convolution */ + for (i = D_COMP_MAX - 1; i >= MIN_LAG_8KHZ; i--) + { + d_comp[i - D_COMP_MIN] += (short)(d_comp[i - 1 - D_COMP_MIN] + d_comp[i - 2 - D_COMP_MIN]); + } + + length_d_srch = 0; + for (i = MIN_LAG_8KHZ; i < MAX_LAG_8KHZ + 1; i++) + { + if (d_comp[i + 1 - D_COMP_MIN] > 0) + { + d_srch[length_d_srch] = i; + length_d_srch++; + } + } + + /* Convolution */ + for (i = D_COMP_MAX - 1; i >= MIN_LAG_8KHZ; i--) + { + d_comp[i - D_COMP_MIN] += (short)(d_comp[i - 1 - D_COMP_MIN] + d_comp[i - 2 - D_COMP_MIN] + d_comp[i - 3 - D_COMP_MIN]); + } + + length_d_comp = 0; + for (i = MIN_LAG_8KHZ; i < D_COMP_MAX; i++) + { + if (d_comp[i - D_COMP_MIN] > 0) + { + d_comp[length_d_comp] = (short)(i - 2); + length_d_comp++; + } + } + + /********************************************************************************** + ** SECOND STAGE, operating at 8 kHz, on lag sections with high correlation + *************************************************************************************/ + + /****************************************************************************** + ** Scale signal down to avoid correlations measures from overflowing + *******************************************************************************/ + /* find scaling as max scaling for each subframe */ + SumSqrShift.silk_sum_sqr_shift(out energy, out shift, frame_8kHz, frame_length_8kHz); + if (shift > 0) + { + shift = Inlines.silk_RSHIFT(shift, 1); + for (i = 0; i < frame_length_8kHz; i++) + { + frame_8kHz[i] = Inlines.silk_RSHIFT16(frame_8kHz[i], shift); + } + } + + /********************************************************************************* + * Find energy of each subframe projected onto its history, for a range of delays + *********************************************************************************/ + Arrays.MemSetShort(C, 0, nb_subfr * CSTRIDE_8KHZ ); + + target = frame_8kHz; + target_ptr = SilkConstants.PE_LTP_MEM_LENGTH_MS * 8; + for (k = 0; k < nb_subfr; k++) + { + + energy_target = Inlines.silk_ADD32(Inlines.silk_inner_prod(target, target_ptr, target, target_ptr, SF_LENGTH_8KHZ), 1); + for (j = 0; j < length_d_comp; j++) + { + d = d_comp[j]; + basis = target; + basis_ptr = target_ptr - d; + + cross_corr = Inlines.silk_inner_prod(target, target_ptr, basis, basis_ptr, SF_LENGTH_8KHZ); + if (cross_corr > 0) + { + energy_basis = Inlines.silk_inner_prod_self(basis, basis_ptr, SF_LENGTH_8KHZ); + Inlines.MatrixSet(C, k, d - (MIN_LAG_8KHZ - 2), CSTRIDE_8KHZ, + (short)Inlines.silk_DIV32_varQ(cross_corr, + Inlines.silk_ADD32(energy_target, + energy_basis), + 13 + 1)); /* Q13 */ + } + else { + Inlines.MatrixSet(C, k, d - (MIN_LAG_8KHZ - 2), CSTRIDE_8KHZ, 0); + } + } + target_ptr += SF_LENGTH_8KHZ; + } + + /* search over lag range and lags codebook */ + /* scale factor for lag codebook, as a function of center lag */ + + CCmax = int.MinValue; + CCmax_b = int.MinValue; + + CBimax = 0; /* To avoid returning undefined lag values */ + lag = -1; /* To check if lag with strong enough correlation has been found */ + + if (prevLag > 0) + { + if (Fs_kHz == 12) + { + prevLag = Inlines.silk_DIV32_16(Inlines.silk_LSHIFT(prevLag, 1), 3); + } + else if (Fs_kHz == 16) + { + prevLag = Inlines.silk_RSHIFT(prevLag, 1); + } + prevLag_log2_Q7 = Inlines.silk_lin2log((int)prevLag); + } + else { + prevLag_log2_Q7 = 0; + } + Inlines.OpusAssert(search_thres2_Q13 == Inlines.silk_SAT16(search_thres2_Q13)); + /* Set up stage 2 codebook based on number of subframes */ + if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR) + { + Lag_CB_ptr = Tables.silk_CB_lags_stage2; + if (Fs_kHz == 8 && complexity > SilkConstants.SILK_PE_MIN_COMPLEX) + { + /* If input is 8 khz use a larger codebook here because it is last stage */ + nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE2_EXT; + } + else { + nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE2; + } + } + else { + Lag_CB_ptr = Tables.silk_CB_lags_stage2_10_ms; + nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE2_10MS; + } + + for (k = 0; k < length_d_srch; k++) + { + d = d_srch[k]; + for (j = 0; j < nb_cbk_search; j++) + { + CC[j] = 0; + for (i = 0; i < nb_subfr; i++) + { + int d_subfr; + /* Try all codebooks */ + d_subfr = d + Lag_CB_ptr[i][j]; + CC[j] = CC[j] + + (int)Inlines.MatrixGet(C, i, + d_subfr - (MIN_LAG_8KHZ - 2), + CSTRIDE_8KHZ); + } + } + /* Find best codebook */ + CCmax_new = int.MinValue; + CBimax_new = 0; + for (i = 0; i < nb_cbk_search; i++) + { + if (CC[i] > CCmax_new) + { + CCmax_new = CC[i]; + CBimax_new = i; + } + } + + /* Bias towards shorter lags */ + lag_log2_Q7 = Inlines.silk_lin2log(d); /* Q7 */ + Inlines.OpusAssert(lag_log2_Q7 == Inlines.silk_SAT16(lag_log2_Q7)); + Inlines.OpusAssert(nb_subfr * ((int)((SilkConstants.PE_SHORTLAG_BIAS) * ((long)1 << (13)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.PE_SHORTLAG_BIAS, 13)*/ == Inlines.silk_SAT16(nb_subfr * ((int)((SilkConstants.PE_SHORTLAG_BIAS) * ((long)1 << (13)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.PE_SHORTLAG_BIAS, 13)*/)); + CCmax_new_b = CCmax_new - Inlines.silk_RSHIFT(Inlines.silk_SMULBB(nb_subfr * ((int)((SilkConstants.PE_SHORTLAG_BIAS) * ((long)1 << (13)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.PE_SHORTLAG_BIAS, 13)*/, lag_log2_Q7), 7); /* Q13 */ + + /* Bias towards previous lag */ + Inlines.OpusAssert(nb_subfr * ((int)((SilkConstants.PE_PREVLAG_BIAS) * ((long)1 << (13)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.PE_PREVLAG_BIAS, 13)*/ == Inlines.silk_SAT16(nb_subfr * ((int)((SilkConstants.PE_PREVLAG_BIAS) * ((long)1 << (13)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.PE_PREVLAG_BIAS, 13)*/)); + if (prevLag > 0) + { + delta_lag_log2_sqr_Q7 = lag_log2_Q7 - prevLag_log2_Q7; + Inlines.OpusAssert(delta_lag_log2_sqr_Q7 == Inlines.silk_SAT16(delta_lag_log2_sqr_Q7)); + delta_lag_log2_sqr_Q7 = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(delta_lag_log2_sqr_Q7, delta_lag_log2_sqr_Q7), 7); + prev_lag_bias_Q13 = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(nb_subfr * ((int)((SilkConstants.PE_PREVLAG_BIAS) * ((long)1 << (13)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.PE_PREVLAG_BIAS, 13)*/, LTPCorr_Q15.Val), 15); /* Q13 */ + prev_lag_bias_Q13 = Inlines.silk_DIV32(Inlines.silk_MUL(prev_lag_bias_Q13, delta_lag_log2_sqr_Q7), delta_lag_log2_sqr_Q7 + ((int)((0.5f) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(0.5f, 7)*/); + CCmax_new_b -= prev_lag_bias_Q13; /* Q13 */ + } + + if (CCmax_new_b > CCmax_b && /* Find maximum biased correlation */ + CCmax_new > Inlines.silk_SMULBB(nb_subfr, search_thres2_Q13) && /* Correlation needs to be high enough to be voiced */ + Tables.silk_CB_lags_stage2[0][CBimax_new] <= MIN_LAG_8KHZ /* Lag must be in range */ + ) + { + CCmax_b = CCmax_new_b; + CCmax = CCmax_new; + lag = d; + CBimax = CBimax_new; + } + } + + if (lag == -1) + { + /* No suitable candidate found */ + Arrays.MemSetInt(pitch_out, 0, nb_subfr); + LTPCorr_Q15.Val = 0; + lagIndex.Val = 0; + contourIndex.Val = 0; + + return 1; + } + + /* Output normalized correlation */ + LTPCorr_Q15.Val = (int)Inlines.silk_LSHIFT(Inlines.silk_DIV32_16(CCmax, nb_subfr), 2); + Inlines.OpusAssert(LTPCorr_Q15.Val >= 0); + + if (Fs_kHz > 8) + { + short[] scratch_mem; + /***************************************************************************/ + /* Scale input signal down to avoid correlations measures from overflowing */ + /***************************************************************************/ + /* find scaling as max scaling for each subframe */ + SumSqrShift.silk_sum_sqr_shift(out energy, out shift, frame, frame_length); + if (shift > 0) + { + scratch_mem = new short[frame_length]; + /* Move signal to scratch mem because the input signal should be unchanged */ + shift = Inlines.silk_RSHIFT(shift, 1); + for (i = 0; i < frame_length; i++) + { + scratch_mem[i] = Inlines.silk_RSHIFT16(frame[i], shift); + } + input_frame_ptr = scratch_mem; + } + else { + input_frame_ptr = frame; + } + + /* Search in original signal */ + CBimax_old = CBimax; + /* Compensate for decimation */ + Inlines.OpusAssert(lag == Inlines.silk_SAT16(lag)); + if (Fs_kHz == 12) + { + lag = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(lag, 3), 1); + } + else if (Fs_kHz == 16) + { + lag = Inlines.silk_LSHIFT(lag, 1); + } + else { + lag = Inlines.silk_SMULBB(lag, 3); + } + + lag = Inlines.silk_LIMIT_int(lag, min_lag, max_lag); + start_lag = Inlines.silk_max_int(lag - 2, min_lag); + end_lag = Inlines.silk_min_int(lag + 2, max_lag); + lag_new = lag; /* to avoid undefined lag */ + CBimax = 0; /* to avoid undefined lag */ + + CCmax = int.MinValue; + /* pitch lags according to second stage */ + for (k = 0; k < nb_subfr; k++) + { + pitch_out[k] = lag + 2 * Tables.silk_CB_lags_stage2[k][CBimax_old]; + } + + /* Set up codebook parameters according to complexity setting and frame length */ + if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR) + { + nb_cbk_search = (int)Tables.silk_nb_cbk_searchs_stage3[complexity]; + Lag_CB_ptr = Tables.silk_CB_lags_stage3; + } + else { + nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE3_10MS; + Lag_CB_ptr = Tables.silk_CB_lags_stage3_10_ms; + } + + /* Calculate the correlations and energies needed in stage 3 */ + energies_st3 = new silk_pe_stage3_vals[nb_subfr * nb_cbk_search]; + cross_corr_st3 = new silk_pe_stage3_vals[nb_subfr * nb_cbk_search]; + for (int c = 0; c < nb_subfr * nb_cbk_search; c++) + { + energies_st3[c] = new silk_pe_stage3_vals(); // fixme: these can be replaced with a linearized array probably, or at least a struct + cross_corr_st3[c] = new silk_pe_stage3_vals(); + } + silk_P_Ana_calc_corr_st3(cross_corr_st3, input_frame_ptr, start_lag, sf_length, nb_subfr, complexity); + silk_P_Ana_calc_energy_st3(energies_st3, input_frame_ptr, start_lag, sf_length, nb_subfr, complexity); + + lag_counter = 0; + Inlines.OpusAssert(lag == Inlines.silk_SAT16(lag)); + contour_bias_Q15 = Inlines.silk_DIV32_16(((int)((SilkConstants.PE_FLATCONTOUR_BIAS) * ((long)1 << (15)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.PE_FLATCONTOUR_BIAS, 15)*/, lag); + + target = input_frame_ptr; + target_ptr = SilkConstants.PE_LTP_MEM_LENGTH_MS * Fs_kHz; + energy_target = Inlines.silk_ADD32(Inlines.silk_inner_prod_self(target, target_ptr, nb_subfr * sf_length), 1); + for (d = start_lag; d <= end_lag; d++) + { + for (j = 0; j < nb_cbk_search; j++) + { + cross_corr = 0; + energy = energy_target; + for (k = 0; k < nb_subfr; k++) + { + cross_corr = Inlines.silk_ADD32(cross_corr, + Inlines.MatrixGet(cross_corr_st3, k, j, + nb_cbk_search).Values[lag_counter]); + energy = Inlines.silk_ADD32(energy, + Inlines.MatrixGet(energies_st3, k, j, + nb_cbk_search).Values[lag_counter]); + Inlines.OpusAssert(energy >= 0); + } + if (cross_corr > 0) + { + CCmax_new = Inlines.silk_DIV32_varQ(cross_corr, energy, 13 + 1); /* Q13 */ + /* Reduce depending on flatness of contour */ + diff = short.MaxValue - Inlines.silk_MUL(contour_bias_Q15, j); /* Q15 */ + Inlines.OpusAssert(diff == Inlines.silk_SAT16(diff)); + CCmax_new = Inlines.silk_SMULWB(CCmax_new, diff); /* Q14 */ + } + else { + CCmax_new = 0; + } + + if (CCmax_new > CCmax && (d + Tables.silk_CB_lags_stage3[0][j]) <= max_lag) + { + CCmax = CCmax_new; + lag_new = d; + CBimax = j; + } + } + lag_counter++; + } + + for (k = 0; k < nb_subfr; k++) + { + pitch_out[k] = lag_new + Lag_CB_ptr[k][CBimax]; + pitch_out[k] = Inlines.silk_LIMIT(pitch_out[k], min_lag, SilkConstants.PE_MAX_LAG_MS * Fs_kHz); + } + lagIndex.Val = (short)(lag_new - min_lag); + contourIndex.Val = (sbyte)CBimax; + } + else { /* Fs_kHz == 8 */ + /* Save Lags */ + for (k = 0; k < nb_subfr; k++) + { + pitch_out[k] = lag + Lag_CB_ptr[k][CBimax]; + pitch_out[k] = Inlines.silk_LIMIT(pitch_out[k], MIN_LAG_8KHZ, SilkConstants.PE_MAX_LAG_MS * 8); + } + lagIndex.Val = (short)(lag - MIN_LAG_8KHZ); + contourIndex.Val = (sbyte)CBimax; + } + Inlines.OpusAssert(lagIndex.Val >= 0); + /* return as voiced */ + + return 0; + } + + /*********************************************************************** + * Calculates the correlations used in stage 3 search. In order to cover + * the whole lag codebook for all the searched offset lags (lag +- 2), + * the following correlations are needed in each sub frame: + * + * sf1: lag range [-8,...,7] total 16 correlations + * sf2: lag range [-4,...,4] total 9 correlations + * sf3: lag range [-3,....4] total 8 correltions + * sf4: lag range [-6,....8] total 15 correlations + * + * In total 48 correlations. The direct implementation computed in worst + * case 4*12*5 = 240 correlations, but more likely around 120. + ***********************************************************************/ + private static void silk_P_Ana_calc_corr_st3( + silk_pe_stage3_vals[] cross_corr_st3, /* O 3 DIM correlation array */ + short[] frame, /* I vector to correlate */ + int start_lag, /* I lag offset to search around */ + int sf_length, /* I length of a 5 ms subframe */ + int nb_subfr, /* I number of subframes */ + int complexity /* I Complexity setting */ + ) + { + int target_ptr; + int i, j, k, lag_counter, lag_low, lag_high; + int nb_cbk_search, delta, idx; + int[] scratch_mem; + int[] xcorr32; + sbyte[][] Lag_range_ptr; + sbyte[][] Lag_CB_ptr; + + Inlines.OpusAssert(complexity >= SilkConstants.SILK_PE_MIN_COMPLEX); + Inlines.OpusAssert(complexity <= SilkConstants.SILK_PE_MAX_COMPLEX); + + if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR) + { + Lag_range_ptr = Tables.silk_Lag_range_stage3[complexity]; + Lag_CB_ptr = Tables.silk_CB_lags_stage3; + nb_cbk_search = Tables.silk_nb_cbk_searchs_stage3[complexity]; + } + else { + Inlines.OpusAssert(nb_subfr == SilkConstants.PE_MAX_NB_SUBFR >> 1); + Lag_range_ptr = Tables.silk_Lag_range_stage3_10_ms; + Lag_CB_ptr = Tables.silk_CB_lags_stage3_10_ms; + nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE3_10MS; + } + scratch_mem = new int[SCRATCH_SIZE]; + xcorr32 = new int[SCRATCH_SIZE]; + + target_ptr = Inlines.silk_LSHIFT(sf_length, 2); /* Pointer to middle of frame */ + for (k = 0; k < nb_subfr; k++) + { + lag_counter = 0; + + /* Calculate the correlations for each subframe */ + lag_low = Lag_range_ptr[k][0]; + lag_high = Lag_range_ptr[k][1]; + Inlines.OpusAssert(lag_high - lag_low + 1 <= SCRATCH_SIZE); + CeltPitchXCorr.pitch_xcorr(frame, target_ptr, frame, target_ptr - start_lag - lag_high, xcorr32, sf_length, lag_high - lag_low + 1); + for (j = lag_low; j <= lag_high; j++) + { + Inlines.OpusAssert(lag_counter < SCRATCH_SIZE); + scratch_mem[lag_counter] = xcorr32[lag_high - j]; + lag_counter++; + } + + delta = Lag_range_ptr[k][0]; + for (i = 0; i < nb_cbk_search; i++) + { + /* Fill out the 3 dim array that stores the correlations for */ + /* each code_book vector for each start lag */ + idx = Lag_CB_ptr[k][i] - delta; + for (j = 0; j < SilkConstants.PE_NB_STAGE3_LAGS; j++) + { + Inlines.OpusAssert(idx + j < SCRATCH_SIZE); + Inlines.OpusAssert(idx + j < lag_counter); + Inlines.MatrixGet(cross_corr_st3, k, i, nb_cbk_search).Values[j] = + scratch_mem[idx + j]; + } + } + target_ptr += sf_length; + } + + } + + /********************************************************************/ + /* Calculate the energies for first two subframes. The energies are */ + /* calculated recursively. */ + /********************************************************************/ + static void silk_P_Ana_calc_energy_st3( + silk_pe_stage3_vals[] energies_st3, /* O 3 DIM energy array */ + short[] frame, /* I vector to calc energy in */ + int start_lag, /* I lag offset to search around */ + int sf_length, /* I length of one 5 ms subframe */ + int nb_subfr, /* I number of subframes */ + int complexity /* I Complexity setting */ + ) + { + int target_ptr, basis_ptr; + int energy; + int k, i, j, lag_counter; + int nb_cbk_search, delta, idx, lag_diff; + int[] scratch_mem; + sbyte[][] Lag_range_ptr; + sbyte[][] Lag_CB_ptr; + + + Inlines.OpusAssert(complexity >= SilkConstants.SILK_PE_MIN_COMPLEX); + Inlines.OpusAssert(complexity <= SilkConstants.SILK_PE_MAX_COMPLEX); + + if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR) + { + Lag_range_ptr = Tables.silk_Lag_range_stage3[complexity]; + Lag_CB_ptr = Tables.silk_CB_lags_stage3; + nb_cbk_search = Tables.silk_nb_cbk_searchs_stage3[complexity]; + } + else { + Inlines.OpusAssert(nb_subfr == SilkConstants.PE_MAX_NB_SUBFR >> 1); + Lag_range_ptr = Tables.silk_Lag_range_stage3_10_ms; + Lag_CB_ptr = Tables.silk_CB_lags_stage3_10_ms; + nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE3_10MS; + } + scratch_mem = new int[SCRATCH_SIZE]; + + target_ptr = Inlines.silk_LSHIFT(sf_length, 2); + for (k = 0; k < nb_subfr; k++) + { + lag_counter = 0; + + /* Calculate the energy for first lag */ + basis_ptr = target_ptr - (start_lag + Lag_range_ptr[k][0]); + energy = Inlines.silk_inner_prod_self(frame, basis_ptr, sf_length); + Inlines.OpusAssert(energy >= 0); + scratch_mem[lag_counter] = energy; + lag_counter++; + + lag_diff = (Lag_range_ptr[k][1] - Lag_range_ptr[k][0] + 1); + for (i = 1; i < lag_diff; i++) + { + /* remove part outside new window */ + energy -= Inlines.silk_SMULBB(frame[basis_ptr + sf_length - i], frame[basis_ptr + sf_length - i]); + Inlines.OpusAssert(energy >= 0); + + /* add part that comes into window */ + energy = Inlines.silk_ADD_SAT32(energy, Inlines.silk_SMULBB(frame[basis_ptr - i], frame[basis_ptr - i])); + Inlines.OpusAssert(energy >= 0); + Inlines.OpusAssert(lag_counter < SCRATCH_SIZE); + scratch_mem[lag_counter] = energy; + lag_counter++; + } + + delta = Lag_range_ptr[k][0]; + for (i = 0; i < nb_cbk_search; i++) + { + /* Fill out the 3 dim array that stores the correlations for */ + /* each code_book vector for each start lag */ + idx = Lag_CB_ptr[k][i] - delta; + for (j = 0; j < SilkConstants.PE_NB_STAGE3_LAGS; j++) + { + Inlines.OpusAssert(idx + j < SCRATCH_SIZE); + Inlines.OpusAssert(idx + j < lag_counter); + Inlines.MatrixGet(energies_st3, k, i, nb_cbk_search).Values[j] = scratch_mem[idx + j]; + Inlines.OpusAssert(Inlines.MatrixGet(energies_st3, k, i, nb_cbk_search).Values[j] >= 0); + } + } + target_ptr += sf_length; + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/ProcessGains.cs b/Libraries/Concentus/CSharp/Concentus/Silk/ProcessGains.cs new file mode 100644 index 000000000..1de9d0e91 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/ProcessGains.cs @@ -0,0 +1,143 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System; + using System.Diagnostics; + + internal static class ProcessGains + { + /* Processing of gains */ + internal static void silk_process_gains( + SilkChannelEncoder psEnc, /* I/O Encoder state */ + SilkEncoderControl psEncCtrl, /* I/O Encoder control */ + int condCoding /* I The type of conditional coding to use */ + ) + { + SilkShapeState psShapeSt = psEnc.sShape; + int k; + int s_Q16, InvMaxSqrVal_Q16, gain, gain_squared, ResNrg, ResNrgPart, quant_offset_Q10; + + /* Gain reduction when LTP coding gain is high */ + if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED) + { + /*s = -0.5f * silk_sigmoid( 0.25f * ( psEncCtrl.LTPredCodGain - 12.0f ) ); */ + s_Q16 = 0 - Sigmoid.silk_sigm_Q15(Inlines.silk_RSHIFT_ROUND(psEncCtrl.LTPredCodGain_Q7 - ((int)((12.0f) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(12.0f, 7)*/, 4)); + for (k = 0; k < psEnc.nb_subfr; k++) + { + psEncCtrl.Gains_Q16[k] = Inlines.silk_SMLAWB(psEncCtrl.Gains_Q16[k], psEncCtrl.Gains_Q16[k], s_Q16); + } + } + + /* Limit the quantized signal */ + /* InvMaxSqrVal = pow( 2.0f, 0.33f * ( 21.0f - SNR_dB ) ) / subfr_length; */ + InvMaxSqrVal_Q16 = Inlines.silk_DIV32_16(Inlines.silk_log2lin( + Inlines.silk_SMULWB(((int)((21 + 16 / 0.33f) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(21 + 16 / 0.33f, 7)*/ - psEnc.SNR_dB_Q7, ((int)((0.33f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.33f, 16)*/)), psEnc.subfr_length); + + for (k = 0; k < psEnc.nb_subfr; k++) + { + /* Soft limit on ratio residual energy and squared gains */ + ResNrg = psEncCtrl.ResNrg[k]; + ResNrgPart = Inlines.silk_SMULWW(ResNrg, InvMaxSqrVal_Q16); + if (psEncCtrl.ResNrgQ[k] > 0) + { + ResNrgPart = Inlines.silk_RSHIFT_ROUND(ResNrgPart, psEncCtrl.ResNrgQ[k]); + } + else { + if (ResNrgPart >= Inlines.silk_RSHIFT(int.MaxValue, -psEncCtrl.ResNrgQ[k])) + { + ResNrgPart = int.MaxValue; + } + else { + ResNrgPart = Inlines.silk_LSHIFT(ResNrgPart, -psEncCtrl.ResNrgQ[k]); + } + } + gain = psEncCtrl.Gains_Q16[k]; + gain_squared = Inlines.silk_ADD_SAT32(ResNrgPart, Inlines.silk_SMMUL(gain, gain)); + if (gain_squared < short.MaxValue) + { + /* recalculate with higher precision */ + gain_squared = Inlines.silk_SMLAWW(Inlines.silk_LSHIFT(ResNrgPart, 16), gain, gain); + Inlines.OpusAssert(gain_squared > 0); + gain = Inlines.silk_SQRT_APPROX(gain_squared); /* Q8 */ + gain = Inlines.silk_min(gain, int.MaxValue >> 8); + psEncCtrl.Gains_Q16[k] = Inlines.silk_LSHIFT_SAT32(gain, 8); /* Q16 */ + } + else { + gain = Inlines.silk_SQRT_APPROX(gain_squared); /* Q0 */ + gain = Inlines.silk_min(gain, int.MaxValue >> 16); + psEncCtrl.Gains_Q16[k] = Inlines.silk_LSHIFT_SAT32(gain, 16); /* Q16 */ + } + + } + + /* Save unquantized gains and gain Index */ + Array.Copy(psEncCtrl.Gains_Q16, psEncCtrl.GainsUnq_Q16, psEnc.nb_subfr); + psEncCtrl.lastGainIndexPrev = psShapeSt.LastGainIndex; + + /* Quantize gains */ + BoxedValueSbyte boxed_lastGainIndex = new BoxedValueSbyte(psShapeSt.LastGainIndex); + GainQuantization.silk_gains_quant(psEnc.indices.GainsIndices, psEncCtrl.Gains_Q16, + boxed_lastGainIndex, condCoding == SilkConstants.CODE_CONDITIONALLY ? 1 : 0, psEnc.nb_subfr); + psShapeSt.LastGainIndex = boxed_lastGainIndex.Val; + + /* Set quantizer offset for voiced signals. Larger offset when LTP coding gain is low or tilt is high (ie low-pass) */ + if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED) + { + if (psEncCtrl.LTPredCodGain_Q7 + Inlines.silk_RSHIFT(psEnc.input_tilt_Q15, 8) > ((int)((1.0f) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(1.0f, 7)*/) + { + psEnc.indices.quantOffsetType = 0; + } + else { + psEnc.indices.quantOffsetType = 1; + } + } + + /* Quantizer boundary adjustment */ + quant_offset_Q10 = Tables.silk_Quantization_Offsets_Q10[psEnc.indices.signalType >> 1][psEnc.indices.quantOffsetType]; + psEncCtrl.Lambda_Q10 = ((int)((TuningParameters.LAMBDA_OFFSET) * ((long)1 << (10)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LAMBDA_OFFSET, 10)*/ + + Inlines.silk_SMULBB(((int)((TuningParameters.LAMBDA_DELAYED_DECISIONS) * ((long)1 << (10)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LAMBDA_DELAYED_DECISIONS, 10)*/, psEnc.nStatesDelayedDecision) + + Inlines.silk_SMULWB(((int)((TuningParameters.LAMBDA_SPEECH_ACT) * ((long)1 << (18)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LAMBDA_SPEECH_ACT, 18)*/, psEnc.speech_activity_Q8) + + Inlines.silk_SMULWB(((int)((TuningParameters.LAMBDA_INPUT_QUALITY) * ((long)1 << (12)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LAMBDA_INPUT_QUALITY, 12)*/, psEncCtrl.input_quality_Q14) + + Inlines.silk_SMULWB(((int)((TuningParameters.LAMBDA_CODING_QUALITY) * ((long)1 << (12)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LAMBDA_CODING_QUALITY, 12)*/, psEncCtrl.coding_quality_Q14) + + Inlines.silk_SMULWB(((int)((TuningParameters.LAMBDA_QUANT_OFFSET) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LAMBDA_QUANT_OFFSET, 16)*/, quant_offset_Q10); + + Inlines.OpusAssert(psEncCtrl.Lambda_Q10 > 0); + Inlines.OpusAssert(psEncCtrl.Lambda_Q10 < ((int)((2) * ((long)1 << (10)) + 0.5))/*Inlines.SILK_CONST(2, 10)*/); + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/QuantizeLTPGains.cs b/Libraries/Concentus/CSharp/Concentus/Silk/QuantizeLTPGains.cs new file mode 100644 index 000000000..f83f90217 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/QuantizeLTPGains.cs @@ -0,0 +1,153 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System; + using System.Diagnostics; + + internal static class QuantizeLTPGains + { + internal static void silk_quant_LTP_gains( + short[] B_Q14, /* I/O (un)quantized LTP gains [MAX_NB_SUBFR * LTP_ORDER] */ + sbyte[] cbk_index, /* O Codebook Index [MAX_NB_SUBFR] */ + BoxedValueSbyte periodicity_index, /* O Periodicity Index */ + BoxedValueInt sum_log_gain_Q7, /* I/O Cumulative max prediction gain */ + int[] W_Q18, /* I Error Weights in Q18 [MAX_NB_SUBFR * LTP_ORDER * LTP_ORDER] */ + int mu_Q9, /* I Mu value (R/D tradeoff) */ + int lowComplexity, /* I Flag for low complexity */ + int nb_subfr /* I number of subframes */ + ) + { + int j, k, cbk_size; + sbyte[] temp_idx = new sbyte[SilkConstants.MAX_NB_SUBFR]; + byte[] cl_ptr_Q5; + sbyte[][] cbk_ptr_Q7; + byte[] cbk_gain_ptr_Q7; + int b_Q14_ptr; + int W_Q18_ptr; + int rate_dist_Q14_subfr, rate_dist_Q14, min_rate_dist_Q14; + int sum_log_gain_tmp_Q7, best_sum_log_gain_Q7, max_gain_Q7, gain_Q7; + + /***************************************************/ + /* iterate over different codebooks with different */ + /* rates/distortions, and choose best */ + /***************************************************/ + min_rate_dist_Q14 = int.MaxValue; + best_sum_log_gain_Q7 = 0; + for (k = 0; k < 3; k++) + { + /* Safety margin for pitch gain control, to take into account factors + such as state rescaling/rewhitening. */ + int gain_safety = ((int)((0.4f) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(0.4f, 7)*/; + + cl_ptr_Q5 = Tables.silk_LTP_gain_BITS_Q5_ptrs[k]; + cbk_ptr_Q7 = Tables.silk_LTP_vq_ptrs_Q7[k]; + cbk_gain_ptr_Q7 = Tables.silk_LTP_vq_gain_ptrs_Q7[k]; + cbk_size = Tables.silk_LTP_vq_sizes[k]; + + /* Set up pointer to first subframe */ + W_Q18_ptr = 0; + b_Q14_ptr = 0; + + rate_dist_Q14 = 0; + sum_log_gain_tmp_Q7 = sum_log_gain_Q7.Val; + for (j = 0; j < nb_subfr; j++) + { + max_gain_Q7 = Inlines.silk_log2lin((((int)((TuningParameters.MAX_SUM_LOG_GAIN_DB / 6.0f) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.MAX_SUM_LOG_GAIN_DB / 6.0f, 7)*/ - sum_log_gain_tmp_Q7) + + ((int)((7) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(7, 7)*/) - gain_safety; + + BoxedValueSbyte temp_idx_box = new BoxedValueSbyte(temp_idx[j]); + BoxedValueInt rate_dist_Q14_subfr_box = new BoxedValueInt(); + BoxedValueInt gain_Q7_box = new BoxedValueInt(); + VQ_WMat_EC.silk_VQ_WMat_EC( + temp_idx_box, /* O index of best codebook vector */ + rate_dist_Q14_subfr_box, /* O best weighted quantization error + mu * rate */ + gain_Q7_box, /* O sum of absolute LTP coefficients */ + B_Q14, + b_Q14_ptr, /* I input vector to be quantized */ + W_Q18, + W_Q18_ptr, /* I weighting matrix */ + cbk_ptr_Q7, /* I codebook */ + cbk_gain_ptr_Q7, /* I codebook effective gains */ + cl_ptr_Q5, /* I code length for each codebook vector */ + mu_Q9, /* I tradeoff between weighted error and rate */ + max_gain_Q7, /* I maximum sum of absolute LTP coefficients */ + cbk_size /* I number of vectors in codebook */ + ); + rate_dist_Q14_subfr = rate_dist_Q14_subfr_box.Val; + gain_Q7 = gain_Q7_box.Val; + temp_idx[j] = temp_idx_box.Val; + + rate_dist_Q14 = Inlines.silk_ADD_POS_SAT32(rate_dist_Q14, rate_dist_Q14_subfr); + sum_log_gain_tmp_Q7 = Inlines.silk_max(0, sum_log_gain_tmp_Q7 + + Inlines.silk_lin2log(gain_safety + gain_Q7) - ((int)((7) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(7, 7)*/); + + b_Q14_ptr += SilkConstants.LTP_ORDER; + W_Q18_ptr += SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER; + } + + /* Avoid never finding a codebook */ + rate_dist_Q14 = Inlines.silk_min(int.MaxValue - 1, rate_dist_Q14); + + if (rate_dist_Q14 < min_rate_dist_Q14) + { + min_rate_dist_Q14 = rate_dist_Q14; + periodicity_index.Val = (sbyte)k; + Array.Copy(temp_idx, 0, cbk_index, 0, nb_subfr); + best_sum_log_gain_Q7 = sum_log_gain_tmp_Q7; + } + + /* Break early in low-complexity mode if rate distortion is below threshold */ + if (lowComplexity != 0 && (rate_dist_Q14 < Tables.silk_LTP_gain_middle_avg_RD_Q14)) + { + break; + } + } + + cbk_ptr_Q7 = Tables.silk_LTP_vq_ptrs_Q7[periodicity_index.Val]; + for (j = 0; j < nb_subfr; j++) + { + for (k = 0; k < SilkConstants.LTP_ORDER; k++) + { + B_Q14[j * SilkConstants.LTP_ORDER + k] = (short)(Inlines.silk_LSHIFT(cbk_ptr_Q7[cbk_index[j]][k], 7)); + } + } + + sum_log_gain_Q7.Val = best_sum_log_gain_Q7; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/RegularizeCorrelations.cs b/Libraries/Concentus/CSharp/Concentus/Silk/RegularizeCorrelations.cs new file mode 100644 index 000000000..6a85fd202 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/RegularizeCorrelations.cs @@ -0,0 +1,61 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal static class RegularizeCorrelations + { + /* Add noise to matrix diagonal */ + internal static void silk_regularize_correlations( + int[] XX, /* I/O Correlation matrices */ + int XX_ptr, + int[] xx, /* I/O Correlation values */ + int xx_ptr, + int noise, /* I Noise to add */ + int D /* I Dimension of XX */ + ) + { + int i; + for (i = 0; i < D; i++) + { + Inlines.MatrixSet(XX, XX_ptr, i, i, D, Inlines.silk_ADD32(Inlines.MatrixGet(XX, XX_ptr, i, i, D), noise)); + } + xx[xx_ptr] += noise; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Resampler.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Resampler.cs new file mode 100644 index 000000000..35c116482 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Resampler.cs @@ -0,0 +1,744 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + // fixme: merge with resampler state + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System; + using System.Diagnostics; + + /* + * Matrix of resampling methods used: + * Fs_out (kHz) + * 8 12 16 24 48 + * + * 8 C UF U UF UF + * 12 AF C UF U UF + * Fs_in (kHz) 16 D AF C UF UF + * 24 AF D AF C U + * 48 AF AF AF D C + * + * C . Copy (no resampling) + * D . Allpass-based 2x downsampling + * U . Allpass-based 2x upsampling + * UF . Allpass-based 2x upsampling followed by FIR interpolation + * AF . AR2 filter followed by FIR interpolation + */ + + internal static class Resampler + { + private const int USE_silk_resampler_copy = 0; + private const int USE_silk_resampler_private_up2_HQ_wrapper = 1; + private const int USE_silk_resampler_private_IIR_FIR = 2; + private const int USE_silk_resampler_private_down_FIR = 3; + + private const int ORDER_FIR = 4; + + /// + /// Simple way to make [8000, 12000, 16000, 24000, 48000] to [0, 1, 2, 3, 4] + /// + /// + /// + private static int rateID(int R) + { + return (((((R) >> 12) - ((R > 16000) ? 1 : 0)) >> ((R > 24000) ? 1 : 0)) - 1); + } + + /// + /// Initialize/reset the resampler state for a given pair of input/output sampling rates + /// + /// I/O Resampler state + /// I Input sampling rate (Hz) + /// I Output sampling rate (Hz) + /// I If 1: encoder; if 0: decoder + /// + internal static int silk_resampler_init( + SilkResamplerState S, + int Fs_Hz_in, + int Fs_Hz_out, + int forEnc) + { + int up2x; + + /* Clear state */ + S.Reset(); + + /* Input checking */ + if (forEnc != 0) + { + if ((Fs_Hz_in != 8000 && Fs_Hz_in != 12000 && Fs_Hz_in != 16000 && Fs_Hz_in != 24000 && Fs_Hz_in != 48000) || + (Fs_Hz_out != 8000 && Fs_Hz_out != 12000 && Fs_Hz_out != 16000)) + { + Inlines.OpusAssert(false); + return -1; + } + S.inputDelay = Tables.delay_matrix_enc[rateID(Fs_Hz_in),rateID(Fs_Hz_out)]; + } + else { + if ((Fs_Hz_in != 8000 && Fs_Hz_in != 12000 && Fs_Hz_in != 16000) || + (Fs_Hz_out != 8000 && Fs_Hz_out != 12000 && Fs_Hz_out != 16000 && Fs_Hz_out != 24000 && Fs_Hz_out != 48000)) + { + Inlines.OpusAssert(false); + return -1; + } + S.inputDelay = Tables.delay_matrix_dec[rateID(Fs_Hz_in),rateID(Fs_Hz_out)]; + } + + S.Fs_in_kHz = Inlines.silk_DIV32_16(Fs_Hz_in, 1000); + S.Fs_out_kHz = Inlines.silk_DIV32_16(Fs_Hz_out, 1000); + + /* Number of samples processed per batch */ + S.batchSize = S.Fs_in_kHz * SilkConstants.RESAMPLER_MAX_BATCH_SIZE_MS; + + /* Find resampler with the right sampling ratio */ + up2x = 0; + if (Fs_Hz_out > Fs_Hz_in) + { + /* Upsample */ + if (Fs_Hz_out == Inlines.silk_MUL(Fs_Hz_in, 2)) + { /* Fs_out : Fs_in = 2 : 1 */ + /* Special case: directly use 2x upsampler */ + S.resampler_function = USE_silk_resampler_private_up2_HQ_wrapper; + } + else { + /* Default resampler */ + S.resampler_function = USE_silk_resampler_private_IIR_FIR; + up2x = 1; + } + } + else if (Fs_Hz_out < Fs_Hz_in) + { + /* Downsample */ + S.resampler_function = USE_silk_resampler_private_down_FIR; + if (Inlines.silk_MUL(Fs_Hz_out, 4) == Inlines.silk_MUL(Fs_Hz_in, 3)) + { /* Fs_out : Fs_in = 3 : 4 */ + S.FIR_Fracs = 3; + S.FIR_Order = SilkConstants.RESAMPLER_DOWN_ORDER_FIR0; + S.Coefs = Tables.silk_Resampler_3_4_COEFS; + } + else if (Inlines.silk_MUL(Fs_Hz_out, 3) == Inlines.silk_MUL(Fs_Hz_in, 2)) + { /* Fs_out : Fs_in = 2 : 3 */ + S.FIR_Fracs = 2; + S.FIR_Order = SilkConstants.RESAMPLER_DOWN_ORDER_FIR0; + S.Coefs = Tables.silk_Resampler_2_3_COEFS; + } + else if (Inlines.silk_MUL(Fs_Hz_out, 2) == Fs_Hz_in) + { /* Fs_out : Fs_in = 1 : 2 */ + S.FIR_Fracs = 1; + S.FIR_Order = SilkConstants.RESAMPLER_DOWN_ORDER_FIR1; + S.Coefs = Tables.silk_Resampler_1_2_COEFS; + } + else if (Inlines.silk_MUL(Fs_Hz_out, 3) == Fs_Hz_in) + { /* Fs_out : Fs_in = 1 : 3 */ + S.FIR_Fracs = 1; + S.FIR_Order = SilkConstants.RESAMPLER_DOWN_ORDER_FIR2; + S.Coefs = Tables.silk_Resampler_1_3_COEFS; + } + else if (Inlines.silk_MUL(Fs_Hz_out, 4) == Fs_Hz_in) + { /* Fs_out : Fs_in = 1 : 4 */ + S.FIR_Fracs = 1; + S.FIR_Order = SilkConstants.RESAMPLER_DOWN_ORDER_FIR2; + S.Coefs = Tables.silk_Resampler_1_4_COEFS; + } + else if (Inlines.silk_MUL(Fs_Hz_out, 6) == Fs_Hz_in) + { /* Fs_out : Fs_in = 1 : 6 */ + S.FIR_Fracs = 1; + S.FIR_Order = SilkConstants.RESAMPLER_DOWN_ORDER_FIR2; + S.Coefs = Tables.silk_Resampler_1_6_COEFS; + } + else + { + /* None available */ + Inlines.OpusAssert(false); + return -1; + } + } + else + { + /* Input and output sampling rates are equal: copy */ + S.resampler_function = USE_silk_resampler_copy; + } + + /* Ratio of input/output samples */ + S.invRatio_Q16 = Inlines.silk_LSHIFT32(Inlines.silk_DIV32(Inlines.silk_LSHIFT32(Fs_Hz_in, 14 + up2x), Fs_Hz_out), 2); + + /* Make sure the ratio is rounded up */ + while (Inlines.silk_SMULWW(S.invRatio_Q16, Fs_Hz_out) < Inlines.silk_LSHIFT32(Fs_Hz_in, up2x)) + { + S.invRatio_Q16++; + } + + return 0; + } + + /// + /// Resampler: convert from one sampling rate to another + /// Input and output sampling rate are at most 48000 Hz + /// + /// I/O Resampler state + /// O Output signal + /// I Input signal + /// I Number of input samples + /// + internal static int silk_resampler( + SilkResamplerState S, + short[] output, + int output_ptr, + short[] input, + int input_ptr, + int inLen) + { + int nSamples; + + /* Need at least 1 ms of input data */ + Inlines.OpusAssert(inLen >= S.Fs_in_kHz); + /* Delay can't exceed the 1 ms of buffering */ + Inlines.OpusAssert(S.inputDelay <= S.Fs_in_kHz); + + nSamples = S.Fs_in_kHz - S.inputDelay; + + short[] delayBufPtr = S.delayBuf; + + /* Copy to delay buffer */ + Array.Copy(input, input_ptr, delayBufPtr, S.inputDelay, nSamples); + + switch (S.resampler_function) + { + case USE_silk_resampler_private_up2_HQ_wrapper: + silk_resampler_private_up2_HQ(S.sIIR, output, output_ptr, delayBufPtr, 0, S.Fs_in_kHz); + silk_resampler_private_up2_HQ(S.sIIR, output, output_ptr + S.Fs_out_kHz, input, input_ptr + nSamples, inLen - S.Fs_in_kHz); + break; + case USE_silk_resampler_private_IIR_FIR: + silk_resampler_private_IIR_FIR(S, output, output_ptr, delayBufPtr, 0, S.Fs_in_kHz); + silk_resampler_private_IIR_FIR(S, output, output_ptr + S.Fs_out_kHz, input, input_ptr + nSamples, inLen - S.Fs_in_kHz); + break; + case USE_silk_resampler_private_down_FIR: + silk_resampler_private_down_FIR(S, output, output_ptr, delayBufPtr, 0, S.Fs_in_kHz); + silk_resampler_private_down_FIR(S, output, output_ptr + S.Fs_out_kHz, input, input_ptr + nSamples, inLen - S.Fs_in_kHz); + break; + default: + Array.Copy(delayBufPtr, 0, output, output_ptr, S.Fs_in_kHz); + Array.Copy(input, input_ptr + nSamples, output, output_ptr + S.Fs_out_kHz, inLen - S.Fs_in_kHz); + break; + } + + /* Copy to delay buffer */ + Array.Copy(input, input_ptr + inLen - S.inputDelay, delayBufPtr, 0, S.inputDelay); + + return SilkError.SILK_NO_ERROR; + } + + /// + /// Downsample by a factor 2 + /// + /// I/O State vector [ 2 ] + /// O Output signal [ floor(len/2) ] + /// I Input signal [ len ] + /// I Number of input samples + internal static void silk_resampler_down2( + int[] S, + short[] output, + short[] input, + int inLen) + { + int k, len2 = Inlines.silk_RSHIFT32(inLen, 1); + int in32, out32, Y, X; + + Inlines.OpusAssert(Tables.silk_resampler_down2_0 > 0); + Inlines.OpusAssert(Tables.silk_resampler_down2_1 < 0); + + /* Internal variables and state are in Q10 format */ + for (k = 0; k < len2; k++) + { + /* Convert to Q10 */ + in32 = Inlines.silk_LSHIFT((int)input[2 * k], 10); + + /* All-pass section for even input sample */ + Y = Inlines.silk_SUB32(in32, S[0]); + X = Inlines.silk_SMLAWB(Y, Y, Tables.silk_resampler_down2_1); + out32 = Inlines.silk_ADD32(S[0], X); + S[0] = Inlines.silk_ADD32(in32, X); + + /* Convert to Q10 */ + in32 = Inlines.silk_LSHIFT((int)input[2 * k + 1], 10); + + /* All-pass section for odd input sample, and add to output of previous section */ + Y = Inlines.silk_SUB32(in32, S[1]); + X = Inlines.silk_SMULWB(Y, Tables.silk_resampler_down2_0); + out32 = Inlines.silk_ADD32(out32, S[1]); + out32 = Inlines.silk_ADD32(out32, X); + S[1] = Inlines.silk_ADD32(in32, X); + + /* Add, convert back to int16 and store to output */ + output[k] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(out32, 11)); + } + } + + /// + /// Downsample by a factor 2/3, low quality + /// + /// I/O State vector [ 6 ] + /// O Output signal [ floor(2*inLen/3) ] + /// I Input signal [ inLen ] + /// I Number of input samples + internal static void silk_resampler_down2_3( + int[] S, + short[] output, + short[] input, + int inLen) + { + int nSamplesIn, counter, res_Q6; + int[] buf = new int[SilkConstants.RESAMPLER_MAX_BATCH_SIZE_IN + ORDER_FIR]; + int buf_ptr; + int input_ptr = 0; + int output_ptr = 0; + + /* Copy buffered samples to start of buffer */ + Array.Copy(S, 0, buf, 0, ORDER_FIR); + + /* Iterate over blocks of frameSizeIn input samples */ + while (true) + { + nSamplesIn = Inlines.silk_min(inLen, SilkConstants.RESAMPLER_MAX_BATCH_SIZE_IN); + + /* Second-order AR filter (output in Q8) */ + silk_resampler_private_AR2(S, ORDER_FIR, buf, ORDER_FIR, input, input_ptr, + Tables.silk_Resampler_2_3_COEFS_LQ, nSamplesIn); + + /* Interpolate filtered signal */ + buf_ptr = 0; + counter = nSamplesIn; + while (counter > 2) + { + /* Inner product */ + res_Q6 = Inlines.silk_SMULWB(buf[buf_ptr], Tables.silk_Resampler_2_3_COEFS_LQ[2]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 1], Tables.silk_Resampler_2_3_COEFS_LQ[3]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 2], Tables.silk_Resampler_2_3_COEFS_LQ[5]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 3], Tables.silk_Resampler_2_3_COEFS_LQ[4]); + + /* Scale down, saturate and store in output array */ + output[output_ptr++] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q6, 6)); + + res_Q6 = Inlines.silk_SMULWB(buf[buf_ptr + 1], Tables.silk_Resampler_2_3_COEFS_LQ[4]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 2], Tables.silk_Resampler_2_3_COEFS_LQ[5]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 3], Tables.silk_Resampler_2_3_COEFS_LQ[3]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 4], Tables.silk_Resampler_2_3_COEFS_LQ[2]); + + /* Scale down, saturate and store in output array */ + output[output_ptr++] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q6, 6)); + + buf_ptr += 3; + counter -= 3; + } + + input_ptr += nSamplesIn; + inLen -= nSamplesIn; + + if (inLen > 0) + { + /* More iterations to do; copy last part of filtered signal to beginning of buffer */ + Array.Copy(buf, nSamplesIn, buf, 0, ORDER_FIR); + } + else + { + break; + } + } + + /* Copy last part of filtered signal to the state for the next call */ + Array.Copy(buf, nSamplesIn, S, 0, ORDER_FIR); + } + + /// + /// Second order AR filter with single delay elements + /// + /// I/O State vector [ 2 ] + /// O Output signal + /// I Input signal + /// I AR coefficients, Q14 + /// I Signal length + internal static void silk_resampler_private_AR2( + int[] S, + int S_ptr, + int[] out_Q8, + int out_Q8_ptr, + short[] input, + int input_ptr, + short[] A_Q14, + int len) + { + int k, out32; + + for (k = 0; k < len; k++) + { + out32 = Inlines.silk_ADD_LSHIFT32(S[S_ptr], (int)input[input_ptr + k], 8); + out_Q8[out_Q8_ptr + k] = out32; + out32 = Inlines.silk_LSHIFT(out32, 2); + S[S_ptr] = Inlines.silk_SMLAWB(S[S_ptr + 1], out32, A_Q14[0]); + S[S_ptr + 1] = Inlines.silk_SMULWB(out32, A_Q14[1]); + } + } + + internal static int silk_resampler_private_down_FIR_INTERPOL( + short[] output, + int output_ptr, + int[] buf, + short[] FIR_Coefs, + int FIR_Coefs_ptr, + int FIR_Order, + int FIR_Fracs, + int max_index_Q16, + int index_increment_Q16) + { + int index_Q16, res_Q6; + int buf_ptr; + int interpol_ind; + int interpol_ptr; + + switch (FIR_Order) + { + case SilkConstants.RESAMPLER_DOWN_ORDER_FIR0: + for (index_Q16 = 0; index_Q16 < max_index_Q16; index_Q16 += index_increment_Q16) + { + /* Integer part gives pointer to buffered input */ + buf_ptr = Inlines.silk_RSHIFT(index_Q16, 16); + + /* Fractional part gives interpolation coefficients */ + interpol_ind = Inlines.silk_SMULWB(index_Q16 & 0xFFFF, FIR_Fracs); + + /* Inner product */ + interpol_ptr = FIR_Coefs_ptr + (SilkConstants.RESAMPLER_DOWN_ORDER_FIR0 / 2 * interpol_ind); + res_Q6 = Inlines.silk_SMULWB(buf[buf_ptr + 0], FIR_Coefs[interpol_ptr + 0]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 1], FIR_Coefs[interpol_ptr + 1]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 2], FIR_Coefs[interpol_ptr + 2]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 3], FIR_Coefs[interpol_ptr + 3]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 4], FIR_Coefs[interpol_ptr + 4]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 5], FIR_Coefs[interpol_ptr + 5]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 6], FIR_Coefs[interpol_ptr + 6]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 7], FIR_Coefs[interpol_ptr + 7]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 8], FIR_Coefs[interpol_ptr + 8]); + interpol_ptr = FIR_Coefs_ptr + (SilkConstants.RESAMPLER_DOWN_ORDER_FIR0 / 2 * (FIR_Fracs - 1 - interpol_ind)); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 17], FIR_Coefs[interpol_ptr + 0]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 16], FIR_Coefs[interpol_ptr + 1]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 15], FIR_Coefs[interpol_ptr + 2]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 14], FIR_Coefs[interpol_ptr + 3]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 13], FIR_Coefs[interpol_ptr + 4]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 12], FIR_Coefs[interpol_ptr + 5]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 11], FIR_Coefs[interpol_ptr + 6]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 10], FIR_Coefs[interpol_ptr + 7]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 9], FIR_Coefs[interpol_ptr + 8]); + + /* Scale down, saturate and store in output array */ + output[output_ptr++] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q6, 6)); + } + break; + case SilkConstants.RESAMPLER_DOWN_ORDER_FIR1: + for (index_Q16 = 0; index_Q16 < max_index_Q16; index_Q16 += index_increment_Q16) + { + /* Integer part gives pointer to buffered input */ + buf_ptr = Inlines.silk_RSHIFT(index_Q16, 16); + + /* Inner product */ + res_Q6 = Inlines.silk_SMULWB(Inlines.silk_ADD32(buf[buf_ptr + 0], buf[buf_ptr + 23]), FIR_Coefs[FIR_Coefs_ptr + 0]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 1], buf[buf_ptr + 22]), FIR_Coefs[FIR_Coefs_ptr + 1]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 2], buf[buf_ptr + 21]), FIR_Coefs[FIR_Coefs_ptr + 2]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 3], buf[buf_ptr + 20]), FIR_Coefs[FIR_Coefs_ptr + 3]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 4], buf[buf_ptr + 19]), FIR_Coefs[FIR_Coefs_ptr + 4]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 5], buf[buf_ptr + 18]), FIR_Coefs[FIR_Coefs_ptr + 5]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 6], buf[buf_ptr + 17]), FIR_Coefs[FIR_Coefs_ptr + 6]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 7], buf[buf_ptr + 16]), FIR_Coefs[FIR_Coefs_ptr + 7]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 8], buf[buf_ptr + 15]), FIR_Coefs[FIR_Coefs_ptr + 8]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 9], buf[buf_ptr + 14]), FIR_Coefs[FIR_Coefs_ptr + 9]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 10], buf[buf_ptr + 13]), FIR_Coefs[FIR_Coefs_ptr + 10]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 11], buf[buf_ptr + 12]), FIR_Coefs[FIR_Coefs_ptr + 11]); + + /* Scale down, saturate and store in output array */ + output[output_ptr++] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q6, 6)); + } + break; + case SilkConstants.RESAMPLER_DOWN_ORDER_FIR2: + for (index_Q16 = 0; index_Q16 < max_index_Q16; index_Q16 += index_increment_Q16) + { + /* Integer part gives pointer to buffered input */ + buf_ptr = Inlines.silk_RSHIFT(index_Q16, 16); + + /* Inner product */ + res_Q6 = Inlines.silk_SMULWB(Inlines.silk_ADD32(buf[buf_ptr + 0], buf[buf_ptr + 35]), FIR_Coefs[FIR_Coefs_ptr + 0]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 1], buf[buf_ptr + 34]), FIR_Coefs[FIR_Coefs_ptr + 1]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 2], buf[buf_ptr + 33]), FIR_Coefs[FIR_Coefs_ptr + 2]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 3], buf[buf_ptr + 32]), FIR_Coefs[FIR_Coefs_ptr + 3]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 4], buf[buf_ptr + 31]), FIR_Coefs[FIR_Coefs_ptr + 4]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 5], buf[buf_ptr + 30]), FIR_Coefs[FIR_Coefs_ptr + 5]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 6], buf[buf_ptr + 29]), FIR_Coefs[FIR_Coefs_ptr + 6]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 7], buf[buf_ptr + 28]), FIR_Coefs[FIR_Coefs_ptr + 7]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 8], buf[buf_ptr + 27]), FIR_Coefs[FIR_Coefs_ptr + 8]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 9], buf[buf_ptr + 26]), FIR_Coefs[FIR_Coefs_ptr + 9]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 10], buf[buf_ptr + 25]), FIR_Coefs[FIR_Coefs_ptr + 10]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 11], buf[buf_ptr + 24]), FIR_Coefs[FIR_Coefs_ptr + 11]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 12], buf[buf_ptr + 23]), FIR_Coefs[FIR_Coefs_ptr + 12]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 13], buf[buf_ptr + 22]), FIR_Coefs[FIR_Coefs_ptr + 13]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 14], buf[buf_ptr + 21]), FIR_Coefs[FIR_Coefs_ptr + 14]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 15], buf[buf_ptr + 20]), FIR_Coefs[FIR_Coefs_ptr + 15]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 16], buf[buf_ptr + 19]), FIR_Coefs[FIR_Coefs_ptr + 16]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 17], buf[buf_ptr + 18]), FIR_Coefs[FIR_Coefs_ptr + 17]); + + /* Scale down, saturate and store in output array */ + output[output_ptr++] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q6, 6)); + } + break; + default: + Inlines.OpusAssert(false); + break; + } + + return output_ptr; + } + + /// + /// Resample with a 2nd order AR filter followed by FIR interpolation + /// + /// I/O Resampler state + /// O Output signal + /// I Input signal + /// I Number of input samples + internal static void silk_resampler_private_down_FIR( + SilkResamplerState S, + short[] output, + int output_ptr, + short[] input, + int input_ptr, + int inLen) + { + int nSamplesIn; + int max_index_Q16, index_increment_Q16; + int[] buf = new int[S.batchSize + S.FIR_Order]; + + /* Copy buffered samples to start of buffer */ + Array.Copy(S.sFIR_i32, buf, S.FIR_Order); + + /* Iterate over blocks of frameSizeIn input samples */ + index_increment_Q16 = S.invRatio_Q16; + while (true) + { + nSamplesIn = Inlines.silk_min(inLen, S.batchSize); + + /* Second-order AR filter (output in Q8) */ + silk_resampler_private_AR2(S.sIIR, 0, buf, S.FIR_Order, input, input_ptr, S.Coefs, nSamplesIn); + + max_index_Q16 = Inlines.silk_LSHIFT32(nSamplesIn, 16); + + /* Interpolate filtered signal */ + output_ptr = silk_resampler_private_down_FIR_INTERPOL(output, output_ptr, buf, S.Coefs, 2, S.FIR_Order, + S.FIR_Fracs, max_index_Q16, index_increment_Q16); + + input_ptr += nSamplesIn; + inLen -= nSamplesIn; + + if (inLen > 1) + { + /* More iterations to do; copy last part of filtered signal to beginning of buffer */ + Array.Copy(buf, nSamplesIn, buf, 0, S.FIR_Order); + } + else + { + break; + } + } + + /* Copy last part of filtered signal to the state for the next call */ + Array.Copy(buf, nSamplesIn, S.sFIR_i32, 0, S.FIR_Order); + } + + internal static int silk_resampler_private_IIR_FIR_INTERPOL( + short[] output, + int output_ptr, + short[] buf, + int max_index_Q16, + int index_increment_Q16) + { + int index_Q16, res_Q15; + int buf_ptr; + int table_index; + + /* Interpolate upsampled signal and store in output array */ + for (index_Q16 = 0; index_Q16 < max_index_Q16; index_Q16 += index_increment_Q16) + { + table_index = Inlines.silk_SMULWB(index_Q16 & 0xFFFF, 12); + buf_ptr = index_Q16 >> 16; + + res_Q15 = Inlines.silk_SMULBB(buf[buf_ptr], Tables.silk_resampler_frac_FIR_12[table_index, 0]); + res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 1], Tables.silk_resampler_frac_FIR_12[table_index, 1]); + res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 2], Tables.silk_resampler_frac_FIR_12[table_index, 2]); + res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 3], Tables.silk_resampler_frac_FIR_12[table_index, 3]); + res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 4], Tables.silk_resampler_frac_FIR_12[11 - table_index, 3]); + res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 5], Tables.silk_resampler_frac_FIR_12[11 - table_index, 2]); + res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 6], Tables.silk_resampler_frac_FIR_12[11 - table_index, 1]); + res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 7], Tables.silk_resampler_frac_FIR_12[11 - table_index, 0]); + output[output_ptr++] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q15, 15)); + } + return output_ptr; + } + + /// + /// Upsample using a combination of allpass-based 2x upsampling and FIR interpolation + /// + /// I/O Resampler state + /// O Output signal + /// I Input signal + /// I Number of input samples + internal static void silk_resampler_private_IIR_FIR( + SilkResamplerState S, + short[] output, + int output_ptr, + short[] input, + int input_ptr, + int inLen) + { + int nSamplesIn; + int max_index_Q16, index_increment_Q16; + + short[] buf = new short[2 * S.batchSize + SilkConstants.RESAMPLER_ORDER_FIR_12]; + + /* Copy buffered samples to start of buffer */ + Array.Copy(S.sFIR_i16, 0, buf, 0, SilkConstants.RESAMPLER_ORDER_FIR_12); + + /* Iterate over blocks of frameSizeIn input samples */ + index_increment_Q16 = S.invRatio_Q16; + while (true) + { + nSamplesIn = Inlines.silk_min(inLen, S.batchSize); + + /* Upsample 2x */ + silk_resampler_private_up2_HQ(S.sIIR, buf, SilkConstants.RESAMPLER_ORDER_FIR_12, input, input_ptr, nSamplesIn); + + max_index_Q16 = Inlines.silk_LSHIFT32(nSamplesIn, 16 + 1); /* + 1 because 2x upsampling */ + output_ptr = silk_resampler_private_IIR_FIR_INTERPOL(output, output_ptr, buf, max_index_Q16, index_increment_Q16); + input_ptr += nSamplesIn; + inLen -= nSamplesIn; + + if (inLen > 0) + { + /* More iterations to do; copy last part of filtered signal to beginning of buffer */ + Array.Copy(buf, nSamplesIn << 1, buf, 0, SilkConstants.RESAMPLER_ORDER_FIR_12); + } + else + { + break; + } + } + + /* Copy last part of filtered signal to the state for the next call */ + Array.Copy(buf, nSamplesIn << 1, S.sFIR_i16, 0, SilkConstants.RESAMPLER_ORDER_FIR_12); + } + + /// + /// Upsample by a factor 2, high quality + /// Uses 2nd order allpass filters for the 2x upsampling, followed by a + /// notch filter just above Nyquist. + /// + /// I/O Resampler state [ 6 ] + /// O Output signal [ 2 * len ] + /// I Input signal [ len ] + /// I Number of input samples + internal static void silk_resampler_private_up2_HQ( + int[] S, + short[] output, + int output_ptr, + short[] input, + int input_ptr, + int len) + { + int k; + int in32, out32_1, out32_2, Y, X; + + Inlines.OpusAssert(Tables.silk_resampler_up2_hq_0[0] > 0); + Inlines.OpusAssert(Tables.silk_resampler_up2_hq_0[1] > 0); + Inlines.OpusAssert(Tables.silk_resampler_up2_hq_0[2] < 0); + Inlines.OpusAssert(Tables.silk_resampler_up2_hq_1[0] > 0); + Inlines.OpusAssert(Tables.silk_resampler_up2_hq_1[1] > 0); + Inlines.OpusAssert(Tables.silk_resampler_up2_hq_1[2] < 0); + + /* Internal variables and state are in Q10 format */ + for (k = 0; k < len; k++) + { + /* Convert to Q10 */ + in32 = Inlines.silk_LSHIFT((int)input[input_ptr + k], 10); + + /* First all-pass section for even output sample */ + Y = Inlines.silk_SUB32(in32, S[0]); + X = Inlines.silk_SMULWB(Y, Tables.silk_resampler_up2_hq_0[0]); + out32_1 = Inlines.silk_ADD32(S[0], X); + S[0] = Inlines.silk_ADD32(in32, X); + + /* Second all-pass section for even output sample */ + Y = Inlines.silk_SUB32(out32_1, S[1]); + X = Inlines.silk_SMULWB(Y, Tables.silk_resampler_up2_hq_0[1]); + out32_2 = Inlines.silk_ADD32(S[1], X); + S[1] = Inlines.silk_ADD32(out32_1, X); + + /* Third all-pass section for even output sample */ + Y = Inlines.silk_SUB32(out32_2, S[2]); + X = Inlines.silk_SMLAWB(Y, Y, Tables.silk_resampler_up2_hq_0[2]); + out32_1 = Inlines.silk_ADD32(S[2], X); + S[2] = Inlines.silk_ADD32(out32_2, X); + + /* Apply gain in Q15, convert back to int16 and store to output */ + output[output_ptr + (2 * k)] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(out32_1, 10)); + + /* First all-pass section for odd output sample */ + Y = Inlines.silk_SUB32(in32, S[3]); + X = Inlines.silk_SMULWB(Y, Tables.silk_resampler_up2_hq_1[0]); + out32_1 = Inlines.silk_ADD32(S[3], X); + S[3] = Inlines.silk_ADD32(in32, X); + + /* Second all-pass section for odd output sample */ + Y = Inlines.silk_SUB32(out32_1, S[4]); + X = Inlines.silk_SMULWB(Y, Tables.silk_resampler_up2_hq_1[1]); + out32_2 = Inlines.silk_ADD32(S[4], X); + S[4] = Inlines.silk_ADD32(out32_1, X); + + /* Third all-pass section for odd output sample */ + Y = Inlines.silk_SUB32(out32_2, S[5]); + X = Inlines.silk_SMLAWB(Y, Y, Tables.silk_resampler_up2_hq_1[2]); + out32_1 = Inlines.silk_ADD32(S[5], X); + S[5] = Inlines.silk_ADD32(out32_2, X); + + /* Apply gain in Q15, convert back to int16 and store to output */ + output[output_ptr + (2 * k) + 1] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(out32_1, 10)); + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/ResidualEnergy.cs b/Libraries/Concentus/CSharp/Concentus/Silk/ResidualEnergy.cs new file mode 100644 index 000000000..0197c7806 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/ResidualEnergy.cs @@ -0,0 +1,193 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal static class ResidualEnergy + { + /* Calculates residual energies of input subframes where all subframes have LPC_order */ + /* of preceding samples */ + internal static void silk_residual_energy( + int[] nrgs, /* O Residual energy per subframe [MAX_NB_SUBFR] */ + int[] nrgsQ, /* O Q value per subframe [MAX_NB_SUBFR] */ + short[] x, /* I Input signal */ + short[][] a_Q12, /* I AR coefs for each frame half [2][MAX_LPC_ORDER] */ + int[] gains, /* I Quantization gains [SilkConstants.MAX_NB_SUBFR] */ + int subfr_length, /* I Subframe length */ + int nb_subfr, /* I Number of subframes */ + int LPC_order /* I LPC order */ + ) + { + int offset, i, j, lz1, lz2; + int rshift, energy; + int LPC_res_ptr; + short[] LPC_res; + int x_ptr; + int tmp32; + + x_ptr = 0; + offset = LPC_order + subfr_length; + + /* Filter input to create the LPC residual for each frame half, and measure subframe energies */ + LPC_res = new short[(SilkConstants.MAX_NB_SUBFR >> 1) * offset]; + Inlines.OpusAssert((nb_subfr >> 1) * (SilkConstants.MAX_NB_SUBFR >> 1) == nb_subfr); + for (i = 0; i < nb_subfr >> 1; i++) + { + /* Calculate half frame LPC residual signal including preceding samples */ + Filters.silk_LPC_analysis_filter(LPC_res, 0, x, x_ptr, a_Q12[i], 0, (SilkConstants.MAX_NB_SUBFR >> 1) * offset, LPC_order); + + /* Point to first subframe of the just calculated LPC residual signal */ + LPC_res_ptr = LPC_order; + for (j = 0; j < (SilkConstants.MAX_NB_SUBFR >> 1); j++) + { + /* Measure subframe energy */ + SumSqrShift.silk_sum_sqr_shift(out energy, out rshift, LPC_res, LPC_res_ptr, subfr_length); + nrgs[i * (SilkConstants.MAX_NB_SUBFR >> 1) + j] = energy; + + /* Set Q values for the measured energy */ + nrgsQ[i * (SilkConstants.MAX_NB_SUBFR >> 1) + j] = 0 - rshift; + + /* Move to next subframe */ + LPC_res_ptr += offset; + } + /* Move to next frame half */ + x_ptr += (SilkConstants.MAX_NB_SUBFR >> 1) * offset; + } + + /* Apply the squared subframe gains */ + for (i = 0; i < nb_subfr; i++) + { + /* Fully upscale gains and energies */ + lz1 = Inlines.silk_CLZ32(nrgs[i]) - 1; + lz2 = Inlines.silk_CLZ32(gains[i]) - 1; + + tmp32 = Inlines.silk_LSHIFT32(gains[i], lz2); + + /* Find squared gains */ + tmp32 = Inlines.silk_SMMUL(tmp32, tmp32); /* Q( 2 * lz2 - 32 )*/ + + /* Scale energies */ + nrgs[i] = Inlines.silk_SMMUL(tmp32, Inlines.silk_LSHIFT32(nrgs[i], lz1)); /* Q( nrgsQ[ i ] + lz1 + 2 * lz2 - 32 - 32 )*/ + nrgsQ[i] += lz1 + 2 * lz2 - 32 - 32; + } + + } + + /* Residual energy: nrg = wxx - 2 * wXx * c + c' * wXX * c */ + internal static int silk_residual_energy16_covar( + short[] c, /* I Prediction vector */ + int c_ptr, + int[] wXX, /* I Correlation matrix */ + int wXX_ptr, + int[] wXx, /* I Correlation vector */ + int wxx, /* I Signal energy */ + int D, /* I Dimension */ + int cQ /* I Q value for c vector 0 - 15 */ + ) + { + int i, j, lshifts, Qxtra; + int c_max, w_max, tmp, tmp2, nrg; + int[] cn = new int[D]; //SilkConstants.MAX_MATRIX_SIZE + int pRow; + + /* Safety checks */ + Inlines.OpusAssert(D >= 0); + Inlines.OpusAssert(D <= 16); + Inlines.OpusAssert(cQ > 0); + Inlines.OpusAssert(cQ < 16); + + lshifts = 16 - cQ; + Qxtra = lshifts; + + c_max = 0; + for (i = c_ptr; i < c_ptr + D; i++) + { + c_max = Inlines.silk_max_32(c_max, Inlines.silk_abs((int)c[i])); + } + Qxtra = Inlines.silk_min_int(Qxtra, Inlines.silk_CLZ32(c_max) - 17); + + w_max = Inlines.silk_max_32(wXX[wXX_ptr], wXX[wXX_ptr + (D * D) - 1]); + Qxtra = Inlines.silk_min_int(Qxtra, Inlines.silk_CLZ32(Inlines.silk_MUL(D, Inlines.silk_RSHIFT(Inlines.silk_SMULWB(w_max, c_max), 4))) - 5); + Qxtra = Inlines.silk_max_int(Qxtra, 0); + for (i = 0; i < D; i++) + { + cn[i] = Inlines.silk_LSHIFT((int)c[c_ptr + i], Qxtra); + Inlines.OpusAssert(Inlines.silk_abs(cn[i]) <= (short.MaxValue + 1)); /* Check that Inlines.silk_SMLAWB can be used */ + } + lshifts -= Qxtra; + + /* Compute wxx - 2 * wXx * c */ + tmp = 0; + for (i = 0; i < D; i++) + { + tmp = Inlines.silk_SMLAWB(tmp, wXx[i], cn[i]); + } + nrg = Inlines.silk_RSHIFT(wxx, 1 + lshifts) - tmp; /* Q: -lshifts - 1 */ + + /* Add c' * wXX * c, assuming wXX is symmetric */ + tmp2 = 0; + for (i = 0; i < D; i++) + { + tmp = 0; + pRow = wXX_ptr + (i * D); + for (j = i + 1; j < D; j++) + { + tmp = Inlines.silk_SMLAWB(tmp, wXX[pRow + j], cn[j]); + } + tmp = Inlines.silk_SMLAWB(tmp, Inlines.silk_RSHIFT(wXX[pRow + i], 1), cn[i]); + tmp2 = Inlines.silk_SMLAWB(tmp2, tmp, cn[i]); + } + nrg = Inlines.silk_ADD_LSHIFT32(nrg, tmp2, lshifts); /* Q: -lshifts - 1 */ + + /* Keep one bit free always, because we add them for LSF interpolation */ + if (nrg < 1) + { + nrg = 1; + } + else if (nrg > Inlines.silk_RSHIFT(int.MaxValue, lshifts + 2)) + { + nrg = int.MaxValue >> 1; + } + else { + nrg = Inlines.silk_LSHIFT(nrg, lshifts + 1); /* Q0 */ + } + return nrg; + + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Schur.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Schur.cs new file mode 100644 index 000000000..88b03c645 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Schur.cs @@ -0,0 +1,198 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal static class Schur + { + /* Faster than schur64(), but much less accurate. */ + /* uses SMLAWB(), requiring armv5E and higher. */ + internal static int silk_schur( /* O Returns residual energy */ + short[] rc_Q15, /* O reflection coefficients [order] Q15 */ + int[] c, /* I correlations [order+1] */ + int order /* I prediction order */ + ) + { + int k, n, lz; + int[][] C = Arrays.InitTwoDimensionalArray(SilkConstants.SILK_MAX_ORDER_LPC + 1, 2); + int Ctmp1, Ctmp2, rc_tmp_Q15; + + Inlines.OpusAssert(order == 6 || order == 8 || order == 10 || order == 12 || order == 14 || order == 16); + + /* Get number of leading zeros */ + lz = Inlines.silk_CLZ32(c[0]); + + /* Copy correlations and adjust level to Q30 */ + if (lz < 2) + { + /* lz must be 1, so shift one to the right */ + for (k = 0; k < order + 1; k++) + { + C[k][0] = C[k][1] = Inlines.silk_RSHIFT(c[k], 1); + } + } + else if (lz > 2) + { + /* Shift to the left */ + lz -= 2; + for (k = 0; k < order + 1; k++) + { + C[k][0] = C[k][1] = Inlines.silk_LSHIFT(c[k], lz); + } + } + else { + /* No need to shift */ + for (k = 0; k < order + 1; k++) + { + C[k][0] = C[k][1] = c[k]; + } + } + + for (k = 0; k < order; k++) + { + /* Check that we won't be getting an unstable rc, otherwise stop here. */ + if (Inlines.silk_abs_int32(C[k + 1][0]) >= C[0][1]) + { + if (C[k + 1][0] > 0) + { + rc_Q15[k] = (short)(0 - ((int)((.99f) * ((long)1 << (15)) + 0.5))/*Inlines.SILK_CONST(.99f, 15)*/); + } + else { + rc_Q15[k] = (short)(((int)((.99f) * ((long)1 << (15)) + 0.5))/*Inlines.SILK_CONST(.99f, 15)*/); + } + k++; + break; + } + + /* Get reflection coefficient */ + rc_tmp_Q15 = 0 - Inlines.silk_DIV32_16(C[k + 1][0], Inlines.silk_max_32(Inlines.silk_RSHIFT(C[0][1], 15), 1)); + + /* Clip (shouldn't happen for properly conditioned inputs) */ + rc_tmp_Q15 = Inlines.silk_SAT16(rc_tmp_Q15); + + /* Store */ + rc_Q15[k] = (short)rc_tmp_Q15; + + /* Update correlations */ + for (n = 0; n < order - k; n++) + { + Ctmp1 = C[n + k + 1][0]; + Ctmp2 = C[n][1]; + C[n + k + 1][0] = Inlines.silk_SMLAWB(Ctmp1, Inlines.silk_LSHIFT(Ctmp2, 1), rc_tmp_Q15); + C[n][1] = Inlines.silk_SMLAWB(Ctmp2, Inlines.silk_LSHIFT(Ctmp1, 1), rc_tmp_Q15); + } + } + + for (; k < order; k++) + { + rc_Q15[k] = 0; + } + + /* return residual energy */ + return Inlines.silk_max_32(1, C[0][1]); + } + + /* Slower than schur(), but more accurate. */ + /* Uses SMULL(), available on armv4 */ + internal static int silk_schur64( /* O returns residual energy */ + int[] rc_Q16, /* O Reflection coefficients [order] Q16 */ + int[] c, /* I Correlations [order+1] */ + int order /* I Prediction order */ + ) + { + int k, n; + int[][] C = Arrays.InitTwoDimensionalArray(SilkConstants.SILK_MAX_ORDER_LPC + 1, 2); + int Ctmp1_Q30, Ctmp2_Q30, rc_tmp_Q31; + + Inlines.OpusAssert(order == 6 || order == 8 || order == 10 || order == 12 || order == 14 || order == 16); + + /* Check for invalid input */ + if (c[0] <= 0) + { + Arrays.MemSetInt(rc_Q16, 0, order); + return 0; + } + + for (k = 0; k < order + 1; k++) + { + C[k][0] = C[k][1] = c[k]; + } + + for (k = 0; k < order; k++) + { + /* Check that we won't be getting an unstable rc, otherwise stop here. */ + if (Inlines.silk_abs_int32(C[k + 1][0]) >= C[0][1]) + { + if (C[k + 1][0] > 0) + { + rc_Q16[k] = -((int)((.99f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(.99f, 16)*/; + } + else { + rc_Q16[k] = ((int)((.99f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(.99f, 16)*/; + } + k++; + break; + } + + /* Get reflection coefficient: divide two Q30 values and get result in Q31 */ + rc_tmp_Q31 = Inlines.silk_DIV32_varQ(-C[k + 1][0], C[0][1], 31); + + /* Save the output */ + rc_Q16[k] = Inlines.silk_RSHIFT_ROUND(rc_tmp_Q31, 15); + + /* Update correlations */ + for (n = 0; n < order - k; n++) + { + Ctmp1_Q30 = C[n + k + 1][0]; + Ctmp2_Q30 = C[n][1]; + + /* Multiply and add the highest int32 */ + C[n + k + 1][0] = Ctmp1_Q30 + Inlines.silk_SMMUL(Inlines.silk_LSHIFT(Ctmp2_Q30, 1), rc_tmp_Q31); + C[n][1] = Ctmp2_Q30 + Inlines.silk_SMMUL(Inlines.silk_LSHIFT(Ctmp1_Q30, 1), rc_tmp_Q31); + } + } + + for (; k < order; k++) + { + rc_Q16[k] = 0; + } + + return Inlines.silk_max_32(1, C[0][1]); + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/ShellCoder.cs b/Libraries/Concentus/CSharp/Concentus/Silk/ShellCoder.cs new file mode 100644 index 000000000..4868cbc9e --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/ShellCoder.cs @@ -0,0 +1,206 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + /// + /// shell coder; pulse-subframe length is hardcoded + /// + internal static class ShellCoder + { + /// + /// + /// O combined pulses vector [len] + /// I input vector [2 * len] + /// I number of OUTPUT samples + internal static void combine_pulses( + int[] output, + int[] input, + int input_ptr, + int len) + { + int k; + for (k = 0; k < len; k++) + { + output[k] = input[input_ptr + (2 * k)] + input[input_ptr + (2 * k) + 1]; + } + } + + /// + /// + /// O combined pulses vector [len] + /// I input vector [2 * len] + /// I number of OUTPUT samples + internal static void combine_pulses( + int[] output, + int[] input, + int len) + { + int k; + for (k = 0; k < len; k++) + { + output[k] = input[2 * k] + input[2 * k + 1]; + } + } + + internal static void encode_split( + EntropyCoder psRangeEnc, /* I/O compressor data structure */ + int p_child1, /* I pulse amplitude of first child subframe */ + int p, /* I pulse amplitude of current subframe */ + byte[] shell_table /* I table of shell cdfs */ + ) + { + if (p > 0) + { + psRangeEnc.enc_icdf( p_child1, shell_table, Tables.silk_shell_code_table_offsets[p], 8); + } + } + + /// + /// + /// + /// O pulse amplitude of first child subframe + /// O pulse amplitude of second child subframe + /// I/O Compressor data structure + /// I pulse amplitude of current subframe + /// I table of shell cdfs + internal static void decode_split( + short[] p_child1, + int child1_ptr, + short[] p_child2, + int p_child2_ptr, + EntropyCoder psRangeDec, + int p, + byte[] shell_table) + { + if (p > 0) + { + p_child1[child1_ptr] = (short)(psRangeDec.dec_icdf(shell_table, (Tables.silk_shell_code_table_offsets[p]), 8)); + p_child2[p_child2_ptr] = (short)(p - p_child1[child1_ptr]); + } + else + { + p_child1[child1_ptr] = 0; + p_child2[p_child2_ptr] = 0; + } + } + + /// + /// Shell encoder, operates on one shell code frame of 16 pulses + /// + /// I/O compressor data structure + /// I data: nonnegative pulse amplitudes + internal static void silk_shell_encoder(EntropyCoder psRangeEnc, int[] pulses0, int pulses0_ptr) + { + int[] pulses1 = new int[8]; + int[] pulses2 = new int[4]; + int[] pulses3 = new int[2]; + int[] pulses4 = new int[1]; + + /* this function operates on one shell code frame of 16 pulses */ + Inlines.OpusAssert(SilkConstants.SHELL_CODEC_FRAME_LENGTH == 16); + + /* tree representation per pulse-subframe */ + combine_pulses(pulses1, pulses0, pulses0_ptr, 8); + combine_pulses(pulses2, pulses1, 4); + combine_pulses(pulses3, pulses2, 2); + combine_pulses(pulses4, pulses3, 1); + + encode_split(psRangeEnc, pulses3[0], pulses4[0], Tables.silk_shell_code_table3); + + encode_split(psRangeEnc, pulses2[0], pulses3[0], Tables.silk_shell_code_table2); + + encode_split(psRangeEnc, pulses1[0], pulses2[0], Tables.silk_shell_code_table1); + encode_split(psRangeEnc, pulses0[pulses0_ptr], pulses1[0], Tables.silk_shell_code_table0); + encode_split(psRangeEnc, pulses0[pulses0_ptr + 2], pulses1[1], Tables.silk_shell_code_table0); + + encode_split(psRangeEnc, pulses1[2], pulses2[1], Tables.silk_shell_code_table1); + encode_split(psRangeEnc, pulses0[pulses0_ptr + 4], pulses1[2], Tables.silk_shell_code_table0); + encode_split(psRangeEnc, pulses0[pulses0_ptr + 6], pulses1[3], Tables.silk_shell_code_table0); + + encode_split(psRangeEnc, pulses2[2], pulses3[1], Tables.silk_shell_code_table2); + + encode_split(psRangeEnc, pulses1[4], pulses2[2], Tables.silk_shell_code_table1); + encode_split(psRangeEnc, pulses0[pulses0_ptr + 8], pulses1[4], Tables.silk_shell_code_table0); + encode_split(psRangeEnc, pulses0[pulses0_ptr + 10], pulses1[5], Tables.silk_shell_code_table0); + + encode_split(psRangeEnc, pulses1[6], pulses2[3], Tables.silk_shell_code_table1); + encode_split(psRangeEnc, pulses0[pulses0_ptr + 12], pulses1[6], Tables.silk_shell_code_table0); + encode_split(psRangeEnc, pulses0[pulses0_ptr + 14], pulses1[7], Tables.silk_shell_code_table0); + } + + + /* Shell decoder, operates on one shell code frame of 16 pulses */ + internal static void silk_shell_decoder( + short[] pulses0, /* O data: nonnegative pulse amplitudes */ + int pulses0_ptr, + EntropyCoder psRangeDec, /* I/O Compressor data structure */ + int pulses4 /* I number of pulses per pulse-subframe */ + ) + { + short[] pulses1 = new short[8]; + short[] pulses2 = new short[4]; + short[] pulses3 = new short[2]; + + /* this function operates on one shell code frame of 16 pulses */ + Inlines.OpusAssert(SilkConstants.SHELL_CODEC_FRAME_LENGTH == 16); + + decode_split(pulses3, 0, pulses3, 1, psRangeDec, pulses4, Tables.silk_shell_code_table3); + + decode_split(pulses2, 0, pulses2, 1, psRangeDec, pulses3[0], Tables.silk_shell_code_table2); + + decode_split(pulses1, 0, pulses1 ,1, psRangeDec, pulses2[0], Tables.silk_shell_code_table1); + decode_split(pulses0, pulses0_ptr, pulses0, pulses0_ptr + 1, psRangeDec, pulses1[0], Tables.silk_shell_code_table0); + decode_split(pulses0, pulses0_ptr + 2, pulses0, pulses0_ptr + 3, psRangeDec, pulses1[1], Tables.silk_shell_code_table0); + + decode_split(pulses1, 2, pulses1, 3, psRangeDec, pulses2[1], Tables.silk_shell_code_table1); + decode_split(pulses0, pulses0_ptr + 4, pulses0, pulses0_ptr + 5, psRangeDec, pulses1[2], Tables.silk_shell_code_table0); + decode_split(pulses0, pulses0_ptr + 6, pulses0, pulses0_ptr + 7, psRangeDec, pulses1[3], Tables.silk_shell_code_table0); + + decode_split(pulses2, 2, pulses2, 3, psRangeDec, pulses3[1], Tables.silk_shell_code_table2); + + decode_split(pulses1, 4, pulses1 ,5, psRangeDec, pulses2[2], Tables.silk_shell_code_table1); + decode_split(pulses0, pulses0_ptr + 8, pulses0, pulses0_ptr + 9, psRangeDec, pulses1[4], Tables.silk_shell_code_table0); + decode_split(pulses0, pulses0_ptr + 10, pulses0, pulses0_ptr + 11, psRangeDec, pulses1[5], Tables.silk_shell_code_table0); + + decode_split(pulses1, 6, pulses1, 7, psRangeDec, pulses2[3], Tables.silk_shell_code_table1); + decode_split(pulses0, pulses0_ptr + 12, pulses0, pulses0_ptr + 13, psRangeDec, pulses1[6], Tables.silk_shell_code_table0); + decode_split(pulses0, pulses0_ptr + 14, pulses0, pulses0_ptr + 15, psRangeDec, pulses1[7], Tables.silk_shell_code_table0); + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Sigmoid.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Sigmoid.cs new file mode 100644 index 000000000..7cc256ae1 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Sigmoid.cs @@ -0,0 +1,93 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + /// + /// Approximate sigmoid function + /// + internal static class Sigmoid + { + private static readonly int[] sigm_LUT_slope_Q10 = { + 237, 153, 73, 30, 12, 7 + }; + + private static readonly int[] sigm_LUT_pos_Q15 = { + 16384, 23955, 28861, 31213, 32178, 32548 + }; + + private static readonly int[] sigm_LUT_neg_Q15 = { + 16384, 8812, 3906, 1554, 589, 219 + }; + + internal static int silk_sigm_Q15(int in_Q5) + { + int ind; + + if (in_Q5 < 0) + { + /* Negative input */ + in_Q5 = -in_Q5; + if (in_Q5 >= 6 * 32) + { + return 0; /* Clip */ + } + else + { + /* Linear interpolation of look up table */ + ind = Inlines.silk_RSHIFT(in_Q5, 5); + return (sigm_LUT_neg_Q15[ind] - Inlines.silk_SMULBB(sigm_LUT_slope_Q10[ind], in_Q5 & 0x1F)); + } + } + else + { + /* Positive input */ + if (in_Q5 >= 6 * 32) + { + return 32767; /* clip */ + } + else + { + /* Linear interpolation of look up table */ + ind = Inlines.silk_RSHIFT(in_Q5, 5); + return (sigm_LUT_pos_Q15[ind] + Inlines.silk_SMULBB(sigm_LUT_slope_Q10[ind], in_Q5 & 0x1F)); + } + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/SilkConstants.cs b/Libraries/Concentus/CSharp/Concentus/Silk/SilkConstants.cs new file mode 100644 index 000000000..7d89c3690 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/SilkConstants.cs @@ -0,0 +1,311 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System; + using System.Diagnostics; + + internal static class SilkConstants + { + /* Max number of encoder channels (1/2) */ + public const int ENCODER_NUM_CHANNELS = 2; + /* Number of decoder channels (1/2) */ + public const int DECODER_NUM_CHANNELS = 2; + + public const int MAX_FRAMES_PER_PACKET = 3; + + /* Limits on bitrate */ + public const int MIN_TARGET_RATE_BPS = 5000; + public const int MAX_TARGET_RATE_BPS = 80000; + public const int TARGET_RATE_TAB_SZ = 8; + + /* LBRR thresholds */ + public const int LBRR_NB_MIN_RATE_BPS = 12000; + public const int LBRR_MB_MIN_RATE_BPS = 14000; + public const int LBRR_WB_MIN_RATE_BPS = 16000; + + /* DTX settings */ + public const int NB_SPEECH_FRAMES_BEFORE_DTX = 10; /* eq 200 ms */ + public const int MAX_CONSECUTIVE_DTX = 20; /* eq 400 ms */ + + /* Maximum sampling frequency */ + public const int MAX_FS_KHZ = 16; + public const int MAX_API_FS_KHZ = 48; + + // FIXME can use enums here + /* Signal types */ + public const int TYPE_NO_VOICE_ACTIVITY = 0; + public const int TYPE_UNVOICED = 1; + public const int TYPE_VOICED = 2; + + /* Conditional coding types */ + public const int CODE_INDEPENDENTLY = 0; + public const int CODE_INDEPENDENTLY_NO_LTP_SCALING = 1; + public const int CODE_CONDITIONALLY = 2; + + /* Settings for stereo processing */ + public const int STEREO_QUANT_TAB_SIZE = 16; + public const int STEREO_QUANT_SUB_STEPS = 5; + public const int STEREO_INTERP_LEN_MS = 8; /* must be even */ + public const float STEREO_RATIO_SMOOTH_COEF = 0.01f; /* smoothing coef for signal norms and stereo width */ + + /* Range of pitch lag estimates */ + public const int PITCH_EST_MIN_LAG_MS = 2; /* 2 ms . 500 Hz */ + public const int PITCH_EST_MAX_LAG_MS = 18; /* 18 ms . 56 Hz */ + + /* Maximum number of subframes */ + public const int MAX_NB_SUBFR = 4; + + /* Number of samples per frame */ + public const int LTP_MEM_LENGTH_MS = 20; + public const int SUB_FRAME_LENGTH_MS = 5; + public const int MAX_SUB_FRAME_LENGTH = (SUB_FRAME_LENGTH_MS * MAX_FS_KHZ); + public const int MAX_FRAME_LENGTH_MS = (SUB_FRAME_LENGTH_MS * MAX_NB_SUBFR); + public const int MAX_FRAME_LENGTH = (MAX_FRAME_LENGTH_MS * MAX_FS_KHZ); + + /* Milliseconds of lookahead for pitch analysis */ + public const int LA_PITCH_MS = 2; + public const int LA_PITCH_MAX = (LA_PITCH_MS * MAX_FS_KHZ); + + /* Order of LPC used in find pitch */ + public const int MAX_FIND_PITCH_LPC_ORDER = 16; + + /* Length of LPC window used in find pitch */ + public const int FIND_PITCH_LPC_WIN_MS = (20 + (LA_PITCH_MS << 1)); + public const int FIND_PITCH_LPC_WIN_MS_2_SF = (10 + (LA_PITCH_MS << 1)); + public const int FIND_PITCH_LPC_WIN_MAX = (FIND_PITCH_LPC_WIN_MS * MAX_FS_KHZ); + + /* Milliseconds of lookahead for noise shape analysis */ + public const int LA_SHAPE_MS = 5; + public const int LA_SHAPE_MAX = (LA_SHAPE_MS * MAX_FS_KHZ); + + /* Maximum length of LPC window used in noise shape analysis */ + public const int SHAPE_LPC_WIN_MAX = (15 * MAX_FS_KHZ); + + /* dB level of lowest gain quantization level */ + public const int MIN_QGAIN_DB = 2; + /* dB level of highest gain quantization level */ + public const int MAX_QGAIN_DB = 88; + /* Number of gain quantization levels */ + public const int N_LEVELS_QGAIN = 64; + /* Max increase in gain quantization index */ + public const int MAX_DELTA_GAIN_QUANT = 36; + /* Max decrease in gain quantization index */ + public const int MIN_DELTA_GAIN_QUANT = -4; + + /* Quantization offsets (multiples of 4) */ + public const short OFFSET_VL_Q10 = 32; + public const short OFFSET_VH_Q10 = 100; + public const short OFFSET_UVL_Q10 = 100; + public const short OFFSET_UVH_Q10 = 240; + + public const int QUANT_LEVEL_ADJUST_Q10 = 80; + + /* Maximum numbers of iterations used to stabilize an LPC vector */ + public const int MAX_LPC_STABILIZE_ITERATIONS = 16; + public const float MAX_PREDICTION_POWER_GAIN = 1e4f; + public const float MAX_PREDICTION_POWER_GAIN_AFTER_RESET = 1e2f; + + public const int SILK_MAX_ORDER_LPC = 16; + public const int MAX_LPC_ORDER = 16; + public const int MIN_LPC_ORDER = 10; + + /* Find Pred Coef defines */ + public const int LTP_ORDER = 5; + + /* LTP quantization settings */ + public const int NB_LTP_CBKS = 3; + + /* Flag to use harmonic noise shaping */ + public const int USE_HARM_SHAPING = 1; + + /* Max LPC order of noise shaping filters */ + public const int MAX_SHAPE_LPC_ORDER = 16; + + public const int HARM_SHAPE_FIR_TAPS = 3; + + /* Maximum number of delayed decision states */ + public const int MAX_DEL_DEC_STATES = 4; + + public const int LTP_BUF_LENGTH = 512; + public const int LTP_MASK = (LTP_BUF_LENGTH - 1); + + public const int DECISION_DELAY = 32; + public const int DECISION_DELAY_MASK = (DECISION_DELAY - 1); + + /* Number of subframes for excitation entropy coding */ + public const int SHELL_CODEC_FRAME_LENGTH = 16; + public const int LOG2_SHELL_CODEC_FRAME_LENGTH = 4; + public const int MAX_NB_SHELL_BLOCKS = (MAX_FRAME_LENGTH / SHELL_CODEC_FRAME_LENGTH); + + /* Number of rate levels, for entropy coding of excitation */ + public const int N_RATE_LEVELS = 10; + + /* Maximum sum of pulses per shell coding frame */ + public const int SILK_MAX_PULSES = 16; + + public const int MAX_MATRIX_SIZE = MAX_LPC_ORDER; /* Max of LPC Order and LTP order */ + + internal static readonly int NSQ_LPC_BUF_LENGTH = Math.Max(MAX_LPC_ORDER, DECISION_DELAY); + + /***************************/ + /* Voice activity detector */ + /***************************/ + public const int VAD_N_BANDS = 4; + + public const int VAD_INTERNAL_SUBFRAMES_LOG2 = 2; + public const int VAD_INTERNAL_SUBFRAMES = (1 << VAD_INTERNAL_SUBFRAMES_LOG2); + + public const int VAD_NOISE_LEVEL_SMOOTH_COEF_Q16 = 1024; /* Must be < 4096 */ + public const int VAD_NOISE_LEVELS_BIAS = 50; + + /* Sigmoid settings */ + public const int VAD_NEGATIVE_OFFSET_Q5 = 128; /* sigmoid is 0 at -128 */ + public const int VAD_SNR_FACTOR_Q16 = 45000; + + /* smoothing for SNR measurement */ + public const int VAD_SNR_SMOOTH_COEF_Q18 = 4096; + + /* Size of the piecewise linear cosine approximation table for the LSFs */ + public const int LSF_COS_TAB_SZ = 128; + + /******************/ + /* NLSF quantizer */ + /******************/ + public const int NLSF_W_Q = 2; + public const int NLSF_VQ_MAX_VECTORS = 32; + public const int NLSF_VQ_MAX_SURVIVORS = 32; + public const int NLSF_QUANT_MAX_AMPLITUDE = 4; + public const int NLSF_QUANT_MAX_AMPLITUDE_EXT = 10; + public const float NLSF_QUANT_LEVEL_ADJ = 0.1f; + public const int NLSF_QUANT_DEL_DEC_STATES_LOG2 = 2; + public const int NLSF_QUANT_DEL_DEC_STATES = (1 << NLSF_QUANT_DEL_DEC_STATES_LOG2); + + /* Transition filtering for mode switching */ + public const int TRANSITION_TIME_MS = 5120; /* 5120 = 64 * FRAME_LENGTH_MS * ( TRANSITION_INT_NUM - 1 ) = 64*(20*4)*/ + public const int TRANSITION_NB = 3; /* Hardcoded in tables */ + public const int TRANSITION_NA = 2; /* Hardcoded in tables */ + public const int TRANSITION_INT_NUM = 5; /* Hardcoded in tables */ + public const int TRANSITION_FRAMES = (TRANSITION_TIME_MS / MAX_FRAME_LENGTH_MS); + public const int TRANSITION_INT_STEPS = (TRANSITION_FRAMES / (TRANSITION_INT_NUM - 1)); + + /* BWE factors to apply after packet loss */ + public const int BWE_AFTER_LOSS_Q16 = 63570; + + /* Defines for CN generation */ + public const int CNG_BUF_MASK_MAX = 255; /* 2^floor(log2(MAX_FRAME_LENGTH))-1 */ + public const int CNG_GAIN_SMTH_Q16 = 4634; /* 0.25^(1/4) */ + public const int CNG_NLSF_SMTH_Q16 = 16348; /* 0.25 */ + + /********************************************************/ + /* Definitions for pitch estimator (from pitch_est_defines.h) */ + /********************************************************/ + + public const int PE_MAX_FS_KHZ = 16; /* Maximum sampling frequency used */ + + public const int PE_MAX_NB_SUBFR = 4; + public const int PE_SUBFR_LENGTH_MS = 5; /* 5 ms */ + + public const int PE_LTP_MEM_LENGTH_MS = (4 * PE_SUBFR_LENGTH_MS); + + public const int PE_MAX_FRAME_LENGTH_MS = (PE_LTP_MEM_LENGTH_MS + PE_MAX_NB_SUBFR * PE_SUBFR_LENGTH_MS); + public const int PE_MAX_FRAME_LENGTH = (PE_MAX_FRAME_LENGTH_MS * PE_MAX_FS_KHZ ); + public const int PE_MAX_FRAME_LENGTH_ST_1 = (PE_MAX_FRAME_LENGTH >> 2); + public const int PE_MAX_FRAME_LENGTH_ST_2 = (PE_MAX_FRAME_LENGTH >> 1); + + public const int PE_MAX_LAG_MS = 18; /* 18 ms . 56 Hz */ + public const int PE_MIN_LAG_MS = 2; /* 2 ms . 500 Hz */ + public const int PE_MAX_LAG = (PE_MAX_LAG_MS * PE_MAX_FS_KHZ); + public const int PE_MIN_LAG = (PE_MIN_LAG_MS * PE_MAX_FS_KHZ); + + public const int PE_D_SRCH_LENGTH = 24; + + public const int PE_NB_STAGE3_LAGS = 5; + + public const int PE_NB_CBKS_STAGE2 = 3; + public const int PE_NB_CBKS_STAGE2_EXT = 11; + + public const int PE_NB_CBKS_STAGE3_MAX = 34; + public const int PE_NB_CBKS_STAGE3_MID = 24; + public const int PE_NB_CBKS_STAGE3_MIN = 16; + + public const int PE_NB_CBKS_STAGE3_10MS = 12; + public const int PE_NB_CBKS_STAGE2_10MS = 3; + + public const float PE_SHORTLAG_BIAS = 0.2f; /* for logarithmic weighting */ + public const float PE_PREVLAG_BIAS = 0.2f; /* for logarithmic weighting */ + public const float PE_FLATCONTOUR_BIAS = 0.05f; + + public const int SILK_PE_MIN_COMPLEX = 0; + public const int SILK_PE_MID_COMPLEX = 1; + public const int SILK_PE_MAX_COMPLEX = 2; + + // Definitions for PLC (from plc.h) + + public const float BWE_COEF = 0.99f; + public const int V_PITCH_GAIN_START_MIN_Q14 = 11469; /* 0.7 in Q14 */ + public const int V_PITCH_GAIN_START_MAX_Q14 = 15565; /* 0.95 in Q14 */ + public const int MAX_PITCH_LAG_MS = 18; + public const int RAND_BUF_SIZE = 128; + public const int RAND_BUF_MASK = (RAND_BUF_SIZE - 1); + public const int LOG2_INV_LPC_GAIN_HIGH_THRES = 3; /* 2^3 = 8 dB LPC gain */ + public const int LOG2_INV_LPC_GAIN_LOW_THRES = 8; /* 2^8 = 24 dB LPC gain */ + public const int PITCH_DRIFT_FAC_Q16 = 655; /* 0.01 in Q16 */ + + // Definitions for resampler (from resampler_structs.h) + + public const int SILK_RESAMPLER_MAX_FIR_ORDER = 36; + public const int SILK_RESAMPLER_MAX_IIR_ORDER = 6; + + // from resampler_rom.h + public const int RESAMPLER_DOWN_ORDER_FIR0 = 18; + public const int RESAMPLER_DOWN_ORDER_FIR1 = 24; + public const int RESAMPLER_DOWN_ORDER_FIR2 = 36; + public const int RESAMPLER_ORDER_FIR_12 = 8; + + // from resampler_private.h + + /* Number of input samples to process in the inner loop */ + public const int RESAMPLER_MAX_BATCH_SIZE_MS = 10; + public const int RESAMPLER_MAX_FS_KHZ = 48; + public const int RESAMPLER_MAX_BATCH_SIZE_IN = (RESAMPLER_MAX_BATCH_SIZE_MS * RESAMPLER_MAX_FS_KHZ); + + // from api.h + + public const int SILK_MAX_FRAMES_PER_PACKET = 3; + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Sort.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Sort.cs new file mode 100644 index 000000000..d7d47fb73 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Sort.cs @@ -0,0 +1,184 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal static class Sort + { + /// + /// + /// + /// (I/O) Unsorted / Sorted vector + /// (O) Index vector for the sorted elements + /// (I) Vector length + /// (I) Number of correctly sorted positions + internal static void silk_insertion_sort_increasing(int[] a, int[] idx, int L, int K) + { + int value; + int i, j; + + // Safety checks + Inlines.OpusAssert(K > 0); + Inlines.OpusAssert(L > 0); + Inlines.OpusAssert(L >= K); + + // Write start indices in index vector + for (i = 0; i < K; i++) + { + idx[i] = i; + } + + // Sort vector elements by value, increasing order + for (i = 1; i < K; i++) + { + value = a[i]; + + for (j = i - 1; (j >= 0) && (value < a[j]); j--) + { + a[j + 1] = a[j]; /* Shift value */ + idx[j + 1] = idx[j]; /* Shift index */ + } + + a[j + 1] = value; /* Write value */ + idx[j + 1] = i; /* Write index */ + } + + // If less than L values are asked for, check the remaining values, + // but only spend CPU to ensure that the K first values are correct + for (i = K; i < L; i++) + { + value = a[i]; + + if (value < a[K - 1]) + { + for (j = K - 2; (j >= 0) && (value < a[j]); j--) + { + a[j + 1] = a[j]; /* Shift value */ + idx[j + 1] = idx[j]; /* Shift index */ + } + + a[j + 1] = value; /* Write value */ + idx[j + 1] = i; /* Write index */ + } + } + } + + /// + /// Insertion sort (fast for already almost sorted arrays): + /// Best case: O(n) for an already sorted array + /// Worst case: O(n^2) for an inversely sorted array + /// + /// (I/O) Unsorted / Sorted vector + /// (I) Vector length + internal static void silk_insertion_sort_increasing_all_values_int16(short[] a, int L) + { + // FIXME: Could just use Array.Sort(a.Array, a.Offset, L); + + short value; + int i, j; + + // Safety checks + Inlines.OpusAssert(L > 0); + + // Sort vector elements by value, increasing order + for (i = 1; i < L; i++) + { + value = a[i]; + for (j = i - 1; (j >= 0) && (value < a[j]); j--) + { + a[j + 1] = a[j]; // Shift value + } + + a[j + 1] = value; // Write value + } + } + + /* This function is only used by the fixed-point build */ + internal static void silk_insertion_sort_decreasing_int16( + short[] a, /* I/O Unsorted / Sorted vector */ + int[] idx, /* O Index vector for the sorted elements */ + int L, /* I Vector length */ + int K /* I Number of correctly sorted positions */ +) + { + int i, j; + short value; + + /* Safety checks */ + Inlines.OpusAssert(K > 0); + Inlines.OpusAssert(L > 0); + Inlines.OpusAssert(L >= K); + + /* Write start indices in index vector */ + for (i = 0; i < K; i++) + { + idx[i] = i; + } + + /* Sort vector elements by value, decreasing order */ + for (i = 1; i < K; i++) + { + value = a[i]; + for (j = i - 1; (j >= 0) && (value > a[j]); j--) + { + a[j + 1] = a[j]; /* Shift value */ + idx[j + 1] = idx[j]; /* Shift index */ + } + a[j + 1] = value; /* Write value */ + idx[j + 1] = i; /* Write index */ + } + + /* If less than L values are asked for, check the remaining values, */ + /* but only spend CPU to ensure that the K first values are correct */ + for (i = K; i < L; i++) + { + value = a[i]; + if (value > a[K - 1]) + { + for (j = K - 2; (j >= 0) && (value > a[j]); j--) + { + a[j + 1] = a[j]; /* Shift value */ + idx[j + 1] = idx[j]; /* Shift index */ + } + a[j + 1] = value; /* Write value */ + idx[j + 1] = i; /* Write index */ + } + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Stereo.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Stereo.cs new file mode 100644 index 000000000..4a60c78e6 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Stereo.cs @@ -0,0 +1,543 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System; + using System.Diagnostics; + + internal static class Stereo + { + /// + /// Decode mid/side predictors + /// + /// I/O Compressor data structure + /// O Predictors + internal static void silk_stereo_decode_pred( + EntropyCoder psRangeDec, + int[] pred_Q13) + { + int n; + int[][] ix = Arrays.InitTwoDimensionalArray(2, 3); + int low_Q13, step_Q13; + + // Entropy decoding + n = psRangeDec.dec_icdf(Tables.silk_stereo_pred_joint_iCDF, 8); + ix[0][2] = Inlines.silk_DIV32_16(n, 5); + ix[1][2] = n - 5 * ix[0][2]; + for (n = 0; n < 2; n++) + { + ix[n][0] = psRangeDec.dec_icdf(Tables.silk_uniform3_iCDF, 8); + ix[n][1] = psRangeDec.dec_icdf(Tables.silk_uniform5_iCDF, 8); + } + + // Dequantize + for (n = 0; n < 2; n++) + { + ix[n][0] += 3 * ix[n][2]; + low_Q13 = Tables.silk_stereo_pred_quant_Q13[ix[n][0]]; + step_Q13 = Inlines.silk_SMULWB(Tables.silk_stereo_pred_quant_Q13[ix[n][0] + 1] - low_Q13, + ((int)((0.5f / SilkConstants.STEREO_QUANT_SUB_STEPS) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.5f / SilkConstants.STEREO_QUANT_SUB_STEPS, 16)*/); + pred_Q13[n] = Inlines.silk_SMLABB(low_Q13, step_Q13, 2 * ix[n][1] + 1); + } + + /* Subtract second from first predictor (helps when actually applying these) */ + pred_Q13[0] -= pred_Q13[1]; + } + + /// + /// Decode mid-only flag + /// + /// I/O Compressor data structure + /// O Flag that only mid channel has been coded + internal static void silk_stereo_decode_mid_only( + EntropyCoder psRangeDec, + BoxedValueInt decode_only_mid + ) + { + /* Decode flag that only mid channel is coded */ + decode_only_mid.Val = psRangeDec.dec_icdf(Tables.silk_stereo_only_code_mid_iCDF, 8); + } + + /// + /// Entropy code the mid/side quantization indices + /// + /// I/O Compressor data structure + /// I Quantization indices [ 2 ][ 3 ] + internal static void silk_stereo_encode_pred(EntropyCoder psRangeEnc, sbyte[][] ix) + { + int n; + + /* Entropy coding */ + n = 5 * ix[0][2] + ix[1][2]; + Inlines.OpusAssert(n < 25); + psRangeEnc.enc_icdf( n, Tables.silk_stereo_pred_joint_iCDF, 8); + for (n = 0; n < 2; n++) + { + Inlines.OpusAssert(ix[n][0] < 3); + Inlines.OpusAssert(ix[n][1] < SilkConstants.STEREO_QUANT_SUB_STEPS); + psRangeEnc.enc_icdf( ix[n][0], Tables.silk_uniform3_iCDF, 8); + psRangeEnc.enc_icdf( ix[n][1], Tables.silk_uniform5_iCDF, 8); + } + } + + /// + /// Entropy code the mid-only flag + /// + /// I/O Compressor data structure + /// + internal static void silk_stereo_encode_mid_only(EntropyCoder psRangeEnc, sbyte mid_only_flag) + { + /* Encode flag that only mid channel is coded */ + psRangeEnc.enc_icdf( mid_only_flag, Tables.silk_stereo_only_code_mid_iCDF, 8); + } + + /// + /// Find least-squares prediction gain for one signal based on another and quantize it + /// + /// O Ratio of residual and mid energies + /// I Basis signal + /// I Target signal + /// I/O Smoothed mid, residual norms + /// I Number of samples + /// I Smoothing coefficient + /// O Returns predictor in Q13 + internal static int silk_stereo_find_predictor( + BoxedValueInt ratio_Q14, + short[] x, + short[] y, + int[] mid_res_amp_Q0, + int mid_res_amp_Q0_ptr, + int length, + int smooth_coef_Q16) + { + int scale; + int nrgx, nrgy, scale1, scale2; + int corr, pred_Q13, pred2_Q10; + + /* Find predictor */ + SumSqrShift.silk_sum_sqr_shift(out nrgx, out scale1, x, length); + SumSqrShift.silk_sum_sqr_shift(out nrgy, out scale2, y, length); + scale = Inlines.silk_max_int(scale1, scale2); + scale = scale + (scale & 1); /* make even */ + nrgy = Inlines.silk_RSHIFT32(nrgy, scale - scale2); + nrgx = Inlines.silk_RSHIFT32(nrgx, scale - scale1); + nrgx = Inlines.silk_max_int(nrgx, 1); + corr = Inlines.silk_inner_prod_aligned_scale(x, y, scale, length); + pred_Q13 = Inlines.silk_DIV32_varQ(corr, nrgx, 13); + pred_Q13 = Inlines.silk_LIMIT(pred_Q13, -(1 << 14), 1 << 14); + pred2_Q10 = Inlines.silk_SMULWB(pred_Q13, pred_Q13); + + /* Faster update for signals with large prediction parameters */ + smooth_coef_Q16 = (int)Inlines.silk_max_int(smooth_coef_Q16, Inlines.silk_abs(pred2_Q10)); + + /* Smoothed mid and residual norms */ + Inlines.OpusAssert(smooth_coef_Q16 < 32768); + scale = Inlines.silk_RSHIFT(scale, 1); + mid_res_amp_Q0[mid_res_amp_Q0_ptr] = Inlines.silk_SMLAWB(mid_res_amp_Q0[mid_res_amp_Q0_ptr], + Inlines.silk_LSHIFT(Inlines.silk_SQRT_APPROX(nrgx), scale) - mid_res_amp_Q0[mid_res_amp_Q0_ptr], smooth_coef_Q16); + /* Residual energy = nrgy - 2 * pred * corr + pred^2 * nrgx */ + nrgy = Inlines.silk_SUB_LSHIFT32(nrgy, Inlines.silk_SMULWB(corr, pred_Q13), 3 + 1); + nrgy = Inlines.silk_ADD_LSHIFT32(nrgy, Inlines.silk_SMULWB(nrgx, pred2_Q10), 6); + mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1] = Inlines.silk_SMLAWB(mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1], + Inlines.silk_LSHIFT(Inlines.silk_SQRT_APPROX(nrgy), scale) - mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1], smooth_coef_Q16); + + /* Ratio of smoothed residual and mid norms */ + ratio_Q14.Val = Inlines.silk_DIV32_varQ(mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1], Inlines.silk_max(mid_res_amp_Q0[mid_res_amp_Q0_ptr], 1), 14); + ratio_Q14.Val = Inlines.silk_LIMIT(ratio_Q14.Val, 0, 32767); + + return pred_Q13; + } + + /// + /// Convert Left/Right stereo signal to adaptive Mid/Side representation + /// + /// I/O State + /// I/O Left input signal, becomes mid signal + /// I/O Right input signal, becomes side signal + /// O Quantization indices [ 2 ][ 3 ] + /// O Flag: only mid signal coded + /// O Bitrates for mid and side signals + /// I Total bitrate + /// I Speech activity level in previous frame + /// I Last frame before a stereo.mono transition + /// I Sample rate (kHz) + /// I Number of samples + internal static void silk_stereo_LR_to_MS( + StereoEncodeState state, + short[] x1, + int x1_ptr, + short[] x2, + int x2_ptr, + sbyte[][] ix, + BoxedValueSbyte mid_only_flag, + int[] mid_side_rates_bps, + int total_rate_bps, + int prev_speech_act_Q8, + int toMono, + int fs_kHz, + int frame_length) + { + int n, is10msFrame, denom_Q16, delta0_Q13, delta1_Q13; + int sum, diff, smooth_coef_Q16, pred0_Q13, pred1_Q13; + int[] pred_Q13 = new int[2]; + int frac_Q16, frac_3_Q16, min_mid_rate_bps, width_Q14, w_Q24, deltaw_Q24; + BoxedValueInt LP_ratio_Q14 = new BoxedValueInt(); + BoxedValueInt HP_ratio_Q14 = new BoxedValueInt(); + short[] side; + short[] LP_mid; + short[] HP_mid; + short[] LP_side; + short[] HP_side; + int mid = x1_ptr - 2; + + side = new short[frame_length + 2]; + + /* Convert to basic mid/side signals */ + for (n = 0; n < frame_length + 2; n++) + { + sum = x1[x1_ptr + n - 2] + (int)x2[x2_ptr + n - 2]; + diff = x1[x1_ptr + n - 2] - (int)x2[x2_ptr + n - 2]; + x1[mid + n] = (short)Inlines.silk_RSHIFT_ROUND(sum, 1); + side[n] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(diff, 1)); + } + + /* Buffering */ + Array.Copy(state.sMid, 0, x1, mid, 2); + Array.Copy(state.sSide, side, 2); + Array.Copy(x1, mid + frame_length, state.sMid, 0, 2); + Array.Copy(side, frame_length, state.sSide, 0, 2); + + /* LP and HP filter mid signal */ + LP_mid = new short[frame_length]; + HP_mid = new short[frame_length]; + for (n = 0; n < frame_length; n++) + { + sum = Inlines.silk_RSHIFT_ROUND(Inlines.silk_ADD_LSHIFT32(x1[mid + n] + x1[mid + n + 2], x1[mid + n + 1], 1), 2); + LP_mid[n] = (short)(sum); + HP_mid[n] = (short)(x1[mid + n + 1] - sum); + } + + /* LP and HP filter side signal */ + LP_side = new short[frame_length]; + HP_side = new short[frame_length]; + for (n = 0; n < frame_length; n++) + { + sum = Inlines.silk_RSHIFT_ROUND(Inlines.silk_ADD_LSHIFT32(side[n] + side[n + 2], side[n + 1], 1), 2); + LP_side[n] = (short)(sum); + HP_side[n] = (short)(side[n + 1] - sum); + } + + /* Find energies and predictors */ + is10msFrame = (frame_length == 10 * fs_kHz ? 1 : 0); + smooth_coef_Q16 = is10msFrame != 0 ? + ((int)((SilkConstants.STEREO_RATIO_SMOOTH_COEF / 2) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.STEREO_RATIO_SMOOTH_COEF / 2, 16)*/ : + ((int)((SilkConstants.STEREO_RATIO_SMOOTH_COEF) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.STEREO_RATIO_SMOOTH_COEF, 16)*/; + smooth_coef_Q16 = Inlines.silk_SMULWB(Inlines.silk_SMULBB(prev_speech_act_Q8, prev_speech_act_Q8), smooth_coef_Q16); + + pred_Q13[0] = silk_stereo_find_predictor(LP_ratio_Q14, LP_mid, LP_side, state.mid_side_amp_Q0, 0, frame_length, smooth_coef_Q16); + pred_Q13[1] = silk_stereo_find_predictor(HP_ratio_Q14, HP_mid, HP_side, state.mid_side_amp_Q0, 2, frame_length, smooth_coef_Q16); + + /* Ratio of the norms of residual and mid signals */ + frac_Q16 = Inlines.silk_SMLABB(HP_ratio_Q14.Val, LP_ratio_Q14.Val, 3); + frac_Q16 = Inlines.silk_min(frac_Q16, ((int)((1) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1, 16)*/); + + /* Determine bitrate distribution between mid and side, and possibly reduce stereo width */ + total_rate_bps -= is10msFrame != 0 ? 1200 : 600; /* Subtract approximate bitrate for coding stereo parameters */ + if (total_rate_bps < 1) + { + total_rate_bps = 1; + } + min_mid_rate_bps = Inlines.silk_SMLABB(2000, fs_kHz, 900); + Inlines.OpusAssert(min_mid_rate_bps < 32767); + /* Default bitrate distribution: 8 parts for Mid and (5+3*frac) parts for Side. so: mid_rate = ( 8 / ( 13 + 3 * frac ) ) * total_ rate */ + frac_3_Q16 = Inlines.silk_MUL(3, frac_Q16); + mid_side_rates_bps[0] = Inlines.silk_DIV32_varQ(total_rate_bps, ((int)((8 + 5) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(8 + 5, 16)*/ + frac_3_Q16, 16 + 3); + /* If Mid bitrate below minimum, reduce stereo width */ + if (mid_side_rates_bps[0] < min_mid_rate_bps) + { + mid_side_rates_bps[0] = min_mid_rate_bps; + mid_side_rates_bps[1] = total_rate_bps - mid_side_rates_bps[0]; + /* width = 4 * ( 2 * side_rate - min_rate ) / ( ( 1 + 3 * frac ) * min_rate ) */ + width_Q14 = Inlines.silk_DIV32_varQ(Inlines.silk_LSHIFT(mid_side_rates_bps[1], 1) - min_mid_rate_bps, + Inlines.silk_SMULWB(((int)((1) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1, 16)*/ + frac_3_Q16, min_mid_rate_bps), 14 + 2); + width_Q14 = Inlines.silk_LIMIT(width_Q14, 0, ((int)((1) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(1, 14)*/); + } + else { + mid_side_rates_bps[1] = total_rate_bps - mid_side_rates_bps[0]; + width_Q14 = ((int)((1) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(1, 14)*/; + } + + /* Smoother */ + state.smth_width_Q14 = (short)Inlines.silk_SMLAWB(state.smth_width_Q14, width_Q14 - state.smth_width_Q14, smooth_coef_Q16); + + /* At very low bitrates or for inputs that are nearly amplitude panned, switch to panned-mono coding */ + mid_only_flag.Val = 0; + if (toMono != 0) + { + /* Last frame before stereo.mono transition; collapse stereo width */ + width_Q14 = 0; + pred_Q13[0] = 0; + pred_Q13[1] = 0; + silk_stereo_quant_pred(pred_Q13, ix); + } + else if (state.width_prev_Q14 == 0 && + (8 * total_rate_bps < 13 * min_mid_rate_bps || Inlines.silk_SMULWB(frac_Q16, state.smth_width_Q14) < ((int)((0.05f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(0.05f, 14)*/)) + { + /* Code as panned-mono; previous frame already had zero width */ + /* Scale down and quantize predictors */ + pred_Q13[0] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[0]), 14); + pred_Q13[1] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[1]), 14); + silk_stereo_quant_pred(pred_Q13, ix); + /* Collapse stereo width */ + width_Q14 = 0; + pred_Q13[0] = 0; + pred_Q13[1] = 0; + mid_side_rates_bps[0] = total_rate_bps; + mid_side_rates_bps[1] = 0; + mid_only_flag.Val = 1; + } + else if (state.width_prev_Q14 != 0 && + (8 * total_rate_bps < 11 * min_mid_rate_bps || Inlines.silk_SMULWB(frac_Q16, state.smth_width_Q14) < ((int)((0.02f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(0.02f, 14)*/)) + { + /* Transition to zero-width stereo */ + /* Scale down and quantize predictors */ + pred_Q13[0] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[0]), 14); + pred_Q13[1] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[1]), 14); + silk_stereo_quant_pred(pred_Q13, ix); + /* Collapse stereo width */ + width_Q14 = 0; + pred_Q13[0] = 0; + pred_Q13[1] = 0; + } + else if (state.smth_width_Q14 > ((int)((0.95f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(0.95f, 14)*/) + { + /* Full-width stereo coding */ + silk_stereo_quant_pred(pred_Q13, ix); + width_Q14 = ((int)((1) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(1, 14)*/; + } + else + { + /* Reduced-width stereo coding; scale down and quantize predictors */ + pred_Q13[0] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[0]), 14); + pred_Q13[1] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[1]), 14); + silk_stereo_quant_pred(pred_Q13, ix); + width_Q14 = state.smth_width_Q14; + } + + /* Make sure to keep on encoding until the tapered output has been transmitted */ + if (mid_only_flag.Val == 1) + { + state.silent_side_len += (short)(frame_length - SilkConstants.STEREO_INTERP_LEN_MS * fs_kHz); + if (state.silent_side_len < SilkConstants.LA_SHAPE_MS * fs_kHz) + { + mid_only_flag.Val = 0; + } + else { + /* Limit to avoid wrapping around */ + state.silent_side_len = 10000; + } + } + else { + state.silent_side_len = 0; + } + + if (mid_only_flag.Val == 0 && mid_side_rates_bps[1] < 1) + { + mid_side_rates_bps[1] = 1; + mid_side_rates_bps[0] = Inlines.silk_max_int(1, total_rate_bps - mid_side_rates_bps[1]); + } + + /* Interpolate predictors and subtract prediction from side channel */ + pred0_Q13 = -state.pred_prev_Q13[0]; + pred1_Q13 = -state.pred_prev_Q13[1]; + w_Q24 = Inlines.silk_LSHIFT(state.width_prev_Q14, 10); + denom_Q16 = Inlines.silk_DIV32_16((int)1 << 16, SilkConstants.STEREO_INTERP_LEN_MS * fs_kHz); + delta0_Q13 = 0 - Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULBB(pred_Q13[0] - state.pred_prev_Q13[0], denom_Q16), 16); + delta1_Q13 = 0 - Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULBB(pred_Q13[1] - state.pred_prev_Q13[1], denom_Q16), 16); + deltaw_Q24 = Inlines.silk_LSHIFT(Inlines.silk_SMULWB(width_Q14 - state.width_prev_Q14, denom_Q16), 10); + for (n = 0; n < SilkConstants.STEREO_INTERP_LEN_MS * fs_kHz; n++) + { + pred0_Q13 += delta0_Q13; + pred1_Q13 += delta1_Q13; + w_Q24 += deltaw_Q24; + sum = Inlines.silk_LSHIFT(Inlines.silk_ADD_LSHIFT(x1[mid + n] + x1[mid + n + 2], x1[mid + n + 1], 1), 9); /* Q11 */ + sum = Inlines.silk_SMLAWB(Inlines.silk_SMULWB(w_Q24, side[n + 1]), sum, pred0_Q13); /* Q8 */ + sum = Inlines.silk_SMLAWB(sum, Inlines.silk_LSHIFT((int)x1[mid + n + 1], 11), pred1_Q13); /* Q8 */ + x2[x2_ptr + n - 1] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(sum, 8)); + } + + pred0_Q13 = 0 - pred_Q13[0]; + pred1_Q13 = 0 - pred_Q13[1]; + w_Q24 = Inlines.silk_LSHIFT(width_Q14, 10); + for (n = SilkConstants.STEREO_INTERP_LEN_MS * fs_kHz; n < frame_length; n++) + { + sum = Inlines.silk_LSHIFT(Inlines.silk_ADD_LSHIFT(x1[mid + n] + x1[mid + n + 2], x1[mid + n + 1], 1), 9); /* Q11 */ + sum = Inlines.silk_SMLAWB(Inlines.silk_SMULWB(w_Q24, side[n + 1]), sum, pred0_Q13); /* Q8 */ + sum = Inlines.silk_SMLAWB(sum, Inlines.silk_LSHIFT((int)x1[mid + n + 1], 11), pred1_Q13); /* Q8 */ + x2[x2_ptr + n - 1] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(sum, 8)); + } + state.pred_prev_Q13[0] = (short)pred_Q13[0]; + state.pred_prev_Q13[1] = (short)pred_Q13[1]; + state.width_prev_Q14 = (short)width_Q14; + } + + /// + /// Convert adaptive Mid/Side representation to Left/Right stereo signal + /// + /// I/O State + /// I/O Left input signal, becomes mid signal + /// I/O Right input signal, becomes side signal + /// I Predictors + /// I Samples rate (kHz) + /// I Number of samples + internal static void silk_stereo_MS_to_LR( + StereoDecodeState state, + short[] x1, + int x1_ptr, + short[] x2, + int x2_ptr, + int[] pred_Q13, + int fs_kHz, + int frame_length) + { + int n, denom_Q16, delta0_Q13, delta1_Q13; + int sum, diff, pred0_Q13, pred1_Q13; + + /* Buffering */ + Array.Copy(state.sMid, 0, x1, x1_ptr, 2); + Array.Copy(state.sSide, 0, x2, x2_ptr, 2); + Array.Copy(x1, x1_ptr + frame_length, state.sMid, 0, 2); + Array.Copy(x2, x2_ptr + frame_length, state.sSide, 0, 2); + + /* Interpolate predictors and add prediction to side channel */ + pred0_Q13 = state.pred_prev_Q13[0]; + pred1_Q13 = state.pred_prev_Q13[1]; + denom_Q16 = Inlines.silk_DIV32_16((int)1 << 16, SilkConstants.STEREO_INTERP_LEN_MS * fs_kHz); + delta0_Q13 = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULBB(pred_Q13[0] - state.pred_prev_Q13[0], denom_Q16), 16); + delta1_Q13 = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULBB(pred_Q13[1] - state.pred_prev_Q13[1], denom_Q16), 16); + for (n = 0; n < SilkConstants.STEREO_INTERP_LEN_MS * fs_kHz; n++) + { + pred0_Q13 += delta0_Q13; + pred1_Q13 += delta1_Q13; + sum = Inlines.silk_LSHIFT(Inlines.silk_ADD_LSHIFT(x1[x1_ptr + n] + x1[x1_ptr + n + 2], x1[x1_ptr + n + 1], 1), 9); /* Q11 */ + sum = Inlines.silk_SMLAWB(Inlines.silk_LSHIFT((int)x2[x2_ptr + n + 1], 8), sum, pred0_Q13); /* Q8 */ + sum = Inlines.silk_SMLAWB(sum, Inlines.silk_LSHIFT((int)x1[x1_ptr + n + 1], 11), pred1_Q13); /* Q8 */ + x2[x2_ptr + n + 1] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(sum, 8)); + } + pred0_Q13 = pred_Q13[0]; + pred1_Q13 = pred_Q13[1]; + for (n = SilkConstants.STEREO_INTERP_LEN_MS * fs_kHz; n < frame_length; n++) + { + sum = Inlines.silk_LSHIFT(Inlines.silk_ADD_LSHIFT(x1[x1_ptr + n] + x1[x1_ptr + n + 2], x1[x1_ptr + n + 1], 1), 9); /* Q11 */ + sum = Inlines.silk_SMLAWB(Inlines.silk_LSHIFT((int)x2[x2_ptr + n + 1], 8), sum, pred0_Q13); /* Q8 */ + sum = Inlines.silk_SMLAWB(sum, Inlines.silk_LSHIFT((int)x1[x1_ptr + n + 1], 11), pred1_Q13); /* Q8 */ + x2[x2_ptr + n + 1] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(sum, 8)); + } + state.pred_prev_Q13[0] = (short)(pred_Q13[0]); + state.pred_prev_Q13[1] = (short)(pred_Q13[1]); + + /* Convert to left/right signals */ + for (n = 0; n < frame_length; n++) + { + sum = x1[x1_ptr + n + 1] + (int)x2[x2_ptr + n + 1]; + diff = x1[x1_ptr + n + 1] - (int)x2[x2_ptr + n + 1]; + x1[x1_ptr + n + 1] = (short)Inlines.silk_SAT16(sum); + x2[x2_ptr + n + 1] = (short)Inlines.silk_SAT16(diff); + } + } + + /// + /// Quantize mid/side predictors + /// + /// I/O Predictors (out: quantized) + /// O Quantization indices [ 2 ][ 3 ] + internal static void silk_stereo_quant_pred( + int[] pred_Q13, + sbyte[][] ix) + { + sbyte i, j; // [porting note] these were originally ints + int n; + int low_Q13, step_Q13, lvl_Q13, err_min_Q13, err_Q13, quant_pred_Q13 = 0; + + // FIXME: ix was formerly an out parameter that was newly allocated here + // but now it relies on the caller to initialize it + // clear ix + Arrays.MemSetSbyte(ix[0], 0, 3); + Arrays.MemSetSbyte(ix[1], 0, 3); + + /* Quantize */ + for (n = 0; n < 2; n++) + { + /* Brute-force search over quantization levels */ + err_min_Q13 = int.MaxValue; + for (i = 0; i < SilkConstants.STEREO_QUANT_TAB_SIZE - 1; i++) + { + low_Q13 = Tables.silk_stereo_pred_quant_Q13[i]; + step_Q13 = Inlines.silk_SMULWB(Tables.silk_stereo_pred_quant_Q13[i + 1] - low_Q13, + ((int)((0.5f / SilkConstants.STEREO_QUANT_SUB_STEPS) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.5f / SilkConstants.STEREO_QUANT_SUB_STEPS, 16)*/); + + for (j = 0; j < SilkConstants.STEREO_QUANT_SUB_STEPS; j++) + { + lvl_Q13 = Inlines.silk_SMLABB(low_Q13, step_Q13, 2 * j + 1); + err_Q13 = Inlines.silk_abs(pred_Q13[n] - lvl_Q13); + if (err_Q13 < err_min_Q13) + { + err_min_Q13 = err_Q13; + quant_pred_Q13 = lvl_Q13; + ix[n][0] = i; + ix[n][1] = j; + } + else + { + /* Error increasing, so we're past the optimum */ + // FIXME: get this crap out of here + goto done; + } + } + } + + done: + ix[n][2] = (sbyte)(Inlines.silk_DIV32_16(ix[n][0], 3)); + ix[n][0] = (sbyte)(ix[n][0] - (sbyte)(ix[n][2] * 3)); + pred_Q13[n] = quant_pred_Q13; + } + + /* Subtract second from first predictor (helps when actually applying these) */ + pred_Q13[0] -= pred_Q13[1]; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Structs/CNGState.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/CNGState.cs new file mode 100644 index 000000000..cbfbdbae6 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/CNGState.cs @@ -0,0 +1,59 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk.Structs +{ + using Concentus.Common.CPlusPlus; + + /// + /// Struct for CNG + /// + internal class CNGState + { + internal readonly int[] CNG_exc_buf_Q14 = new int[SilkConstants.MAX_FRAME_LENGTH]; + internal readonly short[] CNG_smth_NLSF_Q15 = new short[SilkConstants.MAX_LPC_ORDER]; + internal readonly int[] CNG_synth_state = new int[SilkConstants.MAX_LPC_ORDER]; + internal int CNG_smth_Gain_Q16 = 0; + internal int rand_seed = 0; + internal int fs_kHz = 0; + + internal void Reset() + { + Arrays.MemSetInt(CNG_exc_buf_Q14, 0, SilkConstants.MAX_FRAME_LENGTH); + Arrays.MemSetShort(CNG_smth_NLSF_Q15, 0, SilkConstants.MAX_LPC_ORDER); + Arrays.MemSetInt(CNG_synth_state, 0, SilkConstants.MAX_LPC_ORDER); + CNG_smth_Gain_Q16 = 0; + rand_seed = 0; + fs_kHz = 0; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Structs/DecControlState.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/DecControlState.cs new file mode 100644 index 000000000..0ba5fb268 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/DecControlState.cs @@ -0,0 +1,72 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk.Structs +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + + /// + /// Structure for controlling decoder operation and reading decoder status + /// + internal class DecControlState + { + /* I: Number of channels; 1/2 */ + internal int nChannelsAPI = 0; + + /* I: Number of channels; 1/2 */ + internal int nChannelsInternal = 0; + + /* I: Output signal sampling rate in Hertz; 8000/12000/16000/24000/32000/44100/48000 */ + internal int API_sampleRate = 0; + + /* I: Internal sampling rate used, in Hertz; 8000/12000/16000 */ + internal int internalSampleRate = 0; + + /* I: Number of samples per packet in milliseconds; 10/20/40/60 */ + internal int payloadSize_ms = 0; + + /* O: Pitch lag of previous frame (0 if unvoiced), measured in samples at 48 kHz */ + internal int prevPitchLag = 0; + + internal void Reset() + { + nChannelsAPI = 0; + nChannelsInternal = 0; + API_sampleRate = 0; + internalSampleRate = 0; + payloadSize_ms = 0; + prevPitchLag = 0; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Structs/EncControlState.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/EncControlState.cs new file mode 100644 index 000000000..7924fe310 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/EncControlState.cs @@ -0,0 +1,217 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk.Structs +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + + /// + /// Structure for controlling encoder operation + /// + internal class EncControlState + { + /* I: Number of channels; 1/2 */ + internal int nChannelsAPI = 0; + + /* I: Number of channels; 1/2 */ + internal int nChannelsInternal = 0; + + /* I: Input signal sampling rate in Hertz; 8000/12000/16000/24000/32000/44100/48000 */ + internal int API_sampleRate = 0; + + /* I: Maximum internal sampling rate in Hertz; 8000/12000/16000 */ + internal int maxInternalSampleRate = 0; + + /* I: Minimum internal sampling rate in Hertz; 8000/12000/16000 */ + internal int minInternalSampleRate = 0; + + /* I: Soft request for internal sampling rate in Hertz; 8000/12000/16000 */ + internal int desiredInternalSampleRate = 0; + + /* I: Number of samples per packet in milliseconds; 10/20/40/60 */ + internal int payloadSize_ms = 0; + + /* I: Bitrate during active speech in bits/second; internally limited */ + internal int bitRate = 0; + + /* I: Uplink packet loss in percent (0-100) */ + internal int packetLossPercentage = 0; + + /* I: Complexity mode; 0 is lowest, 10 is highest complexity */ + internal int complexity = 0; + + /* I: Flag to enable in-band Forward Error Correction (FEC); 0/1 */ + internal int useInBandFEC = 0; + + /* I: Flag to enable discontinuous transmission (DTX); 0/1 */ + internal int useDTX = 0; + + /* I: Flag to use constant bitrate */ + internal int useCBR = 0; + + /* I: Maximum number of bits allowed for the frame */ + internal int maxBits = 0; + + /* I: Causes a smooth downmix to mono */ + internal int toMono = 0; + + /* I: Opus encoder is allowing us to switch bandwidth */ + internal int opusCanSwitch = 0; + + /* I: Make frames as independent as possible (but still use LPC) */ + internal int reducedDependency = 0; + + /* O: Internal sampling rate used, in Hertz; 8000/12000/16000 */ + internal int internalSampleRate = 0; + + /* O: Flag that bandwidth switching is allowed (because low voice activity) */ + internal int allowBandwidthSwitch = 0; + + /* O: Flag that SILK runs in WB mode without variable LP filter (use for switching between WB/SWB/FB) */ + internal int inWBmodeWithoutVariableLP = 0; + + /* O: Stereo width */ + internal int stereoWidth_Q14 = 0; + + /* O: Tells the Opus encoder we're ready to switch */ + internal int switchReady = 0; + + internal void Reset() + { + nChannelsAPI = 0; + nChannelsInternal = 0; + API_sampleRate = 0; + maxInternalSampleRate = 0; + minInternalSampleRate = 0; + desiredInternalSampleRate = 0; + payloadSize_ms = 0; + bitRate = 0; + packetLossPercentage = 0; + complexity = 0; + useInBandFEC = 0; + useDTX = 0; + useCBR = 0; + maxBits = 0; + toMono = 0; + opusCanSwitch = 0; + reducedDependency = 0; + internalSampleRate = 0; + allowBandwidthSwitch = 0; + inWBmodeWithoutVariableLP = 0; + stereoWidth_Q14 = 0; + switchReady = 0; + } + + /// + /// Checks this encoder control struct and returns error code, if any + /// + /// + internal int check_control_input() + { + if (((API_sampleRate != 8000) && + (API_sampleRate != 12000) && + (API_sampleRate != 16000) && + (API_sampleRate != 24000) && + (API_sampleRate != 32000) && + (API_sampleRate != 44100) && + (API_sampleRate != 48000)) || + ((desiredInternalSampleRate != 8000) && + (desiredInternalSampleRate != 12000) && + (desiredInternalSampleRate != 16000)) || + ((maxInternalSampleRate != 8000) && + (maxInternalSampleRate != 12000) && + (maxInternalSampleRate != 16000)) || + ((minInternalSampleRate != 8000) && + (minInternalSampleRate != 12000) && + (minInternalSampleRate != 16000)) || + (minInternalSampleRate > desiredInternalSampleRate) || + (maxInternalSampleRate < desiredInternalSampleRate) || + (minInternalSampleRate > maxInternalSampleRate)) + { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_FS_NOT_SUPPORTED; + } + if (payloadSize_ms != 10 && + payloadSize_ms != 20 && + payloadSize_ms != 40 && + payloadSize_ms != 60) + { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_PACKET_SIZE_NOT_SUPPORTED; + } + if (packetLossPercentage < 0 || packetLossPercentage > 100) + { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_LOSS_RATE; + } + if (useDTX < 0 || useDTX > 1) + { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_DTX_SETTING; + } + if (useCBR < 0 || useCBR > 1) + { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_CBR_SETTING; + } + if (useInBandFEC < 0 || useInBandFEC > 1) + { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_INBAND_FEC_SETTING; + } + if (nChannelsAPI < 1 || nChannelsAPI > SilkConstants.ENCODER_NUM_CHANNELS) + { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR; + } + if (nChannelsInternal < 1 || nChannelsInternal > SilkConstants.ENCODER_NUM_CHANNELS) + { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR; + } + if (nChannelsInternal > nChannelsAPI) + { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR; + } + if (complexity < 0 || complexity > 10) + { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_COMPLEXITY_SETTING; + } + + return SilkError.SILK_NO_ERROR; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Structs/NLSFCodebook.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/NLSFCodebook.cs new file mode 100644 index 000000000..faa3105cd --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/NLSFCodebook.cs @@ -0,0 +1,108 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk.Structs +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + + /// + /// Structure containing NLSF codebook + /// + internal class NLSFCodebook + { + internal short nVectors = 0; + + internal short order = 0; + + /// + /// Quantization step size + /// + internal short quantStepSize_Q16 = 0; + + /// + /// Inverse quantization step size + /// + internal short invQuantStepSize_Q6 = 0; + + /// + /// POINTER + /// + internal byte[] CB1_NLSF_Q8 = null; + + /// + /// POINTER + /// + internal byte[] CB1_iCDF = null; + + /// + /// POINTER to Backward predictor coefs [ order ] + /// + internal byte[] pred_Q8 = null; + + /// + /// POINTER to Indices to entropy coding tables [ order ] + /// + internal byte[] ec_sel = null; + + /// + /// POINTER + /// + internal byte[] ec_iCDF = null; + + /// + /// POINTER + /// + internal byte[] ec_Rates_Q5 = null; + + /// + /// POINTER + /// + internal short[] deltaMin_Q15 = null; + + internal void Reset() + { + nVectors = 0; + order = 0; + quantStepSize_Q16 = 0; + invQuantStepSize_Q6 = 0; + CB1_NLSF_Q8 = null; + CB1_iCDF = null; + pred_Q8 = null; + ec_sel = null; + ec_iCDF = null; + ec_Rates_Q5 = null; + deltaMin_Q15 = null; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Structs/PLCStruct.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/PLCStruct.cs new file mode 100644 index 000000000..7764517c1 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/PLCStruct.cs @@ -0,0 +1,75 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk.Structs +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + + /// + /// Struct for Packet Loss Concealment + /// + internal class PLCStruct + { + internal int pitchL_Q8 = 0; /* Pitch lag to use for voiced concealment */ + internal readonly short[] LTPCoef_Q14 = new short[SilkConstants.LTP_ORDER]; /* LTP coeficients to use for voiced concealment */ + internal readonly short[] prevLPC_Q12 = new short[SilkConstants.MAX_LPC_ORDER]; + internal int last_frame_lost = 0; /* Was previous frame lost */ + internal int rand_seed = 0; /* Seed for unvoiced signal generation */ + internal short randScale_Q14 = 0; /* Scaling of unvoiced random signal */ + internal int conc_energy = 0; + internal int conc_energy_shift = 0; + internal short prevLTP_scale_Q14 = 0; + internal readonly int[] prevGain_Q16 = new int[2]; + internal int fs_kHz = 0; + internal int nb_subfr = 0; + internal int subfr_length = 0; + + internal void Reset() + { + pitchL_Q8 = 0; + Arrays.MemSetShort(LTPCoef_Q14, 0, SilkConstants.LTP_ORDER); + Arrays.MemSetShort(prevLPC_Q12, 0, SilkConstants.MAX_LPC_ORDER); + last_frame_lost = 0; + rand_seed = 0; + randScale_Q14 = 0; + conc_energy = 0; + conc_energy_shift = 0; + prevLTP_scale_Q14 = 0; + Arrays.MemSetInt(prevGain_Q16, 0, 2); + fs_kHz = 0; + nb_subfr = 0; + subfr_length = 0; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SideInfoIndices.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SideInfoIndices.cs new file mode 100644 index 000000000..fe09b9c5e --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SideInfoIndices.cs @@ -0,0 +1,88 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk.Structs +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using System; + + internal class SideInfoIndices + { + internal readonly sbyte[] GainsIndices = new sbyte[SilkConstants.MAX_NB_SUBFR]; + internal readonly sbyte[] LTPIndex = new sbyte[SilkConstants.MAX_NB_SUBFR]; + internal readonly sbyte[] NLSFIndices = new sbyte[SilkConstants.MAX_LPC_ORDER + 1]; + internal short lagIndex = 0; + internal sbyte contourIndex = 0; + internal sbyte signalType = 0; + internal sbyte quantOffsetType = 0; + internal sbyte NLSFInterpCoef_Q2 = 0; + internal sbyte PERIndex = 0; + internal sbyte LTP_scaleIndex = 0; + internal sbyte Seed = 0; + + internal void Reset() + { + Arrays.MemSetSbyte(GainsIndices, 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSetSbyte(LTPIndex, 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSetSbyte(NLSFIndices, 0, SilkConstants.MAX_LPC_ORDER + 1); + lagIndex = 0; + contourIndex = 0; + signalType = 0; + quantOffsetType = 0; + NLSFInterpCoef_Q2 = 0; + PERIndex = 0; + LTP_scaleIndex = 0; + Seed = 0; + } + + /// + /// Overwrites this struct with values from another one. Equivalent to C struct assignment this = other + /// + /// + internal void Assign(SideInfoIndices other) + { + Array.Copy(other.GainsIndices, this.GainsIndices, SilkConstants.MAX_NB_SUBFR); + Array.Copy(other.LTPIndex, this.LTPIndex, SilkConstants.MAX_NB_SUBFR); + Array.Copy(other.NLSFIndices, this.NLSFIndices, SilkConstants.MAX_LPC_ORDER + 1); + this.lagIndex = other.lagIndex; + this.contourIndex = other.contourIndex; + this.signalType = other.signalType; + this.quantOffsetType = other.quantOffsetType; + this.NLSFInterpCoef_Q2 = other.NLSFInterpCoef_Q2; + this.PERIndex = other.PERIndex; + this.LTP_scaleIndex = other.LTP_scaleIndex; + this.Seed = other.Seed; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkChannelDecoder.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkChannelDecoder.cs new file mode 100644 index 000000000..1cb6954a2 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkChannelDecoder.cs @@ -0,0 +1,359 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk.Structs +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using System; + /// + /// Decoder state + /// + internal class SilkChannelDecoder + { + internal int prev_gain_Q16 = 0; + internal readonly int[] exc_Q14 = new int[SilkConstants.MAX_FRAME_LENGTH]; + internal readonly int[] sLPC_Q14_buf = new int[SilkConstants.MAX_LPC_ORDER]; + internal readonly short[] outBuf = new short[SilkConstants.MAX_FRAME_LENGTH + 2 * SilkConstants.MAX_SUB_FRAME_LENGTH]; /* Buffer for output signal */ + internal int lagPrev = 0; /* Previous Lag */ + internal sbyte LastGainIndex = 0; /* Previous gain index */ + internal int fs_kHz = 0; /* Sampling frequency in kHz */ + internal int fs_API_hz = 0; /* API sample frequency (Hz) */ + internal int nb_subfr = 0; /* Number of 5 ms subframes in a frame */ + internal int frame_length = 0; /* Frame length (samples) */ + internal int subfr_length = 0; /* Subframe length (samples) */ + internal int ltp_mem_length = 0; /* Length of LTP memory */ + internal int LPC_order = 0; /* LPC order */ + internal readonly short[] prevNLSF_Q15 = new short[SilkConstants.MAX_LPC_ORDER]; /* Used to interpolate LSFs */ + internal int first_frame_after_reset = 0; /* Flag for deactivating NLSF interpolation */ + internal byte[] pitch_lag_low_bits_iCDF; /* Pointer to iCDF table for low bits of pitch lag index */ + internal byte[] pitch_contour_iCDF; /* Pointer to iCDF table for pitch contour index */ + + /* For buffering payload in case of more frames per packet */ + internal int nFramesDecoded = 0; + internal int nFramesPerPacket = 0; + + /* Specifically for entropy coding */ + internal int ec_prevSignalType = 0; + internal short ec_prevLagIndex = 0; + + internal readonly int[] VAD_flags = new int[SilkConstants.MAX_FRAMES_PER_PACKET]; + internal int LBRR_flag = 0; + internal readonly int[] LBRR_flags = new int[SilkConstants.MAX_FRAMES_PER_PACKET]; + + internal readonly SilkResamplerState resampler_state = new SilkResamplerState(); + + internal NLSFCodebook psNLSF_CB = null; /* Pointer to NLSF codebook */ + + /* Quantization indices */ + internal readonly SideInfoIndices indices = new SideInfoIndices(); + + /* CNG state */ + internal readonly CNGState sCNG = new CNGState(); + + /* Stuff used for PLC */ + internal int lossCnt = 0; + internal int prevSignalType = 0; + + internal readonly PLCStruct sPLC = new PLCStruct(); + + internal void Reset() + { + prev_gain_Q16 = 0; + Arrays.MemSetInt(exc_Q14, 0, SilkConstants.MAX_FRAME_LENGTH); + Arrays.MemSetInt(sLPC_Q14_buf, 0, SilkConstants.MAX_LPC_ORDER); + Arrays.MemSetShort(outBuf, 0, SilkConstants.MAX_FRAME_LENGTH + 2 * SilkConstants.MAX_SUB_FRAME_LENGTH); + lagPrev = 0; + LastGainIndex = 0; + fs_kHz = 0; + fs_API_hz = 0; + nb_subfr = 0; + frame_length = 0; + subfr_length = 0; + ltp_mem_length = 0; + LPC_order = 0; + Arrays.MemSetShort(prevNLSF_Q15, 0, SilkConstants.MAX_LPC_ORDER); + first_frame_after_reset = 0; + pitch_lag_low_bits_iCDF = null; + pitch_contour_iCDF = null; + nFramesDecoded = 0; + nFramesPerPacket = 0; + ec_prevSignalType = 0; + ec_prevLagIndex = 0; + Arrays.MemSetInt(VAD_flags, 0, SilkConstants.MAX_FRAMES_PER_PACKET); + LBRR_flag = 0; + Arrays.MemSetInt(LBRR_flags, 0, SilkConstants.MAX_FRAMES_PER_PACKET); + resampler_state.Reset(); + psNLSF_CB = null; + indices.Reset(); + sCNG.Reset(); + lossCnt = 0; + prevSignalType = 0; + sPLC.Reset(); + } + + /// + /// Init Decoder State + /// + /// + internal int silk_init_decoder() + { + /* Clear the entire encoder state, except anything copied */ + this.Reset(); + + /* Used to deactivate LSF interpolation */ + this.first_frame_after_reset = 1; + this.prev_gain_Q16 = 65536; + + /* Reset CNG state */ + silk_CNG_Reset(); + + /* Reset PLC state */ + silk_PLC_Reset(); + + return (0); + } + + /// + /// Resets CNG state + /// + private void silk_CNG_Reset() + { + int i, NLSF_step_Q15, NLSF_acc_Q15; + + NLSF_step_Q15 = Inlines.silk_DIV32_16(short.MaxValue, this.LPC_order + 1); + NLSF_acc_Q15 = 0; + for (i = 0; i < this.LPC_order; i++) + { + NLSF_acc_Q15 += NLSF_step_Q15; + this.sCNG.CNG_smth_NLSF_Q15[i] = (short)(NLSF_acc_Q15); + } + this.sCNG.CNG_smth_Gain_Q16 = 0; + this.sCNG.rand_seed = 3176576; + } + + /// + /// Resets PLC state + /// + private void silk_PLC_Reset() + { + this.sPLC.pitchL_Q8 = Inlines.silk_LSHIFT(this.frame_length, 8 - 1); + this.sPLC.prevGain_Q16[0] = ((int)((1) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1, 16)*/; + this.sPLC.prevGain_Q16[1] = ((int)((1) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1, 16)*/; + this.sPLC.subfr_length = 20; + this.sPLC.nb_subfr = 2; + } + + /* Set decoder sampling rate */ + internal int silk_decoder_set_fs( + int fs_kHz, /* I Sampling frequency (kHz) */ + int fs_API_Hz /* I API Sampling frequency (Hz) */ + ) + { + int frame_length, ret = 0; + + Inlines.OpusAssert(fs_kHz == 8 || fs_kHz == 12 || fs_kHz == 16); + Inlines.OpusAssert(this.nb_subfr == SilkConstants.MAX_NB_SUBFR || this.nb_subfr == SilkConstants.MAX_NB_SUBFR / 2); + + /* New (sub)frame length */ + this.subfr_length = Inlines.silk_SMULBB(SilkConstants.SUB_FRAME_LENGTH_MS, fs_kHz); + frame_length = Inlines.silk_SMULBB(this.nb_subfr, this.subfr_length); + + /* Initialize resampler when switching internal or external sampling frequency */ + if (this.fs_kHz != fs_kHz || this.fs_API_hz != fs_API_Hz) + { + /* Initialize the resampler for dec_API.c preparing resampling from fs_kHz to API_fs_Hz */ + ret += Resampler.silk_resampler_init(this.resampler_state, Inlines.silk_SMULBB(fs_kHz, 1000), fs_API_Hz, 0); + + this.fs_API_hz = fs_API_Hz; + } + + if (this.fs_kHz != fs_kHz || frame_length != this.frame_length) + { + if (fs_kHz == 8) + { + if (this.nb_subfr == SilkConstants.MAX_NB_SUBFR) + { + this.pitch_contour_iCDF = Tables.silk_pitch_contour_NB_iCDF; + } + else { + this.pitch_contour_iCDF = Tables.silk_pitch_contour_10_ms_NB_iCDF; + } + } + else { + if (this.nb_subfr == SilkConstants.MAX_NB_SUBFR) + { + this.pitch_contour_iCDF = Tables.silk_pitch_contour_iCDF; + } + else { + this.pitch_contour_iCDF = Tables.silk_pitch_contour_10_ms_iCDF; + } + } + if (this.fs_kHz != fs_kHz) + { + this.ltp_mem_length = Inlines.silk_SMULBB(SilkConstants.LTP_MEM_LENGTH_MS, fs_kHz); + if (fs_kHz == 8 || fs_kHz == 12) + { + this.LPC_order = SilkConstants.MIN_LPC_ORDER; + this.psNLSF_CB = Tables.silk_NLSF_CB_NB_MB; + } + else { + this.LPC_order = SilkConstants.MAX_LPC_ORDER; + this.psNLSF_CB = Tables.silk_NLSF_CB_WB; + } + if (fs_kHz == 16) + { + this.pitch_lag_low_bits_iCDF = Tables.silk_uniform8_iCDF; + } + else if (fs_kHz == 12) + { + this.pitch_lag_low_bits_iCDF = Tables.silk_uniform6_iCDF; + } + else if (fs_kHz == 8) + { + this.pitch_lag_low_bits_iCDF = Tables.silk_uniform4_iCDF; + } + else { + /* unsupported sampling rate */ + Inlines.OpusAssert(false); + } + this.first_frame_after_reset = 1; + this.lagPrev = 100; + this.LastGainIndex = 10; + this.prevSignalType = SilkConstants.TYPE_NO_VOICE_ACTIVITY; + Arrays.MemSetShort(this.outBuf, 0, SilkConstants.MAX_FRAME_LENGTH + 2 * SilkConstants.MAX_SUB_FRAME_LENGTH); + Arrays.MemSetInt(this.sLPC_Q14_buf, 0, SilkConstants.MAX_LPC_ORDER); + } + + this.fs_kHz = fs_kHz; + this.frame_length = frame_length; + } + + /* Check that settings are valid */ + Inlines.OpusAssert(this.frame_length > 0 && this.frame_length <= SilkConstants.MAX_FRAME_LENGTH); + + return ret; + } + + /****************/ + /* Decode frame */ + /****************/ + internal int silk_decode_frame( + EntropyCoder psRangeDec, /* I/O Compressor data structure */ + short[] pOut, /* O Pointer to output speech frame */ + int pOut_ptr, + BoxedValueInt pN, /* O Pointer to size of output frame */ + int lostFlag, /* I 0: no loss, 1 loss, 2 decode fec */ + int condCoding /* I The type of conditional coding to use */ + ) + { + SilkDecoderControl thisCtrl = new SilkDecoderControl(); + int L, mv_len, ret = 0; + + L = this.frame_length; + thisCtrl.LTP_scale_Q14 = 0; + + /* Safety checks */ + Inlines.OpusAssert(L > 0 && L <= SilkConstants.MAX_FRAME_LENGTH); + + if (lostFlag == DecoderAPIFlag.FLAG_DECODE_NORMAL || + (lostFlag == DecoderAPIFlag.FLAG_DECODE_LBRR && this.LBRR_flags[this.nFramesDecoded] == 1)) + { + short[] pulses = new short[(L + SilkConstants.SHELL_CODEC_FRAME_LENGTH - 1) & ~(SilkConstants.SHELL_CODEC_FRAME_LENGTH - 1)]; + /*********************************************/ + /* Decode quantization indices of side info */ + /*********************************************/ + DecodeIndices.silk_decode_indices(this, psRangeDec, this.nFramesDecoded, lostFlag, condCoding); + + /*********************************************/ + /* Decode quantization indices of excitation */ + /*********************************************/ + DecodePulses.silk_decode_pulses(psRangeDec, pulses, this.indices.signalType, + this.indices.quantOffsetType, this.frame_length); + + /********************************************/ + /* Decode parameters and pulse signal */ + /********************************************/ + DecodeParameters.silk_decode_parameters(this, thisCtrl, condCoding); + + /********************************************************/ + /* Run inverse NSQ */ + /********************************************************/ + DecodeCore.silk_decode_core(this, thisCtrl, pOut, pOut_ptr, pulses); + + /********************************************************/ + /* Update PLC state */ + /********************************************************/ + PLC.silk_PLC(this, thisCtrl, pOut, pOut_ptr, 0); + + this.lossCnt = 0; + this.prevSignalType = this.indices.signalType; + Inlines.OpusAssert(this.prevSignalType >= 0 && this.prevSignalType <= 2); + + /* A frame has been decoded without errors */ + this.first_frame_after_reset = 0; + } + else + { + /* Handle packet loss by extrapolation */ + PLC.silk_PLC(this, thisCtrl, pOut, pOut_ptr, 1); + } + + /*************************/ + /* Update output buffer. */ + /*************************/ + Inlines.OpusAssert(this.ltp_mem_length >= this.frame_length); + mv_len = this.ltp_mem_length - this.frame_length; + Arrays.MemMoveShort(this.outBuf, this.frame_length, 0, mv_len); + Array.Copy(pOut, pOut_ptr, this.outBuf, mv_len, this.frame_length); + + /************************************************/ + /* Comfort noise generation / estimation */ + /************************************************/ + CNG.silk_CNG(this, thisCtrl, pOut, pOut_ptr, L); + + /****************************************************************/ + /* Ensure smooth connection of extrapolated and good frames */ + /****************************************************************/ + PLC.silk_PLC_glue_frames(this, pOut, pOut_ptr, L); + + /* Update some decoder state variables */ + this.lagPrev = thisCtrl.pitchL[this.nb_subfr - 1]; + + /* Set output frame length */ + pN.Val = L; + + return ret; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkChannelEncoder.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkChannelEncoder.cs new file mode 100644 index 000000000..eb556ac97 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkChannelEncoder.cs @@ -0,0 +1,1264 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk.Structs +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using System; + /// + /// Encoder state + /// + internal class SilkChannelEncoder + { + internal readonly int[] In_HP_State = new int[2]; /* High pass filter state */ + internal int variable_HP_smth1_Q15 = 0; /* State of first smoother */ + internal int variable_HP_smth2_Q15 = 0; /* State of second smoother */ + internal readonly SilkLPState sLP = new SilkLPState(); /* Low pass filter state */ + internal readonly SilkVADState sVAD = new SilkVADState(); /* Voice activity detector state */ + internal readonly SilkNSQState sNSQ = new SilkNSQState(); /* Noise Shape Quantizer State */ + internal readonly short[] prev_NLSFq_Q15 = new short[SilkConstants.MAX_LPC_ORDER]; /* Previously quantized NLSF vector */ + internal int speech_activity_Q8 = 0; /* Speech activity */ + internal int allow_bandwidth_switch = 0; /* Flag indicating that switching of internal bandwidth is allowed */ + internal sbyte LBRRprevLastGainIndex = 0; + internal sbyte prevSignalType = 0; + internal int prevLag = 0; + internal int pitch_LPC_win_length = 0; + internal int max_pitch_lag = 0; /* Highest possible pitch lag (samples) */ + internal int API_fs_Hz = 0; /* API sampling frequency (Hz) */ + internal int prev_API_fs_Hz = 0; /* Previous API sampling frequency (Hz) */ + internal int maxInternal_fs_Hz = 0; /* Maximum internal sampling frequency (Hz) */ + internal int minInternal_fs_Hz = 0; /* Minimum internal sampling frequency (Hz) */ + internal int desiredInternal_fs_Hz = 0; /* Soft request for internal sampling frequency (Hz) */ + internal int fs_kHz = 0; /* Internal sampling frequency (kHz) */ + internal int nb_subfr = 0; /* Number of 5 ms subframes in a frame */ + internal int frame_length = 0; /* Frame length (samples) */ + internal int subfr_length = 0; /* Subframe length (samples) */ + internal int ltp_mem_length = 0; /* Length of LTP memory */ + internal int la_pitch = 0; /* Look-ahead for pitch analysis (samples) */ + internal int la_shape = 0; /* Look-ahead for noise shape analysis (samples) */ + internal int shapeWinLength = 0; /* Window length for noise shape analysis (samples) */ + internal int TargetRate_bps = 0; /* Target bitrate (bps) */ + internal int PacketSize_ms = 0; /* Number of milliseconds to put in each packet */ + internal int PacketLoss_perc = 0; /* Packet loss rate measured by farend */ + internal int frameCounter = 0; + internal int Complexity = 0; /* Complexity setting */ + internal int nStatesDelayedDecision = 0; /* Number of states in delayed decision quantization */ + internal int useInterpolatedNLSFs = 0; /* Flag for using NLSF interpolation */ + internal int shapingLPCOrder = 0; /* Filter order for noise shaping filters */ + internal int predictLPCOrder = 0; /* Filter order for prediction filters */ + internal int pitchEstimationComplexity = 0; /* Complexity level for pitch estimator */ + internal int pitchEstimationLPCOrder = 0; /* Whitening filter order for pitch estimator */ + internal int pitchEstimationThreshold_Q16 = 0; /* Threshold for pitch estimator */ + internal int LTPQuantLowComplexity = 0; /* Flag for low complexity LTP quantization */ + internal int mu_LTP_Q9 = 0; /* Rate-distortion tradeoff in LTP quantization */ + internal int sum_log_gain_Q7 = 0; /* Cumulative max prediction gain */ + internal int NLSF_MSVQ_Survivors = 0; /* Number of survivors in NLSF MSVQ */ + internal int first_frame_after_reset = 0; /* Flag for deactivating NLSF interpolation, pitch prediction */ + internal int controlled_since_last_payload = 0; /* Flag for ensuring codec_control only runs once per packet */ + internal int warping_Q16 = 0; /* Warping parameter for warped noise shaping */ + internal int useCBR = 0; /* Flag to enable constant bitrate */ + internal int prefillFlag = 0; /* Flag to indicate that only buffers are prefilled, no coding */ + internal byte[] pitch_lag_low_bits_iCDF = null; /* Pointer to iCDF table for low bits of pitch lag index */ + internal byte[] pitch_contour_iCDF = null; /* Pointer to iCDF table for pitch contour index */ + internal NLSFCodebook psNLSF_CB = null; /* Pointer to NLSF codebook */ + internal readonly int[] input_quality_bands_Q15 = new int[SilkConstants.VAD_N_BANDS]; + internal int input_tilt_Q15 = 0; + internal int SNR_dB_Q7 = 0; /* Quality setting */ + + internal readonly sbyte[] VAD_flags = new sbyte[SilkConstants.MAX_FRAMES_PER_PACKET]; + internal sbyte LBRR_flag = 0; + internal readonly int[] LBRR_flags = new int[SilkConstants.MAX_FRAMES_PER_PACKET]; + + internal readonly SideInfoIndices indices = new SideInfoIndices(); + internal readonly sbyte[] pulses = new sbyte[SilkConstants.MAX_FRAME_LENGTH]; + + /* Input/output buffering */ + internal readonly short[] inputBuf = new short[SilkConstants.MAX_FRAME_LENGTH + 2]; /* Buffer containing input signal */ + internal int inputBufIx = 0; + internal int nFramesPerPacket = 0; + internal int nFramesEncoded = 0; /* Number of frames analyzed in current packet */ + + internal int nChannelsAPI = 0; + internal int nChannelsInternal = 0; + internal int channelNb = 0; + + /* Parameters For LTP scaling Control */ + internal int frames_since_onset = 0; + + /* Specifically for entropy coding */ + internal int ec_prevSignalType = 0; + internal short ec_prevLagIndex = 0; + + internal readonly SilkResamplerState resampler_state = new SilkResamplerState(); + + /* DTX */ + internal int useDTX = 0; /* Flag to enable DTX */ + internal int inDTX = 0; /* Flag to signal DTX period */ + internal int noSpeechCounter = 0; /* Counts concecutive nonactive frames, used by DTX */ + + /* Inband Low Bitrate Redundancy (LBRR) data */ + internal int useInBandFEC = 0; /* Saves the API setting for query */ + internal int LBRR_enabled = 0; /* Depends on useInBandFRC, bitrate and packet loss rate */ + internal int LBRR_GainIncreases = 0; /* Gains increment for coding LBRR frames */ + internal readonly SideInfoIndices[] indices_LBRR = new SideInfoIndices[SilkConstants.MAX_FRAMES_PER_PACKET]; + internal readonly sbyte[][] pulses_LBRR = Arrays.InitTwoDimensionalArray(SilkConstants.MAX_FRAMES_PER_PACKET, SilkConstants.MAX_FRAME_LENGTH); + + /* Noise shaping state */ + internal readonly SilkShapeState sShape = new SilkShapeState(); + + /* Prefilter State */ + internal readonly SilkPrefilterState sPrefilt = new SilkPrefilterState(); + + /* Buffer for find pitch and noise shape analysis */ + internal readonly short[] x_buf = new short[2 * SilkConstants.MAX_FRAME_LENGTH + SilkConstants.LA_SHAPE_MAX]; + + /* Normalized correlation from pitch lag estimator */ + internal int LTPCorr_Q15 = 0; + + internal SilkChannelEncoder() + { + for (int c = 0; c < SilkConstants.MAX_FRAMES_PER_PACKET; c++) + { + indices_LBRR[c] = new SideInfoIndices(); + } + } + + internal void Reset() + { + Arrays.MemSetInt(In_HP_State, 0, 2); + variable_HP_smth1_Q15 = 0; + variable_HP_smth2_Q15 = 0; + sLP.Reset(); + sVAD.Reset(); + sNSQ.Reset(); + Arrays.MemSetShort(prev_NLSFq_Q15, 0, SilkConstants.MAX_LPC_ORDER); + speech_activity_Q8 = 0; + allow_bandwidth_switch = 0; + LBRRprevLastGainIndex = 0; + prevSignalType = 0; + prevLag = 0; + pitch_LPC_win_length = 0; + max_pitch_lag = 0; + API_fs_Hz = 0; + prev_API_fs_Hz = 0; + maxInternal_fs_Hz = 0; + minInternal_fs_Hz = 0; + desiredInternal_fs_Hz = 0; + fs_kHz = 0; + nb_subfr = 0; + frame_length = 0; + subfr_length = 0; + ltp_mem_length = 0; + la_pitch = 0; + la_shape = 0; + shapeWinLength = 0; + TargetRate_bps = 0; + PacketSize_ms = 0; + PacketLoss_perc = 0; + frameCounter = 0; + Complexity = 0; + nStatesDelayedDecision = 0; + useInterpolatedNLSFs = 0; + shapingLPCOrder = 0; + predictLPCOrder = 0; + pitchEstimationComplexity = 0; + pitchEstimationLPCOrder = 0; + pitchEstimationThreshold_Q16 = 0; + LTPQuantLowComplexity = 0; + mu_LTP_Q9 = 0; + sum_log_gain_Q7 = 0; + NLSF_MSVQ_Survivors = 0; + first_frame_after_reset = 0; + controlled_since_last_payload = 0; + warping_Q16 = 0; + useCBR = 0; + prefillFlag = 0; + pitch_lag_low_bits_iCDF = null; + pitch_contour_iCDF = null; + psNLSF_CB = null; + Arrays.MemSetInt(input_quality_bands_Q15, 0, SilkConstants.VAD_N_BANDS); + input_tilt_Q15 = 0; + SNR_dB_Q7 = 0; + Arrays.MemSetSbyte(VAD_flags, 0, SilkConstants.MAX_FRAMES_PER_PACKET); + LBRR_flag = 0; + Arrays.MemSetInt(LBRR_flags, 0, SilkConstants.MAX_FRAMES_PER_PACKET); + indices.Reset(); + Arrays.MemSetSbyte(pulses, 0, SilkConstants.MAX_FRAME_LENGTH); + Arrays.MemSetShort(inputBuf, 0, SilkConstants.MAX_FRAME_LENGTH + 2); + inputBufIx = 0; + nFramesPerPacket = 0; + nFramesEncoded = 0; + nChannelsAPI = 0; + nChannelsInternal = 0; + channelNb = 0; + frames_since_onset = 0; + ec_prevSignalType = 0; + ec_prevLagIndex = 0; + resampler_state.Reset(); + useDTX = 0; + inDTX = 0; + noSpeechCounter = 0; + useInBandFEC = 0; + LBRR_enabled = 0; + LBRR_GainIncreases = 0; + for (int c = 0; c < SilkConstants.MAX_FRAMES_PER_PACKET; c++) + { + indices_LBRR[c].Reset(); + Arrays.MemSetSbyte(pulses_LBRR[c], 0, SilkConstants.MAX_FRAME_LENGTH); + } + sShape.Reset(); + sPrefilt.Reset(); + Arrays.MemSetShort(x_buf, 0, 2 * SilkConstants.MAX_FRAME_LENGTH + SilkConstants.LA_SHAPE_MAX); + LTPCorr_Q15 = 0; + } + + /// + /// Control encoder + /// + /// I Control structure + /// I Target max bitrate (bps) + /// I Flag to allow switching audio bandwidth + /// I Channel number + /// + /// + internal int silk_control_encoder( + EncControlState encControl, + int TargetRate_bps, + int allow_bw_switch, + int channelNb, + int force_fs_kHz) + { + int fs_kHz; + int ret = SilkError.SILK_NO_ERROR; + + this.useDTX = encControl.useDTX; + this.useCBR = encControl.useCBR; + this.API_fs_Hz = encControl.API_sampleRate; + this.maxInternal_fs_Hz = encControl.maxInternalSampleRate; + this.minInternal_fs_Hz = encControl.minInternalSampleRate; + this.desiredInternal_fs_Hz = encControl.desiredInternalSampleRate; + this.useInBandFEC = encControl.useInBandFEC; + this.nChannelsAPI = encControl.nChannelsAPI; + this.nChannelsInternal = encControl.nChannelsInternal; + this.allow_bandwidth_switch = allow_bw_switch; + this.channelNb = channelNb; + + if (this.controlled_since_last_payload != 0 && this.prefillFlag == 0) + { + if (this.API_fs_Hz != this.prev_API_fs_Hz && this.fs_kHz > 0) + { + /* Change in API sampling rate in the middle of encoding a packet */ + ret = silk_setup_resamplers(this.fs_kHz); + } + return ret; + } + + /* Beyond this point we know that there are no previously coded frames in the payload buffer */ + + /********************************************/ + /* Determine internal sampling rate */ + /********************************************/ + fs_kHz = silk_control_audio_bandwidth(encControl); + if (force_fs_kHz != 0) + { + fs_kHz = force_fs_kHz; + } + /********************************************/ + /* Prepare resampler and buffered data */ + /********************************************/ + ret = silk_setup_resamplers(fs_kHz); + + /********************************************/ + /* Set internal sampling frequency */ + /********************************************/ + ret = silk_setup_fs(fs_kHz, encControl.payloadSize_ms); + + /********************************************/ + /* Set encoding complexity */ + /********************************************/ + ret = silk_setup_complexity(encControl.complexity); + + /********************************************/ + /* Set packet loss rate measured by farend */ + /********************************************/ + this.PacketLoss_perc = encControl.packetLossPercentage; + + /********************************************/ + /* Set LBRR usage */ + /********************************************/ + ret = silk_setup_LBRR(TargetRate_bps); + + this.controlled_since_last_payload = 1; + + return ret; + } + + /// + /// + /// + /// I + /// + private int silk_setup_resamplers(int fs_kHz) + { + int ret = 0; + + if (this.fs_kHz != fs_kHz || this.prev_API_fs_Hz != this.API_fs_Hz) + { + if (this.fs_kHz == 0) + { + /* Initialize the resampler for enc_API.c preparing resampling from API_fs_Hz to fs_kHz */ + ret += Resampler.silk_resampler_init(this.resampler_state, this.API_fs_Hz, fs_kHz * 1000, 1); + } + else + { + short[] x_buf_API_fs_Hz; + SilkResamplerState temp_resampler_state = null; + + int api_buf_samples; + int old_buf_samples; + int buf_length_ms; + + buf_length_ms = Inlines.silk_LSHIFT(this.nb_subfr * 5, 1) + SilkConstants.LA_SHAPE_MS; + old_buf_samples = buf_length_ms * this.fs_kHz; + + /* Initialize resampler for temporary resampling of x_buf data to API_fs_Hz */ + temp_resampler_state = new SilkResamplerState(); + ret += Resampler.silk_resampler_init(temp_resampler_state, Inlines.silk_SMULBB(this.fs_kHz, 1000), this.API_fs_Hz, 0); + + /* Calculate number of samples to temporarily upsample */ + api_buf_samples = buf_length_ms * Inlines.silk_DIV32_16(this.API_fs_Hz, 1000); + + /* Temporary resampling of x_buf data to API_fs_Hz */ + x_buf_API_fs_Hz = new short[api_buf_samples]; + ret += Resampler.silk_resampler(temp_resampler_state, x_buf_API_fs_Hz, 0, this.x_buf, 0, old_buf_samples); + + /* Initialize the resampler for enc_API.c preparing resampling from API_fs_Hz to fs_kHz */ + ret += Resampler.silk_resampler_init(this.resampler_state, this.API_fs_Hz, Inlines.silk_SMULBB(fs_kHz, 1000), 1); + + /* Correct resampler state by resampling buffered data from API_fs_Hz to fs_kHz */ + ret += Resampler.silk_resampler(this.resampler_state, this.x_buf, 0, x_buf_API_fs_Hz, 0, api_buf_samples); + } + } + + this.prev_API_fs_Hz = this.API_fs_Hz; + + return ret; + } + + /// + /// + /// + /// I + /// I + /// + private int silk_setup_fs( + int fs_kHz, + int PacketSize_ms) + { + int ret = SilkError.SILK_NO_ERROR; + + /* Set packet size */ + if (PacketSize_ms != this.PacketSize_ms) + { + if ((PacketSize_ms != 10) && + (PacketSize_ms != 20) && + (PacketSize_ms != 40) && + (PacketSize_ms != 60)) + { + ret = SilkError.SILK_ENC_PACKET_SIZE_NOT_SUPPORTED; + } + if (PacketSize_ms <= 10) + { + this.nFramesPerPacket = 1; + this.nb_subfr = PacketSize_ms == 10 ? 2 : 1; + this.frame_length = Inlines.silk_SMULBB(PacketSize_ms, fs_kHz); + this.pitch_LPC_win_length = Inlines.silk_SMULBB(SilkConstants.FIND_PITCH_LPC_WIN_MS_2_SF, fs_kHz); + if (this.fs_kHz == 8) + { + this.pitch_contour_iCDF = Tables.silk_pitch_contour_10_ms_NB_iCDF; + } + else { + this.pitch_contour_iCDF = Tables.silk_pitch_contour_10_ms_iCDF; + } + } + else { + this.nFramesPerPacket = Inlines.silk_DIV32_16(PacketSize_ms, SilkConstants.MAX_FRAME_LENGTH_MS); + this.nb_subfr = SilkConstants.MAX_NB_SUBFR; + this.frame_length = Inlines.silk_SMULBB(20, fs_kHz); + this.pitch_LPC_win_length = Inlines.silk_SMULBB(SilkConstants.FIND_PITCH_LPC_WIN_MS, fs_kHz); + if (this.fs_kHz == 8) + { + this.pitch_contour_iCDF = Tables.silk_pitch_contour_NB_iCDF; + } + else { + this.pitch_contour_iCDF = Tables.silk_pitch_contour_iCDF; + } + } + this.PacketSize_ms = PacketSize_ms; + this.TargetRate_bps = 0; /* trigger new SNR computation */ + } + + /* Set internal sampling frequency */ + Inlines.OpusAssert(fs_kHz == 8 || fs_kHz == 12 || fs_kHz == 16); + Inlines.OpusAssert(this.nb_subfr == 2 || this.nb_subfr == 4); + if (this.fs_kHz != fs_kHz) + { + /* reset part of the state */ + this.sShape.Reset(); + this.sPrefilt.Reset(); + this.sNSQ.Reset(); + Arrays.MemSetShort(this.prev_NLSFq_Q15, 0, SilkConstants.MAX_LPC_ORDER); + Arrays.MemSetInt(this.sLP.In_LP_State, 0, 2); + this.inputBufIx = 0; + this.nFramesEncoded = 0; + this.TargetRate_bps = 0; /* trigger new SNR computation */ + + /* Initialize non-zero parameters */ + this.prevLag = 100; + this.first_frame_after_reset = 1; + this.sPrefilt.lagPrev = 100; + this.sShape.LastGainIndex = 10; + this.sNSQ.lagPrev = 100; + this.sNSQ.prev_gain_Q16 = 65536; + this.prevSignalType = SilkConstants.TYPE_NO_VOICE_ACTIVITY; + + this.fs_kHz = fs_kHz; + if (this.fs_kHz == 8) + { + if (this.nb_subfr == SilkConstants.MAX_NB_SUBFR) + { + this.pitch_contour_iCDF = Tables.silk_pitch_contour_NB_iCDF; + } + else { + this.pitch_contour_iCDF = Tables.silk_pitch_contour_10_ms_NB_iCDF; + } + } + else { + if (this.nb_subfr == SilkConstants.MAX_NB_SUBFR) + { + this.pitch_contour_iCDF = Tables.silk_pitch_contour_iCDF; + } + else + { + this.pitch_contour_iCDF = Tables.silk_pitch_contour_10_ms_iCDF; + } + } + + if (this.fs_kHz == 8 || this.fs_kHz == 12) + { + this.predictLPCOrder = SilkConstants.MIN_LPC_ORDER; + this.psNLSF_CB = Tables.silk_NLSF_CB_NB_MB; + } + else + { + this.predictLPCOrder = SilkConstants.MAX_LPC_ORDER; + this.psNLSF_CB = Tables.silk_NLSF_CB_WB; + } + + this.subfr_length = SilkConstants.SUB_FRAME_LENGTH_MS * fs_kHz; + this.frame_length = Inlines.silk_SMULBB(this.subfr_length, this.nb_subfr); + this.ltp_mem_length = Inlines.silk_SMULBB(SilkConstants.LTP_MEM_LENGTH_MS, fs_kHz); + this.la_pitch = Inlines.silk_SMULBB(SilkConstants.LA_PITCH_MS, fs_kHz); + this.max_pitch_lag = Inlines.silk_SMULBB(18, fs_kHz); + + if (this.nb_subfr == SilkConstants.MAX_NB_SUBFR) + { + this.pitch_LPC_win_length = Inlines.silk_SMULBB(SilkConstants.FIND_PITCH_LPC_WIN_MS, fs_kHz); + } + else + { + this.pitch_LPC_win_length = Inlines.silk_SMULBB(SilkConstants.FIND_PITCH_LPC_WIN_MS_2_SF, fs_kHz); + } + + if (this.fs_kHz == 16) + { + this.mu_LTP_Q9 = ((int)((TuningParameters.MU_LTP_QUANT_WB) * ((long)1 << (9)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.MU_LTP_QUANT_WB, 9)*/; + this.pitch_lag_low_bits_iCDF = Tables.silk_uniform8_iCDF; + } + else if (this.fs_kHz == 12) + { + this.mu_LTP_Q9 = ((int)((TuningParameters.MU_LTP_QUANT_MB) * ((long)1 << (9)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.MU_LTP_QUANT_MB, 9)*/; + this.pitch_lag_low_bits_iCDF = Tables.silk_uniform6_iCDF; + } + else + { + this.mu_LTP_Q9 = ((int)((TuningParameters.MU_LTP_QUANT_NB) * ((long)1 << (9)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.MU_LTP_QUANT_NB, 9)*/; + this.pitch_lag_low_bits_iCDF = Tables.silk_uniform4_iCDF; + } + } + + /* Check that settings are valid */ + Inlines.OpusAssert((this.subfr_length * this.nb_subfr) == this.frame_length); + + return ret; + } + + /// + /// + /// + /// O + /// + private int silk_setup_complexity(int Complexity) + { + int ret = 0; + + /* Set encoding complexity */ + Inlines.OpusAssert(Complexity >= 0 && Complexity <= 10); + if (Complexity < 2) + { + this.pitchEstimationComplexity = SilkConstants.SILK_PE_MIN_COMPLEX; + this.pitchEstimationThreshold_Q16 = ((int)((0.8f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.8f, 16)*/; + this.pitchEstimationLPCOrder = 6; + this.shapingLPCOrder = 8; + this.la_shape = 3 * this.fs_kHz; + this.nStatesDelayedDecision = 1; + this.useInterpolatedNLSFs = 0; + this.LTPQuantLowComplexity = 1; + this.NLSF_MSVQ_Survivors = 2; + this.warping_Q16 = 0; + } + else if (Complexity < 4) + { + this.pitchEstimationComplexity = SilkConstants.SILK_PE_MID_COMPLEX; + this.pitchEstimationThreshold_Q16 = ((int)((0.76f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.76f, 16)*/; + this.pitchEstimationLPCOrder = 8; + this.shapingLPCOrder = 10; + this.la_shape = 5 * this.fs_kHz; + this.nStatesDelayedDecision = 1; + this.useInterpolatedNLSFs = 0; + this.LTPQuantLowComplexity = 0; + this.NLSF_MSVQ_Survivors = 4; + this.warping_Q16 = 0; + } + else if (Complexity < 6) + { + this.pitchEstimationComplexity = SilkConstants.SILK_PE_MID_COMPLEX; + this.pitchEstimationThreshold_Q16 = ((int)((0.74f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.74f, 16)*/; + this.pitchEstimationLPCOrder = 10; + this.shapingLPCOrder = 12; + this.la_shape = 5 * this.fs_kHz; + this.nStatesDelayedDecision = 2; + this.useInterpolatedNLSFs = 1; + this.LTPQuantLowComplexity = 0; + this.NLSF_MSVQ_Survivors = 8; + this.warping_Q16 = this.fs_kHz * ((int)((TuningParameters.WARPING_MULTIPLIER) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.WARPING_MULTIPLIER, 16)*/; + } + else if (Complexity < 8) + { + this.pitchEstimationComplexity = SilkConstants.SILK_PE_MID_COMPLEX; + this.pitchEstimationThreshold_Q16 = ((int)((0.72f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.72f, 16)*/; + this.pitchEstimationLPCOrder = 12; + this.shapingLPCOrder = 14; + this.la_shape = 5 * this.fs_kHz; + this.nStatesDelayedDecision = 3; + this.useInterpolatedNLSFs = 1; + this.LTPQuantLowComplexity = 0; + this.NLSF_MSVQ_Survivors = 16; + this.warping_Q16 = this.fs_kHz * ((int)((TuningParameters.WARPING_MULTIPLIER) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.WARPING_MULTIPLIER, 16)*/; + } + else { + this.pitchEstimationComplexity = SilkConstants.SILK_PE_MAX_COMPLEX; + this.pitchEstimationThreshold_Q16 = ((int)((0.7f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.7f, 16)*/; + this.pitchEstimationLPCOrder = 16; + this.shapingLPCOrder = 16; + this.la_shape = 5 * this.fs_kHz; + this.nStatesDelayedDecision = SilkConstants.MAX_DEL_DEC_STATES; + this.useInterpolatedNLSFs = 1; + this.LTPQuantLowComplexity = 0; + this.NLSF_MSVQ_Survivors = 32; + this.warping_Q16 = this.fs_kHz * ((int)((TuningParameters.WARPING_MULTIPLIER) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.WARPING_MULTIPLIER, 16)*/; + } + + /* Do not allow higher pitch estimation LPC order than predict LPC order */ + this.pitchEstimationLPCOrder = Inlines.silk_min_int(this.pitchEstimationLPCOrder, this.predictLPCOrder); + this.shapeWinLength = SilkConstants.SUB_FRAME_LENGTH_MS * this.fs_kHz + 2 * this.la_shape; + this.Complexity = Complexity; + + Inlines.OpusAssert(this.pitchEstimationLPCOrder <= SilkConstants.MAX_FIND_PITCH_LPC_ORDER); + Inlines.OpusAssert(this.shapingLPCOrder <= SilkConstants.MAX_SHAPE_LPC_ORDER); + Inlines.OpusAssert(this.nStatesDelayedDecision <= SilkConstants.MAX_DEL_DEC_STATES); + Inlines.OpusAssert(this.warping_Q16 <= 32767); + Inlines.OpusAssert(this.la_shape <= SilkConstants.LA_SHAPE_MAX); + Inlines.OpusAssert(this.shapeWinLength <= SilkConstants.SHAPE_LPC_WIN_MAX); + Inlines.OpusAssert(this.NLSF_MSVQ_Survivors <= SilkConstants.NLSF_VQ_MAX_SURVIVORS); + + return ret; + } + + /// + /// + /// + /// I + /// + private int silk_setup_LBRR(int TargetRate_bps) + { + int LBRR_in_previous_packet; + int ret = SilkError.SILK_NO_ERROR; + int LBRR_rate_thres_bps; + + LBRR_in_previous_packet = this.LBRR_enabled; + this.LBRR_enabled = 0; + if (this.useInBandFEC != 0 && this.PacketLoss_perc > 0) + { + if (this.fs_kHz == 8) + { + LBRR_rate_thres_bps = SilkConstants.LBRR_NB_MIN_RATE_BPS; + } + else if (this.fs_kHz == 12) + { + LBRR_rate_thres_bps = SilkConstants.LBRR_MB_MIN_RATE_BPS; + } + else + { + LBRR_rate_thres_bps = SilkConstants.LBRR_WB_MIN_RATE_BPS; + } + + LBRR_rate_thres_bps = Inlines.silk_SMULWB(Inlines.silk_MUL(LBRR_rate_thres_bps, 125 - Inlines.silk_min(this.PacketLoss_perc, 25)), ((int)((0.01f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.01f, 16)*/); + + if (TargetRate_bps > LBRR_rate_thres_bps) + { + /* Set gain increase for coding LBRR excitation */ + if (LBRR_in_previous_packet == 0) + { + /* Previous packet did not have LBRR, and was therefore coded at a higher bitrate */ + this.LBRR_GainIncreases = 7; + } + else + { + this.LBRR_GainIncreases = Inlines.silk_max_int(7 - Inlines.silk_SMULWB((int)this.PacketLoss_perc, ((int)((0.4f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.4f, 16)*/), 2); + } + this.LBRR_enabled = 1; + } + } + + return ret; + } + + /// + /// Control internal sampling rate + /// + /// I Control structure + /// + internal int silk_control_audio_bandwidth(EncControlState encControl) + { + int fs_kHz; + int fs_Hz; + + fs_kHz = this.fs_kHz; + fs_Hz = Inlines.silk_SMULBB(fs_kHz, 1000); + + if (fs_Hz == 0) + { + /* Encoder has just been initialized */ + fs_Hz = Inlines.silk_min(this.desiredInternal_fs_Hz, this.API_fs_Hz); + fs_kHz = Inlines.silk_DIV32_16(fs_Hz, 1000); + } + else if (fs_Hz > this.API_fs_Hz || fs_Hz > this.maxInternal_fs_Hz || fs_Hz < this.minInternal_fs_Hz) + { + /* Make sure internal rate is not higher than external rate or maximum allowed, or lower than minimum allowed */ + fs_Hz = this.API_fs_Hz; + fs_Hz = Inlines.silk_min(fs_Hz, this.maxInternal_fs_Hz); + fs_Hz = Inlines.silk_max(fs_Hz, this.minInternal_fs_Hz); + fs_kHz = Inlines.silk_DIV32_16(fs_Hz, 1000); + } + else + { + /* State machine for the internal sampling rate switching */ + if (this.sLP.transition_frame_no >= SilkConstants.TRANSITION_FRAMES) + { + /* Stop transition phase */ + this.sLP.mode = 0; + } + + if (this.allow_bandwidth_switch != 0 || encControl.opusCanSwitch != 0) + { + /* Check if we should switch down */ + if (Inlines.silk_SMULBB(this.fs_kHz, 1000) > this.desiredInternal_fs_Hz) + { + /* Switch down */ + if (this.sLP.mode == 0) + { + /* New transition */ + this.sLP.transition_frame_no = SilkConstants.TRANSITION_FRAMES; + + /* Reset transition filter state */ + Arrays.MemSetInt(this.sLP.In_LP_State, 0, 2); + } + + if (encControl.opusCanSwitch != 0) + { + /* Stop transition phase */ + this.sLP.mode = 0; + + /* Switch to a lower sample frequency */ + fs_kHz = this.fs_kHz == 16 ? 12 : 8; + } + else + { + if (this.sLP.transition_frame_no <= 0) + { + encControl.switchReady = 1; + /* Make room for redundancy */ + encControl.maxBits -= encControl.maxBits * 5 / (encControl.payloadSize_ms + 5); + } + else + { + /* Direction: down (at double speed) */ + this.sLP.mode = -2; + } + } + } + else + { + /* Check if we should switch up */ + if (Inlines.silk_SMULBB(this.fs_kHz, 1000) < this.desiredInternal_fs_Hz) + { + /* Switch up */ + if (encControl.opusCanSwitch != 0) + { + /* Switch to a higher sample frequency */ + fs_kHz = this.fs_kHz == 8 ? 12 : 16; + + /* New transition */ + this.sLP.transition_frame_no = 0; + + /* Reset transition filter state */ + Arrays.MemSetInt(this.sLP.In_LP_State, 0, 2); + + /* Direction: up */ + this.sLP.mode = 1; + } + else + { + if (this.sLP.mode == 0) + { + encControl.switchReady = 1; + /* Make room for redundancy */ + encControl.maxBits -= encControl.maxBits * 5 / (encControl.payloadSize_ms + 5); + } + else + { + /* Direction: up */ + this.sLP.mode = 1; + } + } + } + else + { + if (this.sLP.mode < 0) + { + this.sLP.mode = 1; + } + } + } + } + } + + return fs_kHz; + } + + /* Control SNR of residual quantizer */ + internal int silk_control_SNR( + int TargetRate_bps /* I Target max bitrate (bps) */ + ) + { + int k, ret = SilkError.SILK_NO_ERROR; + int frac_Q6; + int[] rateTable; + + /* Set bitrate/coding quality */ + TargetRate_bps = Inlines.silk_LIMIT(TargetRate_bps, SilkConstants.MIN_TARGET_RATE_BPS, SilkConstants.MAX_TARGET_RATE_BPS); + if (TargetRate_bps != this.TargetRate_bps) + { + this.TargetRate_bps = TargetRate_bps; + + /* If new TargetRate_bps, translate to SNR_dB value */ + if (this.fs_kHz == 8) + { + rateTable = Tables.silk_TargetRate_table_NB; + } + else if (this.fs_kHz == 12) + { + rateTable = Tables.silk_TargetRate_table_MB; + } + else { + rateTable = Tables.silk_TargetRate_table_WB; + } + + /* Reduce bitrate for 10 ms modes in these calculations */ + if (this.nb_subfr == 2) + { + TargetRate_bps -= TuningParameters.REDUCE_BITRATE_10_MS_BPS; + } + + /* Find bitrate interval in table and interpolate */ + for (k = 1; k < SilkConstants.TARGET_RATE_TAB_SZ; k++) + { + if (TargetRate_bps <= rateTable[k]) + { + frac_Q6 = Inlines.silk_DIV32(Inlines.silk_LSHIFT(TargetRate_bps - rateTable[k - 1], 6), + rateTable[k] - rateTable[k - 1]); + this.SNR_dB_Q7 = Inlines.silk_LSHIFT(Tables.silk_SNR_table_Q1[k - 1], 6) + Inlines.silk_MUL(frac_Q6, Tables.silk_SNR_table_Q1[k] - Tables.silk_SNR_table_Q1[k - 1]); + break; + } + } + } + + return ret; + } + + internal void silk_encode_do_VAD() + { + /****************************/ + /* Voice Activity Detection */ + /****************************/ + VoiceActivityDetection.silk_VAD_GetSA_Q8(this, this.inputBuf, 1); + + /**************************************************/ + /* Convert speech activity into VAD and DTX flags */ + /**************************************************/ + if (this.speech_activity_Q8 < ((int)((TuningParameters.SPEECH_ACTIVITY_DTX_THRES) * ((long)1 << (8)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SPEECH_ACTIVITY_DTX_THRES, 8)*/) + { + this.indices.signalType = SilkConstants.TYPE_NO_VOICE_ACTIVITY; + this.noSpeechCounter++; + if (this.noSpeechCounter < SilkConstants.NB_SPEECH_FRAMES_BEFORE_DTX) + { + this.inDTX = 0; + } + else if (this.noSpeechCounter > SilkConstants.MAX_CONSECUTIVE_DTX + SilkConstants.NB_SPEECH_FRAMES_BEFORE_DTX) + { + this.noSpeechCounter = SilkConstants.NB_SPEECH_FRAMES_BEFORE_DTX; + this.inDTX = 0; + } + this.VAD_flags[this.nFramesEncoded] = 0; + } + else { + this.noSpeechCounter = 0; + this.inDTX = 0; + this.indices.signalType = SilkConstants.TYPE_UNVOICED; + this.VAD_flags[this.nFramesEncoded] = 1; + } + } + + /****************/ + /* Encode frame */ + /****************/ + internal int silk_encode_frame( + BoxedValueInt pnBytesOut, /* O Pointer to number of payload bytes; */ + EntropyCoder psRangeEnc, /* I/O compressor data structure */ + int condCoding, /* I The type of conditional coding to use */ + int maxBits, /* I If > 0: maximum number of output bits */ + int useCBR /* I Flag to force constant-bitrate operation */ + ) + { + SilkEncoderControl sEncCtrl = new SilkEncoderControl(); + int i, iter, maxIter, found_upper, found_lower, ret = 0; + int x_frame; + EntropyCoder sRangeEnc_copy = new EntropyCoder(); + EntropyCoder sRangeEnc_copy2 = new EntropyCoder(); + SilkNSQState sNSQ_copy = new SilkNSQState(); + SilkNSQState sNSQ_copy2 = new SilkNSQState(); + int nBits, nBits_lower, nBits_upper, gainMult_lower, gainMult_upper; + int gainsID, gainsID_lower, gainsID_upper; + short gainMult_Q8; + short ec_prevLagIndex_copy; + int ec_prevSignalType_copy; + sbyte LastGainIndex_copy2; + sbyte seed_copy; + + /* This is totally unnecessary but many compilers (including gcc) are too dumb to realise it */ + LastGainIndex_copy2 = 0; + nBits_lower = nBits_upper = gainMult_lower = gainMult_upper = 0; + + this.indices.Seed = (sbyte)(this.frameCounter++ & 3); + + /**************************************************************/ + /* Set up Input Pointers, and insert frame in input buffer */ + /*************************************************************/ + /* start of frame to encode */ + x_frame = this.ltp_mem_length; + + /***************************************/ + /* Ensure smooth bandwidth transitions */ + /***************************************/ + this.sLP.silk_LP_variable_cutoff(this.inputBuf, 1, this.frame_length); + + /*******************************************/ + /* Copy new frame to front of input buffer */ + /*******************************************/ + Array.Copy(this.inputBuf, 1, this.x_buf, x_frame + SilkConstants.LA_SHAPE_MS * this.fs_kHz, this.frame_length); + + if (this.prefillFlag == 0) + { + int[] xfw_Q3; + short[] res_pitch; + byte[] ec_buf_copy; + int res_pitch_frame; + + res_pitch = new short[this.la_pitch + this.frame_length + this.ltp_mem_length]; + /* start of pitch LPC residual frame */ + res_pitch_frame = this.ltp_mem_length; + + /*****************************************/ + /* Find pitch lags, initial LPC analysis */ + /*****************************************/ + FindPitchLags.silk_find_pitch_lags(this, sEncCtrl, res_pitch, this.x_buf, x_frame); + + /************************/ + /* Noise shape analysis */ + /************************/ + NoiseShapeAnalysis.silk_noise_shape_analysis(this, sEncCtrl, res_pitch, res_pitch_frame, this.x_buf, x_frame); + + /***************************************************/ + /* Find linear prediction coefficients (LPC + LTP) */ + /***************************************************/ + FindPredCoefs.silk_find_pred_coefs(this, sEncCtrl, res_pitch, this.x_buf, x_frame, condCoding); + + /****************************************/ + /* Process gains */ + /****************************************/ + ProcessGains.silk_process_gains(this, sEncCtrl, condCoding); + + /*****************************************/ + /* Prefiltering for noise shaper */ + /*****************************************/ + xfw_Q3 = new int[this.frame_length]; + Filters.silk_prefilter(this, sEncCtrl, xfw_Q3, this.x_buf, x_frame); + + /****************************************/ + /* Low Bitrate Redundant Encoding */ + /****************************************/ + silk_LBRR_encode(sEncCtrl, xfw_Q3, condCoding); + + /* Loop over quantizer and entropy coding to control bitrate */ + maxIter = 6; + gainMult_Q8 = (short)(((int)((1) * ((long)1 << (8)) + 0.5))/*Inlines.SILK_CONST(1, 8)*/); + found_lower = 0; + found_upper = 0; + gainsID = GainQuantization.silk_gains_ID(this.indices.GainsIndices, this.nb_subfr); + gainsID_lower = -1; + gainsID_upper = -1; + /* Copy part of the input state */ + sRangeEnc_copy.Assign(psRangeEnc); + sNSQ_copy.Assign(this.sNSQ); + seed_copy = this.indices.Seed; + ec_prevLagIndex_copy = this.ec_prevLagIndex; + ec_prevSignalType_copy = this.ec_prevSignalType; + ec_buf_copy = new byte[1275]; // fixme: this size might be optimized to the actual size + for (iter = 0; ; iter++) + { + if (gainsID == gainsID_lower) + { + nBits = nBits_lower; + } + else if (gainsID == gainsID_upper) + { + nBits = nBits_upper; + } + else { + /* Restore part of the input state */ + if (iter > 0) + { + psRangeEnc.Assign(sRangeEnc_copy); + this.sNSQ.Assign(sNSQ_copy); + this.indices.Seed = seed_copy; + this.ec_prevLagIndex = ec_prevLagIndex_copy; + this.ec_prevSignalType = ec_prevSignalType_copy; + } + + /*****************************************/ + /* Noise shaping quantization */ + /*****************************************/ + if (this.nStatesDelayedDecision > 1 || this.warping_Q16 > 0) + { + sNSQ.silk_NSQ_del_dec( + this, + this.indices, + xfw_Q3, + pulses, + sEncCtrl.PredCoef_Q12, + sEncCtrl.LTPCoef_Q14, + sEncCtrl.AR2_Q13, + sEncCtrl.HarmShapeGain_Q14, + sEncCtrl.Tilt_Q14, + sEncCtrl.LF_shp_Q14, + sEncCtrl.Gains_Q16, + sEncCtrl.pitchL, + sEncCtrl.Lambda_Q10, + sEncCtrl.LTP_scale_Q14); + } + else { + sNSQ.silk_NSQ( + this, + this.indices, + xfw_Q3, + pulses, + sEncCtrl.PredCoef_Q12, + sEncCtrl.LTPCoef_Q14, + sEncCtrl.AR2_Q13, + sEncCtrl.HarmShapeGain_Q14, + sEncCtrl.Tilt_Q14, + sEncCtrl.LF_shp_Q14, + sEncCtrl.Gains_Q16, + sEncCtrl.pitchL, + sEncCtrl.Lambda_Q10, + sEncCtrl.LTP_scale_Q14); + } + + /****************************************/ + /* Encode Parameters */ + /****************************************/ + EncodeIndices.silk_encode_indices(this, psRangeEnc, this.nFramesEncoded, 0, condCoding); + + /****************************************/ + /* Encode Excitation Signal */ + /****************************************/ + EncodePulses.silk_encode_pulses(psRangeEnc, this.indices.signalType, this.indices.quantOffsetType, + this.pulses, this.frame_length); + + nBits = psRangeEnc.tell(); + + if (useCBR == 0 && iter == 0 && nBits <= maxBits) + { + break; + } + } + + if (iter == maxIter) + { + if (found_lower != 0 && (gainsID == gainsID_lower || nBits > maxBits)) + { + /* Restore output state from earlier iteration that did meet the bitrate budget */ + psRangeEnc.Assign(sRangeEnc_copy2); + Inlines.OpusAssert(sRangeEnc_copy2.offs <= 1275); + Array.Copy(ec_buf_copy, 0, psRangeEnc.buf, psRangeEnc.buf_ptr, (int)sRangeEnc_copy2.offs); + this.sNSQ.Assign(sNSQ_copy2); + this.sShape.LastGainIndex = LastGainIndex_copy2; + } + break; + } + + if (nBits > maxBits) + { + if (found_lower == 0 && iter >= 2) + { + /* Adjust the quantizer's rate/distortion tradeoff and discard previous "upper" results */ + sEncCtrl.Lambda_Q10 = Inlines.silk_ADD_RSHIFT32(sEncCtrl.Lambda_Q10, sEncCtrl.Lambda_Q10, 1); + found_upper = 0; + gainsID_upper = -1; + } + else { + found_upper = 1; + nBits_upper = nBits; + gainMult_upper = gainMult_Q8; + gainsID_upper = gainsID; + } + } + else if (nBits < maxBits - 5) + { + found_lower = 1; + nBits_lower = nBits; + gainMult_lower = gainMult_Q8; + if (gainsID != gainsID_lower) + { + gainsID_lower = gainsID; + /* Copy part of the output state */ + sRangeEnc_copy2.Assign(psRangeEnc); + Inlines.OpusAssert(psRangeEnc.offs <= 1275); + Array.Copy(psRangeEnc.buf, psRangeEnc.buf_ptr, ec_buf_copy, 0, (int)psRangeEnc.offs); + sNSQ_copy2.Assign(this.sNSQ); + LastGainIndex_copy2 = this.sShape.LastGainIndex; + } + } + else { + /* Within 5 bits of budget: close enough */ + break; + } + + if ((found_lower & found_upper) == 0) + { + /* Adjust gain according to high-rate rate/distortion curve */ + int gain_factor_Q16; + gain_factor_Q16 = Inlines.silk_log2lin(Inlines.silk_LSHIFT(nBits - maxBits, 7) / this.frame_length + ((int)((16) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(16, 7)*/); + gain_factor_Q16 = Inlines.silk_min_32(gain_factor_Q16, ((int)((2) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(2, 16)*/); + if (nBits > maxBits) + { + gain_factor_Q16 = Inlines.silk_max_32(gain_factor_Q16, ((int)((1.3f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.3f, 16)*/); + } + + gainMult_Q8 = (short)(Inlines.silk_SMULWB(gain_factor_Q16, (int)gainMult_Q8)); + } + else + { + /* Adjust gain by interpolating */ + gainMult_Q8 = (short)(gainMult_lower + Inlines.silk_DIV32_16(Inlines.silk_MUL(gainMult_upper - gainMult_lower, maxBits - nBits_lower), nBits_upper - nBits_lower)); + /* New gain multplier must be between 25% and 75% of old range (note that gainMult_upper < gainMult_lower) */ + if (gainMult_Q8 > Inlines.silk_ADD_RSHIFT32(gainMult_lower, gainMult_upper - gainMult_lower, 2)) + { + gainMult_Q8 = (short)(Inlines.silk_ADD_RSHIFT32(gainMult_lower, gainMult_upper - gainMult_lower, 2)); + } + else if (gainMult_Q8 < Inlines.silk_SUB_RSHIFT32(gainMult_upper, gainMult_upper - gainMult_lower, 2)) + { + gainMult_Q8 = (short)(Inlines.silk_SUB_RSHIFT32(gainMult_upper, gainMult_upper - gainMult_lower, 2)); + } + } + + for (i = 0; i < this.nb_subfr; i++) + { + sEncCtrl.Gains_Q16[i] = Inlines.silk_LSHIFT_SAT32(Inlines.silk_SMULWB(sEncCtrl.GainsUnq_Q16[i], gainMult_Q8), 8); + } + + /* Quantize gains */ + this.sShape.LastGainIndex = sEncCtrl.lastGainIndexPrev; + BoxedValueSbyte boxed_gainIndex = new BoxedValueSbyte(this.sShape.LastGainIndex); + GainQuantization.silk_gains_quant(this.indices.GainsIndices, sEncCtrl.Gains_Q16, + boxed_gainIndex, condCoding == SilkConstants.CODE_CONDITIONALLY ? 1 : 0, this.nb_subfr); + this.sShape.LastGainIndex = boxed_gainIndex.Val; + + /* Unique identifier of gains vector */ + gainsID = GainQuantization.silk_gains_ID(this.indices.GainsIndices, this.nb_subfr); + } + } + + /* Update input buffer */ + Arrays.MemMove(this.x_buf, this.frame_length, 0, this.ltp_mem_length + SilkConstants.LA_SHAPE_MS * this.fs_kHz); + + /* Exit without entropy coding */ + if (this.prefillFlag != 0) + { + /* No payload */ + pnBytesOut.Val = 0; + + return ret; + } + + /* Parameters needed for next frame */ + this.prevLag = sEncCtrl.pitchL[this.nb_subfr - 1]; + this.prevSignalType = this.indices.signalType; + + /****************************************/ + /* Finalize payload */ + /****************************************/ + this.first_frame_after_reset = 0; + /* Payload size */ + pnBytesOut.Val = Inlines.silk_RSHIFT(psRangeEnc.tell() + 7, 3); + + return ret; + } + + /* Low-Bitrate Redundancy (LBRR) encoding. Reuse all parameters but encode excitation at lower bitrate */ + internal void silk_LBRR_encode( + SilkEncoderControl thisCtrl, /* I/O Pointer to Silk FIX encoder control struct */ + int[] xfw_Q3, /* I Input signal */ + int condCoding /* I The type of conditional coding used so far for this frame */ + ) + { + int[] TempGains_Q16 = new int[/*SilkConstants.MAX_NB_SUBFR*/ this.nb_subfr]; + SideInfoIndices psIndices_LBRR = this.indices_LBRR[this.nFramesEncoded]; + SilkNSQState sNSQ_LBRR = new SilkNSQState(); + + /*******************************************/ + /* Control use of inband LBRR */ + /*******************************************/ + if (this.LBRR_enabled != 0 && this.speech_activity_Q8 > ((int)((TuningParameters.LBRR_SPEECH_ACTIVITY_THRES) * ((long)1 << (8)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LBRR_SPEECH_ACTIVITY_THRES, 8)*/) + { + this.LBRR_flags[this.nFramesEncoded] = 1; + + /* Copy noise shaping quantizer state and quantization indices from regular encoding */ + sNSQ_LBRR.Assign(this.sNSQ); + psIndices_LBRR.Assign(this.indices); + + /* Save original gains */ + Array.Copy(thisCtrl.Gains_Q16, TempGains_Q16, this.nb_subfr); + + if (this.nFramesEncoded == 0 || this.LBRR_flags[this.nFramesEncoded - 1] == 0) + { + /* First frame in packet or previous frame not LBRR coded */ + this.LBRRprevLastGainIndex = this.sShape.LastGainIndex; + + /* Increase Gains to get target LBRR rate */ + psIndices_LBRR.GainsIndices[0] = (sbyte)(psIndices_LBRR.GainsIndices[0] + this.LBRR_GainIncreases); + psIndices_LBRR.GainsIndices[0] = (sbyte)(Inlines.silk_min_int(psIndices_LBRR.GainsIndices[0], SilkConstants.N_LEVELS_QGAIN - 1)); + } + + /* Decode to get gains in sync with decoder */ + /* Overwrite unquantized gains with quantized gains */ + BoxedValueSbyte boxed_gainIndex = new BoxedValueSbyte(this.LBRRprevLastGainIndex); + GainQuantization.silk_gains_dequant(thisCtrl.Gains_Q16, psIndices_LBRR.GainsIndices, + boxed_gainIndex, condCoding == SilkConstants.CODE_CONDITIONALLY ? 1 : 0, this.nb_subfr); + this.LBRRprevLastGainIndex = boxed_gainIndex.Val; + + /*****************************************/ + /* Noise shaping quantization */ + /*****************************************/ + if (this.nStatesDelayedDecision > 1 || this.warping_Q16 > 0) + { + sNSQ_LBRR.silk_NSQ_del_dec(this, + psIndices_LBRR, + xfw_Q3, + this.pulses_LBRR[this.nFramesEncoded], + thisCtrl.PredCoef_Q12, + thisCtrl.LTPCoef_Q14, + thisCtrl.AR2_Q13, + thisCtrl.HarmShapeGain_Q14, + thisCtrl.Tilt_Q14, + thisCtrl.LF_shp_Q14, + thisCtrl.Gains_Q16, + thisCtrl.pitchL, + thisCtrl.Lambda_Q10, + thisCtrl.LTP_scale_Q14); + } + else { + sNSQ_LBRR.silk_NSQ(this, + psIndices_LBRR, + xfw_Q3, + this.pulses_LBRR[this.nFramesEncoded], + thisCtrl.PredCoef_Q12, + thisCtrl.LTPCoef_Q14, + thisCtrl.AR2_Q13, + thisCtrl.HarmShapeGain_Q14, + thisCtrl.Tilt_Q14, + thisCtrl.LF_shp_Q14, + thisCtrl.Gains_Q16, + thisCtrl.pitchL, + thisCtrl.Lambda_Q10, + thisCtrl.LTP_scale_Q14); + } + + /* Restore original gains */ + Array.Copy(TempGains_Q16, thisCtrl.Gains_Q16, this.nb_subfr); + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkDecoder.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkDecoder.cs new file mode 100644 index 000000000..8daedadb9 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkDecoder.cs @@ -0,0 +1,70 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk.Structs +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + + /// + /// Decoder super struct + /// + internal class SilkDecoder + { + internal readonly SilkChannelDecoder[] channel_state = new SilkChannelDecoder[SilkConstants.DECODER_NUM_CHANNELS]; + internal readonly StereoDecodeState sStereo = new StereoDecodeState(); + internal int nChannelsAPI = 0; + internal int nChannelsInternal = 0; + internal int prev_decode_only_middle = 0; + + internal SilkDecoder() + { + for (int c = 0; c < SilkConstants.DECODER_NUM_CHANNELS; c++) + { + channel_state[c] = new SilkChannelDecoder(); + } + } + + internal void Reset() + { + for (int c = 0; c < SilkConstants.DECODER_NUM_CHANNELS; c++) + { + channel_state[c].Reset(); + } + sStereo.Reset(); + nChannelsAPI = 0; + nChannelsInternal = 0; + prev_decode_only_middle = 0; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkDecoderControl.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkDecoderControl.cs new file mode 100644 index 000000000..aa80b1fa2 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkDecoderControl.cs @@ -0,0 +1,63 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk.Structs +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + + /// + /// Decoder control + /// + internal class SilkDecoderControl + { + /* Prediction and coding parameters */ + internal readonly int[] pitchL = new int[SilkConstants.MAX_NB_SUBFR]; + internal readonly int[] Gains_Q16 = new int[SilkConstants.MAX_NB_SUBFR]; + + /* Holds interpolated and final coefficients */ + internal readonly short[][] PredCoef_Q12 = Arrays.InitTwoDimensionalArray(2, SilkConstants.MAX_LPC_ORDER); + internal readonly short[] LTPCoef_Q14 = new short[SilkConstants.LTP_ORDER * SilkConstants.MAX_NB_SUBFR]; + internal int LTP_scale_Q14 = 0; + + internal void Reset() + { + Arrays.MemSetInt(pitchL, 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSetInt(Gains_Q16, 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSetShort(PredCoef_Q12[0], 0, SilkConstants.MAX_LPC_ORDER); + Arrays.MemSetShort(PredCoef_Q12[1], 0, SilkConstants.MAX_LPC_ORDER); + Arrays.MemSetShort(LTPCoef_Q14, 0, SilkConstants.LTP_ORDER * SilkConstants.MAX_NB_SUBFR); + LTP_scale_Q14 = 0; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkEncoder.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkEncoder.cs new file mode 100644 index 000000000..590b274dc --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkEncoder.cs @@ -0,0 +1,105 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk.Structs +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + + /// + /// Encoder Super Struct + /// + internal class SilkEncoder + { + internal readonly SilkChannelEncoder[] state_Fxx = new SilkChannelEncoder[SilkConstants.ENCODER_NUM_CHANNELS]; + internal readonly StereoEncodeState sStereo = new StereoEncodeState(); + internal int nBitsUsedLBRR = 0; + internal int nBitsExceeded = 0; + internal int nChannelsAPI = 0; + internal int nChannelsInternal = 0; + internal int nPrevChannelsInternal = 0; + internal int timeSinceSwitchAllowed_ms = 0; + internal int allowBandwidthSwitch = 0; + internal int prev_decode_only_middle = 0; + + internal SilkEncoder() + { + for (int c = 0; c < SilkConstants.ENCODER_NUM_CHANNELS; c++) + { + state_Fxx[c] = new SilkChannelEncoder(); + } + } + + internal void Reset() + { + for (int c = 0; c < SilkConstants.ENCODER_NUM_CHANNELS; c++) + { + state_Fxx[c].Reset(); + } + + sStereo.Reset(); + nBitsUsedLBRR = 0; + nBitsExceeded = 0; + nChannelsAPI = 0; + nChannelsInternal = 0; + nPrevChannelsInternal = 0; + timeSinceSwitchAllowed_ms = 0; + allowBandwidthSwitch = 0; + prev_decode_only_middle = 0; + } + + /// + /// Initialize Silk Encoder state + /// + /// I/O Pointer to Silk FIX encoder state + /// + internal static int silk_init_encoder(SilkChannelEncoder psEnc) + { + int ret = 0; + + // Clear the entire encoder state + psEnc.Reset(); + + psEnc.variable_HP_smth1_Q15 = Inlines.silk_LSHIFT(Inlines.silk_lin2log(((int)((TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ, 16)*/) - (16 << 7), 8); + psEnc.variable_HP_smth2_Q15 = psEnc.variable_HP_smth1_Q15; + + // Used to deactivate LSF interpolation, pitch prediction + psEnc.first_frame_after_reset = 1; + + // Initialize Silk VAD + ret += VoiceActivityDetection.silk_VAD_Init(psEnc.sVAD); + + return ret; + } + } +} \ No newline at end of file diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkEncoderControl.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkEncoderControl.cs new file mode 100644 index 000000000..8449d71bd --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkEncoderControl.cs @@ -0,0 +1,105 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk.Structs +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + + /************************/ + /* Encoder control FIX */ + /************************/ + internal class SilkEncoderControl + { + /* Prediction and coding parameters */ + internal readonly int[] Gains_Q16 = new int[SilkConstants.MAX_NB_SUBFR]; + internal readonly short[][] PredCoef_Q12 = Arrays.InitTwoDimensionalArray(2, SilkConstants.MAX_LPC_ORDER); /* holds interpolated and final coefficients */ + internal readonly short[] LTPCoef_Q14 = new short[SilkConstants.LTP_ORDER * SilkConstants.MAX_NB_SUBFR]; + internal int LTP_scale_Q14 = 0; + internal readonly int[] pitchL = new int[SilkConstants.MAX_NB_SUBFR]; + + /* Noise shaping parameters */ + internal readonly short[] AR1_Q13 = new short[SilkConstants.MAX_NB_SUBFR * SilkConstants.MAX_SHAPE_LPC_ORDER]; + internal readonly short[] AR2_Q13 = new short[SilkConstants.MAX_NB_SUBFR * SilkConstants.MAX_SHAPE_LPC_ORDER]; + internal readonly int[] LF_shp_Q14 = new int[SilkConstants.MAX_NB_SUBFR]; /* Packs two int16 coefficients per int32 value */ + internal readonly int[] GainsPre_Q14 = new int[SilkConstants.MAX_NB_SUBFR]; + internal readonly int[] HarmBoost_Q14 = new int[SilkConstants.MAX_NB_SUBFR]; + internal readonly int[] Tilt_Q14 = new int[SilkConstants.MAX_NB_SUBFR]; + internal readonly int[] HarmShapeGain_Q14 = new int[SilkConstants.MAX_NB_SUBFR]; + internal int Lambda_Q10 = 0; + internal int input_quality_Q14 = 0; + internal int coding_quality_Q14 = 0; + + /* Measures */ + internal int sparseness_Q8 = 0; + internal int predGain_Q16 = 0; + internal int LTPredCodGain_Q7 = 0; + + /* Residual energy per subframe */ + internal readonly int[] ResNrg = new int[SilkConstants.MAX_NB_SUBFR]; + + /* Q domain for the residual energy > 0 */ + internal readonly int[] ResNrgQ = new int[SilkConstants.MAX_NB_SUBFR]; + + /* Parameters for CBR mode */ + internal readonly int[] GainsUnq_Q16 = new int[SilkConstants.MAX_NB_SUBFR]; + internal sbyte lastGainIndexPrev = 0; + + internal void Reset() + { + Arrays.MemSetInt(Gains_Q16, 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSetShort(PredCoef_Q12[0], 0, SilkConstants.MAX_LPC_ORDER); + Arrays.MemSetShort(PredCoef_Q12[1], 0, SilkConstants.MAX_LPC_ORDER); + Arrays.MemSetShort(LTPCoef_Q14, 0, SilkConstants.LTP_ORDER * SilkConstants.MAX_NB_SUBFR); + LTP_scale_Q14 = 0; + Arrays.MemSetInt(pitchL, 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSetShort(AR1_Q13, 0, SilkConstants.MAX_NB_SUBFR * SilkConstants.MAX_SHAPE_LPC_ORDER); + Arrays.MemSetShort(AR2_Q13, 0, SilkConstants.MAX_NB_SUBFR * SilkConstants.MAX_SHAPE_LPC_ORDER); + Arrays.MemSetInt(LF_shp_Q14, 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSetInt(GainsPre_Q14, 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSetInt(HarmBoost_Q14, 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSetInt(Tilt_Q14, 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSetInt(HarmShapeGain_Q14, 0, SilkConstants.MAX_NB_SUBFR); + Lambda_Q10 = 0; + input_quality_Q14 = 0; + coding_quality_Q14 = 0; + sparseness_Q8 = 0; + predGain_Q16 = 0; + LTPredCodGain_Q7 = 0; + Arrays.MemSetInt(ResNrg, 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSetInt(ResNrgQ, 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSetInt(GainsUnq_Q16, 0, SilkConstants.MAX_NB_SUBFR); + lastGainIndexPrev = 0; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkLPState.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkLPState.cs new file mode 100644 index 000000000..16623ab55 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkLPState.cs @@ -0,0 +1,108 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk.Structs +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + + /// + /// Variable cut-off low-pass filter state + /// + internal class SilkLPState + { + /// + /// Low pass filter state + /// + internal readonly int[] In_LP_State = new int[2]; + + /// + /// Counter which is mapped to a cut-off frequency + /// + internal int transition_frame_no = 0; + + /// + /// Operating mode, <0: switch down, >0: switch up; 0: do nothing + /// + internal int mode = 0; + + internal void Reset() + { + In_LP_State[0] = 0; + In_LP_State[1] = 0; + transition_frame_no = 0; + mode = 0; + } + + /* Low-pass filter with variable cutoff frequency based on */ + /* piece-wise linear interpolation between elliptic filters */ + /* Start by setting psEncC.mode <> 0; */ + /* Deactivate by setting psEncC.mode = 0; */ + internal void silk_LP_variable_cutoff( + short[] frame, /* I/O Low-pass filtered output signal */ + int frame_ptr, + int frame_length /* I Frame length */ + ) + { + int[] B_Q28 = new int[SilkConstants.TRANSITION_NB]; + int[] A_Q28 = new int[SilkConstants.TRANSITION_NA]; + int fac_Q16 = 0; + int ind = 0; + + Inlines.OpusAssert(this.transition_frame_no >= 0 && this.transition_frame_no <= SilkConstants.TRANSITION_FRAMES); + + /* Run filter if needed */ + if (this.mode != 0) + { + /* Calculate index and interpolation factor for interpolation */ + fac_Q16 = Inlines.silk_LSHIFT(SilkConstants.TRANSITION_FRAMES - this.transition_frame_no, 16 - 6); + + ind = Inlines.silk_RSHIFT(fac_Q16, 16); + fac_Q16 -= Inlines.silk_LSHIFT(ind, 16); + + Inlines.OpusAssert(ind >= 0); + Inlines.OpusAssert(ind < SilkConstants.TRANSITION_INT_NUM); + + /* Interpolate filter coefficients */ + Filters.silk_LP_interpolate_filter_taps(B_Q28, A_Q28, ind, fac_Q16); + + /* Update transition frame number for next frame */ + this.transition_frame_no = Inlines.silk_LIMIT(this.transition_frame_no + this.mode, 0, SilkConstants.TRANSITION_FRAMES); + + /* ARMA low-pass filtering */ + Inlines.OpusAssert(SilkConstants.TRANSITION_NB == 3 && SilkConstants.TRANSITION_NA == 2); + Filters.silk_biquad_alt(frame, frame_ptr, B_Q28, A_Q28, this.In_LP_State, frame, frame_ptr, frame_length, 1); + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkNSQState.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkNSQState.cs new file mode 100644 index 000000000..251c49dd5 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkNSQState.cs @@ -0,0 +1,1742 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk.Structs +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using System; + /// + /// Noise shaping quantization state + /// + internal class SilkNSQState + { + /// + /// Buffer for quantized output signal + /// + internal readonly short[] xq = new short[2 * SilkConstants.MAX_FRAME_LENGTH]; //opt: make these arrays variable-sized since construction cost is significant here + internal readonly int[] sLTP_shp_Q14 = new int[2 * SilkConstants.MAX_FRAME_LENGTH]; + internal readonly int[] sLPC_Q14 = new int[SilkConstants.MAX_SUB_FRAME_LENGTH + SilkConstants.NSQ_LPC_BUF_LENGTH]; + internal readonly int[] sAR2_Q14 = new int[SilkConstants.MAX_SHAPE_LPC_ORDER]; + internal int sLF_AR_shp_Q14 = 0; + internal int lagPrev = 0; + internal int sLTP_buf_idx = 0; + internal int sLTP_shp_buf_idx = 0; + internal int rand_seed = 0; + internal int prev_gain_Q16 = 0; + internal int rewhite_flag = 0; + + internal void Reset() + { + Arrays.MemSetShort(xq, 0, 2 * SilkConstants.MAX_FRAME_LENGTH); + Arrays.MemSetInt(sLTP_shp_Q14, 0, 2 * SilkConstants.MAX_FRAME_LENGTH); + Arrays.MemSetInt(sLPC_Q14, 0, SilkConstants.MAX_SUB_FRAME_LENGTH + SilkConstants.NSQ_LPC_BUF_LENGTH); + Arrays.MemSetInt(sAR2_Q14, 0, SilkConstants.MAX_SHAPE_LPC_ORDER); + sLF_AR_shp_Q14 = 0; + lagPrev = 0; + sLTP_buf_idx = 0; + sLTP_shp_buf_idx = 0; + rand_seed = 0; + prev_gain_Q16 = 0; + rewhite_flag = 0; + } + + // Copies another nsq state to this one + internal void Assign(SilkNSQState other) + { + this.sLF_AR_shp_Q14 = other.sLF_AR_shp_Q14; + this.lagPrev = other.lagPrev; + this.sLTP_buf_idx = other.sLTP_buf_idx; + this.sLTP_shp_buf_idx = other.sLTP_shp_buf_idx; + this.rand_seed = other.rand_seed; + this.prev_gain_Q16 = other.prev_gain_Q16; + this.rewhite_flag = other.rewhite_flag; + Array.Copy(other.xq, this.xq, 2 * SilkConstants.MAX_FRAME_LENGTH); + Array.Copy(other.sLTP_shp_Q14, this.sLTP_shp_Q14, 2 * SilkConstants.MAX_FRAME_LENGTH); + Array.Copy(other.sLPC_Q14, this.sLPC_Q14, SilkConstants.MAX_SUB_FRAME_LENGTH + SilkConstants.NSQ_LPC_BUF_LENGTH); + Array.Copy(other.sAR2_Q14, this.sAR2_Q14, SilkConstants.MAX_SHAPE_LPC_ORDER); + } + + private class NSQ_del_dec_struct + { + internal readonly int[] sLPC_Q14 = new int[SilkConstants.MAX_SUB_FRAME_LENGTH + SilkConstants.NSQ_LPC_BUF_LENGTH]; + internal readonly int[] RandState = new int[SilkConstants.DECISION_DELAY]; + internal readonly int[] Q_Q10 = new int[SilkConstants.DECISION_DELAY]; + internal readonly int[] Xq_Q14 = new int[SilkConstants.DECISION_DELAY]; + internal readonly int[] Pred_Q15 = new int[SilkConstants.DECISION_DELAY]; + internal readonly int[] Shape_Q14 = new int[SilkConstants.DECISION_DELAY]; + internal int[] sAR2_Q14; + internal int LF_AR_Q14 = 0; + internal int Seed = 0; + internal int SeedInit = 0; + internal int RD_Q10 = 0; + + internal NSQ_del_dec_struct(int shapingOrder) + { + sAR2_Q14 = new int[shapingOrder]; + } + + internal void PartialCopyFrom(NSQ_del_dec_struct other, int q14Offset) + { + Buffer.BlockCopy(other.sLPC_Q14, q14Offset * sizeof(int), sLPC_Q14, q14Offset * sizeof(int), (SilkConstants.MAX_SUB_FRAME_LENGTH + SilkConstants.NSQ_LPC_BUF_LENGTH - q14Offset) * sizeof(int)); + Buffer.BlockCopy(other.RandState, 0, RandState, 0, SilkConstants.DECISION_DELAY * sizeof(int)); + Buffer.BlockCopy(other.Q_Q10, 0, Q_Q10, 0, SilkConstants.DECISION_DELAY * sizeof(int)); + Buffer.BlockCopy(other.Xq_Q14, 0, Xq_Q14, 0, SilkConstants.DECISION_DELAY * sizeof(int)); + Buffer.BlockCopy(other.Pred_Q15, 0, Pred_Q15, 0, SilkConstants.DECISION_DELAY * sizeof(int)); + Buffer.BlockCopy(other.Shape_Q14, 0, Shape_Q14, 0, SilkConstants.DECISION_DELAY * sizeof(int)); + Buffer.BlockCopy(other.sAR2_Q14, 0, sAR2_Q14, 0, sAR2_Q14.Length * sizeof(int)); + LF_AR_Q14 = other.LF_AR_Q14; + Seed = other.Seed; + SeedInit = other.SeedInit; + RD_Q10 = other.RD_Q10; + } + + internal void Assign(NSQ_del_dec_struct other) + { + this.PartialCopyFrom(other, 0); + } + } + + private struct NSQ_sample_struct + { + internal int Q_Q10; + internal int RD_Q10; + internal int xq_Q14; + internal int LF_AR_Q14; + internal int sLTP_shp_Q14; + internal int LPC_exc_Q14; + + //internal void Assign(NSQ_sample_struct other) + //{ + // this.Q_Q10 = other.Q_Q10; + // this.RD_Q10 = other.RD_Q10; + // this.xq_Q14 = other.xq_Q14; + // this.LF_AR_Q14 = other.LF_AR_Q14; + // this.sLTP_shp_Q14 = other.sLTP_shp_Q14; + // this.LPC_exc_Q14 = other.LPC_exc_Q14; + //} + } + + internal void silk_NSQ + ( + SilkChannelEncoder psEncC, /* I/O Encoder State */ + SideInfoIndices psIndices, /* I/O Quantization Indices */ + int[] x_Q3, /* I Prefiltered input signal */ + sbyte[] pulses, /* O Quantized pulse signal */ + short[][] PredCoef_Q12, /* I Short term prediction coefs [2][SilkConstants.MAX_LPC_ORDER] */ + short[] LTPCoef_Q14, /* I Long term prediction coefs [SilkConstants.LTP_ORDER * MAX_NB_SUBFR] */ + short[] AR2_Q13, /* I Noise shaping coefs [MAX_NB_SUBFR * SilkConstants.MAX_SHAPE_LPC_ORDER] */ + int[] HarmShapeGain_Q14, /* I Long term shaping coefs [MAX_NB_SUBFR] */ + int[] Tilt_Q14, /* I Spectral tilt [MAX_NB_SUBFR] */ + int[] LF_shp_Q14, /* I Low frequency shaping coefs [MAX_NB_SUBFR] */ + int[] Gains_Q16, /* I Quantization step sizes [MAX_NB_SUBFR] */ + int[] pitchL, /* I Pitch lags [MAX_NB_SUBFR] */ + int Lambda_Q10, /* I Rate/distortion tradeoff */ + int LTP_scale_Q14 /* I LTP state scaling */ + ) + { + int k, lag, start_idx, LSF_interpolation_flag; + int A_Q12, B_Q14, AR_shp_Q13; + int pxq; + int[] sLTP_Q15; + short[] sLTP; + int HarmShapeFIRPacked_Q14; + int offset_Q10; + int[] x_sc_Q10; + int pulses_ptr = 0; + int x_Q3_ptr = 0; + + this.rand_seed = psIndices.Seed; + + /* Set unvoiced lag to the previous one, overwrite later for voiced */ + lag = this.lagPrev; + + Inlines.OpusAssert(this.prev_gain_Q16 != 0); + + offset_Q10 = Tables.silk_Quantization_Offsets_Q10[psIndices.signalType >> 1][psIndices.quantOffsetType]; + + if (psIndices.NLSFInterpCoef_Q2 == 4) + { + LSF_interpolation_flag = 0; + } + else { + LSF_interpolation_flag = 1; + } + + sLTP_Q15 = new int[psEncC.ltp_mem_length + psEncC.frame_length]; + sLTP = new short[psEncC.ltp_mem_length + psEncC.frame_length]; + x_sc_Q10 = new int[psEncC.subfr_length]; + /* Set up pointers to start of sub frame */ + this.sLTP_shp_buf_idx = psEncC.ltp_mem_length; + this.sLTP_buf_idx = psEncC.ltp_mem_length; + pxq = psEncC.ltp_mem_length; + for (k = 0; k < psEncC.nb_subfr; k++) + { + A_Q12 = (((k >> 1) | (1 - LSF_interpolation_flag))); + B_Q14 = (k * SilkConstants.LTP_ORDER); // opt: does this indicate a partitioned array? + AR_shp_Q13 = (k * SilkConstants.MAX_SHAPE_LPC_ORDER); // opt: same here + + /* Noise shape parameters */ + Inlines.OpusAssert(HarmShapeGain_Q14[k] >= 0); + HarmShapeFIRPacked_Q14 = Inlines.silk_RSHIFT(HarmShapeGain_Q14[k], 2); + HarmShapeFIRPacked_Q14 |= Inlines.silk_LSHIFT((int)Inlines.silk_RSHIFT(HarmShapeGain_Q14[k], 1), 16); + + this.rewhite_flag = 0; + if (psIndices.signalType == SilkConstants.TYPE_VOICED) + { + /* Voiced */ + lag = pitchL[k]; + + /* Re-whitening */ + if ((k & (3 - Inlines.silk_LSHIFT(LSF_interpolation_flag, 1))) == 0) + { + /* Rewhiten with new A coefs */ + start_idx = psEncC.ltp_mem_length - lag - psEncC.predictLPCOrder - SilkConstants.LTP_ORDER / 2; + Inlines.OpusAssert(start_idx > 0); + + Filters.silk_LPC_analysis_filter(sLTP, start_idx, this.xq, start_idx + k * psEncC.subfr_length, + PredCoef_Q12[A_Q12], 0, psEncC.ltp_mem_length - start_idx, psEncC.predictLPCOrder); + + this.rewhite_flag = 1; + this.sLTP_buf_idx = psEncC.ltp_mem_length; + } + } + + silk_nsq_scale_states(psEncC, x_Q3, x_Q3_ptr, x_sc_Q10, sLTP, sLTP_Q15, k, LTP_scale_Q14, Gains_Q16, pitchL, psIndices.signalType); + + silk_noise_shape_quantizer( + psIndices.signalType, + x_sc_Q10, + pulses, + pulses_ptr, + this.xq, + pxq, + sLTP_Q15, + PredCoef_Q12[A_Q12], + LTPCoef_Q14, + B_Q14, + AR2_Q13, + AR_shp_Q13, + lag, + HarmShapeFIRPacked_Q14, + Tilt_Q14[k], + LF_shp_Q14[k], + Gains_Q16[k], + Lambda_Q10, + offset_Q10, + psEncC.subfr_length, + psEncC.shapingLPCOrder, + psEncC.predictLPCOrder); + + x_Q3_ptr += psEncC.subfr_length; + pulses_ptr += psEncC.subfr_length; + pxq += psEncC.subfr_length; + } + + /* Update lagPrev for next frame */ + this.lagPrev = pitchL[psEncC.nb_subfr - 1]; + + /* Save quantized speech and noise shaping signals */ + Arrays.MemMoveShort(this.xq, psEncC.frame_length, 0, psEncC.ltp_mem_length); + Arrays.MemMoveInt(this.sLTP_shp_Q14, psEncC.frame_length, 0, psEncC.ltp_mem_length); + } + + /***********************************/ + /* silk_noise_shape_quantizer */ + /***********************************/ + private void silk_noise_shape_quantizer( + int signalType, /* I Signal type */ + int[] x_sc_Q10, /* I [length] */ + sbyte[] pulses, /* O [length] */ + int pulses_ptr, + short[] xq, /* O [length] */ + int xq_ptr, + int[] sLTP_Q15, /* I/O LTP state */ + short[] a_Q12, /* I Short term prediction coefs */ + short[] b_Q14, /* I Long term prediction coefs */ + int b_Q14_ptr, + short[] AR_shp_Q13, /* I Noise shaping AR coefs */ + int AR_shp_Q13_ptr, + int lag, /* I Pitch lag */ + int HarmShapeFIRPacked_Q14, /* I */ + int Tilt_Q14, /* I Spectral tilt */ + int LF_shp_Q14, /* I */ + int Gain_Q16, /* I */ + int Lambda_Q10, /* I */ + int offset_Q10, /* I */ + int length, /* I Input length */ + int shapingLPCOrder, /* I Noise shaping AR filter order */ + int predictLPCOrder /* I Prediction filter order */ + ) + { + int i, j; + int LTP_pred_Q13, LPC_pred_Q10, n_AR_Q12, n_LTP_Q13; + int n_LF_Q12, r_Q10, rr_Q10, q1_Q0, q1_Q10, q2_Q10, rd1_Q20, rd2_Q20; + int exc_Q14, LPC_exc_Q14, xq_Q14, Gain_Q10; + int tmp1, tmp2, sLF_AR_shp_Q14; + int psLPC_Q14; + int shp_lag_ptr; + int pred_lag_ptr; + + shp_lag_ptr = this.sLTP_shp_buf_idx - lag + SilkConstants.HARM_SHAPE_FIR_TAPS / 2; + pred_lag_ptr = this.sLTP_buf_idx - lag + SilkConstants.LTP_ORDER / 2; + Gain_Q10 = Inlines.silk_RSHIFT(Gain_Q16, 6); + + /* Set up short term AR state */ + psLPC_Q14 = SilkConstants.NSQ_LPC_BUF_LENGTH - 1; + + for (i = 0; i < length; i++) + { + /* Generate dither */ + this.rand_seed = Inlines.silk_RAND(this.rand_seed); + + /* Short-term prediction */ + Inlines.OpusAssert(predictLPCOrder == 10 || predictLPCOrder == 16); + /* Avoids introducing a bias because Inlines.silk_SMLAWB() always rounds to -inf */ + LPC_pred_Q10 = Inlines.silk_RSHIFT(predictLPCOrder, 1); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 0], a_Q12[0]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 1], a_Q12[1]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 2], a_Q12[2]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 3], a_Q12[3]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 4], a_Q12[4]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 5], a_Q12[5]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 6], a_Q12[6]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 7], a_Q12[7]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 8], a_Q12[8]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 9], a_Q12[9]); + if (predictLPCOrder == 16) + { + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 10], a_Q12[10]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 11], a_Q12[11]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 12], a_Q12[12]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 13], a_Q12[13]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 14], a_Q12[14]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 15], a_Q12[15]); + } + + /* Long-term prediction */ + if (signalType == SilkConstants.TYPE_VOICED) + { + /* Unrolled loop */ + /* Avoids introducing a bias because Inlines.silk_SMLAWB() always rounds to -inf */ + LTP_pred_Q13 = 2; + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr], b_Q14[b_Q14_ptr]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 1], b_Q14[b_Q14_ptr + 1]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 2], b_Q14[b_Q14_ptr + 2]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 3], b_Q14[b_Q14_ptr + 3]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 4], b_Q14[b_Q14_ptr + 4]); + pred_lag_ptr += 1; + } + else { + LTP_pred_Q13 = 0; + } + + /* Noise shape feedback */ + Inlines.OpusAssert((shapingLPCOrder & 1) == 0); /* check that order is even */ + tmp2 = this.sLPC_Q14[psLPC_Q14]; + tmp1 = this.sAR2_Q14[0]; + this.sAR2_Q14[0] = tmp2; + n_AR_Q12 = Inlines.silk_RSHIFT(shapingLPCOrder, 1); + n_AR_Q12 = Inlines.silk_SMLAWB(n_AR_Q12, tmp2, AR_shp_Q13[AR_shp_Q13_ptr]); + for (j = 2; j < shapingLPCOrder; j += 2) + { + tmp2 = this.sAR2_Q14[j - 1]; + this.sAR2_Q14[j - 1] = tmp1; + n_AR_Q12 = Inlines.silk_SMLAWB(n_AR_Q12, tmp1, AR_shp_Q13[AR_shp_Q13_ptr + j - 1]); + tmp1 = this.sAR2_Q14[j + 0]; + this.sAR2_Q14[j + 0] = tmp2; + n_AR_Q12 = Inlines.silk_SMLAWB(n_AR_Q12, tmp2, AR_shp_Q13[AR_shp_Q13_ptr + j]); + } + this.sAR2_Q14[shapingLPCOrder - 1] = tmp1; + n_AR_Q12 = Inlines.silk_SMLAWB(n_AR_Q12, tmp1, AR_shp_Q13[AR_shp_Q13_ptr + shapingLPCOrder - 1]); + + n_AR_Q12 = Inlines.silk_LSHIFT32(n_AR_Q12, 1); /* Q11 . Q12 */ + n_AR_Q12 = Inlines.silk_SMLAWB(n_AR_Q12, this.sLF_AR_shp_Q14, Tilt_Q14); + + n_LF_Q12 = Inlines.silk_SMULWB(this.sLTP_shp_Q14[this.sLTP_shp_buf_idx - 1], LF_shp_Q14); + n_LF_Q12 = Inlines.silk_SMLAWT(n_LF_Q12, this.sLF_AR_shp_Q14, LF_shp_Q14); + + Inlines.OpusAssert(lag > 0 || signalType != SilkConstants.TYPE_VOICED); + + /* Combine prediction and noise shaping signals */ + tmp1 = Inlines.silk_SUB32(Inlines.silk_LSHIFT32(LPC_pred_Q10, 2), n_AR_Q12); /* Q12 */ + tmp1 = Inlines.silk_SUB32(tmp1, n_LF_Q12); /* Q12 */ + if (lag > 0) + { + /* Symmetric, packed FIR coefficients */ + n_LTP_Q13 = Inlines.silk_SMULWB(Inlines.silk_ADD32(this.sLTP_shp_Q14[shp_lag_ptr], this.sLTP_shp_Q14[shp_lag_ptr - 2]), HarmShapeFIRPacked_Q14); + n_LTP_Q13 = Inlines.silk_SMLAWT(n_LTP_Q13, this.sLTP_shp_Q14[shp_lag_ptr - 1], HarmShapeFIRPacked_Q14); + n_LTP_Q13 = Inlines.silk_LSHIFT(n_LTP_Q13, 1); + shp_lag_ptr += 1; + + tmp2 = Inlines.silk_SUB32(LTP_pred_Q13, n_LTP_Q13); /* Q13 */ + tmp1 = Inlines.silk_ADD_LSHIFT32(tmp2, tmp1, 1); /* Q13 */ + tmp1 = Inlines.silk_RSHIFT_ROUND(tmp1, 3); /* Q10 */ + } + else { + tmp1 = Inlines.silk_RSHIFT_ROUND(tmp1, 2); /* Q10 */ + } + + r_Q10 = Inlines.silk_SUB32(x_sc_Q10[i], tmp1); /* residual error Q10 */ + + /* Flip sign depending on dither */ + if (this.rand_seed < 0) + { + r_Q10 = -r_Q10; + } + r_Q10 = Inlines.silk_LIMIT_32(r_Q10, -(31 << 10), 30 << 10); + + /* Find two quantization level candidates and measure their rate-distortion */ + q1_Q10 = Inlines.silk_SUB32(r_Q10, offset_Q10); + q1_Q0 = Inlines.silk_RSHIFT(q1_Q10, 10); + if (q1_Q0 > 0) + { + q1_Q10 = Inlines.silk_SUB32(Inlines.silk_LSHIFT(q1_Q0, 10), SilkConstants.QUANT_LEVEL_ADJUST_Q10); + q1_Q10 = Inlines.silk_ADD32(q1_Q10, offset_Q10); + q2_Q10 = Inlines.silk_ADD32(q1_Q10, 1024); + rd1_Q20 = Inlines.silk_SMULBB(q1_Q10, Lambda_Q10); + rd2_Q20 = Inlines.silk_SMULBB(q2_Q10, Lambda_Q10); + } + else if (q1_Q0 == 0) + { + q1_Q10 = offset_Q10; + q2_Q10 = Inlines.silk_ADD32(q1_Q10, 1024 - SilkConstants.QUANT_LEVEL_ADJUST_Q10); + rd1_Q20 = Inlines.silk_SMULBB(q1_Q10, Lambda_Q10); + rd2_Q20 = Inlines.silk_SMULBB(q2_Q10, Lambda_Q10); + } + else if (q1_Q0 == -1) + { + q2_Q10 = offset_Q10; + q1_Q10 = Inlines.silk_SUB32(q2_Q10, 1024 - SilkConstants.QUANT_LEVEL_ADJUST_Q10); + rd1_Q20 = Inlines.silk_SMULBB(-q1_Q10, Lambda_Q10); + rd2_Q20 = Inlines.silk_SMULBB(q2_Q10, Lambda_Q10); + } + else { /* Q1_Q0 < -1 */ + q1_Q10 = Inlines.silk_ADD32(Inlines.silk_LSHIFT(q1_Q0, 10), SilkConstants.QUANT_LEVEL_ADJUST_Q10); + q1_Q10 = Inlines.silk_ADD32(q1_Q10, offset_Q10); + q2_Q10 = Inlines.silk_ADD32(q1_Q10, 1024); + rd1_Q20 = Inlines.silk_SMULBB(-q1_Q10, Lambda_Q10); + rd2_Q20 = Inlines.silk_SMULBB(-q2_Q10, Lambda_Q10); + } + rr_Q10 = Inlines.silk_SUB32(r_Q10, q1_Q10); + rd1_Q20 = Inlines.silk_SMLABB(rd1_Q20, rr_Q10, rr_Q10); + rr_Q10 = Inlines.silk_SUB32(r_Q10, q2_Q10); + rd2_Q20 = Inlines.silk_SMLABB(rd2_Q20, rr_Q10, rr_Q10); + + if (rd2_Q20 < rd1_Q20) + { + q1_Q10 = q2_Q10; + } + + pulses[pulses_ptr + i] = (sbyte)Inlines.silk_RSHIFT_ROUND(q1_Q10, 10); + + /* Excitation */ + exc_Q14 = Inlines.silk_LSHIFT(q1_Q10, 4); + if (this.rand_seed < 0) + { + exc_Q14 = -exc_Q14; + } + + /* Add predictions */ + LPC_exc_Q14 = Inlines.silk_ADD_LSHIFT32(exc_Q14, LTP_pred_Q13, 1); + xq_Q14 = Inlines.silk_ADD_LSHIFT32(LPC_exc_Q14, LPC_pred_Q10, 4); + + /* Scale XQ back to normal level before saving */ + xq[xq_ptr + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWW(xq_Q14, Gain_Q10), 8)); + + /* Update states */ + psLPC_Q14 += 1; + this.sLPC_Q14[psLPC_Q14] = xq_Q14; + sLF_AR_shp_Q14 = Inlines.silk_SUB_LSHIFT32(xq_Q14, n_AR_Q12, 2); + this.sLF_AR_shp_Q14 = sLF_AR_shp_Q14; + + this.sLTP_shp_Q14[this.sLTP_shp_buf_idx] = Inlines.silk_SUB_LSHIFT32(sLF_AR_shp_Q14, n_LF_Q12, 2); + sLTP_Q15[this.sLTP_buf_idx] = Inlines.silk_LSHIFT(LPC_exc_Q14, 1); + this.sLTP_shp_buf_idx++; + this.sLTP_buf_idx++; + + /* Make dither dependent on quantized signal */ + this.rand_seed = Inlines.silk_ADD32_ovflw(this.rand_seed, pulses[pulses_ptr + i]); + } + + /* Update LPC synth buffer */ + Array.Copy(this.sLPC_Q14, length, this.sLPC_Q14, 0, SilkConstants.NSQ_LPC_BUF_LENGTH); + } + + private void silk_nsq_scale_states( + SilkChannelEncoder psEncC, /* I Encoder State */ + int[] x_Q3, /* I input in Q3 */ + int x_Q3_ptr, + int[] x_sc_Q10, /* O input scaled with 1/Gain */ + short[] sLTP, /* I re-whitened LTP state in Q0 */ + int[] sLTP_Q15, /* O LTP state matching scaled input */ + int subfr, /* I subframe number */ + int LTP_scale_Q14, /* I */ + int[] Gains_Q16, /* I [MAX_NB_SUBFR] */ + int[] pitchL, /* I Pitch lag [MAX_NB_SUBFR] */ + int signal_type /* I Signal type */ + ) + { + int i, lag; + int gain_adj_Q16, inv_gain_Q31, inv_gain_Q23; + + lag = pitchL[subfr]; + inv_gain_Q31 = Inlines.silk_INVERSE32_varQ(Inlines.silk_max(Gains_Q16[subfr], 1), 47); + Inlines.OpusAssert(inv_gain_Q31 != 0); + + /* Calculate gain adjustment factor */ + if (Gains_Q16[subfr] != this.prev_gain_Q16) + { + gain_adj_Q16 = Inlines.silk_DIV32_varQ(this.prev_gain_Q16, Gains_Q16[subfr], 16); + } + else { + gain_adj_Q16 = (int)1 << 16; + } + + /* Scale input */ + inv_gain_Q23 = Inlines.silk_RSHIFT_ROUND(inv_gain_Q31, 8); + for (i = 0; i < psEncC.subfr_length; i++) + { + x_sc_Q10[i] = Inlines.silk_SMULWW(x_Q3[x_Q3_ptr + i], inv_gain_Q23); + } + + /* Save inverse gain */ + this.prev_gain_Q16 = Gains_Q16[subfr]; + + /* After rewhitening the LTP state is un-scaled, so scale with inv_gain_Q16 */ + if (this.rewhite_flag != 0) + { + if (subfr == 0) + { + /* Do LTP downscaling */ + inv_gain_Q31 = Inlines.silk_LSHIFT(Inlines.silk_SMULWB(inv_gain_Q31, LTP_scale_Q14), 2); + } + for (i = this.sLTP_buf_idx - lag - SilkConstants.LTP_ORDER / 2; i < this.sLTP_buf_idx; i++) + { + Inlines.OpusAssert(i < SilkConstants.MAX_FRAME_LENGTH); + sLTP_Q15[i] = Inlines.silk_SMULWB(inv_gain_Q31, sLTP[i]); + } + } + + /* Adjust for changing gain */ + if (gain_adj_Q16 != (int)1 << 16) + { + /* Scale long-term shaping state */ + for (i = this.sLTP_shp_buf_idx - psEncC.ltp_mem_length; i < this.sLTP_shp_buf_idx; i++) + { + this.sLTP_shp_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, this.sLTP_shp_Q14[i]); + } + + /* Scale long-term prediction state */ + if (signal_type == SilkConstants.TYPE_VOICED && this.rewhite_flag == 0) + { + for (i = this.sLTP_buf_idx - lag - SilkConstants.LTP_ORDER / 2; i < this.sLTP_buf_idx; i++) + { + sLTP_Q15[i] = Inlines.silk_SMULWW(gain_adj_Q16, sLTP_Q15[i]); + } + } + + this.sLF_AR_shp_Q14 = Inlines.silk_SMULWW(gain_adj_Q16, this.sLF_AR_shp_Q14); + + /* Scale short-term prediction and shaping states */ + for (i = 0; i < SilkConstants.NSQ_LPC_BUF_LENGTH; i++) + { + this.sLPC_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, this.sLPC_Q14[i]); + } + for (i = 0; i < SilkConstants.MAX_SHAPE_LPC_ORDER; i++) + { + this.sAR2_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, this.sAR2_Q14[i]); + } + } + } + + internal void silk_NSQ_del_dec( + SilkChannelEncoder psEncC, /* I Encoder State */ + SideInfoIndices psIndices, /* I/O Quantization Indices */ + int[] x_Q3, /* I Prefiltered input signal */ + sbyte[] pulses, /* O Quantized pulse signal */ + short[][] PredCoef_Q12, /* I Short term prediction coefs [2 * MAX_LPC_ORDER] */ + short[] LTPCoef_Q14, /* I Long term prediction coefs LTP_ORDER * MAX_NB_SUBFR] */ + short[] AR2_Q13, /* I Noise shaping coefs [MAX_NB_SUBFR * MAX_SHAPE_LPC_ORDER] */ + int[] HarmShapeGain_Q14, /* I Long term shaping coefs [MAX_NB_SUBFR] */ + int[] Tilt_Q14, /* I Spectral tilt [MAX_NB_SUBFR] */ + int[] LF_shp_Q14, /* I Low frequency shaping coefs [MAX_NB_SUBFR] */ + int[] Gains_Q16, /* I Quantization step sizes [MAX_NB_SUBFR] */ + int[] pitchL, /* I Pitch lags [MAX_NB_SUBFR] */ + int Lambda_Q10, /* I Rate/distortion tradeoff */ + int LTP_scale_Q14 /* I LTP state scaling */ + ) + { + int i, k, lag, start_idx, LSF_interpolation_flag, Winner_ind, subfr; + int last_smple_idx, smpl_buf_idx, decisionDelay; + int A_Q12; + int pulses_ptr = 0; + int pxq; + int[] sLTP_Q15; + short[] sLTP; + int HarmShapeFIRPacked_Q14; + int offset_Q10; + int RDmin_Q10, Gain_Q10; + int[] x_sc_Q10; + int[] delayedGain_Q10; + int x_Q3_ptr = 0; + NSQ_del_dec_struct[] psDelDec; + NSQ_del_dec_struct psDD; + + /* Set unvoiced lag to the previous one, overwrite later for voiced */ + lag = this.lagPrev; + + Inlines.OpusAssert(this.prev_gain_Q16 != 0); + + /* Initialize delayed decision states */ + psDelDec = new NSQ_del_dec_struct[psEncC.nStatesDelayedDecision]; + for (int c = 0; c < psEncC.nStatesDelayedDecision; c++) + { + psDelDec[c] = new NSQ_del_dec_struct(psEncC.shapingLPCOrder); + } + + for (k = 0; k < psEncC.nStatesDelayedDecision; k++) + { + psDD = psDelDec[k]; + psDD.Seed = (k + psIndices.Seed) & 3; + psDD.SeedInit = psDD.Seed; + psDD.RD_Q10 = 0; + psDD.LF_AR_Q14 = this.sLF_AR_shp_Q14; + psDD.Shape_Q14[0] = this.sLTP_shp_Q14[psEncC.ltp_mem_length - 1]; + Array.Copy(this.sLPC_Q14, psDD.sLPC_Q14, SilkConstants.NSQ_LPC_BUF_LENGTH); + Array.Copy(this.sAR2_Q14, psDD.sAR2_Q14, psEncC.shapingLPCOrder); + } + + offset_Q10 = Tables.silk_Quantization_Offsets_Q10[psIndices.signalType >> 1][psIndices.quantOffsetType]; + smpl_buf_idx = 0; /* index of oldest samples */ + + decisionDelay = Inlines.silk_min_int(SilkConstants.DECISION_DELAY, psEncC.subfr_length); + + /* For voiced frames limit the decision delay to lower than the pitch lag */ + if (psIndices.signalType == SilkConstants.TYPE_VOICED) + { + for (k = 0; k < psEncC.nb_subfr; k++) + { + decisionDelay = Inlines.silk_min_int(decisionDelay, pitchL[k] - SilkConstants.LTP_ORDER / 2 - 1); + } + } + else { + if (lag > 0) + { + decisionDelay = Inlines.silk_min_int(decisionDelay, lag - SilkConstants.LTP_ORDER / 2 - 1); + } + } + + if (psIndices.NLSFInterpCoef_Q2 == 4) + { + LSF_interpolation_flag = 0; + } + else { + LSF_interpolation_flag = 1; + } + + sLTP_Q15 = new int[psEncC.ltp_mem_length + psEncC.frame_length]; + sLTP = new short[psEncC.ltp_mem_length + psEncC.frame_length]; + x_sc_Q10 = new int[psEncC.subfr_length]; + delayedGain_Q10 = new int[SilkConstants.DECISION_DELAY]; + + /* Set up pointers to start of sub frame */ + pxq = psEncC.ltp_mem_length; + this.sLTP_shp_buf_idx = psEncC.ltp_mem_length; + this.sLTP_buf_idx = psEncC.ltp_mem_length; + subfr = 0; + for (k = 0; k < psEncC.nb_subfr; k++) + { + A_Q12 = (((k >> 1) | (1 - LSF_interpolation_flag))); + + /* Noise shape parameters */ + Inlines.OpusAssert(HarmShapeGain_Q14[k] >= 0); + HarmShapeFIRPacked_Q14 = Inlines.silk_RSHIFT(HarmShapeGain_Q14[k], 2); + HarmShapeFIRPacked_Q14 |= Inlines.silk_LSHIFT((int)Inlines.silk_RSHIFT(HarmShapeGain_Q14[k], 1), 16); + + this.rewhite_flag = 0; + if (psIndices.signalType == SilkConstants.TYPE_VOICED) + { + /* Voiced */ + lag = pitchL[k]; + + /* Re-whitening */ + if ((k & (3 - Inlines.silk_LSHIFT(LSF_interpolation_flag, 1))) == 0) + { + if (k == 2) + { + /* RESET DELAYED DECISIONS */ + /* Find winner */ + RDmin_Q10 = psDelDec[0].RD_Q10; + Winner_ind = 0; + for (i = 1; i < psEncC.nStatesDelayedDecision; i++) + { + if (psDelDec[i].RD_Q10 < RDmin_Q10) + { + RDmin_Q10 = psDelDec[i].RD_Q10; + Winner_ind = i; + } + } + for (i = 0; i < psEncC.nStatesDelayedDecision; i++) + { + if (i != Winner_ind) + { + psDelDec[i].RD_Q10 += (int.MaxValue >> 4); + Inlines.OpusAssert(psDelDec[i].RD_Q10 >= 0); + } + } + + /* Copy final part of signals from winner state to output and long-term filter states */ + psDD = psDelDec[Winner_ind]; + last_smple_idx = smpl_buf_idx + decisionDelay; + for (i = 0; i < decisionDelay; i++) + { + last_smple_idx = (last_smple_idx - 1) & SilkConstants.DECISION_DELAY_MASK; + pulses[pulses_ptr + i - decisionDelay] = (sbyte)Inlines.silk_RSHIFT_ROUND(psDD.Q_Q10[last_smple_idx], 10); + this.xq[pxq + i - decisionDelay] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND( + Inlines.silk_SMULWW(psDD.Xq_Q14[last_smple_idx], Gains_Q16[1]), 14)); + this.sLTP_shp_Q14[this.sLTP_shp_buf_idx - decisionDelay + i] = psDD.Shape_Q14[last_smple_idx]; + } + + subfr = 0; + } + + /* Rewhiten with new A coefs */ + start_idx = psEncC.ltp_mem_length - lag - psEncC.predictLPCOrder - SilkConstants.LTP_ORDER / 2; + Inlines.OpusAssert(start_idx > 0); + + Filters.silk_LPC_analysis_filter(sLTP, start_idx, this.xq, start_idx + k * psEncC.subfr_length, + PredCoef_Q12[A_Q12], 0, psEncC.ltp_mem_length - start_idx, psEncC.predictLPCOrder); + + this.sLTP_buf_idx = psEncC.ltp_mem_length; + this.rewhite_flag = 1; + } + } + + silk_nsq_del_dec_scale_states( + psEncC, + psDelDec, + x_Q3, + x_Q3_ptr, + x_sc_Q10, + sLTP, + sLTP_Q15, + k, + psEncC.nStatesDelayedDecision, + LTP_scale_Q14, + Gains_Q16, + pitchL, + psIndices.signalType, + decisionDelay); + + BoxedValueInt smpl_buf_idx_boxed = new BoxedValueInt(smpl_buf_idx); +#if !UNSAFE + silk_noise_shape_quantizer_del_dec( + psDelDec, + psIndices.signalType, + x_sc_Q10, + pulses, + pulses_ptr, + this.xq, + pxq, + sLTP_Q15, + delayedGain_Q10, + PredCoef_Q12[A_Q12], + LTPCoef_Q14, + k * SilkConstants.LTP_ORDER, + AR2_Q13, + k * SilkConstants.MAX_SHAPE_LPC_ORDER, + lag, + HarmShapeFIRPacked_Q14, + Tilt_Q14[k], + LF_shp_Q14[k], + Gains_Q16[k], + Lambda_Q10, + offset_Q10, + psEncC.subfr_length, + subfr++, + psEncC.shapingLPCOrder, + psEncC.predictLPCOrder, + psEncC.warping_Q16, + psEncC.nStatesDelayedDecision, + smpl_buf_idx_boxed, + decisionDelay); + +#else + unsafe + { + fixed (short* pred_coef = PredCoef_Q12[A_Q12]) + { + fixed (short* ltp_coef = LTPCoef_Q14) + { + fixed (int* sltp = sLTP_Q15) + { + short* b_q14 = ltp_coef + (k * SilkConstants.LTP_ORDER); + silk_noise_shape_quantizer_del_dec( + psDelDec, + psIndices.signalType, + x_sc_Q10, + pulses, + pulses_ptr, + this.xq, + pxq, + sltp, + delayedGain_Q10, + pred_coef, + b_q14, + AR2_Q13, + k * SilkConstants.MAX_SHAPE_LPC_ORDER, + lag, + HarmShapeFIRPacked_Q14, + Tilt_Q14[k], + LF_shp_Q14[k], + Gains_Q16[k], + Lambda_Q10, + offset_Q10, + psEncC.subfr_length, + subfr++, + psEncC.shapingLPCOrder, + psEncC.predictLPCOrder, + psEncC.warping_Q16, + psEncC.nStatesDelayedDecision, + smpl_buf_idx_boxed, + decisionDelay); + } + } + } + } +#endif + smpl_buf_idx = smpl_buf_idx_boxed.Val; + + x_Q3_ptr += psEncC.subfr_length; + pulses_ptr += psEncC.subfr_length; + pxq += psEncC.subfr_length; + } + + /* Find winner */ + RDmin_Q10 = psDelDec[0].RD_Q10; + Winner_ind = 0; + for (k = 1; k < psEncC.nStatesDelayedDecision; k++) + { + if (psDelDec[k].RD_Q10 < RDmin_Q10) + { + RDmin_Q10 = psDelDec[k].RD_Q10; + Winner_ind = k; + } + } + + /* Copy final part of signals from winner state to output and long-term filter states */ + psDD = psDelDec[Winner_ind]; + psIndices.Seed = (sbyte)(psDD.SeedInit); + last_smple_idx = smpl_buf_idx + decisionDelay; + Gain_Q10 = Inlines.silk_RSHIFT32(Gains_Q16[psEncC.nb_subfr - 1], 6); + for (i = 0; i < decisionDelay; i++) + { + last_smple_idx = (last_smple_idx - 1) & SilkConstants.DECISION_DELAY_MASK; + pulses[pulses_ptr + i - decisionDelay] = (sbyte)Inlines.silk_RSHIFT_ROUND(psDD.Q_Q10[last_smple_idx], 10); + this.xq[pxq + i - decisionDelay] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND( + Inlines.silk_SMULWW(psDD.Xq_Q14[last_smple_idx], Gain_Q10), 8)); + this.sLTP_shp_Q14[this.sLTP_shp_buf_idx - decisionDelay + i] = psDD.Shape_Q14[last_smple_idx]; + } + Array.Copy(psDD.sLPC_Q14, psEncC.subfr_length, this.sLPC_Q14, 0, SilkConstants.NSQ_LPC_BUF_LENGTH); + Array.Copy(psDD.sAR2_Q14, 0, this.sAR2_Q14, 0, psEncC.shapingLPCOrder); + + /* Update states */ + this.sLF_AR_shp_Q14 = psDD.LF_AR_Q14; + this.lagPrev = pitchL[psEncC.nb_subfr - 1]; + + /* Save quantized speech signal */ + Arrays.MemMoveShort(this.xq, psEncC.frame_length, 0, psEncC.ltp_mem_length); + Arrays.MemMoveInt(this.sLTP_shp_Q14, psEncC.frame_length, 0, psEncC.ltp_mem_length); + } + +#if !UNSAFE + + /******************************************/ + /* Noise shape quantizer for one subframe */ + /******************************************/ + private void silk_noise_shape_quantizer_del_dec( + NSQ_del_dec_struct[] psDelDec, /* I/O Delayed decision states */ + int signalType, /* I Signal type */ + int[] x_Q10, /* I */ + sbyte[] pulses, /* O */ + int pulses_ptr, + short[] xq, /* O */ + int xq_ptr, + int[] sLTP_Q15, /* I/O LTP filter state */ + int[] delayedGain_Q10, /* I/O Gain delay buffer */ + short[] a_Q12, /* I Short term prediction coefs */ + short[] b_Q14, /* I Long term prediction coefs */ + int b_Q14_ptr, + short[] AR_shp_Q13, /* I Noise shaping coefs */ + int AR_shp_Q13_ptr, + int lag, /* I Pitch lag */ + int HarmShapeFIRPacked_Q14, /* I */ + int Tilt_Q14, /* I Spectral tilt */ + int LF_shp_Q14, /* I */ + int Gain_Q16, /* I */ + int Lambda_Q10, /* I */ + int offset_Q10, /* I */ + int length, /* I Input length */ + int subfr, /* I Subframe number */ + int shapingLPCOrder, /* I Shaping LPC filter order */ + int predictLPCOrder, /* I Prediction filter order */ + int warping_Q16, /* I */ + int nStatesDelayedDecision, /* I Number of states in decision tree */ + BoxedValueInt smpl_buf_idx, /* I Index to newest samples in buffers */ + int decisionDelay /* I */ + ) + { + int i, j, k, Winner_ind, RDmin_ind, RDmax_ind, last_smple_idx; + int Winner_rand_state; + int LTP_pred_Q14, LPC_pred_Q14, n_AR_Q14, n_LTP_Q14; + int n_LF_Q14, r_Q10, rr_Q10, rd1_Q10, rd2_Q10, RDmin_Q10, RDmax_Q10; + int q1_Q0, q1_Q10, q2_Q10, exc_Q14, LPC_exc_Q14, xq_Q14, Gain_Q10; + int tmp1, tmp2, sLF_AR_shp_Q14; + int pred_lag_ptr, shp_lag_ptr, psLPC_Q14; + NSQ_sample_struct[] sampleStates; + NSQ_del_dec_struct psDD; + int SS_left; + int SS_right; + + Inlines.OpusAssert(nStatesDelayedDecision > 0); + sampleStates = new NSQ_sample_struct[2 * nStatesDelayedDecision]; + // [porting note] structs must be initialized manually here + for (int c = 0; c < 2 * nStatesDelayedDecision; c++) + { + sampleStates[c] = new NSQ_sample_struct(); + } + + shp_lag_ptr = this.sLTP_shp_buf_idx - lag + SilkConstants.HARM_SHAPE_FIR_TAPS / 2; + pred_lag_ptr = this.sLTP_buf_idx - lag + SilkConstants.LTP_ORDER / 2; + Gain_Q10 = Inlines.silk_RSHIFT(Gain_Q16, 6); + + for (i = 0; i < length; i++) + { + /* Perform common calculations used in all states */ + + /* Long-term prediction */ + if (signalType == SilkConstants.TYPE_VOICED) + { + /* Unrolled loop */ + /* Avoids introducing a bias because Inlines.silk_SMLAWB() always rounds to -inf */ + LTP_pred_Q14 = 2; + LTP_pred_Q14 = Inlines.silk_SMLAWB(LTP_pred_Q14, sLTP_Q15[pred_lag_ptr], b_Q14[b_Q14_ptr + 0]); + LTP_pred_Q14 = Inlines.silk_SMLAWB(LTP_pred_Q14, sLTP_Q15[pred_lag_ptr - 1], b_Q14[b_Q14_ptr + 1]); + LTP_pred_Q14 = Inlines.silk_SMLAWB(LTP_pred_Q14, sLTP_Q15[pred_lag_ptr - 2], b_Q14[b_Q14_ptr + 2]); + LTP_pred_Q14 = Inlines.silk_SMLAWB(LTP_pred_Q14, sLTP_Q15[pred_lag_ptr - 3], b_Q14[b_Q14_ptr + 3]); + LTP_pred_Q14 = Inlines.silk_SMLAWB(LTP_pred_Q14, sLTP_Q15[pred_lag_ptr - 4], b_Q14[b_Q14_ptr + 4]); + LTP_pred_Q14 = Inlines.silk_LSHIFT(LTP_pred_Q14, 1); /* Q13 . Q14 */ + pred_lag_ptr += 1; + } + else { + LTP_pred_Q14 = 0; + } + + /* Long-term shaping */ + if (lag > 0) + { + /* Symmetric, packed FIR coefficients */ + n_LTP_Q14 = Inlines.silk_SMULWB(Inlines.silk_ADD32(this.sLTP_shp_Q14[shp_lag_ptr], this.sLTP_shp_Q14[shp_lag_ptr - 2]), HarmShapeFIRPacked_Q14); + n_LTP_Q14 = Inlines.silk_SMLAWT(n_LTP_Q14, this.sLTP_shp_Q14[shp_lag_ptr - 1], HarmShapeFIRPacked_Q14); + n_LTP_Q14 = Inlines.silk_SUB_LSHIFT32(LTP_pred_Q14, n_LTP_Q14, 2); /* Q12 . Q14 */ + shp_lag_ptr += 1; + } + else { + n_LTP_Q14 = 0; + } + + for (k = 0; k < nStatesDelayedDecision; k++) + { + /* Delayed decision state */ + psDD = psDelDec[k]; + int[] psDD_sAR2 = psDD.sAR2_Q14; + + /* Sample state */ + SS_left = 2 * k; + SS_right = SS_left + 1; + + /* Generate dither */ + psDD.Seed = Inlines.silk_RAND(psDD.Seed); + + /* Pointer used in short term prediction and shaping */ + psLPC_Q14 = SilkConstants.NSQ_LPC_BUF_LENGTH - 1 + i; + /* Short-term prediction */ + Inlines.OpusAssert(predictLPCOrder == 10 || predictLPCOrder == 16); + /* Avoids introducing a bias because Inlines.silk_SMLAWB() always rounds to -inf */ + LPC_pred_Q14 = Inlines.silk_RSHIFT(predictLPCOrder, 1); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14], a_Q12[0]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 1], a_Q12[1]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 2], a_Q12[2]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 3], a_Q12[3]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 4], a_Q12[4]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 5], a_Q12[5]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 6], a_Q12[6]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 7], a_Q12[7]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 8], a_Q12[8]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 9], a_Q12[9]); + if (predictLPCOrder == 16) + { + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 10], a_Q12[10]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 11], a_Q12[11]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 12], a_Q12[12]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 13], a_Q12[13]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 14], a_Q12[14]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 15], a_Q12[15]); + } + LPC_pred_Q14 = Inlines.silk_LSHIFT(LPC_pred_Q14, 4); /* Q10 . Q14 */ + + + /* Noise shape feedback */ + Inlines.OpusAssert((shapingLPCOrder & 1) == 0); /* check that order is even */ + /* Output of lowpass section */ + tmp2 = Inlines.silk_SMLAWB(psDD.sLPC_Q14[psLPC_Q14], psDD_sAR2[0], warping_Q16); + /* Output of allpass section */ + tmp1 = Inlines.silk_SMLAWB(psDD_sAR2[0], psDD_sAR2[1] - tmp2, warping_Q16); + psDD_sAR2[0] = tmp2; + n_AR_Q14 = Inlines.silk_RSHIFT(shapingLPCOrder, 1); + n_AR_Q14 = Inlines.silk_SMLAWB(n_AR_Q14, tmp2, AR_shp_Q13[AR_shp_Q13_ptr]); + /* Loop over allpass sections */ + for (j = 2; j < shapingLPCOrder; j += 2) + { + /* Output of allpass section */ + tmp2 = Inlines.silk_SMLAWB(psDD_sAR2[j - 1], psDD_sAR2[j + 0] - tmp1, warping_Q16); + psDD_sAR2[j - 1] = tmp1; + n_AR_Q14 = Inlines.silk_SMLAWB(n_AR_Q14, tmp1, AR_shp_Q13[AR_shp_Q13_ptr + j - 1]); + /* Output of allpass section */ + tmp1 = Inlines.silk_SMLAWB(psDD_sAR2[j + 0], psDD_sAR2[j + 1] - tmp2, warping_Q16); + psDD_sAR2[j + 0] = tmp2; + n_AR_Q14 = Inlines.silk_SMLAWB(n_AR_Q14, tmp2, AR_shp_Q13[AR_shp_Q13_ptr + j]); + } + psDD_sAR2[shapingLPCOrder - 1] = tmp1; + n_AR_Q14 = Inlines.silk_SMLAWB(n_AR_Q14, tmp1, AR_shp_Q13[AR_shp_Q13_ptr + shapingLPCOrder - 1]); + + n_AR_Q14 = Inlines.silk_LSHIFT(n_AR_Q14, 1); /* Q11 . Q12 */ + n_AR_Q14 = Inlines.silk_SMLAWB(n_AR_Q14, psDD.LF_AR_Q14, Tilt_Q14); /* Q12 */ + n_AR_Q14 = Inlines.silk_LSHIFT(n_AR_Q14, 2); /* Q12 . Q14 */ + + n_LF_Q14 = Inlines.silk_SMULWB(psDD.Shape_Q14[smpl_buf_idx.Val], LF_shp_Q14); /* Q12 */ + n_LF_Q14 = Inlines.silk_SMLAWT(n_LF_Q14, psDD.LF_AR_Q14, LF_shp_Q14); /* Q12 */ + n_LF_Q14 = Inlines.silk_LSHIFT(n_LF_Q14, 2); /* Q12 . Q14 */ + + /* Input minus prediction plus noise feedback */ + /* r = x[ i ] - LTP_pred - LPC_pred + n_AR + n_Tilt + n_LF + n_LTP */ + tmp1 = Inlines.silk_ADD32(n_AR_Q14, n_LF_Q14); /* Q14 */ + tmp2 = Inlines.silk_ADD32(n_LTP_Q14, LPC_pred_Q14); /* Q13 */ + tmp1 = Inlines.silk_SUB32(tmp2, tmp1); /* Q13 */ + tmp1 = Inlines.silk_RSHIFT_ROUND(tmp1, 4); /* Q10 */ + + r_Q10 = Inlines.silk_SUB32(x_Q10[i], tmp1); /* residual error Q10 */ + + /* Flip sign depending on dither */ + if (psDD.Seed < 0) + { + r_Q10 = -r_Q10; + } + r_Q10 = Inlines.silk_LIMIT_32(r_Q10, -(31 << 10), 30 << 10); + + /* Find two quantization level candidates and measure their rate-distortion */ + q1_Q10 = Inlines.silk_SUB32(r_Q10, offset_Q10); + q1_Q0 = Inlines.silk_RSHIFT(q1_Q10, 10); + if (q1_Q0 > 0) + { + q1_Q10 = Inlines.silk_SUB32(Inlines.silk_LSHIFT(q1_Q0, 10), SilkConstants.QUANT_LEVEL_ADJUST_Q10); + q1_Q10 = Inlines.silk_ADD32(q1_Q10, offset_Q10); + q2_Q10 = Inlines.silk_ADD32(q1_Q10, 1024); + rd1_Q10 = Inlines.silk_SMULBB(q1_Q10, Lambda_Q10); + rd2_Q10 = Inlines.silk_SMULBB(q2_Q10, Lambda_Q10); + } + else if (q1_Q0 == 0) + { + q1_Q10 = offset_Q10; + q2_Q10 = Inlines.silk_ADD32(q1_Q10, 1024 - SilkConstants.QUANT_LEVEL_ADJUST_Q10); + rd1_Q10 = Inlines.silk_SMULBB(q1_Q10, Lambda_Q10); + rd2_Q10 = Inlines.silk_SMULBB(q2_Q10, Lambda_Q10); + } + else if (q1_Q0 == -1) + { + q2_Q10 = offset_Q10; + q1_Q10 = Inlines.silk_SUB32(q2_Q10, 1024 - SilkConstants.QUANT_LEVEL_ADJUST_Q10); + rd1_Q10 = Inlines.silk_SMULBB(-q1_Q10, Lambda_Q10); + rd2_Q10 = Inlines.silk_SMULBB(q2_Q10, Lambda_Q10); + } + else { /* q1_Q0 < -1 */ + q1_Q10 = Inlines.silk_ADD32(Inlines.silk_LSHIFT(q1_Q0, 10), SilkConstants.QUANT_LEVEL_ADJUST_Q10); + q1_Q10 = Inlines.silk_ADD32(q1_Q10, offset_Q10); + q2_Q10 = Inlines.silk_ADD32(q1_Q10, 1024); + rd1_Q10 = Inlines.silk_SMULBB(-q1_Q10, Lambda_Q10); + rd2_Q10 = Inlines.silk_SMULBB(-q2_Q10, Lambda_Q10); + } + rr_Q10 = Inlines.silk_SUB32(r_Q10, q1_Q10); + rd1_Q10 = Inlines.silk_RSHIFT(Inlines.silk_SMLABB(rd1_Q10, rr_Q10, rr_Q10), 10); + rr_Q10 = Inlines.silk_SUB32(r_Q10, q2_Q10); + rd2_Q10 = Inlines.silk_RSHIFT(Inlines.silk_SMLABB(rd2_Q10, rr_Q10, rr_Q10), 10); + + if (rd1_Q10 < rd2_Q10) + { + sampleStates[SS_left].RD_Q10 = Inlines.silk_ADD32(psDD.RD_Q10, rd1_Q10); + sampleStates[SS_right].RD_Q10 = Inlines.silk_ADD32(psDD.RD_Q10, rd2_Q10); + sampleStates[SS_left].Q_Q10 = q1_Q10; + sampleStates[SS_right].Q_Q10 = q2_Q10; + } + else { + sampleStates[SS_left].RD_Q10 = Inlines.silk_ADD32(psDD.RD_Q10, rd2_Q10); + sampleStates[SS_right].RD_Q10 = Inlines.silk_ADD32(psDD.RD_Q10, rd1_Q10); + sampleStates[SS_left].Q_Q10 = q2_Q10; + sampleStates[SS_right].Q_Q10 = q1_Q10; + } + + /* Update states for best quantization */ + + /* Quantized excitation */ + exc_Q14 = Inlines.silk_LSHIFT32(sampleStates[SS_left].Q_Q10, 4); + if (psDD.Seed < 0) + { + exc_Q14 = -exc_Q14; + } + + /* Add predictions */ + LPC_exc_Q14 = Inlines.silk_ADD32(exc_Q14, LTP_pred_Q14); + xq_Q14 = Inlines.silk_ADD32(LPC_exc_Q14, LPC_pred_Q14); + + /* Update states */ + sLF_AR_shp_Q14 = Inlines.silk_SUB32(xq_Q14, n_AR_Q14); + sampleStates[SS_left].sLTP_shp_Q14 = Inlines.silk_SUB32(sLF_AR_shp_Q14, n_LF_Q14); + sampleStates[SS_left].LF_AR_Q14 = sLF_AR_shp_Q14; + sampleStates[SS_left].LPC_exc_Q14 = LPC_exc_Q14; + sampleStates[SS_left].xq_Q14 = xq_Q14; + + /* Update states for second best quantization */ + + /* Quantized excitation */ + exc_Q14 = Inlines.silk_LSHIFT32(sampleStates[SS_right].Q_Q10, 4); + if (psDD.Seed < 0) + { + exc_Q14 = -exc_Q14; + } + + + /* Add predictions */ + LPC_exc_Q14 = Inlines.silk_ADD32(exc_Q14, LTP_pred_Q14); + xq_Q14 = Inlines.silk_ADD32(LPC_exc_Q14, LPC_pred_Q14); + + /* Update states */ + sLF_AR_shp_Q14 = Inlines.silk_SUB32(xq_Q14, n_AR_Q14); + sampleStates[SS_right].sLTP_shp_Q14 = Inlines.silk_SUB32(sLF_AR_shp_Q14, n_LF_Q14); + sampleStates[SS_right].LF_AR_Q14 = sLF_AR_shp_Q14; + sampleStates[SS_right].LPC_exc_Q14 = LPC_exc_Q14; + sampleStates[SS_right].xq_Q14 = xq_Q14; + } + + smpl_buf_idx.Val = (smpl_buf_idx.Val - 1) & SilkConstants.DECISION_DELAY_MASK; /* Index to newest samples */ + last_smple_idx = (smpl_buf_idx.Val + decisionDelay) & SilkConstants.DECISION_DELAY_MASK; /* Index to decisionDelay old samples */ + + /* Find winner */ + RDmin_Q10 = sampleStates[0].RD_Q10; + Winner_ind = 0; + for (k = 1; k < nStatesDelayedDecision; k++) + { + if (sampleStates[k * 2].RD_Q10 < RDmin_Q10) + { + RDmin_Q10 = sampleStates[k * 2].RD_Q10; + Winner_ind = k; + } + } + + /* Increase RD values of expired states */ + Winner_rand_state = psDelDec[Winner_ind].RandState[last_smple_idx]; + for (k = 0; k < nStatesDelayedDecision; k++) + { + if (psDelDec[k].RandState[last_smple_idx] != Winner_rand_state) + { + int k2 = k * 2; + sampleStates[k2].RD_Q10 = Inlines.silk_ADD32(sampleStates[k2].RD_Q10, int.MaxValue >> 4); + sampleStates[k2 + 1].RD_Q10 = Inlines.silk_ADD32(sampleStates[k2 + 1].RD_Q10, int.MaxValue >> 4); + Inlines.OpusAssert(sampleStates[k2].RD_Q10 >= 0); + } + } + + /* Find worst in first set and best in second set */ + RDmax_Q10 = sampleStates[0].RD_Q10; + RDmin_Q10 = sampleStates[1].RD_Q10; + RDmax_ind = 0; + RDmin_ind = 0; + for (k = 1; k < nStatesDelayedDecision; k++) + { + int k2 = k * 2; + /* find worst in first set */ + if (sampleStates[k2].RD_Q10 > RDmax_Q10) + { + RDmax_Q10 = sampleStates[k2].RD_Q10; + RDmax_ind = k; + } + /* find best in second set */ + if (sampleStates[k2 + 1].RD_Q10 < RDmin_Q10) + { + RDmin_Q10 = sampleStates[k2 + 1].RD_Q10; + RDmin_ind = k; + } + } + + /* Replace a state if best from second set outperforms worst in first set */ + if (RDmin_Q10 < RDmax_Q10) + { + psDelDec[RDmax_ind].PartialCopyFrom(psDelDec[RDmin_ind], i); + sampleStates[RDmax_ind * 2] = (sampleStates[RDmin_ind * 2 + 1]); // porting note: this uses struct copy-on-assign semantics + } + + /* Write samples from winner to output and long-term filter states */ + psDD = psDelDec[Winner_ind]; + if (subfr > 0 || i >= decisionDelay) + { + pulses[pulses_ptr + i - decisionDelay] = (sbyte)Inlines.silk_RSHIFT_ROUND(psDD.Q_Q10[last_smple_idx], 10); + xq[xq_ptr + i - decisionDelay] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND( + Inlines.silk_SMULWW(psDD.Xq_Q14[last_smple_idx], delayedGain_Q10[last_smple_idx]), 8)); + this.sLTP_shp_Q14[this.sLTP_shp_buf_idx - decisionDelay] = psDD.Shape_Q14[last_smple_idx]; + sLTP_Q15[this.sLTP_buf_idx - decisionDelay] = psDD.Pred_Q15[last_smple_idx]; + } + this.sLTP_shp_buf_idx++; + this.sLTP_buf_idx++; + + /* Update states */ + for (k = 0; k < nStatesDelayedDecision; k++) + { + psDD = psDelDec[k]; + SS_left = k * 2; + psDD.LF_AR_Q14 = sampleStates[SS_left].LF_AR_Q14; + psDD.sLPC_Q14[SilkConstants.NSQ_LPC_BUF_LENGTH + i] = sampleStates[SS_left].xq_Q14; + psDD.Xq_Q14[smpl_buf_idx.Val] = sampleStates[SS_left].xq_Q14; + psDD.Q_Q10[smpl_buf_idx.Val] = sampleStates[SS_left].Q_Q10; + psDD.Pred_Q15[smpl_buf_idx.Val] = Inlines.silk_LSHIFT32(sampleStates[SS_left].LPC_exc_Q14, 1); + psDD.Shape_Q14[smpl_buf_idx.Val] = sampleStates[SS_left].sLTP_shp_Q14; + psDD.Seed = Inlines.silk_ADD32_ovflw(psDD.Seed, Inlines.silk_RSHIFT_ROUND(sampleStates[SS_left].Q_Q10, 10)); + psDD.RandState[smpl_buf_idx.Val] = psDD.Seed; + psDD.RD_Q10 = sampleStates[SS_left].RD_Q10; + } + delayedGain_Q10[smpl_buf_idx.Val] = Gain_Q10; + } + + /* Update LPC states */ + for (k = 0; k < nStatesDelayedDecision; k++) + { + psDD = psDelDec[k]; + Buffer.BlockCopy(psDD.sLPC_Q14, length * sizeof(int), psDD.sLPC_Q14, 0, SilkConstants.NSQ_LPC_BUF_LENGTH * sizeof(int)); + } + } + +#else + + /******************************************/ + /* Noise shape quantizer for one subframe */ + /******************************************/ + private unsafe void silk_noise_shape_quantizer_del_dec( + NSQ_del_dec_struct[] psDelDec, /* I/O Delayed decision states */ + int signalType, /* I Signal type */ + int[] x_Q10, /* I */ + sbyte[] pulses, /* O */ + int pulses_ptr, + short[] xq, /* O */ + int xq_ptr, + int* sLTP_Q15, /* I/O LTP filter state */ + int[] delayedGain_Q10, /* I/O Gain delay buffer */ + short* a_Q12, /* I Short term prediction coefs */ + short* b_Q14, /* I Long term prediction coefs */ + short[] AR_shp_Q13, /* I Noise shaping coefs */ + int AR_shp_Q13_ptr, + int lag, /* I Pitch lag */ + int HarmShapeFIRPacked_Q14, /* I */ + int Tilt_Q14, /* I Spectral tilt */ + int LF_shp_Q14, /* I */ + int Gain_Q16, /* I */ + int Lambda_Q10, /* I */ + int offset_Q10, /* I */ + int length, /* I Input length */ + int subfr, /* I Subframe number */ + int shapingLPCOrder, /* I Shaping LPC filter order */ + int predictLPCOrder, /* I Prediction filter order */ + int warping_Q16, /* I */ + int nStatesDelayedDecision, /* I Number of states in decision tree */ + BoxedValueInt smpl_buf_idx, /* I Index to newest samples in buffers */ + int decisionDelay /* I */ + ) + { + int i, j, k, Winner_ind, RDmin_ind, RDmax_ind, last_smple_idx; + int Winner_rand_state; + int LTP_pred_Q14, LPC_pred_Q14, n_AR_Q14, n_LTP_Q14; + int n_LF_Q14, r_Q10, rr_Q10, rd1_Q10, rd2_Q10, RDmin_Q10, RDmax_Q10; + int q1_Q0, q1_Q10, q2_Q10, exc_Q14, LPC_exc_Q14, xq_Q14, Gain_Q10; + int tmp1, tmp2, sLF_AR_shp_Q14; + int pred_lag_ptr, shp_lag_ptr, psLPC_Q14; + NSQ_sample_struct[] sampleStates; + NSQ_del_dec_struct psDD; + int SS_left; + int SS_right; + + Inlines.OpusAssert(nStatesDelayedDecision > 0); + sampleStates = new NSQ_sample_struct[2 * nStatesDelayedDecision]; + // [porting note] structs must be initialized manually here + for (int c = 0; c < 2 * nStatesDelayedDecision; c++) + { + sampleStates[c] = new NSQ_sample_struct(); + } + + shp_lag_ptr = this.sLTP_shp_buf_idx - lag + SilkConstants.HARM_SHAPE_FIR_TAPS / 2; + pred_lag_ptr = this.sLTP_buf_idx - lag + SilkConstants.LTP_ORDER / 2; + Gain_Q10 = Inlines.silk_RSHIFT(Gain_Q16, 6); + + fixed (int* psLTP_shp_Q14 = sLTP_shp_Q14) + { + for (i = 0; i < length; i++) + { + /* Perform common calculations used in all states */ + + /* Long-term prediction */ + if (signalType == SilkConstants.TYPE_VOICED) + { + /* Unrolled loop */ + /* Avoids introducing a bias because Inlines.silk_SMLAWB() always rounds to -inf */ + LTP_pred_Q14 = 2; + LTP_pred_Q14 = Inlines.silk_SMLAWB(LTP_pred_Q14, sLTP_Q15[pred_lag_ptr], b_Q14[0]); + LTP_pred_Q14 = Inlines.silk_SMLAWB(LTP_pred_Q14, sLTP_Q15[pred_lag_ptr - 1], b_Q14[1]); + LTP_pred_Q14 = Inlines.silk_SMLAWB(LTP_pred_Q14, sLTP_Q15[pred_lag_ptr - 2], b_Q14[2]); + LTP_pred_Q14 = Inlines.silk_SMLAWB(LTP_pred_Q14, sLTP_Q15[pred_lag_ptr - 3], b_Q14[3]); + LTP_pred_Q14 = Inlines.silk_SMLAWB(LTP_pred_Q14, sLTP_Q15[pred_lag_ptr - 4], b_Q14[4]); + LTP_pred_Q14 = Inlines.silk_LSHIFT(LTP_pred_Q14, 1); /* Q13 . Q14 */ + pred_lag_ptr += 1; + } + else + { + LTP_pred_Q14 = 0; + } + + /* Long-term shaping */ + if (lag > 0) + { + /* Symmetric, packed FIR coefficients */ + n_LTP_Q14 = Inlines.silk_SMULWB(Inlines.silk_ADD32(psLTP_shp_Q14[shp_lag_ptr], psLTP_shp_Q14[shp_lag_ptr - 2]), HarmShapeFIRPacked_Q14); + n_LTP_Q14 = Inlines.silk_SMLAWT(n_LTP_Q14, psLTP_shp_Q14[shp_lag_ptr - 1], HarmShapeFIRPacked_Q14); + n_LTP_Q14 = Inlines.silk_SUB_LSHIFT32(LTP_pred_Q14, n_LTP_Q14, 2); /* Q12 . Q14 */ + shp_lag_ptr += 1; + } + else + { + n_LTP_Q14 = 0; + } + + for (k = 0; k < nStatesDelayedDecision; k++) + { + /* Delayed decision state */ + psDD = psDelDec[k]; + fixed (int* psDD_sAR2 = psDD.sAR2_Q14) + { + /* Sample state */ + SS_left = 2 * k; + SS_right = SS_left + 1; + + /* Generate dither */ + psDD.Seed = Inlines.silk_RAND(psDD.Seed); + + /* Pointer used in short term prediction and shaping */ + psLPC_Q14 = SilkConstants.NSQ_LPC_BUF_LENGTH - 1 + i; + /* Short-term prediction */ + Inlines.OpusAssert(predictLPCOrder == 10 || predictLPCOrder == 16); + /* Avoids introducing a bias because Inlines.silk_SMLAWB() always rounds to -inf */ + LPC_pred_Q14 = Inlines.silk_RSHIFT(predictLPCOrder, 1); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14], a_Q12[0]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 1], a_Q12[1]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 2], a_Q12[2]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 3], a_Q12[3]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 4], a_Q12[4]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 5], a_Q12[5]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 6], a_Q12[6]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 7], a_Q12[7]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 8], a_Q12[8]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 9], a_Q12[9]); + if (predictLPCOrder == 16) + { + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 10], a_Q12[10]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 11], a_Q12[11]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 12], a_Q12[12]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 13], a_Q12[13]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 14], a_Q12[14]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 15], a_Q12[15]); + } + LPC_pred_Q14 = Inlines.silk_LSHIFT(LPC_pred_Q14, 4); /* Q10 . Q14 */ + + + /* Noise shape feedback */ + Inlines.OpusAssert((shapingLPCOrder & 1) == 0); /* check that order is even */ + /* Output of lowpass section */ + tmp2 = Inlines.silk_SMLAWB(psDD.sLPC_Q14[psLPC_Q14], psDD_sAR2[0], warping_Q16); + /* Output of allpass section */ + tmp1 = Inlines.silk_SMLAWB(psDD_sAR2[0], psDD_sAR2[1] - tmp2, warping_Q16); + psDD_sAR2[0] = tmp2; + n_AR_Q14 = Inlines.silk_RSHIFT(shapingLPCOrder, 1); + n_AR_Q14 = Inlines.silk_SMLAWB(n_AR_Q14, tmp2, AR_shp_Q13[AR_shp_Q13_ptr]); + /* Loop over allpass sections */ + for (j = 2; j < shapingLPCOrder; j += 2) + { + /* Output of allpass section */ + tmp2 = Inlines.silk_SMLAWB(psDD_sAR2[j - 1], psDD_sAR2[j + 0] - tmp1, warping_Q16); + psDD_sAR2[j - 1] = tmp1; + n_AR_Q14 = Inlines.silk_SMLAWB(n_AR_Q14, tmp1, AR_shp_Q13[AR_shp_Q13_ptr + j - 1]); + /* Output of allpass section */ + tmp1 = Inlines.silk_SMLAWB(psDD_sAR2[j + 0], psDD_sAR2[j + 1] - tmp2, warping_Q16); + psDD_sAR2[j + 0] = tmp2; + n_AR_Q14 = Inlines.silk_SMLAWB(n_AR_Q14, tmp2, AR_shp_Q13[AR_shp_Q13_ptr + j]); + } + psDD_sAR2[shapingLPCOrder - 1] = tmp1; + n_AR_Q14 = Inlines.silk_SMLAWB(n_AR_Q14, tmp1, AR_shp_Q13[AR_shp_Q13_ptr + shapingLPCOrder - 1]); + + n_AR_Q14 = Inlines.silk_LSHIFT(n_AR_Q14, 1); /* Q11 . Q12 */ + n_AR_Q14 = Inlines.silk_SMLAWB(n_AR_Q14, psDD.LF_AR_Q14, Tilt_Q14); /* Q12 */ + n_AR_Q14 = Inlines.silk_LSHIFT(n_AR_Q14, 2); /* Q12 . Q14 */ + + n_LF_Q14 = Inlines.silk_SMULWB(psDD.Shape_Q14[smpl_buf_idx.Val], LF_shp_Q14); /* Q12 */ + n_LF_Q14 = Inlines.silk_SMLAWT(n_LF_Q14, psDD.LF_AR_Q14, LF_shp_Q14); /* Q12 */ + n_LF_Q14 = Inlines.silk_LSHIFT(n_LF_Q14, 2); /* Q12 . Q14 */ + + /* Input minus prediction plus noise feedback */ + /* r = x[ i ] - LTP_pred - LPC_pred + n_AR + n_Tilt + n_LF + n_LTP */ + tmp1 = Inlines.silk_ADD32(n_AR_Q14, n_LF_Q14); /* Q14 */ + tmp2 = Inlines.silk_ADD32(n_LTP_Q14, LPC_pred_Q14); /* Q13 */ + tmp1 = Inlines.silk_SUB32(tmp2, tmp1); /* Q13 */ + tmp1 = Inlines.silk_RSHIFT_ROUND(tmp1, 4); /* Q10 */ + + r_Q10 = Inlines.silk_SUB32(x_Q10[i], tmp1); /* residual error Q10 */ + + /* Flip sign depending on dither */ + if (psDD.Seed < 0) + { + r_Q10 = -r_Q10; + } + r_Q10 = Inlines.silk_LIMIT_32(r_Q10, -(31 << 10), 30 << 10); + + /* Find two quantization level candidates and measure their rate-distortion */ + q1_Q10 = Inlines.silk_SUB32(r_Q10, offset_Q10); + q1_Q0 = Inlines.silk_RSHIFT(q1_Q10, 10); + if (q1_Q0 > 0) + { + q1_Q10 = Inlines.silk_SUB32(Inlines.silk_LSHIFT(q1_Q0, 10), SilkConstants.QUANT_LEVEL_ADJUST_Q10); + q1_Q10 = Inlines.silk_ADD32(q1_Q10, offset_Q10); + q2_Q10 = Inlines.silk_ADD32(q1_Q10, 1024); + rd1_Q10 = Inlines.silk_SMULBB(q1_Q10, Lambda_Q10); + rd2_Q10 = Inlines.silk_SMULBB(q2_Q10, Lambda_Q10); + } + else if (q1_Q0 == 0) + { + q1_Q10 = offset_Q10; + q2_Q10 = Inlines.silk_ADD32(q1_Q10, 1024 - SilkConstants.QUANT_LEVEL_ADJUST_Q10); + rd1_Q10 = Inlines.silk_SMULBB(q1_Q10, Lambda_Q10); + rd2_Q10 = Inlines.silk_SMULBB(q2_Q10, Lambda_Q10); + } + else if (q1_Q0 == -1) + { + q2_Q10 = offset_Q10; + q1_Q10 = Inlines.silk_SUB32(q2_Q10, 1024 - SilkConstants.QUANT_LEVEL_ADJUST_Q10); + rd1_Q10 = Inlines.silk_SMULBB(-q1_Q10, Lambda_Q10); + rd2_Q10 = Inlines.silk_SMULBB(q2_Q10, Lambda_Q10); + } + else + { /* q1_Q0 < -1 */ + q1_Q10 = Inlines.silk_ADD32(Inlines.silk_LSHIFT(q1_Q0, 10), SilkConstants.QUANT_LEVEL_ADJUST_Q10); + q1_Q10 = Inlines.silk_ADD32(q1_Q10, offset_Q10); + q2_Q10 = Inlines.silk_ADD32(q1_Q10, 1024); + rd1_Q10 = Inlines.silk_SMULBB(-q1_Q10, Lambda_Q10); + rd2_Q10 = Inlines.silk_SMULBB(-q2_Q10, Lambda_Q10); + } + rr_Q10 = Inlines.silk_SUB32(r_Q10, q1_Q10); + rd1_Q10 = Inlines.silk_RSHIFT(Inlines.silk_SMLABB(rd1_Q10, rr_Q10, rr_Q10), 10); + rr_Q10 = Inlines.silk_SUB32(r_Q10, q2_Q10); + rd2_Q10 = Inlines.silk_RSHIFT(Inlines.silk_SMLABB(rd2_Q10, rr_Q10, rr_Q10), 10); + + if (rd1_Q10 < rd2_Q10) + { + sampleStates[SS_left].RD_Q10 = Inlines.silk_ADD32(psDD.RD_Q10, rd1_Q10); + sampleStates[SS_right].RD_Q10 = Inlines.silk_ADD32(psDD.RD_Q10, rd2_Q10); + sampleStates[SS_left].Q_Q10 = q1_Q10; + sampleStates[SS_right].Q_Q10 = q2_Q10; + } + else + { + sampleStates[SS_left].RD_Q10 = Inlines.silk_ADD32(psDD.RD_Q10, rd2_Q10); + sampleStates[SS_right].RD_Q10 = Inlines.silk_ADD32(psDD.RD_Q10, rd1_Q10); + sampleStates[SS_left].Q_Q10 = q2_Q10; + sampleStates[SS_right].Q_Q10 = q1_Q10; + } + + /* Update states for best quantization */ + + /* Quantized excitation */ + exc_Q14 = Inlines.silk_LSHIFT32(sampleStates[SS_left].Q_Q10, 4); + if (psDD.Seed < 0) + { + exc_Q14 = -exc_Q14; + } + + /* Add predictions */ + LPC_exc_Q14 = Inlines.silk_ADD32(exc_Q14, LTP_pred_Q14); + xq_Q14 = Inlines.silk_ADD32(LPC_exc_Q14, LPC_pred_Q14); + + /* Update states */ + sLF_AR_shp_Q14 = Inlines.silk_SUB32(xq_Q14, n_AR_Q14); + sampleStates[SS_left].sLTP_shp_Q14 = Inlines.silk_SUB32(sLF_AR_shp_Q14, n_LF_Q14); + sampleStates[SS_left].LF_AR_Q14 = sLF_AR_shp_Q14; + sampleStates[SS_left].LPC_exc_Q14 = LPC_exc_Q14; + sampleStates[SS_left].xq_Q14 = xq_Q14; + + /* Update states for second best quantization */ + + /* Quantized excitation */ + exc_Q14 = Inlines.silk_LSHIFT32(sampleStates[SS_right].Q_Q10, 4); + if (psDD.Seed < 0) + { + exc_Q14 = -exc_Q14; + } + + + /* Add predictions */ + LPC_exc_Q14 = Inlines.silk_ADD32(exc_Q14, LTP_pred_Q14); + xq_Q14 = Inlines.silk_ADD32(LPC_exc_Q14, LPC_pred_Q14); + + /* Update states */ + sLF_AR_shp_Q14 = Inlines.silk_SUB32(xq_Q14, n_AR_Q14); + sampleStates[SS_right].sLTP_shp_Q14 = Inlines.silk_SUB32(sLF_AR_shp_Q14, n_LF_Q14); + sampleStates[SS_right].LF_AR_Q14 = sLF_AR_shp_Q14; + sampleStates[SS_right].LPC_exc_Q14 = LPC_exc_Q14; + sampleStates[SS_right].xq_Q14 = xq_Q14; + } + } + + smpl_buf_idx.Val = (smpl_buf_idx.Val - 1) & SilkConstants.DECISION_DELAY_MASK; /* Index to newest samples */ + last_smple_idx = (smpl_buf_idx.Val + decisionDelay) & SilkConstants.DECISION_DELAY_MASK; /* Index to decisionDelay old samples */ + + /* Find winner */ + RDmin_Q10 = sampleStates[0].RD_Q10; + Winner_ind = 0; + for (k = 1; k < nStatesDelayedDecision; k++) + { + if (sampleStates[k * 2].RD_Q10 < RDmin_Q10) + { + RDmin_Q10 = sampleStates[k * 2].RD_Q10; + Winner_ind = k; + } + } + + /* Increase RD values of expired states */ + Winner_rand_state = psDelDec[Winner_ind].RandState[last_smple_idx]; + for (k = 0; k < nStatesDelayedDecision; k++) + { + if (psDelDec[k].RandState[last_smple_idx] != Winner_rand_state) + { + int k2 = k * 2; + sampleStates[k2].RD_Q10 = Inlines.silk_ADD32(sampleStates[k2].RD_Q10, int.MaxValue >> 4); + sampleStates[k2 + 1].RD_Q10 = Inlines.silk_ADD32(sampleStates[k2 + 1].RD_Q10, int.MaxValue >> 4); + Inlines.OpusAssert(sampleStates[k2].RD_Q10 >= 0); + } + } + + /* Find worst in first set and best in second set */ + RDmax_Q10 = sampleStates[0].RD_Q10; + RDmin_Q10 = sampleStates[1].RD_Q10; + RDmax_ind = 0; + RDmin_ind = 0; + for (k = 1; k < nStatesDelayedDecision; k++) + { + int k2 = k * 2; + /* find worst in first set */ + if (sampleStates[k2].RD_Q10 > RDmax_Q10) + { + RDmax_Q10 = sampleStates[k2].RD_Q10; + RDmax_ind = k; + } + /* find best in second set */ + if (sampleStates[k2 + 1].RD_Q10 < RDmin_Q10) + { + RDmin_Q10 = sampleStates[k2 + 1].RD_Q10; + RDmin_ind = k; + } + } + + /* Replace a state if best from second set outperforms worst in first set */ + if (RDmin_Q10 < RDmax_Q10) + { + psDelDec[RDmax_ind].PartialCopyFrom(psDelDec[RDmin_ind], i); + sampleStates[RDmax_ind * 2] = (sampleStates[RDmin_ind * 2 + 1]); // porting note: this uses struct copy-on-assign semantics + } + + /* Write samples from winner to output and long-term filter states */ + psDD = psDelDec[Winner_ind]; + if (subfr > 0 || i >= decisionDelay) + { + pulses[pulses_ptr + i - decisionDelay] = (sbyte)Inlines.silk_RSHIFT_ROUND(psDD.Q_Q10[last_smple_idx], 10); + xq[xq_ptr + i - decisionDelay] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND( + Inlines.silk_SMULWW(psDD.Xq_Q14[last_smple_idx], delayedGain_Q10[last_smple_idx]), 8)); + psLTP_shp_Q14[this.sLTP_shp_buf_idx - decisionDelay] = psDD.Shape_Q14[last_smple_idx]; + sLTP_Q15[this.sLTP_buf_idx - decisionDelay] = psDD.Pred_Q15[last_smple_idx]; + } + this.sLTP_shp_buf_idx++; + this.sLTP_buf_idx++; + + /* Update states */ + for (k = 0; k < nStatesDelayedDecision; k++) + { + psDD = psDelDec[k]; + SS_left = k * 2; + psDD.LF_AR_Q14 = sampleStates[SS_left].LF_AR_Q14; + psDD.sLPC_Q14[SilkConstants.NSQ_LPC_BUF_LENGTH + i] = sampleStates[SS_left].xq_Q14; + psDD.Xq_Q14[smpl_buf_idx.Val] = sampleStates[SS_left].xq_Q14; + psDD.Q_Q10[smpl_buf_idx.Val] = sampleStates[SS_left].Q_Q10; + psDD.Pred_Q15[smpl_buf_idx.Val] = Inlines.silk_LSHIFT32(sampleStates[SS_left].LPC_exc_Q14, 1); + psDD.Shape_Q14[smpl_buf_idx.Val] = sampleStates[SS_left].sLTP_shp_Q14; + psDD.Seed = Inlines.silk_ADD32_ovflw(psDD.Seed, Inlines.silk_RSHIFT_ROUND(sampleStates[SS_left].Q_Q10, 10)); + psDD.RandState[smpl_buf_idx.Val] = psDD.Seed; + psDD.RD_Q10 = sampleStates[SS_left].RD_Q10; + } + delayedGain_Q10[smpl_buf_idx.Val] = Gain_Q10; + } + } + + /* Update LPC states */ + for (k = 0; k < nStatesDelayedDecision; k++) + { + psDD = psDelDec[k]; + Buffer.BlockCopy(psDD.sLPC_Q14, length * sizeof(int), psDD.sLPC_Q14, 0, SilkConstants.NSQ_LPC_BUF_LENGTH * sizeof(int)); + } + } + +#endif + + private void silk_nsq_del_dec_scale_states( + SilkChannelEncoder psEncC, /* I Encoder State */ + NSQ_del_dec_struct[] psDelDec, /* I/O Delayed decision states */ + int[] x_Q3, /* I Input in Q3 */ + int x_Q3_ptr, + int[] x_sc_Q10, /* O Input scaled with 1/Gain in Q10 */ + short[] sLTP, /* I Re-whitened LTP state in Q0 */ + int[] sLTP_Q15, /* O LTP state matching scaled input */ + int subfr, /* I Subframe number */ + int nStatesDelayedDecision, /* I Number of del dec states */ + int LTP_scale_Q14, /* I LTP state scaling */ + int[] Gains_Q16, /* I [MAX_NB_SUBFR] */ + int[] pitchL, /* I Pitch lag [MAX_NB_SUBFR] */ + int signal_type, /* I Signal type */ + int decisionDelay /* I Decision delay */ + ) + { + int i, k, lag; + int gain_adj_Q16, inv_gain_Q31, inv_gain_Q23; + NSQ_del_dec_struct psDD; + + lag = pitchL[subfr]; + inv_gain_Q31 = Inlines.silk_INVERSE32_varQ(Inlines.silk_max(Gains_Q16[subfr], 1), 47); + Inlines.OpusAssert(inv_gain_Q31 != 0); + + /* Calculate gain adjustment factor */ + if (Gains_Q16[subfr] != this.prev_gain_Q16) + { + gain_adj_Q16 = Inlines.silk_DIV32_varQ(this.prev_gain_Q16, Gains_Q16[subfr], 16); + } + else { + gain_adj_Q16 = (int)1 << 16; + } + + /* Scale input */ + inv_gain_Q23 = Inlines.silk_RSHIFT_ROUND(inv_gain_Q31, 8); + for (i = 0; i < psEncC.subfr_length; i++) + { + x_sc_Q10[i] = Inlines.silk_SMULWW(x_Q3[x_Q3_ptr + i], inv_gain_Q23); + } + + /* Save inverse gain */ + this.prev_gain_Q16 = Gains_Q16[subfr]; + + /* After rewhitening the LTP state is un-scaled, so scale with inv_gain_Q16 */ + if (this.rewhite_flag != 0) + { + if (subfr == 0) + { + /* Do LTP downscaling */ + inv_gain_Q31 = Inlines.silk_LSHIFT(Inlines.silk_SMULWB(inv_gain_Q31, LTP_scale_Q14), 2); + } + for (i = this.sLTP_buf_idx - lag - SilkConstants.LTP_ORDER / 2; i < this.sLTP_buf_idx; i++) + { + Inlines.OpusAssert(i < SilkConstants.MAX_FRAME_LENGTH); + sLTP_Q15[i] = Inlines.silk_SMULWB(inv_gain_Q31, sLTP[i]); + } + } + + /* Adjust for changing gain */ + if (gain_adj_Q16 != (int)1 << 16) + { + /* Scale long-term shaping state */ + for (i = this.sLTP_shp_buf_idx - psEncC.ltp_mem_length; i < this.sLTP_shp_buf_idx; i++) + { + this.sLTP_shp_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, this.sLTP_shp_Q14[i]); + } + + /* Scale long-term prediction state */ + if (signal_type == SilkConstants.TYPE_VOICED && this.rewhite_flag == 0) + { + for (i = this.sLTP_buf_idx - lag - SilkConstants.LTP_ORDER / 2; i < this.sLTP_buf_idx - decisionDelay; i++) + { + sLTP_Q15[i] = Inlines.silk_SMULWW(gain_adj_Q16, sLTP_Q15[i]); + } + } + + for (k = 0; k < nStatesDelayedDecision; k++) + { + psDD = psDelDec[k]; + + /* Scale scalar states */ + psDD.LF_AR_Q14 = Inlines.silk_SMULWW(gain_adj_Q16, psDD.LF_AR_Q14); + + /* Scale short-term prediction and shaping states */ + for (i = 0; i < SilkConstants.NSQ_LPC_BUF_LENGTH; i++) + { + psDD.sLPC_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, psDD.sLPC_Q14[i]); + } + for (i = 0; i < psEncC.shapingLPCOrder; i++) + { + psDD.sAR2_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, psDD.sAR2_Q14[i]); + } + for (i = 0; i < SilkConstants.DECISION_DELAY; i++) + { + psDD.Pred_Q15[i] = Inlines.silk_SMULWW(gain_adj_Q16, psDD.Pred_Q15[i]); + psDD.Shape_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, psDD.Shape_Q14[i]); + } + } + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkPrefilterState.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkPrefilterState.cs new file mode 100644 index 000000000..638ba7214 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkPrefilterState.cs @@ -0,0 +1,70 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk.Structs +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + + /// + /// Prefilter state + /// + internal class SilkPrefilterState + { + internal readonly short[] sLTP_shp = new short[SilkConstants.LTP_BUF_LENGTH]; + internal readonly int[] sAR_shp = new int[SilkConstants.MAX_SHAPE_LPC_ORDER + 1]; + internal int sLTP_shp_buf_idx = 0; + internal int sLF_AR_shp_Q12 = 0; + internal int sLF_MA_shp_Q12 = 0; + internal int sHarmHP_Q2 = 0; + internal int rand_seed = 0; + internal int lagPrev = 0; + + internal SilkPrefilterState() + { + + } + + internal void Reset() + { + Arrays.MemSetShort(sLTP_shp, 0, SilkConstants.LTP_BUF_LENGTH); + Arrays.MemSetInt(sAR_shp, 0, SilkConstants.MAX_SHAPE_LPC_ORDER + 1); + sLTP_shp_buf_idx = 0; + sLF_AR_shp_Q12 = 0; + sLF_MA_shp_Q12 = 0; + sHarmHP_Q2 = 0; + rand_seed = 0; + lagPrev = 0; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkResamplerState.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkResamplerState.cs new file mode 100644 index 000000000..5e497d93a --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkResamplerState.cs @@ -0,0 +1,94 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk.Structs +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using System; + internal class SilkResamplerState + { + internal readonly int[] sIIR = new int[SilkConstants.SILK_RESAMPLER_MAX_IIR_ORDER]; /* this must be the first element of this struct FIXME why? */ + internal readonly int[] sFIR_i32 = new int[SilkConstants.SILK_RESAMPLER_MAX_FIR_ORDER]; // porting note: these two fields were originally a union, so that means only 1 will ever be used at a time. + internal readonly short[] sFIR_i16 = new short[SilkConstants.SILK_RESAMPLER_MAX_FIR_ORDER]; + + internal readonly short[] delayBuf = new short[48]; + internal int resampler_function = 0; + internal int batchSize = 0; + internal int invRatio_Q16 = 0; + internal int FIR_Order = 0; + internal int FIR_Fracs = 0; + internal int Fs_in_kHz = 0; + internal int Fs_out_kHz = 0; + internal int inputDelay = 0; + + /// + /// POINTER + /// + internal short[] Coefs = null; + + internal void Reset() + { + Arrays.MemSetInt(sIIR, 0, SilkConstants.SILK_RESAMPLER_MAX_IIR_ORDER); + Arrays.MemSetInt(sFIR_i32, 0, SilkConstants.SILK_RESAMPLER_MAX_FIR_ORDER); + Arrays.MemSetShort(sFIR_i16, 0, SilkConstants.SILK_RESAMPLER_MAX_FIR_ORDER); + Arrays.MemSetShort(delayBuf, 0, 48); + resampler_function = 0; + batchSize = 0; + invRatio_Q16 = 0; + FIR_Order = 0; + FIR_Fracs = 0; + Fs_in_kHz = 0; + Fs_out_kHz = 0; + inputDelay = 0; + Coefs = null; + } + + internal void Assign(SilkResamplerState other) + { + resampler_function = other.resampler_function; + batchSize = other.batchSize; + invRatio_Q16 = other.invRatio_Q16; + FIR_Order = other.FIR_Order; + FIR_Fracs = other.FIR_Fracs; + Fs_in_kHz = other.Fs_in_kHz; + Fs_out_kHz = other.Fs_out_kHz; + inputDelay = other.inputDelay; + Coefs = other.Coefs; + Array.Copy(other.sIIR, this.sIIR, SilkConstants.SILK_RESAMPLER_MAX_IIR_ORDER); + Array.Copy(other.sFIR_i32, this.sFIR_i32, SilkConstants.SILK_RESAMPLER_MAX_FIR_ORDER); + Array.Copy(other.sFIR_i16, this.sFIR_i16, SilkConstants.SILK_RESAMPLER_MAX_FIR_ORDER); + Array.Copy(other.delayBuf, this.delayBuf, 48); + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkShapeState.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkShapeState.cs new file mode 100644 index 000000000..c2395ca5b --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkShapeState.cs @@ -0,0 +1,57 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk.Structs +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + + /// + /// Noise shaping analysis state + /// + internal class SilkShapeState + { + internal sbyte LastGainIndex = 0; + internal int HarmBoost_smth_Q16 = 0; + internal int HarmShapeGain_smth_Q16 = 0; + internal int Tilt_smth_Q16 = 0; + + internal void Reset() + { + LastGainIndex = 0; + HarmBoost_smth_Q16 = 0; + HarmShapeGain_smth_Q16 = 0; + Tilt_smth_Q16 = 0; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkVADState.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkVADState.cs new file mode 100644 index 000000000..a851d8a3f --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/SilkVADState.cs @@ -0,0 +1,108 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk.Structs +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + + /// + /// VAD state + /// + internal class SilkVADState + { + /// + /// Analysis filterbank state: 0-8 kHz + /// + internal readonly int[] AnaState = new int[2]; + + /// + /// Analysis filterbank state: 0-4 kHz + /// + internal readonly int[] AnaState1 = new int[2]; + + /// + /// Analysis filterbank state: 0-2 kHz + /// + internal readonly int[] AnaState2 = new int[2]; + + /// + /// Subframe energies + /// + internal readonly int[] XnrgSubfr = new int[SilkConstants.VAD_N_BANDS]; + + /// + /// Smoothed energy level in each band + /// + internal readonly int[] NrgRatioSmth_Q8 = new int[SilkConstants.VAD_N_BANDS]; + + /// + /// State of differentiator in the lowest band + /// + internal short HPstate = 0; + + /// + /// Noise energy level in each band + /// + internal readonly int[] NL = new int[SilkConstants.VAD_N_BANDS]; + + /// + /// Inverse noise energy level in each band + /// + internal readonly int[] inv_NL = new int[SilkConstants.VAD_N_BANDS]; + + /// + /// Noise level estimator bias/offset + /// + internal readonly int[] NoiseLevelBias = new int[SilkConstants.VAD_N_BANDS]; + + /// + /// Frame counter used in the initial phase + /// + internal int counter = 0; + + internal void Reset() + { + Arrays.MemSetInt(AnaState, 0, 2); + Arrays.MemSetInt(AnaState1, 0, 2); + Arrays.MemSetInt(AnaState2, 0, 2); + Arrays.MemSetInt(XnrgSubfr, 0, SilkConstants.VAD_N_BANDS); + Arrays.MemSetInt(NrgRatioSmth_Q8, 0, SilkConstants.VAD_N_BANDS); + HPstate = 0; + Arrays.MemSetInt(NL, 0, SilkConstants.VAD_N_BANDS); + Arrays.MemSetInt(inv_NL, 0, SilkConstants.VAD_N_BANDS); + Arrays.MemSetInt(NoiseLevelBias, 0, SilkConstants.VAD_N_BANDS); + counter = 0; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Structs/StereoDecodeState.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/StereoDecodeState.cs new file mode 100644 index 000000000..d07258ab4 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/StereoDecodeState.cs @@ -0,0 +1,52 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk.Structs +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + + internal class StereoDecodeState + { + internal readonly short[] pred_prev_Q13 = new short[2]; + internal readonly short[] sMid = new short[2]; + internal readonly short[] sSide = new short[2]; + + internal void Reset() + { + Arrays.MemSetShort(pred_prev_Q13, 0, 2); + Arrays.MemSetShort(sMid, 0, 2); + Arrays.MemSetShort(sSide, 0, 2); + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Structs/StereoEncodeState.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/StereoEncodeState.cs new file mode 100644 index 000000000..2bb059c9f --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/StereoEncodeState.cs @@ -0,0 +1,71 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk.Structs +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + + internal class StereoEncodeState + { + internal readonly short[] pred_prev_Q13 = new short[2]; + internal readonly short[] sMid = new short[2]; + internal readonly short[] sSide = new short[2]; + internal readonly int[] mid_side_amp_Q0 = new int[4]; + internal short smth_width_Q14 = 0; + internal short width_prev_Q14 = 0; + internal short silent_side_len = 0; + internal readonly sbyte[][][] predIx = Arrays.InitThreeDimensionalArray(SilkConstants.MAX_FRAMES_PER_PACKET, 2, 3); + internal readonly sbyte[] mid_only_flags = new sbyte[SilkConstants.MAX_FRAMES_PER_PACKET]; + + internal void Reset() + { + Arrays.MemSetShort(pred_prev_Q13, 0, 2); + Arrays.MemSetShort(sMid, 0, 2); + Arrays.MemSetShort(sSide, 0, 2); + Arrays.MemSetInt(mid_side_amp_Q0, 0, 4); + smth_width_Q14 = 0; + width_prev_Q14 = 0; + silent_side_len = 0; + for (int x = 0; x < SilkConstants.MAX_FRAMES_PER_PACKET; x++) + { + for (int y= 0; y < 2; y++) + { + Arrays.MemSetSbyte(predIx[x][y], 0, 3); + } + } + + Arrays.MemSetSbyte(mid_only_flags, 0, SilkConstants.MAX_FRAMES_PER_PACKET); + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Structs/TOCStruct.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/TOCStruct.cs new file mode 100644 index 000000000..0f8af7d66 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Structs/TOCStruct.cs @@ -0,0 +1,66 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk.Structs +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + + /// + /// Struct for TOC (Table of Contents) + /// + internal class TOCStruct + { + /// + /// Voice activity for packet + /// + internal int VADFlag = 0; + + /// + /// Voice activity for each frame in packet + /// + internal readonly int[] VADFlags = new int[SilkConstants.SILK_MAX_FRAMES_PER_PACKET]; + + /// + /// Flag indicating if packet contains in-band FEC + /// + internal int inbandFECFlag = 0; + + internal void Reset() + { + VADFlag = 0; + Arrays.MemSetInt(VADFlags, 0, SilkConstants.SILK_MAX_FRAMES_PER_PACKET); + inbandFECFlag = 0; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/SumSqrShift.cs b/Libraries/Concentus/CSharp/Concentus/Silk/SumSqrShift.cs new file mode 100644 index 000000000..a62e0d51a --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/SumSqrShift.cs @@ -0,0 +1,179 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal static class SumSqrShift + { + /// + /// Compute number of bits to right shift the sum of squares of a vector + /// of int16s to make it fit in an int32 + /// + /// O Energy of x, after shifting to the right + /// O Number of bits right shift applied to energy + /// I Input vector + /// I Length of input vector + internal static void silk_sum_sqr_shift( + out int energy, + out int shift, + short[] x, + int x_ptr, + int len) + { + int i, shft; + int nrg_tmp, nrg; + + nrg = 0; + shft = 0; + len--; + + for (i = 0; i < len; i += 2) + { + nrg = Inlines.silk_SMLABB_ovflw(nrg, x[x_ptr + i], x[x_ptr + i]); + nrg = Inlines.silk_SMLABB_ovflw(nrg, x[x_ptr + i + 1], x[x_ptr + i + 1]); + if (nrg < 0) + { + /* Scale down */ + nrg = unchecked((int)Inlines.silk_RSHIFT_uint(unchecked((uint)nrg), 2)); + shft = 2; + i += 2; + break; + } + } + + for (; i < len; i += 2) + { + nrg_tmp = Inlines.silk_SMULBB(x[x_ptr + i], x[x_ptr + i]); + nrg_tmp = Inlines.silk_SMLABB_ovflw(nrg_tmp, x[x_ptr + i + 1], x[x_ptr + i + 1]); + nrg = unchecked((int)Inlines.silk_ADD_RSHIFT_uint(unchecked((uint)nrg), unchecked((uint)nrg_tmp), shft)); + if (nrg < 0) + { + /* Scale down */ + nrg = unchecked((int)Inlines.silk_RSHIFT_uint(unchecked((uint)nrg), 2)); + shft += 2; + } + } + + if (i == len) + { + /* One sample left to process */ + nrg_tmp = Inlines.silk_SMULBB(x[x_ptr + i], x[x_ptr + i]); + nrg = unchecked((int)Inlines.silk_ADD_RSHIFT_uint(unchecked((uint)nrg), unchecked((uint)nrg_tmp), shft)); + } + + /* Make sure to have at least one extra leading zero (two leading zeros in total) */ + if ((nrg & 0xC0000000) != 0) + { + nrg = unchecked((int)Inlines.silk_RSHIFT_uint(unchecked((uint)nrg), 2)); + shft += 2; + } + + /* Output arguments */ + shift = shft; + energy = nrg; + } + + /// + /// Zero-index variant + /// Compute number of bits to right shift the sum of squares of a vector + /// of int16s to make it fit in an int32 + /// + /// O Energy of x, after shifting to the right + /// O Number of bits right shift applied to energy + /// I Input vector + /// I Length of input vector + internal static void silk_sum_sqr_shift( + out int energy, + out int shift, + short[] x, + int len) + { + int i, shft; + int nrg_tmp, nrg; + + nrg = 0; + shft = 0; + len--; + + for (i = 0; i < len; i += 2) + { + nrg = Inlines.silk_SMLABB_ovflw(nrg, x[i], x[i]); + nrg = Inlines.silk_SMLABB_ovflw(nrg, x[i + 1], x[i + 1]); + if (nrg < 0) + { + /* Scale down */ + nrg = unchecked((int)Inlines.silk_RSHIFT_uint(unchecked((uint)nrg), 2)); + shft = 2; + i += 2; + break; + } + } + + for (; i < len; i += 2) + { + nrg_tmp = Inlines.silk_SMULBB(x[i], x[i]); + nrg_tmp = Inlines.silk_SMLABB_ovflw(nrg_tmp, x[i + 1], x[i + 1]); + nrg = unchecked((int)Inlines.silk_ADD_RSHIFT_uint(unchecked((uint)nrg), unchecked((uint)nrg_tmp), shft)); + if (nrg < 0) + { + /* Scale down */ + nrg = unchecked((int)Inlines.silk_RSHIFT_uint(unchecked((uint)nrg), 2)); + shft += 2; + } + } + + if (i == len) + { + /* One sample left to process */ + nrg_tmp = Inlines.silk_SMULBB(x[i], x[i]); + nrg = unchecked((int)Inlines.silk_ADD_RSHIFT_uint(unchecked((uint)nrg), unchecked((uint)nrg_tmp), shft)); + } + + /* Make sure to have at least one extra leading zero (two leading zeros in total) */ + if ((nrg & 0xC0000000) != 0) + { + nrg = unchecked((int)Inlines.silk_RSHIFT_uint(unchecked((uint)nrg), 2)); + shft += 2; + } + + /* Output arguments */ + shift = shft; + energy = nrg; + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/Tables.cs b/Libraries/Concentus/CSharp/Concentus/Silk/Tables.cs new file mode 100644 index 000000000..cd2facb79 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/Tables.cs @@ -0,0 +1,1065 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal static class Tables + { + /// + /// Cosine approximation table for LSF conversion + /// Q12 values (even) + /// + internal static readonly short[] silk_LSFCosTab_Q12 = { + 8192, 8190, 8182, 8170, + 8152, 8130, 8104, 8072, + 8034, 7994, 7946, 7896, + 7840, 7778, 7714, 7644, + 7568, 7490, 7406, 7318, + 7226, 7128, 7026, 6922, + 6812, 6698, 6580, 6458, + 6332, 6204, 6070, 5934, + 5792, 5648, 5502, 5352, + 5198, 5040, 4880, 4718, + 4552, 4382, 4212, 4038, + 3862, 3684, 3502, 3320, + 3136, 2948, 2760, 2570, + 2378, 2186, 1990, 1794, + 1598, 1400, 1202, 1002, + 802, 602, 402, 202, + 0, -202, -402, -602, + -802, -1002, -1202, -1400, + -1598, -1794, -1990, -2186, + -2378, -2570, -2760, -2948, + -3136, -3320, -3502, -3684, + -3862, -4038, -4212, -4382, + -4552, -4718, -4880, -5040, + -5198, -5352, -5502, -5648, + -5792, -5934, -6070, -6204, + -6332, -6458, -6580, -6698, + -6812, -6922, -7026, -7128, + -7226, -7318, -7406, -7490, + -7568, -7644, -7714, -7778, + -7840, -7896, -7946, -7994, + -8034, -8072, -8104, -8130, + -8152, -8170, -8182, -8190, + -8192 + }; + + internal static readonly byte[][] silk_gain_iCDF = + { + new byte[] { + 224, 112, 44, 15, 3, 2, 1, 0 + }, + new byte[] { + 254, 237, 192, 132, 70, 23, 4, 0 + }, + new byte[] { + 255, 252, 226, 155, 61, 11, 2, 0 + } + }; + + internal static readonly byte[] silk_delta_gain_iCDF = { + 250, 245, 234, 203, 71, 50, 42, 38, + 35, 33, 31, 29, 28, 27, 26, 25, + 24, 23, 22, 21, 20, 19, 18, 17, + 16, 15, 14, 13, 12, 11, 10, 9, + 8, 7, 6, 5, 4, 3, 2, 1, + 0 + }; + + internal static readonly byte[] silk_LTP_per_index_iCDF = { + 179, 99, 0 + }; + + internal static readonly byte[] silk_LTP_gain_iCDF_0 = { + 71, 56, 43, 30, 21, 12, 6, 0 + }; + + internal static readonly byte[] silk_LTP_gain_iCDF_1 = { + 199, 165, 144, 124, 109, 96, 84, 71, + 61, 51, 42, 32, 23, 15, 8, 0 + }; + + internal static readonly byte[] silk_LTP_gain_iCDF_2 = { + 241, 225, 211, 199, 187, 175, 164, 153, + 142, 132, 123, 114, 105, 96, 88, 80, + 72, 64, 57, 50, 44, 38, 33, 29, + 24, 20, 16, 12, 9, 5, 2, 0 + }; + + internal static readonly short silk_LTP_gain_middle_avg_RD_Q14 = 12304; + + internal static readonly byte[] silk_LTP_gain_BITS_Q5_0 = { + 15, 131, 138, 138, 155, 155, 173, 173 + }; + + internal static readonly byte[] silk_LTP_gain_BITS_Q5_1 = { + 69, 93, 115, 118, 131, 138, 141, 138, + 150, 150, 155, 150, 155, 160, 166, 160 + }; + + internal static readonly byte[] silk_LTP_gain_BITS_Q5_2 = { + 131, 128, 134, 141, 141, 141, 145, 145, + 145, 150, 155, 155, 155, 155, 160, 160, + 160, 160, 166, 166, 173, 173, 182, 192, + 182, 192, 192, 192, 205, 192, 205, 224 + }; + + internal static readonly byte[][] silk_LTP_gain_iCDF_ptrs = { + silk_LTP_gain_iCDF_0, + silk_LTP_gain_iCDF_1, + silk_LTP_gain_iCDF_2 + }; + + internal static readonly byte[][] silk_LTP_gain_BITS_Q5_ptrs = { + silk_LTP_gain_BITS_Q5_0, + silk_LTP_gain_BITS_Q5_1, + silk_LTP_gain_BITS_Q5_2 + }; + + internal static readonly sbyte[][] silk_LTP_gain_vq_0 = + { + new sbyte[] { 4, 6, 24, 7, 5 }, + new sbyte[] { 0, 0, 2, 0, 0 }, + new sbyte[] { 12, 28, 41, 13, -4 }, + new sbyte[] { -9, 15, 42, 25, 14 }, + new sbyte[] { 1, -2, 62, 41, -9 }, + new sbyte[] { -10, 37, 65, -4, 3 }, + new sbyte[] { -6, 4, 66, 7, -8 }, + new sbyte[] { 16, 14, 38, -3, 33 } + }; + + internal static readonly sbyte[][] silk_LTP_gain_vq_1 = + { + new sbyte[] {13, 22, 39, 23, 12}, + new sbyte[] {-1, 36, 64, 27, -6}, + new sbyte[] {-7, 10, 55, 43, 17}, + new sbyte[] { 1, 1, 8, 1, 1}, + new sbyte[] { 6, -11, 74, 53, -9 }, + new sbyte[] {-12, 55, 76, -12, 8 }, + new sbyte[] { -3, 3, 93, 27, -4 }, + new sbyte[] { 26, 39, 59, 3, -8 }, + new sbyte[] { 2, 0, 77, 11, 9 }, + new sbyte[] { -8, 22, 44, -6, 7 }, + new sbyte[] { 40, 9, 26, 3, 9 }, + new sbyte[] { -7, 20, 101, -7, 4 }, + new sbyte[] { 3, -8, 42, 26, 0 }, + new sbyte[] { -15, 33, 68, 2, 23 }, + new sbyte[] { -2, 55, 46, -2, 15 }, + new sbyte[] { 3, -1, 21, 16, 41 } + }; + + internal static readonly sbyte[][] silk_LTP_gain_vq_2 = + { + new sbyte[] { -6, 27, 61, 39, 5 }, + new sbyte[] { -11, 42, 88, 4, 1 }, + new sbyte[] { -2, 60, 65, 6, -4 }, + new sbyte[] { -1, -5, 73, 56, 1 }, + new sbyte[] { -9, 19, 94, 29, -9 }, + new sbyte[] { 0, 12, 99, 6, 4 }, + new sbyte[] { 8, -19, 102, 46, -13 }, + new sbyte[] { 3, 2, 13, 3, 2 }, + new sbyte[] { 9, -21, 84, 72, -18 }, + new sbyte[] { -11, 46, 104, -22, 8 }, + new sbyte[] { 18, 38, 48, 23, 0 }, + new sbyte[] { -16, 70, 83, -21, 11 }, + new sbyte[] { 5, -11, 117, 22, -8 }, + new sbyte[] { -6, 23, 117, -12, 3 }, + new sbyte[] { 3, -8, 95, 28, 4 }, + new sbyte[] { -10, 15, 77, 60, -15 }, + new sbyte[] { -1, 4, 124, 2, -4 }, + new sbyte[] { 3, 38, 84, 24, -25 }, + new sbyte[] { 2, 13, 42, 13, 31 }, + new sbyte[] { 21, -4, 56, 46, -1 }, + new sbyte[] { -1, 35, 79, -13, 19 }, + new sbyte[] { -7, 65, 88, -9, -14 }, + new sbyte[] { 20, 4, 81, 49, -29 }, + new sbyte[] { 20, 0, 75, 3, -17 }, + new sbyte[] { 5, -9, 44, 92, -8 }, + new sbyte[] { 1, -3, 22, 69, 31 }, + new sbyte[] { -6, 95, 41, -12, 5 }, + new sbyte[] { 39, 67, 16, -4, 1 }, + new sbyte[] { 0, -6, 120, 55, -36 }, + new sbyte[] { -13, 44, 122, 4, -24 }, + new sbyte[] { 81, 5, 11, 3, 7 }, + new sbyte[] { 2, 0, 9, 10, 88 } + }; + + internal static readonly sbyte[][][] silk_LTP_vq_ptrs_Q7 = { + silk_LTP_gain_vq_0, + silk_LTP_gain_vq_1, + silk_LTP_gain_vq_2 + }; + + /* Maximum frequency-dependent response of the pitch taps above, + computed as max(abs(freqz(taps))) */ + internal static readonly byte[] silk_LTP_gain_vq_0_gain = { + 46, 2, 90, 87, 93, 91, 82, 98 + }; + + internal static readonly byte[] silk_LTP_gain_vq_1_gain = { + 109, 120, 118, 12, 113, 115, 117, 119, + 99, 59, 87, 111, 63, 111, 112, 80 + }; + + internal static readonly byte[] silk_LTP_gain_vq_2_gain = { + 126, 124, 125, 124, 129, 121, 126, 23, + 132, 127, 127, 127, 126, 127, 122, 133, + 130, 134, 101, 118, 119, 145, 126, 86, + 124, 120, 123, 119, 170, 173, 107, 109 + }; + + internal static readonly byte[][] silk_LTP_vq_gain_ptrs_Q7 = { + silk_LTP_gain_vq_0_gain, + silk_LTP_gain_vq_1_gain, + silk_LTP_gain_vq_2_gain + }; + + internal static readonly sbyte[] silk_LTP_vq_sizes = { + 8, 16, 32 + }; + + internal static readonly byte[] silk_NLSF_CB1_NB_MB_Q8 = { + 12, 35, 60, 83, 108, 132, 157, 180, + 206, 228, 15, 32, 55, 77, 101, 125, + 151, 175, 201, 225, 19, 42, 66, 89, + 114, 137, 162, 184, 209, 230, 12, 25, + 50, 72, 97, 120, 147, 172, 200, 223, + 26, 44, 69, 90, 114, 135, 159, 180, + 205, 225, 13, 22, 53, 80, 106, 130, + 156, 180, 205, 228, 15, 25, 44, 64, + 90, 115, 142, 168, 196, 222, 19, 24, + 62, 82, 100, 120, 145, 168, 190, 214, + 22, 31, 50, 79, 103, 120, 151, 170, + 203, 227, 21, 29, 45, 65, 106, 124, + 150, 171, 196, 224, 30, 49, 75, 97, + 121, 142, 165, 186, 209, 229, 19, 25, + 52, 70, 93, 116, 143, 166, 192, 219, + 26, 34, 62, 75, 97, 118, 145, 167, + 194, 217, 25, 33, 56, 70, 91, 113, + 143, 165, 196, 223, 21, 34, 51, 72, + 97, 117, 145, 171, 196, 222, 20, 29, + 50, 67, 90, 117, 144, 168, 197, 221, + 22, 31, 48, 66, 95, 117, 146, 168, + 196, 222, 24, 33, 51, 77, 116, 134, + 158, 180, 200, 224, 21, 28, 70, 87, + 106, 124, 149, 170, 194, 217, 26, 33, + 53, 64, 83, 117, 152, 173, 204, 225, + 27, 34, 65, 95, 108, 129, 155, 174, + 210, 225, 20, 26, 72, 99, 113, 131, + 154, 176, 200, 219, 34, 43, 61, 78, + 93, 114, 155, 177, 205, 229, 23, 29, + 54, 97, 124, 138, 163, 179, 209, 229, + 30, 38, 56, 89, 118, 129, 158, 178, + 200, 231, 21, 29, 49, 63, 85, 111, + 142, 163, 193, 222, 27, 48, 77, 103, + 133, 158, 179, 196, 215, 232, 29, 47, + 74, 99, 124, 151, 176, 198, 220, 237, + 33, 42, 61, 76, 93, 121, 155, 174, + 207, 225, 29, 53, 87, 112, 136, 154, + 170, 188, 208, 227, 24, 30, 52, 84, + 131, 150, 166, 186, 203, 229, 37, 48, + 64, 84, 104, 118, 156, 177, 201, 230 + }; + + internal static readonly byte[] silk_NLSF_CB1_iCDF_NB_MB = { + 212, 178, 148, 129, 108, 96, 85, 82, + 79, 77, 61, 59, 57, 56, 51, 49, + 48, 45, 42, 41, 40, 38, 36, 34, + 31, 30, 21, 12, 10, 3, 1, 0, + 255, 245, 244, 236, 233, 225, 217, 203, + 190, 176, 175, 161, 149, 136, 125, 114, + 102, 91, 81, 71, 60, 52, 43, 35, + 28, 20, 19, 18, 12, 11, 5, 0 + }; + + internal static readonly byte[] silk_NLSF_CB2_SELECT_NB_MB = { + 16, 0, 0, 0, 0, 99, 66, 36, + 36, 34, 36, 34, 34, 34, 34, 83, + 69, 36, 52, 34, 116, 102, 70, 68, + 68, 176, 102, 68, 68, 34, 65, 85, + 68, 84, 36, 116, 141, 152, 139, 170, + 132, 187, 184, 216, 137, 132, 249, 168, + 185, 139, 104, 102, 100, 68, 68, 178, + 218, 185, 185, 170, 244, 216, 187, 187, + 170, 244, 187, 187, 219, 138, 103, 155, + 184, 185, 137, 116, 183, 155, 152, 136, + 132, 217, 184, 184, 170, 164, 217, 171, + 155, 139, 244, 169, 184, 185, 170, 164, + 216, 223, 218, 138, 214, 143, 188, 218, + 168, 244, 141, 136, 155, 170, 168, 138, + 220, 219, 139, 164, 219, 202, 216, 137, + 168, 186, 246, 185, 139, 116, 185, 219, + 185, 138, 100, 100, 134, 100, 102, 34, + 68, 68, 100, 68, 168, 203, 221, 218, + 168, 167, 154, 136, 104, 70, 164, 246, + 171, 137, 139, 137, 155, 218, 219, 139 + }; + + internal static readonly byte[] silk_NLSF_CB2_iCDF_NB_MB = { + 255, 254, 253, 238, 14, 3, 2, 1, + 0, 255, 254, 252, 218, 35, 3, 2, + 1, 0, 255, 254, 250, 208, 59, 4, + 2, 1, 0, 255, 254, 246, 194, 71, + 10, 2, 1, 0, 255, 252, 236, 183, + 82, 8, 2, 1, 0, 255, 252, 235, + 180, 90, 17, 2, 1, 0, 255, 248, + 224, 171, 97, 30, 4, 1, 0, 255, + 254, 236, 173, 95, 37, 7, 1, 0 + }; + + internal static readonly byte[] silk_NLSF_CB2_BITS_NB_MB_Q5 = { + 255, 255, 255, 131, 6, 145, 255, 255, + 255, 255, 255, 236, 93, 15, 96, 255, + 255, 255, 255, 255, 194, 83, 25, 71, + 221, 255, 255, 255, 255, 162, 73, 34, + 66, 162, 255, 255, 255, 210, 126, 73, + 43, 57, 173, 255, 255, 255, 201, 125, + 71, 48, 58, 130, 255, 255, 255, 166, + 110, 73, 57, 62, 104, 210, 255, 255, + 251, 123, 65, 55, 68, 100, 171, 255 + }; + + internal static readonly byte[] silk_NLSF_PRED_NB_MB_Q8 = { + 179, 138, 140, 148, 151, 149, 153, 151, + 163, 116, 67, 82, 59, 92, 72, 100, + 89, 92 + }; + + internal static readonly short[] silk_NLSF_DELTA_MIN_NB_MB_Q15 = { + 250, 3, 6, 3, 3, 3, 4, 3, + 3, 3, 461 + }; + + internal static readonly NLSFCodebook silk_NLSF_CB_NB_MB = new NLSFCodebook() + { + nVectors = 32, + order = 10, + quantStepSize_Q16 = (short)(((int)((0.18f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.18f, 16)*/), + invQuantStepSize_Q6 = (short)(((int)((1.0f / 0.18f) * ((long)1 << (6)) + 0.5))/*Inlines.SILK_CONST(1.0f / 0.18f, 6)*/), + CB1_NLSF_Q8 = silk_NLSF_CB1_NB_MB_Q8, + CB1_iCDF = silk_NLSF_CB1_iCDF_NB_MB, + pred_Q8 = silk_NLSF_PRED_NB_MB_Q8, + ec_sel = silk_NLSF_CB2_SELECT_NB_MB, + ec_iCDF = silk_NLSF_CB2_iCDF_NB_MB, + ec_Rates_Q5 = silk_NLSF_CB2_BITS_NB_MB_Q5, + deltaMin_Q15 = silk_NLSF_DELTA_MIN_NB_MB_Q15, + }; + + internal static readonly byte[] silk_NLSF_CB1_WB_Q8 = { + 7, 23, 38, 54, 69, 85, 100, 116, + 131, 147, 162, 178, 193, 208, 223, 239, + 13, 25, 41, 55, 69, 83, 98, 112, + 127, 142, 157, 171, 187, 203, 220, 236, + 15, 21, 34, 51, 61, 78, 92, 106, + 126, 136, 152, 167, 185, 205, 225, 240, + 10, 21, 36, 50, 63, 79, 95, 110, + 126, 141, 157, 173, 189, 205, 221, 237, + 17, 20, 37, 51, 59, 78, 89, 107, + 123, 134, 150, 164, 184, 205, 224, 240, + 10, 15, 32, 51, 67, 81, 96, 112, + 129, 142, 158, 173, 189, 204, 220, 236, + 8, 21, 37, 51, 65, 79, 98, 113, + 126, 138, 155, 168, 179, 192, 209, 218, + 12, 15, 34, 55, 63, 78, 87, 108, + 118, 131, 148, 167, 185, 203, 219, 236, + 16, 19, 32, 36, 56, 79, 91, 108, + 118, 136, 154, 171, 186, 204, 220, 237, + 11, 28, 43, 58, 74, 89, 105, 120, + 135, 150, 165, 180, 196, 211, 226, 241, + 6, 16, 33, 46, 60, 75, 92, 107, + 123, 137, 156, 169, 185, 199, 214, 225, + 11, 19, 30, 44, 57, 74, 89, 105, + 121, 135, 152, 169, 186, 202, 218, 234, + 12, 19, 29, 46, 57, 71, 88, 100, + 120, 132, 148, 165, 182, 199, 216, 233, + 17, 23, 35, 46, 56, 77, 92, 106, + 123, 134, 152, 167, 185, 204, 222, 237, + 14, 17, 45, 53, 63, 75, 89, 107, + 115, 132, 151, 171, 188, 206, 221, 240, + 9, 16, 29, 40, 56, 71, 88, 103, + 119, 137, 154, 171, 189, 205, 222, 237, + 16, 19, 36, 48, 57, 76, 87, 105, + 118, 132, 150, 167, 185, 202, 218, 236, + 12, 17, 29, 54, 71, 81, 94, 104, + 126, 136, 149, 164, 182, 201, 221, 237, + 15, 28, 47, 62, 79, 97, 115, 129, + 142, 155, 168, 180, 194, 208, 223, 238, + 8, 14, 30, 45, 62, 78, 94, 111, + 127, 143, 159, 175, 192, 207, 223, 239, + 17, 30, 49, 62, 79, 92, 107, 119, + 132, 145, 160, 174, 190, 204, 220, 235, + 14, 19, 36, 45, 61, 76, 91, 108, + 121, 138, 154, 172, 189, 205, 222, 238, + 12, 18, 31, 45, 60, 76, 91, 107, + 123, 138, 154, 171, 187, 204, 221, 236, + 13, 17, 31, 43, 53, 70, 83, 103, + 114, 131, 149, 167, 185, 203, 220, 237, + 17, 22, 35, 42, 58, 78, 93, 110, + 125, 139, 155, 170, 188, 206, 224, 240, + 8, 15, 34, 50, 67, 83, 99, 115, + 131, 146, 162, 178, 193, 209, 224, 239, + 13, 16, 41, 66, 73, 86, 95, 111, + 128, 137, 150, 163, 183, 206, 225, 241, + 17, 25, 37, 52, 63, 75, 92, 102, + 119, 132, 144, 160, 175, 191, 212, 231, + 19, 31, 49, 65, 83, 100, 117, 133, + 147, 161, 174, 187, 200, 213, 227, 242, + 18, 31, 52, 68, 88, 103, 117, 126, + 138, 149, 163, 177, 192, 207, 223, 239, + 16, 29, 47, 61, 76, 90, 106, 119, + 133, 147, 161, 176, 193, 209, 224, 240, + 15, 21, 35, 50, 61, 73, 86, 97, + 110, 119, 129, 141, 175, 198, 218, 237 + }; + + internal static readonly byte[] silk_NLSF_CB1_iCDF_WB = { + 225, 204, 201, 184, 183, 175, 158, 154, + 153, 135, 119, 115, 113, 110, 109, 99, + 98, 95, 79, 68, 52, 50, 48, 45, + 43, 32, 31, 27, 18, 10, 3, 0, + 255, 251, 235, 230, 212, 201, 196, 182, + 167, 166, 163, 151, 138, 124, 110, 104, + 90, 78, 76, 70, 69, 57, 45, 34, + 24, 21, 11, 6, 5, 4, 3, 0 + }; + + internal static readonly byte[] silk_NLSF_CB2_SELECT_WB = { + 0, 0, 0, 0, 0, 0, 0, 1, + 100, 102, 102, 68, 68, 36, 34, 96, + 164, 107, 158, 185, 180, 185, 139, 102, + 64, 66, 36, 34, 34, 0, 1, 32, + 208, 139, 141, 191, 152, 185, 155, 104, + 96, 171, 104, 166, 102, 102, 102, 132, + 1, 0, 0, 0, 0, 16, 16, 0, + 80, 109, 78, 107, 185, 139, 103, 101, + 208, 212, 141, 139, 173, 153, 123, 103, + 36, 0, 0, 0, 0, 0, 0, 1, + 48, 0, 0, 0, 0, 0, 0, 32, + 68, 135, 123, 119, 119, 103, 69, 98, + 68, 103, 120, 118, 118, 102, 71, 98, + 134, 136, 157, 184, 182, 153, 139, 134, + 208, 168, 248, 75, 189, 143, 121, 107, + 32, 49, 34, 34, 34, 0, 17, 2, + 210, 235, 139, 123, 185, 137, 105, 134, + 98, 135, 104, 182, 100, 183, 171, 134, + 100, 70, 68, 70, 66, 66, 34, 131, + 64, 166, 102, 68, 36, 2, 1, 0, + 134, 166, 102, 68, 34, 34, 66, 132, + 212, 246, 158, 139, 107, 107, 87, 102, + 100, 219, 125, 122, 137, 118, 103, 132, + 114, 135, 137, 105, 171, 106, 50, 34, + 164, 214, 141, 143, 185, 151, 121, 103, + 192, 34, 0, 0, 0, 0, 0, 1, + 208, 109, 74, 187, 134, 249, 159, 137, + 102, 110, 154, 118, 87, 101, 119, 101, + 0, 2, 0, 36, 36, 66, 68, 35, + 96, 164, 102, 100, 36, 0, 2, 33, + 167, 138, 174, 102, 100, 84, 2, 2, + 100, 107, 120, 119, 36, 197, 24, 0 + }; + + internal static readonly byte[] silk_NLSF_CB2_iCDF_WB = { + 255, 254, 253, 244, 12, 3, 2, 1, + 0, 255, 254, 252, 224, 38, 3, 2, + 1, 0, 255, 254, 251, 209, 57, 4, + 2, 1, 0, 255, 254, 244, 195, 69, + 4, 2, 1, 0, 255, 251, 232, 184, + 84, 7, 2, 1, 0, 255, 254, 240, + 186, 86, 14, 2, 1, 0, 255, 254, + 239, 178, 91, 30, 5, 1, 0, 255, + 248, 227, 177, 100, 19, 2, 1, 0 + }; + + internal static readonly byte[] silk_NLSF_CB2_BITS_WB_Q5 = { + 255, 255, 255, 156, 4, 154, 255, 255, + 255, 255, 255, 227, 102, 15, 92, 255, + 255, 255, 255, 255, 213, 83, 24, 72, + 236, 255, 255, 255, 255, 150, 76, 33, + 63, 214, 255, 255, 255, 190, 121, 77, + 43, 55, 185, 255, 255, 255, 245, 137, + 71, 43, 59, 139, 255, 255, 255, 255, + 131, 66, 50, 66, 107, 194, 255, 255, + 166, 116, 76, 55, 53, 125, 255, 255 + }; + + internal static readonly byte[] silk_NLSF_PRED_WB_Q8 = { + 175, 148, 160, 176, 178, 173, 174, 164, + 177, 174, 196, 182, 198, 192, 182, 68, + 62, 66, 60, 72, 117, 85, 90, 118, + 136, 151, 142, 160, 142, 155 + }; + + internal static readonly short[] silk_NLSF_DELTA_MIN_WB_Q15 = { + 100, 3, 40, 3, 3, 3, 5, 14, + 14, 10, 11, 3, 8, 9, 7, 3, + 347 + }; + + internal static readonly NLSFCodebook silk_NLSF_CB_WB = new NLSFCodebook() + { + nVectors = 32, + order = 16, + quantStepSize_Q16 = (short)(((int)((0.15f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.15f, 16)*/), + invQuantStepSize_Q6 = (short)(((int)((1.0f / 0.15f) * ((long)1 << (6)) + 0.5))/*Inlines.SILK_CONST(1.0f / 0.15f, 6)*/), + CB1_NLSF_Q8 = silk_NLSF_CB1_WB_Q8, + CB1_iCDF = silk_NLSF_CB1_iCDF_WB, + pred_Q8 = silk_NLSF_PRED_WB_Q8, + ec_sel = silk_NLSF_CB2_SELECT_WB, + ec_iCDF = silk_NLSF_CB2_iCDF_WB, + ec_Rates_Q5 = silk_NLSF_CB2_BITS_WB_Q5, + deltaMin_Q15 = silk_NLSF_DELTA_MIN_WB_Q15, + }; + + /* Piece-wise linear mapping from bitrate in kbps to coding quality in dB SNR */ + internal static readonly int[] silk_TargetRate_table_NB = { + 0, 8000, 9400, 11500, 13500, 17500, 25000, SilkConstants.MAX_TARGET_RATE_BPS + }; + internal static readonly int[] silk_TargetRate_table_MB = { + 0, 9000, 12000, 14500, 18500, 24500, 35500, SilkConstants.MAX_TARGET_RATE_BPS + }; + internal static readonly int[] silk_TargetRate_table_WB = { + 0, 10500, 14000, 17000, 21500, 28500, 42000, SilkConstants.MAX_TARGET_RATE_BPS + }; + internal static readonly short[] silk_SNR_table_Q1 = { + 18, 29, 38, 40, 46, 52, 62, 84 + }; + + /* Tables for stereo predictor coding */ + internal static readonly short[] silk_stereo_pred_quant_Q13 = { + -13732, -10050, -8266, -7526, -6500, -5000, -2950, -820, + 820, 2950, 5000, 6500, 7526, 8266, 10050, 13732 + }; + + internal static readonly byte[] silk_stereo_pred_joint_iCDF = { + 249, 247, 246, 245, 244, + 234, 210, 202, 201, 200, + 197, 174, 82, 59, 56, + 55, 54, 46, 22, 12, + 11, 10, 9, 7, 0 + }; + internal static readonly byte[] silk_stereo_only_code_mid_iCDF = { 64, 0 }; + + /* Tables for LBRR flags */ + internal static readonly byte[] silk_LBRR_flags_2_iCDF = { 203, 150, 0 }; + internal static readonly byte[] silk_LBRR_flags_3_iCDF = { 215, 195, 166, 125, 110, 82, 0 }; + internal static readonly byte[][] silk_LBRR_flags_iCDF_ptr = { + silk_LBRR_flags_2_iCDF, + silk_LBRR_flags_3_iCDF + }; + + /* Table for LSB coding */ + internal static readonly byte[] silk_lsb_iCDF = { 120, 0 }; + + /* Tables for LTPScale */ + internal static readonly byte[] silk_LTPscale_iCDF = { 128, 64, 0 }; + + /* Tables for signal type and offset coding */ + internal static readonly byte[] silk_type_offset_VAD_iCDF = { + 232, 158, 10, 0 + }; + internal static readonly byte[] silk_type_offset_no_VAD_iCDF = { + 230, 0 + }; + + /* Tables for NLSF interpolation factor */ + internal static readonly byte[] silk_NLSF_interpolation_factor_iCDF = { 243, 221, 192, 181, 0 }; + + /* Quantization offsets */ + internal static readonly short[][] silk_Quantization_Offsets_Q10 = { + new short[] { SilkConstants.OFFSET_UVL_Q10, SilkConstants.OFFSET_UVH_Q10 }, + new short[]{ SilkConstants.OFFSET_VL_Q10, SilkConstants.OFFSET_VH_Q10 } + }; + + /* Table for LTPScale */ + internal static readonly short[] silk_LTPScales_table_Q14 = { 15565, 12288, 8192 }; + + /* Uniform entropy tables */ + internal static readonly byte[] silk_uniform3_iCDF = { 171, 85, 0 }; + internal static readonly byte[] silk_uniform4_iCDF = { 192, 128, 64, 0 }; + internal static readonly byte[] silk_uniform5_iCDF = { 205, 154, 102, 51, 0 }; + internal static readonly byte[] silk_uniform6_iCDF = { 213, 171, 128, 85, 43, 0 }; + internal static readonly byte[] silk_uniform8_iCDF = { 224, 192, 160, 128, 96, 64, 32, 0 }; + + internal static readonly byte[] silk_NLSF_EXT_iCDF = { 100, 40, 16, 7, 3, 1, 0 }; + + /* Elliptic/Cauer filters designed with 0.1 dB passband ripple, + 80 dB minimum stopband attenuation, and + [0.95 : 0.15 : 0.35] normalized cut off frequencies. */ + + /* Interpolation points for filter coefficients used in the bandwidth transition smoother */ + internal static readonly int[][] silk_Transition_LP_B_Q28 = + { + new int[] { 250767114, 501534038, 250767114 }, + new int[] { 209867381, 419732057, 209867381 }, + new int[] { 170987846, 341967853, 170987846 }, + new int[] { 131531482, 263046905, 131531482 }, + new int[] { 89306658, 178584282, 89306658 } + }; + + /* Interpolation points for filter coefficients used in the bandwidth transition smoother */ + internal static readonly int[][] silk_Transition_LP_A_Q28 = + { + new int[] { 506393414, 239854379 }, + new int[] { 411067935, 169683996 }, + new int[] { 306733530, 116694253 }, + new int[] { 185807084, 77959395 }, + new int[] { 35497197, 57401098 } + }; + + internal static readonly byte[] silk_pitch_lag_iCDF = { + 253, 250, 244, 233, 212, 182, 150, 131, + 120, 110, 98, 85, 72, 60, 49, 40, + 32, 25, 19, 15, 13, 11, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 0 + }; + + internal static readonly byte[] silk_pitch_delta_iCDF = { + 210, 208, 206, 203, 199, 193, 183, 168, + 142, 104, 74, 52, 37, 27, 20, 14, + 10, 6, 4, 2, 0 + }; + + internal static readonly byte[] silk_pitch_contour_iCDF = { + 223, 201, 183, 167, 152, 138, 124, 111, + 98, 88, 79, 70, 62, 56, 50, 44, + 39, 35, 31, 27, 24, 21, 18, 16, + 14, 12, 10, 8, 6, 4, 3, 2, + 1, 0 + }; + + internal static readonly byte[] silk_pitch_contour_NB_iCDF = { + 188, 176, 155, 138, 119, 97, 67, 43, + 26, 10, 0 + }; + + internal static readonly byte[] silk_pitch_contour_10_ms_iCDF = { + 165, 119, 80, 61, 47, 35, 27, 20, + 14, 9, 4, 0 + }; + + internal static readonly byte[] silk_pitch_contour_10_ms_NB_iCDF = { + 113, 63, 0 + }; + + internal static readonly byte[] silk_max_pulses_table = { + 8, 10, 12, 16 + }; + + internal static readonly byte[][] silk_pulses_per_block_iCDF = { + new byte[] { + 125, 51, 26, 18, 15, 12, 11, 10, + 9, 8, 7, 6, 5, 4, 3, 2, + 1, 0 + }, + new byte[] { + 198, 105, 45, 22, 15, 12, 11, 10, + 9, 8, 7, 6, 5, 4, 3, 2, + 1, 0 + }, + new byte[] { + 213, 162, 116, 83, 59, 43, 32, 24, + 18, 15, 12, 9, 7, 6, 5, 3, + 2, 0 + }, + new byte[] { + 239, 187, 116, 59, 28, 16, 11, 10, + 9, 8, 7, 6, 5, 4, 3, 2, + 1, 0 + }, + new byte[] { + 250, 229, 188, 135, 86, 51, 30, 19, + 13, 10, 8, 6, 5, 4, 3, 2, + 1, 0 + }, + new byte[] { + 249, 235, 213, 185, 156, 128, 103, 83, + 66, 53, 42, 33, 26, 21, 17, 13, + 10, 0 + }, + new byte[] { + 254, 249, 235, 206, 164, 118, 77, 46, + 27, 16, 10, 7, 5, 4, 3, 2, + 1, 0 + }, + new byte[] { + 255, 253, 249, 239, 220, 191, 156, 119, + 85, 57, 37, 23, 15, 10, 6, 4, + 2, 0 + }, + new byte[] { + 255, 253, 251, 246, 237, 223, 203, 179, + 152, 124, 98, 75, 55, 40, 29, 21, + 15, 0 + }, + new byte[] { + 255, 254, 253, 247, 220, 162, 106, 67, + 42, 28, 18, 12, 9, 6, 4, 3, + 2, 0 + } + }; + + internal static readonly byte[][] silk_pulses_per_block_BITS_Q5 = { + new byte[] { + 31, 57, 107, 160, 205, 205, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255 + }, + new byte[] { + 69, 47, 67, 111, 166, 205, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255 + }, + new byte[] { + 82, 74, 79, 95, 109, 128, 145, 160, + 173, 205, 205, 205, 224, 255, 255, 224, + 255, 224 + }, + new byte[] { + 125, 74, 59, 69, 97, 141, 182, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255 + }, + new byte[] { + 173, 115, 85, 73, 76, 92, 115, 145, + 173, 205, 224, 224, 255, 255, 255, 255, + 255, 255 + }, + new byte[] { + 166, 134, 113, 102, 101, 102, 107, 118, + 125, 138, 145, 155, 166, 182, 192, 192, + 205, 150 + }, + new byte[] { + 224, 182, 134, 101, 83, 79, 85, 97, + 120, 145, 173, 205, 224, 255, 255, 255, + 255, 255 + }, + new byte[] { + 255, 224, 192, 150, 120, 101, 92, 89, + 93, 102, 118, 134, 160, 182, 192, 224, + 224, 224 + }, + new byte[] { + 255, 224, 224, 182, 155, 134, 118, 109, + 104, 102, 106, 111, 118, 131, 145, 160, + 173, 131 + } + }; + + internal static readonly byte[][] silk_rate_levels_iCDF = + { + new byte[] { + 241, 190, 178, 132, 87, 74, 41, 14, + 0 + }, + new byte[] { + 223, 193, 157, 140, 106, 57, 39, 18, + 0 + } + }; + + internal static readonly byte[][] silk_rate_levels_BITS_Q5 = + { + new byte[] { + 131, 74, 141, 79, 80, 138, 95, 104, + 134 + }, + new byte[] { + 95, 99, 91, 125, 93, 76, 123, 115, + 123 + } + }; + + internal static readonly byte[] silk_shell_code_table0 = { + 128, 0, 214, 42, 0, 235, 128, 21, + 0, 244, 184, 72, 11, 0, 248, 214, + 128, 42, 7, 0, 248, 225, 170, 80, + 25, 5, 0, 251, 236, 198, 126, 54, + 18, 3, 0, 250, 238, 211, 159, 82, + 35, 15, 5, 0, 250, 231, 203, 168, + 128, 88, 53, 25, 6, 0, 252, 238, + 216, 185, 148, 108, 71, 40, 18, 4, + 0, 253, 243, 225, 199, 166, 128, 90, + 57, 31, 13, 3, 0, 254, 246, 233, + 212, 183, 147, 109, 73, 44, 23, 10, + 2, 0, 255, 250, 240, 223, 198, 166, + 128, 90, 58, 33, 16, 6, 1, 0, + 255, 251, 244, 231, 210, 181, 146, 110, + 75, 46, 25, 12, 5, 1, 0, 255, + 253, 248, 238, 221, 196, 164, 128, 92, + 60, 35, 18, 8, 3, 1, 0, 255, + 253, 249, 242, 229, 208, 180, 146, 110, + 76, 48, 27, 14, 7, 3, 1, 0 + }; + + internal static readonly byte[] silk_shell_code_table1 = { + 129, 0, 207, 50, 0, 236, 129, 20, + 0, 245, 185, 72, 10, 0, 249, 213, + 129, 42, 6, 0, 250, 226, 169, 87, + 27, 4, 0, 251, 233, 194, 130, 62, + 20, 4, 0, 250, 236, 207, 160, 99, + 47, 17, 3, 0, 255, 240, 217, 182, + 131, 81, 41, 11, 1, 0, 255, 254, + 233, 201, 159, 107, 61, 20, 2, 1, + 0, 255, 249, 233, 206, 170, 128, 86, + 50, 23, 7, 1, 0, 255, 250, 238, + 217, 186, 148, 108, 70, 39, 18, 6, + 1, 0, 255, 252, 243, 226, 200, 166, + 128, 90, 56, 30, 13, 4, 1, 0, + 255, 252, 245, 231, 209, 180, 146, 110, + 76, 47, 25, 11, 4, 1, 0, 255, + 253, 248, 237, 219, 194, 163, 128, 93, + 62, 37, 19, 8, 3, 1, 0, 255, + 254, 250, 241, 226, 205, 177, 145, 111, + 79, 51, 30, 15, 6, 2, 1, 0 + }; + + internal static readonly byte[] silk_shell_code_table2 = { + 129, 0, 203, 54, 0, 234, 129, 23, + 0, 245, 184, 73, 10, 0, 250, 215, + 129, 41, 5, 0, 252, 232, 173, 86, + 24, 3, 0, 253, 240, 200, 129, 56, + 15, 2, 0, 253, 244, 217, 164, 94, + 38, 10, 1, 0, 253, 245, 226, 189, + 132, 71, 27, 7, 1, 0, 253, 246, + 231, 203, 159, 105, 56, 23, 6, 1, + 0, 255, 248, 235, 213, 179, 133, 85, + 47, 19, 5, 1, 0, 255, 254, 243, + 221, 194, 159, 117, 70, 37, 12, 2, + 1, 0, 255, 254, 248, 234, 208, 171, + 128, 85, 48, 22, 8, 2, 1, 0, + 255, 254, 250, 240, 220, 189, 149, 107, + 67, 36, 16, 6, 2, 1, 0, 255, + 254, 251, 243, 227, 201, 166, 128, 90, + 55, 29, 13, 5, 2, 1, 0, 255, + 254, 252, 246, 234, 213, 183, 147, 109, + 73, 43, 22, 10, 4, 2, 1, 0 + }; + + internal static readonly byte[] silk_shell_code_table3 = { + 130, 0, 200, 58, 0, 231, 130, 26, + 0, 244, 184, 76, 12, 0, 249, 214, + 130, 43, 6, 0, 252, 232, 173, 87, + 24, 3, 0, 253, 241, 203, 131, 56, + 14, 2, 0, 254, 246, 221, 167, 94, + 35, 8, 1, 0, 254, 249, 232, 193, + 130, 65, 23, 5, 1, 0, 255, 251, + 239, 211, 162, 99, 45, 15, 4, 1, + 0, 255, 251, 243, 223, 186, 131, 74, + 33, 11, 3, 1, 0, 255, 252, 245, + 230, 202, 158, 105, 57, 24, 8, 2, + 1, 0, 255, 253, 247, 235, 214, 179, + 132, 84, 44, 19, 7, 2, 1, 0, + 255, 254, 250, 240, 223, 196, 159, 112, + 69, 36, 15, 6, 2, 1, 0, 255, + 254, 253, 245, 231, 209, 176, 136, 93, + 55, 27, 11, 3, 2, 1, 0, 255, + 254, 253, 252, 239, 221, 194, 158, 117, + 76, 42, 18, 4, 3, 2, 1, 0 + }; + + internal static readonly byte[] silk_shell_code_table_offsets = { + 0, 0, 2, 5, 9, 14, 20, 27, + 35, 44, 54, 65, 77, 90, 104, 119, + 135 + }; + + internal static readonly byte[] silk_sign_iCDF = { + 254, 49, 67, 77, 82, 93, 99, + 198, 11, 18, 24, 31, 36, 45, + 255, 46, 66, 78, 87, 94, 104, + 208, 14, 21, 32, 42, 51, 66, + 255, 94, 104, 109, 112, 115, 118, + 248, 53, 69, 80, 88, 95, 102 + }; + + // Resampler tables + + /* Tables with delay compensation values to equalize total delay for different modes */ + internal static readonly sbyte[,] delay_matrix_enc = { + /* in \ out 8 12 16 */ + /* 8 */ { 6, 0, 3 }, + /* 12 */ { 0, 7, 3 }, + /* 16 */ { 0, 1, 10 }, + /* 24 */ { 0, 2, 6 }, + /* 48 */ { 18, 10, 12 } + }; + + internal static readonly sbyte[,] delay_matrix_dec = { + /* in \ out 8 12 16 24 48 */ + /* 8 */ { 4, 0, 2, 0, 0 }, + /* 12 */ { 0, 9, 4, 7, 4 }, + /* 16 */ { 0, 3, 12, 7, 7 } + }; + + + /* Tables with IIR and FIR coefficients for fractional downsamplers (123 Words) */ + internal static readonly /*silk_DWORD_ALIGN*/ short[] silk_Resampler_3_4_COEFS/*[2 + 3 * RESAMPLER_DOWN_ORDER_FIR0 / 2]*/ = { + -20694, -13867, + -49, 64, 17, -157, 353, -496, 163, 11047, 22205, + -39, 6, 91, -170, 186, 23, -896, 6336, 19928, + -19, -36, 102, -89, -24, 328, -951, 2568, 15909, + }; + + internal static readonly /*silk_DWORD_ALIGN*/ short[] silk_Resampler_2_3_COEFS/*[2 + 2 * RESAMPLER_DOWN_ORDER_FIR0 / 2]*/ = { + -14457, -14019, + 64, 128, -122, 36, 310, -768, 584, 9267, 17733, + 12, 128, 18, -142, 288, -117, -865, 4123, 14459, + }; + + internal static readonly /*silk_DWORD_ALIGN*/ short[] silk_Resampler_1_2_COEFS/*[2 + RESAMPLER_DOWN_ORDER_FIR1 / 2]*/ = { + 616, -14323, + -10, 39, 58, -46, -84, 120, 184, -315, -541, 1284, 5380, 9024, + }; + + internal static readonly /*silk_DWORD_ALIGN*/ short[] silk_Resampler_1_3_COEFS/*[2 + RESAMPLER_DOWN_ORDER_FIR2 / 2]*/ = { + 16102, -15162, + -13, 0, 20, 26, 5, -31, -43, -4, 65, 90, 7, -157, -248, -44, 593, 1583, 2612, 3271, + }; + + internal static readonly /*silk_DWORD_ALIGN*/ short[] silk_Resampler_1_4_COEFS/*[2 + RESAMPLER_DOWN_ORDER_FIR2 / 2]*/ = { + 22500, -15099, + 3, -14, -20, -15, 2, 25, 37, 25, -16, -71, -107, -79, 50, 292, 623, 982, 1288, 1464, + }; + + internal static readonly /*silk_DWORD_ALIGN*/ short[] silk_Resampler_1_6_COEFS/*[2 + RESAMPLER_DOWN_ORDER_FIR2 / 2]*/ = { + 27540, -15257, + 17, 12, 8, 1, -10, -22, -30, -32, -22, 3, 44, 100, 168, 243, 317, 381, 429, 455, + }; + + internal static readonly /*silk_DWORD_ALIGN*/ short[] silk_Resampler_2_3_COEFS_LQ/*[2 + 2 * 2]*/ = { + -2797, -6507, + 4697, 10739, + 1567, 8276, + }; + + /* Table with interplation fractions of 1/24, 3/24, 5/24, ... , 23/24 : 23/24 (46 Words) */ + internal static readonly /*silk_DWORD_ALIGN*/ short[,] silk_resampler_frac_FIR_12/*[12][RESAMPLER_ORDER_FIR_12 / 2 ]*/ = { + { 189, -600, 617, 30567 }, + { 117, -159, -1070, 29704 }, + { 52, 221, -2392, 28276 }, + { -4, 529, -3350, 26341 }, + { -48, 758, -3956, 23973 }, + { -80, 905, -4235, 21254 }, + { -99, 972, -4222, 18278 }, + { -107, 967, -3957, 15143 }, + { -103, 896, -3487, 11950 }, + { -91, 773, -2865, 8798 }, + { -71, 611, -2143, 5784 }, + { -46, 425, -1375, 2996 }, + }; + + /* Tables for 2x downsampler */ + public const short silk_resampler_down2_0 = 9872; + public const short silk_resampler_down2_1 = 39809 - 65536; + + /* Tables for 2x upsampler, high quality */ + internal static readonly short[] silk_resampler_up2_hq_0 = { 1746, 14986, 39083 - 65536 }; + internal static readonly short[] silk_resampler_up2_hq_1 = { 6854, 25769, 55542 - 65536 }; + + + // from pitch_estimation_tables.c + + internal static readonly sbyte[][] silk_CB_lags_stage2_10_ms = + { + new sbyte[] {0, 1, 0 }, + new sbyte[] {0, 0, 1 } + }; + + internal static readonly sbyte[][] silk_CB_lags_stage3_10_ms = + { + new sbyte[] {0, 0, 1,-1, 1,-1, 2,-2, 2,-2, 3,-3 }, + new sbyte[] {0, 1, 0, 1,-1, 2,-1, 2,-2, 3,-2, 3 } + }; + + internal static readonly sbyte[][] silk_CB_lags_stage2 /*[ PE_MAX_NB_SUBFR ][ PE_NB_CBKS_STAGE2_EXT ]*/ = + { + new sbyte[] {0, 2,-1,-1,-1, 0, 0, 1, 1, 0, 1 }, + new sbyte[] {0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + new sbyte[] {0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0 }, + new sbyte[] {0,-1, 2, 1, 0, 1, 1, 0, 0,-1,-1 } + }; + + internal static readonly sbyte[][] silk_CB_lags_stage3/*[ PE_MAX_NB_SUBFR ][ PE_NB_CBKS_STAGE3_MAX ]*/ = + { + new sbyte[] {0, 0, 1,-1, 0, 1,-1, 0,-1, 1,-2, 2,-2,-2, 2,-3, 2, 3,-3,-4, 3,-4, 4, 4,-5, 5,-6,-5, 6,-7, 6, 5, 8,-9 }, + new sbyte[] {0, 0, 1, 0, 0, 0, 0, 0, 0, 0,-1, 1, 0, 0, 1,-1, 0, 1,-1,-1, 1,-1, 2, 1,-1, 2,-2,-2, 2,-2, 2, 2, 3,-3 }, + new sbyte[] {0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1,-1, 1, 0, 0, 2, 1,-1, 2,-1,-1, 2,-1, 2, 2,-1, 3,-2,-2,-2, 3 }, + new sbyte[] {0, 1, 0, 0, 1, 0, 1,-1, 2,-1, 2,-1, 2, 3,-2, 3,-2,-2, 4, 4,-3, 5,-3,-4, 6,-4, 6, 5,-5, 8,-6,-5,-7, 9 } + }; + + internal static readonly sbyte[][] silk_Lag_range_stage3_10_ms = + { + new sbyte[] {-3, 7 }, + new sbyte[] {-2, 7 } + }; + + internal static readonly sbyte[][][] silk_Lag_range_stage3 = + { + /* Lags to search for low number of stage3 cbks */ + new sbyte[][] { + new sbyte[] {-5,8 }, + new sbyte[] {-1,6 }, + new sbyte[] {-1,6 }, + new sbyte[] {-4,10 } + }, + /* Lags to search for middle number of stage3 cbks */ + new sbyte[][] { + new sbyte[] {-6,10 }, + new sbyte[] {-2,6 }, + new sbyte[] {-1,6 }, + new sbyte[] {-5,10 } + }, + /* Lags to search for max number of stage3 cbks */ + new sbyte[][] { + new sbyte[] {-9,12 }, + new sbyte[] {-3,7 }, + new sbyte[] {-2,7 }, + new sbyte[] {-7,13 } + } + }; + + internal static readonly sbyte[] silk_nb_cbk_searchs_stage3 = + { + SilkConstants.PE_NB_CBKS_STAGE3_MIN, + SilkConstants.PE_NB_CBKS_STAGE3_MID, + SilkConstants.PE_NB_CBKS_STAGE3_MAX + }; + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/TuningParameters.cs b/Libraries/Concentus/CSharp/Concentus/Silk/TuningParameters.cs new file mode 100644 index 000000000..cb0d8746f --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/TuningParameters.cs @@ -0,0 +1,174 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal class TuningParameters + { + /* Decay time for EntropyCoder.BITREServoir */ + internal const int BITRESERVOIR_DECAY_TIME_MS = 500; + + /*******************/ + /* Pitch estimator */ + /*******************/ + + /* Level of noise floor for whitening filter LPC analysis in pitch analysis */ + internal const float FIND_PITCH_WHITE_NOISE_FRACTION = 1e-3f; + + /* Bandwidth expansion for whitening filter in pitch analysis */ + internal const float FIND_PITCH_BANDWIDTH_EXPANSION = 0.99f; + + /*********************/ + /* Linear prediction */ + /*********************/ + + /* LPC analysis regularization */ + internal const float FIND_LPC_COND_FAC = 1e-5f; + + /* LTP analysis defines */ + internal const float FIND_LTP_COND_FAC = 1e-5f; + internal const float LTP_DAMPING = 0.05f; + internal const float LTP_SMOOTHING = 0.1f; + + /* LTP quantization settings */ + internal const float MU_LTP_QUANT_NB = 0.03f; + internal const float MU_LTP_QUANT_MB = 0.025f; + internal const float MU_LTP_QUANT_WB = 0.02f; + + /* Max cumulative LTP gain */ + internal const float MAX_SUM_LOG_GAIN_DB = 250.0f; + + /***********************/ + /* High pass filtering */ + /***********************/ + + /* Smoothing parameters for low end of pitch frequency range estimation */ + internal const float VARIABLE_HP_SMTH_COEF1 = 0.1f; + internal const float VARIABLE_HP_SMTH_COEF2 = 0.015f; + internal const float VARIABLE_HP_MAX_DELTA_FREQ = 0.4f; + + /* Min and max cut-off frequency values (-3 dB points) */ + internal const int VARIABLE_HP_MIN_CUTOFF_HZ = 60; + internal const int VARIABLE_HP_MAX_CUTOFF_HZ = 100; + + /***********/ + /* Various */ + /***********/ + + /* VAD threshold */ + internal const float SPEECH_ACTIVITY_DTX_THRES = 0.05f; + + /* Speech Activity LBRR enable threshold */ + internal const float LBRR_SPEECH_ACTIVITY_THRES = 0.3f; + + /*************************/ + /* Perceptual parameters */ + /*************************/ + + /* reduction in coding SNR during low speech activity */ + internal const float BG_SNR_DECR_dB = 2.0f; + + /* factor for reducing quantization noise during voiced speech */ + internal const float HARM_SNR_INCR_dB = 2.0f; + + /* factor for reducing quantization noise for unvoiced sparse signals */ + internal const float SPARSE_SNR_INCR_dB = 2.0f; + + /* threshold for sparseness measure above which to use lower quantization offset during unvoiced */ + internal const float SPARSENESS_THRESHOLD_QNT_OFFSET = 0.75f; + + /* warping control */ + internal const float WARPING_MULTIPLIER = 0.015f; + + /* fraction added to first autocorrelation value */ + internal const float SHAPE_WHITE_NOISE_FRACTION = 5e-5f; + + /* noise shaping filter chirp factor */ + internal const float BANDWIDTH_EXPANSION = 0.95f; + + /* difference between chirp factors for analysis and synthesis noise shaping filters at low bitrates */ + internal const float LOW_RATE_BANDWIDTH_EXPANSION_DELTA = 0.01f; + + /* extra harmonic boosting (signal shaping) at low bitrates */ + internal const float LOW_RATE_HARMONIC_BOOST = 0.1f; + + /* extra harmonic boosting (signal shaping) for noisy input signals */ + internal const float LOW_INPUT_QUALITY_HARMONIC_BOOST = 0.1f; + + /* harmonic noise shaping */ + internal const float HARMONIC_SHAPING = 0.3f; + + /* extra harmonic noise shaping for high bitrates or noisy input */ + internal const float HIGH_RATE_OR_LOW_QUALITY_HARMONIC_SHAPING = 0.2f; + + /* parameter for shaping noise towards higher frequencies */ + internal const float HP_NOISE_COEF = 0.25f; + + /* parameter for shaping noise even more towards higher frequencies during voiced speech */ + internal const float HARM_HP_NOISE_COEF = 0.35f; + + /* parameter for applying a high-pass tilt to the input signal */ + internal const float INPUT_TILT = 0.05f; + + /* parameter for extra high-pass tilt to the input signal at high rates */ + internal const float HIGH_RATE_INPUT_TILT = 0.1f; + + /* parameter for reducing noise at the very low frequencies */ + internal const float LOW_FREQ_SHAPING = 4.0f; + + /* less reduction of noise at the very low frequencies for signals with low SNR at low frequencies */ + internal const float LOW_QUALITY_LOW_FREQ_SHAPING_DECR = 0.5f; + + /* subframe smoothing coefficient for HarmBoost, HarmShapeGain, Tilt (lower . more smoothing) */ + internal const float SUBFR_SMTH_COEF = 0.4f; + + /* parameters defining the R/D tradeoff in the residual quantizer */ + internal const float LAMBDA_OFFSET = 1.2f; + internal const float LAMBDA_SPEECH_ACT = -0.2f; + internal const float LAMBDA_DELAYED_DECISIONS = -0.05f; + internal const float LAMBDA_INPUT_QUALITY = -0.1f; + internal const float LAMBDA_CODING_QUALITY = -0.2f; + internal const float LAMBDA_QUANT_OFFSET = 0.8f; + + /* Compensation in bitrate calculations for 10 ms modes */ + internal const int REDUCE_BITRATE_10_MS_BPS = 2200; + + /* Maximum time before allowing a bandwidth transition */ + internal const int MAX_BANDWIDTH_SWITCH_DELAY_MS = 5000; + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/VQ_WMat_EC.cs b/Libraries/Concentus/CSharp/Concentus/Silk/VQ_WMat_EC.cs new file mode 100644 index 000000000..94a64c0f3 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/VQ_WMat_EC.cs @@ -0,0 +1,134 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + internal static class VQ_WMat_EC + { + /* Entropy constrained matrix-weighted VQ, hard-coded to 5-element vectors, for a single input data vector */ + internal static void silk_VQ_WMat_EC( + BoxedValueSbyte ind, /* O index of best codebook vector */ + BoxedValueInt rate_dist_Q14, /* O best weighted quant error + mu * rate */ + BoxedValueInt gain_Q7, /* O sum of absolute LTP coefficients */ + short[] in_Q14, /* I input vector to be quantized */ + int in_Q14_ptr, + int[] W_Q18, /* I weighting matrix */ + int W_Q18_ptr, + sbyte[][] cb_Q7, /* I codebook */ + byte[] cb_gain_Q7, /* I codebook effective gain */ + byte[] cl_Q5, /* I code length for each codebook vector */ + int mu_Q9, /* I tradeoff betw. weighted error and rate */ + int max_gain_Q7, /* I maximum sum of absolute LTP coefficients */ + int L /* I number of vectors in codebook */ +) + { + int k, gain_tmp_Q7; + sbyte[] cb_row_Q7; + int cb_row_Q7_ptr = 0; + short[] diff_Q14 = new short[5]; + int sum1_Q14, sum2_Q16; + + /* Loop over codebook */ + rate_dist_Q14.Val = int.MaxValue; + for (k = 0; k < L; k++) + { + /* Go to next cbk vector */ + cb_row_Q7 = cb_Q7[cb_row_Q7_ptr++]; + gain_tmp_Q7 = cb_gain_Q7[k]; + + diff_Q14[0] = (short)(in_Q14[in_Q14_ptr] - Inlines.silk_LSHIFT(cb_row_Q7[0], 7)); + diff_Q14[1] = (short)(in_Q14[in_Q14_ptr + 1] - Inlines.silk_LSHIFT(cb_row_Q7[1], 7)); + diff_Q14[2] = (short)(in_Q14[in_Q14_ptr + 2] - Inlines.silk_LSHIFT(cb_row_Q7[2], 7)); + diff_Q14[3] = (short)(in_Q14[in_Q14_ptr + 3] - Inlines.silk_LSHIFT(cb_row_Q7[3], 7)); + diff_Q14[4] = (short)(in_Q14[in_Q14_ptr + 4] - Inlines.silk_LSHIFT(cb_row_Q7[4], 7)); + + /* Weighted rate */ + sum1_Q14 = Inlines.silk_SMULBB(mu_Q9, cl_Q5[k]); + + /* Penalty for too large gain */ + sum1_Q14 = Inlines.silk_ADD_LSHIFT32(sum1_Q14, Inlines.silk_max(Inlines.silk_SUB32(gain_tmp_Q7, max_gain_Q7), 0), 10); + + Inlines.OpusAssert(sum1_Q14 >= 0); + + /* first row of W_Q18 */ + sum2_Q16 = Inlines.silk_SMULWB(W_Q18[W_Q18_ptr + 1], diff_Q14[1]); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 2], diff_Q14[2]); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 3], diff_Q14[3]); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 4], diff_Q14[4]); + sum2_Q16 = Inlines.silk_LSHIFT(sum2_Q16, 1); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr], diff_Q14[0]); + sum1_Q14 = Inlines.silk_SMLAWB(sum1_Q14, sum2_Q16, diff_Q14[0]); + + /* second row of W_Q18 */ + sum2_Q16 = Inlines.silk_SMULWB(W_Q18[W_Q18_ptr + 7], diff_Q14[2]); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 8], diff_Q14[3]); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 9], diff_Q14[4]); + sum2_Q16 = Inlines.silk_LSHIFT(sum2_Q16, 1); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 6], diff_Q14[1]); + sum1_Q14 = Inlines.silk_SMLAWB(sum1_Q14, sum2_Q16, diff_Q14[1]); + + /* third row of W_Q18 */ + sum2_Q16 = Inlines.silk_SMULWB(W_Q18[W_Q18_ptr + 13], diff_Q14[3]); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 14], diff_Q14[4]); + sum2_Q16 = Inlines.silk_LSHIFT(sum2_Q16, 1); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 12], diff_Q14[2]); + sum1_Q14 = Inlines.silk_SMLAWB(sum1_Q14, sum2_Q16, diff_Q14[2]); + + /* fourth row of W_Q18 */ + sum2_Q16 = Inlines.silk_SMULWB(W_Q18[W_Q18_ptr + 19], diff_Q14[4]); + sum2_Q16 = Inlines.silk_LSHIFT(sum2_Q16, 1); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 18], diff_Q14[3]); + sum1_Q14 = Inlines.silk_SMLAWB(sum1_Q14, sum2_Q16, diff_Q14[3]); + + /* last row of W_Q18 */ + sum2_Q16 = Inlines.silk_SMULWB(W_Q18[W_Q18_ptr + 24], diff_Q14[4]); + sum1_Q14 = Inlines.silk_SMLAWB(sum1_Q14, sum2_Q16, diff_Q14[4]); + + Inlines.OpusAssert(sum1_Q14 >= 0); + + /* find best */ + if (sum1_Q14 < rate_dist_Q14.Val) + { + rate_dist_Q14.Val = sum1_Q14; + ind.Val = (sbyte)k; + gain_Q7.Val = gain_tmp_Q7; + } + } + } + } +} diff --git a/Libraries/Concentus/CSharp/Concentus/Silk/VoiceActivityDetection.cs b/Libraries/Concentus/CSharp/Concentus/Silk/VoiceActivityDetection.cs new file mode 100644 index 000000000..b4175cee1 --- /dev/null +++ b/Libraries/Concentus/CSharp/Concentus/Silk/VoiceActivityDetection.cs @@ -0,0 +1,409 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to C# by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +namespace Concentus.Silk +{ + using Concentus.Common; + using Concentus.Common.CPlusPlus; + using Concentus.Silk.Enums; + using Concentus.Silk.Structs; + using System.Diagnostics; + + /// + /// Voice Activity Detection module for silk codec + /// + internal static class VoiceActivityDetection + { + /// + /// Weighting factors for tilt measure + /// + private static readonly int[] tiltWeights = { 30000, 6000, -12000, -12000 }; + + /// + /// Initialization of the Silk VAD + /// + /// O Pointer to Silk VAD state. Cannot be nullptr + /// 0 if success + internal static int silk_VAD_Init(SilkVADState psSilk_VAD) + { + int b, ret = 0; + + /* reset state memory */ + psSilk_VAD.Reset(); + + /* init noise levels */ + /* Initialize array with approx pink noise levels (psd proportional to inverse of frequency) */ + for (b = 0; b < SilkConstants.VAD_N_BANDS; b++) + { + psSilk_VAD.NoiseLevelBias[b] = Inlines.silk_max_32(Inlines.silk_DIV32_16(SilkConstants.VAD_NOISE_LEVELS_BIAS, (short)(b + 1)), 1); + } + + /* Initialize state */ + for (b = 0; b < SilkConstants.VAD_N_BANDS; b++) + { + psSilk_VAD.NL[b] = Inlines.silk_MUL(100, psSilk_VAD.NoiseLevelBias[b]); + psSilk_VAD.inv_NL[b] = Inlines.silk_DIV32(int.MaxValue, psSilk_VAD.NL[b]); + } + + psSilk_VAD.counter = 15; + + /* init smoothed energy-to-noise ratio*/ + for (b = 0; b < SilkConstants.VAD_N_BANDS; b++) + { + psSilk_VAD.NrgRatioSmth_Q8[b] = 100 * 256; /* 100 * 256 -. 20 dB SNR */ + } + + return (ret); + } + + + + /// + /// Get the speech activity level in Q8 + /// + /// I/O Encoder state + /// I PCM input + /// 0 if success + internal static int silk_VAD_GetSA_Q8( + SilkChannelEncoder psEncC, + short[] pIn, + int pIn_ptr) + { + int SA_Q15, pSNR_dB_Q7, input_tilt; + int decimated_framelength1, decimated_framelength2; + int decimated_framelength; + int dec_subframe_length, dec_subframe_offset, SNR_Q7, i, b, s; + int sumSquared = 0, smooth_coef_Q16; + short HPstateTmp; + short[] X; + int[] Xnrg = new int[SilkConstants.VAD_N_BANDS]; + int[] NrgToNoiseRatio_Q8 = new int[SilkConstants.VAD_N_BANDS]; + int speech_nrg, x_tmp; + int[] X_offset = new int[SilkConstants.VAD_N_BANDS]; + int ret = 0; + SilkVADState psSilk_VAD = psEncC.sVAD; + + /* Safety checks */ + Inlines.OpusAssert(SilkConstants.VAD_N_BANDS == 4); + Inlines.OpusAssert(SilkConstants.MAX_FRAME_LENGTH >= psEncC.frame_length); + Inlines.OpusAssert(psEncC.frame_length <= 512); + Inlines.OpusAssert(psEncC.frame_length == 8 * Inlines.silk_RSHIFT(psEncC.frame_length, 3)); + + /***********************/ + /* Filter and Decimate */ + /***********************/ + decimated_framelength1 = Inlines.silk_RSHIFT(psEncC.frame_length, 1); + decimated_framelength2 = Inlines.silk_RSHIFT(psEncC.frame_length, 2); + decimated_framelength = Inlines.silk_RSHIFT(psEncC.frame_length, 3); + + /* Decimate into 4 bands: + 0 L 3L L 3L 5L + - -- - -- -- + 8 8 2 4 4 + + [0-1 kHz| temp. |1-2 kHz| 2-4 kHz | 4-8 kHz | + + They're arranged to allow the minimal ( frame_length / 4 ) extra + scratch space during the downsampling process */ + X_offset[0] = 0; + X_offset[1] = decimated_framelength + decimated_framelength2; + X_offset[2] = X_offset[1] + decimated_framelength; + X_offset[3] = X_offset[2] + decimated_framelength2; + X = new short[X_offset[3] + decimated_framelength1]; + + /* 0-8 kHz to 0-4 kHz and 4-8 kHz */ + Filters.silk_ana_filt_bank_1(pIn, pIn_ptr, psSilk_VAD.AnaState, + X, X, X_offset[3], psEncC.frame_length); + + /* 0-4 kHz to 0-2 kHz and 2-4 kHz */ + Filters.silk_ana_filt_bank_1(X, 0, psSilk_VAD.AnaState1, + X, X, X_offset[2], decimated_framelength1); + + /* 0-2 kHz to 0-1 kHz and 1-2 kHz */ + Filters.silk_ana_filt_bank_1(X, 0, psSilk_VAD.AnaState2, + X, X, X_offset[1], decimated_framelength2); + + /*********************************************/ + /* HP filter on lowest band (differentiator) */ + /*********************************************/ + X[decimated_framelength - 1] = (short)(Inlines.silk_RSHIFT(X[decimated_framelength - 1], 1)); + HPstateTmp = X[decimated_framelength - 1]; + + for (i = decimated_framelength - 1; i > 0; i--) + { + X[i - 1] = (short)(Inlines.silk_RSHIFT(X[i - 1], 1)); + X[i] -= X[i - 1]; + } + + X[0] -= psSilk_VAD.HPstate; + psSilk_VAD.HPstate = HPstateTmp; + + /*************************************/ + /* Calculate the energy in each band */ + /*************************************/ + for (b = 0; b < SilkConstants.VAD_N_BANDS; b++) + { + /* Find the decimated framelength in the non-uniformly divided bands */ + decimated_framelength = Inlines.silk_RSHIFT(psEncC.frame_length, Inlines.silk_min_int(SilkConstants.VAD_N_BANDS - b, SilkConstants.VAD_N_BANDS - 1)); + + /* Split length into subframe lengths */ + dec_subframe_length = Inlines.silk_RSHIFT(decimated_framelength, SilkConstants.VAD_INTERNAL_SUBFRAMES_LOG2); + dec_subframe_offset = 0; + + /* Compute energy per sub-frame */ + /* initialize with summed energy of last subframe */ + Xnrg[b] = psSilk_VAD.XnrgSubfr[b]; + for (s = 0; s < SilkConstants.VAD_INTERNAL_SUBFRAMES; s++) + { + sumSquared = 0; + + for (i = 0; i < dec_subframe_length; i++) + { + /* The energy will be less than dec_subframe_length * ( silk_int16_MIN / 8 ) ^ 2. */ + /* Therefore we can accumulate with no risk of overflow (unless dec_subframe_length > 128) */ + x_tmp = Inlines.silk_RSHIFT( + X[X_offset[b] + i + dec_subframe_offset], 3); + sumSquared = Inlines.silk_SMLABB(sumSquared, x_tmp, x_tmp); + /* Safety check */ + Inlines.OpusAssert(sumSquared >= 0); + } + + /* Add/saturate summed energy of current subframe */ + if (s < SilkConstants.VAD_INTERNAL_SUBFRAMES - 1) + { + Xnrg[b] = Inlines.silk_ADD_POS_SAT32(Xnrg[b], sumSquared); + } + else + { + /* Look-ahead subframe */ + Xnrg[b] = Inlines.silk_ADD_POS_SAT32(Xnrg[b], Inlines.silk_RSHIFT(sumSquared, 1)); + } + + dec_subframe_offset += dec_subframe_length; + } + + psSilk_VAD.XnrgSubfr[b] = sumSquared; + } + + /********************/ + /* Noise estimation */ + /********************/ + silk_VAD_GetNoiseLevels(Xnrg, psSilk_VAD); + + /***********************************************/ + /* Signal-plus-noise to noise ratio estimation */ + /***********************************************/ + sumSquared = 0; + input_tilt = 0; + for (b = 0; b < SilkConstants.VAD_N_BANDS; b++) + { + speech_nrg = Xnrg[b] - psSilk_VAD.NL[b]; + if (speech_nrg > 0) + { + /* Divide, with sufficient resolution */ + if ((Xnrg[b] & 0xFF800000) == 0) + { + NrgToNoiseRatio_Q8[b] = Inlines.silk_DIV32(Inlines.silk_LSHIFT(Xnrg[b], 8), psSilk_VAD.NL[b] + 1); + } + else { + NrgToNoiseRatio_Q8[b] = Inlines.silk_DIV32(Xnrg[b], Inlines.silk_RSHIFT(psSilk_VAD.NL[b], 8) + 1); + } + + /* Convert to log domain */ + SNR_Q7 = Inlines.silk_lin2log(NrgToNoiseRatio_Q8[b]) - 8 * 128; + + /* Sum-of-squares */ + sumSquared = Inlines.silk_SMLABB(sumSquared, SNR_Q7, SNR_Q7); /* Q14 */ + + /* Tilt measure */ + if (speech_nrg < ((int)1 << 20)) + { + /* Scale down SNR value for small subband speech energies */ + SNR_Q7 = Inlines.silk_SMULWB(Inlines.silk_LSHIFT(Inlines.silk_SQRT_APPROX(speech_nrg), 6), SNR_Q7); + } + input_tilt = Inlines.silk_SMLAWB(input_tilt, tiltWeights[b], SNR_Q7); + } + else + { + NrgToNoiseRatio_Q8[b] = 256; + } + } + + /* Mean-of-squares */ + sumSquared = Inlines.silk_DIV32_16(sumSquared, SilkConstants.VAD_N_BANDS); /* Q14 */ + + /* Root-mean-square approximation, scale to dBs, and write to output pointer */ + pSNR_dB_Q7 = (short)(3 * Inlines.silk_SQRT_APPROX(sumSquared)); /* Q7 */ + + /*********************************/ + /* Speech Probability Estimation */ + /*********************************/ + SA_Q15 = Sigmoid.silk_sigm_Q15(Inlines.silk_SMULWB(SilkConstants.VAD_SNR_FACTOR_Q16, pSNR_dB_Q7) - SilkConstants.VAD_NEGATIVE_OFFSET_Q5); + + /**************************/ + /* Frequency Tilt Measure */ + /**************************/ + psEncC.input_tilt_Q15 = Inlines.silk_LSHIFT(Sigmoid.silk_sigm_Q15(input_tilt) - 16384, 1); + + /**************************************************/ + /* Scale the sigmoid output based on power levels */ + /**************************************************/ + speech_nrg = 0; + for (b = 0; b < SilkConstants.VAD_N_BANDS; b++) + { + /* Accumulate signal-without-noise energies, higher frequency bands have more weight */ + speech_nrg += (b + 1) * Inlines.silk_RSHIFT(Xnrg[b] - psSilk_VAD.NL[b], 4); + } + + /* Power scaling */ + if (speech_nrg <= 0) + { + SA_Q15 = Inlines.silk_RSHIFT(SA_Q15, 1); + } + else if (speech_nrg < 32768) + { + if (psEncC.frame_length == 10 * psEncC.fs_kHz) + { + speech_nrg = Inlines.silk_LSHIFT_SAT32(speech_nrg, 16); + } + else + { + speech_nrg = Inlines.silk_LSHIFT_SAT32(speech_nrg, 15); + } + + /* square-root */ + speech_nrg = Inlines.silk_SQRT_APPROX(speech_nrg); + SA_Q15 = Inlines.silk_SMULWB(32768 + speech_nrg, SA_Q15); + } + + /* Copy the resulting speech activity in Q8 */ + psEncC.speech_activity_Q8 = Inlines.silk_min_int(Inlines.silk_RSHIFT(SA_Q15, 7), byte.MaxValue); + + /***********************************/ + /* Energy Level and SNR estimation */ + /***********************************/ + /* Smoothing coefficient */ + smooth_coef_Q16 = Inlines.silk_SMULWB(SilkConstants.VAD_SNR_SMOOTH_COEF_Q18, Inlines.silk_SMULWB((int)SA_Q15, SA_Q15)); + + if (psEncC.frame_length == 10 * psEncC.fs_kHz) + { + smooth_coef_Q16 >>= 1; + } + + for (b = 0; b < SilkConstants.VAD_N_BANDS; b++) + { + /* compute smoothed energy-to-noise ratio per band */ + psSilk_VAD.NrgRatioSmth_Q8[b] = Inlines.silk_SMLAWB(psSilk_VAD.NrgRatioSmth_Q8[b], + NrgToNoiseRatio_Q8[b] - psSilk_VAD.NrgRatioSmth_Q8[b], smooth_coef_Q16); + /* signal to noise ratio in dB per band */ + SNR_Q7 = 3 * (Inlines.silk_lin2log(psSilk_VAD.NrgRatioSmth_Q8[b]) - 8 * 128); + /* quality = sigmoid( 0.25 * ( SNR_dB - 16 ) ); */ + psEncC.input_quality_bands_Q15[b] = Sigmoid.silk_sigm_Q15(Inlines.silk_RSHIFT(SNR_Q7 - 16 * 128, 4)); + } + + return (ret); + } + + /// + /// Noise level estimation + /// + /// I subband energies [VAD_N_BANDS] + /// I/O Pointer to Silk VAD state + internal static void silk_VAD_GetNoiseLevels( + int[] pX, + SilkVADState psSilk_VAD) + { + int k; + int nl, nrg, inv_nrg; + int coef, min_coef; + + /* Initially faster smoothing */ + if (psSilk_VAD.counter < 1000) + { /* 1000 = 20 sec */ + min_coef = Inlines.silk_DIV32_16(short.MaxValue, (short)(Inlines.silk_RSHIFT(psSilk_VAD.counter, 4) + 1)); + } + else + { + min_coef = 0; + } + + for (k = 0; k < SilkConstants.VAD_N_BANDS; k++) + { + /* Get old noise level estimate for current band */ + nl = psSilk_VAD.NL[k]; + Inlines.OpusAssert(nl >= 0); + + /* Add bias */ + nrg = Inlines.silk_ADD_POS_SAT32(pX[k], psSilk_VAD.NoiseLevelBias[k]); + Inlines.OpusAssert(nrg > 0); + + /* Invert energies */ + inv_nrg = Inlines.silk_DIV32(int.MaxValue, nrg); + Inlines.OpusAssert(inv_nrg >= 0); + + /* Less update when subband energy is high */ + if (nrg > Inlines.silk_LSHIFT(nl, 3)) + { + coef = SilkConstants.VAD_NOISE_LEVEL_SMOOTH_COEF_Q16 >> 3; + } + else if (nrg < nl) + { + coef = SilkConstants.VAD_NOISE_LEVEL_SMOOTH_COEF_Q16; + } + else + { + coef = Inlines.silk_SMULWB(Inlines.silk_SMULWW(inv_nrg, nl), SilkConstants.VAD_NOISE_LEVEL_SMOOTH_COEF_Q16 << 1); + } + + /* Initially faster smoothing */ + coef = Inlines.silk_max_int(coef, min_coef); + + /* Smooth inverse energies */ + psSilk_VAD.inv_NL[k] = Inlines.silk_SMLAWB(psSilk_VAD.inv_NL[k], inv_nrg - psSilk_VAD.inv_NL[k], coef); + Inlines.OpusAssert(psSilk_VAD.inv_NL[k] >= 0); + + /* Compute noise level by inverting again */ + nl = Inlines.silk_DIV32(int.MaxValue, psSilk_VAD.inv_NL[k]); + Inlines.OpusAssert(nl >= 0); + + /* Limit noise levels (guarantee 7 bits of head room) */ + nl = Inlines.silk_min(nl, 0x00FFFFFF); + + /* Store as part of state */ + psSilk_VAD.NL[k] = nl; + } + + /* Increment frame counter */ + psSilk_VAD.counter++; + } + } +} diff --git a/Libraries/Concentus/LICENSE b/Libraries/Concentus/LICENSE new file mode 100644 index 000000000..74985b00d --- /dev/null +++ b/Libraries/Concentus/LICENSE @@ -0,0 +1,30 @@ +Copyright (c) by various holding parties, including (but not limited to): +Skype Limited, Xiph.Org Foundation, CSIRO, Microsoft Corporation, +Jean-Marc Valin, Gregory Maxwell, Mark Borgerding, Timothy B. Terriberry, +Logan Stromberg. All rights are reserved by their respective holders. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Libraries/Concentus/README.md b/Libraries/Concentus/README.md new file mode 100644 index 000000000..d15eae82a --- /dev/null +++ b/Libraries/Concentus/README.md @@ -0,0 +1,25 @@ +# Concentus: Opus for Everyone + +This project is an effort to port the Opus reference library to work natively in other languages, and to gather together any such ports that may exist. With this code, developers should be left with no excuse to use an inferior codec, regardless of their language or runtime environment. + +[NuGet Package](https://www.nuget.org/packages/Concentus) + +[Related OggOpus Library](https://github.com/lostromb/concentus.oggfile) + +## Project Status + +This repo contains completely functional Opus implementations in portable C# and Java. They are based on libopus master 1.1.2 configured with FIXED_POINT and with an extra switch to enable/disable the floating-point analysis functions. Both the encoder and decoder paths have been thoroughly tested to be bit-exact with their equivalent C functions in all common use cases. I have also included a port of the libspeexdsp resampler for general use. + +Performance-wise, the current build runs about 40-50% as fast as its equivalent libopus build, mostly due to the lack of stack arrays and vectorized intrinsics in managed languages. I do not believe performance will get much better than this; if you need blazing-fast performance then I encourage you to try the P/Opus or JNI library. The API surface is finalized and existing code should not change, but I may add helper classes in the future. + +No other ports beyond C# / Java are planned at this time, but pull requests are welcome from any contributors. + +## Performance + +For those interested in the expected real-world performance of the library, I ran some quick C# benchmarks on a Raspberry Pi 1 (700mhz ARM) at various modes: + +0.82x realtime - 48Khz Voice, Stereo, 32Kbps (SILK), Compexity 0 +0.98x realtime - 48Khz Music, Stereo, 128Kbps (CELT), Compexity 10 +1.55x realtime - 16Khz Voice, Mono , 32Kbps (SILK), Compexity 0 +1.70x realtime - 48Khz Music, Stereo, 96Kbps (CELT), Compexity 0 +3.59x realtime - 16Khz Music, Mono , 96Kbps (CELT), Compexity 0 diff --git a/Libraries/Concentus/pom.xml b/Libraries/Concentus/pom.xml new file mode 100644 index 000000000..f53be8ef7 --- /dev/null +++ b/Libraries/Concentus/pom.xml @@ -0,0 +1,11 @@ + + 4.0.0 + org.concentus + Concentus-parent + 1.0-SNAPSHOT + pom + + Java/Concentus + Java/ConcentusTestConsole + +