00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include <boost/regex.hpp>
00012
00013 #include <cstring>
00014 #include <stdlib.h>
00015
00016
00017 #include <SDL.h>
00018 #include <SDL_mixer.h>
00019 #include <SDL_sound.h>
00020
00021
00022
00023
00024 #include "../global.h"
00025
00026 #include "sound.h"
00027
00028 #include "../basestrm.h"
00029 #include "../music.h"
00030 #include "../sgstream.h"
00031
00032
00033
00034
00037 const int WAIT_SLEEP_MSEC = 50;
00038
00039
00040
00041 class SoundSystem_InternalData {
00042 public:
00043 Mix_Music *musicBuf;
00044 MusicPlayList* currentPlaylist;
00045
00046 Sound* channel[MIX_CHANNELS];
00047
00048 ASCString lastMusicMessage;
00049
00050 SoundSystem_InternalData() : musicBuf(NULL), currentPlaylist(NULL) {
00051 for ( int i = 0; i < MIX_CHANNELS; ++i )
00052 channel[i] = NULL;
00053
00054 };
00055 };
00056
00057 SoundSystem* SoundSystem::instance = NULL;
00058
00059 SoundSystem :: SoundSystem ( bool muteEffects, bool muteMusic, bool _off )
00060 : sdl_initialized(false), mix_initialized( false )
00061 {
00062 internalData = new SoundSystem_InternalData;
00063
00064 musicState = uninitialized;
00065
00066 this->effectsMuted = muteEffects;
00067 this->off = _off;
00068
00069 if ( instance )
00070 fatalError ( "Only one instance of SoundSystem possible !");
00071
00072 instance = this;
00073
00074 if( off )
00075 return;
00076
00077 displayLogMessage(0,"Initializing sound device. If this hangs, run ASC without sound (asc -q)\n");
00078 displayLogMessage(0,"Step 1/3 (SDL_Init)...");
00079
00080 if ( SDL_Init ( SDL_INIT_AUDIO ) < 0 ) {
00081 displayLogMessage(0,"failed, disabling sound\n");
00082 warningMessage("Couldn't initialize SDL audio interface !");
00083 off = true;
00084 sdl_initialized = false;
00085 return;
00086 }
00087
00088 displayLogMessage(0,"ok\nStep 2/3 (SDL_Sound Sound_Init)...");
00089 if (!Sound_Init()) {
00090 displayLogMessage(0,"failed, disabling sound\n");
00091 warningMessage("Couldn't initialize SDL_sound !");
00092 off = true;
00093 sdl_initialized = false;
00094 return;
00095 }
00096
00097 sdl_initialized = true;
00098
00099 int audio_rate = MIX_DEFAULT_FREQUENCY;
00100 Uint16 audio_format = MIX_DEFAULT_FORMAT;
00101 int audio_channels = 2;
00102
00103 displayLogMessage(0,"ok\nStep 3/3 (SDL_Mixer Mix_OpenAudio)...");
00104 if ( Mix_OpenAudio ( audio_rate, audio_format, audio_channels, 2048 ) < 0 ) {
00105 displayLogMessage(0,"failed, disabling sound\n");
00106 mix_initialized = false;
00107 warningMessage("Couldn't initialize SDL_mixer !");
00108 off = true;
00109 return;
00110 } else {
00111 mix_initialized = true;
00112 if ( muteMusic )
00113 musicState = init_paused;
00114 else
00115 musicState = init_ready;
00116
00117 Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels);
00118 displayLogMessage ( 5, "Opened audio at %d Hz %d bit %s\n", audio_rate, (audio_format&0xFF), (audio_channels > 1) ? "stereo" : "mono");
00119 Mix_HookMusicFinished ( trackFinished );
00120
00121 Mix_ChannelFinished( channelFinishedCallback );
00122
00123 displayLogMessage(0,"ok\nSound system successfully initialized!\n");
00124 }
00125 }
00126
00127
00128 void SoundSystem::setEffectsMute ( bool mute )
00129 {
00130 if ( off )
00131 return;
00132
00133 if ( mute != this->effectsMuted ) {
00134 if ( mute ) {
00135 for ( int i = 0; i < MIX_CHANNELS; i++ )
00136 if ( Mix_Playing(i) )
00137 Mix_HaltChannel( i );
00138
00139 }
00140 this->effectsMuted = mute;
00141 }
00142 }
00143
00144
00145 void SoundSystem :: trackFinished( void )
00146 {
00147 getInstance()->nextTrack();
00148 }
00149
00150 ASCString SoundSystem :: getDiagnosticText()
00151 {
00152
00153 static ASCString boolean[2] = {"false", "true"};
00154
00155 ASCString text = "Sound System Status: ";
00156
00157 if( off )
00158 text += "off\n";
00159 else
00160 text += "on\n";
00161
00162 text += "Effects Muted: " + boolean[effectsMuted] + "\n";
00163 text += "SDL Initialized: " + boolean[sdl_initialized] + "\n";
00164 text += "Mixer Initialized: " + boolean[mix_initialized] + "\n";
00165 text += "Music Volume: " + ASCString::toString( musicVolume ) + "\n";
00166 text += "Effects Volume: " + ASCString::toString( effectVolume ) + "\n";
00167
00168 text += "\n";
00169 text += "Last message from mixer was:\n";
00170 text += internalData->lastMusicMessage + "\n\n";
00171
00172 if ( internalData->currentPlaylist )
00173 text += internalData->currentPlaylist->getDiagnosticText();
00174 else
00175 text += "No play list active!";
00176
00177 return text;
00178 }
00179
00180
00181 void SoundSystem :: nextTrack( void )
00182 {
00183 if ( off || musicState==paused || musicState==init_paused)
00184 return;
00185
00186 if ( internalData->musicBuf ) {
00187 Mix_FreeMusic( internalData->musicBuf );
00188 internalData->musicBuf = NULL;
00189 }
00190
00191 if ( internalData->currentPlaylist ) {
00192 ASCString filename = internalData->currentPlaylist->getNextTrack();
00193
00194 if ( !filename.empty() ) {
00195 musicState = playing;
00196 internalData->musicBuf = Mix_LoadMUS( filename.c_str() );
00197
00198 if ( !internalData->musicBuf ) {
00199 internalData->lastMusicMessage = "Could not load music file " + filename + " ; SDL reports error " + SDL_GetError();
00200 displayLogMessage ( 1, internalData->lastMusicMessage + "\n" );
00201 SDL_ClearError();
00202 } else {
00203 int chan = Mix_PlayMusic ( internalData->musicBuf, 1 );
00204 Mix_VolumeMusic ( musicVolume );
00205 displayLogMessage ( 4, "Playing music on channel %d \n", chan );
00206 }
00207 }
00208 } else
00209 displayLogMessage ( 1, "No play list available \n" );
00210 }
00211
00212 void SoundSystem :: playMusic ( MusicPlayList* playlist )
00213 {
00214 internalData->currentPlaylist = playlist;
00215 nextTrack();
00216 }
00217
00218
00219 void SoundSystem :: pauseMusic()
00220 {
00221 if ( off )
00222 return;
00223
00224 if ( musicState == playing ) {
00225 Mix_PauseMusic ();
00226 musicState = paused;
00227 }
00228 }
00229
00230 void SoundSystem :: resumeMusic()
00231 {
00232 if ( off )
00233 return;
00234
00235 if ( musicState == init_ready || musicState == init_paused ) {
00236 if (musicState == init_paused)
00237 musicState = init_ready;
00238 nextTrack();
00239 return;
00240 }
00241
00242 if ( musicState == paused ) {
00243 Mix_ResumeMusic ();
00244 musicState = playing;
00245 }
00246 }
00247
00248 void SoundSystem :: resumePauseMusic()
00249 {
00250 if ( musicState == playing )
00251 pauseMusic();
00252 else
00253 resumeMusic();
00254 }
00255
00256 void SoundSystem :: setMusicVolume( int volume )
00257 {
00258 musicVolume = volume * 128 / 100;
00259
00260 if ( off )
00261 return;
00262
00263 Mix_VolumeMusic ( musicVolume );
00264 }
00265
00266 void SoundSystem :: setEffectVolume( int volume )
00267 {
00268 effectVolume = volume * 128 / 100;
00269
00270 if ( off )
00271 return;
00272
00273 Mix_Volume ( -1, effectVolume );
00274 }
00275
00276
00277 SoundSystem::~SoundSystem()
00278 {
00279 if ( !off ) {
00280 Mix_HaltMusic();
00281
00282 if ( internalData->musicBuf ) {
00283 Mix_FreeMusic( internalData->musicBuf );
00284 internalData-> musicBuf = NULL;
00285 }
00286 }
00287
00288 if( mix_initialized )
00289 Mix_CloseAudio();
00290
00291 if( sdl_initialized )
00292 SDL_CloseAudio();
00293
00294 delete internalData;
00295
00296 instance = NULL;
00297 }
00298
00299
00300
00301 void SoundSystem::channelFinishedCallback( int channelnum )
00302 {
00303 if ( getInstance()->internalData->channel[channelnum] )
00304 getInstance()->internalData->channel[channelnum]->finishedSignal( channelnum );
00305 }
00306
00307
00308
00309 pair<Sound_Sample*, Mix_Chunk*> loadWave ( const ASCString& name )
00310 {
00311 Mix_Chunk* chunk = NULL;
00312 Sound_Sample* sample = NULL;
00313
00314 if ( !exist ( name )) {
00315 errorMessage ( " can't open sound file: " + name );
00316 return make_pair(sample,chunk);
00317 }
00318
00319 tnfilestream stream ( name, tnstream::reading );
00320
00321 ASCString ext;
00322 boost::smatch what;
00323 static boost::regex extension( ".*\\.([^\\.]+)");
00324 if( boost::regex_match( name, what, extension))
00325 ext.assign( what[1].first, what[1].second );
00326
00327
00328 int frequency;
00329 Uint16 format;
00330 int channels;
00331 Mix_QuerySpec(&frequency, &format, &channels);
00332 Sound_AudioInfo ai;
00333 ai.format = format;
00334 ai.channels = channels;
00335 ai.rate = frequency;
00336
00337 try {
00338 sample = Sound_NewSample ( SDL_RWFromStream ( &stream ), ext.c_str(), &ai, 1<<16);
00339 if ( sample )
00340 displayLogMessage ( 10, " SoundSystem::loadWave - sound " + name + " loaded successfully\n");
00341 else {
00342 displayLogMessage (0, ASCString(" SoundSystem::loadWave" ) + name + " failed. Message: " + Sound_GetError() + "\n");
00343 return make_pair(sample,chunk);
00344 }
00345 }
00346 catch ( tfileerror err ) {
00347 warningMessage(" Error loading sound file " + name );
00348 sample = NULL;
00349 return make_pair(sample,chunk);
00350 }
00351
00352 Uint32 size = Sound_DecodeAll ( sample );
00353 if ( sample->buffer_size <= 0 )
00354 warningMessage( "decoding of file " + name + " failed");
00355
00356 chunk = new Mix_Chunk;
00357 chunk->allocated = size;
00358 chunk->abuf = (Uint8*) sample->buffer;
00359 chunk->alen = size;
00360 chunk->volume = MIX_MAX_VOLUME;
00361
00362 return make_pair(sample,chunk);
00363 }
00364
00365
00366 class Sound_InternalData {
00367 public:
00369 Mix_Chunk *mainwave;
00370 Mix_Chunk *startwave;
00371 Sound_Sample *mainsample;
00372 Sound_Sample *startsample;
00373 Sound_InternalData() : mainwave(NULL), startwave(NULL), mainsample(NULL), startsample(NULL) {};
00374 };
00375
00376 Sound::Sound( const ASCString& filename, int _fadeIn ) : name ( filename ), fadeIn ( _fadeIn ), waitingForMainWave(false)
00377 {
00378 internalData = new Sound_InternalData;
00379
00380 if ( !SoundSystem::instance )
00381 fatalError ( "Sound::Sound failed, because there is no SoundSystem initialized");
00382
00383 pair<Sound_Sample*, Mix_Chunk*> res = loadWave( filename );
00384 internalData->mainsample = res.first;
00385 internalData->mainwave = res.second;
00386 }
00387
00388 Sound::Sound( const ASCString& startSoundFilename, const ASCString& continuousSoundFilename, int _fadeIn ) : name ( startSoundFilename ), fadeIn ( _fadeIn ), waitingForMainWave(false)
00389 {
00390 internalData = new Sound_InternalData;
00391
00392 if ( !SoundSystem::instance )
00393 fatalError ( "Sound::Sound failed, because there is no SoundSystem initialized");
00394
00395 pair<Sound_Sample*, Mix_Chunk*> res = loadWave( startSoundFilename );
00396 internalData->startsample = res.first;
00397 internalData->startwave = res.second;
00398
00399 res = loadWave( continuousSoundFilename );
00400 internalData->mainsample = res.first;
00401 internalData->mainwave = res.second;
00402 }
00403
00404
00405
00406
00407 int Sound::startPlaying( bool loop )
00408 {
00409 int channel;
00410
00411 int loopcontrol;
00412 if ( loop )
00413 loopcontrol = -1;
00414 else
00415 loopcontrol = 0;
00416
00417 Mix_Chunk* wave = internalData->startwave;
00418 if ( !wave )
00419 wave = internalData->mainwave;
00420 else
00421 loopcontrol = 0;
00422
00423 if ( fadeIn )
00424 channel = Mix_FadeInChannel ( -1, wave, loopcontrol, fadeIn );
00425 else {
00426 channel = Mix_PlayChannel ( -1, wave, loopcontrol );
00427 Mix_Volume ( channel, SoundSystem::instance->getEffectVolume() );
00428 }
00429 if ( internalData->startwave ) {
00430 waitingForMainWave = true;
00431 }
00432
00433 return channel;
00434 }
00435
00436 void Sound::finishedSignal( int channelnum )
00437 {
00438 if ( waitingForMainWave ) {
00439 Mix_PlayChannel ( channelnum, internalData->mainwave, -1 );
00440 Mix_Volume ( channelnum, SoundSystem::instance->getEffectVolume() );
00441 waitingForMainWave = false;
00442 }
00443 }
00444
00445 void Sound::play(void)
00446 {
00447 if( SoundSystem::instance->areEffectsMuted() || !internalData->mainwave)
00448 return;
00449
00450 int channel = startPlaying( false );
00451 SoundSystem::instance->internalData->channel[ channel ] = this;
00452 }
00453
00454 void Sound::playLoop()
00455 {
00456 if( SoundSystem::instance->areEffectsMuted() || !internalData->mainwave)
00457 return;
00458
00459 int channel = startPlaying( true );
00460 SoundSystem::instance->internalData->channel[ channel ] = this;
00461 }
00462
00463 void Sound::stop()
00464 {
00465 if ( SoundSystem::instance )
00466 for ( int i = 0; i < MIX_CHANNELS; i++ )
00467 if ( SoundSystem::instance->internalData->channel[ i ] == this && Mix_Playing(i) )
00468 Mix_HaltChannel( i );
00469 }
00470
00471
00472 void Sound::playWait(void)
00473 {
00474 if( SoundSystem::instance->areEffectsMuted() || !internalData->mainwave)
00475 return;
00476
00477 if ( internalData->startwave ) {
00478 int channel = Mix_PlayChannel ( -1, internalData->startwave, 0 );
00479 SoundSystem::instance->internalData->channel[ channel ] = this;
00480 do {
00481 SDL_Delay(WAIT_SLEEP_MSEC);
00482 } while( SoundSystem::instance->internalData->channel[ channel ] == this && Mix_Playing(channel) );
00483 }
00484
00485 int channel = Mix_PlayChannel ( -1, internalData->mainwave, 0 );
00486 SoundSystem::instance->internalData->channel[ channel ] = this;
00487
00488 do {
00489 SDL_Delay(WAIT_SLEEP_MSEC);
00490 } while( SoundSystem::instance->internalData->channel[ channel ] == this && Mix_Playing(channel) );
00491 }
00492
00493 void Sound :: fadeOut ( int ms )
00494 {
00495 for ( int i = 0; i < MIX_CHANNELS; i++ )
00496 if ( SoundSystem::instance->internalData->channel[ i ] == this && Mix_Playing(i) )
00497 Mix_FadeOutChannel( i, ms );
00498 }
00499
00500
00501
00502 Sound::~Sound(void)
00503 {
00504 stop();
00505
00506 if ( internalData->mainwave )
00507 delete internalData->mainwave ;
00508
00509 if ( internalData->startwave )
00510 delete internalData->startwave;
00511
00512 if ( internalData->mainsample )
00513 Sound_FreeSample ( internalData->mainsample );
00514
00515 if ( internalData->startsample )
00516 Sound_FreeSample ( internalData->startsample );
00517
00518 delete internalData;
00519 }
00520
00521