Jump to content
  • Advertisement
Sign in to follow this  
blueshogun96

Ogg streaming skips a few samples when starting playback.

This topic is 549 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Originally, my game used some cheap .wav streaming code I whipped up just to get audio streaming working.  Yeah, I should have used ogg from the start, but at the time, I was lazy and wanted to get a quick prototype going.  So I changed the code to use ogg vorbis, and it works just fine... except when I begin playback, the first few samples skip and then play normally.  Not sure what I'm doing wrong here.

 

I'll share the relevant code that I'm using.  Also, I'm using OpenAL-Soft as my audio API.

 

oggstream.h

#include "stb_vorbis.h"

#define NUM_BUFFERS	3
#ifdef __ANDROID__
#define BUFFER_SIZE	(4096*4)	// 16kb (Android needs a larger buffer)
#else
#define BUFFER_SIZE	(4096*2)	// 8kb
#endif

/* ogg stream structure */
struct ogg_stream
{
	ALuint source;
	ALuint buffers[NUM_BUFFERS];
	ALuint frequency;
	ALenum format;
	ALfloat volume;
	ALuint ogg_format;
	int channels;
	int bits;
	ALboolean is_playing;
	stb_vorbis*	vorbis;
	stb_vorbis_info vorbis_info;
	unsigned char* buf;
	unsigned char* buf2;
	void* thread;
};

oggstream.c

/* Opens a .wav file for streaming */
int		oggstream_open( const char* filename, struct ogg_stream* o )
{
	int ret;

	/* Sanity check */
	if(!o)
		return 0;

	/* Set this oggstream to not playing */
	o->is_playing = AL_FALSE;

	/* Does the wav file exist? */
	o->vorbis = stb_vorbis_open_filename( filename, NULL, NULL );
	if( !o->vorbis )
		return 0;

	/* Get the info on this vorbis file */
	o->vorbis_info = stb_vorbis_get_info( o->vorbis );

	/* Create buffers and sources */
	alGenBuffers( NUM_BUFFERS, o->buffers );
	alGenSources( 1, &o->source );
    
	/* Get the channel count */
	o->channels = o->vorbis_info.channels;

	/* Get the sample frequency */
    o->frequency = o->vorbis_info.sample_rate;

	/* Get the bit depth */
	o->bits = 16;

	if( o->channels == 1 )
		o->format = AL_FORMAT_MONO16;
	else if( o->channels == 2 )
		o->format = AL_FORMAT_STEREO16;

	/* Allocate a buffer large enough to hold our sound bytes */
	o->buf = malloc( BUFFER_SIZE * sizeof(ALshort) );
	
	/* Fill the buffer with the amount of bytes per buffer for OpenAL to use */
	ret = stb_vorbis_get_samples_short_interleaved( o->vorbis, o->vorbis_info.channels, o->buf, BUFFER_SIZE );
    alBufferData( o->buffers[0], o->format, o->buf, ret*sizeof(ALshort), o->frequency );
    ret = stb_vorbis_get_samples_short_interleaved( o->vorbis, o->vorbis_info.channels, o->buf, BUFFER_SIZE );
    alBufferData( o->buffers[1], o->format, o->buf, ret*sizeof(ALshort), o->frequency );
    ret = stb_vorbis_get_samples_short_interleaved( o->vorbis, o->vorbis_info.channels, o->buf, BUFFER_SIZE );
    alBufferData( o->buffers[2], o->format, o->buf, ret*sizeof(ALshort), o->frequency );


	if( alGetError() != AL_NO_ERROR )
	{
		stb_vorbis_close( o->vorbis );
		alDeleteSources( 1, &o->source );
		alDeleteBuffers( NUM_BUFFERS, o->buffers );
		return 0;
	}

	o->volume = 1.0f;

	/* Queue the buffers onto the source and start playback */

	return 1;
}

void	oggstream_close( struct ogg_stream* o )
{
	ALint val = 0;

    /* Sanity check */
    if( !o )
        return;
    
	/* Free any used data and wait until the buffers are 
	   finished before deleting */
	if( o->vorbis )
	{
		stb_vorbis_close( o->vorbis );
		o->vorbis = NULL;
	}

	if( o->buf )
	{
		free( o->buf );
		o->buf = NULL;
	}
	
	do
	{
		alGetSourcei( o->source, AL_SOURCE_STATE, &val );
	} while( val == AL_PLAYING );

	alDeleteSources( 1, &o->source );
	alDeleteBuffers( NUM_BUFFERS, o->buffers );
}

void	oggstream_play( struct ogg_stream* o )
{
	/* is this stream already playing? */
	if( o->is_playing )
		return;

	o->is_playing = AL_TRUE;

	/* Foamy the squirrel voice: PLAAAAAAAAAY! */
	oggstream_set_volume( o, o->volume );
	alSourceQueueBuffers( o->source, NUM_BUFFERS, o->buffers );
	alSourcePlay( o->source );
	if( alGetError() != AL_NO_ERROR )
		return;
}

int		oggstream_update( struct ogg_stream* o )
{
	ALuint buffer;
	ALint val;
	ALint ret;

	/* Don't update if the oggstream isn't open */
	if( !o->vorbis )
		return 0;

	/* Don't call this if the stream isn't playing */
	if( !o->is_playing )
		return 0;

	/* Check if OpenAL is done with any of the queued buffers */
	alGetSourcei( o->source, AL_BUFFERS_PROCESSED, &val );
	if( val <= 0 )
		return 1;

	/* For each processed buffer... */
	while( val-- )
	{
		/* Red the next chunk of decoded data from the stream */
       int size = 0, result = 0;

		while(size < BUFFER_SIZE)
		{
			result = stb_vorbis_get_samples_short_interleaved( o->vorbis, o->vorbis_info.channels, &o->buf[size], BUFFER_SIZE-size);
			if( result > 0 ) 
				size += result * o->vorbis_info.channels;
			else 
				break;
		}

        /* Pop the oldest queued buffer from the source, fill it with
            new data, then requeue it */
        alSourceUnqueueBuffers( o->source, 1, &buffer );
        alBufferData( buffer, o->format, o->buf, size*sizeof(ALshort), o->frequency );
        alSourceQueueBuffers( o->source, 1, &buffer );
        if( alGetError() != AL_NO_ERROR )
        {
            /* TODO */
        }

		/* Restart if we reached the end of this ogg stream */
		if( result == 0 )
		{
			stb_vorbis_seek_start( o->vorbis );
		}
	}

	/* Make sure that the source is still playing and restart it if necessary */
	alGetSourcei( o->source, AL_SOURCE_STATE, &val );
	if( val != AL_PLAYING )
		alSourcePlay( o->source );

	return 1;
}

And if it helps, I'm using stb's vorbis code and simply used it's API to replace the areas where I've read the bytes from the .wav files.

 

Any ideas?  Thanks.

 

Shogun

Share this post


Link to post
Share on other sites
Advertisement

Out of curiosity, why stb_vorbis? It's slower than reference lib from Xiph, 2x slower, as mentioned in some comments on github.

If you're targeting android and arm, it's worth trying Tremolo (decoding only). Its speed is amazing.

Share this post


Link to post
Share on other sites

How did you establish that it was skipping samples?

When an audio stream starts, it begins playing the first few sound bytes, but suddenly jumps a head less than a second.

 

Out of curiosity, why stb_vorbis? It's slower than reference lib from Xiph, 2x slower, as mentioned in some comments on github.

If you're targeting android and arm, it's worth trying Tremolo (decoding only). Its speed is amazing.

Okay, I did not know that.  I chose stb_vorbis because it was a small, one source file solution.  This game is portable to everything and anything (not just mobile, but consoles also) so I chose it to keep things as simple as possible without having to worry about dependencies as much.  This game has to work on x86-64 also and Tremolo appears to be optimized solely for ARM.

 

If I have to use Xiph instead, I'm fine with that.  Since this is a mobile game, I do try to optimize for battery life.

 

Shogun.

Share this post


Link to post
Share on other sites

When an audio stream starts, it begins playing the first few sound bytes, but suddenly jumps a head less than a second.

 
I'm asking how you determined that this is what is happening. Are you saying that this is what you think based on what you're hearing or that you're looking at the data flow somehow and this is what you're seeing?

Share this post


Link to post
Share on other sites

 

When an audio stream starts, it begins playing the first few sound bytes, but suddenly jumps a head less than a second.

 
I'm asking how you determined that this is what is happening. Are you saying that this is what you think based on what you're hearing or that you're looking at the data flow somehow and this is what you're seeing?

 

It's based on what I am hearing.  The .wav version I ported from does not give me this issue.

 

Shogun

Share this post


Link to post
Share on other sites

Is it possible that some header data is being fed to the decoder? Can you dump the decoder input frames to a file and see if anything is screwy?

Edited by Khatharr

Share this post


Link to post
Share on other sites

Is it possible that some header data is being fed to the decoder? Can you dump the decoder input frames to a file and see if anything is screwy?

With the .wav streaming code, that was the case as it did cause a pop noise whenever that happened.  I highly doubt this is the case this time though.  So what I'll try first is using actual xiph decoder or Tremolo as it has been suggested and see if it's stb_vorbis that's causing the issue.

 

Shogun

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!