Browse Source

Pre-calculate resampling rate, and use it instead of .freq in most places

Brick 1 year ago
parent
commit
383084e0ad
2 changed files with 54 additions and 53 deletions
  1. 53 53
      src/audio/SDL_audiocvt.c
  2. 1 0
      src/audio/SDL_sysaudio.h

+ 53 - 53
src/audio/SDL_audiocvt.c

@@ -32,21 +32,34 @@
 
 #include "SDL_audio_resampler_filter.h"
 
-static Sint64 GetResampleRate(const int inrate, const int outrate)
+static Sint64 GetResampleRate(const int src_rate, const int dst_rate)
 {
-    return ((Sint64)inrate << 32) / (Sint64)outrate;
+    SDL_assert(src_rate > 0);
+    SDL_assert(dst_rate > 0);
+
+    if (src_rate == dst_rate)
+        return 0;
+
+    return ((Sint64)src_rate << 32) / (Sint64)dst_rate;
 }
 
-static int GetResamplerPaddingFrames(const int inrate, const int outrate)
+static size_t GetResamplerAvailableOutputFrames(const size_t input_frames, const Sint64 resample_rate, const Sint64 resample_offset)
 {
-    SDL_assert(inrate > 0);
-    SDL_assert(outrate > 0);
+    SDL_assert(resample_offset < resample_rate);
 
-    if (inrate == outrate) {
-        return 0;
-    }
+    return (size_t)((((Sint64)input_frames << 32) - resample_offset + resample_rate - 1) / resample_rate);
+}
+
+static int GetResamplerNeededInputFrames(const int output_frames, const Sint64 resample_rate, const Sint64 resample_offset)
+{
+    const Sint64 lastpos = ((output_frames - 1) * resample_rate) + resample_offset;
 
-    return RESAMPLER_ZERO_CROSSINGS + 1;
+    return (int)(Sint32)(lastpos >> 32) + 1;
+}
+
+static int GetResamplerPaddingFrames(const Sint64 resample_rate)
+{
+    return resample_rate ? (RESAMPLER_ZERO_CROSSINGS + 1) : 0;
 }
 
 static int GetHistoryBufferSampleFrames(const Sint32 required_resampler_frames)
@@ -63,23 +76,22 @@ static int GetHistoryBufferSampleFrames(const Sint32 required_resampler_frames)
     return (int) SDL_max(required_resampler_frames, 5000);
 }
 
-// lpadding and rpadding are expected to be buffers of (GetResamplePadding(inrate, outrate) * chans * sizeof (float)) bytes.
-static void ResampleAudio(const int chans, const int inrate, const int outrate,
-                         const float *lpadding, const float *rpadding,
-                         const float *inbuf, const int inframes,
-                         float *outbuf, const int outframes, Sint64* resample_offset)
+// lpadding and rpadding are expected to be buffers of (GetResamplerPaddingFrames(resample_rate) * chans * sizeof (float)) bytes.
+static void ResampleAudio(const int chans, const float *lpadding, const float *rpadding,
+                         const float *inbuf, const int inframes, float *outbuf, const int outframes,
+                         const Sint64 resample_rate, Sint64* resample_offset)
 {
-    const int paddinglen = GetResamplerPaddingFrames(inrate, outrate);
+    SDL_assert(resample_rate != 0);
+    const int paddinglen = GetResamplerPaddingFrames(resample_rate);
     float *dst = outbuf;
     int i, j, chan;
 
-    const Sint64 srcstep = GetResampleRate(inrate, outrate);
     Sint64 srcpos = *resample_offset;
 
     for (i = 0; i < outframes; i++) {
         int srcindex = (int)(Sint32)(srcpos >> 32);
         Uint32 srcfraction = (Uint32)(srcpos & 0xFFFFFFFF);
-        srcpos += srcstep;
+        srcpos += resample_rate;
 
         SDL_assert(srcindex >= -1);
         SDL_assert(srcindex < inframes);
@@ -461,7 +473,8 @@ static int SetAudioStreamFormat(SDL_AudioStream *stream, const SDL_AudioSpec *sr
     const int max_sample_frame_size = CalculateMaxSampleFrameSize(src_format, src_channels, dst_format, dst_channels);
     const int prev_history_buffer_frames = stream->history_buffer_frames;
     const int pre_resample_channels = SDL_min(src_channels, dst_channels);
-    const int resampler_padding_frames = GetResamplerPaddingFrames(src_rate, dst_rate);
+    const Sint64 resample_rate = GetResampleRate(src_rate, dst_rate);
+    const int resampler_padding_frames = GetResamplerPaddingFrames(resample_rate);
     const size_t resampler_padding_allocation = ((size_t) resampler_padding_frames) * max_sample_frame_size;
     const size_t future_buffer_allocation = resampler_padding_allocation;
     const int history_buffer_frames = GetHistoryBufferSampleFrames(resampler_padding_frames);
@@ -553,6 +566,7 @@ static int SetAudioStreamFormat(SDL_AudioStream *stream, const SDL_AudioSpec *sr
     stream->src_sample_frame_size = src_sample_frame_size;
     stream->dst_sample_frame_size = dst_sample_frame_size;
     stream->pre_resample_channels = pre_resample_channels;
+    stream->resample_rate = resample_rate;
 
     if (src_spec != &stream->src_spec) {
         SDL_memcpy(&stream->src_spec, src_spec, sizeof (SDL_AudioSpec));
@@ -814,11 +828,10 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int le
     const int max_available = SDL_GetAudioStreamAvailable(stream);
     const SDL_AudioFormat src_format = stream->src_spec.format;
     const int src_channels = stream->src_spec.channels;
-    const int src_rate = stream->src_spec.freq;
     const int src_sample_frame_size = stream->src_sample_frame_size;
     const SDL_AudioFormat dst_format = stream->dst_spec.format;
     const int dst_channels = stream->dst_spec.channels;
-    const int dst_rate = stream->dst_spec.freq;
+    const Sint64 resample_rate = stream->resample_rate;
     const int dst_sample_frame_size = stream->dst_sample_frame_size;
     const int max_sample_frame_size = stream->max_sample_frame_size;
     const int pre_resample_channels = stream->pre_resample_channels;
@@ -852,11 +865,8 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int le
     // figure out how much data we need to fulfill the request.
     input_frames = output_frames;  // total sample frames caller wants
 
-    if (dst_rate != src_rate) {
-        // Make sure this matches the logic used in ResampleAudio
-        const Sint64 srcstep = GetResampleRate(src_rate, dst_rate);
-        const Sint64 lastpos = ((output_frames - 1) * srcstep) + stream->resample_offset;
-        input_frames = (int)(Sint32)(lastpos >> 32) + 1;
+    if (resample_rate) {
+        input_frames = GetResamplerNeededInputFrames(output_frames, resample_rate, stream->resample_offset);
 
         if (input_frames == 0) {  // uhoh, not enough input frames!
             // if they are upsampling and we end up needing less than a frame of input, we reject it because it would cause artifacts on future reads to eat a full input frame.
@@ -906,7 +916,6 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int le
         br = (int) SDL_ReadFromDataQueue(stream->queue, workbuf + workbufpos, request_bytes);
         read_frames = br / src_sample_frame_size;
         workbuf_frames += read_frames;
-        input_frames = workbuf_frames; // what we actually have to work with
     }
 
     // for some resamples, we need to fill up the future buffer, too, to use as right padding.
@@ -926,7 +935,6 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int le
                 const int stealcpy = stealcpyframes * src_sample_frame_size;
                 SDL_memcpy(future_buffer + (future_buffer_filled_frames * src_sample_frame_size), workbuf + ((workbuf_frames - stealcpyframes) * src_sample_frame_size), stealcpy);
                 workbuf_frames -= stealcpyframes;
-                input_frames = workbuf_frames;  // what we actually have to work with, now
                 future_buffer_filled_frames += stealcpyframes;
                 SDL_assert(future_buffer_filled_frames <= resampler_padding_frames);
             }
@@ -939,7 +947,7 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int le
     if (resampler_padding_frames > 0) {
         const int history_buffer_bytes = history_buffer_frames * src_sample_frame_size;
         const int resampler_padding_bytes = resampler_padding_frames * src_sample_frame_size;
-        SDL_assert(src_rate != dst_rate);
+        SDL_assert(resample_rate != 0);
         SDL_assert(history_buffer_bytes >= resampler_padding_bytes);
         ConvertAudio(resampler_padding_frames, history_buffer + (history_buffer_bytes - resampler_padding_bytes), src_format, src_channels, stream->left_padding, SDL_AUDIO_F32SYS, pre_resample_channels);
         ConvertAudio(resampler_padding_frames, future_buffer, src_format, src_channels, stream->right_padding, SDL_AUDIO_F32SYS, pre_resample_channels);
@@ -948,8 +956,8 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int le
     // slide in new data to the history buffer, shuffling out the oldest, for the next run, since we've already updated left_padding with current data.
     {
         const int history_buffer_bytes = history_buffer_frames * src_sample_frame_size;
-        const int request_bytes = input_frames * src_sample_frame_size;
-        if (history_buffer_frames > input_frames) {
+        const int request_bytes = workbuf_frames * src_sample_frame_size;
+        if (history_buffer_frames > workbuf_frames) {
             const int preserve_bytes = history_buffer_bytes - request_bytes;
             SDL_memmove(history_buffer, history_buffer + request_bytes, preserve_bytes);
             SDL_memcpy(history_buffer + preserve_bytes, workbuf, request_bytes);
@@ -959,20 +967,20 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int le
     }
 
     // Not resampling? It's an easy conversion (and maybe not even that!)
-    if (src_rate == dst_rate) {
+    if (resample_rate == 0) {
         SDL_assert(resampler_padding_frames == 0);
         // see if we can do the conversion in-place (will fit in `buf` while in-progress), or if we need to do it in the workbuf and copy it over
         if (max_sample_frame_size <= dst_sample_frame_size) {
-            ConvertAudio(input_frames, workbuf, src_format, src_channels, buf, dst_format, dst_channels);
+            ConvertAudio(workbuf_frames, workbuf, src_format, src_channels, buf, dst_format, dst_channels);
         } else {
-            ConvertAudio(input_frames, workbuf, src_format, src_channels, workbuf, dst_format, dst_channels);
-            SDL_memcpy(buf, workbuf, input_frames * dst_sample_frame_size);
+            ConvertAudio(workbuf_frames, workbuf, src_format, src_channels, workbuf, dst_format, dst_channels);
+            SDL_memcpy(buf, workbuf, workbuf_frames * dst_sample_frame_size);
         }
-        return input_frames * dst_sample_frame_size;
+        return workbuf_frames * dst_sample_frame_size;
     }
 
     // Resampling! get the work buffer to float32 format, etc, in-place.
-    ConvertAudio(input_frames, workbuf, src_format, src_channels, workbuf, SDL_AUDIO_F32SYS, pre_resample_channels);
+    ConvertAudio(workbuf_frames, workbuf, src_format, src_channels, workbuf, SDL_AUDIO_F32SYS, pre_resample_channels);
 
     if ((dst_format == SDL_AUDIO_F32SYS) && (dst_channels == pre_resample_channels)) {
         resample_outbuf = (float *) buf;
@@ -981,12 +989,9 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int le
         resample_outbuf = (float *) ((workbuf + stream->work_buffer_allocation) - output_bytes);  // do at the end of the buffer so we have room for final convert at front.
     }
 
-    // SDL_Log("IN: %i, WORK: %i, OUT: %i", input_frames, workbuf_frames, output_frames);
-
-    ResampleAudio(pre_resample_channels, src_rate, dst_rate,
-                  stream->left_padding, stream->right_padding,
-                  (const float *) workbuf, input_frames,
-                  resample_outbuf, output_frames, &stream->resample_offset);
+    ResampleAudio(pre_resample_channels, stream->left_padding, stream->right_padding,
+                  (const float *) workbuf, workbuf_frames, resample_outbuf, output_frames,
+                  stream->resample_rate, &stream->resample_offset);
 
     // Get us to the final format!
     // see if we can do the conversion in-place (will fit in `buf` while in-progress), or if we need to do it in the workbuf and copy it over
@@ -1026,9 +1031,9 @@ int SDL_GetAudioStreamData(SDL_AudioStream *stream, void *voidbuf, int len)
     // give the callback a chance to fill in more stream data if it wants.
     if (stream->get_callback) {
         int approx_request = len / stream->dst_sample_frame_size;  // start with sample frames desired
-        if (stream->src_spec.freq != stream->dst_spec.freq) {
-            // calculate difference in dataset size after resampling. Use a Uint64 so the multiplication doesn't overflow.
-            approx_request = (int) (size_t) ((((Uint64) approx_request) * stream->src_spec.freq) / stream->dst_spec.freq);
+        if (stream->resample_rate) {
+            approx_request = GetResamplerNeededInputFrames(approx_request, stream->resample_rate, stream->resample_offset);
+
             if (!stream->flushed) {  // do we need to fill the future buffer to accommodate this, too?
                 approx_request += stream->resampler_padding_frames - stream->future_buffer_filled_frames;
             }
@@ -1042,8 +1047,6 @@ int SDL_GetAudioStreamData(SDL_AudioStream *stream, void *voidbuf, int len)
         }
     }
 
-    // we convert in chunks, so we don't end up allocating a massive work buffer, etc.
-#if 1  // !!! FIXME: see https://github.com/libsdl-org/SDL/issues/8036#issuecomment-1680708349
     int retval = 0;
     while (len > 0) { // didn't ask for a whole sample frame, nothing to do
         const int chunk_size = 1024 * 1024;  // !!! FIXME: a megabyte might be overly-aggressive.
@@ -1069,9 +1072,6 @@ int SDL_GetAudioStreamData(SDL_AudioStream *stream, void *voidbuf, int len)
             }
         }
     }
-#else
-    const int retval = GetAudioStreamDataInternal(stream, buf, len);
-#endif
 
     SDL_UnlockMutex(stream->lock);
 
@@ -1099,13 +1099,13 @@ int SDL_GetAudioStreamAvailable(SDL_AudioStream *stream)
     count += stream->future_buffer_filled_frames;
 
     // sample frames after resampling
-    if (stream->src_spec.freq != stream->dst_spec.freq) {
+    if (stream->resample_rate) {
         if (!stream->flushed) {
             // have to save some samples for padding. They aren't available until more data is added or the stream is flushed.
             count = (count < ((size_t) stream->resampler_padding_frames)) ? 0 : (count - stream->resampler_padding_frames);
         }
-        // calculate difference in dataset size after resampling. Use a Uint64 so the multiplication doesn't overflow.
-        count = (size_t) ((((Uint64) count) * stream->dst_spec.freq) / stream->src_spec.freq);
+
+        count = GetResamplerAvailableOutputFrames(count, stream->resample_rate, stream->resample_offset);
     }
 
     // convert from sample frames to bytes in destination format.

+ 1 - 0
src/audio/SDL_sysaudio.h

@@ -183,6 +183,7 @@ struct SDL_AudioStream
 
     SDL_AudioSpec src_spec;
     SDL_AudioSpec dst_spec;
+    Sint64 resample_rate;
 
     int src_sample_frame_size;
     int dst_sample_frame_size;