audio: Improvements in channel conversion code.
authorRyan C. Gordon <icculus@icculus.org>
Sun, 08 Jan 2017 16:18:49 -0500
changeset 107937e657de3758d
parent 10792 98447b8fda53
child 10794 fb67bf50547d
audio: Improvements in channel conversion code.
src/audio/SDL_audiocvt.c
test/testresample.c
     1.1 --- a/src/audio/SDL_audiocvt.c	Sun Jan 08 14:28:44 2017 -0500
     1.2 +++ b/src/audio/SDL_audiocvt.c	Sun Jan 08 16:18:49 2017 -0500
     1.3 @@ -31,7 +31,7 @@
     1.4  
     1.5  /* Effectively mix right and left channels into a single channel */
     1.6  static void SDLCALL
     1.7 -SDL_ConvertMono(SDL_AudioCVT * cvt, SDL_AudioFormat format)
     1.8 +SDL_ConvertStereoToMono(SDL_AudioCVT * cvt, SDL_AudioFormat format)
     1.9  {
    1.10      float *dst = (float *) cvt->buf;
    1.11      const float *src = dst;
    1.12 @@ -51,20 +51,22 @@
    1.13  }
    1.14  
    1.15  
    1.16 -/* Discard top 4 channels */
    1.17 +/* Convert from 5.1 to stereo. Average left and right, discard subwoofer. */
    1.18  static void SDLCALL
    1.19 -SDL_ConvertStrip(SDL_AudioCVT * cvt, SDL_AudioFormat format)
    1.20 +SDL_Convert51ToStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
    1.21  {
    1.22      float *dst = (float *) cvt->buf;
    1.23      const float *src = dst;
    1.24      int i;
    1.25  
    1.26 -    LOG_DEBUG_CONVERT("6 channels", "stereo");
    1.27 +    LOG_DEBUG_CONVERT("5.1", "stereo");
    1.28      SDL_assert(format == AUDIO_F32SYS);
    1.29  
    1.30 +    /* this assumes FL+FR+FC+subwoof+BL+BR layout. */
    1.31      for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 2) {
    1.32 -        dst[0] = src[0];
    1.33 -        dst[1] = src[1];
    1.34 +        const double front_center = (double) src[2];
    1.35 +        dst[0] = (float) ((src[0] + front_center + src[4]) / 3.0);  /* left */
    1.36 +        dst[1] = (float) ((src[1] + front_center + src[5]) / 3.0);  /* right */
    1.37      }
    1.38  
    1.39      cvt->len_cvt /= 3;
    1.40 @@ -74,22 +76,25 @@
    1.41  }
    1.42  
    1.43  
    1.44 -/* Discard top 2 channels of 6 */
    1.45 +/* Convert from 5.1 to quad */
    1.46  static void SDLCALL
    1.47 -SDL_ConvertStrip_2(SDL_AudioCVT * cvt, SDL_AudioFormat format)
    1.48 +SDL_Convert51ToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format)
    1.49  {
    1.50      float *dst = (float *) cvt->buf;
    1.51      const float *src = dst;
    1.52      int i;
    1.53  
    1.54 -    LOG_DEBUG_CONVERT("6 channels", "quad");
    1.55 +    LOG_DEBUG_CONVERT("5.1", "quad");
    1.56      SDL_assert(format == AUDIO_F32SYS);
    1.57  
    1.58 +    /* assumes quad is FL+FR+BL+BR layout and 5.1 is FL+FR+FC+subwoof+BL+BR */
    1.59      for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 4) {
    1.60 -        dst[0] = src[0];
    1.61 -        dst[1] = src[1];
    1.62 -        dst[2] = src[2];
    1.63 -        dst[3] = src[3];
    1.64 +        /* FIXME: this is a good candidate for SIMD. */
    1.65 +        const double front_center = (double) src[2];
    1.66 +        dst[0] = (float) ((src[0] + front_center) * 0.5);  /* FL */
    1.67 +        dst[1] = (float) ((src[1] + front_center) * 0.5);  /* FR */
    1.68 +        dst[2] = (float) ((src[4] + front_center) * 0.5);  /* BL */
    1.69 +        dst[3] = (float) ((src[5] + front_center) * 0.5);  /* BR */
    1.70      }
    1.71  
    1.72      cvt->len_cvt /= 6;
    1.73 @@ -99,9 +104,10 @@
    1.74      }
    1.75  }
    1.76  
    1.77 +
    1.78  /* Duplicate a mono channel to both stereo channels */
    1.79  static void SDLCALL
    1.80 -SDL_ConvertStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
    1.81 +SDL_ConvertMonoToStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
    1.82  {
    1.83      const float *src = (const float *) (cvt->buf + cvt->len_cvt);
    1.84      float *dst = (float *) (cvt->buf + cvt->len_cvt * 2);
    1.85 @@ -125,7 +131,7 @@
    1.86  
    1.87  /* Duplicate a stereo channel to a pseudo-5.1 stream */
    1.88  static void SDLCALL
    1.89 -SDL_ConvertSurround(SDL_AudioCVT * cvt, SDL_AudioFormat format)
    1.90 +SDL_ConvertStereoTo51(SDL_AudioCVT * cvt, SDL_AudioFormat format)
    1.91  {
    1.92      int i;
    1.93      float lf, rf, ce;
    1.94 @@ -140,12 +146,13 @@
    1.95          src -= 2;
    1.96          lf = src[0];
    1.97          rf = src[1];
    1.98 -        ce = (lf * 0.5f) + (rf * 0.5f);
    1.99 -        dst[0] = src[0];
   1.100 -        dst[1] = src[1];
   1.101 -        dst[2] = lf - ce;
   1.102 -        dst[3] = rf - ce;
   1.103 -        dst[4] = dst[5] = ce;
   1.104 +        ce = (lf + rf) * 0.5f;
   1.105 +        dst[0] = lf + (lf - ce);  /* FL */
   1.106 +        dst[1] = rf + (rf - ce);  /* FR */
   1.107 +        dst[2] = ce;  /* FC */
   1.108 +        dst[3] = ce;  /* !!! FIXME: wrong! This is the subwoofer. */
   1.109 +        dst[4] = lf;  /* BL */
   1.110 +        dst[5] = rf;  /* BR */
   1.111      }
   1.112  
   1.113      cvt->len_cvt *= 3;
   1.114 @@ -157,11 +164,11 @@
   1.115  
   1.116  /* Duplicate a stereo channel to a pseudo-4.0 stream */
   1.117  static void SDLCALL
   1.118 -SDL_ConvertSurround_4(SDL_AudioCVT * cvt, SDL_AudioFormat format)
   1.119 +SDL_ConvertStereoToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format)
   1.120  {
   1.121      const float *src = (const float *) (cvt->buf + cvt->len_cvt);
   1.122      float *dst = (float *) (cvt->buf + cvt->len_cvt * 2);
   1.123 -    float lf, rf, ce;
   1.124 +    float lf, rf;
   1.125      int i;
   1.126  
   1.127      LOG_DEBUG_CONVERT("stereo", "quad");
   1.128 @@ -172,11 +179,10 @@
   1.129          src -= 2;
   1.130          lf = src[0];
   1.131          rf = src[1];
   1.132 -        ce = (lf / 2) + (rf / 2);
   1.133 -        dst[0] = src[0];
   1.134 -        dst[1] = src[1];
   1.135 -        dst[2] = lf - ce;
   1.136 -        dst[3] = rf - ce;
   1.137 +        dst[0] = lf;  /* FL */
   1.138 +        dst[1] = rf;  /* FR */
   1.139 +        dst[2] = lf;  /* BL */
   1.140 +        dst[3] = rf;  /* BR */
   1.141      }
   1.142  
   1.143      cvt->len_cvt *= 2;
   1.144 @@ -536,36 +542,36 @@
   1.145      /* Channel conversion */
   1.146      if (src_channels != dst_channels) {
   1.147          if ((src_channels == 1) && (dst_channels > 1)) {
   1.148 -            cvt->filters[cvt->filter_index++] = SDL_ConvertStereo;
   1.149 +            cvt->filters[cvt->filter_index++] = SDL_ConvertMonoToStereo;
   1.150              cvt->len_mult *= 2;
   1.151              src_channels = 2;
   1.152              cvt->len_ratio *= 2;
   1.153          }
   1.154          if ((src_channels == 2) && (dst_channels == 6)) {
   1.155 -            cvt->filters[cvt->filter_index++] = SDL_ConvertSurround;
   1.156 +            cvt->filters[cvt->filter_index++] = SDL_ConvertStereoTo51;
   1.157              src_channels = 6;
   1.158              cvt->len_mult *= 3;
   1.159              cvt->len_ratio *= 3;
   1.160          }
   1.161          if ((src_channels == 2) && (dst_channels == 4)) {
   1.162 -            cvt->filters[cvt->filter_index++] = SDL_ConvertSurround_4;
   1.163 +            cvt->filters[cvt->filter_index++] = SDL_ConvertStereoToQuad;
   1.164              src_channels = 4;
   1.165              cvt->len_mult *= 2;
   1.166              cvt->len_ratio *= 2;
   1.167          }
   1.168          while ((src_channels * 2) <= dst_channels) {
   1.169 -            cvt->filters[cvt->filter_index++] = SDL_ConvertStereo;
   1.170 +            cvt->filters[cvt->filter_index++] = SDL_ConvertMonoToStereo;
   1.171              cvt->len_mult *= 2;
   1.172              src_channels *= 2;
   1.173              cvt->len_ratio *= 2;
   1.174          }
   1.175          if ((src_channels == 6) && (dst_channels <= 2)) {
   1.176 -            cvt->filters[cvt->filter_index++] = SDL_ConvertStrip;
   1.177 +            cvt->filters[cvt->filter_index++] = SDL_Convert51ToStereo;
   1.178              src_channels = 2;
   1.179              cvt->len_ratio /= 3;
   1.180          }
   1.181          if ((src_channels == 6) && (dst_channels == 4)) {
   1.182 -            cvt->filters[cvt->filter_index++] = SDL_ConvertStrip_2;
   1.183 +            cvt->filters[cvt->filter_index++] = SDL_Convert51ToQuad;
   1.184              src_channels = 4;
   1.185              cvt->len_ratio /= 2;
   1.186          }
   1.187 @@ -575,7 +581,7 @@
   1.188           */
   1.189          while (((src_channels % 2) == 0) &&
   1.190                 ((src_channels / 2) >= dst_channels)) {
   1.191 -            cvt->filters[cvt->filter_index++] = SDL_ConvertMono;
   1.192 +            cvt->filters[cvt->filter_index++] = SDL_ConvertStereoToMono;
   1.193              src_channels /= 2;
   1.194              cvt->len_ratio /= 2;
   1.195          }
     2.1 --- a/test/testresample.c	Sun Jan 08 14:28:44 2017 -0500
     2.2 +++ b/test/testresample.c	Sun Jan 08 16:18:49 2017 -0500
     2.3 @@ -20,6 +20,7 @@
     2.4      Uint32 len = 0;
     2.5      Uint8 *data = NULL;
     2.6      int cvtfreq = 0;
     2.7 +    int cvtchans = 0;
     2.8      int bitsize = 0;
     2.9      int blockalign = 0;
    2.10      int avgbytes = 0;
    2.11 @@ -28,12 +29,13 @@
    2.12      /* Enable standard application logging */
    2.13      SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
    2.14  
    2.15 -    if (argc != 4) {
    2.16 -        SDL_Log("USAGE: %s in.wav out.wav newfreq\n", argv[0]);
    2.17 +    if (argc != 5) {
    2.18 +        SDL_Log("USAGE: %s in.wav out.wav newfreq newchans\n", argv[0]);
    2.19          return 1;
    2.20      }
    2.21  
    2.22      cvtfreq = SDL_atoi(argv[3]);
    2.23 +    cvtchans = SDL_atoi(argv[4]);
    2.24  
    2.25      if (SDL_Init(SDL_INIT_AUDIO) == -1) {
    2.26          SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_Init() failed: %s\n", SDL_GetError());
    2.27 @@ -47,7 +49,7 @@
    2.28      }
    2.29  
    2.30      if (SDL_BuildAudioCVT(&cvt, spec.format, spec.channels, spec.freq,
    2.31 -                          spec.format, spec.channels, cvtfreq) == -1) {
    2.32 +                          spec.format, cvtchans, cvtfreq) == -1) {
    2.33          SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "failed to build CVT: %s\n", SDL_GetError());
    2.34          SDL_FreeWAV(data);
    2.35          SDL_Quit();
    2.36 @@ -83,7 +85,7 @@
    2.37      }
    2.38  
    2.39      bitsize = SDL_AUDIO_BITSIZE(spec.format);
    2.40 -    blockalign = (bitsize / 8) * spec.channels;
    2.41 +    blockalign = (bitsize / 8) * cvtchans;
    2.42      avgbytes = cvtfreq * blockalign;
    2.43  
    2.44      SDL_WriteLE32(io, 0x46464952);      /* RIFF */
    2.45 @@ -92,7 +94,7 @@
    2.46      SDL_WriteLE32(io, 0x20746D66);      /* fmt */
    2.47      SDL_WriteLE32(io, 16);      /* chunk size */
    2.48      SDL_WriteLE16(io, 1);       /* uncompressed */
    2.49 -    SDL_WriteLE16(io, spec.channels);   /* channels */
    2.50 +    SDL_WriteLE16(io, cvtchans);   /* channels */
    2.51      SDL_WriteLE32(io, cvtfreq); /* sample rate */
    2.52      SDL_WriteLE32(io, avgbytes);        /* average bytes per second */
    2.53      SDL_WriteLE16(io, blockalign);      /* block align */