1 /* 2 * libPCM by László Szerémi. 3 * Copyright under Boost License. 4 */ 5 6 module libPCM.codecs; 7 8 //import std.stdio; 9 10 package static immutable byte[16] ADPCM_IndexTable = 11 [-1, -1, -1, -1, 2, 4, 6, 8, 12 -1, -1, -1, -1, 2, 4, 6, 8]; 13 package static immutable byte[4] ADPCM_IndexTable_2Bit = 14 [-1, 2, 15 -1, 2]; 16 package static immutable ushort[49] DIALOGIC_ADPCM_StepTable = [16,17,19,21,23,25,28,31,34,37,41,45,50,55, 17 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]; 18 package static immutable ushort[89] IMA_ADPCM_StepTable = 19 [7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 20 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 21 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 22 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 23 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 24 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 25 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 26 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 27 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767]; 28 /*package static immutable int[27] CompactADPCM_StepTable = 29 [4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 11, 12, 13, 15, 16, 18, 30 20, 22, 24, 26, 29, 32, 35, 38, 42, 46, 51];*/ 31 32 /* 33 * A note on workpads: 34 * Dynamic decode functions use 16 bytes of workpad, consisting of 4 32 bit integers, which is the recommended initialization method to avoid misaligned 35 * integers. For looping an audio sample, you need to back up the workpad at the start of the loop, monitor the third integer (which is the position), then 36 * replace the current workpad's data with the backed up one. This is extremly important with ADPCM as they depend on many local values. 37 * Dynamic encode functions use 32 bytes. In these cases, the 3rd and 6th integers need to be set to zero if working on a fixed length buffer. 38 * Functions meant to be used on a fixed length buffer coming soon. 39 * 40 * None of the functions depend on external libraries or functions, and require no garbage collection. 41 */ 42 /** 43 * Dinamically decodes an IMA ADPCM stream. Workpad is 16 bytes long, inputStream always points to the first byte. 44 */ 45 public @nogc short dynamicDecodeIMAADPCM(ubyte* inputStream, void* workpad){ 46 int stepIndex = *cast(int*)(workpad); 47 int x_nMinusOne = *cast(int*)(workpad + 4); 48 uint position = *cast(uint*)(workpad + 8); 49 uint stepSize; 50 int d_n; 51 ubyte index; 52 //get the next index 53 if(position & 1) 54 index = *(inputStream + (position>>1)) & 0x0F; 55 else 56 index = (*(inputStream + (position>>1)))>>4; 57 //calculate the next step size 58 stepIndex += ADPCM_IndexTable[index]; 59 //clamp the index data within the steptable's range 60 if(stepIndex < 0) 61 stepIndex = 0; 62 else if(stepIndex > 88) 63 stepIndex = 88; 64 stepSize = IMA_ADPCM_StepTable[stepIndex]; 65 66 //d_n = ((stepSize) * (index & 0b0100)>>2) + ((stepSize/2) * (index & 0b0010)>>1) + ((stepSize/4) * index & 0b0001) + (stepSize/8); 67 d_n = ((stepSize) * (index & 0b0100)>>2) + ((stepSize>>1) * (index & 0b0010)>>1) + ((stepSize>>2) * index & 0b0001) + (stepSize>>4); 68 69 if(index & 0b1000) 70 d_n *= -1; 71 //adding positive feedback value 72 d_n += x_nMinusOne; 73 74 position++; 75 *cast(int*)(workpad + 8) = position; 76 *cast(int*)(workpad) = stepIndex; 77 *cast(int*)(workpad + 4) = d_n; 78 return cast(short)d_n; 79 } 80 /** 81 * Dinamically decodes an Dialogic ADPCM stream. Workpad is 16 bytes long, inputStream always points to the first byte. 82 */ 83 public @nogc short dynamicDecodeDialogicADPCM(ubyte* inputStream, void* workpad){ 84 int stepIndex = *cast(int*)(workpad); 85 int x_nMinusOne = *cast(int*)(workpad + 4); 86 uint position = *cast(uint*)(workpad + 8); 87 uint stepSize; 88 int d_n; 89 ubyte index; 90 //get the next index 91 if(position & 1) 92 index = *(inputStream + (position>>1)) & 0x0F; 93 else 94 index = (*(inputStream + (position>>1)))>>4; 95 //calculate the next step size 96 stepIndex += ADPCM_IndexTable[index]; 97 //clamp the index data within the steptable's range 98 if(stepIndex < 0) 99 stepIndex = 0; 100 else if(stepIndex > 48) 101 stepIndex = 48; 102 stepSize = DIALOGIC_ADPCM_StepTable[stepIndex]; 103 104 d_n = ((stepSize) * (index & 0b0100)>>2) + ((stepSize>>1) * (index & 0b0010)>>1) + ((stepSize>>2) * index & 0b0001) + (stepSize>>4); 105 106 if(index & 0b1000) 107 d_n *= -1; 108 //adding positive feedback value 109 d_n += x_nMinusOne; 110 111 position++; 112 *cast(int*)(workpad + 8) = position; 113 *cast(int*)(workpad) = stepIndex; 114 *cast(int*)(workpad + 4) = d_n; 115 return cast(short)(d_n * 16); 116 } 117 /** 118 * Initializes the index at 16 for Dialogic ADPCM codecs. 119 */ 120 public @nogc uint[4] initializeDialogicADPCMDecoderWorkpad(){ 121 return [16,0,0,0]; 122 } 123 /** 124 * Appends 8 bit unsigned PCM to 16 bit signed PCM. Workpad is 16 bytes long, inputStream always points to the first byte. 125 */ 126 public @nogc short dynamicDecode8BitPCMUnsigned(ubyte* inputStream, void* workpad){ 127 uint* position = cast(uint*)(workpad + 8); 128 int output = *(inputStream + *position); 129 output += byte.min; 130 output *= 256; 131 132 (*position)++; 133 return cast(short)(output); 134 } 135 /** 136 * Dinamically encodes a stream with IMA ADPCM. Workpad is 32 bytes long, inputStream and outputStream always points to the first byte. 137 */ 138 public @nogc void dynamicEncodeIMAADPCM(short* inputStream, ubyte* outputStream, void* workpad){ 139 int x_nMinusOne = *cast(int*)(workpad + 4); 140 uint position = *cast(uint*)(workpad + 8); 141 uint stepSize = *cast(uint*)(workpad + 12); 142 ubyte index; 143 144 int d_n = *(inputStream + position) - x_nMinusOne; //applying negative feedback to x_n 145 if(d_n < 0){ 146 d_n *=-1; //get the absolute value of d_n 147 index = 0b1000; //set the sign if d_n is negative 148 } 149 if(d_n >= stepSize){ 150 index |= 0b0100; 151 d_n -= stepSize; 152 } 153 stepSize >>= 1; 154 if(d_n >= stepSize){ 155 index |= 0b0010; 156 d_n -= stepSize; 157 } 158 stepSize >>= 1; 159 if(d_n >= stepSize) 160 index |= 0b0001; 161 162 //calculate next step size 163 int stepIndex = *cast(int*)(workpad); 164 stepIndex += ADPCM_IndexTable[index]; 165 //clamp the index data within the steptable's range 166 if(stepIndex < 0) 167 stepIndex = 0; 168 else if(stepIndex > 88) 169 stepIndex = 88; 170 *cast(int*)(workpad + 12) = IMA_ADPCM_StepTable[stepIndex]; 171 *cast(int*)(workpad) = stepIndex; 172 173 //write the new index into the outputStream 174 if(position & 1) 175 *(outputStream + (position>>1)) |= index; 176 else 177 *(outputStream + (position>>1)) = cast(ubyte)(index<<4); 178 179 //calculate new x_nMinusOne 180 *cast(int*)(workpad + 4) = dynamicDecodeIMAADPCM(outputStream, workpad+16); 181 182 position++; 183 *cast(int*)(workpad + 8) = position; 184 } 185 /** 186 * Dinamically encodes a stream with Dialogic ADPCM. Workpad is 32 bytes long, inputStream and outputStream always points to the first byte. 187 */ 188 public @nogc void dynamicEncodeDialogicADPCM(short* inputStream, ubyte* outputStream, void* workpad){ 189 int x_nMinusOne = *cast(int*)(workpad + 4); 190 uint position = *cast(uint*)(workpad + 8); 191 uint stepSize = *cast(uint*)(workpad + 12); 192 ubyte index; 193 194 int d_n = *(inputStream + position) - x_nMinusOne; //applying negative feedback to x_n 195 d_n /= 16; 196 if(d_n < 0){ 197 d_n *=-1; //get the absolute value of d_n 198 index = 0b1000; //set the sign if d_n is negative 199 } 200 if(d_n >= stepSize){ 201 index |= 0b0100; 202 d_n -= stepSize; 203 } 204 stepSize >>= 1; 205 if(d_n >= stepSize){ 206 index |= 0b0010; 207 d_n -= stepSize; 208 } 209 stepSize >>= 1; 210 if(d_n >= stepSize) 211 index |= 0b0001; 212 213 //calculate next step size 214 int stepIndex = *cast(int*)(workpad); 215 stepIndex += ADPCM_IndexTable[index]; 216 //clamp the index data within the steptable's range 217 if(stepIndex < 0) 218 stepIndex = 0; 219 else if(stepIndex > 48) 220 stepIndex = 48; 221 *cast(int*)(workpad + 12) = DIALOGIC_ADPCM_StepTable[stepIndex]; 222 *cast(int*)(workpad) = stepIndex; 223 224 //write the new index into the outputStream 225 if(position & 1) 226 *(outputStream + (position>>1)) |= index; 227 else 228 *(outputStream + (position>>1)) = cast(ubyte)(index<<4); 229 230 //calculate new x_nMinusOne 231 *cast(int*)(workpad + 4) = dynamicDecodeDialogicADPCM(outputStream, workpad+16); 232 233 position++; 234 *cast(int*)(workpad + 8) = position; 235 } 236 /** 237 * Initializes the index at 16 for Dialogic ADPCM codecs. 238 */ 239 public @nogc uint[8] initializeDialogicADPCMEncoderWorkpad(){ 240 return [16,0,0,0,16,0,0,0]; 241 } 242 /** 243 * Dinamically encodes 16 bit stream into 8 bit. Workpad is 32 bytes long, inputStream and outputStream always points to the first byte. 244 */ 245 public @nogc void dynamicEncode8BitPCMUnsigned(short* inputStream, ubyte* outputStream, void* workpad){ 246 uint position = *cast(uint*)(workpad + 8); 247 int outputValue = *(inputStream + position); 248 outputValue += short.max; 249 outputValue /= 256; 250 *(outputStream + position) = cast(ubyte)outputValue; 251 position++; 252 *cast(uint*)(workpad + 8) = position; 253 } 254 /** 255 * Decodes a preexisting stream automatically. 256 */ 257 public @nogc void decodeStreamIMAADPCM(ubyte* inputStream, short* outputStream, uint length){ 258 uint[4] workpad; 259 for(uint i ; i < length ; i++) 260 *(outputStream + i) = dynamicDecodeIMAADPCM(inputStream, workpad.ptr); 261 } 262 /** 263 * Decodes a preexisting stream automatically. 264 */ 265 public @nogc void decodeStreamDialogicADPCM(ubyte* inputStream, short* outputStream, uint length){ 266 uint[4] workpad = initializeDialogicADPCMDecoderWorkpad; 267 for(uint i ; i < length ; i++) 268 *(outputStream + i) = dynamicDecodeDialogicADPCM(inputStream, workpad.ptr); 269 } 270 /** 271 * Decodes a preexisting stream automatically. 272 */ 273 public @nogc void decodeStream8BitPCMUnsigned(ubyte* inputStream, short* outputStream, uint length){ 274 uint[4] workpad; 275 for(uint i ; i < length ; i++) 276 *(outputStream + i) = dynamicDecode8BitPCMUnsigned(inputStream, workpad.ptr); 277 } 278 /** 279 * Encodes a preexisting stream automatically. 280 */ 281 public @nogc void encodeStreamIMAADPCM(short* inputStream, ubyte* outputStream, uint length){ 282 uint[8] workpad; 283 for(uint i ; i < length ; i++) 284 dynamicEncodeIMAADPCM(inputStream, outputStream, workpad.ptr); 285 } 286 /** 287 * Encodes a preexisting stream automatically. 288 */ 289 public @nogc void encodeStreamDialogicADPCM(short* inputStream, ubyte* outputStream, uint length){ 290 uint[8] workpad = initializeDialogicADPCMEncoderWorkpad; 291 for(uint i ; i < length ; i++) 292 dynamicEncodeDialogicADPCM(inputStream, outputStream, workpad.ptr); 293 //writeln(workpad); 294 295 } 296 /** 297 * Encodes a preexisting stream automatically. 298 */ 299 public @nogc void encodeStream8BitPCMUnsigned(short* inputStream, ubyte* outputStream, uint length){ 300 uint[8] workpad; 301 for(uint i ; i < length ; i++) 302 dynamicEncode8BitPCMUnsigned(inputStream, outputStream, workpad.ptr); 303 }