• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
coyote-development

OpenAL Streaming (Queue Buffers)

12 posts in this topic

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);
}
1

Share this post


Link to post
Share on other sites
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.

0

Share this post


Link to post
Share on other sites
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
}
0

Share this post


Link to post
Share on other sites
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.
0

Share this post


Link to post
Share on other sites
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 thread
while( 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 thread
while(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.
0

Share this post


Link to post
Share on other sites
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.
0

Share this post


Link to post
Share on other sites
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?
0

Share this post


Link to post
Share on other sites
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").
0

Share this post


Link to post
Share on other sites
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.
2

Share this post


Link to post
Share on other sites
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.
0

Share this post


Link to post
Share on other sites
Okay, I changed sleep time from 92ms to 20ms, and I don't notice any more CPU usage (program uses 0-3% CPU). I listened to a stream for many minutes and there seemed to be no interrupt.

Maybe that's really the solution now :)
2

Share this post


Link to post
Share on other sites
I had the same problem. When the buffers are depleted it stopped. I solved it by remembering what state it should be in then querying OpenAL for the state it is in. When it should be playing but isn't I just call alSourcePlay again:


if( Stop && BufferQueue.empty() )

{

InfoLog << "Stopping sound because we reached end of file" << endl;

alSourceStop( Sources[ 0 ] );

State = eStopped;

Reset();

}

else

{

ALenum ALState = 0;

alGetSourcei( Sources[ 0 ], AL_SOURCE_STATE, &ALState);

if( ALState != AL_PLAYING)

{

InfoLog << "Starting sound (or resuming sound because we ran out of buffers)" << endl;

alSourcePlay( Sources[ 0 ] );

}

}


2

Share this post


Link to post
Share on other sites
Quote:

Original post by coyote-development
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?

I assumed you were working on Windows(tm) the operating system. Linux has similar issues with sleep, but the scheduler is different and so the jitter is different (and I don't recall the average skew on a sleep). On Windows(tm), sleeps tend to skew to the next multiple of 15ms. So, by sleeping for too much time, there is a good chance your app "misses" the opportunity to update the buffers.
0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0