Commits

Alex Szpakowski  committed cbf1271

Added Source:clone() and love.audio.newSource(source): creates a new (stopped) Source which has all of the settable state of the original.
Static sources don’t duplicate their OpenAL data buffer when cloned (resolves issue #319). Cloned streaming sources also clone the original Decoder.

  • Participants
  • Parent commits 4e09d57

Comments (0)

Files changed (8)

File src/modules/audio/Source.h

 	Source(Type type);
 	virtual ~Source();
 
-	virtual Source *copy() = 0;
+	virtual Source *clone() = 0;
 
 	virtual void play() = 0;
 	virtual void stop() = 0;

File src/modules/audio/null/Source.cpp

 {
 }
 
-love::audio::Source *Source::copy()
+love::audio::Source *Source::clone()
 {
 	this->retain();
 	return this;

File src/modules/audio/null/Source.h

 	Source();
 	virtual ~Source();
 
-	virtual love::audio::Source *copy();
+	virtual love::audio::Source *clone();
 	virtual void play();
 	virtual void stop();
 	virtual void pause();

File src/modules/audio/openal/Source.cpp

 namespace openal
 {
 
+StaticDataBuffer::StaticDataBuffer(ALenum format, const ALvoid *data, ALsizei size, ALsizei freq)
+{
+	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)
 	, valid(false)
+	, staticBuffer(nullptr)
 	, pitch(1.0f)
 	, volume(1.0f)
 	, relative(false)
 	, offsetSamples(0)
 	, offsetSeconds(0)
 	, channels(soundData->getChannels())
-	, decoder(0)
+	, decoder(nullptr)
 	, toLoop(0)
 {
-	alGenBuffers(1, buffers);
 	ALenum fmt = getFormat(soundData->getChannels(), soundData->getBitDepth());
-	alBufferData(buffers[0], fmt, soundData->getData(), soundData->getSize(), soundData->getSampleRate());
+	staticBuffer = new StaticDataBuffer(fmt, soundData->getData(), soundData->getSize(), soundData->getSampleRate());
 
-	static float z[3] = {0, 0, 0};
+	float z[3] = {0, 0, 0};
 
 	setFloatv(position, z);
 	setFloatv(velocity, z);
 	: love::audio::Source(Source::TYPE_STREAM)
 	, pool(pool)
 	, valid(false)
+	, staticBuffer(nullptr)
 	, pitch(1.0f)
 	, volume(1.0f)
 	, relative(false)
 	, toLoop(0)
 {
 	decoder->retain();
-	alGenBuffers(MAX_BUFFERS, buffers);
+	alGenBuffers(MAX_BUFFERS, streamBuffers);
 
-	static float z[3] = {0, 0, 0};
+	float z[3] = {0, 0, 0};
 
 	setFloatv(position, z);
 	setFloatv(velocity, z);
 	setFloatv(direction, z);
 }
 
+Source::Source(Source *s)
+	: love::audio::Source(s->type)
+	, pool(s->pool)
+	, valid(false)
+	, staticBuffer(s->staticBuffer)
+	, pitch(s->pitch)
+	, volume(s->volume)
+	, relative(s->relative)
+	, looping(s->looping)
+	, paused(false)
+	, minVolume(s->minVolume)
+	, maxVolume(s->maxVolume)
+	, referenceDistance(s->referenceDistance)
+	, rolloffFactor(s->rolloffFactor)
+	, maxDistance(s->maxDistance)
+	, cone(s->cone)
+	, offsetSamples(0)
+	, offsetSeconds(0)
+	, channels(s->channels)
+	, decoder(nullptr)
+	, toLoop(0)
+{
+	if (type == TYPE_STREAM)
+	{
+		if (s->decoder)
+			decoder = s->decoder->clone();
+
+		alGenBuffers(MAX_BUFFERS, streamBuffers);
+	}
+	else
+		staticBuffer->retain();
+
+	setFloatv(position, s->position);
+	setFloatv(velocity, s->velocity);
+	setFloatv(direction, s->direction);
+}
+
 Source::~Source()
 {
 	if (valid)
 		pool->stop(this);
-	alDeleteBuffers((type == TYPE_STATIC) ? 1 : MAX_BUFFERS, buffers);
+
+	if (type == TYPE_STREAM)
+		alDeleteBuffers(MAX_BUFFERS, streamBuffers);
+
+	if (staticBuffer)
+		staticBuffer->release();
+
 	if (decoder)
 		decoder->release();
 }
 
-love::audio::Source *Source::copy()
+love::audio::Source *Source::clone()
 {
-	return 0;
+	return new Source(this);
 }
 
 void Source::play()
 			break;
 		case Source::UNIT_SECONDS:
 		default:
-			alGetSourcef(source, AL_SAMPLE_OFFSET, &offset);
-			ALint buffer;
-			alGetSourcei(source, AL_BUFFER, &buffer);
-			int freq;
-			alGetBufferi(buffer, AL_FREQUENCY, &freq);
-			offset /= freq;
-			if (type == TYPE_STREAM) offset += offsetSeconds;
+			{
+				alGetSourcef(source, AL_SAMPLE_OFFSET, &offset);
+				ALint buffer;
+				alGetSourcei(source, AL_BUFFER, &buffer);
+				int freq;
+				alGetBufferi(buffer, AL_FREQUENCY, &freq);
+				offset /= freq;
+				if (type == TYPE_STREAM) offset += offsetSeconds;
+			}
 			break;
 		}
 		return offset;
 {
 	if (type == TYPE_STATIC)
 	{
-		alSourcei(source, AL_BUFFER, buffers[0]);
+		alSourcei(source, AL_BUFFER, staticBuffer->getBuffer());
 	}
 	else if (type == TYPE_STREAM)
 	{
 
 		for (unsigned int i = 0; i < MAX_BUFFERS; i++)
 		{
-			streamAtomic(buffers[i], decoder);
+			streamAtomic(streamBuffers[i], decoder);
 			++usedBuffers;
 			if (decoder->isFinished())
 				break;
 		}
 
 		if (usedBuffers > 0)
-			alSourceQueueBuffers(source, usedBuffers, buffers);
+			alSourceQueueBuffers(source, usedBuffers, streamBuffers);
 	}
 
 	// This Source may now be associated with an OpenAL source that still has

File src/modules/audio/openal/Source.h

 class Audio;
 class Pool;
 
+// Basically just a reference-counted non-streaming OpenAL buffer object.
+class StaticDataBuffer : public love::Object
+{
+public:
+
+	StaticDataBuffer(ALenum format, const ALvoid *data, ALsizei size, ALsizei freq);
+	virtual ~StaticDataBuffer();
+
+	inline ALuint getBuffer() const
+	{
+		return buffer;
+	}
+
+private:
+
+	ALuint buffer;
+
+}; // StaticDataBuffer
+
 class Source : public love::audio::Source
 {
 public:
+
 	Source(Pool *pool, love::sound::SoundData *soundData);
 	Source(Pool *pool, love::sound::Decoder *decoder);
+	Source(Source *s);
 	virtual ~Source();
 
-	virtual love::audio::Source *copy();
+	virtual love::audio::Source *clone();
 	virtual void play();
 	virtual void stop();
 	virtual void pause();
 	Pool *pool;
 	ALuint source;
 	bool valid;
+
 	static const unsigned int MAX_BUFFERS = 32;
-	ALuint buffers[MAX_BUFFERS];
+	ALuint streamBuffers[MAX_BUFFERS];
+
+	StaticDataBuffer *staticBuffer;
 
 	float pitch;
 	float volume;

File src/modules/audio/wrap_Audio.cpp

 
 int w_newSource(lua_State *L)
 {
+	if (luax_istype(L, 1, AUDIO_SOURCE_T))
+	{
+		Source *t = luax_checksource(L, 1);
+		Source *clone = 0;
+		EXCEPT_GUARD(clone = t->clone();)
+		luax_pushtype(L, "Source", AUDIO_SOURCE_T, clone);
+		return 1;
+	}
+
 	if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T))
 		luax_convobj(L, 1, "filesystem", "newFileData");
 

File src/modules/audio/wrap_Source.cpp

 	return luax_checktype<Source>(L, idx, "Source", AUDIO_SOURCE_T);
 }
 
+int w_Source_clone(lua_State *L)
+{
+	Source *t = luax_checksource(L, 1);
+	Source *clone = nullptr;
+	EXCEPT_GUARD(clone = t->clone();)
+	luax_pushtype(L, "Source", AUDIO_SOURCE_T, clone);
+	return 1;
+}
+
 int w_Source_play(lua_State *L)
 {
 	Source *t = luax_checksource(L, 1);
 
 static const luaL_Reg functions[] =
 {
+	{ "clone", w_Source_clone },
+
 	{ "play", w_Source_play },
 	{ "stop", w_Source_stop },
 	{ "pause", w_Source_pause },

File src/modules/audio/wrap_Source.h

 {
 
 Source *luax_checksource(lua_State *L, int idx);
+int w_Source_clone(lua_State *L);
 int w_Source_play(lua_State *L);
 int w_Source_stop(lua_State *L);
 int w_Source_pause(lua_State *L);