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:
Raidho
2016-12-03 20:54:48 +01:00
parent e5aad03f2d
commit 332ba76e62
15 changed files with 667 additions and 2 deletions
+4
View File
@@ -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
+1
View File
@@ -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.
+79
View File
@@ -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
+65
View File
@@ -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
+7
View File
@@ -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> &params) = 0;
virtual bool setFilter() = 0;
virtual bool getFilter(Filter::Type &type, std::vector<float> &params) = 0;
virtual int getFreeBufferCount() const = 0;
virtual bool queue(void *data, size_t length, int dataSampleRate, int dataBitDepth, int dataChannels) = 0;
+15
View File
@@ -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
+5
View File
@@ -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> &params);
virtual bool setFilter();
virtual bool getFilter(love::audio::Filter::Type &type, std::vector<float> &params);
private:
float pitch;
+105 -1
View File
@@ -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
+43 -1
View File
@@ -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
+136
View File
@@ -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> &params)
{
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
+75
View File
@@ -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> &params);
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
+49
View File
@@ -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> &params)
{
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> &params)
{
if (!filter)
return false;
type = filter->getType();
params = filter->getParams();
return true;
}
} // openal
} // audio
} // love
+9
View File
@@ -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> &params);
virtual bool setFilter();
virtual bool getFilter(love::audio::Filter::Type &type, std::vector<float> &params);
virtual int getFreeBufferCount() const;
virtual bool queue(void *data, size_t length, int dataSampleRate, int dataBitDepth, int dataChannels);
virtual bool queueAtomic(void *data, ALsizei length);
@@ -214,6 +221,8 @@ private:
int unusedBufferTop = -1;
ALsizei bufferedBytes = 0;
Filter *filter = nullptr;
}; // Source
} // openal
+73
View File
@@ -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 },
+1
View File
@@ -23,6 +23,7 @@
#include "common/runtime.h"
#include "Source.h"
#include "Filter.h"
namespace love
{