sound.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                                                         *
00003  *   This program is free software; you can redistribute it and/or modify  *
00004  *   it under the terms of the GNU General Public License as published by  *
00005  *   the Free Software Foundation; either version 2 of the License, or     *
00006  *   (at your option) any later version.                                   *
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 

Generated on Mon May 21 01:26:37 2012 for Advanced Strategic Command by  doxygen 1.5.1