SDL_AudioSpec's sample buffer (=samples)

Hello fellow SDL developers,

Currently I’m experiencing a problem with SDL’s soundcode
and most importantly with Audio samplebuffer (IE samples).

For my programm (GBA emu :wink: I should use a desired sample
buffer of 735 or 1470 if possible. This isn’t a problem
when I use SDL on WIN32 and/or BeOS systems. But for FreeBSD
and Linux this is a problem. It’s my understanding that
Linux needs a sample buffer size powered by two. After much
testing I’ve finally had the soundcode working using a powered
by two sample size (2048), only problem is that the sound
becomes inaccurate at a later stadium or that the sound
quality becomes worse, worser and worser, even with the
desired 60 Frames per second (which is GBA speed).

The problem is simple, the GBA emu needs the value of 735 or
1470, because of it’s internal GBA sound emulation timing but
unfortunately this can’t be archieved by me using SDL on Linux.

Now my question (it took some time, I know). How can I use
a powered by two sample buffersize while the value should
be 735 or 1470? I know it needs a kind of conversion but atm I
haven’t find a way to do that. I hope somebody can provide
me some help with this because I’m a bit fedup with the
inacurate sound and worse sound quality. Or should I use
SDL_Mixer for the audio code?

Extra information :

The source (RAW) soundbuffer is a Uint8 buffer which can be
mixed directly to the destination stream. The value’s of the
source below is as followed :

BUFFER_SIZE = 2048 (this should be 1470 or better, 735)
SAMPLE_RATE = 44100
BUFFER_TOTAL = 5880

Here’s my source of the soundcode I know use :

int BoycaMain::SdlSoundInit()
{
int i;
SDL_AudioSpec wanted;

if(EmuOptions.Sound==FALSE) return 0;

wanted.format   = AUDIO_S16SYS; 
wanted.channels = 2;
wanted.freq     = SAMPLE_RATE;

  #ifdef TARGET_BEOS
   wanted.samples  = BUFFER_TOTAL; // BeOS seems to have better 

quality set with this on.
#else
wanted.samples = BUFFER_SIZE;
#endif

wanted.callback = BufferProc;
wanted.userdata = 0;

if( SDL_InitSubSystem( SDL_INIT_AUDIO ) < 0 ) {
	printf("Couldn't open SDL audio system\n");
	exit(1);
}
						
if(SDL_OpenAudio( &wanted, NULL) < 0) {
	fprintf(stderr, "Couldn't open audio : %s\n", SDL_GetError());
	EmuOptions.Sound = FALSE;
	return 1;
}

SDL_PauseAudio(0);

return 0;

}

void BoycaMain::BufferProc(void *user, Uint8 *stream, int len)
{
SoundUpdate(0, BUFFER_SIZE); // Update the entire in-emu
soundbuffer.
SDL_MixAudio( stream, (Uint8 *)GB_Data.buf, len, SDL_MIX_MAXVOLUME);
// Similar to SDL_MixAudio but then with memcpy :
// memcpy(stream, (Uint8 *)GB_Data.buf, len); // for Linux audio.
}

I hope somebody could anwser my question. Don’t hessitate to e-mail me
privately should you need more information : sdlemu at ngemu.com.

Regards,

Niels Wagenaar

First of all, is audio used as the time base for the emulator? If not, is
this also the case on the real h/w? That is, is audio expected to drift
somewhat in relation to the video frame rate, and thus driven by an IRQ
from the sound chip, or is the GBA designed to generate exactly one audio
buffer for each video frame, so that everything can be done inside the
"main loop"?

Anyway, your problem is rather similar to that of a module player; to
have notes and effects update at the right times, you need to run the
"player" callback every N samples, where N is a function of the sample
rate, the tempo and the “speed” of the module. Of course, N has to be
rather accurate, so using the nearest power-of-two buffer size won’t cut
it.

What you do is basically something like this (many ways to arrange this
code… :-):

int remaining = 0;	/* Remaining samples to next "event" */

audio_callback(..., int samples)
{
	while(samples)
	{
		int chunk = remaining;
		if(chunk > samples)
			chunk = samples;

		if(chunk)
			audio_mix(..., chunk);

		samples -= chunk;
		remaining -= chunk;

		if(!remaining)
		{
			audio_update();
			remaining = SAMPLES_PER_UPDATE;
		}
	}
}

…where audio_mix() generates/grabs/mixes/ ‘chunk’ samples of
output and mixes or writes them to the output buffer, and audio_update()
latches new sound control data, starts new notes, runs the sound ISR of
the emulated game to fill in another internal buffer, or whatever.

SAMPLES_PER_UPDATE would be your 735 samples, provided you’re using 44.1
kHz. (I’d recommend note requiring any specific sample rate for the
code to work at all - although 44.1 should be rather safe these days.)

In short, mix samples into the output buffer, and just call your "update"
function every N samples, where N can be any number you like. (Like 735.
:slight_smile:

//David Olofson — Programmer, Reologica Instruments AB

.- M A I A -------------------------------------------------.
| Multimedia Application Integration Architecture |
| A Free/Open Source Plugin API for Professional Multimedia |
----------------------------> http://www.linuxdj.com/maia -' .- David Olofson -------------------------------------------. | Audio Hacker - Open Source Advocate - Singer - Songwriter |-------------------------------------> http://olofson.net -'On Thursday 28 March 2002 16:24, nwagenaar at digitaldynamics.nl wrote:

Hello fellow SDL developers,

Currently I’m experiencing a problem with SDL’s soundcode
and most importantly with Audio samplebuffer (IE samples).

For my programm (GBA emu :wink: I should use a desired sample
buffer of 735 or 1470 if possible. This isn’t a problem
when I use SDL on WIN32 and/or BeOS systems. But for FreeBSD
and Linux this is a problem. It’s my understanding that
Linux needs a sample buffer size powered by two. After much
testing I’ve finally had the soundcode working using a powered
by two sample size (2048), only problem is that the sound
becomes inaccurate at a later stadium or that the sound
quality becomes worse, worser and worser, even with the
desired 60 Frames per second (which is GBA speed).

The problem is simple, the GBA emu needs the value of 735 or
1470, because of it’s internal GBA sound emulation timing but
unfortunately this can’t be archieved by me using SDL on Linux.