Files
lime/legacy/project/src/openal/OpenALSound.cpp
Brian Gunn f8ae184a79 Changed to build OpenAL from source for Android.
Untested with the HXCPP_X86 build.
2014-12-21 13:24:12 -08:00

1325 lines
33 KiB
C++

#include "OpenALSound.h"
#include <math.h>
namespace nme
{
OpenALChannel::OpenALChannel(Object *inSound, ALuint inBufferID, int startTime, int inLoops, const SoundTransform &inTransform)
{
//LOG_SOUND("OpenALChannel constructor %d",inBufferID);
mSound = inSound;
inSound->IncRef();
mSourceID = 0;
mDynamicDone = true;
mDynamicBuffer[0] = 0;
mDynamicBuffer[1] = 0;
mDynamicStackSize = 0;
mWasPlaying = false;
mSampleBuffer = 0;
float seek = 0;
mSize = 0;
mStream = 0;
mUseStream = false;
mStartTime = startTime;
mLoops = inLoops;
if (inBufferID>0)
{
// grab a source ID from openAL
alGenSources(1, &mSourceID);
//if (inLoops < 1)
//{
// attach the buffer to the source
alSourcei(mSourceID, AL_BUFFER, inBufferID);
/*}
else
{
for (int i = 0; i <= inLoops; i++)
{
alSourceQueueBuffers(mSourceID, 1, &inBufferID);
}
}*/
// set some basic source prefs
alSourcef(mSourceID, AL_PITCH, 1.0f);
alSourcef(mSourceID, AL_GAIN, inTransform.volume);
alSource3f(mSourceID, AL_POSITION, (float) cos((inTransform.pan - 1) * (1.5707)), 0, (float) sin((inTransform.pan + 1) * (1.5707)));
// TODO: not right!
//if (inLoops>1)
//alSourcei(mSourceID, AL_LOOPING, AL_TRUE);
if (startTime > 0)
{
ALint bits, channels, freq;
alGetBufferi(inBufferID, AL_SIZE, &mSize);
alGetBufferi(inBufferID, AL_BITS, &bits);
alGetBufferi(inBufferID, AL_CHANNELS, &channels);
alGetBufferi(inBufferID, AL_FREQUENCY, &freq);
mLength = (ALfloat)((ALuint)mSize/channels/(bits/8)) / (ALfloat)freq;
seek = (startTime * 0.001) / mLength;
}
if (seek < 1)
{
//alSourceQueueBuffers(mSourceID, 1, &inBufferID);
alSourcePlay(mSourceID);
if (seek != 0)
{
alSourcef(mSourceID, AL_BYTE_OFFSET, seek * mSize);
}
}
mWasPlaying = true;
sgOpenChannels.push_back((intptr_t)this);
}
}
OpenALChannel::OpenALChannel(Object *inSound, AudioStream *inStream, int startTime, int inLoops, const SoundTransform &inTransform)
{
mSound = inSound;
inSound->IncRef();
mSourceID = 0;
mDynamicDone = true;
mDynamicBuffer[0] = 0;
mDynamicBuffer[1] = 0;
mDynamicStackSize = 0;
mWasPlaying = false;
mSampleBuffer = 0;
float seek = 0;
int size = 0;
mStartTime = startTime;
mLoops = inLoops;
mStream = inStream;
mUseStream = true;
if (mStream)
{
//mStream->update();
mStream->playback();
}
mWasPlaying = true;
sgOpenChannels.push_back((intptr_t)this);
}
OpenALChannel::OpenALChannel(const ByteArray &inBytes,const SoundTransform &inTransform)
{
//LOG_SOUND("OpenALChannel dynamic %d",inBytes.Size());
mSound = 0;
mSourceID = 0;
mUseStream = false;
mDynamicBuffer[0] = 0;
mDynamicBuffer[1] = 0;
mDynamicStackSize = 0;
mSampleBuffer = 0;
mWasPlaying = true;
mStream = 0;
alGenBuffers(2, mDynamicBuffer);
if (!mDynamicBuffer[0])
{
//LOG_SOUND("Error creating dynamic sound buffer!");
}
else
{
mSampleBuffer = new short[8192*STEREO_SAMPLES];
// grab a source ID from openAL
alGenSources(1, &mSourceID);
QueueBuffer(mDynamicBuffer[0],inBytes);
if (!mDynamicDone)
mDynamicStack[mDynamicStackSize++] = mDynamicBuffer[1];
// set some basic source prefs
alSourcef(mSourceID, AL_PITCH, 1.0f);
alSourcef(mSourceID, AL_GAIN, inTransform.volume);
alSource3f(mSourceID, AL_POSITION, (float) cos((inTransform.pan - 1) * (1.5707)), 0, (float) sin((inTransform.pan + 1) * (1.5707)));
alSourcePlay(mSourceID);
}
//sgOpenChannels.push_back((intptr_t)this);
}
void OpenALChannel::QueueBuffer(ALuint inBuffer, const ByteArray &inBytes)
{
int time_samples = inBytes.Size()/sizeof(float)/STEREO_SAMPLES;
const float *buffer = (const float *)inBytes.Bytes();
for(int i=0;i<time_samples;i++)
{
mSampleBuffer[ i<<1 ] = *buffer++ * ((1<<15)-1);
mSampleBuffer[ (i<<1) + 1 ] = *buffer++ * ((1<<15)-1);
}
mDynamicDone = time_samples < 1024;
alBufferData(inBuffer, AL_FORMAT_STEREO16, mSampleBuffer, time_samples*STEREO_SAMPLES*sizeof(short), 44100 );
//LOG_SOUND("Dynamic queue buffer %d (%d)", inBuffer, time_samples );
alSourceQueueBuffers(mSourceID, 1, &inBuffer );
}
void OpenALChannel::unqueueBuffers()
{
ALint processed = 0;
alGetSourcei(mSourceID, AL_BUFFERS_PROCESSED, &processed);
//LOG_SOUND("Recover buffers : %d (%d)", processed, mDynamicStackSize);
if (processed)
{
alSourceUnqueueBuffers(mSourceID,processed,&mDynamicStack[mDynamicStackSize]);
mDynamicStackSize += processed;
}
}
bool OpenALChannel::needsData()
{
if (!mDynamicBuffer[0] || mDynamicDone)
return false;
unqueueBuffers();
//LOG_SOUND("needsData (%d)", mDynamicStackSize);
if (mDynamicStackSize)
{
mDynamicDone = true;
return true;
}
return false;
}
void OpenALChannel::addData(const ByteArray &inBytes)
{
if (!mDynamicStackSize)
{
//LOG_SOUND("Adding data with no buffers?");
return;
}
mDynamicDone = false;
ALuint buffer = mDynamicStack[0];
mDynamicStack[0] = mDynamicStack[1];
mDynamicStackSize--;
QueueBuffer(buffer,inBytes);
// Make sure it is still playing ...
if (!mDynamicDone && mDynamicStackSize==1)
{
ALint val = 0;
alGetSourcei(mSourceID, AL_SOURCE_STATE, &val);
if(val != AL_PLAYING)
{
//LOG_SOUND("Kickstart (%d/%d)",val,mDynamicStackSize);
// This is an indication that the previous buffer finished playing before we could deliver the new buffer.
// You will hear ugly popping noises...
alSourcePlay(mSourceID);
}
}
}
OpenALChannel::~OpenALChannel()
{
//LOG_SOUND("OpenALChannel destructor");
if (mSourceID)
alDeleteSources(1, &mSourceID);
if (mDynamicBuffer[0])
alDeleteBuffers(2, mDynamicBuffer);
delete [] mSampleBuffer;
if (mSound)
mSound->DecRef();
if (mStream)
{
mStream->release();
delete mStream;
}
for (int i = 0; i < sgOpenChannels.size(); i++)
{
if (sgOpenChannels[i] == (intptr_t)this)
{
sgOpenChannels.erase (i, 1);
break;
}
}
}
bool OpenALChannel::isComplete()
{
if (mUseStream)
{
if (mStream)
{
mStream->update();
return !(mStream->isActive());
}
else
{
return true;
}
}
if (!mSourceID)
{
//LOG_SOUND("OpenALChannel isComplete() - never started!");
return true;
}
if (!mDynamicDone)
return false;
// got this hint from
// http://www.gamedev.net/topic/410696-openal-how-to-query-if-a-source-sound-is-playing-solved/
ALint state;
alGetSourcei(mSourceID, AL_SOURCE_STATE, &state);
/*
Possible values of state
AL_INITIAL
AL_STOPPED
AL_PLAYING
AL_PAUSED
*/
if(state == AL_STOPPED)
{
if (mLoops > 0)
{
float seek = 0;
if (mStartTime > 0)
{
seek = (mStartTime * 0.001) / mLength;
}
if (seek < 1)
{
//alSourceQueueBuffers(mSourceID, 1, &inBufferID);
alSourcePlay(mSourceID);
alSourcef(mSourceID, AL_BYTE_OFFSET, seek * mSize);
}
mLoops --;
return false;
}
else
{
return true;
}
//LOG_SOUND("OpenALChannel isComplete() returning true");
}
else
{
//LOG_SOUND("OpenALChannel isComplete() returning false");
return false;
}
}
double OpenALChannel::getLeft()
{
if (mUseStream)
{
return mStream ? mStream->getLeft() : 0;
}
else
{
float panX=0;
float panY=0;
float panZ=0;
alGetSource3f(mSourceID, AL_POSITION, &panX, &panY, &panZ);
return (1-panX)/2;
}
}
double OpenALChannel::getRight()
{
if (mUseStream)
{
return mStream ? mStream->getRight() : 0;
}
else
{
float panX=0;
float panY=0;
float panZ=0;
alGetSource3f(mSourceID, AL_POSITION, &panX, &panY, &panZ);
return (panX+1)/2;
}
}
double OpenALChannel::setPosition(const float &inFloat)
{
if (mUseStream)
{
return mStream ? mStream->setPosition(inFloat) : inFloat;
}
else
{
alSourcef(mSourceID,AL_SEC_OFFSET,inFloat);
return inFloat;
}
}
double OpenALChannel::getPosition()
{
if (mUseStream)
{
return mStream ? mStream->getPosition() : 0;
}
else
{
float pos = 0;
alGetSourcef(mSourceID, AL_SEC_OFFSET, &pos);
return pos * 1000.0;
}
}
void OpenALChannel::setTransform(const SoundTransform &inTransform)
{
if (mUseStream)
{
if (mStream) mStream->setTransform(inTransform);
}
else
{
alSourcef(mSourceID, AL_GAIN, inTransform.volume);
alSource3f(mSourceID, AL_POSITION, (float) cos((inTransform.pan - 1) * (1.5707)), 0, (float) sin((inTransform.pan + 1) * (1.5707)));
}
}
void OpenALChannel::stop()
{
if (mUseStream)
{
if (mStream)
{
mStream->release();
delete mStream;
mStream = 0;
mWasPlaying = false;
}
}
else
{
ALint state;
alGetSourcei(mSourceID, AL_SOURCE_STATE, &state);
if (state == AL_PLAYING)
{
alSourceStop(mSourceID);
}
}
}
void OpenALChannel::suspend()
{
if (mUseStream)
{
if (mStream)
{
mStream->suspend();
mWasPlaying = true;
}
else
{
mWasPlaying = false;
}
}
else
{
ALint state;
alGetSourcei(mSourceID, AL_SOURCE_STATE, &state);
if (state == AL_PLAYING)
{
alSourcePause(mSourceID);
mWasPlaying = true;
return;
}
mWasPlaying = false;
}
}
void OpenALChannel::resume()
{
if (mUseStream)
{
if (mWasPlaying)
{
mStream->resume();
}
}
else
{
if (mWasPlaying)
{
alSourcePlay(mSourceID);
}
}
}
SoundChannel *SoundChannel::Create(const ByteArray &inBytes,const SoundTransform &inTransform)
{
if (!OpenALInit())
return 0;
return new OpenALChannel(inBytes, inTransform);
}
OpenALSound::OpenALSound(const std::string &inFilename, bool inForceMusic)
{
IncRef();
mBufferID = 0;
mIsStream = false;
mTotalTime = -1;
#ifdef HX_MACOS
char fileURL[1024];
GetBundleFilename(inFilename.c_str(),fileURL,1024);
#else
#ifdef IPHONE
std::string asset = GetResourcePath() + gAssetBase + inFilename;
const char *fileURL = asset.c_str();
#else
const char *fileURL = inFilename.c_str();
#endif
#endif
if (!fileURL) {
//LOG_SOUND("OpenALSound constructor() error in url");
mError = "Error int url: " + inFilename;
} else {
QuickVec<uint8> buffer;
int _channels;
int _bitsPerSample;
ALenum format;
ALsizei freq;
bool ok = false;
//Determine the file format before we try anything
AudioFormat type = Audio::determineFormatFromFile(std::string(fileURL));
switch(type) {
case eAF_ogg:
if (inForceMusic)
{
mIsStream = true;
mStreamPath = fileURL;
}
else
{
ok = Audio::loadOggSampleFromFile( fileURL, buffer, &_channels, &_bitsPerSample, &freq );
}
break;
case eAF_wav:
ok = Audio::loadWavSampleFromFile( fileURL, buffer, &_channels, &_bitsPerSample, &freq );
break;
default:
LOG_SOUND("Error opening sound file, unsupported type.\n");
}
if (mIsStream)
return;
//Work out the format from the data
if (_channels == 1) {
if (_bitsPerSample == 8 ) {
format = AL_FORMAT_MONO8;
} else if (_bitsPerSample == 16) {
format = (int)AL_FORMAT_MONO16;
}
} else if (_channels == 2) {
if (_bitsPerSample == 8 ) {
format = (int)AL_FORMAT_STEREO8;
} else if (_bitsPerSample == 16) {
format = (int)AL_FORMAT_STEREO16;
}
} //channels = 2
if (!ok) {
LOG_SOUND("Error opening sound data\n");
mError = "Error opening sound data";
} else if (alGetError() != AL_NO_ERROR) {
LOG_SOUND("Error after opening sound data\n");
mError = "Error after opening sound data";
} else {
// grab a buffer ID from openAL
alGenBuffers(1, &mBufferID);
// load the awaiting data blob into the openAL buffer.
alBufferData(mBufferID,format,&buffer[0],buffer.size(),freq);
// once we have all our information loaded, get some extra flags
alGetBufferi(mBufferID, AL_SIZE, &bufferSize);
alGetBufferi(mBufferID, AL_FREQUENCY, &frequency);
alGetBufferi(mBufferID, AL_CHANNELS, &channels);
alGetBufferi(mBufferID, AL_BITS, &bitsPerSample);
} //!ok
}
}
OpenALSound::OpenALSound(float *inData, int len)
{
IncRef();
mBufferID = 0;
mIsStream = false;
QuickVec<uint8> buffer;
int _channels;
int _bitsPerSample;
ALenum format;
ALsizei freq;
bool ok = false;
//Determine the file format before we try anything
AudioFormat type = Audio::determineFormatFromBytes(inData, len);
switch(type) {
case eAF_ogg:
ok = Audio::loadOggSampleFromBytes(inData, len, buffer, &_channels, &_bitsPerSample, &freq );
break;
case eAF_wav:
ok = Audio::loadWavSampleFromBytes(inData, len, buffer, &_channels, &_bitsPerSample, &freq );
break;
default:
LOG_SOUND("Error opening sound file, unsupported type.\n");
}
//Work out the format from the data
if (_channels == 1) {
if (_bitsPerSample == 8 ) {
format = AL_FORMAT_MONO8;
} else if (_bitsPerSample == 16) {
format = (int)AL_FORMAT_MONO16;
}
} else if (_channels == 2) {
if (_bitsPerSample == 8 ) {
format = (int)AL_FORMAT_STEREO8;
} else if (_bitsPerSample == 16) {
format = (int)AL_FORMAT_STEREO16;
}
} //channels = 2
if (!ok) {
LOG_SOUND("Error opening sound data\n");
mError = "Error opening sound data";
} else if (alGetError() != AL_NO_ERROR) {
LOG_SOUND("Error after opening sound data\n");
mError = "Error after opening sound data";
} else {
// grab a buffer ID from openAL
alGenBuffers(1, &mBufferID);
// load the awaiting data blob into the openAL buffer.
alBufferData(mBufferID,format,&buffer[0],buffer.size(),freq);
// once we have all our information loaded, get some extra flags
alGetBufferi(mBufferID, AL_SIZE, &bufferSize);
alGetBufferi(mBufferID, AL_FREQUENCY, &frequency);
alGetBufferi(mBufferID, AL_CHANNELS, &channels);
alGetBufferi(mBufferID, AL_BITS, &bitsPerSample);
}
}
OpenALSound::~OpenALSound()
{
//LOG_SOUND("OpenALSound destructor() ###################################");
if (mBufferID!=0)
alDeleteBuffers(1, &mBufferID);
}
double OpenALSound::getLength()
{
if (mTotalTime == -1)
{
if (mIsStream)
{
AudioStream_Ogg *audioStream = new AudioStream_Ogg();
if (audioStream)
{
int length = audioStream->getLength(mStreamPath.c_str());
audioStream->release();
mTotalTime = length * 1000;
delete audioStream;
}
else
{
mTotalTime = 0;
}
}
else
{
double result = ((double)bufferSize) / (frequency * channels * (bitsPerSample/8) );
//LOG_SOUND("OpenALSound getLength returning %f", toBeReturned);
mTotalTime = result * 1000;
}
}
return mTotalTime;
}
void OpenALSound::getID3Value(const std::string &inKey, std::string &outValue)
{
//LOG_SOUND("OpenALSound getID3Value returning empty string");
outValue = "";
}
int OpenALSound::getBytesLoaded()
{
int toBeReturned = ok() ? 100 : 0;
//LOG_SOUND("OpenALSound getBytesLoaded returning %i", toBeReturned);
return toBeReturned;
}
int OpenALSound::getBytesTotal()
{
int toBeReturned = ok() ? 100 : 0;
//LOG_SOUND("OpenALSound getBytesTotal returning %i", toBeReturned);
return toBeReturned;
}
bool OpenALSound::ok()
{
bool toBeReturned = mError.empty();
//LOG_SOUND("OpenALSound ok() returning BOOL = %@\n", (toBeReturned ? @"YES" : @"NO"));
return toBeReturned;
}
std::string OpenALSound::getError()
{
//LOG_SOUND("OpenALSound getError()");
return mError;
}
void OpenALSound::close()
{
//LOG_SOUND("OpenALSound close() doing nothing");
}
SoundChannel *OpenALSound::openChannel(double startTime, int loops, const SoundTransform &inTransform)
{
//LOG_SOUND("OpenALSound openChannel()");
if (mIsStream)
{
AudioStream_Ogg *audioStream = new AudioStream_Ogg();
if (audioStream) audioStream->open(mStreamPath.c_str(), startTime, loops, inTransform);
return new OpenALChannel(this, audioStream, startTime, loops, inTransform);
}
else
{
return new OpenALChannel(this, mBufferID, startTime, loops, inTransform);
}
}
#ifndef IPHONE
Sound *Sound::Create(const std::string &inFilename,bool inForceMusic)
{
//Always check if openal is intitialized
if (!OpenALInit())
return 0;
//Return a reference
OpenALSound *sound;
#ifdef ANDROID
if (!inForceMusic)
{
ByteArray bytes = AndroidGetAssetBytes(inFilename.c_str());
sound = new OpenALSound((float*)bytes.Bytes(), bytes.Size());
}
else
{
sound = new OpenALSound(inFilename, inForceMusic);
}
#else
sound = new OpenALSound(inFilename, inForceMusic);
#endif
if (sound->ok ())
return sound;
else
return 0;
}
Sound *Sound::Create(float *inData, int len, bool inForceMusic)
{
//Always check if openal is intitialized
if (!OpenALInit())
return 0;
//Return a reference
OpenALSound *sound = new OpenALSound(inData, len);
if (sound->ok ())
return sound;
else
return 0;
}
#endif
void Sound::Suspend()
{
//Always check if openal is initialized
if (!OpenALInit())
return;
OpenALChannel* channel = 0;
for (int i = 0; i < sgOpenChannels.size(); i++)
{
channel = (OpenALChannel*)(sgOpenChannels[i]);
if (channel)
{
channel->suspend();
}
}
alcMakeContextCurrent(0);
alcSuspendContext(sgContext);
#ifdef ANDROID
alcandroid_Suspend();
#endif
}
void Sound::Resume()
{
//Always check if openal is initialized
if (!OpenALInit())
return;
#ifdef ANDROID
alcandroid_Resume();
#endif
alcMakeContextCurrent(sgContext);
OpenALChannel* channel = 0;
for (int i = 0; i < sgOpenChannels.size(); i++)
{
channel = (OpenALChannel*)(sgOpenChannels[i]);
if (channel)
{
channel->resume();
}
}
alcProcessContext(sgContext);
}
void Sound::Shutdown()
{
OpenALClose();
}
//Ogg Audio Stream implementation
AudioStream_Ogg::AudioStream_Ogg() {
source = 0;
mIsValid = false;
mSuspend = false;
oggFile = 0;
oggStream = 0;
mLoops = 0;
mStartTime = 0;
}
int AudioStream_Ogg::getLength(const std::string &path) {
if (openal_is_shutdown) return 0;
int result;
mPath = std::string(path.c_str());
mIsValid = true;
#ifdef ANDROID
mInfo = AndroidGetAssetFD(path.c_str());
oggFile = fdopen(mInfo.fd, "rb");
fseek(oggFile, mInfo.offset, 0);
ov_callbacks callbacks;
callbacks.read_func = &nme::AudioStream_Ogg::read_func;
callbacks.seek_func = &nme::AudioStream_Ogg::seek_func;
callbacks.close_func = &nme::AudioStream_Ogg::close_func;
callbacks.tell_func = &nme::AudioStream_Ogg::tell_func;
#else
oggFile = fopen(path.c_str(), "rb");
#endif
if(!oggFile) {
//throw std::string("Could not open Ogg file.");
LOG_SOUND("Could not open Ogg file.");
mIsValid = false;
return 0;
}
oggStream = new OggVorbis_File();
#ifdef ANDROID
result = ov_open_callbacks(this, oggStream, NULL, 0, callbacks);
#else
result = ov_open(oggFile, oggStream, NULL, 0);
#endif
if(result < 0) {
fclose(oggFile);
oggFile = 0;
//throw std::string("Could not open Ogg stream. ") + errorString(result);
LOG_SOUND("Could not open Ogg stream.");
//LOG_SOUND(errorString(result).c_str());
mIsValid = false;
return 0;
}
return ov_time_total(oggStream, -1);
}
void AudioStream_Ogg::open(const std::string &path, int startTime, int inLoops, const SoundTransform &inTransform) {
if (openal_is_shutdown) return;
int result;
mPath = std::string(path.c_str());
mStartTime = startTime;
mLoops = inLoops;
mIsValid = true;
mSuspend = false;
#ifdef ANDROID
mInfo = AndroidGetAssetFD(path.c_str());
oggFile = fdopen(mInfo.fd, "rb");
fseek(oggFile, mInfo.offset, 0);
ov_callbacks callbacks;
callbacks.read_func = &nme::AudioStream_Ogg::read_func;
callbacks.seek_func = &nme::AudioStream_Ogg::seek_func;
callbacks.close_func = &nme::AudioStream_Ogg::close_func;
callbacks.tell_func = &nme::AudioStream_Ogg::tell_func;
#else
oggFile = fopen(path.c_str(), "rb");
#endif
if(!oggFile) {
//throw std::string("Could not open Ogg file.");
LOG_SOUND("Could not open Ogg file.");
mIsValid = false;
return;
}
oggStream = new OggVorbis_File();
#ifdef ANDROID
result = ov_open_callbacks(this, oggStream, NULL, 0, callbacks);
#else
result = ov_open(oggFile, oggStream, NULL, 0);
#endif
if(result < 0) {
fclose(oggFile);
oggFile = 0;
//throw std::string("Could not open Ogg stream. ") + errorString(result);
LOG_SOUND("Could not open Ogg stream.");
//LOG_SOUND(errorString(result).c_str());
mIsValid = false;
return;
}
vorbisInfo = ov_info(oggStream, -1);
vorbisComment = ov_comment(oggStream, -1);
if(vorbisInfo->channels == 1) {
format = AL_FORMAT_MONO16;
} else {
format = AL_FORMAT_STEREO16;
}
if (startTime != 0)
{
double seek = startTime * 0.001;
ov_time_seek(oggStream, seek);
}
alGenBuffers(2, buffers);
check();
alGenSources(1, &source);
check();
alSource3f(source, AL_POSITION, 0.0, 0.0, 0.0);
alSource3f(source, AL_VELOCITY, 0.0, 0.0, 0.0);
alSource3f(source, AL_DIRECTION, 0.0, 0.0, 0.0);
alSourcef (source, AL_ROLLOFF_FACTOR, 0.0 );
alSourcei (source, AL_SOURCE_RELATIVE, AL_TRUE );
setTransform(inTransform);
} //open
void AudioStream_Ogg::release() {
if (openal_is_shutdown) return;
if (source) {
alSourceStop(source);
empty();
alDeleteSources(1, &source);
check();
alDeleteBuffers(2, buffers);
check();
source = 0;
}
if (oggStream) {
ov_clear(oggStream);
delete oggStream;
oggStream = 0;
oggFile = 0;
}
mIsValid = false;
} //release
bool AudioStream_Ogg::playback() {
if (openal_is_shutdown) return false;
if(playing()) {
return true;
}
if(!stream(buffers[0])) {
return false;
}
if(!stream(buffers[1])) {
return false;
}
alSourceQueueBuffers(source, 2, buffers);
alSourcePlay(source);
return true;
} //playback
bool AudioStream_Ogg::playing() {
if (openal_is_shutdown) return false;
ALint state;
alGetSourcei(source, AL_SOURCE_STATE, &state);
return (state == AL_PLAYING);
} //playing
bool AudioStream_Ogg::update() {
if (openal_is_shutdown) return false;
if (mSuspend) return true;
if (!mIsValid) return false;
int processed = 0;
bool active = true;
alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
while(processed--) {
ALuint buffer;
alSourceUnqueueBuffers(source, 1, &buffer);
alGetError();
if (buffer)
{
active = stream(buffer);
alSourceQueueBuffers(source, 1, &buffer);
check();
}
}
if (active && !playing())
alSourcePlay(source);
/*if (!active && mLoops > 0)
{
mLoops --;
double seek = mStartTime * 0.001;
ov_time_seek(oggStream, seek);
return update();
}*/
return active;
} //update
bool AudioStream_Ogg::stream( ALuint buffer ) {
if (openal_is_shutdown) return false;
if (mSuspend) return true;
//LOG_SOUND("STREAM\n");
char pcm[STREAM_BUFFER_SIZE];
int size = 0;
int section;
int result;
while (size < STREAM_BUFFER_SIZE) {
result = ov_read(oggStream, pcm + size, STREAM_BUFFER_SIZE - size, 0, 2, 1, &section);
if(result > 0)
size += result;
else
if(result < 0) {
if ( mLoops > 0 ) {
mLoops --;
ov_time_seek(oggStream, 0);
}else{
break;
}
//LOG_SOUND ("Result is less than 0");
//throw errorString(result);
}
else
break;
}
if(size <= 0) {
if ( mLoops > 0 ) {
mLoops --;
ov_time_seek(oggStream, 0);
return stream( buffer );
}else{
alSourceStop(source);
return false;
}
}
alBufferData(buffer, format, pcm, size, vorbisInfo->rate);
check();
return true;
} //stream
void AudioStream_Ogg::empty() {
if (openal_is_shutdown) return;
int queued;
alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
while(queued--) {
ALuint buffer;
alSourceUnqueueBuffers(source, 1, &buffer);
check();
}
} //empty
void AudioStream_Ogg::check()
{
if (openal_is_shutdown) return;
int error = alGetError();
if(error != AL_NO_ERROR) {
//todo : print meaningful errors instead
LOG_SOUND("OpenAL error was raised: %d\n", error);
mIsValid = false;
//throw std::string("OpenAL error was raised.");
}
} //check
std::string AudioStream_Ogg::errorString(int code) {
switch(code) {
case OV_EREAD:
return std::string("Read from media.");
case OV_ENOTVORBIS:
return std::string("Not Vorbis data.");
case OV_EVERSION:
return std::string("Vorbis version mismatch.");
case OV_EBADHEADER:
return std::string("Invalid Vorbis header.");
case OV_EFAULT:
return std::string("Internal logic fault (bug or heap/stack corruption.");
default:
return std::string("Unknown Ogg error.");
} //switch
} //errorString
double AudioStream_Ogg::getLeft()
{
float panX=0;
float panY=0;
float panZ=0;
alGetSource3f(source, AL_POSITION, &panX, &panY, &panZ);
return (1-panX)/2;
}
double AudioStream_Ogg::getRight()
{
float panX=0;
float panY=0;
float panZ=0;
alGetSource3f(source, AL_POSITION, &panX, &panY, &panZ);
return (panX+1)/2;
}
double AudioStream_Ogg::setPosition(const float &inFloat)
{
if (!mSuspend)
{
double seek = inFloat * 0.001;
ov_time_seek(oggStream, seek);
}
return inFloat;
}
double AudioStream_Ogg::getPosition()
{
if (mSuspend)
{
return 0;
}
else
{
double pos = ov_time_tell(oggStream);
return pos * 1000.0;
}
}
void AudioStream_Ogg::setTransform(const SoundTransform &inTransform)
{
if (!mSuspend)
{
alSourcef(source, AL_GAIN, inTransform.volume);
//magic number : Half PI
alSource3f(source, AL_POSITION, (float) cos((inTransform.pan - 1) * (1.5707)), 0, (float) sin((inTransform.pan + 1) * (1.5707)));
}
}
void AudioStream_Ogg::suspend()
{
mSuspend = true;
alSourcePause(source);
//release();
}
void AudioStream_Ogg::resume()
{
alSourcePlay(source);
//SoundTransform transform = SoundTransform();
//transform.volume = 1;
//transform.pan = 0;
//open(mPath, mStartTime, mLoops, transform);
mSuspend = false;
//update();
//update();
//playback();
}
bool AudioStream_Ogg::isActive()
{
//#ifdef ANDROID
// TODO: Android stream sounds won't resume :(
//if (mSuspend) return false;
//#else
if (mSuspend) return true;
//#endif
//playback();
return (mIsValid && playing());
}
}