From 332ba76e62e3701c3444e3bd2670a69181491ea9 Mon Sep 17 00:00:00 2001 From: Raidho Date: Sat, 3 Dec 2016 20:54:48 +0100 Subject: [PATCH] Implement audio filters Use Source:setFilter(type, params...) to set a filter. Use Source:setFilter() to detach the current filter. Use Source:getFilter() to get the currently set filter (or nil). Squashed from pull request 27, and the clean-pr27 branch in love-experiments. --HG-- branch : minor --- CMakeLists.txt | 4 + changes.txt | 1 + src/modules/audio/Filter.cpp | 79 ++++++++++++++++ src/modules/audio/Filter.h | 65 +++++++++++++ src/modules/audio/Source.h | 7 ++ src/modules/audio/null/Source.cpp | 15 +++ src/modules/audio/null/Source.h | 5 + src/modules/audio/openal/Audio.cpp | 106 +++++++++++++++++++++- src/modules/audio/openal/Audio.h | 44 ++++++++- src/modules/audio/openal/Filter.cpp | 136 ++++++++++++++++++++++++++++ src/modules/audio/openal/Filter.h | 75 +++++++++++++++ src/modules/audio/openal/Source.cpp | 49 ++++++++++ src/modules/audio/openal/Source.h | 9 ++ src/modules/audio/wrap_Source.cpp | 73 +++++++++++++++ src/modules/audio/wrap_Source.h | 1 + 15 files changed, 667 insertions(+), 2 deletions(-) create mode 100644 src/modules/audio/Filter.cpp create mode 100644 src/modules/audio/Filter.h create mode 100644 src/modules/audio/openal/Filter.cpp create mode 100644 src/modules/audio/openal/Filter.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b4d624e6..8daeca534 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -316,6 +316,8 @@ set(LOVE_SRC_MODULE_AUDIO_ROOT src/modules/audio/Source.h src/modules/audio/RecordingDevice.cpp src/modules/audio/RecordingDevice.h + src/modules/audio/Filter.cpp + src/modules/audio/Filter.h src/modules/audio/wrap_Audio.cpp src/modules/audio/wrap_Audio.h src/modules/audio/wrap_Source.cpp @@ -342,6 +344,8 @@ set(LOVE_SRC_MODULE_AUDIO_OPENAL src/modules/audio/openal/Source.h src/modules/audio/openal/RecordingDevice.cpp src/modules/audio/openal/RecordingDevice.h + src/modules/audio/openal/Filter.cpp + src/modules/audio/openal/Filter.h ) set(LOVE_SRC_MODULE_AUDIO diff --git a/changes.txt b/changes.txt index 99414517a..03c4b8705 100644 --- a/changes.txt +++ b/changes.txt @@ -25,6 +25,7 @@ Released: N/A * Added 'anisotropy' graphics limit. * Added a Mesh:attachAttribute variant that takes a different target attribute name. * Added Mesh:detachAttribute. + * Added Source filters: low gain, high gain and band pass. * Changed all color values to be in the range 0-1, rather than 0-255. * Changed love.graphics.print and friends to ignore carriage returns. diff --git a/src/modules/audio/Filter.cpp b/src/modules/audio/Filter.cpp new file mode 100644 index 000000000..a56983cb7 --- /dev/null +++ b/src/modules/audio/Filter.cpp @@ -0,0 +1,79 @@ +/** + * 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 "Filter.h" + +namespace love +{ +namespace audio +{ + +Filter::Filter() +{ +} + +Filter::~Filter() +{ +} + +Filter::Type Filter::getType() const +{ + return type; +} + +bool Filter::getConstant(const char *in, Type &out) +{ + return types.find(in, out); +} + +bool Filter::getConstant(Type in, const char *&out) +{ + return types.find(in, out); +} + +int Filter::getParameterCount(Type in) +{ + return parameterCount[in]; +} + +int Filter::getParameterCount() +{ + return parameterCount[TYPE_MAX_ENUM]; +} + +StringMap::Entry Filter::typeEntries[] = +{ + {"lowpass", Filter::TYPE_LOWPASS}, + {"highpass", Filter::TYPE_HIGHPASS}, + {"bandpass", Filter::TYPE_BANDPASS}, +}; + +StringMap Filter::types(Filter::typeEntries, sizeof(Filter::typeEntries)); + +std::map Filter::parameterCount = +{ + {Filter::TYPE_LOWPASS, 2}, + {Filter::TYPE_HIGHPASS, 2}, + {Filter::TYPE_BANDPASS, 3}, + {Filter::TYPE_MAX_ENUM, 3}, +}; + +} //audio +} //love diff --git a/src/modules/audio/Filter.h b/src/modules/audio/Filter.h new file mode 100644 index 000000000..82a2e743b --- /dev/null +++ b/src/modules/audio/Filter.h @@ -0,0 +1,65 @@ +/** + * 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_FILTERS_H +#define LOVE_AUDIO_FILTERS_H + +#include "common/Object.h" +#include "common/StringMap.h" +#include + +namespace love +{ +namespace audio +{ + +class Filter +{ +public: + enum Type + { + TYPE_LOWPASS, + TYPE_HIGHPASS, + TYPE_BANDPASS, + TYPE_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(); + +protected: + Type type; + +private: + static StringMap::Entry typeEntries[]; + static StringMap types; + static std::map parameterCount; +}; + +} //audio +} //love + +#endif //LOVE_AUDIO_FILTERS_H diff --git a/src/modules/audio/Source.h b/src/modules/audio/Source.h index d6ce57621..7cb80c376 100644 --- a/src/modules/audio/Source.h +++ b/src/modules/audio/Source.h @@ -24,6 +24,9 @@ // LOVE #include "common/Object.h" #include "common/StringMap.h" +#include "Filter.h" + +#include namespace love { @@ -104,6 +107,10 @@ public: virtual int getChannels() const = 0; + virtual bool setFilter(Filter::Type type, std::vector ¶ms) = 0; + virtual bool setFilter() = 0; + virtual bool getFilter(Filter::Type &type, std::vector ¶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/Source.cpp b/src/modules/audio/null/Source.cpp index 4fca8ac4a..ae11d2dc1 100644 --- a/src/modules/audio/null/Source.cpp +++ b/src/modules/audio/null/Source.cpp @@ -227,6 +227,21 @@ bool Source::queue(void *, size_t, int, int, int) return false; } +bool Source::setFilter(love::audio::Filter::Type, std::vector &) +{ + return false; +} + +bool Source::setFilter() +{ + return false; +} + +bool Source::getFilter(love::audio::Filter::Type &, std::vector &) +{ + return false; +} + } // null } // audio } // love diff --git a/src/modules/audio/null/Source.h b/src/modules/audio/null/Source.h index 6e930123f..007d03f3d 100644 --- a/src/modules/audio/null/Source.h +++ b/src/modules/audio/null/Source.h @@ -24,6 +24,7 @@ // LOVE #include "common/Object.h" #include "audio/Source.h" +#include "audio/Filter.h" namespace love { @@ -79,6 +80,10 @@ public: 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(); + virtual bool getFilter(love::audio::Filter::Type &type, std::vector ¶ms); + private: float pitch; diff --git a/src/modules/audio/openal/Audio.cpp b/src/modules/audio/openal/Audio.cpp index 920197d05..3d989be07 100644 --- a/src/modules/audio/openal/Audio.cpp +++ b/src/modules/audio/openal/Audio.cpp @@ -110,7 +110,8 @@ Audio::Audio() if (!alcMakeContextCurrent(context) || alcGetError(device) != ALC_NO_ERROR) throw love::Exception("Could not make context current."); - // pool must be allocated after AL context. + initializeEFX(); + try { pool = new Pool(); @@ -383,6 +384,109 @@ const std::vector &Audio::getRecordingDevices() return capture; } +#ifdef ALC_EXT_EFX +LPALGENEFFECTS alGenEffects = nullptr; +LPALDELETEEFFECTS alDeleteEffects = nullptr; +LPALISEFFECT alIsEffect = nullptr; +LPALEFFECTI alEffecti = nullptr; +LPALEFFECTIV alEffectiv = nullptr; +LPALEFFECTF alEffectf = nullptr; +LPALEFFECTFV alEffectfv = nullptr; +LPALGETEFFECTI alGetEffecti = nullptr; +LPALGETEFFECTIV alGetEffectiv = nullptr; +LPALGETEFFECTF alGetEffectf = nullptr; +LPALGETEFFECTFV alGetEffectfv = nullptr; +LPALGENFILTERS alGenFilters = nullptr; +LPALDELETEFILTERS alDeleteFilters = nullptr; +LPALISFILTER alIsFilter = nullptr; +LPALFILTERI alFilteri = nullptr; +LPALFILTERIV alFilteriv = nullptr; +LPALFILTERF alFilterf = nullptr; +LPALFILTERFV alFilterfv = nullptr; +LPALGETFILTERI alGetFilteri = nullptr; +LPALGETFILTERIV alGetFilteriv = nullptr; +LPALGETFILTERF alGetFilterf = nullptr; +LPALGETFILTERFV alGetFilterfv = nullptr; +LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots = nullptr; +LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots = nullptr; +LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot = nullptr; +LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti = nullptr; +LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv = nullptr; +LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf = nullptr; +LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv = nullptr; +LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti = nullptr; +LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv = nullptr; +LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf = nullptr; +LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv = nullptr; +#endif + +void Audio::initializeEFX() +{ + #ifdef ALC_EXT_EFX + if (alcIsExtensionPresent(device, "ALC_EXT_EFX") == AL_FALSE) + return; + + alGenEffects = (LPALGENEFFECTS)alGetProcAddress("alGenEffects"); + alDeleteEffects = (LPALDELETEEFFECTS)alGetProcAddress("alDeleteEffects"); + alIsEffect = (LPALISEFFECT)alGetProcAddress("alIsEffect"); + alEffecti = (LPALEFFECTI)alGetProcAddress("alEffecti"); + alEffectiv = (LPALEFFECTIV)alGetProcAddress("alEffectiv"); + alEffectf = (LPALEFFECTF)alGetProcAddress("alEffectf"); + alEffectfv = (LPALEFFECTFV)alGetProcAddress("alEffectfv"); + alGetEffecti = (LPALGETEFFECTI)alGetProcAddress("alGetEffecti"); + alGetEffectiv = (LPALGETEFFECTIV)alGetProcAddress("alGetEffectiv"); + alGetEffectf = (LPALGETEFFECTF)alGetProcAddress("alGetEffectf"); + alGetEffectfv = (LPALGETEFFECTFV)alGetProcAddress("alGetEffectfv"); + alGenFilters = (LPALGENFILTERS)alGetProcAddress("alGenFilters"); + alDeleteFilters = (LPALDELETEFILTERS)alGetProcAddress("alDeleteFilters"); + alIsFilter = (LPALISFILTER)alGetProcAddress("alIsFilter"); + alFilteri = (LPALFILTERI)alGetProcAddress("alFilteri"); + alFilteriv = (LPALFILTERIV)alGetProcAddress("alFilteriv"); + alFilterf = (LPALFILTERF)alGetProcAddress("alFilterf"); + alFilterfv = (LPALFILTERFV)alGetProcAddress("alFilterfv"); + alGetFilteri = (LPALGETFILTERI)alGetProcAddress("alGetFilteri"); + alGetFilteriv = (LPALGETFILTERIV)alGetProcAddress("alGetFilteriv"); + alGetFilterf = (LPALGETFILTERF)alGetProcAddress("alGetFilterf"); + alGetFilterfv = (LPALGETFILTERFV)alGetProcAddress("alGetFilterfv"); + alGenAuxiliaryEffectSlots = (LPALGENAUXILIARYEFFECTSLOTS)alGetProcAddress("alGenAuxiliaryEffectSlots"); + alDeleteAuxiliaryEffectSlots = (LPALDELETEAUXILIARYEFFECTSLOTS)alGetProcAddress("alDeleteAuxiliaryEffectSlots"); + alIsAuxiliaryEffectSlot = (LPALISAUXILIARYEFFECTSLOT)alGetProcAddress("alIsAuxiliaryEffectSlot"); + alAuxiliaryEffectSloti = (LPALAUXILIARYEFFECTSLOTI)alGetProcAddress("alAuxiliaryEffectSloti"); + alAuxiliaryEffectSlotiv = (LPALAUXILIARYEFFECTSLOTIV)alGetProcAddress("alAuxiliaryEffectSlotiv"); + alAuxiliaryEffectSlotf = (LPALAUXILIARYEFFECTSLOTF)alGetProcAddress("alAuxiliaryEffectSlotf"); + alAuxiliaryEffectSlotfv = (LPALAUXILIARYEFFECTSLOTFV)alGetProcAddress("alAuxiliaryEffectSlotfv"); + alGetAuxiliaryEffectSloti = (LPALGETAUXILIARYEFFECTSLOTI)alGetProcAddress("alGetAuxiliaryEffectSloti"); + alGetAuxiliaryEffectSlotiv = (LPALGETAUXILIARYEFFECTSLOTIV)alGetProcAddress("alGetAuxiliaryEffectSlotiv"); + alGetAuxiliaryEffectSlotf = (LPALGETAUXILIARYEFFECTSLOTF)alGetProcAddress("alGetAuxiliaryEffectSlotf"); + alGetAuxiliaryEffectSlotfv = (LPALGETAUXILIARYEFFECTSLOTFV)alGetProcAddress("alGetAuxiliaryEffectSlotfv"); + + //failed to initialize functions, revert to nullptr + if (!alGenEffects || !alDeleteEffects || !alIsEffect || + !alGenFilters || !alDeleteFilters || !alIsFilter || + !alGenAuxiliaryEffectSlots || !alDeleteAuxiliaryEffectSlots || !alIsAuxiliaryEffectSlot || + !alEffecti || !alEffectiv || !alEffectf || !alEffectfv || + !alGetEffecti || !alGetEffectiv || !alGetEffectf || !alGetEffectfv || + !alFilteri || !alFilteriv || !alFilterf || !alFilterfv || + !alGetFilteri || !alGetFilteriv || !alGetFilterf || !alGetFilterfv || + !alAuxiliaryEffectSloti || !alAuxiliaryEffectSlotiv || !alAuxiliaryEffectSlotf || !alAuxiliaryEffectSlotfv || + !alGetAuxiliaryEffectSloti || !alGetAuxiliaryEffectSlotiv || !alGetAuxiliaryEffectSlotf || !alGetAuxiliaryEffectSlotfv) + { + alGenEffects = nullptr; alDeleteEffects = nullptr; alIsEffect = nullptr; + alEffecti = nullptr; alEffectiv = nullptr; alEffectf = nullptr; alEffectfv = nullptr; + alGetEffecti = nullptr; alGetEffectiv = nullptr; alGetEffectf = nullptr; alGetEffectfv = nullptr; + alGenFilters = nullptr; alDeleteFilters = nullptr; alIsFilter = nullptr; + alFilteri = nullptr; alFilteriv = nullptr; alFilterf = nullptr; alFilterfv = nullptr; + alGetFilteri = nullptr; alGetFilteriv = nullptr; alGetFilterf = nullptr; alGetFilterfv = nullptr; + alGenAuxiliaryEffectSlots = nullptr; alDeleteAuxiliaryEffectSlots = nullptr; alIsAuxiliaryEffectSlot = nullptr; + alAuxiliaryEffectSloti = nullptr; alAuxiliaryEffectSlotiv = nullptr; + alAuxiliaryEffectSlotf = nullptr; alAuxiliaryEffectSlotfv = nullptr; + alGetAuxiliaryEffectSloti = nullptr; alGetAuxiliaryEffectSlotiv = nullptr; + alGetAuxiliaryEffectSlotf = nullptr; alGetAuxiliaryEffectSlotfv = nullptr; + } + + #endif +} + } // openal } // audio } // love diff --git a/src/modules/audio/openal/Audio.h b/src/modules/audio/openal/Audio.h index aaa675fdc..b4054872b 100644 --- a/src/modules/audio/openal/Audio.h +++ b/src/modules/audio/openal/Audio.h @@ -30,6 +30,7 @@ // LOVE #include "audio/Audio.h" #include "audio/RecordingDevice.h" +#include "audio/Filter.h" #include "common/config.h" #include "sound/SoundData.h" @@ -111,7 +112,7 @@ public: void setDistanceModel(DistanceModel distanceModel); private: - + void initializeEFX(); // The OpenAL device. ALCdevice *device; @@ -150,6 +151,47 @@ private: }; // Audio +#ifdef ALC_EXT_EFX + // Effect objects +extern LPALGENEFFECTS alGenEffects; +extern LPALDELETEEFFECTS alDeleteEffects; +extern LPALISEFFECT alIsEffect; +extern LPALEFFECTI alEffecti; +extern LPALEFFECTIV alEffectiv; +extern LPALEFFECTF alEffectf; +extern LPALEFFECTFV alEffectfv; +extern LPALGETEFFECTI alGetEffecti; +extern LPALGETEFFECTIV alGetEffectiv; +extern LPALGETEFFECTF alGetEffectf; +extern LPALGETEFFECTFV alGetEffectfv; + +//Filter objects +extern LPALGENFILTERS alGenFilters; +extern LPALDELETEFILTERS alDeleteFilters; +extern LPALISFILTER alIsFilter; +extern LPALFILTERI alFilteri; +extern LPALFILTERIV alFilteriv; +extern LPALFILTERF alFilterf; +extern LPALFILTERFV alFilterfv; +extern LPALGETFILTERI alGetFilteri; +extern LPALGETFILTERIV alGetFilteriv; +extern LPALGETFILTERF alGetFilterf; +extern LPALGETFILTERFV alGetFilterfv; + +// Auxiliary slot object +extern LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots; +extern LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots; +extern LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot; +extern LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti; +extern LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv; +extern LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf; +extern LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv; +extern LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti; +extern LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv; +extern LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf; +extern LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv; +#endif + } // openal } // audio } // love diff --git a/src/modules/audio/openal/Filter.cpp b/src/modules/audio/openal/Filter.cpp new file mode 100644 index 000000000..148ad1051 --- /dev/null +++ b/src/modules/audio/openal/Filter.cpp @@ -0,0 +1,136 @@ +/** + * 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 "Filter.h" +#include "common/Exception.h" + +namespace love +{ +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 +} + +Filter::Filter(const Filter &s) + : Filter() +{ + setParams(s.getType(), s.getParams()); +} + +Filter::~Filter() +{ + #ifdef ALC_EXT_EFX + if (filter != AL_FILTER_NULL) + alDeleteFilters(1, &filter); + #endif +} + +Filter *Filter::clone() +{ + return new Filter(*this); +} + +ALuint Filter::getFilter() const +{ + return filter; +} + +bool Filter::setParams(Type type, const std::vector ¶ms) +{ + this->type = type; + this->params = params; + + if (filter == AL_FILTER_NULL) + return false; + + #ifdef ALC_EXT_EFX + switch (type) + { + case TYPE_LOWPASS: + alFilteri(filter, AL_FILTER_TYPE, AL_FILTER_LOWPASS); + break; + case TYPE_HIGHPASS: + alFilteri(filter, AL_FILTER_TYPE, AL_FILTER_HIGHPASS); + break; + case TYPE_BANDPASS: + alFilteri(filter, AL_FILTER_TYPE, AL_FILTER_BANDPASS); + break; + case TYPE_MAX_ENUM: + break; + } + //failed to make filter specific type - not supported etc. + if (alGetError() != AL_NO_ERROR) + { + filter = AL_FILTER_NULL; + return false; + } + + 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)); + 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)); + 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)); + break; + case TYPE_MAX_ENUM: + break; + } + #endif + + return true; +} + +const std::vector &Filter::getParams() const +{ + return params; +} + +} //openal +} //audio +} //love diff --git a/src/modules/audio/openal/Filter.h b/src/modules/audio/openal/Filter.h new file mode 100644 index 000000000..9ca984d66 --- /dev/null +++ b/src/modules/audio/openal/Filter.h @@ -0,0 +1,75 @@ +/** + * 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_FILTERS_H +#define LOVE_AUDIO_OPENAL_FILTERS_H + +// OpenAL +#ifdef LOVE_APPLE_USE_FRAMEWORKS // Frameworks have different include paths. +#ifdef LOVE_IOS +#include +#include +#else +#include +#include +#endif +#else +#include +#include +#include +#endif + +#include + +#include "audio/Filter.h" +#include "Audio.h" + +#ifndef AL_FILTER_NULL +#define AL_FILTER_NULL (0) +#endif + +namespace love +{ +namespace audio +{ +namespace openal +{ + +class Filter : public love::audio::Filter +{ +public: + Filter(); + Filter(const Filter &s); + virtual ~Filter(); + virtual Filter *clone(); + ALuint getFilter() const; + virtual bool setParams(Type type, const std::vector ¶ms); + virtual const std::vector &getParams() const; + +private: + ALuint filter = AL_FILTER_NULL; + std::vector params; +}; + +} //openal +} //audio +} //love + +#endif //LOVE_AUDIO_OPENAL_FILTERS_H diff --git a/src/modules/audio/openal/Source.cpp b/src/modules/audio/openal/Source.cpp index 56c2f661a..b1cd950e1 100644 --- a/src/modules/audio/openal/Source.cpp +++ b/src/modules/audio/openal/Source.cpp @@ -19,6 +19,7 @@ **/ #include "Source.h" +#include "Filter.h" #include "Pool.h" #include "Audio.h" #include "common/math.h" @@ -213,6 +214,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(); setFloatv(position, s.position); setFloatv(velocity, s.velocity); @@ -226,6 +229,9 @@ Source::~Source() if (sourceType != TYPE_STATIC) alDeleteBuffers(MAX_BUFFERS, streamBuffers); + + if (filter) + delete filter; } love::audio::Source *Source::clone() @@ -989,6 +995,9 @@ void Source::reset() 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 + alSourcei(source, AL_DIRECT_FILTER, filter ? filter->getFilter() : AL_FILTER_NULL); + #endif } void Source::setFloatv(float *dst, const float *src) const @@ -1195,6 +1204,46 @@ int Source::getChannels() const return channels; } +bool Source::setFilter(love::audio::Filter::Type type, std::vector ¶ms) +{ + if (!filter) + filter = new Filter(); + + bool result = filter->setParams(type, params); + + #ifdef ALC_EXT_EFX + if (valid) + alSourcei(source, AL_DIRECT_FILTER, filter->getFilter()); + #endif + + return result; +} + +bool Source::setFilter() +{ + if (filter) + delete filter; + + filter = nullptr; + + #ifdef ALC_EXT_EFX + if (valid) + alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL); + #endif + + return true; +} + +bool Source::getFilter(love::audio::Filter::Type &type, std::vector ¶ms) +{ + if (!filter) + return false; + + type = filter->getType(); + params = filter->getParams(); + return true; +} + } // openal } // audio } // love diff --git a/src/modules/audio/openal/Source.h b/src/modules/audio/openal/Source.h index 680cf5ddb..798b1efeb 100644 --- a/src/modules/audio/openal/Source.h +++ b/src/modules/audio/openal/Source.h @@ -25,8 +25,11 @@ #include "common/config.h" #include "common/Object.h" #include "audio/Source.h" +#include "audio/Filter.h" #include "sound/SoundData.h" #include "sound/Decoder.h" +#include "Audio.h" +#include "Filter.h" // STL #include @@ -141,6 +144,10 @@ public: virtual float getMaxDistance() const; virtual int getChannels() const; + virtual bool setFilter(love::audio::Filter::Type type, std::vector ¶ms); + virtual bool setFilter(); + virtual bool getFilter(love::audio::Filter::Type &type, std::vector ¶ms); + virtual int getFreeBufferCount() const; virtual bool queue(void *data, size_t length, int dataSampleRate, int dataBitDepth, int dataChannels); virtual bool queueAtomic(void *data, ALsizei length); @@ -214,6 +221,8 @@ private: int unusedBufferTop = -1; ALsizei bufferedBytes = 0; + Filter *filter = nullptr; + }; // Source } // openal diff --git a/src/modules/audio/wrap_Source.cpp b/src/modules/audio/wrap_Source.cpp index c34c57762..556bd3133 100644 --- a/src/modules/audio/wrap_Source.cpp +++ b/src/modules/audio/wrap_Source.cpp @@ -330,6 +330,76 @@ int w_Source_getChannels(lua_State *L) return 1; } +int w_Source_setFilter(lua_State *L) +{ + Source *t = luax_checksource(L, 1); + + Filter::Type type; + std::vector params; + + params.reserve(Filter::getParameterCount()); + + 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 (ftypestr && !Filter::getConstant(ftypestr, type)) + return luaL_error(L, "Invalid filter type: %s", ftypestr); + + 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)) + { + if (lua_objlen(L, 2) == 0) //empty table also clears filter + { + lua_pushboolean(L, t->setFilter()); + return 1; + } + lua_rawgeti(L, 2, 1); + const char *ftypestr = luaL_checkstring(L, -1); + if (ftypestr && !Filter::getConstant(ftypestr, type)) + return luaL_error(L, "Invalid filter type: %s", ftypestr); + lua_pop(L, 1); + + unsigned int count = Filter::getParameterCount(type); + for (unsigned int i = 0; i < count; i++) + { + lua_rawgeti(L, 2, i + 2); + params.push_back(luaL_checknumber(L, -1)); + lua_pop(L, 1); + } + } + else + return luax_typerror(L, 2, "filter description"); + + 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 params; + if (!t->getFilter(type, params)) + return 0; + + const char *str = nullptr; + Filter::getConstant(type, str); + lua_pushstring(L, str); + + for (unsigned int i = 0; i < params.size(); i++) + lua_pushnumber(L, params[i]); + + return params.size() + 1; +} + int w_Source_getFreeBufferCount(lua_State *L) { Source *t = luax_checksource(L, 1); @@ -440,6 +510,9 @@ static const luaL_Reg w_Source_functions[] = { "getChannels", w_Source_getChannels }, + { "setFilter", w_Source_setFilter }, + { "getFilter", w_Source_getFilter }, + { "getFreeBufferCount", w_Source_getFreeBufferCount }, { "queue", w_Source_queue }, diff --git a/src/modules/audio/wrap_Source.h b/src/modules/audio/wrap_Source.h index a005bdd6c..8b6f02cc8 100644 --- a/src/modules/audio/wrap_Source.h +++ b/src/modules/audio/wrap_Source.h @@ -23,6 +23,7 @@ #include "common/runtime.h" #include "Source.h" +#include "Filter.h" namespace love {