Converting OSS sound code to SDL

I’m working on the Stella (Atari 2600) emulator. Right now, there is an
external sound server written using OSS. I’m trying to convert it to SDL
sound (so it wil be portable) and re-integrate it into the program (eliminate
the server part).

For some reason, the sound is somewhat ‘choppy’ and delayed in the SDL
version. It’s like the sound is not continuous in SDL. BTW, I’ve tried
different sample rates.

I’ve asked questions on this topic before, but I don’t know if I was really
clear. So this time, I’m providing links to the original OSS code and my
current attempt at an SDL equivalent.

Would someone be able to take a look at both, and tell me what’s missing??

Also, how does one adjust volume in SDL? Right now, the OSS code is changing
the hardware mixer upon start, and restoring it again on exit. Does SDL
support changing the data itself, so that I don’t have to change the
hardware mixer? If so, how? If not, how does one change the hardware mixer
in SDL?

The files are located at:

http://www.cs.mun.ca/~stephena/linux/OSS_orig.c
http://www.cs.mun.ca/~stephena/linux/OSS_sdl.c

Thanks for any info,
Steve

While I won’t claim to be an expert, I think this might be part of the
problem:

desired.samples = 1024;

If I’m not mistaken, that should be at least 2048? Admittedly, I have
done little with SDL’s audio API, but I seem to remember Ryan mentioning
this…On Mon, 2002-08-12 at 06:55, Stephen Anthony wrote:

Would someone be able to take a look at both, and tell me what’s missing??

Would someone be able to take a look at both, and tell me what’s
missing??

While I won’t claim to be an expert, I think this might be part of the
problem:

desired.samples = 1024;

If I’m not mistaken, that should be at least 2048? Admittedly, I have
done little with SDL’s audio API, but I seem to remember Ryan mentioning
this…

I tried 2048 (and 4096 and 8192), and while some of the smoothness returns,
the sounds begin to ‘sound’ different.

Does anyone know what number of samples are used in OSS? I don’t actually
specify the number of samples in OSS, but it seems to work fine with the
default.

Thanks,
SteveOn August 12, 2002 11:58 am, you wrote:

On Mon, 2002-08-12 at 06:55, Stephen Anthony wrote:

By chance are you running a Sound Server in the background? Like Arts,
or ESD?On Mon, 2002-08-12 at 09:37, Stephen Anthony wrote:

I tried 2048 (and 4096 and 8192), and while some of the smoothness returns,
the sounds begin to ‘sound’ different.

I tried 2048 (and 4096 and 8192), and while some of the smoothness
returns, the sounds begin to ‘sound’ different.

By chance are you running a Sound Server in the background? Like Arts,
or ESD?

Definitely not. But it does sound that way. I don’t run (and never have)
any sound servers. They screw with real-time sound from games and emulators.

I’m thinking it may have something to do with blocking vs. non-blocking mode.
The OSS code sets non-blocking mode. Does SDL do this by default? If not,
does anyone know how to enable it??

Thanks,
SteveOn August 12, 2002 12:34 pm, you wrote:

On Mon, 2002-08-12 at 09:37, Stephen Anthony wrote:

I’m working on the Stella (Atari 2600) emulator. Right now, there is an
external sound server written using OSS. I’m trying to convert it to SDL
sound (so it wil be portable) and re-integrate it into the program (eliminate
the server part).

Awesome! :slight_smile: Sound for the Zaurus soon! :slight_smile:

For some reason, the sound is somewhat ‘choppy’ and delayed in the SDL
version. It’s like the sound is not continuous in SDL. BTW, I’ve tried
different sample rates.

Have you tried different buffer sizes?

-bill!On Mon, Aug 12, 2002 at 09:25:21AM -0230, Stephen Anthony wrote:

I’m working on the Stella (Atari 2600) emulator. Right now, there is an
external sound server written using OSS. I’m trying to convert it to SDL
sound (so it wil be portable) and re-integrate it into the program
(eliminate the server part).

Awesome! :slight_smile: Sound for the Zaurus soon! :slight_smile:

Well, maybe not so soon :slight_smile: I’m hoping to get it working ASAP, but sound
programming is not my strong point. Hence this posting.

For some reason, the sound is somewhat ‘choppy’ and delayed in the SDL
version. It’s like the sound is not continuous in SDL. BTW, I’ve tried
different sample rates.

Have you tried different buffer sizes?

Of course, that is what I meant to say :slight_smile: I’ve tried different buffer sizes.
The sample rate is set at 31400 Hz. I may try to go to 22050 (or 44100), but
SDL will emulate whatever it requires, won’t it ??

SteveOn August 12, 2002 01:52 pm, you wrote:

On Mon, Aug 12, 2002 at 09:25:21AM -0230, Stephen Anthony wrote:

I have another question wrt. this. When using OSS, you open /dev/dsp and
write to the soundcard directly (using write(…)). When that write is
finished, does the sound keep playing? That is, when you write data to the
sound card, does it keep using that data until something else is written to
it?

That’s what seems to be happening in the OSS code I have now. If SDL doesn’t
do this, that could explain why the SDL version is choppy. It only plays
what data is written to it, then stops. These ‘stops’ could be interpreted
as choppiness in the playback.

Is this how it works? Or is this theory useless.

Is this somehow related to blocking vs. non-blocking mode? I’m still not
sure what the difference is between these.

Thanks,
Steve

I’m thinking it may have something to do with blocking vs. non-blocking mode.
The OSS code sets non-blocking mode. Does SDL do this by default? If not,
does anyone know how to enable it??

I don’t think it blocks, since it runs in a separate thread. If you are
choking the processor, this might be a problem.

–ryan.

Of course, that is what I meant to say :slight_smile: I’ve tried different buffer sizes.
The sample rate is set at 31400 Hz. I may try to go to 22050 (or 44100), but
SDL will emulate whatever it requires, won’t it ??

31400?!

If your sound card can’t do that, SDL will emulate it, poorly, in software.

–ryan.

I have another question wrt. this. When using OSS, you open /dev/dsp and
write to the soundcard directly (using write(…)). When that write is
finished, does the sound keep playing? That is, when you write data to the
sound card, does it keep using that data until something else is written to
it?

Depends on the driver, I think.

That’s what seems to be happening in the OSS code I have now. If SDL doesn’t
do this, that could explain why the SDL version is choppy. It only plays
what data is written to it, then stops. These ‘stops’ could be interpreted
as choppiness in the playback.

Is this how it works? Or is this theory useless.

SDL writes to /dev/dsp (or whatnot) and then calls your callback when it
needs more data. If you don’t feed it as much data as it wants when your
callback gets called, you’ll probably get choppiness or maybe repeats if
you happen to get the same memory buffer and it hasn’t been reinitialized
(depends on the SDL target and hardware driver). But that memory buffer in
your callback is going to the audio device, ready or not.

Is this somehow related to blocking vs. non-blocking mode? I’m still not
sure what the difference is between these.

Blocking says “write to the audio device, and don’t return from the
write() call until all the data is written (or a fatal error occurs)”. Non
blocking says “write to the audio device and return immediately, telling
me how much you managed to write”.

SDL writes to the audio device in a separate thread, so blocking is more
or less irrelevant, since all this thread does is write to the audio
device, get more data from your callback as needed, and write to the audio
device again.

–ryan.

Hehe - Was that rate picked because it had something to do with the
original Atari 8-bit’s sound chips, I wonder? :slight_smile:

-bill!On Mon, Aug 12, 2002 at 03:17:57PM -0400, Ryan C. Gordon wrote:

Of course, that is what I meant to say :slight_smile: I’ve tried different buffer sizes.
The sample rate is set at 31400 Hz. I may try to go to 22050 (or 44100), but
SDL will emulate whatever it requires, won’t it ??

31400?!

Of course, that is what I meant to say :slight_smile: I’ve tried different
buffer sizes. The sample rate is set at 31400 Hz. I may try to go
to 22050 (or 44100), but SDL will emulate whatever it requires,
won’t it ??

31400?!

Hehe - Was that rate picked because it had something to do with the
original Atari 8-bit’s sound chips, I wonder? :slight_smile:

Yes, it was. I tried going back to 22050, and it sounds the same. Since
it doesn’t make it any worse, I’ll leave it at that for now. That
should be supported by almost all soundcards I think.

SteveOn August 12, 2002 05:55 pm, nbs wrote:

On Mon, Aug 12, 2002 at 03:17:57PM -0400, Ryan C. Gordon wrote:

I have another question wrt. this. When using OSS, you open /dev/dsp
and write to the soundcard directly (using write(…)). When that
write is finished, does the sound keep playing? That is, when you
write data to the sound card, does it keep using that data until
something else is written to it?

Depends on the driver, I think.

That’s what seems to be happening in the OSS code I have now. If SDL
doesn’t do this, that could explain why the SDL version is choppy.
It only plays what data is written to it, then stops. These 'stops’
could be interpreted as choppiness in the playback.

Is this how it works? Or is this theory useless.

SDL writes to /dev/dsp (or whatnot) and then calls your callback when
it needs more data. If you don’t feed it as much data as it wants when
your callback gets called, you’ll probably get choppiness or maybe
repeats if you happen to get the same memory buffer and it hasn’t been
reinitialized (depends on the SDL target and hardware driver). But that
memory buffer in your callback is going to the audio device, ready or
not.

Now you may have something here. I think this may be the problem. Right
now, when there is data to write (to the soundcard), the code does a
write(), and thats it.

With SDL, it seems that the buffer has to kept full when the callback
wants to use it. As the code is now, the buffer is only filled when the
sound server wants to write some data.

Is there an easy way to make SDL behave this way? I want the code itself
controlling when to do the write, not have the callback function control
that.

Guess what I want is a way to simulate write(…) to the soundcard, and
only send stuff to the card when a write is done (and under my control).

Any ideas?

SteveOn August 12, 2002 04:52 pm, Ryan C. Gordon wrote:

31400?!
Hehe - Was that rate picked because it had something to do with the
original Atari 8-bit’s sound chips, I wonder? :slight_smile:
Yes, it was. I tried going back to 22050, and it sounds the same. Since
it doesn’t make it any worse, I’ll leave it at that for now. That
should be supported by almost all soundcards I think.

What would be the difficulty/problem with upsampling to 44100? Roughly,
every 1.4044 samples, you would insert your own “oversampled” sample that
is halfway between the prior and next sample. It’s a crude method, but
you’ll find that you’ll get significantly more frequency response by using
a higher sampling rate (and yes, the chip DID generate tones higher than
11khz). Quite a bit of the pokey’s character will be lost without it.

BTW, Are you doing Pokey emulation? there’s already a pokey library out
there that emulates the Pokey at whatever speed you want it to. Let me
know if you’d like a copy.

–>Neil-------------------------------------------------------------------------------
Neil Bradley What are burger lovers saying
Synthcom Systems, Inc. about the new BK Back Porch Griller?
ICQ #29402898 “It tastes like it came off the back porch.” - Me

31400?!

Hehe - Was that rate picked because it had something to do with the
original Atari 8-bit’s sound chips, I wonder? :slight_smile:

Yes, it was. I tried going back to 22050, and it sounds the same.
Since it doesn’t make it any worse, I’ll leave it at that for now.
That should be supported by almost all soundcards I think.

What would be the difficulty/problem with upsampling to 44100? Roughly,
every 1.4044 samples, you would insert your own “oversampled” sample
that is halfway between the prior and next sample. It’s a crude method,
but you’ll find that you’ll get significantly more frequency response
by using a higher sampling rate (and yes, the chip DID generate tones
higher than 11khz). Quite a bit of the pokey’s character will be lost
without it.

Main difficulty would be the fact that sound programming is my weak spot,
and I don’t know enough (right now) on how to do it. While your
suggestion is probably a better solution, I just want to translate as
close as possible from the OSS code. Also, when I went to 44100, I get
no sound at all :frowning: Very frustrating. I don’t know if SDL is causing the
problem, or if it’s the TIASound library.

BTW, Are you doing Pokey emulation? there’s already a pokey library out
there that emulates the Pokey at whatever speed you want it to. Let me
know if you’d like a copy.

Sorry, I’m not familiar with that. Actually, I’m working on adding
features and completing the port to SDL. For the guts of the emulator,
Brad Mott is the one to speak to. He originally wrote the thing. But
send along any info which you think is relevant. I (or somebody else on
the team) may be able to make use of it.

Thanks,
SteveOn August 12, 2002 07:55 pm, Neil Bradley wrote:

Now you may have something here. I think this may be the problem. Right
now, when there is data to write (to the soundcard), the code does a
write(), and thats it.

With SDL, it seems that the buffer has to kept full when the callback
wants to use it. As the code is now, the buffer is only filled when the
sound server wants to write some data.

Is there an easy way to make SDL behave this way? I want the code itself
controlling when to do the write, not have the callback function control
that.

What you can do is keep a write queue of audio buffers, and have the audio
decoding callback pull data from that queue. That’s actually how the write()
interface is implemented in the kernel. So, you can use a ring buffer or
a mutex protected queue for queueing sound. The reason why SDL uses a
callback is because the audio data is requested exactly when the callback
runs. If you prepare the data too far in advance, then you’ll have audio
delays, if you don’t keep the queue filled you’ll have drop outs.

For what it’s worth, this is tricky stuff to get right, so don’t feel
bad about struggling with it. :slight_smile:

Oh, and you’re probably best off using the 22Khz sound until you’re
comfortable with the sound pipeline. Once you have it working, then
you can work on upsampling to 44Khz.

See ya,
-Sam Lantinga, Software Engineer, Blizzard Entertainment

Guess what I want is a way to simulate write(…) to the soundcard, and
only send stuff to the card when a write is done (and under my control).

Keep a buffer of data somewhere. Call SDL_LockAudio() when putting more
data into that buffer, and SDL_UnlockAudio() when you are done putting
data into it. (Lock/UnlockAudio guarantees that the audio callback won’t
run…do NOT use it for extended periods of time! Just as a mutex to
prevent race conditions. Do not call LockAudio from inside the callback,
either.)

As the callback runs, feed SDL as much data as it wants. If you don’t have
anymore data, feed it silence.

This is a different paradigm than direct write()s to /dev/dsp, and your
code needs to be adapted to it.

–ryan.

Guess what I want is a way to simulate write(…) to the soundcard,
and only send stuff to the card when a write is done (and under my
control).

Keep a buffer of data somewhere. Call SDL_LockAudio() when putting more
data into that buffer, and SDL_UnlockAudio() when you are done putting
data into it. (Lock/UnlockAudio guarantees that the audio callback
won’t run…do NOT use it for extended periods of time! Just as a mutex
to prevent race conditions. Do not call LockAudio from inside the
callback, either.)

Yes, that is what I’m doing now. As an experiment, I set up a mutex (of
sorts) and 2 count variables. In between calls to SDL_LockAudio() /
SDL_UnlockAudio(), I update the sound buffer (separate code to do this),
set the mutex to 1 and increment a variable that counts how many times
the buffer was updated.

In the callback, I check to see if the mutex is 1, then copy the buffer to
the SDL stream, set mutex to 0, and increment a variable that says how
many times the stream was copied.

All things being equal, the 2 counts should be the same at the end of the
run. But they aren’t. There are approx. 4 times as many attempts as
there are actual updates to the stream. In other words, only 25% of the
sounds I process are actually sent to the soundcard. This would
definitely explain the dropouts I’m hearing.

Problem is, I don’t know what causes it. Is the callback ‘atomic’. That
is, while it’s being called, it can’t be called again (from the sound
thread)?

I’m really lost here. The mutex is set to 1 whenever the buffer is
updated. The callback seems to not ‘see’ many of these changes. So
maybe the callback isn’t being called often enough??

As the callback runs, feed SDL as much data as it wants. If you don’t
have anymore data, feed it silence.

How do you know how much data “SDL wants”? And how to you feed it silence
(I assume by memset’ing 0 to the stream)?

This is a different paradigm than direct write()s to /dev/dsp, and your
code needs to be adapted to it.

Problem is, I’m not sure how to do that :slight_smile:

Thanks,
SteveOn August 13, 2002 05:54 am, Ryan C. Gordon wrote:

— Stephen Anthony
wrote:

Guess what I want is a way to simulate
write(…) to the soundcard,

and only send stuff to the card when a write is
done (and under my

control).

Keep a buffer of data somewhere. Call
SDL_LockAudio() when putting more
data into that buffer, and SDL_UnlockAudio() when
you are done putting
data into it. (Lock/UnlockAudio guarantees that
the audio callback
won’t run…do NOT use it for extended periods of
time! Just as a mutex
to prevent race conditions. Do not call LockAudio
from inside the
callback, either.)

Yes, that is what I’m doing now. As an experiment,
I set up a mutex (of
sorts) and 2 count variables. In between calls to
SDL_LockAudio() /
SDL_UnlockAudio(), I update the sound buffer
(separate code to do this),
set the mutex to 1 and increment a variable that
counts how many times
the buffer was updated.

In the callback, I check to see if the mutex is 1,
then copy the buffer to
the SDL stream, set mutex to 0, and increment a
variable that says how
many times the stream was copied.

All things being equal, the 2 counts should be the
same at the end of the
run. But they aren’t. There are approx. 4 times as
many attempts as
there are actual updates to the stream. In other
words, only 25% of the
sounds I process are actually sent to the soundcard.
This would
definitely explain the dropouts I’m hearing.

Problem is, I don’t know what causes it. Is the
callback ‘atomic’. That
is, while it’s being called, it can’t be called
again (from the sound
thread)?

Unless it’s recursive, it should not be called again
until it returns. But it is in a seperate thread, it
won’t be at all in sync with the rest of your program.

I’m really lost here. The mutex is set to 1
whenever the buffer is
updated. The callback seems to not ‘see’ many of
these changes. So
maybe the callback isn’t being called often enough??

For me anyway, it is much easier to to think of the
mutex being “locked” or “unlocked” rather than 1 or
0…

The point is the callback isn’t called in sync with
any part of your program, so you should probably make
a mutex-protected ring buffer… The callback can read
out of it, and the rest of your sound code can feed
it… When it becomes empty, the callback should send
the silence value.

As the callback runs, feed SDL as much data as it
wants. If you don’t
have anymore data, feed it silence.

How do you know how much data “SDL wants”? And how
to you feed it silence
(I assume by memset’ing 0 to the stream)?

The SDL_AudioSpec structure has a member value called
"silence" that indicates what silence value to write
to the stream.

This is a different paradigm than direct write()s
to /dev/dsp, and your
code needs to be adapted to it.

Problem is, I’m not sure how to do that :slight_smile:

Hope this helps,

-Loren> On August 13, 2002 05:54 am, Ryan C. Gordon wrote:


Do You Yahoo!?
HotJobs - Search Thousands of New Jobs
http://www.hotjobs.com