EFX implemented

--HG--
branch : minor
This commit is contained in:
Raidho
2016-12-08 19:27:47 +03:00
parent 8e2d9c5138
commit e60f3c6cda
20 changed files with 1726 additions and 118 deletions
+160 -8
View File
@@ -102,7 +102,13 @@ Audio::Audio()
if (device == nullptr)
throw love::Exception("Could not open device.");
context = alcCreateContext(device, nullptr);
#ifdef ALC_EXT_EFX
ALint attribs[4] = { ALC_MAX_AUXILIARY_SENDS, MAX_SOURCE_EFFECTS, 0, 0 };
#else
ALint *attribs = nullptr;
#endif
context = alcCreateContext(device, attribs);
if (context == nullptr)
throw love::Exception("Could not create context.");
@@ -110,20 +116,53 @@ Audio::Audio()
if (!alcMakeContextCurrent(context) || alcGetError(device) != ALC_NO_ERROR)
throw love::Exception("Could not make context current.");
#ifdef ALC_EXT_EFX
initializeEFX();
alcGetIntegerv(device, ALC_MAX_AUXILIARY_SENDS, 1, &MAX_SOURCE_EFFECTS);
alGetError();
if (alGenAuxiliaryEffectSlots)
{
for (int i = 0; i < MAX_SCENE_EFFECTS; i++)
{
ALuint slot;
alGenAuxiliaryEffectSlots(1, &slot);
if (alGetError() == AL_NO_ERROR)
{
effectIndex[slot] = effectSlots.size();
effectSlots.push_back(slot);
effects.push_back(nullptr);
}
else
{
MAX_SCENE_EFFECTS = i;
break;
}
}
}
else
MAX_SCENE_EFFECTS = MAX_SOURCE_EFFECTS = 0;
#else
MAX_SCENE_EFFECTS = MAX_SOURCE_EFFECTS = 0;
#endif
try
{
pool = new Pool();
}
catch (love::Exception &)
{
for (auto c : capture)
delete c;
#ifdef ALC_EXT_EFX
if (alDeleteAuxiliaryEffectSlots)
for (auto slot : effectSlots)
alDeleteAuxiliaryEffectSlots(1, &slot);
#endif
alcMakeContextCurrent(nullptr);
alcDestroyContext(context);
alcCloseDevice(device);
for (auto c : capture)
delete c;
throw;
}
@@ -138,15 +177,21 @@ Audio::~Audio()
delete poolThread;
delete pool;
for (auto c : capture)
delete c;
#ifdef ALC_EXT_EFX
for (auto e : effects)
if (e != nullptr)
delete e;
if (alDeleteAuxiliaryEffectSlots)
for (auto slot : effectSlots)
alDeleteAuxiliaryEffectSlots(1, &slot);
#endif
alcMakeContextCurrent(nullptr);
alcDestroyContext(context);
alcCloseDevice(device);
for (auto c : capture)
delete c;
}
const char *Audio::getName() const
{
return "love.audio.openal";
@@ -269,7 +314,23 @@ float Audio::getDopplerScale() const
{
return alGetFloat(AL_DOPPLER_FACTOR);
}
/*
void Audio::setMeter(float scale)
{
if (scale >= 0.0f)
{
metersPerUnit = scale;
#ifdef ALC_EXT_EFX
alListenerf(AL_METERS_PER_UNIT, scale);
#endif
}
}
float Audio::getMeter() const
{
return metersPerUnit;
}
*/
Audio::DistanceModel Audio::getDistanceModel() const
{
return distanceModel;
@@ -384,6 +445,97 @@ const std::vector<love::audio::RecordingDevice*> &Audio::getRecordingDevices()
return capture;
}
bool Audio::setSceneEffect(int slot, Effect::Type type, std::vector<float> &params)
{
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<float> &params)
{
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;
+21 -1
View File
@@ -35,6 +35,7 @@
#include "sound/SoundData.h"
#include "Source.h"
#include "Effect.h"
#include "Pool.h"
#include "thread/threads.h"
@@ -105,12 +106,24 @@ public:
void setDopplerScale(float scale);
float getDopplerScale() const;
//void setMeter(float scale);
//float getMeter() const;
const std::vector<love::audio::RecordingDevice*> &getRecordingDevices();
DistanceModel getDistanceModel() const;
void setDistanceModel(DistanceModel distanceModel);
bool setSceneEffect(int slot, Effect::Type type, std::vector<float> &params);
bool setSceneEffect(int slot);
bool getSceneEffect(int slot, Effect::Type &type, std::vector<float> &params);
int getMaxSceneEffects() const;
int getMaxSourceEffects() const;
bool isEFXsupported() const;
ALuint getSceneEffectID(int slot);
int getSceneEffectIndex(ALuint effect);
private:
void initializeEFX();
// The OpenAL device.
@@ -122,6 +135,13 @@ private:
// The OpenAL context.
ALCcontext *context;
// The OpenAL effects
std::vector<Effect*> effects;
std::vector<ALuint> effectSlots;
std::map<ALuint, int> effectIndex;
int MAX_SCENE_EFFECTS = 16;
int MAX_SOURCE_EFFECTS = 16;
// The Pool.
Pool *pool;
@@ -148,7 +168,7 @@ private:
PoolThread *poolThread;
DistanceModel distanceModel;
float metersPerUnit = 1.0;
}; // Audio
#ifdef ALC_EXT_EFX
+408
View File
@@ -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 <cmath>
#include <iostream>
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<float> &params)
{
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<Effect::Waveform>(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<Effect::Waveform>(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<Effect::Direction>(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<Effect::Direction>(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<Effect::Waveform>(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<Effect::Phoneme>(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<Effect::Phoneme>(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<Effect::Waveform>(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<float> &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::Phoneme, ALint> Effect::phonemeMap =
{
{Effect::PHONEME_A, AL_VOCAL_MORPHER_PHONEME_A},
{Effect::PHONEME_E, AL_VOCAL_MORPHER_PHONEME_E},
{Effect::PHONEME_I, AL_VOCAL_MORPHER_PHONEME_I},
{Effect::PHONEME_O, AL_VOCAL_MORPHER_PHONEME_O},
{Effect::PHONEME_U, AL_VOCAL_MORPHER_PHONEME_U},
{Effect::PHONEME_AA, AL_VOCAL_MORPHER_PHONEME_AA},
{Effect::PHONEME_AE, AL_VOCAL_MORPHER_PHONEME_AE},
{Effect::PHONEME_AH, AL_VOCAL_MORPHER_PHONEME_AH},
{Effect::PHONEME_AO, AL_VOCAL_MORPHER_PHONEME_AO},
{Effect::PHONEME_EH, AL_VOCAL_MORPHER_PHONEME_EH},
{Effect::PHONEME_ER, AL_VOCAL_MORPHER_PHONEME_ER},
{Effect::PHONEME_IH, AL_VOCAL_MORPHER_PHONEME_IH},
{Effect::PHONEME_IY, AL_VOCAL_MORPHER_PHONEME_IY},
{Effect::PHONEME_UH, AL_VOCAL_MORPHER_PHONEME_UH},
{Effect::PHONEME_UW, AL_VOCAL_MORPHER_PHONEME_UW},
{Effect::PHONEME_B, AL_VOCAL_MORPHER_PHONEME_B},
{Effect::PHONEME_D, AL_VOCAL_MORPHER_PHONEME_D},
{Effect::PHONEME_F, AL_VOCAL_MORPHER_PHONEME_F},
{Effect::PHONEME_G, AL_VOCAL_MORPHER_PHONEME_G},
{Effect::PHONEME_J, AL_VOCAL_MORPHER_PHONEME_J},
{Effect::PHONEME_K, AL_VOCAL_MORPHER_PHONEME_K},
{Effect::PHONEME_L, AL_VOCAL_MORPHER_PHONEME_L},
{Effect::PHONEME_M, AL_VOCAL_MORPHER_PHONEME_M},
{Effect::PHONEME_N, AL_VOCAL_MORPHER_PHONEME_N},
{Effect::PHONEME_P, AL_VOCAL_MORPHER_PHONEME_P},
{Effect::PHONEME_R, AL_VOCAL_MORPHER_PHONEME_R},
{Effect::PHONEME_S, AL_VOCAL_MORPHER_PHONEME_S},
{Effect::PHONEME_T, AL_VOCAL_MORPHER_PHONEME_T},
{Effect::PHONEME_V, AL_VOCAL_MORPHER_PHONEME_V},
{Effect::PHONEME_Z, AL_VOCAL_MORPHER_PHONEME_Z}
};
} //openal
} //audio
} //love
+85
View File
@@ -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 <OpenAL/alc.h>
#include <OpenAL/al.h>
#else
#include <OpenAL-Soft/alc.h>
#include <OpenAL-Soft/al.h>
#include <OpenAL-Soft/alext.h>
#endif
#else
#include <AL/alc.h>
#include <AL/al.h>
#include <AL/alext.h>
#endif
#include <vector>
#include <map>
#include "audio/Effect.h"
#include "Audio.h"
#ifndef AL_EFFECT_NULL
#define AL_EFFECT_NULL (0)
#endif
#ifndef AL_EFFECTSLOT_NULL
#define AL_EFFECTSLOT_NULL (0)
#endif
namespace love
{
namespace audio
{
namespace openal
{
class Effect : public love::audio::Effect
{
public:
Effect();
Effect(const Effect &s);
virtual ~Effect();
virtual Effect *clone();
ALuint getEffect() const;
virtual bool setParams(Type type, const std::vector<float> &params);
virtual const std::vector<float> &getParams() const;
private:
bool generateEffect();
void deleteEffect();
float clampf(float val, float min, float max, float def);
ALuint effect = AL_EFFECT_NULL;
std::vector<float> params;
static std::map<Phoneme, ALint> phonemeMap;
};
} //openal
} //audio
} //love
#endif //LOVE_AUDIO_OPENAL_EFFECTS_H
+56 -30
View File
@@ -21,6 +21,8 @@
#include "Filter.h"
#include "common/Exception.h"
#include <cmath>
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<float> &params)
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<float> &params)
//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<float> &Filter::getParams() const
@@ -132,6 +149,15 @@ const std::vector<float> &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
+3
View File
@@ -65,6 +65,9 @@ public:
virtual const std::vector<float> &getParams() const;
private:
bool generateFilter();
void deleteFilter();
float clampf(float val, float min, float max, float def);
ALuint filter = AL_FILTER_NULL;
std::vector<float> params;
};
+160 -17
View File
@@ -28,6 +28,8 @@
#include <iostream>
#include <algorithm>
#define audiomodule() (Module::getInstance<Audio>(Module::M_AUDIO))
namespace love
{
namespace audio
@@ -120,6 +122,8 @@ Source::Source(Pool *pool, love::sound::SoundData *soundData)
, sampleRate(soundData->getSampleRate())
, channels(soundData->getChannels())
, bitDepth(soundData->getBitDepth())
, sendfilters(audiomodule()->getMaxSourceEffects(), nullptr)
, sendtargets(audiomodule()->getMaxSourceEffects(), AL_EFFECTSLOT_NULL)
{
ALenum fmt = Audio::getFormat(soundData->getBitDepth(), soundData->getChannels());
if (fmt == AL_NONE)
@@ -142,6 +146,8 @@ Source::Source(Pool *pool, love::sound::Decoder *decoder)
, bitDepth(decoder->getBitDepth())
, decoder(decoder)
, unusedBufferTop(MAX_BUFFERS - 1)
, sendfilters(audiomodule()->getMaxSourceEffects(), nullptr)
, sendtargets(audiomodule()->getMaxSourceEffects(), AL_EFFECTSLOT_NULL)
{
if (Audio::getFormat(decoder->getBitDepth(), decoder->getChannels()) == AL_NONE)
throw InvalidFormatException(decoder->getChannels(), decoder->getBitDepth());
@@ -163,6 +169,8 @@ Source::Source(Pool *pool, int sampleRate, int bitDepth, int channels)
, sampleRate(sampleRate)
, channels(channels)
, bitDepth(bitDepth)
, sendfilters(audiomodule()->getMaxSourceEffects(), nullptr)
, sendtargets(audiomodule()->getMaxSourceEffects(), AL_EFFECTSLOT_NULL)
{
ALenum fmt = Audio::getFormat(bitDepth, channels);
if (fmt == AL_NONE)
@@ -202,6 +210,8 @@ Source::Source(const Source &s)
, decoder(nullptr)
, toLoop(0)
, unusedBufferTop(s.sourceType == TYPE_STREAM ? MAX_BUFFERS - 1 : -1)
, sendfilters(s.sendfilters)
, sendtargets(s.sendtargets)
{
if (sourceType == TYPE_STREAM)
{
@@ -214,8 +224,8 @@ Source::Source(const Source &s)
for (unsigned int i = 0; i < MAX_BUFFERS; i++)
unusedBuffers[i] = streamBuffers[i];
}
if (s.filter)
filter = s.filter->clone();
if (s.directfilter)
directfilter = s.directfilter->clone();
setFloatv(position, s.position);
setFloatv(velocity, s.velocity);
@@ -230,8 +240,12 @@ Source::~Source()
if (sourceType != TYPE_STATIC)
alDeleteBuffers(MAX_BUFFERS, streamBuffers);
if (filter)
delete filter;
if (directfilter)
delete directfilter;
for (auto sf : sendfilters)
if (sf != nullptr)
delete sf;
}
love::audio::Source *Source::clone()
@@ -619,7 +633,7 @@ void Source::getDirection(float *v) const
setFloatv(v, direction);
}
void Source::setCone(float innerAngle, float outerAngle, float outerVolume)
void Source::setCone(float innerAngle, float outerAngle, float outerVolume, float outerHighGain)
{
if (channels > 1)
throw SpatialSupportException();
@@ -627,16 +641,20 @@ void Source::setCone(float innerAngle, float outerAngle, float outerVolume)
cone.innerAngle = (int) LOVE_TODEG(innerAngle);
cone.outerAngle = (int) LOVE_TODEG(outerAngle);
cone.outerVolume = outerVolume;
cone.outerHighGain = outerHighGain;
if (valid)
{
alSourcei(source, AL_CONE_INNER_ANGLE, cone.innerAngle);
alSourcei(source, AL_CONE_OUTER_ANGLE, cone.outerAngle);
alSourcef(source, AL_CONE_OUTER_GAIN, cone.outerVolume);
#ifdef ALC_EXT_EFX
alSourcef(source, AL_CONE_OUTER_GAINHF, cone.outerHighGain);
#endif
}
}
void Source::getCone(float &innerAngle, float &outerAngle, float &outerVolume) const
void Source::getCone(float &innerAngle, float &outerAngle, float &outerVolume, float &outerHighGain) const
{
if (channels > 1)
throw SpatialSupportException();
@@ -644,6 +662,7 @@ void Source::getCone(float &innerAngle, float &outerAngle, float &outerVolume) c
innerAngle = LOVE_TORAD(cone.innerAngle);
outerAngle = LOVE_TORAD(cone.outerAngle);
outerVolume = cone.outerVolume;
outerHighGain = cone.outerHighGain;
}
void Source::setRelative(bool enable)
@@ -996,7 +1015,13 @@ void Source::reset()
alSourcei(source, AL_CONE_OUTER_ANGLE, cone.outerAngle);
alSourcef(source, AL_CONE_OUTER_GAIN, cone.outerVolume);
#ifdef ALC_EXT_EFX
alSourcei(source, AL_DIRECT_FILTER, filter ? filter->getFilter() : AL_FILTER_NULL);
alSourcef(source, AL_AIR_ABSORPTION_FACTOR, absorptionFactor);
alSourcef(source, AL_CONE_OUTER_GAINHF, cone.outerHighGain);
alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, rolloffFactor); //reverb-specific rolloff
alSourcei(source, AL_DIRECT_FILTER, directfilter ? directfilter->getFilter() : AL_FILTER_NULL);
for (unsigned int i = 0; i < sendtargets.size(); i++)
alSource3i(source, AL_AUXILIARY_SEND_FILTER, sendtargets[i], i, sendfilters[i] ? sendfilters[i]->getFilter() : AL_FILTER_NULL);
//alGetError();
#endif
}
@@ -1199,6 +1224,29 @@ float Source::getMaxDistance() const
return maxDistance;
}
void Source::setAirAbsorptionFactor(float factor)
{
if (channels > 1)
throw SpatialSupportException();
absorptionFactor = factor;
#ifdef ALC_EXT_EFX
if (valid)
{
alSourcef(source, AL_AIR_ABSORPTION_FACTOR, absorptionFactor);
//alGetError();
}
#endif
}
float Source::getAirAbsorptionFactor() const
{
if (channels > 1)
throw SpatialSupportException();
return absorptionFactor;
}
int Source::getChannels() const
{
return channels;
@@ -1206,14 +1254,18 @@ int Source::getChannels() const
bool Source::setFilter(love::audio::Filter::Type type, std::vector<float> &params)
{
if (!filter)
filter = new Filter();
if (!directfilter)
directfilter = new Filter();
bool result = filter->setParams(type, params);
bool result = directfilter->setParams(type, params);
#ifdef ALC_EXT_EFX
if (valid)
alSourcei(source, AL_DIRECT_FILTER, filter->getFilter());
{
//in case of failure contains AL_FILTER_NULL, a valid non-filter
alSourcei(source, AL_DIRECT_FILTER, directfilter->getFilter());
//alGetError();
}
#endif
return result;
@@ -1221,14 +1273,17 @@ bool Source::setFilter(love::audio::Filter::Type type, std::vector<float> &param
bool Source::setFilter()
{
if (filter)
delete filter;
if (directfilter)
delete directfilter;
filter = nullptr;
directfilter = nullptr;
#ifdef ALC_EXT_EFX
if (valid)
{
alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL);
//alGetError();
}
#endif
return true;
@@ -1236,11 +1291,99 @@ bool Source::setFilter()
bool Source::getFilter(love::audio::Filter::Type &type, std::vector<float> &params)
{
if (!filter)
if (!directfilter)
return false;
type = filter->getType();
params = filter->getParams();
type = directfilter->getType();
params = directfilter->getParams();
return true;
}
bool Source::setSceneEffect(int slot, int effect)
{
if (slot < 0 || slot >= (int)sendtargets.size())
return false;
sendtargets[slot] = dynamic_cast<Audio*>(audiomodule())->getSceneEffectID(effect);
if (sendfilters[slot])
delete sendfilters[slot];
sendfilters[slot] = nullptr;
#ifdef ALC_EXT_EFX
if (valid)
{
alSource3i(source, AL_AUXILIARY_SEND_FILTER, sendtargets[slot], slot, AL_FILTER_NULL);
//alGetError();
}
#endif
return true;
}
bool Source::setSceneEffect(int slot, int effect, love::audio::Filter::Type type, std::vector<float> &params)
{
if (slot < 0 || slot >= (int)sendtargets.size())
return false;
sendtargets[slot] = dynamic_cast<Audio*>(audiomodule())->getSceneEffectID(effect);
if (!sendfilters[slot])
sendfilters[slot] = new Filter();
sendfilters[slot]->setParams(type, params);
#ifdef ALC_EXT_EFX
if (valid)
{
//in case of failure contains AL_FILTER_NULL, a valid non-filter
alSource3i(source, AL_AUXILIARY_SEND_FILTER, sendtargets[slot], slot, sendfilters[slot]->getFilter());
//alGetError();
}
#endif
return true;
}
bool Source::setSceneEffect(int slot)
{
if (slot < 0 || slot >= (int)sendtargets.size())
return false;
sendtargets[slot] = AL_EFFECTSLOT_NULL;
if (sendfilters[slot])
delete sendfilters[slot];
sendfilters[slot] = nullptr;
#ifdef ALC_EXT_EFX
if (valid)
{
alSource3i(source, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, slot, AL_FILTER_NULL);
//alGetError();
}
#endif
return true;
}
bool Source::getSceneEffect(int slot, int &effect, love::audio::Filter::Type &type, std::vector<float> &params)
{
if (slot < 0 || slot >= (int)sendtargets.size())
return false;
if (sendtargets[slot] == AL_EFFECTSLOT_NULL)
return false;
effect = dynamic_cast<Audio*>(audiomodule())->getSceneEffectIndex(sendtargets[slot]);
if(sendfilters[slot])
{
type = sendfilters[slot]->getType();
params = sendfilters[slot]->getParams();
}
return true;
}
+14 -4
View File
@@ -126,8 +126,8 @@ public:
virtual void getVelocity(float *v) const;
virtual void setDirection(float *v);
virtual void getDirection(float *v) const;
virtual void setCone(float innerAngle, float outerAngle, float outerVolume);
virtual void getCone(float &innerAngle, float &outerAngle, float &outerVolume) const;
virtual void setCone(float innerAngle, float outerAngle, float outerVolume, float outerHighGain);
virtual void getCone(float &innerAngle, float &outerAngle, float &outerVolume, float &outerHighGain) const;
virtual void setRelative(bool enable);
virtual bool isRelative() const;
void setLooping(bool looping);
@@ -142,12 +142,19 @@ public:
virtual float getRolloffFactor() const;
virtual void setMaxDistance(float distance);
virtual float getMaxDistance() const;
virtual void setAirAbsorptionFactor(float factor);
virtual float getAirAbsorptionFactor() const;
virtual int getChannels() const;
virtual bool setFilter(love::audio::Filter::Type type, std::vector<float> &params);
virtual bool setFilter();
virtual bool getFilter(love::audio::Filter::Type &type, std::vector<float> &params);
virtual bool setSceneEffect(int slot, int effect);
virtual bool setSceneEffect(int slot, int effect, love::audio::Filter::Type type, std::vector<float> &params);
virtual bool setSceneEffect(int slot);
virtual bool getSceneEffect(int slot, int &effect, love::audio::Filter::Type &type, std::vector<float> &params);
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);
@@ -199,6 +206,7 @@ private:
float maxVolume = 1.0f;
float referenceDistance = 1.0f;
float rolloffFactor = 1.0f;
float absorptionFactor = 0.0f;
float maxDistance = MAX_ATTENUATION_DISTANCE;
struct Cone
@@ -206,6 +214,7 @@ private:
int innerAngle = 360; // degrees
int outerAngle = 360; // degrees
float outerVolume = 0.0f;
float outerHighGain = 1.0f;
} cone;
float offsetSamples = 0.0f;
@@ -221,8 +230,9 @@ private:
int unusedBufferTop = -1;
ALsizei bufferedBytes = 0;
Filter *filter = nullptr;
Filter *directfilter = nullptr;
std::vector<Filter*> sendfilters;
std::vector<ALint> sendtargets;
}; // Source
} // openal