mirror of
https://github.com/love2d/love.git
synced 2026-08-16 00:02:12 +02:00
EFX implemented
--HG-- branch : minor
This commit is contained in:
@@ -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<float> ¶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<float> ¶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<DistanceModel, DISTANCE_MAX_ENUM>::Entry distanceModelEntries[];
|
||||
|
||||
@@ -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::ParameterType> &Effect::getParameterTypes(Effect::Type in)
|
||||
{
|
||||
return parameterTypes[in];
|
||||
}
|
||||
|
||||
StringMap<Effect::Type, Effect::TYPE_MAX_ENUM>::Entry Effect::typeEntries[] =
|
||||
{
|
||||
{"reverb", Effect::TYPE_REVERB},
|
||||
{"chorus", Effect::TYPE_CHORUS},
|
||||
{"distortion", Effect::TYPE_DISTORTION},
|
||||
{"echo", Effect::TYPE_ECHO},
|
||||
{"flanger", Effect::TYPE_FLANGER},
|
||||
{"frequencyshifter", Effect::TYPE_FREQSHIFTER},
|
||||
{"vocalmorpher", Effect::TYPE_MORPHER},
|
||||
{"pitchshifter", Effect::TYPE_PITCHSHIFTER},
|
||||
{"ringmodulator", Effect::TYPE_MODULATOR},
|
||||
{"autowah", Effect::TYPE_AUTOWAH},
|
||||
{"compressor", Effect::TYPE_COMPRESSOR},
|
||||
{"equalizer", Effect::TYPE_EQUALIZER},
|
||||
};
|
||||
|
||||
StringMap<Effect::Type, Effect::TYPE_MAX_ENUM> Effect::types(Effect::typeEntries, sizeof(Effect::typeEntries));
|
||||
|
||||
StringMap<Effect::Waveform, Effect::WAVE_MAX_ENUM>::Entry Effect::waveformEntries[] =
|
||||
{
|
||||
{"sine", Effect::WAVE_SINE},
|
||||
{"triangle", Effect::WAVE_TRIANGLE},
|
||||
{"sawtooth", Effect::WAVE_SAWTOOTH},
|
||||
{"square", Effect::WAVE_SQUARE},
|
||||
};
|
||||
|
||||
StringMap<Effect::Waveform, Effect::WAVE_MAX_ENUM> Effect::waveforms(Effect::waveformEntries, sizeof(Effect::waveformEntries));
|
||||
|
||||
StringMap<Effect::Direction, Effect::DIR_MAX_ENUM>::Entry Effect::directionEntries[] =
|
||||
{
|
||||
{"up", Effect::DIR_UP},
|
||||
{"down", Effect::DIR_DOWN},
|
||||
{"none", Effect::DIR_NONE},
|
||||
};
|
||||
|
||||
StringMap<Effect::Direction, Effect::DIR_MAX_ENUM> Effect::directions(Effect::directionEntries, sizeof(Effect::directionEntries));
|
||||
|
||||
StringMap<Effect::Phoneme, Effect::PHONEME_MAX_ENUM>::Entry Effect::phonemeEntries[] =
|
||||
{
|
||||
{"a", Effect::PHONEME_A},
|
||||
{"e", Effect::PHONEME_E},
|
||||
{"i", Effect::PHONEME_I},
|
||||
{"o", Effect::PHONEME_O},
|
||||
{"u", Effect::PHONEME_U},
|
||||
{"aa", Effect::PHONEME_AA},
|
||||
{"ae", Effect::PHONEME_AE},
|
||||
{"ah", Effect::PHONEME_AH},
|
||||
{"ao", Effect::PHONEME_AO},
|
||||
{"eh", Effect::PHONEME_EH},
|
||||
{"er", Effect::PHONEME_ER},
|
||||
{"ih", Effect::PHONEME_IH},
|
||||
{"iy", Effect::PHONEME_IY},
|
||||
{"uh", Effect::PHONEME_UH},
|
||||
{"uw", Effect::PHONEME_UW},
|
||||
{"b", Effect::PHONEME_B},
|
||||
{"d", Effect::PHONEME_D},
|
||||
{"f", Effect::PHONEME_F},
|
||||
{"g", Effect::PHONEME_G},
|
||||
{"j", Effect::PHONEME_J},
|
||||
{"k", Effect::PHONEME_K},
|
||||
{"l", Effect::PHONEME_L},
|
||||
{"m", Effect::PHONEME_M},
|
||||
{"n", Effect::PHONEME_N},
|
||||
{"p", Effect::PHONEME_P},
|
||||
{"r", Effect::PHONEME_R},
|
||||
{"s", Effect::PHONEME_S},
|
||||
{"t", Effect::PHONEME_T},
|
||||
{"v", Effect::PHONEME_V},
|
||||
{"z", Effect::PHONEME_Z},
|
||||
};
|
||||
|
||||
StringMap<Effect::Phoneme, Effect::PHONEME_MAX_ENUM> Effect::phonemes(Effect::phonemeEntries, sizeof(Effect::phonemeEntries));
|
||||
|
||||
std::map<Effect::Type, std::vector<Effect::ParameterType>> 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
|
||||
@@ -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 <map>
|
||||
#include <vector>
|
||||
|
||||
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<ParameterType> &getParameterTypes(Type in);
|
||||
|
||||
protected:
|
||||
Type type;
|
||||
|
||||
private:
|
||||
static StringMap<Type, TYPE_MAX_ENUM>::Entry typeEntries[];
|
||||
static StringMap<Type, TYPE_MAX_ENUM> types;
|
||||
static StringMap<Waveform, WAVE_MAX_ENUM>::Entry waveformEntries[];
|
||||
static StringMap<Waveform, WAVE_MAX_ENUM> waveforms;
|
||||
static StringMap<Direction, DIR_MAX_ENUM>::Entry directionEntries[];
|
||||
static StringMap<Direction, DIR_MAX_ENUM> directions;
|
||||
static StringMap<Phoneme, PHONEME_MAX_ENUM>::Entry phonemeEntries[];
|
||||
static StringMap<Phoneme, PHONEME_MAX_ENUM> phonemes;
|
||||
static std::map<Type, std::vector<Effect::ParameterType>> parameterTypes;
|
||||
};
|
||||
|
||||
} //audio
|
||||
} //love
|
||||
|
||||
#endif //LOVE_AUDIO_EFFECTS_H
|
||||
@@ -67,12 +67,12 @@ StringMap<Filter::Type, Filter::TYPE_MAX_ENUM>::Entry Filter::typeEntries[] =
|
||||
|
||||
StringMap<Filter::Type, Filter::TYPE_MAX_ENUM> Filter::types(Filter::typeEntries, sizeof(Filter::typeEntries));
|
||||
|
||||
//all parameters are floats, therefore bare count will suffice
|
||||
std::map<Filter::Type, int> 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
|
||||
|
||||
@@ -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<float> ¶ms) = 0;
|
||||
|
||||
virtual bool setSceneEffect(int slot, int effect) = 0;
|
||||
virtual bool setSceneEffect(int slot, int effect, Filter::Type type, std::vector<float> ¶ms) = 0;
|
||||
virtual bool setSceneEffect(int slot) = 0;
|
||||
virtual bool getSceneEffect(int slot, int &effect, Filter::Type &type, std::vector<float> ¶ms) = 0;
|
||||
|
||||
virtual int getFreeBufferCount() const = 0;
|
||||
virtual bool queue(void *data, size_t length, int dataSampleRate, int dataBitDepth, int dataChannels) = 0;
|
||||
|
||||
|
||||
@@ -143,7 +143,16 @@ float Audio::getDopplerScale() const
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
/*
|
||||
void setMeter(float)
|
||||
{
|
||||
}
|
||||
|
||||
float getMeter() const
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
*/
|
||||
const std::vector<love::audio::RecordingDevice*> &Audio::getRecordingDevices()
|
||||
{
|
||||
return capture;
|
||||
@@ -159,6 +168,36 @@ void Audio::setDistanceModel(DistanceModel distanceModel)
|
||||
this->distanceModel = distanceModel;
|
||||
}
|
||||
|
||||
bool Audio::setSceneEffect(int, Effect::Type, std::vector<float> &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Audio::setSceneEffect(int)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Audio::getSceneEffect(int, Effect::Type &, std::vector<float> &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int Audio::getMaxSceneEffects() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Audio::getMaxSourceEffects() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Audio::isEFXsupported() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} // null
|
||||
} // audio
|
||||
} // love
|
||||
|
||||
@@ -70,12 +70,21 @@ public:
|
||||
|
||||
void setDopplerScale(float scale);
|
||||
float getDopplerScale() const;
|
||||
//void setMeter(float scale);
|
||||
//float getMeter() const;
|
||||
|
||||
const std::vector<love::audio::RecordingDevice*> &getRecordingDevices();
|
||||
|
||||
DistanceModel getDistanceModel() const;
|
||||
void setDistanceModel(DistanceModel distanceModel);
|
||||
|
||||
bool setSceneEffect(int slot, Effect::Type type, std::vector<float> ¶ms);
|
||||
bool setSceneEffect(int slot);
|
||||
bool getSceneEffect(int slot, Effect::Type &type, std::vector<float> ¶ms);
|
||||
int getMaxSceneEffects() const;
|
||||
int getMaxSourceEffects() const;
|
||||
bool isEFXsupported() const;
|
||||
|
||||
private:
|
||||
float volume;
|
||||
DistanceModel distanceModel;
|
||||
|
||||
@@ -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<float> &)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Source::setSceneEffect(int, int)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Source::setSceneEffect(int, int, love::audio::Filter::Type, std::vector<float> &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Source::setSceneEffect(int)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Source::getSceneEffect(int, int &, love::audio::Filter::Type &, std::vector<float> &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} // null
|
||||
} // audio
|
||||
} // love
|
||||
|
||||
@@ -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<float> ¶ms);
|
||||
|
||||
virtual bool setSceneEffect(int slot, int effect);
|
||||
virtual bool setSceneEffect(int slot, int effect, love::audio::Filter::Type type, std::vector<float> ¶ms);
|
||||
virtual bool setSceneEffect(int slot);
|
||||
virtual bool getSceneEffect(int slot, int &effect, love::audio::Filter::Type &type, std::vector<float> ¶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
|
||||
|
||||
|
||||
@@ -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> ¶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<float> ¶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;
|
||||
|
||||
@@ -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> ¶ms);
|
||||
bool setSceneEffect(int slot);
|
||||
bool getSceneEffect(int slot, Effect::Type &type, std::vector<float> ¶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<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
|
||||
|
||||
@@ -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> ¶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<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
|
||||
@@ -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> ¶ms);
|
||||
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
|
||||
@@ -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> ¶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<float> ¶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<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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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> ¶ms)
|
||||
{
|
||||
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> ¶m
|
||||
|
||||
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> ¶ms)
|
||||
{
|
||||
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> ¶ms)
|
||||
{
|
||||
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> ¶ms)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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> ¶ms);
|
||||
virtual bool setFilter();
|
||||
virtual bool getFilter(love::audio::Filter::Type &type, std::vector<float> ¶ms);
|
||||
|
||||
virtual bool setSceneEffect(int slot, int effect);
|
||||
virtual bool setSceneEffect(int slot, int effect, love::audio::Filter::Type type, std::vector<float> ¶ms);
|
||||
virtual bool setSceneEffect(int slot);
|
||||
virtual bool getSceneEffect(int slot, int &effect, love::audio::Filter::Type &type, std::vector<float> ¶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);
|
||||
@@ -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
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
// C++
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -256,7 +257,19 @@ int w_getDopplerScale(lua_State *L)
|
||||
lua_pushnumber(L, instance()->getDopplerScale());
|
||||
return 1;
|
||||
}
|
||||
/*
|
||||
int w_setMeter(lua_State *L)
|
||||
{
|
||||
instance()->setMeter(luax_checkfloat(L, 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_getMeter(lua_State *L)
|
||||
{
|
||||
lua_pushnumber(L, instance()->getMeter());
|
||||
return 1;
|
||||
}
|
||||
*/
|
||||
int w_setDistanceModel(lua_State *L)
|
||||
{
|
||||
const char *modelStr = luaL_checkstring(L, 1);
|
||||
@@ -292,6 +305,179 @@ int w_getRecordingDevices(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int setSceneEffectHandleParam(lua_State *L, int pos, Effect::ParameterType type, std::vector<float> ¶ms)
|
||||
{
|
||||
if (lua_isnoneornil(L, pos))
|
||||
params.push_back(nanf(""));
|
||||
else
|
||||
{
|
||||
const char *typestr;
|
||||
switch (type)
|
||||
{
|
||||
case Effect::PAR_FLOAT:
|
||||
params.push_back(luaL_checknumber(L, pos));
|
||||
break;
|
||||
case Effect::PAR_BOOL:
|
||||
if (!lua_isboolean(L, pos))
|
||||
return luax_typerror(L, pos, "boolean");
|
||||
params.push_back(lua_toboolean(L, pos) ? 1.0 : 0.0);
|
||||
break;
|
||||
case Effect::PAR_WAVEFORM:
|
||||
{
|
||||
typestr = luaL_checkstring(L, pos);
|
||||
Effect::Waveform waveform;
|
||||
if (!Effect::getConstant(typestr, waveform))
|
||||
return luaL_error(L, "Invalid waveform type: %s", typestr);
|
||||
params.push_back(static_cast<int>(waveform));
|
||||
break;
|
||||
}
|
||||
case Effect::PAR_DIRECTION:
|
||||
{
|
||||
typestr = luaL_checkstring(L, pos);
|
||||
Effect::Direction direction;
|
||||
if (!Effect::getConstant(typestr, direction))
|
||||
return luaL_error(L, "Invalid direction type: %s", typestr);
|
||||
params.push_back(static_cast<int>(direction));
|
||||
break;
|
||||
}
|
||||
case Effect::PAR_PHONEME:
|
||||
{
|
||||
typestr = luaL_checkstring(L, pos);
|
||||
Effect::Phoneme phoneme;
|
||||
if (!Effect::getConstant(typestr, phoneme))
|
||||
return luaL_error(L, "Invalid phoneme type: %s", typestr);
|
||||
params.push_back(static_cast<int>(phoneme));
|
||||
break;
|
||||
}
|
||||
case Effect::PAR_MAX_ENUM:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_setSceneEffect(lua_State *L)
|
||||
{
|
||||
int slot = luaL_checknumber(L, 1) - 1;
|
||||
|
||||
const char *typestr;
|
||||
Effect::Type type;
|
||||
std::vector<float> params;
|
||||
|
||||
if (lua_gettop(L) == 1)
|
||||
{
|
||||
lua_pushboolean(L, instance()->setSceneEffect(slot));
|
||||
return 1;
|
||||
}
|
||||
else if (lua_gettop(L) > 2)
|
||||
{
|
||||
typestr = luaL_checkstring(L, 2);
|
||||
if (!Effect::getConstant(typestr, type))
|
||||
return luaL_error(L, "Invalid effect type: %s", typestr);
|
||||
|
||||
params.push_back(luaL_checknumber(L, 3));
|
||||
const std::vector<Effect::ParameterType> ¶mtypes = Effect::getParameterTypes(type);
|
||||
for (unsigned int i = 0; i < paramtypes.size(); i++)
|
||||
setSceneEffectHandleParam(L, i + 4, paramtypes[i], params);
|
||||
}
|
||||
else if (lua_istable(L, 2))
|
||||
{
|
||||
if (lua_objlen(L, 2) == 0) //empty table also clears effect
|
||||
{
|
||||
lua_pushboolean(L, instance()->setSceneEffect(slot));
|
||||
return 1;
|
||||
}
|
||||
lua_rawgeti(L, 2, 1);
|
||||
typestr = luaL_checkstring(L, -1);
|
||||
if (!Effect::getConstant(typestr, type))
|
||||
return luaL_error(L, "Invalid effect type: %s", typestr);
|
||||
lua_pop(L, 1);
|
||||
|
||||
const std::vector<Effect::ParameterType> ¶mtypes = Effect::getParameterTypes(type);
|
||||
lua_rawgeti(L, 2, 2);
|
||||
params.push_back(luaL_checknumber(L, -1));
|
||||
lua_pop(L, 1);
|
||||
for (unsigned int i = 0; i < paramtypes.size(); i++)
|
||||
{
|
||||
lua_rawgeti(L, 2, i + 3);
|
||||
setSceneEffectHandleParam(L, -1, paramtypes[i], params);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
return luax_typerror(L, 2, "effect description");
|
||||
|
||||
luax_catchexcept(L, [&]() { lua_pushboolean(L, instance()->setSceneEffect(slot, type, params)); });
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_getSceneEffect(lua_State *L)
|
||||
{
|
||||
int slot = luaL_checknumber(L, 1) - 1;
|
||||
|
||||
Effect::Type type;
|
||||
std::vector<float> params;
|
||||
|
||||
if (!instance()->getSceneEffect(slot, type, params))
|
||||
return 0;
|
||||
|
||||
const char *str = nullptr;
|
||||
Effect::getConstant(type, str);
|
||||
lua_pushstring(L, str);
|
||||
lua_pushnumber(L, params[0]);
|
||||
|
||||
const std::vector<Effect::ParameterType> ¶mtypes = Effect::getParameterTypes(type);
|
||||
|
||||
for (unsigned int i = 1; i < params.size(); i++)
|
||||
{
|
||||
if (isnanf(params[i]))
|
||||
lua_pushnil(L);
|
||||
else
|
||||
switch(paramtypes[i-1])
|
||||
{
|
||||
case Effect::PAR_FLOAT:
|
||||
lua_pushnumber(L, params[i]);
|
||||
break;
|
||||
case Effect::PAR_BOOL:
|
||||
lua_pushboolean(L, params[i] < 0.5 ? false : true);
|
||||
break;
|
||||
case Effect::PAR_WAVEFORM:
|
||||
Effect::getConstant(static_cast<Effect::Waveform>((int)params[i]), str);
|
||||
lua_pushstring(L, str);
|
||||
break;
|
||||
case Effect::PAR_DIRECTION:
|
||||
Effect::getConstant(static_cast<Effect::Direction>((int)params[i]), str);
|
||||
lua_pushstring(L, str);
|
||||
break;
|
||||
case Effect::PAR_PHONEME:
|
||||
Effect::getConstant(static_cast<Effect::Phoneme>((int)params[i]), str);
|
||||
lua_pushstring(L, str);
|
||||
break;
|
||||
case Effect::PAR_MAX_ENUM:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return params.size() + 1;
|
||||
}
|
||||
|
||||
int w_getMaxSceneEffects(lua_State *L)
|
||||
{
|
||||
lua_pushnumber(L, instance()->getMaxSceneEffects());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_getMaxSourceEffects(lua_State *L)
|
||||
{
|
||||
lua_pushnumber(L, instance()->getMaxSourceEffects());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_isSceneEffectsSupported(lua_State *L)
|
||||
{
|
||||
lua_pushboolean(L, instance()->isEFXsupported());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// List of functions to wrap.
|
||||
static const luaL_Reg functions[] =
|
||||
{
|
||||
@@ -311,9 +497,16 @@ static const luaL_Reg functions[] =
|
||||
{ "getVelocity", w_getVelocity },
|
||||
{ "setDopplerScale", w_setDopplerScale },
|
||||
{ "getDopplerScale", w_getDopplerScale },
|
||||
//{ "setMeter", w_setMeter },
|
||||
//{ "getMeter", w_setMeter },
|
||||
{ "setDistanceModel", w_setDistanceModel },
|
||||
{ "getDistanceModel", w_getDistanceModel },
|
||||
{ "getRecordingDevices", w_getRecordingDevices },
|
||||
{ "setSceneEffect", w_setSceneEffect },
|
||||
{ "getSceneEffect", w_getSceneEffect },
|
||||
{ "getMaxSceneEffects", w_getMaxSceneEffects },
|
||||
{ "getMaxSourceEffects", w_getMaxSourceEffects },
|
||||
{ "isSceneEffectsSupported", w_isSceneEffectsSupported },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
#include "sound/SoundData.h"
|
||||
#include "wrap_Source.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace audio
|
||||
@@ -212,19 +215,21 @@ int w_Source_setCone(lua_State *L)
|
||||
float innerAngle = (float) luaL_checknumber(L, 2);
|
||||
float outerAngle = (float) luaL_checknumber(L, 3);
|
||||
float outerVolume = (float) luaL_optnumber(L, 4, 0.0);
|
||||
luax_catchexcept(L, [&](){ t->setCone(innerAngle, outerAngle, outerVolume); });
|
||||
float outerHighGain = (float) luaL_optnumber(L, 5, 1.0);
|
||||
luax_catchexcept(L, [&](){ t->setCone(innerAngle, outerAngle, outerVolume, outerHighGain); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Source_getCone(lua_State *L)
|
||||
{
|
||||
Source *t = luax_checksource(L, 1);
|
||||
float innerAngle, outerAngle, outerVolume;
|
||||
luax_catchexcept(L, [&](){ t->getCone(innerAngle, outerAngle, outerVolume); });
|
||||
float innerAngle, outerAngle, outerVolume, outerHighGain;
|
||||
luax_catchexcept(L, [&](){ t->getCone(innerAngle, outerAngle, outerVolume, outerHighGain); });
|
||||
lua_pushnumber(L, innerAngle);
|
||||
lua_pushnumber(L, outerAngle);
|
||||
lua_pushnumber(L, outerVolume);
|
||||
return 3;
|
||||
lua_pushnumber(L, outerHighGain);
|
||||
return 4;
|
||||
}
|
||||
|
||||
int w_Source_setRelative(lua_State *L)
|
||||
@@ -323,6 +328,16 @@ int w_Source_getRolloff(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Source_setAirAbsorption(lua_State *L)
|
||||
{
|
||||
Source *t = luax_checksource(L, 1);
|
||||
float factor = (float)luaL_checknumber(L, 2);
|
||||
if (factor < 0.0f)
|
||||
return luaL_error(L, "Invalid air absorption factor: %f. Must be > 0.", factor);
|
||||
luax_catchexcept(L, [&](){ t->setAirAbsorptionFactor(factor); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Source_getChannels(lua_State *L)
|
||||
{
|
||||
Source *t = luax_checksource(L, 1);
|
||||
@@ -330,6 +345,58 @@ int w_Source_getChannels(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int setFilterReadFilter(lua_State *L, int pos, Filter::Type &type, std::vector<float> ¶ms)
|
||||
{
|
||||
if (lua_isnoneornil(L, pos))
|
||||
return 0;
|
||||
else if (lua_isstring(L, pos))
|
||||
{
|
||||
const char *ftypestr = luaL_checkstring(L, pos);
|
||||
if (!Filter::getConstant(ftypestr, type))
|
||||
return luaL_error(L, "Invalid filter type: %s", ftypestr);
|
||||
|
||||
params.push_back(luaL_checknumber(L, pos + 1));
|
||||
int count = Filter::getParameterCount(type);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (lua_isnoneornil(L, i + pos + 2))
|
||||
params.push_back(nanf(""));
|
||||
else
|
||||
params.push_back(luaL_checknumber(L, i + pos + 2));
|
||||
}
|
||||
}
|
||||
else if (lua_istable(L, pos))
|
||||
{
|
||||
if (lua_objlen(L, pos) == 0) //empty table also clears filter
|
||||
return 0;
|
||||
|
||||
lua_rawgeti(L, pos, 1);
|
||||
const char *ftypestr = luaL_checkstring(L, -1);
|
||||
if (!Filter::getConstant(ftypestr, type))
|
||||
return luaL_error(L, "Invalid filter type: %s", ftypestr);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_rawgeti(L, pos, 2);
|
||||
params.push_back(luaL_checknumber(L, -1));
|
||||
lua_pop(L, 1);
|
||||
|
||||
int count = Filter::getParameterCount(type);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
lua_rawgeti(L, pos, i + 3);
|
||||
if (lua_isnoneornil(L, -1))
|
||||
params.push_back(nanf(""));
|
||||
else
|
||||
params.push_back(luaL_checknumber(L, -1));
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
return luax_typerror(L, pos, "filter description");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Source_setFilter(lua_State *L)
|
||||
{
|
||||
Source *t = luax_checksource(L, 1);
|
||||
@@ -337,48 +404,10 @@ int w_Source_setFilter(lua_State *L)
|
||||
Filter::Type type;
|
||||
std::vector<float> 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 (!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 (!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);
|
||||
}
|
||||
}
|
||||
if (setFilterReadFilter(L, 2, type, params))
|
||||
luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setFilter(type, params)); });
|
||||
else
|
||||
return luax_typerror(L, 2, "filter description");
|
||||
|
||||
luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setFilter(type, params)); });
|
||||
luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setFilter()); });
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -400,6 +429,60 @@ int w_Source_getFilter(lua_State *L)
|
||||
return params.size() + 1;
|
||||
}
|
||||
|
||||
int w_Source_setSceneEffect(lua_State *L)
|
||||
{
|
||||
Source *t = luax_checksource(L, 1);
|
||||
|
||||
int slot = luaL_checknumber(L, 2) - 1;
|
||||
if (lua_gettop(L) == 2)
|
||||
{
|
||||
luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setSceneEffect(slot)); });
|
||||
return 1;
|
||||
}
|
||||
|
||||
int effect = luaL_checknumber(L, 3) - 1;
|
||||
if (lua_gettop(L) == 3)
|
||||
{
|
||||
luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setSceneEffect(slot, effect)); });
|
||||
return 1;
|
||||
}
|
||||
|
||||
Filter::Type type;
|
||||
std::vector<float> params;
|
||||
|
||||
if (setFilterReadFilter(L, 4, type, params))
|
||||
luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setSceneEffect(slot, effect, type, params)); });
|
||||
else
|
||||
luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setSceneEffect(slot, effect)); });
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Source_getSceneEffect(lua_State *L)
|
||||
{
|
||||
Source *t = luax_checksource(L, 1);
|
||||
int slot = luaL_checknumber(L, 2) - 1;
|
||||
|
||||
int effect;
|
||||
Filter::Type type = Filter::TYPE_MAX_ENUM;
|
||||
std::vector<float> params;
|
||||
if (!t->getSceneEffect(slot, effect, type, params))
|
||||
return 0;
|
||||
|
||||
lua_pushnumber(L, effect + 1);
|
||||
if(type == Filter::TYPE_MAX_ENUM)
|
||||
return 1;
|
||||
|
||||
const char *str = nullptr;
|
||||
Filter::getConstant(type, str);
|
||||
if (str)
|
||||
lua_pushstring(L, str);
|
||||
|
||||
for (unsigned int i = 0; i < params.size(); i++)
|
||||
lua_pushnumber(L, params[i]);
|
||||
|
||||
return params.size() + 2;
|
||||
}
|
||||
|
||||
int w_Source_getFreeBufferCount(lua_State *L)
|
||||
{
|
||||
Source *t = luax_checksource(L, 1);
|
||||
@@ -505,13 +588,15 @@ static const luaL_Reg w_Source_functions[] =
|
||||
{ "getVolumeLimits", w_Source_getVolumeLimits },
|
||||
{ "setAttenuationDistances", w_Source_setAttenuationDistances },
|
||||
{ "getAttenuationDistances", w_Source_getAttenuationDistances },
|
||||
{ "setRolloff", w_Source_setRolloff},
|
||||
{ "getRolloff", w_Source_getRolloff},
|
||||
{ "setRolloff", w_Source_setRolloff },
|
||||
{ "getRolloff", w_Source_getRolloff },
|
||||
|
||||
{ "getChannels", w_Source_getChannels },
|
||||
|
||||
{ "setFilter", w_Source_setFilter },
|
||||
{ "getFilter", w_Source_getFilter },
|
||||
{ "setSceneEffect", w_Source_setSceneEffect },
|
||||
{ "getSceneEffect", w_Source_getSceneEffect },
|
||||
|
||||
{ "getFreeBufferCount", w_Source_getFreeBufferCount },
|
||||
{ "queue", w_Source_queue },
|
||||
|
||||
Reference in New Issue
Block a user