Back to index

extremetuxracer  0.5beta
audio.cpp
Go to the documentation of this file.
00001 /* 
00002  * PPRacer 
00003  * Copyright (C) 2004-2005 Volker Stroebel <volker@planetpenguin.de>
00004  *
00005  * Copyright (C) 1999-2001 Jasmin F. Patry
00006  * 
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  * 
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  * 
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00020  */
00021 
00022 #include "game_config.h"
00023 #include "string_util.h"
00024 
00025 #include "ppgltk/ui_mgr.h"
00026 
00027 
00028 #if defined(HAVE_SDL_MIXER)
00029 
00030 #include "SDL.h"
00031 #include "SDL_mixer.h"
00032 #include "audio.h"
00033 #include "audio_data.h"
00034 
00035 #include <string>
00036 #include <map>
00037 
00038 
00039 typedef struct {
00040     int num_sounds;
00041     char **sound_names; /* array of sound names */
00042     Mix_Chunk **chunks; /* array of cached Mix_Chunk* */
00043     int loop_count;
00044     int channel;
00045     int volume;
00046 } sound_context_data_t;
00047 
00048 typedef struct {
00049     char *music_name;
00050     Mix_Music *music;
00051     int loop;
00052 } music_context_data_t;
00053 
00054 //static hash_table_t sound_contexts_;
00055 static std::map<std::string,sound_context_data_t> soundTable;
00056 
00057 //static hash_table_t music_contexts_;
00058 static std::map<std::string,music_context_data_t> musicTable;
00059 
00060 
00061 static bool initialized_ = false;
00062 
00063 /* Data related to currently-playing music */
00064 static Mix_Music *current_music_data_ = NULL;
00065 static char *current_music_name_ = NULL;
00066 
00073 void init_audio()
00074 {
00075     int hz, channels, buffer;
00076     Uint16 format;
00077 
00078     check_assertion( !initialized_,
00079                    "init_audio called twice" );
00080 
00081     //sound_contexts_ = create_hash_table();
00082     //music_contexts_ = create_hash_table();
00083     initialized_ = true;
00084 
00085     /*
00086      * Init SDL Audio 
00087      */    
00088     if ( getparam_no_audio() == false ) {
00089 
00090        if ( SDL_Init( SDL_INIT_AUDIO ) < 0 ) {
00091            handle_error( 1, "Couldn't initialize SDL: %s", SDL_GetError() );
00092        }
00093 
00094        /* Open the audio device */
00095        switch (getparam_audio_freq_mode()) {
00096        case 0:
00097            hz = 11025;
00098            break;
00099        case 1:
00100            hz = 22050;
00101            break;
00102        case 2:
00103            hz = 44100;
00104            break;
00105        default:
00106            hz = 22050;
00107            setparam_audio_freq_mode(1);
00108        }
00109 
00110        switch ( getparam_audio_format_mode() ) {
00111        case 0:
00112            format = AUDIO_U8;
00113            break;
00114        case 1:
00115            format = AUDIO_S16SYS;
00116            break;
00117        default:
00118            format = AUDIO_S16SYS;
00119            setparam_audio_format_mode( 1 );
00120        }
00121 
00122        if ( getparam_audio_stereo() ) {
00123            channels = 2;
00124        } else {
00125            channels = 1;
00126        }
00127 
00128        buffer = getparam_audio_buffer_size();
00129 
00130        if ( Mix_OpenAudio(hz, format, channels, buffer) < 0 ) {
00131            print_warning( 1,
00132                         "Warning: Couldn't set %d Hz %d-bit audio\n"
00133                         "  Reason: %s\n", 
00134                         hz,  
00135                         getparam_audio_format_mode() == 0 ? 8 : 16,
00136                         SDL_GetError());
00137        } else {
00138            print_debug( DEBUG_SOUND,
00139                       "Opened audio device at %d Hz %d-bit audio",
00140                       hz, 
00141                       getparam_audio_format_mode() == 0 ? 8 : 16 );
00142        }
00143     }
00144 }
00145 
00146 
00154 bool is_audio_open()
00155 {
00156     int tmp_freq;
00157     Uint16 tmp_format;
00158     int tmp_channels;
00159 
00160     return Mix_QuerySpec( &tmp_freq, &tmp_format, &tmp_channels );
00161 }
00162 
00180 void bind_sounds_to_context( CONST84 char *sound_context, CONST84 char **names, int num_sounds )
00181 {
00182     int i;
00183     sound_context_data_t *data;
00184 
00185     check_assertion( initialized_, "audio module not initialized" );
00186 
00187        std::map<std::string,sound_context_data_t>::iterator it;
00188        
00189        if((it=soundTable.find(sound_context))!=soundTable.end()){
00190               /* Entry for this context already exists; decrement ref_cts &
00191               delete arrays */
00192 
00193               for (i=0; i<it->second.num_sounds; i++) {
00194               decr_sound_data_ref_ctr( it->second.sound_names[i] );
00195               free( it->second.sound_names[i] );
00196               }
00197               free( it->second.sound_names );
00198               free( it->second.chunks );
00199               soundTable.erase(it);
00200     }
00201     
00202        if ( num_sounds == 0 ) {
00203               return;
00204     }
00205 
00206        data = &soundTable[sound_context];
00207 
00208     check_assertion( num_sounds > 0, "num_sounds isn't > 0 " );
00209 
00210     data->num_sounds = num_sounds;
00211     data->sound_names = (char**)malloc( sizeof(char*)*num_sounds );
00212     data->chunks = (Mix_Chunk**)malloc( sizeof(Mix_Chunk*)*num_sounds );
00213     data->loop_count = 0;
00214     data->channel = 0;
00215     data->volume = 128;
00216 
00217     for (i=0; i<num_sounds; i++) {
00218               data->sound_names[i] = string_copy( names[i] );
00219               incr_sound_data_ref_ctr( names[i] );
00220               data->chunks[i] = NULL;
00221     }
00222 }
00223 
00224 
00239 void bind_music_to_context( CONST84 char *music_context, CONST84 char *name, int loop )
00240 {
00241     music_context_data_t *data;
00242        std::map<std::string,music_context_data_t>::iterator it;
00243 
00244        
00245     check_assertion( initialized_, "audio module not initialized" );
00246 
00247        if((it=musicTable.find(music_context))!=musicTable.end()){
00248               /* Entry for this context already exists; decrement ref count &
00249                  delete string */
00250 
00251               /* check if music is playing, stop if it is */
00252               if ( current_music_name_ != NULL && 
00253               strcmp( current_music_name_, it->second.music_name ) == 0 )
00254               {
00255               Mix_HaltMusic();
00256               current_music_name_ = NULL;
00257               current_music_data_ = NULL;
00258               check_assertion( get_music_playing_status( it->second.music_name ),
00259                             "inconsistent music playing status info" );
00260               set_music_playing_status( it->second.music_name, false );
00261               }
00262 
00263               decr_music_data_ref_ctr( it->second.music_name );
00264               free( it->second.music_name );
00265               musicTable.erase(it);
00266     }
00267               
00268     if ( name == NULL || name[0]=='\0' ) {
00269               return;
00270     }
00271        
00272        data = &musicTable[music_context];
00273 
00274     data->music_name = string_copy( name );
00275     incr_music_data_ref_ctr( name );
00276     data->music = NULL;
00277     data->loop = loop;
00278 }
00279 
00280 
00291 static void flush_cached_sound_chunks()
00292 {
00293        std::map<std::string,sound_context_data_t>::iterator it;
00294        
00295        for(it=soundTable.begin(); it!=soundTable.end(); it++){
00296               for (int i=0; i<it->second.num_sounds; i++) {
00297                   it->second.chunks[i] = NULL;
00298               }
00299               if (it->second.loop_count == -1) {
00300                      Mix_HaltChannel(it->second.channel);
00301               }
00302     }
00303 } 
00304 
00315 static void flush_cached_music()
00316 {
00317        std::map<std::string,music_context_data_t>::iterator it;
00318        
00319        for(it=musicTable.begin(); it!=musicTable.end(); it++){
00320               it->second.music = NULL;
00321        }
00322 } 
00323 
00324 
00336 static Mix_Chunk* get_Mix_Chunk( sound_context_data_t *data, char **name )
00337 {
00338     int i;
00339 
00340     check_assertion( initialized_, "audio module not initialized" );
00341 
00342     if ( is_sound_data_dirty() ) {
00343        flush_cached_sound_chunks();
00344        mark_sound_data_clean();
00345     }
00346 
00347     /* pick a random sound */
00348     
00349     i = (int) (((double)data->num_sounds)*rand()/(RAND_MAX+1.0));
00350     if ( data->chunks[i] == NULL ) {
00351        bool found;
00352        found = get_sound_data( data->sound_names[i], &(data->chunks[i]) );
00353        check_assertion( found, "sound name not found" );
00354        check_assertion( data->chunks[i]!=NULL, "sound chunk not set" );
00355     }
00356     if ( name != NULL ) {
00357        *name = data->sound_names[i];
00358     }
00359 
00360     return data->chunks[i];
00361 }
00362 
00363 
00379 static void get_music_context_data( char *music_context, Mix_Music **music,
00380                                 char **name, int *loop )
00381 {
00382     check_assertion( music != NULL, "music is null" );
00383 
00384     if ( is_music_data_dirty() ) {
00385               flush_cached_music();
00386               mark_music_data_clean();
00387     }
00388 
00389        std::map<std::string,music_context_data_t>::iterator it;
00390        
00391        if((it=musicTable.find(music_context))!=musicTable.end()){
00392               if ( it->second.music == NULL ) {
00393               bool found;
00394               found = get_music_data( it->second.music_name, &(it->second.music) );
00395               check_assertion( found, "music name not found" );
00396               check_assertion( it->second.music!=NULL, "music data not set" );
00397               }
00398 
00399               *music = it->second.music;
00400 
00401               if (name != NULL) {
00402                  *name = it->second.music_name;
00403               }
00404 
00405               if (loop != NULL) {
00406                  *loop = it->second.loop;
00407               }
00408     } else {
00409               // couldn't find any data for this sound_context
00410               *music = NULL;
00411     }
00412 }
00413 
00414 
00425 bool play_sound( const char *sound_context, int loop )
00426 {
00427     Mix_Chunk *chunk;
00428     check_assertion( initialized_, "audio module not initialized" );
00429 
00430     if ( getparam_sound_enabled() == false ) {
00431               return false;
00432     }
00433 
00434     if ( ! is_audio_open() ) {
00435               return false;
00436     }
00437 
00438     check_assertion( sound_context != NULL, "sound_context is null" );
00439 
00440        std::map<std::string,sound_context_data_t>::iterator it;
00441        
00442        if((it=soundTable.find(sound_context))==soundTable.end()){
00443               return false;
00444     }
00445 
00446     chunk = get_Mix_Chunk( &(it->second), NULL );
00447 
00448     if ( chunk == NULL ) {
00449               return false;
00450     }
00451 
00452     if (it->second.loop_count == -1) {
00453               return false;
00454     }
00455 
00456     Mix_VolumeChunk( chunk, it->second.volume );
00457     
00458     it->second.channel = Mix_PlayChannel( -1, chunk, loop );
00459     it->second.loop_count = loop;
00460 
00461     return true;
00462 }
00463 
00474 bool halt_sound( const char *sound_context )
00475 {
00476     Mix_Chunk *chunk;
00477 
00478     if ( getparam_sound_enabled() == false ) {
00479        return false;
00480     }
00481 
00482     if ( ! is_audio_open() ) {
00483        return false;
00484     }
00485 
00486     check_assertion( sound_context != NULL, "sound_context is null" );
00487 
00488        std::map<std::string,sound_context_data_t>::iterator it;
00489        
00490        if((it=soundTable.find(sound_context))==soundTable.end()){
00491               return false;
00492     }
00493 
00494     chunk = get_Mix_Chunk( &(it->second), NULL );
00495 
00496     if ( chunk == NULL ) {
00497               return false;
00498     }
00499        
00500     if (it->second.loop_count == -1) {
00501               Mix_HaltChannel( it->second.channel );
00502     }
00503 
00504     it->second.loop_count = 0;
00505        it->second.channel = 0;
00506     return true;
00507 }
00508 
00519 bool set_sound_volume( const char *sound_context, int volume )
00520 {
00521     //int i;
00522     //sound_context_data_t *data;
00523 
00524        
00525        
00526        std::map<std::string,sound_context_data_t>::iterator it;
00527        
00528        if((it=soundTable.find(sound_context))==soundTable.end()){
00529               return false;
00530     }
00531        
00532     it->second.volume = volume;
00533 
00534     //i = (int) (((double)data->num_sounds)*rand()/(RAND_MAX+1.0));
00535     for (int i=0; i<it->second.num_sounds; i++ ) {
00536               if ( it->second.chunks[i] == NULL ) {
00537                      bool found;
00538               found = get_sound_data( it->second.sound_names[i], &(it->second.chunks[i]) );
00539               check_assertion( found, "sound name not found" );
00540               check_assertion( it->second.chunks[i]!=NULL, "sound chunk not set" );
00541               }
00542               Mix_VolumeChunk( it->second.chunks[i], it->second.volume );
00543     }
00544     return true;
00545 }
00546 
00547 
00548 
00549 /*---------------------------------------------------------------------------*/
00561 bool play_music( char *music_context )
00562 {
00563     Mix_Music *music;
00564     char *music_name;
00565     int loop;
00566 
00567     check_assertion( initialized_, "audio module not initialized" );
00568 
00569     if ( getparam_music_enabled() == false ) {
00570        return false;
00571     }
00572 
00573     if ( ! is_audio_open() ) {
00574        return false;
00575     }
00576 
00577     if ( music_context == NULL ) {
00578        if ( current_music_name_ != NULL ) {
00579            set_music_playing_status( current_music_name_, false );
00580        }
00581        Mix_HaltMusic();
00582        current_music_name_ = NULL;
00583        current_music_data_ = NULL;
00584        return false;
00585     }
00586 
00587     get_music_context_data( music_context, &music, &music_name, &loop );
00588 
00589     if ( music == NULL ) {
00590        return false;
00591     }
00592 
00593     if ( current_music_data_ != music ) {
00594        if ( current_music_name_ != NULL ) {
00595            set_music_playing_status( current_music_name_, false );
00596        }
00597        Mix_HaltMusic();
00598     }
00599 
00600     if ( ! Mix_PlayingMusic() ) {
00601        Mix_PlayMusic( music, loop );
00602        set_music_playing_status( music_name, true );
00603     }
00604 
00605     current_music_data_ = music;
00606     current_music_name_ = music_name;
00607 
00608     return true;
00609 }
00610 
00611 
00620 void 
00621 update_audio()
00622 {
00623     int volume;
00624 
00625     check_assertion( initialized_, "audio module not initialized" );
00626 
00627     if ( ! is_audio_open() ) {
00628        return;
00629     }
00630 
00631     if ( getparam_music_enabled() == false ) {
00632        Mix_HaltMusic();
00633     }
00634 
00635     /* Set sounds volume level */
00636     volume = getparam_sound_volume();
00637     if ( volume < 0 ) {
00638        volume = 0;
00639     }
00640     if ( volume > 128 ) {
00641        volume = 128;
00642     }
00643     setparam_sound_volume( volume );
00644 
00645     Mix_Volume( -1, volume ); /* channel of -1 sets volume for all channels */
00646 
00647     /* Set music volume level */
00648     volume = getparam_music_volume();
00649     if ( volume < 0 ) {
00650        volume = 0;
00651     }
00652     if ( volume > 128 ) {
00653        volume = 128;
00654     }
00655     setparam_music_volume( volume );
00656 
00657     Mix_VolumeMusic( volume );
00658 
00659     /* Update music status */
00660     if ( current_music_name_ != NULL && !Mix_PlayingMusic() ) {
00661        set_music_playing_status( current_music_name_, false );
00662        current_music_name_ = NULL;
00663        current_music_data_ = NULL;
00664     }
00665 }
00666 
00667 
00676 bool
00677 is_music_playing()
00678 {
00679     check_assertion( initialized_, "audio module not initialized" );
00680 
00681     check_assertion( ( current_music_name_ == NULL &&
00682                      current_music_data_ == NULL ) ||
00683                    ( current_music_name_ != NULL &&
00684                      current_music_data_ != NULL ),
00685                    "inconsistent state in sound module" );
00686     return (current_music_name_ != NULL);
00687 }
00688 
00689 
00696 static int bind_sounds_cb( ClientData cd, Tcl_Interp *ip, 
00697                         int argc, CONST84 char *argv[]) 
00698 {
00699     check_assertion( initialized_, "audio module not initialized" );
00700 
00701     if ( argc < 2 ) {
00702         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n", 
00703                       "Usage: ", argv[0], " <sound context> <sound name>"
00704                       " [<sound name> ...]",
00705                       (char *)0 );
00706         return TCL_ERROR;
00707     } 
00708 
00709     bind_sounds_to_context( argv[1], argv+2, argc-2 );
00710     return TCL_OK;
00711 } 
00712 
00713 
00720 static int bind_music_cb( ClientData cd, Tcl_Interp *ip, 
00721                        int argc, CONST84 char *argv[]) 
00722 {
00723     int loops;
00724 
00725     check_assertion( initialized_, "audio module not initialized" );
00726 
00727     if ( argc != 4 ) {
00728         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n", 
00729                       "Usage: ", argv[0], " <music context> <music name> "
00730                       "<loops>",
00731                       (char *)0 );
00732         return TCL_ERROR;
00733     } 
00734 
00735     if ( Tcl_GetInt( ip, argv[3], &loops ) ) {
00736        Tcl_AppendResult(
00737            ip, "invalid value for loops parameter", (char *) 0 );
00738        return TCL_ERROR;
00739     }
00740 
00741     bind_music_to_context( argv[1], argv[2], loops );
00742 
00743     return TCL_OK;
00744 } 
00745 
00746 
00747 /*---------------------------------------------------------------------------*/
00754 void shutdown_audio()
00755 {
00756     if ( is_audio_open() ) {
00757        Mix_CloseAudio();
00758     }
00759 }
00760 
00761 
00771 void register_sound_tcl_callbacks( Tcl_Interp *ip )
00772 {
00773     Tcl_CreateCommand (ip, "tux_bind_sounds", bind_sounds_cb,  0,0);
00774     Tcl_CreateCommand (ip, "tux_bind_music", bind_music_cb,  0,0);
00775 }
00776 
00777 
00778 /*---------------------------------------------------------------------------*/
00779 /*---------------------------------------------------------------------------*/
00780 #else
00781 /*---------------------------------------------------------------------------*/
00782 /*---------------------------------------------------------------------------*/
00783 
00784 void init_audio()
00785 {
00786 }
00787 
00788 void bind_sounds_to_context( CONST84 char *sound_context, CONST84 char **names, int num_sounds )
00789 {
00790 }
00791 
00792 void bind_music_to_context( CONST84 char *music_context, CONST84 char *name, int loop )
00793 {
00794 }
00795 
00796 bool play_sound( const char *sound_context, int loop )
00797 {
00798     return false;
00799 }
00800 
00801 bool play_music( char *music_context )
00802 {
00803     return false;
00804 }
00805 
00806 void update_audio()
00807 {
00808 }
00809 
00810 bool is_music_playing()
00811 {
00812     return false;
00813 }
00814 
00815 static int bind_sounds_cb( ClientData cd, Tcl_Interp *ip, 
00816                         int argc, CONST84 char *argv[]) 
00817 {
00818     return TCL_OK;
00819 } 
00820 
00821 static int bind_music_cb( ClientData cd, Tcl_Interp *ip, 
00822                        int argc, CONST84 char *argv[]) 
00823 {
00824     return TCL_OK;
00825 } 
00826 
00827 void register_sound_tcl_callbacks( Tcl_Interp *ip )
00828 {
00829     Tcl_CreateCommand (ip, "tux_bind_sounds", bind_sounds_cb,  0,0);
00830     Tcl_CreateCommand (ip, "tux_bind_music", bind_music_cb,  0,0);
00831 }
00832 
00833 bool halt_sound( const char *sound_context )
00834 {
00835     return true;
00836 }
00837 
00838 void shutdown_audio()
00839 {
00840 }
00841 
00842 bool set_sound_volume( const char *sound_context, int volume )
00843 {
00844     return true;
00845 }
00846 
00847 #endif /* defined(HAVE_SDL_MIXER) */