Browse Source

Merged in rcoaxil/love-patches/minor (pull request #75)

EFX implemented

Approved-by: Bart van Strien
Approved-by: Alex Szpakowski

--HG--
branch : minor
Bart van Strien 8 years ago
parent
commit
cbaf9e047b

+ 4 - 0
CMakeLists.txt

@@ -318,6 +318,8 @@ set(LOVE_SRC_MODULE_AUDIO_ROOT
 	src/modules/audio/RecordingDevice.h
 	src/modules/audio/Filter.cpp
 	src/modules/audio/Filter.h
+	src/modules/audio/Effect.cpp
+	src/modules/audio/Effect.h
 	src/modules/audio/wrap_Audio.cpp
 	src/modules/audio/wrap_Audio.h
 	src/modules/audio/wrap_Source.cpp
@@ -346,6 +348,8 @@ set(LOVE_SRC_MODULE_AUDIO_OPENAL
 	src/modules/audio/openal/RecordingDevice.h
 	src/modules/audio/openal/Filter.cpp
 	src/modules/audio/openal/Filter.h
+	src/modules/audio/openal/Effect.cpp
+	src/modules/audio/openal/Effect.h
 )
 
 set(LOVE_SRC_MODULE_AUDIO

+ 44 - 1
src/modules/audio/Audio.h

@@ -28,6 +28,7 @@
 #include "common/Module.h"
 #include "common/StringMap.h"
 #include "Source.h"
+#include "Effect.h"
 #include "RecordingDevice.h"
 
 namespace love
@@ -188,7 +189,8 @@ public:
 
 	virtual void setDopplerScale(float scale) = 0;
 	virtual float getDopplerScale() const = 0;
-
+	//virtual void setMeter(float scale) = 0;
+	//virtual float getMeter() const = 0;
 	/**
 	 * @return Reference to a vector of pointers to recording devices. May be empty.
 	 **/
@@ -206,6 +208,47 @@ public:
 	 */
 	virtual void setDistanceModel(DistanceModel distanceModel) = 0;
 
+	/**
+	 * Sets scene EFX effect.
+	 * @param slot Slot to put effect into.
+	 * @param fxparams Effect description table.
+	 * @return true if successful, false otherwise.
+	 */
+	virtual bool setSceneEffect(int slot, std::map<Effect::Parameter, float> &params) = 0;
+
+	/**
+	 * Removes scene EFX effect.
+	 * @param slot Effect slot to clear.
+	 * @return true if successful, false otherwise.
+	 */
+	virtual bool setSceneEffect(int slot) = 0;
+
+	/**
+	 * Gets scene EFX effect.
+	 * @param slot Slot from which to get effect.
+	 * @param fxparams Effect description table.
+	 * @return true if effect was present, false otherwise.
+	 */
+	virtual bool getSceneEffect(int slot, std::map<Effect::Parameter, float> &params) = 0;
+
+	/**
+	 * Gets maximum number of scene EFX effects.
+	 * @return number of effects.
+	 */
+	virtual int getMaxSceneEffects() const = 0;
+
+	/**
+	 * Gets maximum number of source EFX effects.
+	 * @return number of effects.
+	 */
+	virtual int getMaxSourceEffects() const = 0;
+
+	/**
+	 * Gets EFX (or analog) availability.
+	 * @return true if supported.
+	 */
+	virtual bool isEFXsupported() const = 0;
+
 private:
 
 	static StringMap<DistanceModel, DISTANCE_MAX_ENUM>::Entry distanceModelEntries[];

+ 390 - 0
src/modules/audio/Effect.cpp

@@ -0,0 +1,390 @@
+/**
+ * Copyright (c) 2006-2016 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#include "Effect.h"
+
+namespace love
+{
+namespace audio
+{
+
+Effect::Effect()
+{
+}
+
+Effect::~Effect()
+{
+}
+
+Effect::Type Effect::getType() const
+{
+	return type;
+}
+
+bool Effect::getConstant(const char *in, Type &out)
+{
+	return types.find(in, out);
+}
+
+bool Effect::getConstant(Type in, const char *&out)
+{
+	return types.find(in, out);
+}
+/*
+bool Effect::getConstant(const char *in, Phoneme &out)
+{
+	return phonemes.find(in, out);
+}
+
+bool Effect::getConstant(Phoneme in, const char *&out)
+{
+	return phonemes.find(in, out);
+}
+*/
+bool Effect::getConstant(const char *in, Waveform &out)
+{
+	return waveforms.find(in, out);
+}
+
+bool Effect::getConstant(Waveform in, const char *&out)
+{
+	return waveforms.find(in, out);
+}
+/*
+bool Effect::getConstant(const char *in, Direction &out)
+{
+	return directions.find(in, out);
+}
+
+bool Effect::getConstant(Direction in, const char *&out)
+{
+	return directions.find(in, out);
+}
+*/
+bool Effect::getConstant(const char *in, Parameter &out, Type t)
+{
+	return parameterNames[t].find(in, out);
+}
+
+bool Effect::getConstant(Parameter in, const char *&out, Type t)
+{
+	return parameterNames[t].find(in, out);
+}
+
+Effect::ParameterType Effect::getParameterType(Effect::Parameter in)
+{
+	return parameterTypes[in];
+}
+
+StringMap<Effect::Type, Effect::TYPE_MAX_ENUM>::Entry Effect::typeEntries[] =
+{
+	{"reverb", Effect::TYPE_REVERB},
+	{"chorus", Effect::TYPE_CHORUS},
+	{"distortion", Effect::TYPE_DISTORTION},
+	{"echo", Effect::TYPE_ECHO},
+	{"flanger", Effect::TYPE_FLANGER},
+	//{"frequencyshifter", Effect::TYPE_FREQSHIFTER},
+	//{"vocalmorpher", Effect::TYPE_MORPHER},
+	//{"pitchshifter", Effect::TYPE_PITCHSHIFTER},
+	{"ringmodulator", Effect::TYPE_MODULATOR},
+	//{"autowah", Effect::TYPE_AUTOWAH},
+	{"compressor", Effect::TYPE_COMPRESSOR},
+	{"equalizer", Effect::TYPE_EQUALIZER},
+};
+
+StringMap<Effect::Type, Effect::TYPE_MAX_ENUM> Effect::types(Effect::typeEntries, sizeof(Effect::typeEntries));
+
+StringMap<Effect::Waveform, Effect::WAVE_MAX_ENUM>::Entry Effect::waveformEntries[] =
+{
+	{"sine", Effect::WAVE_SINE},
+	{"triangle", Effect::WAVE_TRIANGLE},
+	{"sawtooth", Effect::WAVE_SAWTOOTH},
+	{"square", Effect::WAVE_SQUARE},
+};
+
+StringMap<Effect::Waveform, Effect::WAVE_MAX_ENUM> Effect::waveforms(Effect::waveformEntries, sizeof(Effect::waveformEntries));
+
+/*
+StringMap<Effect::Direction, Effect::DIR_MAX_ENUM>::Entry Effect::directionEntries[] =
+{
+	{"up", Effect::DIR_UP},
+	{"down", Effect::DIR_DOWN},
+	{"none", Effect::DIR_NONE},
+};
+
+StringMap<Effect::Direction, Effect::DIR_MAX_ENUM> Effect::directions(Effect::directionEntries, sizeof(Effect::directionEntries));
+
+StringMap<Effect::Phoneme, Effect::PHONEME_MAX_ENUM>::Entry Effect::phonemeEntries[] =
+{
+	{"a", Effect::PHONEME_A},
+	{"e", Effect::PHONEME_E},
+	{"i", Effect::PHONEME_I},
+	{"o", Effect::PHONEME_O},
+	{"u", Effect::PHONEME_U},
+	{"aa", Effect::PHONEME_AA},
+	{"ae", Effect::PHONEME_AE},
+	{"ah", Effect::PHONEME_AH},
+	{"ao", Effect::PHONEME_AO},
+	{"eh", Effect::PHONEME_EH},
+	{"er", Effect::PHONEME_ER},
+	{"ih", Effect::PHONEME_IH},
+	{"iy", Effect::PHONEME_IY},
+	{"uh", Effect::PHONEME_UH},
+	{"uw", Effect::PHONEME_UW},
+	{"b", Effect::PHONEME_B},
+	{"d", Effect::PHONEME_D},
+	{"f", Effect::PHONEME_F},
+	{"g", Effect::PHONEME_G},
+	{"j", Effect::PHONEME_J},
+	{"k", Effect::PHONEME_K},
+	{"l", Effect::PHONEME_L},
+	{"m", Effect::PHONEME_M},
+	{"n", Effect::PHONEME_N},
+	{"p", Effect::PHONEME_P},
+	{"r", Effect::PHONEME_R},
+	{"s", Effect::PHONEME_S},
+	{"t", Effect::PHONEME_T},
+	{"v", Effect::PHONEME_V},
+	{"z", Effect::PHONEME_Z},
+};
+
+StringMap<Effect::Phoneme, Effect::PHONEME_MAX_ENUM> Effect::phonemes(Effect::phonemeEntries, sizeof(Effect::phonemeEntries));
+*/
+
+#define StringMap LazierAndSlowerButEasilyArrayableStringMap
+
+std::vector<StringMap<Effect::Parameter>::Entry> Effect::basicParameters =
+{
+	{"type", Effect::EFFECT_TYPE},
+	{"volume", Effect::EFFECT_VOLUME}
+};
+
+std::vector<StringMap<Effect::Parameter>::Entry> Effect::reverbParameters =
+{
+	{"gain", Effect::REVERB_GAIN},
+	{"highgain", Effect::REVERB_HFGAIN},
+	{"density", Effect::REVERB_DENSITY},
+	{"diffusion", Effect::REVERB_DIFFUSION},
+	{"decaytime", Effect::REVERB_DECAY},
+	{"decayhighratio", Effect::REVERB_HFDECAY},
+	{"earlygain", Effect::REVERB_EARLYGAIN},
+	{"earlydelay", Effect::REVERB_EARLYDELAY},
+	{"lategain", Effect::REVERB_LATEGAIN},
+	{"latedelay", Effect::REVERB_LATEDELAY},
+	{"roomrolloff", Effect::REVERB_ROLLOFF},
+	{"airabsorption", Effect::REVERB_AIRHFGAIN},
+	{"highlimit", Effect::REVERB_HFLIMITER}
+};
+
+std::vector<StringMap<Effect::Parameter>::Entry> Effect::chorusParameters =
+{
+	{"waveform", Effect::CHORUS_WAVEFORM},
+	{"phase", Effect::CHORUS_PHASE},
+	{"rate", Effect::CHORUS_RATE},
+	{"depth", Effect::CHORUS_DEPTH},
+	{"feedback", Effect::CHORUS_FEEDBACK},
+	{"delay", Effect::CHORUS_DELAY}
+};
+
+std::vector<StringMap<Effect::Parameter>::Entry> Effect::distortionParameters = 
+{
+	{"gain", Effect::DISTORTION_GAIN},
+	{"edge", Effect::DISTORTION_EDGE},
+	{"lowcut", Effect::DISTORTION_LOWCUT},
+	{"center", Effect::DISTORTION_EQCENTER},
+	{"bandwidth", Effect::DISTORTION_EQBAND}
+};
+
+std::vector<StringMap<Effect::Parameter>::Entry> Effect::echoParameters = 
+{
+	{"delay", Effect::ECHO_DELAY},
+	{"tapdelay", Effect::ECHO_LRDELAY},
+	{"damping", Effect::ECHO_DAMPING},
+	{"feedback", Effect::ECHO_FEEDBACK},
+	{"spread", Effect::ECHO_SPREAD}
+};
+
+std::vector<StringMap<Effect::Parameter>::Entry> Effect::flangerParameters = 
+{
+	{"waveform", Effect::FLANGER_WAVEFORM},
+	{"phase", Effect::FLANGER_PHASE},
+	{"rate", Effect::FLANGER_RATE},
+	{"depth", Effect::FLANGER_DEPTH},
+	{"feedback", Effect::FLANGER_FEEDBACK},
+	{"delay", Effect::FLANGER_DELAY}
+};
+/*
+std::vector<StringMap<Effect::Parameter>::Entry> Effect::freqshifterParameters = 
+{
+	{"frequency", Effect::FREQSHIFTER_FREQ},
+	{"leftdirection", Effect::FREQSHIFTER_LEFTDIR},
+	{"rightdirection", Effect::FREQSHIFTER_RIGHTDIR}
+};
+
+std::vector<StringMap<Effect::Parameter>::Entry> Effect::morpherParameters = 
+{
+	{"waveform", Effect::MORPHER_WAVEFORM},
+	{"rate", Effect::MORPHER_RATE},
+	{"phonemea", Effect::MORPHER_PHONEMEA},
+	{"phonemeb", Effect::MORPHER_PHONEMEB},
+	{"tunea", Effect::MORPHER_COARSEA},
+	{"tuneb", Effect::MORPHER_COARSEB}
+}
+;
+std::vector<StringMap<Effect::Parameter>::Entry> Effect::pitchshifterParameters = 
+{
+	{"pitch", Effect::PITCHSHIFTER_PITCH}
+};
+*/
+std::vector<StringMap<Effect::Parameter>::Entry> Effect::modulatorParameters = 
+{
+	{"waveform", Effect::MODULATOR_WAVEFORM},
+	{"frequency", Effect::MODULATOR_FREQ},
+	{"highcut", Effect::MODULATOR_HIGHCUT}
+};
+/*
+std::vector<StringMap<Effect::Parameter>::Entry> Effect::autowahParameters = 
+{
+	{"attack", Effect::AUTOWAH_ATTACK},
+	{"release", Effect::AUTOWAH_RELEASE},
+	{"resonance", Effect::AUTOWAH_RESONANCE},
+	{"peakgain", Effect::AUTOWAH_PEAKGAIN}
+};
+*/
+std::vector<StringMap<Effect::Parameter>::Entry> Effect::compressorParameters = 
+{
+	{"enable", Effect::COMPRESSOR_ENABLE}
+};
+
+std::vector<StringMap<Effect::Parameter>::Entry> Effect::equalizerParameters = 
+{
+	{"lowgain", Effect::EQUALIZER_LOWGAIN},
+	{"lowcut", Effect::EQUALIZER_LOWCUT},
+	{"lowmidgain", Effect::EQUALIZER_MID1GAIN},
+	{"lowmidfrequency", Effect::EQUALIZER_MID1FREQ},
+	{"lowmidbandwidth", Effect::EQUALIZER_MID1BAND},
+	{"highmidgain", Effect::EQUALIZER_MID2GAIN},
+	{"highmidfrequency", Effect::EQUALIZER_MID2FREQ},
+	{"highmidbandwidth", Effect::EQUALIZER_MID2BAND},
+	{"highgain", Effect::EQUALIZER_HIGHGAIN},
+	{"highcut", Effect::EQUALIZER_HIGHCUT}
+};
+
+std::map<Effect::Type, StringMap<Effect::Parameter>> Effect::parameterNames = 
+{
+	{Effect::TYPE_BASIC, Effect::basicParameters},
+	{Effect::TYPE_REVERB, Effect::reverbParameters},
+	{Effect::TYPE_CHORUS, Effect::chorusParameters},
+	{Effect::TYPE_DISTORTION, Effect::distortionParameters},
+	{Effect::TYPE_ECHO, Effect::echoParameters},
+	{Effect::TYPE_FLANGER, Effect::flangerParameters},
+	//{Effect::TYPE_FREQSHIFTER, Effect::freqshifterParameters},
+	//{Effect::TYPE_MORPHER, Effect::morpherbParameters},
+	//{Effect::TYPE_PITCHSHIFTER, Effect::pitchshifterParameters},
+	{Effect::TYPE_MODULATOR, Effect::modulatorParameters},
+	//{Effect::TYPE_AUTOWAH, Effect::autowahParameters},
+	{Effect::TYPE_COMPRESSOR, Effect::compressorParameters},
+	{Effect::TYPE_EQUALIZER, Effect::equalizerParameters}
+};
+#undef StringMap
+
+std::map<Effect::Parameter, Effect::ParameterType> Effect::parameterTypes = 
+{
+	{Effect::EFFECT_TYPE, Effect::PARAM_TYPE},
+	{Effect::EFFECT_VOLUME, Effect::PARAM_FLOAT},
+
+	{Effect::REVERB_GAIN, Effect::PARAM_FLOAT},
+	{Effect::REVERB_HFGAIN, Effect::PARAM_FLOAT},
+	{Effect::REVERB_DENSITY, Effect::PARAM_FLOAT},
+	{Effect::REVERB_DIFFUSION, Effect::PARAM_FLOAT},
+	{Effect::REVERB_DECAY, Effect::PARAM_FLOAT},
+	{Effect::REVERB_HFDECAY, Effect::PARAM_FLOAT},
+	{Effect::REVERB_EARLYGAIN, Effect::PARAM_FLOAT},
+	{Effect::REVERB_EARLYDELAY, Effect::PARAM_FLOAT},
+	{Effect::REVERB_LATEGAIN, Effect::PARAM_FLOAT},
+	{Effect::REVERB_LATEDELAY, Effect::PARAM_FLOAT},
+	{Effect::REVERB_ROLLOFF, Effect::PARAM_FLOAT},
+	{Effect::REVERB_AIRHFGAIN, Effect::PARAM_FLOAT},
+	{Effect::REVERB_HFLIMITER, Effect::PARAM_BOOL},
+
+	{Effect::CHORUS_WAVEFORM, Effect::PARAM_WAVEFORM},
+	{Effect::CHORUS_PHASE, Effect::PARAM_FLOAT},
+	{Effect::CHORUS_RATE, Effect::PARAM_FLOAT},
+	{Effect::CHORUS_DEPTH, Effect::PARAM_FLOAT},
+	{Effect::CHORUS_FEEDBACK, Effect::PARAM_FLOAT},
+	{Effect::CHORUS_DELAY, Effect::PARAM_FLOAT},
+
+	{Effect::DISTORTION_GAIN, Effect::PARAM_FLOAT},
+	{Effect::DISTORTION_EDGE, Effect::PARAM_FLOAT},
+	{Effect::DISTORTION_LOWCUT, Effect::PARAM_FLOAT},
+	{Effect::DISTORTION_EQCENTER, Effect::PARAM_FLOAT},
+	{Effect::DISTORTION_EQBAND, Effect::PARAM_FLOAT},
+
+	{Effect::ECHO_DELAY, Effect::PARAM_FLOAT},
+	{Effect::ECHO_LRDELAY, Effect::PARAM_FLOAT},
+	{Effect::ECHO_DAMPING, Effect::PARAM_FLOAT},
+	{Effect::ECHO_FEEDBACK, Effect::PARAM_FLOAT},
+	{Effect::ECHO_SPREAD, Effect::PARAM_FLOAT},
+
+	{Effect::FLANGER_WAVEFORM, Effect::PARAM_WAVEFORM},
+	{Effect::FLANGER_PHASE, Effect::PARAM_FLOAT},
+	{Effect::FLANGER_RATE, Effect::PARAM_FLOAT},
+	{Effect::FLANGER_DEPTH, Effect::PARAM_FLOAT},
+	{Effect::FLANGER_FEEDBACK, Effect::PARAM_FLOAT},
+	{Effect::FLANGER_DELAY, Effect::PARAM_FLOAT},
+/*
+	{Effect::FREQSHIFTER_FREQ, Effect::PARAM_FLOAT},
+	{Effect::FREQSHIFTER_LEFTDIR, Effect::PARAM_DIRECTION},
+	{Effect::FREQSHIFTER_RIGHTDIR, Effect::PARAM_DIRECTION},
+
+	{Effect::MORPHER_WAVEFORM, Effect::PARAM_WAVEFORM},
+	{Effect::MORPHER_RATE, Effect::PARAM_FLOAT},
+	{Effect::MORPHER_PHONEMEA, Effect::PARAM_PHONEME},
+	{Effect::MORPHER_PHONEMEB, Effect::PARAM_PHONEME},
+	{Effect::MORPHER_TUNEA, Effect::PARAM_FLOAT},
+	{Effect::MORPHER_TUNEB, Effect::PARAM_FLOAT},
+
+	{Effect::PITCHSHIFTER_PITCH, Effect::PARAM_FLOAT},
+*/
+	{Effect::MODULATOR_WAVEFORM, Effect::PARAM_WAVEFORM},
+	{Effect::MODULATOR_FREQ, Effect::PARAM_FLOAT},
+	{Effect::MODULATOR_HIGHCUT, Effect::PARAM_FLOAT},
+/*
+	{Effect::AUTOWAH_ATTACK, Effect::PARAM_FLOAT},
+	{Effect::AUTOWAH_RELEASE, Effect::PARAM_FLOAT},
+	{Effect::AUTOWAH_RESONANCE, Effect::PARAM_FLOAT},
+	{Effect::AUTOWAH_PEAKGAIN, Effect::PARAM_FLOAT},
+*/
+	{Effect::COMPRESSOR_ENABLE, Effect::PARAM_BOOL},
+
+	{Effect::EQUALIZER_LOWGAIN, Effect::PARAM_FLOAT},
+	{Effect::EQUALIZER_LOWCUT, Effect::PARAM_FLOAT},
+	{Effect::EQUALIZER_MID1GAIN, Effect::PARAM_FLOAT},
+	{Effect::EQUALIZER_MID1FREQ, Effect::PARAM_FLOAT},
+	{Effect::EQUALIZER_MID1BAND, Effect::PARAM_FLOAT},
+	{Effect::EQUALIZER_MID2GAIN, Effect::PARAM_FLOAT},
+	{Effect::EQUALIZER_MID2FREQ, Effect::PARAM_FLOAT},
+	{Effect::EQUALIZER_MID2BAND, Effect::PARAM_FLOAT},
+	{Effect::EQUALIZER_HIGHGAIN, Effect::PARAM_FLOAT},
+	{Effect::EQUALIZER_HIGHCUT, Effect::PARAM_FLOAT}
+};
+
+} //audio
+} //love

+ 296 - 0
src/modules/audio/Effect.h

@@ -0,0 +1,296 @@
+/**
+ * Copyright (c) 2006-2016 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#ifndef LOVE_AUDIO_EFFECTS_H
+#define LOVE_AUDIO_EFFECTS_H
+
+#include "common/Object.h"
+#include "common/StringMap.h"
+#include <map>
+#include <vector>
+
+template<typename T>
+class LazierAndSlowerButEasilyArrayableStringMap
+{
+public:
+	struct Entry
+	{
+		const char *key;
+		T value;
+	};
+	LazierAndSlowerButEasilyArrayableStringMap()
+	{
+	}
+
+	LazierAndSlowerButEasilyArrayableStringMap(const std::vector<Entry> &entries)
+	{
+		for (auto entry : entries)
+		{
+			forward[entry.key] = entry.value;
+			reverse[entry.value] = entry.key;
+		}
+	}
+
+	bool find(const char *key, T &t)
+	{
+		if (forward.find(key) == forward.end()) 
+			return false;
+		t = forward[key];
+		return true;
+	}
+
+	bool find(T key, const char *&str) 
+	{
+		if (reverse.find(key) == reverse.end())
+			return false;
+		str = reverse[key];
+		return true;
+	}
+
+private:
+	std::map<std::string, T> forward;
+	std::map<T, const char*> reverse;
+};
+
+namespace love
+{
+namespace audio
+{
+
+class Effect
+{
+public:
+	enum Type
+	{
+		TYPE_BASIC, //not a real type
+		TYPE_REVERB,
+		TYPE_CHORUS,
+		TYPE_DISTORTION,
+		TYPE_ECHO,
+		TYPE_FLANGER,
+		//TYPE_FREQSHIFTER,
+		//TYPE_MORPHER,
+		//TYPE_PITCHSHIFTER,
+		TYPE_MODULATOR,
+		//TYPE_AUTOWAH,
+		TYPE_COMPRESSOR,
+		TYPE_EQUALIZER,
+		TYPE_MAX_ENUM
+	};
+
+	enum Parameter
+	{
+		EFFECT_TYPE,
+		EFFECT_VOLUME,
+
+		REVERB_GAIN,
+		REVERB_HFGAIN,
+		REVERB_DENSITY,
+		REVERB_DIFFUSION,
+		REVERB_DECAY,
+		REVERB_HFDECAY,
+		REVERB_EARLYGAIN,
+		REVERB_EARLYDELAY,
+		REVERB_LATEGAIN,
+		REVERB_LATEDELAY,
+		REVERB_ROLLOFF,
+		REVERB_AIRHFGAIN,
+		REVERB_HFLIMITER,
+
+		CHORUS_WAVEFORM,
+		CHORUS_PHASE,
+		CHORUS_RATE,
+		CHORUS_DEPTH,
+		CHORUS_FEEDBACK,
+		CHORUS_DELAY,
+
+		DISTORTION_GAIN,
+		DISTORTION_EDGE,
+		DISTORTION_LOWCUT,
+		DISTORTION_EQCENTER,
+		DISTORTION_EQBAND,
+
+		ECHO_DELAY,
+		ECHO_LRDELAY,
+		ECHO_DAMPING,
+		ECHO_FEEDBACK,
+		ECHO_SPREAD,
+
+		FLANGER_WAVEFORM,
+		FLANGER_PHASE,
+		FLANGER_RATE,
+		FLANGER_DEPTH,
+		FLANGER_FEEDBACK,
+		FLANGER_DELAY,
+/*
+		FREQSHIFTER_FREQ,
+		FREQSHIFTER_LEFTDIR,
+		FREQSHIFTER_RIGHTDIR,
+
+		MORPHER_WAVEFORM,
+		MORPHER_RATE,
+		MORPHER_PHONEMEA,
+		MORPHER_PHONEMEB,
+		MORPHER_TUNEA,
+		MORPHER_TUNEB,
+
+		PITCHSHIFTER_PITCH,
+*/
+		MODULATOR_WAVEFORM,
+		MODULATOR_FREQ,
+		MODULATOR_HIGHCUT,
+/*
+		AUTOWAH_ATTACK,
+		AUTOWAH_RELEASE,
+		AUTOWAH_RESONANCE,
+		AUTOWAH_PEAKGAIN,
+*/
+		COMPRESSOR_ENABLE,
+
+		EQUALIZER_LOWGAIN,
+		EQUALIZER_LOWCUT,
+		EQUALIZER_MID1GAIN,
+		EQUALIZER_MID1FREQ,
+		EQUALIZER_MID1BAND,
+		EQUALIZER_MID2GAIN,
+		EQUALIZER_MID2FREQ,
+		EQUALIZER_MID2BAND,
+		EQUALIZER_HIGHGAIN,
+		EQUALIZER_HIGHCUT,
+
+		EFFECT_MAX_ENUM
+	};
+
+	enum ParameterType
+	{
+		PARAM_TYPE,
+		PARAM_FLOAT,
+		PARAM_BOOL,
+		PARAM_WAVEFORM,
+		//PARAM_DIRECTION,
+		//PARAM_PHONEME,
+		PARAM_MAX_ENUM
+	};
+
+	enum Waveform
+	{
+		WAVE_SINE,
+		WAVE_TRIANGLE,
+		WAVE_SAWTOOTH,
+		WAVE_SQUARE,
+		WAVE_MAX_ENUM
+	};
+	/*
+	enum Direction
+	{
+		DIR_NONE,
+		DIR_UP,
+		DIR_DOWN,
+		DIR_MAX_ENUM
+	};
+
+	enum Phoneme
+	{
+		PHONEME_A,
+		PHONEME_E,
+		PHONEME_I,
+		PHONEME_O,
+		PHONEME_U,
+		PHONEME_AA,
+		PHONEME_AE,
+		PHONEME_AH,
+		PHONEME_AO,
+		PHONEME_EH,
+		PHONEME_ER,
+		PHONEME_IH,
+		PHONEME_IY,
+		PHONEME_UH,
+		PHONEME_UW,
+		PHONEME_B,
+		PHONEME_D,
+		PHONEME_F,
+		PHONEME_G,
+		PHONEME_J,
+		PHONEME_K,
+		PHONEME_L,
+		PHONEME_M,
+		PHONEME_N,
+		PHONEME_P,
+		PHONEME_R,
+		PHONEME_S,
+		PHONEME_T,
+		PHONEME_V,
+		PHONEME_Z,
+		PHONEME_MAX_ENUM
+	};
+	*/
+
+	Effect();
+	virtual ~Effect();
+	Type getType() const;
+
+	static bool getConstant(const char *in, Type &out);
+	static bool getConstant(Type in, const char *&out);
+	static bool getConstant(const char *in, Waveform &out);
+	static bool getConstant(Waveform in, const char *&out);
+	//static bool getConstant(const char *in, Direction &out);
+	//static bool getConstant(Direction in, const char *&out);
+	//static bool getConstant(const char *in, Phoneme &out);
+	//static bool getConstant(Phoneme in, const char *&out);
+	static bool getConstant(const char *in, Parameter &out, Type t);
+	static bool getConstant(Parameter in, const char *&out, Type t);
+	static ParameterType getParameterType(Parameter in);
+
+protected:
+	Type type;
+
+private:
+	static StringMap<Type, TYPE_MAX_ENUM>::Entry typeEntries[];
+	static StringMap<Type, TYPE_MAX_ENUM> types;
+	static StringMap<Waveform, WAVE_MAX_ENUM>::Entry waveformEntries[];
+	static StringMap<Waveform, WAVE_MAX_ENUM> waveforms;
+	//static StringMap<Direction, DIR_MAX_ENUM>::Entry directionEntries[];
+	//static StringMap<Direction, DIR_MAX_ENUM> directions;
+	//static StringMap<Phoneme, PHONEME_MAX_ENUM>::Entry phonemeEntries[];
+	//static StringMap<Phoneme, PHONEME_MAX_ENUM> phonemes;
+#define StringMap LazierAndSlowerButEasilyArrayableStringMap
+	static std::vector<StringMap<Effect::Parameter>::Entry> basicParameters;
+	static std::vector<StringMap<Effect::Parameter>::Entry> reverbParameters;
+	static std::vector<StringMap<Effect::Parameter>::Entry> chorusParameters;
+	static std::vector<StringMap<Effect::Parameter>::Entry> distortionParameters;
+	static std::vector<StringMap<Effect::Parameter>::Entry> echoParameters;
+	static std::vector<StringMap<Effect::Parameter>::Entry> flangerParameters;
+	//static std::vector<StringMap<Effect::Parameter>::Entry> freqshifterParameters;
+	//static std::vector<StringMap<Effect::Parameter>::Entry> morpherParameters;
+	//static std::vector<StringMap<Effect::Parameter>::Entry> pitchshifterParameters;
+	static std::vector<StringMap<Effect::Parameter>::Entry> modulatorParameters;
+	//static std::vector<StringMap<Effect::Parameter>::Entry> autowahParameters;
+	static std::vector<StringMap<Effect::Parameter>::Entry> compressorParameters;
+	static std::vector<StringMap<Effect::Parameter>::Entry> equalizerParameters;
+	static std::map<Type, StringMap<Parameter>> parameterNames;
+#undef StringMap
+	static std::map<Parameter, ParameterType> parameterTypes;
+
+};
+
+} //audio
+} //love
+
+#endif //LOVE_AUDIO_EFFECTS_H

+ 46 - 9
src/modules/audio/Filter.cpp

@@ -48,14 +48,19 @@ bool Filter::getConstant(Type in, const char *&out)
 	return types.find(in, out);
 }
 
-int Filter::getParameterCount(Type in)
+bool Filter::getConstant(const char *in, Parameter &out, Type t)
 {
-	return parameterCount[in];
+	return parameterNames[t].find(in, out);
 }
 
-int Filter::getParameterCount()
+bool Filter::getConstant(Parameter in, const char *&out, Type t)
 {
-	return parameterCount[TYPE_MAX_ENUM];
+	return parameterNames[t].find(in, out);
+}
+
+Filter::ParameterType Filter::getParameterType(Parameter in)
+{
+	return parameterTypes[in];
 }
 
 StringMap<Filter::Type, Filter::TYPE_MAX_ENUM>::Entry Filter::typeEntries[] =
@@ -67,12 +72,44 @@ StringMap<Filter::Type, Filter::TYPE_MAX_ENUM>::Entry Filter::typeEntries[] =
 
 StringMap<Filter::Type, Filter::TYPE_MAX_ENUM> Filter::types(Filter::typeEntries, sizeof(Filter::typeEntries));
 
-std::map<Filter::Type, int> Filter::parameterCount =
+#define StringMap LazierAndSlowerButEasilyArrayableStringMap2
+std::vector<StringMap<Filter::Parameter>::Entry> Filter::basicParameters =
+{
+	{"type", Filter::FILTER_TYPE},
+	{"volume", Filter::FILTER_VOLUME}
+};
+
+std::vector<StringMap<Filter::Parameter>::Entry> Filter::lowpassParameters =
+{
+	{"highgain", Filter::FILTER_HIGHGAIN}
+};
+
+std::vector<StringMap<Filter::Parameter>::Entry> Filter::highpassParameters =
+{
+	{"lowgain", Filter::FILTER_LOWGAIN}
+};
+
+std::vector<StringMap<Filter::Parameter>::Entry> Filter::bandpassParameters =
+{
+	{"lowgain", Filter::FILTER_LOWGAIN},
+	{"highgain", Filter::FILTER_HIGHGAIN}
+};
+
+std::map<Filter::Type, StringMap<Filter::Parameter>> Filter::parameterNames = 
+{
+	{Filter::TYPE_BASIC, Filter::basicParameters},
+	{Filter::TYPE_LOWPASS, Filter::lowpassParameters},
+	{Filter::TYPE_HIGHPASS, Filter::highpassParameters},
+	{Filter::TYPE_BANDPASS, Filter::bandpassParameters},
+};
+#undef StringMap
+
+std::map<Filter::Parameter, Filter::ParameterType> Filter::parameterTypes = 
 {
-	{Filter::TYPE_LOWPASS, 2},
-	{Filter::TYPE_HIGHPASS, 2},
-	{Filter::TYPE_BANDPASS, 3},
-	{Filter::TYPE_MAX_ENUM, 3},
+	{Filter::FILTER_TYPE, Filter::PARAM_TYPE},
+	{Filter::FILTER_VOLUME, Filter::PARAM_FLOAT},
+	{Filter::FILTER_LOWGAIN, Filter::PARAM_FLOAT},
+	{Filter::FILTER_HIGHGAIN, Filter::PARAM_FLOAT}
 };
 
 } //audio

+ 73 - 3
src/modules/audio/Filter.h

@@ -25,6 +25,49 @@
 #include "common/StringMap.h"
 #include <map>
 
+template<typename T>
+class LazierAndSlowerButEasilyArrayableStringMap2
+{
+public:
+	struct Entry
+	{
+		const char *key;
+		T value;
+	};
+	LazierAndSlowerButEasilyArrayableStringMap2()
+	{
+	}
+
+	LazierAndSlowerButEasilyArrayableStringMap2(const std::vector<Entry> &entries)
+	{
+		for (auto entry : entries)
+		{
+			forward[entry.key] = entry.value;
+			reverse[entry.value] = entry.key;
+		}
+	}
+
+	bool find(const char *key, T &t)
+	{
+		if (forward.find(key) == forward.end()) 
+			return false;
+		t = forward[key];
+		return true;
+	}
+
+	bool find(T key, const char *&str) 
+	{
+		if (reverse.find(key) == reverse.end())
+			return false;
+		str = reverse[key];
+		return true;
+	}
+
+private:
+	std::map<std::string, T> forward;
+	std::map<T, const char*> reverse;
+};
+
 namespace love
 {
 namespace audio
@@ -35,20 +78,40 @@ class Filter
 public:
 	enum Type
 	{
+		TYPE_BASIC,
 		TYPE_LOWPASS,
 		TYPE_HIGHPASS,
 		TYPE_BANDPASS,
 		TYPE_MAX_ENUM
 	};
 
+	enum Parameter
+	{
+		FILTER_TYPE,
+		FILTER_VOLUME,
+
+		FILTER_LOWGAIN,
+		FILTER_HIGHGAIN,
+
+		FILTER_MAX_ENUM
+	};
+
+	enum ParameterType
+	{
+		PARAM_TYPE,
+		PARAM_FLOAT,
+		PARAM_MAX_ENUM
+	};
+
 	Filter();
 	virtual ~Filter();
 	Type getType() const;
 
 	static bool getConstant(const char *in, Type &out);
 	static bool getConstant(Type in, const char *&out);
-	static int getParameterCount(Type in);
-	static int getParameterCount();
+	static bool getConstant(const char *in, Parameter &out, Type t);
+	static bool getConstant(Parameter in, const char *&out, Type t);
+	static ParameterType getParameterType(Parameter in);
 
 protected:
 	Type type;
@@ -56,7 +119,14 @@ protected:
 private:
 	static StringMap<Type, TYPE_MAX_ENUM>::Entry typeEntries[];
 	static StringMap<Type, TYPE_MAX_ENUM> types;
-	static std::map<Type, int> parameterCount;
+#define StringMap LazierAndSlowerButEasilyArrayableStringMap2
+	static std::vector<StringMap<Filter::Parameter>::Entry> basicParameters;
+	static std::vector<StringMap<Filter::Parameter>::Entry> lowpassParameters;
+	static std::vector<StringMap<Filter::Parameter>::Entry> highpassParameters;
+	static std::vector<StringMap<Filter::Parameter>::Entry> bandpassParameters;
+	static std::map<Type, StringMap<Parameter>> parameterNames;
+#undef StringMap
+	static std::map<Parameter, ParameterType> parameterTypes;
 };
 
 } //audio

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

@@ -84,8 +84,8 @@ public:
 	virtual void setDirection(float *v) = 0;
 	virtual void getDirection(float *v) const = 0;
 
-	virtual void setCone(float innerAngle, float outerAngle, float outerVolume) = 0;
-	virtual void getCone(float &innerAngle, float &outerAngle, float &outerVolume) const = 0;
+	virtual void setCone(float innerAngle, float outerAngle, float outerVolume, float outerHighGain) = 0;
+	virtual void getCone(float &innerAngle, float &outerAngle, float &outerVolume, float &outerHighGain) const = 0;
 
 	virtual void setRelative(bool enable) = 0;
 	virtual bool isRelative() const = 0;
@@ -104,12 +104,19 @@ public:
 	virtual float getRolloffFactor() const = 0;
 	virtual void setMaxDistance(float distance) = 0;
 	virtual float getMaxDistance() const = 0;
+	virtual void setAirAbsorptionFactor(float factor) = 0;
+	virtual float getAirAbsorptionFactor() const = 0;
 
 	virtual int getChannels() const = 0;
 
-	virtual bool setFilter(Filter::Type type, std::vector<float> &params) = 0;
+	virtual bool setFilter(std::map<Filter::Parameter, float> &params) = 0;
 	virtual bool setFilter() = 0;
-	virtual bool getFilter(Filter::Type &type, std::vector<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, 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 int getFreeBufferCount() const = 0;
 	virtual bool queue(void *data, size_t length, int dataSampleRate, int dataBitDepth, int dataChannels) = 0;

+ 39 - 0
src/modules/audio/null/Audio.cpp

@@ -143,7 +143,16 @@ float Audio::getDopplerScale() const
 {
 	return 1.0f;
 }
+/*
+void setMeter(float)
+{
+}
 
+float getMeter() const
+{
+	return 1.0f;
+}
+*/
 const std::vector<love::audio::RecordingDevice*> &Audio::getRecordingDevices()
 {
 	return capture;
@@ -159,6 +168,36 @@ void Audio::setDistanceModel(DistanceModel distanceModel)
 	this->distanceModel = distanceModel;
 }
 
+bool Audio::setSceneEffect(int, std::map<Effect::Parameter, float> &)
+{
+	return false;
+}
+
+bool Audio::setSceneEffect(int)
+{
+	return false;
+}
+
+bool Audio::getSceneEffect(int, std::map<Effect::Parameter, float> &)
+{
+	return false;
+}
+
+int Audio::getMaxSceneEffects() const
+{
+	return 0;
+}
+
+int Audio::getMaxSourceEffects() const
+{
+	return 0;
+}
+
+bool Audio::isEFXsupported() const
+{
+	return false;
+}
+
 } // null
 } // audio
 } // love

+ 9 - 0
src/modules/audio/null/Audio.h

@@ -70,12 +70,21 @@ public:
 
 	void setDopplerScale(float scale);
 	float getDopplerScale() const;
+	//void setMeter(float scale);
+	//float getMeter() const;
 
 	const std::vector<love::audio::RecordingDevice*> &getRecordingDevices();
 
 	DistanceModel getDistanceModel() const;
 	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);
+	int getMaxSceneEffects() const;
+	int getMaxSourceEffects() const;
+	bool isEFXsupported() const;
+
 private:
 	float volume;
 	DistanceModel distanceModel;

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

@@ -128,18 +128,20 @@ void Source::getDirection(float *) const
 {
 }
 
-void Source::setCone(float innerAngle, float outerAngle, float outerVolume)
+void Source::setCone(float innerAngle, float outerAngle, float outerVolume, float outerHighGain)
 {
 	coneInnerAngle = innerAngle;
 	coneOuterAngle = outerAngle;
 	coneOuterVolume = outerVolume;
+	coneOuterHighGain = outerHighGain;
 }
 
-void Source::getCone(float &innerAngle, float &outerAngle, float &outerVolume) const
+void Source::getCone(float &innerAngle, float &outerAngle, float &outerVolume, float &outerHighGain) const
 {
 	innerAngle = coneInnerAngle;
 	outerAngle = coneOuterAngle;
 	outerVolume = coneOuterVolume;
+	outerHighGain = coneOuterHighGain;
 }
 
 void Source::setRelative(bool enable)
@@ -212,6 +214,16 @@ float Source::getMaxDistance() const
 	return this->maxDistance;
 }
 
+void Source::setAirAbsorptionFactor(float factor)
+{
+	absorptionFactor = factor;
+}
+
+float Source::getAirAbsorptionFactor() const
+{
+	return absorptionFactor;
+}
+
 int Source::getChannels() const
 {
 	return 2;
@@ -227,7 +239,7 @@ bool Source::queue(void *, size_t, int, int, int)
 	return false;
 }
 
-bool Source::setFilter(love::audio::Filter::Type, std::vector<float> &)
+bool Source::setFilter(std::map<Filter::Parameter, float> &)
 {
 	return false;
 }
@@ -237,7 +249,27 @@ bool Source::setFilter()
 	return false;
 }
 
-bool Source::getFilter(love::audio::Filter::Type &, std::vector<float> &)
+bool Source::getFilter(std::map<Filter::Parameter, float> &)
+{
+	return false;
+}
+
+bool Source::setSceneEffect(int, int)
+{
+	return false;
+}
+
+bool Source::setSceneEffect(int, int, std::map<Filter::Parameter, float> &)
+{
+	return false;
+}
+
+bool Source::setSceneEffect(int)
+{
+	return false;
+}
+
+bool Source::getSceneEffect(int, int &, std::map<Filter::Parameter, float> &)
 {
 	return false;
 }

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

@@ -59,8 +59,8 @@ public:
 	virtual void getVelocity(float *v) const;
 	virtual void setDirection(float *v);
 	virtual void getDirection(float *v) const;
-	virtual void setCone(float innerAngle, float outerAngle, float outerVolume);
-	virtual void getCone(float &innerAngle, float &outerAngle, float &outerVolume) const;
+	virtual void setCone(float innerAngle, float outerAngle, float outerVolume, float outerHighGain);
+	virtual void getCone(float &innerAngle, float &outerAngle, float &outerVolume, float &outerHighGain) const;
 	virtual void setRelative(bool enable);
 	virtual bool isRelative() const;
 	void setLooping(bool looping);
@@ -75,14 +75,21 @@ public:
 	virtual float getRolloffFactor() const;
 	virtual void setMaxDistance(float distance);
 	virtual float getMaxDistance() const;
+	virtual void setAirAbsorptionFactor(float factor);
+	virtual float getAirAbsorptionFactor() const;
 	virtual int getChannels() const;
 
 	virtual int getFreeBufferCount() const;
 	virtual bool queue(void *data, size_t length, int dataSampleRate, int dataBitDepth, int dataChannels);
 
-	virtual bool setFilter(love::audio::Filter::Type type, std::vector<float> &params);
+	virtual bool setFilter(std::map<Filter::Parameter, float> &params);
 	virtual bool setFilter();
-	virtual bool getFilter(love::audio::Filter::Type &type, std::vector<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, std::map<Filter::Parameter, float> &params);
+	virtual bool setSceneEffect(int slot);
+	virtual bool getSceneEffect(int slot, int &effect, std::map<Filter::Parameter, float> &params);
 
 private:
 
@@ -91,6 +98,7 @@ private:
 	float coneInnerAngle;
 	float coneOuterAngle;
 	float coneOuterVolume;
+	float coneOuterHighGain;
 	bool relative;
 	bool looping;
 	float minVolume;
@@ -98,6 +106,7 @@ private:
 	float referenceDistance;
 	float rolloffFactor;
 	float maxDistance;
+	float absorptionFactor;
 
 }; // Source
 

+ 160 - 8
src/modules/audio/openal/Audio.cpp

@@ -102,7 +102,13 @@ Audio::Audio()
 	if (device == nullptr)
 		throw love::Exception("Could not open device.");
 
-	context = alcCreateContext(device, nullptr);
+	#ifdef ALC_EXT_EFX
+	ALint attribs[4] = { ALC_MAX_AUXILIARY_SENDS, MAX_SOURCE_EFFECTS, 0, 0 }; 
+	#else
+	ALint *attribs = nullptr;
+	#endif
+
+	context = alcCreateContext(device, attribs);
 
 	if (context == nullptr)
 		throw love::Exception("Could not create context.");
@@ -110,20 +116,53 @@ Audio::Audio()
 	if (!alcMakeContextCurrent(context) || alcGetError(device) != ALC_NO_ERROR)
 		throw love::Exception("Could not make context current.");
 
+	#ifdef ALC_EXT_EFX
 	initializeEFX();
 
+	alcGetIntegerv(device, ALC_MAX_AUXILIARY_SENDS, 1, &MAX_SOURCE_EFFECTS);
+
+	alGetError(); 
+	if (alGenAuxiliaryEffectSlots)
+	{
+		for (int i = 0; i < MAX_SCENE_EFFECTS; i++)
+		{
+			ALuint slot;
+			alGenAuxiliaryEffectSlots(1, &slot);
+			if (alGetError() == AL_NO_ERROR)
+			{
+				effectIndex[slot] = effectSlots.size();
+				effectSlots.push_back(slot);
+				effects.push_back(nullptr);
+			}
+			else
+			{
+				MAX_SCENE_EFFECTS = i;
+				break;
+			}
+		}
+	}
+	else
+		MAX_SCENE_EFFECTS = MAX_SOURCE_EFFECTS = 0;
+	#else
+	MAX_SCENE_EFFECTS = MAX_SOURCE_EFFECTS = 0;
+	#endif
+
 	try
 	{
 		pool = new Pool();
 	}
 	catch (love::Exception &)
 	{
+		for (auto c : capture)
+			delete c;
+		#ifdef ALC_EXT_EFX
+		if (alDeleteAuxiliaryEffectSlots)
+			for (auto slot : effectSlots)
+				alDeleteAuxiliaryEffectSlots(1, &slot);
+		#endif
 		alcMakeContextCurrent(nullptr);
 		alcDestroyContext(context);
 		alcCloseDevice(device);
-		for (auto c : capture)
-			delete c;
-
 		throw;
 	}
 
@@ -138,15 +177,21 @@ Audio::~Audio()
 
 	delete poolThread;
 	delete pool;
-
+	for (auto c : capture)
+		delete c;
+	#ifdef ALC_EXT_EFX
+	for (auto e : effects)
+		if (e != nullptr)
+			delete e;
+	if (alDeleteAuxiliaryEffectSlots)
+		for (auto slot : effectSlots)
+			alDeleteAuxiliaryEffectSlots(1, &slot);
+	#endif
 	alcMakeContextCurrent(nullptr);
 	alcDestroyContext(context);
 	alcCloseDevice(device);
-	for (auto c : capture)
-		delete c;
 }
 
-
 const char *Audio::getName() const
 {
 	return "love.audio.openal";
@@ -269,7 +314,23 @@ float Audio::getDopplerScale() const
 {
 	return alGetFloat(AL_DOPPLER_FACTOR);
 }
+/*
+void Audio::setMeter(float scale)
+{
+	if (scale >= 0.0f)
+	{
+		metersPerUnit = scale;
+		#ifdef ALC_EXT_EFX
+		alListenerf(AL_METERS_PER_UNIT, scale);
+		#endif
+	}
+}
 
+float Audio::getMeter() const
+{
+	return metersPerUnit;
+}
+*/
 Audio::DistanceModel Audio::getDistanceModel() const
 {
 	return distanceModel;
@@ -384,6 +445,97 @@ const std::vector<love::audio::RecordingDevice*> &Audio::getRecordingDevices()
 	return capture;
 }
 
+bool Audio::setSceneEffect(int slot, std::map<Effect::Parameter, float> &params)
+{
+	if (slot < 0 || slot >= MAX_SCENE_EFFECTS)
+		return false;
+
+	if (!effects[slot])
+		effects[slot] = new Effect();
+
+	bool result = effects[slot]->setParams(params);
+
+	#ifdef ALC_EXT_EFX
+	if (alAuxiliaryEffectSloti)
+	{
+		if (result == true)
+		{
+			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());
+		}
+		else
+			alAuxiliaryEffectSloti(effectSlots[slot], AL_EFFECTSLOT_EFFECT, AL_EFFECT_NULL);
+		alGetError();
+	}
+	#endif
+
+	return result;
+}
+
+bool Audio::setSceneEffect(int slot)
+{
+	if (slot < 0 || slot >= MAX_SCENE_EFFECTS)
+		return false;
+
+	if (effects[slot])
+		delete effects[slot];
+
+	effects[slot] = nullptr;
+
+	#ifdef ALC_EXT_EFX
+	if (alAuxiliaryEffectSloti)
+		alAuxiliaryEffectSloti(effectSlots[slot], AL_EFFECTSLOT_EFFECT, AL_EFFECT_NULL);
+	#endif
+
+	return true;
+}
+
+bool Audio::getSceneEffect(int slot, std::map<Effect::Parameter, float> &params)
+{
+	if (slot < 0 || slot >= MAX_SCENE_EFFECTS)
+		return false;
+
+	if (!effects[slot])
+		return false;
+
+	params = effects[slot]->getParams();
+
+	return true;
+}
+
+int Audio::getMaxSceneEffects() const
+{
+	return MAX_SCENE_EFFECTS;
+}
+
+int Audio::getMaxSourceEffects() const
+{
+	return MAX_SOURCE_EFFECTS;
+}
+
+bool Audio::isEFXsupported() const
+{
+	#ifdef ALC_EXT_EFX
+	return (alGenEffects != nullptr);
+	#else
+	return false;
+	#endif
+}
+
+ALuint Audio::getSceneEffectID(int slot)
+{
+	if (slot < 0 || slot >= MAX_SCENE_EFFECTS)
+		return effectSlots[0];
+
+	return effectSlots[slot];
+}
+
+int Audio::getSceneEffectIndex(ALuint effect)
+{
+	return effectIndex[effect];
+}
+
 #ifdef ALC_EXT_EFX
 LPALGENEFFECTS alGenEffects = nullptr;
 LPALDELETEEFFECTS alDeleteEffects = nullptr;

+ 21 - 1
src/modules/audio/openal/Audio.h

@@ -35,6 +35,7 @@
 #include "sound/SoundData.h"
 
 #include "Source.h"
+#include "Effect.h"
 #include "Pool.h"
 #include "thread/threads.h"
 
@@ -105,12 +106,24 @@ public:
 
 	void setDopplerScale(float scale);
 	float getDopplerScale() const;
+	//void setMeter(float scale);
+	//float getMeter() const;
 
 	const std::vector<love::audio::RecordingDevice*> &getRecordingDevices();
 
 	DistanceModel getDistanceModel() const;
 	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);
+	int getMaxSceneEffects() const;
+	int getMaxSourceEffects() const;
+	bool isEFXsupported() const;
+
+	ALuint getSceneEffectID(int slot);
+	int getSceneEffectIndex(ALuint effect);
+
 private:
 	void initializeEFX();
 	// The OpenAL device.
@@ -122,6 +135,13 @@ private:
 	// The OpenAL context.
 	ALCcontext *context;
 
+	// 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;
+
 	// The Pool.
 	Pool *pool;
 
@@ -148,7 +168,7 @@ private:
 	PoolThread *poolThread;
 
 	DistanceModel distanceModel;
-
+	//float metersPerUnit = 1.0;
 }; // Audio
 
 #ifdef ALC_EXT_EFX

+ 417 - 0
src/modules/audio/openal/Effect.cpp

@@ -0,0 +1,417 @@
+/**
+ * Copyright (c) 2006-2016 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#include "Effect.h"
+#include "common/Exception.h"
+
+#include <cmath>
+#include <iostream>
+
+namespace love
+{
+namespace audio
+{
+namespace openal
+{
+
+//base class
+Effect::Effect()
+{
+	generateEffect();
+}
+
+Effect::Effect(const Effect &s)
+	: Effect()
+{
+	setParams(s.getParams());
+}
+
+Effect::~Effect()
+{
+	deleteEffect();
+}
+
+Effect *Effect::clone()
+{
+	return new Effect(*this);
+}
+
+bool Effect::generateEffect()
+{
+	#ifdef ALC_EXT_EFX
+	if (!alGenEffects)
+		return false;
+
+	if (effect != AL_EFFECT_NULL)
+		return true;
+
+	alGenEffects(1, &effect);
+	if (alGetError() != AL_NO_ERROR)
+		throw love::Exception("Failed to create sound Effect.");
+
+	return true;
+	#else
+	return false;
+	#endif
+}
+
+void Effect::deleteEffect()
+{
+	#ifdef ALC_EXT_EFX
+	if (effect != AL_EFFECT_NULL)
+		alDeleteEffects(1, &effect);
+	#endif
+	effect = AL_EFFECT_NULL;
+}
+
+ALuint Effect::getEffect() const
+{
+	return effect;
+}
+
+bool Effect::setParams(const std::map<Parameter, float> &params)
+{
+	this->params = params;
+	type = static_cast<Type>(this->params[EFFECT_TYPE]);
+
+	if (!generateEffect())
+		return false;
+
+	#ifdef ALC_EXT_EFX
+	//parameter table without EFFECT_TYPE entry is illegal
+	switch (type)
+	{
+	case TYPE_REVERB:
+		alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_REVERB);
+		break;
+	case TYPE_CHORUS:
+		alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_CHORUS);
+		break;
+	case TYPE_DISTORTION:
+		alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_DISTORTION);
+		break;
+	case TYPE_ECHO:
+		alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_ECHO);
+		break;
+	case TYPE_FLANGER:
+		alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_FLANGER);
+		break;
+/*
+	case TYPE_FREQSHIFTER:
+		alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_FREQUENCY_SHIFTER);
+		break;
+	case TYPE_MORPHER:
+		alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_VOCAL_MORPHER);
+		break;
+	case TYPE_PITCHSHIFTER:
+		alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_PITCH_SHIFTER);
+		break;
+*/
+	case TYPE_MODULATOR:
+		alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_RING_MODULATOR);
+		break;
+/*
+	case TYPE_AUTOWAH:
+		alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_AUTOWAH);
+		break;
+*/
+	case TYPE_COMPRESSOR:
+		alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_COMPRESSOR);
+		break;
+	case TYPE_EQUALIZER:
+		alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_EQUALIZER);
+		break;
+	case TYPE_BASIC:
+	case TYPE_MAX_ENUM:
+		break;
+	}
+
+	//failed to make effect specific type - not supported etc.
+	if (alGetError() != AL_NO_ERROR)
+	{
+		deleteEffect();
+		return false;
+	}
+
+	#define clampf(v,l,h) fmax(fmin((v),(h)),(l))
+	#define PARAMSTR(i,e,v) effect,AL_##e##_##v,clampf(getValue(i,AL_##e##_DEFAULT_##v),AL_##e##_MIN_##v,AL_##e##_MAX_##v)
+	switch (type)
+	{
+	case TYPE_REVERB:
+	{
+		alEffectf(PARAMSTR(REVERB_GAIN,REVERB,GAIN));
+		alEffectf(PARAMSTR(REVERB_HFGAIN,REVERB,GAINHF));
+		alEffectf(PARAMSTR(REVERB_DENSITY,REVERB,DENSITY));
+		alEffectf(PARAMSTR(REVERB_DIFFUSION,REVERB,DIFFUSION));
+		alEffectf(PARAMSTR(REVERB_DECAY,REVERB,DECAY_TIME));
+		alEffectf(PARAMSTR(REVERB_HFDECAY,REVERB,DECAY_HFRATIO));
+		alEffectf(PARAMSTR(REVERB_EARLYGAIN,REVERB,REFLECTIONS_GAIN));
+		alEffectf(PARAMSTR(REVERB_EARLYDELAY,REVERB,REFLECTIONS_DELAY));
+		alEffectf(PARAMSTR(REVERB_LATEGAIN,REVERB,LATE_REVERB_GAIN));
+		alEffectf(PARAMSTR(REVERB_LATEDELAY,REVERB,LATE_REVERB_DELAY));;
+		alEffectf(PARAMSTR(REVERB_ROLLOFF,REVERB,ROOM_ROLLOFF_FACTOR));
+		alEffectf(PARAMSTR(REVERB_AIRHFGAIN,REVERB,AIR_ABSORPTION_GAINHF));
+		alEffecti(effect, AL_REVERB_DECAY_HFLIMIT, getValue(REVERB_HFLIMITER, 0));
+		break;
+	}
+	case TYPE_CHORUS:
+	{
+		Waveform wave = static_cast<Waveform>(getValue(CHORUS_WAVEFORM, static_cast<int>(WAVE_MAX_ENUM)));
+		if (wave == WAVE_SINE)
+			alEffecti(effect, AL_CHORUS_WAVEFORM, AL_CHORUS_WAVEFORM_SINUSOID);
+		else if (wave == WAVE_TRIANGLE)
+			alEffecti(effect, AL_CHORUS_WAVEFORM, AL_CHORUS_WAVEFORM_TRIANGLE);
+		else
+			alEffecti(effect, AL_CHORUS_WAVEFORM, AL_CHORUS_DEFAULT_WAVEFORM);
+
+		alEffecti(PARAMSTR(CHORUS_PHASE,CHORUS,PHASE));
+		alEffectf(PARAMSTR(CHORUS_RATE,CHORUS,RATE));
+		alEffectf(PARAMSTR(CHORUS_DEPTH,CHORUS,DEPTH));
+		alEffectf(PARAMSTR(CHORUS_FEEDBACK,CHORUS,FEEDBACK));
+		alEffectf(PARAMSTR(CHORUS_DELAY,CHORUS,DELAY));
+		break;
+	}
+	case TYPE_DISTORTION:
+		alEffectf(PARAMSTR(DISTORTION_GAIN,DISTORTION,GAIN));
+		alEffectf(PARAMSTR(DISTORTION_EDGE,DISTORTION,EDGE));
+		alEffectf(PARAMSTR(DISTORTION_LOWCUT,DISTORTION,LOWPASS_CUTOFF));
+		alEffectf(PARAMSTR(DISTORTION_EQCENTER,DISTORTION,EQCENTER));
+		alEffectf(PARAMSTR(DISTORTION_EQBAND,DISTORTION,EQBANDWIDTH));
+		break;
+
+	case TYPE_ECHO:
+		alEffectf(PARAMSTR(ECHO_DELAY,ECHO,DELAY));
+		alEffectf(PARAMSTR(ECHO_LRDELAY,ECHO,LRDELAY));
+		alEffectf(PARAMSTR(ECHO_DAMPING,ECHO,DAMPING));
+		alEffectf(PARAMSTR(ECHO_FEEDBACK,ECHO,FEEDBACK));
+		alEffectf(PARAMSTR(ECHO_SPREAD,ECHO,SPREAD));
+		break;
+
+	case TYPE_FLANGER:
+	{
+		Waveform wave = static_cast<Waveform>(getValue(FLANGER_WAVEFORM, static_cast<int>(WAVE_MAX_ENUM)));
+		if (wave == WAVE_SINE)
+			alEffecti(effect, AL_FLANGER_WAVEFORM, AL_FLANGER_WAVEFORM_SINUSOID);
+		else if (wave == WAVE_TRIANGLE)
+			alEffecti(effect, AL_FLANGER_WAVEFORM, AL_FLANGER_WAVEFORM_TRIANGLE);
+		else
+			alEffecti(effect, AL_FLANGER_WAVEFORM, AL_FLANGER_DEFAULT_WAVEFORM);
+
+		alEffecti(PARAMSTR(FLANGER_PHASE,FLANGER,PHASE));
+		alEffectf(PARAMSTR(FLANGER_RATE,FLANGER,RATE));
+		alEffectf(PARAMSTR(FLANGER_DEPTH,FLANGER,DEPTH));
+		alEffectf(PARAMSTR(FLANGER_FEEDBACK,FLANGER,FEEDBACK));
+		alEffectf(PARAMSTR(FLANGER_DELAY,FLANGER,DELAY));
+		break;
+	}
+/*
+	case TYPE_FREQSHIFTER:
+	{
+		alEffectf(PARAMSTR(FREQSHIFTER_FREQ,FREQUENCY_SHIFTER,FREQUENCY)); 
+
+		Direction dir = static_cast<Direction>(getValue(FREQSHIFTER_LEFTDIR, static_cast<int>(DIR_MAX_ENUM)));
+		if (dir == DIR_NONE)
+			alEffecti(effect, AL_FREQUENCY_SHIFTER_LEFT_DIRECTION, AL_FREQUENCY_SHIFTER_DIRECTION_OFF);
+		else if(dir == DIR_UP)
+			alEffecti(effect, AL_FREQUENCY_SHIFTER_LEFT_DIRECTION, AL_FREQUENCY_SHIFTER_DIRECTION_UP);
+		else if(dir == DIR_DOWN)
+			alEffecti(effect, AL_FREQUENCY_SHIFTER_LEFT_DIRECTION, AL_FREQUENCY_SHIFTER_DIRECTION_DOWN);
+		else 
+			alEffecti(effect, AL_FREQUENCY_SHIFTER_LEFT_DIRECTION, AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION);
+
+		dir = static_cast<Direction>(getValue(FREQSHIFTER_RIGHTDIR, static_cast<int>(DIR_MAX_ENUM)));
+		if (dir == DIR_NONE)
+			alEffecti(effect, AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION, AL_FREQUENCY_SHIFTER_DIRECTION_OFF);
+		else if(dir == DIR_UP)
+			alEffecti(effect, AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION, AL_FREQUENCY_SHIFTER_DIRECTION_UP);
+		else if(dir == DIR_DOWN)
+			alEffecti(effect, AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION, AL_FREQUENCY_SHIFTER_DIRECTION_DOWN);
+		else 
+			alEffecti(effect, AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION, AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION);
+		break;
+	}
+	case TYPE_MORPHER:
+	{
+		Waveform wave = static_cast<Waveform>(getValue(MORPHER_WAVEFORM, static_cast<int>(WAVE_MAX_ENUM)));
+		if (wave == WAVE_SINE)
+			alEffecti(effect, AL_VOCAL_MORPHER_WAVEFORM, AL_VOCAL_MORPHER_WAVEFORM_SINUSOID);
+		else if (wave == WAVE_TRIANGLE)
+			alEffecti(effect, AL_VOCAL_MORPHER_WAVEFORM, AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE);
+		else if (wave == WAVE_SAWTOOTH)
+			alEffecti(effect, AL_VOCAL_MORPHER_WAVEFORM, AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH);
+		else
+			alEffecti(effect, AL_VOCAL_MORPHER_WAVEFORM, AL_VOCAL_MORPHER_DEFAULT_WAVEFORM);
+
+		Phoneme phoneme = static_cast<Phoneme>(getValue(MORPHER_PHONEMEA, static_cast<int>(PHONEME_MAX_ENUM)));
+		if (phoneme == PHONEME_MAX_ENUM)
+			alEffecti(effect, AL_VOCAL_MORPHER_PHONEMEA, AL_VOCAL_MORPHER_DEFAULT_PHONEMEA);
+		else
+			alEffecti(effect, AL_VOCAL_MORPHER_PHONEMEA, phonemeMap[phoneme]);
+
+		phoneme = static_cast<Phoneme>(getValue(MORPHER_PHONEMEB, static_cast<int>(PHONEME_MAX_ENUM)));
+		if (phoneme == PHONEME_MAX_ENUM)
+			alEffecti(effect, AL_VOCAL_MORPHER_PHONEMEB, AL_VOCAL_MORPHER_DEFAULT_PHONEMEB);
+		else
+			alEffecti(effect, AL_VOCAL_MORPHER_PHONEMEB, phonemeMap[phoneme]);
+
+		alEffectf(PARAMSTR(MORPHER_RATE,VOCAL_MORPHER,RATE));
+		alEffecti(PARAMSTR(MORPHER_TUNEA,VOCAL_MORPHER,PHONEMEA_COARSE_TUNING));
+		alEffecti(PARAMSTR(MORPHER_TUNEB,VOCAL_MORPHER,PHONEMEB_COARSE_TUNING));
+		break;
+	}
+	case TYPE_PITCHSHIFTER:
+	{
+		float tune = getValue(PITCHSHIFTER_PITCH, (float)AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE + (float)(AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE - 50) / 100.0 );
+
+		int coarse = (int)floor(tune);
+		int fine = (int)(fmod(tune, 1.0)*100.0);
+		if (fine > 50)
+		{
+			fine -= 100;
+			coarse += 1;
+		}
+		else if (fine < -50)
+		{
+			fine += 100;
+			coarse -= 1;
+		}
+		if (coarse > AL_PITCH_SHIFTER_MAX_COARSE_TUNE)
+		{
+			coarse = AL_PITCH_SHIFTER_MAX_COARSE_TUNE;
+			fine = AL_PITCH_SHIFTER_MAX_FINE_TUNE;
+		}
+		else if (coarse < AL_PITCH_SHIFTER_MIN_COARSE_TUNE)
+		{
+			coarse = AL_PITCH_SHIFTER_MIN_COARSE_TUNE;
+			fine = AL_PITCH_SHIFTER_MIN_FINE_TUNE;
+		}
+		alEffecti(effect, AL_PITCH_SHIFTER_COARSE_TUNE, coarse);
+		alEffecti(effect, AL_PITCH_SHIFTER_FINE_TUNE, fine);
+		break;
+	}
+*/
+	case TYPE_MODULATOR:
+	{
+		Waveform wave = static_cast<Waveform>(getValue(MODULATOR_WAVEFORM,static_cast<int>(WAVE_MAX_ENUM)));
+		if (wave == WAVE_SINE)
+			alEffecti(effect, AL_RING_MODULATOR_WAVEFORM, AL_RING_MODULATOR_SINUSOID);
+		else if (wave == WAVE_SAWTOOTH)
+			alEffecti(effect, AL_RING_MODULATOR_WAVEFORM, AL_RING_MODULATOR_SAWTOOTH);
+		else if (wave == WAVE_SQUARE)
+			alEffecti(effect, AL_RING_MODULATOR_WAVEFORM, AL_RING_MODULATOR_SQUARE);
+		else
+			alEffecti(effect, AL_RING_MODULATOR_WAVEFORM, AL_RING_MODULATOR_DEFAULT_WAVEFORM);
+
+		alEffectf(PARAMSTR(MODULATOR_FREQ,RING_MODULATOR,FREQUENCY));
+		alEffectf(PARAMSTR(MODULATOR_HIGHCUT,RING_MODULATOR,HIGHPASS_CUTOFF));
+		break;
+	}
+/*
+	case TYPE_AUTOWAH:
+		alEffectf(PARAMSTR(AUTOWAH_ATTACK,AUTOWAH,ATTACK_TIME));
+		alEffectf(PARAMSTR(AUTOWAH_RELEASE,AUTOWAH,RELEASE_TIME));
+		alEffectf(PARAMSTR(AUTOWAH_RESONANCE,AUTOWAH,RESONANCE));
+		alEffectf(PARAMSTR(AUTOWAH_PEAKGAIN,AUTOWAH,PEAK_GAIN));
+		break;
+*/
+	case TYPE_COMPRESSOR:
+		alEffecti(effect, AL_COMPRESSOR_ONOFF, getValue(COMPRESSOR_ENABLE,static_cast<int>(AL_COMPRESSOR_DEFAULT_ONOFF)));
+		break;
+
+	case TYPE_EQUALIZER:
+		alEffectf(PARAMSTR(EQUALIZER_LOWGAIN,EQUALIZER,LOW_GAIN));
+		alEffectf(PARAMSTR(EQUALIZER_LOWCUT,EQUALIZER,LOW_CUTOFF));
+		alEffectf(PARAMSTR(EQUALIZER_MID1GAIN,EQUALIZER,MID1_GAIN));
+		alEffectf(PARAMSTR(EQUALIZER_MID1FREQ,EQUALIZER,MID1_CENTER));
+		alEffectf(PARAMSTR(EQUALIZER_MID1BAND,EQUALIZER,MID1_WIDTH));
+		alEffectf(PARAMSTR(EQUALIZER_MID2GAIN,EQUALIZER,MID2_GAIN));
+		alEffectf(PARAMSTR(EQUALIZER_MID2FREQ,EQUALIZER,MID2_CENTER));
+		alEffectf(PARAMSTR(EQUALIZER_MID2BAND,EQUALIZER,MID2_WIDTH));
+		alEffectf(PARAMSTR(EQUALIZER_HIGHGAIN,EQUALIZER,HIGH_GAIN));
+		alEffectf(PARAMSTR(EQUALIZER_HIGHCUT,EQUALIZER,HIGH_CUTOFF));
+		break;
+	case TYPE_BASIC:
+	case TYPE_MAX_ENUM:
+		break;
+	}
+	#undef PARAMSTR
+	#undef clampf
+	//alGetError();
+
+	return true;
+	#else
+	return false;
+	#endif //ALC_EXT_EFX
+}
+
+const std::map<Effect::Parameter, float> &Effect::getParams() const
+{
+	return params;
+}
+
+float Effect::getValue(Parameter in, float def) const
+{
+	return params.find(in) == params.end() ? def : params.at(in);
+}
+
+int Effect::getValue(Parameter in, int def) const
+{
+	return params.find(in) == params.end() ? def : static_cast<int>(params.at(in));
+}
+
+/*
+std::map<Effect::Phoneme, ALint> Effect::phonemeMap = 
+{
+	{Effect::PHONEME_A, AL_VOCAL_MORPHER_PHONEME_A},
+	{Effect::PHONEME_E, AL_VOCAL_MORPHER_PHONEME_E},
+	{Effect::PHONEME_I, AL_VOCAL_MORPHER_PHONEME_I},
+	{Effect::PHONEME_O, AL_VOCAL_MORPHER_PHONEME_O},
+	{Effect::PHONEME_U, AL_VOCAL_MORPHER_PHONEME_U},
+	{Effect::PHONEME_AA, AL_VOCAL_MORPHER_PHONEME_AA},
+	{Effect::PHONEME_AE, AL_VOCAL_MORPHER_PHONEME_AE},
+	{Effect::PHONEME_AH, AL_VOCAL_MORPHER_PHONEME_AH},
+	{Effect::PHONEME_AO, AL_VOCAL_MORPHER_PHONEME_AO},
+	{Effect::PHONEME_EH, AL_VOCAL_MORPHER_PHONEME_EH},
+	{Effect::PHONEME_ER, AL_VOCAL_MORPHER_PHONEME_ER},
+	{Effect::PHONEME_IH, AL_VOCAL_MORPHER_PHONEME_IH},
+	{Effect::PHONEME_IY, AL_VOCAL_MORPHER_PHONEME_IY},
+	{Effect::PHONEME_UH, AL_VOCAL_MORPHER_PHONEME_UH},
+	{Effect::PHONEME_UW, AL_VOCAL_MORPHER_PHONEME_UW},
+	{Effect::PHONEME_B, AL_VOCAL_MORPHER_PHONEME_B},
+	{Effect::PHONEME_D, AL_VOCAL_MORPHER_PHONEME_D},
+	{Effect::PHONEME_F, AL_VOCAL_MORPHER_PHONEME_F},
+	{Effect::PHONEME_G, AL_VOCAL_MORPHER_PHONEME_G},
+	{Effect::PHONEME_J, AL_VOCAL_MORPHER_PHONEME_J},
+	{Effect::PHONEME_K, AL_VOCAL_MORPHER_PHONEME_K},
+	{Effect::PHONEME_L, AL_VOCAL_MORPHER_PHONEME_L},
+	{Effect::PHONEME_M, AL_VOCAL_MORPHER_PHONEME_M},
+	{Effect::PHONEME_N, AL_VOCAL_MORPHER_PHONEME_N},
+	{Effect::PHONEME_P, AL_VOCAL_MORPHER_PHONEME_P},
+	{Effect::PHONEME_R, AL_VOCAL_MORPHER_PHONEME_R},
+	{Effect::PHONEME_S, AL_VOCAL_MORPHER_PHONEME_S},
+	{Effect::PHONEME_T, AL_VOCAL_MORPHER_PHONEME_T},
+	{Effect::PHONEME_V, AL_VOCAL_MORPHER_PHONEME_V},
+	{Effect::PHONEME_Z, AL_VOCAL_MORPHER_PHONEME_Z}
+};
+*/
+
+} //openal
+} //audio
+} //love

+ 87 - 0
src/modules/audio/openal/Effect.h

@@ -0,0 +1,87 @@
+/**
+ * Copyright (c) 2006-2016 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#ifndef LOVE_AUDIO_OPENAL_EFFECTS_H
+#define LOVE_AUDIO_OPENAL_EFFECTS_H
+
+// OpenAL
+#ifdef LOVE_APPLE_USE_FRAMEWORKS // Frameworks have different include paths.
+#ifdef LOVE_IOS
+#include <OpenAL/alc.h>
+#include <OpenAL/al.h>
+#else
+#include <OpenAL-Soft/alc.h>
+#include <OpenAL-Soft/al.h>
+#include <OpenAL-Soft/alext.h>
+#endif
+#else
+#include <AL/alc.h>
+#include <AL/al.h>
+#include <AL/alext.h>
+#endif
+
+#include <vector>
+#include <map>
+
+#include "audio/Effect.h"
+#include "Audio.h"
+
+#ifndef AL_EFFECT_NULL
+#define AL_EFFECT_NULL (0)
+#endif
+
+#ifndef AL_EFFECTSLOT_NULL
+#define AL_EFFECTSLOT_NULL (0)
+#endif
+
+namespace love
+{
+namespace audio
+{
+namespace openal
+{
+
+class Effect : public love::audio::Effect
+{
+public:
+	Effect();
+	Effect(const Effect &s);
+	virtual ~Effect();
+	virtual Effect *clone();
+	ALuint getEffect() const;
+	virtual bool setParams(const std::map<Parameter, float> &params);
+	virtual const std::map<Parameter, float> &getParams() const;
+
+private:
+	bool generateEffect();
+	void deleteEffect();
+	float getValue(Parameter in, float def) const;
+	int getValue(Parameter in, int def) const;
+
+	ALuint effect = AL_EFFECT_NULL;
+	std::map<Parameter, float> params;
+	//static std::map<Phoneme, ALint> phonemeMap;
+};
+
+} //openal
+} //audio
+} //love
+
+#endif //LOVE_AUDIO_OPENAL_EFFECTS_H

+ 64 - 33
src/modules/audio/openal/Filter.cpp

@@ -21,6 +21,8 @@
 #include "Filter.h"
 #include "common/Exception.h"
 
+#include <cmath>
+
 namespace love
 {
 namespace audio
@@ -28,44 +30,54 @@ namespace audio
 namespace openal
 {
 
-//clamp values silently to avoid randomly throwing errors due to implementation differences
-float clampf(float val, float min, float max)
-{
-	if      (val < min) val = min;
-	else if (val > max) val = max;
-	return val;
-}
-
 //base class
 Filter::Filter()
 {
-	#ifdef ALC_EXT_EFX
-	if (!alGenFilters)
-		return;
-
-	alGenFilters(1, &filter);
-	if (alGetError() != AL_NO_ERROR)
-		throw love::Exception("Failed to create sound Filter.");
-	#endif
+	generateFilter();
 }
 
 Filter::Filter(const Filter &s)
 	: Filter()
 {
-	setParams(s.getType(), s.getParams());
+	setParams(s.getParams());
 }
 
 Filter::~Filter()
+{
+	deleteFilter();
+}
+
+Filter *Filter::clone()
+{
+	return new Filter(*this);
+}
+
+bool Filter::generateFilter()
 {
 	#ifdef ALC_EXT_EFX
+	if (!alGenFilters)
+		return false;
+
 	if (filter != AL_FILTER_NULL)
-		alDeleteFilters(1, &filter);
+		return true;
+
+	alGenFilters(1, &filter);
+	if (alGetError() != AL_NO_ERROR)
+		throw love::Exception("Failed to create sound Filter.");
+
+	return true;
+	#else
+	return false;
 	#endif
 }
 
-Filter *Filter::clone()
+void Filter::deleteFilter()
 {
-	return new Filter(*this);
+	#ifdef ALC_EXT_EFX
+	if (filter != AL_FILTER_NULL)
+		alDeleteFilters(1, &filter);
+	#endif
+	filter = AL_FILTER_NULL;
 }
 
 ALuint Filter::getFilter() const
@@ -73,12 +85,12 @@ ALuint Filter::getFilter() const
 	return filter;
 }
 
-bool Filter::setParams(Type type, const std::vector<float> &params)
+bool Filter::setParams(const std::map<Parameter, float> &params)
 {
-	this->type = type;
 	this->params = params;
+	type = static_cast<Type>(this->params[FILTER_TYPE]);
 
-	if (filter == AL_FILTER_NULL)
+	if (!generateFilter())
 		return false;
 
 	#ifdef ALC_EXT_EFX
@@ -93,6 +105,7 @@ bool Filter::setParams(Type type, const std::vector<float> &params)
 	case TYPE_BANDPASS:
 		alFilteri(filter, AL_FILTER_TYPE, AL_FILTER_BANDPASS);
 		break;
+	case TYPE_BASIC:
 	case TYPE_MAX_ENUM:
 		break;
 	}
@@ -100,38 +113,56 @@ bool Filter::setParams(Type type, const std::vector<float> &params)
 	//failed to make filter specific type - not supported etc.
 	if (alGetError() != AL_NO_ERROR)
 	{
-		filter = AL_FILTER_NULL;
+		deleteFilter();
 		return false;
 	}
 
+	#define clampf(v,l,h) fmax(fmin((v),(h)),(l))
+	#define PARAMSTR(i,e,v) filter,AL_##e##_##v,clampf(getValue(i,AL_##e##_DEFAULT_##v),AL_##e##_MIN_##v,AL_##e##_MAX_##v)
 	switch (type)
 	{
 	case TYPE_LOWPASS:
-		alFilterf(filter, AL_LOWPASS_GAIN, clampf(params[0], AL_LOWPASS_MIN_GAIN, AL_LOWPASS_MAX_GAIN));
-		alFilterf(filter, AL_LOWPASS_GAINHF, clampf(params[1], AL_LOWPASS_MIN_GAINHF, AL_LOWPASS_MAX_GAINHF));
+		alFilterf(PARAMSTR(FILTER_VOLUME,LOWPASS,GAIN));
+		alFilterf(PARAMSTR(FILTER_HIGHGAIN,LOWPASS,GAINHF));
 		break;
 	case TYPE_HIGHPASS:
-		alFilterf(filter, AL_HIGHPASS_GAIN, clampf(params[0], AL_HIGHPASS_MIN_GAIN, AL_HIGHPASS_MAX_GAIN));
-		alFilterf(filter, AL_HIGHPASS_GAINLF, clampf(params[1], AL_HIGHPASS_MIN_GAINLF, AL_HIGHPASS_MAX_GAINLF));
+		alFilterf(PARAMSTR(FILTER_VOLUME,HIGHPASS,GAIN));
+		alFilterf(PARAMSTR(FILTER_LOWGAIN,HIGHPASS,GAINLF));
 		break;
 	case TYPE_BANDPASS:
-		alFilterf(filter, AL_BANDPASS_GAIN, clampf(params[0], AL_BANDPASS_MIN_GAIN, AL_BANDPASS_MAX_GAIN));
-		alFilterf(filter, AL_BANDPASS_GAINLF, clampf(params[1], AL_BANDPASS_MIN_GAINLF, AL_BANDPASS_MAX_GAINLF));
-		alFilterf(filter, AL_BANDPASS_GAINHF, clampf(params[2], AL_BANDPASS_MIN_GAINHF, AL_BANDPASS_MAX_GAINHF));
+		alFilterf(PARAMSTR(FILTER_VOLUME,BANDPASS,GAIN));
+		alFilterf(PARAMSTR(FILTER_LOWGAIN,BANDPASS,GAINLF));
+		alFilterf(PARAMSTR(FILTER_HIGHGAIN,BANDPASS,GAINHF));
 		break;
+	case TYPE_BASIC:
 	case TYPE_MAX_ENUM:
 		break;
 	}
-	#endif
+	#undef clampf
+	#undef PARAMSTR
+	//alGetError();
 
 	return true;
+	#else
+	return false;
+	#endif
 }
 
-const std::vector<float> &Filter::getParams() const
+const std::map<Filter::Parameter, float> &Filter::getParams() const
 {
 	return params;
 }
 
+float Filter::getValue(Parameter in, float def) const
+{
+	return params.find(in) == params.end() ? def : params.at(in);
+}
+
+int Filter::getValue(Parameter in, int def) const
+{
+	return params.find(in) == params.end() ? def : static_cast<int>(params.at(in));
+}
+
 } //openal
 } //audio
 } //love

+ 7 - 3
src/modules/audio/openal/Filter.h

@@ -63,12 +63,16 @@ public:
 	virtual ~Filter();
 	virtual Filter *clone();
 	ALuint getFilter() const;
-	virtual bool setParams(Type type, const std::vector<float> &params);
-	virtual const std::vector<float> &getParams() const;
+	virtual bool setParams(const std::map<Parameter, float> &params);
+	virtual const std::map<Parameter, float> &getParams() const;
 
 private:
+	bool generateFilter();
+	void deleteFilter();
+	float getValue(Parameter in, float def) const;
+	int getValue(Parameter in, int def) const;
 	ALuint filter = AL_FILTER_NULL;
-	std::vector<float> params;
+	std::map<Parameter, float> params;
 };
 
 } //openal

+ 158 - 19
src/modules/audio/openal/Source.cpp

@@ -28,6 +28,8 @@
 #include <iostream>
 #include <algorithm>
 
+#define audiomodule() (Module::getInstance<Audio>(Module::M_AUDIO))
+
 namespace love
 {
 namespace audio
@@ -120,6 +122,8 @@ Source::Source(Pool *pool, love::sound::SoundData *soundData)
 	, sampleRate(soundData->getSampleRate())
 	, channels(soundData->getChannels())
 	, bitDepth(soundData->getBitDepth())
+	, sendfilters(audiomodule()->getMaxSourceEffects(), nullptr)
+	, sendtargets(audiomodule()->getMaxSourceEffects(), AL_EFFECTSLOT_NULL)
 {
 	ALenum fmt = Audio::getFormat(soundData->getBitDepth(), soundData->getChannels());
 	if (fmt == AL_NONE)
@@ -142,6 +146,8 @@ Source::Source(Pool *pool, love::sound::Decoder *decoder)
 	, bitDepth(decoder->getBitDepth())
 	, decoder(decoder)
 	, unusedBufferTop(MAX_BUFFERS - 1)
+	, sendfilters(audiomodule()->getMaxSourceEffects(), nullptr)
+	, sendtargets(audiomodule()->getMaxSourceEffects(), AL_EFFECTSLOT_NULL)
 {
 	if (Audio::getFormat(decoder->getBitDepth(), decoder->getChannels()) == AL_NONE)
 		throw InvalidFormatException(decoder->getChannels(), decoder->getBitDepth());
@@ -163,6 +169,8 @@ Source::Source(Pool *pool, int sampleRate, int bitDepth, int channels)
 	, sampleRate(sampleRate)
 	, channels(channels)
 	, bitDepth(bitDepth)
+	, sendfilters(audiomodule()->getMaxSourceEffects(), nullptr)
+	, sendtargets(audiomodule()->getMaxSourceEffects(), AL_EFFECTSLOT_NULL)
 {
 	ALenum fmt = Audio::getFormat(bitDepth, channels);
 	if (fmt == AL_NONE)
@@ -202,6 +210,8 @@ Source::Source(const Source &s)
 	, decoder(nullptr)
 	, toLoop(0)
 	, unusedBufferTop(s.sourceType == TYPE_STREAM ? MAX_BUFFERS - 1 : -1)
+	, sendfilters(s.sendfilters)
+	, sendtargets(s.sendtargets)
 {
 	if (sourceType == TYPE_STREAM)
 	{
@@ -214,8 +224,8 @@ Source::Source(const Source &s)
 		for (unsigned int i = 0; i < MAX_BUFFERS; i++)
 			unusedBuffers[i] = streamBuffers[i];
 	}
-	if (s.filter)
-		filter = s.filter->clone();
+	if (s.directfilter)
+		directfilter = s.directfilter->clone();
 
 	setFloatv(position, s.position);
 	setFloatv(velocity, s.velocity);
@@ -230,8 +240,12 @@ Source::~Source()
 	if (sourceType != TYPE_STATIC)
 		alDeleteBuffers(MAX_BUFFERS, streamBuffers);
 
-	if (filter)
-		delete filter;
+	if (directfilter)
+		delete directfilter;
+
+	for (auto sf : sendfilters)
+		if (sf != nullptr)
+			delete sf;
 }
 
 love::audio::Source *Source::clone()
@@ -619,7 +633,7 @@ void Source::getDirection(float *v) const
 		setFloatv(v, direction);
 }
 
-void Source::setCone(float innerAngle, float outerAngle, float outerVolume)
+void Source::setCone(float innerAngle, float outerAngle, float outerVolume, float outerHighGain)
 {
 	if (channels > 1)
 		throw SpatialSupportException();
@@ -627,16 +641,20 @@ void Source::setCone(float innerAngle, float outerAngle, float outerVolume)
 	cone.innerAngle  = (int) LOVE_TODEG(innerAngle);
 	cone.outerAngle  = (int) LOVE_TODEG(outerAngle);
 	cone.outerVolume = outerVolume;
+	cone.outerHighGain = outerHighGain;
 
 	if (valid)
 	{
 		alSourcei(source, AL_CONE_INNER_ANGLE, cone.innerAngle);
 		alSourcei(source, AL_CONE_OUTER_ANGLE, cone.outerAngle);
 		alSourcef(source, AL_CONE_OUTER_GAIN, cone.outerVolume);
+		#ifdef ALC_EXT_EFX
+		alSourcef(source, AL_CONE_OUTER_GAINHF, cone.outerHighGain);
+		#endif
 	}
 }
 
-void Source::getCone(float &innerAngle, float &outerAngle, float &outerVolume) const
+void Source::getCone(float &innerAngle, float &outerAngle, float &outerVolume, float &outerHighGain) const
 {
 	if (channels > 1)
 		throw SpatialSupportException();
@@ -644,6 +662,7 @@ void Source::getCone(float &innerAngle, float &outerAngle, float &outerVolume) c
 	innerAngle  = LOVE_TORAD(cone.innerAngle);
 	outerAngle  = LOVE_TORAD(cone.outerAngle);
 	outerVolume = cone.outerVolume;
+	outerHighGain = cone.outerHighGain;
 }
 
 void Source::setRelative(bool enable)
@@ -996,7 +1015,13 @@ void Source::reset()
 	alSourcei(source, AL_CONE_OUTER_ANGLE, cone.outerAngle);
 	alSourcef(source, AL_CONE_OUTER_GAIN, cone.outerVolume);
 	#ifdef ALC_EXT_EFX
-	alSourcei(source, AL_DIRECT_FILTER, filter ? filter->getFilter() : AL_FILTER_NULL);
+	alSourcef(source, AL_AIR_ABSORPTION_FACTOR, absorptionFactor);
+	alSourcef(source, AL_CONE_OUTER_GAINHF, cone.outerHighGain);
+	alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, rolloffFactor); //reverb-specific rolloff
+	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);
+	//alGetError();
 	#endif
 }
 
@@ -1199,21 +1224,48 @@ float Source::getMaxDistance() const
 	return maxDistance;
 }
 
+void Source::setAirAbsorptionFactor(float factor)
+{
+	if (channels > 1)
+		throw SpatialSupportException();
+
+	absorptionFactor = factor;
+	#ifdef ALC_EXT_EFX
+	if (valid)
+	{
+		alSourcef(source, AL_AIR_ABSORPTION_FACTOR, absorptionFactor);
+		//alGetError();
+	}
+	#endif
+}
+
+float Source::getAirAbsorptionFactor() const
+{
+	if (channels > 1)
+		throw SpatialSupportException();
+
+	return absorptionFactor;
+}
+
 int Source::getChannels() const
 {
 	return channels;
 }
 
-bool Source::setFilter(love::audio::Filter::Type type, std::vector<float> &params)
+bool Source::setFilter(std::map<Filter::Parameter, float> &params)
 {
-	if (!filter)
-		filter = new Filter();
+	if (!directfilter)
+		directfilter = new Filter();
 
-	bool result = filter->setParams(type, params);
+	bool result = directfilter->setParams(params);
 
 	#ifdef ALC_EXT_EFX
 	if (valid)
-		alSourcei(source, AL_DIRECT_FILTER, filter->getFilter());
+	{
+		//in case of failure contains AL_FILTER_NULL, a valid non-filter
+		alSourcei(source, AL_DIRECT_FILTER, directfilter->getFilter());
+		//alGetError();
+	}
 	#endif
 
 	return result;
@@ -1221,26 +1273,113 @@ bool Source::setFilter(love::audio::Filter::Type type, std::vector<float> &param
 
 bool Source::setFilter()
 {
-	if (filter)
-		delete filter;
+	if (directfilter)
+		delete directfilter;
 
-	filter = nullptr;
+	directfilter = nullptr;
 
 	#ifdef ALC_EXT_EFX
 	if (valid)
+	{
 		alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL);
+		//alGetError();
+	}
 	#endif
 
 	return true;
 }
 
-bool Source::getFilter(love::audio::Filter::Type &type, std::vector<float> &params)
+bool Source::getFilter(std::map<Filter::Parameter, float> &params)
+{
+	if (!directfilter)
+		return false;
+
+	params = directfilter->getParams();
+
+	return true;
+}
+
+bool Source::setSceneEffect(int slot, int effect)
 {
-	if (!filter)
+	if (slot < 0 || slot >= (int)sendtargets.size())
 		return false;
 
-	type = filter->getType();
-	params = filter->getParams();
+	sendtargets[slot] = dynamic_cast<Audio*>(audiomodule())->getSceneEffectID(effect);
+
+	if (sendfilters[slot])
+		delete sendfilters[slot];
+
+	sendfilters[slot] = nullptr;
+
+	#ifdef ALC_EXT_EFX
+	if (valid)
+	{
+		alSource3i(source, AL_AUXILIARY_SEND_FILTER, sendtargets[slot], slot, AL_FILTER_NULL);
+		//alGetError();
+	}
+	#endif
+
+	return true;
+}
+
+bool Source::setSceneEffect(int slot, int effect, std::map<Filter::Parameter, float> &params)
+{
+	if (slot < 0 || slot >= (int)sendtargets.size())
+		return false;
+
+	sendtargets[slot] = dynamic_cast<Audio*>(audiomodule())->getSceneEffectID(effect);
+
+	if (!sendfilters[slot])
+		sendfilters[slot] = new Filter();
+
+	sendfilters[slot]->setParams(params);
+
+	#ifdef ALC_EXT_EFX
+	if (valid)
+	{
+		//in case of failure contains AL_FILTER_NULL, a valid non-filter
+		alSource3i(source, AL_AUXILIARY_SEND_FILTER, sendtargets[slot], slot, sendfilters[slot]->getFilter());
+		//alGetError();
+	}
+	#endif
+	return true;
+}
+
+bool Source::setSceneEffect(int slot)
+{
+	if (slot < 0 || slot >= (int)sendtargets.size())
+		return false;
+
+	sendtargets[slot] = AL_EFFECTSLOT_NULL;
+
+	if (sendfilters[slot])
+		delete sendfilters[slot];
+	sendfilters[slot] = nullptr;
+
+	#ifdef ALC_EXT_EFX
+	if (valid)
+	{
+		alSource3i(source, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, slot, AL_FILTER_NULL);
+		//alGetError();
+	}
+	#endif
+
+	return true;
+}
+
+bool Source::getSceneEffect(int slot, int &effect, std::map<Filter::Parameter, float> &params)
+{
+	if (slot < 0 || slot >= (int)sendtargets.size())
+		return false;
+
+	if (sendtargets[slot] == AL_EFFECTSLOT_NULL)
+		return false;
+
+	effect = dynamic_cast<Audio*>(audiomodule())->getSceneEffectIndex(sendtargets[slot]);
+
+	if(sendfilters[slot])
+		params = sendfilters[slot]->getParams();
+
 	return true;
 }
 

+ 16 - 6
src/modules/audio/openal/Source.h

@@ -126,8 +126,8 @@ public:
 	virtual void getVelocity(float *v) const;
 	virtual void setDirection(float *v);
 	virtual void getDirection(float *v) const;
-	virtual void setCone(float innerAngle, float outerAngle, float outerVolume);
-	virtual void getCone(float &innerAngle, float &outerAngle, float &outerVolume) const;
+	virtual void setCone(float innerAngle, float outerAngle, float outerVolume, float outerHighGain);
+	virtual void getCone(float &innerAngle, float &outerAngle, float &outerVolume, float &outerHighGain) const;
 	virtual void setRelative(bool enable);
 	virtual bool isRelative() const;
 	void setLooping(bool looping);
@@ -142,11 +142,18 @@ public:
 	virtual float getRolloffFactor() const;
 	virtual void setMaxDistance(float distance);
 	virtual float getMaxDistance() const;
+	virtual void setAirAbsorptionFactor(float factor);
+	virtual float getAirAbsorptionFactor() const;
 	virtual int getChannels() const;
 
-	virtual bool setFilter(love::audio::Filter::Type type, std::vector<float> &params);
+	virtual bool setFilter(std::map<Filter::Parameter, float> &params);
 	virtual bool setFilter();
-	virtual bool getFilter(love::audio::Filter::Type &type, std::vector<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, 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 int getFreeBufferCount() const;
 	virtual bool queue(void *data, size_t length, int dataSampleRate, int dataBitDepth, int dataChannels);
@@ -199,6 +206,7 @@ private:
 	float maxVolume = 1.0f;
 	float referenceDistance = 1.0f;
 	float rolloffFactor = 1.0f;
+	float absorptionFactor = 0.0f;
 	float maxDistance = MAX_ATTENUATION_DISTANCE;
 
 	struct Cone
@@ -206,6 +214,7 @@ private:
 		int innerAngle = 360; // degrees
 		int outerAngle = 360; // degrees
 		float outerVolume = 0.0f;
+		float outerHighGain = 1.0f;
 	} cone;
 
 	float offsetSamples = 0.0f;
@@ -221,8 +230,9 @@ private:
 	int unusedBufferTop = -1;
 	ALsizei bufferedBytes = 0;
 
-	Filter *filter = nullptr;
-
+	Filter *directfilter = nullptr;
+	std::vector<Filter*> sendfilters;
+	std::vector<ALint> sendtargets;
 }; // Source
 
 } // openal

+ 196 - 0
src/modules/audio/wrap_Audio.cpp

@@ -28,6 +28,7 @@
 
 // C++
 #include <iostream>
+#include <cmath>
 
 namespace love
 {
@@ -256,7 +257,19 @@ int w_getDopplerScale(lua_State *L)
 	lua_pushnumber(L, instance()->getDopplerScale());
 	return 1;
 }
+/*
+int w_setMeter(lua_State *L)
+{
+	instance()->setMeter(luax_checkfloat(L, 1));
+	return 0;
+}
 
+int w_getMeter(lua_State *L)
+{
+	lua_pushnumber(L, instance()->getMeter());
+	return 1;
+}
+*/
 int w_setDistanceModel(lua_State *L)
 {
 	const char *modelStr = luaL_checkstring(L, 1);
@@ -292,6 +305,182 @@ int w_getRecordingDevices(lua_State *L)
 	return 1;
 }
 
+int w_setSceneEffect(lua_State *L)
+{
+	int slot = luaL_checknumber(L, 1) - 1;
+
+	if (lua_gettop(L) == 1 || (lua_gettop(L) == 2 && lua_isnoneornil(L, 2)))
+	{
+		lua_pushboolean(L, instance()->setSceneEffect(slot));
+		return 1;
+	}
+
+	luaL_checktype(L, 2, LUA_TTABLE);
+
+	const char *paramstr = nullptr;
+
+	//find type (mandatory)
+	Effect::getConstant(Effect::EFFECT_TYPE, paramstr, Effect::TYPE_BASIC);
+	lua_pushstring(L, paramstr);
+	lua_rawget(L, 2);
+	if (lua_type(L, -1) == LUA_TNIL)
+		return luaL_error(L, "Effect type not specificed.");
+
+	Effect::Type type = Effect::TYPE_MAX_ENUM;
+	const char *typestr = luaL_checkstring(L, -1);
+	if (!Effect::getConstant(typestr, type))
+		return luaL_error(L, "Invalid Effect type: %s", typestr);
+
+	lua_pop(L, 1);
+	std::map<Effect::Parameter, float> params;
+	params[Effect::EFFECT_TYPE] = static_cast<int>(type);
+
+	// Iterate over the whole table, reading valid parameters and erroring on invalid ones
+	lua_pushnil(L);
+	while (lua_next(L, 2))
+	{
+		const char *keystr = luaL_checkstring(L, -2);
+		Effect::Parameter param;
+
+		if(Effect::getConstant(keystr, param, type) || Effect::getConstant(keystr, param, Effect::TYPE_BASIC))
+		{
+			#define luax_effecterror(l,t) luaL_error(l,"Bad parameter type for %s %s: " t " expected, got %s", typestr, keystr, lua_typename(L, -1))
+			switch(Effect::getParameterType(param))
+			{
+			case Effect::PARAM_FLOAT:
+				if (!lua_isnumber(L, -1))
+					return luax_effecterror(L, "number");
+				params[param] = lua_tonumber(L, -1);
+				break;
+			case Effect::PARAM_BOOL:
+				if (!lua_isboolean(L, -1))
+					return luax_effecterror(L, "boolean");
+				params[param] = lua_toboolean(L, -1) ? 1.0 : 0.0; 
+				break;
+			case Effect::PARAM_WAVEFORM:
+			{
+				if (!lua_isstring(L, -1))
+					return luax_effecterror(L, "string");
+				paramstr = lua_tostring(L, -1);
+				Effect::Waveform waveform;
+				if (!Effect::getConstant(paramstr, waveform))
+					return luaL_error(L, "Invalid waveform type: %s", paramstr);
+				params[param] = static_cast<int>(waveform);
+				break;
+			}
+			/*
+			case Effect::PARAM_DIRECTION:
+			{
+				if (!lua_isstring(L, -1))
+					return luax_effecterror(L, "string");
+				paramstr = lua_tostring(L, -1);
+				Effect::Direction direction;
+				if (!Effect::getConstant(paramstr, direction))
+					return luaL_error(L, "Invalid direction type: %s", paramstr);
+				params[param] = static_cast<int>(direction);
+				break;
+			}
+			case Effect::PARAM_PHONEME:
+			{
+				if (!lua_isstring(L, -1))
+					return luax_effecterror(L, "string");
+				paramstr = lua_tostring(L, -1);
+				Effect::Phoneme phoneme;
+				if (!Effect::getConstant(basicstr, phoneme))
+					return luaL_error(L, "Invalid phoneme type: %s", paramstr);
+				params[param] = static_cast<int>(phoneme);
+				break;
+			}
+			*/
+			case Effect::PARAM_TYPE:
+			case Effect::PARAM_MAX_ENUM:
+				break;
+			}
+			#undef luax_effecterror
+		}
+		else
+			luaL_error(L, "Invalid '%s' Effect parameter: %s", typestr, keystr);
+
+		//remove the value (-1) from stack, keep the key (-2) to feed into lua_next
+		lua_pop(L, 1);
+	}
+
+	luax_catchexcept(L, [&]() { lua_pushboolean(L, instance()->setSceneEffect(slot, params)); });
+	return 1;
+}
+
+int w_getSceneEffect(lua_State *L)
+{
+	int slot = luaL_checknumber(L, 1) - 1;
+
+	std::map<Effect::Parameter, float> params;
+
+	if (!instance()->getSceneEffect(slot, params))
+		return 0;
+
+	const char *keystr, *valstr;
+	Effect::Type type = static_cast<Effect::Type>((int)params[Effect::EFFECT_TYPE]);
+
+	lua_createtable(L, 0, params.size());
+
+	for (auto p : params)
+	{
+		if (!Effect::getConstant(p.first, keystr, type))
+			Effect::getConstant(p.first, keystr, Effect::TYPE_BASIC);
+
+		lua_pushstring(L, keystr);
+		switch (Effect::getParameterType(p.first))
+		{
+		case Effect::PARAM_FLOAT:
+			lua_pushnumber(L, p.second);
+			break;
+		case Effect::PARAM_BOOL:
+			lua_pushboolean(L, p.second > 0.5 ? true : false);
+			break;
+		case Effect::PARAM_WAVEFORM:
+			Effect::getConstant(static_cast<Effect::Waveform>((int)p.second), valstr);
+			lua_pushstring(L, valstr);
+			break;
+/*
+		case Effect::PARAM_DIRECTION:
+			Effect::getConstant(static_cast<Effect::Direction>((int)p.second), valstr);
+			lua_pushstring(L, valstr);
+			break;
+		case Effect::PARAM_PHONEME:
+			Effect::getConstant(static_cast<Effect::Phoneme>((int)p.second), valstr);
+			lua_pushstring(L, valstr);
+			break;
+*/
+		case Effect::PARAM_TYPE:
+			Effect::getConstant(static_cast<Effect::Type>((int)p.second), valstr);
+			lua_pushstring(L, valstr);
+			break;
+		case Effect::PARAM_MAX_ENUM:
+			break;
+		}
+		lua_rawset(L, -3);
+	}
+	return 1;
+}
+
+int w_getMaxSceneEffects(lua_State *L)
+{
+	lua_pushnumber(L, instance()->getMaxSceneEffects());
+	return 1;
+}
+
+int w_getMaxSourceEffects(lua_State *L)
+{
+	lua_pushnumber(L, instance()->getMaxSourceEffects());
+	return 1;
+}
+
+int w_isSceneEffectsSupported(lua_State *L)
+{
+	lua_pushboolean(L, instance()->isEFXsupported());
+	return 1;
+}
+
 // List of functions to wrap.
 static const luaL_Reg functions[] =
 {
@@ -311,9 +500,16 @@ static const luaL_Reg functions[] =
 	{ "getVelocity", w_getVelocity },
 	{ "setDopplerScale", w_setDopplerScale },
 	{ "getDopplerScale", w_getDopplerScale },
+	//{ "setMeter", w_setMeter },
+	//{ "getMeter", w_setMeter },
 	{ "setDistanceModel", w_setDistanceModel },
 	{ "getDistanceModel", w_getDistanceModel },
 	{ "getRecordingDevices", w_getRecordingDevices },
+	{ "setEffect", w_setSceneEffect },
+	{ "getEffect", w_getSceneEffect },
+	{ "getMaxSceneEffects", w_getMaxSceneEffects },
+	{ "getMaxSourceEffects", w_getMaxSourceEffects },
+	{ "isEffectsSupported", w_isSceneEffectsSupported },
 	{ 0, 0 }
 };
 

+ 152 - 49
src/modules/audio/wrap_Source.cpp

@@ -23,6 +23,9 @@
 #include "sound/SoundData.h"
 #include "wrap_Source.h"
 
+#include <cmath>
+#include <iostream>
+
 namespace love
 {
 namespace audio
@@ -212,19 +215,21 @@ int w_Source_setCone(lua_State *L)
 	float innerAngle = (float) luaL_checknumber(L, 2);
 	float outerAngle = (float) luaL_checknumber(L, 3);
 	float outerVolume = (float) luaL_optnumber(L, 4, 0.0);
-	luax_catchexcept(L, [&](){ t->setCone(innerAngle, outerAngle, outerVolume); });
+	float outerHighGain = (float) luaL_optnumber(L, 5, 1.0);
+	luax_catchexcept(L, [&](){ t->setCone(innerAngle, outerAngle, outerVolume, outerHighGain); });
 	return 0;
 }
 
 int w_Source_getCone(lua_State *L)
 {
 	Source *t = luax_checksource(L, 1);
-	float innerAngle, outerAngle, outerVolume;
-	luax_catchexcept(L, [&](){ t->getCone(innerAngle, outerAngle, outerVolume); });
+	float innerAngle, outerAngle, outerVolume, outerHighGain;
+	luax_catchexcept(L, [&](){ t->getCone(innerAngle, outerAngle, outerVolume, outerHighGain); });
 	lua_pushnumber(L, innerAngle);
 	lua_pushnumber(L, outerAngle);
 	lua_pushnumber(L, outerVolume);
-	return 3;
+	lua_pushnumber(L, outerHighGain);
+	return 4;
 }
 
 int w_Source_setRelative(lua_State *L)
@@ -323,6 +328,16 @@ int w_Source_getRolloff(lua_State *L)
 	return 1;
 }
 
+int w_Source_setAirAbsorption(lua_State *L)
+{
+	Source *t = luax_checksource(L, 1);
+	float factor = (float)luaL_checknumber(L, 2);
+	if (factor < 0.0f)
+		return luaL_error(L, "Invalid air absorption factor: %f. Must be > 0.", factor);
+	luax_catchexcept(L, [&](){ t->setAirAbsorptionFactor(factor); });
+	return 0;
+}
+
 int w_Source_getChannels(lua_State *L)
 {
 	Source *t = luax_checksource(L, 1);
@@ -330,74 +345,160 @@ int w_Source_getChannels(lua_State *L)
 	return 1;
 }
 
-int w_Source_setFilter(lua_State *L)
+int setFilterReadFilter(lua_State *L, int idx, std::map<Filter::Parameter, float> &params)
 {
-	Source *t = luax_checksource(L, 1);
+	if (lua_gettop(L) < idx || lua_isnoneornil(L, idx))
+		return 0;
 
-	Filter::Type type;
-	std::vector<float> params;
+	luaL_checktype(L, idx, LUA_TTABLE);
 
-	params.reserve(Filter::getParameterCount());
+	const char *paramstr = nullptr;
 
-	if (lua_gettop(L) == 1)
-	{
-		lua_pushboolean(L, t->setFilter());
-		return 1;
-	}
-	else if (lua_gettop(L) > 2)
-	{
-		const char *ftypestr = luaL_checkstring(L, 2);
-		if (!Filter::getConstant(ftypestr, type))
-			return luaL_error(L, "Invalid filter type: %s", ftypestr);
+	Filter::getConstant(Filter::FILTER_TYPE, paramstr, Filter::TYPE_BASIC);
+	lua_pushstring(L, paramstr);
+	lua_rawget(L, idx);
+	if (lua_type(L, -1) == LUA_TNIL)
+		return luaL_error(L, "Filter type not specificed.");
 
-		unsigned int count = Filter::getParameterCount(type);
-		for (unsigned int i = 0; i < count; i++)
-			params.push_back(luaL_checknumber(L, i + 3));
-	}
-	else if (lua_istable(L, 2))
+	Filter::Type type = Filter::TYPE_MAX_ENUM;
+	const char *typestr = luaL_checkstring(L, -1);
+	if (!Filter::getConstant(typestr, type))
+		return luaL_error(L, "Invalid Filter type: %s", typestr);
+
+	lua_pop(L, 1);
+	params[Filter::FILTER_TYPE] = static_cast<int>(type);
+
+	lua_pushnil(L);
+	while (lua_next(L, idx))
 	{
-		if (lua_objlen(L, 2) == 0) //empty table also clears filter
+		const char *keystr = luaL_checkstring(L, -2);
+		Filter::Parameter param;
+
+		if(Filter::getConstant(keystr, param, type) || Filter::getConstant(keystr, param, Filter::TYPE_BASIC))
 		{
-			lua_pushboolean(L, t->setFilter());
-			return 1;
+			#define luax_effecterror(l,t) luaL_error(l,"Bad parameter type for %s %s: " t " expected, got %s", typestr, keystr, lua_typename(L, -1))
+			switch(Filter::getParameterType(param))
+			{
+			case Filter::PARAM_FLOAT:
+				if (!lua_isnumber(L, -1))
+					return luax_effecterror(L, "number");
+				params[param] = lua_tonumber(L, -1);
+				break;
+			case Filter::PARAM_TYPE:
+			case Filter::PARAM_MAX_ENUM:
+				break;
+			}
+			#undef luax_effecterror
 		}
-		lua_rawgeti(L, 2, 1);
-		const char *ftypestr = luaL_checkstring(L, -1);
-		if (!Filter::getConstant(ftypestr, type))
-			return luaL_error(L, "Invalid filter type: %s", ftypestr);
+		else
+			luaL_error(L, "Invalid '%s' Effect parameter: %s", typestr, keystr);
+
+		//remove the value (-1) from stack, keep the key (-2) to feed into lua_next
 		lua_pop(L, 1);
+	}
+
+	return 1;
+}
+
+void getFilterWriteFilter(lua_State *L, std::map<Filter::Parameter, float> &params)
+{
+	const char *keystr, *valstr;
+	Filter::Type type = static_cast<Filter::Type>((int)params[Filter::FILTER_TYPE]);
+
+	lua_createtable(L, 0, params.size());
+
+	for (auto p : params)
+	{
+		if (!Filter::getConstant(p.first, keystr, type))
+			Filter::getConstant(p.first, keystr, Filter::TYPE_BASIC);
 
-		unsigned int count = Filter::getParameterCount(type);
-		for (unsigned int i = 0; i < count; i++)
+		lua_pushstring(L, keystr);
+		switch (Filter::getParameterType(p.first))
 		{
-			lua_rawgeti(L, 2, i + 2);
-			params.push_back(luaL_checknumber(L, -1));
-			lua_pop(L, 1);
+		case Filter::PARAM_FLOAT:
+			lua_pushnumber(L, p.second);
+			break;
+		case Filter::PARAM_TYPE:
+			Filter::getConstant(static_cast<Filter::Type>((int)p.second), valstr);
+			lua_pushstring(L, valstr);
+			break;
+		case Filter::PARAM_MAX_ENUM:
+			break;
 		}
+		lua_rawset(L, -3);
 	}
+}
+
+int w_Source_setFilter(lua_State *L)
+{
+	Source *t = luax_checksource(L, 1);
+
+	std::map<Filter::Parameter, float> params;
+
+	if (setFilterReadFilter(L, 2, params) == 1)
+		luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setFilter(params)); });
 	else
-		return luax_typerror(L, 2, "filter description");
+		luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setFilter()); });
 
-	luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setFilter(type, params)); });
 	return 1;
 }
 
 int w_Source_getFilter(lua_State *L)
 {
 	Source *t = luax_checksource(L, 1);
-	Filter::Type type;
-	std::vector<float> params;
-	if (!t->getFilter(type, params))
+
+	std::map<Filter::Parameter, float> params;
+
+	if (!t->getFilter(params))
 		return 0;
 
-	const char *str = nullptr;
-	Filter::getConstant(type, str);
-	lua_pushstring(L, str);
+	getFilterWriteFilter(L, params);
+	return 1;
+}
+
+int w_Source_setSceneEffect(lua_State *L)
+{
+	Source *t = luax_checksource(L, 1);
+
+	int slot = luaL_checknumber(L, 2) - 1;
+	if (lua_gettop(L) == 2)
+	{
+		luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setSceneEffect(slot)); });
+		return 1;
+	}
 
-	for (unsigned int i = 0; i < params.size(); i++)
-		lua_pushnumber(L, params[i]);
+	int effect = luaL_checknumber(L, 3) - 1;
+	if (lua_gettop(L) == 3)
+	{
+		luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setSceneEffect(slot, effect)); });
+		return 1;
+	}
+
+	std::map<Filter::Parameter, float> params;
+
+	if (setFilterReadFilter(L, 4, params) == 1)
+		luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setSceneEffect(slot, effect, params)); });
+	else
+		luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setSceneEffect(slot, effect)); });
+	return 1;
+}
 
-	return params.size() + 1;
+int w_Source_getSceneEffect(lua_State *L)
+{
+	Source *t = luax_checksource(L, 1);
+	int slot = luaL_checknumber(L, 2) - 1;
+
+	int effect;
+	std::map<Filter::Parameter, float> params;
+	if (!t->getSceneEffect(slot, effect, params))
+		return 0;
+
+	lua_pushnumber(L, effect + 1);
+	if (params.size() == 0)
+		return 1;
+
+	getFilterWriteFilter(L, params);
+	return 2;
 }
 
 int w_Source_getFreeBufferCount(lua_State *L)
@@ -505,13 +606,15 @@ static const luaL_Reg w_Source_functions[] =
 	{ "getVolumeLimits", w_Source_getVolumeLimits },
 	{ "setAttenuationDistances", w_Source_setAttenuationDistances },
 	{ "getAttenuationDistances", w_Source_getAttenuationDistances },
-	{ "setRolloff", w_Source_setRolloff},
-	{ "getRolloff", w_Source_getRolloff},
+	{ "setRolloff", w_Source_setRolloff },
+	{ "getRolloff", w_Source_getRolloff },
 
 	{ "getChannels", w_Source_getChannels },
 
 	{ "setFilter", w_Source_setFilter },
 	{ "getFilter", w_Source_getFilter },
+	{ "setEffect", w_Source_setSceneEffect },
+	{ "getEffect", w_Source_getSceneEffect },
 
 	{ "getFreeBufferCount", w_Source_getFreeBufferCount },
 	{ "queue", w_Source_queue },