From cf6556b94df3f345bb516793cffcd3fe9833107d Mon Sep 17 00:00:00 2001 From: fgenesis Date: Wed, 24 Jul 2013 19:57:13 +0200 Subject: [PATCH] Implement stereo to mono mixing for proper L/R separation even for stereo samples. This mixes only those sounds that are supposed to be played positional. All centered (default) sounds are played in stereo, as usual. --- Aquaria/DSQ.cpp | 1 + Aquaria/ScriptInterface.cpp | 9 ++++++-- BBGE/FmodOpenALBridge.cpp | 46 ++++++++++++++++++++++++++++++------- BBGE/SoundManager.cpp | 4 ++-- BBGE/SoundManager.h | 5 ++-- 5 files changed, 51 insertions(+), 14 deletions(-) diff --git a/Aquaria/DSQ.cpp b/Aquaria/DSQ.cpp index 1ea2ab6..ed39c0a 100644 --- a/Aquaria/DSQ.cpp +++ b/Aquaria/DSQ.cpp @@ -2299,6 +2299,7 @@ void DSQ::playPositionalSfx(const std::string &name, const Vector &position, flo sfx.freq = f; sfx.name = name; sfx.relative = false; + sfx.positional = true; sfx.x = position.x; sfx.y = position.y; diff --git a/Aquaria/ScriptInterface.cpp b/Aquaria/ScriptInterface.cpp index 8749efa..f4af088 100644 --- a/Aquaria/ScriptInterface.cpp +++ b/Aquaria/ScriptInterface.cpp @@ -2774,6 +2774,7 @@ luaFunc(entity_playSfx) float fadeOut = lua_tonumber(L, 6); sfx.maxdist = lua_tonumber(L, 7); sfx.relative = false; + sfx.positional = true; h = core->sound->playSfx(sfx); if (fadeOut != 0) @@ -5944,8 +5945,12 @@ luaFunc(playSfx) 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); + if(lua_isnumber(L, 6) && lua_isnumber(L, 7)) + { + sfx.x = lua_tonumber(L, 5); + sfx.y = lua_tonumber(L, 6); + sfx.positional = true; + } sfx.maxdist = lua_tonumber(L, 7); if(lua_isnoneornil(L, 8)) sfx.relative = (sfx.x == 0 && sfx.y == 0); diff --git a/BBGE/FmodOpenALBridge.cpp b/BBGE/FmodOpenALBridge.cpp index 9a7b105..f845248 100644 --- a/BBGE/FmodOpenALBridge.cpp +++ b/BBGE/FmodOpenALBridge.cpp @@ -69,8 +69,12 @@ public: ~OggDecoder(); - // Start playing on the given channel, with optional looping. - bool start(ALuint source, bool loop); + // Prepare playing on the given channel. + bool preStart(ALuint source); + + // Decodes the first few buffers, starts the actual playback and detaches the decoder + // from the main thread, with optional looping. + void start(bool loop); // Decode audio into any free buffers. Must be called periodically // on systems without threads; may be called without harm on systems @@ -322,10 +326,9 @@ OggDecoder::~OggDecoder() } } -bool OggDecoder::start(ALuint source, bool loop) +bool OggDecoder::preStart(ALuint source) { this->source = source; - this->loop = loop; if (fp) { if (ov_open_callbacks(fp, &vf, NULL, 0, local_OV_CALLBACKS_NOCLOSE) != 0) @@ -387,6 +390,13 @@ bool OggDecoder::start(ALuint source, bool loop) return false; } + return true; +} + +void OggDecoder::start(bool loop) +{ + this->loop = loop; + playing = true; eof = false; samples_done = 0; @@ -394,8 +404,6 @@ bool OggDecoder::start(ALuint source, bool loop) queue(buffers[i]); detachDecoder(this); - - return true; } void OggDecoder::update() @@ -512,7 +520,22 @@ void OggDecoder::queue(ALuint buffer) if (pcm_size > 0) { - alBufferData(buffer, format, pcm_buffer, pcm_size, freq); + ALuint fmt = format; + if(channels == 2 && forcemono) + { + signed short *buf = (short*)&pcm_buffer[0]; + int numSamples = pcm_size / 2; // 16 bit samples + int j = 0; + for (int i = 0; i < numSamples ; i += 2) + { + // This is in theory not quite correct, but it doesn't add any artifacts or clipping. + // FIXME: Seems that simple and stupid is the method of choice, then... -- FG + buf[j++] = (buf[i] + buf[i+1]) / 2; + } + pcm_size = numSamples; + fmt = AL_FORMAT_MONO16; + } + alBufferData(buffer, fmt, pcm_buffer, pcm_size, freq); alSourceQueueBuffers(source, 1, &buffer); } } @@ -839,7 +862,7 @@ bool OpenALChannel::start(OpenALSound *sound) decoder = new OggDecoder(sound->getFile()); else decoder = new OggDecoder(sound->getData(), sound->getSize()); - if (!decoder->start(sid, sound->isLooping())) + if (!decoder->preStart(sid)) { delete decoder; decoder = NULL; @@ -954,6 +977,11 @@ FMOD_RESULT OpenALChannel::setPaused(const bool _paused, const bool setstate) } else if ((!_paused) && (initial || ((state == AL_INITIAL) || (state == AL_PAUSED)))) { + if (initial) + { + decoder->setForceMono(mindist || maxdist); // HACK: this is set for positional sounds. + decoder->start(sound->isLooping()); + } alSourcePlay(sid); initial = false; SANITY_CHECK_OPENAL_CALL(); @@ -1429,6 +1457,8 @@ FMOD_RESULT OpenALSystem::createSound(const char *name_or_data, const FMOD_MODE alGenBuffers(1, &bid); if (bid != 0) { + // FIXME: This needs to stored seperately and fed to the AL on demand, + // converting stereo to mono when it's known whether to do so or not. alBufferData(bid, format, data, size, freq); *sound = (Sound *) new OpenALSound(bid, (((mode & FMOD_LOOP_OFF) == 0) && (mode & FMOD_LOOP_NORMAL))); ((OpenALSound*)*sound)->setNumChannels(format == AL_FORMAT_STEREO16 ? 2 : 1); diff --git a/BBGE/SoundManager.cpp b/BBGE/SoundManager.cpp index 0e189e0..f57ad1f 100644 --- a/BBGE/SoundManager.cpp +++ b/BBGE/SoundManager.cpp @@ -1158,9 +1158,9 @@ void *SoundManager::playSfx(const PlaySfx &play) // distance gain attenuation: stereo separation + silence at further away than maxdist float maxdist = play.maxdist; if (!maxdist) - maxdist = 1300; + maxdist = 1800; - if(maxdist > 0) + if(maxdist > 0 && play.positional) channel->set3DMinMaxDistance(maxdist * 0.3, maxdist); // HACK: this works reasonably well else channel->set3DMinMaxDistance(0, 0); // no attenuation diff --git a/BBGE/SoundManager.h b/BBGE/SoundManager.h index cd18d65..245388a 100644 --- a/BBGE/SoundManager.h +++ b/BBGE/SoundManager.h @@ -106,7 +106,7 @@ struct PlaySfx { 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) {} + maxdist(0), x(0), y(0), relative(true), positional(false) {} std::string name; intptr_t handle; @@ -119,7 +119,8 @@ struct PlaySfx float maxdist; // distance gain attenuation. if 0: use default value, -1: don't attenuate at all SoundFadeType fade; float x, y; - bool relative; + bool relative; // relative to listener? + bool positional; // if true, this indicates that we want positional sound (stereo will be downmixed to mono to make OpenAL happy) }; class SoundHolder; // defined below