public static byte[] Encode(short[] pcm, CriAdxParameters config) { var c = config; int sampleCount = pcm.Length + c.Padding; int samplesPerFrame = (c.FrameSize - 2) * 2; int frameCount = sampleCount.DivideByRoundUp(samplesPerFrame); int paddingRemaining = c.Padding; short[] coefs = c.Type == CriAdxType.Fixed ? Coefs[c.Filter] : CalculateCoefficients(500, c.SampleRate); var pcmBuffer = new short[samplesPerFrame + 2]; var adpcmBuffer = new byte[c.FrameSize]; var adpcmOut = new byte[frameCount * c.FrameSize]; if (c.Version == 4 && c.Padding == 0) { pcmBuffer[0] = pcm[0]; pcmBuffer[1] = pcm[0]; c.History = pcm[0]; } for (int i = 0; i < frameCount; i++) { int samplesToCopy = Math.Min(sampleCount - i * samplesPerFrame, samplesPerFrame); int pcmBufferStart = 2; if (paddingRemaining != 0) { while (paddingRemaining > 0 && samplesToCopy > 0) { paddingRemaining--; samplesToCopy--; pcmBufferStart++; } if (samplesToCopy == 0) { continue; } } Array.Copy(pcm, Math.Max(i * samplesPerFrame - c.Padding, 0), pcmBuffer, pcmBufferStart, samplesToCopy); Array.Clear(pcmBuffer, pcmBufferStart + samplesToCopy, samplesPerFrame - samplesToCopy - pcmBufferStart + 2); EncodeFrame(pcmBuffer, adpcmBuffer, coefs, samplesPerFrame, c.Type, c.Version); if (c.Type == CriAdxType.Fixed) { adpcmBuffer[0] |= (byte)(c.Filter << 5); } Array.Copy(adpcmBuffer, 0, adpcmOut, i * c.FrameSize, c.FrameSize); pcmBuffer[0] = pcmBuffer[samplesPerFrame]; pcmBuffer[1] = pcmBuffer[samplesPerFrame + 1]; config.Progress?.ReportAdd(1); } return(adpcmOut); }
public static short[] Decode(byte[] adpcm, int sampleCount, CriAdxParameters config = null) { CriAdxParameters c = config ?? new CriAdxParameters(); int samplesPerFrame = (c.FrameSize - 2) * 2; short[][] coefs = c.Type == CriAdxType.Fixed ? Coefs : new[] { CalculateCoefficients(c.HighpassFrequency, c.SampleRate) }; var pcm = new short[sampleCount]; int hist1 = c.History; int hist2 = c.History; int frameCount = sampleCount.DivideByRoundUp(samplesPerFrame); int currentSample = 0; int startSample = c.Padding > 0 ? c.Padding % samplesPerFrame : 0; int inIndex = c.Padding / samplesPerFrame * c.FrameSize; for (int i = 0; i < frameCount; i++) { int filterNum = GetHighNibble(adpcm[inIndex]) >> 1; short scale = (short)((adpcm[inIndex] << 8 | adpcm[inIndex + 1]) & 0x1FFF); scale = (short)(c.Type == CriAdxType.Exponential ? 1 << (12 - scale) : scale + 1); inIndex += 2 + startSample / 2; int samplesToRead = Math.Min(samplesPerFrame, sampleCount - currentSample); for (int s = startSample; s < samplesToRead; s++) { int sample = s % 2 == 0 ? GetHighNibbleSigned(adpcm[inIndex]) : GetLowNibbleSigned(adpcm[inIndex++]); if (c.Version == 4) { sample = scale * sample + ((hist1 * coefs[filterNum][0] + hist2 * coefs[filterNum][1]) >> 12); } else { sample = scale * sample + (hist1 * coefs[filterNum][0] >> 12) + (hist2 * coefs[filterNum][1] >> 12); } short finalSample = Clamp16(sample); hist2 = hist1; hist1 = finalSample; pcm[currentSample++] = finalSample; } startSample = 0; } return(pcm); }