using System; using System.Collections.Generic; using System.Text; namespace JT1078.Protocol.Audio { public class AdpcmCodec: IAudioCodec { static readonly int[] indexTable = { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8 }; static readonly int[] stepsizeTable = { 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 }; //public static byte[] ToAdpcm(short[] indata, AdpcmState state) //{ // int val; /* Current input sample value */ // int sign; /* Current adpcm sign bit */ // int delta; /* Current adpcm output value */ // int diff; /* Difference between val and valprev */ // int step; /* Stepsize */ // int valpred; /* Predicted output value */ // int vpdiff; /* Current change to valpred */ // int index; /* Current step change index */ // int outputbuffer = 0; /* place to keep previous 4-bit value */ // int bufferstep; /* toggle between outputbuffer/output */ // List outp = new List(); // short[] inp = indata; // var len = indata.Length; // valpred = state.Valprev; // index = state.Index; // step = stepsizeTable[index]; // bufferstep = 1; // int k = 0; // for (int i = 0; len > 0; len--, i++) // { // val = inp[i]; // /* Step 1 - compute difference with previous value */ // diff = val - valpred; // sign = (diff < 0) ? 8 : 0; // if (sign != 0) diff = (-diff); // /* Step 2 - Divide and clamp */ // /* Note: // ** This code *approximately* computes: // ** delta = diff*4/step; // ** vpdiff = (delta+0.5)*step/4; // ** but in shift step bits are dropped. The net result of this is // ** that even if you have fast mul/div hardware you cannot put it to // ** good use since the fixup would be too expensive. // */ // delta = 0; // vpdiff = (step >> 3); // if (diff >= step) // { // delta = 4; // diff -= step; // vpdiff += step; // } // step >>= 1; // if (diff >= step) // { // delta |= 2; // diff -= step; // vpdiff += step; // } // step >>= 1; // if (diff >= step) // { // delta |= 1; // vpdiff += step; // } // /* Step 3 - Update previous value */ // if (sign != 0) // valpred -= vpdiff; // else // valpred += vpdiff; // /* Step 4 - Clamp previous value to 16 bits */ // if (valpred > 32767) // valpred = 32767; // else if (valpred < -32768) // valpred = -32768; // /* Step 5 - Assemble value, update index and step values */ // delta |= sign; // index += indexTable[delta]; // if (index < 0) index = 0; // if (index > 88) index = 88; // step = stepsizeTable[index]; // /* Step 6 - Output value */ // if (bufferstep != 0) // { // outputbuffer = (delta << 4) & 0xf0; // } // else // { // outp.Add((byte)((delta & 0x0f) | outputbuffer)); // } // bufferstep = bufferstep == 0 ? 1 : 0; // } // /* Output last step, if needed */ // if (bufferstep == 0) // outp.Add((byte)outputbuffer); // state.Valprev = (short)valpred; // state.Index = (byte)index; // return outp.ToArray(); //} /// /// 将adpcm转为pcm /// /// /// /// /// public byte[] ToPcm(byte[] audio, IAudioAttachData audioAttachData) { AdpcmState state = (AdpcmState)audioAttachData; // signed char *inp; /* Input buffer pointer */ // short *outp; /* output buffer pointer */ int sign; /* Current adpcm sign bit */ int delta; /* Current adpcm output value */ int step; /* Stepsize */ int valpred = state.Valprev; /* Predicted value */ int vpdiff; /* Current change to valpred */ byte index = state.Index; /* Current step change index */ int inputbuffer = 0; /* place to keep next 4-bit value */ bool bufferstep = false; /* toggle between inputbuffer/input */ step = stepsizeTable[index]; var outdata = new List(); var len = audio.Length * 2; for (int i = 0; len > 0; len--) { /* Step 1 - get the delta value */ if (bufferstep) { delta = inputbuffer & 0xf; } else { inputbuffer = audio[i++]; delta = (inputbuffer >> 4) & 0xf; } bufferstep = !bufferstep; /* Step 2 - Find new index value (for later) */ index += (byte)indexTable[delta]; if (index < 0) index = 0; if (index > 88) index = 88; /* Step 3 - Separate sign and magnitude */ sign = delta & 8; delta &= 7; /* Step 4 - Compute difference and new predicted value */ /* ** Computes 'vpdiff = (delta+0.5)*step/4', but see comment ** in adpcm_coder. */ vpdiff = step >> 3; if ((delta & 4) > 0) vpdiff += step; if ((delta & 2) > 0) vpdiff += step >> 1; if ((delta & 1) > 0) vpdiff += step >> 2; if (sign != 0) valpred -= vpdiff; else valpred += vpdiff; /* Step 5 - clamp output value */ if (valpred > 32767) valpred = 32767; else if (valpred < -32768) valpred = -32768; /* Step 6 - Update step value */ step = stepsizeTable[index]; /* Step 7 - Output value */ outdata.AddRange(BitConverter.GetBytes((short)valpred)); } state.Valprev = (short)valpred; state.Index = index; return outdata.ToArray(); } } public class AdpcmState : IAudioAttachData { /// /// 上一个采样数据,当index为0是该值应该为未压缩的原数据 /// public short Valprev { get; set; } /// /// 保留数据(未使用) /// public byte Reserved { get; set; } /// /// 上一个block最后一个index,第一个block的index=0 /// public byte Index { get; set; } } public static class AdpcmDecoderExtension { /// /// 添加wav头 /// 仅用于测试pcm是否转成成功,因此没考虑性能,因为播放器可播——# /// /// pcm数据 /// 采样率 /// 位深 /// public static byte[] ToWav(this byte[] input, uint frequency, byte bitDepth = 16) { byte[] output = new byte[input.Length + 44]; Array.Copy(Encoding.ASCII.GetBytes("RIFF"), 0, output, 0, 4); WriteUint(4, (uint)output.Length - 8, output); Array.Copy(Encoding.ASCII.GetBytes("WAVE"), 0, output, 8, 4); Array.Copy(Encoding.ASCII.GetBytes("fmt "), 0, output, 12, 4); WriteUint(16, 16, output); //Header size output[20] = 1; //PCM output[22] = 1; //1 channel WriteUint(24, frequency, output); //Sample Rate WriteUint(28, (uint)(frequency * (bitDepth / 8)), output); //Bytes per second output[32] = (byte)(bitDepth >> 3); //Bytes per sample output[34] = bitDepth; //Bits per sample Array.Copy(Encoding.ASCII.GetBytes("data"), 0, output, 36, 4); WriteUint(40, (uint)output.Length, output); //Date size Array.Copy(input, 0, output, 44, input.Length); return output; } private static void WriteUint(uint offset, uint value, byte[] destination) { for (int i = 0; i < 4; i++) { destination[offset + i] = (byte)(value & 0xFF); value >>= 8; } } } }