/** * Copyright (c) 2006-2016 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 "Pool.h" #include "Source.h" namespace love { namespace audio { namespace openal { Pool::Pool() : sources() , totalSources(0) { // Clear errors. alGetError(); // Generate sources. for (int i = 0; i < MAX_SOURCES; i++) { alGenSources(1, &sources[i]); // We might hit an implementation-dependent limit on the total number // of sources before reaching MAX_SOURCES. if (alGetError() != AL_NO_ERROR) break; totalSources++; } if (totalSources < 4) throw love::Exception("Could not generate sources."); #ifdef AL_SOFT_direct_channels ALboolean hasext = alIsExtensionPresent("AL_SOFT_direct_channels"); #endif // Make all sources available initially. for (int i = 0; i < totalSources; i++) { #ifdef AL_SOFT_direct_channels if (hasext) { // Bypass virtualization of speakers for multi-channel sources in OpenAL Soft. alSourcei(sources[i], AL_DIRECT_CHANNELS_SOFT, AL_TRUE); } #endif available.push(sources[i]); } } Pool::~Pool() { stop(); // Free all sources. alDeleteSources(totalSources, sources); } bool Pool::isAvailable() const { bool has = false; { thread::Lock lock(mutex); has = !available.empty(); } return has; } bool Pool::isPlaying(Source *s) { bool p = false; { thread::Lock lock(mutex); p = (playing.find(s) != playing.end()); } return p; } void Pool::update() { thread::Lock lock(mutex); std::map::iterator i = playing.begin(); while (i != playing.end()) { if (!i->first->update()) { i->first->stopAtomic(); i->first->release(); available.push(i->second); playing.erase(i++); } else i++; } } int Pool::getSourceCount() const { return (int) playing.size(); } int Pool::getMaxSources() const { return totalSources; } bool Pool::assignSource(Source *source, ALuint &out, char *wasPlaying) { out = 0; if (findSource(source, out)) { if (wasPlaying) *wasPlaying = true; return true; } if (wasPlaying) *wasPlaying = false; if (available.empty()) return false; out = available.front(); available.pop(); playing.insert(std::make_pair(source, out)); source->retain(); return true; } bool Pool::play(Source *source) { thread::Lock lock(mutex); ALuint out; char wasPlaying; if (!assignSource(source, out, &wasPlaying)) return false; if (!wasPlaying) return source->playAtomic(out); else { source->resumeAtomic(); return true; } } bool Pool::play(const std::vector &sources) { thread::Lock lock(mutex); std::vector ids(sources.size()); // NOTE: not bool, because std::vector is implemented as a bitvector // which means no pointers can be created. std::vector wasPlaying(sources.size()); for (size_t i = 0; i < sources.size(); i++) { Source *source = (Source*) sources[i]; if (!assignSource(source, ids[i], &wasPlaying[i])) { // Now we need to release the resources we had already allocated for (size_t j = 0; j < sources.size(); j++) if (!wasPlaying[j]) release((Source*) sources[j]); return false; } } return Source::playAtomic(sources, ids, wasPlaying); } void Pool::stop() { thread::Lock lock(mutex); for (const auto &i : playing) { i.first->stopAtomic(); i.first->release(); available.push(i.second); } playing.clear(); } void Pool::stop(Source *source) { thread::Lock lock(mutex); removeSource(source); } void Pool::stop(const std::vector &sources) { thread::Lock lock(mutex); Source::stopAtomic(sources); } std::vector Pool::pause() { thread::Lock lock(mutex); std::vector werePlaying; werePlaying.reserve(playing.size()); for (const auto &i : playing) { if (!i.first->isPlaying()) continue; werePlaying.push_back(i.first); i.first->pauseAtomic(); } return werePlaying; } void Pool::pause(Source *source) { thread::Lock lock(mutex); ALuint out; if (findSource(source, out)) source->pauseAtomic(); } void Pool::pause(const std::vector &sources) { thread::Lock lock(mutex); Source::pauseAtomic(sources); } void Pool::release(Source *source) { ALuint s = findi(source); if (s != 0) { available.push(s); playing.erase(source); } } void Pool::seek(Source *source, float offset, void *unit) { thread::Lock lock(mutex); return source->seekAtomic(offset, unit); } float Pool::tell(Source *source, void *unit) { thread::Lock lock(mutex); return source->tellAtomic(unit); } double Pool::getDuration(Source *source, void *unit) { thread::Lock lock(mutex); return source->getDurationAtomic(unit); } bool Pool::queue(Source *source, void *data, ALsizei length) { thread::Lock lock(mutex); return source->queueAtomic(data, length); } ALuint Pool::findi(const Source *source) const { std::map::const_iterator i = playing.find((Source *)source); if (i != playing.end()) return i->second; return 0; } bool Pool::findSource(Source *source, ALuint &out) { std::map::const_iterator i = playing.find((Source *)source); bool found = i != playing.end(); if (found) out = i->second; return found; } bool Pool::removeSource(Source *source) { std::map::iterator i = playing.find((Source *)source); if (i != playing.end()) { source->stopAtomic(); available.push(i->second); playing.erase(i++); source->release(); return true; } return false; } } // openal } // audio } // love