Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

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

Generated on Tue Jun 24 01:27:52 2008 for Advanced Strategic Command by  doxygen 1.4.2