mirror of
https://github.com/love2d/love.git
synced 2026-08-12 08:30:56 +02:00
Implement audio filters
Use Source:setFilter(type, params...) to set a filter. Use Source:setFilter() to detach the current filter. Use Source:getFilter() to get the currently set filter (or nil). Squashed from pull request 27, and the clean-pr27 branch in love-experiments. --HG-- branch : minor
This commit is contained in:
@@ -316,6 +316,8 @@ set(LOVE_SRC_MODULE_AUDIO_ROOT
|
||||
src/modules/audio/Source.h
|
||||
src/modules/audio/RecordingDevice.cpp
|
||||
src/modules/audio/RecordingDevice.h
|
||||
src/modules/audio/Filter.cpp
|
||||
src/modules/audio/Filter.h
|
||||
src/modules/audio/wrap_Audio.cpp
|
||||
src/modules/audio/wrap_Audio.h
|
||||
src/modules/audio/wrap_Source.cpp
|
||||
@@ -342,6 +344,8 @@ set(LOVE_SRC_MODULE_AUDIO_OPENAL
|
||||
src/modules/audio/openal/Source.h
|
||||
src/modules/audio/openal/RecordingDevice.cpp
|
||||
src/modules/audio/openal/RecordingDevice.h
|
||||
src/modules/audio/openal/Filter.cpp
|
||||
src/modules/audio/openal/Filter.h
|
||||
)
|
||||
|
||||
set(LOVE_SRC_MODULE_AUDIO
|
||||
|
||||
@@ -25,6 +25,7 @@ Released: N/A
|
||||
* Added 'anisotropy' graphics limit.
|
||||
* Added a Mesh:attachAttribute variant that takes a different target attribute name.
|
||||
* Added Mesh:detachAttribute.
|
||||
* Added Source filters: low gain, high gain and band pass.
|
||||
|
||||
* Changed all color values to be in the range 0-1, rather than 0-255.
|
||||
* Changed love.graphics.print and friends to ignore carriage returns.
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2016 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#include "Filter.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace audio
|
||||
{
|
||||
|
||||
Filter::Filter()
|
||||
{
|
||||
}
|
||||
|
||||
Filter::~Filter()
|
||||
{
|
||||
}
|
||||
|
||||
Filter::Type Filter::getType() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
bool Filter::getConstant(const char *in, Type &out)
|
||||
{
|
||||
return types.find(in, out);
|
||||
}
|
||||
|
||||
bool Filter::getConstant(Type in, const char *&out)
|
||||
{
|
||||
return types.find(in, out);
|
||||
}
|
||||
|
||||
int Filter::getParameterCount(Type in)
|
||||
{
|
||||
return parameterCount[in];
|
||||
}
|
||||
|
||||
int Filter::getParameterCount()
|
||||
{
|
||||
return parameterCount[TYPE_MAX_ENUM];
|
||||
}
|
||||
|
||||
StringMap<Filter::Type, Filter::TYPE_MAX_ENUM>::Entry Filter::typeEntries[] =
|
||||
{
|
||||
{"lowpass", Filter::TYPE_LOWPASS},
|
||||
{"highpass", Filter::TYPE_HIGHPASS},
|
||||
{"bandpass", Filter::TYPE_BANDPASS},
|
||||
};
|
||||
|
||||
StringMap<Filter::Type, Filter::TYPE_MAX_ENUM> Filter::types(Filter::typeEntries, sizeof(Filter::typeEntries));
|
||||
|
||||
std::map<Filter::Type, int> Filter::parameterCount =
|
||||
{
|
||||
{Filter::TYPE_LOWPASS, 2},
|
||||
{Filter::TYPE_HIGHPASS, 2},
|
||||
{Filter::TYPE_BANDPASS, 3},
|
||||
{Filter::TYPE_MAX_ENUM, 3},
|
||||
};
|
||||
|
||||
} //audio
|
||||
} //love
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2016 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#ifndef LOVE_AUDIO_FILTERS_H
|
||||
#define LOVE_AUDIO_FILTERS_H
|
||||
|
||||
#include "common/Object.h"
|
||||
#include "common/StringMap.h"
|
||||
#include <map>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace audio
|
||||
{
|
||||
|
||||
class Filter
|
||||
{
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
TYPE_LOWPASS,
|
||||
TYPE_HIGHPASS,
|
||||
TYPE_BANDPASS,
|
||||
TYPE_MAX_ENUM
|
||||
};
|
||||
|
||||
Filter();
|
||||
virtual ~Filter();
|
||||
Type getType() const;
|
||||
|
||||
static bool getConstant(const char *in, Type &out);
|
||||
static bool getConstant(Type in, const char *&out);
|
||||
static int getParameterCount(Type in);
|
||||
static int getParameterCount();
|
||||
|
||||
protected:
|
||||
Type type;
|
||||
|
||||
private:
|
||||
static StringMap<Type, TYPE_MAX_ENUM>::Entry typeEntries[];
|
||||
static StringMap<Type, TYPE_MAX_ENUM> types;
|
||||
static std::map<Type, int> parameterCount;
|
||||
};
|
||||
|
||||
} //audio
|
||||
} //love
|
||||
|
||||
#endif //LOVE_AUDIO_FILTERS_H
|
||||
@@ -24,6 +24,9 @@
|
||||
// LOVE
|
||||
#include "common/Object.h"
|
||||
#include "common/StringMap.h"
|
||||
#include "Filter.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -104,6 +107,10 @@ public:
|
||||
|
||||
virtual int getChannels() const = 0;
|
||||
|
||||
virtual bool setFilter(Filter::Type type, std::vector<float> ¶ms) = 0;
|
||||
virtual bool setFilter() = 0;
|
||||
virtual bool getFilter(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;
|
||||
|
||||
|
||||
@@ -227,6 +227,21 @@ bool Source::queue(void *, size_t, int, int, int)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Source::setFilter(love::audio::Filter::Type, std::vector<float> &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Source::setFilter()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Source::getFilter(love::audio::Filter::Type &, std::vector<float> &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} // null
|
||||
} // audio
|
||||
} // love
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
// LOVE
|
||||
#include "common/Object.h"
|
||||
#include "audio/Source.h"
|
||||
#include "audio/Filter.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -79,6 +80,10 @@ public:
|
||||
virtual int getFreeBufferCount() const;
|
||||
virtual bool queue(void *data, size_t length, int dataSampleRate, int dataBitDepth, int dataChannels);
|
||||
|
||||
virtual bool setFilter(love::audio::Filter::Type type, std::vector<float> ¶ms);
|
||||
virtual bool setFilter();
|
||||
virtual bool getFilter(love::audio::Filter::Type &type, std::vector<float> ¶ms);
|
||||
|
||||
private:
|
||||
|
||||
float pitch;
|
||||
|
||||
@@ -110,7 +110,8 @@ Audio::Audio()
|
||||
if (!alcMakeContextCurrent(context) || alcGetError(device) != ALC_NO_ERROR)
|
||||
throw love::Exception("Could not make context current.");
|
||||
|
||||
// pool must be allocated after AL context.
|
||||
initializeEFX();
|
||||
|
||||
try
|
||||
{
|
||||
pool = new Pool();
|
||||
@@ -383,6 +384,109 @@ const std::vector<love::audio::RecordingDevice*> &Audio::getRecordingDevices()
|
||||
return capture;
|
||||
}
|
||||
|
||||
#ifdef ALC_EXT_EFX
|
||||
LPALGENEFFECTS alGenEffects = nullptr;
|
||||
LPALDELETEEFFECTS alDeleteEffects = nullptr;
|
||||
LPALISEFFECT alIsEffect = nullptr;
|
||||
LPALEFFECTI alEffecti = nullptr;
|
||||
LPALEFFECTIV alEffectiv = nullptr;
|
||||
LPALEFFECTF alEffectf = nullptr;
|
||||
LPALEFFECTFV alEffectfv = nullptr;
|
||||
LPALGETEFFECTI alGetEffecti = nullptr;
|
||||
LPALGETEFFECTIV alGetEffectiv = nullptr;
|
||||
LPALGETEFFECTF alGetEffectf = nullptr;
|
||||
LPALGETEFFECTFV alGetEffectfv = nullptr;
|
||||
LPALGENFILTERS alGenFilters = nullptr;
|
||||
LPALDELETEFILTERS alDeleteFilters = nullptr;
|
||||
LPALISFILTER alIsFilter = nullptr;
|
||||
LPALFILTERI alFilteri = nullptr;
|
||||
LPALFILTERIV alFilteriv = nullptr;
|
||||
LPALFILTERF alFilterf = nullptr;
|
||||
LPALFILTERFV alFilterfv = nullptr;
|
||||
LPALGETFILTERI alGetFilteri = nullptr;
|
||||
LPALGETFILTERIV alGetFilteriv = nullptr;
|
||||
LPALGETFILTERF alGetFilterf = nullptr;
|
||||
LPALGETFILTERFV alGetFilterfv = nullptr;
|
||||
LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots = nullptr;
|
||||
LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots = nullptr;
|
||||
LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot = nullptr;
|
||||
LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti = nullptr;
|
||||
LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv = nullptr;
|
||||
LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf = nullptr;
|
||||
LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv = nullptr;
|
||||
LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti = nullptr;
|
||||
LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv = nullptr;
|
||||
LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf = nullptr;
|
||||
LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv = nullptr;
|
||||
#endif
|
||||
|
||||
void Audio::initializeEFX()
|
||||
{
|
||||
#ifdef ALC_EXT_EFX
|
||||
if (alcIsExtensionPresent(device, "ALC_EXT_EFX") == AL_FALSE)
|
||||
return;
|
||||
|
||||
alGenEffects = (LPALGENEFFECTS)alGetProcAddress("alGenEffects");
|
||||
alDeleteEffects = (LPALDELETEEFFECTS)alGetProcAddress("alDeleteEffects");
|
||||
alIsEffect = (LPALISEFFECT)alGetProcAddress("alIsEffect");
|
||||
alEffecti = (LPALEFFECTI)alGetProcAddress("alEffecti");
|
||||
alEffectiv = (LPALEFFECTIV)alGetProcAddress("alEffectiv");
|
||||
alEffectf = (LPALEFFECTF)alGetProcAddress("alEffectf");
|
||||
alEffectfv = (LPALEFFECTFV)alGetProcAddress("alEffectfv");
|
||||
alGetEffecti = (LPALGETEFFECTI)alGetProcAddress("alGetEffecti");
|
||||
alGetEffectiv = (LPALGETEFFECTIV)alGetProcAddress("alGetEffectiv");
|
||||
alGetEffectf = (LPALGETEFFECTF)alGetProcAddress("alGetEffectf");
|
||||
alGetEffectfv = (LPALGETEFFECTFV)alGetProcAddress("alGetEffectfv");
|
||||
alGenFilters = (LPALGENFILTERS)alGetProcAddress("alGenFilters");
|
||||
alDeleteFilters = (LPALDELETEFILTERS)alGetProcAddress("alDeleteFilters");
|
||||
alIsFilter = (LPALISFILTER)alGetProcAddress("alIsFilter");
|
||||
alFilteri = (LPALFILTERI)alGetProcAddress("alFilteri");
|
||||
alFilteriv = (LPALFILTERIV)alGetProcAddress("alFilteriv");
|
||||
alFilterf = (LPALFILTERF)alGetProcAddress("alFilterf");
|
||||
alFilterfv = (LPALFILTERFV)alGetProcAddress("alFilterfv");
|
||||
alGetFilteri = (LPALGETFILTERI)alGetProcAddress("alGetFilteri");
|
||||
alGetFilteriv = (LPALGETFILTERIV)alGetProcAddress("alGetFilteriv");
|
||||
alGetFilterf = (LPALGETFILTERF)alGetProcAddress("alGetFilterf");
|
||||
alGetFilterfv = (LPALGETFILTERFV)alGetProcAddress("alGetFilterfv");
|
||||
alGenAuxiliaryEffectSlots = (LPALGENAUXILIARYEFFECTSLOTS)alGetProcAddress("alGenAuxiliaryEffectSlots");
|
||||
alDeleteAuxiliaryEffectSlots = (LPALDELETEAUXILIARYEFFECTSLOTS)alGetProcAddress("alDeleteAuxiliaryEffectSlots");
|
||||
alIsAuxiliaryEffectSlot = (LPALISAUXILIARYEFFECTSLOT)alGetProcAddress("alIsAuxiliaryEffectSlot");
|
||||
alAuxiliaryEffectSloti = (LPALAUXILIARYEFFECTSLOTI)alGetProcAddress("alAuxiliaryEffectSloti");
|
||||
alAuxiliaryEffectSlotiv = (LPALAUXILIARYEFFECTSLOTIV)alGetProcAddress("alAuxiliaryEffectSlotiv");
|
||||
alAuxiliaryEffectSlotf = (LPALAUXILIARYEFFECTSLOTF)alGetProcAddress("alAuxiliaryEffectSlotf");
|
||||
alAuxiliaryEffectSlotfv = (LPALAUXILIARYEFFECTSLOTFV)alGetProcAddress("alAuxiliaryEffectSlotfv");
|
||||
alGetAuxiliaryEffectSloti = (LPALGETAUXILIARYEFFECTSLOTI)alGetProcAddress("alGetAuxiliaryEffectSloti");
|
||||
alGetAuxiliaryEffectSlotiv = (LPALGETAUXILIARYEFFECTSLOTIV)alGetProcAddress("alGetAuxiliaryEffectSlotiv");
|
||||
alGetAuxiliaryEffectSlotf = (LPALGETAUXILIARYEFFECTSLOTF)alGetProcAddress("alGetAuxiliaryEffectSlotf");
|
||||
alGetAuxiliaryEffectSlotfv = (LPALGETAUXILIARYEFFECTSLOTFV)alGetProcAddress("alGetAuxiliaryEffectSlotfv");
|
||||
|
||||
//failed to initialize functions, revert to nullptr
|
||||
if (!alGenEffects || !alDeleteEffects || !alIsEffect ||
|
||||
!alGenFilters || !alDeleteFilters || !alIsFilter ||
|
||||
!alGenAuxiliaryEffectSlots || !alDeleteAuxiliaryEffectSlots || !alIsAuxiliaryEffectSlot ||
|
||||
!alEffecti || !alEffectiv || !alEffectf || !alEffectfv ||
|
||||
!alGetEffecti || !alGetEffectiv || !alGetEffectf || !alGetEffectfv ||
|
||||
!alFilteri || !alFilteriv || !alFilterf || !alFilterfv ||
|
||||
!alGetFilteri || !alGetFilteriv || !alGetFilterf || !alGetFilterfv ||
|
||||
!alAuxiliaryEffectSloti || !alAuxiliaryEffectSlotiv || !alAuxiliaryEffectSlotf || !alAuxiliaryEffectSlotfv ||
|
||||
!alGetAuxiliaryEffectSloti || !alGetAuxiliaryEffectSlotiv || !alGetAuxiliaryEffectSlotf || !alGetAuxiliaryEffectSlotfv)
|
||||
{
|
||||
alGenEffects = nullptr; alDeleteEffects = nullptr; alIsEffect = nullptr;
|
||||
alEffecti = nullptr; alEffectiv = nullptr; alEffectf = nullptr; alEffectfv = nullptr;
|
||||
alGetEffecti = nullptr; alGetEffectiv = nullptr; alGetEffectf = nullptr; alGetEffectfv = nullptr;
|
||||
alGenFilters = nullptr; alDeleteFilters = nullptr; alIsFilter = nullptr;
|
||||
alFilteri = nullptr; alFilteriv = nullptr; alFilterf = nullptr; alFilterfv = nullptr;
|
||||
alGetFilteri = nullptr; alGetFilteriv = nullptr; alGetFilterf = nullptr; alGetFilterfv = nullptr;
|
||||
alGenAuxiliaryEffectSlots = nullptr; alDeleteAuxiliaryEffectSlots = nullptr; alIsAuxiliaryEffectSlot = nullptr;
|
||||
alAuxiliaryEffectSloti = nullptr; alAuxiliaryEffectSlotiv = nullptr;
|
||||
alAuxiliaryEffectSlotf = nullptr; alAuxiliaryEffectSlotfv = nullptr;
|
||||
alGetAuxiliaryEffectSloti = nullptr; alGetAuxiliaryEffectSlotiv = nullptr;
|
||||
alGetAuxiliaryEffectSlotf = nullptr; alGetAuxiliaryEffectSlotfv = nullptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
} // openal
|
||||
} // audio
|
||||
} // love
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
// LOVE
|
||||
#include "audio/Audio.h"
|
||||
#include "audio/RecordingDevice.h"
|
||||
#include "audio/Filter.h"
|
||||
#include "common/config.h"
|
||||
#include "sound/SoundData.h"
|
||||
|
||||
@@ -111,7 +112,7 @@ public:
|
||||
void setDistanceModel(DistanceModel distanceModel);
|
||||
|
||||
private:
|
||||
|
||||
void initializeEFX();
|
||||
// The OpenAL device.
|
||||
ALCdevice *device;
|
||||
|
||||
@@ -150,6 +151,47 @@ private:
|
||||
|
||||
}; // Audio
|
||||
|
||||
#ifdef ALC_EXT_EFX
|
||||
// Effect objects
|
||||
extern LPALGENEFFECTS alGenEffects;
|
||||
extern LPALDELETEEFFECTS alDeleteEffects;
|
||||
extern LPALISEFFECT alIsEffect;
|
||||
extern LPALEFFECTI alEffecti;
|
||||
extern LPALEFFECTIV alEffectiv;
|
||||
extern LPALEFFECTF alEffectf;
|
||||
extern LPALEFFECTFV alEffectfv;
|
||||
extern LPALGETEFFECTI alGetEffecti;
|
||||
extern LPALGETEFFECTIV alGetEffectiv;
|
||||
extern LPALGETEFFECTF alGetEffectf;
|
||||
extern LPALGETEFFECTFV alGetEffectfv;
|
||||
|
||||
//Filter objects
|
||||
extern LPALGENFILTERS alGenFilters;
|
||||
extern LPALDELETEFILTERS alDeleteFilters;
|
||||
extern LPALISFILTER alIsFilter;
|
||||
extern LPALFILTERI alFilteri;
|
||||
extern LPALFILTERIV alFilteriv;
|
||||
extern LPALFILTERF alFilterf;
|
||||
extern LPALFILTERFV alFilterfv;
|
||||
extern LPALGETFILTERI alGetFilteri;
|
||||
extern LPALGETFILTERIV alGetFilteriv;
|
||||
extern LPALGETFILTERF alGetFilterf;
|
||||
extern LPALGETFILTERFV alGetFilterfv;
|
||||
|
||||
// Auxiliary slot object
|
||||
extern LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots;
|
||||
extern LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots;
|
||||
extern LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot;
|
||||
extern LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti;
|
||||
extern LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv;
|
||||
extern LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf;
|
||||
extern LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv;
|
||||
extern LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti;
|
||||
extern LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv;
|
||||
extern LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf;
|
||||
extern LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv;
|
||||
#endif
|
||||
|
||||
} // openal
|
||||
} // audio
|
||||
} // love
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2016 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#include "Filter.h"
|
||||
#include "common/Exception.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace audio
|
||||
{
|
||||
namespace openal
|
||||
{
|
||||
|
||||
//clamp values silently to avoid randomly throwing errors due to implementation differences
|
||||
float clampf(float val, float min, float max)
|
||||
{
|
||||
if (val < min) val = min;
|
||||
else if (val > max) val = max;
|
||||
return val;
|
||||
}
|
||||
|
||||
//base class
|
||||
Filter::Filter()
|
||||
{
|
||||
#ifdef ALC_EXT_EFX
|
||||
if (!alGenFilters)
|
||||
return;
|
||||
|
||||
alGenFilters(1, &filter);
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
throw love::Exception("Failed to create sound Filter.");
|
||||
#endif
|
||||
}
|
||||
|
||||
Filter::Filter(const Filter &s)
|
||||
: Filter()
|
||||
{
|
||||
setParams(s.getType(), s.getParams());
|
||||
}
|
||||
|
||||
Filter::~Filter()
|
||||
{
|
||||
#ifdef ALC_EXT_EFX
|
||||
if (filter != AL_FILTER_NULL)
|
||||
alDeleteFilters(1, &filter);
|
||||
#endif
|
||||
}
|
||||
|
||||
Filter *Filter::clone()
|
||||
{
|
||||
return new Filter(*this);
|
||||
}
|
||||
|
||||
ALuint Filter::getFilter() const
|
||||
{
|
||||
return filter;
|
||||
}
|
||||
|
||||
bool Filter::setParams(Type type, const std::vector<float> ¶ms)
|
||||
{
|
||||
this->type = type;
|
||||
this->params = params;
|
||||
|
||||
if (filter == AL_FILTER_NULL)
|
||||
return false;
|
||||
|
||||
#ifdef ALC_EXT_EFX
|
||||
switch (type)
|
||||
{
|
||||
case TYPE_LOWPASS:
|
||||
alFilteri(filter, AL_FILTER_TYPE, AL_FILTER_LOWPASS);
|
||||
break;
|
||||
case TYPE_HIGHPASS:
|
||||
alFilteri(filter, AL_FILTER_TYPE, AL_FILTER_HIGHPASS);
|
||||
break;
|
||||
case TYPE_BANDPASS:
|
||||
alFilteri(filter, AL_FILTER_TYPE, AL_FILTER_BANDPASS);
|
||||
break;
|
||||
case TYPE_MAX_ENUM:
|
||||
break;
|
||||
}
|
||||
//failed to make filter specific type - not supported etc.
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
{
|
||||
filter = AL_FILTER_NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case TYPE_LOWPASS:
|
||||
alFilterf(filter, AL_LOWPASS_GAIN, clampf(params[0], AL_LOWPASS_MIN_GAIN, AL_LOWPASS_MAX_GAIN));
|
||||
alFilterf(filter, AL_LOWPASS_GAINHF, clampf(params[1], AL_LOWPASS_MIN_GAINHF, AL_LOWPASS_MAX_GAINHF));
|
||||
break;
|
||||
case TYPE_HIGHPASS:
|
||||
alFilterf(filter, AL_HIGHPASS_GAIN, clampf(params[0], AL_HIGHPASS_MIN_GAIN, AL_HIGHPASS_MAX_GAIN));
|
||||
alFilterf(filter, AL_HIGHPASS_GAINLF, clampf(params[1], AL_HIGHPASS_MIN_GAINLF, AL_HIGHPASS_MAX_GAINLF));
|
||||
break;
|
||||
case TYPE_BANDPASS:
|
||||
alFilterf(filter, AL_BANDPASS_GAIN, clampf(params[0], AL_BANDPASS_MIN_GAIN, AL_BANDPASS_MAX_GAIN));
|
||||
alFilterf(filter, AL_BANDPASS_GAINLF, clampf(params[1], AL_BANDPASS_MIN_GAINLF, AL_BANDPASS_MAX_GAINLF));
|
||||
alFilterf(filter, AL_BANDPASS_GAINHF, clampf(params[2], AL_BANDPASS_MIN_GAINHF, AL_BANDPASS_MAX_GAINHF));
|
||||
break;
|
||||
case TYPE_MAX_ENUM:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<float> &Filter::getParams() const
|
||||
{
|
||||
return params;
|
||||
}
|
||||
|
||||
} //openal
|
||||
} //audio
|
||||
} //love
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2016 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#ifndef LOVE_AUDIO_OPENAL_FILTERS_H
|
||||
#define LOVE_AUDIO_OPENAL_FILTERS_H
|
||||
|
||||
// OpenAL
|
||||
#ifdef LOVE_APPLE_USE_FRAMEWORKS // Frameworks have different include paths.
|
||||
#ifdef LOVE_IOS
|
||||
#include <OpenAL/alc.h>
|
||||
#include <OpenAL/al.h>
|
||||
#else
|
||||
#include <OpenAL-Soft/alc.h>
|
||||
#include <OpenAL-Soft/al.h>
|
||||
#endif
|
||||
#else
|
||||
#include <AL/alc.h>
|
||||
#include <AL/al.h>
|
||||
#include <AL/alext.h>
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "audio/Filter.h"
|
||||
#include "Audio.h"
|
||||
|
||||
#ifndef AL_FILTER_NULL
|
||||
#define AL_FILTER_NULL (0)
|
||||
#endif
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace audio
|
||||
{
|
||||
namespace openal
|
||||
{
|
||||
|
||||
class Filter : public love::audio::Filter
|
||||
{
|
||||
public:
|
||||
Filter();
|
||||
Filter(const Filter &s);
|
||||
virtual ~Filter();
|
||||
virtual Filter *clone();
|
||||
ALuint getFilter() const;
|
||||
virtual bool setParams(Type type, const std::vector<float> ¶ms);
|
||||
virtual const std::vector<float> &getParams() const;
|
||||
|
||||
private:
|
||||
ALuint filter = AL_FILTER_NULL;
|
||||
std::vector<float> params;
|
||||
};
|
||||
|
||||
} //openal
|
||||
} //audio
|
||||
} //love
|
||||
|
||||
#endif //LOVE_AUDIO_OPENAL_FILTERS_H
|
||||
@@ -19,6 +19,7 @@
|
||||
**/
|
||||
|
||||
#include "Source.h"
|
||||
#include "Filter.h"
|
||||
#include "Pool.h"
|
||||
#include "Audio.h"
|
||||
#include "common/math.h"
|
||||
@@ -213,6 +214,8 @@ Source::Source(const Source &s)
|
||||
for (unsigned int i = 0; i < MAX_BUFFERS; i++)
|
||||
unusedBuffers[i] = streamBuffers[i];
|
||||
}
|
||||
if (s.filter)
|
||||
filter = s.filter->clone();
|
||||
|
||||
setFloatv(position, s.position);
|
||||
setFloatv(velocity, s.velocity);
|
||||
@@ -226,6 +229,9 @@ Source::~Source()
|
||||
|
||||
if (sourceType != TYPE_STATIC)
|
||||
alDeleteBuffers(MAX_BUFFERS, streamBuffers);
|
||||
|
||||
if (filter)
|
||||
delete filter;
|
||||
}
|
||||
|
||||
love::audio::Source *Source::clone()
|
||||
@@ -989,6 +995,9 @@ void Source::reset()
|
||||
alSourcei(source, AL_CONE_INNER_ANGLE, cone.innerAngle);
|
||||
alSourcei(source, AL_CONE_OUTER_ANGLE, cone.outerAngle);
|
||||
alSourcef(source, AL_CONE_OUTER_GAIN, cone.outerVolume);
|
||||
#ifdef ALC_EXT_EFX
|
||||
alSourcei(source, AL_DIRECT_FILTER, filter ? filter->getFilter() : AL_FILTER_NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Source::setFloatv(float *dst, const float *src) const
|
||||
@@ -1195,6 +1204,46 @@ int Source::getChannels() const
|
||||
return channels;
|
||||
}
|
||||
|
||||
bool Source::setFilter(love::audio::Filter::Type type, std::vector<float> ¶ms)
|
||||
{
|
||||
if (!filter)
|
||||
filter = new Filter();
|
||||
|
||||
bool result = filter->setParams(type, params);
|
||||
|
||||
#ifdef ALC_EXT_EFX
|
||||
if (valid)
|
||||
alSourcei(source, AL_DIRECT_FILTER, filter->getFilter());
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Source::setFilter()
|
||||
{
|
||||
if (filter)
|
||||
delete filter;
|
||||
|
||||
filter = nullptr;
|
||||
|
||||
#ifdef ALC_EXT_EFX
|
||||
if (valid)
|
||||
alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Source::getFilter(love::audio::Filter::Type &type, std::vector<float> ¶ms)
|
||||
{
|
||||
if (!filter)
|
||||
return false;
|
||||
|
||||
type = filter->getType();
|
||||
params = filter->getParams();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // openal
|
||||
} // audio
|
||||
} // love
|
||||
|
||||
@@ -25,8 +25,11 @@
|
||||
#include "common/config.h"
|
||||
#include "common/Object.h"
|
||||
#include "audio/Source.h"
|
||||
#include "audio/Filter.h"
|
||||
#include "sound/SoundData.h"
|
||||
#include "sound/Decoder.h"
|
||||
#include "Audio.h"
|
||||
#include "Filter.h"
|
||||
|
||||
// STL
|
||||
#include <vector>
|
||||
@@ -141,6 +144,10 @@ public:
|
||||
virtual float getMaxDistance() 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 int getFreeBufferCount() const;
|
||||
virtual bool queue(void *data, size_t length, int dataSampleRate, int dataBitDepth, int dataChannels);
|
||||
virtual bool queueAtomic(void *data, ALsizei length);
|
||||
@@ -214,6 +221,8 @@ private:
|
||||
int unusedBufferTop = -1;
|
||||
ALsizei bufferedBytes = 0;
|
||||
|
||||
Filter *filter = nullptr;
|
||||
|
||||
}; // Source
|
||||
|
||||
} // openal
|
||||
|
||||
@@ -330,6 +330,76 @@ int w_Source_getChannels(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Source_setFilter(lua_State *L)
|
||||
{
|
||||
Source *t = luax_checksource(L, 1);
|
||||
|
||||
Filter::Type type;
|
||||
std::vector<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 (ftypestr && !Filter::getConstant(ftypestr, type))
|
||||
return luaL_error(L, "Invalid filter type: %s", ftypestr);
|
||||
|
||||
unsigned int count = Filter::getParameterCount(type);
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
params.push_back(luaL_checknumber(L, i + 3));
|
||||
}
|
||||
else if (lua_istable(L, 2))
|
||||
{
|
||||
if (lua_objlen(L, 2) == 0) //empty table also clears filter
|
||||
{
|
||||
lua_pushboolean(L, t->setFilter());
|
||||
return 1;
|
||||
}
|
||||
lua_rawgeti(L, 2, 1);
|
||||
const char *ftypestr = luaL_checkstring(L, -1);
|
||||
if (ftypestr && !Filter::getConstant(ftypestr, type))
|
||||
return luaL_error(L, "Invalid filter type: %s", ftypestr);
|
||||
lua_pop(L, 1);
|
||||
|
||||
unsigned int count = Filter::getParameterCount(type);
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
{
|
||||
lua_rawgeti(L, 2, i + 2);
|
||||
params.push_back(luaL_checknumber(L, -1));
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
return luax_typerror(L, 2, "filter description");
|
||||
|
||||
luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setFilter(type, params)); });
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Source_getFilter(lua_State *L)
|
||||
{
|
||||
Source *t = luax_checksource(L, 1);
|
||||
Filter::Type type;
|
||||
std::vector<float> params;
|
||||
if (!t->getFilter(type, params))
|
||||
return 0;
|
||||
|
||||
const char *str = nullptr;
|
||||
Filter::getConstant(type, str);
|
||||
lua_pushstring(L, str);
|
||||
|
||||
for (unsigned int i = 0; i < params.size(); i++)
|
||||
lua_pushnumber(L, params[i]);
|
||||
|
||||
return params.size() + 1;
|
||||
}
|
||||
|
||||
int w_Source_getFreeBufferCount(lua_State *L)
|
||||
{
|
||||
Source *t = luax_checksource(L, 1);
|
||||
@@ -440,6 +510,9 @@ static const luaL_Reg w_Source_functions[] =
|
||||
|
||||
{ "getChannels", w_Source_getChannels },
|
||||
|
||||
{ "setFilter", w_Source_setFilter },
|
||||
{ "getFilter", w_Source_getFilter },
|
||||
|
||||
{ "getFreeBufferCount", w_Source_getFreeBufferCount },
|
||||
{ "queue", w_Source_queue },
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "common/runtime.h"
|
||||
#include "Source.h"
|
||||
#include "Filter.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user