From e60f3c6cda48e6c3b49456d21ce8edc45b3eca30 Mon Sep 17 00:00:00 2001 From: Raidho Date: Thu, 8 Dec 2016 19:27:47 +0300 Subject: [PATCH 1/4] EFX implemented --HG-- branch : minor --- CMakeLists.txt | 4 + src/modules/audio/Audio.h | 47 +++- src/modules/audio/Effect.cpp | 190 +++++++++++++ src/modules/audio/Effect.h | 148 ++++++++++ src/modules/audio/Filter.cpp | 8 +- src/modules/audio/Source.h | 11 +- src/modules/audio/null/Audio.cpp | 39 +++ src/modules/audio/null/Audio.h | 9 + src/modules/audio/null/Source.cpp | 36 ++- src/modules/audio/null/Source.h | 13 +- src/modules/audio/openal/Audio.cpp | 168 +++++++++++- src/modules/audio/openal/Audio.h | 22 +- src/modules/audio/openal/Effect.cpp | 408 ++++++++++++++++++++++++++++ src/modules/audio/openal/Effect.h | 85 ++++++ src/modules/audio/openal/Filter.cpp | 86 ++++-- src/modules/audio/openal/Filter.h | 3 + src/modules/audio/openal/Source.cpp | 177 ++++++++++-- src/modules/audio/openal/Source.h | 18 +- src/modules/audio/wrap_Audio.cpp | 193 +++++++++++++ src/modules/audio/wrap_Source.cpp | 179 ++++++++---- 20 files changed, 1726 insertions(+), 118 deletions(-) create mode 100644 src/modules/audio/Effect.cpp create mode 100644 src/modules/audio/Effect.h create mode 100644 src/modules/audio/openal/Effect.cpp create mode 100644 src/modules/audio/openal/Effect.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8daeca534..3e0589778 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 811845bd4..66d83e04d 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,49 @@ public: */ virtual void setDistanceModel(DistanceModel distanceModel) = 0; + /** + * Sets scene EFX effect. + * @param slot Slot to put effect into. + * @param type Effect type to use. + * @param params Effect description table. + * @return true if successful, false otherwise. + */ + virtual bool setSceneEffect(int slot, Effect::Type type, std::vector ¶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 type Effect type. + * @param params Effect description table. + * @return true if effect was present, false otherwise. + */ + virtual bool getSceneEffect(int slot, Effect::Type &type, std::vector ¶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..92292d211 --- /dev/null +++ b/src/modules/audio/Effect.cpp @@ -0,0 +1,190 @@ +/** + * 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); +} + +const std::vector &Effect::getParameterTypes(Effect::Type 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)); + +std::map> Effect::parameterTypes = +{ + //gain, high gain, density, diffusion, decay, high decay ratio, refl gain, refl delay, late gain, late delay, rolloff, air high gain, high limiter + {Effect::TYPE_REVERB, {Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT, + Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_BOOL}}, + //waveform, phase, rate, depth, feedback, delay + {Effect::TYPE_CHORUS, { Effect::PAR_WAVEFORM, Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT}}, + //gain, edge, lowpass cutoff, eq center, eq width + {Effect::TYPE_DISTORTION, {Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT}}, + //delay, LR delay, damping, feedback, spread + {Effect::TYPE_ECHO, {Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT}}, + //waveform, phase, rate, depth, feedback, delay + {Effect::TYPE_FLANGER, {Effect::PAR_WAVEFORM, Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT}}, + //frequency, left direction, right direction + {Effect::TYPE_FREQSHIFTER, {Effect::PAR_FLOAT, Effect::PAR_DIRECTION, Effect::PAR_DIRECTION}}, + //waveform, rate, phoneme A, phoneme B, phoneme A coarse tune, phoneme B coarse tune + {Effect::TYPE_MORPHER, {Effect::PAR_WAVEFORM, Effect::PAR_FLOAT, Effect::PAR_PHONEME, Effect::PAR_PHONEME, Effect::PAR_FLOAT, Effect::PAR_FLOAT}}, + //pitch(semitones) + {Effect::TYPE_PITCHSHIFTER, {Effect::PAR_FLOAT}}, + //waveform, frequency, highpass cutoff + {Effect::TYPE_MODULATOR, {Effect::PAR_WAVEFORM, Effect::PAR_FLOAT, Effect::PAR_FLOAT}}, + //attack, release, resonance, peak gain + {Effect::TYPE_AUTOWAH, {Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT}}, + // on-off switch + {Effect::TYPE_COMPRESSOR, {Effect::PAR_BOOL}}, + //low gain, low cut, mid1 gain, mid1 freq, mid1 band, mid2 gain, mid2 freq, mid2 band, high gain, high cut + {Effect::TYPE_EQUALIZER, {Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT, + Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT, Effect::PAR_FLOAT}}, +}; + +} //audio +} //love diff --git a/src/modules/audio/Effect.h b/src/modules/audio/Effect.h new file mode 100644 index 000000000..d4f10d5d6 --- /dev/null +++ b/src/modules/audio/Effect.h @@ -0,0 +1,148 @@ +/** + * 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 + +namespace love +{ +namespace audio +{ + +class Effect +{ +public: + enum 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 ParameterType + { + PAR_FLOAT, + PAR_BOOL, + PAR_WAVEFORM, + PAR_DIRECTION, + PAR_PHONEME, + PAR_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 const std::vector &getParameterTypes(Type 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; + 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 a56983cb7..0a1915343 100644 --- a/src/modules/audio/Filter.cpp +++ b/src/modules/audio/Filter.cpp @@ -67,12 +67,12 @@ StringMap::Entry Filter::typeEntries[] = StringMap Filter::types(Filter::typeEntries, sizeof(Filter::typeEntries)); +//all parameters are floats, therefore bare count will suffice std::map Filter::parameterCount = { - {Filter::TYPE_LOWPASS, 2}, - {Filter::TYPE_HIGHPASS, 2}, - {Filter::TYPE_BANDPASS, 3}, - {Filter::TYPE_MAX_ENUM, 3}, + {Filter::TYPE_LOWPASS, 1}, + {Filter::TYPE_HIGHPASS, 1}, + {Filter::TYPE_BANDPASS, 2}, }; } //audio diff --git a/src/modules/audio/Source.h b/src/modules/audio/Source.h index 7cb80c376..d80420c04 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,6 +104,8 @@ 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; @@ -111,6 +113,11 @@ public: virtual bool setFilter() = 0; virtual bool getFilter(Filter::Type &type, std::vector ¶ms) = 0; + virtual bool setSceneEffect(int slot, int effect) = 0; + virtual bool setSceneEffect(int slot, int effect, Filter::Type type, std::vector ¶ms) = 0; + virtual bool setSceneEffect(int slot) = 0; + virtual bool getSceneEffect(int slot, int &effect, 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/Audio.cpp b/src/modules/audio/null/Audio.cpp index 9db2e67b1..49e82cac7 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, Effect::Type, std::vector &) +{ + return false; +} + +bool Audio::setSceneEffect(int) +{ + return false; +} + +bool Audio::getSceneEffect(int, Effect::Type &, std::vector &) +{ + 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 fdd8325fc..58e07183c 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, Effect::Type type, std::vector ¶ms); + bool setSceneEffect(int slot); + bool getSceneEffect(int slot, Effect::Type &type, std::vector ¶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 ae11d2dc1..bb7725c1b 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; @@ -242,6 +254,26 @@ bool Source::getFilter(love::audio::Filter::Type &, std::vector &) return false; } +bool Source::setSceneEffect(int, int) +{ + return false; +} + +bool Source::setSceneEffect(int, int, love::audio::Filter::Type, std::vector &) +{ + return false; +} + +bool Source::setSceneEffect(int) +{ + return false; +} + +bool Source::getSceneEffect(int, int &, 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 007d03f3d..be8041d48 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,6 +75,8 @@ 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; @@ -84,6 +86,11 @@ public: virtual bool setFilter(); virtual bool getFilter(love::audio::Filter::Type &type, std::vector ¶ms); + virtual bool setSceneEffect(int slot, int effect); + virtual bool setSceneEffect(int slot, int effect, love::audio::Filter::Type type, std::vector ¶ms); + virtual bool setSceneEffect(int slot); + virtual bool getSceneEffect(int slot, int &effect, love::audio::Filter::Type &type, std::vector ¶ms); + private: float pitch; @@ -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 3d989be07..e52d1f200 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, Effect::Type type, std::vector ¶ms) +{ + if (slot < 0 || slot >= MAX_SCENE_EFFECTS) + return false; + + if (!effects[slot]) + effects[slot] = new Effect(); + + bool result = effects[slot]->setParams(type, params); + + #ifdef ALC_EXT_EFX + if (alAuxiliaryEffectSloti) + { + if (result == true) + { + alAuxiliaryEffectSloti(effectSlots[slot], AL_EFFECTSLOT_EFFECT, effects[slot]->getEffect()); + alAuxiliaryEffectSlotf(effectSlots[slot], AL_EFFECTSLOT_GAIN, params[0]); + } + 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, Effect::Type &type, std::vector ¶ms) +{ + if (slot < 0 || slot >= MAX_SCENE_EFFECTS) + return false; + + if (!effects[slot]) + return false; + + type = effects[slot]->getType(); + 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 b4054872b..c7a56f7aa 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, Effect::Type type, std::vector ¶ms); + bool setSceneEffect(int slot); + bool getSceneEffect(int slot, Effect::Type &type, std::vector ¶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..881e4a435 --- /dev/null +++ b/src/modules/audio/openal/Effect.cpp @@ -0,0 +1,408 @@ +/** + * 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.getType(), 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(Type type, const std::vector ¶ms) +{ + this->type = type; + this->params = params; + + if (!generateEffect()) + return false; + + #ifdef ALC_EXT_EFX + 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_MAX_ENUM: + break; + } + + //failed to make effect specific type - not supported etc. + if (alGetError() != AL_NO_ERROR) + { + deleteEffect(); + return false; + } + + #define PARAMSTR(i,e,v) effect, AL_ ## e ## _ ## v, clampf(params[(i)], AL_ ## e ## _MIN_ ## v, AL_ ## e ## _MAX_ ## v, AL_ ## e ## _DEFAULT_ ## v) + switch (type) + { + case TYPE_REVERB: + { + alEffectf(PARAMSTR(1,REVERB,GAIN)); + alEffectf(PARAMSTR(2,REVERB,GAINHF)); + alEffectf(PARAMSTR(3,REVERB,DENSITY)); + alEffectf(PARAMSTR(4,REVERB,DIFFUSION)); + alEffectf(PARAMSTR(5,REVERB,DECAY_TIME)); + alEffectf(PARAMSTR(6,REVERB,DECAY_HFRATIO)); + alEffectf(PARAMSTR(7,REVERB,REFLECTIONS_GAIN)); + alEffectf(PARAMSTR(8,REVERB,REFLECTIONS_DELAY)); + alEffectf(PARAMSTR(9,REVERB,LATE_REVERB_GAIN)); + alEffectf(PARAMSTR(10,REVERB,LATE_REVERB_DELAY));; + alEffectf(PARAMSTR(11,REVERB,ROOM_ROLLOFF_FACTOR)); + alEffectf(PARAMSTR(12,REVERB,AIR_ABSORPTION_GAINHF)); + alEffecti(effect, AL_REVERB_DECAY_HFLIMIT, params[13] < 0.5 ? AL_FALSE : AL_TRUE); + break; + } + case TYPE_CHORUS: + { + Effect::Waveform wave = static_cast(params[1]); + if (wave == Effect::WAVE_SINE) + alEffecti(effect, AL_CHORUS_WAVEFORM, AL_CHORUS_WAVEFORM_SINUSOID); + else if (wave == Effect::WAVE_TRIANGLE) + alEffecti(effect, AL_CHORUS_WAVEFORM, AL_CHORUS_WAVEFORM_TRIANGLE); + else + alEffecti(effect, AL_CHORUS_WAVEFORM, AL_CHORUS_DEFAULT_WAVEFORM); + + alEffecti(PARAMSTR(2,CHORUS,PHASE)); + alEffectf(PARAMSTR(3,CHORUS,RATE)); + alEffectf(PARAMSTR(4,CHORUS,DEPTH)); + alEffectf(PARAMSTR(5,CHORUS,FEEDBACK)); + alEffectf(PARAMSTR(6,CHORUS,DELAY)); + break; + } + case TYPE_DISTORTION: + alEffectf(PARAMSTR(1,DISTORTION,GAIN)); + alEffectf(PARAMSTR(2,DISTORTION,EDGE)); + alEffectf(PARAMSTR(3,DISTORTION,LOWPASS_CUTOFF)); + alEffectf(PARAMSTR(4,DISTORTION,EQCENTER)); + alEffectf(PARAMSTR(5,DISTORTION,EQBANDWIDTH)); + break; + + case TYPE_ECHO: + alEffectf(PARAMSTR(1,ECHO,DELAY)); + alEffectf(PARAMSTR(2,ECHO,LRDELAY)); + alEffectf(PARAMSTR(3,ECHO,DAMPING)); + alEffectf(PARAMSTR(4,ECHO,FEEDBACK)); + alEffectf(PARAMSTR(5,ECHO,SPREAD)); + break; + + case TYPE_FLANGER: + { + Effect::Waveform wave = static_cast(params[1]); + if (wave == Effect::WAVE_SINE) + alEffecti(effect, AL_FLANGER_WAVEFORM, AL_FLANGER_WAVEFORM_SINUSOID); + else if (wave == Effect::WAVE_TRIANGLE) + alEffecti(effect, AL_FLANGER_WAVEFORM, AL_FLANGER_WAVEFORM_TRIANGLE); + else + alEffecti(effect, AL_FLANGER_WAVEFORM, AL_FLANGER_DEFAULT_WAVEFORM); + + alEffecti(PARAMSTR(2,FLANGER,PHASE)); + alEffectf(PARAMSTR(3,FLANGER,RATE)); + alEffectf(PARAMSTR(4,FLANGER,DEPTH)); + alEffectf(PARAMSTR(5,FLANGER,FEEDBACK)); + alEffectf(PARAMSTR(6,FLANGER,DELAY)); + break; + } + case TYPE_FREQSHIFTER: + { + alEffectf(PARAMSTR(1,FREQUENCY_SHIFTER,FREQUENCY)); + + Effect::Direction dir = static_cast(params[2]); + if (dir == Effect::DIR_NONE) + alEffecti(effect, AL_FREQUENCY_SHIFTER_LEFT_DIRECTION, AL_FREQUENCY_SHIFTER_DIRECTION_OFF); + else if(dir == Effect::DIR_UP) + alEffecti(effect, AL_FREQUENCY_SHIFTER_LEFT_DIRECTION, AL_FREQUENCY_SHIFTER_DIRECTION_UP); + else if(dir == Effect::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(params[3]); + if (dir == Effect::DIR_NONE) + alEffecti(effect, AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION, AL_FREQUENCY_SHIFTER_DIRECTION_OFF); + else if(dir == Effect::DIR_UP) + alEffecti(effect, AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION, AL_FREQUENCY_SHIFTER_DIRECTION_UP); + else if(dir == Effect::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: + { + Effect::Waveform wave = static_cast(params[1]); + if (wave == Effect::WAVE_SINE) + alEffecti(effect, AL_VOCAL_MORPHER_WAVEFORM, AL_VOCAL_MORPHER_WAVEFORM_SINUSOID); + else if (wave == Effect::WAVE_TRIANGLE) + alEffecti(effect, AL_VOCAL_MORPHER_WAVEFORM, AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE); + else if (wave == Effect::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); + + alEffectf(PARAMSTR(2,VOCAL_MORPHER,RATE)); + + if (isnanf(params[3])) + alEffecti(effect, AL_VOCAL_MORPHER_PHONEMEA, AL_VOCAL_MORPHER_DEFAULT_PHONEMEA); + else + alEffecti(effect, AL_VOCAL_MORPHER_PHONEMEA, phonemeMap[static_cast(params[2])]); + + if (isnanf(params[4])) + alEffecti(effect, AL_VOCAL_MORPHER_PHONEMEB, AL_VOCAL_MORPHER_DEFAULT_PHONEMEB); + else + alEffecti(effect, AL_VOCAL_MORPHER_PHONEMEB, phonemeMap[static_cast(params[3])]); + + alEffecti(PARAMSTR(5,VOCAL_MORPHER,PHONEMEA_COARSE_TUNING)); + alEffecti(PARAMSTR(6,VOCAL_MORPHER,PHONEMEB_COARSE_TUNING)); + break; + } + case TYPE_PITCHSHIFTER: + { + int coarse = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE; + int fine = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE; + if (!isnanf(params[1])) + { + coarse = (int)ceil(params[1]); + fine = (int)(fmod(params[1], 1.0)*100.0); + if (fine > 50) + { + fine -= 100; + coarse += 1; + } + else if (fine < -50) + { + fine += 100; + coarse -= 1; + } + std::cout << params[1] << " " << coarse << " " << fine << std::endl; + 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: + { + Effect::Waveform wave = static_cast(params[1]); + if (wave == Effect::WAVE_SINE) + alEffecti(effect, AL_RING_MODULATOR_WAVEFORM, AL_RING_MODULATOR_SINUSOID); + else if (wave == Effect::WAVE_SAWTOOTH) + alEffecti(effect, AL_RING_MODULATOR_WAVEFORM, AL_RING_MODULATOR_SAWTOOTH); + else if (wave == Effect::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(2,RING_MODULATOR,FREQUENCY)); + alEffectf(PARAMSTR(3,RING_MODULATOR,HIGHPASS_CUTOFF)); + break; + } + case TYPE_AUTOWAH: + alEffectf(PARAMSTR(1,AUTOWAH,ATTACK_TIME)); + alEffectf(PARAMSTR(2,AUTOWAH,RELEASE_TIME)); + alEffectf(PARAMSTR(3,AUTOWAH,RESONANCE)); + alEffectf(PARAMSTR(4,AUTOWAH,PEAK_GAIN)); + break; + + case TYPE_COMPRESSOR: + alEffecti(effect, AL_COMPRESSOR_ONOFF, params[1] < 0.5 ? 0 : 1); + break; + + case TYPE_EQUALIZER: + alEffectf(PARAMSTR(1,EQUALIZER,LOW_GAIN)); + alEffectf(PARAMSTR(2,EQUALIZER,LOW_CUTOFF)); + alEffectf(PARAMSTR(3,EQUALIZER,MID1_GAIN)); + alEffectf(PARAMSTR(4,EQUALIZER,MID1_CENTER)); + alEffectf(PARAMSTR(5,EQUALIZER,MID1_WIDTH)); + alEffectf(PARAMSTR(6,EQUALIZER,MID2_GAIN)); + alEffectf(PARAMSTR(7,EQUALIZER,MID2_CENTER)); + alEffectf(PARAMSTR(8,EQUALIZER,MID2_WIDTH)); + alEffectf(PARAMSTR(9,EQUALIZER,HIGH_GAIN)); + alEffectf(PARAMSTR(10,EQUALIZER,HIGH_CUTOFF)); + break; + + case TYPE_MAX_ENUM: + break; + } + #undef PARAMSTR + //alGetError(); + + return true; + #else + return false; + #endif //ALC_EXT_EFX +} + +const std::vector &Effect::getParams() const +{ + return params; +} + +//clamp values silently to avoid randomly throwing errors due to implementation differences +float Effect::clampf(float val, float min, float max, float def) +{ + if (isnanf(val)) return def; + else if (val < min) val = min; + else if (val > max) val = max; + return val; +} + +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..52147f225 --- /dev/null +++ b/src/modules/audio/openal/Effect.h @@ -0,0 +1,85 @@ +/** + * 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(Type type, const std::vector ¶ms); + virtual const std::vector &getParams() const; + +private: + bool generateEffect(); + void deleteEffect(); + float clampf(float val, float min, float max, float def); + ALuint effect = AL_EFFECT_NULL; + std::vector 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 1a28d8a2b..0a2e8dde4 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,25 +30,10 @@ 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) @@ -57,10 +44,7 @@ Filter::Filter(const Filter &s) Filter::~Filter() { - #ifdef ALC_EXT_EFX - if (filter != AL_FILTER_NULL) - alDeleteFilters(1, &filter); - #endif + deleteFilter(); } Filter *Filter::clone() @@ -68,6 +52,34 @@ 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; @@ -78,7 +90,7 @@ bool Filter::setParams(Type type, const std::vector ¶ms) this->type = type; this->params = params; - if (filter == AL_FILTER_NULL) + if (!generateFilter()) return false; #ifdef ALC_EXT_EFX @@ -100,31 +112,36 @@ 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 PARAMSTR(i,e,v) filter, AL_ ## e ## _ ## v, clampf(params[(i)], AL_ ## e ## _MIN_ ## v, AL_ ## e ## _MAX_ ## v, AL_ ## e ## _DEFAULT_ ## 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(0,LOWPASS,GAIN)); + alFilterf(PARAMSTR(1,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(0,HIGHPASS,GAIN)); + alFilterf(PARAMSTR(1,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(0,BANDPASS,GAIN)); + alFilterf(PARAMSTR(1,BANDPASS,GAINLF)); + alFilterf(PARAMSTR(2,BANDPASS,GAINHF)); break; case TYPE_MAX_ENUM: break; } - #endif + #undef PARAMSTR + //alGetError(); return true; + #else + return false; + #endif } const std::vector &Filter::getParams() const @@ -132,6 +149,15 @@ const std::vector &Filter::getParams() const return params; } +//clamp values silently to avoid randomly throwing errors due to implementation differences +float Filter::clampf(float val, float min, float max, float def) +{ + if (isnanf(val)) return def; + else if (val < min) val = min; + else if (val > max) val = max; + return val; +} + } //openal } //audio } //love diff --git a/src/modules/audio/openal/Filter.h b/src/modules/audio/openal/Filter.h index 3d41e835b..0d80288b7 100644 --- a/src/modules/audio/openal/Filter.h +++ b/src/modules/audio/openal/Filter.h @@ -65,6 +65,9 @@ public: virtual const std::vector &getParams() const; private: + bool generateFilter(); + void deleteFilter(); + float clampf(float val, float min, float max, float def); ALuint filter = AL_FILTER_NULL; std::vector params; }; diff --git a/src/modules/audio/openal/Source.cpp b/src/modules/audio/openal/Source.cpp index b1cd950e1..44e97f97e 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