Sign in to follow this  
gxsh

How to Loop Stream In OpenAl?

Recommended Posts

This is my code piece,I want to replay a stream (mp3/ogg) when it is over. The piece of code is in my "Update(...)" function which called every 200 ms. But the stream can not be replayed when it is over. I don't know why. Somebody gives me a clue ? Thx.
void  COggFileWrap::Update()
{
...
alGetSourcei(m_uiSource, AL_SOURCE_STATE, &m_iState ) ;
if (m_iState != AL_PLAYING)
{
	// If there are Buffers in the Source Queue then the Source was starved of audio
	// data, so needs to be restarted (because there is more audio data to play)
	alGetSourcei(m_uiSource, AL_BUFFERS_QUEUED, &m_iQueuedBuffers);
	if (m_iQueuedBuffers)
	{
		alSourcePlay(m_uiSource);
	}
	else
	{
		// the last cycle
		if( m_iLoop == 0 )
		{

		}
		// not last cycle
		else if( m_iLoop > 0 )
		{
			alSourceRewind( m_uiSource );
			ConfigBuffers() ;
			alSourcePlay(m_uiSource);      // doesn't work!!
			m_iLoop -- ;
		}
		// infinite cycle
		else
		{
			alSourceRewind( m_uiSource );
			ConfigBuffers() ;
			alSourcePlay(m_uiSource);      // doesn't work !!
		}
	}
}
...
}

// In this function the source and buffers are already generated,just use it.
// DecodeOggVorbis is a function from the official sample code .

void COggFileWrap::ConfigBuffers()
{
	unsigned long	ulBytesWritten ;

	for ( int index = 0 ; index < NUMBUFFERS ; index ++ )
	{
		ulBytesWritten = DecodeOggVorbis( &m_OggFile , m_pDecodeBuffer, m_ulBufferSize, m_ulChannels ) ;
		if (ulBytesWritten)
		{
			alBufferData( m_uiBuffers[index] , m_ulFormat , m_pDecodeBuffer, ulBytesWritten, m_ulFrequency ) ;
			alSourceQueueBuffers( m_uiSource , 1 , &m_uiBuffers[index] ) ;
		}
	}
}

Share this post


Link to post
Share on other sites

Hi,

Just wanna ask you a question, are you decoding the whole OGG(/MP3) file at once? I mean, if this is for background music, doesn't it take a bit of memory?

OpenAL is a bit hard to understand, since it doesn't provide much of information what is wrong in it, it expects you to read its mind ... anyway, I'd add some error checking code here and see where it fails exactly.

It is possible that you should delete the source and recreate it before starting again. I had similar issues (source not playing, source playing at wrong speed) when streaming with OpenAL.

Cheers!

Share this post


Link to post
Share on other sites
I don't decode the ogg file at one time ,because the ogg may be over 100MB.
The function "DecodeOggVorbis" just reads fixed size of data to the prepared buffer.Every buffer contains 250ms' sound ,and I totally use 4 buffers.
Evety 200 ms , the function "Update" is called to check which buffer is processed.If any ,check if there is more data to read and read it to the spare buffer if so,then queue the buffer to the source.And go on...

From the OpenAl document , "alSourceRewind( ALuint uiSource )" is described distinctly for reseting the stream attached to the source.But it seems that it doesn't work.

Firstly I thought it may be something wrong with the OggVorbis_File.Maybe I should reset the FILE* of the OggVorbis_File,but I can't find a proper function.

Do I have to recreate all the resources for replaying?
It doesn't sound like a smart way...

Share this post


Link to post
Share on other sites
I think you need to unqueue all buffers before rewinding source

ALuint buffer;
int nbufs;
alGetSourcei(source, AL_BUFFERS_PROCESSED, &nbufs);
while(nbufs--) {
alSourceUnqueueBuffers(source, 1, &buffer);
}

Share this post


Link to post
Share on other sites
Quote:
Original post by gxsh
I don't decode the ogg file at one time ,because the ogg may be over 100MB.
The function "DecodeOggVorbis" just reads fixed size of data to the prepared buffer.Every buffer contains 250ms' sound ,and I totally use 4 buffers.
Evety 200 ms , the function "Update" is called to check which buffer is processed.If any ,check if there is more data to read and read it to the spare buffer if so,then queue the buffer to the source.And go on...


Yeah, my OpenAL/OGGVorbis is a bit rusty, so that part is clear. I checked my code and that's the way it works.

Quote:

From the OpenAl document , "alSourceRewind( ALuint uiSource )" is described distinctly for reseting the stream attached to the source.But it seems that it doesn't work.


Well, I think that it isn't enough to rewind the source since you are constantly streaming new data in the source, so rewinding would bring back only the last second or less.

Quote:

Firstly I thought it may be something wrong with the OggVorbis_File.Maybe I should reset the FILE* of the OggVorbis_File,but I can't find a proper function.


In order to start playing the song from the beginning, you'll need to reset the ogg-vorbis file decoder. I mean, you want to replay the whole song, not just a second of it? There should be functions for file seeking within OggVorbis libraries.

Quote:

Do I have to recreate all the resources for replaying?
It doesn't sound like a smart way...


I checked my code and in the code I am recreating the source used as streaming source. If you can quarantee that the songs have exactly the same buffer format, then the recreation may not be necessary. In my situation, I had one song in stereo and another one in mono, and that resulted as the second song playing at wrong speed (ie. double or half speed).

Anyway, as a suggestion, you should make your player to play one song from the beginning to the end correctly, then recreate your ogg vorbis streamer and source, and start playing again.

It may not sound "smart" but the overhead of doing this (extra cpu cycles spend / resource handling etc) is rather small compared to the benefits. It will make your player logics simpler (ie. cleaner code) since you don't need to worry about different buffer formats or no need for state managing. So, in that way it is rather smart.

Cheers!

Share this post


Link to post
Share on other sites
When you stream in OpenAL, it doesn't have much of a concept of "beginning" and "end". Rewinding the source doesn't do much because the beginning is actually the oldest queued buffer that wasn't unqueued, and the end is the last queued buffer.

To loop a stream in anything, OpenAL or whatever, you simply need to rewind the decoder when it runs out of data, and continue feeding the stream as you left it. So it's basically something like this:
/** Start Stream **/
OpenDecoder();

ret = DecodeData(buf, BUFFER_SIZE);
alBufferData(buffers[0], format, buf, ret, frequency);
ret = DecodeData(buf, BUFFER_SIZE);
alBufferData(buffers[1], format, buf, ret, frequency);
if((ret=alGetError()) != AL_NO_ERROR)
{
fprintf(stderr, "Error loading, %#04x :(\n", ret);
return 1;
}

alSourceQueueBuffers(source, 2, buffers);
alSourcePlay(source);
if(alGetError() != AL_NO_ERROR)
{
fprintf(stderr, "Error starting :(\n");
return 1;
}

/** Update Stream **/
ALuint buffer;
ALint val;

alGetSourcei(source, AL_BUFFERS_PROCESSED, &val);
if(val <= 0)
continue;

/* For each processed buffer... */
while(val--)
{
/* Read the next bit of decoded data */
ret = DecodeData(buf, BUFFER_SIZE);
if(ret == 0)
{
if(Looping)
{
/* End of Stream; restart decoder */
CloseDecoder();
OpenDecoder();
ret = DecodeData(buf, BUFFER_SIZE);
}
if(ret == 0)
break;
}

alSourceUnqueueBuffers(source, 1, &buffer);
alBufferData(buffer, format, buf, ret, frequency);
alSourceQueueBuffers(source, 1, &buffer);
if(alGetError() != AL_NO_ERROR)
{
fprintf(stderr, "Error buffering :(\n");
break;
}
}
/* Make sure the source is still playing, and restart it if
* needed. */
alGetSourcei(source, AL_SOURCE_STATE, &val);
if(val != AL_PLAYING)
alSourcePlay(source);

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