diff --git a/Aquaria/Avatar.cpp b/Aquaria/Avatar.cpp index 6e74540..6bf4ad7 100644 --- a/Aquaria/Avatar.cpp +++ b/Aquaria/Avatar.cpp @@ -3466,7 +3466,7 @@ void Avatar::lockToWallCommon() dsq->spawnParticleEffect("LockToWall", position); stopBurst(); stopRoll(); - core->sound->playSfx("LockToWall", 1.0, 0);//, (1000+rand()%100)/1000.0f); + core->sound->playSfx("LockToWall", 1.0); //bursting = false; this->burst = 1; //lastLockToWallPos = position; diff --git a/Aquaria/DSQ.cpp b/Aquaria/DSQ.cpp index ac8f254..1ea2ab6 100644 --- a/Aquaria/DSQ.cpp +++ b/Aquaria/DSQ.cpp @@ -2293,58 +2293,22 @@ void DSQ::playMenuSelectSfx() core->sound->playSfx("MenuSelect"); } -PlaySfx DSQ::calcPositionalSfx(const Vector &position, float maxdist) +void DSQ::playPositionalSfx(const std::string &name, const Vector &position, float f, float fadeOut, SoundHolder *holder) { PlaySfx sfx; - sfx.vol = 0; - if (dsq->game && dsq->game->avatar) - { - Vector diff = position - dsq->game->avatar->position; - - // Aspect-ratio-adjustment: - // Just multiplying the cut-off distance with aspect increases it too much on widescreen, - // so only a part of it is aspect-corrected to make it sound better. - // Aspect is most likely >= 5/4 here, which results in a higher value than - // the default of 1024; this is intended. -- FG - if (maxdist <= 0) - maxdist = 724 + (300 * aspect); - - float dist = diff.getLength2D(); - if (dist < maxdist) - { - sfx.vol = 1.0f - (dist / maxdist); - sfx.pan = (diff.x / maxdist) * 2.0f; - if (sfx.pan < -1) - sfx.pan = -1; - if (sfx.pan > 1) - sfx.pan = 1; - } - } - return sfx; -} - -void DSQ::playPositionalSfx(const std::string &name, const Vector &position, float f, float fadeOut) -{ - PlaySfx sfx = calcPositionalSfx(position); - - // FIXME: Right now, positional sound effects never update their relative position to the - // listener, which means that if they are spawned too far away to be audible, it is not possible - // that they ever get audible at all. Additionally, the current scripting API only provides - // functions to fade sounds OUT, not to set their volume arbitrarily. - // Because audio thread creation is costly, drop sounds that can not be heard. - // This needs to be removed once proper audio source/listener positioning is implemented, - // or the scripting interface gets additional functions to mess with sound. -- FG - if (sfx.vol <= 0) - return; - sfx.freq = f; sfx.name = name; + sfx.relative = false; + sfx.x = position.x; + sfx.y = position.y; void *c = sound->playSfx(sfx); + if (fadeOut != 0) - { sound->fadeSfx(c, SFT_OUT, fadeOut); - } + + if (holder) + holder->linkSound(c); } void DSQ::shutdown() diff --git a/Aquaria/DSQ.h b/Aquaria/DSQ.h index 0360ca3..ed000ff 100644 --- a/Aquaria/DSQ.h +++ b/Aquaria/DSQ.h @@ -1426,8 +1426,7 @@ public: bool voiceOversEnabled; int recentSaveSlot; - PlaySfx calcPositionalSfx(const Vector &position, float maxdist = 0); - void playPositionalSfx(const std::string &name, const Vector &position, float freq=1.0f, float fadeOut=0); + void playPositionalSfx(const std::string &name, const Vector &position, float freq=1.0f, float fadeOut=0, SoundHolder *holder = 0); void playMenuSelectSfx(); diff --git a/Aquaria/Entity.cpp b/Aquaria/Entity.cpp index fda5d24..9bec306 100644 --- a/Aquaria/Entity.cpp +++ b/Aquaria/Entity.cpp @@ -284,6 +284,8 @@ Entity::Entity() setDamageTarget(DT_AVATAR_BUBBLE, false); setDamageTarget(DT_AVATAR_SEED, false); + stopSoundsOnDeath = false; + //debugLog("End Entity::Entity()"); } @@ -573,6 +575,10 @@ void Entity::watchEntity(Entity *e) void Entity::destroy() { + if (stopSoundsOnDeath) + this->stopAllSounds(); + this->unlinkAllSounds(); + /* if (hair) { @@ -1231,6 +1237,8 @@ void Entity::update(float dt) position = backupPos; if (vel.isNan()) vel = backupVel; + + updateSoundPosition(); } void Entity::postUpdate(float dt) @@ -2239,7 +2247,8 @@ void Entity::songNoteDone(int note, float len) void Entity::sound(const std::string &sound, float freq, float fadeOut) { - dsq->playPositionalSfx(sound, position, freq, fadeOut); + dsq->playPositionalSfx(sound, position, freq, fadeOut, this); + updateSoundPosition(); } Vector Entity::getEnergyShotTargetPosition() @@ -3139,3 +3148,8 @@ bool Entity::isEntityInside() } return false; } + +void Entity::updateSoundPosition() +{ + SoundHolder::updateSoundPosition(position.x + offset.x, position.y + offset.y); +} diff --git a/Aquaria/Entity.h b/Aquaria/Entity.h index 71bcf70..a9ae2df 100644 --- a/Aquaria/Entity.h +++ b/Aquaria/Entity.h @@ -196,7 +196,7 @@ enum BounceType BOUNCE_REAL = 1 }; -class Entity : public Quad, public StateMachine +class Entity : public Quad, public StateMachine, public SoundHolder { public: Entity(); @@ -276,6 +276,7 @@ public: void popBubble(); void sound(const std::string &sound, float freq=1, float fadeOut=0); + void setStopSoundsOnDeath(bool stop) { stopSoundsOnDeath = stop; } void freeze(float time); @@ -492,6 +493,8 @@ public: bool isEntityInside(); + void updateSoundPosition(); + protected: bool calledEntityDied; Path *waterBubble; @@ -602,6 +605,8 @@ private: int maxSpeed; int oldMaxSpeed; + bool stopSoundsOnDeath; + }; #endif diff --git a/Aquaria/Game.cpp b/Aquaria/Game.cpp index 69066a1..066d599 100644 --- a/Aquaria/Game.cpp +++ b/Aquaria/Game.cpp @@ -8134,13 +8134,13 @@ void Game::playBurstSound(bool wallJump) int freqBase = 950; if (wallJump) freqBase += 100; - sound->playSfx("Burst", 1, 0);//, (freqBase+rand()%25)/1000.0f); + sound->playSfx("Burst", 1); if (chance(50)) { switch (dsq->continuity.form) { case FORM_BEAST: - sound->playSfx("BeastBurst", (128+rand()%64)/256.0f, 0);//, (freqBase+rand()%25)/1000.0f); + sound->playSfx("BeastBurst", (128+rand()%64)/256.0f); break; } } diff --git a/Aquaria/ScriptInterface.cpp b/Aquaria/ScriptInterface.cpp index 6b57e9c..b795311 100644 --- a/Aquaria/ScriptInterface.cpp +++ b/Aquaria/ScriptInterface.cpp @@ -2763,28 +2763,36 @@ luaFunc(entity_playSfx) void *h = NULL; if (e && !dsq->isSkippingCutscene()) { - PlaySfx sfx = dsq->calcPositionalSfx(e->position, lua_tonumber(L, 7)); + PlaySfx sfx; sfx.name = getString(L, 2); sfx.freq = lua_tonumber(L, 3); - float vol = lua_tonumber(L, 4); + sfx.vol = lua_tonumber(L, 4); sfx.loops = lua_tonumber(L, 5); + float fadeOut = lua_tonumber(L, 6); - if(vol > 0) - sfx.vol *= vol; - - // FIXME: See comment in DSQ::playPositionalSfx() -- FG - if (sfx.vol <= 0) - luaReturnPtr(NULL); + sfx.maxdist = lua_tonumber(L, 7); + sfx.relative = false; h = core->sound->playSfx(sfx); if (fadeOut != 0) { sound->fadeSfx(h, SFT_OUT, fadeOut); } + + e->linkSound(h); + e->updateSoundPosition(); } luaReturnPtr(h); } +luaFunc(entity_setStopSoundsOnDeath) +{ + Entity *e = entity(L); + if (e) + e->setStopSoundsOnDeath(getBool(L, 2)); + luaReturnNil(); +} + luaFunc(entity_setSpiritFreeze) { Entity *e = entity(L); @@ -5927,32 +5935,20 @@ luaFunc(emote) luaFunc(playSfx) { - float freq = lua_tonumber(L, 2); - float vol = lua_tonumber(L, 3); - int loops = lua_tointeger(L, 4); - if (vol <= 0) - vol = 1; - PlaySfx sfx; - - if (lua_isnumber(L, 5) && lua_isnumber(L, 6)) - { - const Vector pos(lua_tonumber(L, 5), lua_tonumber(L, 6)); - sfx = dsq->calcPositionalSfx(pos, lua_tonumber(L, 7)); - sfx.vol *= vol; - } - else - { - sfx.vol = vol; - } - - // FIXME: See comment in DSQ::playPositionalSfx() -- FG - if (sfx.vol <= 0) - luaReturnPtr(NULL); - sfx.name = getString(L, 1); - sfx.freq = freq; - sfx.loops = loops; + sfx.freq = lua_tonumber(L, 2); + sfx.vol = lua_tonumber(L, 3); + if (sfx.vol <= 0) + sfx.vol = 1; + sfx.loops = lua_tointeger(L, 4); + sfx.x = lua_tonumber(L, 5); + sfx.y = lua_tonumber(L, 6); + sfx.maxdist = lua_tonumber(L, 7); + if(lua_isnoneornil(L, 8)) + sfx.relative = (sfx.x == 0 && sfx.y == 0); + else + sfx.relative = getBool(L, 8); void *handle = NULL; diff --git a/BBGE/Core.cpp b/BBGE/Core.cpp index 5593056..09acce9 100644 --- a/BBGE/Core.cpp +++ b/BBGE/Core.cpp @@ -3138,6 +3138,8 @@ void Core::main(float runTime) } } + sound->setListenerPos(screenCenter.x, screenCenter.y); + if (doScreenshot) { if (verbose) debugLog("screenshot"); diff --git a/BBGE/FmodOpenALBridge.cpp b/BBGE/FmodOpenALBridge.cpp index 42a67ef..47f480e 100644 --- a/BBGE/FmodOpenALBridge.cpp +++ b/BBGE/FmodOpenALBridge.cpp @@ -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... diff --git a/BBGE/FmodOpenALBridge.h b/BBGE/FmodOpenALBridge.h index 3da553b..6de856b 100644 --- a/BBGE/FmodOpenALBridge.h +++ b/BBGE/FmodOpenALBridge.h @@ -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); diff --git a/BBGE/SoundManager.cpp b/BBGE/SoundManager.cpp index 4dfa57c..621c83b 100644 --- a/BBGE/SoundManager.cpp +++ b/BBGE/SoundManager.cpp @@ -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::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()); +} + diff --git a/BBGE/SoundManager.h b/BBGE/SoundManager.h index eb9646f..cd18d65 100644 --- a/BBGE/SoundManager.h +++ b/BBGE/SoundManager.h @@ -24,6 +24,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #include #include +#include #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 activeSounds; +}; + + extern SoundManager *sound; + + + + #endif