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 }