1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2025-10-04 05:13:19 +00:00

Implemenet proper positional audio, one issue still.

With this patch, entities can serve as a sound source whose position
updates relative to the listener. This implements the last missing
feature of my previous positional audio patch, which is moving sound sources.
Previously, all sounds were relative to the listener with the listener
centered at (0, 0, 0), and once a sound started playing, the position could
not be changed. Volume was set as an estimation of the distance to the listener.

These restrictions are now gone; OpenAL will handle the volume & panning
based on the distance.

The remaining problem is that stereo sounds are not attenuated, at all.

Lua additions:
- playSfx() takes an additional parameter now.
- entity_setStopSoundsOnDeath()
This commit is contained in:
fgenesis 2013-07-22 03:29:57 +02:00
commit e6680da428
12 changed files with 435 additions and 174 deletions

View file

@ -3138,6 +3138,8 @@ void Core::main(float runTime)
}
}
sound->setListenerPos(screenCenter.x, screenCenter.y);
if (doScreenshot)
{
if (verbose) debugLog("screenshot");

View file

@ -672,7 +672,7 @@ class OpenALChannel
{
public:
OpenALChannel();
FMOD_RESULT setVolume(const float _volume, const bool setstate=true);
FMOD_RESULT setVolume(const float _volume);
FMOD_RESULT setPaused(const bool _paused, const bool setstate=true);
FMOD_RESULT setFrequency(const float _frequency);
FMOD_RESULT setPriority(int _priority);
@ -682,6 +682,14 @@ public:
FMOD_RESULT setChannelGroup(ChannelGroup *channelgroup);
FMOD_RESULT stop();
FMOD_RESULT setPan(const float pan);
FMOD_RESULT setCallback(FMOD_CHANNEL_CALLBACK callback);
FMOD_RESULT getUserData(void **userdata);
FMOD_RESULT setUserData(void *userdata);
FMOD_RESULT set3DAttributes(const FMOD_VECTOR *pos, const FMOD_VECTOR *vel);
FMOD_RESULT set3DMinMaxDistance(float mindistance, float maxdistance);
FMOD_RESULT setMode(FMOD_MODE mode);
FMOD_RESULT getMode(FMOD_MODE *mode);
void setGroupVolume(const float _volume);
void setSourceName(const ALuint _sid) { sid = _sid; }
ALuint getSourceName() const { return sid; }
@ -703,6 +711,9 @@ private:
OggDecoder *decoder;
bool inuse;
bool initial;
FMOD_CHANNEL_CALLBACK callback;
void *userdata;
FMOD_MODE _mode;
};
@ -743,6 +754,7 @@ OpenALChannel::OpenALChannel()
, decoder(NULL)
, inuse(false)
, initial(true)
, _mode(FMOD_DEFAULT)
{
}
@ -805,10 +817,9 @@ void OpenALChannel::update()
}
ALBRIDGE(Channel,setVolume,(float volume),(volume))
FMOD_RESULT OpenALChannel::setVolume(const float _volume, const bool setstate)
FMOD_RESULT OpenALChannel::setVolume(const float _volume)
{
if (setstate)
volume = _volume;
volume = _volume;
alSourcef(sid, AL_GAIN, _volume * groupvolume);
SANITY_CHECK_OPENAL_CALL();
return FMOD_OK;
@ -922,6 +933,98 @@ FMOD_RESULT OpenALChannel::setPan(const float pan)
return FMOD_OK;
}
ALBRIDGE(Channel,stop,(),())
FMOD_RESULT OpenALChannel::stop()
{
if (decoder)
{
decoder->stop();
decoder = NULL;
}
alSourceStop(sid);
SANITY_CHECK_OPENAL_CALL();
alSourcei(sid, AL_BUFFER, 0);
SANITY_CHECK_OPENAL_CALL();
if (sound)
{
sound->release();
sound = NULL;
}
if (inuse && callback)
callback(this, FMOD_CHANNEL_CALLBACKTYPE_END, NULL, NULL); // FIXME commanddata missing
paused = false;
inuse = false;
initial = false;
return FMOD_OK;
}
ALBRIDGE(Channel,setCallback,(FMOD_CHANNEL_CALLBACK callback),(callback))
FMOD_RESULT OpenALChannel::setCallback(FMOD_CHANNEL_CALLBACK callback)
{
this->callback = callback;
return FMOD_OK;
}
ALBRIDGE(Channel,getUserData,(void **userdata),(userdata))
FMOD_RESULT OpenALChannel::getUserData(void **userdata)
{
*userdata = this->userdata;
return FMOD_OK;
}
ALBRIDGE(Channel,setUserData,(void *userdata),(userdata))
FMOD_RESULT OpenALChannel::setUserData(void *userdata)
{
this->userdata = userdata;
return FMOD_OK;
}
ALBRIDGE(Channel,set3DAttributes,(const FMOD_VECTOR *pos, const FMOD_VECTOR *vel),(pos, vel))
FMOD_RESULT OpenALChannel::set3DAttributes(const FMOD_VECTOR *pos, const FMOD_VECTOR *vel)
{
if (pos)
alSource3f(sid, AL_POSITION, pos->x, pos->y, pos->z);
if(vel)
alSource3f(sid, AL_VELOCITY, vel->x, vel->y, vel->z);
SANITY_CHECK_OPENAL_CALL();
return FMOD_OK;
}
ALBRIDGE(Channel,set3DMinMaxDistance,(float mindistance, float maxdistance),(mindistance, maxdistance))
FMOD_RESULT OpenALChannel::set3DMinMaxDistance(float mindistance, float maxdistance)
{
alSourcef(sid, AL_REFERENCE_DISTANCE, mindistance);
alSourcef(sid, AL_MAX_DISTANCE, maxdistance);
SANITY_CHECK_OPENAL_CALL();
return FMOD_OK;
}
ALBRIDGE(Channel,setMode,(FMOD_MODE mode),(mode))
FMOD_RESULT OpenALChannel::setMode(FMOD_MODE mode)
{
_mode = mode;
if(mode & FMOD_3D_HEADRELATIVE)
alSourcei(sid, AL_SOURCE_RELATIVE, AL_TRUE);
else // FMOD_3D_WORLDRELATIVE is the default according to FMOD docs
alSourcei(sid, AL_SOURCE_RELATIVE, AL_FALSE);
SANITY_CHECK_OPENAL_CALL();
return FMOD_OK;
}
ALBRIDGE(Channel,getMode,(FMOD_MODE *mode),(mode))
FMOD_RESULT OpenALChannel::getMode(FMOD_MODE *mode)
{
*mode = _mode;
return FMOD_OK;
}
// FMOD::ChannelGroup implementation...
@ -1066,29 +1169,6 @@ void OpenALChannel::setSound(OpenALSound *_sound)
}
ALBRIDGE(Channel,stop,(),())
FMOD_RESULT OpenALChannel::stop()
{
if (decoder)
{
decoder->stop();
decoder = NULL;
}
alSourceStop(sid);
SANITY_CHECK_OPENAL_CALL();
alSourcei(sid, AL_BUFFER, 0);
SANITY_CHECK_OPENAL_CALL();
if (sound)
{
sound->release();
sound = NULL;
}
paused = false;
inuse = false;
initial = false;
return FMOD_OK;
}
// FMOD::System implementation ...
@ -1112,6 +1192,7 @@ public:
FMOD_RESULT getDriverCaps(const int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode);
FMOD_RESULT getMasterChannelGroup(ChannelGroup **channelgroup);
FMOD_RESULT playSound(FMOD_CHANNELINDEX channelid, Sound *sound, bool paused, Channel **channel);
FMOD_RESULT set3DListenerAttributes(int listener, const FMOD_VECTOR *pos, const FMOD_VECTOR *vel, const FMOD_VECTOR *forward, const FMOD_VECTOR *up);
FMOD_RESULT getNumChannels(int *maxchannels_ret);
@ -1415,7 +1496,7 @@ FMOD_RESULT OpenALSystem::init(int maxchannels, const FMOD_INITFLAGS flags, cons
break;
}
alSourcei(sid, AL_SOURCE_RELATIVE, AL_TRUE);
alSourcei(sid, AL_SOURCE_RELATIVE, AL_FALSE);
SANITY_CHECK_OPENAL_CALL();
alSource3f(sid, AL_POSITION, 0.0f, 0.0f, 0.0f); // no panning or spatialization in Aquaria.
SANITY_CHECK_OPENAL_CALL();
@ -1426,6 +1507,12 @@ FMOD_RESULT OpenALSystem::init(int maxchannels, const FMOD_INITFLAGS flags, cons
ss << "Using " << num_channels << " sound channels.";
debugLog(ss.str());
// HACK: FMOD doesn't do this.
// For completeness, we pass FMOD_3D_LINEARROLLOFF to createSound().
alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
SANITY_CHECK_OPENAL_CALL();
OggDecoder::startDecoderThread();
return FMOD_OK;
@ -1539,6 +1626,43 @@ FMOD_RESULT OpenALSystem::update()
return FMOD_OK;
}
ALBRIDGE(System, set3DListenerAttributes, (int listener, const FMOD_VECTOR *pos, const FMOD_VECTOR *vel, const FMOD_VECTOR *forward, const FMOD_VECTOR *up),
(listener, pos, vel, forward, up));
FMOD_RESULT OpenALSystem::set3DListenerAttributes(int listener, const FMOD_VECTOR *pos, const FMOD_VECTOR *vel, const FMOD_VECTOR *forward, const FMOD_VECTOR *up)
{
// ignore listener parameter; there is only one listener in OpenAL.
if(up || forward)
{
ALfloat orientation[6];
alGetListenerfv(AL_ORIENTATION, &orientation[0]);
if(forward)
{
orientation[0] = forward->x;
orientation[1] = forward->y;
orientation[2] = forward->z;
}
if(up)
{
orientation[3] = up->x;
orientation[4] = up->y;
orientation[5] = up->z;
}
alListenerfv(AL_ORIENTATION, &orientation[0]);
}
if(pos)
alListener3f(AL_POSITION, pos->x, pos->y, pos->z);
if(vel)
alListener3f(AL_VELOCITY, vel->x, vel->y, vel->z);
return FMOD_OK;
}
// misc FMOD bits...

View file

@ -60,6 +60,24 @@ typedef enum
FMOD_DSP_TYPE_REVERB,
} FMOD_DSP_TYPE;
typedef enum
{
FMOD_CHANNEL_CALLBACKTYPE_END, /* Called when a sound ends. */
FMOD_CHANNEL_CALLBACKTYPE_VIRTUALVOICE, /* Called when a voice is swapped out or swapped in. */
FMOD_CHANNEL_CALLBACKTYPE_SYNCPOINT, /* Called when a syncpoint is encountered. Can be from wav file markers. */
FMOD_CHANNEL_CALLBACKTYPE_OCCLUSION, /* Called when the channel has its geometry occlusion value calculated. Can be used to clamp or change the value. */
FMOD_CHANNEL_CALLBACKTYPE_MAX, /* Maximum number of callback types supported. */
FMOD_CHANNEL_CALLBACKTYPE_FORCEINT = 65536 /* Makes sure this enum is signed 32bit. */
} FMOD_CHANNEL_CALLBACKTYPE;
typedef struct
{
float x; /* X co-ordinate in 3D space. */
float y; /* Y co-ordinate in 3D space. */
float z; /* Z co-ordinate in 3D space. */
} FMOD_VECTOR;
#define F_CALLBACK
typedef int FMOD_CAPS;
@ -77,8 +95,13 @@ typedef int FMOD_MODE;
#define FMOD_LOOP_OFF (1<<4)
#define FMOD_LOOP_NORMAL (1<<5)
#define FMOD_LOWMEM (1<<6)
#define FMOD_3D (1<<7)
#define FMOD_3D_HEADRELATIVE (1<<8)
#define FMOD_3D_WORLDRELATIVE (1<<9)
#define FMOD_3D_LINEARROLLOFF (1<<10)
#define FMOD_DEFAULT (FMOD_2D | FMOD_HARDWARE)
typedef int FMOD_CREATESOUNDEXINFO;
#define FMOD_INIT_NORMAL 0
@ -92,6 +115,8 @@ typedef FMOD_RESULT (*FMOD_FILE_CLOSECALLBACK)(void *,void *);
typedef FMOD_RESULT (*FMOD_FILE_READCALLBACK)(void *,void *,unsigned int,unsigned int *,void *);
typedef FMOD_RESULT (*FMOD_FILE_SEEKCALLBACK)(void *,unsigned int,void *);
typedef FMOD_RESULT (*FMOD_CHANNEL_CALLBACK)(void *channel, FMOD_CHANNEL_CALLBACKTYPE type, void *commanddata1, void *commanddata2);
typedef enum
{
@ -146,6 +171,13 @@ namespace FMOD
FMOD_RESULT stop();
FMOD_RESULT setPaused(bool paused);
FMOD_RESULT setPan(float pan);
FMOD_RESULT setCallback(FMOD_CHANNEL_CALLBACK callback);
FMOD_RESULT getUserData(void **userdata);
FMOD_RESULT setUserData(void *userdata);
FMOD_RESULT set3DAttributes(const FMOD_VECTOR *pos, const FMOD_VECTOR *vel);
FMOD_RESULT set3DMinMaxDistance(float mindistance, float maxdistance);
FMOD_RESULT setMode(FMOD_MODE mode);
FMOD_RESULT getMode(FMOD_MODE *mode);
};
class System
@ -165,6 +197,7 @@ namespace FMOD
FMOD_RESULT setFileSystem(FMOD_FILE_OPENCALLBACK useropen, FMOD_FILE_CLOSECALLBACK userclose, FMOD_FILE_READCALLBACK userread, FMOD_FILE_SEEKCALLBACK userseek, int blockalign);
FMOD_RESULT setSpeakerMode(FMOD_SPEAKERMODE speakermode);
FMOD_RESULT update();
FMOD_RESULT set3DListenerAttributes(int listener, const FMOD_VECTOR *pos, const FMOD_VECTOR *vel, const FMOD_VECTOR *forward, const FMOD_VECTOR *up);
// BBGE-specific...
FMOD_RESULT getNumChannels(int *maxchannels_ret);

View file

@ -1057,6 +1057,11 @@ bool SoundManager::playVoice(const std::string &name, SoundVoiceType svt, float
voiceChannel->setPan(0);
voiceChannel->setFrequency(1);
voiceChannel->setCallback(NULL);
voiceChannel->setUserData(NULL);
voiceChannel->set3DMinMaxDistance(0.0f, 0.0f);
setSoundPos(voiceChannel, 0, 0);
setSoundRelative(voiceChannel, true);
result = voiceChannel->setPaused(false);
checkError();
@ -1143,13 +1148,32 @@ void *SoundManager::playSfx(const PlaySfx &play)
checkError();
}
channel->setPan(play.pan);
//channel->setPan(play.pan);
float freq = play.freq;
if (freq <= 0)
freq = 1;
channel->setFrequency(freq);
channel->setCallback(NULL);
channel->setUserData(NULL);
// position in space
setSoundPos(channel, play.x, play.y);
setSoundRelative(channel, play.relative);
// distance gain attenuation: stereo separation + silence at further away than maxdist
float maxdist = play.maxdist;
if (!maxdist)
maxdist = 1300;
if(maxdist > 0)
channel->set3DMinMaxDistance(maxdist * 0.3, maxdist); // HACK: this works reasonably well
else
channel->set3DMinMaxDistance(0, 0); // no attenuation
result = channel->setPaused(false);
checkError();
@ -1160,23 +1184,11 @@ void *SoundManager::playSfx(const PlaySfx &play)
return 0;
}
void *SoundManager::playSfx(const std::string &name, float vol, float pan, float freq)
void *SoundManager::playSfx(const std::string &name, float vol)
{
PlaySfx play;
play.name = name;
play.vol = vol;
play.pan = pan;
play.freq = freq;
return playSfx(play);
}
void *SoundManager::playSfx(int handle, float vol, float pan, float freq)
{
PlaySfx play;
play.handle = handle;
play.vol = vol;
play.pan = pan;
play.freq = freq;
return playSfx(play);
}
@ -1205,49 +1217,6 @@ bool SoundManager::isPlayingMusic(const std::string &name)
return false;
}
bool SoundManager::playMod(const std::string &name)
{
std::string fn;
fn = musicPath + name;
stringToLower(fn);
FMOD_MODE mode=0;
//FMOD_CREATESOUNDEXINFO exinfo;
//mode = FMOD_2D | FMOD_SOFTWARE | FMOD_CREATESAMPLE;//FMOD_CREATESTREAM;
mode = FMOD_HARDWARE | FMOD_2D | FMOD_CREATESTREAM;
debugLog("createSound: " + fn);
result = SoundCore::system->createSound(fn.c_str(), mode, 0, &modSound);
if (checkError())
{
debugLog("createSound failed");
return false;
}
debugLog("playSound");
result = SoundCore::system->playSound(FMOD_CHANNEL_FREE, modSound, false, &modChannel);
checkError();
debugLog("setChannelGroup");
result = modChannel->setChannelGroup(group_mus);
checkError();
debugLog("setPriority");
result = modChannel->setPriority(0); // should be highest priority (according to the docs)
checkError();
debugLog("setPaused");
result = modChannel->setPaused(false);
checkError();
debugLog("returning");
return true;
}
bool SoundManager::playMusic(const std::string &name, SoundLoopType slt, SoundFadeType sft, float trans, SoundConditionType sct)
{
debugLog("playMusic: " + name);
@ -1373,6 +1342,11 @@ bool SoundManager::playMusic(const std::string &name, SoundLoopType slt, SoundFa
musicChannel->setFrequency(1); // in case the channel was used by a pitch-shifted sound before
musicChannel->setPan(0);
musicChannel->setCallback(NULL);
musicChannel->setUserData(NULL);
musicChannel->set3DMinMaxDistance(0.0f, 0.0f); // disable attenuation // FIXME: is that right?
setSoundPos(musicChannel, 0, 0);
setSoundRelative(musicChannel, true);
result = musicChannel->setPaused(false); // This is where the sound really starts.
checkError();
@ -1557,7 +1531,7 @@ Buffer SoundManager::loadSoundIntoBank(const std::string &filename, const std::s
if (sound)
return sound;
FMOD_MODE mode = FMOD_DEFAULT | FMOD_LOWMEM;
FMOD_MODE mode = FMOD_DEFAULT | FMOD_LOWMEM | FMOD_3D | FMOD_3D_LINEARROLLOFF;
if (loop)
mode |= FMOD_LOOP_NORMAL;
@ -1680,3 +1654,122 @@ bool SoundManager::checkError()
#endif
return false;
}
void SoundManager::setListenerPos(float x, float y)
{
FMOD_VECTOR pos;
pos.x = x;
pos.y = y;
pos.z = 0.0f;
FMOD_VECTOR forward;
forward.x = 0.0f;
forward.y = 0.0f;
forward.z = -1.0f;
FMOD_VECTOR up;
up.x = 0.0f;
up.y = 1.0f;
up.z = 0.0f;
SoundCore::system->set3DListenerAttributes(0, &pos, NULL, &forward, &up);
}
void SoundManager::setSoundPos(void *channel, float x, float y)
{
if (!channel)
return;
FMOD::Channel *pChannel = (FMOD::Channel*)channel;
FMOD_VECTOR pos;
pos.x = x;
pos.y = y;
pos.z = 0.0f;
pChannel->set3DAttributes(&pos, NULL);
}
void SoundManager::setSoundRelative(void *channel, bool relative)
{
if (!channel)
return;
FMOD::Channel *pChannel = (FMOD::Channel*)channel;
FMOD_MODE mode = 0;
pChannel->getMode(&mode);
FMOD_MODE newmode = mode & ~(FMOD_3D_WORLDRELATIVE | FMOD_3D_HEADRELATIVE);
if(relative)
newmode |= (FMOD_3D_HEADRELATIVE | FMOD_3D);
else
newmode |= (FMOD_3D_WORLDRELATIVE | FMOD_3D);
if (mode != newmode)
pChannel->setMode(newmode);
}
static FMOD_RESULT s_soundHolderCallback(void *channel, FMOD_CHANNEL_CALLBACKTYPE type, void *commanddata1, void *commanddata2)
{
if (!channel)
return FMOD_ERR_INVALID_PARAM;
SoundHolder *holder = NULL;
FMOD::Channel *pChannel = (FMOD::Channel*)channel;
pChannel->getUserData((void**)&holder);
switch(type)
{
case FMOD_CHANNEL_CALLBACKTYPE_END:
holder->unlinkSound(channel);
break;
default:
return FMOD_ERR_INVALID_PARAM;
}
return FMOD_OK;
}
SoundHolder::~SoundHolder()
{
unlinkAllSounds();
}
void SoundHolder::updateSoundPosition(float x, float y)
{
if (activeSounds.size())
for(std::set<void*>::iterator it = activeSounds.begin(); it != activeSounds.end(); ++it)
sound->setSoundPos(*it, x, y);
}
void SoundHolder::stopAllSounds()
{
// activeSounds is modified by SoundManager::stopSfx(), which calls unkinkSound(), can't use iterator here
while(activeSounds.size())
sound->stopSfx(*activeSounds.begin());
}
void SoundHolder::unlinkSound(void *channel)
{
FMOD::Channel *pChannel = (FMOD::Channel*)channel;
pChannel->setUserData(NULL);
pChannel->setCallback(NULL);
activeSounds.erase(channel);
}
void SoundHolder::linkSound(void *channel)
{
if (!channel)
return;
FMOD::Channel *pChannel = (FMOD::Channel*)channel;
pChannel->setUserData(this);
pChannel->setCallback(s_soundHolderCallback);
activeSounds.insert(channel);
}
void SoundHolder::unlinkAllSounds()
{
while(activeSounds.size())
unlinkSound(*activeSounds.begin());
}

View file

@ -24,6 +24,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <string>
#include <list>
#include <queue>
#include <set>
#include "Vector.h"
// if using SDL_MIXER
@ -103,20 +104,26 @@ enum SoundLoadType
struct PlaySfx
{
PlaySfx() : priority(0.5), handle(0), pan(0), vol(1), fade(SFT_NONE), time(0), freq(1), loops(0), channel(BBGE_AUDIO_NOCHANNEL) {}
PlaySfx() : priority(0.5), handle(0), vol(1), fade(SFT_NONE),
time(0), freq(1), loops(0), channel(BBGE_AUDIO_NOCHANNEL),
maxdist(0), x(0), y(0), relative(true) {}
std::string name;
intptr_t handle;
float pan;
float vol;
float time;
float freq;
int loops;
int channel;
float priority;
float maxdist; // distance gain attenuation. if 0: use default value, -1: don't attenuate at all
SoundFadeType fade;
float x, y;
bool relative;
};
class SoundHolder; // defined below
class SoundManager
{
public:
@ -140,16 +147,18 @@ public:
SoundCore::Buffer getBuffer(const std::string &name);
void *playSfx(const PlaySfx &play);
void *playSfx(const std::string &name, float vol=1, float pan=0, float freq=1);
void *playSfx(int handle, float vol=1, float pan=0, float freq=1);
void *playSfx(const std::string &name, float vol=1);
bool playMod(const std::string &name);
bool playMusic(const std::string &name, SoundLoopType=SLT_NORMAL, SoundFadeType sft=SFT_NONE, float trans=0, SoundConditionType sct=SCT_NORMAL);
bool playVoice(const std::string &name, SoundVoiceType=SVT_QUEUE, float vmod=-1);
float getMusicFader();
float getVoxFader();
void setListenerPos(float x, float y);
void setSoundPos(void *channel, float x, float y);
void setSoundRelative(void *channel, bool relative);
std::string getVolumeString();
void toggleEffectMusic(SoundEffectType effect, bool on);
@ -248,6 +257,28 @@ private:
void (*loadProgressCallback)();
};
class SoundHolder
{
friend class SoundManager;
public:
void updateSoundPosition(float x, float y);
void stopAllSounds();
void unlinkSound(void *channel);
void linkSound(void *channel);
void unlinkAllSounds();
protected:
virtual ~SoundHolder();
private:
std::set<void*> activeSounds;
};
extern SoundManager *sound;
#endif