[SDL] Re: Help needed with SDL audio

David Olofson david.olofson at reologica.se
Wed Aug 29 05:14:00 PDT 2001

On Wednesday 29 August 2001 11:00, Jari Karppinen wrote:
> Hm, I could have been more descriptive about the problem with audio
> output. I will paste here the instructions written by the author of the
> emulation core.
>  Here's a brief overview of how to use all this:
>  - do machine dependant initialization (audio, video, init input, etc.)
>  - set up bitmap structure
>  - set up cart structure (load game)
>  - call system_init()
>  - if snd.enabled is set, we can use sound
>  - load sram data if it exists for the game
>  in a loop:
>    - update input structure based on gamepad/keyboard
>    - call sms_frame()
>    - play sound using 'snd.buffer'
>    - copy 'bitmap.data' to the video display
>    - quit if needed
>  - save sram data if the game used it
>  - call system_shutdown()
> This plan is very clear, except for the sound part. The dos port of the
> emulator simply pushes the data in sound buffers to the soundcard. With
> SDL, the audio callback is supposed to get more data when it is needed.
> I don't understand how to get the emulation core work in synch with the
> audio callback.

This is the problem; you *can't*. The emulator runs at one speed, and the 
audio card at another, and even if you had full control over the refresh 
and sample rates, you'd still have to deal with drift problems.

> I'm hoping someone with more experience could look at the code and give
> me some advice. David proposed some good solutions, for example running
> the audio emulation inside callback seems to be used in some emulators
> (e.g. gnuboy). However, I don't know a lot of the internal workings of
> the emulation core, so this solution is beyond me.

I looked at the sound chip emulators, and they seems pretty simple. They 
have a "write_register()" type call, and a "generate_output()" type call 
- no reading from the chips is supported. (OPLRead() is never used.)

Wrap the "write_register()" call so that the rest of the emulator 
actually writes timestamped commands to a lock-free FIFO, for example my 
little sfifo hack. (Rather than directly fiddling with the inner workins 
of the sound emulators - that would wreck timing and introduce a ton of 
multithreading related issues.)

For example; emulator call:

	void ym2413_write(int chip, int address, int data);

your wrapper:

	typedef struct
		int timestamp;
		int chip;
		int address;
		int data;
	} ym2413_command_t;

	sfifo_t ym2413_fifo;

	void b_ym2413_write(int chip, int address, int data, int timestamp)
		ym2413_command_t cmd;
		cmd.timestamp = timestamp;
		cmd.chip = chip;
		cmd.address = address;
		cmd.data = data;
		if(sfifo_space(&ym2413_fifo) >= sizeof(cmd))
			sfifo_write(&ym2413_fifo, cmd, sizeof(cmd));
			fprintf(stderr, "WARNING: Command FIFO full!\n");

Now, move the sound chip emulator into the SDL audio callback, and set it 
up to generate one buffer of the requested size each time it's called. 
Inside the audio callback you'd basically do this to get one buffer with 
(if you like) single sample timing accuracy:

		ym2413_command_t cmd;
		int todo;
		if(sfifo_used(&ym2413_fifo) >= sizeof(cmd))
			sfifo_read(&ym2413_fifo, cmd, sizeof(cmd));
			todo = cmd.timestamp - tnow;
			todo = total_buffer_size - tnow;
			chip = -1;
			for(<all chips>)
				Update(<chip>, buffer, todo);
			buffer += todo;
			tnow += todo;
		if(cmd.chip != -1)
			ym2413_write(cmd.chip, cmd.address, cmd.data);

//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 |
`--------------------------------------> david at linuxdj.com -'

More information about the SDL mailing list