|
@@ -331,67 +331,31 @@ SDL_BuildAudioTypeCVTFromFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat dst_fmt) |
|
|
return retval; |
|
|
} |
|
|
|
|
|
static int |
|
|
SDL_FindFrequencyMultiple(const int src_rate, const int dst_rate) |
|
|
{ |
|
|
int retval = 0; |
|
|
|
|
|
/* If we only built with the arbitrary resamplers, ignore multiples. */ |
|
|
int lo, hi; |
|
|
int div; |
|
|
|
|
|
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; |
|
|
} |
|
|
|
|
|
/* zero means "not a supported multiple" ... we only do 2x and 4x. */ |
|
|
if ((hi % lo) != 0) |
|
|
return 0; /* not a multiple. */ |
|
|
|
|
|
div = hi / lo; |
|
|
retval = ((div == 2) || (div == 4)) ? div : 0; |
|
|
|
|
|
return retval; |
|
|
} |
|
|
|
|
|
/* !!! 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. */ |
|
|
#define RESAMPLER_FUNCS(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_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \ |
|
|
SDL_assert(format == AUDIO_F32SYS); \ |
|
|
SDL_Downsample_Arbitrary(cvt, chans); \ |
|
|
} \ |
|
|
static void SDLCALL \ |
|
|
SDL_Upsample_x2_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \ |
|
|
SDL_Upsample_Multiple_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \ |
|
|
SDL_assert(format == AUDIO_F32SYS); \ |
|
|
SDL_Upsample_x2(cvt, chans); \ |
|
|
SDL_Upsample_Multiple(cvt, chans); \ |
|
|
} \ |
|
|
static void SDLCALL \ |
|
|
SDL_Downsample_x2_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \ |
|
|
SDL_Upsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \ |
|
|
SDL_assert(format == AUDIO_F32SYS); \ |
|
|
SDL_Downsample_Multiple(cvt, 2, chans); \ |
|
|
} \ |
|
|
SDL_Upsample_Arbitrary(cvt, chans); \ |
|
|
}\ |
|
|
static void SDLCALL \ |
|
|
SDL_Upsample_x4_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \ |
|
|
SDL_Downsample_Multiple_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \ |
|
|
SDL_assert(format == AUDIO_F32SYS); \ |
|
|
SDL_Upsample_x4(cvt, chans); \ |
|
|
SDL_Downsample_Multiple(cvt, chans); \ |
|
|
} \ |
|
|
static void SDLCALL \ |
|
|
SDL_Downsample_x4_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \ |
|
|
SDL_Downsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \ |
|
|
SDL_assert(format == AUDIO_F32SYS); \ |
|
|
SDL_Downsample_Multiple(cvt, 4, chans); \ |
|
|
SDL_Downsample_Arbitrary(cvt, chans); \ |
|
|
} |
|
|
RESAMPLER_FUNCS(1) |
|
|
RESAMPLER_FUNCS(2) |
|
@@ -401,61 +365,89 @@ RESAMPLER_FUNCS(8) |
|
|
#undef RESAMPLER_FUNCS |
|
|
|
|
|
static int |
|
|
SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, int dst_channels, |
|
|
int src_rate, int dst_rate) |
|
|
SDL_FindFrequencyMultiple(const int src_rate, const int dst_rate) |
|
|
{ |
|
|
if (src_rate != dst_rate) { |
|
|
const int upsample = (src_rate < dst_rate) ? 1 : 0; |
|
|
const int multiple = SDL_FindFrequencyMultiple(src_rate, dst_rate); |
|
|
SDL_AudioFilter filter = NULL; |
|
|
int lo, hi; |
|
|
|
|
|
#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; \ |
|
|
} |
|
|
SDL_assert(src_rate != 0); |
|
|
SDL_assert(dst_rate != 0); |
|
|
SDL_assert(src_rate != dst_rate); |
|
|
|
|
|
if (upsample) { |
|
|
if (multiple == 0) { |
|
|
PICK_CHANNEL_FILTER(Upsample, Arbitrary); |
|
|
} else if (multiple == 2) { |
|
|
PICK_CHANNEL_FILTER(Upsample, x2); |
|
|
} else if (multiple == 4) { |
|
|
PICK_CHANNEL_FILTER(Upsample, x4); |
|
|
} |
|
|
} else { |
|
|
if (multiple == 0) { |
|
|
PICK_CHANNEL_FILTER(Downsample, Arbitrary); |
|
|
} else if (multiple == 2) { |
|
|
PICK_CHANNEL_FILTER(Downsample, x2); |
|
|
} else if (multiple == 4) { |
|
|
PICK_CHANNEL_FILTER(Downsample, x4); |
|
|
} |
|
|
} |
|
|
if (src_rate < dst_rate) { |
|
|
lo = src_rate; |
|
|
hi = dst_rate; |
|
|
} else { |
|
|
lo = dst_rate; |
|
|
hi = src_rate; |
|
|
} |
|
|
|
|
|
#undef PICK_CHANNEL_FILTER |
|
|
if ((hi % lo) != 0) |
|
|
return 0; /* not a multiple. */ |
|
|
|
|
|
if (filter == NULL) { |
|
|
return SDL_SetError("No conversion available for these rates"); |
|
|
} |
|
|
return hi / lo; |
|
|
} |
|
|
|
|
|
/* Update (cvt) with filter details... */ |
|
|
cvt->filters[cvt->filter_index++] = filter; |
|
|
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; |
|
|
static SDL_AudioFilter |
|
|
ChooseResampler(const int dst_channels, const int src_rate, const int dst_rate) |
|
|
{ |
|
|
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 { |
|
|
cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate); |
|
|
PICK_CHANNEL_FILTER(Downsample, Arbitrary); |
|
|
} |
|
|
} |
|
|
|
|
|
#undef PICK_CHANNEL_FILTER |
|
|
|
|
|
return filter; |
|
|
} |
|
|
|
|
|
static int |
|
|
SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, const int dst_channels, |
|
|
const int src_rate, const int dst_rate) |
|
|
{ |
|
|
SDL_AudioFilter filter; |
|
|
|
|
|
if (src_rate == dst_rate) { |
|
|
return 0; /* no conversion necessary. */ |
|
|
} |
|
|
|
|
|
filter = ChooseResampler(dst_channels, src_rate, dst_rate); |
|
|
if (filter == NULL) { |
|
|
return SDL_SetError("No conversion available for these rates"); |
|
|
} |
|
|
|
|
|
return 1; /* added a converter. */ |
|
|
/* Update (cvt) with filter details... */ |
|
|
cvt->filters[cvt->filter_index++] = filter; |
|
|
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 0; /* no conversion necessary. */ |
|
|
return 1; /* added a converter. */ |
|
|
} |
|
|
|
|
|
|
|
@@ -514,7 +506,7 @@ SDL_BuildAudioCVT(SDL_AudioCVT * cvt, |
|
|
|
|
|
The expectation is we can process data faster in float32 |
|
|
(possibly with SIMD), and making several passes over the same |
|
|
buffer in is likely to be CPU cache-friendly, avoiding the |
|
|
buffer is likely to be CPU cache-friendly, avoiding the |
|
|
biggest performance hit in modern times. Previously we had |
|
|
(script-generated) custom converters for every data type and |
|
|
it was a bloat on SDL compile times and final library size. */ |
|
@@ -585,11 +577,11 @@ SDL_BuildAudioCVT(SDL_AudioCVT * cvt, |
|
|
} |
|
|
|
|
|
/* Do rate conversion, if necessary. Updates (cvt). */ |
|
|
if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) == |
|
|
-1) { |
|
|
if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) == -1) { |
|
|
return -1; /* shouldn't happen, but just in case... */ |
|
|
} |
|
|
|
|
|
/* Move to final data type. */ |
|
|
if (SDL_BuildAudioTypeCVTFromFloat(cvt, dst_fmt) == -1) { |
|
|
return -1; /* shouldn't happen, but just in case... */ |
|
|
} |
|
|