Advanced Strategic Command
sound.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * *
3  * This program is free software; you can redistribute it and/or modify *
4  * it under the terms of the GNU General Public License as published by *
5  * the Free Software Foundation; either version 2 of the License, or *
6  * (at your option) any later version. *
7  * *
8  ***************************************************************************/
9 
10 
11 #include <boost/regex.hpp>
12 
13 #include <cstring>
14 #include <stdlib.h>
15 
16 
17 #include <SDL.h>
18 #include <SDL_mixer.h>
19 #include <SDL_sound.h>
20 
21 
22 
23 
24 #include "../global.h"
25 
26 #include "sound.h"
27 
28 #include "../basestrm.h"
29 #include "../music.h"
30 #include "../sgstream.h"
31 
32 
33 
34 
37 const int WAIT_SLEEP_MSEC = 50;
38 
39 
40 
42  public:
43  Mix_Music *musicBuf;
45 
46  Sound* channel[MIX_CHANNELS];
47 
49 
51  for ( int i = 0; i < MIX_CHANNELS; ++i )
52  channel[i] = NULL;
53 
54  };
55 };
56 
57 SoundSystem* SoundSystem::instance = NULL;
58 
59 SoundSystem :: SoundSystem ( bool muteEffects, bool muteMusic, bool _off )
60  : sdl_initialized(false), mix_initialized( false )
61 {
62  internalData = new SoundSystem_InternalData;
63 
64  musicState = uninitialized;
65 
66  this->effectsMuted = muteEffects;
67  this->off = _off;
68 
69  if ( instance )
70  fatalError ( "Only one instance of SoundSystem possible !");
71 
72  instance = this;
73 
74  if( off )
75  return;
76 
77  displayLogMessage(0,"Initializing sound device. If this hangs, run ASC without sound (asc -q)\n");
78  displayLogMessage(0,"Step 1/3 (SDL_Init)...");
79 
80  if ( SDL_Init ( SDL_INIT_AUDIO ) < 0 ) {
81  displayLogMessage(0,"failed, disabling sound\n");
82  warningMessage("Couldn't initialize SDL audio interface !");
83  off = true;
84  sdl_initialized = false;
85  return;
86  }
87 
88  displayLogMessage(0,"ok\nStep 2/3 (SDL_Sound Sound_Init)...");
89  if (!Sound_Init()) {
90  displayLogMessage(0,"failed, disabling sound\n");
91  warningMessage("Couldn't initialize SDL_sound !");
92  off = true;
93  sdl_initialized = false;
94  return;
95  }
96 
97  sdl_initialized = true;
98 
99  int audio_rate = MIX_DEFAULT_FREQUENCY;
100  Uint16 audio_format = MIX_DEFAULT_FORMAT;
101  int audio_channels = 2;
102 
103  displayLogMessage(0,"ok\nStep 3/3 (SDL_Mixer Mix_OpenAudio)...");
104  if ( Mix_OpenAudio ( audio_rate, audio_format, audio_channels, 2048 ) < 0 ) {
105  displayLogMessage(0,"failed, disabling sound\n");
106  mix_initialized = false;
107  warningMessage("Couldn't initialize SDL_mixer !");
108  off = true;
109  return;
110  } else {
111  mix_initialized = true;
112  if ( muteMusic )
113  musicState = init_paused;
114  else
115  musicState = init_ready;
116 
117  Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels);
118  displayLogMessage ( 5, "Opened audio at %d Hz %d bit %s\n", audio_rate, (audio_format&0xFF), (audio_channels > 1) ? "stereo" : "mono");
119  Mix_HookMusicFinished ( trackFinished );
120 
121  Mix_ChannelFinished( channelFinishedCallback );
122 
123  displayLogMessage(0,"ok\nSound system successfully initialized!\n");
124  }
125 }
126 
127 
128 void SoundSystem::setEffectsMute ( bool mute )
129 {
130  if ( off )
131  return;
132 
133  if ( mute != this->effectsMuted ) {
134  if ( mute ) {
135  for ( int i = 0; i < MIX_CHANNELS; i++ )
136  if ( Mix_Playing(i) )
137  Mix_HaltChannel( i );
138 
139  }
140  this->effectsMuted = mute;
141  }
142 }
143 
144 
145 void SoundSystem :: trackFinished( void )
146 {
147  getInstance()->nextTrack();
148 }
149 
151 {
152 
153  static ASCString boolean[2] = {"false", "true"};
154 
155  ASCString text = "Sound System Status: ";
156 
157  if( off )
158  text += "off\n";
159  else
160  text += "on\n";
161 
162  text += "Effects Muted: " + boolean[effectsMuted] + "\n";
163  text += "SDL Initialized: " + boolean[sdl_initialized] + "\n";
164  text += "Mixer Initialized: " + boolean[mix_initialized] + "\n";
165  text += "Music Volume: " + ASCString::toString( musicVolume ) + "\n";
166  text += "Effects Volume: " + ASCString::toString( effectVolume ) + "\n";
167 
168  text += "\n";
169  text += "Last message from mixer was:\n";
170  text += internalData->lastMusicMessage + "\n\n";
171 
172  if ( internalData->currentPlaylist )
173  text += internalData->currentPlaylist->getDiagnosticText();
174  else
175  text += "No play list active!";
176 
177  SDL_version mixCompiled;
178  MIX_VERSION( &mixCompiled );
179  text += "SDL_mixer compiled version: " + ASCString::toString( (int) mixCompiled.major ) + "." + ASCString::toString( (int) mixCompiled.minor ) + "." + ASCString::toString( (int) mixCompiled.patch ) + "\n";
180 
181  const SDL_version *mixVersion = Mix_Linked_Version();
182  text += "SDL_mixer linked version: " + ASCString::toString( (int) mixVersion->major ) + "." + ASCString::toString( (int) mixVersion->minor ) + "." + ASCString::toString( (int) mixVersion->patch )+ "\n";
183 
184 
185  return text;
186 }
187 
188 
189 void SoundSystem :: nextTrack( void )
190 {
191  if ( off || musicState==paused || musicState==init_paused)
192  return;
193 
194  if ( internalData->musicBuf ) {
195  Mix_FreeMusic( internalData->musicBuf );
196  internalData->musicBuf = NULL;
197  }
198 
199  if ( internalData->currentPlaylist ) {
200  ASCString filename = internalData->currentPlaylist->getNextTrack();
201 
202  if ( !filename.empty() ) {
203  musicState = playing;
204  internalData->musicBuf = Mix_LoadMUS( filename.c_str() );
205 
206  if ( !internalData->musicBuf ) {
207  internalData->lastMusicMessage = "Could not load music file " + filename + " ; SDL reports error " + SDL_GetError();
208  displayLogMessage ( 1, internalData->lastMusicMessage + "\n" );
209  SDL_ClearError();
210  } else {
211  int chan = Mix_PlayMusic ( internalData->musicBuf, 1 );
212  Mix_VolumeMusic ( musicVolume );
213  displayLogMessage ( 4, "Playing music on channel %d \n", chan );
214  }
215  }
216  } else
217  displayLogMessage ( 1, "No play list available \n" );
218 }
219 
221 {
222  internalData->currentPlaylist = playlist;
223  nextTrack();
224 }
225 
226 
228 {
229  if ( off )
230  return;
231 
232  if ( musicState == playing ) {
233  Mix_PauseMusic ();
234  musicState = paused;
235  }
236 }
237 
239 {
240  if ( off )
241  return;
242 
243  if ( musicState == init_ready || musicState == init_paused ) {
244  if (musicState == init_paused)
245  musicState = init_ready;
246  nextTrack();
247  return;
248  }
249 
250  if ( musicState == paused ) {
251  Mix_ResumeMusic ();
252  musicState = playing;
253  }
254 }
255 
257 {
258  if ( musicState == playing )
259  pauseMusic();
260  else
261  resumeMusic();
262 }
263 
265 {
266  musicVolume = volume * 128 / 100;
267 
268  if ( off )
269  return;
270 
271  Mix_VolumeMusic ( musicVolume );
272 }
273 
275 {
276  effectVolume = volume * 128 / 100;
277 
278  if ( off )
279  return;
280 
281  Mix_Volume ( -1, effectVolume );
282 }
283 
284 
286 {
287  if ( !off ) {
288  Mix_HaltMusic();
289 
290  if ( internalData->musicBuf ) {
291  Mix_FreeMusic( internalData->musicBuf );
292  internalData-> musicBuf = NULL;
293  }
294  }
295 
296  if( mix_initialized )
297  Mix_CloseAudio();
298 
299  if( sdl_initialized )
300  SDL_CloseAudio();
301 
302  delete internalData;
303 
304  instance = NULL;
305 }
306 
307 
308 
309 void SoundSystem::channelFinishedCallback( int channelnum )
310 {
311  if ( getInstance()->internalData->channel[channelnum] )
312  getInstance()->internalData->channel[channelnum]->finishedSignal( channelnum );
313 }
314 
315 
316 
317 pair<Sound_Sample*, Mix_Chunk*> loadWave ( const ASCString& name )
318 {
319  Mix_Chunk* chunk = NULL;
320  Sound_Sample* sample = NULL;
321 
322  if ( !exist ( name )) {
323  errorMessage ( " can't open sound file: " + name );
324  return make_pair(sample,chunk);
325  }
326 
327  tnfilestream stream ( name, tnstream::reading );
328 
329  ASCString ext;
330  boost::smatch what;
331  static boost::regex extension( ".*\\.([^\\.]+)");
332  if( boost::regex_match( name, what, extension))
333  ext.assign( what[1].first, what[1].second );
334 
335 
336  int frequency;
337  Uint16 format;
338  int channels;
339  Mix_QuerySpec(&frequency, &format, &channels);
340  Sound_AudioInfo ai;
341  ai.format = format;
342  ai.channels = channels;
343  ai.rate = frequency;
344 
345  try {
346  sample = Sound_NewSample ( SDL_RWFromStream ( &stream ), ext.c_str(), &ai, 1<<16);
347  if ( sample )
348  displayLogMessage ( 10, " SoundSystem::loadWave - sound " + name + " loaded successfully\n");
349  else {
350  displayLogMessage (0, ASCString(" SoundSystem::loadWave" ) + name + " failed. Message: " + Sound_GetError() + "\n");
351  return make_pair(sample,chunk);
352  }
353  }
354  catch ( tfileerror err ) {
355  warningMessage(" Error loading sound file " + name );
356  sample = NULL;
357  return make_pair(sample,chunk);
358  }
359 
360  Uint32 size = Sound_DecodeAll ( sample );
361  if ( sample->buffer_size <= 0 )
362  warningMessage( "decoding of file " + name + " failed");
363 
364  chunk = new Mix_Chunk;
365  chunk->allocated = size;
366  chunk->abuf = (Uint8*) sample->buffer;
367  chunk->alen = size;
368  chunk->volume = MIX_MAX_VOLUME;
369 
370  return make_pair(sample,chunk);
371 }
372 
373 
375  public:
377  Mix_Chunk *mainwave;
378  Mix_Chunk *startwave;
379  Sound_Sample *mainsample;
380  Sound_Sample *startsample;
381  Sound_InternalData() : mainwave(NULL), startwave(NULL), mainsample(NULL), startsample(NULL) {};
382 };
383 
384 Sound::Sound( const ASCString& filename, int _fadeIn ) : name ( filename ), fadeIn ( _fadeIn ), waitingForMainWave(false)
385 {
386  internalData = new Sound_InternalData;
387 
388  if ( !SoundSystem::instance )
389  fatalError ( "Sound::Sound failed, because there is no SoundSystem initialized");
390 
391  pair<Sound_Sample*, Mix_Chunk*> res = loadWave( filename );
392  internalData->mainsample = res.first;
393  internalData->mainwave = res.second;
394 }
395 
396 Sound::Sound( const ASCString& startSoundFilename, const ASCString& continuousSoundFilename, int _fadeIn ) : name ( startSoundFilename ), fadeIn ( _fadeIn ), waitingForMainWave(false)
397 {
398  internalData = new Sound_InternalData;
399 
400  if ( !SoundSystem::instance )
401  fatalError ( "Sound::Sound failed, because there is no SoundSystem initialized");
402 
403  pair<Sound_Sample*, Mix_Chunk*> res = loadWave( startSoundFilename );
404  internalData->startsample = res.first;
405  internalData->startwave = res.second;
406 
407  res = loadWave( continuousSoundFilename );
408  internalData->mainsample = res.first;
409  internalData->mainwave = res.second;
410 }
411 
412 
413 
414 
415 int Sound::startPlaying( bool loop )
416 {
417  int channel;
418 
419  int loopcontrol;
420  if ( loop )
421  loopcontrol = -1;
422  else
423  loopcontrol = 0;
424 
425  Mix_Chunk* wave = internalData->startwave;
426  if ( !wave )
427  wave = internalData->mainwave;
428  else
429  loopcontrol = 0;
430 
431  if ( fadeIn )
432  channel = Mix_FadeInChannel ( -1, wave, loopcontrol, fadeIn );
433  else {
434  channel = Mix_PlayChannel ( -1, wave, loopcontrol );
435  Mix_Volume ( channel, SoundSystem::instance->getEffectVolume() );
436  }
437  if ( internalData->startwave ) {
438  waitingForMainWave = true;
439  }
440 
441  return channel;
442 }
443 
444 void Sound::finishedSignal( int channelnum )
445 {
446  if ( waitingForMainWave ) {
447  Mix_PlayChannel ( channelnum, internalData->mainwave, -1 );
448  Mix_Volume ( channelnum, SoundSystem::instance->getEffectVolume() );
449  waitingForMainWave = false;
450  }
451 }
452 
453 void Sound::play(void)
454 {
455  if( SoundSystem::instance->areEffectsMuted() || !internalData->mainwave)
456  return;
457 
458  int channel = startPlaying( false );
459  SoundSystem::instance->internalData->channel[ channel ] = this;
460 }
461 
463 {
464  if( SoundSystem::instance->areEffectsMuted() || !internalData->mainwave)
465  return;
466 
467  int channel = startPlaying( true );
468  SoundSystem::instance->internalData->channel[ channel ] = this;
469 }
470 
472 {
473  if ( SoundSystem::instance )
474  for ( int i = 0; i < MIX_CHANNELS; i++ )
475  if ( SoundSystem::instance->internalData->channel[ i ] == this && Mix_Playing(i) )
476  Mix_HaltChannel( i );
477 }
478 
479 
480 void Sound::playWait(void)
481 {
482  if( SoundSystem::instance->areEffectsMuted() || !internalData->mainwave)
483  return;
484 
485  if ( internalData->startwave ) {
486  int channel = Mix_PlayChannel ( -1, internalData->startwave, 0 );
487  SoundSystem::instance->internalData->channel[ channel ] = this;
488  do {
489  SDL_Delay(WAIT_SLEEP_MSEC);
490  } while( SoundSystem::instance->internalData->channel[ channel ] == this && Mix_Playing(channel) );
491  }
492 
493  int channel = Mix_PlayChannel ( -1, internalData->mainwave, 0 );
494  SoundSystem::instance->internalData->channel[ channel ] = this;
495 
496  do {
497  SDL_Delay(WAIT_SLEEP_MSEC);
498  } while( SoundSystem::instance->internalData->channel[ channel ] == this && Mix_Playing(channel) );
499 }
500 
501 void Sound :: fadeOut ( int ms )
502 {
503  for ( int i = 0; i < MIX_CHANNELS; i++ )
504  if ( SoundSystem::instance->internalData->channel[ i ] == this && Mix_Playing(i) )
505  Mix_FadeOutChannel( i, ms );
506 }
507 
508 
509 
511 {
512  stop();
513 
514  if ( internalData->mainwave )
515  delete internalData->mainwave ;
516 
517  if ( internalData->startwave )
518  delete internalData->startwave;
519 
520  if ( internalData->mainsample )
521  Sound_FreeSample ( internalData->mainsample );
522 
523  if ( internalData->startsample )
524  Sound_FreeSample ( internalData->startsample );
525 
526  delete internalData;
527 }
528 
529 
ASCString getDiagnosticText()
Definition: music.cpp:117
void setEffectsMute(bool mute)
Turns the sound on and off.
Definition: sound.cpp:128
void play(void)
Definition: sound.cpp:453
void playMusic(MusicPlayList *playlist)
plays the pieces of music which are referenced in the playlist
Definition: sound.cpp:220
Definition: sound.h:20
void pauseMusic()
Pauses the music that is currently being played.
Definition: sound.cpp:227
void stop()
Definition: sound.cpp:471
A List containing several tracks of music.
Definition: music.h:19
void warningMessage(const ASCString &str)
Sound_Sample * startsample
Definition: sound.cpp:380
~SoundSystem()
Definition: sound.cpp:285
void setMusicVolume(int Volume)
Sets the music volume. Range is 0 .. 100.
Definition: sound.cpp:264
ASCString getDiagnosticText()
Definition: sound.cpp:150
The ASCString class provides an abstract way to manipulate strings.
Definition: ascstring.h:14
pair< Sound_Sample *, Mix_Chunk * > loadWave(const ASCString &name)
Definition: sound.cpp:317
void resumeMusic()
resumes the music
Definition: sound.cpp:238
Sound * channel[MIX_CHANNELS]
Definition: sound.cpp:46
Sound_Sample * mainsample
Definition: sound.cpp:379
SoundSystem(bool muteEffects, bool muteMusic, bool off)
Sets up ASC's sound system.
Definition: sound.cpp:59
static ASCString toString(int i)
converts the parameter to a String
Definition: ascstring.cpp:193
static SoundSystem * getInstance()
Definition: sound.h:127
void displayLogMessage(int msgVerbosity, const char *message,...)
void playWait(void)
Definition: sound.cpp:480
MusicPlayList * currentPlaylist
Definition: sound.cpp:44
Mix_Music * musicBuf
Definition: sound.cpp:43
Sound(const ASCString &filename, int fadeIn=0)
Create a Sound from the .wav file specified by filename.
Definition: sound.cpp:384
const ASCString & getNextTrack()
returns the filename of the next music track
Definition: music.cpp:79
const int WAIT_SLEEP_MSEC
How long should this process sleep while waiting for a sound to play.
Definition: sound.cpp:37
void errorMessage(const ASCString &string)
Mix_Chunk * startwave
Definition: sound.cpp:378
ASCString lastMusicMessage
Definition: sound.cpp:48
~Sound(void)
Definition: sound.cpp:510
Mix_Chunk * mainwave
the actual wave data
Definition: sound.cpp:377
void resumePauseMusic()
resumes or resumes the music, depending whether is music is paused or playing
Definition: sound.cpp:256
void playLoop()
Definition: sound.cpp:462
bool exist(const ASCString &s)
does a file s exist (wildcards allowed)
Definition: basestrm.cpp:2444
SDL_RWops * SDL_RWFromStream(tnstream *stream)
Definition: basestrm.cpp:693
void fadeOut(int ms)
Definition: sound.cpp:501
void fatalError(const ASCString &string)
void setEffectVolume(int Volume)
Sets the sound effect volume. Range is 0 .. 100.
Definition: sound.cpp:274