Initial commit
This commit is contained in:
442
Torque/SDK/engine/audio/vorbisStreamSource.cc
Normal file
442
Torque/SDK/engine/audio/vorbisStreamSource.cc
Normal file
@@ -0,0 +1,442 @@
|
||||
//--------------------------------------
|
||||
// vorbisStreamSource.cc
|
||||
// implementation of streaming audio source
|
||||
//
|
||||
// Kurtis Seebaldt
|
||||
//--------------------------------------
|
||||
|
||||
#include "audio/vorbisStreamSource.h"
|
||||
#include "vorbis/codec.h"
|
||||
|
||||
#define BUFFERSIZE 32768
|
||||
#define CHUNKSIZE 4096
|
||||
|
||||
#if defined(TORQUE_BIG_ENDIAN)
|
||||
#define ENDIAN 1
|
||||
#else
|
||||
#define ENDIAN 0
|
||||
#endif
|
||||
|
||||
extern const char * MusicPlayerStreamingHook(const AUDIOHANDLE & handle);
|
||||
|
||||
VorbisStreamSource::VorbisStreamSource(const char *filename)
|
||||
{
|
||||
stream = NULL;
|
||||
bIsValid = false;
|
||||
bBuffersAllocated = false;
|
||||
bVorbisFileInitialized = false;
|
||||
mBufferList[0] = 0;
|
||||
clear();
|
||||
|
||||
mFilename = filename;
|
||||
mPosition = Point3F(0.f,0.f,0.f);
|
||||
}
|
||||
|
||||
VorbisStreamSource::~VorbisStreamSource()
|
||||
{
|
||||
if(bReady && bIsValid)
|
||||
freeStream();
|
||||
}
|
||||
|
||||
void VorbisStreamSource::clear()
|
||||
{
|
||||
if(stream)
|
||||
freeStream();
|
||||
|
||||
mHandle = NULL_AUDIOHANDLE;
|
||||
mSource = NULL;
|
||||
|
||||
if(mBufferList[0] != 0)
|
||||
alDeleteBuffers(NUMBUFFERS, mBufferList);
|
||||
for(int i = 0; i < NUMBUFFERS; i++)
|
||||
mBufferList[i] = 0;
|
||||
|
||||
dMemset(&mDescription, 0, sizeof(Audio::Description));
|
||||
mEnvironment = 0;
|
||||
mPosition.set(0.f,0.f,0.f);
|
||||
mDirection.set(0.f,1.f,0.f);
|
||||
mPitch = 1.f;
|
||||
mScore = 0.f;
|
||||
mCullTime = 0;
|
||||
|
||||
bReady = false;
|
||||
bFinishedPlaying = false;
|
||||
bIsValid = false;
|
||||
bBuffersAllocated = false;
|
||||
bVorbisFileInitialized = false;
|
||||
}
|
||||
|
||||
bool VorbisStreamSource::initStream()
|
||||
{
|
||||
vorbis_info *vi;
|
||||
|
||||
ALint error;
|
||||
|
||||
bFinished = false;
|
||||
|
||||
// JMQ: changed buffer to static and doubled size. workaround for
|
||||
// https://206.163.64.242/mantis/view_bug_page.php?f_id=0000242
|
||||
static char data[BUFFERSIZE*2];
|
||||
|
||||
alSourceStop(mSource);
|
||||
alSourcei(mSource, AL_BUFFER, 0);
|
||||
|
||||
stream = ResourceManager->openStream(mFilename);
|
||||
if(stream != NULL)
|
||||
{
|
||||
if(vf.ov_open(stream, NULL, 0) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bVorbisFileInitialized = true;
|
||||
|
||||
//Read Vorbis File Info
|
||||
vi = vf.ov_info(-1);
|
||||
freq = vi->rate;
|
||||
|
||||
long samples = (long)vf.ov_pcm_total(-1);
|
||||
|
||||
if(vi->channels == 1)
|
||||
{
|
||||
format = AL_FORMAT_MONO16;
|
||||
DataSize = 2 * samples;
|
||||
}
|
||||
else
|
||||
{
|
||||
format = AL_FORMAT_STEREO16;
|
||||
DataSize = 4 * samples;
|
||||
}
|
||||
DataLeft = DataSize;
|
||||
|
||||
// Clear Error Code
|
||||
alGetError();
|
||||
|
||||
alGenBuffers(NUMBUFFERS, mBufferList);
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
|
||||
bBuffersAllocated = true;
|
||||
|
||||
int numBuffers = 0;
|
||||
for(int loop = 0; loop < NUMBUFFERS; loop++)
|
||||
{
|
||||
ALuint DataToRead = (DataLeft > BUFFERSIZE) ? BUFFERSIZE : DataLeft;
|
||||
if (DataToRead == DataLeft)
|
||||
bFinished = AL_TRUE;
|
||||
|
||||
long ret = oggRead(data, BUFFERSIZE, ENDIAN, ¤t_section);
|
||||
if(ret <= 0)
|
||||
{
|
||||
bFinished = AL_TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
DataLeft -= ret;
|
||||
alBufferData(mBufferList[loop], format, data, ret, freq);
|
||||
++numBuffers;
|
||||
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
if(bFinished)
|
||||
break;
|
||||
}
|
||||
|
||||
// Queue the buffers on the source
|
||||
alSourceQueueBuffers(mSource, NUMBUFFERS, mBufferList);
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
|
||||
alSourcei(mSource, AL_LOOPING, AL_FALSE);
|
||||
bReady = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bIsValid = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VorbisStreamSource::updateBuffers()
|
||||
{
|
||||
ALint processed;
|
||||
ALuint BufferID;
|
||||
ALint error;
|
||||
// JMQ: changed buffer to static and doubled size. workaround for
|
||||
// https://206.163.64.242/mantis/view_bug_page.php?f_id=0000242
|
||||
static char data[BUFFERSIZE*2];
|
||||
|
||||
// don't do anything if stream not loaded properly
|
||||
if(!bIsValid)
|
||||
return false;
|
||||
|
||||
if(bFinished && mDescription.mIsLooping)
|
||||
resetStream();
|
||||
|
||||
// reset AL error code
|
||||
alGetError();
|
||||
|
||||
#if 1 //def TORQUE_OS_LINUX
|
||||
// JMQ: this doesn't really help on linux. it may make things worse.
|
||||
// if it doesn't help on mac/win either, could disable it.
|
||||
ALint state;
|
||||
alGetSourcei(mSource, AL_SOURCE_STATE, &state);
|
||||
if (state == AL_STOPPED)
|
||||
{
|
||||
// um, yeah. you should be playing
|
||||
// restart
|
||||
alSourcePlay(mSource);
|
||||
//#ifdef TORQUE_DEBUG
|
||||
//Con::errorf(">><<>><< THE MUSIC STOPPED >><<>><<");
|
||||
//#endif
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TORQUE_OS_LINUX
|
||||
checkPosition();
|
||||
#endif
|
||||
|
||||
// Get status
|
||||
alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &processed);
|
||||
|
||||
// If some buffers have been played, unqueue them and load new audio into them, then add them to the queue
|
||||
if (processed > 0)
|
||||
{
|
||||
while (processed)
|
||||
{
|
||||
alSourceUnqueueBuffers(mSource, 1, &BufferID);
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
|
||||
if (!bFinished)
|
||||
{
|
||||
ALuint DataToRead = (DataLeft > BUFFERSIZE) ? BUFFERSIZE : DataLeft;
|
||||
|
||||
if (DataToRead == DataLeft)
|
||||
bFinished = AL_TRUE;
|
||||
|
||||
long ret = oggRead(data, BUFFERSIZE, ENDIAN, ¤t_section);
|
||||
if(ret > 0)
|
||||
{
|
||||
DataLeft -= ret;
|
||||
|
||||
alBufferData(BufferID, format, data, ret, freq);
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
|
||||
// Queue buffer
|
||||
alSourceQueueBuffers(mSource, 1, &BufferID);
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
}
|
||||
|
||||
processed--;
|
||||
|
||||
if(bFinished && mDescription.mIsLooping)
|
||||
{
|
||||
resetStream();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buffersinqueue--;
|
||||
processed--;
|
||||
|
||||
if (buffersinqueue == 0)
|
||||
{
|
||||
bFinishedPlaying = AL_TRUE;
|
||||
return AL_FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VorbisStreamSource::freeStream()
|
||||
{
|
||||
bReady = false;
|
||||
|
||||
if(stream != NULL)
|
||||
ResourceManager->closeStream(stream);
|
||||
|
||||
stream = NULL;
|
||||
|
||||
if(bBuffersAllocated)
|
||||
{
|
||||
if(mBufferList[0] != 0)
|
||||
alDeleteBuffers(NUMBUFFERS, mBufferList);
|
||||
for(int i = 0; i < NUMBUFFERS; i++)
|
||||
mBufferList[i] = 0;
|
||||
|
||||
bBuffersAllocated = false;
|
||||
}
|
||||
|
||||
if(bVorbisFileInitialized)
|
||||
{
|
||||
vf.ov_clear();
|
||||
bVorbisFileInitialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
void VorbisStreamSource::resetStream()
|
||||
{
|
||||
// MusicPlayerStreamingHook allow you to create a handler
|
||||
// where you can rotate through streaming files
|
||||
// Comment in and provide hook if desired.
|
||||
//
|
||||
//const char * newFile = MusicPlayerStreamingHook(mHandle);
|
||||
//if (newFile)
|
||||
//{
|
||||
// setNewFile(newFile);
|
||||
// return;
|
||||
//}
|
||||
//else
|
||||
{
|
||||
vf.ov_pcm_seek(0);
|
||||
DataLeft = DataSize;
|
||||
bFinished = AL_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void VorbisStreamSource::setNewFile(const char * file)
|
||||
{
|
||||
//---------------------
|
||||
// close down old file...
|
||||
//---------------------
|
||||
|
||||
if(stream != NULL)
|
||||
{
|
||||
ResourceManager->closeStream(stream);
|
||||
stream = NULL;
|
||||
}
|
||||
|
||||
if(bVorbisFileInitialized)
|
||||
{
|
||||
vf.ov_clear();
|
||||
bVorbisFileInitialized = false;
|
||||
}
|
||||
|
||||
//---------------------
|
||||
// open up new file...
|
||||
//---------------------
|
||||
|
||||
mFilename = file;
|
||||
stream = ResourceManager->openStream(mFilename);
|
||||
if(stream != NULL)
|
||||
{
|
||||
if(vf.ov_open(stream, NULL, 0) < 0)
|
||||
{
|
||||
bFinished = AL_TRUE;
|
||||
bVorbisFileInitialized = false;
|
||||
bIsValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
//Read Vorbis File Info
|
||||
vorbis_info * vi = vf.ov_info(-1);
|
||||
freq = vi->rate;
|
||||
|
||||
long samples = (long)vf.ov_pcm_total(-1);
|
||||
|
||||
if(vi->channels == 1)
|
||||
{
|
||||
format = AL_FORMAT_MONO16;
|
||||
DataSize = 2 * samples;
|
||||
}
|
||||
else
|
||||
{
|
||||
format = AL_FORMAT_STEREO16;
|
||||
DataSize = 4 * samples;
|
||||
}
|
||||
DataLeft = DataSize;
|
||||
|
||||
// Clear Error Code
|
||||
alGetError();
|
||||
|
||||
bFinished = AL_FALSE;
|
||||
bVorbisFileInitialized = true;
|
||||
bIsValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
// ov_read() only returns a maximum of one page worth of data
|
||||
// this helper function will repeat the read until buffer is full
|
||||
long VorbisStreamSource::oggRead(char *buffer,int length,
|
||||
int bigendianp,int *bitstream)
|
||||
{
|
||||
long bytesRead = 0;
|
||||
long totalBytes = 0;
|
||||
long offset = 0;
|
||||
long bytesToRead = 0;
|
||||
|
||||
while((offset) < length)
|
||||
{
|
||||
if((length - offset) < CHUNKSIZE)
|
||||
bytesToRead = length - offset;
|
||||
else
|
||||
bytesToRead = CHUNKSIZE;
|
||||
|
||||
bytesRead = vf.ov_read(buffer, bytesToRead, bigendianp, bitstream);
|
||||
//#ifdef TORQUE_OS_LINUX
|
||||
#if 1 // Might fix mac audio issue and possibly others...based on references, this looks like correct action
|
||||
// linux ver will hang on exit after a stream loop if we don't
|
||||
// do this
|
||||
if (bytesRead == OV_HOLE)
|
||||
// retry, see:
|
||||
// http://www.xiph.org/archives/vorbis-dev/200102/0163.html
|
||||
// http://www.mit.edu/afs/sipb/user/xiphmont/ogg-sandbox/vorbis/doc/vorbis-errors.txt
|
||||
continue;
|
||||
#endif
|
||||
|
||||
if(bytesRead <= 0)
|
||||
break;
|
||||
offset += bytesRead;
|
||||
buffer += bytesRead;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
#ifdef TORQUE_OS_LINUX
|
||||
|
||||
// JMQ: OpenAL sometimes replaces the stream source's position with its own
|
||||
// nifty value, causing the music to pan around the listener. how nice.
|
||||
// this function checks to see if the current source position in openal
|
||||
// is near the initial position, and slams it to the correct value if it
|
||||
// is wrong.
|
||||
|
||||
// This is a bad place to put this, but I don't feel like adding a new
|
||||
// .cc file. And since this is an incredibly lame hack to
|
||||
// workaround a stupid OpenAL bug, I see no point in overengineering it.
|
||||
|
||||
void AudioStreamSource::checkPosition()
|
||||
{
|
||||
ALfloat pos[3];
|
||||
alGetSourcefv(mSource, AL_POSITION, pos);
|
||||
|
||||
// just compute the difference between the openal pos and the
|
||||
// correct pos. it better be pretty friggin small.
|
||||
Point3F openalPos(pos[0], pos[1], pos[2]);
|
||||
F32 slopDist = 0.0001f;
|
||||
|
||||
F32 dist = mFabs((openalPos - mPosition).len());
|
||||
if (dist > slopDist)
|
||||
// slam!
|
||||
alSource3f(mSource, AL_POSITION, mPosition.x, mPosition.y, mPosition.z);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
F32 VorbisStreamSource::getElapsedTime()
|
||||
{
|
||||
return vf.ov_time_tell();
|
||||
}
|
||||
|
||||
F32 VorbisStreamSource::getTotalTime()
|
||||
{
|
||||
return vf.ov_time_total(-1);
|
||||
}
|
||||
Reference in New Issue
Block a user