diff --git a/CMakeLists.txt b/CMakeLists.txt index 2561fded5..342eee33e 100755 --- a/CMakeLists.txt +++ b/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 diff --git a/src/modules/audio/Audio.h b/src/modules/audio/Audio.h index 1f54ccdc5..b59496750 100644 --- a/src/modules/audio/Audio.h +++ b/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 ¶ms) = 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 ¶ms) = 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::Entry distanceModelEntries[]; diff --git a/src/modules/audio/Effect.cpp b/src/modules/audio/Effect.cpp new file mode 100644 index 000000000..e4e6f20a2 --- /dev/null +++ b/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::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::types(Effect::typeEntries, sizeof(Effect::typeEntries)); + +StringMap::Entry Effect::waveformEntries[] = +{ + {"sine", Effect::WAVE_SINE}, + {"triangle", Effect::WAVE_TRIANGLE}, + {"sawtooth", Effect::WAVE_SAWTOOTH}, + {"square", Effect::WAVE_SQUARE}, +}; + +StringMap Effect::waveforms(Effect::waveformEntries, sizeof(Effect::waveformEntries)); + +/* +StringMap::Entry Effect::directionEntries[] = +{ + {"up", Effect::DIR_UP}, + {"down", Effect::DIR_DOWN}, + {"none", Effect::DIR_NONE}, +}; + +StringMap Effect::directions(Effect::directionEntries, sizeof(Effect::directionEntries)); + +StringMap::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::phonemes(Effect::phonemeEntries, sizeof(Effect::phonemeEntries)); +*/ + +#define StringMap LazierAndSlowerButEasilyArrayableStringMap + +std::vector::Entry> Effect::basicParameters = +{ + {"type", Effect::EFFECT_TYPE}, + {"volume", Effect::EFFECT_VOLUME} +}; + +std::vector::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::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::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::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::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::Entry> Effect::freqshifterParameters = +{ + {"frequency", Effect::FREQSHIFTER_FREQ}, + {"leftdirection", Effect::FREQSHIFTER_LEFTDIR}, + {"rightdirection", Effect::FREQSHIFTER_RIGHTDIR} +}; + +std::vector::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::Entry> Effect::pitchshifterParameters = +{ + {"pitch", Effect::PITCHSHIFTER_PITCH} +}; +*/ +std::vector::Entry> Effect::modulatorParameters = +{ + {"waveform", Effect::MODULATOR_WAVEFORM}, + {"frequency", Effect::MODULATOR_FREQ}, + {"highcut", Effect::MODULATOR_HIGHCUT} +}; +/* +std::vector::Entry> Effect::autowahParameters = +{ + {"attack", Effect::AUTOWAH_ATTACK}, + {"release", Effect::AUTOWAH_RELEASE}, + {"resonance", Effect::AUTOWAH_RESONANCE}, + {"peakgain", Effect::AUTOWAH_PEAKGAIN} +}; +*/ +std::vector::Entry> Effect::compressorParameters = +{ + {"enable", Effect::COMPRESSOR_ENABLE} +}; + +std::vector::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::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::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 diff --git a/src/modules/audio/Effect.h b/src/modules/audio/Effect.h new file mode 100644 index 000000000..c39f8f927 --- /dev/null +++ b/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 +#include + +template +class LazierAndSlowerButEasilyArrayableStringMap +{ +public: + struct Entry + { + const char *key; + T value; + }; + LazierAndSlowerButEasilyArrayableStringMap() + { + } + + LazierAndSlowerButEasilyArrayableStringMap(const std::vector &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 forward; + std::map 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::Entry typeEntries[]; + static StringMap types; + static StringMap::Entry waveformEntries[]; + static StringMap waveforms; + //static StringMap::Entry directionEntries[]; + //static StringMap directions; + //static StringMap::Entry phonemeEntries[]; + //static StringMap phonemes; +#define StringMap LazierAndSlowerButEasilyArrayableStringMap + static std::vector::Entry> basicParameters; + static std::vector::Entry> reverbParameters; + static std::vector::Entry> chorusParameters; + static std::vector::Entry> distortionParameters; + static std::vector::Entry> echoParameters; + static std::vector::Entry> flangerParameters; + //static std::vector::Entry> freqshifterParameters; + //static std::vector::Entry> morpherParameters; + //static std::vector::Entry> pitchshifterParameters; + static std::vector::Entry> modulatorParameters; + //static std::vector::Entry> autowahParameters; + static std::vector::Entry> compressorParameters; + static std::vector::Entry> equalizerParameters; + static std::map> parameterNames; +#undef StringMap + static std::map parameterTypes; + +}; + +} //audio +} //love + +#endif //LOVE_AUDIO_EFFECTS_H diff --git a/src/modules/audio/Filter.cpp b/src/modules/audio/Filter.cpp index 08677ec02..08c35fc43 100644 --- a/src/modules/audio/Filter.cpp +++ b/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::Entry Filter::typeEntries[] = @@ -67,12 +72,44 @@ StringMap::Entry Filter::typeEntries[] = StringMap Filter::types(Filter::typeEntries, sizeof(Filter::typeEntries)); -std::map Filter::parameterCount = +#define StringMap LazierAndSlowerButEasilyArrayableStringMap2 +std::vector::Entry> Filter::basicParameters = { - {Filter::TYPE_LOWPASS, 2}, - {Filter::TYPE_HIGHPASS, 2}, - {Filter::TYPE_BANDPASS, 3}, - {Filter::TYPE_MAX_ENUM, 3}, + {"type", Filter::FILTER_TYPE}, + {"volume", Filter::FILTER_VOLUME} +}; + +std::vector::Entry> Filter::lowpassParameters = +{ + {"highgain", Filter::FILTER_HIGHGAIN} +}; + +std::vector::Entry> Filter::highpassParameters = +{ + {"lowgain", Filter::FILTER_LOWGAIN} +}; + +std::vector::Entry> Filter::bandpassParameters = +{ + {"lowgain", Filter::FILTER_LOWGAIN}, + {"highgain", Filter::FILTER_HIGHGAIN} +}; + +std::map> 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::parameterTypes = +{ + {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 diff --git a/src/modules/audio/Filter.h b/src/modules/audio/Filter.h index e2dcd6cf1..746f0596d 100644 --- a/src/modules/audio/Filter.h +++ b/src/modules/audio/Filter.h @@ -25,6 +25,49 @@ #include "common/StringMap.h" #include +template +class LazierAndSlowerButEasilyArrayableStringMap2 +{ +public: + struct Entry + { + const char *key; + T value; + }; + LazierAndSlowerButEasilyArrayableStringMap2() + { + } + + LazierAndSlowerButEasilyArrayableStringMap2(const std::vector &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 forward; + std::map 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::Entry typeEntries[]; static StringMap types; - static std::map parameterCount; +#define StringMap LazierAndSlowerButEasilyArrayableStringMap2 + static std::vector::Entry> basicParameters; + static std::vector::Entry> lowpassParameters; + static std::vector::Entry> highpassParameters; + static std::vector::Entry> bandpassParameters; + static std::map> parameterNames; +#undef StringMap + static std::map parameterTypes; }; } //audio diff --git a/src/modules/audio/Source.h b/src/modules/audio/Source.h index f3da30437..c1fd71893 100644 --- a/src/modules/audio/Source.h +++ b/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 ¶ms) = 0; + virtual bool setFilter(std::map ¶ms) = 0; virtual bool setFilter() = 0; - virtual bool getFilter(Filter::Type &type, std::vector ¶ms) = 0; + virtual bool getFilter(std::map ¶ms) = 0; + + virtual bool setSceneEffect(int slot, int effect) = 0; + virtual bool setSceneEffect(int slot, int effect, std::map ¶ms) = 0; + virtual bool setSceneEffect(int slot) = 0; + virtual bool getSceneEffect(int slot, int &effect, std::map ¶ms) = 0; virtual int getFreeBufferCount() const = 0; virtual bool queue(void *data, size_t length, int dataSampleRate, int dataBitDepth, int dataChannels) = 0; diff --git a/src/modules/audio/null/Audio.cpp b/src/modules/audio/null/Audio.cpp index c924a49b2..a52f27814 100644 --- a/src/modules/audio/null/Audio.cpp +++ b/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 &Audio::getRecordingDevices() { return capture; @@ -159,6 +168,36 @@ void Audio::setDistanceModel(DistanceModel distanceModel) this->distanceModel = distanceModel; } +bool Audio::setSceneEffect(int, std::map &) +{ + return false; +} + +bool Audio::setSceneEffect(int) +{ + return false; +} + +bool Audio::getSceneEffect(int, std::map &) +{ + return false; +} + +int Audio::getMaxSceneEffects() const +{ + return 0; +} + +int Audio::getMaxSourceEffects() const +{ + return 0; +} + +bool Audio::isEFXsupported() const +{ + return false; +} + } // null } // audio } // love diff --git a/src/modules/audio/null/Audio.h b/src/modules/audio/null/Audio.h index 724f4c480..102293fd8 100644 --- a/src/modules/audio/null/Audio.h +++ b/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 &getRecordingDevices(); DistanceModel getDistanceModel() const; void setDistanceModel(DistanceModel distanceModel); + bool setSceneEffect(int slot, std::map ¶ms); + bool setSceneEffect(int slot); + bool getSceneEffect(int slot, std::map ¶ms); + int getMaxSceneEffects() const; + int getMaxSourceEffects() const; + bool isEFXsupported() const; + private: float volume; DistanceModel distanceModel; diff --git a/src/modules/audio/null/Source.cpp b/src/modules/audio/null/Source.cpp index b11ba6dd4..eea9306fa 100644 --- a/src/modules/audio/null/Source.cpp +++ b/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 &) +bool Source::setFilter(std::map &) { return false; } @@ -237,7 +249,27 @@ bool Source::setFilter() return false; } -bool Source::getFilter(love::audio::Filter::Type &, std::vector &) +bool Source::getFilter(std::map &) +{ + return false; +} + +bool Source::setSceneEffect(int, int) +{ + return false; +} + +bool Source::setSceneEffect(int, int, std::map &) +{ + return false; +} + +bool Source::setSceneEffect(int) +{ + return false; +} + +bool Source::getSceneEffect(int, int &, std::map &) { return false; } diff --git a/src/modules/audio/null/Source.h b/src/modules/audio/null/Source.h index 901dc7f6d..eaeaea96d 100644 --- a/src/modules/audio/null/Source.h +++ b/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 ¶ms); + virtual bool setFilter(std::map ¶ms); virtual bool setFilter(); - virtual bool getFilter(love::audio::Filter::Type &type, std::vector ¶ms); + virtual bool getFilter(std::map ¶ms); + + virtual bool setSceneEffect(int slot, int effect); + virtual bool setSceneEffect(int slot, int effect, std::map ¶ms); + virtual bool setSceneEffect(int slot); + virtual bool getSceneEffect(int slot, int &effect, std::map ¶ms); 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 diff --git a/src/modules/audio/openal/Audio.cpp b/src/modules/audio/openal/Audio.cpp index 456ef8bbd..c93b77c18 100644 --- a/src/modules/audio/openal/Audio.cpp +++ b/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 &Audio::getRecordingDevices() return capture; } +bool Audio::setSceneEffect(int slot, std::map ¶ms) +{ + 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 ¶ms) +{ + 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; diff --git a/src/modules/audio/openal/Audio.h b/src/modules/audio/openal/Audio.h index c205f067e..54d4e14b4 100644 --- a/src/modules/audio/openal/Audio.h +++ b/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 &getRecordingDevices(); DistanceModel getDistanceModel() const; void setDistanceModel(DistanceModel distanceModel); + bool setSceneEffect(int slot, std::map ¶ms); + bool setSceneEffect(int slot); + bool getSceneEffect(int slot, std::map ¶ms); + 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 effects; + std::vector effectSlots; + std::map 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 diff --git a/src/modules/audio/openal/Effect.cpp b/src/modules/audio/openal/Effect.cpp new file mode 100644 index 000000000..850ae7c48 --- /dev/null +++ b/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 +#include + +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 ¶ms) +{ + this->params = params; + type = static_cast(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(getValue(CHORUS_WAVEFORM, static_cast(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(getValue(FLANGER_WAVEFORM, static_cast(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(getValue(FREQSHIFTER_LEFTDIR, static_cast(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(getValue(FREQSHIFTER_RIGHTDIR, static_cast(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(getValue(MORPHER_WAVEFORM, static_cast(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(getValue(MORPHER_PHONEMEA, static_cast(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(getValue(MORPHER_PHONEMEB, static_cast(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(getValue(MODULATOR_WAVEFORM,static_cast(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(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::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(params.at(in)); +} + +/* +std::map 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 diff --git a/src/modules/audio/openal/Effect.h b/src/modules/audio/openal/Effect.h new file mode 100644 index 000000000..0512acc7a --- /dev/null +++ b/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 +#include +#else +#include +#include +#include +#endif +#else +#include +#include +#include +#endif + +#include +#include + +#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 ¶ms); + virtual const std::map &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 params; + //static std::map phonemeMap; +}; + +} //openal +} //audio +} //love + +#endif //LOVE_AUDIO_OPENAL_EFFECTS_H diff --git a/src/modules/audio/openal/Filter.cpp b/src/modules/audio/openal/Filter.cpp index a75e45208..653954026 100644 --- a/src/modules/audio/openal/Filter.cpp +++ b/src/modules/audio/openal/Filter.cpp @@ -21,6 +21,8 @@ #include "Filter.h" #include "common/Exception.h" +#include + namespace love { namespace audio @@ -28,39 +30,21 @@ 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() { - #ifdef ALC_EXT_EFX - if (filter != AL_FILTER_NULL) - alDeleteFilters(1, &filter); - #endif + deleteFilter(); } Filter *Filter::clone() @@ -68,17 +52,45 @@ Filter *Filter::clone() return new Filter(*this); } +bool Filter::generateFilter() +{ + #ifdef ALC_EXT_EFX + if (!alGenFilters) + return false; + + if (filter != AL_FILTER_NULL) + return true; + + alGenFilters(1, &filter); + if (alGetError() != AL_NO_ERROR) + throw love::Exception("Failed to create sound Filter."); + + return true; + #else + return false; + #endif +} + +void Filter::deleteFilter() +{ + #ifdef ALC_EXT_EFX + if (filter != AL_FILTER_NULL) + alDeleteFilters(1, &filter); + #endif + filter = AL_FILTER_NULL; +} + ALuint Filter::getFilter() const { return filter; } -bool Filter::setParams(Type type, const std::vector ¶ms) +bool Filter::setParams(const std::map ¶ms) { - this->type = type; this->params = params; + type = static_cast(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 ¶ms) 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 ¶ms) //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 &Filter::getParams() const +const std::map &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(params.at(in)); +} + } //openal } //audio } //love diff --git a/src/modules/audio/openal/Filter.h b/src/modules/audio/openal/Filter.h index 5cf8c299e..b36f00a44 100644 --- a/src/modules/audio/openal/Filter.h +++ b/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 ¶ms); - virtual const std::vector &getParams() const; + virtual bool setParams(const std::map ¶ms); + virtual const std::map &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 params; + std::map params; }; } //openal diff --git a/src/modules/audio/openal/Source.cpp b/src/modules/audio/openal/Source.cpp index 0b1844ca7..91fd2a7c3 100644 --- a/src/modules/audio/openal/Source.cpp +++ b/src/modules/audio/openal/Source.cpp @@ -28,6 +28,8 @@ #include #include +#define audiomodule() (Module::getInstance