335 lines
6.7 KiB
C++
Executable File
335 lines
6.7 KiB
C++
Executable File
//--------------------------------------
|
|
// audioStreamSource.cc
|
|
// implementation of streaming audio source
|
|
//
|
|
// Kurtis Seebaldt
|
|
//--------------------------------------
|
|
|
|
#include "audio/wavStreamSource.h"
|
|
|
|
#define BUFFERSIZE 32768
|
|
|
|
typedef struct
|
|
{
|
|
ALubyte riff[4]; // 'RIFF'
|
|
ALsizei riffSize;
|
|
ALubyte wave[4]; // 'WAVE'
|
|
ALubyte fmt[4]; // 'fmt '
|
|
ALuint fmtSize;
|
|
ALushort Format;
|
|
ALushort Channels;
|
|
ALuint SamplesPerSec;
|
|
ALuint BytesPerSec;
|
|
ALushort BlockAlign;
|
|
ALushort BitsPerSample;
|
|
ALubyte data[4]; // 'data'
|
|
ALuint dataSize;
|
|
} WAVE_Struct;
|
|
|
|
/// WAV File-header
|
|
struct WAVFileHdr
|
|
{
|
|
ALubyte id[4];
|
|
ALsizei size;
|
|
ALubyte type[4];
|
|
};
|
|
|
|
//// WAV Fmt-header
|
|
struct WAVFmtHdr
|
|
{
|
|
ALushort format;
|
|
ALushort channels;
|
|
ALuint samplesPerSec;
|
|
ALuint bytesPerSec;
|
|
ALushort blockAlign;
|
|
ALushort bitsPerSample;
|
|
};
|
|
|
|
/// WAV FmtEx-header
|
|
struct WAVFmtExHdr
|
|
{
|
|
ALushort size;
|
|
ALushort samplesPerBlock;
|
|
};
|
|
|
|
/// WAV Smpl-header
|
|
struct WAVSmplHdr
|
|
{
|
|
ALuint manufacturer;
|
|
ALuint product;
|
|
ALuint samplePeriod;
|
|
ALuint note;
|
|
ALuint fineTune;
|
|
ALuint SMPTEFormat;
|
|
ALuint SMPTEOffest;
|
|
ALuint loops;
|
|
ALuint samplerData;
|
|
struct
|
|
{
|
|
ALuint identifier;
|
|
ALuint type;
|
|
ALuint start;
|
|
ALuint end;
|
|
ALuint fraction;
|
|
ALuint count;
|
|
} loop[1];
|
|
};
|
|
|
|
/// WAV Chunk-header
|
|
struct WAVChunkHdr
|
|
{
|
|
ALubyte id[4];
|
|
ALuint size;
|
|
};
|
|
|
|
|
|
|
|
WavStreamSource::WavStreamSource(const char *filename) {
|
|
stream = NULL;
|
|
bIsValid = false;
|
|
bBuffersAllocated = false;
|
|
mBufferList[0] = 0;
|
|
clear();
|
|
|
|
mFilename = filename;
|
|
mPosition = Point3F(0.f,0.f,0.f);
|
|
}
|
|
|
|
WavStreamSource::~WavStreamSource() {
|
|
if(bReady && bIsValid)
|
|
freeStream();
|
|
}
|
|
|
|
void WavStreamSource::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;
|
|
}
|
|
|
|
bool WavStreamSource::initStream() {
|
|
WAVChunkHdr chunkHdr;
|
|
WAVFmtExHdr fmtExHdr;
|
|
WAVFileHdr fileHdr;
|
|
WAVSmplHdr smplHdr;
|
|
WAVFmtHdr fmtHdr;
|
|
|
|
WAVE_Struct wave;
|
|
|
|
ALint error;
|
|
|
|
bFinished = false;
|
|
|
|
char data[BUFFERSIZE];
|
|
|
|
alSourceStop(mSource);
|
|
alSourcei(mSource, AL_BUFFER, 0);
|
|
|
|
stream = ResourceManager->openStream(mFilename);
|
|
if(stream != NULL) {
|
|
stream->read(4, &fileHdr.id[0]);
|
|
stream->read(&fileHdr.size);
|
|
stream->read(4, &fileHdr.type[0]);
|
|
|
|
stream->read(4, &chunkHdr.id[0]);
|
|
stream->read(&chunkHdr.size);
|
|
|
|
// WAV Format header
|
|
stream->read(&fmtHdr.format);
|
|
stream->read(&fmtHdr.channels);
|
|
stream->read(&fmtHdr.samplesPerSec);
|
|
stream->read(&fmtHdr.bytesPerSec);
|
|
stream->read(&fmtHdr.blockAlign);
|
|
stream->read(&fmtHdr.bitsPerSample);
|
|
|
|
format=(fmtHdr.channels==1?
|
|
(fmtHdr.bitsPerSample==8?AL_FORMAT_MONO8:AL_FORMAT_MONO16):
|
|
(fmtHdr.bitsPerSample==8?AL_FORMAT_STEREO8:AL_FORMAT_STEREO16));
|
|
freq=fmtHdr.samplesPerSec;
|
|
|
|
stream->read(4, &chunkHdr.id[0]);
|
|
stream->read(&chunkHdr.size);
|
|
|
|
DataSize = chunkHdr.size;
|
|
DataLeft = DataSize;
|
|
dataStart = stream->getPosition();
|
|
|
|
// 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;
|
|
stream->read(DataToRead, data);
|
|
DataLeft -= DataToRead;
|
|
alBufferData(mBufferList[loop], format, data, DataToRead, freq);
|
|
if ((error = alGetError()) != AL_NO_ERROR) {
|
|
return false;
|
|
}
|
|
++numBuffers;
|
|
if(bFinished)
|
|
break;
|
|
}
|
|
|
|
// Queue the buffers on the source
|
|
alSourceQueueBuffers(mSource, numBuffers, mBufferList);
|
|
if ((error = alGetError()) != AL_NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
buffersinqueue = numBuffers;
|
|
alSourcei(mSource, AL_LOOPING, AL_FALSE);
|
|
bReady = true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
|
|
bIsValid = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool WavStreamSource::updateBuffers() {
|
|
|
|
ALint processed;
|
|
ALuint BufferID;
|
|
ALint error;
|
|
char data[BUFFERSIZE];
|
|
|
|
// don't do anything if buffer isn't initialized
|
|
if(!bIsValid)
|
|
return false;
|
|
|
|
if(bFinished && mDescription.mIsLooping) {
|
|
resetStream();
|
|
}
|
|
|
|
// reset AL error code
|
|
alGetError();
|
|
|
|
// 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;
|
|
}
|
|
|
|
stream->read(DataToRead, data);
|
|
DataLeft -= DataToRead;
|
|
|
|
alBufferData(BufferID, format, data, DataToRead, 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 AL_TRUE;
|
|
}
|
|
|
|
void WavStreamSource::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;
|
|
}
|
|
}
|
|
|
|
void WavStreamSource::resetStream() {
|
|
stream->setPosition(dataStart);
|
|
DataLeft = DataSize;
|
|
bFinished = AL_FALSE;
|
|
}
|
|
|
|
#include "console/console.h"
|
|
|
|
F32 WavStreamSource::getElapsedTime()
|
|
{
|
|
Con::warnf( "GetElapsedTime not implemented in WaveStreams yet" );
|
|
return -1.f;
|
|
}
|
|
|
|
F32 WavStreamSource::getTotalTime()
|
|
{
|
|
Con::warnf( "GetTotalTime not implemented in WaveStreams yet" );
|
|
return -1.f;
|
|
}
|