How to safely stop audio callback

Hi guys,

I’m trying to implement sound in our application using SDL.

I’m almost happy with the stuff I have so far, but I would not be able to get
discent sleep if I don’t know how to safely stop audio callback. :wink:

At the moment, what I’m doing is:

void audio_callback (void* unused, Uint8 audio_buff, Uint32 audio_len)
{
/
Set up the pointers */
Uint8 *ptr = ps_audio_buff + ps_audio_pos;
Uint32 len_play = min(ps_audio_len - ps_audio_pos, audio_len);

SDL_MixAudio(audio_buff, ptr, len_play, SDL_MIX_MAXVOLUME);
ps_audio_pos += len_play;

if (ps_audio_pos >= ps_audio_len)
{
    SDL_CloseAudio();
    SDL_FreeWAV(ps_audio_buff);
}

}

… somehow SDL_CloseAudio () and SDL_FreeWAV () don’t seem right, where they
are (audio_callback() is executed in a separate thread), and I’m getting
suspicious:
audio: Bad file descriptor

Any ideas? Thank you.
Dishlav

Hi guys,

I’m trying to implement sound in our application using SDL.

I’m almost happy with the stuff I have so far, but I would not be able to get
discent sleep if I don’t know how to safely stop audio callback. :wink:

You should set a global variable and then stop the sound from your main
thread during housekeeping, if that variable is set.

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

Thank you Sam,

but your solution I know already. It works but is not applicable to my
application (at least not at the current stage)

You see, I want to stop playing that bloody wave file :wink: after one go, and
then have the thread that calls the callback terminated.

There should be some mechanism to tell from inside the callback that it’s job
is done, so the SDL would not call it any more - maybe something like this:
bool callback(void *userdata, Uint8 *stream, int len);
instead of
void callback(void *userdata, Uint8 *stream, int len);

bool would mean:
true - keep calling the callback;
false - don’t call the callback anymore.

unfortunatly this solution isn’t currently implemented. What can I do now?

Regards
DishlavOn Friday 14 September 2001 12:34, you wrote:

You should set a global variable and then stop the sound from your main
thread during housekeeping, if that variable is set.

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


SDL mailing list
SDL at libsdl.org
http://www.libsdl.org/mailman/listinfo/sdl

Thank you Sam,

but your solution I know already. It works but is not applicable to my
application (at least not at the current stage)

You see, I want to stop playing that bloody wave file :wink: after one go, and
then have the thread that calls the callback terminated.

There should be some mechanism to tell from inside the callback that it’s job
is done, so the SDL would not call it any more - maybe something like this:
bool callback(void *userdata, Uint8 *stream, int len);
instead of
void callback(void *userdata, Uint8 *stream, int len);

bool would mean:
true - keep calling the callback;
false - don’t call the callback anymore.

unfortunatly this solution isn’t currently implemented. What can I do now?

Usually what I do is just not fill the buffer if the wave is finished,
from within the callback…

i.e. return early if there’s nothing to play.

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

But then your thread takes system resources…

Regards
DishlavOn Friday 14 September 2001 14:12, you wrote:

Usually what I do is just not fill the buffer if the wave is finished,
from within the callback…

i.e. return early if there’s nothing to play.

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


SDL mailing list
SDL at libsdl.org
http://www.libsdl.org/mailman/listinfo/sdl

But then your thread takes system resources…

…then set a global variable and shut the thread down from the main thread.

I’m sorry if you don’t like this answer. We can look at it for the next
development branch, but changing the callback behaviour now will almost
certainly break programs that are expecting it to return void.

–ryan.

…then set a global variable and shut the thread down from the main
thread.

Sometimes you simply can’t do that from the main thread (heard about event
driven environment?)

I’m sorry if you don’t like this answer.

No, I don’t…

development branch, but changing the callback behaviour now will almost
certainly break programs that are expecting it to return void.

That’s something what I can uderstand…
… and I do appreciate your time.

Till the better version, regards
Dishlav

Sometimes you simply can’t do that from the main thread (heard about event
driven environment?)

I have, and most event driven environments, somewhere in their core, have
something like this:

event_type event;
do {
getnextevent(&event);
dispatch_event(&event);
} while (event.type != event_quit);

Perhaps you can put the check in there?

A better question would be, “what environment is this?” An MFC program? A
Qt program? There is a solution somewhere for most of them; let’s find it.

Alternately, there’s no reason such a return value can’t be added to your
local copy of SDL’s audio callback (or have the code in SDL check a global
variable before calling the callback), I just don’t imagine it’d be wise
to put it in the official source tree right now.

–ryan.

…then set a global variable and shut the thread down from the main
thread.

Sometimes you simply can’t do that from the main thread (heard about event
driven environment?)

Usually what happens is that the audio callback becomes a sound server,
handling requests from the main thread (or other event sources). The
audio runs continuously during this process.

If the audio only needs to run for a brief period, the main application
should open the device, play the audio, and close the audio when it’s done.

Other ways of managing audio will be considered in the future, but that’s
the way it works now.

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

Ryan,

I thought that my last mail would end the thread :-), and I think I will hack
SDL’s code localy.

Once again many thanks,
DishlavOn Friday 14 September 2001 14:58, you wrote:

Sometimes you simply can’t do that from the main thread (heard about
event driven environment?)

I have, and most event driven environments, somewhere in their core, have
something like this:

event_type event;
do {
getnextevent(&event);
dispatch_event(&event);
} while (event.type != event_quit);

Perhaps you can put the check in there?

A better question would be, “what environment is this?” An MFC program? A
Qt program? There is a solution somewhere for most of them; let’s find it.

Alternately, there’s no reason such a return value can’t be added to your
local copy of SDL’s audio callback (or have the code in SDL check a global
variable before calling the callback), I just don’t imagine it’d be wise
to put it in the official source tree right now.

–ryan.


SDL mailing list
SDL at libsdl.org
http://www.libsdl.org/mailman/listinfo/sdl

But then your thread takes system resources…

…then set a global variable and shut the thread down from the main thread.

That would probably work (though I haven’t been following too closely) but…

I’m sorry if you don’t like this answer. We can look at it for the next
development branch, but changing the callback behaviour now will almost
certainly break programs that are expecting it to return void.

This doesn’t seem to be the case. It would not break any code. It probably
won’t even generate compiler warnings.

Heck, you treat a function with a return value this way every time
you call printf() and don’t assign the return value to anything. (That’s just
one example; other functions are treated the same way).

Unless some architecture I’m not aware of uses some kind of screwed up
calling convention that I’ve never heard of (educate me, please…) adding
a return value to a function previously declared as void should break nothing.

It might be a bad idea in this case for other reasons but I’ll leave that
to some else.

(OK, I’ll mention one thing; it should break if a hypothetical new version
of libSDL was used with code built for an older one where callbacks were
declared as void. The compiler won’t generate code to assign any particular
value to the register through which the bool would be returned, ie eax, so
the callback would effectively return garbage or undefined values resulting
in undefined behaviour. An incorrect version of the library might not work
for other reasons though so I don’t know if it’s something to worry about.)On Fri, Sep 14, 2001 at 12:24:12AM -0400, Ryan C. Gordon wrote:

–ryan.


Greg V. (hmaon)

This doesn’t seem to be the case. It would not break any code. It probably
won’t even generate compiler warnings.

It will.

#include <stdio.h>

void mycallback(int x)
{
}

int (*callback)(int x) = mycallback;

int main(int argc, char **argv)
{
printf("%d\n", callback(5));
return(0);
}

[icculus at gemini ~]$ gcc -o test test.c
test.c:7: warning: initialization from incompatible pointer type
[icculus at gemini ~]$ ./test
1074801592

Heck, you treat a function with a return value this way every time
you call printf() and don’t assign the return value to anything. (That’s just
one example; other functions are treated the same way).

That is legal; the return value from printf() is an expression without
effect (or something like that). You can throw away a return value, but
you can’t safely demand a return value when one is not forthcoming. "void"
does not mean “set the return value register to 0x00, just in case” on ANY
platform.

Unless some architecture I’m not aware of uses some kind of screwed up
calling convention that I’ve never heard of (educate me, please…) adding
a return value to a function previously declared as void should break nothing.

See above.

(OK, I’ll mention one thing; it should break if a hypothetical new version
of libSDL was used with code built for an older one where callbacks were
declared as void. The compiler won’t generate code to assign any particular
value to the register through which the bool would be returned, ie eax, so
the callback would effectively return garbage or undefined values resulting
in undefined behaviour. An incorrect version of the library might not work
for other reasons though so I don’t know if it’s something to worry about.)

  1. It shouldn’t cleanly assign to the callback function pointer if we
    change it.
  2. It will still assign, however, while the callback declared as
    returning void will not properly fill in eax (on Intel platforms).
  3. SDL’s audio gets a “return value” with random garbage, which will
    randomly stop the audio callback.
  4. Even if 1, 2, and 3 weren’t true, it WILL break binary compatibility,
    which is what I was concerned with in this case. Even between 1.0 and 1.2,
    this sort of thing should be avoided, when reasonable. Dropping this
    between 1.2.2 and 1.2.3? No way.

–ryan.

First of all, there’s a very good reason not to stop the audio driver:
If you do, you’ll lose track of “audio time”, and more importantly,
you’ll introduce unspecified start latency and on some cards, popps and
clicks. (Seen this on several cards under Win32 and Linux.)

In short, if you can about audio quality at all DON’T stop the driver
unless you need to change parameters, or until you terminate you program!

If you really have to keep things synchronous (or whatever it is you
need to do - I’m not sure I get the idea), and global flags aren’t
sufficient, there are more sophisticated approaches. For example, you can
set up lock-free FIFOs, or other forms of thread safe event queues
between the audio callback and the main thread, so that you can pass
"commands" in both ways. Then you just hook the commands up to perform
certain actions, and/or change the state of a state machine on each side
of the FIFOs/queues.

//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 -'On Friday 14 September 2001 06:43, Zdzislaw Sliwinski wrote:

…then set a global variable and shut the thread down from the main
thread.

Sometimes you simply can’t do that from the main thread (heard about
event driven environment?)