OpenAL Streaming (Queue Buffers)

Started by
11 comments, last by KulSeran 13 years, 8 months ago
I have written some code that plays an ogg vorbis file with OpenAL.
My code opens the ogg-file and reads from it whenever a buffer is to be filled.

As a "simple" loop would cost me 100% CPU, I have to sleep a little bit somewhere.
So I set up 4 buffers which will be queued and played one after the other.
After one processed buffer is filled again and queued on the source, I sleep as long as 1 buffer should need to be played. That is (float)PCM_PER_BUFFER/44100*1000 ms.

But it can still happen that the stream stops playing (e.g. when computer is busy I think), which means that all 4 queued buffers were played before new buffers are filled and queued.
But how can that be? I can't imagine that some of my code in the thread takes as long as 3 or 4 buffers.

Does anybody has a tip how I might solve that problem? Is it possible to use callbacks that OpenAL automatically calls as a buffer on a source was processed?


My code looks about like that (pseudo-code):
while(playing){  n = getProcessedBuffers(); // should be 0, 1 or maybe 2. Never 3 or 4...  while(n--) // while there are processed buffers to fill  {    // unqueue processed buffer    alSourceUnqueueBuffers(Source, 1, &b);    cur = getCurrentBuffer(); // buffer num (0-3) to fill    fillBuffer(Buffer[cur]);    alSourceQueueBuffers(Source, 1, &(Buffer[cur]));  }  sleep(sleepTime);}
Advertisement
sleep causes your thread to go to sleep for AT LEAST (x) time. It will not return before that time, but can return any time later (there is nothing that says it is reasonable either, it could be seconds or minutes later depending on what else is running).

You'd probably be better off with condition variables or locks or something. That way you have some control over when the thread will wake up.

Quote:Original post by KulSeran
sleep causes your thread to go to sleep for AT LEAST (x) time. It will not return before that time, but can return any time later (there is nothing that says it is reasonable either, it could be seconds or minutes later depending on what else is running).

You'd probably be better off with condition variables or locks or something. That way you have some control over when the thread will wake up.

If I don't use sleep the thread would use 100% CPU.
I can't lock something from somewere out of the thread as I am working on something else outside that thread.

Using for example a condition variable wouldn't solve it, if I understand what you mean:
while(playing){  if(buffersToFill) // <-- the "condition" variable  {    // fill buffer...  }  // else loop will loop and causes 100% CPU in any case}
why do you get the buffer with getCurrentbuffer, etc ?
alSourceUnqueueBuffers writes the buffer that is unqueued into b, which you can use then:
while(playing){  n = getProcessedBuffers(); // should be 0, 1 or maybe 2. Never 3 or 4...  while(n--) // while there are processed buffers to fill  {    // unqueue processed buffer    alSourceUnqueueBuffers(Source, 1, &b);    fillBuffer(b);    alSourceQueueBuffers(Source, 1, &b);  }  sleep(sleepTime);}


out of curiousity, how big is your buffer ?
I use 4 buffers, each has 16384 bytes, and my audio thread sleeps 20 ms + some work in between, and I have no problems.
No. I meant Condition Variables... but I think I misread your post. You aren't doing this in two threads, like I thought. If you were using two threads, you'd want something like
//in some shared place:mutex bufferLock;condition_variable bufferFilled;//in your loading threadwhile( playing ){	// load some data into a local buffer	aquire_mutex( bufferLock );	// copy that data to the sound buffer	release_mutex( bufferLock );	bufferFilled.signal_one();}// in your sound threadwhile(playing){  aquire_mutex( bufferLock );  filledBuffers.wait( bufferLock );  n = getProcessedBuffers(); // should be 0, 1 or maybe 2. Never 3 or 4...  while(n--) // while there are processed buffers to fill  {    // unqueue processed buffer    alSourceUnqueueBuffers(Source, 1, &b);    fillBuffer(b);    alSourceQueueBuffers(Source, 1, &b);  }  release_mutex( bufferLock );}

But that still assumes you have something else to do in your main thread.
So really, I suggest you make sure to queue up MORE THAN 15ms worth of audio and then only call sleep(1). There is a good chance that your application will get more runtime within about 15ms and be able to add more stuff to the queue.
Quote:Original post by TTK-Bandit
why do you get the buffer with getCurrentbuffer, etc ?
alSourceUnqueueBuffers writes the buffer that is unqueued into b, which you can use then:
*** Source Snippet Removed ***

Okay, I didn't know about the third param on alSourceUnqueueBuffers().

Quote:out of curiousity, how big is your buffer ?
I use 4 buffers, each has 16384 bytes, and my audio thread sleeps 20 ms + some work in between, and I have no problems.


Well, I first tried with 2 buffers sized 1024 bytes each (note that 1 sample might need 4 bytes: Stereo+16Bit), and now I was hoping it works all right with 4 buffers sized 4096 bytes each (1024*4).

As you are saying using 4 buffers sized 16384 bytes each, I should try it.
I was just thinking that it is not usual using 4, and not 2 buffers, or at least not sizing them more than 4096 bytes.

If there is still any problem, I will tell.
It happened again. I listened to a (looping) song for some minutes and it suddenly stopped again. Buffer size is 16384 bytes, and there are 4 buffers.
The calculated sleep time is 92 ms and the song is played at 44.1 kHz.

But the program itself does nothing which might "stop" the song. The main program code just waits for user input (cin.get()) while the thread is updating the processed buffers and sleeping 92ms after all buffers filled and queued.

What might be the problem? Could it be that the program "miss" the time to update the stream and the source stops?
Is it maybe possible to tell the source to loop the last buffer or something until there are new buffers queued?
Quote:
What might be the problem? Could it be that the program "miss" the time to update the stream and the source stops?

Seems you didn't read what I said about sleep. If you say "sleep(92)" the program won't return for ATLEAST 92ms. It doesn't mean it will return after exactly 92ms or before 92ms. It will only return sometime after 92ms have passed. There is a really good chance that you are going to miss your window (windows is granular on sleeps with about 15ms of jitter). You need to sleep for less time, and poll OpenAL to see if the buffers are ready to be filled (looks like "number of processed buffers can be detected using an alSourcei call to retrieve AL_BUFFERS_PROCESSED").
Quote:Original post by KulSeran
Quote:
What might be the problem? Could it be that the program "miss" the time to update the stream and the source stops?

Seems you didn't read what I said about sleep. If you say "sleep(92)" the program won't return for ATLEAST 92ms. It doesn't mean it will return after exactly 92ms or before 92ms. It will only return sometime after 92ms have passed. There is a really good chance that you are going to miss your window (windows is granular on sleeps with about 15ms of jitter). You need to sleep for less time, and poll OpenAL to see if the buffers are ready to be filled (looks like "number of processed buffers can be detected using an alSourcei call to retrieve AL_BUFFERS_PROCESSED").

The calculation of buffer duration results (~)92 ms. That means, with all the lost time for other code in the thread and somewhere else it should be VERY propably that 1 of 4 buffers is processed. That means after sleeping there should be a buffer to fill.
Even if I haven't slept enough and there's no buffer to fill, after 92 more ms there must be 1 or even 2 (of 4) buffers to fill, but (usually) never 3 or 4.

(I have a "semi-solution": If the source stopped for some reason I just resume it. Still I might get a short moment in the stream where just nothing is to be heard.)



Quote:Original post by KulSeran
There is a really good chance that you are going to miss your window (windows is granular on sleeps with about 15ms of jitter).

What do you mean by that? What does a thread in the background have to do with a window?


Quote:Original post by KulSeran
You need to sleep for less time, ...

But if I sleep less then 92ms (the duration of a single buffer) it is more propably that one buffer is processed yet and I waste CPU.
nah, the check to see if a buffer is available is such a short command, that it probably won't be noticeable.
I have 20ms and barely see any cpu usage for the audio thread, even with ogg decompression going on for music.
I'd say you should at least try to see if it fixes the problem.. if not, you at least know that it's not the time.

Also I would suggest using openal soft, It's more up to date, but still uses (almost) the same interface, so you don't need to rewrite your code for the change.

This topic is closed to new replies.

Advertisement