OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4SoundSystem.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 1998-2000, Matthes Bender
5  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
6  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
7  *
8  * Distributed under the terms of the ISC license; see accompanying file
9  * "COPYING" for details.
10  *
11  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12  * See accompanying file "TRADEMARK" for details.
13  *
14  * To redistribute this file separately, substitute the full license texts
15  * for the above references.
16  */
17 
18 /* Handles the sound bank and plays effects using DSoundX */
19 
20 #include "C4Include.h"
21 #include "platform/C4SoundSystem.h"
22 
23 #include "lib/C4Random.h"
24 #include "game/C4Application.h"
25 #include "game/C4Viewport.h"
26 #include "object/C4Object.h"
30 
32 
34 
36 {
39  return false;
40 
41  // Might be reinitialisation
42  ClearEffects();
43  // (re)init EFX
44  Modifiers.Init();
45  // Open sound file
46  if (!SoundFile.IsOpen())
47  if (!Reloc.Open(SoundFile, C4CFN_Sound)) return false;
48  // Load static sound from Sound.ocg
49  LoadEffects(SoundFile, nullptr, false);
50 #if AUDIO_TK == AUDIO_TK_SDL_MIXER
51  Mix_AllocateChannels(C4MaxSoundInstances);
52 #endif
53  return true;
54 }
55 
57 {
58  ClearEffects();
59  Modifiers.Clear();
60  // Close sound file
61  SoundFile.Close();
62 }
63 
65 {
66  // Clear sound bank
67  C4SoundEffect *csfx,*next;
68  for (csfx=FirstSound; csfx; csfx=next)
69  {
70  next=csfx->Next;
71  delete csfx;
72  }
73  FirstSound=nullptr;
74 }
75 
77 {
78 #if AUDIO_TK == AUDIO_TK_OPENAL
80 #endif
81  C4SoundEffect *csfx;
82  for (csfx=FirstSound; csfx; csfx=csfx->Next)
83  {
84  // Instance removal check
85  csfx->Execute();
86  }
87 }
88 
89 C4SoundEffect* C4SoundSystem::GetEffect(const char *szSndName)
90 {
91  // Remember wildcards before adding .* extension - if there are 2 versions with different file extensions, play the last added
92  bool bRandomSound = SCharCount('?',szSndName) || SCharCount('*',szSndName);
93  // Evaluate sound name
94  char szName[C4MaxSoundName+2+1];
95  SCopy(szSndName,szName,C4MaxSoundName);
96  // Any extension accepted
97  DefaultExtension(szName,"*");
98  // Play nth Sound. Standard: 1
99  int32_t iNumber = 1;
100  // Sound with a wildcard: determine number of available matches
101  if (bRandomSound)
102  {
103  iNumber = 0;
104  // Count matching sounds
105  for (C4SoundEffect *pSfx=FirstSound; pSfx; pSfx=pSfx->Next)
106  if (WildcardMatch(szName,pSfx->Name))
107  ++iNumber;
108  // Nothing found? Abort
109  if(iNumber == 0)
110  return nullptr;
111  iNumber=UnsyncedRandom(iNumber)+1;
112  }
113  // Find requested sound effect in bank
114  C4SoundEffect *pSfx;
115  for (pSfx=FirstSound; pSfx; pSfx=pSfx->Next)
116  if (WildcardMatch(szName,pSfx->Name))
117  if(!--iNumber)
118  break;
119  return pSfx; // Is still nullptr if nothing is found
120 }
121 
122 C4SoundInstance *C4SoundSystem::NewEffect(const char *szSndName, bool fLoop, int32_t iVolume, C4Object *pObj, int32_t iCustomFalloffDistance, int32_t iPitch, C4SoundModifier *modifier)
123 {
124  // Sound not active
125  if (!Config.Sound.RXSound) return nullptr;
126  // Get sound
127  C4SoundEffect *csfx;
128  if (!(csfx = GetEffect(szSndName)))
129  {
130  // Warn about missing or incorrectly spelled sound to allow finding mistakes earlier.
131  DebugLogF("Warning: could not find sound matching '%s'", szSndName);
132  return nullptr;
133  }
134  // Play
135  return csfx->New(fLoop, iVolume, pObj, iCustomFalloffDistance, iPitch, modifier);
136 }
137 
139 {
140  char szName[C4MaxSoundName+2+1];
141  // Evaluate sound name (see GetEffect)
142  SCopy(szSndName,szName,C4MaxSoundName);
143  DefaultExtension(szName,"*");
144  // Find an effect with a matching instance
145  for (C4SoundEffect *csfx = FirstSound; csfx; csfx = csfx->Next)
146  if (WildcardMatch(szName, csfx->Name))
147  {
148  C4SoundInstance *pInst = csfx->GetInstance(pObj);
149  if (pInst) return pInst;
150  }
151  return nullptr;
152 }
153 
154 // LoadEffects will load all sound effects of all known sound types (i.e. *.wav and *.ogg) as defined in C4CFN_SoundFiles
155 
156 int32_t C4SoundSystem::LoadEffects(C4Group &hGroup, const char *namespace_prefix, bool group_is_root)
157 {
158  // Local definition sounds: If there is a Sound.ocg in the group, load the sound from there
159  if(group_is_root && hGroup.FindEntry(C4CFN_Sound))
160  {
161  C4Group g;
162  g.OpenAsChild(&hGroup, C4CFN_Sound, false, false);
163  return LoadEffects(g, namespace_prefix, false);
164  }
165  int32_t iNum=0;
166  char szFilename[_MAX_FNAME+1];
167  char szFileType[_MAX_FNAME+1];
168  C4SoundEffect *nsfx;
169  // Process segmented list of file types
170  for (int32_t i = 0; SCopySegment(C4CFN_SoundFiles, i, szFileType, '|', _MAX_FNAME); i++)
171  {
172  // Search all sound files in group
173  hGroup.ResetSearch();
174  while (hGroup.FindNextEntry(szFileType, szFilename))
175  // Create and load effect
176  if ((nsfx = new C4SoundEffect))
177  {
178  if (nsfx->Load(szFilename, hGroup, namespace_prefix))
179  {
180  // Overload same name effects
181  RemoveEffect(nsfx->Name);
182  // Add effect
183  nsfx->Next=FirstSound;
184  FirstSound=nsfx;
185  iNum++;
186  }
187  else
188  delete nsfx;
189  }
190  }
191  // Load subgroups from Sound.ocg and other subgroups
192  if (!group_is_root)
193  {
194  hGroup.ResetSearch();
195  while (hGroup.FindNextEntry(C4CFN_SoundSubgroups, szFilename))
196  {
197  // Load from subgroup as a sub-namespace
198  // get namespace name
199  StdStrBuf sub_namespace;
200  if (namespace_prefix)
201  {
202  sub_namespace.Copy(namespace_prefix);
203  sub_namespace.Append("::");
204  }
205  sub_namespace.Append(szFilename, strlen(szFilename) - strlen(C4CFN_SoundSubgroups) + 1);
206  // load from child group
207  C4Group subgroup;
208  if (subgroup.OpenAsChild(&hGroup, szFilename, false, false))
209  {
210  iNum += LoadEffects(subgroup, sub_namespace.getData(), false);
211  }
212  }
213  }
214  return iNum;
215 }
216 
217 int32_t C4SoundSystem::RemoveEffect(const char *szFilename)
218 {
219  int32_t iResult=0;
220  C4SoundEffect *pNext,*pPrev=nullptr;
221  for (C4SoundEffect *pSfx=FirstSound; pSfx; pSfx=pNext)
222  {
223  pNext=pSfx->Next;
224  if (WildcardMatch(szFilename,pSfx->Name))
225  {
226  delete pSfx;
227  if (pPrev) pPrev->Next=pNext;
228  else FirstSound=pNext;
229  iResult++;
230  }
231  else
232  pPrev=pSfx;
233  }
234  return iResult;
235 }
236 
238 {
239  for (C4SoundEffect *pEff=FirstSound; pEff; pEff=pEff->Next)
240  pEff->ClearPointers(pObj);
241 }
242 
244 {
245  // Return by searching through effect linked list.
246  for (C4SoundEffect *pSfx = FirstSound; pSfx; pSfx = pSfx->Next)
247  if (pSfx->FirstInst) return pSfx->FirstInst;
248  return nullptr;
249 }
250 
252 {
253  // Return by searching through instance linked list and parent linked list of effects
254  assert(prev && prev->pEffect);
255  if (prev->pNext) return prev->pNext;
256  for (C4SoundEffect *pSfx = prev->pEffect->Next; pSfx; pSfx = pSfx->Next)
257  if (pSfx->FirstInst) return pSfx->FirstInst;
258  return nullptr;
259 }
260 
261 C4SoundInstance *StartSoundEffect(const char *szSndName, bool fLoop, int32_t iVolume, C4Object *pObj, int32_t iCustomFalloffDistance, int32_t iPitch, C4SoundModifier *modifier)
262 {
263  // Sound check
264  if (!Config.Sound.RXSound) return nullptr;
265  // Start new
266  return Application.SoundSystem.NewEffect(szSndName, fLoop, iVolume, pObj, iCustomFalloffDistance, iPitch, modifier);
267 }
268 
269 C4SoundInstance *StartSoundEffectAt(const char *szSndName, int32_t iX, int32_t iY, int32_t iVolume, int32_t iCustomFallofDistance, int32_t iPitch, C4SoundModifier *modifier)
270 {
271  // Sound check
272  if (!Config.Sound.RXSound) return nullptr;
273  // Create
274  C4SoundInstance *pInst = StartSoundEffect(szSndName, false, iVolume, nullptr, iCustomFallofDistance, iPitch, modifier);
275  // Set volume by position
276  if (pInst) pInst->SetVolumeByPos(iX, iY);
277  // Return
278  return pInst;
279 }
280 
281 C4SoundInstance *GetSoundInstance(const char *szSndName, C4Object *pObj)
282 {
283  return Application.SoundSystem.FindInstance(szSndName, pObj);
284 }
285 
286 void StopSoundEffect(const char *szSndName, C4Object *pObj)
287 {
288  // Find instance
289  C4SoundInstance *pInst = Application.SoundSystem.FindInstance(szSndName, pObj);
290  if (!pInst) return;
291  // Stop
292  pInst->Stop();
293 }
294 void SoundLevel(const char *szSndName, C4Object *pObj, int32_t iLevel)
295 {
296  // Sound level zero? Stop
297  if (!iLevel) { StopSoundEffect(szSndName, pObj); return; }
298  // Find or create instance
299  C4SoundInstance *pInst = Application.SoundSystem.FindInstance(szSndName, pObj);
300  if (!pInst) pInst = StartSoundEffect(szSndName, true, iLevel, pObj);
301  if (!pInst) return;
302  // Set volume
303  pInst->SetVolume(iLevel);
304  pInst->Execute();
305 }
306 
307 void SoundPan(const char *szSndName, C4Object *pObj, int32_t iPan)
308 {
309  // Find instance
310  C4SoundInstance *pInst = Application.SoundSystem.FindInstance(szSndName, pObj);
311  if (!pInst) return;
312  // Set pan
313  pInst->SetPan(iPan);
314  pInst->Execute();
315 }
316 
317 void SoundPitch(const char *szSndName, C4Object *pObj, int32_t iPitch)
318 {
319  // Find instance
320  C4SoundInstance *pInst = Application.SoundSystem.FindInstance(szSndName, pObj);
321  if (!pInst) return;
322  // Set pitch
323  pInst->SetPitch(iPitch);
324  pInst->Execute();
325 }
326 
327 void SoundUpdate(C4SoundInstance *pInst, int32_t iLevel, int32_t iPitch)
328 {
329  // Set sound data
330  pInst->SetVolume(iLevel);
331  pInst->SetPitch(iPitch);
332  // Ensure it's reflected in audio engine
333  pInst->Execute();
334 }
const char * getData() const
Definition: StdBuf.h:442
C4SoundSystem SoundSystem
Definition: C4Application.h:42
bool FindEntry(const char *szWildCard, StdStrBuf *sFileName=nullptr, size_t *iSize=nullptr)
Definition: C4Group.cpp:1774
C4Config Config
Definition: C4Config.cpp:833
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:130
uint32_t UnsyncedRandom()
Definition: C4Random.cpp:58
bool SCopySegment(const char *szString, int iSegment, char *sTarget, char cSeparator, int iMaxL, bool fSkipWhitespace)
Definition: Standard.cpp:251
int32_t LoadEffects(C4Group &hGroup, const char *namespace_prefix, bool group_is_root)
C4SoundInstance * StartSoundEffectAt(const char *szSndName, int32_t iX, int32_t iY, int32_t iVolume, int32_t iCustomFallofDistance, int32_t iPitch, C4SoundModifier *modifier)
int32_t RXSound
Definition: C4Config.h:126
void SelectContext()
C4SoundEffect * GetEffect(const char *szSound)
char Name[C4MaxSoundName+1]
void SetPitch(int32_t inPitch)
void StopSoundEffect(const char *szSndName, C4Object *pObj)
const int32_t C4MaxSoundName
Definition: C4SoundSystem.h:27
C4SoundInstance * pNext
unsigned int SCharCount(char cTarget, const char *szInStr, const char *cpUntil)
Definition: Standard.cpp:298
#define C4CFN_SoundSubgroups
Definition: C4Components.h:27
C4SoundEffect * Next
C4SoundInstance * StartSoundEffect(const char *szSndName, bool fLoop, int32_t iVolume, C4Object *pObj, int32_t iCustomFalloffDistance, int32_t iPitch, C4SoundModifier *modifier)
void SoundUpdate(C4SoundInstance *pInst, int32_t iLevel, int32_t iPitch)
int iResult
Definition: C4GroupMain.cpp:39
bool DebugLogF(const char *strMessage...)
Definition: C4Log.cpp:278
C4SoundModifierList Modifiers
Definition: C4SoundSystem.h:54
#define C4CFN_SoundFiles
Definition: C4Components.h:171
bool IsOpen() const
Definition: C4Group.cpp:1891
C4SoundInstance * GetNextInstance(C4SoundInstance *prev) const
bool Load(const char *szFileName, C4Group &hGroup, const char *namespace_prefix)
void Append(const char *pnData, size_t iChars)
Definition: StdBuf.h:519
#define C4CFN_Sound
Definition: C4Components.h:26
bool Close()
Definition: C4Group.cpp:755
bool Open(C4Group &hGroup, const char *filename) const
Definition: C4Reloc.cpp:68
const int32_t C4MaxSoundInstances
Definition: C4SoundSystem.h:28
C4SoundEffect * FirstSound
Definition: C4SoundSystem.h:57
#define _MAX_FNAME
void DefaultExtension(char *szFilename, const char *szExtension)
Definition: StdFile.cpp:274
void SoundPitch(const char *szSndName, C4Object *pObj, int32_t iPitch)
C4SoundInstance * New(bool fLoop=false, int32_t iVolume=100, C4Object *pObj=nullptr, int32_t iCustomFalloffDistance=0, int32_t iPitch=0, C4SoundModifier *modifier=nullptr)
C4Reloc Reloc
Definition: C4Reloc.cpp:21
void SetPan(int32_t inPan)
void SetVolume(int32_t inVolume)
C4SoundInstance * GetFirstInstance() const
bool FindNextEntry(const char *szWildCard, StdStrBuf *sFileName=nullptr, size_t *iSize=nullptr, bool fStartAtFilename=false)
Definition: C4Group.cpp:1780
void SoundPan(const char *szSndName, C4Object *pObj, int32_t iPan)
C4SoundEffect * pEffect
C4Group SoundFile
Definition: C4SoundSystem.h:56
C4SoundInstance * GetSoundInstance(const char *szSndName, C4Object *pObj)
bool WildcardMatch(const char *szWildcard, const char *szString)
Definition: StdFile.cpp:374
C4SoundInstance * NewEffect(const char *szSound, bool fLoop=false, int32_t iVolume=100, C4Object *pObj=nullptr, int32_t iCustomFalloffDistance=0, int32_t iPitch=0, C4SoundModifier *modifier=nullptr)
void ClearPointers(C4Object *pObj)
void ResetSearch(bool reload_contents=false)
Definition: C4Group.cpp:1013
void SoundLevel(const char *szSndName, C4Object *pObj, int32_t iLevel)
C4ConfigSound Sound
Definition: C4Config.h:254
C4SoundInstance * FindInstance(const char *szSound, C4Object *pObj)
void Copy()
Definition: StdBuf.h:467
void SetVolumeByPos(int32_t x, int32_t y)
int32_t RemoveEffect(const char *szFilename)
C4Application Application
Definition: C4Globals.cpp:44
bool InitializeMOD()
bool OpenAsChild(C4Group *pMother, const char *szEntryName, bool fExclusive=false, bool fCreate=false)
Definition: C4Group.cpp:1585
C4MusicSystem MusicSystem
Definition: C4Application.h:41