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