From 202ab30c16a2fca33755f9913518566641a434e2 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 22 Jan 2017 20:27:48 -0500 Subject: [PATCH] audio: Special case for resampling stereo AUDIO_S16SYS audio data. This is a fairly common case, so we avoid the conversion to/from float here. --- src/audio/SDL_audiocvt.c | 109 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/src/audio/SDL_audiocvt.c b/src/audio/SDL_audiocvt.c index 7e5251c6aecc4..85196b141056d 100644 --- a/src/audio/SDL_audiocvt.c +++ b/src/audio/SDL_audiocvt.c @@ -301,6 +301,91 @@ SDL_ResampleAudioSimple(const int chans, const double rate_incr, return (int) ((dst - outbuf) * ((int) sizeof (float))); } +/* We keep one special-case fast path around for an extremely common audio format. */ +static int +SDL_ResampleAudioSimple_si16_c2(const double rate_incr, + Sint16 *last_sample, const Sint16 *inbuf, + const int inbuflen, Sint16 *outbuf, const int outbuflen) +{ + const int chans = 2; + const int framelen = 4; /* stereo 16 bit */ + const int total = (inbuflen / framelen); + const int finalpos = (total * chans) - chans; + const int dest_samples = (int)(((double)total) * rate_incr); + const double src_incr = 1.0 / rate_incr; + Sint16 *dst; + double idx; + + SDL_assert((dest_samples * framelen) <= outbuflen); + SDL_assert((inbuflen % framelen) == 0); + + if (rate_incr > 1.0) { + Sint16 *target = (outbuf + chans); + const Sint16 final_right = inbuf[finalpos+1]; + const Sint16 final_left = inbuf[finalpos]; + Sint16 earlier_right = inbuf[finalpos-1]; + Sint16 earlier_left = inbuf[finalpos-2]; + dst = outbuf + (dest_samples * chans); + idx = (double) total; + + while (dst > target) { + const int pos = ((int) idx) * chans; + const Sint16 *src = &inbuf[pos]; + const Sint16 right = *(--src); + const Sint16 left = *(--src); + SDL_assert(pos >= 0.0); + *(--dst) = (((Sint32) right) + ((Sint32) earlier_right)) >> 1; + *(--dst) = (((Sint32) left) + ((Sint32) earlier_left)) >> 1; + earlier_right = right; + earlier_left = left; + idx -= src_incr; + } + + /* do last sample, interpolated against previous run's state. */ + *(--dst) = (((Sint32) inbuf[1]) + ((Sint32) last_sample[1])) >> 1; + *(--dst) = (((Sint32) inbuf[0]) + ((Sint32) last_sample[0])) >> 1; + last_sample[1] = final_right; + last_sample[0] = final_left; + + dst = (outbuf + (dest_samples * chans)) - 1; + } else { + Sint16 *target = (outbuf + (dest_samples * chans)); + dst = outbuf; + idx = 0.0; + while (dst < target) { + const int pos = ((int) idx) * chans; + const Sint16 *src = &inbuf[pos]; + const Sint16 left = *(src++); + const Sint16 right = *(src++); + SDL_assert(pos <= finalpos); + *(dst++) = (((Sint32) left) + ((Sint32) last_sample[0])) >> 1; + *(dst++) = (((Sint32) right) + ((Sint32) last_sample[1])) >> 1; + last_sample[0] = left; + last_sample[1] = right; + idx += src_incr; + } + } + + return (int) ((dst - outbuf) * ((int) sizeof (Sint16))); +} + +static void SDLCALL +SDL_ResampleCVT_si16_c2(SDL_AudioCVT *cvt, SDL_AudioFormat format) +{ + const Sint16 *src = (const Sint16 *) cvt->buf; + const int srclen = cvt->len_cvt; + Sint16 *dst = (Sint16 *) (cvt->buf + srclen); + const int dstlen = (cvt->len * cvt->len_mult) - srclen; + Sint16 state[2] = { src[0], src[1] }; + + SDL_assert(format == AUDIO_S16SYS); + + cvt->len_cvt = SDL_ResampleAudioSimple_si16_c2(cvt->rate_incr, state, src, srclen, dst, dstlen); + if (cvt->filters[++cvt->filter_index]) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + int SDL_ConvertAudio(SDL_AudioCVT * cvt) @@ -575,6 +660,30 @@ SDL_BuildAudioCVT(SDL_AudioCVT * cvt, cvt->len_ratio = 1.0; cvt->rate_incr = ((double) dst_rate) / ((double) src_rate); + /* SDL now favors float32 as its preferred internal format, and considers + everything else to be a degenerate case that we might have to make + multiple passes over the data to convert to and from float32 as + necessary. That being said, we keep one special case around for + efficiency: stereo data in Sint16 format, in the native byte order, + that only needs resampling. This is likely to be the most popular + legacy format, that apps, hardware and the OS are likely to be able + to process directly, so we handle this one case directly without + unnecessary conversions. This means that apps on embedded devices + without floating point hardware should consider aiming for this + format as well. */ + if ((src_channels == 2) && (dst_channels == 2) && (src_fmt == AUDIO_S16SYS) && (dst_fmt == AUDIO_S16SYS) && (src_rate != dst_rate)) { + cvt->needed = 1; + cvt->filters[cvt->filter_index++] = SDL_ResampleCVT_si16_c2; + if (src_rate < dst_rate) { + const double mult = ((double) dst_rate) / ((double) src_rate); + cvt->len_mult *= (int) SDL_ceil(mult); + cvt->len_ratio *= mult; + } else { + cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate); + } + return 1; + } + /* Type conversion goes like this now: - byteswap to CPU native format first if necessary. - convert to native Float32 if necessary.