src/audio/SDL_audiocvt.c
changeset 10773 016a40fda1bb
parent 10767 b6046389b839
child 10776 7e935b2c430f
     1.1 --- a/src/audio/SDL_audiocvt.c	Fri Jan 06 00:47:42 2017 -0800
     1.2 +++ b/src/audio/SDL_audiocvt.c	Fri Jan 06 02:16:26 2017 -0800
     1.3 @@ -25,9 +25,14 @@
     1.4  #include "SDL_audio.h"
     1.5  #include "SDL_audio_c.h"
     1.6  
     1.7 +#include "SDL_loadso.h"
     1.8  #include "SDL_assert.h"
     1.9  #include "../SDL_dataqueue.h"
    1.10  
    1.11 +#ifdef HAVE_LIBSAMPLERATE
    1.12 +#include "samplerate.h"
    1.13 +#endif
    1.14 +
    1.15  
    1.16  /* Effectively mix right and left channels into a single channel */
    1.17  static void SDLCALL
    1.18 @@ -598,6 +603,9 @@
    1.19      return (cvt->needed);
    1.20  }
    1.21  
    1.22 +typedef int (*SDL_ResampleAudioStreamFunc)(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen);
    1.23 +typedef void (*SDL_ResetAudioStreamResamplerFunc)(SDL_AudioStream *stream);
    1.24 +typedef void (*SDL_CleanupAudioStreamResamplerFunc)(SDL_AudioStream *stream);
    1.25  
    1.26  struct SDL_AudioStream
    1.27  {
    1.28 @@ -618,10 +626,202 @@
    1.29      int dst_rate;
    1.30      double rate_incr;
    1.31      Uint8 pre_resample_channels;
    1.32 +    int packetlen;
    1.33 +    void *resampler_state;
    1.34 +    SDL_ResampleAudioStreamFunc resampler_func;
    1.35 +    SDL_ResetAudioStreamResamplerFunc reset_resampler_func;
    1.36 +    SDL_CleanupAudioStreamResamplerFunc cleanup_resampler_func;
    1.37 +};
    1.38 +
    1.39 +#ifdef HAVE_LIBSAMPLERATE
    1.40 +
    1.41 +typedef struct
    1.42 +{
    1.43 +    void *SRC_lib;
    1.44 +
    1.45 +    SRC_STATE* (*src_new)(int converter_type, int channels, int *error);
    1.46 +    int (*src_process)(SRC_STATE *state, SRC_DATA *data);
    1.47 +    int (*src_reset)(SRC_STATE *state);
    1.48 +    SRC_STATE* (*src_delete)(SRC_STATE *state);
    1.49 +    const char* (*src_strerror)(int error);
    1.50 +
    1.51 +    SRC_STATE *SRC_state;
    1.52 +} SDL_AudioStreamResamplerState_SRC;
    1.53 +
    1.54 +static SDL_bool
    1.55 +LoadLibSampleRate(SDL_AudioStreamResamplerState_SRC *state)
    1.56 +{
    1.57 +#ifdef LIBSAMPLERATE_DYNAMIC
    1.58 +    state->SRC_lib = SDL_LoadObject(LIBSAMPLERATE_DYNAMIC);
    1.59 +    if (!state->SRC_lib) {
    1.60 +        return SDL_FALSE;
    1.61 +    }
    1.62 +#endif
    1.63 +
    1.64 +    state->src_new = (SRC_STATE* (*)(int converter_type, int channels, int *error))SDL_LoadFunction(state->SRC_lib, "src_new");
    1.65 +    state->src_process = (int (*)(SRC_STATE *state, SRC_DATA *data))SDL_LoadFunction(state->SRC_lib, "src_process");
    1.66 +    state->src_reset = (int(*)(SRC_STATE *state))SDL_LoadFunction(state->SRC_lib, "src_reset");
    1.67 +    state->src_delete = (SRC_STATE* (*)(SRC_STATE *state))SDL_LoadFunction(state->SRC_lib, "src_delete");
    1.68 +    state->src_strerror = (const char* (*)(int error))SDL_LoadFunction(state->SRC_lib, "src_strerror");
    1.69 +    if (!state->src_new || !state->src_process || !state->src_reset || !state->src_delete || !state->src_strerror) {
    1.70 +        return SDL_FALSE;
    1.71 +    }
    1.72 +    return SDL_TRUE;
    1.73 +}
    1.74 +
    1.75 +static int
    1.76 +SDL_ResampleAudioStream_SRC(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen)
    1.77 +{
    1.78 +    SDL_AudioStreamResamplerState_SRC *state = (SDL_AudioStreamResamplerState_SRC*)stream->resampler_state;
    1.79 +    SRC_DATA data;
    1.80 +    int result;
    1.81 +
    1.82 +    data.data_in = inbuf;
    1.83 +    data.input_frames = inbuflen / ( sizeof(float) * stream->pre_resample_channels );
    1.84 +    data.input_frames_used = 0;
    1.85 +
    1.86 +    data.data_out = outbuf;
    1.87 +    data.output_frames = outbuflen / (sizeof(float) * stream->pre_resample_channels);
    1.88 +
    1.89 +    data.end_of_input = 0;
    1.90 +    data.src_ratio = stream->rate_incr;
    1.91 +
    1.92 +    result = state->src_process(state->SRC_state, &data);
    1.93 +    if (result != 0) {
    1.94 +        SDL_SetError("src_process() failed: %s", state->src_strerror(result));
    1.95 +        return 0;
    1.96 +    }
    1.97 +
    1.98 +    /* If this fails, we need to store them off somewhere */
    1.99 +    SDL_assert(data.input_frames_used == data.input_frames);
   1.100 +
   1.101 +    return data.output_frames_gen * (sizeof(float) * stream->pre_resample_channels);
   1.102 +}
   1.103 +
   1.104 +static void
   1.105 +SDL_ResetAudioStreamResampler_SRC(SDL_AudioStream *stream)
   1.106 +{
   1.107 +    SDL_AudioStreamResamplerState_SRC *state = (SDL_AudioStreamResamplerState_SRC*)stream->resampler_state;
   1.108 +    state->src_reset(state->SRC_state);
   1.109 +}
   1.110 +
   1.111 +static void
   1.112 +SDL_CleanupAudioStreamResampler_SRC(SDL_AudioStream *stream)
   1.113 +{
   1.114 +    SDL_AudioStreamResamplerState_SRC *state = (SDL_AudioStreamResamplerState_SRC*)stream->resampler_state;
   1.115 +    if (state) {
   1.116 +        if (state->SRC_lib) {
   1.117 +            SDL_UnloadObject(state->SRC_lib);
   1.118 +        }
   1.119 +        state->src_delete(state->SRC_state);
   1.120 +        SDL_free(state);
   1.121 +    }
   1.122 +
   1.123 +    stream->resampler_state = NULL;
   1.124 +    stream->resampler_func = NULL;
   1.125 +    stream->reset_resampler_func = NULL;
   1.126 +    stream->cleanup_resampler_func = NULL;
   1.127 +}
   1.128 +
   1.129 +static SDL_bool
   1.130 +SetupLibSampleRateResampling(SDL_AudioStream *stream)
   1.131 +{
   1.132 +    int result;
   1.133 +
   1.134 +    SDL_AudioStreamResamplerState_SRC *state = (SDL_AudioStreamResamplerState_SRC *)SDL_calloc(1, sizeof(*state));
   1.135 +    if (!state) {
   1.136 +        return SDL_FALSE;
   1.137 +    }
   1.138 +
   1.139 +    if (!LoadLibSampleRate(state)) {
   1.140 +        SDL_free(state);
   1.141 +        return SDL_FALSE;
   1.142 +    }
   1.143 +
   1.144 +    stream->resampler_state = state;
   1.145 +    stream->resampler_func = SDL_ResampleAudioStream_SRC;
   1.146 +    stream->reset_resampler_func = SDL_ResetAudioStreamResampler_SRC;
   1.147 +    stream->cleanup_resampler_func = SDL_CleanupAudioStreamResampler_SRC;
   1.148 +
   1.149 +    state->SRC_state = state->src_new(SRC_SINC_FASTEST, stream->pre_resample_channels, &result);
   1.150 +    if (!state->SRC_state) {
   1.151 +        SDL_SetError("src_new() failed: %s", state->src_strerror(result));
   1.152 +        SDL_CleanupAudioStreamResampler_SRC(stream);
   1.153 +        return SDL_FALSE;
   1.154 +    }
   1.155 +    return SDL_TRUE;
   1.156 +}
   1.157 +
   1.158 +#endif /* HAVE_LIBSAMPLERATE */
   1.159 +
   1.160 +typedef struct
   1.161 +{
   1.162      SDL_bool resampler_seeded;
   1.163      float resampler_state[8];
   1.164 -    int packetlen;
   1.165 -};
   1.166 +} SDL_AudioStreamResamplerState;
   1.167 +
   1.168 +static int
   1.169 +SDL_ResampleAudioStream(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen)
   1.170 +{
   1.171 +    /* !!! FIXME: this resampler sucks, but not much worse than our usual resampler.  :)  */  /* ... :( */
   1.172 +    SDL_AudioStreamResamplerState *state = (SDL_AudioStreamResamplerState*)stream->resampler_state;
   1.173 +    const int chans = (int)stream->pre_resample_channels;
   1.174 +    const int framelen = chans * sizeof(float);
   1.175 +    const int total = (inbuflen / framelen);
   1.176 +    const int finalpos = total - chans;
   1.177 +    const double src_incr = 1.0 / stream->rate_incr;
   1.178 +    double idx = 0.0;
   1.179 +    float *dst = outbuf;
   1.180 +    float last_sample[SDL_arraysize(state->resampler_state)];
   1.181 +    int consumed = 0;
   1.182 +    int i;
   1.183 +
   1.184 +    SDL_assert(chans <= SDL_arraysize(last_sample));
   1.185 +    SDL_assert((inbuflen % framelen) == 0);
   1.186 +
   1.187 +    if (!state->resampler_seeded) {
   1.188 +        for (i = 0; i < chans; i++) {
   1.189 +            state->resampler_state[i] = inbuf[i];
   1.190 +        }
   1.191 +        state->resampler_seeded = SDL_TRUE;
   1.192 +    }
   1.193 +
   1.194 +    for (i = 0; i < chans; i++) {
   1.195 +        last_sample[i] = state->resampler_state[i];
   1.196 +    }
   1.197 +
   1.198 +    while (consumed < total) {
   1.199 +        const int pos = ((int)idx) * chans;
   1.200 +        const float *src = &inbuf[(pos >= finalpos) ? finalpos : pos];
   1.201 +        SDL_assert(dst < (outbuf + (outbuflen / framelen)));
   1.202 +        for (i = 0; i < chans; i++) {
   1.203 +            const float val = *(src++);
   1.204 +            *(dst++) = (val + last_sample[i]) * 0.5f;
   1.205 +            last_sample[i] = val;
   1.206 +        }
   1.207 +        consumed = pos + chans;
   1.208 +        idx += src_incr;
   1.209 +    }
   1.210 +
   1.211 +    for (i = 0; i < chans; i++) {
   1.212 +        state->resampler_state[i] = last_sample[i];
   1.213 +    }
   1.214 +
   1.215 +    return (int)((dst - outbuf) * sizeof(float));
   1.216 +}
   1.217 +
   1.218 +static void
   1.219 +SDL_ResetAudioStreamResampler(SDL_AudioStream *stream)
   1.220 +{
   1.221 +    SDL_AudioStreamResamplerState *state = (SDL_AudioStreamResamplerState*)stream->resampler_state;
   1.222 +    state->resampler_seeded = SDL_FALSE;
   1.223 +}
   1.224 +
   1.225 +static void
   1.226 +SDL_CleanupAudioStreamResampler(SDL_AudioStream *stream)
   1.227 +{
   1.228 +    SDL_free(stream->resampler_state);
   1.229 +}
   1.230  
   1.231  SDL_AudioStream *SDL_NewAudioStream(const SDL_AudioFormat src_format,
   1.232                                      const Uint8 src_channels,
   1.233 @@ -661,84 +861,50 @@
   1.234      if (src_rate == dst_rate) {
   1.235          retval->cvt_before_resampling.needed = SDL_FALSE;
   1.236          retval->cvt_before_resampling.len_mult = 1;
   1.237 -        if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, src_format, src_channels, dst_rate, dst_format, dst_channels, dst_rate) == -1) {
   1.238 -            SDL_free(retval);
   1.239 +        if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, src_format, src_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) {
   1.240 +            SDL_FreeAudioStream(retval);
   1.241              return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
   1.242          }
   1.243      } else {
   1.244          /* Don't resample at first. Just get us to Float32 format. */
   1.245          /* !!! FIXME: convert to int32 on devices without hardware float. */
   1.246 -        if (SDL_BuildAudioCVT(&retval->cvt_before_resampling, src_format, src_channels, src_rate, AUDIO_F32SYS, pre_resample_channels, src_rate) == -1) {
   1.247 -            SDL_free(retval);
   1.248 +        if (SDL_BuildAudioCVT(&retval->cvt_before_resampling, src_format, src_channels, src_rate, AUDIO_F32SYS, pre_resample_channels, src_rate) < 0) {
   1.249 +            SDL_FreeAudioStream(retval);
   1.250              return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
   1.251          }
   1.252  
   1.253 +#ifdef HAVE_LIBSAMPLERATE
   1.254 +        SetupLibSampleRateResampling(retval);
   1.255 +#endif
   1.256 +
   1.257 +        if (!retval->resampler_func) {
   1.258 +            retval->resampler_state = SDL_calloc(1, sizeof(SDL_AudioStreamResamplerState));
   1.259 +            if (!retval->resampler_state) {
   1.260 +                SDL_FreeAudioStream(retval);
   1.261 +                SDL_OutOfMemory();
   1.262 +                return NULL;
   1.263 +            }
   1.264 +            retval->resampler_func = SDL_ResampleAudioStream;
   1.265 +            retval->reset_resampler_func = SDL_ResetAudioStreamResampler;
   1.266 +            retval->cleanup_resampler_func = SDL_CleanupAudioStreamResampler;
   1.267 +        }
   1.268 +
   1.269          /* Convert us to the final format after resampling. */
   1.270 -        if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, AUDIO_F32SYS, pre_resample_channels, dst_rate, dst_format, dst_channels, dst_rate) == -1) {
   1.271 -            SDL_free(retval);
   1.272 +        if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, AUDIO_F32SYS, pre_resample_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) {
   1.273 +            SDL_FreeAudioStream(retval);
   1.274              return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
   1.275          }
   1.276      }
   1.277  
   1.278      retval->queue = SDL_NewDataQueue(packetlen, packetlen * 2);
   1.279      if (!retval->queue) {
   1.280 -        SDL_free(retval);
   1.281 +        SDL_FreeAudioStream(retval);
   1.282          return NULL;  /* SDL_NewDataQueue should have called SDL_SetError. */
   1.283      }
   1.284  
   1.285      return retval;
   1.286  }
   1.287  
   1.288 -
   1.289 -static int
   1.290 -ResampleAudioStream(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen)
   1.291 -{
   1.292 -    /* !!! FIXME: this resampler sucks, but not much worse than our usual resampler.  :)  */  /* ... :( */
   1.293 -    const int chans = (int) stream->pre_resample_channels;
   1.294 -    const int framelen = chans * sizeof (float);
   1.295 -    const int total = (inbuflen / framelen);
   1.296 -    const int finalpos = total - chans;
   1.297 -    const double src_incr = 1.0 / stream->rate_incr;
   1.298 -    double idx = 0.0;
   1.299 -    float *dst = outbuf;
   1.300 -    float last_sample[SDL_arraysize(stream->resampler_state)];
   1.301 -    int consumed = 0;
   1.302 -    int i;
   1.303 -
   1.304 -    SDL_assert(chans <= SDL_arraysize(last_sample));
   1.305 -    SDL_assert((inbuflen % framelen) == 0);
   1.306 -
   1.307 -    if (!stream->resampler_seeded) {
   1.308 -        for (i = 0; i < chans; i++) {
   1.309 -            stream->resampler_state[i] = inbuf[i];
   1.310 -        }
   1.311 -        stream->resampler_seeded = SDL_TRUE;
   1.312 -    }
   1.313 -
   1.314 -    for (i = 0; i < chans; i++) {
   1.315 -        last_sample[i] = stream->resampler_state[i];
   1.316 -    }
   1.317 -
   1.318 -    while (consumed < total) {
   1.319 -        const int pos = ((int) idx) * chans;
   1.320 -        const float *src = &inbuf[(pos >= finalpos) ? finalpos : pos];
   1.321 -        SDL_assert(dst < (outbuf + (outbuflen / framelen)));
   1.322 -        for (i = 0; i < chans; i++) {
   1.323 -            const float val = *(src++);
   1.324 -            *(dst++) = (val + last_sample[i]) * 0.5f;
   1.325 -            last_sample[i] = val;
   1.326 -        }
   1.327 -        consumed = pos + chans;
   1.328 -        idx += src_incr;
   1.329 -    }
   1.330 -
   1.331 -    for (i = 0; i < chans; i++) {
   1.332 -        stream->resampler_state[i] = last_sample[i];
   1.333 -    }
   1.334 -
   1.335 -    return (int) ((dst - outbuf) * sizeof (float));
   1.336 -}
   1.337 -
   1.338  static Uint8 *
   1.339  EnsureBufferSize(Uint8 **buf, int *len, const int newlen)
   1.340  {
   1.341 @@ -791,7 +957,7 @@
   1.342          if (workbuf == NULL) {
   1.343              return -1;  /* probably out of memory. */
   1.344          }
   1.345 -        buflen = ResampleAudioStream(stream, (float *) buf, buflen, workbuf, workbuflen);
   1.346 +        buflen = stream->resampler_func(stream, (float *) buf, buflen, workbuf, workbuflen);
   1.347          buf = workbuf;
   1.348      }
   1.349  
   1.350 @@ -832,7 +998,7 @@
   1.351          SDL_InvalidParamError("stream");
   1.352      } else {
   1.353          SDL_ClearDataQueue(stream->queue, stream->packetlen * 2);
   1.354 -        stream->resampler_seeded = SDL_FALSE;
   1.355 +        stream->reset_resampler_func(stream);
   1.356      }
   1.357  }
   1.358  
   1.359 @@ -866,6 +1032,9 @@
   1.360  SDL_FreeAudioStream(SDL_AudioStream *stream)
   1.361  {
   1.362      if (stream) {
   1.363 +        if (stream->cleanup_resampler_func) {
   1.364 +            stream->cleanup_resampler_func(stream);
   1.365 +        }
   1.366          SDL_FreeDataQueue(stream->queue);
   1.367          SDL_free(stream->work_buffer);
   1.368          SDL_free(stream->resample_buffer);