Hi,
I’m having problems with the SDL_BuildAudioCVT() function in SDL 1.2.0.
I’m loading a WAV file, and trying to convert it to the hardware audio
spec format. The frequency conversion does not work properly.
Looking at SDL_audiocvt.c the problem is obvious, since the correct
conversion code is commented out:
if ( (lo_rate/100) != (hi_rate/100) ) {
#if 1
/* 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 = (double)lo_rate/hi_rate;
cvt->len_mult *= 2;
cvt->len_ratio /= cvt->rate_incr;
} else {
cvt->rate_incr = (double)hi_rate/lo_rate;
cvt->len_ratio *= cvt->rate_incr;
}
cvt->filters[cvt->filter_index++] = SDL_RateSLOW;
#endif
}
Well, the rate distortion is great. However I don’t understand this
comment. I’m new to SDL so I may be misunderstanding the API, but I
thought that SDL_AudioSpec::samples was the hardware audio buffer size.
This needs to be a power of 2. The length of my own audio buffer should
be independent of this and not have this requirement. After all, my
audio_callback() (and that of the sample code I’ve seen) will not always
mix in the full “len” parameter if we are near the end of the audio
buffer. I assumed the rest of the “stream” parameter that was passed
would be pre-filled with silence. Is this not true?
void audio_callback (Uint8 * stream, int len) {
if (len <= 0)
return;
if ((Uint32)len > wav_length_ - wav_position_)
len = wav_length_ - wav_position_;
SDL_MixAudio(stream, wav_buffer_ + wav_position_, len,
SDL_MIX_MAXVOLUME);
wav_position_ += len;
if (wav_position_ >= wav_length_) {
// Loop. FIXME: Silence for wav_length_%samples bytes before
looping.
wav_position_ = 0;
}
}
BTW, is there any reason I shouldn’t just do
memcpy(stream, wav_buffer_ + wav_position_, len);
instead of the SDL_MixAudio() call since I don’t need any mixing and
have already converted wav_buffer_ to the hardware format?
Another possible issue with SDL_BuildAudioCVT() is the
if ( (src_rate/100) != (dst_rate/100) )
tests in the code. Doesn’t this make the conversion only guaranteed
accurate to 100 Hz? My program needs to play up to several minutes of
sound with strict synchronization with other streaming data that I’m
recording. For an 8000 Hz conversion (probably worst case) doesn’t this
give us up to a 100/8000 second error per second (0.0125). That’s 0.75 s
error per minute, or 7.5 s off for a 10 minute sound file, assuming my
math is right.
Proposed patch is attached. I’ve tested this and it seems to work with
my program using the Linux 2.4 emu10k1 (SB Live) driver. However only my
code calls my version of BuildAudioCVT, SDL itself was unchanged so I
can run with standard libSDL 1.2 binaries. Perhaps SDL itself calls
BuildAudioCVT with some other assumptions (SDL_OpenAudio with NULL
"obtained" pointer?), but if so I don’t think they should be imposed on
the user API.–
Michael Babcock
Jim Henson’s Creature Shop - Los Angeles
@Michael_Babcock
-------------- next part --------------
— SDL_audiocvt.c~ Fri Feb 9 23:20:03 2001
+++ SDL_audiocvt.c Fri Jun 8 11:33:42 2001
@@ -579,7 +579,7 @@
/* Do rate conversion */
cvt->rate_incr = 0.0;
- if ( (src_rate/100) != (dst_rate/100) ) {
- if ( src_rate != dst_rate ) {
Uint32 hi_rate, lo_rate;
int len_mult;
double len_ratio;
@@ -599,23 +599,14 @@
len_ratio = 2.0;
}
/* If hi_rate = lo_rate*2^x then conversion is easy */
-
while ( ((lo_rate*2)/100) <= (hi_rate/100) ) {
-
while ( (lo_rate*2) <= hi_rate ) { cvt->filters[cvt->filter_index++] = rate_cvt; cvt->len_mult *= len_mult; lo_rate *= 2; cvt->len_ratio *= len_ratio; } /* We may need a slow conversion here to finish up */
-
if ( (lo_rate/100) != (hi_rate/100) ) {
-#if 1
-
/* 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 ( lo_rate != hi_rate ) { if ( src_rate < dst_rate ) { cvt->rate_incr = (double)lo_rate/hi_rate; cvt->len_mult *= 2;
@@ -625,7 +616,6 @@
cvt->len_ratio *= cvt->rate_incr;
}
cvt->filters[cvt->filter_index++] = SDL_RateSLOW;
-#endif
}
}