The SDL forums have moved to discourse.libsdl.org.
This is just a read-only archive of the previous forums, to keep old links working.


SDL Forum Index
SDL
Simple Directmedia Layer Forums
SDL Audio Specs and Samples, and pulseaudio
Matt Scheirer


Joined: 10 Jul 2013
Posts: 1
So I've been writing an SDL backend for an emulator, it works, but in the process I ran into some issues.

First, SDL_OpenAudio has a quip in it that confuses me,

Quote:
SDL_OpenAudio() is legacy and can only act on Device ID #1.


is there another preferred more modern way to open an audio sync given an AudioSpec, that can use multiple simultaneous devices? I don't need it for this project, but I probably will if I want multiple out sinks in the future, and a cursory google search and forum search didn't turn up anything. What is the story on this?

Second, in the signature of OpenAudio,

Code:
int SDL_OpenAudio(SDL_AudioSpec * desired, SDL_AudioSpec * obtained)


on certain backends, and this is documented, can mutate the spec given even if you pass a null obtained spec in terms of its sample count or buffer size. However, I had a nasty run debugging why even with an obtained spec SDL still mutates the desired spec - my impression is that it should probably be const, or at least in the presence of an obtained spec telling you the actual audio buffer you are getting, or have a note in the documentation. It was just inherently unintuitive that both specs get mutated if you proivde a desired and obtained one, and it makes me have to do some hacky coding whenever I reinit SDL audio to reset the sample count in my desired spec. Just seems unintuitive.

I'm also curious if there is a story behind why the callback for the AudioSpec:

Code:
(void *userdata, Uint8 * stream,  int len);


Uses an int, where every other integral value in SDL uses either Sint32 or Uint32. I mean you can read the size of the buffer from the AudioSpec after opening it as a Uint32, so whats up with this? There is no documentation what a negative value would mean here, if it is intentional. Just seemed odd considering the pervasive use of Uint32 for the buffer.

While investigating why this was happening (and it always happens in pulseaudio) I was digging in PA and found this block:

Code:

    /* Calculate the final parameters for this audio specification */
#ifdef PA_STREAM_ADJUST_LATENCY
    this->spec.samples /= 2; /* Mix in smaller chunck to avoid underruns */
#endif
    SDL_CalculateAudioSpec(&this->spec);

    /* Allocate mixing buffer */
    h->mixlen = this->spec.size;


Code:

    /* Reduced prebuffering compared to the defaults. */
#ifdef PA_STREAM_ADJUST_LATENCY
    /* 2x original requested bufsize */
    paattr.tlength = h->mixlen * 4;
    paattr.prebuf = -1;
    paattr.maxlength = -1;
    /* -1 can lead to pa_stream_writable_size() >= mixlen never being true */
    paattr.minreq = h->mixlen;
    flags = PA_STREAM_ADJUST_LATENCY;
#else
    paattr.tlength = h->mixlen*2;
    paattr.prebuf = h->mixlen*2;
    paattr.maxlength = h->mixlen*2;
    paattr.minreq = h->mixlen;
#endif

If you want to prevent underruns, you adjust minreq, I don't get why this is always cutting sample count in half. I specify a sample poll rate I think is appropriate, and having to test on every sound backend to see which ones slice the sample count (in the documentation, SDL says 512 is a minimum, but since Pulse cuts that in half any time you run this on PA version > 1.0, it causes distortion and noise on some machines I've tested on which seems against the intent.

Additionally, these audio buffers are all bigger than the length from the spec anyway. I know from experience pulse can handle a 2 * 2 * 512 buffer size just fine (512 stereo 16 bit samples), polling at 48 khz. I just don't get why you would intentionally mangle user provided values they want when you don't have to. Rather than guessing an optimal buffer size, pulseaudio has a method given context and spec about what size to make spec->samples & size relative to the returned PA buffer(similar to how alsa does it - supply your desired values, calcuate real ones based off what the backend says):

Code:
size_t pa_context_get_tile_size(pa_context *c, const pa_sample_spec *ss)


The function has been available since 0.9.2, too.

That problem doesn't happen in ALSA, which, while it does reset samples after setup, usually gets back the same sample count it puts into the backend. The way you would expect (ie, pass in num frames and size of a frame, get out a number of frames per pass, set samples to half that because stereo samples are 2 frames each). Though humorously:

Code:
 
/* !!! FIXME: Is this safe to do? */
 this->spec.samples = bufsize / 2;


From alsa.c, only works with stereo. Because 2 channels. There is some surround support, I haven't tested it, I don't know if that will murder babies if you try running surround sound on alsa at the moment. So if you give it surround audio, you do mangle it. I just found this while skimming the alsa implementation.

Just some thoughts. It works, I just think the pulseaudio module could be improved to not wreck the user-requested sample rate. Should I try patching it, or am I missing some genius here?
SDL Audio Specs and Samples, and pulseaudio
Ryan C. Gordon
Guest

Quote:
Quote:
SDL_OpenAudio() is legacy and can only act on Device ID #1.

is there another preferred more modern way to open an audio sync

Use SDL_OpenAudioDevice(). SDL_OpenAudio() is just there for people
migrating from SDL 1.2 (or have simple needs), since SDL 1.2 only
allowed a single audio device.

Quote:
Code:
int SDL_OpenAudio(SDL_AudioSpec * desired, SDL_AudioSpec * obtained)

on certain backends, and this is documented, can mutate the spec given
even if you pass a null obtained spec in terms of its sample count or
buffer size. However, I had a nasty run debugging why even with an
obtained spec SDL still mutates the desired spec - my impression is that
it should probably be const

The legacy function isn't const because it wasn't in SDL 1.2, but
SDL_OpenAudioDevice() _does_ make (desired) const. I don't think that
SDL_OpenAudio() should ever actually change this struct in SDL 2.0,
either. If that's still happening for you, let's talk about that as an
SDL bug that should be resolved.

Quote:
I'm also curious if there is a story behind why the callback for the
AudioSpec:
Uses an int, where every other integral value in SDL uses either Sint32
or Uint32. I mean you can read the size of the buffer from the AudioSpec
after opening it as a Uint32, so whats up with this?

It's just an accident of history. We never changed it to keep things
binary compatible. Maybe we should fix that after 2.0 ships. This
variable is never (should never be, at least) negative.

Quote:
If you want to prevent underruns, you adjust minreq, I don't get why
this is always cutting sample count in half. I specify a sample poll

It's possibly a bug. I'll look into it tonight and figure out why we did
that.

Quote:
Should I try patching it, or am I missing some genius here?

It could go either way. Smile I'll do some research and get back to you
on this tonight.

--ryan.


_______________________________________________
SDL mailing list

http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org