[Vorbis-dev] How to loop a Vobis sound ?

steve steve at astrocorp.com.tw
Tue Jan 17 21:52:22 PST 2006


The sound file is played correctly for the first time, then when rewind to the initial position.
then copy PCM to buffer, the OpenAL report an error.
It seems like the OpenAL doesn't recognize the PCM data.


The OpenAL error number :
AL_INVALID_VALUE                          0xA003

  void Buffer::PCMData (ALuint id, ALenum eFormat, ALvoid *data, ALsizei size, ALsizei freq)
  {
    // Copy PCM data into ALBuffer
	// all variables id, eFomat and size, freq are correct.

    alBufferData (id, eFormat, data, size, freq);
    ALErrorCheck ();
  }

  void Buffer::ALErrorCheck ()
  {
    int error = alGetError ();
    if (error != AL_NO_ERROR)
    {
      char szErr[512];
      MakeErrorString (szErr, "", error);
      throw string (szErr);
------------->  error here
    }
  }

I also tried the ov_XXX_lap functions, but the problem still remains.

thank you.

Steve


// ----------   Full source code -----------------

// ----------  OggStream.cpp  ---------------

  int OggStream::Stream (char *pcm, int iBufSize)
  {
    int  size = 0;
    while (size < iBufSize)
    {
      int   section;
      const int iSample = 2;    // 1 for 8 bit, 2 for 16 bit

      int result = ov_read (&m_tOggStream, pcm + size, iBufSize - size, 0, iSample, 1, &section);

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

  bool OggStream::Rewind ()
  {
    if (!ov_seekable (&m_tOggStream))
      return false;

    int result = ov_pcm_seek (&m_tOggStream, m_iStreamStart);
   // int result = ov_time_seek_lap (&m_tOggStream, 0);

    if (result < 0)
      throw OvErrorString  (result);
    return true;
  }

  bool OggStream::LoadFromMemory (FileInMemory *pMemFile)
  {
    m_tOggMemoryFile = *pMemFile;

    // Open the file from memory.  We need to pass it a pointer to our data  (in this case our FileInMemory structure),
    // a pointer to our ogg stream  (which the vorbis libs will fill up for us), and our s_callbacks
    if  (ov_open_callbacks (&m_tOggMemoryFile, &m_tOggStream, NULL, 0, s_callbacks) != 0)
      throw string ("Could not read Ogg file from memory");

    m_iStreamStart = ov_pcm_tell (&m_tOggStream);
    m_bReleaseStream = true;
    return true;
  }

// -------------	Source.h

 class GAL_API Source
  {
  public:
    // 1. Set a loaded buffer as current buffer
    void  SetCurrentBuffer (Buffer *pBuf);

    // 2. operations
    virtual bool Play ();
    virtual void Stop () { alSourceStop (m_id); }
    virtual void Pause () { alSourcePause (m_id); }
    virtual void Rewind () { alSourceRewind (m_id); }

    // 3. Update if buffers are queued
    virtual bool Update ();   // Update Queued Buffers
    
    bool IsPlaying ();
    void Empty ();    // Empty Queued Buffer

    // play attributes
    // called after current buffer is set.
    // n: number of repeats, n < 0: Infinite Loop, n = [0,1]: play once,
#define AL_SRC_LOOP -1
    void SetRepeats (int n); 
    void Volume (ALfloat fVol) { alSourcef (m_id, AL_GAIN, fVol); }

    // 3D sound Attributes
    void SetRelative (bool bRel);
    void SetPosition (ALfloat *pPos) { alSourcefv (m_id, AL_POSITION, pPos); }
    void SetVelocity (ALfloat *p3f) { alSourcefv (m_id, AL_VELOCITY, p3f); }

    // default is enabled, Set disable for background music
    void Enable3DEffect (bool bEnable);

    const ALuint	Id () const { return m_id; }
    Buffer* GetBuffer () const { return m_pBuf; }

    // To continue play multiple sounds
    // Buffer can be queued at any source state.
    // All buffers in a queue must have the same format and attributes.
    // currently only working for single buffer stream.
    void QueueBuffer (Buffer *pBuf);
    void UnQueueBuffer (Buffer *pBuf);

    // Get vector attributes
    // AL_POSITION, AL_VELOCITY,
    void Get (ALenum eParam, ALfloat *p3f) { alGetSourcefv (m_id, eParam, p3f); }
 
    Source ();
    virtual ~Source ();

  private:
    void Release ();

    ALuint  m_id;	// OpanAL id
    Buffer  *m_pBuf;    // associated buffer.

    int	    m_iRepeats;	// for double buffering stream
  };

  typedef std::vector <Source*> SOURCE_LIST;
}


// -------------  Source.cpp  -------------------
  Source::Source ()
  {
    m_pBuf = NULL;
    m_iRepeats = 0;

    // generate one source id from OpenAL.
    alGenSources (1, &m_id);
    assert (alGetError() == AL_NO_ERROR);

    // set a default position
    // initial listener's position is (0,0,0)
    // default AL_REFERENCE_DISTANCE = 1
    ALfloat pos[3] = {0, 0, -1};
    this->SetPosition (pos);
  }

  Source::~Source ()
  {
    Release ();
  }

  void Source::SetCurrentBuffer (Buffer *pBuf)
  {
    // For a source in the AL_PLAYING or AL_PAUSED state, setting AL_BUFFER will
    // result in the AL_INVALID_OPERATION error state being set. AL_BUFFER can be
    // applied only to sources in the AL_INITIAL and AL_STOPPED states.

    // NULL, release the current buffer queue on a source
    if (!pBuf)
      alSourcei (m_id, AL_BUFFER, AL_NONE);
    else
    {
      // for single clip stream (WAV), attach the buffer id directly
      // for double buffering stream (Ogg), don't attach buffer id
      // Queue the streaming buffers instead.

      if (!pBuf->IsDoubleBuffering ())
	alSourcei (m_id, AL_BUFFER, pBuf->Id ()[0]);
    }

    m_pBuf = pBuf;
  }

  void Source::SetRelative (bool bRel)
  {
    if (bRel) 
      alSourcef (m_id, AL_SOURCE_RELATIVE, AL_TRUE);
    else
      alSourcef (m_id, AL_SOURCE_RELATIVE, AL_FALSE);
  }

  void Source::QueueBuffer (Buffer *pBuf)
  { 
    ALint id = pBuf->Id ()[0];
    alSourceQueueBuffers (m_id, 1, (const ALuint*)&id); 
    ALint error = alGetError();
    assert (error == AL_NO_ERROR);
  }

  void Source::UnQueueBuffer (Buffer *pBuf)
  { 
    ALint id = pBuf->Id ()[0];
    alSourceUnqueueBuffers (m_id, 1, (ALuint*)&id); 
    ALint error = alGetError();
    assert (error == AL_NO_ERROR);
  }

  void Source::SetRepeats (int n)
  {
    if (n == 1)	n = 0;	// currently playing

    // OpenAL Loop function doesn't work for double buffering, we have to loop manually
    if (m_pBuf->IsDoubleBuffering ()) 
    {
      if (0 == n || 1 == n)
	alSourcei (m_id, AL_LOOPING, AL_FALSE);

      m_iRepeats = n;
      return;
    }

    if (n < 0)
      alSourcei (m_id, AL_LOOPING, AL_TRUE);
    else if (0 == n || 1 == n)
      alSourcei (m_id, AL_LOOPING, AL_FALSE);
    else
    {
      // current buffer as counted 1.
      // The following code is only work for single buffer stream.
      // todo: process the double buffering stream differently.
      ALint curId = 0;
      alGetSourcei (m_id, AL_BUFFER, &curId);
      if (curId != 0)
      {
	n--;
	for (int i = 0; i < n; ++i)
	  alSourceQueueBuffers (m_id, 1, (const ALuint*)&curId);
      }
    }
    m_iRepeats = n;
  }

  bool Source::IsPlaying ()
  {
    ALint play;
    alGetSourcei (m_id, AL_SOURCE_STATE, &play);
    return (AL_PLAYING == play);
  }

  void Source::Enable3DEffect (bool bEnable)
  {
    if (bEnable)
    {
      alSourcei (m_id, AL_SOURCE_RELATIVE, AL_FALSE);
      alSourcef (m_id, AL_ROLLOFF_FACTOR, 1);
    }
    else
    {
      // the sources will be exempt from distance attenuation,
      // if AL_ROLLOFF_FACTOR = 0, 
      // and set relative position
      alSourcei (m_id, AL_SOURCE_RELATIVE, AL_TRUE);
      alSourcef (m_id, AL_ROLLOFF_FACTOR, 0);
    }
  }

  void Source::Empty ()
  {
    if (!m_pBuf->IsDoubleBuffering ()) return;

    // empty queue for the Ogg Buffer
    int iQueued;
    alGetSourcei (m_id, AL_BUFFERS_QUEUED, &iQueued);

    while (iQueued--)
    {
      ALuint uBufId;
      alSourceUnqueueBuffers (m_id, 1, &uBufId);
      //check ();
    }
  }

  bool Source::Update ()
  {
    if (!m_pBuf->IsDoubleBuffering ()) return true;

    ALint  error;
    char   szErr[1024];

    // OpenAL Loop function doesn't work for double buffering, we have to loop manually
    // todo: loop ogg doen't work, try to solve the problem
    if (!IsPlaying ())
    {
      if (m_iRepeats > 0)
      {
	m_pBuf->Rewind ();
	Play ();
	--m_iRepeats;
      }
      else if (m_iRepeats < 0)
      {
	m_pBuf->Rewind ();
	Play ();
      }
      return true;
    }

    bool  bActive = true;
    int	  processed;

    alGetSourcei (m_id, AL_BUFFERS_PROCESSED, &processed);

    while (processed--)
    {
      ALuint buffer;

      alSourceUnqueueBuffers (m_id, 1, &buffer);
      CHECK_ERROR ("Update () : ");

      bActive = m_pBuf->Stream (buffer);
      if (bActive)
      {
	alSourceQueueBuffers (m_id, 1, &buffer);
	CHECK_ERROR ("Update () : ");
      }
    }

    return bActive;
  }

  bool Source::Play ()
  {
    ALint  error;
    char   szErr[1024];

    if (IsPlaying ())
      return true;

    if (!m_pBuf->IsDoubleBuffering ())
    { 
      // single buffer
      alSourcePlay (m_id); 
      return true;
    }

    // Play with double buffering
    if (!m_pBuf->Stream ())
      return false;

    alSourceQueueBuffers (m_id, 2, m_pBuf->Id ());
    CHECK_ERROR ("Play () : ");

    alSourcePlay (m_id);
    CHECK_ERROR ("Play () : ");
    return true;
  }

  void Source::Release ()
  {
    Stop ();
    Empty ();
    alDeleteSources (1, &m_id);
    //check ();
  }

  ALint Source::Geti (ALenum eParam)
  { 
    ALint val; 
    alGetSourcei (m_id, eParam, &val); 
    return val; 
  }

  ALfloat Source::Get (ALenum eParam)
  { 
    ALfloat val; 
    alGetSourcef (m_id, eParam, &val); 
    return val; 
  }

// ------  Buffer.h ----------------
  class GAL_API Buffer
  {
  public:
    bool  Load (char *szWaveFile, char *szErr = NULL);

    ALuint*  Id () { return m_id; }
    bool IsDoubleBuffering () const;

    // If file name is used for search, set file name after Load ()
    void	  File (const char *pFile);
    const char*	  File () const { return m_szFile; }

    // Stream sound data into buffers for double buffering.
    // Stream 2 buffers for Source::Play ()
    virtual bool  Stream ();

    // Stream single buffer for Source::Update ()
    virtual bool  Stream (ALuint buffer);
    
    virtual bool  Rewind ();

    OggStream*	GetOggStream () const { return m_pOggStream; }
    void	SetOggStream (OggStream *pStream) { m_pOggStream = pStream; }

    void PCMData (ALuint id, ALenum eFormat, ALvoid *data, ALsizei size, ALsizei freq);

    // get sound data format after loading, return AL_FORMAT_MONO8,...., AL_FORMAT_STERIO16
    ALenum  Format () const { return m_eFormat; }	

    static void ALErrorCheck ();

    Buffer ();
    virtual ~Buffer ();

  protected:
    bool  StreamOggData (ALuint id);

    ALuint  m_id[2];		  // OpanAL id, for double buffering
    ALenum  m_eFormat;		  // sound data format, 
    char    m_szFile[_MAX_PATH];  // File name

    OggStream	*m_pOggStream;
  };

// ------  Buffer.cpp ----------------

  Buffer::Buffer ()
  {
    strcpy (m_szFile, "");
    m_eFormat = AL_FORMAT_STEREO16;
    m_pOggStream = NULL;

    // generate one buffer id from OpenAL.
    alGenBuffers (2, m_id);

    ALint error = alGetError();
    assert (error == AL_NO_ERROR);
  }

  Buffer::~Buffer ()
  {
    alDeleteBuffers (2, m_id);
    if (m_pOggStream)
      delete m_pOggStream;
  }

  void Buffer::PCMData (ALuint id, ALenum eFormat, ALvoid *data, ALsizei size, ALsizei freq)
  {
    // Copy PCM data into ALBuffer
    alBufferData (id, eFormat, data, size, freq);
    ALErrorCheck ();
  }

  bool Buffer::Load (char *szFile, char *szErr)
  {
    ALint   error;
    ALsizei size,freq;
    ALvoid  *data;
    ALboolean loop;

    if (!szFile)
      return false;

    char  *pExt = strstr (szFile, ".ogg");
    if (pExt)
    {
      m_pOggStream = new OggStream;
      m_pOggStream->Load (szFile, szErr);
      m_eFormat = m_pOggStream->GetFormat ();
    }
    else
    {
	// assume is a wav file
      alutLoadWAVFile (szFile, &m_eFormat, &data, &size, &freq, &loop);
      CHECK_ERROR ("alutLoadWAVFile, Failed to load :");

      PCMData (m_id[0], m_eFormat, data, size, freq);

      // Unload wave file
      alutUnloadWAV (m_eFormat, data, size, freq);
      CHECK_ERROR ("alutUnloadWAV : ");
    }

    strncpy (m_szFile, szFile, _MAX_PATH - 1); 
    return true;
  }

  bool Buffer::IsDoubleBuffering () const
  { 
    if (m_pOggStream)
      return true;
    return false; 
  }

  bool Buffer::Stream () 
  { 
    if (m_pOggStream)
    {
      if (!StreamOggData (m_id[0]))
	return false;

      if (!StreamOggData (m_id[1]))
	return false;

      return true;
    }
    return false; 
  }

  bool Buffer::Stream (ALuint id) 
  { 
    return StreamOggData (id);
  }

  bool Buffer::StreamOggData (ALuint id)
  {
    if (m_pOggStream)
    {
      char pcm[BUFFER_SIZE] = {0};

      int size = m_pOggStream->Stream (pcm, BUFFER_SIZE);

      if (size == 0)
	return false;

      PCMData (id, m_eFormat, pcm, size, m_pOggStream->GetRate ());
      return true;
    }
    return false; 
  }

  bool Buffer::Rewind ()
  {
    if (m_pOggStream)
      return m_pOggStream->Rewind ();
    return false;
  }

  ALint Buffer::Get (ALenum eParam)
  { 
    ALint val; 
    alGetBufferi (m_id[0], eParam, &val); 
    return val; 
  }

  void Buffer::Set (ALenum eParam, ALint val) 
  { 
    alBufferi (m_id[0], eParam, val); 
    alBufferi (m_id[1], eParam, val); 
  }

  void Buffer::ALErrorCheck ()
  {
    int error = alGetError ();
    if (error != AL_NO_ERROR)
    {
      char szErr[512];
      MakeErrorString (szErr, "", error);
      throw string (szErr);
    }
  }


More information about the Vorbis-dev mailing list