Browse Source

queueData now returns success state, couple bug fixes

success state return added to queueData functions
queueable source is now seekable while stopped
fixed bug with creating queueable source with invalid format silently crashing LOVE
fixed bug with stopped queueable source reported as not queueable

--HG--
branch : minor
raidho36 8 years ago
parent
commit
10710d3428

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

@@ -103,7 +103,7 @@ public:
 	virtual int getChannels() const = 0;
 	
 	virtual bool isQueueable() const = 0;
-	virtual void queueData(void *data, int length, int dataSampleRate, int dataBitDepth, int dataChannels) = 0;
+	virtual bool queueData(void *data, int length, int dataSampleRate, int dataBitDepth, int dataChannels) = 0;
 	
 	virtual Type getType() const;
 

+ 2 - 2
src/modules/audio/null/Source.cpp

@@ -222,9 +222,9 @@ bool Source::isQueueable() const
 	return false;
 }
 
-void Source::queueData(void *data, int length, int dataSampleRate, int dataBitDepth, int dataChannels)
+bool Source::queueData(void *data, int length, int dataSampleRate, int dataBitDepth, int dataChannels)
 {
-	return;
+	return false;
 }
 
 

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

@@ -77,7 +77,7 @@ public:
 	virtual int getChannels() const;
 
 	virtual bool isQueueable() const;
-	virtual void queueData(void *data, int length, int dataSampleRate, int dataBitDepth, int dataChannels);
+	virtual bool queueData(void *data, int length, int dataSampleRate, int dataBitDepth, int dataChannels);
 
 private:
 

+ 2 - 2
src/modules/audio/openal/Pool.cpp

@@ -283,10 +283,10 @@ double Pool::getDuration(Source *source, void *unit)
 	return source->getDurationAtomic(unit);
 }
 
-void Pool::queueData(Source *source, void *data, ALsizei length)
+bool Pool::queueData(Source *source, void *data, ALsizei length)
 {
 	thread::Lock lock(mutex);
-	source->queueDataAtomic(data, length);
+	return source->queueDataAtomic(data, length);
 }
 
 ALuint Pool::findi(const Source *source) const

+ 1 - 1
src/modules/audio/openal/Pool.h

@@ -91,7 +91,7 @@ public:
 	void seek(Source *source, float offset, void *unit);
 	float tell(Source *source, void *unit);
 	double getDuration(Source *source, void *unit);
-	void queueData(Source *source, void *data, ALsizei length);
+	bool queueData(Source *source, void *data, ALsizei length);
 
 	bool play(const std::vector<love::audio::Source*> &sources);
 	void stop(const std::vector<love::audio::Source*> &sources);

+ 70 - 30
src/modules/audio/openal/Source.cpp

@@ -390,7 +390,6 @@ bool Source::update()
 				bufferedBytes -= size;
 				unusedBufferPush(buffers[i]);
 			}
-			
 			return !isFinished();
 		}
 	}
@@ -442,12 +441,6 @@ float Source::getVolume() const
 
 void Source::seekAtomic(float offset, void *unit)
 {
-	bool wasPlaying = isPlaying();
-
-	// To drain all buffers
-	if (valid && type == TYPE_STREAM)
-		stopAtomic();
-
 	switch (*((Source::Unit *) unit))
 	{
 	case Source::UNIT_SAMPLES:
@@ -460,17 +453,58 @@ void Source::seekAtomic(float offset, void *unit)
 		offsetSamples = offset * sampleRate;
 		break;
 	}
-
-	if (type == TYPE_STREAM)
-		decoder->seek(offsetSeconds);
-	else if (valid) // Playing static or queue
+	
+	switch (type)
 	{
-		alSourcef(source, AL_SAMPLE_OFFSET, offsetSamples);
-		offsetSamples = offsetSeconds = 0;
-	}
+		case TYPE_STATIC:
+			alSourcef(source, AL_SAMPLE_OFFSET, offsetSamples);
+			offsetSamples = offsetSeconds = 0;
+			break;
+		case TYPE_STREAM:
+		{
+			bool wasPlaying = isPlaying();
 
-	if (wasPlaying && type == TYPE_STREAM)
-		playAtomic(source);
+			// To drain all buffers
+			if (valid)
+				stopAtomic();
+			
+			decoder->seek(offsetSeconds);
+
+			if (wasPlaying)
+				playAtomic(source);
+				
+			break;
+		}
+		case TYPE_QUEUE:
+			if (valid)
+			{
+				alSourcef(source, AL_SAMPLE_OFFSET, offsetSamples);
+				offsetSamples = offsetSeconds = 0;
+			}
+			else
+			{
+				ALint size;
+				ALuint buffer = unusedBufferPeek();
+				
+				//emulate AL behavior, discarding buffer once playback head is past one
+				while (buffer != AL_NONE)
+				{
+					alGetBufferi(buffer, AL_SIZE, &size);
+					
+					if (offsetSamples < size / (bitDepth / 8 * channels))
+						break;
+
+					unusedBufferPop();
+					buffer = unusedBufferPeek();
+					bufferedBytes -= size;
+					offsetSamples -= size / (bitDepth / 8 * channels);
+				}
+				if (buffer == AL_NONE)
+					offsetSamples = 0;
+				offsetSeconds = offsetSamples / sampleRate;
+			}
+			break;
+	}
 }
 
 void Source::seek(float offset, Source::Unit unit)
@@ -673,7 +707,7 @@ bool Source::isLooping() const
 	return looping;
 }
 
-void Source::queueData(void *data, int length, int dataSampleRate, int dataBitDepth, int dataChannels)
+bool Source::queueData(void *data, int length, int dataSampleRate, int dataBitDepth, int dataChannels)
 {
 	if (type != TYPE_QUEUE)
 		throw QueueTypeMismatchException();
@@ -688,41 +722,39 @@ void Source::queueData(void *data, int length, int dataSampleRate, int dataBitDe
 		throw QueueMalformedLengthException(bitDepth / 8 * channels);
 	
 	if (length > 0)
-		pool->queueData(this, data, (ALsizei)length);
+		return pool->queueData(this, data, (ALsizei)length);
 }
 
-void Source::queueDataAtomic(void *data, ALsizei length)
+bool Source::queueDataAtomic(void *data, ALsizei length)
 {
 	if (valid)
 	{
 		ALuint buffer = unusedBufferPeek();
 		if (buffer == AL_NONE)
-			return; //FIXME: out of buffer space handling?
+			return false;
 			
 		alBufferData(buffer, getFormat(channels, bitDepth), data, length, sampleRate);
 		alSourceQueueBuffers(source, 1, &buffer);
 		unusedBufferPop();
 	}
-	else //in not valid state, queue stack is "reversed"
+	else
 	{
-		//remaining buffers are stored beyond the tip of unused stack
-		if (unusedBufferTop == MAX_BUFFERS - 1)
-			return;
+		//remaining buffers are stored beyond the tail of unused stack
+		if (unusedBufferTop >= (int)MAX_BUFFERS - 1)
+			return false;
 		
-		ALuint buffer = unusedBuffers[++unusedBufferTop];
+		ALuint buffer = unusedBuffers[unusedBufferTop + 1];
 		alBufferData(buffer, getFormat(channels, bitDepth), data, length, sampleRate);
 		//new buffer must go last, so it goes to the base of the stack
-		for (unsigned int i = unusedBufferTop; i > 0; i--)
-			unusedBuffers[i] = unusedBuffers[i - 1];
-		unusedBuffers[0] = buffer;
+		unusedBufferQueue(buffer);
 	}
 	bufferedBytes += length;
+	return true;
 }
 
 bool Source::isQueueable() const
 {
-	//in not valid state, queue stack is "reversed"
-	if (type != TYPE_QUEUE || (valid && unusedBufferTop < 0) || (!valid && unusedBufferTop == MAX_BUFFERS - 1))
+	if (type != TYPE_QUEUE || (valid && unusedBufferTop < 0) || (!valid && unusedBufferTop >= (int)MAX_BUFFERS - 1))
 		return false;
 	return true;
 }
@@ -811,6 +843,7 @@ void Source::teardownAtomic()
 				alSourceUnqueueBuffers(source, 1, &buffer);
 				unusedBufferPush(buffer);
 			}
+			
 			// put unused buffers at the end of stack
 			for (unsigned int i = 0; i < unused; i++)
 				unusedBuffers[unusedBufferTop + 1 + i] = buffers[i];
@@ -1013,6 +1046,13 @@ void Source::unusedBufferPush(ALuint buffer)
 	unusedBuffers[++unusedBufferTop] = buffer;
 }
 
+void Source::unusedBufferQueue(ALuint buffer)
+{
+	for (unsigned int i = ++unusedBufferTop; i > 0; i--)
+		unusedBuffers[i] = unusedBuffers[i - 1];
+	unusedBuffers[0] = buffer;
+}
+
 int Source::streamAtomic(ALuint buffer, love::sound::Decoder *d)
 {
 	// Get more sound data.

+ 3 - 2
src/modules/audio/openal/Source.h

@@ -132,8 +132,8 @@ public:
 	virtual int getChannels() const;
 
 	virtual bool isQueueable() const;
-	virtual void queueData(void *data, int length, int dataSampleRate, int dataBitDepth, int dataChannels);
-	virtual void queueDataAtomic(void *data, ALsizei length);
+	virtual bool queueData(void *data, int length, int dataSampleRate, int dataBitDepth, int dataChannels);
+	virtual bool queueDataAtomic(void *data, ALsizei length);
 
 	void prepareAtomic();
 	void teardownAtomic();
@@ -167,6 +167,7 @@ private:
 	ALuint unusedBufferPeek();
 	ALuint *unusedBufferPop();
 	void unusedBufferPush(ALuint buffer);
+	void unusedBufferQueue(ALuint buffer);
 	
 	Pool *pool;
 	ALuint source;

+ 6 - 4
src/modules/audio/wrap_Audio.cpp

@@ -47,18 +47,20 @@ int w_newSource(lua_State *L)
 	Source *t = 0;
 	
 	if (lua_isnumber(L, 1) && lua_isnumber(L, 2) && lua_isnumber(L, 3))
-		t = instance()->newSource((int)lua_tonumber(L, 1), (int)lua_tonumber(L, 2), (int)lua_tonumber(L, 3));
+		luax_catchexcept(L, [&]() {
+			t = instance()->newSource((int)lua_tonumber(L, 1), (int)lua_tonumber(L, 2), (int)lua_tonumber(L, 3));
+		});
 	else
 	{
-		if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_ID) || luax_istype(L, 1, FILESYSTEM_FILE_DATA_ID))
-			luax_convobj(L, 1, "sound", "newDecoder");
-
 		Source::Type stype = Source::TYPE_STREAM;
 
 		const char *stypestr = lua_isnoneornil(L, 2) ? 0 : lua_tostring(L, 2);
 		if (stypestr && !Source::getConstant(stypestr, stype))
 			return luaL_error(L, "Invalid source type: %s", stypestr);
 
+		if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_ID) || luax_istype(L, 1, FILESYSTEM_FILE_DATA_ID))
+			luax_convobj(L, 1, "sound", "newDecoder");
+
 		if (stype == Source::TYPE_STATIC && luax_istype(L, 1, SOUND_DECODER_ID))
 			luax_convobj(L, 1, "sound", "newSoundData");
 			

+ 5 - 2
src/modules/audio/wrap_Source.cpp

@@ -340,22 +340,25 @@ int w_Source_isQueueable(lua_State *L)
 int w_Source_queueData(lua_State *L)
 {
 	Source *t = luax_checksource(L, 1);
+	bool success;
+	
 	luax_catchexcept(L, [&]() {
 		if (luax_istype(L, 2, SOUND_SOUND_DATA_ID))
 		{
 			love::sound::SoundData *s = luax_totype<love::sound::SoundData>(L, 2, SOUND_SOUND_DATA_ID);
-			t->queueData(s->getData(), lua_isnumber(L, 3) ? (int)lua_tonumber(L, 3) : s->getSize(), s->getSampleRate(), s->getBitDepth(), s->getChannels());
+			success = t->queueData(s->getData(), lua_isnumber(L, 3) ? (int)lua_tonumber(L, 3) : s->getSize(), s->getSampleRate(), s->getBitDepth(), s->getChannels());
 		}
 		else if (lua_islightuserdata(L, 2))
 		{
 			if (lua_isnumber(L, 4) && lua_isnumber(L, 5) && lua_isnumber(L, 6))
-				t->queueData(lua_touserdata(L, 2), (int)lua_tonumber(L, 3), (int)lua_tonumber(L, 4), (int)lua_tonumber(L, 5), (int)lua_tonumber(L, 6));
+				success = t->queueData(lua_touserdata(L, 2), (int)lua_tonumber(L, 3), (int)lua_tonumber(L, 4), (int)lua_tonumber(L, 5), (int)lua_tonumber(L, 6));
 			else
 				return luaL_error(L, "No format specified.");
 		}
 		else
 			return luaL_error(L, "Invalid data type.");
 	});
+	luax_pushboolean(L, success);
 	return 1;
 }