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
+10
View File
@@ -86,6 +86,16 @@
# define LOVE_UNUSED(x) (void)sizeof(x) # define LOVE_UNUSED(x) (void)sizeof(x)
#endif #endif
// Warn on unused return values
#ifdef __GNUC__
# define LOVE_WARN_UNUSED __attribute__((warn_unused_result))
#elif _MSC_VER
# define LOVE_WARN_UNUSED _Check_return_
#else
# define LOVE_WARN_UNUSED
#endif
#ifndef LOVE_BUILD #ifndef LOVE_BUILD
# define LOVE_BUILD # define LOVE_BUILD
# define LOVE_BUILD_STANDALONE # define LOVE_BUILD_STANDALONE
+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) 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) 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) void Audio::stop(const std::vector<love::audio::Source*> &sources)
{ {
return pool->stop(sources); return Source::stop(sources);
} }
void Audio::stop() void Audio::stop()
{ {
pool->stop(); return Source::stop(pool);
} }
void Audio::pause(love::audio::Source *source) 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) 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() std::vector<love::audio::Source*> Audio::pause()
{ {
return pool->pause(); return Source::pause(pool);
} }
void Audio::setVolume(float volume) void Audio::setVolume(float volume)
+38 -185
View File
@@ -73,7 +73,7 @@ Pool::Pool()
Pool::~Pool() Pool::~Pool()
{ {
stop(); Source::stop(this);
// Free all sources. // Free all sources.
alDeleteSources(totalSources, sources); alDeleteSources(totalSources, sources);
@@ -103,20 +103,9 @@ void Pool::update()
{ {
thread::Lock lock(mutex); thread::Lock lock(mutex);
std::map<Source *, ALuint>::iterator i = playing.begin(); for (const auto &i : playing)
if (!i.first->update())
while (i != playing.end()) releaseSource(i.first);
{
if (!i->first->update())
{
i->first->stopAtomic();
i->first->release();
available.push(i->second);
playing.erase(i++);
}
else
i++;
}
} }
int Pool::getSourceCount() const int Pool::getSourceCount() const
@@ -129,19 +118,14 @@ int Pool::getMaxSources() const
return totalSources; return totalSources;
} }
bool Pool::assignSource(Source *source, ALuint &out, char *wasPlaying) bool Pool::assignSource(Source *source, ALuint &out, char &wasPlaying)
{ {
out = 0; out = 0;
if (findSource(source, out)) if (findSource(source, out))
{ return wasPlaying = true;
if (wasPlaying)
*wasPlaying = true;
return true;
}
if (wasPlaying) wasPlaying = false;
*wasPlaying = false;
if (available.empty()) if (available.empty())
return false; return false;
@@ -154,179 +138,48 @@ bool Pool::assignSource(Source *source, ALuint &out, char *wasPlaying)
return true; return true;
} }
bool Pool::play(Source *source) bool Pool::releaseSource(Source *source, bool stop)
{ {
thread::Lock lock(mutex); ALuint s;
ALuint out;
char wasPlaying; if (findSource(source, s))
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 (stop)
source->stopAtomic();
source->release();
available.push(s); available.push(s);
playing.erase(source); 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 true;
} }
return false; 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 } // openal
} // audio } // audio
} // love } // love
+6 -19
View File
@@ -83,33 +83,20 @@ public:
int getSourceCount() const; int getSourceCount() const;
int getMaxSources() 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: private:
friend class Source;
LOVE_WARN_UNUSED thread::Lock lock();
std::vector<love::audio::Source*> getPlayingSources();
/** /**
* Makes the specified OpenAL source available for use. * Makes the specified OpenAL source available for use.
* @param source The OpenAL source. * @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);
bool assignSource(Source *source, ALuint &out, char *wasPlaying = nullptr);
bool findSource(Source *source, ALuint &out); bool findSource(Source *source, ALuint &out);
bool removeSource(Source *source);
// Maximum possible number of OpenAL sources the pool attempts to generate. // Maximum possible number of OpenAL sources the pool attempts to generate.
static const int MAX_SOURCES = 64; static const int MAX_SOURCES = 64;
+84 -46
View File
@@ -30,6 +30,8 @@
#define audiomodule() (Module::getInstance<Audio>(Module::M_AUDIO)) #define audiomodule() (Module::getInstance<Audio>(Module::M_AUDIO))
using love::thread::Lock;
namespace love namespace love
{ {
namespace audio namespace audio
@@ -234,8 +236,7 @@ Source::Source(const Source &s)
Source::~Source() Source::~Source()
{ {
if (valid) stop();
pool->stop(this);
if (sourceType != TYPE_STATIC) if (sourceType != TYPE_STATIC)
alDeleteBuffers(MAX_BUFFERS, streamBuffers); alDeleteBuffers(MAX_BUFFERS, streamBuffers);
@@ -257,19 +258,34 @@ love::audio::Source *Source::clone()
bool Source::play() bool Source::play()
{ {
valid = pool->play(this); Lock l = pool->lock();
return valid; 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() void Source::stop()
{ {
if (valid) if (!valid)
pool->stop(this); return;
Lock l = pool->lock();
pool->releaseSource(this);
} }
void Source::pause() void Source::pause()
{ {
pool->pause(this); Lock l = pool->lock();
if (pool->isPlaying(this))
pauseAtomic();
} }
bool Source::isPlaying() const bool Source::isPlaying() const
@@ -343,7 +359,7 @@ bool Source::update()
return true; return true;
} }
return false; return false;
case TYPE_QUEUE: case TYPE_QUEUE:
{ {
ALint processed; ALint processed;
ALuint buffers[MAX_BUFFERS]; ALuint buffers[MAX_BUFFERS];
@@ -409,11 +425,13 @@ float Source::getVolume() const
return volume; return volume;
} }
void Source::seekAtomic(float offset, void *unit) void Source::seek(float offset, Source::Unit unit)
{ {
Lock l = pool->lock();
float offsetSamples, offsetSeconds; float offsetSamples, offsetSeconds;
switch (*((Source::Unit *) unit)) switch (unit)
{ {
case Source::UNIT_SAMPLES: case Source::UNIT_SAMPLES:
offsetSamples = offset; offsetSamples = offset;
@@ -492,16 +510,13 @@ void Source::seekAtomic(float offset, void *unit)
this->offsetSeconds = offsetSeconds; 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; float offset = 0.0f;
switch (*((Source::Unit *) unit)) switch (unit)
{ {
case Source::UNIT_SAMPLES: case Source::UNIT_SAMPLES:
if (valid) if (valid)
@@ -519,14 +534,9 @@ float Source::tellAtomic(void *unit) const
return offset; return offset;
} }
float Source::tell(Source::Unit unit) double Source::getDuration(Unit unit)
{ {
return pool->tell(this, &unit); Lock l = pool->lock();
}
double Source::getDurationAtomic(void *vunit)
{
Unit unit = *(Unit *) vunit;
switch (sourceType) switch (sourceType)
{ {
@@ -564,11 +574,6 @@ double Source::getDurationAtomic(void *vunit)
return 0.0; return 0.0;
} }
double Source::getDuration(Unit unit)
{
return pool->getDuration(this, &unit);
}
void Source::setPosition(float *v) void Source::setPosition(float *v)
{ {
if (channels > 1) if (channels > 1)
@@ -716,11 +721,8 @@ bool Source::queue(void *data, size_t length, int dataSampleRate, int dataBitDep
if (length == 0) if (length == 0)
return true; return true;
return pool->queue(this, data, (ALsizei)length); Lock l = pool->lock();
}
bool Source::queueAtomic(void *data, ALsizei length)
{
if (valid) if (valid)
{ {
ALuint buffer = unusedBufferPeek(); ALuint buffer = unusedBufferPeek();
@@ -887,10 +889,6 @@ bool Source::playAtomic(ALuint source)
if (sourceType != TYPE_STREAM) if (sourceType != TYPE_STREAM)
offsetSamples = offsetSeconds = 0; 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; 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) if (sources.size() == 0)
return true; 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; std::vector<ALuint> toPlay;
toPlay.reserve(sources.size()); toPlay.reserve(sources.size());
for (size_t i = 0; i < sources.size(); i++) 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]) if (wasPlaying[i])
continue; continue;
Source *source = (Source*) sources[i];
source->source = ids[i]; source->source = ids[i];
source->prepareAtomic(); source->prepareAtomic();
toPlay.push_back(ids[i]);
} }
alGetError(); alGetError();
@@ -957,11 +969,14 @@ bool Source::playAtomic(const std::vector<love::audio::Source*> &sources, const
return success; 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) if (sources.size() == 0)
return; return;
Pool *pool = ((Source*) sources[0])->pool;
Lock l = pool->lock();
std::vector<ALuint> sourceIds; std::vector<ALuint> sourceIds;
sourceIds.reserve(sources.size()); sourceIds.reserve(sources.size());
for (auto &_source : sources) for (auto &_source : sources)
@@ -978,14 +993,17 @@ void Source::stopAtomic(const std::vector<love::audio::Source*> &sources)
Source *source = (Source*) _source; Source *source = (Source*) _source;
if (source->valid) if (source->valid)
source->teardownAtomic(); 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) if (sources.size() == 0)
return; return;
Lock l = ((Source*) sources[0])->pool->lock();
std::vector<ALuint> sourceIds; std::vector<ALuint> sourceIds;
sourceIds.reserve(sources.size()); sourceIds.reserve(sources.size());
for (auto &_source : sources) for (auto &_source : sources)
@@ -998,6 +1016,26 @@ void Source::pauseAtomic(const std::vector<love::audio::Source*> &sources)
alSourcePausev((ALsizei) sources.size(), &sourceIds[0]); 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() void Source::reset()
{ {
alSourcei(source, AL_BUFFER, AL_NONE); alSourcei(source, AL_BUFFER, AL_NONE);
+6 -7
View File
@@ -114,11 +114,8 @@ public:
virtual float getPitch() const; virtual float getPitch() const;
virtual void setVolume(float volume); virtual void setVolume(float volume);
virtual float getVolume() const; virtual float getVolume() const;
virtual void seekAtomic(float offset, void *unit);
virtual void seek(float offset, Unit unit); virtual void seek(float offset, Unit unit);
virtual float tellAtomic(void *unit) const;
virtual float tell(Unit unit); virtual float tell(Unit unit);
virtual double getDurationAtomic(void *unit);
virtual double getDuration(Unit unit); virtual double getDuration(Unit unit);
virtual void setPosition(float *v); virtual void setPosition(float *v);
virtual void getPosition(float *v) const; virtual void getPosition(float *v) const;
@@ -157,7 +154,6 @@ public:
virtual int getFreeBufferCount() const; virtual int getFreeBufferCount() const;
virtual bool queue(void *data, size_t length, int dataSampleRate, int dataBitDepth, int dataChannels); virtual bool queue(void *data, size_t length, int dataSampleRate, int dataBitDepth, int dataChannels);
virtual bool queueAtomic(void *data, ALsizei length);
void prepareAtomic(); void prepareAtomic();
void teardownAtomic(); void teardownAtomic();
@@ -167,9 +163,12 @@ public:
void pauseAtomic(); void pauseAtomic();
void resumeAtomic(); void resumeAtomic();
static bool playAtomic(const std::vector<love::audio::Source*> &sources, const std::vector<ALuint> &ids, const std::vector<char> &wasPlaying); static bool play(const std::vector<love::audio::Source*> &sources);
static void stopAtomic(const std::vector<love::audio::Source*> &sources); static void stop(const std::vector<love::audio::Source*> &sources);
static void pauseAtomic(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: private:
+8 -1
View File
@@ -37,9 +37,16 @@ Lock::Lock(Mutex &m)
mutex->lock(); mutex->lock();
} }
Lock::Lock(Lock &&other)
{
mutex = other.mutex;
other.mutex = nullptr;
}
Lock::~Lock() Lock::~Lock()
{ {
mutex->unlock(); if (mutex)
mutex->unlock();
} }
EmptyLock::EmptyLock() EmptyLock::EmptyLock()
+1
View File
@@ -57,6 +57,7 @@ class Lock
public: public:
Lock(Mutex *m); Lock(Mutex *m);
Lock(Mutex &m); Lock(Mutex &m);
Lock(Lock &&other);
~Lock(); ~Lock();
private: private:
+1 -1
View File
@@ -27,7 +27,7 @@ const unsigned char nogame_lua[] =
0x2d, 0x2d, 0x5b, 0x5b, 0x0a, 0x2d, 0x2d, 0x5b, 0x5b, 0x0a,
0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36,
0x2d, 0x32, 0x30, 0x31, 0x36, 0x20, 0x4c, 0x4f, 0x56, 0x45, 0x20, 0x44, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x2d, 0x32, 0x30, 0x31, 0x37, 0x20, 0x4c, 0x4f, 0x56, 0x45, 0x20, 0x44, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70,
0x6d, 0x65, 0x6e, 0x74, 0x20, 0x54, 0x65, 0x61, 0x6d, 0x0a, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x54, 0x65, 0x61, 0x6d, 0x0a,
0x54, 0x68, 0x69, 0x73, 0x20, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x70, 0x54, 0x68, 0x69, 0x73, 0x20, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x70,
0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x20, 0x27, 0x61, 0x73, 0x2d, 0x69, 0x73, 0x27, 0x2c, 0x20, 0x77, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x20, 0x27, 0x61, 0x73, 0x2d, 0x69, 0x73, 0x27, 0x2c, 0x20, 0x77,