views:

265

answers:

1

Dear all,

I have problem looping a streamed ogg vorbis file.

This is the code :

fslStream_OGG::fslStream_OGG()
{
 className = "fslSound";
 iMemSize = 0;
 iLength = 0;
 bSourceRelative = false;
 bIsLooping = false;
 bForceStop = false;
 bActive = false;
 source = buffer = 0;
 current_gain = 1.0f;
 outer_gain = 0;
 snd_info.uiChannels = snd_info.uiFrequency = snd_info.uiSampling = 0;
}

fslStream_OGG::~fslStream_OGG()
{
 if (bStreamCreated)
 {
  alSourceStop(source);
  Empty();
  alDeleteSources(1,&source);
  alDeleteBuffers(2,buffers);
  ov_clear(&oggStream);
 }
}

bool fslStream_OGG::Update()
{
 ALenum state; 
 alGetSourcei(source,AL_SOURCE_STATE,&state);

 if (state == AL_PAUSED || !bActive) return false;

 int processed;
    alGetSourcei(source,AL_BUFFERS_PROCESSED,&processed);

    while (processed--)
    {
        ALuint bufferI;
        alSourceUnqueueBuffers(source,1,&bufferI);
        Stream(bufferI);
  if (bActive) alSourceQueueBuffers(source,1,&bufferI);
    }

 if (state == AL_STOPPED || !bActive)
 {
  bActive = false;
  StreamSetPos(0.0f);
  if (bForceStop) return false;

  if (bIsLooping)
  {
   alSourceStop(source); // <- *** note these ***
   Empty();    // <- *** 2 lines of code ***
   StreamPlay();
   alSourcePlay(source);
  }
  else
  {
   return true;
  }
 }

 return false;
}

void fslStream_OGG::StreamPlay()
{
 if (!bActive)
 {
  bActive = true;
  bForceStop = false;
  Stream(buffers[0]);
  Stream(buffers[1]);

  alSourceQueueBuffers(source,2,buffers);
 }
}

bool fslStream_OGG::Open(const char* strFile)
{
 bStreamCreated = false;
 vorbis_info* vorbisInfo;
 oggFile = fopen(strFile,"rb");

 if (!oggFile) return false;

 if (ov_open_callbacks(oggFile,&oggStream,NULL,0,OV_CALLBACKS_DEFAULT) != 0)
 {
  fclose(oggFile);
  return false;
 }

 vorbisInfo = ov_info(&oggStream,-1);

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

 alGenBuffers(2,buffers);
 alGenSources(1,&source);

 iLength = (long)(ov_time_total(&oggStream,-1) * 1000.0);
 snd_info.uiChannels = (format == AL_FORMAT_MONO8 || format == AL_FORMAT_MONO16)? 1:2;
 snd_info.uiSampling = (format == AL_FORMAT_MONO8 || format == AL_FORMAT_STEREO8)? 8:16;
 snd_info.uiFrequency = vorbisInfo->rate;

 bStreamCreated = true;
 bIsStream = true;

 return true;
}

void fslStream_OGG::Stream(ALuint bufferI)
{
 int  size = 0;
 int  section;
 int  result;

 bActive = true;

 while (size < OGG_STREAM_BUFFER_SIZE)
 {
  result = ov_read(&oggStream,data + size,OGG_STREAM_BUFFER_SIZE - size,0,2,1,&section);

  if (result > 0)
  {
   size += result;
  }
  else
  {
   if (result < 0) return; else break;
  }
 }

 if (size == 0) { bActive = false; return; }

    alBufferData(bufferI,format,data,size,snd_info.uiFrequency);
}

void fslStream_OGG::Empty()
{
 int queued;
 alGetSourcei(source,AL_BUFFERS_QUEUED,&queued);

 while (queued--)
 {
        ALuint bufferI;
  alSourceUnqueueBuffers(source,1,&bufferI);
 }
}

void fslStream_OGG::StreamSetPos(float p)
{
 ov_time_seek(&oggStream,p);
}

float fslStream_OGG::StreamGetPos()
{
 return (float)ov_time_tell(&oggStream);
}

Note the 2 lines of code I have marked with ***.

In all cases, the file starts playing fine and rewinds when it ends. However :

Without those 2 lines of code, when repeated the file sounds "corrupted". If let to repeat again, it sounds even more "corrupted". I believe this is because OpenAl and the Vorbis decoder get "unsynchronized" writing/reading the buffer when the stream is repeated.

If I add those 2 lines of code, the file repeats without sounding corrupted. However, the file isn't repeated seamlessly; it rewinds a few centiseconds before it ends. I suspect this is because the buffers are not played to the end before rewinding to start.

I would be obliged if anyone could lend a helping hand.

Many thanks in advance,

Bill

+1  A: 

It seems that I have fixed the problem (I won't be sure without extensive testing).

I have fixed the Update method as follows :

bool fslStream_OGG::Update()
{
    ALenum state; 
    alGetSourcei(source,AL_SOURCE_STATE,&state);

    //if (state == AL_PAUSED || !bActive) return false; // <- WRONG
    if (state == AL_PAUSED) return false;

    int processed;
    alGetSourcei(source,AL_BUFFERS_PROCESSED,&processed);

    while (processed--)
    {
        ALuint bufferI;
        alSourceUnqueueBuffers(source,1,&bufferI);
        Stream(bufferI);
        if (bActive) alSourceQueueBuffers(source,1,&bufferI);
    }

    //if (state == AL_STOPPED || !bActive) /// <- WRONG
    if (state == AL_STOPPED)
    {
        bActive = false;
        StreamSetPos(0.0f);
        if (bForceStop) return false;

        if (bIsLooping)
        {
            //alSourceStop(source); // <- I have added these
            //Empty();              // <- 2 lines of code
            StreamPlay();
            alSourcePlay(source);
        }
        else
        {
            return true;
        }
    }

    return false;
}

Those 2 lines of code seem not to be necessary now. Will have to test it with different OSes, hardware etc...

Bill Kotsias
Hey man, I had a very similar problem to yours (with a very similar `Update` function; looks like we read the same tutorial.) How did you go with this solution? My problem with looping was the same as yours. It would jump back a few milliseconds before restarting the stream. But it would also do this without looping. When it got to the end of the audio file it would replay the last few ms of the sound before stopping. An annoying little jitter. How did you go with this solution?
Duracell