/** * Copyright (c) 2006-2011 LOVE Development Team * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. **/ #include "Source.h" #include "Pool.h" // STD #include namespace love { namespace audio { namespace openal { Source::Source(Pool * pool, love::sound::SoundData * soundData) : love::audio::Source(Source::TYPE_STATIC), pool(pool), valid(false), pitch(1.0f), volume(1.0f), looping(false), decoder(0) { alGenBuffers(1, buffers); ALenum fmt = getFormat(soundData->getChannels(), soundData->getBits()); alBufferData(buffers[0], fmt, soundData->getData(), soundData->getSize(), soundData->getSampleRate()); static float z[3] = {0, 0, 0}; setFloatv(position, z); setFloatv(velocity, z); setFloatv(direction, z); } Source::Source(Pool * pool, love::sound::Decoder * decoder) : love::audio::Source(Source::TYPE_STREAM), pool(pool), valid(false), pitch(1.0f), volume(1.0f), looping(false), decoder(decoder) { decoder->retain(); alGenBuffers(MAX_BUFFERS, buffers); static float z[3] = {0, 0, 0}; setFloatv(position, z); setFloatv(velocity, z); setFloatv(direction, z); } Source::~Source() { stop(); alDeleteBuffers((type == TYPE_STATIC) ? 1 : MAX_BUFFERS, buffers); } love::audio::Source * Source::copy() { return 0; } void Source::play() { valid = pool->play(this, source); if(valid) reset(source); } void Source::stop() { if (!isStopped()) pool->stop(this); } void Source::pause() { pool->pause(this); } void Source::resume() { pool->resume(this); } void Source::rewind() { pool->rewind(this); } bool Source::isStopped() const { if(valid) { ALenum state; alGetSourcei(source, AL_SOURCE_STATE, &state); return (state == AL_STOPPED); } return true; } bool Source::isPaused() const { if(valid) { ALenum state; alGetSourcei(source, AL_SOURCE_STATE, &state); return (state == AL_PAUSED); } return false; } bool Source::isFinished() const { return type == TYPE_STATIC ? isStopped() : isStopped() && !isLooping() && decoder->isFinished(); } void Source::update() { if(valid && type == TYPE_STATIC) { // Looping mode could have changed. alSourcei(source, AL_LOOPING, isLooping() ? AL_TRUE : AL_FALSE); } else if(valid && type == TYPE_STREAM) { // Number of processed buffers. ALint processed; alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed); while(processed--) { ALuint buffer; // Get a free buffer. alSourceUnqueueBuffers(source, 1, &buffer); if(streamAtomic(buffer, decoder) > 0) alSourceQueueBuffers(source, 1, &buffer); } } } void Source::setPitch(float pitch) { if(valid) alSourcef(source, AL_PITCH, pitch); this->pitch = pitch; } float Source::getPitch() const { if(valid) { ALfloat f; alGetSourcef(source, AL_PITCH, &f); return f; } // In case the Source isn't playing. return pitch; } void Source::setVolume(float volume) { if(valid) { alSourcef(source, AL_GAIN, volume); } this->volume = volume; } float Source::getVolume() const { if(valid) { ALfloat f; alGetSourcef(source, AL_GAIN, &f); return f; } // In case the Source isn't playing. return volume; } void Source::setPosition(float * v) { if(valid) alSourcefv(source, AL_POSITION, v); setFloatv(position, v); } void Source::getPosition(float * v) const { if(valid) alGetSourcefv(source, AL_POSITION, v); else setFloatv(v, position); } void Source::setVelocity(float * v) { if(valid) alSourcefv(source, AL_VELOCITY, v); setFloatv(velocity, v); } void Source::getVelocity(float * v) const { if(valid) alGetSourcefv(source, AL_VELOCITY, v); else setFloatv(v, velocity); } void Source::setDirection(float * v) { if(valid) alSourcefv(source, AL_DIRECTION, v); else setFloatv(direction, v); } void Source::getDirection(float * v) const { if(valid) alGetSourcefv(source, AL_DIRECTION, v); else setFloatv(v, direction); } void Source::setLooping(bool looping) { if(valid && type == TYPE_STATIC) alSourcei(source, AL_LOOPING, looping ? AL_TRUE : AL_FALSE); this->looping = looping; } bool Source::isLooping() const { return looping; } void Source::playAtomic() { if(type == TYPE_STATIC) { alSourcei(source, AL_BUFFER, buffers[0]); } else if(type == TYPE_STREAM) { int usedBuffers = 0; for(unsigned int i = 0; i < MAX_BUFFERS; i++) { int decoded = streamAtomic(buffers[i], decoder); ++usedBuffers; if(decoded < decoder->getSize()) break; } if(usedBuffers > 0) alSourceQueueBuffers(source, usedBuffers, buffers); } // Set these properties. These may have changed while we've // been without an AL source. alSourcef(source, AL_PITCH, pitch); alSourcef(source, AL_GAIN, volume); alSourcePlay(source); valid = true; //if it fails it will be set to false again //but this prevents a horrible, horrible bug } void Source::stopAtomic() { if(valid) { if(type == TYPE_STATIC) { alSourceStop(source); } else if(type == TYPE_STREAM) { alSourceStop(source); int queued = 0; alGetSourcei(source, AL_BUFFERS_QUEUED, &queued); while(queued--) { ALuint buffer; alSourceUnqueueBuffers(source, 1, &buffer); } } alSourcei(source, AL_BUFFER, AL_NONE); } rewindAtomic(); valid = false; } void Source::pauseAtomic() { if(valid) { alSourcePause(source); } } void Source::resumeAtomic() { if(valid) { alSourcePlay(source); } } void Source::rewindAtomic() { if(valid && type == TYPE_STATIC) { alSourceRewind(source); } else if(valid && type == TYPE_STREAM) { decoder->rewind(); } } void Source::reset(ALenum source) { alSourcefv(source, AL_POSITION, position); alSourcefv(source, AL_VELOCITY, velocity); alSourcefv(source, AL_DIRECTION, direction); alSourcef(source, AL_PITCH, pitch); alSourcef(source, AL_GAIN, volume); } void Source::setFloatv(float * dst, const float * src) const { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; } ALenum Source::getFormat(int channels, int bits) const { if(channels == 1 && bits == 8) return AL_FORMAT_MONO8; else if(channels == 1 && bits == 16) return AL_FORMAT_MONO16; else if(channels == 2 && bits == 8) return AL_FORMAT_STEREO8; else if(channels == 2 && bits == 16) return AL_FORMAT_STEREO16; else return 0; } int Source::streamAtomic(ALuint buffer, love::sound::Decoder * d) { // Get more sound data. int decoded = d->decode(); int fmt = getFormat(d->getChannels(), d->getBits()); if(decoded > 0 && fmt != 0) alBufferData(buffer, fmt, d->getBuffer(), decoded, d->getSampleRate()); if(decoded < d->getSize() && isLooping()) d->rewind(); return decoded; } bool Source::isStatic() const { return (type == TYPE_STATIC); } } // openal } // audio } // love