Browse Source

New audio playback API

Instead of having three states, stopped, paused and playing, now there's only
two states: paused and playing.

This means resume() is gone, as is isStopped(), and isPaused() has become
isPlaying(). stop() now pauses and rewinds, whereas pause() only pauses.
rewind() has been removed, and should either be replaced with stop() or
seek(0).

The same has been applied to the functions in the love.audio module, except they
get some extra magic:

- love.audio.pause() now returns a table/list of Source objects, which contains
  only the sources it has just paused. This means you can then later restart
  only them, instead of everything (as before).
- love.audio.play/pause/stop now have variants that take a list of Source
  objects, and it starts or stops them all at the same time, this means calling
  play(srcA, srcB) should mean srcA and srcB are much more (but possibly not
  exactly) synchronised than they were with play(srcA); play(srcB).

--HG--
branch : minor
Bart van Strien 9 years ago
parent
commit
f76879c6b7

+ 21 - 22
src/modules/audio/Audio.h

@@ -21,6 +21,10 @@
 #ifndef LOVE_AUDIO_AUDIO_H
 #define LOVE_AUDIO_AUDIO_H
 
+// STL
+#include <vector>
+
+// LOVE
 #include "common/Module.h"
 #include "common/StringMap.h"
 #include "Source.h"
@@ -90,12 +94,24 @@ public:
 	 **/
 	virtual bool play(Source *source) = 0;
 
+	/**
+	 * Play the specified Sources.
+	 * @param sources The Sources to play.
+	 **/
+	virtual bool play(const std::vector<Source*> &sources) = 0;
+
 	/**
 	 * Stops playback on the specified source.
 	 * @param source The source on which to stop the playback.
 	 **/
 	virtual void stop(Source *source) = 0;
 
+	/**
+	 * Stops playback on the specified sources.
+	 * @param sources The sources on which to stop the playback.
+	 **/
+	virtual void stop(const std::vector<Source*> &sources) = 0;
+
 	/**
 	 * Stops all playing audio.
 	 **/
@@ -108,32 +124,15 @@ public:
 	virtual void pause(Source *source) = 0;
 
 	/**
-	 * Pauses all audio.
-	 **/
-	virtual void pause() = 0;
-
-	/**
-	 * Resumes playback on the specified source.
-	 * @param source The source on which to resume the playback.
-	 **/
-	virtual void resume(Source *source) = 0;
-
-	/**
-	 * Resumes all audio.
+	 * Pauses playback on the specified sources.
+	 * @param sources The sources on which to pause the playback.
 	 **/
-	virtual void resume() = 0;
+	virtual void pause(const std::vector<Source*> &sources) = 0;
 
 	/**
-	 * Rewinds the specified source. Whatever is playing on this
-	 * source gets rewound to the start.
-	 * @param source The source to rewind.
-	 **/
-	virtual void rewind(Source *source) = 0;
-
-	/**
-	 * Rewinds all playing audio.
+	 * Pauses all audio.
 	 **/
-	virtual void rewind() = 0;
+	virtual std::vector<Source*> pause() = 0;
 
 	/**
 	 * Sets the master volume, where 0.0f is min (off) and 1.0f is max.

+ 1 - 4
src/modules/audio/Source.h

@@ -56,10 +56,7 @@ public:
 	virtual bool play() = 0;
 	virtual void stop() = 0;
 	virtual void pause() = 0;
-	virtual void resume() = 0;
-	virtual void rewind() = 0;
-	virtual bool isStopped() const = 0;
-	virtual bool isPaused() const = 0;
+	virtual bool isPlaying() const = 0;
 	virtual bool isFinished() const = 0;
 	virtual bool update() = 0;
 

+ 9 - 11
src/modules/audio/null/Audio.cpp

@@ -66,36 +66,34 @@ bool Audio::play(love::audio::Source *)
 	return false;
 }
 
-void Audio::stop(love::audio::Source *)
-{
-}
-
-void Audio::stop()
+bool Audio::play(const std::vector<love::audio::Source*>&)
 {
+	return false;
 }
 
-void Audio::pause(love::audio::Source *)
+void Audio::stop(love::audio::Source *)
 {
 }
 
-void Audio::pause()
+void Audio::stop(const std::vector<love::audio::Source*>&)
 {
 }
 
-void Audio::resume(love::audio::Source *)
+void Audio::stop()
 {
 }
 
-void Audio::resume()
+void Audio::pause(love::audio::Source *)
 {
 }
 
-void Audio::rewind(love::audio::Source *)
+void Audio::pause(const std::vector<love::audio::Source*>&)
 {
 }
 
-void Audio::rewind()
+std::vector<love::audio::Source*> Audio::pause()
 {
+	return {};
 }
 
 void Audio::setVolume(float volume)

+ 4 - 5
src/modules/audio/null/Audio.h

@@ -49,14 +49,13 @@ public:
 	int getSourceCount() const;
 	int getMaxSources() const;
 	bool play(love::audio::Source *source);
+	bool play(const std::vector<love::audio::Source*> &sources);
 	void stop(love::audio::Source *source);
+	void stop(const std::vector<love::audio::Source*> &sources);
 	void stop();
 	void pause(love::audio::Source *source);
-	void pause();
-	void resume(love::audio::Source *source);
-	void resume();
-	void rewind(love::audio::Source *source);
-	void rewind();
+	void pause(const std::vector<love::audio::Source*> &sources);
+	std::vector<love::audio::Source*> pause();
 	void setVolume(float volume);
 	float getVolume() const;
 

+ 1 - 14
src/modules/audio/null/Source.cpp

@@ -55,20 +55,7 @@ void Source::pause()
 {
 }
 
-void Source::resume()
-{
-}
-
-void Source::rewind()
-{
-}
-
-bool Source::isStopped() const
-{
-	return true;
-}
-
-bool Source::isPaused() const
+bool Source::isPlaying() const
 {
 	return false;
 }

+ 1 - 4
src/modules/audio/null/Source.h

@@ -42,10 +42,7 @@ public:
 	virtual bool play();
 	virtual void stop();
 	virtual void pause();
-	virtual void resume();
-	virtual void rewind();
-	virtual bool isStopped() const;
-	virtual bool isPaused() const;
+	virtual bool isPlaying() const;
 	virtual bool isFinished() const;
 	virtual bool update();
 	virtual void setPitch(float pitch);

+ 14 - 25
src/modules/audio/openal/Audio.cpp

@@ -172,50 +172,39 @@ bool Audio::play(love::audio::Source *source)
 	return source->play();
 }
 
-void Audio::stop(love::audio::Source *source)
-{
-	source->stop();
-}
-
-void Audio::stop()
+bool Audio::play(const std::vector<love::audio::Source*> &sources)
 {
-	pool->stop();
+	return pool->play(sources);
 }
 
-void Audio::pause(love::audio::Source *source)
+void Audio::stop(love::audio::Source *source)
 {
-	source->pause();
+	source->stop();
 }
 
-void Audio::pause()
+void Audio::stop(const std::vector<love::audio::Source*> &sources)
 {
-	pool->pause();
-#ifdef LOVE_ANDROID
-	alcDevicePauseSOFT(device);
-#endif
+	return pool->stop(sources);
 }
 
-void Audio::resume(love::audio::Source *source)
+void Audio::stop()
 {
-	source->resume();
+	pool->stop();
 }
 
-void Audio::resume()
+void Audio::pause(love::audio::Source *source)
 {
-#ifdef LOVE_ANDROID
-	alcDeviceResumeSOFT(device);
-#endif
-	pool->resume();
+	source->pause();
 }
 
-void Audio::rewind(love::audio::Source *source)
+void Audio::pause(const std::vector<love::audio::Source*> &sources)
 {
-	source->rewind();
+	return pool->pause(sources);
 }
 
-void Audio::rewind()
+std::vector<love::audio::Source*> Audio::pause()
 {
-	pool->rewind();
+	return pool->pause();
 }
 
 void Audio::setVolume(float volume)

+ 5 - 5
src/modules/audio/openal/Audio.h

@@ -24,6 +24,7 @@
 // STD
 #include <queue>
 #include <map>
+#include <vector>
 #include <cmath>
 
 // LOVE
@@ -73,14 +74,13 @@ public:
 	int getSourceCount() const;
 	int getMaxSources() const;
 	bool play(love::audio::Source *source);
+	bool play(const std::vector<love::audio::Source*> &sources);
 	void stop(love::audio::Source *source);
+	void stop(const std::vector<love::audio::Source*> &sources);
 	void stop();
 	void pause(love::audio::Source *source);
-	void pause();
-	void resume(love::audio::Source *source);
-	void resume();
-	void rewind(love::audio::Source *source);
-	void rewind();
+	void pause(const std::vector<love::audio::Source*> &sources);
+	std::vector<love::audio::Source*> pause();
 	void setVolume(float volume);
 	float getVolume() const;
 

+ 72 - 58
src/modules/audio/openal/Pool.cpp

@@ -110,7 +110,6 @@ void Pool::update()
 		if (!i->first->update())
 		{
 			i->first->stopAtomic();
-			i->first->rewindAtomic();
 			i->first->release();
 			available.push(i->second);
 			playing.erase(i++);
@@ -130,44 +129,72 @@ int Pool::getMaxSources() const
 	return totalSources;
 }
 
-bool Pool::play(Source *source, ALuint &out)
+bool Pool::assignSource(Source *source, ALuint &out, char *wasPlaying)
 {
-	thread::Lock lock(mutex);
-
-	bool ok = true;
 	out = 0;
 
-	bool alreadyPlaying = findSource(source, out);
-
-	if (!alreadyPlaying)
+	if (findSource(source, out))
 	{
-		// Try to play.
-		if (!available.empty())
-		{
-			// Get the first available source.
-			out = available.front();
+		if (wasPlaying)
+			*wasPlaying = true;
+		return true;
+	}
 
-			// Remove it.
-			available.pop();
+	if (wasPlaying)
+		*wasPlaying = false;
 
-			// Insert into map of playing sources.
-			playing.insert(std::pair<Source *, ALuint>(source, out));
+	if (available.empty())
+		return false;
 
-			source->retain();
+	out = available.front();
+	available.pop();
 
-			ok = source->playAtomic();
-		}
-		else
-		{
-			ok = false;
-		}
-	}
+	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
 	{
-		ok = true;
+		source->resumeAtomic();
+		return true;
 	}
+}
 
-	return ok;
+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()
@@ -176,7 +203,6 @@ void Pool::stop()
 	for (const auto &i : playing)
 	{
 		i.first->stopAtomic();
-		i.first->rewindAtomic();
 		i.first->release();
 		available.push(i.second);
 	}
@@ -190,54 +216,42 @@ void Pool::stop(Source *source)
 	removeSource(source);
 }
 
-void Pool::pause()
+void Pool::stop(const std::vector<love::audio::Source*> &sources)
 {
 	thread::Lock lock(mutex);
-	for (const auto &i : playing)
-		i.first->pauseAtomic();
+	Source::stopAtomic(sources);
 }
 
-void Pool::pause(Source *source)
+std::vector<love::audio::Source*> Pool::pause()
 {
 	thread::Lock lock(mutex);
-	ALuint out;
-	if (findSource(source, out))
-		source->pauseAtomic();
-}
 
-void Pool::resume()
-{
-	thread::Lock lock(mutex);
+	std::vector<love::audio::Source*> werePlaying;
+	werePlaying.reserve(playing.size());
+
 	for (const auto &i : playing)
-		i.first->resumeAtomic();
+	{
+		if (!i.first->isPlaying())
+			continue;
+		werePlaying.push_back(i.first);
+		i.first->pauseAtomic();
+	}
+
+	return werePlaying;
 }
 
-void Pool::resume(Source *source)
+void Pool::pause(Source *source)
 {
 	thread::Lock lock(mutex);
 	ALuint out;
 	if (findSource(source, out))
-		source->resumeAtomic();
-}
-
-void Pool::rewind()
-{
-	thread::Lock lock(mutex);
-	for (const auto &i : playing)
-		i.first->rewindAtomic();
-}
-
-// For those times we don't need it backed.
-void Pool::softRewind(Source *source)
-{
-	thread::Lock lock(mutex);
-	source->rewindAtomic();
+		source->pauseAtomic();
 }
 
-void Pool::rewind(Source *source)
+void Pool::pause(const std::vector<love::audio::Source*> &sources)
 {
 	thread::Lock lock(mutex);
-	source->rewindAtomic();
+	Source::pauseAtomic(sources);
 }
 
 void Pool::release(Source *source)

+ 9 - 7
src/modules/audio/openal/Pool.h

@@ -24,12 +24,14 @@
 // STD
 #include <queue>
 #include <map>
+#include <vector>
 #include <cmath>
 
 // LOVE
 #include "common/config.h"
 #include "common/Exception.h"
 #include "thread/threads.h"
+#include "audio/Source.h"
 
 // OpenAL
 #ifdef LOVE_APPLE_USE_FRAMEWORKS
@@ -81,20 +83,19 @@ public:
 	int getSourceCount() const;
 	int getMaxSources() const;
 
-	bool play(Source *source, ALuint &out);
+	bool play(Source *source);
 	void stop();
 	void stop(Source *source);
-	void pause();
+	std::vector<love::audio::Source*> pause();
 	void pause(Source *source);
-	void resume();
-	void resume(Source *source);
-	void rewind();
-	void rewind(Source *source);
-	void softRewind(Source *source);
 	void seek(Source *source, float offset, void *unit);
 	float tell(Source *source, void *unit);
 	double getDuration(Source *source, void *unit);
 
+	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:
 
 	/**
@@ -105,6 +106,7 @@ private:
 
 	ALuint findi(const Source *source) const;
 
+	bool assignSource(Source *source, ALuint &out, char *wasPlaying = nullptr);
 	bool findSource(Source *source, ALuint &out);
 	bool removeSource(Source *source);
 

+ 149 - 127
src/modules/audio/openal/Source.cpp

@@ -85,7 +85,6 @@ Source::Source(Pool *pool, love::sound::SoundData *soundData)
 	, volume(1.0f)
 	, relative(false)
 	, looping(false)
-	, paused(false)
 	, minVolume(0.0f)
 	, maxVolume(1.0f)
 	, referenceDistance(1.0f)
@@ -122,7 +121,6 @@ Source::Source(Pool *pool, love::sound::Decoder *decoder)
 	, volume(1.0f)
 	, relative(false)
 	, looping(false)
-	, paused(false)
 	, minVolume(0.0f)
 	, maxVolume(1.0f)
 	, referenceDistance(1.0f)
@@ -158,7 +156,6 @@ Source::Source(const Source &s)
 	, volume(s.volume)
 	, relative(s.relative)
 	, looping(s.looping)
-	, paused(false)
 	, minVolume(s.minVolume)
 	, maxVolume(s.maxVolume)
 	, referenceDistance(s.referenceDistance)
@@ -202,23 +199,14 @@ love::audio::Source *Source::clone()
 
 bool Source::play()
 {
-	if (valid && paused)
-	{
-		pool->resume(this);
-		return true;
-	}
-
-	valid = pool->play(this, source);
+	valid = pool->play(this);
 	return valid;
 }
 
 void Source::stop()
 {
-	if (!isStopped())
-	{
+	if (valid)
 		pool->stop(this);
-		pool->softRewind(this);
-	}
 }
 
 void Source::pause()
@@ -226,43 +214,27 @@ void Source::pause()
 	pool->pause(this);
 }
 
-void Source::resume()
+bool Source::isPlaying() const
 {
-	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);
-	}
+	if (!valid)
+		return false;
 
-	return true;
+	ALenum state;
+	alGetSourcei(source, AL_SOURCE_STATE, &state);
+	return state == AL_PLAYING;
 }
 
-bool Source::isPaused() const
+bool Source::isFinished() const
 {
-	if (valid)
-	{
-		ALenum state;
-		alGetSourcei(source, AL_SOURCE_STATE, &state);
-		return (state == AL_PAUSED);
-	}
+	if (!valid)
+		return false;
 
-	return false;
-}
+	if (type == TYPE_STREAM && (isLooping() || !decoder->isFinished()))
+		return false;
 
-bool Source::isFinished() const
-{
-	return type == TYPE_STATIC ? isStopped() : (isStopped() && !isLooping() && decoder->isFinished());
+	ALenum state;
+	alGetSourcei(source, AL_SOURCE_STATE, &state);
+	return state == AL_STOPPED;
 }
 
 bool Source::update()
@@ -274,7 +246,7 @@ bool Source::update()
 	{
 		// Looping mode could have changed.
 		alSourcei(source, AL_LOOPING, isLooping() ? AL_TRUE : AL_FALSE);
-		return !isStopped();
+		return !isFinished();
 	}
 	else if (type == TYPE_STREAM && (isLooping() || !isFinished()))
 	{
@@ -362,45 +334,46 @@ float Source::getVolume() const
 
 void Source::seekAtomic(float offset, void *unit)
 {
-	if (valid)
+	if (!valid)
+		return;
+
+	bool waspaused = !isPlaying();
+
+	// To drain all buffers
+	if (type == TYPE_STREAM)
+		stopAtomic();
+
+	switch (*((Source::Unit *) unit))
 	{
-		switch (*((Source::Unit *) unit))
+	case Source::UNIT_SAMPLES:
+		if (type == TYPE_STREAM)
 		{
-		case Source::UNIT_SAMPLES:
-			if (type == TYPE_STREAM)
-			{
-				offsetSamples = offset;
-				offset /= decoder->getSampleRate();
-				offsetSeconds = offset;
-				decoder->seek(offset);
-			}
-			else
-				alSourcef(source, AL_SAMPLE_OFFSET, offset);
-			break;
-		case Source::UNIT_SECONDS:
-		default:
-			if (type == TYPE_STREAM)
-			{
-				offsetSeconds = offset;
-				decoder->seek(offset);
-				offsetSamples = offset * decoder->getSampleRate();
-			}
-			else
-				alSourcef(source, AL_SEC_OFFSET, offset);
-			break;
+			offsetSamples = offset;
+			offset /= decoder->getSampleRate();
+			offsetSeconds = offset;
+			decoder->seek(offset);
 		}
+		else
+			alSourcef(source, AL_SAMPLE_OFFSET, offset);
+		break;
+	case Source::UNIT_SECONDS:
+	default:
 		if (type == TYPE_STREAM)
 		{
-			bool waspaused = paused;
-			// Because we still have old data
-			// from before the seek in the buffers
-			// let's empty them.
-			stopAtomic();
-			playAtomic();
-			if (waspaused)
-				pauseAtomic();
+			offsetSeconds = offset;
+			decoder->seek(offset);
+			offsetSamples = offset * decoder->getSampleRate();
 		}
+		else
+			alSourcef(source, AL_SEC_OFFSET, offset);
+		break;
 	}
+
+	if (type == TYPE_STREAM)
+		playAtomic(source);
+
+	if (waspaused)
+		pauseAtomic();
 }
 
 void Source::seek(float offset, Source::Unit unit)
@@ -593,7 +566,7 @@ bool Source::isLooping() const
 	return looping;
 }
 
-bool Source::playAtomic()
+void Source::prepareAtomic()
 {
 	if (type == TYPE_STATIC)
 	{
@@ -622,6 +595,38 @@ bool Source::playAtomic()
 	// the properties of another love Source. Let's reset it to the settings
 	// of the new one.
 	reset();
+}
+
+void Source::teardownAtomic()
+{
+	if (type == TYPE_STATIC)
+	{
+		alSourcef(source, AL_SAMPLE_OFFSET, 0);
+	}
+	else if (type == TYPE_STREAM)
+	{
+		decoder->seek(0);
+
+		int queued = 0;
+		alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
+
+		while (queued--)
+		{
+			ALuint buffer;
+			alSourceUnqueueBuffers(source, 1, &buffer);
+		}
+	}
+
+	alSourcei(source, AL_BUFFER, AL_NONE);
+
+	toLoop = 0;
+	valid = false;
+}
+
+bool Source::playAtomic(ALuint source)
+{
+	this->source = source;
+	prepareAtomic();
 
 	// Clear errors.
 	alGetError();
@@ -640,76 +645,93 @@ bool Source::playAtomic()
 
 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);
-	}
-
-	toLoop = 0;
-	valid = false;
+	if (!valid)
+		return;
+	alSourceStop(source);
+	teardownAtomic();
 }
 
 void Source::pauseAtomic()
 {
 	if (valid)
-	{
 		alSourcePause(source);
-		paused = true;
-	}
 }
 
 void Source::resumeAtomic()
 {
-	if (valid && paused)
-	{
+	if (valid && !isPlaying())
 		alSourcePlay(source);
-		paused = false;
+}
+
+bool Source::playAtomic(const std::vector<love::audio::Source*> &sources, const std::vector<ALuint> &ids, const std::vector<char> &wasPlaying)
+{
+	if (sources.size() == 0)
+		return true;
+
+	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(toPlay.size(), &toPlay[0]);
+	bool success = alGetError() == AL_NO_ERROR;
+
+	for (auto &_source : sources)
+	{
+		Source *source = (Source*) _source;
+		source->valid = source->valid || success;
+	}
+
+	return success;
 }
 
-void Source::rewindAtomic()
+void Source::stopAtomic(const std::vector<love::audio::Source*> &sources)
 {
-	if (valid && type == TYPE_STATIC)
+	if (sources.size() == 0)
+		return;
+
+	std::vector<ALuint> sourceIds;
+	sourceIds.reserve(sources.size());
+	for (auto &_source : sources)
 	{
-		alSourceRewind(source);
-		if (!paused)
-			alSourcePlay(source);
+		Source *source = (Source*) _source;
+		if (source->valid)
+			sourceIds.push_back(source->source);
 	}
-	else if (valid && type == TYPE_STREAM)
+
+	alSourceStopv(sources.size(), &sourceIds[0]);
+
+	for (auto &_source : sources)
 	{
-		bool waspaused = paused;
-		decoder->rewind();
-		// Because we still have old data
-		// from before the seek in the buffers
-		// let's empty them.
-		stopAtomic();
-		playAtomic();
-		if (waspaused)
-			pauseAtomic();
-		offsetSamples = 0;
-		offsetSeconds = 0;
+		Source *source = (Source*) _source;
+		if (source->valid)
+			source->teardownAtomic();
 	}
-	else if (type == TYPE_STREAM)
+}
+
+void Source::pauseAtomic(const std::vector<love::audio::Source*> &sources)
+{
+	if (sources.size() == 0)
+		return;
+
+	std::vector<ALuint> sourceIds;
+	sourceIds.reserve(sources.size());
+	for (auto &_source : sources)
 	{
-		decoder->rewind();
-		offsetSamples = 0;
-		offsetSeconds = 0;
+		Source *source = (Source*) _source;
+		if (source->valid)
+			sourceIds.push_back(source->source);
 	}
+
+	alSourcePausev(sources.size(), &sourceIds[0]);
 }
 
 void Source::reset()

+ 12 - 7
src/modules/audio/openal/Source.h

@@ -28,6 +28,9 @@
 #include "sound/SoundData.h"
 #include "sound/Decoder.h"
 
+// STL
+#include <vector>
+
 // OpenAL
 #ifdef LOVE_APPLE_USE_FRAMEWORKS
 #ifdef LOVE_IOS
@@ -90,10 +93,7 @@ public:
 	virtual bool play();
 	virtual void stop();
 	virtual void pause();
-	virtual void resume();
-	virtual void rewind();
-	virtual bool isStopped() const;
-	virtual bool isPaused() const;
+	virtual bool isPlaying() const;
 	virtual bool isFinished() const;
 	virtual bool update();
 	virtual void setPitch(float pitch);
@@ -130,11 +130,17 @@ public:
 	virtual float getMaxDistance() const;
 	virtual int getChannels() const;
 
-	bool playAtomic();
+	void prepareAtomic();
+	void teardownAtomic();
+
+	bool playAtomic(ALuint source);
 	void stopAtomic();
 	void pauseAtomic();
 	void resumeAtomic();
-	void rewindAtomic();
+
+	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);
 
 private:
 
@@ -169,7 +175,6 @@ private:
 	float direction[3];
 	bool relative;
 	bool looping;
-	bool paused;
 	float minVolume;
 	float maxVolume;
 	float referenceDistance;

+ 39 - 35
src/modules/audio/wrap_Audio.cpp

@@ -75,8 +75,32 @@ int w_newSource(lua_State *L)
 		return luax_typerror(L, 1, "Decoder or SoundData");
 }
 
+static std::vector<Source*> readSourceList(lua_State *L, int n)
+{
+	if (n < 0)
+		n += lua_gettop(L) + 1;
+
+	size_t items = lua_objlen(L, n);
+	std::vector<Source*> sources(items);
+
+	for (size_t i = 0; i < items; i++)
+	{
+		lua_rawgeti(L, n, i+1);
+		sources[i] = luax_checksource(L, -1);
+		lua_pop(L, 1);
+	}
+
+	return sources;
+}
+
 int w_play(lua_State *L)
 {
+	if (lua_istable(L, 1))
+	{
+		luax_pushboolean(L, instance()->play(readSourceList(L, 1)));
+		return 1;
+	}
+
 	Source *s = luax_checksource(L, 1);
 	luax_pushboolean(L, instance()->play(s));
 	return 1;
@@ -84,10 +108,10 @@ int w_play(lua_State *L)
 
 int w_stop(lua_State *L)
 {
-	if (lua_gettop(L) == 0)
-	{
+	if (lua_isnone(L, 1))
 		instance()->stop();
-	}
+	else if (lua_istable(L, 1))
+		instance()->stop(readSourceList(L, 1));
 	else
 	{
 		Source *s = luax_checksource(L, 1);
@@ -98,44 +122,26 @@ int w_stop(lua_State *L)
 
 int w_pause(lua_State *L)
 {
-	if (lua_gettop(L) == 0)
+	if (lua_isnone(L, 1))
 	{
-		instance()->pause();
-	}
-	else
-	{
-		Source *s = luax_checksource(L, 1);
-		s->pause();
-	}
+		auto sources = instance()->pause();
 
-	return 0;
-}
-
-int w_resume(lua_State *L)
-{
-	if (lua_gettop(L) == 0)
-	{
-		instance()->resume();
+		lua_createtable(L, sources.size(), 0);
+		for (size_t i = 0; i < sources.size(); i++)
+		{
+			luax_pushtype(L, AUDIO_SOURCE_ID, sources[i]);
+			lua_rawseti(L, -2, i+1);
+		}
+		return 1;
 	}
+	else if (lua_istable(L, 1))
+		instance()->pause(readSourceList(L, 1));
 	else
 	{
 		Source *s = luax_checksource(L, 1);
-		s->resume();
+		s->pause();
 	}
-	return 0;
-}
 
-int w_rewind(lua_State *L)
-{
-	if (lua_gettop(L) == 0)
-	{
-		instance()->rewind();
-	}
-	else
-	{
-		Source *s = luax_checksource(L, 1);
-		s->rewind();
-	}
 	return 0;
 }
 
@@ -301,8 +307,6 @@ static const luaL_Reg functions[] =
 	{ "play", w_play },
 	{ "stop", w_stop },
 	{ "pause", w_pause },
-	{ "resume", w_resume },
-	{ "rewind", w_rewind },
 	{ "setVolume", w_setVolume },
 	{ "getVolume", w_getVolume },
 	{ "setPosition", w_setPosition },

+ 1 - 33
src/modules/audio/wrap_Source.cpp

@@ -63,20 +63,6 @@ int w_Source_pause(lua_State *L)
 	return 0;
 }
 
-int w_Source_resume(lua_State *L)
-{
-	Source *t = luax_checksource(L, 1);
-	t->resume();
-	return 0;
-}
-
-int w_Source_rewind(lua_State *L)
-{
-	Source *t = luax_checksource(L, 1);
-	t->rewind();
-	return 0;
-}
-
 int w_Source_setPitch(lua_State *L)
 {
 	Source *t = luax_checksource(L, 1);
@@ -268,24 +254,10 @@ int w_Source_isLooping(lua_State *L)
 	return 1;
 }
 
-int w_Source_isStopped(lua_State *L)
-{
-	Source *t = luax_checksource(L, 1);
-	luax_pushboolean(L, t->isStopped());
-	return 1;
-}
-
-int w_Source_isPaused(lua_State *L)
-{
-	Source *t = luax_checksource(L, 1);
-	luax_pushboolean(L, t->isPaused());
-	return 1;
-}
-
 int w_Source_isPlaying(lua_State *L)
 {
 	Source *t = luax_checksource(L, 1);
-	luax_pushboolean(L, !t->isStopped() && !t->isPaused());
+	luax_pushboolean(L, t->isPlaying());
 	return 1;
 }
 
@@ -377,8 +349,6 @@ static const luaL_Reg w_Source_functions[] =
 	{ "play", w_Source_play },
 	{ "stop", w_Source_stop },
 	{ "pause", w_Source_pause },
-	{ "resume", w_Source_resume },
-	{ "rewind", w_Source_rewind },
 
 	{ "setPitch", w_Source_setPitch },
 	{ "getPitch", w_Source_getPitch },
@@ -401,8 +371,6 @@ static const luaL_Reg w_Source_functions[] =
 
 	{ "setLooping", w_Source_setLooping },
 	{ "isLooping", w_Source_isLooping },
-	{ "isStopped", w_Source_isStopped },
-	{ "isPaused", w_Source_isPaused },
 	{ "isPlaying", w_Source_isPlaying },
 
 	{ "setVolumeLimits", w_Source_setVolumeLimits },

+ 1 - 1
src/modules/video/VideoStream.cpp

@@ -161,7 +161,7 @@ void VideoStream::SourceSync::seek(double time)
 
 bool VideoStream::SourceSync::isPlaying() const
 {
-	return !source->isStopped() && !source->isPaused();
+	return source->isPlaying();
 }
 
 } // video