using System; using System.IO; using System.Collections; using System.Runtime.InteropServices; using System.Diagnostics; namespace SpiffLib { /// /// Summary description for AudioFormats. /// public class AudioFormats { // Fibonacci static int[] s_anFibonacci = { -34, -21, -13, -8, -5, -3, -2, -1, 0, 1, 2, 3, 5, 8, 13, 21 }; public static byte[] EncodeFibonacciDelta(byte[] abPcm) { return Encode8BitDelta(abPcm, s_anFibonacci); } public static byte[] DecodeFibonacciDelta(byte[] abEncoded) { return Decode8BitDelta(abEncoded, s_anFibonacci); } // Exponential static int[] s_anExponential = { -128, -64, -32, -16, -8, -4, -2, -1, 0, 1, 2, 4, 8, 16, 32, 64 }; public static byte[] EncodeExponentialDelta(byte[] abPcm) { return Encode8BitDelta(abPcm, s_anExponential); } public static byte[] DecodeExponentialDelta(byte[] abEncoded) { return Decode8BitDelta(abEncoded, s_anExponential); } static byte[] Encode8BitDelta(byte[] abPcm, int[] anDelta) { byte[] abEncoded = new byte[1 + (abPcm.Length + 1) / 2]; int nLast = (int)abPcm[0]; abEncoded[0] = abPcm[0]; for (int i = 1; i < abPcm.Length; i++) { // Important - the shortest path to the next sample may be by overflowing or underflowing. // If this isn't done, the results will be poor. sbyte sbDelta = (sbyte)(abPcm[i] - nLast); int iEntry = -1; int nDiffSmallest = Int32.MaxValue; for (int j = 0; j < anDelta.Length; j++) { int nDiff = (anDelta[j] - sbDelta); if (nDiff < 0) nDiff = -nDiff; if (nDiff < nDiffSmallest) { nDiffSmallest = nDiff; iEntry = j; } } Debug.Assert(iEntry >= 0 && iEntry <= 15); if ((i & 1) != 0) { abEncoded[(i - 1) / 2 + 1] = (byte)((iEntry << 4) + 8); } else { abEncoded[(i - 1) / 2 + 1] = (byte)((byte)(abEncoded[(i - 1) / 2 + 1] & 0xf0) | (byte)iEntry); } nLast += anDelta[iEntry]; } return abEncoded; } static byte[] Decode8BitDelta(byte[] abEncoded, int[] anDelta) { byte[] abPcm = new byte[(abEncoded.Length - 1) * 2 + 1]; byte bSample = abEncoded[0]; abPcm[0] = bSample; for (int i = 1; i < abEncoded.Length; i++) { bSample += (byte)anDelta[abEncoded[i] >> 4]; abPcm[(i - 1) * 2 + 1] = bSample; bSample += (byte)anDelta[abEncoded[i] & 0x0f]; abPcm[(i - 1) * 2 + 2] = bSample; } return abPcm; } // Wrappers public unsafe static byte[] EncodeImaAdpcm(short[] ashPcm, int cBits, bool fPreClamp) { byte[] abEncoded = new byte[ashPcm.Length / 2]; short *pshPcm = (short *)Marshal.UnsafeAddrOfPinnedArrayElement(ashPcm, 0); byte *pbOut = (byte *)Marshal.UnsafeAddrOfPinnedArrayElement(abEncoded, 0); adpcm_state state; state.valprev = 0; state.index = 0; adpcm_coder(pshPcm, pbOut, ashPcm.Length, &state, cBits, fPreClamp); return abEncoded; } public unsafe static short[] DecodeImaAdpcm(byte[] abEncoded, bool fPreClamp) { short[] ashOut = new short[abEncoded.Length * 2]; byte *pbEncoded = (byte *)Marshal.UnsafeAddrOfPinnedArrayElement(abEncoded, 0); short *pshOut = (short *)Marshal.UnsafeAddrOfPinnedArrayElement(ashOut, 0); adpcm_state state; state.valprev = 0; state.index = 0; adpcm_decoder(pbEncoded, pshOut, abEncoded.Length * 2, &state, fPreClamp); return ashOut; } // IMD ADPCM // This implementation Copyright 1992 by Stichting Mathematisch Centrum, Amsterdam, The // Netherlands. /* Intel ADPCM step variation table */ static int[] indexTable = new int[16] { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8, }; static int[] stepsizeTable = new int[89] { 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 }; struct adpcm_state { public int valprev; public int index; }; unsafe static void adpcm_coder(short *indata, byte *outdata, int len, adpcm_state *state, int cBits, bool fPreClamp) { short *inp; /* Input buffer pointer */ byte *outp; /* output buffer pointer */ 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; /* place to keep previous 4-bit value */ int bufferstep; /* toggle between outputbuffer/output */ outp = outdata; inp = indata; valpred = state->valprev; index = state->index; step = stepsizeTable[index]; outputbuffer = 0; bufferstep = 1; for ( ; len > 0 ; len-- ) { val = *inp++; /* Step 1 - compute difference with previous value */ diff = val - valpred; sign = (diff < 0) ? 8 : 0; if (sign != 0) diff = (-diff); // If we preclamp, then at decode time we don't need to clamp // at all. It results in some quantization error however, but // it is so slight it fails the blind test. int vpdiffMax = int.MaxValue; if (fPreClamp) { if (sign != 0) { vpdiffMax = valpred - -32768; } else { vpdiffMax = 32767 - valpred; } } /* 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; if (fPreClamp) { vpdiff = 0; } else { vpdiff = (step >> 3); } if ( diff >= step ) { delta = 4; diff -= step; vpdiff += step; if (vpdiff > vpdiffMax) { vpdiff -= step; delta &= ~4; } } if (cBits > 2) { step >>= 1; if ( diff >= step ) { delta |= 2; diff -= step; vpdiff += step; if (vpdiff > vpdiffMax) { vpdiff -= step; delta &= ~2; } } } if (cBits > 3) { step >>= 1; if ( diff >= step ) { delta |= 1; vpdiff += step; if (vpdiff > vpdiffMax) { vpdiff -= step; delta &= ~1; } } } /* 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; Debug.Assert(!fPreClamp); } else if ( valpred < -32768 ) { valpred = -32768; Debug.Assert(!fPreClamp); } /* 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++ = (byte)((delta & 0x0f) | outputbuffer); } bufferstep ^= 1; } /* Output last step, if needed */ if (bufferstep == 0) *outp++ = (byte)outputbuffer; state->valprev = valpred; state->index = index; } unsafe static void adpcm_decoder(byte *indata, short *outdata, int len, adpcm_state *state, bool fPreClamp) { byte *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; /* Predicted value */ int vpdiff; /* Current change to valpred */ int index; /* Current step change index */ int inputbuffer; /* place to keep next 4-bit value */ int bufferstep; /* toggle between inputbuffer/input */ outp = outdata; inp = indata; valpred = state->valprev; index = state->index; step = stepsizeTable[index]; inputbuffer = 0; bufferstep = 0; for ( ; len > 0 ; len-- ) { /* Step 1 - get the delta value */ if (bufferstep != 0) { delta = inputbuffer & 0xf; } else { inputbuffer = *inp++; delta = (inputbuffer >> 4) & 0xf; } bufferstep ^= 1; /* Step 2 - Find new index value (for later) */ index += indexTable[delta]; if ( index < 0 ) index = 0; if ( index > 88 ) index = 88; /* Step 3 - Separate sign and magnitude */ sign = delta & 8; delta = delta & 7; /* Step 4 - Compute difference and new predicted value */ /* ** Computes 'vpdiff = (delta+0.5)*step/4', but see comment ** in adpcm_coder. */ if (fPreClamp) { vpdiff = 0; } else { 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 (!fPreClamp) { if ( valpred > 32767 ) valpred = 32767; else if ( valpred < -32768 ) valpred = -32768; } /* Step 6 - Update step value */ step = stepsizeTable[index]; /* Step 7 - Output value */ *outp++ = (short)valpred; } state->valprev = valpred; state->index = index; } static int[] s_anStepFractions = new int[] { 1,1,1,1,1,2,2,2,2,2,2,3,3,3,4,4,4,5,5,6,6,7,8,8,9,10,11,12,13,15,16,18,20,22,24,26,29,32,35,38,42,46,51,56,62,68,75,82,91,100,110,120,133,146,160,176,194,213,235,258,284,312,344,378,416,458,503,554,609,670,737,811,892,981,1079,1187,1305,1436,1579,1737,1911,2102,2313,2544,2798,3078,3386,3724,4096, 2,2,2,3,3,3,3,4,4,4,5,5,6,6,7,8,9,9,10,11,13,14,15,17,18,20,22,24,27,30,33,36,39,43,48,52,58,63,70,77,84,93,102,112,124,136,150,165,181,199,219,241,265,292,321,353,388,427,470,517,568,625,687,756,832,915,1007,1107,1218,1340,1474,1621,1783,1961,2158,2373,2611,2872,3159,3475,3822,4205,4625,5088,5596,6156,6772,7449,8192, 4,4,5,5,6,6,7,7,8,9,10,11,12,13,14,16,17,19,21,23,25,28,30,33,37,40,44,49,54,59,65,72,79,87,95,105,115,127,140,154,169,186,204,225,247,272,299,329,362,398,438,482,530,583,641,706,776,854,939,1033,1136,1250,1375,1512,1664,1830,2013,2214,2436,2679,2947,3242,3566,3923,4315,4747,5221,5744,6318,6950,7645,8409,9250,10175,11193,12312,13543,14897,16384, 5,6,7,8,8,9,10,11,12,13,14,16,17,19,21,23,26,28,31,34,38,41,45,50,55,60,66,73,80,89,98,107,118,130,143,157,173,190,209,230,253,278,306,337,371,408,449,494,543,597,657,722,795,875,962,1058,1164,1280,1409,1550,1704,1874,2062,2268,2495,2745,3020,3321,3653,4019,4421,4863,5349,5884,6473,7120,7832,8615,9476,10424,11467,12614,13875,15263,16789,18467,20315,22346,24575, 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, 9,10,11,13,14,15,16,18,20,21,24,26,29,31,35,39,43,46,51,56,63,69,75,83,91,100,110,121,134,148,163,179,196,216,238,261,288,316,349,384,421,464,510,561,618,680,748,823,905,995,1095,1204,1325,1458,1603,1764,1940,2134,2348,2583,2840,3124,3436,3780,4159,4575,5033,5535,6089,6698,7368,8105,8915,9806,10788,11866,13053,14359,15794,17374,19111,21023,23125,25438,27981,30779,33858,37243,40959, 11,12,14,15,17,18,20,21,24,26,29,32,35,38,42,47,51,56,62,68,75,83,90,99,110,120,132,146,161,177,195,215,236,260,285,314,345,380,419,461,506,557,612,674,741,816,897,987,1086,1194,1314,1445,1590,1749,1923,2117,2328,2561,2817,3099,3408,3749,4124,4536,4991,5490,6039,6642,7307,8037,8841,9726,10698,11768,12945,14240,15663,17231,18953,20849,22934,25227,27750,30525,33578,36935,40629,44691,49151, 12,14,16,18,19,21,23,25,28,30,33,37,40,44,49,54,60,65,72,79,88,96,105,116,128,140,154,170,187,207,228,250,275,303,333,366,403,443,488,537,590,649,714,786,865,952,1047,1152,1267,1393,1533,1685,1855,2041,2244,2469,2716,2987,3287,3616,3976,4373,4811,5292,5822,6405,7046,7749,8524,9377,10315,11347,12481,13729,15103,16613,18274,20102,22111,24323,26756,29432,32375,35613,39174,43090,47401,52140,57342, -1,-1,-1,-1,-1,-2,-2,-2,-2,-2,-2,-3,-3,-3,-4,-4,-4,-5,-5,-6,-6,-7,-8,-8,-9,-10,-11,-12,-13,-15,-16,-18,-20,-22,-24,-26,-29,-32,-35,-38,-42,-46,-51,-56,-62,-68,-75,-82,-91,-100,-110,-120,-133,-146,-160,-176,-194,-213,-235,-258,-284,-312,-344,-378,-416,-458,-503,-554,-609,-670,-737,-811,-892,-981,-1079,-1187,-1305,-1436,-1579,-1737,-1911,-2102,-2313,-2544,-2798,-3078,-3386,-3724,-4096, -2,-2,-2,-3,-3,-3,-3,-4,-4,-4,-5,-5,-6,-6,-7,-8,-9,-9,-10,-11,-13,-14,-15,-17,-18,-20,-22,-24,-27,-30,-33,-36,-39,-43,-48,-52,-58,-63,-70,-77,-84,-93,-102,-112,-124,-136,-150,-165,-181,-199,-219,-241,-265,-292,-321,-353,-388,-427,-470,-517,-568,-625,-687,-756,-832,-915,-1007,-1107,-1218,-1340,-1474,-1621,-1783,-1961,-2158,-2373,-2611,-2872,-3159,-3475,-3822,-4205,-4625,-5088,-5596,-6156,-6772,-7449,-8192, -4,-4,-5,-5,-6,-6,-7,-7,-8,-9,-10,-11,-12,-13,-14,-16,-17,-19,-21,-23,-25,-28,-30,-33,-37,-40,-44,-49,-54,-59,-65,-72,-79,-87,-95,-105,-115,-127,-140,-154,-169,-186,-204,-225,-247,-272,-299,-329,-362,-398,-438,-482,-530,-583,-641,-706,-776,-854,-939,-1033,-1136,-1250,-1375,-1512,-1664,-1830,-2013,-2214,-2436,-2679,-2947,-3242,-3566,-3923,-4315,-4747,-5221,-5744,-6318,-6950,-7645,-8409,-9250,-10175,-11193,-12312,-13543,-14897,-16384, -5,-6,-7,-8,-8,-9,-10,-11,-12,-13,-14,-16,-17,-19,-21,-23,-26,-28,-31,-34,-38,-41,-45,-50,-55,-60,-66,-73,-80,-89,-98,-107,-118,-130,-143,-157,-173,-190,-209,-230,-253,-278,-306,-337,-371,-408,-449,-494,-543,-597,-657,-722,-795,-875,-962,-1058,-1164,-1280,-1409,-1550,-1704,-1874,-2062,-2268,-2495,-2745,-3020,-3321,-3653,-4019,-4421,-4863,-5349,-5884,-6473,-7120,-7832,-8615,-9476,-10424,-11467,-12614,-13875,-15263,-16789,-18467,-20315,-22346,-24575, -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, -9,-10,-11,-13,-14,-15,-16,-18,-20,-21,-24,-26,-29,-31,-35,-39,-43,-46,-51,-56,-63,-69,-75,-83,-91,-100,-110,-121,-134,-148,-163,-179,-196,-216,-238,-261,-288,-316,-349,-384,-421,-464,-510,-561,-618,-680,-748,-823,-905,-995,-1095,-1204,-1325,-1458,-1603,-1764,-1940,-2134,-2348,-2583,-2840,-3124,-3436,-3780,-4159,-4575,-5033,-5535,-6089,-6698,-7368,-8105,-8915,-9806,-10788,-11866,-13053,-14359,-15794,-17374,-19111,-21023,-23125,-25438,-27981,-30779,-33858,-37243,-40959, -11,-12,-14,-15,-17,-18,-20,-21,-24,-26,-29,-32,-35,-38,-42,-47,-51,-56,-62,-68,-75,-83,-90,-99,-110,-120,-132,-146,-161,-177,-195,-215,-236,-260,-285,-314,-345,-380,-419,-461,-506,-557,-612,-674,-741,-816,-897,-987,-1086,-1194,-1314,-1445,-1590,-1749,-1923,-2117,-2328,-2561,-2817,-3099,-3408,-3749,-4124,-4536,-4991,-5490,-6039,-6642,-7307,-8037,-8841,-9726,-10698,-11768,-12945,-14240,-15663,-17231,-18953,-20849,-22934,-25227,-27750,-30525,-33578,-36935,-40629,-44691,-49151, -12,-14,-16,-18,-19,-21,-23,-25,-28,-30,-33,-37,-40,-44,-49,-54,-60,-65,-72,-79,-88,-96,-105,-116,-128,-140,-154,-170,-187,-207,-228,-250,-275,-303,-333,-366,-403,-443,-488,-537,-590,-649,-714,-786,-865,-952,-1047,-1152,-1267,-1393,-1533,-1685,-1855,-2041,-2244,-2469,-2716,-2987,-3287,-3616,-3976,-4373,-4811,-5292,-5822,-6405,-7046,-7749,-8524,-9377,-10315,-11347,-12481,-13729,-15103,-16613,-18274,-20102,-22111,-24323,-26756,-29432,-32375,-35613,-39174,-43090,-47401,-52140,-57342 }; static int[] s_anStepIndexMap = new int[] { 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87, 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87, 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87, 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87, 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,88,88, 4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,88,88,88,88, 6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,88,88,88,88,88,88, 8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,88,88,88,88,88,88,88,88, 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87, 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87, 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87, 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87, 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,88,88, 4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,88,88,88,88, 6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,88,88,88,88,88,88, 8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,88,88,88,88,88,88,88,88 }; public static byte[] EncodeImaAdpcmTableDriven(short[] ashPcm) { ArrayList alsEncoded = new ArrayList(); short shPredicted = 0; int nStepIndex = 0; int[] anStep = new int[2]; for (int i = 0; i < ashPcm.Length; i += 2) { // Encoded size is a nibble, so 2 per byte for (int k = 0; k < 2; k++) { anStep[k] = 0; if (i + k == ashPcm.Length) break; int nDelta = ashPcm[i + k] - shPredicted; int iStepSmallest = -1; int nDiffSmallest = int.MaxValue; for (int j = 0; j < 16; j++) { int nStepFraction = s_anStepFractions[j * 89 + nStepIndex]; if (shPredicted + nStepFraction < -32768 || shPredicted + nStepFraction > 32767) continue; int nDiff = Math.Abs(nStepFraction - nDelta); if (nDiff < nDiffSmallest) { nDiffSmallest = nDiff; iStepSmallest = j; } } anStep[k] = iStepSmallest; shPredicted += (short)s_anStepFractions[iStepSmallest * 89 + nStepIndex]; nStepIndex = s_anStepIndexMap[iStepSmallest * 89 + nStepIndex]; } // Add 1 byte to output alsEncoded.Add((byte)((anStep[0] << 4) | anStep[1])); } return (byte[])alsEncoded.ToArray(typeof(byte)); } public static short[] DecodeImaAdpcmTableDriven(byte[] abIn) { ArrayList alsPcm = new ArrayList(); short shPredicted = 0; int nStepIndex = 0; foreach (byte bT in abIn) { int nDelta = bT >> 4; shPredicted += (short)s_anStepFractions[nDelta * 89 + nStepIndex]; alsPcm.Add(shPredicted); nStepIndex = s_anStepIndexMap[nDelta * 89 + nStepIndex]; nDelta = bT & 0xf; shPredicted += (short)s_anStepFractions[nDelta * 89 + nStepIndex]; alsPcm.Add(shPredicted); nStepIndex = s_anStepIndexMap[nDelta * 89 + nStepIndex]; } return (short[])alsPcm.ToArray(typeof(short)); } static int[] s_anStepFractions8BitEncode = new int[] { 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,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,4,4,4,5,5,6,6,7,8,8,9,10,11,12,14,15,16,18,20,22,24,26,29,32, 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,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,4,4,5,5,5,6,7,7,8,9,10,11,12,13,14,15,17,19,20,22,25,27,30,33,36,40,44,48,53,58,64, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,4,4,4,5,5,6,6,7,8,8,9,10,11,12,13,14,16,17,19,21,23,25,28,31,34,37,41,45,49,54,60,66,72,79,87,96,106,116,128, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,3,3,4,4,5,5,5,5,6,7,8,8,9,10,11,12,14,14,16,18,20,21,23,26,29,32,35,38,42,46,50,56,61,67,74,81,89,98,108,119,131,144,158,174,191, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,3,3,3,3,4,4,5,5,6,6,7,7,8,9,10,11,12,13,15,16,18,19,21,24,26,28,31,34,38,42,46,50,56,61,67,74,81,89,98,108,119,131,144,158,174,192,211,232,255, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,4,4,4,4,5,5,6,6,8,8,9,9,10,11,13,14,15,16,19,20,23,24,26,30,33,35,39,43,48,53,58,63,70,76,84,93,101,111,123,135,149,164,180,198,218,240,264,290,319, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,5,5,5,5,6,6,8,8,9,9,11,11,12,14,15,17,18,20,23,24,27,29,32,36,39,42,47,51,57,63,69,75,84,92,101,111,122,134,147,162,179,197,216,237,261,288,317,348,383, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,5,5,5,5,7,7,9,9,11,11,12,12,14,16,18,19,21,23,26,28,32,33,37,42,46,49,54,60,67,74,81,88,98,107,117,130,142,156,172,189,208,229,252,277,305,336,369,406,446, 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,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2,-2,-3,-3,-3,-4,-4,-4,-5,-5,-6,-6,-7,-8,-8,-9,-10,-11,-12,-14,-15,-16,-18,-20,-22,-24,-26,-29,-32, 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,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2,-2,-3,-3,-3,-3,-4,-4,-5,-5,-5,-6,-7,-7,-8,-9,-10,-11,-12,-13,-14,-15,-17,-19,-20,-22,-25,-27,-30,-33,-36,-40,-44,-48,-53,-58,-64, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2,-2,-3,-3,-3,-3,-4,-4,-4,-5,-5,-6,-6,-7,-8,-8,-9,-10,-11,-12,-13,-14,-16,-17,-19,-21,-23,-25,-28,-31,-34,-37,-41,-45,-49,-54,-60,-66,-72,-79,-87,-96,-106,-116,-128, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2,-2,-2,-2,-2,-3,-3,-4,-4,-5,-5,-5,-5,-6,-7,-8,-8,-9,-10,-11,-12,-14,-14,-16,-18,-20,-21,-23,-26,-29,-32,-35,-38,-42,-46,-50,-56,-61,-67,-74,-81,-89,-98,-108,-119,-131,-144,-158,-174,-191, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2,-3,-3,-3,-3,-4,-4,-5,-5,-6,-6,-7,-7,-8,-9,-10,-11,-12,-13,-15,-16,-18,-19,-21,-24,-26,-28,-31,-34,-38,-42,-46,-50,-56,-61,-67,-74,-81,-89,-98,-108,-119,-131,-144,-158,-174,-192,-211,-232,-255, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-3,-3,-3,-3,-3,-4,-4,-4,-4,-5,-5,-6,-6,-8,-8,-9,-9,-10,-11,-13,-14,-15,-16,-19,-20,-23,-24,-26,-30,-33,-35,-39,-43,-48,-53,-58,-63,-70,-76,-84,-93,-101,-111,-123,-135,-149,-164,-180,-198,-218,-240,-264,-290,-319, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-3,-3,-3,-3,-3,-5,-5,-5,-5,-6,-6,-8,-8,-9,-9,-11,-11,-12,-14,-15,-17,-18,-20,-23,-24,-27,-29,-32,-36,-39,-42,-47,-51,-57,-63,-69,-75,-84,-92,-101,-111,-122,-134,-147,-162,-179,-197,-216,-237,-261,-288,-317,-348,-383, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-4,-4,-4,-4,-4,-5,-5,-5,-5,-7,-7,-9,-9,-11,-11,-12,-12,-14,-16,-18,-19,-21,-23,-26,-28,-32,-33,-37,-42,-46,-49,-54,-60,-67,-74,-81,-88,-98,-107,-117,-130,-142,-156,-172,-189,-208,-229,-252,-277,-305,-336,-369,-406,-446 }; public static byte[] EncodeImaAdpcmTableDriven8Bit(byte[] abPcm) { ArrayList alsEncoded = new ArrayList(); byte bPredicted = 128; int nStepIndex = 0; int[] anStep = new int[2]; for (int i = 0; i < abPcm.Length; i += 2) { // Encoded size is a nibble, so 2 per byte for (int k = 0; k < 2; k++) { anStep[k] = 0; if (i + k == abPcm.Length) break; int nDelta = abPcm[i + k] - bPredicted; int iStepSmallest = -1; int nDiffSmallest = int.MaxValue; for (int j = 0; j < 16; j++) { int nStepFraction = s_anStepFractions8BitEncode[j * 89 + nStepIndex]; if (bPredicted + nStepFraction < 0 || bPredicted + nStepFraction > 255) continue; int nDiff = Math.Abs(nStepFraction - nDelta); if (nDiff <= nDiffSmallest) { nDiffSmallest = nDiff; iStepSmallest = j; } } anStep[k] = iStepSmallest; bPredicted += (byte)s_anStepFractions8BitEncode[iStepSmallest * 89 + nStepIndex]; nStepIndex = s_anStepIndexMap[iStepSmallest * 89 + nStepIndex]; } // Add 1 byte to output alsEncoded.Add((byte)((anStep[0] << 4) | anStep[1])); } return (byte[])alsEncoded.ToArray(typeof(byte)); } static byte[] s_anStepFractions8BitDecode = new byte[] { 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,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,4,4,4,5,5,6,6,7,8,8,9,10,11,12,14,15,16,18,20,22,24,26,29,32, 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,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,4,4,5,5,5,6,7,7,8,9,10,11,12,13,14,15,17,19,20,22,25,27,30,33,36,40,44,48,53,58,64, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,4,4,4,5,5,6,6,7,8,8,9,10,11,12,13,14,16,17,19,21,23,25,28,31,34,37,41,45,49,54,60,66,72,79,87,96,106,116,128, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,3,3,4,4,5,5,5,5,6,7,8,8,9,10,11,12,14,14,16,18,20,21,23,26,29,32,35,38,42,46,50,56,61,67,74,81,89,98,108,119,131,144,158,174,191, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,3,3,3,3,4,4,5,5,6,6,7,7,8,9,10,11,12,13,15,16,18,19,21,24,26,28,31,34,38,42,46,50,56,61,67,74,81,89,98,108,119,131,144,158,174,192,211,232,255, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,4,4,4,4,5,5,6,6,8,8,9,9,10,11,13,14,15,16,19,20,23,24,26,30,33,35,39,43,48,53,58,63,70,76,84,93,101,111,123,135,149,164,180,198,218,240,255,255,255, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,5,5,5,5,6,6,8,8,9,9,11,11,12,14,15,17,18,20,23,24,27,29,32,36,39,42,47,51,57,63,69,75,84,92,101,111,122,134,147,162,179,197,216,237,255,255,255,255,255, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,5,5,5,5,7,7,9,9,11,11,12,12,14,16,18,19,21,23,26,28,32,33,37,42,46,49,54,60,67,74,81,88,98,107,117,130,142,156,172,189,208,229,252,255,255,255,255,255,255, 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,255,255,255,255,255,255,255,255,255,255,255,255,254,254,254,254,254,254,253,253,253,252,252,252,251,251,250,250,249,248,248,247,246,245,244,242,241,240,238,236,234,232,230,227,224, 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,255,255,255,255,255,255,255,255,255,255,255,255,255,254,254,254,254,254,254,253,253,253,253,252,252,251,251,251,250,249,249,248,247,246,245,244,243,242,241,239,237,236,234,231,229,226,223,220,216,212,208,203,198,192, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,254,254,254,254,254,254,253,253,253,253,252,252,252,251,251,250,250,249,248,248,247,246,245,244,243,242,240,239,237,235,233,231,228,225,222,219,215,211,207,202,196,190,184,177,169,160,150,140,128, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,254,254,254,254,254,254,254,254,254,253,253,252,252,251,251,251,251,250,249,248,248,247,246,245,244,242,242,240,238,236,235,233,230,227,224,221,218,214,210,206,200,195,189,182,175,167,158,148,137,125,112,98,82,65, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,254,254,254,254,254,253,253,253,253,252,252,251,251,250,250,249,249,248,247,246,245,244,243,241,240,238,237,235,232,230,228,225,222,218,214,210,206,200,195,189,182,175,167,158,148,137,125,112,98,82,64,45,24,1, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,253,253,253,253,253,252,252,252,252,251,251,250,250,248,248,247,247,246,245,243,242,241,240,237,236,233,232,230,226,223,221,217,213,208,203,198,193,186,180,172,163,155,145,133,121,107,92,76,58,38,16,1,1,1, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,254,254,254,254,254,254,254,254,254,254,254,253,253,253,253,253,251,251,251,251,250,250,248,248,247,247,245,245,244,242,241,239,238,236,233,232,229,227,224,220,217,214,209,205,199,193,187,181,172,164,155,145,134,122,109,94,77,59,40,19,1,1,1,1,1, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,254,254,254,254,254,254,254,254,254,254,254,252,252,252,252,252,251,251,251,251,249,249,247,247,245,245,244,244,242,240,238,237,235,233,230,228,224,223,219,214,210,207,202,196,189,182,175,168,158,149,139,126,114,100,84,67,48,27,4,1,1,1,1,1,1 }; public static byte[] DecodeImaAdpcmTableDriven8Bit(byte[] abIn) { ArrayList alsPcm = new ArrayList(); byte bPredicted = 128; int nStepIndex = 0; foreach (byte bT in abIn) { int nDelta = bT >> 4; bPredicted += s_anStepFractions8BitDecode[nDelta * 89 + nStepIndex]; alsPcm.Add(bPredicted); nStepIndex = s_anStepIndexMap[nDelta * 89 + nStepIndex]; nDelta = bT & 0xf; bPredicted += s_anStepFractions8BitDecode[nDelta * 89 + nStepIndex]; alsPcm.Add(bPredicted); nStepIndex = s_anStepIndexMap[nDelta * 89 + nStepIndex]; } return (byte[])alsPcm.ToArray(typeof(byte)); } public static byte[] EncodeYamahaAdpcm(short[] ashPcm) { ArrayList alsEncoded = new ArrayList(); int nPcmPredicted = 0; int nStep = 0x7f; byte bOutput = 0; for (int ish = 0; ish < ashPcm.Length; ish++) { //Console.WriteLine("ish: " + ish + " nPcmPredicted: " + nPcmPredicted + " nStep: " + nStep); //if (ish == 256) // Debug.Assert(false); int nPcm = ashPcm[ish]; int nDiff = nPcm - nPcmPredicted; bool fNegative = false; if (nDiff < 0) { fNegative = true; nDiff = -nDiff; } // Get fraction in multiples of 1/4ths. Note by first shifting down by two, resolution is lost. // I.e. x / 4 * 2 != x / 2. int cTimes = 0; int nStep4th = nStep / 4; int nFracT = nStep4th; while (cTimes < 7) { if (nDiff < nFracT) break; nFracT += nStep4th; cTimes++; } // Interestingly this "rebuilding" from bits has more precision that the above // because of the above nStep / 4. The nStep / 2 below has more precision than // 2 * (nStep / 4). For example 23 / 4 * 2 == 10 vs. 23 / 2 == 11. int nStepFraction = 0; if ((cTimes & 1) != 0) nStepFraction += nStep / 4; if ((cTimes & 2) != 0) nStepFraction += nStep / 2; if ((cTimes & 4) != 0) nStepFraction += nStep; nStepFraction += nStep / 8; // Add to predicted, do range check byte b = (byte)cTimes; if (fNegative) { nPcmPredicted -= nStepFraction; b |= 8; } else { nPcmPredicted += nStepFraction; } if (nPcmPredicted > 32767) { nPcmPredicted = 32767; } else if (nPcmPredicted < -32768) { nPcmPredicted = -32768; } // Yamaha calcs the next step like this. Same as MS ADPCM in terms of constants used switch (b & 7) { case 0: case 1: case 2: case 3: nStep = nStep * 230 / 256; break; case 4: nStep = nStep * 307 / 256; break; case 5: nStep = nStep * 409 / 256; break; case 6: nStep = nStep * 512 / 256; break; case 7: nStep = nStep * 614 / 256; break; } // Bounds check the step if (nStep < 0x7f) { nStep = 0x7f; } else if (nStep > 0x6000) { nStep = 0x6000; } // Output a byte. Yamaha uses "little endian" nibble order. if ((ish & 1) == 0) { bOutput = (byte)(b & 0x0f); } else { bOutput |= (byte)((b << 4) & 0xf0); alsEncoded.Add(bOutput); } } return (byte[])alsEncoded.ToArray(typeof(byte)); } public static byte[] EncodeYamahaAdpcmFaster(short[] ashPcm) { ArrayList alsEncoded = new ArrayList(); int nPcmPredicted = 0; int nStep = 0x7f; // Encoding test for "fastest" encoding. Essentially this unrolls the conditionals as much as possible. // This is an algorithmic test before encoding this in assembly language for (int ish = 0; ish < ashPcm.Length; ish += 2) { //Console.WriteLine("ish: " + ish + " nPcmPredicted: " + nPcmPredicted + " nStep: " + nStep); //if (ish == 256) // Debug.Assert(false); byte bOut; int nDiff; // Low nibble (first sample of byte) nDiff = ashPcm[ish] - nPcmPredicted; if (nDiff >= 0) { // nDiff positive int nStepT = nStep & ~3; if (nDiff >= nStepT) { nDiff -= nStepT; nStepT = nStepT >> 1; if (nDiff >= nStepT) { nDiff -= nStepT; nStepT >>= 1; if (nDiff >= nStepT) { // case 0x07 nPcmPredicted += nStep + (nStep >> 1) + nStepT + (nStepT >> 1); if (nPcmPredicted > 32767) nPcmPredicted = 32767; nStep = (nStep * 614) >> 8; if (nStep > 0x6000) nStep = 0x6000; bOut = 0x07; } else { // case 0x06 nPcmPredicted += nStep + (nStep >> 1) + (nStepT >> 1); if (nPcmPredicted > 32767) nPcmPredicted = 32767; nStep <<= 1; if (nStep > 0x6000) nStep = 0x6000; bOut = 0x06; } } else { nStepT >>= 1; if (nDiff >= nStepT) { // case 0x05 nPcmPredicted += nStep + nStepT + (nStepT >> 1); if (nPcmPredicted > 32767) nPcmPredicted = 32767; nStep = (nStep * 409) >> 8; if (nStep > 0x6000) nStep = 0x6000; bOut = 0x05; } else { // case 0x04 nPcmPredicted += nStep + (nStepT >> 1); if (nPcmPredicted > 32767) nPcmPredicted = 32767; nStep = (nStep * 307) >> 8; if (nStep > 0x6000) nStep = 0x6000; bOut = 0x04; } } } else { nStepT >>= 1; if (nDiff >= nStepT) { nDiff -= nStepT; nStepT >>= 1; if (nDiff >= nStepT) { // case 0x03 nPcmPredicted += (nStep >> 1) + nStepT + (nStepT >> 1); if (nPcmPredicted > 32767) nPcmPredicted = 32767; nStep = (nStep * 230) >> 8; if (nStep < 0x7f) nStep = 0x7f; bOut = 0x03; } else { // case 0x02 nPcmPredicted += (nStep >> 1) + (nStepT >> 1); if (nPcmPredicted > 32767) nPcmPredicted = 32767; nStep = (nStep * 230) >> 8; if (nStep < 0x7f) nStep = 0x7f; bOut = 0x02; } } else { nStepT >>= 1; if (nDiff >= nStepT) { // case 0x01 nPcmPredicted += nStepT + (nStepT >> 1); if (nPcmPredicted > 32767) nPcmPredicted = 32767; nStep = (nStep * 230) >> 8; if (nStep < 0x7f) nStep = 0x7f; bOut = 0x01; } else { // case 0x00 nPcmPredicted += (nStepT >> 1); if (nPcmPredicted > 32767) nPcmPredicted = 32767; nStep = (nStep * 230) >> 8; if (nStep < 0x7f) nStep = 0x7f; bOut = 0x00; } } } } else { // nDiff negative nDiff = -nDiff; int nStepT = nStep & ~3; if (nDiff >= nStepT) { nDiff -= nStepT; nStepT >>= 1; if (nDiff >= nStepT) { nDiff -= nStepT; nStepT >>= 1; if (nDiff >= nStepT) { // case 0x0f nPcmPredicted -= nStep + (nStep >> 1) + nStepT + (nStepT >> 1); if (nPcmPredicted < -32768) nPcmPredicted = -32768; nStep = (nStep * 614) >> 8; if (nStep > 0x6000) nStep = 0x6000; bOut = 0x0f; } else { // case 0x0e nPcmPredicted -= nStep + (nStep >> 1) + (nStepT >> 1); if (nPcmPredicted < -32768) nPcmPredicted = -32768; nStep <<= 1; if (nStep > 0x6000) nStep = 0x6000; bOut = 0x0e; } } else { nStepT >>= 1; if (nDiff >= nStepT) { // case 0x0d nPcmPredicted -= nStep + nStepT + (nStepT >> 1); if (nPcmPredicted < -32768) nPcmPredicted = -32768; nStep = (nStep * 409) >> 8; if (nStep > 0x6000) nStep = 0x6000; bOut = 0x0d; } else { // case 0x0c nPcmPredicted -= nStep + (nStepT >> 1); if (nPcmPredicted < -32768) nPcmPredicted = -32768; nStep = (nStep * 307) >> 8; if (nStep > 0x6000) nStep = 0x6000; bOut = 0x0c; } } } else { nStepT >>= 1; if (nDiff >= nStepT) { nDiff -= nStepT; nStepT >>= 1; if (nDiff >= nStepT) { // case 0x0b nPcmPredicted -= (nStep >> 1) + nStepT + (nStepT >> 1); if (nPcmPredicted < -32768) nPcmPredicted = -32768; nStep = (nStep * 230) >> 8; if (nStep < 0x7f) nStep = 0x7f; bOut = 0x0b; } else { // case 0x0a nPcmPredicted -= (nStep >> 1) + (nStepT >> 1); if (nPcmPredicted < -32768) nPcmPredicted = -32768; nStep = (nStep * 230) >> 8; if (nStep < 0x7f) nStep = 0x7f; bOut = 0x0a; } } else { nStepT >>= 1; if (nDiff >= nStepT) { // case 0x09 nPcmPredicted -= nStepT + (nStepT >> 1); if (nPcmPredicted < -32768) nPcmPredicted = -32768; nStep = (nStep * 230) >> 8; if (nStep < 0x7f) nStep = 0x7f; bOut = 0x09; } else { // case 0x08 nPcmPredicted -= (nStepT >> 1); if (nPcmPredicted < -32768) nPcmPredicted = -32768; nStep = (nStep * 230) >> 8; if (nStep < 0x7f) nStep = 0x7f; bOut = 0x08; } } } } // High nibble, second sample of byte nDiff = ashPcm[ish + 1] - nPcmPredicted; if (nDiff >= 0) { // nDiff positive int nStepT = nStep & ~3; if (nDiff >= nStepT) { nDiff -= nStepT; nStepT >>= 1; if (nDiff >= nStepT) { nDiff -= nStepT; nStepT >>= 1; if (nDiff >= nStepT) { // case 0x07 nPcmPredicted += nStep + (nStep >> 1) + nStepT + (nStepT >> 1); if (nPcmPredicted > 32767) nPcmPredicted = 32767; nStep = (nStep * 614) >> 8; if (nStep > 0x6000) nStep = 0x6000; alsEncoded.Add((byte)(0x70 + bOut)); } else { // case 0x06 nPcmPredicted += nStep + (nStep >> 1) + (nStepT >> 1); if (nPcmPredicted > 32767) nPcmPredicted = 32767; nStep <<= 1; if (nStep > 0x6000) nStep = 0x6000; alsEncoded.Add((byte)(0x60 + bOut)); } } else { nStepT >>= 1; if (nDiff >= nStepT) { // case 0x05 nPcmPredicted += nStep + nStepT + (nStepT >> 1); if (nPcmPredicted > 32767) nPcmPredicted = 32767; nStep = (nStep * 409) >> 8; if (nStep > 0x6000) nStep = 0x6000; alsEncoded.Add((byte)(0x50 + bOut)); } else { // case 0x04 nPcmPredicted += nStep + (nStepT >> 1); if (nPcmPredicted > 32767) nPcmPredicted = 32767; nStep = (nStep * 307) >> 8; if (nStep > 0x6000) nStep = 0x6000; alsEncoded.Add((byte)(0x40 + bOut)); } } } else { nStepT >>= 1; if (nDiff >= nStepT) { nDiff -= nStepT; nStepT >>= 1; if (nDiff >= nStepT) { // case 0x03 nPcmPredicted += (nStep >> 1) + nStepT + (nStepT >> 1); if (nPcmPredicted > 32767) nPcmPredicted = 32767; nStep = (nStep * 230) >> 8; if (nStep < 0x7f) nStep = 0x7f; alsEncoded.Add((byte)(0x30 + bOut)); } else { // case 0x02 nPcmPredicted += (nStep >> 1) + (nStepT >> 1); if (nPcmPredicted > 32767) nPcmPredicted = 32767; nStep = (nStep * 230) >> 8; if (nStep < 0x7f) nStep = 0x7f; alsEncoded.Add((byte)(0x20 + bOut)); } } else { nStepT >>= 1; if (nDiff >= nStepT) { // case 0x01 nPcmPredicted += nStepT + (nStepT >> 1); if (nPcmPredicted > 32767) nPcmPredicted = 32767; nStep = (nStep * 230) >> 8; if (nStep < 0x7f) nStep = 0x7f; alsEncoded.Add((byte)(0x10 + bOut)); } else { // case 0x00 nPcmPredicted += (nStepT >> 1); if (nPcmPredicted > 32767) nPcmPredicted = 32767; nStep = (nStep * 230) >> 8; if (nStep < 0x7f) nStep = 0x7f; alsEncoded.Add((byte)(0x00 + bOut)); } } } } else { // nDiff negative nDiff = -nDiff; int nStepT = nStep & ~3; if (nDiff >= nStepT) { nDiff -= nStepT; nStepT >>= 1; if (nDiff >= nStepT) { nDiff -= nStepT; nStepT >>= 1; if (nDiff >= nStepT) { // case 0x0f nPcmPredicted -= nStep + (nStep >> 1) + nStepT + (nStepT >> 1); if (nPcmPredicted < -32768) nPcmPredicted = -32768; nStep = (nStep * 614) >> 8; if (nStep > 0x6000) nStep = 0x6000; alsEncoded.Add((byte)(0xf0 + bOut)); } else { // case 0x0e nPcmPredicted -= nStep + (nStep >> 1) + (nStepT >> 1); if (nPcmPredicted < -32768) nPcmPredicted = -32768; nStep <<= 1; if (nStep > 0x6000) nStep = 0x6000; alsEncoded.Add((byte)(0xe0 + bOut)); } } else { nStepT >>= 1; if (nDiff >= nStepT) { // case 0x0d nPcmPredicted -= nStep + nStepT + (nStepT >> 1); if (nPcmPredicted < -32768) nPcmPredicted = -32768; nStep = (nStep * 409) >> 8; if (nStep > 0x6000) nStep = 0x6000; alsEncoded.Add((byte)(0xd0 + bOut)); } else { // case 0x0c nPcmPredicted -= nStep + (nStepT >> 1); if (nPcmPredicted < -32768) nPcmPredicted = -32768; nStep = (nStep * 307) >> 8; if (nStep > 0x6000) nStep = 0x6000; alsEncoded.Add((byte)(0xc0 + bOut)); } } } else { nStepT >>= 1; if (nDiff >= nStepT) { nDiff -= nStepT; nStepT >>= 1; if (nDiff >= nStepT) { // case 0x0b nPcmPredicted -= (nStep >> 1) + nStepT + (nStepT >> 1); if (nPcmPredicted < -32768) nPcmPredicted = -32768; nStep = (nStep * 230) >> 8; if (nStep < 0x7f) nStep = 0x7f; alsEncoded.Add((byte)(0xb0 + bOut)); } else { // case 0x0a nPcmPredicted -= (nStep >> 1) + (nStepT >> 1); if (nPcmPredicted < -32768) nPcmPredicted = -32768; nStep = (nStep * 230) >> 8; if (nStep < 0x7f) nStep = 0x7f; alsEncoded.Add((byte)(0xa0 + bOut)); } } else { nStepT >>= 1; if (nDiff >= nStepT) { // case 0x09 nPcmPredicted -= nStepT + (nStepT >> 1); if (nPcmPredicted < -32768) nPcmPredicted = -32768; nStep = (nStep * 230) >> 8; if (nStep < 0x7f) nStep = 0x7f; alsEncoded.Add((byte)(0x90 + bOut)); } else { // case 0x08 nPcmPredicted -= (nStepT >> 1); if (nPcmPredicted < -32768) nPcmPredicted = -32768; nStep = (nStep * 230) >> 8; if (nStep < 0x7f) nStep = 0x7f; alsEncoded.Add((byte)(0x80 + bOut)); } } } } } return (byte[])alsEncoded.ToArray(typeof(byte)); } public static void Write8BitAdpcmDecodeTable() { TextWriter tw = new StreamWriter("decode.s"); tw.WriteLine("Decode8BitAdpcm:"); for (int nStepIndex = 0; nStepIndex < 89; nStepIndex++) { tw.WriteLine("| Step Index " + nStepIndex); tw.Write(".byte "); for (int nDelta = 0; nDelta < 16; nDelta++) tw.Write(s_anStepFractions8BitDecode[nDelta * 89 + nStepIndex] + (nDelta == 15 ? "\n" : ", ")); tw.Write(".word "); for (int nDelta = 0; nDelta < 16; nDelta++) { int offCurrentIndex = 48 * nStepIndex; int nStepIndexNew = s_anStepIndexMap[nDelta * 89 + nStepIndex]; int offNewIndex = 48 * nStepIndexNew; tw.Write("0x" + ((ushort)(offNewIndex - offCurrentIndex)).ToString("x") + (nDelta == 15 ? "\n" : ", ")); } } tw.Close(); } public static void Write8BitAdpcmDecodeTableC() { TextWriter tw = new StreamWriter("decode.cpp"); tw.WriteLine("byte gabAdpcmSteppings[89][16] = {"); for (int nStepIndex = 0; nStepIndex < 89; nStepIndex++) { tw.Write(" "); for (int nDelta = 0; nDelta < 16; nDelta++) tw.Write(s_anStepFractions8BitDecode[nDelta * 89 + nStepIndex] + (nDelta == 15 ? ",\n" : ", ")); } tw.WriteLine("};"); tw.WriteLine("char s_anStepIndexMap[89][16] = {"); for (int nStepIndex = 0; nStepIndex < 89; nStepIndex++) { tw.Write(" "); for (int nDelta = 0; nDelta < 16; nDelta++) tw.Write(s_anStepIndexMap[nDelta * 89 + nStepIndex] + (nDelta == 15 ? ",\n" : ", ")); } tw.WriteLine("};"); tw.Close(); } } class Pcm { int m_cBits; int m_hz; byte[] m_ab; short[] m_ash; // See http://www.intersrv.com/~dcross/wavio.html for format info, or // http://ccrma-www.stanford.edu/courses/422/projects/WaveFormat/ [StructLayout(LayoutKind.Sequential)] struct FourCC { public byte b0; public byte b1; public byte b2; public byte b3; public FourCC(string str) { b0 = (byte)str[0]; b1 = (byte)str[1]; b2 = (byte)str[2]; b3 = (byte)str[3]; } }; [StructLayout(LayoutKind.Sequential)] struct WavFileHeader { public FourCC fccRIFF; public uint cbRiffBytes; public FourCC fccWAVE; public FourCC fccfmt; public uint cbfmtBytes; public ushort wFormatTag; public ushort nChannels; public uint nSamplesPerSec; public uint nAvgBytesPerSec; public ushort nBlockAlign; public ushort nBitsPerSample; public FourCC fccdata; public uint cbdataBytes; }; unsafe public Pcm (string strWav) { if (strWav.EndsWith(".wav") || strWav.EndsWith(".WAV")) { LoadWavFile(strWav); } if (strWav.EndsWith(".snd")) { LoadSndFile(strWav); } } unsafe void LoadWavFile(string strWav) { BinaryReader brdr = new BinaryReader(new FileStream(strWav, FileMode.Open, FileAccess.Read, FileShare.Read)); byte[] ab = brdr.ReadBytes((int)brdr.BaseStream.Length); brdr.Close(); WavFileHeader *pwavhdr = (WavFileHeader *)Marshal.UnsafeAddrOfPinnedArrayElement(ab, 0); bool fOk = true; if (pwavhdr->wFormatTag != 1) fOk = false; if (pwavhdr->nBitsPerSample != 8 && pwavhdr->nBitsPerSample != 16) fOk = false; if (!fOk) throw new Exception("Only support reading 8 / 16 bit raw pcm!"); m_cBits = pwavhdr->nBitsPerSample; m_hz = (int)pwavhdr->nSamplesPerSec; int cbSizeType = Marshal.SizeOf(pwavhdr->GetType()); switch (m_cBits) { case 8: m_ab = new byte[pwavhdr->cbdataBytes]; for (int i = 0; i < m_ab.Length; i++) m_ab[i] = ab[i + cbSizeType]; break; case 16: m_ash = new short[pwavhdr->cbdataBytes / 2]; for (int i = 0; i < pwavhdr->cbdataBytes; i += 2) m_ash[i / 2] = (short)(ab[i + cbSizeType] + ab[1 + i + cbSizeType] * 256); break; } } void LoadSndFile(string strSnd) { BinaryReader brdr = new BinaryReader(new FileStream(strSnd, FileMode.Open, FileAccess.Read, FileShare.Read)); byte[] ab = brdr.ReadBytes((int)brdr.BaseStream.Length); brdr.Close(); m_cBits = 8; m_hz = 8000; m_ab = new byte[ab.Length - 6]; for (int i = 0; i < m_ab.Length; i++) m_ab[i] = ab[i + 6]; } public Pcm(int hz, byte[] ab) { m_cBits = 8; m_hz = hz; m_ab = ab; } public Pcm(int hz, short[] ash) { m_cBits = 16; m_hz = hz; m_ash = ash; } public int BitsPerSample { get { return m_cBits; } } public int Hertz { get { return m_hz; } } public byte[] Data8Bit { get { return (byte[])m_ab.Clone(); } set { m_cBits = 8; m_ash = null; m_ab = (byte[])value.Clone(); } } public short[] Data16Bit { get { return (short[])m_ash.Clone(); } set { m_cBits = 16; m_ab = null; m_ash = (short[])value.Clone(); } } public void ConvertTo16Bit() { if (m_cBits == 16) return; // 8 bit unsigned (0,127==-128,-1 and 128,255==0,127) to // 16 bit signed (-32768 -> 32767) short[] ash = new short[m_ab.Length]; for (int i = 0; i < ash.Length; i++) { ash[i] = (short)((short)m_ab[i] * 256); ash[i] = (short)(ash[i] ^ 0x8000); } Data16Bit = ash; } public void ConvertTo8Bit() { if (m_cBits == 8) return; // 16 bit signed (-32768 -> 32767) to // 8 bit unsigned (0,127==-128,-1 and 128,255==0,127) byte[] ab = new byte[m_ash.Length]; for (int i = 0; i < ab.Length; i++) ab[i] = (byte)((m_ash[i] ^ 0x8000) / 256); Data8Bit = ab; } public void SetMaxSize(int cbMax) { if (m_cBits == 8) { if (m_ab.Length > cbMax) { byte[] abT = new byte[32700]; Array.Copy(m_ab, 0, abT, 0, abT.Length); m_ab = abT; } } else { if (m_ash.Length * 2 > cbMax) { short[] ashT = new short[cbMax / 2]; Array.Copy(m_ash, 0, ashT, 0, ashT.Length); m_ash = ashT; } } } [DllImportAttribute("winmm.dll")] private static extern int PlaySoundW(IntPtr pbSound, IntPtr hModule, uint fdwSound); const UInt32 SND_SYNC = 0x0000; // play synchronously (default) const UInt32 SND_ASYNC = 0x0001; // play asynchronously const UInt32 SND_NODEFAULT = 0x0002; // silence (!default) if sound not found const UInt32 SND_MEMORY = 0x0004; // pszSound points to a memory file const UInt32 SND_LOOP = 0x0008; // loop the sound until next sndPlaySound const UInt32 SND_NOSTOP = 0x0010; // don't stop any currently playing sound const UInt32 SND_NOWAIT = 0x00002000; // don't wait if the driver is busy const UInt32 SND_ALIAS = 0x00010000; // name is a registry alias const UInt32 SND_ALIAS_ID = 0x00110000; // alias is a predefined ID const UInt32 SND_FILENAME = 0x00020000; // name is file name const UInt32 SND_RESOURCE = 0x00040004; // name is resource name or atom const UInt32 SND_PURGE = 0x0040; const UInt32 SND_APPLICATION = 0x0080; // look for application specific association unsafe public void Play() { // Fill in the header if (m_ab == null && m_ash == null) return; int cbData = m_cBits == 8 ? m_ab.Length : m_ash.Length * 2; WavFileHeader wavhdr; wavhdr.fccRIFF = new FourCC("RIFF"); wavhdr.cbRiffBytes = (uint)(Marshal.SizeOf(typeof(WavFileHeader)) - 8 + cbData); wavhdr.fccWAVE = new FourCC("WAVE"); wavhdr.fccfmt = new FourCC("fmt "); wavhdr.cbfmtBytes = 16; wavhdr.wFormatTag = 1; wavhdr.nChannels = 1; wavhdr.nSamplesPerSec = (uint)m_hz; wavhdr.nAvgBytesPerSec = (uint)(m_cBits / 8 * m_hz); wavhdr.nBlockAlign = (ushort)(m_cBits / 8); wavhdr.nBitsPerSample = (ushort)m_cBits; wavhdr.fccdata = new FourCC("data"); wavhdr.cbdataBytes = (uint)cbData; // Serialize the data byte[] ab = SerializeStructure(null, &wavhdr, Marshal.SizeOf(wavhdr.GetType())); switch (m_cBits) { case 8: ab = SerializeStructure(ab, (void *)Marshal.UnsafeAddrOfPinnedArrayElement(m_ab, 0), m_ab.Length); break; case 16: ab = SerializeStructure(ab, (void *)Marshal.UnsafeAddrOfPinnedArrayElement(m_ash, 0), m_ash.Length * 2); break; } // Play the sound IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(ab, 0); int nSuccess = PlaySoundW(ptr, IntPtr.Zero, SND_ASYNC | SND_MEMORY); Debug.Assert(nSuccess != 0); } unsafe static byte[] SerializeStructure(byte[] ab, void *pv, int cb) { byte *pb = (byte *)pv; byte[] abNew = null; if (ab == null) { abNew = new byte[cb]; for (int i = 0; i < cb; i++) abNew[i] = pb[i]; } else { abNew = new byte[ab.Length + cb]; for (int i = 0; i < ab.Length; i++) abNew[i] = ab[i]; for (int i = 0; i < cb; i++) abNew[i + ab.Length] = pb[i]; } return abNew; } unsafe public static void PlayWavFile(string strWav) { // Get the bytes BinaryReader brdr = new BinaryReader(new FileStream(strWav, FileMode.Open)); byte[] ab = brdr.ReadBytes((int)brdr.BaseStream.Length); brdr.Close(); // Play the wav IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(ab, 0); int nSuccess = PlaySoundW(ptr, IntPtr.Zero, SND_ASYNC | SND_MEMORY); Debug.Assert(nSuccess != 0); } public byte[] GetSndEncoding() { ConvertTo8Bit(); SetMaxSize(32700 * 2); return AudioFormats.EncodeImaAdpcmTableDriven8Bit(Data8Bit); } #if false static byte[] MassageSample(byte[] abPcm) { // Need to make sure that the encoded size is a multiple of 2 bytes in size, which means the // raw size needs to be a multiple of 4 in size, and need to trail the amplitude to 128 to // reduce click sounds. byte[] abPcmNew = new byte[(abPcm.Length + 7) & ~3]; // Copy in the existing pcm Array.Copy(abPcm, 0, abPcmNew, 0, abPcm.Length); // Perform fade int cSamplesFade = abPcmNew.Length - abPcm.Length; int nSampleLast = (int)abPcm[abPcm.Length - 1]; int nStep = -((nSampleLast - 128) / cSamplesFade); for (int iSample = abPcm.Length; iSample < abPcmNew.Length; iSample++) { if (iSample == abPcmNew.Length - 1) { nSampleLast = 0; } else { nSampleLast += nStep; } abPcmNew[iSample] = (byte)nSampleLast; } return abPcmNew; } #endif } }