Added a staging buffer to the audio stream so that we can accumulate small amounts of data if needed when resampling
authorSam Lantinga <slouken@libsdl.org>
Wed, 18 Oct 2017 19:26:36 -0700
changeset 11632385312bd6594
parent 11631 150b925ddfbb
child 11633 37aca00967db
Added a staging buffer to the audio stream so that we can accumulate small amounts of data if needed when resampling
src/audio/SDL_audiocvt.c
     1.1 --- a/src/audio/SDL_audiocvt.c	Wed Oct 18 15:54:05 2017 -0700
     1.2 +++ b/src/audio/SDL_audiocvt.c	Wed Oct 18 19:26:36 2017 -0700
     1.3 @@ -1083,6 +1083,9 @@
     1.4      SDL_AudioCVT cvt_after_resampling;
     1.5      SDL_DataQueue *queue;
     1.6      SDL_bool first_run;
     1.7 +    Uint8 *staging_buffer;
     1.8 +    int staging_buffer_size;
     1.9 +    int staging_buffer_filled;
    1.10      Uint8 *work_buffer_base;  /* maybe unaligned pointer from SDL_realloc(). */
    1.11      int work_buffer_len;
    1.12      int src_sample_frame_size;
    1.13 @@ -1293,7 +1296,17 @@
    1.14          return NULL;
    1.15      }
    1.16  
    1.17 -    /* Not resampling? It's an easy conversion (and maybe not even that!). */
    1.18 +    retval->staging_buffer_size = ((retval->resampler_padding_samples / retval->pre_resample_channels) * retval->src_sample_frame_size);
    1.19 +    if (retval->staging_buffer_size > 0) {
    1.20 +        retval->staging_buffer = (Uint8 *) SDL_malloc(retval->staging_buffer_size);
    1.21 +        if (retval->resampler_padding == NULL) {
    1.22 +            SDL_FreeAudioStream(retval);
    1.23 +            SDL_OutOfMemory();
    1.24 +            return NULL;
    1.25 +        }
    1.26 +    }
    1.27 +
    1.28 +    /* Not resampling? It's an easy conversion (and maybe not even that!) */
    1.29      if (src_rate == dst_rate) {
    1.30          retval->cvt_before_resampling.needed = SDL_FALSE;
    1.31          if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, src_format, src_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) {
    1.32 @@ -1348,8 +1361,8 @@
    1.33      return retval;
    1.34  }
    1.35  
    1.36 -int
    1.37 -SDL_AudioStreamPut(SDL_AudioStream *stream, const void *buf, int len)
    1.38 +static int
    1.39 +SDL_AudioStreamPutInternal(SDL_AudioStream *stream, const void *buf, int len)
    1.40  {
    1.41      int buflen = len;
    1.42      int workbuflen;
    1.43 @@ -1367,36 +1380,11 @@
    1.44         !!! FIXME:  isn't a multiple of 16. In these cases, we should chop off
    1.45         !!! FIXME:  a few samples at the end and convert them separately. */
    1.46  
    1.47 -    #if DEBUG_AUDIOSTREAM
    1.48 -    printf("AUDIOSTREAM: wants to put %d preconverted bytes\n", buflen);
    1.49 -    #endif
    1.50 -
    1.51 -    if (!stream) {
    1.52 -        return SDL_InvalidParamError("stream");
    1.53 -    } else if (!buf) {
    1.54 -        return SDL_InvalidParamError("buf");
    1.55 -    } else if (buflen == 0) {
    1.56 -        return 0;  /* nothing to do. */
    1.57 -    } else if ((buflen % stream->src_sample_frame_size) != 0) {
    1.58 -        return SDL_SetError("Can't add partial sample frames");
    1.59 -    } else if (buflen < ((stream->resampler_padding_samples / stream->pre_resample_channels) * stream->src_sample_frame_size)) {
    1.60 -        return SDL_SetError("Need to put a larger buffer");
    1.61 -    }
    1.62 -
    1.63      /* no padding prepended on first run. */
    1.64      neededpaddingbytes = stream->resampler_padding_samples * sizeof (float);
    1.65      paddingbytes = stream->first_run ? 0 : neededpaddingbytes;
    1.66      stream->first_run = SDL_FALSE;
    1.67  
    1.68 -    if (!stream->cvt_before_resampling.needed &&
    1.69 -        (stream->dst_rate == stream->src_rate) &&
    1.70 -        !stream->cvt_after_resampling.needed) {
    1.71 -        #if DEBUG_AUDIOSTREAM
    1.72 -        printf("AUDIOSTREAM: no conversion needed at all, queueing %d bytes.\n", buflen);
    1.73 -        #endif
    1.74 -        return SDL_WriteToDataQueue(stream->queue, buf, buflen);
    1.75 -    }
    1.76 -
    1.77      /* Make sure the work buffer can hold all the data we need at once... */
    1.78      workbuflen = buflen;
    1.79      if (stream->cvt_before_resampling.needed) {
    1.80 @@ -1495,6 +1483,71 @@
    1.81      return buflen ? SDL_WriteToDataQueue(stream->queue, resamplebuf, buflen) : 0;
    1.82  }
    1.83  
    1.84 +int
    1.85 +SDL_AudioStreamPut(SDL_AudioStream *stream, const void *buf, int len)
    1.86 +{
    1.87 +    /* !!! FIXME: several converters can take advantage of SIMD, but only
    1.88 +       !!! FIXME:  if the data is aligned to 16 bytes. EnsureStreamBufferSize()
    1.89 +       !!! FIXME:  guarantees the buffer will align, but the
    1.90 +       !!! FIXME:  converters will iterate over the data backwards if
    1.91 +       !!! FIXME:  the output grows, and this means we won't align if buflen
    1.92 +       !!! FIXME:  isn't a multiple of 16. In these cases, we should chop off
    1.93 +       !!! FIXME:  a few samples at the end and convert them separately. */
    1.94 +
    1.95 +    #if DEBUG_AUDIOSTREAM
    1.96 +    printf("AUDIOSTREAM: wants to put %d preconverted bytes\n", buflen);
    1.97 +    #endif
    1.98 +
    1.99 +    if (!stream) {
   1.100 +        return SDL_InvalidParamError("stream");
   1.101 +    } else if (!buf) {
   1.102 +        return SDL_InvalidParamError("buf");
   1.103 +    } else if (len == 0) {
   1.104 +        return 0;  /* nothing to do. */
   1.105 +    } else if ((len % stream->src_sample_frame_size) != 0) {
   1.106 +        return SDL_SetError("Can't add partial sample frames");
   1.107 +    }
   1.108 +
   1.109 +    if (!stream->cvt_before_resampling.needed &&
   1.110 +        (stream->dst_rate == stream->src_rate) &&
   1.111 +        !stream->cvt_after_resampling.needed) {
   1.112 +        #if DEBUG_AUDIOSTREAM
   1.113 +        printf("AUDIOSTREAM: no conversion needed at all, queueing %d bytes.\n", len);
   1.114 +        #endif
   1.115 +        return SDL_WriteToDataQueue(stream->queue, buf, len);
   1.116 +    }
   1.117 +
   1.118 +    while (len > 0) {
   1.119 +        int amount;
   1.120 +
   1.121 +        /* If we don't have a staging buffer or we're given enough data that
   1.122 +           we don't need to store it for later, skip the staging process.
   1.123 +         */
   1.124 +        if (!stream->staging_buffer_filled && len >= stream->staging_buffer_size) {
   1.125 +            return SDL_AudioStreamPutInternal(stream, buf, len);
   1.126 +        }
   1.127 +
   1.128 +        /* If there's not enough data to fill the staging buffer, just save it */
   1.129 +        if ((stream->staging_buffer_filled + len) < stream->staging_buffer_size) {
   1.130 +            SDL_memcpy(stream->staging_buffer + stream->staging_buffer_filled, buf, len);
   1.131 +            stream->staging_buffer_filled += len;
   1.132 +            return 0;
   1.133 +        }
   1.134 + 
   1.135 +        /* Fill the staging buffer, process it, and continue */
   1.136 +        amount = (stream->staging_buffer_size - stream->staging_buffer_filled);
   1.137 +        SDL_assert(amount > 0);
   1.138 +        SDL_memcpy(stream->staging_buffer + stream->staging_buffer_filled, buf, amount);
   1.139 +        stream->staging_buffer_filled = 0;
   1.140 +        if (SDL_AudioStreamPutInternal(stream, stream->staging_buffer, stream->staging_buffer_size) < 0) {
   1.141 +            return -1;
   1.142 +        }
   1.143 +        buf = (void *)((Uint8 *)buf + amount);
   1.144 +        len -= amount;
   1.145 +    }
   1.146 +    return 0;
   1.147 +}
   1.148 +
   1.149  /* get converted/resampled data from the stream */
   1.150  int
   1.151  SDL_AudioStreamGet(SDL_AudioStream *stream, void *buf, int len)
   1.152 @@ -1546,6 +1599,7 @@
   1.153              stream->cleanup_resampler_func(stream);
   1.154          }
   1.155          SDL_FreeDataQueue(stream->queue);
   1.156 +        SDL_free(stream->staging_buffer);
   1.157          SDL_free(stream->work_buffer_base);
   1.158          SDL_free(stream->resampler_padding);
   1.159          SDL_free(stream);