audio: More effort to improve and simplify audio resamplers.
authorRyan C. Gordon <icculus@icculus.org>
Thu, 05 Jan 2017 19:12:20 -0500
changeset 10756073957aca821
parent 10755 43953bdaa3ee
child 10757 329d6d46fb90
audio: More effort to improve and simplify audio resamplers.
src/audio/SDL_audio_c.h
src/audio/SDL_audiocvt.c
src/audio/SDL_audiotypecvt.c
     1.1 --- a/src/audio/SDL_audio_c.h	Thu Jan 05 19:30:45 2017 -0500
     1.2 +++ b/src/audio/SDL_audio_c.h	Thu Jan 05 19:12:20 2017 -0500
     1.3 @@ -50,9 +50,8 @@
     1.4  void SDLCALL SDL_Convert_F32_to_U16(SDL_AudioCVT *cvt, SDL_AudioFormat format);
     1.5  void SDLCALL SDL_Convert_F32_to_S32(SDL_AudioCVT *cvt, SDL_AudioFormat format);
     1.6  void SDL_Upsample_Arbitrary(SDL_AudioCVT *cvt, const int channels);
     1.7 +void SDL_Upsample_Multiple(SDL_AudioCVT *cvt, const int channels);
     1.8  void SDL_Downsample_Arbitrary(SDL_AudioCVT *cvt, const int channels);
     1.9 -void SDL_Upsample_x2(SDL_AudioCVT *cvt, const int channels);
    1.10 -void SDL_Upsample_x4(SDL_AudioCVT *cvt, const int channels);
    1.11 -void SDL_Downsample_Multiple(SDL_AudioCVT *cvt, const int multiple, const int channels);
    1.12 +void SDL_Downsample_Multiple(SDL_AudioCVT *cvt, const int channels);
    1.13  
    1.14  /* vi: set ts=4 sw=4 expandtab: */
     2.1 --- a/src/audio/SDL_audiocvt.c	Thu Jan 05 19:30:45 2017 -0500
     2.2 +++ b/src/audio/SDL_audiocvt.c	Thu Jan 05 19:12:20 2017 -0500
     2.3 @@ -331,14 +331,43 @@
     2.4      return retval;
     2.5  }
     2.6  
     2.7 +
     2.8 +/* !!! FIXME: We only have this macro salsa because SDL_AudioCVT doesn't store
     2.9 +   !!! FIXME:  channel info or integer sample rates, so we have to have
    2.10 +   !!! FIXME:  function entry points for each supported channel count and
    2.11 +   !!! FIXME:  multiple vs arbitrary. When we rev the ABI, remove this. */
    2.12 +#define RESAMPLER_FUNCS(chans) \
    2.13 +    static void SDLCALL \
    2.14 +    SDL_Upsample_Multiple_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
    2.15 +        SDL_assert(format == AUDIO_F32SYS); \
    2.16 +        SDL_Upsample_Multiple(cvt, chans); \
    2.17 +    } \
    2.18 +    static void SDLCALL \
    2.19 +    SDL_Upsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
    2.20 +        SDL_assert(format == AUDIO_F32SYS); \
    2.21 +        SDL_Upsample_Arbitrary(cvt, chans); \
    2.22 +    }\
    2.23 +    static void SDLCALL \
    2.24 +    SDL_Downsample_Multiple_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
    2.25 +        SDL_assert(format == AUDIO_F32SYS); \
    2.26 +        SDL_Downsample_Multiple(cvt, chans); \
    2.27 +    } \
    2.28 +    static void SDLCALL \
    2.29 +    SDL_Downsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
    2.30 +        SDL_assert(format == AUDIO_F32SYS); \
    2.31 +        SDL_Downsample_Arbitrary(cvt, chans); \
    2.32 +    }
    2.33 +RESAMPLER_FUNCS(1)
    2.34 +RESAMPLER_FUNCS(2)
    2.35 +RESAMPLER_FUNCS(4)
    2.36 +RESAMPLER_FUNCS(6)
    2.37 +RESAMPLER_FUNCS(8)
    2.38 +#undef RESAMPLER_FUNCS
    2.39 +
    2.40  static int
    2.41  SDL_FindFrequencyMultiple(const int src_rate, const int dst_rate)
    2.42  {
    2.43 -    int retval = 0;
    2.44 -
    2.45 -    /* If we only built with the arbitrary resamplers, ignore multiples. */
    2.46      int lo, hi;
    2.47 -    int div;
    2.48  
    2.49      SDL_assert(src_rate != 0);
    2.50      SDL_assert(dst_rate != 0);
    2.51 @@ -352,110 +381,73 @@
    2.52          hi = src_rate;
    2.53      }
    2.54  
    2.55 -    /* zero means "not a supported multiple" ... we only do 2x and 4x. */
    2.56      if ((hi % lo) != 0)
    2.57          return 0;               /* not a multiple. */
    2.58  
    2.59 -    div = hi / lo;
    2.60 -    retval = ((div == 2) || (div == 4)) ? div : 0;
    2.61 -
    2.62 -    return retval;
    2.63 +    return hi / lo;
    2.64  }
    2.65  
    2.66 -#define RESAMPLER_FUNCS(chans) \
    2.67 -    static void SDLCALL \
    2.68 -    SDL_Upsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
    2.69 -        SDL_assert(format == AUDIO_F32SYS); \
    2.70 -        SDL_Upsample_Arbitrary(cvt, chans); \
    2.71 -    }\
    2.72 -    static void SDLCALL \
    2.73 -    SDL_Downsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
    2.74 -        SDL_assert(format == AUDIO_F32SYS); \
    2.75 -        SDL_Downsample_Arbitrary(cvt, chans); \
    2.76 -    } \
    2.77 -    static void SDLCALL \
    2.78 -    SDL_Upsample_x2_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
    2.79 -        SDL_assert(format == AUDIO_F32SYS); \
    2.80 -        SDL_Upsample_x2(cvt, chans); \
    2.81 -    } \
    2.82 -    static void SDLCALL \
    2.83 -    SDL_Downsample_x2_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
    2.84 -        SDL_assert(format == AUDIO_F32SYS); \
    2.85 -        SDL_Downsample_Multiple(cvt, 2, chans); \
    2.86 -    } \
    2.87 -    static void SDLCALL \
    2.88 -    SDL_Upsample_x4_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
    2.89 -        SDL_assert(format == AUDIO_F32SYS); \
    2.90 -        SDL_Upsample_x4(cvt, chans); \
    2.91 -    } \
    2.92 -    static void SDLCALL \
    2.93 -    SDL_Downsample_x4_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
    2.94 -        SDL_assert(format == AUDIO_F32SYS); \
    2.95 -        SDL_Downsample_Multiple(cvt, 4, chans); \
    2.96 +static SDL_AudioFilter
    2.97 +ChooseResampler(const int dst_channels, const int src_rate, const int dst_rate)
    2.98 +{
    2.99 +    const int upsample = (src_rate < dst_rate) ? 1 : 0;
   2.100 +    const int multiple = SDL_FindFrequencyMultiple(src_rate, dst_rate);
   2.101 +    SDL_AudioFilter filter = NULL;
   2.102 +
   2.103 +    #define PICK_CHANNEL_FILTER(upordown, resampler) switch (dst_channels) { \
   2.104 +        case 1: filter = SDL_##upordown##_##resampler##_c1; break; \
   2.105 +        case 2: filter = SDL_##upordown##_##resampler##_c2; break; \
   2.106 +        case 4: filter = SDL_##upordown##_##resampler##_c4; break; \
   2.107 +        case 6: filter = SDL_##upordown##_##resampler##_c6; break; \
   2.108 +        case 8: filter = SDL_##upordown##_##resampler##_c8; break; \
   2.109 +        default: break; \
   2.110      }
   2.111 -RESAMPLER_FUNCS(1)
   2.112 -RESAMPLER_FUNCS(2)
   2.113 -RESAMPLER_FUNCS(4)
   2.114 -RESAMPLER_FUNCS(6)
   2.115 -RESAMPLER_FUNCS(8)
   2.116 -#undef RESAMPLER_FUNCS
   2.117 +
   2.118 +    if (upsample) {
   2.119 +        if (multiple) {
   2.120 +            PICK_CHANNEL_FILTER(Upsample, Multiple);
   2.121 +        } else {
   2.122 +            PICK_CHANNEL_FILTER(Upsample, Arbitrary);
   2.123 +        }
   2.124 +    } else {
   2.125 +        if (multiple) {
   2.126 +            PICK_CHANNEL_FILTER(Downsample, Multiple);
   2.127 +        } else {
   2.128 +            PICK_CHANNEL_FILTER(Downsample, Arbitrary);
   2.129 +        }
   2.130 +    }
   2.131 +
   2.132 +    #undef PICK_CHANNEL_FILTER
   2.133 +
   2.134 +    return filter;
   2.135 +}
   2.136  
   2.137  static int
   2.138 -SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, int dst_channels,
   2.139 -                          int src_rate, int dst_rate)
   2.140 +SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, const int dst_channels,
   2.141 +                          const int src_rate, const int dst_rate)
   2.142  {
   2.143 -    if (src_rate != dst_rate) {
   2.144 -        const int upsample = (src_rate < dst_rate) ? 1 : 0;
   2.145 -        const int multiple = SDL_FindFrequencyMultiple(src_rate, dst_rate);
   2.146 -        SDL_AudioFilter filter = NULL;
   2.147 +    SDL_AudioFilter filter;
   2.148  
   2.149 -        #define PICK_CHANNEL_FILTER(upordown, resampler) switch (dst_channels) { \
   2.150 -            case 1: filter = SDL_##upordown##_##resampler##_c1; break; \
   2.151 -            case 2: filter = SDL_##upordown##_##resampler##_c2; break; \
   2.152 -            case 4: filter = SDL_##upordown##_##resampler##_c4; break; \
   2.153 -            case 6: filter = SDL_##upordown##_##resampler##_c6; break; \
   2.154 -            case 8: filter = SDL_##upordown##_##resampler##_c8; break; \
   2.155 -            default: break; \
   2.156 -        }
   2.157 -
   2.158 -        if (upsample) {
   2.159 -            if (multiple == 0) {
   2.160 -                PICK_CHANNEL_FILTER(Upsample, Arbitrary);
   2.161 -            } else if (multiple == 2) {
   2.162 -                PICK_CHANNEL_FILTER(Upsample, x2);
   2.163 -            } else if (multiple == 4) {
   2.164 -                PICK_CHANNEL_FILTER(Upsample, x4);
   2.165 -            }
   2.166 -        } else {
   2.167 -            if (multiple == 0) {
   2.168 -                PICK_CHANNEL_FILTER(Downsample, Arbitrary);
   2.169 -            } else if (multiple == 2) {
   2.170 -                PICK_CHANNEL_FILTER(Downsample, x2);
   2.171 -            } else if (multiple == 4) {
   2.172 -                PICK_CHANNEL_FILTER(Downsample, x4);
   2.173 -            }
   2.174 -        }
   2.175 -
   2.176 -        #undef PICK_CHANNEL_FILTER
   2.177 -
   2.178 -        if (filter == NULL) {
   2.179 -            return SDL_SetError("No conversion available for these rates");
   2.180 -        }
   2.181 -
   2.182 -        /* Update (cvt) with filter details... */
   2.183 -        cvt->filters[cvt->filter_index++] = filter;
   2.184 -        if (src_rate < dst_rate) {
   2.185 -            const double mult = ((double) dst_rate) / ((double) src_rate);
   2.186 -            cvt->len_mult *= (int) SDL_ceil(mult);
   2.187 -            cvt->len_ratio *= mult;
   2.188 -        } else {
   2.189 -            cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate);
   2.190 -        }
   2.191 -
   2.192 -        return 1;               /* added a converter. */
   2.193 +    if (src_rate == dst_rate) {
   2.194 +        return 0;  /* no conversion necessary. */
   2.195      }
   2.196  
   2.197 -    return 0;                   /* no conversion necessary. */
   2.198 +    filter = ChooseResampler(dst_channels, src_rate, dst_rate);
   2.199 +    if (filter == NULL) {
   2.200 +        return SDL_SetError("No conversion available for these rates");
   2.201 +    }
   2.202 +
   2.203 +    /* Update (cvt) with filter details... */
   2.204 +    cvt->filters[cvt->filter_index++] = filter;
   2.205 +    if (src_rate < dst_rate) {
   2.206 +        const double mult = ((double) dst_rate) / ((double) src_rate);
   2.207 +        cvt->len_mult *= (int) SDL_ceil(mult);
   2.208 +        cvt->len_ratio *= mult;
   2.209 +    } else {
   2.210 +        cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate);
   2.211 +    }
   2.212 +
   2.213 +    return 1;               /* added a converter. */
   2.214  }
   2.215  
   2.216  
   2.217 @@ -514,7 +506,7 @@
   2.218  
   2.219         The expectation is we can process data faster in float32
   2.220         (possibly with SIMD), and making several passes over the same
   2.221 -       buffer in is likely to be CPU cache-friendly, avoiding the
   2.222 +       buffer is likely to be CPU cache-friendly, avoiding the
   2.223         biggest performance hit in modern times. Previously we had
   2.224         (script-generated) custom converters for every data type and
   2.225         it was a bloat on SDL compile times and final library size. */
   2.226 @@ -585,11 +577,11 @@
   2.227      }
   2.228  
   2.229      /* Do rate conversion, if necessary. Updates (cvt). */
   2.230 -    if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) ==
   2.231 -        -1) {
   2.232 +    if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) == -1) {
   2.233          return -1;              /* shouldn't happen, but just in case... */
   2.234      }
   2.235  
   2.236 +    /* Move to final data type. */
   2.237      if (SDL_BuildAudioTypeCVTFromFloat(cvt, dst_fmt) == -1) {
   2.238          return -1;              /* shouldn't happen, but just in case... */
   2.239      }
     3.1 --- a/src/audio/SDL_audiotypecvt.c	Thu Jan 05 19:30:45 2017 -0500
     3.2 +++ b/src/audio/SDL_audiotypecvt.c	Thu Jan 05 19:12:20 2017 -0500
     3.3 @@ -220,14 +220,14 @@
     3.4  SDL_Upsample_Arbitrary(SDL_AudioCVT *cvt, const int channels)
     3.5  {
     3.6      const int srcsize = cvt->len_cvt - (64 * channels);
     3.7 -    const int dstsize = (int) (((double)(cvt->len_cvt/(channels*4))) * cvt->rate_incr) * (channels*4);
     3.8 +    const int dstsize = (int) ((((double)(cvt->len_cvt/(channels*4))) * cvt->rate_incr)) * (channels*4);
     3.9      register int eps = 0;
    3.10      float *dst = ((float *) (cvt->buf + dstsize)) - channels;
    3.11      const float *src = ((float *) (cvt->buf + cvt->len_cvt)) - channels;
    3.12      const float *target = ((const float *) cvt->buf);
    3.13      const size_t cpy = sizeof (float) * channels;
    3.14 +    float sample[8];
    3.15      float last_sample[8];
    3.16 -    float sample[8];
    3.17      int i;
    3.18  
    3.19  #if DEBUG_CONVERT
    3.20 @@ -236,7 +236,9 @@
    3.21  
    3.22      SDL_assert(channels <= 8);
    3.23  
    3.24 -    SDL_memcpy(sample, src, cpy);
    3.25 +    for (i = 0; i < channels; i++) {
    3.26 +        sample[i] = (float) ((((double) src[i]) + ((double) src[i - channels])) * 0.5);
    3.27 +    }
    3.28      SDL_memcpy(last_sample, src, cpy);
    3.29  
    3.30      while (dst > target) {
    3.31 @@ -244,11 +246,15 @@
    3.32          dst -= channels;
    3.33          eps += srcsize;
    3.34          if ((eps << 1) >= dstsize) {
    3.35 -            src -= channels;
    3.36 -            for (i = 0; i < channels; i++) {
    3.37 -                sample[i] = (float) ((((double) src[i]) + ((double) last_sample[i])) * 0.5);
    3.38 +            if (src > target) {
    3.39 +                src -= channels;
    3.40 +                for (i = 0; i < channels; i++) {
    3.41 +                    sample[i] = (float) ((((double) src[i]) + ((double) last_sample[i])) * 0.5);
    3.42 +                }
    3.43 +            } else {
    3.44 +
    3.45              }
    3.46 -            SDL_memcpy(last_sample, sample, cpy);
    3.47 +            SDL_memcpy(last_sample, src, cpy);
    3.48              eps -= dstsize;
    3.49          }
    3.50      }
    3.51 @@ -291,7 +297,7 @@
    3.52              for (i = 0; i < channels; i++) {
    3.53                  sample[i] = (float) ((((double) src[i]) + ((double) last_sample[i])) * 0.5);
    3.54              }
    3.55 -            SDL_memcpy(last_sample, sample, cpy);
    3.56 +            SDL_memcpy(last_sample, src, cpy);
    3.57              eps -= srcsize;
    3.58          }
    3.59      }
    3.60 @@ -303,32 +309,43 @@
    3.61  }
    3.62  
    3.63  void
    3.64 -SDL_Upsample_x2(SDL_AudioCVT *cvt, const int channels)
    3.65 +SDL_Upsample_Multiple(SDL_AudioCVT *cvt, const int channels)
    3.66  {
    3.67 -    const int dstsize = cvt->len_cvt * 2;
    3.68 -    float *dst = ((float *) (cvt->buf + dstsize)) - (channels * 2);
    3.69 +    const int multiple = (int) cvt->rate_incr;
    3.70 +    const int dstsize = cvt->len_cvt * multiple;
    3.71 +    float *buf = (float *) cvt->buf;
    3.72 +    float *dst = ((float *) (cvt->buf + dstsize)) - channels;
    3.73      const float *src = ((float *) (cvt->buf + cvt->len_cvt)) - channels;
    3.74 -    const float *target = ((const float *) cvt->buf);
    3.75 +    const float *target = buf + channels;
    3.76      const size_t cpy = sizeof (float) * channels;
    3.77      float last_sample[8];
    3.78      int i;
    3.79  
    3.80  #if DEBUG_CONVERT
    3.81 -    fprintf(stderr, "Upsample (x2), %d channels.\n", channels);
    3.82 +    fprintf(stderr, "Upsample (x%d), %d channels.\n", multiple, channels);
    3.83  #endif
    3.84  
    3.85      SDL_assert(channels <= 8);
    3.86 +
    3.87      SDL_memcpy(last_sample, src, cpy);
    3.88  
    3.89      while (dst > target) {
    3.90 +        SDL_assert(src >= buf);
    3.91 +
    3.92          for (i = 0; i < channels; i++) {
    3.93              dst[i] = (float) ((((double)src[i]) + ((double)last_sample[i])) * 0.5);
    3.94          }
    3.95          dst -= channels;
    3.96 -        SDL_memcpy(dst, src, cpy);
    3.97 -        SDL_memcpy(last_sample, src, cpy);
    3.98 +
    3.99 +        for (i = 1; i < multiple; i++) {
   3.100 +            SDL_memcpy(dst, dst + channels, cpy);
   3.101 +            dst -= channels;
   3.102 +        }
   3.103 +
   3.104          src -= channels;
   3.105 -        dst -= channels;
   3.106 +        if (src > buf) {
   3.107 +            SDL_memcpy(last_sample, src - channels, cpy);
   3.108 +        }
   3.109      }
   3.110  
   3.111      cvt->len_cvt = dstsize;
   3.112 @@ -338,51 +355,9 @@
   3.113  }
   3.114  
   3.115  void
   3.116 -SDL_Upsample_x4(SDL_AudioCVT *cvt, const int channels)
   3.117 +SDL_Downsample_Multiple(SDL_AudioCVT *cvt, const int channels)
   3.118  {
   3.119 -    const int dstsize = cvt->len_cvt * 4;
   3.120 -    float *dst = ((float *) (cvt->buf + dstsize)) - (channels * 4);
   3.121 -    const float *src = ((float *) (cvt->buf + cvt->len_cvt)) - channels;
   3.122 -    const float *target = ((const float *) cvt->buf);
   3.123 -    const size_t cpy = sizeof (float) * channels;
   3.124 -    float last_sample[8];
   3.125 -    int i;
   3.126 -
   3.127 -#if DEBUG_CONVERT
   3.128 -    fprintf(stderr, "Upsample (x4), %d channels.\n", channels);
   3.129 -#endif
   3.130 -
   3.131 -    SDL_assert(channels <= 8);
   3.132 -    SDL_memcpy(last_sample, src, cpy);
   3.133 -
   3.134 -    while (dst > target) {
   3.135 -        for (i = 0; i < channels; i++) {
   3.136 -            dst[i] = (float) ((((double) src[i]) + (3.0 * ((double) last_sample[i]))) * 0.25);
   3.137 -        }
   3.138 -        dst -= channels;
   3.139 -        for (i = 0; i < channels; i++) {
   3.140 -            dst[i] = (float) ((((double) src[i]) + ((double) last_sample[i])) * 0.25);
   3.141 -        }
   3.142 -        dst -= channels;
   3.143 -        for (i = 0; i < channels; i++) {
   3.144 -            dst[i] = (float) (((3.0 * ((double) src[i])) + ((double) last_sample[i])) * 0.25);
   3.145 -        }
   3.146 -        dst -= channels;
   3.147 -        SDL_memcpy(dst, src, cpy);
   3.148 -        dst -= channels;
   3.149 -        SDL_memcpy(last_sample, src, cpy);
   3.150 -        src -= channels;
   3.151 -    }
   3.152 -
   3.153 -    cvt->len_cvt = dstsize;
   3.154 -    if (cvt->filters[++cvt->filter_index]) {
   3.155 -        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
   3.156 -    }
   3.157 -}
   3.158 -
   3.159 -void
   3.160 -SDL_Downsample_Multiple(SDL_AudioCVT *cvt, const int multiple, const int channels)
   3.161 -{
   3.162 +    const int multiple = (int) (1.0 / cvt->rate_incr);
   3.163      const int dstsize = cvt->len_cvt / multiple;
   3.164      float *dst = (float *) cvt->buf;
   3.165      const float *src = (float *) cvt->buf;