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.