You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

268 line
10 KiB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. namespace JT1078.Protocol.Audio
  5. {
  6. public class AdpcmCodec: IAudioCodec
  7. {
  8. static readonly int[] indexTable = {
  9. -1, -1, -1, -1, 2, 4, 6, 8,
  10. -1, -1, -1, -1, 2, 4, 6, 8
  11. };
  12. static readonly int[] stepsizeTable = {
  13. 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
  14. 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
  15. 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
  16. 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
  17. 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
  18. 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
  19. 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
  20. 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
  21. 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
  22. };
  23. //public static byte[] ToAdpcm(short[] indata, AdpcmState state)
  24. //{
  25. // int val; /* Current input sample value */
  26. // int sign; /* Current adpcm sign bit */
  27. // int delta; /* Current adpcm output value */
  28. // int diff; /* Difference between val and valprev */
  29. // int step; /* Stepsize */
  30. // int valpred; /* Predicted output value */
  31. // int vpdiff; /* Current change to valpred */
  32. // int index; /* Current step change index */
  33. // int outputbuffer = 0; /* place to keep previous 4-bit value */
  34. // int bufferstep; /* toggle between outputbuffer/output */
  35. // List<byte> outp = new List<byte>();
  36. // short[] inp = indata;
  37. // var len = indata.Length;
  38. // valpred = state.Valprev;
  39. // index = state.Index;
  40. // step = stepsizeTable[index];
  41. // bufferstep = 1;
  42. // int k = 0;
  43. // for (int i = 0; len > 0; len--, i++)
  44. // {
  45. // val = inp[i];
  46. // /* Step 1 - compute difference with previous value */
  47. // diff = val - valpred;
  48. // sign = (diff < 0) ? 8 : 0;
  49. // if (sign != 0) diff = (-diff);
  50. // /* Step 2 - Divide and clamp */
  51. // /* Note:
  52. // ** This code *approximately* computes:
  53. // ** delta = diff*4/step;
  54. // ** vpdiff = (delta+0.5)*step/4;
  55. // ** but in shift step bits are dropped. The net result of this is
  56. // ** that even if you have fast mul/div hardware you cannot put it to
  57. // ** good use since the fixup would be too expensive.
  58. // */
  59. // delta = 0;
  60. // vpdiff = (step >> 3);
  61. // if (diff >= step)
  62. // {
  63. // delta = 4;
  64. // diff -= step;
  65. // vpdiff += step;
  66. // }
  67. // step >>= 1;
  68. // if (diff >= step)
  69. // {
  70. // delta |= 2;
  71. // diff -= step;
  72. // vpdiff += step;
  73. // }
  74. // step >>= 1;
  75. // if (diff >= step)
  76. // {
  77. // delta |= 1;
  78. // vpdiff += step;
  79. // }
  80. // /* Step 3 - Update previous value */
  81. // if (sign != 0)
  82. // valpred -= vpdiff;
  83. // else
  84. // valpred += vpdiff;
  85. // /* Step 4 - Clamp previous value to 16 bits */
  86. // if (valpred > 32767)
  87. // valpred = 32767;
  88. // else if (valpred < -32768)
  89. // valpred = -32768;
  90. // /* Step 5 - Assemble value, update index and step values */
  91. // delta |= sign;
  92. // index += indexTable[delta];
  93. // if (index < 0) index = 0;
  94. // if (index > 88) index = 88;
  95. // step = stepsizeTable[index];
  96. // /* Step 6 - Output value */
  97. // if (bufferstep != 0)
  98. // {
  99. // outputbuffer = (delta << 4) & 0xf0;
  100. // }
  101. // else
  102. // {
  103. // outp.Add((byte)((delta & 0x0f) | outputbuffer));
  104. // }
  105. // bufferstep = bufferstep == 0 ? 1 : 0;
  106. // }
  107. // /* Output last step, if needed */
  108. // if (bufferstep == 0)
  109. // outp.Add((byte)outputbuffer);
  110. // state.Valprev = (short)valpred;
  111. // state.Index = (byte)index;
  112. // return outp.ToArray();
  113. //}
  114. /// <summary>
  115. /// 将adpcm转为pcm
  116. /// </summary>
  117. /// <see cref="https://github.com/ctuning/ctuning-programs/blob/master/program/cbench-telecom-adpcm-d/adpcm.c"/>
  118. /// <param name="audio"></param>
  119. /// <param name="audioAttachData"></param>
  120. /// <returns></returns>
  121. public byte[] ToPcm(byte[] audio, IAudioAttachData audioAttachData)
  122. {
  123. AdpcmState state = (AdpcmState)audioAttachData;
  124. // signed char *inp; /* Input buffer pointer */
  125. // short *outp; /* output buffer pointer */
  126. int sign; /* Current adpcm sign bit */
  127. int delta; /* Current adpcm output value */
  128. int step; /* Stepsize */
  129. int valpred = state.Valprev; /* Predicted value */
  130. int vpdiff; /* Current change to valpred */
  131. byte index = state.Index; /* Current step change index */
  132. int inputbuffer = 0; /* place to keep next 4-bit value */
  133. bool bufferstep = false; /* toggle between inputbuffer/input */
  134. step = stepsizeTable[index];
  135. var outdata = new List<byte>();
  136. var len = audio.Length * 2;
  137. for (int i = 0; len > 0; len--)
  138. {
  139. /* Step 1 - get the delta value */
  140. if (bufferstep)
  141. {
  142. delta = inputbuffer & 0xf;
  143. }
  144. else
  145. {
  146. inputbuffer = audio[i++];
  147. delta = (inputbuffer >> 4) & 0xf;
  148. }
  149. bufferstep = !bufferstep;
  150. /* Step 2 - Find new index value (for later) */
  151. index += (byte)indexTable[delta];
  152. if (index < 0) index = 0;
  153. if (index > 88) index = 88;
  154. /* Step 3 - Separate sign and magnitude */
  155. sign = delta & 8;
  156. delta &= 7;
  157. /* Step 4 - Compute difference and new predicted value */
  158. /*
  159. ** Computes 'vpdiff = (delta+0.5)*step/4', but see comment
  160. ** in adpcm_coder.
  161. */
  162. vpdiff = step >> 3;
  163. if ((delta & 4) > 0) vpdiff += step;
  164. if ((delta & 2) > 0) vpdiff += step >> 1;
  165. if ((delta & 1) > 0) vpdiff += step >> 2;
  166. if (sign != 0)
  167. valpred -= vpdiff;
  168. else
  169. valpred += vpdiff;
  170. /* Step 5 - clamp output value */
  171. if (valpred > 32767)
  172. valpred = 32767;
  173. else if (valpred < -32768)
  174. valpred = -32768;
  175. /* Step 6 - Update step value */
  176. step = stepsizeTable[index];
  177. /* Step 7 - Output value */
  178. outdata.AddRange(BitConverter.GetBytes((short)valpred));
  179. }
  180. state.Valprev = (short)valpred;
  181. state.Index = index;
  182. return outdata.ToArray();
  183. }
  184. }
  185. public class AdpcmState : IAudioAttachData
  186. {
  187. /// <summary>
  188. /// 上一个采样数据,当index为0是该值应该为未压缩的原数据
  189. /// </summary>
  190. public short Valprev { get; set; }
  191. /// <summary>
  192. /// 保留数据(未使用)
  193. /// </summary>
  194. public byte Reserved { get; set; }
  195. /// <summary>
  196. /// 上一个block最后一个index,第一个block的index=0
  197. /// </summary>
  198. public byte Index { get; set; }
  199. }
  200. public static class AdpcmDecoderExtension
  201. {
  202. /// <summary>
  203. /// 添加wav头
  204. /// 仅用于测试pcm是否转成成功,因此没考虑性能,因为播放器可播——#
  205. /// </summary>
  206. /// <param name="input">pcm数据</param>
  207. /// <param name="frequency">采样率</param>
  208. /// <param name="bitDepth">位深</param>
  209. /// <returns></returns>
  210. public static byte[] ToWav(this byte[] input, uint frequency, byte bitDepth = 16)
  211. {
  212. byte[] output = new byte[input.Length + 44];
  213. Array.Copy(Encoding.ASCII.GetBytes("RIFF"), 0, output, 0, 4);
  214. WriteUint(4, (uint)output.Length - 8, output);
  215. Array.Copy(Encoding.ASCII.GetBytes("WAVE"), 0, output, 8, 4);
  216. Array.Copy(Encoding.ASCII.GetBytes("fmt "), 0, output, 12, 4);
  217. WriteUint(16, 16, output); //Header size
  218. output[20] = 1; //PCM
  219. output[22] = 1; //1 channel
  220. WriteUint(24, frequency, output); //Sample Rate
  221. WriteUint(28, (uint)(frequency * (bitDepth / 8)), output); //Bytes per second
  222. output[32] = (byte)(bitDepth >> 3); //Bytes per sample
  223. output[34] = bitDepth; //Bits per sample
  224. Array.Copy(Encoding.ASCII.GetBytes("data"), 0, output, 36, 4);
  225. WriteUint(40, (uint)output.Length, output); //Date size
  226. Array.Copy(input, 0, output, 44, input.Length);
  227. return output;
  228. }
  229. private static void WriteUint(uint offset, uint value, byte[] destination)
  230. {
  231. for (int i = 0; i < 4; i++)
  232. {
  233. destination[offset + i] = (byte)(value & 0xFF);
  234. value >>= 8;
  235. }
  236. }
  237. }
  238. }