From 9875d74abf1aa264b1c8aafee9332b7f4de4f61e Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 14 Aug 2010 14:28:17 +0200 Subject: [PATCH] Implemented 3D sound placement. Sound management / lookup system not done yet. --- CMakeLists.txt | 4 +- apps/openmw/engine.cpp | 4 +- apps/openmw/mwsound/soundmanager.cpp | 149 ++++++++++++++++++++++----- apps/openmw/mwsound/soundmanager.hpp | 11 +- libs/mangle | 2 +- old_d_version/scene/gamesettings.d | 47 --------- old_d_version/scene/soundlist.d | 96 ----------------- 7 files changed, 139 insertions(+), 174 deletions(-) delete mode 100644 old_d_version/scene/gamesettings.d delete mode 100644 old_d_version/scene/soundlist.d diff --git a/CMakeLists.txt b/CMakeLists.txt index c7edb1072..d4b021916 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,9 @@ project(OpenMW) # Sound source selection -option(USE_AUDIERE "use Audiere for sound" OFF) +option(USE_AUDIERE "use Audiere for sound" ON) option(USE_FFMPEG "use ffmpeg for sound" OFF) -option(USE_MPG123 "use mpg123 for sound" ON) +option(USE_MPG123 "use mpg123 for sound" OFF) # We probably support older versions than this. cmake_minimum_required(VERSION 2.6) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index b90c1d0a5..a77ee925e 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -234,7 +234,9 @@ void OMW::Engine::go() // Create sound system mEnvironment.mSoundManager = new MWSound::SoundManager(mOgre.getRoot(), - mOgre.getCamera()); + mOgre.getCamera(), + mEnvironment.mWorld->getStore(), + (mDataDir / "Sound").file_string()); // Create script system mScriptContext = new MWScript::CompilerContext (MWScript::CompilerContext::Type_Full, diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index 20b365b37..b5a7a84fe 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -1,12 +1,20 @@ #include "soundmanager.hpp" +#include +using namespace std; + #include #include #include -/* Set up the sound manager to use Audiere of FFMPEG for input. The - OPENMW_USE_x macros are set in CMakeLists.txt. +#include +#include + +#include + +/* Set up the sound manager to use Audiere, FFMPEG or MPG123 for + input. The OPENMW_USE_x macros are set in CMakeLists.txt. */ #ifdef OPENMW_USE_AUDIERE #include @@ -36,13 +44,14 @@ typedef OEngine::Sound::SoundManagerPtr OEManagerPtr; frame is expensive, so there should be a special flag for sounds that need to track their attached object. */ -static void setPos(SoundPtr snd, const MWWorld::Ptr ref) +static void setPos(SoundPtr &snd, const MWWorld::Ptr ref) { // Get sound position from the reference const float *pos = ref.getCellRef().pos.pos; - // Move the sound. Might need to convert coordinates, test. - snd->setPos(pos[0], pos[1], pos[2]); + // Move the sound, converting from MW coordinates to Ogre + // coordinates. + snd->setPos(pos[0], pos[2], -pos[1]); } namespace MWSound @@ -64,19 +73,89 @@ namespace MWSound */ Mangle::Sound::OgreListenerMover cameraTracker; - SoundImpl() + const ESMS::ESMStore &store; + std::string dir; + + SoundImpl(Ogre::Root *root, Ogre::Camera *camera, + const ESMS::ESMStore &str, + const std::string &soundDir) : mgr(new OEManager(SoundFactoryPtr(new SOUND_FACTORY))) , updater(mgr) , cameraTracker(mgr) - {} + , store(str) + { + // Attach the camera to the camera tracker + cameraTracker.followCamera(camera); + + // Tell Ogre to update the sound system each frame + root->addFrameListener(&updater); + + dir = soundDir + "/"; + } + + // Convert a soundId to file name, and modify the volume + // according to the sounds local volume setting, minRange and + // maxRange. + std::string lookup(const std::string &soundId, + float &volume, float &min, float &max) + { + const ESM::Sound *snd = store.sounds.search(soundId); + if(snd == NULL) return ""; + + volume *= snd->data.volume / 255.0; + // These factors are not very fine tuned. + min = snd->data.minRange * 4; + max = snd->data.maxRange * 1000; + std::string file = dir + snd->sound; + std::replace(file.begin(), file.end(), '\\', '/'); + return file; + } + + // Add a sound to the list and play it + void add(const std::string &file, + MWWorld::Ptr reference, + const std::string &id, + float volume, float pitch, + float min, float max, + bool loop) + { + SoundPtr snd = mgr->load(file); + snd->setRepeat(loop); + snd->setVolume(volume); + snd->setPitch(pitch); + snd->setRange(min,max); + setPos(snd, reference); + snd->play(); + } + + // Stop a sound and remove it from the list. If id="" then + // remove the entire object and stop all its sounds. + void remove(MWWorld::Ptr reference, const std::string &id = "") + { + } + + bool isPlaying(MWWorld::Ptr reference, const std::string &id) const + { + return true; + } + + void removeCell(const MWWorld::Ptr::CellStore *cell) + { + // Note to Nico: You can get the cell of a Ptr via the getCell + // function. Just iterate over all sounds and remove those + // with matching cell. + } + + void updatePositions(MWWorld::Ptr reference) + { + } }; - SoundManager::SoundManager(Ogre::Root *root, Ogre::Camera *camera) + SoundManager::SoundManager(Ogre::Root *root, Ogre::Camera *camera, + const ESMS::ESMStore &store, + const std::string &soundDir) { - mData = new SoundImpl; - - // Attach the camera to the camera tracker - mData->cameraTracker.followCamera(camera); + mData = new SoundImpl(root, camera, store, soundDir); } SoundManager::~SoundManager() @@ -86,48 +165,66 @@ namespace MWSound void SoundManager::say (MWWorld::Ptr reference, const std::string& filename) { - // Play the sound at the correct position - SoundPtr snd = mData->mgr->play(filename); - setPos(snd, reference); - // TODO: We need to attach it to the reference somehow. A weak - // pointer is probably the best bet + // The range values are not tested + mData->add(filename, reference, "_say_sound", 1, 1, 100, 10000, false); } bool SoundManager::sayDone (MWWorld::Ptr reference) const { - return true; - // TODO: Ask the reference to check its attached 'say' sound. + return !mData->isPlaying(reference, "_say_sound"); } void SoundManager::streamMusic (const std::string& filename) { - // Play the sound and tell it to stream, if possible. - mData->mgr->play(filename)->setStreaming(true); + // Play the sound and tell it to stream, if possible. TODO: + // Store the reference, the jukebox will need to check status, + // control volume etc. + SoundPtr music = mData->mgr->play(filename); + music->setStreaming(true); + music->setVolume(0.6); } void SoundManager::playSound (const std::string& soundId, float volume, float pitch) { + // Play and forget + float min, max; + const std::string &file = mData->lookup(soundId, volume, min, max); + if(file != "") + { + SoundPtr snd = mData->mgr->play(file); + snd->setVolume(volume); + snd->setRange(min,max); + snd->setPitch(pitch); + } } void SoundManager::playSound3D (MWWorld::Ptr reference, const std::string& soundId, float volume, float pitch, bool loop) { - // Not implemented - need both a way to find sounds by id and - // a way to attach them to the reference + // Look up the sound in the ESM data + float min, max; + const std::string &file = mData->lookup(soundId, volume, min, max); + if(file != "") + mData->add(file, reference, soundId, volume, pitch, min, max, loop); } void SoundManager::stopSound3D (MWWorld::Ptr reference, const std::string& soundId) { + mData->remove(reference, soundId); } void SoundManager::stopSound (MWWorld::Ptr::CellStore *cell) { - // Note to Nico: You can get the cell of a Ptr via the getCell function. Just iterate over all - // sounds and remove those with matching cell. + mData->removeCell(cell); } bool SoundManager::getSoundPlaying (MWWorld::Ptr reference, const std::string& soundId) const { - return false; + return mData->isPlaying(reference, soundId); + } + + void SoundManager::updateObject(MWWorld::Ptr reference) + { + mData->updatePositions(reference); } } diff --git a/apps/openmw/mwsound/soundmanager.hpp b/apps/openmw/mwsound/soundmanager.hpp index 9ea86fea8..9b4745203 100644 --- a/apps/openmw/mwsound/soundmanager.hpp +++ b/apps/openmw/mwsound/soundmanager.hpp @@ -12,6 +12,11 @@ namespace Ogre class Camera; } +namespace ESMS +{ + struct ESMStore; +} + namespace MWSound { class SoundManager @@ -22,7 +27,8 @@ namespace MWSound SoundImpl *mData; public: - SoundManager(Ogre::Root*, Ogre::Camera*); + SoundManager(Ogre::Root*, Ogre::Camera*, const ESMS::ESMStore &store, + const std::string &soundDir); ~SoundManager(); void say (MWWorld::Ptr reference, const std::string& filename); @@ -52,6 +58,9 @@ namespace MWSound bool getSoundPlaying (MWWorld::Ptr reference, const std::string& soundId) const; ///< Is the given sound currently playing on the given object? + + void updateObject(MWWorld::Ptr reference); + ///< Update the position of all sounds connected to the given object. }; } diff --git a/libs/mangle b/libs/mangle index 85fa6d392..3db61c8bd 160000 --- a/libs/mangle +++ b/libs/mangle @@ -1 +1 @@ -Subproject commit 85fa6d392319e72cbe359c55c7651faafcebdd2b +Subproject commit 3db61c8bdde65910e43a3a06b34738296960e9e8 diff --git a/old_d_version/scene/gamesettings.d b/old_d_version/scene/gamesettings.d deleted file mode 100644 index a07133fe3..000000000 --- a/old_d_version/scene/gamesettings.d +++ /dev/null @@ -1,47 +0,0 @@ -module scene.gamesettings; - -import monster.monster; -import esm.records : gameSettings; -import esm.defs : VarType; -import std.stdio; -import std.string; - -MonsterObject *gmstObj; - -void loadGameSettings() -{ - // Load the GameSettings Monster class, and get the singleton - // instance - MonsterClass mc = vm.load("GMST"); - gmstObj = mc.getSing(); - - foreach(a, b; gameSettings.names) - { - assert(a == b.id); - assert(a[0] == 'i' || a[0] == 'f' || a[0] == 's'); - - // There's three cases of variable names containing - // spaces. Since these are so seldom, it makes more sense to - // make special workarounds for them instead of searching every - // string. - char[] name = a; - if(name.length > 13 && (name[6] == ' ' || name[5] == ' ')) - { - name = name.replace(" ", "_"); - // Make sure we don't change the original string! - assert(name != a); - } - - if(!mc.sc.lookupName(name).isVar) - { - writefln("WARNING: GMST %s not supported!", name); - continue; - } - - if(b.type == VarType.Int) gmstObj.setInt(name, b.i); - else if(b.type == VarType.Float) gmstObj.setFloat(name, b.f); - // TODO: At some point we will probably translate strings into - // UTF32 at load time, so string8 will not be needed here. - else if(b.type == VarType.String) gmstObj.setString8(name, b.str); - } -} diff --git a/old_d_version/scene/soundlist.d b/old_d_version/scene/soundlist.d deleted file mode 100644 index bad47f2df..000000000 --- a/old_d_version/scene/soundlist.d +++ /dev/null @@ -1,96 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - - This file (soundlist.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - 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 - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - -module scene.soundlist; - -import esm.loadsoun; -import sound.audio; -import sound.sfx; - -SoundList soundScene; - -// Has a list over all sounds currently playing in the -// scene. Currently only holds static, looping sounds, mainly -// torches. Later I will have to decide what to do with play-once -// sounds. Do I bother to update their position, or do I assume that -// once it starts playing, it is short enough that it doesn't matter? -// The last option is probably not viable, since some sounds can be -// very long. -struct SoundList -{ - SoundInstance[] list; - - // Get a sound instance from a Sound struct - static SoundInstance getInstance(Sound *s, bool loop=false) - { - const distFactor = 40.0; // Just guessing, really. - - assert(!s.sound.isEmpty()); - - SoundInstance inst = s.sound.getInstance(); - inst.setParams(s.data.volume/255.0, - s.data.minRange*distFactor, - s.data.maxRange*distFactor, - loop); - return inst; - } - - SoundInstance *insert(Sound *snd, bool loop=false) - { - // For some reason, we get called with empty sound instances here - // if some files are missing, but not for others. Check into it - // later. - if(snd.sound.isEmpty) return null; - - // Reuse a dead instance if one exists - foreach(ref s; list) - { - if(s.owner == null) - { - s = getInstance(snd, loop); - return &s; - } - } - // Otherwise append a new one - list ~= getInstance(snd, loop); - return &list[$-1]; - } - - void update(float x, float y, float z, - float frontx, float fronty, float frontz, - float upx, float upy, float upz) - { - SoundInstance.setPlayerPos(x,y,z,frontx,fronty,frontz,upx,upy,upz); - foreach(ref s; list) - if(s.owner) s.updateSound(); - } - - void kill() - { - foreach(ref s; list) - { - if(s.owner) s.kill(); - } - list = null; - } -}