question about SDL_audio |
jeroen.clarysse
|
i’ve been looking at the loopwave sample, but can find out where the actual LOOPING happens…
there is a call to LoadWav to fill the SDL_AudioSpec structure, then a call to SDL_OpenAudio to open the audio device, and then SDL_PauseAudio(0) to start playing the callback fillerup does the actuall buffer filling: fillerup(void *unused, Uint8 * stream, int len) { Uint8 *waveptr; int waveleft; /* Set up the pointers */ waveptr = wave.sound + wave.soundpos; waveleft = wave.soundlen - wave.soundpos; /* Go! */ while (waveleft <= len) { SDL_memcpy(stream, waveptr, waveleft); stream += waveleft; len -= waveleft; waveptr = wave.sound; waveleft = wave.soundlen; wave.soundpos = 0; } SDL_memcpy(stream, waveptr, len); wave.soundpos += len; } the first two lines are just declarations the next two lines determine where to start filling and how much there will be left of the data after filling then there is a loop until we reached the passed amount (len) where is the actual repeating of the sound done ? _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
|||||||||||
|
question about SDL_audio |
David Olofson
Guest
|
On Tue, Apr 19, 2016 at 12:03 PM, jeroen clarysse
wrote: [...]
It's a bit "backwards," as the looping is in terms of the source index (wave.soundpos) rather than the write index. :-) What happens is, if there's less than a full output buffer's worth of wave data left, the while() loop is entered, takes care of that, resets the index, and then the final SDL_memcpy() takes care of any remaining samples. The reason it's while() and not if() is that the output buffer may be larger than the wave. Typically, it'll be the other way around, so the while() will only be entered when the loop point falls within the current output buffer. -- //David Olofson - Consultant, Developer, Artist, Open Source Advocate .--- Games, examples, libraries, scripting, sound, music, graphics ---. | http://consulting.olofson.net http://olofsonarcade.com | '---------------------------------------------------------------------' _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
|||||||||||||||
|
question about SDL_audio |
jeroen.clarysse
|
aha… that explains it a bit… but where do I turn off the looping ? I just want to play the sound ONCE...
_______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
|||||||||||||||||
|
question about SDL_audio |
David Olofson
Guest
|
On Tue, Apr 19, 2016 at 12:57 PM, jeroen clarysse
wrote:
Well, the "wave.soundpos = 0;" line is where the wave is looped. Remove that, replace the final SDL_memcpy() with an SDL_memset() or something (in SDL2, buffer contents is undefined when the callback is entered!), and remove the final "wave.soundpos += len;", so it just sits there outputting silence after the end of the wave. Something like that. I leave it as an exercise for the reader to implement and debug the logic. ;-) -- //David Olofson - Consultant, Developer, Artist, Open Source Advocate .--- Games, examples, libraries, scripting, sound, music, graphics ---. | http://consulting.olofson.net http://olofsonarcade.com | '---------------------------------------------------------------------' _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
|||||||||||||
|
question about SDL_audio |
jeroen.clarysse
|
thanks for that info !!
but I’m still a bit stuck about the whole concept of that callback : you are provided with : * a pointer to userdata that you can use to fetch audio from * a stream that you need to copy audio into * a len parameter than determines the size of stream so you don’t copy out-of-bounds The documentation says that this callback is invoked "with some frequencyâ€. - will the stream always be “at the start†? Or do I have to fill it somewhere in the middle sometimes ? - do I have to fill the stream from 0 to len-1 ? - why do all the sample-code tutorials use a loop to fill the stream ? Why not just fill it with ONE big SDL_memcpy of size min(len, size_of_sound_file) and then fill the rest with zeroes in one additional memcpy if size_of_sound_file < len ?? I don’t see the point of this while loop sorry if I come over as a newbie, but the documentation is a bit fuzzy to me
_______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
|||||||||||||||
|
question about SDL_audio |
jeroen.clarysse
|
oh, and I forgot one part that is also bothering me : if I want to play a short audio at irregular intervals, should I call SDL_OpenAudio at the moment where I want to start playing, and start SDL_PauseAudio(0)at the beginning and leave it like that throughout the whole session ? Or should I call SDL_PauseAudio(1) whenever the sound is done ?
I find it difficult to understand which calls I need to use : - SDL_LoadWAV is used to fetch the wav file from disk - SDL_OpenAudio is used to initialize the callback - SDL_PauseAudio is used to start the callback-loop can I call SDL_CloseAudio from within the callback when my sound is done, and then have it re-open at a later time, or is this a slow/stupid thing to do ? Should I call SDL_OpenAudio also at the beginning of the session ? how then can my callback know when to actually start playing ? i’m really sorry to come over so un-educated _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
|||||||||||
|
question about SDL_audio |
David Olofson
Guest
|
On Fri, Apr 22, 2016 at 11:17 AM, jeroen clarysse
wrote: [...]
It's really just a buffer from the callback's perspective.
Yes.
Haven't examined the other examples closely, but I suspect the idea for the looping one(s?) is that it's expected to do the right thing (as in, loop seamlessly) even if the output buffer is longer than the waveform. But if you're only ever copying one fragment of the waveform in each callback, sure; the loop is unnecessary.
Well, this is a low level API for low latency realtime audio, and it's based on the same principles as any other audio API of this type. So, you're not really supposed to go that low level unless you're implementing an audio engine or similar - and if you are, you're expected to be familiar with the relevant concepts. In short, it's an API for weirdos like yours truly. ;-) -- //David Olofson - Consultant, Developer, Artist, Open Source Advocate .--- Games, examples, libraries, scripting, sound, music, graphics ---. | http://consulting.olofson.net http://olofsonarcade.com | '---------------------------------------------------------------------' _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
|||||||||||||||||||
|
question about SDL_audio |
jeroen.clarysse
|
awesome. Many thanks for all that info. I get one step closer to actually playing audio :-)
I’ve contemplated using SDL_mixer, which is probably the best thing to do, but I also want as little latency as possible : for our program, getting the sound out the speakers as fast as possible is rather crucial. I know that there are a ton of variables beyond my control (such as the operating system) so we will have to add a lot of testing to figure out how variable the actual delay between “start sound†and “hearing sound†is. I plan to loop the audio into a second computer together with a TTL signal from the first computer, which gets raised when “start sound†is issued. That should give me an idea of the latencies
_______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
|||||||||||||||||||||
|
question about SDL_audio |
David Olofson
Guest
|
On Fri, Apr 22, 2016 at 11:25 AM, jeroen clarysse
wrote:
The typical use of SDL_PauseAudio() is just to enable the audio callback once, as soon as you're ready for action. (An audio engine typically needs to allocate buffers, calculate filter cores and whatnot, and you need to know the actual sample rate, buffer size etc before you can do that. That's why audio starts paused, so you don't have the callback firing in the middle of initialization.)
Just a utility function. If you have your own loaders, use external file I/O libs, you don't need this.
Yep.
You might get away with this in some environments, but generally speaking, this is asking for a crash or deadlock. Don't do that! :-) Basically, you should assume that the audio callback runs in interrupt context (which it actually does on some old SDL 1.2 platforms!), which means no system calls, no memory allocations, no locks, no blocking calls etc. (This is actually a pretty hairy subject...)
(That's actually related to the aforementioned hairy subject.) Typically, you'll have an audio engine running via the callback, and the application tells the callback what to do via some form of state updates, messages or similar. This should preferably be lock free to avoid priority inversion, which can result in audio glitches. In very simple cases, you can get away with just having some atomic values (integers typically) that the callback checks and responds to, but this quickly becomes a lot more complicated than it sounds. (Start by reading up on "race conditions" if you're interested in that.) What I usually do is use a lock-free FIFO or similar construct, to safely pass proper "commands" to the audio callback. If the application needs to know what the audio callback is up to, you can use another lock-free FIFO to pass messages in the other direction. There are a few different implementations of lock-free FIFOs around.
No worries. This subject is a lot more complicated than it may seem at first, and that's why most people just use some existing audio engine with a higher level API instead. :-) -- //David Olofson - Consultant, Developer, Artist, Open Source Advocate .--- Games, examples, libraries, scripting, sound, music, graphics ---. | http://consulting.olofson.net http://olofsonarcade.com | '---------------------------------------------------------------------' _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
|||||||||||||||||||||||
|
question about SDL_audio |
jeroen.clarysse
|
reading all this, I am reconsidering my choice to not use SDL_mixer. I guess I’m only making things harder for myself at the cost of instability
_______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
|||||||||||||||||||||||||
|
question about SDL_audio |
David Olofson
Guest
|
On Fri, Apr 22, 2016 at 1:29 PM, jeroen clarysse
wrote:
Yeah... This might not be something you want to dive into unless you really have to, or you're really interested in learning about it. As to SDL_mixer, AFAIK, it doesn't add any extra buffering, so it shouldn't affect latency. Be warned though, that it uses locking to synchronize API calls with the audio context, which is no a great solution if you have zero tolerance for glitches. That said, locking is probably not your biggest issue, unless you're on a finely tuned audio workstation, an embedded system with a Linux lowlatency kernel or similar. -- //David Olofson - Consultant, Developer, Artist, Open Source Advocate .--- Games, examples, libraries, scripting, sound, music, graphics ---. | http://consulting.olofson.net http://olofsonarcade.com | '---------------------------------------------------------------------' _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
|||||||||||||
|
question about SDL_audio |
jeroen.clarysse
|
the amount of sounds played will be very small : usually only one sound and a very very short one (50 msec)
I’m in charge of writing the code for our psychology experiments where we “startle†subjects with a very short but loud “probe†(around 100dB, so very loud) and then measure their eyeblink response time. usually, such sound probes are administered via hardware units that get triggered via a TTL signal from a PC, but i’m investigating the accuracy when using software sound via a PC. i’m rather curious about the results :-)
_______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
|||||||||||||||
|
question about SDL_audio |
David Olofson
Guest
|
On Fri, Apr 22, 2016 at 2:17 PM, jeroen clarysse
wrote:
Doesn't that mean that controlling (or even just knowing) exactly when the probe sound is played, is more important than "command latency?" If so, I'm not sure using minimal buffer sizes and just trying to keep latency low is the way to go. That only makes it harder to guarantee stability and accuracy, especially if you're on a general purpose OS without proper realtime scheduling. Theoretically, you could use huge buffers and still achieve sub-sample accurate timing, but the tricky part is keeping track of exactly when each buffer reaches the audio outputs. Most audio APIs are severely lacking in that department, and the best you can do is measure the latency from callback to output, and hope that's consistent. The only way to be sure is to use dedicated hardware. If you're writing an application that people will run on random computers, I'm afraid things are going to have to be configured and calibrated for each install. Oh, and there's plenty of "fun" stuff to discover in that area, like this: ;-) https://github.com/olofson/audiality2/issues/209 (Haven't really solved that. Just reduced the risk of running into it by changing the default sample rate to 48 kHz, which seems to be much more common than 44.1 kHz these days.) -- //David Olofson - Consultant, Developer, Artist, Open Source Advocate .--- Games, examples, libraries, scripting, sound, music, graphics ---. | http://consulting.olofson.net http://olofsonarcade.com | '---------------------------------------------------------------------' _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
|||||||||||||
|
question about SDL_audio |
jeroen.clarysse
|
yep. Latency itself is irrelevant as long as it is low in variance and not excessive. I’d prefer to have a constant latency between 95 and 100 milliseconds over a variable latency between 20 and 50 milliseconds.
hm.. and can SDL_mixer manipulate that buffer size ? I’ll build my app with configurability in mind so those settings can be read from a config file. I guess I’ll have to play with these options a bit
yup : we will need some sort of calibration box. The biggest issue with such a box is : you need at least ONE way to communicate accurately (in terms of timing) with that box ! If the box itself has a latency when receiving the “start calibration†command, you’re still in limbo. And USB isn’t perfect on all computers either ! so far, we use TTL, but parallel ports have become obsolete, so that is an issue as well
I couldn’t even understand half of what you were explaining :-) _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
|||||||||||||||||||||
|
question about SDL_audio |
David Olofson
Guest
|
On Fri, Apr 22, 2016 at 2:43 PM, jeroen clarysse
wrote: [...]
Mix_OpenAudio[Device]() just forwards the requested buffer size to SDL_OpenAudioDevice(), so the behavior should be the same.
Yeah... The easiest "fool proof" way is usually to somehow turn the relevant events into analog audio signals, and record all sources using one or more channels on another sound card. (A small USB sound interface for studio use, for example.) Any reasonably well designed sound card should have zero phase difference between channels, so you should get extremely accurate measurements that way, even if you use one input for each source.
Speaking of which, this thing seems like a perfect platform for things like this: http://bela.io/ [...]
TL;DR: Make sure you're using the native sample rate of the hardware, to avoid weird callback timing. :-) -- //David Olofson - Consultant, Developer, Artist, Open Source Advocate .--- Games, examples, libraries, scripting, sound, music, graphics ---. | http://consulting.olofson.net http://olofsonarcade.com | '---------------------------------------------------------------------' _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
|||||||||||||||||||||||||
|
question about SDL_audio |
jeroen.clarysse
|
that is actually a VERY COOL idea !
unfortunately that is still in kickstarter-phase… I was thinking arduino for now _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
|||||||||||||||||
|
question about SDL_audio |
Ryan C. Gordon
Guest
|
SDL_OpenAudio() tends to be an expensive call (SDL has to obtain hardware from the operating system, things can go wrong, etc). SDL_PauseAudio() just sets a flag and returns, so you probably want to do that. Once the audio is unpaused, within a few milliseconds, your callback will start firing on a different thread.
I'm not sure it would be safe to call CloseAudio from inside the callback. I've never tried. It's probably safe to pause the audio from inside the callback, but: I'd recommend you just write silence to the output stream and return immediately in the callback if you don't have anything to play at the moment, and leave it unpaused all the time. Open the device at program startup, close it at shutdown. This is efficient and safe.
No worries, the audio subsystem is sort of hard to understand at first glance. --ryan. _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
|||||||||||||||||
|
question about SDL_audio |
Ryan C. Gordon
Guest
|
SDL_OpenAudio() tends to be an expensive call (SDL has to obtain hardware from the operating system, things can go wrong, etc). SDL_PauseAudio() just sets a flag and returns, so you probably want to do that. Once the audio is unpaused, within a few milliseconds, your callback will start firing on a different thread.
I'm not sure it would be safe to call CloseAudio from inside the callback. I've never tried. It's probably safe to pause the audio from inside the callback, but: I'd recommend you just write silence to the output stream and return immediately in the callback if you don't have anything to play at the moment, and leave it unpaused all the time. Open the device at program startup, close it at shutdown. This is efficient and safe.
No worries, the audio subsystem is sort of hard to understand at first glance. --ryan. _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
|||||||||||||||||
|
question about SDL_audio |
Ryan C. Gordon
Guest
|
SDL_OpenAudio() tends to be an expensive call (SDL has to obtain hardware from the operating system, things can go wrong, etc). SDL_PauseAudio() just sets a flag and returns, so you probably want to do that. Once the audio is unpaused, within a few milliseconds, your callback will start firing on a different thread.
I'm not sure it would be safe to call CloseAudio from inside the callback. I've never tried. It's probably safe to pause the audio from inside the callback, but: I'd recommend you just write silence to the output stream and return immediately in the callback if you don't have anything to play at the moment, and leave it unpaused all the time. Open the device at program startup, close it at shutdown. This is efficient and safe.
No worries, the audio subsystem is sort of hard to understand at first glance. --ryan. _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
|||||||||||||||||
|
question about SDL_audio |
Ryan C. Gordon
Guest
|
Another option, if you have SDL 2.0.4, is to specify NULL for a callback and use SDL_QueueAudio() to push more sound data when it's available and let SDL handle the details for you. /** * Queue more audio on non-callback devices. * * SDL offers two ways to feed audio to the device: you can either supply a * callback that SDL triggers with some frequency to obtain more audio * (pull method), or you can supply no callback, and then SDL will expect * you to supply data at regular intervals (push method) with this function. * * There are no limits on the amount of data you can queue, short of * exhaustion of address space. Queued data will drain to the device as * necessary without further intervention from you. If the device needs * audio but there is not enough queued, it will play silence to make up * the difference. This means you will have skips in your audio playback * if you aren't routinely queueing sufficient data. * * This function copies the supplied data, so you are safe to free it when * the function returns. This function is thread-safe, but queueing to the * same device from two threads at once does not promise which buffer will * be queued first. * * You may not queue audio on a device that is using an application-supplied * callback; doing so returns an error. You have to use the audio callback * or queue audio with this function, but not both. * * You should not call SDL_LockAudio() on the device before queueing; SDL * handles locking internally for this function. * * \param dev The device ID to which we will queue audio. * \param data The data to queue to the device for later playback. * \param len The number of bytes (not samples!) to which (data) points. * \return zero on success, -1 on error. * * \sa SDL_GetQueuedAudioSize * \sa SDL_ClearQueuedAudio */ extern DECLSPEC int SDLCALL SDL_QueueAudio(SDL_AudioDeviceID dev, const void *data, Uint32 len); --ryan. _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
|||||||||||||
|