Mercurial > SDL_sound_CoreAudio
comparison alt_audio_convert.c @ 338:7b9a0f3f030e
Initial add.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Mon, 20 May 2002 16:18:09 +0000 |
parents | |
children | fbbb1f25b944 |
comparison
equal
deleted
inserted
replaced
337:83233d8a5929 | 338:7b9a0f3f030e |
---|---|
1 /* | |
2 Extended Audio Converter for SDL (Simple DirectMedia Layer) | |
3 Copyright (C) 2002 Frank Ranostaj | |
4 Institute of Applied Physik | |
5 Johann Wolfgang Goethe-Universität | |
6 Frankfurt am Main, Germany | |
7 | |
8 This library is free software; you can redistribute it and/or | |
9 modify it under the terms of the GNU Library General Public | |
10 License as published by the Free Software Foundation; either | |
11 version 2 of the License, or (at your option) any later version. | |
12 | |
13 This library is distributed in the hope that it will be useful, | |
14 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 Library General Public License for more details. | |
17 | |
18 You should have received a copy of the GNU Library General Public | |
19 License along with this library; if not, write to the Free | |
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 | |
22 Frank Ranostaj | |
23 ranostaj@stud.uni-frankfurt.de | |
24 | |
25 (This code blatantly abducted for SDL_sound. Thanks, Frank! --ryan.) | |
26 */ | |
27 | |
28 #include "alt_audio_convert.h" | |
29 | |
30 #include <stdlib.h> | |
31 #include <math.h> | |
32 | |
33 /*provisorisch*/ | |
34 #define AUDIO_8 (4) | |
35 #define AUDIO_16WRONG (8) | |
36 #define AUDIO_FORMAT (AUDIO_8 | AUDIO_16WRONG) | |
37 #define AUDIO_SIGN (1) | |
38 | |
39 | |
40 /*-------------------------------------------------------------------------*/ | |
41 /* this filter (Kaiser-window beta=6.8) gives a decent -80dB attentuation */ | |
42 static const int filter[_fsize/2] = { | |
43 0, 20798, 0, -6764, 0, 3863, 0, -2560, 0, 1800, 0, -1295, 0, 936, 0, | |
44 -671, | |
45 0, 474, 0, -326, 0, 217, 0, -138, 0, 83, 0, -46, 0, 23, 0, -9 | |
46 }; | |
47 | |
48 /* Mono (1 channel ) */ | |
49 #define Suffix(x) x##1 | |
50 #include "filter_templates.h" | |
51 #undef Suffix | |
52 | |
53 /* Stereo (2 channel ) */ | |
54 #define Suffix(x) x##2 | |
55 #include "filter_templates.h" | |
56 #undef Suffix | |
57 | |
58 /* !!! FIXME: Lose all the "short" vars for "Sint16", etc. */ | |
59 | |
60 /*-------------------------------------------------------------------------*/ | |
61 int DECLSPEC Sound_ConvertAudio( Sound_AudioCVT *Data ) | |
62 { | |
63 AdapterC Temp; | |
64 int i; | |
65 | |
66 /* !!! FIXME: Try the looping stuff under certain circumstances? --ryan. */ | |
67 int mode = 0; | |
68 | |
69 /* Make sure there's a converter */ | |
70 if( Data == NULL ) { | |
71 SDL_SetError("No converter given"); | |
72 return(-1); | |
73 } | |
74 | |
75 /* Make sure there's data to convert */ | |
76 if( Data->buf == NULL ) { | |
77 SDL_SetError("No buffer allocated for conversion"); | |
78 return(-1); | |
79 } | |
80 | |
81 /* Set up the conversion and go! */ | |
82 Temp.buffer = (short*)Data->buf; | |
83 Temp.mode = mode; | |
84 Temp.filter = &Data->filter; | |
85 | |
86 Data->len_cvt = Data->len; | |
87 for( i = 0; Data->adapter[i] != NULL; i++ ) | |
88 Data->len_cvt = (*Data->adapter[i])( Temp, Data->len_cvt); | |
89 | |
90 return(0); | |
91 } | |
92 | |
93 /*-------------------------------------------------------------------------*/ | |
94 int expand8BitTo16Bit( AdapterC Data, int length ) | |
95 { | |
96 int i; | |
97 char* inp = (char*)Data.buffer-1; | |
98 short* buffer = Data.buffer-1; | |
99 for( i = length; i; i-- ) | |
100 buffer[i] = inp[i]<<8; | |
101 return length*2; | |
102 } | |
103 | |
104 /*-------------------------------------------------------------------------*/ | |
105 int swapBytes( AdapterC Data, int length ) | |
106 { | |
107 int i; | |
108 unsigned short a,b; | |
109 short* buffer = Data.buffer; | |
110 for( i = 0; i < length; i++ ) | |
111 { | |
112 a = b = buffer[i]; | |
113 a <<= 8; | |
114 b >>= 8; | |
115 buffer[i] = a | b; | |
116 } | |
117 return length; | |
118 } | |
119 | |
120 /*-------------------------------------------------------------------------*/ | |
121 int cut16BitTo8Bit( AdapterC Data, int length ) | |
122 { | |
123 int i; | |
124 short* inp = Data.buffer-1; | |
125 char* buffer = (char*)Data.buffer-1; | |
126 for( i = 0; i < length; i++ ) | |
127 buffer[i] = inp[i]>>8; | |
128 return length/2; | |
129 } | |
130 | |
131 /*-------------------------------------------------------------------------*/ | |
132 int changeSigned( AdapterC Data, int length ) | |
133 { | |
134 int i; | |
135 short* buffer = Data.buffer; | |
136 for( i = 0; i < length; i++ ) | |
137 buffer[i] ^= 0x8000; | |
138 return length; | |
139 } | |
140 | |
141 /*-------------------------------------------------------------------------*/ | |
142 int convertStereoToMono( AdapterC Data, int length ) | |
143 { | |
144 int i; | |
145 short* buffer = Data.buffer; | |
146 | |
147 /* | |
148 * !!! FIXME: Can we avoid the division in this loop and just keep | |
149 * !!! FIXME: a second index variable? --ryan. | |
150 */ | |
151 for( i = 0; i < length; i+=2 ) | |
152 buffer[i/2] = ((int)buffer[i] + buffer[i+1] ) >> 1; | |
153 return length/2; | |
154 } | |
155 | |
156 /*-------------------------------------------------------------------------*/ | |
157 int convertMonoToStereo( AdapterC Data, int length ) | |
158 { | |
159 int i; | |
160 short* buffer = Data.buffer-2; | |
161 length *= 2; | |
162 | |
163 /* | |
164 * !!! FIXME: Can we avoid the division in this loop and just keep | |
165 * !!! FIXME: a second index variable? --ryan. | |
166 */ | |
167 for( i = length; i; i-=2 ) | |
168 buffer[i] = buffer [i+1] = buffer[i/2]; | |
169 return length*2; | |
170 } | |
171 | |
172 /*-------------------------------------------------------------------------*/ | |
173 int minus5dB( AdapterC Data, int length ) | |
174 { | |
175 int i; | |
176 short* buffer = Data.buffer; | |
177 for(i = length; i >= 0; i--) | |
178 buffer[i]= 38084 * buffer[i] >> 16; | |
179 return length; | |
180 } | |
181 | |
182 /*-------------------------------------------------------------------------*/ | |
183 int doubleRateStereo( AdapterC Data, int length ) | |
184 { | |
185 _doubleRate2( Data.buffer, Data.mode, length/2 ); | |
186 return 2*_doubleRate2( Data.buffer+1, Data.mode, length/2 ); | |
187 } | |
188 | |
189 int doubleRateMono( AdapterC Data, int length ) | |
190 { | |
191 return _doubleRate1( Data.buffer, Data.mode, length ); | |
192 } | |
193 | |
194 /*-------------------------------------------------------------------------*/ | |
195 int halfRateStereo( AdapterC Data, int length ) | |
196 { | |
197 _halfRate2( Data.buffer, Data.mode, length/2 ); | |
198 return 2*_halfRate2( Data.buffer+1, Data.mode, length/2 ); | |
199 } | |
200 | |
201 int halfRateMono( AdapterC Data, int length ) | |
202 { | |
203 return _halfRate2( Data.buffer, Data.mode, length ); | |
204 } | |
205 | |
206 /*-------------------------------------------------------------------------*/ | |
207 int varRateStereo( AdapterC Data, int length ) | |
208 { | |
209 _varRate2( Data.buffer, Data.mode, Data.filter, length/2 ); | |
210 return 2*_varRate2( Data.buffer+1, Data.mode, Data.filter, length/2 ); | |
211 } | |
212 | |
213 int varRateMono( AdapterC Data, int length ) | |
214 { | |
215 return _varRate1( Data.buffer, Data.mode, Data.filter, length ); | |
216 } | |
217 | |
218 /*-------------------------------------------------------------------------*/ | |
219 typedef struct{ | |
220 short denominator; | |
221 short numerator; | |
222 } Fraction; | |
223 | |
224 /*-------------------------------------------------------------------------*/ | |
225 Fraction findFraction( float Value ) | |
226 { | |
227 /* gives a maximal error of 3% and typical less than 0.2% */ | |
228 const char frac[96]={ | |
229 1, 2, -1, /* /1 */ | |
230 1, 3, -1, /* /2 */ | |
231 2, 4, 5, -1, /* /3 */ | |
232 3, 5, 7, -1, /* /4 */ | |
233 3, 4, 6, 7, 8, 9, -1, /* /5 */ | |
234 5, 7, 11, -1, /* /6 */ | |
235 4, 5, 6, 8, 9, 10, 11, 12, 13, -1, /* /7 */ | |
236 5, 7, 9, 11, 13, 15, -1, /* /8 */ | |
237 5, 7, 8, 10, 11, 13, 14, 16, -1, /* /9 */ | |
238 7, 9, 11, 13, -1, /* /10 */ | |
239 6, 7, 8, 9, 10, 12, 13, 14, 15, 16, -1, /* /11 */ | |
240 7, 11, 13, -1, /* /12 */ | |
241 7, 8, 9, 10, 11, 12, 14, 15, 16, -1, /* /13 */ | |
242 9, 11, 13, 15, -1, /* /14 */ | |
243 8, 11, 13, 14, 16, -1, /* /15 */ | |
244 9, 11, 13, 15 }; /* /16 */ | |
245 | |
246 | |
247 Fraction Result = {0,0}; | |
248 int n,num,den=2; | |
249 | |
250 float RelErr, BestErr = 0; | |
251 if( Value < 31/64. || Value > 64/31. ) return Result; | |
252 | |
253 for( n = 0; n < sizeof(frac); num=frac[++n] ) | |
254 { | |
255 if( num < 0 ) den++; | |
256 RelErr = Value * num / den; | |
257 RelErr = ( RelErr < (1/RelErr) ? RelErr : 1/RelErr ); | |
258 if( RelErr > BestErr ) | |
259 { | |
260 BestErr = RelErr; | |
261 Result.denominator = den; | |
262 Result.numerator = num; | |
263 } | |
264 } | |
265 return Result; | |
266 } | |
267 | |
268 | |
269 float sinc( float x ) | |
270 { | |
271 if( x > -1e-24 && x < 1e-24 ) return 1.; | |
272 else return sin(x)/x; | |
273 } | |
274 | |
275 void calculateVarFilter( short* dst, float Ratio, float phase, float scale ) | |
276 { | |
277 const unsigned short KaiserWindow7[]= { | |
278 22930, 16292, 14648, 14288, 14470, 14945, 15608, 16404, | |
279 17304, 18289, 19347, 20467, 21644, 22872, 24145, 25460, | |
280 26812, 28198, 29612, 31052, 32513, 33991, 35482, 36983, | |
281 38487, 39993, 41494, 42986, 44466, 45928, 47368, 48782, | |
282 50165, 51513, 52821, 54086, 55302, 56466, 57575, 58624, | |
283 59610, 60529, 61379, 62156, 62858, 63483, 64027, 64490, | |
284 64870, 65165, 65375, 65498, 65535, 65484, 65347, 65124, | |
285 64815, 64422, 63946, 63389, 62753, 62039, 61251, 60391 }; | |
286 int i; | |
287 float w; | |
288 const float fg = -.018 + .5 / Ratio; | |
289 const float omega = 2 * M_PI * fg; | |
290 phase -= 63; | |
291 for( i = 0; i < 64; i++) | |
292 { | |
293 w = scale * ( KaiserWindow7[i] * ( i + 1 )); | |
294 dst[i] = w * sinc( omega * (i+phase) ); | |
295 dst[127-i] = w * sinc( omega * (127-i+phase) ); | |
296 } | |
297 } | |
298 | |
299 typedef struct{ | |
300 float scale; | |
301 int incr; | |
302 } VarFilterMode; | |
303 | |
304 const VarFilterMode Up = { 0.0211952, 0 }; | |
305 const VarFilterMode Down = { 0.0364733, 2 }; | |
306 | |
307 | |
308 void setupVarFilter( VarFilter* filter, | |
309 float Ratio, VarFilterMode Direction ) | |
310 { | |
311 int i,n,d; | |
312 Fraction IRatio; | |
313 float phase; | |
314 IRatio = findFraction( Ratio ); | |
315 | |
316 if ( (1/Ratio) < Ratio ) | |
317 Ratio = 1/Ratio; | |
318 | |
319 n = IRatio.numerator; | |
320 d = IRatio.denominator; | |
321 filter->pos_mod = n; | |
322 | |
323 for( i = 0; i < d; i++ ) | |
324 { | |
325 if( phase >= n ) | |
326 { | |
327 phase -= d; | |
328 filter->incr[i] = Direction.incr; | |
329 } | |
330 else | |
331 filter->incr[i] = 1; | |
332 | |
333 calculateVarFilter( filter->c[i], Ratio, phase/(float)n, | |
334 Direction.scale ); | |
335 phase += d; | |
336 } | |
337 } | |
338 | |
339 int createRateConverter( Sound_AudioCVT *Data, int filter_index, | |
340 int SrcRate, int DestRate, int Channel ) | |
341 { | |
342 int VarPos = 0; | |
343 int Mono = 2 - Channel; | |
344 float Ratio = DestRate; | |
345 if( SrcRate < 1 || SrcRate > 1<<18 || | |
346 DestRate < 1 || DestRate > 1<<18 ) return -1; | |
347 if( SrcRate == DestRate ) return filter_index; | |
348 Ratio /= SrcRate; | |
349 | |
350 if( Ratio > 1.0) | |
351 VarPos = filter_index++; | |
352 else | |
353 Data->adapter[filter_index++] = minus5dB; | |
354 | |
355 while( Ratio > 64.0/31.0) | |
356 { | |
357 Data->adapter[filter_index++] = | |
358 Mono ? doubleRateMono : doubleRateStereo; | |
359 Ratio /= 2; | |
360 Data->len_mult *= 2; | |
361 Data->add *= 2; | |
362 Data->add += _fsize; | |
363 } | |
364 | |
365 while( Ratio < 31.0/64.0 ) | |
366 { | |
367 Data->adapter[filter_index++] = | |
368 Mono ? halfRateMono : halfRateStereo; | |
369 Ratio *= 2; | |
370 } | |
371 | |
372 if( Ratio > 1.0 ) | |
373 { | |
374 setupVarFilter( &Data->filter, Ratio, Up ); | |
375 Data->adapter[VarPos] = | |
376 Mono ? varRateMono : varRateStereo; | |
377 Data->len_mult *= 2; | |
378 Data->add *= 2; | |
379 Data->add += _fsize; | |
380 } | |
381 else | |
382 { | |
383 setupVarFilter( &Data->filter, Ratio, Down ); | |
384 Data->adapter[filter_index++] = | |
385 Mono ? varRateMono : varRateStereo; | |
386 } | |
387 return 0; | |
388 } | |
389 | |
390 int DECLSPEC Sound_BuildAudioCVT(Sound_AudioCVT *Data, | |
391 Uint16 src_format, Uint8 src_channels, int src_rate, | |
392 Uint16 dst_format, Uint8 dst_channels, int dst_rate) | |
393 { | |
394 int filter_index = 0; | |
395 | |
396 if( Data == NULL ) return -1; | |
397 Data->len_mult = 1.0; | |
398 Data->add = 0; | |
399 | |
400 /* Check channels */ | |
401 if( src_channels < 1 || src_channels > 2 || | |
402 dst_channels < 1 || dst_channels > 2 ) goto error_exit; | |
403 | |
404 /* First filter: Size/Endian conversion */ | |
405 switch( src_format & AUDIO_FORMAT) | |
406 { | |
407 case AUDIO_8: | |
408 Data->adapter[filter_index++] = expand8BitTo16Bit; | |
409 Data->len_mult *= 2; | |
410 break; | |
411 case AUDIO_16WRONG: | |
412 Data->adapter[filter_index++] = swapBytes; | |
413 } | |
414 | |
415 /* Second adapter: Sign conversion -- unsigned/signed */ | |
416 if( src_format & AUDIO_SIGN ) | |
417 Data->adapter[filter_index++] = changeSigned; | |
418 | |
419 /* Third adapter: Stereo->Mono conversion */ | |
420 if( src_channels == 2 && dst_channels == 1 ) | |
421 Data->adapter[filter_index++] = convertStereoToMono; | |
422 | |
423 /* Do rate conversion */ | |
424 if( src_channels == 2 && dst_channels == 2 ) | |
425 filter_index = createRateConverter( Data, filter_index, | |
426 src_rate, dst_rate, 2 ); | |
427 else | |
428 filter_index = createRateConverter( Data, filter_index, | |
429 src_rate, dst_rate, 1 ); | |
430 | |
431 if( filter_index < 0 ) goto error_exit; /* propagate error */ | |
432 | |
433 /* adapter: Mono->Stereo conversion */ | |
434 if( src_channels == 1 && dst_channels == 2 ){ | |
435 Data->adapter[filter_index++] = convertMonoToStereo; | |
436 Data->add *= 2; | |
437 Data->len_mult *= 2; | |
438 } | |
439 | |
440 /* adapter: final Sign conversion -- unsigned/signed */ | |
441 if( dst_format & AUDIO_SIGN ) | |
442 Data->adapter[filter_index++] = changeSigned; | |
443 | |
444 /* final adapter: Size/Endian conversion */ | |
445 switch( dst_format & AUDIO_FORMAT) | |
446 { | |
447 case AUDIO_8: | |
448 Data->adapter[filter_index++] = cut16BitTo8Bit; | |
449 break; | |
450 case AUDIO_16WRONG: | |
451 Data->adapter[filter_index++] = swapBytes; | |
452 } | |
453 /* Set up the filter information */ | |
454 Data->adapter[filter_index] = NULL; | |
455 Data->needed = (filter_index > 0); | |
456 return 0; | |
457 | |
458 error_exit: | |
459 Data->adapter[0] = NULL; | |
460 return -1; | |
461 } | |
462 /*-------------------------------------------------------------------------*/ | |
463 |