tge/engine/audio/wavStreamSource.cc
2025-02-17 23:17:30 -06:00

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;
}