vanfanel wrote:That's not totally true, Huge: conversion to 48Khz before the final output takes A LOT of CPU in my system. I really need 48Khz in the Linux port, as I've had the same problems with every emulator around: I know what I'm talking about. Please, don't try to avoid Snake adding it: a lot of people with ALSA drivers with 48Khz as their native frequency will be fine with this. My system specs are out of question here: It IS far enough, and I don't have any problems with KEGA as it is, but there's no reason to use a lot of CPU when it can be avoided with souch a little option.
Thanks
Conversion between two frequencies is slow when you use SDL because SDL only uses the "fast" converter when the two frequencies are easy multiples of each other (like powers of two). If they aren't it defaults to the "slow" converter... which is really slow. On a modern PC, you don't tend to notice how slow the slow routines are since they are normally only used when a sound is loaded. A stream will show how slow they slow converter is. A slow computer will also show how slow it is - versions of Duke Nukem 3D that use SDL will, on a slow computer, given a brief jerk when a sound effect plays (or not so brief depending on how slow the computer is). That is because it uses the slow converter. It was such a bad slow-down that people forced SDL to use the "fast" converter, even though it wouldn't sound right (you wound up with the pitch wrong since the fast converter only converted into/from powers of two).
So I modified my local SDL to have a decent slow converter for when the frequencies aren't related. This made D3D sound right with no jerking.
From SDL/src/audio/SDL_audiocvt.c
Code: Select all
/* Very slow rate conversion routine */
void SDL_RateSLOW(SDL_AudioCVT *cvt, Uint16 format)
{
int ipos, incr;
int i, clen;
#ifdef DEBUG_CONVERT
fprintf(stderr, "Converting audio rate * %4.4f\n", 1.0/cvt->rate_incr);
#endif
incr = (int)(cvt->rate_incr * 512.0f);
clen = (int)((float)cvt->len_cvt / cvt->rate_incr);
if ( cvt->rate_incr > 1.0 ) {
switch (format & 0xFF) {
case 8: {
Uint8 *output;
output = cvt->buf;
ipos = 0;
for ( i=clen; i; --i ) {
*output = cvt->buf[ipos>>9];
ipos += incr;
output += 1;
}
}
break;
case 16: {
Uint16 *output;
clen &= ~1;
output = (Uint16 *)cvt->buf;
ipos = 0;
for ( i=clen/2; i; --i ) {
*output=((Uint16 *)cvt->buf)[ipos>>9];
ipos += incr;
output += 1;
}
}
break;
}
} else {
switch (format & 0xFF) {
case 8: {
Uint8 *output;
output = cvt->buf+clen;
ipos = cvt->len_cvt<<9;
for ( i=clen; i; --i ) {
ipos -= incr;
output -= 1;
*output = cvt->buf[ipos>>9];
}
}
break;
case 16: {
Uint16 *output;
clen &= ~1;
output = (Uint16 *)(cvt->buf+clen);
ipos = cvt->len_cvt<<8;
for ( i=clen/2; i; --i ) {
ipos -= incr;
output -= 1;
*output=((Uint16 *)cvt->buf)[ipos>>9];
}
}
break;
}
}
cvt->len_cvt = clen;
if ( cvt->filters[++cvt->filter_index] ) {
cvt->filters[cvt->filter_index](cvt, format);
}
}
Notice that (among other changes) the routine uses fixed point math instead of floating point like the original routine did. You also need to enable this in the SDL_BuildAudioCVT() function a little below the above function
Code: Select all
/* We may need a slow conversion here to finish up */
if ( (lo_rate/100) != (hi_rate/100) ) {
#if 0
/* The problem with this is that if the input buffer is
say 1K, and the conversion rate is say 1.1, then the
output buffer is 1.1K, which may not be an acceptable
buffer size for the audio driver (not a power of 2)
*/
/* For now, punt and hope the rate distortion isn't great.
*/
#else
if ( src_rate < dst_rate ) {
cvt->rate_incr = (float)lo_rate/hi_rate;
cvt->len_mult *= 2;
cvt->len_ratio /= cvt->rate_incr;
} else {
cvt->rate_incr = (float)hi_rate/lo_rate;
cvt->len_ratio *= cvt->rate_incr;
}
cvt->filters[cvt->filter_index++] = SDL_RateSLOW;
#endif
}
In the original code, the "#if 0" will be an "#if 1". Their "punt and hope the rate distortion isn't great" was quick, but sounded bad for games like D3D.