Implemented 3D sound placement. Sound management / lookup system not

done yet.
actorid
Nicolay Korslund 15 years ago
parent 973ddc54e8
commit 9875d74abf

@ -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)

@ -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,

@ -1,12 +1,20 @@
#include "soundmanager.hpp"
#include <iostream>
using namespace std;
#include <openengine/sound/sndmanager.hpp>
#include <mangle/sound/clients/ogre_listener_mover.hpp>
#include <mangle/sound/clients/ogre_output_updater.hpp>
/* Set up the sound manager to use Audiere of FFMPEG for input. The
OPENMW_USE_x macros are set in CMakeLists.txt.
#include <components/esm_store/store.hpp>
#include <algorithm>
#include <OgreRoot.h>
/* 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 <mangle/sound/filters/openal_audiere.hpp>
@ -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);
}
}

@ -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.
};
}

@ -1 +1 @@
Subproject commit 85fa6d392319e72cbe359c55c7651faafcebdd2b
Subproject commit 3db61c8bdde65910e43a3a06b34738296960e9e8

@ -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);
}
}

@ -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;
}
}
Loading…
Cancel
Save