comparison src/audio/SDL_audiocvt.c @ 2656:dd74182b3c3c gsoc2008_audio_resampling

Began implementing IIR and FIR filters, and got zero stuffing and sample discarding working.
author Aaron Wishnick <schnarf@gmail.com>
date Wed, 18 Jun 2008 18:55:50 +0000
parents b8e736c8a5a8
children 29306e52dab8
comparison
equal deleted inserted replaced
2655:b8e736c8a5a8 2656:dd74182b3c3c
24 24
25 /* Functions for audio drivers to perform runtime conversion of audio format */ 25 /* Functions for audio drivers to perform runtime conversion of audio format */
26 26
27 #include "SDL_audio.h" 27 #include "SDL_audio.h"
28 #include "SDL_audio_c.h" 28 #include "SDL_audio_c.h"
29
30 #define DEBUG_CONVERT
29 31
30 /* Effectively mix right and left channels into a single channel */ 32 /* Effectively mix right and left channels into a single channel */
31 static void SDLCALL 33 static void SDLCALL
32 SDL_ConvertMono(SDL_AudioCVT * cvt, SDL_AudioFormat format) 34 SDL_ConvertMono(SDL_AudioCVT * cvt, SDL_AudioFormat format)
33 { 35 {
1235 SDL_Resample(SDL_AudioCVT * cvt, SDL_AudioFormat format) 1237 SDL_Resample(SDL_AudioCVT * cvt, SDL_AudioFormat format)
1236 { 1238 {
1237 int i, j; 1239 int i, j;
1238 1240
1239 #ifdef DEBUG_CONVERT 1241 #ifdef DEBUG_CONVERT
1240 fprintf(stderr, "Converting audio rate via proper resampling (mono)\n"); 1242 printf("Converting audio rate via proper resampling (mono)\n");
1241 #endif 1243 #endif
1242 1244
1243 #define zerostuff_mono(type) { \ 1245 #define zerostuff_mono(type) { \
1244 const type *src = (const type *) (cvt->buf + cvt->len_cvt); \ 1246 const type *src = (const type *) (cvt->buf + cvt->len_cvt); \
1245 type *dst = (type *) (cvt->buf + (cvt->len_cvt * cvt->len_mult)); \ 1247 type *dst = (type *) (cvt->buf + (cvt->len_cvt * cvt->len_mult)); \
1254 } 1256 }
1255 1257
1256 #define discard_mono(type) { \ 1258 #define discard_mono(type) { \
1257 const type *src = (const type *) (cvt->buf); \ 1259 const type *src = (const type *) (cvt->buf); \
1258 type *dst = (type *) (cvt->buf); \ 1260 type *dst = (type *) (cvt->buf); \
1259 for (i = 0; i < cvt->len_cvt / sizeof (type); ++i) { \ 1261 for (i = 0; i < cvt->len_cvt / cvt->len_div / sizeof (type); ++i) { \
1260 dst[0] = src[0]; \ 1262 dst[0] = src[0]; \
1261 src += cvt->len_div; \ 1263 src += cvt->len_div; \
1262 ++dst; \ 1264 ++dst; \
1263 } \ 1265 } \
1264 } 1266 }
1265 1267
1266 // Step 1: Zero stuff the conversion buffer 1268 // Step 1: Zero stuff the conversion buffer
1269 #ifdef DEBUG_CONVERT
1270 printf("Zero-stuffing by a factor of %u\n", cvt->len_mult);
1271 #endif
1267 switch (SDL_AUDIO_BITSIZE(format)) { 1272 switch (SDL_AUDIO_BITSIZE(format)) {
1268 case 8: 1273 case 8:
1269 zerostuff_mono(Uint8); 1274 zerostuff_mono(Uint8);
1270 break; 1275 break;
1271 case 16: 1276 case 16:
1279 cvt->len_cvt *= cvt->len_mult; 1284 cvt->len_cvt *= cvt->len_mult;
1280 1285
1281 // Step 2: Use either a windowed sinc FIR filter or IIR lowpass filter to remove all alias frequencies 1286 // Step 2: Use either a windowed sinc FIR filter or IIR lowpass filter to remove all alias frequencies
1282 1287
1283 // Step 3: Discard unnecessary samples 1288 // Step 3: Discard unnecessary samples
1289 #ifdef DEBUG_CONVERT
1290 printf("Discarding samples by a factor of %u\n", cvt->len_div);
1291 #endif
1284 switch (SDL_AUDIO_BITSIZE(format)) { 1292 switch (SDL_AUDIO_BITSIZE(format)) {
1285 case 8: 1293 case 8:
1286 discard_mono(Uint8); 1294 discard_mono(Uint8);
1287 break; 1295 break;
1288 case 16: 1296 case 16:
1416 1424
1417 coeff[3] = 1.0f; /* a0 is normalized to 1 */ 1425 coeff[3] = 1.0f; /* a0 is normalized to 1 */
1418 coeff[4] = -2.0f * cosw0 * scale; 1426 coeff[4] = -2.0f * cosw0 * scale;
1419 coeff[5] = (1.0f - alpha) * scale; 1427 coeff[5] = (1.0f - alpha) * scale;
1420 1428
1421 /* Convert coefficients to fixed point, using the range (-2.0, 2.0) */ 1429 /* Copy the coefficients to the struct. If necessary, convert coefficients to fixed point, using the range (-2.0, 2.0) */
1430 if(SDL_AUDIO_ISFLOAT(format) && SDL_AUDIO_BITSIZE(format) == 32) {
1431 float *cvt_coeff = (float *)cvt->coeff;
1432 int i;
1433 for(i = 0; i < 6; ++i) {
1434 cvt_coeff[i] = coeff[i];
1435 }
1436 } else {
1437 }
1422 1438
1423 /* Initialize the state buffer to all zeroes, and set initial position */ 1439 /* Initialize the state buffer to all zeroes, and set initial position */
1424 memset(cvt->state_buf, 0, 4 * SDL_AUDIO_BITSIZE(format) / 4); 1440 memset(cvt->state_buf, 0, 4 * SDL_AUDIO_BITSIZE(format) / 4);
1425 cvt->state_pos = 0; 1441 cvt->state_pos = 0;
1442 }
1443
1444 /* Apply the lowpass IIR filter to the given SDL_AudioCVT struct */
1445 int SDL_FilterIIR(SDL_AudioCVT * cvt, SDL_AudioFormat format) {
1446 int i, n;
1447
1448 n = cvt->len_cvt / (SDL_AUDIO_BITSIZE(format) / 4);
1449
1450 if(SDL_AUDIO_ISFLOAT(format) && SDL_AUDIO_BITSIZE(format) == 32) {
1451 float *coeff = (float *)cvt->coeff;
1452 float *state = (float *)cvt->state_buf;
1453 float *buf = (float *)cvt->buf;
1454 float temp;
1455
1456
1457 for(i = 0; i < n; ++i) {
1458 /* y[n] = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] - a1 * y[n-1] - a[2] * y[n-2] */
1459 temp = buf[n];
1460 if( cvt->state_pos ) {
1461 buf[n] = coeff[0] * buf[n] + coeff[1] * state[0] + coeff[2] * state[1] - coeff[4] * state[2] - coeff[5] * state[3];
1462 state[1] = temp;
1463 state[3] = buf[n];
1464 cvt->state_pos = 0;
1465 } else {
1466 buf[n] = coeff[0] * buf[n] + coeff[1] * state[1] + coeff[2] * state[0] - coeff[4] * state[3] - coeff[5] * state[2];
1467 state[0] = temp;
1468 state[2] = buf[n];
1469 cvt->state_pos = 1;
1470 }
1471 }
1472 } else {
1473 }
1426 } 1474 }
1427 1475
1428 /* Apply the windowed sinc FIR filter to the given SDL_AudioCVT struct */ 1476 /* Apply the windowed sinc FIR filter to the given SDL_AudioCVT struct */
1429 int SDL_FilterFIR(SDL_AudioCVT * cvt, SDL_AudioFormat format) { 1477 int SDL_FilterFIR(SDL_AudioCVT * cvt, SDL_AudioFormat format) {
1430 int n = cvt->len_cvt / (SDL_AUDIO_BITSIZE(format) / 4); 1478 int n = cvt->len_cvt / (SDL_AUDIO_BITSIZE(format) / 4);
1435 We can also make a big optimization here by taking advantage 1483 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 1484 of the fact that the signal is zero stuffed, so we can do
1437 significantly fewer multiplications and additions. 1485 significantly fewer multiplications and additions.
1438 */ 1486 */
1439 #define filter_sinc(type, shift_bits) { \ 1487 #define filter_sinc(type, shift_bits) { \
1440 type *sinc = (type *)cvt->sinc; \ 1488 type *sinc = (type *)cvt->coeff; \
1441 type *state = (type *)cvt->state_buf; \ 1489 type *state = (type *)cvt->state_buf; \
1442 type *buf = (type *)cvt->buf; \ 1490 type *buf = (type *)cvt->buf; \
1443 for(i = 0; i < n; ++i) { \ 1491 for(i = 0; i < n; ++i) { \
1444 state[cvt->state_pos++] = buf[i] >> shift_bits; \ 1492 state[cvt->state_pos++] = buf[i] >> shift_bits; \
1445 if(cvt->state_pos == m) cvt->state_pos = 0; \ 1493 if(cvt->state_pos == m) cvt->state_pos = 0; \
1447 for(j = 0; j < m; ++j) { \ 1495 for(j = 0; j < m; ++j) { \
1448 buf[i] += state[j] * sinc[j]; \ 1496 buf[i] += state[j] * sinc[j]; \
1449 } \ 1497 } \
1450 } \ 1498 } \
1451 } 1499 }
1452 1500
1453 switch (SDL_AUDIO_BITSIZE(format)) { 1501 /* If it's floating point, we don't need to do any shifting */
1454 case 8: 1502 if(SDL_AUDIO_ISFLOAT(format) && SDL_AUDIO_BITSIZE(format) == 32) {
1455 filter_sinc(Uint8, 4); 1503 float *sinc = (float *)cvt->coeff;
1456 break; 1504 float *state = (float *)cvt->state_buf;
1457 case 16: 1505 float *buf = (float *)cvt->buf;
1458 filter_sinc(Uint16, 8); 1506
1459 break; 1507 for(i = 0; i < n; ++i) {
1460 case 32: 1508 state[cvt->state_pos++] = buf[i];
1461 filter_sinc(Uint32, 16); 1509 if(cvt->state_pos == m) cvt->state_pos = 0;
1462 break; 1510 buf[i] = 0.0f;
1511 for(j = 0; j < m; ++j) {
1512 buf[i] += state[j] * sinc[j];
1513 }
1514 }
1515 } else {
1516 switch (SDL_AUDIO_BITSIZE(format)) {
1517 case 8:
1518 filter_sinc(Uint8, 4);
1519 break;
1520 case 16:
1521 filter_sinc(Uint16, 8);
1522 break;
1523 case 32:
1524 filter_sinc(Uint32, 16);
1525 break;
1526 }
1463 } 1527 }
1464 1528
1465 #undef filter_sinc 1529 #undef filter_sinc
1466 1530
1467 } 1531 }
1480 float two_pi_fc, two_pi_over_m, four_pi_over_m, m_over_two; 1544 float two_pi_fc, two_pi_over_m, four_pi_over_m, m_over_two;
1481 float norm_sum, norm_fact; 1545 float norm_sum, norm_fact;
1482 unsigned int i; 1546 unsigned int i;
1483 1547
1484 /* Check that the buffer is allocated */ 1548 /* Check that the buffer is allocated */
1485 if( cvt->sinc == NULL ) { 1549 if( cvt->coeff == NULL ) {
1486 return -1; 1550 return -1;
1487 } 1551 }
1488 1552
1489 /* Set the length */ 1553 /* Set the length */
1490 cvt->len_sinc = m; 1554 cvt->len_sinc = m;
1517 /* Now normalize and convert to fixed point. We scale everything to half the precision 1581 /* 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 */ 1582 of whatever datatype we're using, for example, 16 bit data means we use 8 bits */
1519 1583
1520 #define convert_fixed(type, size) { \ 1584 #define convert_fixed(type, size) { \
1521 norm_fact = size / norm_sum; \ 1585 norm_fact = size / norm_sum; \
1522 type *dst = (type *)cvt->sinc; \ 1586 type *dst = (type *)cvt->coeff; \
1523 for( i = 0; i <= m; ++i ) { \ 1587 for( i = 0; i <= m; ++i ) { \
1524 dst[i] = (type)(fSinc[i] * norm_fact); \ 1588 dst[i] = (type)(fSinc[i] * norm_fact); \
1525 } \ 1589 } \
1526 } 1590 }
1527 1591
1528 /* If we're using floating point, we only need to normalize */ 1592 /* If we're using floating point, we only need to normalize */
1529 if(SDL_AUDIO_ISFLOAT(format) && SDL_AUDIO_BITSIZE(format) == 32) { 1593 if(SDL_AUDIO_ISFLOAT(format) && SDL_AUDIO_BITSIZE(format) == 32) {
1530 float *fDest = (float *)cvt->sinc; 1594 float *fDest = (float *)cvt->coeff;
1531 norm_fact = 1.0f / norm_sum; 1595 norm_fact = 1.0f / norm_sum;
1532 for(i = 0; i <= m; ++i) { 1596 for(i = 0; i <= m; ++i) {
1533 fDest[i] = fSinc[i] * norm_fact; 1597 fDest[i] = fSinc[i] * norm_fact;
1534 } 1598 }
1535 } else { 1599 } else {
1553 /* Clean up */ 1617 /* Clean up */
1554 #undef convert_fixed 1618 #undef convert_fixed
1555 free(fSinc); 1619 free(fSinc);
1556 } 1620 }
1557 1621
1622 /* This is used to reduce the resampling ratio */
1623 inline int SDL_GCD(int a, int b) {
1624 int temp;
1625 while(b != 0) {
1626 temp = a % b;
1627 a = b;
1628 b = temp;
1629 }
1630 return a;
1631 }
1632
1558 1633
1559 /* Creates a set of audio filters to convert from one format to another. 1634 /* Creates a set of audio filters to convert from one format to another.
1560 Returns -1 if the format conversion is not supported, 0 if there's 1635 Returns -1 if the format conversion is not supported, 0 if there's
1561 no conversion needed, or 1 if the audio filter is set up. 1636 no conversion needed, or 1 if the audio filter is set up.
1562 */ 1637 */
1642 /* Uh oh.. */ ; 1717 /* Uh oh.. */ ;
1643 } 1718 }
1644 } 1719 }
1645 1720
1646 /* Do rate conversion */ 1721 /* Do rate conversion */
1647 cvt->rate_incr = 0.0; 1722 int rate_gcd;
1723 rate_gcd = SDL_GCD(src_rate, dst_rate);
1724 cvt->len_mult = dst_rate / rate_gcd;
1725 cvt->len_div = src_rate / rate_gcd;
1726 cvt->len_ratio = (double)cvt->len_mult / (double)cvt->len_div;
1727 cvt->filters[cvt->filter_index++] = SDL_Resample;
1728
1729 /*cvt->rate_incr = 0.0;
1648 if ((src_rate / 100) != (dst_rate / 100)) { 1730 if ((src_rate / 100) != (dst_rate / 100)) {
1649 Uint32 hi_rate, lo_rate; 1731 Uint32 hi_rate, lo_rate;
1650 int len_mult; 1732 int len_mult;
1651 double len_ratio; 1733 double len_ratio;
1652 SDL_AudioFilter rate_cvt = NULL; 1734 SDL_AudioFilter rate_cvt = NULL;
1691 default: 1773 default:
1692 return -1; 1774 return -1;
1693 } 1775 }
1694 len_mult = 2; 1776 len_mult = 2;
1695 len_ratio = 2.0; 1777 len_ratio = 2.0;
1696 } 1778 }*/
1697 /* If hi_rate = lo_rate*2^x then conversion is easy */ 1779 /* If hi_rate = lo_rate*2^x then conversion is easy */
1698 while (((lo_rate * 2) / 100) <= (hi_rate / 100)) { 1780 /*while (((lo_rate * 2) / 100) <= (hi_rate / 100)) {
1699 cvt->filters[cvt->filter_index++] = rate_cvt; 1781 cvt->filters[cvt->filter_index++] = rate_cvt;
1700 cvt->len_mult *= len_mult; 1782 cvt->len_mult *= len_mult;
1701 lo_rate *= 2; 1783 lo_rate *= 2;
1702 cvt->len_ratio *= len_ratio; 1784 cvt->len_ratio *= len_ratio;
1703 } 1785 }*/
1704 /* We may need a slow conversion here to finish up */ 1786 /* We may need a slow conversion here to finish up */
1705 if ((lo_rate / 100) != (hi_rate / 100)) { 1787 /*if ((lo_rate / 100) != (hi_rate / 100)) {*/
1706 #if 1 1788 #if 1
1707 /* The problem with this is that if the input buffer is 1789 /* The problem with this is that if the input buffer is
1708 say 1K, and the conversion rate is say 1.1, then the 1790 say 1K, and the conversion rate is say 1.1, then the
1709 output buffer is 1.1K, which may not be an acceptable 1791 output buffer is 1.1K, which may not be an acceptable
1710 buffer size for the audio driver (not a power of 2) 1792 buffer size for the audio driver (not a power of 2)
1720 cvt->rate_incr = (double) hi_rate / lo_rate; 1802 cvt->rate_incr = (double) hi_rate / lo_rate;
1721 cvt->len_ratio *= cvt->rate_incr; 1803 cvt->len_ratio *= cvt->rate_incr;
1722 } 1804 }
1723 cvt->filters[cvt->filter_index++] = SDL_RateSLOW; 1805 cvt->filters[cvt->filter_index++] = SDL_RateSLOW;
1724 #endif 1806 #endif
1725 } 1807 /* }
1726 } 1808 }*/
1727 1809
1728 /* Set up the filter information */ 1810 /* Set up the filter information */
1729 if (cvt->filter_index != 0) { 1811 if (cvt->filter_index != 0) {
1730 cvt->needed = 1; 1812 cvt->needed = 1;
1731 cvt->src_format = src_fmt; 1813 cvt->src_format = src_fmt;