sound.cpp

Go to the documentation of this file.
00001 /* $Id: sound.cpp 52533 2012-01-07 02:35:17Z shadowmaster $ */
00002 /*
00003    Copyright (C) 2003 - 2012 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 #include "global.hpp"
00017 
00018 #include "config.hpp"
00019 #include "filesystem.hpp"
00020 #include "foreach.hpp"
00021 #include "game_preferences.hpp"
00022 #include "log.hpp"
00023 #include "serialization/string_utils.hpp"
00024 #include "sound.hpp"
00025 #include "sound_music_track.hpp"
00026 #include "util.hpp"
00027 
00028 #include "SDL_mixer.h"
00029 
00030 #include <list>
00031 
00032 static lg::log_domain log_audio("audio");
00033 #define LOG_AUDIO LOG_STREAM(info, log_audio)
00034 #define ERR_AUDIO LOG_STREAM(err, log_audio)
00035 
00036 namespace sound {
00037 // Channel-chunk mapping lets us know, if we can safely free a given chunk
00038 std::vector<Mix_Chunk*> channel_chunks;
00039 
00040 // Channel-id mapping for use with sound sources (to check if given source
00041 // is playing on a channel for fading/panning)
00042 std::vector<int> channel_ids;
00043 
00044 static void play_sound_internal(const std::string& files, channel_group group, unsigned int repeats=0,
00045                  unsigned int distance=0, int id=-1, int loop_ticks=0, int fadein_ticks=0);
00046 }
00047 
00048 namespace {
00049 
00050 bool mix_ok = false;
00051 int music_start_time = 0;
00052 unsigned music_refresh = 0;
00053 unsigned music_refresh_rate = 20;
00054 bool want_new_music = false;
00055 int fadingout_time=5000;
00056 bool no_fading = false;
00057 
00058 // number of allocated channels,
00059 const size_t n_of_channels = 16;
00060 
00061 // we need 2 channels, because we it for timer as well
00062 const size_t bell_channel = 0;
00063 const size_t timer_channel = 1;
00064 
00065 // number of channels reserved for sound sources
00066 const size_t source_channels = n_of_channels - 8;
00067 const size_t source_channel_start = timer_channel + 1;
00068 const size_t source_channel_last = source_channel_start + source_channels - 1;
00069 const size_t UI_sound_channel = source_channel_last + 1;
00070 const size_t n_reserved_channels = UI_sound_channel + 1; // sources, bell, timer and UI
00071 
00072 // Max number of sound chunks that we want to cache
00073 // Keep this above number of available channels to avoid busy-looping
00074 #ifdef LOW_MEM
00075 unsigned max_cached_chunks = 64;
00076 #else
00077 unsigned max_cached_chunks = 256;
00078 #endif
00079 
00080 std::map< Mix_Chunk*, int > chunk_usage;
00081 
00082 }
00083 
00084 static void increment_chunk_usage(Mix_Chunk* mcp) {
00085     ++(chunk_usage[mcp]);
00086 }
00087 
00088 static void decrement_chunk_usage(Mix_Chunk* mcp) {
00089     if(mcp == NULL) return;
00090     std::map< Mix_Chunk*, int >::iterator this_usage = chunk_usage.find(mcp);
00091     assert(this_usage != chunk_usage.end());
00092     if(--(this_usage->second) == 0) {
00093         Mix_FreeChunk(mcp);
00094         chunk_usage.erase(this_usage);
00095     }
00096 }
00097 
00098 namespace {
00099 
00100 class sound_cache_chunk {
00101 public:
00102     sound_cache_chunk(const std::string& f) : group(sound::NULL_CHANNEL), file(f), data_(NULL) {}
00103     sound_cache_chunk(const sound_cache_chunk& scc)
00104         : group(scc.group), file(scc.file), data_(scc.data_)
00105     {
00106         increment_chunk_usage(data_);
00107     }
00108 
00109     ~sound_cache_chunk()
00110     {
00111         decrement_chunk_usage(data_);
00112     }
00113 
00114     sound::channel_group group;
00115     std::string file;
00116 
00117     void set_data(Mix_Chunk* d) {
00118         increment_chunk_usage(d);
00119         decrement_chunk_usage(data_);
00120         data_ = d;
00121     }
00122 
00123     Mix_Chunk* get_data() const {
00124         return data_;
00125     }
00126 
00127     bool operator==(sound_cache_chunk const &scc) const {
00128         return file == scc.file;
00129     }
00130 
00131     bool operator!=(sound_cache_chunk const &scc) const { return !operator==(scc); }
00132 
00133     sound_cache_chunk& operator=(const sound_cache_chunk& scc) {
00134         file = scc.file;
00135         group = scc.group;
00136         set_data(scc.get_data());
00137         return *this;
00138     }
00139 
00140 private:
00141     Mix_Chunk* data_;
00142 };
00143 
00144 std::list< sound_cache_chunk > sound_cache;
00145 typedef std::list< sound_cache_chunk >::iterator sound_cache_iterator;
00146 std::map<std::string,Mix_Music*> music_cache;
00147 
00148 std::vector<std::string> played_before;
00149 
00150 //
00151 // FIXME: the first music_track may be initialized before main()
00152 // is reached. Using the logging facilities may lead to a SIGSEGV
00153 // because it's not guaranteed that their objects are already alive.
00154 //
00155 // Use the music_track default constructor to avoid trying to
00156 // invoke a log object while resolving paths.
00157 //
00158 std::vector<sound::music_track> current_track_list;
00159 sound::music_track current_track;
00160 sound::music_track last_track;
00161 
00162 }
00163 
00164 static bool track_ok(const std::string& id)
00165 {
00166     LOG_AUDIO << "Considering " << id << "\n";
00167 
00168     // If they committed changes to list, we forget previous plays, but
00169     // still *never* repeat same track twice if we have an option.
00170     if (id == current_track.file_path())
00171         return false;
00172 
00173     if (current_track_list.size() <= 3)
00174         return true;
00175 
00176     // Timothy Pinkham says:
00177     // 1) can't be repeated without 2 other pieces have already played
00178     // since A was played.
00179     // 2) cannot play more than 2 times without every other piece
00180     // having played at least 1 time.
00181 
00182     // Dammit, if our musicians keep coming up with algorithms, I'll
00183     // be out of a job!
00184     unsigned int num_played = 0;
00185     std::set<std::string> played;
00186     std::vector<std::string>::reverse_iterator i;
00187 
00188     for (i = played_before.rbegin(); i != played_before.rend(); ++i) {
00189         if (*i == id) {
00190             ++num_played;
00191             if (num_played == 2)
00192                 break;
00193         } else {
00194             played.insert(*i);
00195         }
00196     }
00197 
00198     // If we've played this twice, must have played every other track.
00199     if (num_played == 2 && played.size() != current_track_list.size() - 1) {
00200         LOG_AUDIO << "Played twice with only " << played.size()
00201             << " tracks between\n";
00202         return false;
00203     }
00204 
00205     // Check previous previous track not same.
00206     i = played_before.rbegin();
00207     if (i != played_before.rend()) {
00208         ++i;
00209         if (i != played_before.rend()) {
00210             if (*i == id) {
00211                 LOG_AUDIO << "Played just before previous\n";
00212                 return false;
00213             }
00214         }
00215     }
00216 
00217     return true;
00218 }
00219 
00220 
00221 static const sound::music_track &choose_track()
00222 {
00223     assert(!current_track_list.empty());
00224 
00225     unsigned int track = 0;
00226 
00227     if (current_track_list.size() > 1) {
00228         do {
00229             track = rand()%current_track_list.size();
00230         } while (!track_ok( current_track_list[track].file_path() ));
00231     }
00232 
00233     //LOG_AUDIO << "Next track will be " << current_track_list[track].file_path() << "\n";
00234     played_before.push_back( current_track_list[track].file_path() );
00235     return current_track_list[track];
00236 }
00237 
00238 static std::string pick_one(const std::string &files)
00239 {
00240     std::vector<std::string> ids = utils::split(files);
00241 
00242     if (ids.empty())
00243         return "";
00244     if (ids.size() == 1)
00245         return ids[0];
00246 
00247 #ifdef LOW_MEM
00248     // We're memory constrained, so we shouldn't cache too many chunks
00249     return ids[0];
00250 #endif
00251 
00252     // We avoid returning same choice twice if we can avoid it.
00253     static std::map<std::string,unsigned int> prev_choices;
00254     unsigned int choice;
00255 
00256     if (prev_choices.find(files) != prev_choices.end()) {
00257         choice = rand()%(ids.size()-1);
00258         if (choice >= prev_choices[files])
00259             ++choice;
00260         prev_choices[files] = choice;
00261     } else {
00262         choice = rand()%ids.size();
00263         prev_choices.insert(std::pair<std::string,unsigned int>(files,choice));
00264     }
00265 
00266     return ids[choice];
00267 }
00268 
00269 namespace {
00270 
00271 struct audio_lock
00272 {
00273     audio_lock()
00274     {
00275         SDL_LockAudio();
00276     }
00277 
00278     ~audio_lock()
00279     {
00280         SDL_UnlockAudio();
00281     }
00282 };
00283 
00284 } // end of anonymous namespace
00285 
00286 
00287 namespace sound {
00288 
00289 // Removes channel-chunk and channel-id mapping
00290 static void channel_finished_hook(int channel)
00291 {
00292     channel_chunks[channel] = NULL;
00293     channel_ids[channel] = -1;
00294 }
00295 
00296 bool init_sound() {
00297     LOG_AUDIO << "Initializing audio...\n";
00298     if(SDL_WasInit(SDL_INIT_AUDIO) == 0)
00299         if(SDL_InitSubSystem(SDL_INIT_AUDIO) == -1)
00300             return false;
00301 
00302     if(!mix_ok) {
00303         if(Mix_OpenAudio(preferences::sample_rate(), MIX_DEFAULT_FORMAT, 2, preferences::sound_buffer_size()) == -1) {
00304             mix_ok = false;
00305             ERR_AUDIO << "Could not initialize audio: " << Mix_GetError() << "\n";
00306             return false;
00307         }
00308 
00309         mix_ok = true;
00310         Mix_AllocateChannels(n_of_channels);
00311         Mix_ReserveChannels(n_reserved_channels);
00312 
00313         channel_chunks.clear();
00314         channel_chunks.resize(n_of_channels, NULL);
00315         channel_ids.resize(n_of_channels, -1);
00316 
00317         Mix_GroupChannel(bell_channel, SOUND_BELL);
00318         Mix_GroupChannel(timer_channel, SOUND_TIMER);
00319         Mix_GroupChannels(source_channel_start, source_channel_last, SOUND_SOURCES);
00320         Mix_GroupChannel(UI_sound_channel, SOUND_UI);
00321         Mix_GroupChannels(n_reserved_channels, n_of_channels - 1, SOUND_FX);
00322 
00323         set_sound_volume(preferences::sound_volume());
00324         set_UI_volume(preferences::UI_volume());
00325         set_music_volume(preferences::music_volume());
00326         set_bell_volume(preferences::bell_volume());
00327 
00328         Mix_ChannelFinished(channel_finished_hook);
00329 
00330         LOG_AUDIO << "Audio initialized.\n";
00331 
00332         play_music();
00333     }
00334     return true;
00335 }
00336 
00337 void close_sound() {
00338     int frequency, channels;
00339     Uint16 format;
00340 
00341     if(mix_ok) {
00342         stop_bell();
00343         stop_UI_sound();
00344         stop_sound();
00345         sound_cache.clear();
00346         stop_music();
00347         mix_ok = false;
00348 
00349         int numtimesopened = Mix_QuerySpec(&frequency, &format, &channels);
00350         if(numtimesopened == 0) {
00351             ERR_AUDIO << "Error closing audio device: " << Mix_GetError() << "\n";
00352         }
00353         while (numtimesopened) {
00354             Mix_CloseAudio();
00355             --numtimesopened;
00356         }
00357     }
00358     if(SDL_WasInit(SDL_INIT_AUDIO) != 0)
00359         SDL_QuitSubSystem(SDL_INIT_AUDIO);
00360 
00361     LOG_AUDIO << "Audio device released.\n";
00362 }
00363 
00364 void reset_sound() {
00365     bool music = preferences::music_on();
00366     bool sound = preferences::sound_on();
00367     bool UI_sound = preferences::UI_sound_on();
00368     bool bell = preferences::turn_bell();
00369 
00370     if (music || sound || bell || UI_sound) {
00371         sound::close_sound();
00372         if (!sound::init_sound()) {
00373             ERR_AUDIO << "Error initializing audio device: " << Mix_GetError() << "\n";
00374         }
00375         if (!music)
00376             sound::stop_music();
00377         if (!sound)
00378             sound::stop_sound();
00379         if (!UI_sound)
00380             sound::stop_UI_sound();
00381         if (!bell)
00382             sound::stop_bell();
00383     }
00384 }
00385 
00386 void stop_music() {
00387     if(mix_ok) {
00388         Mix_HaltMusic();
00389 
00390         std::map<std::string,Mix_Music*>::iterator i;
00391         for(i = music_cache.begin(); i != music_cache.end(); ++i)
00392             Mix_FreeMusic(i->second);
00393         music_cache.clear();
00394     }
00395 }
00396 
00397 void stop_sound() {
00398     if(mix_ok) {
00399         Mix_HaltGroup(SOUND_SOURCES);
00400         Mix_HaltGroup(SOUND_FX);
00401         sound_cache_iterator itor = sound_cache.begin();
00402         while(itor != sound_cache.end())
00403         {
00404             if(itor->group == SOUND_SOURCES || itor->group == SOUND_FX) {
00405                 itor = sound_cache.erase(itor);
00406             } else {
00407                 ++itor;
00408             }
00409         }
00410     }
00411 }
00412 
00413 /*
00414  * For the purpose of channel manipulation, we treat turn timer the same as bell
00415  */
00416 void stop_bell() {
00417     if(mix_ok) {
00418         Mix_HaltGroup(SOUND_BELL);
00419         Mix_HaltGroup(SOUND_TIMER);
00420         sound_cache_iterator itor = sound_cache.begin();
00421         while(itor != sound_cache.end())
00422         {
00423             if(itor->group == SOUND_BELL || itor->group == SOUND_TIMER) {
00424                 itor = sound_cache.erase(itor);
00425             } else {
00426                 ++itor;
00427             }
00428         }
00429     }
00430 }
00431 
00432 void stop_UI_sound() {
00433     if(mix_ok) {
00434         Mix_HaltGroup(SOUND_UI);
00435         sound_cache_iterator itor = sound_cache.begin();
00436         while(itor != sound_cache.end())
00437         {
00438             if(itor->group == SOUND_UI) {
00439                 itor = sound_cache.erase(itor);
00440             } else {
00441                 ++itor;
00442             }
00443         }
00444     }
00445 }
00446 
00447 void play_music_once(const std::string &file)
00448 {
00449     // Clear list so it's not replayed.
00450     current_track_list.clear();
00451     current_track = music_track(file);
00452     play_music();
00453 }
00454 
00455 void empty_playlist()
00456 {
00457     current_track_list.clear();
00458 }
00459 
00460 void play_music()
00461 {
00462     music_start_time = 1; //immediate (same as effect as SDL_GetTicks())
00463     want_new_music=true;
00464     no_fading=false;
00465     fadingout_time=current_track.ms_after();
00466 }
00467 
00468 static void play_new_music()
00469 {
00470     music_start_time = 0; //reset status: no start time
00471     want_new_music = true;
00472 
00473     if(!preferences::music_on() || !mix_ok || !current_track.valid()) {
00474         return;
00475     }
00476 
00477     const std::string& filename = current_track.file_path();
00478 
00479     std::map<std::string,Mix_Music*>::const_iterator itor = music_cache.find(filename);
00480     if(itor == music_cache.end()) {
00481         LOG_AUDIO << "attempting to insert track '" << filename << "' into cache\n";
00482         Mix_Music* const music = Mix_LoadMUS(filename.c_str());
00483         if(music == NULL) {
00484             ERR_AUDIO << "Could not load music file '" << filename << "': "
00485                       << Mix_GetError() << "\n";
00486             return;
00487         }
00488         itor = music_cache.insert(std::pair<std::string,Mix_Music*>(filename,music)).first;
00489         last_track=current_track;
00490     }
00491 
00492     LOG_AUDIO << "Playing track '" << filename << "'\n";
00493     int fading_time=current_track.ms_before();
00494     if(no_fading)
00495     {
00496         fading_time=0;
00497     }
00498 
00499     const int res = Mix_FadeInMusic(itor->second, 1, fading_time);
00500     if(res < 0)
00501     {
00502         ERR_AUDIO << "Could not play music: " << Mix_GetError() << " " << filename <<" \n";
00503     }
00504 
00505     want_new_music=false;
00506 }
00507 
00508 void play_music_repeatedly(const std::string &id)
00509 {
00510     // Can happen if scenario doesn't specify.
00511     if (id.empty())
00512         return;
00513 
00514     current_track_list.clear();
00515     current_track_list.push_back(music_track(id));
00516 
00517     // If we're already playing it, don't interrupt.
00518     if (current_track != id) {
00519         current_track = music_track(id);
00520         play_music();
00521     }
00522 }
00523 
00524 void play_music_config(const config &music_node)
00525 {
00526     music_track track( music_node );
00527 
00528     // If they say play once, we don't alter playlist.
00529     if (track.play_once()) {
00530         current_track = track;
00531         play_music();
00532         return;
00533     }
00534 
00535     // Clear play list unless they specify append.
00536     if (!track.append()) {
00537         current_track_list.clear();
00538     }
00539 
00540     if(track.valid()) {
00541         // Avoid 2 tracks with the same name, since that can cause an infinite loop
00542         // in choose_track(), 2 tracks with the same name will always return the
00543         // current track and track_ok() doesn't allow that.
00544         std::vector<music_track>::const_iterator itor = current_track_list.begin();
00545         while(itor != current_track_list.end()) {
00546             if(track == *itor) break;
00547             ++itor;
00548         }
00549 
00550         if(itor == current_track_list.end()) {
00551             current_track_list.push_back(track);
00552         } else {
00553             ERR_AUDIO << "tried to add duplicate track '" << track.file_path() << "'\n";
00554         }
00555     }
00556     else if(track.id().empty() == false) {
00557         ERR_AUDIO << "cannot open track '" << track.id() << "'; disabled in this playlist.\n";
00558     }
00559 
00560     // They can tell us to start playing this list immediately.
00561     if (track.immediate()) {
00562         current_track = track;
00563         play_music();
00564     }
00565 }
00566 
00567 void music_thinker::process(events::pump_info &info) {
00568     if(preferences::music_on()) {
00569         if(!music_start_time && !current_track_list.empty() && !Mix_PlayingMusic()) {
00570             // Pick next track, add ending time to its start time.
00571             current_track = choose_track();
00572             music_start_time = info.ticks();
00573             no_fading=true;
00574             fadingout_time=0;
00575         }
00576 
00577         if(music_start_time && info.ticks(&music_refresh, music_refresh_rate) >= music_start_time - fadingout_time) {
00578             want_new_music=true;
00579         }
00580 
00581         if(want_new_music) {
00582             if(Mix_PlayingMusic()) {
00583                 Mix_FadeOutMusic(fadingout_time);
00584             }
00585             play_new_music();
00586         }
00587     }
00588 }
00589 
00590 void commit_music_changes()
00591 {
00592     played_before.clear();
00593 
00594     // Play-once is OK if still playing.
00595     if (current_track.play_once())
00596         return;
00597 
00598     // If current track no longer on playlist, change it.
00599     foreach (const music_track &m, current_track_list) {
00600         if (current_track == m)
00601             return;
00602     }
00603 
00604     // Victory empties playlist: if next scenario doesn't specify one...
00605     if (current_track_list.empty())
00606         return;
00607 
00608     // FIXME: we don't pause ms_before on this first track.  Should we?
00609     current_track = choose_track();
00610     play_music();
00611 }
00612 
00613 void write_music_play_list(config& snapshot)
00614 {
00615     // First entry clears playlist, others append to it.
00616     bool append = false;
00617     foreach (music_track &m, current_track_list) {
00618         m.write(snapshot, append);
00619         append = true;
00620     }
00621 }
00622 
00623 void reposition_sound(int id, unsigned int distance)
00624 {
00625     audio_lock lock;
00626     for (unsigned ch = 0; ch < channel_ids.size(); ++ch)
00627     {
00628         if (channel_ids[ch] != id) continue;
00629         if (distance >= DISTANCE_SILENT) {
00630             Mix_FadeOutChannel(ch, 100);
00631         } else {
00632             Mix_SetDistance(ch, distance);
00633         }
00634     }
00635 }
00636 
00637 bool is_sound_playing(int id)
00638 {
00639     audio_lock lock;
00640     return std::find(channel_ids.begin(), channel_ids.end(), id) != channel_ids.end();
00641 }
00642 
00643 void stop_sound(int id)
00644 {
00645     reposition_sound(id, DISTANCE_SILENT);
00646 }
00647 
00648 void play_sound_positioned(const std::string &files, int id, int repeats, unsigned int distance)
00649 {
00650     if(preferences::sound_on()) {
00651         play_sound_internal(files, SOUND_SOURCES, repeats, distance, id);
00652     }
00653 }
00654 
00655 struct chunk_load_exception { };
00656 
00657 static Mix_Chunk* load_chunk(const std::string& file, channel_group group)
00658 {
00659     sound_cache_iterator it_bgn, it_end;
00660     sound_cache_iterator it;
00661 
00662     sound_cache_chunk temp_chunk(file); // search the sound cache on this key
00663     it_bgn = sound_cache.begin(), it_end = sound_cache.end();
00664     it = std::find(it_bgn, it_end, temp_chunk);
00665 
00666     if (it != it_end) {
00667         if(it->group != group) {
00668             // cached item has been used in multiple sound groups
00669             it->group = NULL_CHANNEL;
00670         }
00671 
00672         //splice the most recently used chunk to the front of the cache
00673         sound_cache.splice(it_bgn, sound_cache, it);
00674     } else {
00675         // remove the least recently used chunk from cache if it's full
00676         bool cache_full = (sound_cache.size() == max_cached_chunks);
00677         while( cache_full && it != it_bgn ) {
00678             // make sure this chunk is not being played before freeing it
00679             std::vector<Mix_Chunk*>::iterator ch_end = channel_chunks.end();
00680             if(std::find(channel_chunks.begin(), ch_end, (--it)->get_data()) == ch_end) {
00681                 sound_cache.erase(it);
00682                 cache_full = false;
00683             }
00684         }
00685         if(cache_full) {
00686             LOG_AUDIO << "Maximum sound cache size reached and all are busy, skipping.\n";
00687             throw chunk_load_exception();
00688         }
00689         temp_chunk.group = group;
00690         std::string const &filename = get_binary_file_location("sounds", file);
00691 
00692         if (!filename.empty()) {
00693             temp_chunk.set_data(Mix_LoadWAV(filename.c_str()));
00694         } else {
00695             ERR_AUDIO << "Could not load sound file '" << file << "'.\n";
00696             throw chunk_load_exception();
00697         }
00698 
00699         if (temp_chunk.get_data() == NULL) {
00700             ERR_AUDIO << "Could not load sound file '" << filename << "': "
00701                 << Mix_GetError() << "\n";
00702             throw chunk_load_exception();
00703         }
00704 
00705         sound_cache.push_front(temp_chunk);
00706     }
00707 
00708     return sound_cache.begin()->get_data();
00709 }
00710 
00711 void play_sound_internal(const std::string& files, channel_group group, unsigned int repeats,
00712             unsigned int distance, int id, int loop_ticks, int fadein_ticks)
00713 {
00714     if(files.empty() || distance >= DISTANCE_SILENT || !mix_ok) {
00715         return;
00716     }
00717 
00718     audio_lock lock;
00719 
00720     // find a free channel in the desired group
00721     int channel = Mix_GroupAvailable(group);
00722     if(channel == -1) {
00723         LOG_AUDIO << "All channels dedicated to sound group(" << group << ") are busy, skipping.\n";
00724         return;
00725     }
00726 
00727     Mix_Chunk *chunk;
00728     std::string file = pick_one(files);
00729 
00730     try {
00731         chunk = load_chunk(file, group);
00732         assert(chunk);
00733     } catch(const chunk_load_exception&) {
00734         return;
00735     }
00736 
00737     /*
00738      * This check prevents SDL_Mixer from blowing up on Windows when UI sound is played
00739      * in response to toggling the checkbox which disables sound.
00740      */
00741     if(group != SOUND_UI) {
00742         Mix_SetDistance(channel, distance);
00743     }
00744 
00745     int res;
00746     if(loop_ticks > 0) {
00747         if(fadein_ticks > 0) {
00748             res = Mix_FadeInChannelTimed(channel, chunk, -1, fadein_ticks, loop_ticks);
00749         } else {
00750             res = Mix_PlayChannel(channel, chunk, -1);
00751         }
00752 
00753         if(res >= 0) {
00754             Mix_ExpireChannel(channel, loop_ticks);
00755         }
00756     } else {
00757         if(fadein_ticks > 0) {
00758             res = Mix_FadeInChannel(channel, chunk, repeats, fadein_ticks);
00759         } else {
00760             res = Mix_PlayChannel(channel, chunk, repeats);
00761         }
00762     }
00763 
00764     if(res < 0) {
00765         ERR_AUDIO << "error playing sound effect: " << Mix_GetError() << "\n";
00766         //still keep it in the sound cache, in case we want to try again later
00767         return;
00768     }
00769 
00770     channel_ids[channel] = id;
00771 
00772     //reserve the channel's chunk from being freed, since it is playing
00773     channel_chunks[res] = chunk;
00774 }
00775 
00776 void play_sound(const std::string& files, channel_group group, unsigned int repeats)
00777 {
00778     if(preferences::sound_on()) {
00779         play_sound_internal(files, group, repeats);
00780     }
00781 }
00782 
00783 // Play bell with separate volume setting
00784 void play_bell(const std::string& files)
00785 {
00786     if (preferences::turn_bell()) {
00787         play_sound_internal(files, SOUND_BELL);
00788     }
00789 }
00790 
00791 // Play timer with separate volume setting
00792 void play_timer(const std::string& files, int loop_ticks, int fadein_ticks)
00793 {
00794     if(preferences::sound_on()) {
00795         play_sound_internal(files, SOUND_TIMER, 0, 0, -1, loop_ticks, fadein_ticks);
00796     }
00797 }
00798 
00799 // Play UI sounds on separate volume than soundfx
00800 void play_UI_sound(const std::string& files)
00801 {
00802     if(preferences::UI_sound_on()) {
00803         play_sound_internal(files, SOUND_UI);
00804     }
00805 }
00806 
00807 void set_music_volume(int vol)
00808 {
00809     if(mix_ok && vol >= 0) {
00810         if(vol > MIX_MAX_VOLUME)
00811             vol = MIX_MAX_VOLUME;
00812 
00813         Mix_VolumeMusic(vol);
00814     }
00815 }
00816 
00817 void set_sound_volume(int vol)
00818 {
00819     if(mix_ok && vol >= 0) {
00820         if(vol > MIX_MAX_VOLUME)
00821             vol = MIX_MAX_VOLUME;
00822 
00823         // Bell, timer and UI have separate channels which we can't set up from this
00824         for (unsigned i = 0; i < n_of_channels; ++i){
00825             if(i != UI_sound_channel && i != bell_channel && i != timer_channel) {
00826                 Mix_Volume(i, vol);
00827             }
00828         }
00829     }
00830 }
00831 
00832 /*
00833  * For the purpose of volume setting, we treat turn timer the same as bell
00834  */
00835 void set_bell_volume(int vol)
00836 {
00837     if(mix_ok && vol >= 0) {
00838         if(vol > MIX_MAX_VOLUME)
00839             vol = MIX_MAX_VOLUME;
00840 
00841         Mix_Volume(bell_channel, vol);
00842         Mix_Volume(timer_channel, vol);
00843     }
00844 }
00845 
00846 void set_UI_volume(int vol)
00847 {
00848     if(mix_ok && vol >= 0) {
00849         if(vol > MIX_MAX_VOLUME)
00850             vol = MIX_MAX_VOLUME;
00851 
00852         Mix_Volume(UI_sound_channel, vol);
00853     }
00854 }
00855 
00856 } // end of sound namespace
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:03:10 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs