// 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