Jump to content

  • Log In with Google      Sign In   
  • Create Account

Interested in a FREE copy of HTML5 game maker Construct 2?

We'll be giving away three Personal Edition licences in next Tuesday's GDNet Direct email newsletter!

Sign up from the right-hand sidebar on our homepage and read Tuesday's newsletter for details!


We're also offering banner ads on our site from just $5! 1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


OpenAL Streaming (Queue Buffers)


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
12 replies to this topic

#1 ShyGuy92   Members   -  Reputation: 154

Like
1Likes
Like

Posted 14 August 2010 - 09:42 AM

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


Sponsor:

#2 KulSeran   Members   -  Reputation: 2564

Like
0Likes
Like

Posted 14 August 2010 - 10:07 AM

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.



#3 ShyGuy92   Members   -  Reputation: 154

Like
0Likes
Like

Posted 14 August 2010 - 10:59 AM

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
}


#4 TTK-Bandit   Members   -  Reputation: 120

Like
0Likes
Like

Posted 14 August 2010 - 01:00 PM

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.

#5 KulSeran   Members   -  Reputation: 2564

Like
0Likes
Like

Posted 14 August 2010 - 01:32 PM

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.

#6 ShyGuy92   Members   -  Reputation: 154

Like
0Likes
Like

Posted 14 August 2010 - 03:11 PM

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.

#7 ShyGuy92   Members   -  Reputation: 154

Like
0Likes
Like

Posted 15 August 2010 - 07:54 AM

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?

#8 KulSeran   Members   -  Reputation: 2564

Like
0Likes
Like

Posted 15 August 2010 - 01:14 PM

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").

#9 ShyGuy92   Members   -  Reputation: 154

Like
1Likes
Like

Posted 15 August 2010 - 01:38 PM

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.

#10 TTK-Bandit   Members   -  Reputation: 120

Like
0Likes
Like

Posted 16 August 2010 - 12:45 AM

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.

#11 ShyGuy92   Members   -  Reputation: 154

Like
1Likes
Like

Posted 16 August 2010 - 03:21 AM

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 :)

#12 Ron AF Greve   Members   -  Reputation: 628

Like
1Likes
Like

Posted 16 August 2010 - 04:01 AM

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 ] );

}

}




#13 KulSeran   Members   -  Reputation: 2564

Like
0Likes
Like

Posted 16 August 2010 - 04:31 AM

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™ 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™, 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.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS