Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
SoundGroup.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2012 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /**
19 * =========================================================================
20 * File : SoundGroup.cpp
21 * Project : 0 A.D.
22 * Description : Loads up a group of sound files with shared properties,
23 * and provides a simple interface for playing them.
24 * =========================================================================
25 */
26 
27 #include "precompiled.h"
28 
29 #include "SoundGroup.h"
30 #include "graphics/Camera.h"
31 #include "graphics/GameView.h"
32 #include "lib/rand.h"
33 #include "ps/Game.h"
34 #include "ps/CLogger.h"
35 #include "ps/CStr.h"
36 #include "ps/Filesystem.h"
37 #include "ps/Util.h"
38 #include "ps/XML/Xeromyces.h"
41 
42 #include <algorithm>
43 
44 
45 extern CGame *g_Game;
46 
47 #define PI 3.14126f
48 
49 
50 static const bool DISABLE_INTENSITY = true; // disable for now since it's broken
51 
52 void CSoundGroup::SetGain(float gain)
53 {
54  gain = std::min(gain, 1.0f);
55  m_Gain = gain;
56 }
57 
59 {
60  m_index = 0;
61  m_Flags = 0;
62  m_Intensity = 0;
63  m_CurTime = 0.0f;
64 
65  // sane defaults; will probably be replaced by the values read during LoadSoundGroup.
66  SetGain(0.7f);
67  m_Pitch = 1.0f;
68  m_Priority = 60;
69  m_PitchUpper = 1.1f;
70  m_PitchLower = 0.9f;
71  m_GainUpper = 1.0f;
72  m_GainLower = 0.8f;
73  m_ConeOuterGain = 0.0f;
74  m_ConeInnerAngle = 360.0f;
75  m_ConeOuterAngle = 360.0f;
76  m_Decay = 3.0f;
78  // WARNING: m_TimeWindow is currently unused and uninitialized
79 }
80 
82 {
84 }
85 
86 CSoundGroup::CSoundGroup(const VfsPath& pathnameXML)
87 {
89  LoadSoundGroup(pathnameXML);
90 }
91 
93 {
94  // clean up all the handles from this group.
95  ReleaseGroup();
96 }
97 
98 #if CONFIG2_AUDIO
99 static float RandFloat(float min, float max)
100 {
101  return float(rand(min*100.0f, max*100.0f) / 100.0f);
102 }
103 #endif // CONFIG2_AUDIO
104 
105 float CSoundGroup::RadiansOffCenter(const CVector3D& position, bool& onScreen, float& itemRollOff)
106 {
107  float x, y;
108  float answer = 0.0;
109  const size_t screenWidth = g_Game->GetView()->GetCamera()->GetViewPort().m_Width;
110  const size_t screenHeight = g_Game->GetView()->GetCamera()->GetViewPort().m_Height;
111  float bufferSize = screenWidth * 0.10;
112  float yBufferSize = 15;
113  const size_t audioWidth = screenWidth;
114  float radianCap = PI / 3;
115 
116  g_Game->GetView()->GetCamera()->GetScreenCoordinates(position, x, y);
117 
118  onScreen = true;
119 
120  if (x < -bufferSize)
121  {
122  onScreen = false;
123  answer = -radianCap;
124  }
125  else if (x > screenWidth + bufferSize)
126  {
127  onScreen = false;
128  answer = radianCap;
129  }
130  else
131  {
132  if ((x < 0) || (x > screenWidth))
133  {
134  itemRollOff = 0.5;
135  }
136  float pixPerRadian = audioWidth / (radianCap * 2);
137  answer = (x - (screenWidth/2)) / pixPerRadian;
138  }
139 
140  if (y < -yBufferSize)
141  {
142  onScreen = false;
143  }
144  else if (y > screenHeight + yBufferSize)
145  {
146  onScreen = false;
147  }
148  else if ((y < 0) || (y > screenHeight))
149  {
150  itemRollOff = 0.5;
151  }
152 
153  return answer;
154 }
155 
156 void CSoundGroup::UploadPropertiesAndPlay(size_t theIndex, const CVector3D& position, entity_id_t source)
157 {
158 #if CONFIG2_AUDIO
159  if ( !g_SoundManager )
160  return;
161 
162  bool isOnscreen = false;
163  ALfloat initialRolllOff = 0.1f;
164  ALfloat itemRollOff = initialRolllOff;
165 
166 
167  float offSet = RadiansOffCenter(position, isOnscreen, itemRollOff);
168  bool shouldBePlayed = isOnscreen || TestFlag(eDistanceless) || TestFlag(eOmnipresent);
169 
170  if ( !shouldBePlayed )
171  return;
172 
173  if (snd_group.size() == 0)
174  Reload();
175 
176  if ( snd_group.size() <= theIndex )
177  return;
178 
179  CSoundData* sndData = snd_group[theIndex];
180  if ( sndData == NULL )
181  return;
182 
183  ISoundItem* hSound = ((CSoundManager*)g_SoundManager)->ItemForEntity( source, sndData);
184  if ( hSound == NULL )
185  return;
186 
187  if (!TestFlag(eOmnipresent))
188  {
189  CVector3D origin = g_Game->GetView()->GetCamera()->GetOrientation().GetTranslation();
190  float sndDist = origin.Y;
191  float itemDist = ( position - origin ).Length();
192 
193  if ( (sndDist * 2) < itemDist )
194  sndDist = itemDist;
195 
196  if (TestFlag(eDistanceless))
197  itemRollOff = 0;
198 
199  if ( sndData->IsStereo() )
200  LOGWARNING( L"OpenAL: stereo sounds can't be positioned: %ls", sndData->GetFileName()->string().c_str() );
201 
202  hSound->SetLocation(CVector3D((sndDist * sin(offSet)), 0, - sndDist * cos(offSet)));
203  hSound->SetRollOff(itemRollOff);
204  }
205 
206  if (TestFlag(eRandPitch))
208  else
209  hSound->SetPitch(m_Pitch);
210 
211  ALfloat theGain = m_Gain;
212  if (TestFlag(eRandGain))
213  theGain = RandFloat(m_GainLower, m_GainUpper);
214 
216  ((CSoundManager*)g_SoundManager)->PlayGroupItem(hSound, theGain);
217 
218 #else // !CONFIG2_AUDIO
219  UNUSED2(theIndex);
220  UNUSED2(position);
221  UNUSED2(source);
222 #endif // !CONFIG2_AUDIO
223 }
224 
225 
226 static void HandleError(const CStrW& message, const VfsPath& pathname, Status err)
227 {
228  if (err == ERR::AGAIN)
229  return; // open failed because sound is disabled (don't log this)
230  LOGERROR(L"%ls: pathname=%ls, error=%ls", message.c_str(), pathname.string().c_str(), ErrorString(err));
231 }
232 
233 void CSoundGroup::PlayNext(const CVector3D& position, entity_id_t source)
234 {
235  // if no sounds, return
236  if (filenames.size() == 0)
237  return;
238 
239  m_index = rand(0, (size_t)filenames.size());
240  UploadPropertiesAndPlay(m_index, position, source);
241 }
242 
244 {
245  m_index = 0; // reset our index
246 
247 #if CONFIG2_AUDIO
248  ReleaseGroup();
249 
250  if ( g_SoundManager ) {
251  for (size_t i = 0; i < filenames.size(); i++)
252  {
253  VfsPath thePath = m_filepath/filenames[i];
254  CSoundData* itemData = CSoundData::SoundDataFromFile(thePath);
255 
256  if (itemData == NULL)
257  HandleError(L"error loading sound", thePath, ERR::FAIL);
258  else
259  snd_group.push_back(itemData->IncrementCount());
260  }
261 
262  if (TestFlag(eRandOrder))
263  random_shuffle(snd_group.begin(), snd_group.end());
264  }
265 #endif // CONFIG2_AUDIO
266 }
267 
269 {
270 #if CONFIG2_AUDIO
271  for (size_t i = 0; i < snd_group.size(); i++)
272  {
274  }
275  snd_group.clear();
276 #endif // CONFIG2_AUDIO
277 }
278 
279 void CSoundGroup::Update(float UNUSED(TimeSinceLastFrame))
280 {
281 }
282 
283 bool CSoundGroup::LoadSoundGroup(const VfsPath& pathnameXML)
284 {
285  CXeromyces XeroFile;
286  if (XeroFile.Load(g_VFS, pathnameXML) != PSRETURN_OK)
287  {
288  HandleError(L"error loading file", pathnameXML, ERR::FAIL);
289  return false;
290  }
291  // Define elements used in XML file
292  #define EL(x) int el_##x = XeroFile.GetElementID(#x)
293  #define AT(x) int at_##x = XeroFile.GetAttributeID(#x)
294  EL(soundgroup);
295  EL(gain);
296  EL(looping);
297  EL(omnipresent);
298  EL(heardby);
299  EL(distanceless);
300  EL(pitch);
301  EL(priority);
302  EL(randorder);
303  EL(randgain);
304  EL(randpitch);
305  EL(conegain);
306  EL(coneinner);
307  EL(coneouter);
308  EL(sound);
309  EL(gainupper);
310  EL(gainlower);
311  EL(pitchupper);
312  EL(pitchlower);
313  EL(path);
314  EL(threshold);
315  EL(decay);
316  #undef AT
317  #undef EL
318 
319  XMBElement root = XeroFile.GetRoot();
320 
321  if (root.GetNodeName() != el_soundgroup)
322  {
323  LOGERROR(L"Invalid SoundGroup format (unrecognised root element '%hs')", XeroFile.GetElementString(root.GetNodeName()).c_str());
324  return false;
325  }
326 
327  XERO_ITER_EL(root, child)
328  {
329 
330  int child_name = child.GetNodeName();
331 
332  if(child_name == el_gain)
333  {
334  SetGain(child.GetText().ToFloat());
335  }
336  else if(child_name == el_looping)
337  {
338  if(child.GetText().ToInt() == 1)
339  SetFlag(eLoop);
340  }
341  else if(child_name == el_omnipresent)
342  {
343  if(child.GetText().ToInt() == 1)
345  }
346  else if(child_name == el_heardby)
347  {
348  if(child.GetText().FindInsensitive( "owner" ) == 0 )
350  }
351  else if(child_name == el_distanceless)
352  {
353  if(child.GetText().ToInt() == 1)
355  }
356  else if(child_name == el_pitch)
357  {
358  this->m_Pitch = child.GetText().ToFloat();
359  }
360  else if(child_name == el_priority)
361  {
362  this->m_Priority = child.GetText().ToFloat();
363  }
364  else if(child_name == el_randorder)
365  {
366  if(child.GetText().ToInt() == 1)
368  }
369  else if(child_name == el_randgain)
370  {
371  if(child.GetText().ToInt() == 1)
373  }
374  else if(child_name == el_gainupper)
375  {
376  this->m_GainUpper = child.GetText().ToFloat();
377  }
378  else if(child_name == el_gainlower)
379  {
380  this->m_GainLower = child.GetText().ToFloat();
381  }
382  else if(child_name == el_randpitch)
383  {
384  if(child.GetText().ToInt() == 1)
386  }
387  else if(child_name == el_pitchupper)
388  {
389  this->m_PitchUpper = child.GetText().ToFloat();
390  }
391  else if(child_name == el_pitchlower)
392  {
393  this->m_PitchLower = child.GetText().ToFloat();
394  }
395  else if(child_name == el_conegain)
396  {
397  this->m_ConeOuterGain = child.GetText().ToFloat();
398  }
399  else if(child_name == el_coneinner)
400  {
401  this->m_ConeInnerAngle = child.GetText().ToFloat();
402  }
403  else if(child_name == el_coneouter)
404  {
405  this->m_ConeOuterAngle = child.GetText().ToFloat();
406  }
407  else if(child_name == el_sound)
408  {
409  this->filenames.push_back(child.GetText().FromUTF8());
410  }
411  else if(child_name == el_path)
412  {
413  m_filepath = child.GetText().FromUTF8();
414  }
415  else if(child_name == el_threshold)
416  {
417  m_IntensityThreshold = child.GetText().ToFloat();
418  }
419  else if(child_name == el_decay)
420  {
421  m_Decay = child.GetText().ToFloat();
422  }
423  }
424 
425  return true;
426 }
427 
The container that holds the rules, resources and attributes of the game.
Definition: Game.h:39
float m_Gain
Definition: SoundGroup.h:132
float m_Pitch
Definition: SoundGroup.h:133
VfsPath m_filepath
Definition: SoundGroup.h:123
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
virtual void SetLocation(const CVector3D &position)=0
virtual void SetPitch(float pitch)=0
PSRETURN Load(const PIVFS &vfs, const VfsPath &filename)
Load from an XML file (with invisible XMB caching).
Definition: Xeromyces.cpp:65
#define LOGERROR
Definition: CLogger.h:35
const PSRETURN PSRETURN_OK
Definition: Errors.h:103
float m_GainUpper
Definition: SoundGroup.h:138
ISoundManager * g_SoundManager
void SetDefaultValues()
Definition: SoundGroup.cpp:58
size_t m_IntensityThreshold
Definition: SoundGroup.h:127
void UploadPropertiesAndPlay(size_t theIndex, const CVector3D &position, entity_id_t source)
Definition: SoundGroup.cpp:156
const wchar_t * ErrorString(int err)
Definition: Util.cpp:160
std::vector< std::wstring > filenames
Definition: SoundGroup.h:121
void SetFlag(int flag)
Definition: SoundGroup.h:104
size_t m_Intensity
Definition: SoundGroup.h:128
#define XERO_ITER_EL(parent_element, child_element)
Definition: Xeromyces.h:91
static void HandleError(const CStrW &message, const VfsPath &pathname, Status err)
Definition: SoundGroup.cpp:226
virtual void SetCone(float innerCone, float outerCone, float coneGain)=0
virtual bool IsStereo()
Definition: SoundData.cpp:157
void Update(float TimeSinceLastFrame)
Definition: SoundGroup.cpp:279
static CSoundData * SoundDataFromFile(const VfsPath &itemPath)
Definition: SoundData.cpp:68
virtual Path * GetFileName()
Definition: SoundData.cpp:122
int m_Height
Definition: Camera.h:36
static float RandFloat(float min, float max)
Definition: SoundGroup.cpp:99
const Status AGAIN
Definition: status.h:427
float RadiansOffCenter(const CVector3D &position, bool &onScreen, float &itemRollOff)
Definition: SoundGroup.cpp:105
#define LOGWARNING
Definition: CLogger.h:34
bool LoadSoundGroup(const VfsPath &pathnameXML)
Definition: SoundGroup.cpp:283
virtual void SetRollOff(float gain)=0
float m_Priority
Definition: SoundGroup.h:134
float m_ConeInnerAngle
Definition: SoundGroup.h:140
#define UNUSED2(param)
mark a function local variable or parameter as unused and avoid the corresponding compiler warning...
float m_CurTime
Definition: SoundGroup.h:125
CMatrix3D & GetOrientation()
Definition: Camera.h:52
CSoundData * IncrementCount()
Definition: SoundData.cpp:134
Definition: path.h:75
float Y
Definition: Vector3D.h:31
const String & string() const
Definition: path.h:123
float m_Decay
Definition: SoundGroup.h:129
float m_ConeOuterGain
Definition: SoundGroup.h:135
CCamera * GetCamera()
Definition: GameView.cpp:390
CGame * g_Game
Globally accessible pointer to the CGame object.
Definition: Game.cpp:56
void GetScreenCoordinates(const CVector3D &world, float &x, float &y) const
Definition: Camera.cpp:192
float m_ConeOuterAngle
Definition: SoundGroup.h:141
i64 Status
Error handling system.
Definition: status.h:171
std::string GetElementString(const int ID) const
Definition: XeroXMB.cpp:148
void Reload()
Definition: SoundGroup.cpp:243
#define EL(x)
float m_PitchUpper
Definition: SoundGroup.h:136
std::vector< CSoundData * > snd_group
Definition: SoundGroup.h:119
CGameView * GetView()
Get the pointer to the game view object.
Definition: Game.h:128
const SViewPort & GetViewPort() const
Definition: Camera.h:65
void ReleaseGroup()
Definition: SoundGroup.cpp:268
float m_GainLower
Definition: SoundGroup.h:139
XMBElement GetRoot() const
Definition: XeroXMB.cpp:84
int m_Width
Definition: Camera.h:35
size_t m_index
Definition: SoundGroup.h:116
void SetGain(float gain)
Definition: SoundGroup.cpp:52
const Status FAIL
Definition: status.h:406
#define PI
Definition: SoundGroup.cpp:47
size_t rand(size_t min_inclusive, size_t max_exclusive)
return random integer in [min, max).
Definition: rand.cpp:53
CVector3D GetTranslation() const
Definition: Matrix3D.cpp:195
unsigned char m_Flags
Definition: SoundGroup.h:130
static float Length(const SVec3 v)
Definition: mikktspace.cpp:112
PIVFS g_VFS
Definition: Filesystem.cpp:30
float m_PitchLower
Definition: SoundGroup.h:137
u32 entity_id_t
Entity ID type.
Definition: Entity.h:24
bool TestFlag(int flag)
Definition: SoundGroup.h:107
static void ReleaseSoundData(CSoundData *theData)
Definition: SoundData.cpp:54
static const bool DISABLE_INTENSITY
Definition: SoundGroup.cpp:50
void PlayNext(const CVector3D &position, entity_id_t source)
Definition: SoundGroup.cpp:233
int GetNodeName() const
Definition: XeroXMB.cpp:166