Simplify love.audio's Pool class

Move most logic back to Source. Pool now simply contains a list of playing
sources and a mutex. Also gets rid of the pause/pauseAtomic duplication in
Source, since it can now request a lock from Pool itself.

--HG--
branch : minor
This commit is contained in:
Bart van Strien
2017-02-11 21:41:22 +01:00
parent 7b6e1523b4
commit 03b863d788
9 changed files with 159 additions and 264 deletions
+5 -5
View File
@@ -240,7 +240,7 @@ bool Audio::play(love::audio::Source *source)
bool Audio::play(const std::vector<love::audio::Source*> &sources)
{
return pool->play(sources);
return Source::play(sources);
}
void Audio::stop(love::audio::Source *source)
@@ -250,12 +250,12 @@ void Audio::stop(love::audio::Source *source)
void Audio::stop(const std::vector<love::audio::Source*> &sources)
{
return pool->stop(sources);
return Source::stop(sources);
}
void Audio::stop()
{
pool->stop();
return Source::stop(pool);
}
void Audio::pause(love::audio::Source *source)
@@ -265,12 +265,12 @@ void Audio::pause(love::audio::Source *source)
void Audio::pause(const std::vector<love::audio::Source*> &sources)
{
return pool->pause(sources);
return Source::pause(sources);
}
std::vector<love::audio::Source*> Audio::pause()
{
return pool->pause();
return Source::pause(pool);
}
void Audio::setVolume(float volume)
+38 -185
View File
@@ -73,7 +73,7 @@ Pool::Pool()
Pool::~Pool()
{
stop();
Source::stop(this);
// Free all sources.
alDeleteSources(totalSources, sources);
@@ -103,20 +103,9 @@ void Pool::update()
{
thread::Lock lock(mutex);
std::map<Source *, ALuint>::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++;
}
for (const auto &i : playing)
if (!i.first->update())
releaseSource(i.first);
}
int Pool::getSourceCount() const
@@ -129,19 +118,14 @@ int Pool::getMaxSources() const
return totalSources;
}
bool Pool::assignSource(Source *source, ALuint &out, char *wasPlaying)
bool Pool::assignSource(Source *source, ALuint &out, char &wasPlaying)
{
out = 0;
if (findSource(source, out))
{
if (wasPlaying)
*wasPlaying = true;
return true;
}
return wasPlaying = true;
if (wasPlaying)
*wasPlaying = false;
wasPlaying = false;
if (available.empty())
return false;
@@ -154,179 +138,48 @@ bool Pool::assignSource(Source *source, ALuint &out, char *wasPlaying)
return true;
}
bool Pool::play(Source *source)
bool Pool::releaseSource(Source *source, bool stop)
{
thread::Lock lock(mutex);
ALuint out;
ALuint s;
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<love::audio::Source*> &sources)
{
thread::Lock lock(mutex);
std::vector<ALuint> ids(sources.size());
// NOTE: not bool, because std::vector<bool> is implemented as a bitvector
// which means no pointers can be created.
std::vector<char> 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<love::audio::Source*> &sources)
{
thread::Lock lock(mutex);
Source::stopAtomic(sources);
}
std::vector<love::audio::Source*> Pool::pause()
{
thread::Lock lock(mutex);
std::vector<love::audio::Source*> 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<love::audio::Source*> &sources)
{
thread::Lock lock(mutex);
Source::pauseAtomic(sources);
}
void Pool::release(Source *source)
{
ALuint s = findi(source);
if (s != 0)
if (findSource(source, s))
{
if (stop)
source->stopAtomic();
source->release();
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<Source *, ALuint>::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<Source *, ALuint>::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<Source *, ALuint>::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;
}
bool Pool::findSource(Source *source, ALuint &out)
{
std::map<Source *, ALuint>::const_iterator i = playing.find(source);
if (i == playing.end())
return false;
out = i->second;
return true;
}
thread::Lock Pool::lock()
{
return thread::Lock(mutex);
}
std::vector<love::audio::Source*> Pool::getPlayingSources()
{
std::vector<love::audio::Source*> sources;
sources.reserve(playing.size());
for (auto &i : playing)
sources.push_back(i.first);
return sources;
}
} // openal
} // audio
} // love
+6 -19
View File
@@ -83,33 +83,20 @@ public:
int getSourceCount() const;
int getMaxSources() const;
bool play(Source *source);
void stop();
void stop(Source *source);
std::vector<love::audio::Source*> pause();
void pause(Source *source);
void seek(Source *source, float offset, void *unit);
float tell(Source *source, void *unit);
double getDuration(Source *source, void *unit);
bool queue(Source *source, void *data, ALsizei length);
bool play(const std::vector<love::audio::Source*> &sources);
void stop(const std::vector<love::audio::Source*> &sources);
void pause(const std::vector<love::audio::Source*> &sources);
private:
friend class Source;
LOVE_WARN_UNUSED thread::Lock lock();
std::vector<love::audio::Source*> getPlayingSources();
/**
* Makes the specified OpenAL source available for use.
* @param source The OpenAL source.
**/
void release(Source *source);
bool releaseSource(Source *source, bool stop = true);
ALuint findi(const Source *source) const;
bool assignSource(Source *source, ALuint &out, char *wasPlaying = nullptr);
bool assignSource(Source *source, ALuint &out, char &wasPlaying);
bool findSource(Source *source, ALuint &out);
bool removeSource(Source *source);
// Maximum possible number of OpenAL sources the pool attempts to generate.
static const int MAX_SOURCES = 64;
+84 -46
View File
@@ -30,6 +30,8 @@
#define audiomodule() (Module::getInstance<Audio>(Module::M_AUDIO))
using love::thread::Lock;
namespace love
{
namespace audio
@@ -234,8 +236,7 @@ Source::Source(const Source &s)
Source::~Source()
{
if (valid)
pool->stop(this);
stop();
if (sourceType != TYPE_STATIC)
alDeleteBuffers(MAX_BUFFERS, streamBuffers);
@@ -257,19 +258,34 @@ love::audio::Source *Source::clone()
bool Source::play()
{
valid = pool->play(this);
return valid;
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)
pool->stop(this);
if (!valid)
return;
Lock l = pool->lock();
pool->releaseSource(this);
}
void Source::pause()
{
pool->pause(this);
Lock l = pool->lock();
if (pool->isPlaying(this))
pauseAtomic();
}
bool Source::isPlaying() const
@@ -343,7 +359,7 @@ bool Source::update()
return true;
}
return false;
case TYPE_QUEUE:
case TYPE_QUEUE:
{
ALint processed;
ALuint buffers[MAX_BUFFERS];
@@ -409,11 +425,13 @@ float Source::getVolume() const
return volume;
}
void Source::seekAtomic(float offset, void *unit)
void Source::seek(float offset, Source::Unit unit)
{
Lock l = pool->lock();
float offsetSamples, offsetSeconds;
switch (*((Source::Unit *) unit))
switch (unit)
{
case Source::UNIT_SAMPLES:
offsetSamples = offset;
@@ -492,16 +510,13 @@ void Source::seekAtomic(float offset, void *unit)
this->offsetSeconds = offsetSeconds;
}
void Source::seek(float offset, Source::Unit unit)
float Source::tell(Source::Unit unit)
{
return pool->seek(this, offset, &unit);
}
Lock l = pool->lock();
float Source::tellAtomic(void *unit) const
{
float offset = 0.0f;
switch (*((Source::Unit *) unit))
switch (unit)
{
case Source::UNIT_SAMPLES:
if (valid)
@@ -519,14 +534,9 @@ float Source::tellAtomic(void *unit) const
return offset;
}
float Source::tell(Source::Unit unit)
double Source::getDuration(Unit unit)
{
return pool->tell(this, &unit);
}
double Source::getDurationAtomic(void *vunit)
{
Unit unit = *(Unit *) vunit;
Lock l = pool->lock();
switch (sourceType)
{
@@ -564,11 +574,6 @@ double Source::getDurationAtomic(void *vunit)
return 0.0;
}
double Source::getDuration(Unit unit)
{
return pool->getDuration(this, &unit);
}
void Source::setPosition(float *v)
{
if (channels > 1)
@@ -716,11 +721,8 @@ bool Source::queue(void *data, size_t length, int dataSampleRate, int dataBitDep
if (length == 0)
return true;
return pool->queue(this, data, (ALsizei)length);
}
Lock l = pool->lock();
bool Source::queueAtomic(void *data, ALsizei length)
{
if (valid)
{
ALuint buffer = unusedBufferPeek();
@@ -887,10 +889,6 @@ bool Source::playAtomic(ALuint source)
if (sourceType != TYPE_STREAM)
offsetSamples = offsetSeconds = 0;
//this is set to success state afterwards anyway, but setting it here
//to true preemptively avoids race condition with update bug
valid = true;
return success;
}
@@ -919,26 +917,40 @@ void Source::resumeAtomic()
}
}
bool Source::playAtomic(const std::vector<love::audio::Source*> &sources, const std::vector<ALuint> &ids, const std::vector<char> &wasPlaying)
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++)
{
Source *source = (Source*) sources[i];
// Paused sources have wasPlaying set to true, so do this first
if (!source->isPlaying())
toPlay.push_back(ids[i]);
// If it wasn't playing (nor paused), we have just allocated a new
// source
if (wasPlaying[i])
continue;
Source *source = (Source*) sources[i];
source->source = ids[i];
source->prepareAtomic();
toPlay.push_back(ids[i]);
}
alGetError();
@@ -957,11 +969,14 @@ bool Source::playAtomic(const std::vector<love::audio::Source*> &sources, const
return success;
}
void Source::stopAtomic(const std::vector<love::audio::Source*> &sources)
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)
@@ -978,14 +993,17 @@ void Source::stopAtomic(const std::vector<love::audio::Source*> &sources)
Source *source = (Source*) _source;
if (source->valid)
source->teardownAtomic();
pool->releaseSource(source, false);
}
}
void Source::pauseAtomic(const std::vector<love::audio::Source*> &sources)
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)
@@ -998,6 +1016,26 @@ void Source::pauseAtomic(const std::vector<love::audio::Source*> &sources)
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);
+6 -7
View File
@@ -114,11 +114,8 @@ public:
virtual float getPitch() const;
virtual void setVolume(float volume);
virtual float getVolume() const;
virtual void seekAtomic(float offset, void *unit);
virtual void seek(float offset, Unit unit);
virtual float tellAtomic(void *unit) const;
virtual float tell(Unit unit);
virtual double getDurationAtomic(void *unit);
virtual double getDuration(Unit unit);
virtual void setPosition(float *v);
virtual void getPosition(float *v) const;
@@ -157,7 +154,6 @@ public:
virtual int getFreeBufferCount() const;
virtual bool queue(void *data, size_t length, int dataSampleRate, int dataBitDepth, int dataChannels);
virtual bool queueAtomic(void *data, ALsizei length);
void prepareAtomic();
void teardownAtomic();
@@ -167,9 +163,12 @@ public:
void pauseAtomic();
void resumeAtomic();
static bool playAtomic(const std::vector<love::audio::Source*> &sources, const std::vector<ALuint> &ids, const std::vector<char> &wasPlaying);
static void stopAtomic(const std::vector<love::audio::Source*> &sources);
static void pauseAtomic(const std::vector<love::audio::Source*> &sources);
static bool play(const std::vector<love::audio::Source*> &sources);
static void stop(const std::vector<love::audio::Source*> &sources);
static void pause(const std::vector<love::audio::Source*> &sources);
static std::vector<love::audio::Source*> pause(Pool *pool);
static void stop(Pool *pool);
private: