Skip to content

Commit

Permalink
audio: Replaced older resamplers in SDL_AudioCVT with the new ones.
Browse files Browse the repository at this point in the history
  • Loading branch information
icculus committed Jan 9, 2017
1 parent a41103b commit 063c9d4
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 288 deletions.
4 changes: 0 additions & 4 deletions src/audio/SDL_audio_c.h
Expand Up @@ -63,10 +63,6 @@ void SDLCALL SDL_Convert_F32_to_U8(SDL_AudioCVT *cvt, SDL_AudioFormat format);
void SDLCALL SDL_Convert_F32_to_S16(SDL_AudioCVT *cvt, SDL_AudioFormat format);
void SDLCALL SDL_Convert_F32_to_U16(SDL_AudioCVT *cvt, SDL_AudioFormat format);
void SDLCALL SDL_Convert_F32_to_S32(SDL_AudioCVT *cvt, SDL_AudioFormat format);
void SDL_Upsample_Arbitrary(SDL_AudioCVT *cvt, const int channels);
void SDL_Upsample_Multiple(SDL_AudioCVT *cvt, const int channels);
void SDL_Downsample_Arbitrary(SDL_AudioCVT *cvt, const int channels);
void SDL_Downsample_Multiple(SDL_AudioCVT *cvt, const int channels);


/* SDL_AudioStream is a new audio conversion interface. It
Expand Down
229 changes: 118 additions & 111 deletions src/audio/SDL_audiocvt.c
Expand Up @@ -191,6 +191,38 @@ SDL_ConvertStereoToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format)
}
}

static int
SDL_ResampleAudioSimple(const int chans, const double rate_incr,
float *last_sample, const float *inbuf,
const int inbuflen, float *outbuf, const int outbuflen)
{
const int framelen = chans * sizeof(float);
const int total = (inbuflen / framelen);
const int finalpos = total - chans;
const double src_incr = 1.0 / rate_incr;
double idx = 0.0;
float *dst = outbuf;
int consumed = 0;
int i;

SDL_assert((inbuflen % framelen) == 0);

while (consumed < total) {
const int pos = ((int)idx) * chans;
const float *src = &inbuf[(pos >= finalpos) ? finalpos : pos];
SDL_assert(dst < (outbuf + (outbuflen / framelen)));
for (i = 0; i < chans; i++) {
const float val = *(src++);
*(dst++) = (val + last_sample[i]) * 0.5f;
last_sample[i] = val;
}
consumed = pos + chans;
idx += src_incr;
}

return (int)((dst - outbuf) * sizeof(float));
}


int
SDL_ConvertAudio(SDL_AudioCVT * cvt)
Expand Down Expand Up @@ -338,31 +370,75 @@ SDL_BuildAudioTypeCVTFromFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat dst_fmt)
return retval;
}

static void
SDL_ResampleCVT(SDL_AudioCVT *cvt, const int chans, const SDL_AudioFormat format)
{
const float *src = (const float *) cvt->buf;
const int srclen = cvt->len_cvt;
float *dst = (float *) (cvt->buf + srclen);
const int dstlen = (cvt->len * cvt->len_mult) - srclen;
SDL_bool do_simple = SDL_TRUE;

SDL_assert(format == AUDIO_F32SYS);

#ifdef HAVE_LIBSAMPLERATE_H
if (SRC_available) {
int result = 0;
SRC_STATE *state = SRC_src_new(SRC_SINC_FASTEST, chans, &result);
if (state) {
const int framelen = sizeof(float) * chans;
SRC_DATA data;

data.data_in = (float *)src; /* Older versions of libsamplerate had a non-const pointer, but didn't write to it */
data.input_frames = srclen / framelen;
data.input_frames_used = 0;

data.data_out = dst;
data.output_frames = dstlen / framelen;

data.end_of_input = 0;
data.src_ratio = cvt->rate_incr;

result = SRC_src_process(state, &data);
SDL_assert(result == 0); /* what to do if this fails? Can it fail? */

/* !!! FIXME: We only have this macro salsa because SDL_AudioCVT doesn't store
!!! FIXME: channel info or integer sample rates, so we have to have
!!! FIXME: function entry points for each supported channel count and
!!! FIXME: multiple vs arbitrary. When we rev the ABI, remove this. */
/* What to do if this fails...? */
SDL_assert(data.input_frames_used == data.input_frames);

SRC_src_delete(state);
cvt->len_cvt = data.output_frames_gen * (sizeof(float) * chans);
do_simple = SDL_FALSE;
}

/* failed to create state? Fall back to simple method. */
}
#endif

if (do_simple) {
float state[8];
int i;

for (i = 0; i < chans; i++) {
state[i] = src[i];
}

cvt->len_cvt = SDL_ResampleAudioSimple(chans, cvt->rate_incr, state, src, srclen, dst, dstlen);
}

SDL_memcpy(cvt->buf, dst, cvt->len_cvt);
if (cvt->filters[++cvt->filter_index]) {
cvt->filters[cvt->filter_index](cvt, format);
}
}

/* !!! FIXME: We only have this macro salsa because SDL_AudioCVT doesn't
!!! FIXME: store channel info, so we have to have function entry
!!! FIXME: points for each supported channel count and multiple
!!! FIXME: vs arbitrary. When we rev the ABI, clean this up. */
#define RESAMPLER_FUNCS(chans) \
static void SDLCALL \
SDL_Upsample_Multiple_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
SDL_assert(format == AUDIO_F32SYS); \
SDL_Upsample_Multiple(cvt, chans); \
} \
static void SDLCALL \
SDL_Upsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
SDL_assert(format == AUDIO_F32SYS); \
SDL_Upsample_Arbitrary(cvt, chans); \
}\
static void SDLCALL \
SDL_Downsample_Multiple_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
SDL_assert(format == AUDIO_F32SYS); \
SDL_Downsample_Multiple(cvt, chans); \
} \
static void SDLCALL \
SDL_Downsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
SDL_assert(format == AUDIO_F32SYS); \
SDL_Downsample_Arbitrary(cvt, chans); \
SDL_ResampleCVT_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
SDL_ResampleCVT(cvt, chans, format); \
}
RESAMPLER_FUNCS(1)
RESAMPLER_FUNCS(2)
Expand All @@ -371,62 +447,19 @@ RESAMPLER_FUNCS(6)
RESAMPLER_FUNCS(8)
#undef RESAMPLER_FUNCS

static int
SDL_FindFrequencyMultiple(const int src_rate, const int dst_rate)
{
int lo, hi;

SDL_assert(src_rate != 0);
SDL_assert(dst_rate != 0);
SDL_assert(src_rate != dst_rate);

if (src_rate < dst_rate) {
lo = src_rate;
hi = dst_rate;
} else {
lo = dst_rate;
hi = src_rate;
}

if ((hi % lo) != 0)
return 0; /* not a multiple. */

return hi / lo;
}

static SDL_AudioFilter
ChooseResampler(const int dst_channels, const int src_rate, const int dst_rate)
ChooseCVTResampler(const int dst_channels)
{
const int upsample = (src_rate < dst_rate) ? 1 : 0;
const int multiple = SDL_FindFrequencyMultiple(src_rate, dst_rate);
SDL_AudioFilter filter = NULL;

#define PICK_CHANNEL_FILTER(upordown, resampler) switch (dst_channels) { \
case 1: filter = SDL_##upordown##_##resampler##_c1; break; \
case 2: filter = SDL_##upordown##_##resampler##_c2; break; \
case 4: filter = SDL_##upordown##_##resampler##_c4; break; \
case 6: filter = SDL_##upordown##_##resampler##_c6; break; \
case 8: filter = SDL_##upordown##_##resampler##_c8; break; \
default: break; \
}

if (upsample) {
if (multiple) {
PICK_CHANNEL_FILTER(Upsample, Multiple);
} else {
PICK_CHANNEL_FILTER(Upsample, Arbitrary);
}
} else {
if (multiple) {
PICK_CHANNEL_FILTER(Downsample, Multiple);
} else {
PICK_CHANNEL_FILTER(Downsample, Arbitrary);
}
switch (dst_channels) {
case 1: return SDL_ResampleCVT_c1;
case 2: return SDL_ResampleCVT_c2;
case 4: return SDL_ResampleCVT_c4;
case 6: return SDL_ResampleCVT_c6;
case 8: return SDL_ResampleCVT_c8;
default: break;
}

#undef PICK_CHANNEL_FILTER

return filter;
return NULL;
}

static int
Expand All @@ -439,7 +472,7 @@ SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, const int dst_channels,
return 0; /* no conversion necessary. */
}

filter = ChooseResampler(dst_channels, src_rate, dst_rate);
filter = ChooseCVTResampler(dst_channels);
if (filter == NULL) {
return SDL_SetError("No conversion available for these rates");
}
Expand All @@ -454,6 +487,10 @@ SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, const int dst_channels,
cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate);
}

/* the buffer is big enough to hold the destination now, but
we need it large enough to hold a separate scratch buffer. */
cvt->len_mult *= 2;

return 1; /* added a converter. */
}

Expand Down Expand Up @@ -638,16 +675,17 @@ struct SDL_AudioStream
static int
SDL_ResampleAudioStream_SRC(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen)
{
const int framelen = sizeof(float) * stream->pre_resample_channels;
SRC_STATE *state = (SRC_STATE *)stream->resampler_state;
SRC_DATA data;
int result;

data.data_in = (float *)inbuf; /* Older versions of libsamplerate had a non-const pointer, but didn't write to it */
data.input_frames = inbuflen / ( sizeof(float) * stream->pre_resample_channels );
data.input_frames = inbuflen / framelen;
data.input_frames_used = 0;

data.data_out = outbuf;
data.output_frames = outbuflen / (sizeof(float) * stream->pre_resample_channels);
data.output_frames = outbuflen / framelen;

data.end_of_input = 0;
data.src_ratio = stream->rate_incr;
Expand Down Expand Up @@ -721,51 +759,20 @@ typedef struct
static int
SDL_ResampleAudioStream(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen)
{
/* !!! FIXME: this resampler sucks, but not much worse than our usual resampler. :) */ /* ... :( */
SDL_AudioStreamResamplerState *state = (SDL_AudioStreamResamplerState*)stream->resampler_state;
const int chans = (int)stream->pre_resample_channels;
const int framelen = chans * sizeof(float);
const int total = (inbuflen / framelen);
const int finalpos = total - chans;
const double src_incr = 1.0 / stream->rate_incr;
double idx = 0.0;
float *dst = outbuf;
float last_sample[SDL_arraysize(state->resampler_state)];
int consumed = 0;
int i;

SDL_assert(chans <= SDL_arraysize(last_sample));
SDL_assert((inbuflen % framelen) == 0);
SDL_assert(chans <= SDL_arraysize(state->resampler_state));

if (!state->resampler_seeded) {
int i;
for (i = 0; i < chans; i++) {
state->resampler_state[i] = inbuf[i];
}
state->resampler_seeded = SDL_TRUE;
}

for (i = 0; i < chans; i++) {
last_sample[i] = state->resampler_state[i];
}

while (consumed < total) {
const int pos = ((int)idx) * chans;
const float *src = &inbuf[(pos >= finalpos) ? finalpos : pos];
SDL_assert(dst < (outbuf + (outbuflen / framelen)));
for (i = 0; i < chans; i++) {
const float val = *(src++);
*(dst++) = (val + last_sample[i]) * 0.5f;
last_sample[i] = val;
}
consumed = pos + chans;
idx += src_incr;
}

for (i = 0; i < chans; i++) {
state->resampler_state[i] = last_sample[i];
}

return (int)((dst - outbuf) * sizeof(float));
return SDL_ResampleAudioSimple(chans, stream->rate_incr, state->resampler_state, inbuf, inbuflen, outbuf, outbuflen);
}

static void
Expand Down

0 comments on commit 063c9d4

Please sign in to comment.