From 3016f40fc733d478bed082358cfba27c46163b88 Mon Sep 17 00:00:00 2001 From: Hawkynt Date: Sat, 29 Aug 2026 12:19:45 +0200 Subject: [PATCH] # the two ADPCM encoders landed on top of each other and main stopped building #105 and #106 were each green on their own branch and collided on the squash commit no run ever saw. #106 added the span-based encoders in {Ima,Ms}AdpcmCodec.Encode.cs; #105 then rewrote {Ima,Ms}AdpcmCodec.cs to carry its own list-based encoders, re-declaring helpers the partial already had. Both public surfaces stay - AdpcmEncodeTests covers the list-based pair and AdpcmEncoderTests the span-based pair, so neither is dead. IMA: the two EncodeNibble bodies were the same quantizer, so the partial's copy goes and every caller uses the one in the main file; short widens to int. Its copies of QuickTimePacketBytes, QuickTimeSamplesPerPacket and StartIndexFor go for the same reason. MS: the two are genuinely different - the main file quantizes the residual directly, the partial searches all sixteen nibbles for the smallest error - so the partial's keeps its body and takes the name SearchNibble. Sharing the name was what made a short argument silently pick the search over the quantizer. DecodeNibble takes the predictor index by value now that it is no longer mutated per nibble, which is what the rewrite in #105 established. --- Codecs/Codec.ImaAdpcm/ImaAdpcmCodec.Encode.cs | 33 ------------------- Codecs/Codec.MsAdpcm/MsAdpcmCodec.Encode.cs | 17 +++++----- 2 files changed, 8 insertions(+), 42 deletions(-) diff --git a/Codecs/Codec.ImaAdpcm/ImaAdpcmCodec.Encode.cs b/Codecs/Codec.ImaAdpcm/ImaAdpcmCodec.Encode.cs index 07d37490e..9e9cba3b5 100644 --- a/Codecs/Codec.ImaAdpcm/ImaAdpcmCodec.Encode.cs +++ b/Codecs/Codec.ImaAdpcm/ImaAdpcmCodec.Encode.cs @@ -6,8 +6,6 @@ public static partial class ImaAdpcmCodec { private const int WaveHeaderBytesPerChannel = 4; private const int WaveStereoGroupBytesPerChannel = 4; - private const int QuickTimePacketBytes = 34; - private const int QuickTimeSamplesPerPacket = 64; /// /// Encodes interleaved 16-bit PCM into Microsoft/IMA WAV ADPCM blocks. @@ -132,35 +130,4 @@ private static void ValidateWaveLayout(ReadOnlySpan interleaved, int chan private static short GetSample(ReadOnlySpan interleaved, int frames, int channels, int frame, int channel, short fallback) => frame < frames ? interleaved[frame * channels + channel] : fallback; - - private static int StartIndexFor(int delta) { - var index = 0; - while (index < StepTable.Length - 1 && StepTable[index] < delta) - ++index; - return index; - } - - private static byte EncodeNibble(short sample, ref int predictor, ref int index) { - var step = StepTable[index]; - var delta = (int)sample - predictor; - byte nibble = 0; - if (delta < 0) { - nibble = 8; - delta = -delta; - } - - if (delta >= step) { - nibble |= 4; - delta -= step; - } - if (delta >= step >> 1) { - nibble |= 2; - delta -= step >> 1; - } - if (delta >= step >> 2) - nibble |= 1; - - DecodeNibble(nibble, ref predictor, ref index); - return nibble; - } } diff --git a/Codecs/Codec.MsAdpcm/MsAdpcmCodec.Encode.cs b/Codecs/Codec.MsAdpcm/MsAdpcmCodec.Encode.cs index fd7f09c66..33853db35 100644 --- a/Codecs/Codec.MsAdpcm/MsAdpcmCodec.Encode.cs +++ b/Codecs/Codec.MsAdpcm/MsAdpcmCodec.Encode.cs @@ -68,18 +68,18 @@ public static byte[] Encode(ReadOnlySpan interleaved, int channels, int b for (var i = 0; i < dataBytes; ++i) { var frame = baseFrame + 2 + i * 2; var highSample = GetSample(interleaved, frames, channels, frame, 0, (short)sample1[0]); - var high = EncodeNibble(highSample, ref predictorIndex[0], ref delta[0], ref sample1[0], ref sample2[0]); + var high = SearchNibble(highSample, predictorIndex[0], ref delta[0], ref sample1[0], ref sample2[0]); var lowSample = GetSample(interleaved, frames, channels, frame + 1, 0, (short)sample1[0]); - var low = EncodeNibble(lowSample, ref predictorIndex[0], ref delta[0], ref sample1[0], ref sample2[0]); + var low = SearchNibble(lowSample, predictorIndex[0], ref delta[0], ref sample1[0], ref sample2[0]); output[p + i] = (byte)(high << 4 | low); } } else { for (var i = 0; i < dataBytes; ++i) { var frame = baseFrame + 2 + i; var leftSample = GetSample(interleaved, frames, channels, frame, 0, (short)sample1[0]); - var left = EncodeNibble(leftSample, ref predictorIndex[0], ref delta[0], ref sample1[0], ref sample2[0]); + var left = SearchNibble(leftSample, predictorIndex[0], ref delta[0], ref sample1[0], ref sample2[0]); var rightSample = GetSample(interleaved, frames, channels, frame, 1, (short)sample1[1]); - var right = EncodeNibble(rightSample, ref predictorIndex[1], ref delta[1], ref sample1[1], ref sample2[1]); + var right = SearchNibble(rightSample, predictorIndex[1], ref delta[1], ref sample1[1], ref sample2[1]); output[p + i] = (byte)(left << 4 | right); } } @@ -130,7 +130,7 @@ private static void SelectInitialState( for (var sampleIndex = 2; sampleIndex < lookAhead; ++sampleIndex) { var target = GetSample(interleaved, frames, channels, baseFrame + sampleIndex, channel, (short)s1); - EncodeNibble(target, ref statePredictor, ref stateDelta, ref s1, ref s2); + SearchNibble(target, statePredictor, ref stateDelta, ref s1, ref s2); var difference = (long)target - s1; error += difference * difference; if (error >= bestError) @@ -151,16 +151,15 @@ private static void SelectInitialState( private static short GetSample(ReadOnlySpan interleaved, int frames, int channels, int frame, int channel, short fallback) => frame < frames ? interleaved[frame * channels + channel] : fallback; - private static int EncodeNibble(short sample, ref int predictorIndex, ref int delta, ref int sample1, ref int sample2) { + private static int SearchNibble(short sample, int predictorIndex, ref int delta, ref int sample1, ref int sample2) { var bestNibble = 0; var bestError = int.MaxValue; for (var nibble = 0; nibble < 16; ++nibble) { - var testPredictorIndex = predictorIndex; var testDelta = delta; var testSample1 = sample1; var testSample2 = sample2; - var reconstructed = DecodeNibble(nibble, ref testPredictorIndex, ref testDelta, ref testSample1, ref testSample2); + var reconstructed = DecodeNibble((byte)nibble, predictorIndex, ref testDelta, ref testSample1, ref testSample2); var error = Math.Abs((int)sample - reconstructed); if (error >= bestError) continue; @@ -170,7 +169,7 @@ private static int EncodeNibble(short sample, ref int predictorIndex, ref int de break; } - DecodeNibble(bestNibble, ref predictorIndex, ref delta, ref sample1, ref sample2); + DecodeNibble((byte)bestNibble, predictorIndex, ref delta, ref sample1, ref sample2); return bestNibble; } }