mirror of https://github.com/encounter/SDL.git
audio: Replaced older resamplers in SDL_AudioCVT with the new ones.
This commit is contained in:
parent
a41103b170
commit
063c9d40d7
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
/* !!! 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. */
|
||||
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? */
|
||||
|
||||
/* 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)
|
||||
|
@ -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; \
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
#undef PICK_CHANNEL_FILTER
|
||||
|
||||
return filter;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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. */
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
|
|
@ -216,177 +216,4 @@ SDL_Convert_F32_to_S32(SDL_AudioCVT *cvt, SDL_AudioFormat format)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
SDL_Upsample_Arbitrary(SDL_AudioCVT *cvt, const int channels)
|
||||
{
|
||||
const int srcsize = cvt->len_cvt - (64 * channels);
|
||||
const int dstsize = (int) ((((double)(cvt->len_cvt/(channels*4))) * cvt->rate_incr)) * (channels*4);
|
||||
register int eps = 0;
|
||||
float *dst = ((float *) (cvt->buf + dstsize)) - channels;
|
||||
const float *src = ((float *) (cvt->buf + cvt->len_cvt)) - channels;
|
||||
const float *target = ((const float *) cvt->buf);
|
||||
const size_t cpy = sizeof (float) * channels;
|
||||
float sample[8];
|
||||
float last_sample[8];
|
||||
int i;
|
||||
|
||||
#if DEBUG_CONVERT
|
||||
fprintf(stderr, "Upsample arbitrary (x%f), %d channels.\n", cvt->rate_incr, channels);
|
||||
#endif
|
||||
|
||||
SDL_assert(channels <= 8);
|
||||
|
||||
for (i = 0; i < channels; i++) {
|
||||
sample[i] = (float) ((((double) src[i]) + ((double) src[i - channels])) * 0.5);
|
||||
}
|
||||
SDL_memcpy(last_sample, src, cpy);
|
||||
|
||||
while (dst > target) {
|
||||
SDL_memcpy(dst, sample, cpy);
|
||||
dst -= channels;
|
||||
eps += srcsize;
|
||||
if ((eps << 1) >= dstsize) {
|
||||
if (src > target) {
|
||||
src -= channels;
|
||||
for (i = 0; i < channels; i++) {
|
||||
sample[i] = (float) ((((double) src[i]) + ((double) last_sample[i])) * 0.5);
|
||||
}
|
||||
} else {
|
||||
|
||||
}
|
||||
SDL_memcpy(last_sample, src, cpy);
|
||||
eps -= dstsize;
|
||||
}
|
||||
}
|
||||
|
||||
cvt->len_cvt = dstsize;
|
||||
if (cvt->filters[++cvt->filter_index]) {
|
||||
cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SDL_Downsample_Arbitrary(SDL_AudioCVT *cvt, const int channels)
|
||||
{
|
||||
const int srcsize = cvt->len_cvt - (64 * channels);
|
||||
const int dstsize = (int) (((double)(cvt->len_cvt/(channels*4))) * cvt->rate_incr) * (channels*4);
|
||||
register int eps = 0;
|
||||
float *dst = (float *) cvt->buf;
|
||||
const float *src = (float *) cvt->buf;
|
||||
const float *target = (const float *) (cvt->buf + dstsize);
|
||||
const size_t cpy = sizeof (float) * channels;
|
||||
float last_sample[8];
|
||||
float sample[8];
|
||||
int i;
|
||||
|
||||
#if DEBUG_CONVERT
|
||||
fprintf(stderr, "Downsample arbitrary (x%f), %d channels.\n", cvt->rate_incr, channels);
|
||||
#endif
|
||||
|
||||
SDL_assert(channels <= 8);
|
||||
|
||||
SDL_memcpy(sample, src, cpy);
|
||||
SDL_memcpy(last_sample, src, cpy);
|
||||
|
||||
while (dst < target) {
|
||||
src += channels;
|
||||
eps += dstsize;
|
||||
if ((eps << 1) >= srcsize) {
|
||||
SDL_memcpy(dst, sample, cpy);
|
||||
dst += channels;
|
||||
for (i = 0; i < channels; i++) {
|
||||
sample[i] = (float) ((((double) src[i]) + ((double) last_sample[i])) * 0.5);
|
||||
}
|
||||
SDL_memcpy(last_sample, src, cpy);
|
||||
eps -= srcsize;
|
||||
}
|
||||
}
|
||||
|
||||
cvt->len_cvt = dstsize;
|
||||
if (cvt->filters[++cvt->filter_index]) {
|
||||
cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SDL_Upsample_Multiple(SDL_AudioCVT *cvt, const int channels)
|
||||
{
|
||||
const int multiple = (int) cvt->rate_incr;
|
||||
const int dstsize = cvt->len_cvt * multiple;
|
||||
float *buf = (float *) cvt->buf;
|
||||
float *dst = ((float *) (cvt->buf + dstsize)) - channels;
|
||||
const float *src = ((float *) (cvt->buf + cvt->len_cvt)) - channels;
|
||||
const float *target = buf + channels;
|
||||
const size_t cpy = sizeof (float) * channels;
|
||||
float last_sample[8];
|
||||
int i;
|
||||
|
||||
#if DEBUG_CONVERT
|
||||
fprintf(stderr, "Upsample (x%d), %d channels.\n", multiple, channels);
|
||||
#endif
|
||||
|
||||
SDL_assert(channels <= 8);
|
||||
|
||||
SDL_memcpy(last_sample, src, cpy);
|
||||
|
||||
while (dst > target) {
|
||||
SDL_assert(src >= buf);
|
||||
|
||||
for (i = 0; i < channels; i++) {
|
||||
dst[i] = (float) ((((double)src[i]) + ((double)last_sample[i])) * 0.5);
|
||||
}
|
||||
dst -= channels;
|
||||
|
||||
for (i = 1; i < multiple; i++) {
|
||||
SDL_memcpy(dst, dst + channels, cpy);
|
||||
dst -= channels;
|
||||
}
|
||||
|
||||
src -= channels;
|
||||
if (src > buf) {
|
||||
SDL_memcpy(last_sample, src - channels, cpy);
|
||||
}
|
||||
}
|
||||
|
||||
cvt->len_cvt = dstsize;
|
||||
if (cvt->filters[++cvt->filter_index]) {
|
||||
cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SDL_Downsample_Multiple(SDL_AudioCVT *cvt, const int channels)
|
||||
{
|
||||
const int multiple = (int) (1.0 / cvt->rate_incr);
|
||||
const int dstsize = cvt->len_cvt / multiple;
|
||||
float *dst = (float *) cvt->buf;
|
||||
const float *src = (float *) cvt->buf;
|
||||
const float *target = (const float *) (cvt->buf + dstsize);
|
||||
const size_t cpy = sizeof (float) * channels;
|
||||
float last_sample[8];
|
||||
int i;
|
||||
|
||||
#if DEBUG_CONVERT
|
||||
fprintf(stderr, "Downsample (x%d), %d channels.\n", multiple, channels);
|
||||
#endif
|
||||
|
||||
SDL_assert(channels <= 8);
|
||||
SDL_memcpy(last_sample, src, cpy);
|
||||
|
||||
while (dst < target) {
|
||||
for (i = 0; i < channels; i++) {
|
||||
dst[i] = (float) ((((double)src[i]) + ((double)last_sample[i])) * 0.5);
|
||||
}
|
||||
dst += channels;
|
||||
|
||||
SDL_memcpy(last_sample, src, cpy);
|
||||
src += (channels * multiple);
|
||||
}
|
||||
|
||||
cvt->len_cvt = dstsize;
|
||||
if (cvt->filters[++cvt->filter_index]) {
|
||||
cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
|
||||
}
|
||||
}
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
|
|
Loading…
Reference in New Issue