Browse Source

Effects re-made to operate on literal names instead of numerical indices.

Added buffer count parameter to queueable source constructor.
Added getEffectsList function.
Internal cleanup.

--HG--
branch : minor
raidho36 8 years ago
parent
commit
157d94fae3

+ 14 - 7
src/modules/audio/Audio.h

@@ -77,7 +77,7 @@ public:
 
 
 	virtual Source *newSource(love::sound::Decoder *decoder) = 0;
 	virtual Source *newSource(love::sound::Decoder *decoder) = 0;
 	virtual Source *newSource(love::sound::SoundData *soundData) = 0;
 	virtual Source *newSource(love::sound::SoundData *soundData) = 0;
-	virtual Source *newSource(int sampleRate, int bitDepth, int channels) = 0;
+	virtual Source *newSource(int sampleRate, int bitDepth, int channels, int buffers) = 0;
 
 
 	/**
 	/**
 	 * Gets the current number of simultaneous playing sources.
 	 * Gets the current number of simultaneous playing sources.
@@ -210,26 +210,33 @@ public:
 
 
 	/**
 	/**
 	 * Sets scene EFX effect.
 	 * Sets scene EFX effect.
-	 * @param slot Slot to put effect into.
+	 * @param name Effect name to use.
 	 * @param fxparams Effect description table.
 	 * @param fxparams Effect description table.
 	 * @return true if successful, false otherwise.
 	 * @return true if successful, false otherwise.
 	 */
 	 */
-	virtual bool setSceneEffect(int slot, std::map<Effect::Parameter, float> &params) = 0;
+	virtual bool setEffect(const char *name, std::map<Effect::Parameter, float> &params) = 0;
 
 
 	/**
 	/**
 	 * Removes scene EFX effect.
 	 * Removes scene EFX effect.
-	 * @param slot Effect slot to clear.
+	 * @param name Effect name to clear.
 	 * @return true if successful, false otherwise.
 	 * @return true if successful, false otherwise.
 	 */
 	 */
-	virtual bool setSceneEffect(int slot) = 0;
+	virtual bool unsetEffect(const char *name) = 0;
 
 
 	/**
 	/**
 	 * Gets scene EFX effect.
 	 * Gets scene EFX effect.
-	 * @param slot Slot from which to get effect.
+	 * @param name Effect name to get data from.
 	 * @param fxparams Effect description table.
 	 * @param fxparams Effect description table.
 	 * @return true if effect was present, false otherwise.
 	 * @return true if effect was present, false otherwise.
 	 */
 	 */
-	virtual bool getSceneEffect(int slot, std::map<Effect::Parameter, float> &params) = 0;
+	virtual bool getEffect(const char *name, std::map<Effect::Parameter, float> &params) = 0;
+
+	/**
+	 * Gets list of EFX effect names.
+	 * @param list List of EFX names to fill.
+	 * @return true if effect was present, false otherwise.
+	 */
+	virtual bool getEffectsList(std::vector<std::string> &list) = 0;
 
 
 	/**
 	/**
 	 * Gets maximum number of scene EFX effects.
 	 * Gets maximum number of scene EFX effects.

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

@@ -113,10 +113,11 @@ public:
 	virtual bool setFilter() = 0;
 	virtual bool setFilter() = 0;
 	virtual bool getFilter(std::map<Filter::Parameter, float> &params) = 0;
 	virtual bool getFilter(std::map<Filter::Parameter, float> &params) = 0;
 
 
-	virtual bool setSceneEffect(int slot, int effect) = 0;
-	virtual bool setSceneEffect(int slot, int effect, const std::map<Filter::Parameter, float> &params) = 0;
-	virtual bool setSceneEffect(int slot) = 0;
-	virtual bool getSceneEffect(int slot, int &effect, std::map<Filter::Parameter, float> &params) = 0;
+	virtual bool setEffect(const char *effect) = 0;
+	virtual bool setEffect(const char *effect, const std::map<Filter::Parameter, float> &params) = 0;
+	virtual bool unsetEffect(const char *effect) = 0;
+	virtual bool getEffect(const char *effect, std::map<Filter::Parameter, float> &params) = 0;
+	virtual bool getEffectsList(std::vector<std::string> &list) = 0;
 
 
 	virtual int getFreeBufferCount() const = 0;
 	virtual int getFreeBufferCount() const = 0;
 	virtual bool queue(void *data, size_t length, int dataSampleRate, int dataBitDepth, int dataChannels) = 0;
 	virtual bool queue(void *data, size_t length, int dataSampleRate, int dataBitDepth, int dataChannels) = 0;

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

@@ -51,7 +51,7 @@ love::audio::Source *Audio::newSource(love::sound::SoundData *)
 	return new Source();
 	return new Source();
 }
 }
 
 
-love::audio::Source *Audio::newSource(int, int, int)
+love::audio::Source *Audio::newSource(int, int, int, int)
 {
 {
 	return new Source();
 	return new Source();
 }
 }
@@ -168,17 +168,22 @@ void Audio::setDistanceModel(DistanceModel distanceModel)
 	this->distanceModel = distanceModel;
 	this->distanceModel = distanceModel;
 }
 }
 
 
-bool Audio::setSceneEffect(int, std::map<Effect::Parameter, float> &)
+bool Audio::setEffect(const char *, std::map<Effect::Parameter, float> &)
 {
 {
 	return false;
 	return false;
 }
 }
 
 
-bool Audio::setSceneEffect(int)
+bool Audio::unsetEffect(const char *)
 {
 {
 	return false;
 	return false;
 }
 }
 
 
-bool Audio::getSceneEffect(int, std::map<Effect::Parameter, float> &)
+bool Audio::getEffect(const char *, std::map<Effect::Parameter, float> &)
+{
+	return false;
+}
+
+bool Audio::getEffectsList(std::vector<std::string> &list)
 {
 {
 	return false;
 	return false;
 }
 }

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

@@ -47,7 +47,7 @@ public:
 	// Implements Audio.
 	// Implements Audio.
 	love::audio::Source *newSource(love::sound::Decoder *decoder);
 	love::audio::Source *newSource(love::sound::Decoder *decoder);
 	love::audio::Source *newSource(love::sound::SoundData *soundData);
 	love::audio::Source *newSource(love::sound::SoundData *soundData);
-	love::audio::Source *newSource(int sampleRate, int bitDepth, int channels);
+	love::audio::Source *newSource(int sampleRate, int bitDepth, int channels, int buffers);
 	int getSourceCount() const;
 	int getSourceCount() const;
 	int getMaxSources() const;
 	int getMaxSources() const;
 	bool play(love::audio::Source *source);
 	bool play(love::audio::Source *source);
@@ -78,9 +78,10 @@ public:
 	DistanceModel getDistanceModel() const;
 	DistanceModel getDistanceModel() const;
 	void setDistanceModel(DistanceModel distanceModel);
 	void setDistanceModel(DistanceModel distanceModel);
 
 
-	bool setSceneEffect(int slot, std::map<Effect::Parameter, float> &params);
-	bool setSceneEffect(int slot);
-	bool getSceneEffect(int slot, std::map<Effect::Parameter, float> &params);
+	bool setEffect(const char *, std::map<Effect::Parameter, float> &params);
+	bool unsetEffect(const char *);
+	bool getEffect(const char *, std::map<Effect::Parameter, float> &params);
+	bool getEffectsList(std::vector<std::string> &list);
 	int getMaxSceneEffects() const;
 	int getMaxSceneEffects() const;
 	int getMaxSourceEffects() const;
 	int getMaxSourceEffects() const;
 	bool isEFXsupported() const;
 	bool isEFXsupported() const;

+ 9 - 4
src/modules/audio/null/Source.cpp

@@ -254,22 +254,27 @@ bool Source::getFilter(std::map<Filter::Parameter, float> &)
 	return false;
 	return false;
 }
 }
 
 
-bool Source::setSceneEffect(int, int)
+bool Source::setEffect(const char *)
 {
 {
 	return false;
 	return false;
 }
 }
 
 
-bool Source::setSceneEffect(int, int, const std::map<Filter::Parameter, float> &)
+bool Source::setEffect(const char *, const std::map<Filter::Parameter, float> &)
 {
 {
 	return false;
 	return false;
 }
 }
 
 
-bool Source::setSceneEffect(int)
+bool Source::unsetEffect(const char *)
 {
 {
 	return false;
 	return false;
 }
 }
 
 
-bool Source::getSceneEffect(int, int &, std::map<Filter::Parameter, float> &)
+bool Source::getEffect(const char *, std::map<Filter::Parameter, float> &)
+{
+	return false;
+}
+
+bool Source::getEffectsList(std::vector<std::string> &)
 {
 {
 	return false;
 	return false;
 }
 }

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

@@ -86,10 +86,11 @@ public:
 	virtual bool setFilter();
 	virtual bool setFilter();
 	virtual bool getFilter(std::map<Filter::Parameter, float> &params);
 	virtual bool getFilter(std::map<Filter::Parameter, float> &params);
 
 
-	virtual bool setSceneEffect(int slot, int effect);
-	virtual bool setSceneEffect(int slot, int effect, const std::map<Filter::Parameter, float> &params);
-	virtual bool setSceneEffect(int slot);
-	virtual bool getSceneEffect(int slot, int &effect, std::map<Filter::Parameter, float> &params);
+	virtual bool setEffect(const char *effect);
+	virtual bool setEffect(const char *effect, const std::map<Filter::Parameter, float> &params);
+	virtual bool unsetEffect(const char *effect);
+	virtual bool getEffect(const char *effect, std::map<Filter::Parameter, float> &params);
+	virtual bool getEffectsList(std::vector<std::string> &list);
 
 
 private:
 private:
 
 

+ 72 - 44
src/modules/audio/openal/Audio.cpp

@@ -129,11 +129,7 @@ Audio::Audio()
 			ALuint slot;
 			ALuint slot;
 			alGenAuxiliaryEffectSlots(1, &slot);
 			alGenAuxiliaryEffectSlots(1, &slot);
 			if (alGetError() == AL_NO_ERROR)
 			if (alGetError() == AL_NO_ERROR)
-			{
-				effectIndex[slot] = effectSlots.size();
-				effectSlots.push_back(slot);
-				effects.push_back(nullptr);
-			}
+				slotlist.push(slot);
 			else
 			else
 			{
 			{
 				MAX_SCENE_EFFECTS = i;
 				MAX_SCENE_EFFECTS = i;
@@ -159,8 +155,11 @@ Audio::Audio()
 #ifdef ALC_EXT_EFX
 #ifdef ALC_EXT_EFX
 		if (alDeleteAuxiliaryEffectSlots)
 		if (alDeleteAuxiliaryEffectSlots)
 		{
 		{
-			for (auto slot : effectSlots)
-				alDeleteAuxiliaryEffectSlots(1, &slot);
+			while (!slotlist.empty())
+			{
+				alDeleteAuxiliaryEffectSlots(1, &slotlist.top());
+				slotlist.pop();
+			}
 		}
 		}
 #endif
 #endif
 
 
@@ -186,16 +185,19 @@ Audio::~Audio()
 		delete c;
 		delete c;
 
 
 #ifdef ALC_EXT_EFX
 #ifdef ALC_EXT_EFX
-	for (auto e : effects)
+	for (auto e : effectmap)
 	{
 	{
-		if (e != nullptr)
-			delete e;
+		delete e.second.effect;
+		slotlist.push(e.second.slot);
 	}
 	}
 
 
 	if (alDeleteAuxiliaryEffectSlots)
 	if (alDeleteAuxiliaryEffectSlots)
 	{
 	{
-		for (auto slot : effectSlots)
-			alDeleteAuxiliaryEffectSlots(1, &slot);
+		while (!slotlist.empty())
+		{
+			alDeleteAuxiliaryEffectSlots(1, &slotlist.top());
+			slotlist.pop();
+		}
 	}
 	}
 #endif
 #endif
 	alcMakeContextCurrent(nullptr);
 	alcMakeContextCurrent(nullptr);
@@ -218,9 +220,9 @@ love::audio::Source *Audio::newSource(love::sound::SoundData *soundData)
 	return new Source(pool, soundData);
 	return new Source(pool, soundData);
 }
 }
 
 
-love::audio::Source *Audio::newSource(int sampleRate, int bitDepth, int channels)
+love::audio::Source *Audio::newSource(int sampleRate, int bitDepth, int channels, int buffers)
 {
 {
-	return new Source(pool, sampleRate, bitDepth, channels);
+	return new Source(pool, sampleRate, bitDepth, channels, buffers);
 }
 }
 
 
 int Audio::getSourceCount() const
 int Audio::getSourceCount() const
@@ -456,27 +458,44 @@ const std::vector<love::audio::RecordingDevice*> &Audio::getRecordingDevices()
 	return capture;
 	return capture;
 }
 }
 
 
-bool Audio::setSceneEffect(int slot, std::map<Effect::Parameter, float> &params)
+bool Audio::setEffect(const char *name, std::map<Effect::Parameter, float> &params)
 {
 {
-	if (slot < 0 || slot >= MAX_SCENE_EFFECTS)
-		return false;
+	Effect *effect;
+	ALuint slot;
 
 
-	if (!effects[slot])
-		effects[slot] = new Effect();
+	auto iter = effectmap.find(name);
+	if (iter == effectmap.end())
+	{
+		//new effect needed but no more slots
+		if (effectmap.size() >= (unsigned int)MAX_SCENE_EFFECTS)
+			return false;
+
+		effect = new Effect();
+		slot = slotlist.top();
+		slotlist.pop();
 
 
-	bool result = effects[slot]->setParams(params);
+		effectmap[name] = {effect, slot};
+	}
+	else
+	{
+		effect = iter->second.effect;
+		slot = iter->second.slot;
+	}
+
+	bool result = effect->setParams(params);
 
 
 #ifdef ALC_EXT_EFX
 #ifdef ALC_EXT_EFX
 	if (alAuxiliaryEffectSloti)
 	if (alAuxiliaryEffectSloti)
 	{
 	{
 		if (result)
 		if (result)
 		{
 		{
-			if (params.find(Effect::EFFECT_VOLUME) != params.end())
-				alAuxiliaryEffectSlotf(effectSlots[slot], AL_EFFECTSLOT_GAIN, params[Effect::EFFECT_VOLUME]);
-			alAuxiliaryEffectSloti(effectSlots[slot], AL_EFFECTSLOT_EFFECT, effects[slot]->getEffect());
+			auto iter = params.find(Effect::EFFECT_VOLUME);
+			if (iter != params.end())
+				alAuxiliaryEffectSlotf(slot, AL_EFFECTSLOT_GAIN, iter->second);
+			alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_EFFECT, effect->getEffect());
 		}
 		}
 		else
 		else
-			alAuxiliaryEffectSloti(effectSlots[slot], AL_EFFECTSLOT_EFFECT, AL_EFFECT_NULL);
+			alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_EFFECT, AL_EFFECT_NULL);
 		alGetError();
 		alGetError();
 	}
 	}
 #endif
 #endif
@@ -484,33 +503,45 @@ bool Audio::setSceneEffect(int slot, std::map<Effect::Parameter, float> &params)
 	return result;
 	return result;
 }
 }
 
 
-bool Audio::setSceneEffect(int slot)
+bool Audio::unsetEffect(const char *name)
 {
 {
-	if (slot < 0 || slot >= MAX_SCENE_EFFECTS)
+	auto iter = effectmap.find(name);
+	if (iter == effectmap.end())
 		return false;
 		return false;
 
 
-	if (effects[slot])
-		delete effects[slot];
-
-	effects[slot] = nullptr;
+	Effect *effect = iter->second.effect;
+	ALuint slot = iter->second.slot;
 
 
 #ifdef ALC_EXT_EFX
 #ifdef ALC_EXT_EFX
 	if (alAuxiliaryEffectSloti)
 	if (alAuxiliaryEffectSloti)
-		alAuxiliaryEffectSloti(effectSlots[slot], AL_EFFECTSLOT_EFFECT, AL_EFFECT_NULL);
+		alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_EFFECT, AL_EFFECT_NULL);
 #endif
 #endif
 
 
+	delete effect;
+	effectmap.erase(iter);
+	slotlist.push(slot);
 	return true;
 	return true;
 }
 }
 
 
-bool Audio::getSceneEffect(int slot, std::map<Effect::Parameter, float> &params)
+bool Audio::getEffect(const char *name, std::map<Effect::Parameter, float> &params)
 {
 {
-	if (slot < 0 || slot >= MAX_SCENE_EFFECTS)
+	auto iter = effectmap.find(name);
+	if (iter == effectmap.end())
 		return false;
 		return false;
 
 
-	if (!effects[slot])
+	params = iter->second.effect->getParams();
+
+	return true;
+}
+
+bool Audio::getEffectsList(std::vector<std::string> &list)
+{
+	if (effectmap.empty())
 		return false;
 		return false;
 
 
-	params = effects[slot]->getParams();
+	list.reserve(effectmap.size());
+	for (auto i : effectmap)
+		list.push_back(i.first);
 
 
 	return true;
 	return true;
 }
 }
@@ -534,17 +565,14 @@ bool Audio::isEFXsupported() const
 #endif
 #endif
 }
 }
 
 
-ALuint Audio::getSceneEffectID(int slot)
+bool Audio::getEffectID(const char *name, ALuint &id)
 {
 {
-	if (slot < 0 || slot >= MAX_SCENE_EFFECTS)
-		return effectSlots[0];
-
-	return effectSlots[slot];
-}
+	auto iter = effectmap.find(name);
+	if (iter == effectmap.end())
+		return false;
 
 
-int Audio::getSceneEffectIndex(ALuint effect)
-{
-	return effectIndex[effect];
+	id = iter->second.slot;
+	return true;
 }
 }
 
 
 #ifdef ALC_EXT_EFX
 #ifdef ALC_EXT_EFX

+ 16 - 11
src/modules/audio/openal/Audio.h

@@ -25,6 +25,7 @@
 #include <queue>
 #include <queue>
 #include <map>
 #include <map>
 #include <vector>
 #include <vector>
+#include <stack>
 #include <cmath>
 #include <cmath>
 
 
 // LOVE
 // LOVE
@@ -83,7 +84,7 @@ public:
 	// Implements Audio.
 	// Implements Audio.
 	love::audio::Source *newSource(love::sound::Decoder *decoder);
 	love::audio::Source *newSource(love::sound::Decoder *decoder);
 	love::audio::Source *newSource(love::sound::SoundData *soundData);
 	love::audio::Source *newSource(love::sound::SoundData *soundData);
-	love::audio::Source *newSource(int sampleRate, int bitDepth, int channels);
+	love::audio::Source *newSource(int sampleRate, int bitDepth, int channels, int buffers);
 	int getSourceCount() const;
 	int getSourceCount() const;
 	int getMaxSources() const;
 	int getMaxSources() const;
 	bool play(love::audio::Source *source);
 	bool play(love::audio::Source *source);
@@ -114,15 +115,15 @@ public:
 	DistanceModel getDistanceModel() const;
 	DistanceModel getDistanceModel() const;
 	void setDistanceModel(DistanceModel distanceModel);
 	void setDistanceModel(DistanceModel distanceModel);
 
 
-	bool setSceneEffect(int slot, std::map<Effect::Parameter, float> &params);
-	bool setSceneEffect(int slot);
-	bool getSceneEffect(int slot, std::map<Effect::Parameter, float> &params);
+	bool setEffect(const char *name, std::map<Effect::Parameter, float> &params);
+	bool unsetEffect(const char *name);
+	bool getEffect(const char *name, std::map<Effect::Parameter, float> &params);
+	bool getEffectsList(std::vector<std::string> &list);
 	int getMaxSceneEffects() const;
 	int getMaxSceneEffects() const;
 	int getMaxSourceEffects() const;
 	int getMaxSourceEffects() const;
 	bool isEFXsupported() const;
 	bool isEFXsupported() const;
 
 
-	ALuint getSceneEffectID(int slot);
-	int getSceneEffectIndex(ALuint effect);
+	bool getEffectID(const char *name, ALuint &id);
 
 
 private:
 private:
 	void initializeEFX();
 	void initializeEFX();
@@ -136,11 +137,15 @@ private:
 	ALCcontext *context;
 	ALCcontext *context;
 
 
 	// The OpenAL effects
 	// The OpenAL effects
-	std::vector<Effect*> effects;
-	std::vector<ALuint> effectSlots;
-	std::map<ALuint, int> effectIndex;
-	int MAX_SCENE_EFFECTS = 16;
-	int MAX_SOURCE_EFFECTS = 16;
+	struct effectmapStorage
+	{
+		Effect *effect;
+		ALuint slot;
+	};
+	std::map<std::string, struct effectmapStorage> effectmap;
+	std::stack<ALuint> slotlist;
+	int MAX_SCENE_EFFECTS = 64;
+	int MAX_SOURCE_EFFECTS = 64;
 
 
 	// The Pool.
 	// The Pool.
 	Pool *pool;
 	Pool *pool;

+ 213 - 140
src/modules/audio/openal/Source.cpp

@@ -124,8 +124,6 @@ Source::Source(Pool *pool, love::sound::SoundData *soundData)
 	, sampleRate(soundData->getSampleRate())
 	, sampleRate(soundData->getSampleRate())
 	, channels(soundData->getChannels())
 	, channels(soundData->getChannels())
 	, bitDepth(soundData->getBitDepth())
 	, bitDepth(soundData->getBitDepth())
-	, sendfilters(audiomodule()->getMaxSourceEffects(), nullptr)
-	, sendtargets(audiomodule()->getMaxSourceEffects(), AL_EFFECTSLOT_NULL)
 {
 {
 	ALenum fmt = Audio::getFormat(soundData->getBitDepth(), soundData->getChannels());
 	ALenum fmt = Audio::getFormat(soundData->getBitDepth(), soundData->getChannels());
 	if (fmt == AL_NONE)
 	if (fmt == AL_NONE)
@@ -138,6 +136,9 @@ Source::Source(Pool *pool, love::sound::SoundData *soundData)
 	setFloatv(position, z);
 	setFloatv(position, z);
 	setFloatv(velocity, z);
 	setFloatv(velocity, z);
 	setFloatv(direction, z);
 	setFloatv(direction, z);
+
+	for (unsigned int i = 0; i < (unsigned int)audiomodule()->getMaxSourceEffects(); i++)
+		slotlist.push(i);
 }
 }
 
 
 Source::Source(Pool *pool, love::sound::Decoder *decoder)
 Source::Source(Pool *pool, love::sound::Decoder *decoder)
@@ -147,46 +148,72 @@ Source::Source(Pool *pool, love::sound::Decoder *decoder)
 	, channels(decoder->getChannels())
 	, channels(decoder->getChannels())
 	, bitDepth(decoder->getBitDepth())
 	, bitDepth(decoder->getBitDepth())
 	, decoder(decoder)
 	, decoder(decoder)
-	, unusedBufferTop(MAX_BUFFERS - 1)
-	, sendfilters(audiomodule()->getMaxSourceEffects(), nullptr)
-	, sendtargets(audiomodule()->getMaxSourceEffects(), AL_EFFECTSLOT_NULL)
+	, buffers(DEFAULT_BUFFERS)
 {
 {
 	if (Audio::getFormat(decoder->getBitDepth(), decoder->getChannels()) == AL_NONE)
 	if (Audio::getFormat(decoder->getBitDepth(), decoder->getChannels()) == AL_NONE)
 		throw InvalidFormatException(decoder->getChannels(), decoder->getBitDepth());
 		throw InvalidFormatException(decoder->getChannels(), decoder->getBitDepth());
 
 
-	alGenBuffers(MAX_BUFFERS, streamBuffers);
-	for (unsigned int i = 0; i < MAX_BUFFERS; i++)
-		unusedBuffers[i] = streamBuffers[i];
+	for (int i = 0; i < buffers; i++)
+	{
+		ALuint buf;
+		alGenBuffers(1, &buf);
+		if (alGetError() == AL_NO_ERROR)
+			unusedBuffers.push(buf);
+		else
+		{
+			buffers = i;
+			break;
+		}
+	}
 
 
 	float z[3] = {0, 0, 0};
 	float z[3] = {0, 0, 0};
 
 
 	setFloatv(position, z);
 	setFloatv(position, z);
 	setFloatv(velocity, z);
 	setFloatv(velocity, z);
 	setFloatv(direction, z);
 	setFloatv(direction, z);
+
+	for (unsigned int i = 0; i < (unsigned int)audiomodule()->getMaxSourceEffects(); i++)
+		slotlist.push(i);
 }
 }
 
 
-Source::Source(Pool *pool, int sampleRate, int bitDepth, int channels)
+Source::Source(Pool *pool, int sampleRate, int bitDepth, int channels, int buffers)
 	: love::audio::Source(Source::TYPE_QUEUE)
 	: love::audio::Source(Source::TYPE_QUEUE)
 	, pool(pool)
 	, pool(pool)
 	, sampleRate(sampleRate)
 	, sampleRate(sampleRate)
 	, channels(channels)
 	, channels(channels)
 	, bitDepth(bitDepth)
 	, bitDepth(bitDepth)
-	, sendfilters(audiomodule()->getMaxSourceEffects(), nullptr)
-	, sendtargets(audiomodule()->getMaxSourceEffects(), AL_EFFECTSLOT_NULL)
+	, buffers(buffers)
 {
 {
 	ALenum fmt = Audio::getFormat(bitDepth, channels);
 	ALenum fmt = Audio::getFormat(bitDepth, channels);
 	if (fmt == AL_NONE)
 	if (fmt == AL_NONE)
 		throw InvalidFormatException(channels, bitDepth);
 		throw InvalidFormatException(channels, bitDepth);
 
 
-	alGenBuffers(MAX_BUFFERS, streamBuffers);
-	for (unsigned int i = 0; i < MAX_BUFFERS; i++)
-		unusedBuffers[i] = streamBuffers[i];
+	if (buffers < 1)
+		buffers = DEFAULT_BUFFERS;
+	if (buffers > MAX_BUFFERS)
+		buffers = MAX_BUFFERS;
+
+	for (int i = 0; i < buffers; i++)
+	{
+		ALuint buf;
+		alGenBuffers(1, &buf);
+		if (alGetError() == AL_NO_ERROR)
+			unusedBuffers.push(buf);
+		else
+		{
+			buffers = i;
+			break;
+		}
+	}
 
 
 	float z[3] = {0, 0, 0};
 	float z[3] = {0, 0, 0};
 
 
 	setFloatv(position, z);
 	setFloatv(position, z);
 	setFloatv(velocity, z);
 	setFloatv(velocity, z);
 	setFloatv(direction, z);
 	setFloatv(direction, z);
+
+	for (unsigned int i = 0; i < (unsigned int)audiomodule()->getMaxSourceEffects(); i++)
+		slotlist.push(i);
 }
 }
 
 
 Source::Source(const Source &s)
 Source::Source(const Source &s)
@@ -211,9 +238,7 @@ Source::Source(const Source &s)
 	, bitDepth(s.bitDepth)
 	, bitDepth(s.bitDepth)
 	, decoder(nullptr)
 	, decoder(nullptr)
 	, toLoop(0)
 	, toLoop(0)
-	, unusedBufferTop(s.sourceType == TYPE_STREAM ? MAX_BUFFERS - 1 : -1)
-	, sendfilters(s.sendfilters)
-	, sendtargets(s.sendtargets)
+	, buffers(s.buffers)
 {
 {
 	if (sourceType == TYPE_STREAM)
 	if (sourceType == TYPE_STREAM)
 	{
 	{
@@ -222,16 +247,43 @@ Source::Source(const Source &s)
 	}
 	}
 	if (sourceType != TYPE_STATIC)
 	if (sourceType != TYPE_STATIC)
 	{
 	{
-		alGenBuffers(MAX_BUFFERS, streamBuffers);
-		for (unsigned int i = 0; i < MAX_BUFFERS; i++)
-			unusedBuffers[i] = streamBuffers[i];
+		for (int i = 0; i < buffers; i++)
+		{
+			ALuint buf;
+			alGenBuffers(1, &buf);
+			if (alGetError() == AL_NO_ERROR)
+				unusedBuffers.push(buf);
+			else
+			{
+				buffers = i;
+				break;
+			}
+		}
 	}
 	}
+
 	if (s.directfilter)
 	if (s.directfilter)
 		directfilter = s.directfilter->clone();
 		directfilter = s.directfilter->clone();
 
 
+	for (auto e : s.effectmap)
+		effectmap[e.first] = { e.second.filter ? e.second.filter->clone() : nullptr, e.second.slot, e.second.target };
+
 	setFloatv(position, s.position);
 	setFloatv(position, s.position);
 	setFloatv(velocity, s.velocity);
 	setFloatv(velocity, s.velocity);
 	setFloatv(direction, s.direction);
 	setFloatv(direction, s.direction);
+
+	for (unsigned int i = 0; i < (unsigned int)audiomodule()->getMaxSourceEffects(); i++)
+	{
+		// filter out already taken slots
+		bool push = true;
+		for (auto e : effectmap)
+		{
+			if (e.second.slot)
+				push = false;
+				break;
+		}
+		if (push)
+			slotlist.push(i);
+	}
 }
 }
 
 
 Source::~Source()
 Source::~Source()
@@ -239,15 +291,26 @@ Source::~Source()
 	stop();
 	stop();
 
 
 	if (sourceType != TYPE_STATIC)
 	if (sourceType != TYPE_STATIC)
-		alDeleteBuffers(MAX_BUFFERS, streamBuffers);
+	{
+		while (!streamBuffers.empty())
+		{
+			alDeleteBuffers(1, &streamBuffers.front());
+			streamBuffers.pop();
+		}
+		while (!unusedBuffers.empty())
+		{
+			alDeleteBuffers(1, &unusedBuffers.top());
+			unusedBuffers.pop();
+		}
+	}
 
 
 	if (directfilter)
 	if (directfilter)
 		delete directfilter;
 		delete directfilter;
 
 
-	for (auto sf : sendfilters)
+	for (auto e : effectmap)
 	{
 	{
-		if (sf != nullptr)
-			delete sf;
+		if (e.second.filter)
+			delete e.second.filter;
 	}
 	}
 }
 }
 
 
@@ -329,7 +392,7 @@ bool Source::update()
 			if (!isFinished())
 			if (!isFinished())
 			{
 			{
 				ALint processed;
 				ALint processed;
-				ALuint buffers[MAX_BUFFERS];
+				ALuint buffers[this->buffers];
 				float curOffsetSamples, curOffsetSecs, newOffsetSamples, newOffsetSecs;
 				float curOffsetSamples, curOffsetSecs, newOffsetSamples, newOffsetSecs;
 				int freq = decoder->getSampleRate();
 				int freq = decoder->getSampleRate();
 
 
@@ -346,12 +409,16 @@ bool Source::update()
 				offsetSeconds += (curOffsetSecs - newOffsetSecs);
 				offsetSeconds += (curOffsetSecs - newOffsetSecs);
 
 
 				for (unsigned int i = 0; i < (unsigned int)processed; i++)
 				for (unsigned int i = 0; i < (unsigned int)processed; i++)
-					unusedBufferPush(buffers[i]);
+					unusedBuffers.push(buffers[i]);
 
 
-				while (unusedBufferPeek() != AL_NONE)
+				while (!unusedBuffers.empty())
 				{
 				{
-					if(streamAtomic(unusedBufferPeek(), decoder.get()) > 0)
-						alSourceQueueBuffers(source, 1, unusedBufferPop());
+					auto b = unusedBuffers.top();
+					if (streamAtomic(b, decoder.get()) > 0)
+					{
+						alSourceQueueBuffers(source, 1, &b);
+						unusedBuffers.pop();
+					}
 					else
 					else
 						break;
 						break;
 				}
 				}
@@ -359,10 +426,10 @@ 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[this->buffers];
 
 
 			alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
 			alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
 			alSourceUnqueueBuffers(source, processed, buffers);
 			alSourceUnqueueBuffers(source, processed, buffers);
@@ -372,7 +439,7 @@ bool Source::update()
 				ALint size;
 				ALint size;
 				alGetBufferi(buffers[i], AL_SIZE, &size);
 				alGetBufferi(buffers[i], AL_SIZE, &size);
 				bufferedBytes -= size;
 				bufferedBytes -= size;
-				unusedBufferPush(buffers[i]);
+				unusedBuffers.push(buffers[i]);
 			}
 			}
 			return !isFinished();
 			return !isFinished();
 		}
 		}
@@ -475,23 +542,21 @@ void Source::seek(float offset, Source::Unit unit)
 			}
 			}
 			else
 			else
 			{
 			{
-				ALint size;
-				ALuint buffer = unusedBufferPeek();
-
 				//emulate AL behavior, discarding buffer once playback head is past one
 				//emulate AL behavior, discarding buffer once playback head is past one
-				while (buffer != AL_NONE)
+				while (!unusedBuffers.empty())
 				{
 				{
+					ALint size;
+					auto buffer = unusedBuffers.top();
 					alGetBufferi(buffer, AL_SIZE, &size);
 					alGetBufferi(buffer, AL_SIZE, &size);
 
 
 					if (offsetSamples < size / (bitDepth / 8 * channels))
 					if (offsetSamples < size / (bitDepth / 8 * channels))
 						break;
 						break;
 
 
-					unusedBufferPop();
-					buffer = unusedBufferPeek();
+					unusedBuffers.pop();
 					bufferedBytes -= size;
 					bufferedBytes -= size;
 					offsetSamples -= size / (bitDepth / 8 * channels);
 					offsetSamples -= size / (bitDepth / 8 * channels);
 				}
 				}
-				if (buffer == AL_NONE)
+				if (unusedBuffers.empty())
 					offsetSamples = 0;
 					offsetSamples = 0;
 				offsetSeconds = offsetSamples / sampleRate;
 				offsetSeconds = offsetSamples / sampleRate;
 			}
 			}
@@ -723,27 +788,18 @@ bool Source::queue(void *data, size_t length, int dataSampleRate, int dataBitDep
 
 
 	Lock l = pool->lock();
 	Lock l = pool->lock();
 
 
-	if (valid)
-	{
-		ALuint buffer = unusedBufferPeek();
-		if (buffer == AL_NONE)
-			return false;
+	if (unusedBuffers.empty())
+		return false;
+
+	auto buffer = unusedBuffers.top();
+	unusedBuffers.pop();
+	alBufferData(buffer, Audio::getFormat(bitDepth, channels), data, length, sampleRate);
+	bufferedBytes += length;
 
 
-		alBufferData(buffer, Audio::getFormat(bitDepth, channels), data, length, sampleRate);
+	if (valid)
 		alSourceQueueBuffers(source, 1, &buffer);
 		alSourceQueueBuffers(source, 1, &buffer);
-		unusedBufferPop();
-	}
 	else
 	else
-	{
-		ALuint buffer = unusedBufferPeekNext();
-		if (buffer == AL_NONE)
-			return false;
-
-		//stack acts as queue while stopped
-		alBufferData(buffer, Audio::getFormat(bitDepth, channels), data, length, sampleRate);
-		unusedBufferQueue(buffer);
-	}
-	bufferedBytes += length;
+		streamBuffers.push(buffer);
 
 
 	return true;
 	return true;
 }
 }
@@ -755,9 +811,9 @@ int Source::getFreeBufferCount() const
 		case TYPE_STATIC:
 		case TYPE_STATIC:
 			return 0;
 			return 0;
 		case TYPE_STREAM:
 		case TYPE_STREAM:
-			return unusedBufferTop + 1;
+			return unusedBuffers.size();
 		case TYPE_QUEUE:
 		case TYPE_QUEUE:
-			return valid ? unusedBufferTop + 1 : (int)MAX_BUFFERS - unusedBufferTop - 1;
+			return unusedBuffers.size();
 		case TYPE_MAX_ENUM:
 		case TYPE_MAX_ENUM:
 			return 0;
 			return 0;
 	}
 	}
@@ -777,12 +833,14 @@ void Source::prepareAtomic()
 			alSourcei(source, AL_BUFFER, staticBuffer->getBuffer());
 			alSourcei(source, AL_BUFFER, staticBuffer->getBuffer());
 			break;
 			break;
 		case TYPE_STREAM:
 		case TYPE_STREAM:
-			while (unusedBufferPeek() != AL_NONE)
+			while (!unusedBuffers.empty())
 			{
 			{
-				if(streamAtomic(unusedBufferPeek(), decoder.get()) == 0)
+				auto b = unusedBuffers.top();
+				if (streamAtomic(b, decoder.get()) == 0)
 					break;
 					break;
 
 
-				alSourceQueueBuffers(source, 1, unusedBufferPop());
+				alSourceQueueBuffers(source, 1, &b);
+				unusedBuffers.pop();
 
 
 				if (decoder->isFinished())
 				if (decoder->isFinished())
 					break;
 					break;
@@ -790,14 +848,11 @@ void Source::prepareAtomic()
 			break;
 			break;
 		case TYPE_QUEUE:
 		case TYPE_QUEUE:
 		{
 		{
-			int top = unusedBufferTop;
-			//when queue source is stopped, loaded buffers are stored in unused buffers stack
-			while (unusedBufferPeek() != AL_NONE)
-				alSourceQueueBuffers(source, 1, unusedBufferPop());
-			//construct a stack of unused buffers (beyond the end of stack)
-			for (unsigned int i = top + 1; i < MAX_BUFFERS; i++)
-				unusedBufferPush(unusedBuffers[i]);
-
+			while (!streamBuffers.empty())
+			{
+				alSourceQueueBuffers(source, 1, &streamBuffers.front());
+				streamBuffers.pop();
+			}
 			break;
 			break;
 		}
 		}
 		case TYPE_MAX_ENUM:
 		case TYPE_MAX_ENUM:
@@ -821,13 +876,10 @@ void Source::teardownAtomic()
 			//since we only unqueue 1 buffer, it's OK to use singular variable pointer instead of array
 			//since we only unqueue 1 buffer, it's OK to use singular variable pointer instead of array
 			alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
 			alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
 			for (unsigned int i = 0; i < (unsigned int)queued; i++)
 			for (unsigned int i = 0; i < (unsigned int)queued; i++)
+			{
 				alSourceUnqueueBuffers(source, 1, &buffer);
 				alSourceUnqueueBuffers(source, 1, &buffer);
-
-			// generate unused buffers list
-			for (unsigned int i = 0; i < MAX_BUFFERS; i++)
-				unusedBuffers[i] = streamBuffers[i];
-
-			unusedBufferTop = MAX_BUFFERS - 1;
+				unusedBuffers.push(buffer);
+			}
 			break;
 			break;
 		}
 		}
 		case TYPE_QUEUE:
 		case TYPE_QUEUE:
@@ -837,13 +889,10 @@ void Source::teardownAtomic()
 
 
 			alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
 			alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
 			for (unsigned int i = (unsigned int)queued; i > 0; i--)
 			for (unsigned int i = (unsigned int)queued; i > 0; i--)
+			{
 				alSourceUnqueueBuffers(source, 1, &buffer);
 				alSourceUnqueueBuffers(source, 1, &buffer);
-
-			// generate unused buffers list
-			for (unsigned int i = 0; i < MAX_BUFFERS; i++)
-				unusedBuffers[i] = streamBuffers[i];
-
-			unusedBufferTop = -1;
+				unusedBuffers.push(buffer);
+			}
 			break;
 			break;
 		}
 		}
 		case TYPE_MAX_ENUM:
 		case TYPE_MAX_ENUM:
@@ -912,7 +961,8 @@ void Source::resumeAtomic()
 	{
 	{
 		alSourcePlay(source);
 		alSourcePlay(source);
 
 
-		if (alGetError() == AL_INVALID_VALUE || (sourceType == TYPE_STREAM && unusedBufferTop == MAX_BUFFERS - 1))
+		//failed to play or nothing to play
+		if (alGetError() == AL_INVALID_VALUE || (sourceType == TYPE_STREAM && unusedBuffers.empty()))
 			stop();
 			stop();
 	}
 	}
 }
 }
@@ -1059,8 +1109,11 @@ void Source::reset()
 	alSourcef(source, AL_CONE_OUTER_GAINHF, cone.outerHighGain);
 	alSourcef(source, AL_CONE_OUTER_GAINHF, cone.outerHighGain);
 	alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, rolloffFactor); //reverb-specific rolloff
 	alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, rolloffFactor); //reverb-specific rolloff
 	alSourcei(source, AL_DIRECT_FILTER, directfilter ? directfilter->getFilter() : AL_FILTER_NULL);
 	alSourcei(source, AL_DIRECT_FILTER, directfilter ? directfilter->getFilter() : AL_FILTER_NULL);
-	for (unsigned int i = 0; i < sendtargets.size(); i++)
-		alSource3i(source, AL_AUXILIARY_SEND_FILTER, sendtargets[i], i, sendfilters[i] ? sendfilters[i]->getFilter() : AL_FILTER_NULL);
+	// clear all send slots, then re-enable applied ones
+	for (int i = 0; i < audiomodule()->getMaxSourceEffects(); i++)
+		alSource3i(source, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, i, AL_FILTER_NULL);
+	for (auto i : effectmap)
+		alSource3i(source, AL_AUXILIARY_SEND_FILTER, i.second.target, i.second.slot, i.second.filter ? i.second.filter->getFilter() : AL_FILTER_NULL);
 	//alGetError();
 	//alGetError();
 #endif
 #endif
 }
 }
@@ -1072,33 +1125,6 @@ void Source::setFloatv(float *dst, const float *src) const
 	dst[2] = src[2];
 	dst[2] = src[2];
 }
 }
 
 
-ALuint Source::unusedBufferPeek()
-{
-	return (unusedBufferTop < 0) ? AL_NONE : unusedBuffers[unusedBufferTop];
-}
-
-ALuint Source::unusedBufferPeekNext()
-{
-	return (unusedBufferTop >= (int)MAX_BUFFERS - 1) ? AL_NONE : unusedBuffers[unusedBufferTop + 1];
-}
-
-ALuint *Source::unusedBufferPop()
-{
-	return &unusedBuffers[unusedBufferTop--];
-}
-
-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)
 int Source::streamAtomic(ALuint buffer, love::sound::Decoder *d)
 {
 {
 	// Get more sound data.
 	// Get more sound data.
@@ -1123,7 +1149,7 @@ int Source::streamAtomic(ALuint buffer, love::sound::Decoder *d)
 		if (queued > processed)
 		if (queued > processed)
 			toLoop = queued-processed;
 			toLoop = queued-processed;
 		else
 		else
-			toLoop = MAX_BUFFERS-processed;
+			toLoop = buffers-processed;
 		d->rewind();
 		d->rewind();
 	}
 	}
 
 
@@ -1339,22 +1365,39 @@ bool Source::getFilter(std::map<Filter::Parameter, float> &params)
 	return true;
 	return true;
 }
 }
 
 
-bool Source::setSceneEffect(int slot, int effect)
+bool Source::setEffect(const char *name)
 {
 {
-	if (slot < 0 || slot >= (int)sendtargets.size())
+	ALuint slot, target;
+	Filter *filter;
+
+	// effect with this name doesn't exist
+	if (!dynamic_cast<Audio*>(audiomodule())->getEffectID(name, target))
 		return false;
 		return false;
 
 
-	sendtargets[slot] = dynamic_cast<Audio*>(audiomodule())->getSceneEffectID(effect);
+	auto iter = effectmap.find(name);
+	if (iter == effectmap.end())
+	{
+		// new send target needed but no more room
+		if (slotlist.empty())
+			return false;
 
 
-	if (sendfilters[slot])
-		delete sendfilters[slot];
+		slot = slotlist.top();
+		slotlist.pop();
+	}
+	else
+	{
+		slot = iter->second.slot;
+		filter = iter->second.filter;
 
 
-	sendfilters[slot] = nullptr;
+		if (filter)
+			delete filter;
+	}
+	effectmap[name] = {nullptr, slot, target};
 
 
 #ifdef ALC_EXT_EFX
 #ifdef ALC_EXT_EFX
 	if (valid)
 	if (valid)
 	{
 	{
-		alSource3i(source, AL_AUXILIARY_SEND_FILTER, sendtargets[slot], slot, AL_FILTER_NULL);
+		alSource3i(source, AL_AUXILIARY_SEND_FILTER, target, slot, AL_FILTER_NULL);
 		//alGetError();
 		//alGetError();
 	}
 	}
 #endif
 #endif
@@ -1362,39 +1405,59 @@ bool Source::setSceneEffect(int slot, int effect)
 	return true;
 	return true;
 }
 }
 
 
-bool Source::setSceneEffect(int slot, int effect, const std::map<Filter::Parameter, float> &params)
+bool Source::setEffect(const char *name, const std::map<Filter::Parameter, float> &params)
 {
 {
-	if (slot < 0 || slot >= (int)sendtargets.size())
+	ALuint slot, target;
+	Filter *filter;
+
+	// effect with this name doesn't exist
+	if (!dynamic_cast<Audio*>(audiomodule())->getEffectID(name, target))
 		return false;
 		return false;
 
 
-	sendtargets[slot] = dynamic_cast<Audio*>(audiomodule())->getSceneEffectID(effect);
+	auto iter = effectmap.find(name);
+	if (iter == effectmap.end())
+	{
+		// new send target needed but no more room
+		if (slotlist.empty())
+			return false;
+
+		slot = slotlist.top();
+		slotlist.pop();
+	}
+	else
+	{
+		slot = iter->second.slot;
+		filter = iter->second.filter;
+	}
+	if (!filter)
+		filter = new Filter();
 
 
-	if (!sendfilters[slot])
-		sendfilters[slot] = new Filter();
+	effectmap[name] = {filter, slot, target};
 
 
-	sendfilters[slot]->setParams(params);
+	filter->setParams(params);
 
 
 #ifdef ALC_EXT_EFX
 #ifdef ALC_EXT_EFX
 	if (valid)
 	if (valid)
 	{
 	{
 		//in case of failure contains AL_FILTER_NULL, a valid non-filter
 		//in case of failure contains AL_FILTER_NULL, a valid non-filter
-		alSource3i(source, AL_AUXILIARY_SEND_FILTER, sendtargets[slot], slot, sendfilters[slot]->getFilter());
+		alSource3i(source, AL_AUXILIARY_SEND_FILTER, target, slot, filter->getFilter());
 		//alGetError();
 		//alGetError();
 	}
 	}
 #endif
 #endif
 	return true;
 	return true;
 }
 }
 
 
-bool Source::setSceneEffect(int slot)
+bool Source::unsetEffect(const char *name)
 {
 {
-	if (slot < 0 || slot >= (int)sendtargets.size())
+	auto iter = effectmap.find(name);
+	if (iter == effectmap.end())
 		return false;
 		return false;
 
 
-	sendtargets[slot] = AL_EFFECTSLOT_NULL;
+	ALuint slot = iter->second.slot;
+	Filter *filter = iter->second.filter;
 
 
-	if (sendfilters[slot])
-		delete sendfilters[slot];
-	sendfilters[slot] = nullptr;
+	if (filter)
+		delete filter;
 
 
 #ifdef ALC_EXT_EFX
 #ifdef ALC_EXT_EFX
 	if (valid)
 	if (valid)
@@ -1403,22 +1466,32 @@ bool Source::setSceneEffect(int slot)
 		//alGetError();
 		//alGetError();
 	}
 	}
 #endif
 #endif
-
+	effectmap.erase(iter);
+	slotlist.push(slot);
 	return true;
 	return true;
 }
 }
 
 
-bool Source::getSceneEffect(int slot, int &effect, std::map<Filter::Parameter, float> &params)
+bool Source::getEffect(const char *name, std::map<Filter::Parameter, float> &params)
 {
 {
-	if (slot < 0 || slot >= (int)sendtargets.size())
+	auto iter = effectmap.find(name);
+	if (iter == effectmap.end())
 		return false;
 		return false;
 
 
-	if (sendtargets[slot] == AL_EFFECTSLOT_NULL)
+	if (iter->second.filter)
+		params = iter->second.filter->getParams();
+
+	return true;
+}
+
+bool Source::getEffectsList(std::vector<std::string> &list)
+{
+	if (effectmap.empty())
 		return false;
 		return false;
 
 
-	effect = dynamic_cast<Audio*>(audiomodule())->getSceneEffectIndex(sendtargets[slot]);
+	list.reserve(effectmap.size());
 
 
-	if(sendfilters[slot])
-		params = sendfilters[slot]->getParams();
+	for (auto i : effectmap)
+		list.push_back(i.first);
 
 
 	return true;
 	return true;
 }
 }

+ 20 - 17
src/modules/audio/openal/Source.h

@@ -33,6 +33,7 @@
 
 
 // STL
 // STL
 #include <vector>
 #include <vector>
+#include <stack>
 
 
 // C
 // C
 #include <float.h>
 #include <float.h>
@@ -99,7 +100,7 @@ public:
 
 
 	Source(Pool *pool, love::sound::SoundData *soundData);
 	Source(Pool *pool, love::sound::SoundData *soundData);
 	Source(Pool *pool, love::sound::Decoder *decoder);
 	Source(Pool *pool, love::sound::Decoder *decoder);
-	Source(Pool *pool, int sampleRate, int bitDepth, int channels);
+	Source(Pool *pool, int sampleRate, int bitDepth, int channels, int buffers);
 	Source(const Source &s);
 	Source(const Source &s);
 	virtual ~Source();
 	virtual ~Source();
 
 
@@ -147,10 +148,11 @@ public:
 	virtual bool setFilter();
 	virtual bool setFilter();
 	virtual bool getFilter(std::map<Filter::Parameter, float> &params);
 	virtual bool getFilter(std::map<Filter::Parameter, float> &params);
 
 
-	virtual bool setSceneEffect(int slot, int effect);
-	virtual bool setSceneEffect(int slot, int effect, const std::map<Filter::Parameter, float> &params);
-	virtual bool setSceneEffect(int slot);
-	virtual bool getSceneEffect(int slot, int &effect, std::map<Filter::Parameter, float> &params);
+	virtual bool setEffect(const char *effect);
+	virtual bool setEffect(const char *effect, const std::map<Filter::Parameter, float> &params);
+	virtual bool unsetEffect(const char *effect);
+	virtual bool getEffect(const char *effect, std::map<Filter::Parameter, float> &params);
+	virtual bool getEffectsList(std::vector<std::string> &list);
 
 
 	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);
@@ -178,19 +180,14 @@ private:
 
 
 	int streamAtomic(ALuint buffer, love::sound::Decoder *d);
 	int streamAtomic(ALuint buffer, love::sound::Decoder *d);
 
 
-	ALuint unusedBufferPeek();
-	ALuint unusedBufferPeekNext();
-	ALuint *unusedBufferPop();
-	void unusedBufferPush(ALuint buffer);
-	void unusedBufferQueue(ALuint buffer);
-	
 	Pool *pool = nullptr;
 	Pool *pool = nullptr;
 	ALuint source = 0;
 	ALuint source = 0;
 	bool valid = false;
 	bool valid = false;
 
 
-	static const unsigned int MAX_BUFFERS = 8;
-	ALuint streamBuffers[MAX_BUFFERS];
-	ALuint unusedBuffers[MAX_BUFFERS];
+	const static int DEFAULT_BUFFERS = 8;
+	const static int MAX_BUFFERS = 64;
+	std::queue<ALuint> streamBuffers;
+	std::stack<ALuint> unusedBuffers;
 
 
 	StrongRef<StaticDataBuffer> staticBuffer;
 	StrongRef<StaticDataBuffer> staticBuffer;
 
 
@@ -226,12 +223,18 @@ private:
 	StrongRef<love::sound::Decoder> decoder;
 	StrongRef<love::sound::Decoder> decoder;
 
 
 	unsigned int toLoop = 0;
 	unsigned int toLoop = 0;
-	int unusedBufferTop = -1;
 	ALsizei bufferedBytes = 0;
 	ALsizei bufferedBytes = 0;
+	int buffers = 0;
 
 
 	Filter *directfilter = nullptr;
 	Filter *directfilter = nullptr;
-	std::vector<Filter*> sendfilters;
-	std::vector<ALint> sendtargets;
+
+	struct effectmapStorage
+	{
+		Filter *filter;
+		ALuint slot, target;
+	};
+	std::map<std::string, struct effectmapStorage> effectmap;
+	std::stack<ALuint> slotlist;
 }; // Source
 }; // Source
 
 
 } // openal
 } // openal

+ 30 - 13
src/modules/audio/wrap_Audio.cpp

@@ -84,7 +84,7 @@ int w_newQueueableSource(lua_State *L)
 	Source *t = nullptr;
 	Source *t = nullptr;
 
 
 	luax_catchexcept(L, [&]() {
 	luax_catchexcept(L, [&]() {
-		t = instance()->newSource((int)luaL_checknumber(L, 1), (int)luaL_checknumber(L, 2), (int)luaL_checknumber(L, 3));
+		t = instance()->newSource((int)luaL_checknumber(L, 1), (int)luaL_checknumber(L, 2), (int)luaL_checknumber(L, 3), (int)luaL_optnumber(L, 4, 0));
 	});
 	});
 
 
 	if (t != nullptr)
 	if (t != nullptr)
@@ -305,13 +305,13 @@ int w_getRecordingDevices(lua_State *L)
 	return 1;
 	return 1;
 }
 }
 
 
-int w_setSceneEffect(lua_State *L)
+int w_setEffect(lua_State *L)
 {
 {
-	int slot = luaL_checknumber(L, 1) - 1;
+	const char *namestr = luaL_checkstring(L, 1);
 
 
-	if (lua_gettop(L) == 1 || (lua_gettop(L) == 2 && lua_isnoneornil(L, 2)))
+	if (lua_isnoneornil(L, 2) || (lua_gettop(L) == 2 && lua_isboolean(L, 2) && !lua_toboolean(L, 2)))
 	{
 	{
-		lua_pushboolean(L, instance()->setSceneEffect(slot));
+		lua_pushboolean(L, instance()->unsetEffect(namestr));
 		return 1;
 		return 1;
 	}
 	}
 
 
@@ -405,17 +405,17 @@ int w_setSceneEffect(lua_State *L)
 		lua_pop(L, 1);
 		lua_pop(L, 1);
 	}
 	}
 
 
-	luax_catchexcept(L, [&]() { lua_pushboolean(L, instance()->setSceneEffect(slot, params)); });
+	luax_catchexcept(L, [&]() { lua_pushboolean(L, instance()->setEffect(namestr, params)); });
 	return 1;
 	return 1;
 }
 }
 
 
-int w_getSceneEffect(lua_State *L)
+int w_getEffect(lua_State *L)
 {
 {
-	int slot = luaL_checknumber(L, 1) - 1;
+	const char *namestr = luaL_checkstring(L, 1);
 
 
 	std::map<Effect::Parameter, float> params;
 	std::map<Effect::Parameter, float> params;
 
 
-	if (!instance()->getSceneEffect(slot, params))
+	if (!instance()->getEffect(namestr, params))
 		return 0;
 		return 0;
 
 
 	const char *keystr, *valstr;
 	const char *keystr, *valstr;
@@ -463,6 +463,22 @@ int w_getSceneEffect(lua_State *L)
 	return 1;
 	return 1;
 }
 }
 
 
+int w_getEffectsList(lua_State *L)
+{
+	std::vector<std::string> list;
+	if (!instance()->getEffectsList(list))
+		return 0;
+
+	lua_createtable(L, 0, list.size());
+	for (unsigned int i = 0; i < list.size(); i++)
+	{
+		lua_pushnumber(L, i + 1);
+		lua_pushstring(L, list[i].c_str());
+		lua_rawset(L, -3);
+	}
+	return 1;
+}
+
 int w_getMaxSceneEffects(lua_State *L)
 int w_getMaxSceneEffects(lua_State *L)
 {
 {
 	lua_pushnumber(L, instance()->getMaxSceneEffects());
 	lua_pushnumber(L, instance()->getMaxSceneEffects());
@@ -475,7 +491,7 @@ int w_getMaxSourceEffects(lua_State *L)
 	return 1;
 	return 1;
 }
 }
 
 
-int w_isSceneEffectsSupported(lua_State *L)
+int w_isEffectsSupported(lua_State *L)
 {
 {
 	lua_pushboolean(L, instance()->isEFXsupported());
 	lua_pushboolean(L, instance()->isEFXsupported());
 	return 1;
 	return 1;
@@ -505,11 +521,12 @@ static const luaL_Reg functions[] =
 	{ "setDistanceModel", w_setDistanceModel },
 	{ "setDistanceModel", w_setDistanceModel },
 	{ "getDistanceModel", w_getDistanceModel },
 	{ "getDistanceModel", w_getDistanceModel },
 	{ "getRecordingDevices", w_getRecordingDevices },
 	{ "getRecordingDevices", w_getRecordingDevices },
-	{ "setEffect", w_setSceneEffect },
-	{ "getEffect", w_getSceneEffect },
+	{ "setEffect", w_setEffect },
+	{ "getEffect", w_getEffect },
+	{ "getEffectsList", w_getEffectsList },
 	{ "getMaxSceneEffects", w_getMaxSceneEffects },
 	{ "getMaxSceneEffects", w_getMaxSceneEffects },
 	{ "getMaxSourceEffects", w_getMaxSourceEffects },
 	{ "getMaxSourceEffects", w_getMaxSourceEffects },
-	{ "isEffectsSupported", w_isSceneEffectsSupported },
+	{ "isEffectsSupported", w_isEffectsSupported },
 	{ 0, 0 }
 	{ 0, 0 }
 };
 };
 
 

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

@@ -456,49 +456,58 @@ int w_Source_getFilter(lua_State *L)
 	return 1;
 	return 1;
 }
 }
 
 
-int w_Source_setSceneEffect(lua_State *L)
+int w_Source_setEffect(lua_State *L)
 {
 {
 	Source *t = luax_checksource(L, 1);
 	Source *t = luax_checksource(L, 1);
+	const char *namestr = luaL_checkstring(L, 2);
 
 
-	int slot = luaL_checknumber(L, 2) - 1;
-	if (lua_gettop(L) == 2)
+	// :setEffect(effect, false) = clear effect
+	if (lua_gettop(L) == 3 && lua_isboolean(L, 3) && !lua_toboolean(L, 3))
 	{
 	{
-		luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setSceneEffect(slot)); });
-		return 1;
-	}
-
-	int effect = luaL_checknumber(L, 3) - 1;
-	if (lua_gettop(L) == 3)
-	{
-		luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setSceneEffect(slot, effect)); });
+		luax_catchexcept(L, [&]() { lua_pushboolean(L, t->unsetEffect(namestr)); });
 		return 1;
 		return 1;
 	}
 	}
 
 
 	std::map<Filter::Parameter, float> params;
 	std::map<Filter::Parameter, float> params;
 
 
-	if (setFilterReadFilter(L, 4, params) == 1)
-		luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setSceneEffect(slot, effect, params)); });
+	if (setFilterReadFilter(L, 3, params) == 1)
+		luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setEffect(namestr, params)); });
 	else
 	else
-		luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setSceneEffect(slot, effect)); });
+		luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setEffect(namestr)); });
 	return 1;
 	return 1;
 }
 }
 
 
-int w_Source_getSceneEffect(lua_State *L)
+int w_Source_getEffect(lua_State *L)
 {
 {
 	Source *t = luax_checksource(L, 1);
 	Source *t = luax_checksource(L, 1);
-	int slot = luaL_checknumber(L, 2) - 1;
+	const char *namestr = luaL_checkstring(L, 2);
 
 
-	int effect;
 	std::map<Filter::Parameter, float> params;
 	std::map<Filter::Parameter, float> params;
-	if (!t->getSceneEffect(slot, effect, params))
+	if (!t->getEffect(namestr, params))
 		return 0;
 		return 0;
 
 
-	lua_pushnumber(L, effect + 1);
 	if (params.size() == 0)
 	if (params.size() == 0)
-		return 1;
+		return 0;
 
 
 	getFilterWriteFilter(L, params);
 	getFilterWriteFilter(L, params);
-	return 2;
+	return 1;
+}
+
+int w_Source_getEffectsList(lua_State *L)
+{
+	Source *t = luax_checksource(L, 1);
+	std::vector<std::string> list;
+	if (!t->getEffectsList(list))
+		return 0;
+
+	lua_createtable(L, 0, list.size());
+	for (unsigned int i = 0; i < list.size(); i++)
+	{
+		lua_pushnumber(L, i + 1);
+		lua_pushstring(L, list[i].c_str());
+		lua_rawset(L, -3);
+	}
+	return 1;
 }
 }
 
 
 int w_Source_getFreeBufferCount(lua_State *L)
 int w_Source_getFreeBufferCount(lua_State *L)
@@ -613,8 +622,9 @@ static const luaL_Reg w_Source_functions[] =
 
 
 	{ "setFilter", w_Source_setFilter },
 	{ "setFilter", w_Source_setFilter },
 	{ "getFilter", w_Source_getFilter },
 	{ "getFilter", w_Source_getFilter },
-	{ "setEffect", w_Source_setSceneEffect },
-	{ "getEffect", w_Source_getSceneEffect },
+	{ "setEffect", w_Source_setEffect },
+	{ "getEffect", w_Source_getEffect },
+	{ "getEffectsList", w_Source_getEffectsList },
 
 
 	{ "getFreeBufferCount", w_Source_getFreeBufferCount },
 	{ "getFreeBufferCount", w_Source_getFreeBufferCount },
 	{ "queue", w_Source_queue },
 	{ "queue", w_Source_queue },