1
0
forked from 0ad/0ad
0ad/source/sound/SoundGroup.cpp
janwas cccd6849a7 cleanup (requires update-workspaces)
lib_errors.cpp: replace with status.cpp, adapt to needs at work
wutil: fix runtime warning reported via feedback box
config: merge CONFIG_PARANOIA and !CONFIG_FINAL into
CONFIG_ENABLE_CHECKS
add openmp, pointer_typedefs.h

This was SVN commit r9410.
2011-05-03 12:38:42 +00:00

367 lines
8.6 KiB
C++

/* Copyright (C) 2010 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. 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
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* =========================================================================
* File : SoundGroup.cpp
* Project : 0 A.D.
* Description : Loads up a group of sound files with shared properties,
* and provides a simple interface for playing them.
* =========================================================================
*/
#include "precompiled.h"
#include "SoundGroup.h"
#include <algorithm>
#include "lib/rand.h"
#include "ps/XML/Xeromyces.h"
#include "ps/CLogger.h"
#include "ps/Filesystem.h"
#include "ps/Util.h"
static const bool DISABLE_INTENSITY = true; // disable for now since it's broken
void CSoundGroup::SetGain(float gain)
{
gain = std::min(gain, 1.0f);
m_Gain = gain;
}
void CSoundGroup::SetDefaultValues()
{
m_index = 0;
m_Flags = 0;
m_Intensity = 0;
m_CurTime = 0.0f;
// sane defaults; will probably be replaced by the values read during LoadSoundGroup.
SetGain(0.5f);
m_Pitch = 1.0f;
m_Priority = 60;
m_PitchUpper = 1.1f;
m_PitchLower = 0.9f;
m_GainUpper = 1.0f;
m_GainLower = 0.8f;
m_ConeOuterGain = 0.0f;
m_ConeInnerAngle = 360.0f;
m_ConeOuterAngle = 360.0f;
m_Decay = 3.0f;
m_IntensityThreshold = 3;
// WARNING: m_TimeWindow is currently unused and uninitialized
}
CSoundGroup::CSoundGroup()
{
SetDefaultValues();
}
CSoundGroup::CSoundGroup(const VfsPath& pathnameXML)
{
SetDefaultValues();
LoadSoundGroup(pathnameXML);
}
CSoundGroup::~CSoundGroup()
{
// clean up all the handles from this group.
ReleaseGroup();
}
static float RandFloat(float min, float max)
{
return float(rand(min*100.0f, max*100.0f) / 100.0f);
}
void CSoundGroup::UploadPropertiesAndPlay(Handle hSound, const CVector3D& position)
{
// interface/UI sounds should always be played at the listener's
// position, which is achieved by setting position to 0 and
// having that treated as relative to the listener.
float x = 0.0f, y = 0.0f, z = 0.0f;
bool relative = true;
if(!TestFlag(eOmnipresent))
{
x = position.X;
y = position.Y;
z = position.Z;
relative = false;
}
snd_set_pos(hSound, x, y, z, relative);
float gain = TestFlag(eRandGain)? RandFloat(m_GainLower, m_GainUpper) : m_Gain;
gain = std::min(gain, 1.0f); // guard against roundoff error in RandFloat or too high m_GainUpper
snd_set_gain(hSound, gain);
const float pitch = TestFlag(eRandPitch)? RandFloat(m_PitchLower, m_PitchUpper) : m_Pitch;
snd_set_pitch(hSound, pitch);
snd_play(hSound, m_Priority);
}
static void HandleError(const std::wstring& message, const VfsPath& pathname, Status err)
{
if(err == ERR::AGAIN)
return; // open failed because sound is disabled (don't log this)
LOGERROR(L"%ls: pathname=%ls, error=%ls", message.c_str(), pathname.string().c_str(), ErrorString(err));
}
void CSoundGroup::PlayNext(const CVector3D& position)
{
if(m_Intensity >= m_IntensityThreshold && !DISABLE_INTENSITY)
{
if(!snd_is_playing(m_hReplacement))
{
// load up replacement file
const VfsPath pathname(m_filepath / m_intensity_file);
m_hReplacement = snd_open(g_VFS, pathname);
if(m_hReplacement < 0)
{
HandleError(L"PlayNext: snd_open for replacement file failed", pathname, (Status)m_hReplacement);
return;
}
UploadPropertiesAndPlay(m_hReplacement, position);
}
}
else
{
// if no sounds, return
if (filenames.size() == 0)
return;
// try loading on the fly only when we need the sound to see if that fixes release problems...
if(TestFlag(eRandOrder))
m_index = (size_t)rand(0, (size_t)filenames.size());
// (note: previously snd_group[m_index] was used in place of hs)
const VfsPath pathname(m_filepath / filenames[m_index]);
Handle hs = snd_open(g_VFS, pathname);
if(hs < 0)
{
HandleError(L"PlayNext: snd_open failed", pathname, (Status)hs);
return;
}
UploadPropertiesAndPlay(hs, position);
}
playtimes.at(m_index) = 0.0f;
m_index++;
m_Intensity++;
if(m_Intensity > m_IntensityThreshold)
m_Intensity = m_IntensityThreshold;
if(m_index >= filenames.size())
Reload();
}
void CSoundGroup::Reload()
{
m_index = 0; // reset our index
// get rid of the used handles
snd_group.clear();
// clear out the old timers
playtimes.clear();
//Reload the sounds
/*for(size_t i = 0; i < filenames.size(); i++)
{
string szTemp = m_filepath + filenames[i];
Handle temp = snd_open(m_filepath + filenames[i]);
snd_set_gain(temp, m_Gain);
snd_set_pitch(temp, m_Pitch);
snd_set_cone(temp, m_ConeInnerAngle, m_ConeOuterAngle, m_ConeOuterGain);
snd_group.push_back(temp);
}*/
while(playtimes.size() < filenames.size())
playtimes.push_back(-1.0f);
//if(TestFlag(eRandOrder))
//random_shuffle(snd_group.begin(), snd_group.end());
}
void CSoundGroup::ReleaseGroup()
{
for(size_t i = m_index; i<snd_group.size(); i++)
{
//if(!snd_is_playing(snd_group[i]))
snd_free(snd_group[i]);
}
snd_group.clear();
playtimes.clear();
//if(snd_is_playing(m_hReplacement))
// snd_free(m_hReplacement);
m_index = 0;
}
void CSoundGroup::Update(float TimeSinceLastFrame)
{
for(size_t i = 0; i < playtimes.size(); i++)
{
if(playtimes[i] >= 0.0f)
playtimes[i] += TimeSinceLastFrame;
if(playtimes[i] >= m_Decay)
{
playtimes[i] = -1.0f;
m_Intensity--;
}
}
}
bool CSoundGroup::LoadSoundGroup(const VfsPath& pathnameXML)
{
CXeromyces XeroFile;
if (XeroFile.Load(g_VFS, pathnameXML) != PSRETURN_OK)
return false;
// Define elements used in XML file
#define EL(x) int el_##x = XeroFile.GetElementID(#x)
#define AT(x) int at_##x = XeroFile.GetAttributeID(#x)
EL(soundgroup);
EL(gain);
EL(looping);
EL(omnipresent);
EL(pitch);
EL(priority);
EL(randorder);
EL(randgain);
EL(randpitch);
EL(conegain);
EL(coneinner);
EL(coneouter);
EL(sound);
EL(gainupper);
EL(gainlower);
EL(pitchupper);
EL(pitchlower);
EL(path);
EL(threshold);
EL(decay);
EL(replacement);
#undef AT
#undef EL
XMBElement root = XeroFile.GetRoot();
if (root.GetNodeName() != el_soundgroup)
{
LOGERROR(L"Invalid SoundGroup format (unrecognised root element '%hs')", XeroFile.GetElementString(root.GetNodeName()).c_str());
return false;
}
XERO_ITER_EL(root, child)
{
int child_name = child.GetNodeName();
if(child_name == el_gain)
{
SetGain(child.GetText().ToFloat());
}
else if(child_name == el_looping)
{
if(child.GetText().ToInt() == 1)
SetFlag(eLoop);
}
else if(child_name == el_omnipresent)
{
if(child.GetText().ToInt() == 1)
SetFlag(eOmnipresent);
}
else if(child_name == el_pitch)
{
this->m_Pitch = child.GetText().ToFloat();
}
else if(child_name == el_priority)
{
this->m_Priority = child.GetText().ToFloat();
}
else if(child_name == el_randorder)
{
if(child.GetText().ToInt() == 1)
SetFlag(eRandOrder);
}
else if(child_name == el_randgain)
{
if(child.GetText().ToInt() == 1)
SetFlag(eRandGain);
}
else if(child_name == el_gainupper)
{
this->m_GainUpper = child.GetText().ToFloat();
}
else if(child_name == el_gainlower)
{
this->m_GainLower = child.GetText().ToFloat();
}
else if(child_name == el_randpitch)
{
if(child.GetText().ToInt() == 1)
SetFlag(eRandPitch);
}
else if(child_name == el_pitchupper)
{
this->m_PitchUpper = child.GetText().ToFloat();
}
else if(child_name == el_pitchlower)
{
this->m_PitchLower = child.GetText().ToFloat();
}
else if(child_name == el_conegain)
{
this->m_ConeOuterGain = child.GetText().ToFloat();
}
else if(child_name == el_coneinner)
{
this->m_ConeInnerAngle = child.GetText().ToFloat();
}
else if(child_name == el_coneouter)
{
this->m_ConeOuterAngle = child.GetText().ToFloat();
}
else if(child_name == el_sound)
{
this->filenames.push_back(child.GetText().FromUTF8());
}
else if(child_name == el_path)
{
m_filepath = child.GetText().FromUTF8();
}
else if(child_name == el_threshold)
{
m_IntensityThreshold = child.GetText().ToFloat();
}
else if(child_name == el_decay)
{
m_Decay = child.GetText().ToFloat();
}
else if(child_name == el_replacement)
{
m_intensity_file = child.GetText().FromUTF8();
}
}
Reload();
return true;
}