home *** CD-ROM | disk | FTP | other *** search
- /***************************************************************************
- * audio.cpp - Audio Engine
- *
- * Copyright (C) 2003 - 2008 Florian Richter
- ***************************************************************************/
- /*
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
- #include "../audio/audio.h"
- #include "../audio/sound_manager.h"
- #include "../core/game_core.h"
- #include "../level/level.h"
- #include "../overworld/overworld.h"
- #include "../user/preferences.h"
- #include "../core/i18n.h"
-
- /* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */
-
- void Finished_Sound( int channel )
- {
- // find the finished sound and free the data
- for( AudioSoundList::iterator itr = pAudio->sounds.begin(), itr_end = pAudio->sounds.end(); itr != itr_end; ++itr )
- {
- cAudio_Sound *obj = (*itr);
-
- if( obj->m_channel == channel )
- {
- obj->Finished();
- }
- }
- }
-
- /* *** *** *** *** *** *** *** *** Audio Sound *** *** *** *** *** *** *** *** *** */
-
- cAudio_Sound :: cAudio_Sound( void )
- {
- m_data = NULL;
- m_channel = -1;
- m_resource_id = -1;
- }
-
- cAudio_Sound :: ~cAudio_Sound( void )
- {
- Free();
- }
-
- void cAudio_Sound :: Load( cSound *data )
- {
- Free();
-
- m_data = data;
- }
-
- void cAudio_Sound :: Free( void )
- {
- Stop();
-
- if( m_data )
- {
- m_data = NULL;
- }
-
- m_channel = -1;
- m_resource_id = -1;
- }
-
- void cAudio_Sound :: Finished( void )
- {
- m_channel = -1;
- }
-
- int cAudio_Sound :: Play( int use_res_id /* = -1 */, int loops /* = 0 */ )
- {
- if( !m_data || !m_data->m_chunk )
- {
- return 0;
- }
-
- if( use_res_id >= 0 )
- {
- for( AudioSoundList::iterator itr = pAudio->sounds.begin(), itr_end = pAudio->sounds.end(); itr != itr_end; ++itr )
- {
- // get object pointer
- cAudio_Sound *obj = (*itr);
-
- // skip self
- if( !obj || obj->m_channel == m_channel )
- {
- continue;
- }
-
- // stop Sounds using the given resource id
- if( obj->m_resource_id == use_res_id )
- {
- obj->Stop();
- }
- }
- }
-
- m_resource_id = use_res_id;
- // play sound
- m_channel = Mix_PlayChannel( -1, m_data->m_chunk, loops );
- // add callback if sound finished playing
- Mix_ChannelFinished( &Finished_Sound );
-
- return m_channel;
- }
-
- void cAudio_Sound :: Stop( void )
- {
- // if not loaded or not playing
- if( !m_data || m_channel < 0 )
- {
- return;
- }
-
- Mix_HaltChannel( m_channel );
- m_channel = -1;
- }
-
- /* *** *** *** *** *** *** *** *** Audio *** *** *** *** *** *** *** *** *** */
-
- cAudio :: cAudio( void )
- {
- initialised = 0;
- sound_enabled = 0;
- music_enabled = 0;
-
- debug = 0;
-
- sound_volume = 100;
- music_volume = 80;
-
- music = NULL;
- music_old = NULL;
-
- max_sounds = 0;
- sound_count = 0;
-
- audio_buffer = 4096; // below 2000 can be choppy
- audio_channels = MIX_DEFAULT_CHANNELS; // 1 = Mono, 2 = Stereo
-
- sounds_played = 0;
- music_played = 0;
- }
-
- cAudio :: ~cAudio( void )
- {
- Close();
- }
-
- bool cAudio :: Init( void )
- {
- // Get current device parameters
- int dev_frequency = 0;
- Uint16 dev_format = 0;
- int dev_channels = 0;
- Mix_QuerySpec( &dev_frequency, &dev_format, &dev_channels );
-
- bool sound = pPreferences->audio_sound;
- bool music = pPreferences->audio_music;
-
- // if no change
- if( music_enabled == music && sound_enabled == sound && dev_frequency == pPreferences->audio_hz )
- {
- return 1;
- }
-
- Close();
-
- // if no audio
- if( !music && !sound )
- {
- return 1;
- }
-
- // if audio system is not initialised
- if( !initialised )
- {
- if( debug )
- {
- printf( "Initializing Audio System - Buffer %i, Frequency %i, Speaker Channels %i\n", audio_buffer, pPreferences->audio_hz, audio_channels );
- }
-
- /* Initializing preferred Audio System specs with Mixer Standard format (Stereo)
- *
- * frequency : Output sampling frequency in samples per second (Hz).
- * format : Output sample format.
- * channels : Number of sound channels in output. 2 for stereo and 1 for mono.
- * chunk size : Bytes used per output sample.
- */
-
- if( Mix_OpenAudio( pPreferences->audio_hz, MIX_DEFAULT_FORMAT, audio_channels, audio_buffer ) < 0 )
- {
- printf( "Warning : Could not init 16-bit Audio\n- Reason : %s\n", SDL_GetError() );
- return 0;
- }
-
- initialised = 1;
- }
-
-
- if( debug )
- {
- printf( "Audio Sound Channels available : %d\n", Mix_AllocateChannels( -1 ) );
- }
-
- // music initialization
- if( music && !music_enabled )
- {
- music_enabled = 1;
-
- // set music volume
- Set_Music_Volume( music_volume );
- }
- // music de-initialization
- else if( !music && music_enabled )
- {
- Halt_Music();
-
- music_enabled = 0;
- }
-
- // sound initialization
- if( sound && !sound_enabled )
- {
- sound_enabled = 1;
-
- // create sound array
- Set_Max_Sounds();
- // set sound volume
- Set_Sound_Volume( sound_volume );
- }
- // sound de-initialization
- else if( !sound && sound_enabled )
- {
- Stop_Sounds();
-
- sound_enabled = 0;
- }
-
- return 1;
- }
-
- void cAudio :: Close( void )
- {
- if( initialised )
- {
- if( debug )
- {
- printf( "Closing Audio System\n" );
- }
-
- if( sound_enabled )
- {
- Stop_Sounds();
-
- // clear sounds
- for( AudioSoundList::iterator itr = sounds.begin(), itr_end = sounds.end(); itr != itr_end; ++itr )
- {
- delete *itr;
- }
-
- sounds.clear();
-
- Mix_AllocateChannels( 0 );
- max_sounds = 0;
- sound_enabled = 0;
- }
-
- if( music_enabled )
- {
- Halt_Music();
-
- if( music )
- {
- Mix_FreeMusic( music );
- music = NULL;
- }
-
- if( music_old )
- {
- Mix_FreeMusic( music_old );
- music_old = NULL;
- }
-
- music_enabled = 0;
- }
-
- Mix_CloseAudio();
-
- initialised = 0;
- }
- }
-
- void cAudio :: Set_Max_Sounds( unsigned int limit /* = 10 */ )
- {
- if( !initialised || !sound_enabled )
- {
- return;
- }
-
- // if limit is too small set it to the minimum
- if( limit < 5 )
- {
- limit = 5;
- }
-
- max_sounds = limit;
-
- // remove exceeding sounds
- while( sounds.size() > max_sounds )
- {
- delete *sounds.end();
- sounds.erase( sounds.end() );
- }
-
- // change channels managed by the mixer
- Mix_AllocateChannels( max_sounds );
-
- if( debug )
- {
- printf( "Audio Sound Channels changed : %d\n", Mix_AllocateChannels( -1 ) );
- }
- }
-
- cSound *cAudio :: Get_Sound_File( string filename )
- {
- if( !initialised || !sound_enabled )
- {
- return NULL;
- }
-
- // not available
- if( !File_Exists( filename ) )
- {
- // add sound directory
- if( filename.find( DATA_DIR "/" GAME_SOUNDS_DIR "/" ) == string::npos )
- {
- filename.insert( 0, DATA_DIR "/" GAME_SOUNDS_DIR "/" );
- }
- }
-
- cSound *sound = pSound_Manager->Get_Pointer( filename );
-
- // if not already cached
- if( !sound )
- {
- sound = new cSound();
-
- // loaded sound
- if( sound->Load( filename ) )
- {
- pSound_Manager->Add( sound );
-
- if( debug )
- {
- printf( "Loaded sound file : %s\n", filename.c_str() );
- }
- }
- // failed loading
- else
- {
- printf( "Could not load sound file : %s \nReason : %s\n", filename.c_str(), SDL_GetError() );
-
- delete sound;
- return NULL;
- }
- }
-
- return sound;
- }
-
- bool cAudio :: Play_Sound( string filename, int res_id /* = -1 */, int volume /* = -1 */, int loops /* = 0 */ )
- {
- if( !initialised || !sound_enabled )
- {
- return 0;
- }
-
- // not available
- if( !File_Exists( filename ) )
- {
- // add sound directory
- if( filename.find( DATA_DIR "/" GAME_SOUNDS_DIR "/" ) == string::npos )
- {
- filename.insert( 0, DATA_DIR "/" GAME_SOUNDS_DIR "/" );
- }
-
- // not found
- if( !File_Exists( filename ) )
- {
- printf( "Could not find sound file : %s\n", filename.c_str() );
- return 0;
- }
- }
-
- cSound *sound_data = Get_Sound_File( filename );
-
- // failed loading
- if( !sound_data )
- {
- printf( "Warning : Could not load sound file : %s\n", filename.c_str() );
- return 0;
- }
-
- // create channel
- cAudio_Sound *sound = Create_Sound_Channel();
-
- if( !sound )
- {
- // no free channel available
- return 0;
- }
-
- // load data
- sound->Load( sound_data );
- // play
- sound->Play( res_id, loops );
-
- // failed to play
- if( sound->m_channel < 0 )
- {
- if( debug )
- {
- printf( "Could not play sound file : %s\n", filename.c_str() );
- }
-
- return 0;
- }
- // playing successfully
- else
- {
- sounds_played++;
-
- // volume is out of range
- if( volume > MIX_MAX_VOLUME )
- {
- printf( "PlaySound Volume is out of range : %d\n", volume );
- volume = sound_volume;
- }
- // no volume is given
- else if( volume < 0 )
- {
- volume = sound_volume;
- }
-
- // set volume
- Mix_Volume( sound->m_channel, volume );
- }
-
- return 1;
- }
-
- bool cAudio :: Play_Music( string filename, int loops /* = 0 */, bool force /* = 1 */, unsigned int fadein_ms /* = 0 */ )
- {
- if( !music_enabled || !initialised )
- {
- return 0;
- }
-
- if( filename.find( DATA_DIR "/" GAME_MUSIC_DIR "/" ) == string::npos )
- {
- filename.insert( 0, DATA_DIR "/" GAME_MUSIC_DIR "/" );
- }
-
- // no valid file
- if( !File_Exists( filename ) )
- {
- printf( "Couldn't find music file : %s\n", filename.c_str() );
- return 0;
- }
-
- // if music is stopped resume it
- Resume_Music();
-
- // if no music is playing or force to play the given music
- if( !Is_Music_Playing() || force )
- {
- // stop and free current music
- if( music )
- {
- Halt_Music();
- Mix_FreeMusic( music );
- }
- // free old music
- if( music_old )
- {
- Mix_FreeMusic( music_old );
- music_old = NULL;
- }
-
- // load the given music
- music = Mix_LoadMUS( filename.c_str() );
-
- // loaded
- if( music )
- {
- // count success
- music_played++;
-
- // no fade in
- if( !fadein_ms )
- {
- Mix_PlayMusic( music, loops );
- }
- // fade in
- else
- {
- Mix_FadeInMusic( music, loops, fadein_ms );
- }
- }
- // not loaded
- else
- {
- if( debug )
- {
- printf( "Couldn't load music file : %s\n", filename.c_str() );
- }
-
- // failed to play
- return 0;
- }
- }
- // music is playing and is not forced
- else
- {
- // if music is loaded
- if( music )
- {
- // if old music is loaded free the wanted next playing music data
- if( music_old )
- {
- Mix_FreeMusic( music );
- music = NULL;
- }
- // if no old music move current to old music
- else
- {
- music_old = music;
- music = NULL;
- }
- }
-
- // load the wanted next playing music
- music = Mix_LoadMUS( filename.c_str() );
- }
-
- return 1;
- }
-
- cAudio_Sound *cAudio :: Get_Playing_Sound( string filename )
- {
- if( !sound_enabled || !initialised )
- {
- return NULL;
- }
-
- // add sound directory
- if( filename.find( DATA_DIR "/" GAME_SOUNDS_DIR "/" ) == string::npos )
- {
- filename.insert( 0, DATA_DIR "/" GAME_SOUNDS_DIR "/" );
- }
-
- // get all sounds
- for( AudioSoundList::iterator itr = sounds.begin(), itr_end = sounds.end(); itr != itr_end; ++itr )
- {
- // get object pointer
- cAudio_Sound *obj = (*itr);
-
- // if not playing
- if( obj->m_channel < 0 )
- {
- continue;
- }
-
- // found it
- if( obj->m_data->m_filename.compare( filename ) == 0 )
- {
- // return first found
- return obj;
- }
- }
-
- // not found
- return NULL;
- }
-
- cAudio_Sound *cAudio :: Create_Sound_Channel( void )
- {
- // get all sounds
- for( AudioSoundList::iterator itr = sounds.begin(), itr_end = sounds.end(); itr != itr_end; ++itr )
- {
- // get object pointer
- cAudio_Sound *obj = (*itr);
-
- // if not playing
- if( obj->m_channel < 0 )
- {
- // found a free channel
- obj->Free();
- return obj;
- }
- }
-
- // if not maximum sounds
- if( sounds.size() < max_sounds )
- {
- cAudio_Sound *sound = new cAudio_Sound();
- sounds.push_back( sound );
- return sound;
- }
-
- // none found
- return NULL;
- }
-
- void cAudio :: Toggle_Music( void )
- {
- pPreferences->audio_music = !pPreferences->audio_music;
- Init();
-
- // play music
- if( music_enabled )
- {
- // valid level music available
- if( pActive_Level->valid_music )
- {
- Play_Music( pActive_Level->musicfile, -1, 1, 2000 );
- }
- // in overworld
- else if( Game_Mode == MODE_OVERWORLD )
- {
- Play_Music( pActive_Overworld->musicfile, -1, 1, 2000 );
- }
- // in menu
- else
- {
- Play_Music( "game/menu.ogg", -1, 1, 2000 );
- }
-
- // Warning if no music pack is installed and music got enabled
- if( !File_Exists( DATA_DIR "/" GAME_MUSIC_DIR "/game/menu.ogg" ) && !File_Exists( DATA_DIR "/" GAME_MUSIC_DIR "/land/land_1.ogg" ) )
- {
- Draw_Static_Text( _("Warning : No music addon detected"), &orange );
- }
- }
- }
-
- void cAudio :: Toggle_Sounds( void )
- {
- pPreferences->audio_sound = !pPreferences->audio_sound;
- Init();
-
- // play a test sound
- if( sound_enabled )
- {
- Play_Sound( "audio_on.ogg" );
- }
- }
-
- void cAudio :: Pause_Music( void )
- {
- if( !music_enabled || !initialised )
- {
- return;
- }
-
- // Check if music is currently playing
- if( Mix_PlayingMusic() )
- {
- Mix_PauseMusic();
- }
- }
-
- void cAudio :: Resume_Sound( int channel /* = -1 */ )
- {
- if( !sound_enabled || !initialised )
- {
- return;
- }
-
- // resume playback on all previously active channels
- Mix_Resume( channel );
- }
-
- void cAudio :: Resume_Music( void )
- {
- if( !music_enabled || !initialised )
- {
- return;
- }
-
- // Check if music is currently paused
- if( Mix_PausedMusic() )
- {
- Mix_ResumeMusic();
- }
- }
-
- void cAudio :: Fadeout_Sounds( unsigned int ms /* = 200 */, int channel /* = -1 */, bool overwrite_fading /* = 0 */ )
- {
- if( !sound_enabled || !initialised )
- {
- return;
- }
-
- // Check the Channels
- if( Mix_Playing( channel ) )
- {
- // Do not fade out the sound again
- if( !overwrite_fading && Is_Sound_Fading( -1 ) == MIX_FADING_OUT )
- {
- return;
- }
-
- Mix_FadeOutChannel( channel, ms );
- }
- }
-
- void cAudio :: Fadeout_Sounds( unsigned int ms, string filename, bool overwrite_fading /* = 0 */ )
- {
- if( !sound_enabled || !initialised )
- {
- return;
- }
-
- // add sound directory
- if( filename.find( DATA_DIR "/" GAME_SOUNDS_DIR "/" ) == string::npos )
- {
- filename.insert( 0, DATA_DIR "/" GAME_SOUNDS_DIR "/" );
- }
-
- // get all sounds
- for( AudioSoundList::iterator itr = sounds.begin(), itr_end = sounds.end(); itr != itr_end; ++itr )
- {
- // get object pointer
- cAudio_Sound *obj = (*itr);
-
- // filename does not match
- if( obj->m_data->m_filename.compare( filename ) != 0 )
- {
- continue;
- }
-
- // Do not fade out the sound again
- if( !overwrite_fading && Is_Sound_Fading( obj->m_channel ) == MIX_FADING_OUT )
- {
- continue;
- }
-
- Mix_FadeOutChannel( obj->m_channel, ms );
- }
- }
-
- void cAudio :: Fadeout_Music( unsigned int ms /* = 500 */, bool overwrite_fading /* = 0 */ )
- {
- if( !music_enabled || !initialised )
- {
- return;
- }
-
- // if music is currently playing
- if( Mix_PlayingMusic() )
- {
- Mix_Fading status = Is_Music_Fading();
-
- // don't fade the music out again
- if( !overwrite_fading && status == MIX_FADING_OUT )
- {
- return;
- }
- // Can't stop fade in with SDL_Mixer and fade out is ignored when fading in
- else if( status == MIX_FADING_IN )
- {
- Halt_Music();
- }
-
- Mix_FadeOutMusic( ms );
- }
- }
-
- void cAudio :: Set_Music_Position( float position )
- {
- if( !music_enabled || !initialised || Is_Music_Fading() == MIX_FADING_OUT )
- {
- return;
- }
-
- Mix_SetMusicPosition( position );
- }
-
- Mix_Fading cAudio :: Is_Music_Fading( void )
- {
- if( !music_enabled || !initialised )
- {
- return MIX_NO_FADING;
- }
-
- return Mix_FadingMusic();
- }
-
- Mix_Fading cAudio :: Is_Sound_Fading( int sound_channel )
- {
- if( !sound_enabled || !initialised )
- {
- return MIX_NO_FADING;
- }
-
- return Mix_FadingChannel( sound_channel );
- }
-
- bool cAudio :: Is_Music_Paused( void )
- {
- if( !music_enabled || !initialised )
- {
- return 0;
- }
-
- if( Mix_PausedMusic() )
- {
- return 1;
- }
-
- return 0;
- }
-
- bool cAudio :: Is_Music_Playing( void )
- {
- if( !music_enabled || !initialised )
- {
- return 0;
- }
-
- if( Mix_PlayingMusic() )
- {
- return 1;
- }
-
- return 0;
- }
-
- void cAudio :: Halt_Sounds( int channel /* = -1 */ )
- {
- if( !sound_enabled || !initialised )
- {
- return;
- }
-
- // Check all Channels
- if( Mix_Playing( channel ) )
- {
- Mix_HaltChannel( channel );
- }
- }
-
- void cAudio :: Halt_Music( void )
- {
- if( !initialised )
- {
- return;
- }
-
- // Checks if music is playing
- if( Mix_PlayingMusic() )
- {
- Mix_HaltMusic();
- }
- }
-
- void cAudio :: Stop_Sounds( void )
- {
- if( !initialised )
- {
- return;
- }
-
- // Stop all channels
- if( Mix_Playing( -1 ) )
- {
- Mix_HaltChannel( -1 );
- }
- }
-
- void cAudio :: Set_Sound_Volume( Uint8 volume, int channel /* = -1 */ )
- {
- if( volume > MIX_MAX_VOLUME || !initialised )
- {
- return;
- }
-
- Mix_Volume( channel, volume );
- }
-
- void cAudio :: Set_Music_Volume( Uint8 volume )
- {
- if( volume > MIX_MAX_VOLUME || !initialised )
- {
- return;
- }
-
- Mix_VolumeMusic( volume );
- }
-
- void cAudio :: Update( void )
- {
- if( !initialised )
- {
- return;
- }
-
- // if music is enabled
- if( music_enabled )
- {
- // if no music is playing
- if( !Mix_PlayingMusic() && music )
- {
- Mix_PlayMusic( music, 0 );
- music_played++;
-
- // delete old music if available
- if( music_old )
- {
- Mix_FreeMusic( music_old );
- music_old = NULL;
- }
- }
- }
- }
-
- /* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */
-
- cAudio *pAudio = NULL;
-