OpenClonk
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  initialized = true;
54  return true;
55 }
56 
58 {
59  initialized = false;
60  ClearEffects();
61  Modifiers.Clear();
62  // Close sound file
63  SoundFile.Close();
64 }
65 
67 {
68  // Clear sound bank
69  C4SoundEffect *csfx,*next;
70  for (csfx=FirstSound; csfx; csfx=next)
71  {
72  next=csfx->Next;
73  delete csfx;
74  }
75  FirstSound=nullptr;
76 }
77 
79 {
80 #if AUDIO_TK == AUDIO_TK_OPENAL
82 #endif
83  C4SoundEffect *csfx;
84  for (csfx=FirstSound; csfx; csfx=csfx->Next)
85  {
86  // Instance removal check
87  csfx->Execute();
88  }
89 }
90 
91 C4SoundEffect* C4SoundSystem::GetEffect(const char *szSndName)
92 {
93  // Remember wildcards before adding .* extension - if there are 2 versions with different file extensions, play the last added
94  bool bRandomSound = IsWildcardString(szSndName);
95  // Evaluate sound name
96  char szName[C4MaxSoundName+2+1];
97  SCopy(szSndName,szName,C4MaxSoundName);
98  // Any extension accepted
99  DefaultExtension(szName,"*");
100  // Play nth Sound. Standard: 1
101  int32_t iNumber = 1;
102  // Sound with a wildcard: determine number of available matches
103  if (bRandomSound)
104  {
105  iNumber = 0;
106  // Count matching sounds
107  for (C4SoundEffect *pSfx=FirstSound; pSfx; pSfx=pSfx->Next)
108  if (WildcardMatch(szName,pSfx->Name))
109  ++iNumber;
110  // Nothing found? Abort
111  if(iNumber == 0)
112  return nullptr;
113  iNumber=UnsyncedRandom(iNumber)+1;
114  }
115  // Find requested sound effect in bank
116  C4SoundEffect *pSfx;
117  for (pSfx=FirstSound; pSfx; pSfx=pSfx->Next)
118  if (WildcardMatch(szName,pSfx->Name))
119  if(!--iNumber)
120  break;
121  return pSfx; // Is still nullptr if nothing is found
122 }
123 
124 C4SoundInstance *C4SoundSystem::NewEffect(const char *szSndName, bool fLoop, int32_t iVolume, C4Object *pObj, int32_t iCustomFalloffDistance, int32_t iPitch, C4SoundModifier *modifier)
125 {
126  // Sound not active
127  if (!initialized || !Config.Sound.RXSound) return nullptr;
128  // Get sound
129  C4SoundEffect *csfx;
130  if (!(csfx = GetEffect(szSndName)))
131  {
132  // Warn about missing or incorrectly spelled sound to allow finding mistakes earlier.
133  DebugLogF("Warning: could not find sound matching '%s'", szSndName);
134  return nullptr;
135  }
136  // Play
137  return csfx->New(fLoop, iVolume, pObj, iCustomFalloffDistance, iPitch, modifier);
138 }
139 
141 {
142  char szName[C4MaxSoundName+2+1];
143  // Evaluate sound name (see GetEffect)
144  SCopy(szSndName,szName,C4MaxSoundName);
145  DefaultExtension(szName,"*");
146  // Find an effect with a matching instance
147  for (C4SoundEffect *csfx = FirstSound; csfx; csfx = csfx->Next)
148  if (WildcardMatch(szName, csfx->Name))
149  {
150  C4SoundInstance *pInst = csfx->GetInstance(pObj);
151  if (pInst) return pInst;
152  }
153  return nullptr;
154 }
155 
156 // LoadEffects will load all sound effects of all known sound types (i.e. *.wav and *.ogg) as defined in C4CFN_SoundFiles
157 
158 int32_t C4SoundSystem::LoadEffects(C4Group &hGroup, const char *namespace_prefix, bool group_is_root)
159 {
160  // Local definition sounds: If there is a Sound.ocg in the group, load the sound from there
161  if(group_is_root && hGroup.FindEntry(C4CFN_Sound))
162  {
163  C4Group g;
164  g.OpenAsChild(&hGroup, C4CFN_Sound, false, false);
165  return LoadEffects(g, namespace_prefix, false);
166  }
167  int32_t iNum=0;
168  char szFilename[_MAX_FNAME_LEN];
169  char szFileType[_MAX_FNAME_LEN];
170  C4SoundEffect *nsfx;
171  // Process segmented list of file types
172  for (int32_t i = 0; SCopySegment(C4CFN_SoundFiles, i, szFileType, '|', _MAX_FNAME); i++)
173  {
174  // Search all sound files in group
175  hGroup.ResetSearch();
176  while (hGroup.FindNextEntry(szFileType, szFilename))
177  // Create and load effect
178  if ((nsfx = new C4SoundEffect))
179  {
180  if (nsfx->Load(szFilename, hGroup, namespace_prefix))
181  {
182  // Overload same name effects
183  RemoveEffect(nsfx->Name);
184  // Add effect
185  nsfx->Next=FirstSound;
186  FirstSound=nsfx;
187  iNum++;
188  }
189  else
190  delete nsfx;
191  }
192  }
193  // Load subgroups from Sound.ocg and other subgroups
194  if (!group_is_root)
195  {
196  hGroup.ResetSearch();
197  while (hGroup.FindNextEntry(C4CFN_SoundSubgroups, szFilename))
198  {
199  // Load from subgroup as a sub-namespace
200  // get namespace name
201  StdStrBuf sub_namespace;
202  if (namespace_prefix)
203  {
204  sub_namespace.Copy(namespace_prefix);
205  sub_namespace.Append("::");
206  }
207  sub_namespace.Append(szFilename, strlen(szFilename) - strlen(C4CFN_SoundSubgroups) + 1);
208  // load from child group
209  C4Group subgroup;
210  if (subgroup.OpenAsChild(&hGroup, szFilename, false, false))
211  {
212  iNum += LoadEffects(subgroup, sub_namespace.getData(), false);
213  }
214  }
215  }
216  return iNum;
217 }
218 
219 int32_t C4SoundSystem::RemoveEffect(const char *szFilename)
220 {
221  int32_t iResult=0;
222  C4SoundEffect *pNext,*pPrev=nullptr;
223  for (C4SoundEffect *pSfx=FirstSound; pSfx; pSfx=pNext)
224  {
225  pNext=pSfx->Next;
226  if (WildcardMatch(szFilename,pSfx->Name))
227  {
228  delete pSfx;
229  if (pPrev) pPrev->Next=pNext;
230  else FirstSound=pNext;
231  iResult++;
232  }
233  else
234  pPrev=pSfx;
235  }
236  return iResult;
237 }
238 
240 {
241  for (C4SoundEffect *pEff=FirstSound; pEff; pEff=pEff->Next)
242  pEff->ClearPointers(pObj);
243 }
244 
246 {
247  // Return by searching through effect linked list.
248  for (C4SoundEffect *pSfx = FirstSound; pSfx; pSfx = pSfx->Next)
249  if (pSfx->FirstInst) return pSfx->FirstInst;
250  return nullptr;
251 }
252 
254 {
255  // Return by searching through instance linked list and parent linked list of effects
256  assert(prev && prev->pEffect);
257  if (prev->pNext) return prev->pNext;
258  for (C4SoundEffect *pSfx = prev->pEffect->Next; pSfx; pSfx = pSfx->Next)
259  if (pSfx->FirstInst) return pSfx->FirstInst;
260  return nullptr;
261 }
262 
263 C4SoundInstance *StartSoundEffect(const char *szSndName, bool fLoop, int32_t iVolume, C4Object *pObj, int32_t iCustomFalloffDistance, int32_t iPitch, C4SoundModifier *modifier)
264 {
265  // Sound check
266  if (!Config.Sound.RXSound) return nullptr;
267  // Start new
268  return Application.SoundSystem.NewEffect(szSndName, fLoop, iVolume, pObj, iCustomFalloffDistance, iPitch, modifier);
269 }
270 
271 C4SoundInstance *StartSoundEffectAt(const char *szSndName, int32_t iX, int32_t iY, int32_t iVolume, int32_t iCustomFallofDistance, int32_t iPitch, C4SoundModifier *modifier)
272 {
273  // Sound check
274  if (!Config.Sound.RXSound) return nullptr;
275  // Create
276  C4SoundInstance *pInst = StartSoundEffect(szSndName, false, iVolume, nullptr, iCustomFallofDistance, iPitch, modifier);
277  // Set volume by position
278  if (pInst) pInst->SetVolumeByPos(iX, iY, iVolume);
279  // Return
280  return pInst;
281 }
282 
283 C4SoundInstance *GetSoundInstance(const char *szSndName, C4Object *pObj)
284 {
285  return Application.SoundSystem.FindInstance(szSndName, pObj);
286 }
287 
288 void StopSoundEffect(const char *szSndName, C4Object *pObj)
289 {
290  // Find instance
291  C4SoundInstance *pInst = Application.SoundSystem.FindInstance(szSndName, pObj);
292  if (!pInst) return;
293  // Stop
294  pInst->Stop();
295 }
296 void SoundLevel(const char *szSndName, C4Object *pObj, int32_t iLevel)
297 {
298  // Sound level zero? Stop
299  if (!iLevel) { StopSoundEffect(szSndName, pObj); return; }
300  // Find or create instance
301  C4SoundInstance *pInst = Application.SoundSystem.FindInstance(szSndName, pObj);
302  if (!pInst) pInst = StartSoundEffect(szSndName, true, iLevel, pObj);
303  if (!pInst) return;
304  // Set volume
305  pInst->SetVolume(iLevel);
306  pInst->Execute();
307 }
308 
309 void SoundPan(const char *szSndName, C4Object *pObj, int32_t iPan)
310 {
311  // Find instance
312  C4SoundInstance *pInst = Application.SoundSystem.FindInstance(szSndName, pObj);
313  if (!pInst) return;
314  // Set pan
315  pInst->SetPan(iPan);
316  pInst->Execute();
317 }
318 
319 void SoundPitch(const char *szSndName, C4Object *pObj, int32_t iPitch)
320 {
321  // Find instance
322  C4SoundInstance *pInst = Application.SoundSystem.FindInstance(szSndName, pObj);
323  if (!pInst) return;
324  // Set pitch
325  pInst->SetPitch(iPitch);
326  pInst->Execute();
327 }
328 
329 void SoundUpdate(C4SoundInstance *pInst, int32_t iLevel, int32_t iPitch)
330 {
331  // Set sound data
332  pInst->SetVolume(iLevel);
333  pInst->SetPitch(iPitch);
334  // Ensure it's reflected in audio engine
335  pInst->Execute();
336 }
#define C4CFN_SoundFiles
Definition: C4Components.h:172
#define C4CFN_Sound
Definition: C4Components.h:26
#define C4CFN_SoundSubgroups
Definition: C4Components.h:27
C4Config Config
Definition: C4Config.cpp:930
C4Application Application
Definition: C4Globals.cpp:44
int iResult
Definition: C4GroupMain.cpp:40
bool DebugLogF(const char *strMessage ...)
Definition: C4Log.cpp:290
uint32_t UnsyncedRandom()
Definition: C4Random.cpp:58
C4Reloc Reloc
Definition: C4Reloc.cpp:21
void SoundUpdate(C4SoundInstance *pInst, int32_t iLevel, int32_t iPitch)
void StopSoundEffect(const char *szSndName, C4Object *pObj)
C4SoundInstance * GetSoundInstance(const char *szSndName, C4Object *pObj)
void SoundPitch(const char *szSndName, C4Object *pObj, int32_t iPitch)
C4SoundInstance * StartSoundEffect(const char *szSndName, bool fLoop, int32_t iVolume, C4Object *pObj, int32_t iCustomFalloffDistance, int32_t iPitch, C4SoundModifier *modifier)
void SoundPan(const char *szSndName, C4Object *pObj, int32_t iPan)
void SoundLevel(const char *szSndName, C4Object *pObj, int32_t iLevel)
C4SoundInstance * StartSoundEffectAt(const char *szSndName, int32_t iX, int32_t iY, int32_t iVolume, int32_t iCustomFallofDistance, int32_t iPitch, C4SoundModifier *modifier)
const int32_t C4MaxSoundInstances
Definition: C4SoundSystem.h:28
const int32_t C4MaxSoundName
Definition: C4SoundSystem.h:27
#define _MAX_FNAME
#define _MAX_FNAME_LEN
bool SCopySegment(const char *szString, int iSegment, char *sTarget, char cSeparator, int iMaxL, bool fSkipWhitespace)
Definition: Standard.cpp:279
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:152
bool WildcardMatch(const char *szWildcard, const char *szString)
Definition: StdFile.cpp:396
void DefaultExtension(char *szFilename, const char *szExtension)
Definition: StdFile.cpp:271
bool IsWildcardString(const char *szString)
Definition: StdFile.cpp:363
C4MusicSystem MusicSystem
Definition: C4Application.h:41
C4SoundSystem SoundSystem
Definition: C4Application.h:42
C4ConfigSound Sound
Definition: C4Config.h:258
int32_t RXSound
Definition: C4Config.h:126
bool FindNextEntry(const char *wildcard, StdStrBuf *filename=nullptr, size_t *size=nullptr, bool start_at_filename=false)
Definition: C4Group.cpp:2217
bool OpenAsChild(C4Group *mother, const char *entry_name, bool is_exclusive=false, bool do_create=false)
Definition: C4Group.cpp:1952
bool IsOpen() const
Definition: C4Group.cpp:2373
void ResetSearch(bool reload_contents=false)
Definition: C4Group.cpp:1316
bool Close()
Definition: C4Group.cpp:971
bool FindEntry(const char *wildcard, StdStrBuf *filename=nullptr, size_t *size=nullptr)
Definition: C4Group.cpp:2211
void SelectContext()
bool InitializeMOD()
bool Open(C4Group &group, const char *filename) const
Definition: C4Reloc.cpp:156
bool Load(const char *szFileName, C4Group &hGroup, const char *namespace_prefix)
C4SoundInstance * New(bool fLoop=false, int32_t iVolume=100, C4Object *pObj=nullptr, int32_t iCustomFalloffDistance=0, int32_t iPitch=0, C4SoundModifier *modifier=nullptr)
char Name[C4MaxSoundName+1]
C4SoundEffect * Next
void SetVolume(int32_t inVolume)
void SetVolumeByPos(int32_t x, int32_t y, int32_t relative_volume=100)
void SetPitch(int32_t inPitch)
C4SoundInstance * pNext
void SetPan(int32_t inPan)
C4SoundEffect * pEffect
int32_t RemoveEffect(const char *szFilename)
C4Group SoundFile
Definition: C4SoundSystem.h:56
C4SoundEffect * FirstSound
Definition: C4SoundSystem.h:57
C4SoundInstance * FindInstance(const char *szSound, C4Object *pObj)
C4SoundInstance * GetFirstInstance() const
C4SoundInstance * GetNextInstance(C4SoundInstance *prev) const
C4SoundModifierList Modifiers
Definition: C4SoundSystem.h:54
void ClearPointers(C4Object *pObj)
int32_t LoadEffects(C4Group &hGroup, const char *namespace_prefix, bool group_is_root)
C4SoundEffect * GetEffect(const char *szSound)
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)
const char * getData() const
Definition: StdBuf.h:442
void Copy()
Definition: StdBuf.h:467
void Append(const char *pnData, size_t iChars)
Definition: StdBuf.h:519