Mercurial > sdl-ios-xcode
comparison src/audio/SDL_audiocvt.c @ 2655:b8e736c8a5a8 gsoc2008_audio_resampling
Added beginnings of resampling code.
author | Aaron Wishnick <schnarf@gmail.com> |
---|---|
date | Wed, 18 Jun 2008 04:51:10 +0000 |
parents | 3ee59c43d784 |
children | dd74182b3c3c |
comparison
equal
deleted
inserted
replaced
2654:2e14bdaaff18 | 2655:b8e736c8a5a8 |
---|---|
18 | 18 |
19 Sam Lantinga | 19 Sam Lantinga |
20 slouken@libsdl.org | 20 slouken@libsdl.org |
21 */ | 21 */ |
22 #include "SDL_config.h" | 22 #include "SDL_config.h" |
23 #include <math.h> | |
23 | 24 |
24 /* Functions for audio drivers to perform runtime conversion of audio format */ | 25 /* Functions for audio drivers to perform runtime conversion of audio format */ |
25 | 26 |
26 #include "SDL_audio.h" | 27 #include "SDL_audio.h" |
27 #include "SDL_audio_c.h" | 28 #include "SDL_audio_c.h" |
1227 if (cvt->filters[++cvt->filter_index]) { | 1228 if (cvt->filters[++cvt->filter_index]) { |
1228 cvt->filters[cvt->filter_index] (cvt, format); | 1229 cvt->filters[cvt->filter_index] (cvt, format); |
1229 } | 1230 } |
1230 } | 1231 } |
1231 | 1232 |
1233 /* Perform proper resampling */ | |
1234 static void SDLCALL | |
1235 SDL_Resample(SDL_AudioCVT * cvt, SDL_AudioFormat format) | |
1236 { | |
1237 int i, j; | |
1238 | |
1239 #ifdef DEBUG_CONVERT | |
1240 fprintf(stderr, "Converting audio rate via proper resampling (mono)\n"); | |
1241 #endif | |
1242 | |
1243 #define zerostuff_mono(type) { \ | |
1244 const type *src = (const type *) (cvt->buf + cvt->len_cvt); \ | |
1245 type *dst = (type *) (cvt->buf + (cvt->len_cvt * cvt->len_mult)); \ | |
1246 for (i = cvt->len_cvt / sizeof (type); i; --i) { \ | |
1247 src--; \ | |
1248 dst[-1] = src[0]; \ | |
1249 for( j = -cvt->len_mult; j < -1; ++j ) { \ | |
1250 dst[j] = 0; \ | |
1251 } \ | |
1252 dst -= cvt->len_mult; \ | |
1253 } \ | |
1254 } | |
1255 | |
1256 #define discard_mono(type) { \ | |
1257 const type *src = (const type *) (cvt->buf); \ | |
1258 type *dst = (type *) (cvt->buf); \ | |
1259 for (i = 0; i < cvt->len_cvt / sizeof (type); ++i) { \ | |
1260 dst[0] = src[0]; \ | |
1261 src += cvt->len_div; \ | |
1262 ++dst; \ | |
1263 } \ | |
1264 } | |
1265 | |
1266 // Step 1: Zero stuff the conversion buffer | |
1267 switch (SDL_AUDIO_BITSIZE(format)) { | |
1268 case 8: | |
1269 zerostuff_mono(Uint8); | |
1270 break; | |
1271 case 16: | |
1272 zerostuff_mono(Uint16); | |
1273 break; | |
1274 case 32: | |
1275 zerostuff_mono(Uint32); | |
1276 break; | |
1277 } | |
1278 | |
1279 cvt->len_cvt *= cvt->len_mult; | |
1280 | |
1281 // Step 2: Use either a windowed sinc FIR filter or IIR lowpass filter to remove all alias frequencies | |
1282 | |
1283 // Step 3: Discard unnecessary samples | |
1284 switch (SDL_AUDIO_BITSIZE(format)) { | |
1285 case 8: | |
1286 discard_mono(Uint8); | |
1287 break; | |
1288 case 16: | |
1289 discard_mono(Uint16); | |
1290 break; | |
1291 case 32: | |
1292 discard_mono(Uint32); | |
1293 break; | |
1294 } | |
1295 | |
1296 #undef zerostuff_mono | |
1297 #undef discard_mono | |
1298 | |
1299 cvt->len_cvt /= cvt->len_div; | |
1300 | |
1301 if (cvt->filters[++cvt->filter_index]) { | |
1302 cvt->filters[cvt->filter_index] (cvt, format); | |
1303 } | |
1304 } | |
1305 | |
1232 int | 1306 int |
1233 SDL_ConvertAudio(SDL_AudioCVT * cvt) | 1307 SDL_ConvertAudio(SDL_AudioCVT * cvt) |
1234 { | 1308 { |
1235 /* Make sure there's data to convert */ | 1309 /* Make sure there's data to convert */ |
1236 if (cvt->buf == NULL) { | 1310 if (cvt->buf == NULL) { |
1307 } | 1381 } |
1308 | 1382 |
1309 return 0; /* no conversion necessary. */ | 1383 return 0; /* no conversion necessary. */ |
1310 } | 1384 } |
1311 | 1385 |
1386 /* Generate the necessary IIR lowpass coefficients for resampling. | |
1387 Assume that the SDL_AudioCVT struct is already set up with | |
1388 the correct values for len_mult and len_div, and use the | |
1389 type of dst_format. Also assume the buffer is allocated. | |
1390 Note the buffer needs to be 6 units long. | |
1391 For now, use RBJ's cookbook coefficients. It might be more | |
1392 optimal to create a Butterworth filter, but this is more difficult. | |
1393 */ | |
1394 int SDL_BuildIIRLowpass(SDL_AudioCVT * cvt, SDL_AudioFormat format) { | |
1395 float fc; /* cutoff frequency */ | |
1396 float coeff[6]; /* floating point iir coefficients b0, b1, b2, a0, a1, a2 */ | |
1397 float scale; | |
1398 float w0, alpha, cosw0; | |
1399 | |
1400 /* The higher Q is, the higher CUTOFF can be. Need to find a good balance to avoid aliasing */ | |
1401 static const float Q = 5.0f; | |
1402 static const float CUTOFF = 0.4f; | |
1403 | |
1404 fc = (cvt->len_mult > cvt->len_div) ? CUTOFF / (float)cvt->len_mult : CUTOFF / (float)cvt->len_div; | |
1405 | |
1406 w0 = 2.0f * M_PI * fc; | |
1407 cosw0 = cosf(w0); | |
1408 alpha = sin(w0) / (2.0f * Q); | |
1409 | |
1410 /* Compute coefficients, normalizing by a0 */ | |
1411 scale = 1.0f / (1.0f + alpha); | |
1412 | |
1413 coeff[0] = (1.0f - cosw0) / 2.0f * scale; | |
1414 coeff[1] = (1.0f - cosw0) * scale; | |
1415 coeff[2] = coeff[0]; | |
1416 | |
1417 coeff[3] = 1.0f; /* a0 is normalized to 1 */ | |
1418 coeff[4] = -2.0f * cosw0 * scale; | |
1419 coeff[5] = (1.0f - alpha) * scale; | |
1420 | |
1421 /* Convert coefficients to fixed point, using the range (-2.0, 2.0) */ | |
1422 | |
1423 /* Initialize the state buffer to all zeroes, and set initial position */ | |
1424 memset(cvt->state_buf, 0, 4 * SDL_AUDIO_BITSIZE(format) / 4); | |
1425 cvt->state_pos = 0; | |
1426 } | |
1427 | |
1428 /* Apply the windowed sinc FIR filter to the given SDL_AudioCVT struct */ | |
1429 int SDL_FilterFIR(SDL_AudioCVT * cvt, SDL_AudioFormat format) { | |
1430 int n = cvt->len_cvt / (SDL_AUDIO_BITSIZE(format) / 4); | |
1431 int m = cvt->len_sinc; | |
1432 int i, j; | |
1433 | |
1434 /* Note: this makes use of the symmetry of the sinc filter. | |
1435 We can also make a big optimization here by taking advantage | |
1436 of the fact that the signal is zero stuffed, so we can do | |
1437 significantly fewer multiplications and additions. | |
1438 */ | |
1439 #define filter_sinc(type, shift_bits) { \ | |
1440 type *sinc = (type *)cvt->sinc; \ | |
1441 type *state = (type *)cvt->state_buf; \ | |
1442 type *buf = (type *)cvt->buf; \ | |
1443 for(i = 0; i < n; ++i) { \ | |
1444 state[cvt->state_pos++] = buf[i] >> shift_bits; \ | |
1445 if(cvt->state_pos == m) cvt->state_pos = 0; \ | |
1446 buf[i] = 0; \ | |
1447 for(j = 0; j < m; ++j) { \ | |
1448 buf[i] += state[j] * sinc[j]; \ | |
1449 } \ | |
1450 } \ | |
1451 } | |
1452 | |
1453 switch (SDL_AUDIO_BITSIZE(format)) { | |
1454 case 8: | |
1455 filter_sinc(Uint8, 4); | |
1456 break; | |
1457 case 16: | |
1458 filter_sinc(Uint16, 8); | |
1459 break; | |
1460 case 32: | |
1461 filter_sinc(Uint32, 16); | |
1462 break; | |
1463 } | |
1464 | |
1465 #undef filter_sinc | |
1466 | |
1467 } | |
1468 | |
1469 /* Generate the necessary windowed sinc filter for resampling. | |
1470 Assume that the SDL_AudioCVT struct is already set up with | |
1471 the correct values for len_mult and len_div, and use the | |
1472 type of dst_format. Also assume the buffer is allocated. | |
1473 Note the buffer needs to be m+1 units long. | |
1474 */ | |
1475 int | |
1476 SDL_BuildWindowedSinc(SDL_AudioCVT * cvt, SDL_AudioFormat format, unsigned int m) { | |
1477 float fScale; /* scale factor for fixed point */ | |
1478 float *fSinc; /* floating point sinc buffer, to be converted to fixed point */ | |
1479 float fc; /* cutoff frequency */ | |
1480 float two_pi_fc, two_pi_over_m, four_pi_over_m, m_over_two; | |
1481 float norm_sum, norm_fact; | |
1482 unsigned int i; | |
1483 | |
1484 /* Check that the buffer is allocated */ | |
1485 if( cvt->sinc == NULL ) { | |
1486 return -1; | |
1487 } | |
1488 | |
1489 /* Set the length */ | |
1490 cvt->len_sinc = m; | |
1491 | |
1492 /* Allocate the floating point windowed sinc. */ | |
1493 fSinc = (float *)malloc(m * sizeof(float)); | |
1494 if( fSinc == NULL ) { | |
1495 return -1; | |
1496 } | |
1497 | |
1498 /* Set up the filter parameters */ | |
1499 fc = (cvt->len_mult > cvt->len_div) ? 0.5f / (float)cvt->len_mult : 0.5f / (float)cvt->len_div; | |
1500 two_pi_fc = 2.0f * M_PI * fc; | |
1501 two_pi_over_m = 2.0f * M_PI / (float)m; | |
1502 four_pi_over_m = 2.0f * two_pi_over_m; | |
1503 m_over_two = (float)m / 2.0f; | |
1504 norm_sum = 0.0f; | |
1505 | |
1506 for(i = 0; i <= m; ++i ) { | |
1507 if( i == m/2 ) { | |
1508 fSinc[i] = two_pi_fc; | |
1509 } else { | |
1510 fSinc[i] = sinf(two_pi_fc * ((float)i - m_over_two)) / ((float)i - m_over_two); | |
1511 /* Apply blackman window */ | |
1512 fSinc[i] *= 0.42f - 0.5f * cosf(two_pi_over_m * (float)i) + 0.08f * cosf(four_pi_over_m * (float)i); | |
1513 } | |
1514 norm_sum += abs(fSinc[i]); | |
1515 } | |
1516 | |
1517 /* Now normalize and convert to fixed point. We scale everything to half the precision | |
1518 of whatever datatype we're using, for example, 16 bit data means we use 8 bits */ | |
1519 | |
1520 #define convert_fixed(type, size) { \ | |
1521 norm_fact = size / norm_sum; \ | |
1522 type *dst = (type *)cvt->sinc; \ | |
1523 for( i = 0; i <= m; ++i ) { \ | |
1524 dst[i] = (type)(fSinc[i] * norm_fact); \ | |
1525 } \ | |
1526 } | |
1527 | |
1528 /* If we're using floating point, we only need to normalize */ | |
1529 if(SDL_AUDIO_ISFLOAT(format) && SDL_AUDIO_BITSIZE(format) == 32) { | |
1530 float *fDest = (float *)cvt->sinc; | |
1531 norm_fact = 1.0f / norm_sum; | |
1532 for(i = 0; i <= m; ++i) { | |
1533 fDest[i] = fSinc[i] * norm_fact; | |
1534 } | |
1535 } else { | |
1536 switch (SDL_AUDIO_BITSIZE(format)) { | |
1537 case 8: | |
1538 convert_fixed(Uint8, 0x0e); | |
1539 break; | |
1540 case 16: | |
1541 convert_fixed(Uint16, 0xfe); | |
1542 break; | |
1543 case 32: | |
1544 convert_fixed(Uint32, 0xfffe); | |
1545 break; | |
1546 } | |
1547 } | |
1548 | |
1549 /* Initialize the state buffer to all zeroes, and set initial position */ | |
1550 memset(cvt->state_buf, 0, cvt->len_sinc * SDL_AUDIO_BITSIZE(format) / 4); | |
1551 cvt->state_pos = 0; | |
1552 | |
1553 /* Clean up */ | |
1554 #undef convert_fixed | |
1555 free(fSinc); | |
1556 } | |
1312 | 1557 |
1313 | 1558 |
1314 /* Creates a set of audio filters to convert from one format to another. | 1559 /* Creates a set of audio filters to convert from one format to another. |
1315 Returns -1 if the format conversion is not supported, 0 if there's | 1560 Returns -1 if the format conversion is not supported, 0 if there's |
1316 no conversion needed, or 1 if the audio filter is set up. | 1561 no conversion needed, or 1 if the audio filter is set up. |