// NeL - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "stdopenal.h"
#include "sound_driver_al.h"
#include "listener_al.h"
#include "effect_al.h"
#include "buffer_al.h"
#include "source_al.h"
#include "ext_al.h"
// #define NLSOUND_DEBUG_GAIN
using namespace std;
using namespace NLMISC;
namespace NLSOUND {
CSourceAL::CSourceAL(CSoundDriverAL *soundDriver) :
_SoundDriver(NULL), _Buffer(NULL), _Source(AL_NONE),
_DirectFilter(AL_FILTER_NULL), _EffectFilter(AL_FILTER_NULL),
_IsPlaying(false), _IsPaused(false), _StartTime(0), _IsStreaming(false), _RelativeMode(false),
_Pos(0.0f, 0.0f, 0.0f), _Gain(NLSOUND_DEFAULT_GAIN), _Alpha(1.0),
_MinDistance(1.0f), _MaxDistance(sqrt(numeric_limits::max())),
_Effect(NULL), _Direct(true),
_DirectGain(NLSOUND_DEFAULT_DIRECT_GAIN), _EffectGain(NLSOUND_DEFAULT_EFFECT_GAIN),
_DirectFilterType(ISource::FilterLowPass), _EffectFilterType(ISource::FilterLowPass),
_DirectFilterEnabled(false), _EffectFilterEnabled(false),
_DirectFilterPassGain(NLSOUND_DEFAULT_FILTER_PASS_GAIN), _EffectFilterPassGain(NLSOUND_DEFAULT_FILTER_PASS_GAIN)
{
// create the al source
alGenSources(1, &_Source);
alTestError();
// configure rolloff
if (soundDriver->getOption(ISoundDriver::OptionManualRolloff))
{
alSourcef(_Source, AL_ROLLOFF_FACTOR, 0);
alTestError();
}
else
{
alSourcef(_Source, AL_ROLLOFF_FACTOR, soundDriver->getRolloffFactor());
alTestError();
}
// create filters
if (soundDriver->getOption(ISoundDriver::OptionEnvironmentEffects))
{
alGenFilters(1, &_DirectFilter);
alFilteri(_DirectFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS);
alFilterf(_DirectFilter, AL_LOWPASS_GAIN, NLSOUND_DEFAULT_DIRECT_GAIN);
alFilterf(_DirectFilter, AL_LOWPASS_GAINHF, NLSOUND_DEFAULT_FILTER_PASS_GAIN);
alTestError();
alGenFilters(1, &_EffectFilter);
alFilteri(_EffectFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS);
alFilterf(_EffectFilter, AL_LOWPASS_GAIN, NLSOUND_DEFAULT_EFFECT_GAIN);
alFilterf(_EffectFilter, AL_LOWPASS_GAINHF, NLSOUND_DEFAULT_FILTER_PASS_GAIN);
alTestError();
}
// if everything went well, the source will be added in the sounddriver
_SoundDriver = soundDriver;
}
CSourceAL::~CSourceAL()
{
CSoundDriverAL *soundDriver = _SoundDriver;
release();
if (soundDriver) soundDriver->removeSource(this);
}
void CSourceAL::release()
{
if (_Source != AL_NONE) { alDeleteSources(1, &_Source); _Source = AL_NONE; }
if (_DirectFilter != AL_FILTER_NULL) { alDeleteFilters(1, &_DirectFilter); _DirectFilter = AL_FILTER_NULL; }
if (_EffectFilter != AL_FILTER_NULL) { alDeleteFilters(1, &_EffectFilter); _EffectFilter = AL_FILTER_NULL; }
_SoundDriver = NULL;
}
/// (Internal) Update the 3d changes.
void CSourceAL::updateManualRolloff()
{
CVector distanceVector = _RelativeMode ? _Pos : (_Pos - CListenerAL::getInstance()->getPos());
float distanceSquare = distanceVector.sqrnorm();
float rolloff = ISource::computeManualRolloff(_Alpha, distanceSquare, _MinDistance, _MaxDistance);
alSourcef(_Source, AL_GAIN, _Gain * rolloff);
alTestError();
#ifdef NLSOUND_DEBUG_GAIN
ALfloat gain;
alGetSourcef(_Source, AL_GAIN, &gain);
nlwarning("Called updateManualRolloff(), physical gain is %f, configured gain is %f, distanceSquare is %f, rolloff is %f", gain, _Gain, distanceSquare, rolloff);
alTestError();
#endif
}
/// Enable or disable streaming mode. Source must be stopped to call this.
void CSourceAL::setStreaming(bool streaming)
{
nlassert(isStopped());
// bring the source type to AL_UNDETERMINED
alSourcei(_Source, AL_BUFFER, AL_NONE);
alTestError();
_Buffer = NULL;
_IsStreaming = streaming;
}
/* Set the buffer that will be played (no streaming)
* If the buffer is stereo, the source mode becomes stereo and the source relative mode is on,
* otherwise the source is considered as a 3D source.
*/
void CSourceAL::setStaticBuffer( IBuffer *buffer )
{
// Stop source
alSourceStop(_Source);
alTestError();
// Set buffer
if ( buffer == NULL )
{
alSourcei(_Source, AL_BUFFER, AL_NONE );
alTestError();
_Buffer = NULL;
}
else
{
CBufferAL *bufferAL = dynamic_cast(buffer);
alSourcei(_Source, AL_BUFFER, bufferAL->bufferName() );
alTestError();
// Set relative mode if the buffer is stereo
setSourceRelativeMode( bufferAL->isStereo() );
_Buffer = bufferAL;
}
}
IBuffer *CSourceAL::getStaticBuffer()
{
return _Buffer;
}
/// Add a buffer to the streaming queue. A buffer of 100ms length is optimal for streaming.
/// Should be called by a thread which checks countStreamingBuffers every 100ms.
void CSourceAL::submitStreamingBuffer(IBuffer *buffer)
{
CBufferAL *bufferAL = static_cast(buffer);
ALuint bufferName = bufferAL->bufferName();
nlassert(bufferName);
if (!bufferAL->isBufferLoaded())
{
nlwarning("AL: MUSICBUG: Streaming buffer was not loaded, skipping buffer. This should not happen.");
return;
}
alSourceQueueBuffers(_Source, 1, &bufferName);
alTestError();
_QueuedBuffers.push(bufferAL);
// Resume playback if the internal OpenAL source stopped due to buffer underrun.
ALint srcstate;
alGetSourcei(_Source, AL_SOURCE_STATE, &srcstate);
alTestError();
if (_IsPlaying && (srcstate == AL_STOPPED || srcstate == AL_INITIAL))
{
nlwarning("AL: Streaming buffer underrun, resuming playback.");
play();
}
}
/// Return the amount of buffers in the queue (playing and waiting). 3 buffers is optimal.
uint CSourceAL::countStreamingBuffers() const
{
// a bit ugly here, but makes a much easier/simpler implementation on both drivers
ALint buffersProcessed;
alGetSourcei(_Source, AL_BUFFERS_PROCESSED, &buffersProcessed);
while (buffersProcessed)
{
ALuint bufferName = _QueuedBuffers.front()->bufferName();
alSourceUnqueueBuffers(_Source, 1, &bufferName);
alTestError();
const_cast &>(_QueuedBuffers).pop();
--buffersProcessed;
}
// return how many are left in the queue
//ALint buffersQueued;
//alGetSourcei(_SourceName, AL_BUFFERS_QUEUED, &buffersQueued);
//alTestError();
//return (uint)buffersQueued;
return (uint)_QueuedBuffers.size();
}
/// Set looping on/off for future playbacks (default: off)
void CSourceAL::setLooping( bool l )
{
alSourcei(_Source, AL_LOOPING, l?AL_TRUE:AL_FALSE );
alTestError();
}
/// Return the looping state
bool CSourceAL::getLooping() const
{
ALint b;
alGetSourcei(_Source, AL_LOOPING, &b );
alTestError();
return ( b == AL_TRUE );
}
/// Play the static buffer (or stream in and play)
bool CSourceAL::play()
{
#ifdef NLSOUND_DEBUG_GAIN
if (_IsStreaming)
{
nlwarning("Called play on a streaming source");
}
#endif
// Commit 3D changes before starting play
if (_SoundDriver->getOption(ISoundDriver::OptionManualRolloff))
updateManualRolloff();
if (_Buffer)
{
// Static playing mode
_IsPaused = false;
alSourcePlay(_Source);
_IsPlaying = (alGetError() == AL_NO_ERROR);
if (_IsPlaying)
_StartTime = CTime::getLocalTime();
return _IsPlaying;
}
else if (_IsStreaming)
{
_IsPaused = false;
/* NEW */
// called by user as well as by code to resume after buffer underrun
if (!_IsPlaying) // set start time if not playing yet
_StartTime = CTime::getLocalTime();
_IsPlaying = true; // this play always virtually succeed but may not actually be playing
if (_QueuedBuffers.size()) // ensure buffers have actually queued
{
alSourcePlay(_Source);
if (alGetError() != AL_NO_ERROR)
{
nlwarning("AL: MUSICBUG: Unknown error while trying to play streaming source.");
}
}
else
{
nlwarning("AL: MUSICBUG: Trying to play stream with no buffers queued.");
}
return true;
/* OLD
alSourcePlay(_Source);
_IsPlaying = (alGetError() == AL_NO_ERROR);
if (_IsPlaying)
_StartTime = CTime::getLocalTime(); // TODO: Played time should freeze when buffering fails, and be calculated based on the number of buffers played plus passed time. This is necessary for synchronizing animation with sound.
return _IsPlaying;
*/
// Streaming mode
//nlwarning("AL: Cannot play null buffer; streaming not implemented" );
//nlstop;
}
else
{
nlwarning("Invalid play call, not streaming and no static buffer assigned");
return false;
}
}
/// Stop playing
void CSourceAL::stop()
{
_StartTime = 0;
if ( _Buffer != NULL )
{
// Static playing mode
_IsPlaying = false;
_IsPaused = false;
alSourceStop(_Source);
alTestError();
}
else
{
// TODO: Verify streaming mode?
_IsPlaying = false;
_IsPaused = false;
alSourceStop(_Source);
alTestError();
// unqueue buffers
while (_QueuedBuffers.size())
{
ALuint bufferName = _QueuedBuffers.front()->bufferName();
alSourceUnqueueBuffers(_Source, 1, &bufferName);
_QueuedBuffers.pop();
alTestError();
}
// Streaming mode
//nlwarning("AL: Cannot stop null buffer; streaming not implemented" );
//nlstop;
}
}
/// Pause. Call play() to resume.
void CSourceAL::pause()
{
if ( _Buffer != NULL )
{
if (_IsPaused) nlwarning("AL: Called pause() while _IsPaused == true!");
// Static playing mode
if (!isStopped())
{
_IsPaused = true;
alSourcePause(_Source);
alTestError();
}
}
else
{
// TODO: Verify streaming mode?
_IsPaused = true;
alSourcePause(_Source);
alTestError();
// Streaming mode
//nlwarning("AL: Cannot pause null buffer; streaming not implemented" );
//nlstop;
}
}
/// Return true if play() or pause(), false if stop().
bool CSourceAL::isPlaying() const
{
//return !isStopped() && !_IsPaused;
if (_Buffer != NULL)
{
ALint srcstate;
alGetSourcei(_Source, AL_SOURCE_STATE, &srcstate);
alTestError();
return (srcstate == AL_PLAYING || srcstate == AL_PAUSED);
}
else
{
// streaming mode
return _IsPlaying;
}
}
/// Return true if playing is finished or stop() has been called.
bool CSourceAL::isStopped() const
{
if (_Buffer != NULL)
{
ALint srcstate;
alGetSourcei(_Source, AL_SOURCE_STATE, &srcstate);
alTestError();
return (srcstate == AL_STOPPED || srcstate == AL_INITIAL);
}
else
{
// streaming mode
return !_IsPlaying;
}
}
/// Return true if the playing source is paused
bool CSourceAL::isPaused() const
{
if (_Buffer != NULL)
{
ALint srcstate;
alGetSourcei(_Source, AL_SOURCE_STATE, &srcstate);
alTestError();
return (srcstate == AL_PAUSED);
}
else
{
// streaming mode
return _IsPaused;
}
}
/// Returns the number of milliseconds the source has been playing
uint32 CSourceAL::getTime()
{
if (!_StartTime) return 0;
return (uint32)(CTime::getLocalTime() - _StartTime);
}
/// Set the position vector.
void CSourceAL::setPos(const NLMISC::CVector& pos, bool /* deffered */)
{
_Pos = pos;
// Coordinate system: conversion from NeL to OpenAL/GL:
alSource3f(_Source, AL_POSITION, pos.x, pos.z, -pos.y );
alTestError();
}
/// Get the position vector.
const NLMISC::CVector &CSourceAL::getPos() const
{
return _Pos;
}
/// Set the velocity vector (3D mode only)
void CSourceAL::setVelocity( const NLMISC::CVector& vel, bool /* deferred */)
{
// Coordsys conversion
alSource3f(_Source, AL_VELOCITY, vel.x, vel.z, -vel.y );
alTestError();
}
/// Get the velocity vector
void CSourceAL::getVelocity( NLMISC::CVector& vel ) const
{
ALfloat v[3];
alGetSourcefv(_Source, AL_VELOCITY, v );
alTestError();
// Coordsys conversion
vel.set( v[0], -v[2], v[1] );
}
/// Set the direction vector (3D mode only)
void CSourceAL::setDirection( const NLMISC::CVector& dir )
{
// Coordsys conversion
alSource3f(_Source, AL_DIRECTION, dir.x, dir.z, -dir.y );
alTestError();
}
/// Get the direction vector
void CSourceAL::getDirection( NLMISC::CVector& dir ) const
{
ALfloat v[3];
alGetSourcefv(_Source, AL_DIRECTION, v );
alTestError();
// Coordsys conversion
dir.set( v[0], -v[2], v[1] );
}
/// Set the gain (volume value inside [0 , 1]).
void CSourceAL::setGain(float gain)
{
_Gain = std::min(std::max(gain, NLSOUND_MIN_GAIN), NLSOUND_MAX_GAIN);
if (!_SoundDriver->getOption(ISoundDriver::OptionManualRolloff))
{
alSourcef(_Source, AL_GAIN, _Gain);
alTestError();
}
#ifdef NLSOUND_DEBUG_GAIN
else
{
nlwarning("Called setGain(), manual rolloff, commit deferred to play or update, physical gain is %f, configured gain is %f", gain, _Gain);
}
#endif
}
/// Get the gain
float CSourceAL::getGain() const
{
#ifdef NLSOUND_DEBUG_GAIN
ALfloat gain;
alGetSourcef(_Source, AL_GAIN, &gain);
nlwarning("Called getGain(), physical gain is %f, configured gain is %f", gain, _Gain);
alTestError();
#endif
return _Gain;
}
/// Shift the frequency. 1.0f equals identity, each reduction of 50% equals a pitch shift
void CSourceAL::setPitch(float pitch)
{
alSourcef(_Source, AL_PITCH, std::min(std::max(pitch, NLSOUND_MIN_PITCH), NLSOUND_MAX_PITCH));
alTestError();
}
/// Get the pitch
float CSourceAL::getPitch() const
{
ALfloat pitch;
alGetSourcef(_Source, AL_PITCH, &pitch);
alTestError();
return pitch;
}
/// Set the source relative mode. If true, positions are interpreted relative to the listener position.
void CSourceAL::setSourceRelativeMode( bool mode )
{
_RelativeMode = mode;
alSourcei(_Source, AL_SOURCE_RELATIVE, mode?AL_TRUE:AL_FALSE );
alTestError();
}
/// Get the source relative mode (3D mode only)
bool CSourceAL::getSourceRelativeMode() const
{
//ALint b;
//alGetSourcei(_Source, AL_SOURCE_RELATIVE, &b );
//alTestError();
//return (b==AL_TRUE);
return _RelativeMode;
}
/// Set the min and max distances (3D mode only)
void CSourceAL::setMinMaxDistances( float mindist, float maxdist, bool /* deferred */)
{
nlassert( (mindist >= 0.0f) && (maxdist >= 0.0f) );
static float maxSqrt = sqrt(std::numeric_limits::max());
if (maxdist >= maxSqrt)
{
nlwarning("SOUND_DEV (OpenAL): Ridiculously high max distance set on source");
maxdist = maxSqrt;
}
_MinDistance = mindist;
_MaxDistance = maxdist;
if (!_SoundDriver->getOption(ISoundDriver::OptionManualRolloff))
{
alSourcef(_Source, AL_REFERENCE_DISTANCE, mindist);
alSourcef(_Source, AL_MAX_DISTANCE, maxdist);
alTestError();
}
}
/// Get the min and max distances
void CSourceAL::getMinMaxDistances( float& mindist, float& maxdist ) const
{
/*alGetSourcef(_Source, AL_REFERENCE_DISTANCE, &mindist );
alGetSourcef(_Source, AL_MAX_DISTANCE, &maxdist );
alTestError();*/
mindist = _MinDistance;
maxdist = _MaxDistance;
}
/// Set the cone angles (in radian) and gain (in [0 , 1]) (3D mode only)
void CSourceAL::setCone( float innerAngle, float outerAngle, float outerGain )
{
nlassert( (outerGain >= 0.0f) && (outerGain <= 1.0f ) );
alSourcef(_Source, AL_CONE_INNER_ANGLE, radToDeg(innerAngle) );
alSourcef(_Source, AL_CONE_OUTER_ANGLE, radToDeg(outerAngle) );
alSourcef(_Source, AL_CONE_OUTER_GAIN, outerGain );
alTestError();
}
/// Get the cone angles (in radian)
void CSourceAL::getCone( float& innerAngle, float& outerAngle, float& outerGain ) const
{
float ina, outa;
alGetSourcef(_Source, AL_CONE_INNER_ANGLE, &ina );
innerAngle = degToRad(ina);
alGetSourcef(_Source, AL_CONE_OUTER_ANGLE, &outa );
outerAngle = degToRad(outa);
alGetSourcef(_Source, AL_CONE_OUTER_GAIN, &outerGain );
alTestError();
}
/** Set the alpha value for the volume-distance curve
*
* Useful only with OptionManualRolloff. value from -1 to 1 (default 0)
*
* alpha.0: the volume will decrease linearly between 0dB and -100 dB
* alpha = 1.0: the volume will decrease linearly between 1.0 and 0.0 (linear scale)
* alpha = -1.0: the volume will decrease inversely with the distance (1/dist). This
* is the default used by DirectSound/OpenAL
*
* For any other value of alpha, an interpolation is be done between the two
* adjacent curves. For example, if alpha equals 0.5, the volume will be halfway between
* the linear dB curve and the linear amplitude curve.
*/
void CSourceAL::setAlpha(double a)
{
_Alpha = a;
}
/// (Internal) Setup the effect send filter.
void CSourceAL::setupDirectFilter()
{
if (_Direct && _DirectGain > 0)
{
if (_DirectGain < 1 || (_DirectFilterPassGain < 1 && _DirectFilterEnabled))
{
// direct gain is lowered or a filter is applied
alFilterf(_DirectFilter, AL_BANDPASS_GAIN, _DirectGain);
if (_DirectFilterEnabled)
{
alFilterf(_DirectFilter, AL_BANDPASS_GAINLF, _DirectFilterPassGain);
if (_DirectFilterType == FilterBandPass)
alFilterf(_DirectFilter, AL_BANDPASS_GAINHF, _DirectFilterPassGain);
}
else
{
alFilterf(_DirectFilter, AL_BANDPASS_GAINLF, 1.0f);
if (_DirectFilterType == FilterBandPass)
alFilterf(_DirectFilter, AL_BANDPASS_GAINHF, 1.0f);
}
alSourcei(_Source, AL_DIRECT_FILTER, _DirectFilter);
}
else
{
// no filtering
alSourcei(_Source, AL_DIRECT_FILTER, AL_FILTER_NULL);
}
}
else
{
// mute
alFilterf(_DirectFilter, AL_BANDPASS_GAIN, 0.0f);
alSourcei(_Source, AL_DIRECT_FILTER, _DirectFilter);
}
alTestError();
}
/// Enable or disable direct output [true/false], default: true
void CSourceAL::setDirect(bool enable)
{
_Direct = enable;
setupDirectFilter();
}
/// Return if the direct output is enabled
bool CSourceAL::getDirect() const
{
return _Direct;
}
/// Set the gain for the direct path
void CSourceAL::setDirectGain(float gain)
{
_DirectGain = min(max(gain, NLSOUND_MIN_GAIN), NLSOUND_MAX_GAIN);
setupDirectFilter();
}
/// Get the gain for the direct path
float CSourceAL::getDirectGain() const
{
return _DirectGain;
}
/// Enable or disable the filter for the direct channel
void CSourceAL::enableDirectFilter(bool enable)
{
_DirectFilterEnabled = enable;
setupDirectFilter();
}
/// Check if the filter on the direct channel is enabled
bool CSourceAL::isDirectFilterEnabled() const
{
return _DirectFilterEnabled;
}
/// Set the filter parameters for the direct channel
void CSourceAL::setDirectFilter(TFilter filterType, float /* lowFrequency */, float /* highFrequency */, float passGain)
{
_DirectFilterType = filterType;
_DirectFilterPassGain = passGain;
switch (filterType)
{
case FilterHighPass:
alFilteri(_DirectFilter, AL_FILTER_TYPE, AL_FILTER_HIGHPASS);
break;
case FilterLowPass:
alFilteri(_DirectFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS);
break;
case FilterBandPass:
alFilteri(_DirectFilter, AL_FILTER_TYPE, AL_FILTER_BANDPASS);
break;
}
setupDirectFilter();
}
/// Get the filter parameters for the direct channel
void CSourceAL::getDirectFilter(TFilter &filterType, float &lowFrequency, float &highFrequency, float &passGain) const
{
filterType = _DirectFilterType;
lowFrequency = NLSOUND_DEFAULT_FILTER_PASS_LF;
highFrequency = NLSOUND_DEFAULT_FILTER_PASS_HF;
passGain = _DirectFilterPassGain;
}
/// Set the direct filter gain
void CSourceAL::setDirectFilterPassGain(float passGain)
{
_DirectFilterPassGain = min(max(passGain, NLSOUND_MIN_GAIN), NLSOUND_MAX_GAIN);
setupDirectFilter();
}
/// Get the direct filter gain
float CSourceAL::getDirectFilterPassGain() const
{
return _DirectFilterPassGain;
}
/// (Internal) Setup the effect send filter.
void CSourceAL::setupEffectFilter()
{
if (_Effect && _EffectGain > 0)
{
if (_EffectGain < 1 || (_EffectFilterPassGain < 1 && _EffectFilterEnabled))
{
// effect gain is lowered or a filter is applied
alFilterf(_EffectFilter, AL_BANDPASS_GAIN, _EffectGain);
if (_EffectFilterEnabled)
{
alFilterf(_EffectFilter, AL_BANDPASS_GAINLF, _EffectFilterPassGain);
if (_EffectFilterType == FilterBandPass)
alFilterf(_EffectFilter, AL_BANDPASS_GAINHF, _EffectFilterPassGain);
}
else
{
alFilterf(_EffectFilter, AL_BANDPASS_GAINLF, 1.0f);
if (_EffectFilterType == FilterBandPass)
alFilterf(_EffectFilter, AL_BANDPASS_GAINHF, 1.0f);
}
alSource3i(_Source, AL_AUXILIARY_SEND_FILTER, _Effect->getAuxEffectSlot(), 0, _EffectFilter);
}
else
{
// no filtering
alSource3i(_Source, AL_AUXILIARY_SEND_FILTER, _Effect->getAuxEffectSlot(), 0, AL_FILTER_NULL);
}
}
else
{
// mute
alSource3i(_Source, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL);
}
alTestError();
}
/// Set the effect send for this source, NULL to disable. [IEffect], default: NULL
void CSourceAL::setEffect(CEffectAL *effect)
{
_Effect = effect;
setupEffectFilter();
}
/// Set the effect send for this source, NULL to disable. [IEffect], default: NULL
void CSourceAL::setEffect(IReverbEffect *reverbEffect)
{
setEffect(reverbEffect ? dynamic_cast(reverbEffect) : NULL);
}
/// Get the effect send for this source
IEffect *CSourceAL::getEffect() const
{
// return _Effect ? NLMISC::safe_cast(_Effect) : NULL;
return NULL;
}
/// Set the gain for the effect path
void CSourceAL::setEffectGain(float gain)
{
_EffectGain = min(max(gain, NLSOUND_MIN_GAIN), NLSOUND_MAX_GAIN);
setupEffectFilter();
}
/// Get the gain for the effect path
float CSourceAL::getEffectGain() const
{
return _EffectGain;
}
/// Enable or disable the filter for the effect channel
void CSourceAL::enableEffectFilter(bool enable)
{
_EffectFilterEnabled = enable;
setupEffectFilter();
}
/// Check if the filter on the effect channel is enabled
bool CSourceAL::isEffectFilterEnabled() const
{
return _EffectFilterEnabled;
}
/// Set the filter parameters for the effect channel
void CSourceAL::setEffectFilter(TFilter filterType, float /* lowFrequency */, float /* highFrequency */, float passGain)
{
_EffectFilterType = filterType;
_EffectFilterPassGain = passGain;
switch (filterType)
{
case FilterHighPass:
alFilteri(_EffectFilter, AL_FILTER_TYPE, AL_FILTER_HIGHPASS);
break;
case FilterLowPass:
alFilteri(_EffectFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS);
break;
case FilterBandPass:
alFilteri(_EffectFilter, AL_FILTER_TYPE, AL_FILTER_BANDPASS);
break;
}
setupEffectFilter();
}
/// Get the filter parameters for the effect channel
void CSourceAL::getEffectFilter(TFilter &filterType, float &lowFrequency, float &highFrequency, float &passGain) const
{
filterType = _EffectFilterType;
lowFrequency = NLSOUND_DEFAULT_FILTER_PASS_LF;
highFrequency = NLSOUND_DEFAULT_FILTER_PASS_HF;
passGain = _EffectFilterPassGain;
}
/// Set the effect filter gain
void CSourceAL::setEffectFilterPassGain(float passGain)
{
_EffectFilterPassGain = min(max(passGain, NLSOUND_MIN_GAIN), NLSOUND_MAX_GAIN);
setupEffectFilter();
}
/// Get the effect filter gain
float CSourceAL::getEffectFilterPassGain() const
{
return _EffectFilterPassGain;
}
} // NLSOUND