123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428 |
- /**
- * Copyright (c) 2006-2017 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 "Filter.h"
- #include "Pool.h"
- #include "Audio.h"
- #include "common/math.h"
- // STD
- #include <iostream>
- #include <algorithm>
- #define audiomodule() (Module::getInstance<Audio>(Module::M_AUDIO))
- using love::thread::Lock;
- namespace love
- {
- namespace audio
- {
- namespace openal
- {
- class InvalidFormatException : public love::Exception
- {
- public:
- InvalidFormatException(int channels, int bitdepth)
- : Exception("%d-channel Sources with %d bits per sample are not supported.", channels, bitdepth)
- {
- }
- };
- class SpatialSupportException : public love::Exception
- {
- public:
- SpatialSupportException()
- : Exception("This spatial audio functionality is only available for mono Sources. \
- Ensure the Source is not multi-channel before calling this function.")
- {
- }
- };
- class QueueFormatMismatchException : public love::Exception
- {
- public:
- QueueFormatMismatchException()
- : Exception("Queued sound data must have same format as sound Source.")
- {
- }
- };
- class QueueTypeMismatchException : public love::Exception
- {
- public:
- QueueTypeMismatchException()
- : Exception("Only queueable Sources can be queued with sound data.")
- {
- }
- };
- class QueueMalformedLengthException : public love::Exception
- {
- public:
- QueueMalformedLengthException(int bytes)
- : Exception("Data length must be a multiple of sample size (%d bytes).", bytes)
- {
- }
- };
- class QueueLoopingException : public love::Exception
- {
- public:
- QueueLoopingException()
- : Exception("Queueable Sources can not be looped.")
- {
- }
- };
- StaticDataBuffer::StaticDataBuffer(ALenum format, const ALvoid *data, ALsizei size, ALsizei freq)
- : size(size)
- {
- alGenBuffers(1, &buffer);
- alBufferData(buffer, format, data, size, freq);
- }
- StaticDataBuffer::~StaticDataBuffer()
- {
- alDeleteBuffers(1, &buffer);
- }
- Source::Source(Pool *pool, love::sound::SoundData *soundData)
- : love::audio::Source(Source::TYPE_STATIC)
- , pool(pool)
- , sampleRate(soundData->getSampleRate())
- , channels(soundData->getChannels())
- , bitDepth(soundData->getBitDepth())
- , sendfilters(audiomodule()->getMaxSourceEffects(), nullptr)
- , sendtargets(audiomodule()->getMaxSourceEffects(), AL_EFFECTSLOT_NULL)
- {
- ALenum fmt = Audio::getFormat(soundData->getBitDepth(), soundData->getChannels());
- if (fmt == AL_NONE)
- throw InvalidFormatException(soundData->getChannels(), soundData->getBitDepth());
- staticBuffer.set(new StaticDataBuffer(fmt, soundData->getData(), (ALsizei) soundData->getSize(), sampleRate), Acquire::NORETAIN);
- 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)
- , sampleRate(decoder->getSampleRate())
- , channels(decoder->getChannels())
- , bitDepth(decoder->getBitDepth())
- , decoder(decoder)
- , unusedBufferTop(MAX_BUFFERS - 1)
- , sendfilters(audiomodule()->getMaxSourceEffects(), nullptr)
- , sendtargets(audiomodule()->getMaxSourceEffects(), AL_EFFECTSLOT_NULL)
- {
- if (Audio::getFormat(decoder->getBitDepth(), decoder->getChannels()) == AL_NONE)
- throw InvalidFormatException(decoder->getChannels(), decoder->getBitDepth());
- alGenBuffers(MAX_BUFFERS, streamBuffers);
- for (unsigned int i = 0; i < MAX_BUFFERS; i++)
- unusedBuffers[i] = streamBuffers[i];
- float z[3] = {0, 0, 0};
- setFloatv(position, z);
- setFloatv(velocity, z);
- setFloatv(direction, z);
- }
- Source::Source(Pool *pool, int sampleRate, int bitDepth, int channels)
- : love::audio::Source(Source::TYPE_QUEUE)
- , pool(pool)
- , sampleRate(sampleRate)
- , channels(channels)
- , bitDepth(bitDepth)
- , sendfilters(audiomodule()->getMaxSourceEffects(), nullptr)
- , sendtargets(audiomodule()->getMaxSourceEffects(), AL_EFFECTSLOT_NULL)
- {
- ALenum fmt = Audio::getFormat(bitDepth, channels);
- if (fmt == AL_NONE)
- throw InvalidFormatException(channels, bitDepth);
- alGenBuffers(MAX_BUFFERS, streamBuffers);
- for (unsigned int i = 0; i < MAX_BUFFERS; i++)
- unusedBuffers[i] = streamBuffers[i];
- float z[3] = {0, 0, 0};
- setFloatv(position, z);
- setFloatv(velocity, z);
- setFloatv(direction, z);
- }
- Source::Source(const Source &s)
- : love::audio::Source(s.sourceType)
- , pool(s.pool)
- , valid(false)
- , staticBuffer(s.staticBuffer)
- , pitch(s.pitch)
- , volume(s.volume)
- , relative(s.relative)
- , looping(s.looping)
- , minVolume(s.minVolume)
- , maxVolume(s.maxVolume)
- , referenceDistance(s.referenceDistance)
- , rolloffFactor(s.rolloffFactor)
- , maxDistance(s.maxDistance)
- , cone(s.cone)
- , offsetSamples(0)
- , offsetSeconds(0)
- , sampleRate(s.sampleRate)
- , channels(s.channels)
- , bitDepth(s.bitDepth)
- , decoder(nullptr)
- , toLoop(0)
- , unusedBufferTop(s.sourceType == TYPE_STREAM ? MAX_BUFFERS - 1 : -1)
- , sendfilters(s.sendfilters)
- , sendtargets(s.sendtargets)
- {
- if (sourceType == TYPE_STREAM)
- {
- if (s.decoder.get())
- decoder.set(s.decoder->clone(), Acquire::NORETAIN);
- }
- if (sourceType != TYPE_STATIC)
- {
- alGenBuffers(MAX_BUFFERS, streamBuffers);
- for (unsigned int i = 0; i < MAX_BUFFERS; i++)
- unusedBuffers[i] = streamBuffers[i];
- }
- if (s.directfilter)
- directfilter = s.directfilter->clone();
- setFloatv(position, s.position);
- setFloatv(velocity, s.velocity);
- setFloatv(direction, s.direction);
- }
- Source::~Source()
- {
- stop();
- if (sourceType != TYPE_STATIC)
- alDeleteBuffers(MAX_BUFFERS, streamBuffers);
- if (directfilter)
- delete directfilter;
- for (auto sf : sendfilters)
- {
- if (sf != nullptr)
- delete sf;
- }
- }
- love::audio::Source *Source::clone()
- {
- return new Source(*this);
- }
- bool Source::play()
- {
- Lock l = pool->lock();
- ALuint out;
- char wasPlaying;
- if (!pool->assignSource(this, out, wasPlaying))
- return valid = false;
- if (!wasPlaying)
- return valid = playAtomic(out);
- resumeAtomic();
- return valid = true;
- }
- void Source::stop()
- {
- if (!valid)
- return;
- Lock l = pool->lock();
- pool->releaseSource(this);
- }
- void Source::pause()
- {
- Lock l = pool->lock();
- if (pool->isPlaying(this))
- pauseAtomic();
- }
- bool Source::isPlaying() const
- {
- if (!valid)
- return false;
- ALenum state;
- alGetSourcei(source, AL_SOURCE_STATE, &state);
- return state == AL_PLAYING;
- }
- bool Source::isFinished() const
- {
- if (!valid)
- return false;
- if (sourceType == TYPE_STREAM && (isLooping() || !decoder->isFinished()))
- return false;
- ALenum state;
- alGetSourcei(source, AL_SOURCE_STATE, &state);
- return state == AL_STOPPED;
- }
- bool Source::update()
- {
- if (!valid)
- return false;
- switch (sourceType)
- {
- case TYPE_STATIC:
- {
- // Looping mode could have changed.
- // FIXME: make looping mode change atomically so this is not needed
- alSourcei(source, AL_LOOPING, isLooping() ? AL_TRUE : AL_FALSE);
- return !isFinished();
- }
- case TYPE_STREAM:
- if (!isFinished())
- {
- ALint processed;
- ALuint buffers[MAX_BUFFERS];
- float curOffsetSamples, curOffsetSecs, newOffsetSamples, newOffsetSecs;
- int freq = decoder->getSampleRate();
- alGetSourcef(source, AL_SAMPLE_OFFSET, &curOffsetSamples);
- curOffsetSecs = curOffsetSamples / freq;
- alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
- alSourceUnqueueBuffers(source, processed, buffers);
- alGetSourcef(source, AL_SAMPLE_OFFSET, &newOffsetSamples);
- newOffsetSecs = newOffsetSamples / freq;
- offsetSamples += (curOffsetSamples - newOffsetSamples);
- offsetSeconds += (curOffsetSecs - newOffsetSecs);
- for (unsigned int i = 0; i < (unsigned int)processed; i++)
- unusedBufferPush(buffers[i]);
- while (unusedBufferPeek() != AL_NONE)
- {
- if(streamAtomic(unusedBufferPeek(), decoder.get()) > 0)
- alSourceQueueBuffers(source, 1, unusedBufferPop());
- else
- break;
- }
- return true;
- }
- return false;
- case TYPE_QUEUE:
- {
- ALint processed;
- ALuint buffers[MAX_BUFFERS];
- alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
- alSourceUnqueueBuffers(source, processed, buffers);
- for (unsigned int i = 0; i < (unsigned int)processed; i++)
- {
- ALint size;
- alGetBufferi(buffers[i], AL_SIZE, &size);
- bufferedBytes -= size;
- unusedBufferPush(buffers[i]);
- }
- return !isFinished();
- }
- case TYPE_MAX_ENUM:
- break;
- }
- return false;
- }
- 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::seek(float offset, Source::Unit unit)
- {
- Lock l = pool->lock();
- float offsetSamples, offsetSeconds;
- switch (unit)
- {
- case Source::UNIT_SAMPLES:
- offsetSamples = offset;
- offsetSeconds = offset / sampleRate;
- break;
- case Source::UNIT_SECONDS:
- default:
- offsetSeconds = offset;
- offsetSamples = offset * sampleRate;
- break;
- }
- bool wasPlaying = isPlaying();
- switch (sourceType)
- {
- case TYPE_STATIC:
- if (valid)
- {
- alSourcef(source, AL_SAMPLE_OFFSET, offsetSamples);
- offsetSamples = offsetSeconds = 0;
- }
- break;
- case TYPE_STREAM:
- {
- // To drain all buffers
- if (valid)
- stop();
- decoder->seek(offsetSeconds);
- if (wasPlaying)
- play();
- break;
- }
- case TYPE_QUEUE:
- if (valid)
- {
- alSourcef(source, AL_SAMPLE_OFFSET, offsetSamples);
- offsetSamples = offsetSeconds = 0;
- }
- else
- {
- ALint size;
- ALuint buffer = unusedBufferPeek();
- //emulate AL behavior, discarding buffer once playback head is past one
- while (buffer != AL_NONE)
- {
- alGetBufferi(buffer, AL_SIZE, &size);
- if (offsetSamples < size / (bitDepth / 8 * channels))
- break;
- unusedBufferPop();
- buffer = unusedBufferPeek();
- bufferedBytes -= size;
- offsetSamples -= size / (bitDepth / 8 * channels);
- }
- if (buffer == AL_NONE)
- offsetSamples = 0;
- offsetSeconds = offsetSamples / sampleRate;
- }
- break;
- case TYPE_MAX_ENUM:
- break;
- }
- if (wasPlaying && (alGetError() == AL_INVALID_VALUE || (sourceType == TYPE_STREAM && !isPlaying())))
- {
- stop();
- if (isLooping())
- play();
- return;
- }
- this->offsetSamples = offsetSamples;
- this->offsetSeconds = offsetSeconds;
- }
- float Source::tell(Source::Unit unit)
- {
- Lock l = pool->lock();
- float offset = 0.0f;
- switch (unit)
- {
- case Source::UNIT_SAMPLES:
- if (valid)
- alGetSourcef(source, AL_SAMPLE_OFFSET, &offset);
- offset += offsetSamples;
- break;
- case Source::UNIT_SECONDS:
- default:
- if (valid)
- alGetSourcef(source, AL_SEC_OFFSET, &offset);
- offset += offsetSeconds;
- break;
- }
- return offset;
- }
- double Source::getDuration(Unit unit)
- {
- Lock l = pool->lock();
- switch (sourceType)
- {
- case TYPE_STATIC:
- {
- ALsizei size = staticBuffer->getSize();
- ALsizei samples = (size / channels) / (bitDepth / 8);
- if (unit == UNIT_SAMPLES)
- return (double) samples;
- else
- return (double) samples / (double) sampleRate;
- }
- case TYPE_STREAM:
- {
- double seconds = decoder->getDuration();
- if (unit == UNIT_SECONDS)
- return seconds;
- else
- return seconds * decoder->getSampleRate();
- }
- case TYPE_QUEUE:
- {
- ALsizei samples = (bufferedBytes / channels) / (bitDepth / 8);
- if (unit == UNIT_SAMPLES)
- return (double)samples;
- else
- return (double)samples / (double)sampleRate;
- }
- case TYPE_MAX_ENUM:
- return 0.0;
- }
- return 0.0;
- }
- void Source::setPosition(float *v)
- {
- if (channels > 1)
- throw SpatialSupportException();
- if (valid)
- alSourcefv(source, AL_POSITION, v);
- setFloatv(position, v);
- }
- void Source::getPosition(float *v) const
- {
- if (channels > 1)
- throw SpatialSupportException();
- if (valid)
- alGetSourcefv(source, AL_POSITION, v);
- else
- setFloatv(v, position);
- }
- void Source::setVelocity(float *v)
- {
- if (channels > 1)
- throw SpatialSupportException();
- if (valid)
- alSourcefv(source, AL_VELOCITY, v);
- setFloatv(velocity, v);
- }
- void Source::getVelocity(float *v) const
- {
- if (channels > 1)
- throw SpatialSupportException();
- if (valid)
- alGetSourcefv(source, AL_VELOCITY, v);
- else
- setFloatv(v, velocity);
- }
- void Source::setDirection(float *v)
- {
- if (channels > 1)
- throw SpatialSupportException();
- if (valid)
- alSourcefv(source, AL_DIRECTION, v);
- else
- setFloatv(direction, v);
- }
- void Source::getDirection(float *v) const
- {
- if (channels > 1)
- throw SpatialSupportException();
- if (valid)
- alGetSourcefv(source, AL_DIRECTION, v);
- else
- setFloatv(v, direction);
- }
- void Source::setCone(float innerAngle, float outerAngle, float outerVolume, float outerHighGain)
- {
- if (channels > 1)
- throw SpatialSupportException();
- cone.innerAngle = (int) LOVE_TODEG(innerAngle);
- cone.outerAngle = (int) LOVE_TODEG(outerAngle);
- cone.outerVolume = outerVolume;
- cone.outerHighGain = outerHighGain;
- if (valid)
- {
- alSourcei(source, AL_CONE_INNER_ANGLE, cone.innerAngle);
- alSourcei(source, AL_CONE_OUTER_ANGLE, cone.outerAngle);
- alSourcef(source, AL_CONE_OUTER_GAIN, cone.outerVolume);
- #ifdef ALC_EXT_EFX
- alSourcef(source, AL_CONE_OUTER_GAINHF, cone.outerHighGain);
- #endif
- }
- }
- void Source::getCone(float &innerAngle, float &outerAngle, float &outerVolume, float &outerHighGain) const
- {
- if (channels > 1)
- throw SpatialSupportException();
- innerAngle = LOVE_TORAD(cone.innerAngle);
- outerAngle = LOVE_TORAD(cone.outerAngle);
- outerVolume = cone.outerVolume;
- outerHighGain = cone.outerHighGain;
- }
- void Source::setRelative(bool enable)
- {
- if (channels > 1)
- throw SpatialSupportException();
- if (valid)
- alSourcei(source, AL_SOURCE_RELATIVE, enable ? AL_TRUE : AL_FALSE);
- relative = enable;
- }
- bool Source::isRelative() const
- {
- if (channels > 1)
- throw SpatialSupportException();
- return relative;
- }
- void Source::setLooping(bool enable)
- {
- if (sourceType == TYPE_QUEUE)
- throw QueueLoopingException();
- if (valid && sourceType == TYPE_STATIC)
- alSourcei(source, AL_LOOPING, enable ? AL_TRUE : AL_FALSE);
- looping = enable;
- }
- bool Source::isLooping() const
- {
- return looping;
- }
- bool Source::queue(void *data, size_t length, int dataSampleRate, int dataBitDepth, int dataChannels)
- {
- if (sourceType != TYPE_QUEUE)
- throw QueueTypeMismatchException();
- if (dataSampleRate != sampleRate || dataBitDepth != bitDepth || dataChannels != channels )
- throw QueueFormatMismatchException();
- if (length % (bitDepth / 8 * channels) != 0)
- throw QueueMalformedLengthException(bitDepth / 8 * channels);
- if (length == 0)
- return true;
- Lock l = pool->lock();
- if (valid)
- {
- ALuint buffer = unusedBufferPeek();
- if (buffer == AL_NONE)
- return false;
- alBufferData(buffer, Audio::getFormat(bitDepth, channels), data, length, sampleRate);
- alSourceQueueBuffers(source, 1, &buffer);
- unusedBufferPop();
- }
- else
- {
- ALuint buffer = unusedBufferPeekNext();
- if (buffer == AL_NONE)
- return false;
- //stack acts as queue while stopped
- alBufferData(buffer, Audio::getFormat(bitDepth, channels), data, length, sampleRate);
- unusedBufferQueue(buffer);
- }
- bufferedBytes += length;
- return true;
- }
- int Source::getFreeBufferCount() const
- {
- switch (sourceType) //why not :^)
- {
- case TYPE_STATIC:
- return 0;
- case TYPE_STREAM:
- return unusedBufferTop + 1;
- case TYPE_QUEUE:
- return valid ? unusedBufferTop + 1 : (int)MAX_BUFFERS - unusedBufferTop - 1;
- case TYPE_MAX_ENUM:
- return 0;
- }
- return 0;
- }
- void Source::prepareAtomic()
- {
- // This Source may now be associated with an OpenAL source that still has
- // the properties of another love Source. Let's reset it to the settings
- // of the new one.
- reset();
- switch (sourceType)
- {
- case TYPE_STATIC:
- alSourcei(source, AL_BUFFER, staticBuffer->getBuffer());
- break;
- case TYPE_STREAM:
- while (unusedBufferPeek() != AL_NONE)
- {
- if(streamAtomic(unusedBufferPeek(), decoder.get()) == 0)
- break;
- alSourceQueueBuffers(source, 1, unusedBufferPop());
- if (decoder->isFinished())
- break;
- }
- break;
- case TYPE_QUEUE:
- {
- int top = unusedBufferTop;
- //when queue source is stopped, loaded buffers are stored in unused buffers stack
- while (unusedBufferPeek() != AL_NONE)
- alSourceQueueBuffers(source, 1, unusedBufferPop());
- //construct a stack of unused buffers (beyond the end of stack)
- for (unsigned int i = top + 1; i < MAX_BUFFERS; i++)
- unusedBufferPush(unusedBuffers[i]);
- break;
- }
- case TYPE_MAX_ENUM:
- break;
- }
- }
- void Source::teardownAtomic()
- {
- switch (sourceType)
- {
- case TYPE_STATIC:
- break;
- case TYPE_STREAM:
- {
- ALint queued;
- ALuint buffer;
- decoder->seek(0);
- // drain buffers
- //since we only unqueue 1 buffer, it's OK to use singular variable pointer instead of array
- alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
- for (unsigned int i = 0; i < (unsigned int)queued; i++)
- alSourceUnqueueBuffers(source, 1, &buffer);
- // generate unused buffers list
- for (unsigned int i = 0; i < MAX_BUFFERS; i++)
- unusedBuffers[i] = streamBuffers[i];
- unusedBufferTop = MAX_BUFFERS - 1;
- break;
- }
- case TYPE_QUEUE:
- {
- ALint queued;
- ALuint buffer;
- alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
- for (unsigned int i = (unsigned int)queued; i > 0; i--)
- alSourceUnqueueBuffers(source, 1, &buffer);
- // generate unused buffers list
- for (unsigned int i = 0; i < MAX_BUFFERS; i++)
- unusedBuffers[i] = streamBuffers[i];
- unusedBufferTop = -1;
- break;
- }
- case TYPE_MAX_ENUM:
- break;
- }
- alSourcei(source, AL_BUFFER, AL_NONE);
- toLoop = 0;
- valid = false;
- offsetSamples = offsetSeconds = 0;
- }
- bool Source::playAtomic(ALuint source)
- {
- this->source = source;
- prepareAtomic();
- // Clear errors.
- alGetError();
- alSourcePlay(source);
- bool success = alGetError() == AL_NO_ERROR;
- if (sourceType == TYPE_STREAM)
- {
- valid = true; //isPlaying() needs source to be valid
- if (!isPlaying())
- success = false;
- }
- else if (success)
- {
- alSourcef(source, AL_SAMPLE_OFFSET, offsetSamples);
- success = alGetError() == AL_NO_ERROR;
- }
- if (!success)
- {
- valid = true; //stop() needs source to be valid
- stop();
- }
- if (sourceType != TYPE_STREAM)
- offsetSamples = offsetSeconds = 0;
- return success;
- }
- void Source::stopAtomic()
- {
- if (!valid)
- return;
- alSourceStop(source);
- teardownAtomic();
- }
- void Source::pauseAtomic()
- {
- if (valid)
- alSourcePause(source);
- }
- void Source::resumeAtomic()
- {
- if (valid && !isPlaying())
- {
- alSourcePlay(source);
- if (alGetError() == AL_INVALID_VALUE || (sourceType == TYPE_STREAM && unusedBufferTop == MAX_BUFFERS - 1))
- stop();
- }
- }
- bool Source::play(const std::vector<love::audio::Source*> &sources)
- {
- if (sources.size() == 0)
- return true;
- Pool *pool = ((Source*) sources[0])->pool;
- Lock l = pool->lock();
- // NOTE: not bool, because std::vector<bool> is implemented as a bitvector
- // which means no bool references can be created.
- std::vector<char> wasPlaying(sources.size());
- std::vector<ALuint> ids(sources.size());
- for (size_t i = 0; i < sources.size(); i++)
- {
- if (!pool->assignSource((Source*) sources[i], ids[i], wasPlaying[i]))
- {
- for (size_t j = 0; j < i; j++)
- if (!wasPlaying[j])
- pool->releaseSource((Source*) sources[j], false);
- return false;
- }
- }
- std::vector<ALuint> toPlay;
- toPlay.reserve(sources.size());
- for (size_t i = 0; i < sources.size(); i++)
- {
- if (wasPlaying[i])
- continue;
- Source *source = (Source*) sources[i];
- source->source = ids[i];
- source->prepareAtomic();
- toPlay.push_back(ids[i]);
- }
- alGetError();
- alSourcePlayv((ALsizei) toPlay.size(), &toPlay[0]);
- bool success = alGetError() == AL_NO_ERROR;
- for (auto &_source : sources)
- {
- Source *source = (Source*) _source;
- source->valid = source->valid || success;
- if (success && source->sourceType != TYPE_STREAM)
- source->offsetSamples = source->offsetSeconds = 0;
- }
- return success;
- }
- void Source::stop(const std::vector<love::audio::Source*> &sources)
- {
- if (sources.size() == 0)
- return;
- Pool *pool = ((Source*) sources[0])->pool;
- Lock l = pool->lock();
- std::vector<ALuint> sourceIds;
- sourceIds.reserve(sources.size());
- for (auto &_source : sources)
- {
- Source *source = (Source*) _source;
- if (source->valid)
- sourceIds.push_back(source->source);
- }
- alSourceStopv((ALsizei) sources.size(), &sourceIds[0]);
- for (auto &_source : sources)
- {
- Source *source = (Source*) _source;
- if (source->valid)
- source->teardownAtomic();
- pool->releaseSource(source, false);
- }
- }
- void Source::pause(const std::vector<love::audio::Source*> &sources)
- {
- if (sources.size() == 0)
- return;
- Lock l = ((Source*) sources[0])->pool->lock();
- std::vector<ALuint> sourceIds;
- sourceIds.reserve(sources.size());
- for (auto &_source : sources)
- {
- Source *source = (Source*) _source;
- if (source->valid)
- sourceIds.push_back(source->source);
- }
- alSourcePausev((ALsizei) sources.size(), &sourceIds[0]);
- }
- std::vector<love::audio::Source*> Source::pause(Pool *pool)
- {
- Lock l = pool->lock();
- std::vector<love::audio::Source*> sources = pool->getPlayingSources();
- auto newend = std::remove_if(sources.begin(), sources.end(), [](love::audio::Source* s) {
- return !s->isPlaying();
- });
- sources.erase(newend, sources.end());
- pause(sources);
- return sources;
- }
- void Source::stop(Pool *pool)
- {
- Lock l = pool->lock();
- stop(pool->getPlayingSources());
- }
- void Source::reset()
- {
- alSourcei(source, AL_BUFFER, AL_NONE);
- 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);
- alSourcef(source, AL_MIN_GAIN, minVolume);
- alSourcef(source, AL_MAX_GAIN, maxVolume);
- alSourcef(source, AL_REFERENCE_DISTANCE, referenceDistance);
- alSourcef(source, AL_ROLLOFF_FACTOR, rolloffFactor);
- alSourcef(source, AL_MAX_DISTANCE, maxDistance);
- alSourcei(source, AL_LOOPING, (sourceType == TYPE_STATIC) && isLooping() ? AL_TRUE : AL_FALSE);
- alSourcei(source, AL_SOURCE_RELATIVE, relative ? AL_TRUE : AL_FALSE);
- alSourcei(source, AL_CONE_INNER_ANGLE, cone.innerAngle);
- alSourcei(source, AL_CONE_OUTER_ANGLE, cone.outerAngle);
- alSourcef(source, AL_CONE_OUTER_GAIN, cone.outerVolume);
- #ifdef ALC_EXT_EFX
- alSourcef(source, AL_AIR_ABSORPTION_FACTOR, absorptionFactor);
- alSourcef(source, AL_CONE_OUTER_GAINHF, cone.outerHighGain);
- alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, rolloffFactor); //reverb-specific rolloff
- alSourcei(source, AL_DIRECT_FILTER, directfilter ? directfilter->getFilter() : AL_FILTER_NULL);
- for (unsigned int i = 0; i < sendtargets.size(); i++)
- alSource3i(source, AL_AUXILIARY_SEND_FILTER, sendtargets[i], i, sendfilters[i] ? sendfilters[i]->getFilter() : AL_FILTER_NULL);
- //alGetError();
- #endif
- }
- void Source::setFloatv(float *dst, const float *src) const
- {
- dst[0] = src[0];
- dst[1] = src[1];
- dst[2] = src[2];
- }
- ALuint Source::unusedBufferPeek()
- {
- return (unusedBufferTop < 0) ? AL_NONE : unusedBuffers[unusedBufferTop];
- }
- ALuint Source::unusedBufferPeekNext()
- {
- return (unusedBufferTop >= (int)MAX_BUFFERS - 1) ? AL_NONE : unusedBuffers[unusedBufferTop + 1];
- }
- ALuint *Source::unusedBufferPop()
- {
- return &unusedBuffers[unusedBufferTop--];
- }
- void Source::unusedBufferPush(ALuint buffer)
- {
- unusedBuffers[++unusedBufferTop] = buffer;
- }
- void Source::unusedBufferQueue(ALuint buffer)
- {
- for (unsigned int i = ++unusedBufferTop; i > 0; i--)
- unusedBuffers[i] = unusedBuffers[i - 1];
- unusedBuffers[0] = buffer;
- }
- int Source::streamAtomic(ALuint buffer, love::sound::Decoder *d)
- {
- // Get more sound data.
- int decoded = std::max(d->decode(), 0);
- // OpenAL implementations are allowed to ignore 0-size alBufferData calls.
- if (decoded > 0)
- {
- int fmt = Audio::getFormat(d->getBitDepth(), d->getChannels());
- if (fmt != AL_NONE)
- alBufferData(buffer, fmt, d->getBuffer(), decoded, d->getSampleRate());
- else
- decoded = 0;
- }
- if (decoder->isFinished() && isLooping())
- {
- int queued, processed;
- alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
- alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
- if (queued > processed)
- toLoop = queued-processed;
- else
- toLoop = MAX_BUFFERS-processed;
- d->rewind();
- }
- if (toLoop > 0)
- {
- if (--toLoop == 0)
- {
- offsetSamples = 0;
- offsetSeconds = 0;
- }
- }
- return decoded;
- }
- void Source::setMinVolume(float volume)
- {
- if (valid)
- alSourcef(source, AL_MIN_GAIN, volume);
- minVolume = volume;
- }
- float Source::getMinVolume() const
- {
- if (valid)
- {
- ALfloat f;
- alGetSourcef(source, AL_MIN_GAIN, &f);
- return f;
- }
- // In case the Source isn't playing.
- return this->minVolume;
- }
- void Source::setMaxVolume(float volume)
- {
- if (valid)
- alSourcef(source, AL_MAX_GAIN, volume);
- maxVolume = volume;
- }
- float Source::getMaxVolume() const
- {
- if (valid)
- {
- ALfloat f;
- alGetSourcef(source, AL_MAX_GAIN, &f);
- return f;
- }
- // In case the Source isn't playing.
- return maxVolume;
- }
- void Source::setReferenceDistance(float distance)
- {
- if (channels > 1)
- throw SpatialSupportException();
- if (valid)
- alSourcef(source, AL_REFERENCE_DISTANCE, distance);
- referenceDistance = distance;
- }
- float Source::getReferenceDistance() const
- {
- if (channels > 1)
- throw SpatialSupportException();
- if (valid)
- {
- ALfloat f;
- alGetSourcef(source, AL_REFERENCE_DISTANCE, &f);
- return f;
- }
- // In case the Source isn't playing.
- return referenceDistance;
- }
- void Source::setRolloffFactor(float factor)
- {
- if (channels > 1)
- throw SpatialSupportException();
- if (valid)
- alSourcef(source, AL_ROLLOFF_FACTOR, factor);
- rolloffFactor = factor;
- }
- float Source::getRolloffFactor() const
- {
- if (channels > 1)
- throw SpatialSupportException();
- if (valid)
- {
- ALfloat f;
- alGetSourcef(source, AL_ROLLOFF_FACTOR, &f);
- return f;
- }
- // In case the Source isn't playing.
- return rolloffFactor;
- }
- void Source::setMaxDistance(float distance)
- {
- if (channels > 1)
- throw SpatialSupportException();
- distance = std::min(distance, MAX_ATTENUATION_DISTANCE);
- if (valid)
- alSourcef(source, AL_MAX_DISTANCE, distance);
- maxDistance = distance;
- }
- float Source::getMaxDistance() const
- {
- if (channels > 1)
- throw SpatialSupportException();
- if (valid)
- {
- ALfloat f;
- alGetSourcef(source, AL_MAX_DISTANCE, &f);
- return f;
- }
- // In case the Source isn't playing.
- return maxDistance;
- }
- void Source::setAirAbsorptionFactor(float factor)
- {
- if (channels > 1)
- throw SpatialSupportException();
- absorptionFactor = factor;
- #ifdef ALC_EXT_EFX
- if (valid)
- {
- alSourcef(source, AL_AIR_ABSORPTION_FACTOR, absorptionFactor);
- //alGetError();
- }
- #endif
- }
- float Source::getAirAbsorptionFactor() const
- {
- if (channels > 1)
- throw SpatialSupportException();
- return absorptionFactor;
- }
- int Source::getChannels() const
- {
- return channels;
- }
- bool Source::setFilter(const std::map<Filter::Parameter, float> ¶ms)
- {
- if (!directfilter)
- directfilter = new Filter();
- bool result = directfilter->setParams(params);
- #ifdef ALC_EXT_EFX
- if (valid)
- {
- //in case of failure contains AL_FILTER_NULL, a valid non-filter
- alSourcei(source, AL_DIRECT_FILTER, directfilter->getFilter());
- //alGetError();
- }
- #endif
- return result;
- }
- bool Source::setFilter()
- {
- if (directfilter)
- delete directfilter;
- directfilter = nullptr;
- #ifdef ALC_EXT_EFX
- if (valid)
- {
- alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL);
- //alGetError();
- }
- #endif
- return true;
- }
- bool Source::getFilter(std::map<Filter::Parameter, float> ¶ms)
- {
- if (!directfilter)
- return false;
- params = directfilter->getParams();
- return true;
- }
- bool Source::setSceneEffect(int slot, int effect)
- {
- if (slot < 0 || slot >= (int)sendtargets.size())
- return false;
- sendtargets[slot] = dynamic_cast<Audio*>(audiomodule())->getSceneEffectID(effect);
- if (sendfilters[slot])
- delete sendfilters[slot];
- sendfilters[slot] = nullptr;
- #ifdef ALC_EXT_EFX
- if (valid)
- {
- alSource3i(source, AL_AUXILIARY_SEND_FILTER, sendtargets[slot], slot, AL_FILTER_NULL);
- //alGetError();
- }
- #endif
- return true;
- }
- bool Source::setSceneEffect(int slot, int effect, const std::map<Filter::Parameter, float> ¶ms)
- {
- if (slot < 0 || slot >= (int)sendtargets.size())
- return false;
- sendtargets[slot] = dynamic_cast<Audio*>(audiomodule())->getSceneEffectID(effect);
- if (!sendfilters[slot])
- sendfilters[slot] = new Filter();
- sendfilters[slot]->setParams(params);
- #ifdef ALC_EXT_EFX
- if (valid)
- {
- //in case of failure contains AL_FILTER_NULL, a valid non-filter
- alSource3i(source, AL_AUXILIARY_SEND_FILTER, sendtargets[slot], slot, sendfilters[slot]->getFilter());
- //alGetError();
- }
- #endif
- return true;
- }
- bool Source::setSceneEffect(int slot)
- {
- if (slot < 0 || slot >= (int)sendtargets.size())
- return false;
- sendtargets[slot] = AL_EFFECTSLOT_NULL;
- if (sendfilters[slot])
- delete sendfilters[slot];
- sendfilters[slot] = nullptr;
- #ifdef ALC_EXT_EFX
- if (valid)
- {
- alSource3i(source, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, slot, AL_FILTER_NULL);
- //alGetError();
- }
- #endif
- return true;
- }
- bool Source::getSceneEffect(int slot, int &effect, std::map<Filter::Parameter, float> ¶ms)
- {
- if (slot < 0 || slot >= (int)sendtargets.size())
- return false;
- if (sendtargets[slot] == AL_EFFECTSLOT_NULL)
- return false;
- effect = dynamic_cast<Audio*>(audiomodule())->getSceneEffectIndex(sendtargets[slot]);
- if(sendfilters[slot])
- params = sendfilters[slot]->getParams();
- return true;
- }
- } // openal
- } // audio
- } // love
|