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, §ion);
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);
}
}