00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "global.hpp"
00016 #include "events.hpp"
00017 #include "filesystem.hpp"
00018 #include "game_config.hpp"
00019 #include "game_preferences.hpp"
00020 #include "log.hpp"
00021 #include "random.hpp"
00022 #include "sound.hpp"
00023
00024 #include "SDL.h"
00025 #include "SDL_mixer.h"
00026
00027 #include <cassert>
00028 #include <iostream>
00029 #include <map>
00030 #include <list>
00031
00032 #define LOG_AUDIO LOG_STREAM(info, audio)
00033 #define ERR_AUDIO LOG_STREAM(err, audio)
00034
00035 namespace sound {
00036
00037 std::vector<Mix_Chunk*> channel_chunks;
00038
00039
00040
00041 std::vector<int> channel_ids;
00042
00043 static void play_sound_internal(const std::string& files, channel_group group, unsigned int repeats=0,
00044 unsigned int distance=0, int id=-1, int loop_ticks=0, int fadein_ticks=0);
00045 }
00046
00047 namespace {
00048
00049 bool mix_ok = false;
00050 int music_start_time = 0;
00051 unsigned music_refresh = 0;
00052 unsigned music_refresh_rate = 20;
00053 bool want_new_music = false;
00054 int fadingout_time=5000;
00055 bool no_fading = false;
00056
00057
00058 const size_t n_of_channels = 16;
00059
00060
00061 const size_t bell_channel = 0;
00062 const size_t timer_channel = 1;
00063
00064
00065 const size_t source_channels = n_of_channels - 8;
00066 const size_t source_channel_start = timer_channel + 1;
00067 const size_t source_channel_last = source_channel_start + source_channels - 1;
00068 const size_t UI_sound_channel = source_channel_last + 1;
00069 const size_t n_reserved_channels = UI_sound_channel + 1;
00070
00071
00072
00073 #ifdef LOW_MEM
00074 unsigned max_cached_chunks = 64;
00075 #else
00076 unsigned max_cached_chunks = 256;
00077 #endif
00078
00079 std::map< Mix_Chunk*, int > chunk_usage;
00080
00081 }
00082
00083 static void increment_chunk_usage(Mix_Chunk* mcp) {
00084 ++(chunk_usage[mcp]);
00085 }
00086
00087 static void decrement_chunk_usage(Mix_Chunk* mcp) {
00088 if(mcp == NULL) return;
00089 std::map< Mix_Chunk*, int >::iterator this_usage = chunk_usage.find(mcp);
00090 assert(this_usage != chunk_usage.end());
00091 if(--(this_usage->second) == 0) {
00092 Mix_FreeChunk(mcp);
00093 chunk_usage.erase(this_usage);
00094 }
00095 }
00096
00097 namespace {
00098
00099 class sound_cache_chunk {
00100 public:
00101 sound_cache_chunk(const std::string& f) : group(sound::NULL_CHANNEL), file(f), data_(NULL) {}
00102 sound_cache_chunk(const sound_cache_chunk& scc)
00103 : group(scc.group), file(scc.file), data_(scc.data_)
00104 {
00105 increment_chunk_usage(data_);
00106 }
00107
00108 ~sound_cache_chunk()
00109 {
00110 decrement_chunk_usage(data_);
00111 }
00112
00113 sound::channel_group group;
00114 std::string file;
00115
00116 void set_data(Mix_Chunk* d) {
00117 increment_chunk_usage(d);
00118 decrement_chunk_usage(data_);
00119 data_ = d;
00120 }
00121
00122 Mix_Chunk* get_data() const {
00123 return data_;
00124 }
00125
00126 bool operator==(sound_cache_chunk const &scc) const {
00127 return file == scc.file;
00128 }
00129
00130 bool operator!=(sound_cache_chunk const &scc) const { return !operator==(scc); }
00131
00132 sound_cache_chunk& operator=(const sound_cache_chunk& scc) {
00133 file = scc.file;
00134 group = scc.group;
00135 set_data(scc.get_data());
00136 return *this;
00137 }
00138
00139 private:
00140 Mix_Chunk* data_;
00141 };
00142
00143 std::list< sound_cache_chunk > sound_cache;
00144 typedef std::list< sound_cache_chunk >::iterator sound_cache_iterator;
00145 std::map<std::string,Mix_Music*> music_cache;
00146
00147 struct music_track
00148 {
00149 music_track(const std::string &tname);
00150 music_track(const std::string &tname,
00151 const std::string &ms_before_str,
00152 const std::string &ms_after_str);
00153 void write(config &snapshot, bool append);
00154
00155 std::string name;
00156 unsigned int ms_before, ms_after;
00157 bool once;
00158 };
00159
00160 std::vector<std::string> played_before;
00161
00162 music_track::music_track(const std::string &tname)
00163 : name(tname), ms_before(0), ms_after(0), once(false)
00164 {
00165 }
00166
00167 music_track::music_track(const std::string &tname,
00168 const std::string &ms_before_str,
00169 const std::string &ms_after_str)
00170 : name(tname), once(false)
00171 {
00172 if (ms_before_str.empty())
00173 ms_before = 0;
00174 else
00175 ms_before = lexical_cast<int,std::string>(ms_before_str);
00176
00177 if (ms_after_str.empty())
00178 ms_after = 0;
00179 else
00180 ms_after = lexical_cast<int,std::string>(ms_after_str);
00181 }
00182
00183 void music_track::write(config &snapshot, bool append)
00184 {
00185 config& m = snapshot.add_child("music");
00186 m["name"] = name;
00187 m["ms_before"] = lexical_cast<std::string>(ms_before);
00188 m["ms_after"] = lexical_cast<std::string>(ms_after);
00189 if (append)
00190 m["append"] = "yes";
00191 }
00192
00193 std::vector<music_track> current_track_list;
00194 struct music_track current_track("");
00195 struct music_track last_track("");
00196
00197 }
00198
00199 static bool track_ok(const std::string &name)
00200 {
00201 LOG_AUDIO << "Considering " << name << "\n";
00202
00203
00204
00205 if (name == current_track.name)
00206 return false;
00207
00208 if (current_track_list.size() <= 3)
00209 return true;
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219 unsigned int num_played = 0;
00220 std::set<std::string> played;
00221 std::vector<std::string>::reverse_iterator i;
00222
00223 for (i = played_before.rbegin(); i != played_before.rend(); i++) {
00224 if (*i == name) {
00225 num_played++;
00226 if (num_played == 2)
00227 break;
00228 } else {
00229 played.insert(*i);
00230 }
00231 }
00232
00233
00234 if (num_played == 2 && played.size() != current_track_list.size() - 1) {
00235 LOG_AUDIO << "Played twice with only "
00236 << lexical_cast<std::string>(played.size())
00237 << " tracks between\n";
00238 return false;
00239 }
00240
00241
00242 i = played_before.rbegin();
00243 if (i != played_before.rend()) {
00244 i++;
00245 if (i != played_before.rend()) {
00246 if (*i == name) {
00247 LOG_AUDIO << "Played just before previous\n";
00248 return false;
00249 }
00250 }
00251 }
00252
00253 return true;
00254 }
00255
00256
00257 static const music_track &choose_track()
00258 {
00259 assert(!current_track_list.empty());
00260
00261 std::string name;
00262 unsigned int track = 0;
00263
00264 if (current_track_list.size() > 1) {
00265 do {
00266 track = rand()%current_track_list.size();
00267 } while (!track_ok(current_track_list[track].name));
00268 }
00269
00270
00271 played_before.push_back(current_track_list[track].name);
00272 return current_track_list[track];
00273 }
00274
00275 static std::string pick_one(const std::string &files)
00276 {
00277 std::vector<std::string> names = utils::split(files);
00278
00279 if (names.size() == 0)
00280 return "";
00281 if (names.size() == 1)
00282 return names[0];
00283
00284 #ifdef LOW_MEM
00285
00286 return names[0];
00287 #endif
00288
00289
00290 static std::map<std::string,unsigned int> prev_choices;
00291 unsigned int choice;
00292
00293 if (prev_choices.find(files) != prev_choices.end()) {
00294 choice = rand()%(names.size()-1);
00295 if (choice >= prev_choices[files])
00296 choice++;
00297 prev_choices[files] = choice;
00298 } else {
00299 choice = rand()%names.size();
00300 prev_choices.insert(std::pair<std::string,unsigned int>(files,choice));
00301 }
00302
00303 return names[choice];
00304 }
00305
00306 namespace {
00307
00308 struct audio_lock
00309 {
00310 audio_lock()
00311 {
00312 SDL_LockAudio();
00313 }
00314
00315 ~audio_lock()
00316 {
00317 SDL_UnlockAudio();
00318 }
00319 };
00320
00321 }
00322
00323
00324 namespace sound {
00325
00326
00327 static void channel_finished_hook(int channel)
00328 {
00329 channel_chunks[channel] = NULL;
00330 channel_ids[channel] = -1;
00331 }
00332
00333 bool init_sound() {
00334 LOG_AUDIO << "Initializing audio...\n";
00335 if(SDL_WasInit(SDL_INIT_AUDIO) == 0)
00336 if(SDL_InitSubSystem(SDL_INIT_AUDIO) == -1)
00337 return false;
00338
00339 if(!mix_ok) {
00340 if(Mix_OpenAudio(preferences::sample_rate(), MIX_DEFAULT_FORMAT, 2, preferences::sound_buffer_size()) == -1) {
00341 mix_ok = false;
00342 ERR_AUDIO << "Could not initialize audio: " << Mix_GetError() << "\n";
00343 return false;
00344 }
00345
00346 mix_ok = true;
00347 Mix_AllocateChannels(n_of_channels);
00348 Mix_ReserveChannels(n_reserved_channels);
00349
00350 channel_chunks.clear();
00351 channel_chunks.resize(n_of_channels, NULL);
00352 channel_ids.resize(n_of_channels, -1);
00353
00354 Mix_GroupChannel(bell_channel, SOUND_BELL);
00355 Mix_GroupChannel(timer_channel, SOUND_TIMER);
00356 Mix_GroupChannels(source_channel_start, source_channel_last, SOUND_SOURCES);
00357 Mix_GroupChannel(UI_sound_channel, SOUND_UI);
00358 Mix_GroupChannels(n_reserved_channels, n_of_channels - 1, SOUND_FX);
00359
00360 set_sound_volume(preferences::sound_volume());
00361 set_UI_volume(preferences::UI_volume());
00362 set_music_volume(preferences::music_volume());
00363 set_bell_volume(preferences::bell_volume());
00364
00365 Mix_ChannelFinished(channel_finished_hook);
00366
00367 LOG_AUDIO << "Audio initialized.\n";
00368
00369 play_music();
00370 }
00371 return true;
00372 }
00373
00374 void close_sound() {
00375 int numtimesopened, frequency, channels;
00376 Uint16 format;
00377
00378 if(mix_ok) {
00379 stop_bell();
00380 stop_UI_sound();
00381 stop_sound();
00382 sound_cache.clear();
00383 stop_music();
00384 mix_ok = false;
00385
00386 numtimesopened = Mix_QuerySpec(&frequency, &format, &channels);
00387 if(numtimesopened == 0) {
00388 ERR_AUDIO << "Error closing audio device: " << Mix_GetError() << "\n";
00389 }
00390 while (numtimesopened) {
00391 Mix_CloseAudio();
00392 --numtimesopened;
00393 }
00394 }
00395 if(SDL_WasInit(SDL_INIT_AUDIO) != 0)
00396 SDL_QuitSubSystem(SDL_INIT_AUDIO);
00397
00398 LOG_AUDIO << "Audio device released.\n";
00399 }
00400
00401 void reset_sound() {
00402 bool music = preferences::music_on();
00403 bool sound = preferences::sound_on();
00404 bool UI_sound = preferences::UI_sound_on();
00405 bool bell = preferences::turn_bell();
00406
00407 if (music || sound || bell || UI_sound) {
00408 sound::close_sound();
00409 if (!sound::init_sound()) {
00410 ERR_AUDIO << "Error initializing audio device: " << Mix_GetError() << "\n";
00411 }
00412 if (!music)
00413 sound::stop_music();
00414 if (!sound)
00415 sound::stop_sound();
00416 if (!UI_sound)
00417 sound::stop_UI_sound();
00418 if (!bell)
00419 sound::stop_bell();
00420 }
00421 }
00422
00423 void stop_music() {
00424 if(mix_ok) {
00425 Mix_HaltMusic();
00426
00427 std::map<std::string,Mix_Music*>::iterator i;
00428 for(i = music_cache.begin(); i != music_cache.end(); ++i)
00429 Mix_FreeMusic(i->second);
00430 music_cache.clear();
00431 }
00432 }
00433
00434 void stop_sound() {
00435 if(mix_ok) {
00436 Mix_HaltGroup(SOUND_SOURCES);
00437 Mix_HaltGroup(SOUND_FX);
00438 sound_cache_iterator itor = sound_cache.begin();
00439 while(itor != sound_cache.end())
00440 {
00441 if(itor->group == SOUND_SOURCES || itor->group == SOUND_FX) {
00442 itor = sound_cache.erase(itor);
00443 } else {
00444 ++itor;
00445 }
00446 }
00447 }
00448 }
00449
00450
00451
00452
00453 void stop_bell() {
00454 if(mix_ok) {
00455 Mix_HaltGroup(SOUND_BELL);
00456 Mix_HaltGroup(SOUND_TIMER);
00457 sound_cache_iterator itor = sound_cache.begin();
00458 while(itor != sound_cache.end())
00459 {
00460 if(itor->group == SOUND_BELL || itor->group == SOUND_TIMER) {
00461 itor = sound_cache.erase(itor);
00462 } else {
00463 ++itor;
00464 }
00465 }
00466 }
00467 }
00468
00469 void stop_UI_sound() {
00470 if(mix_ok) {
00471 Mix_HaltGroup(SOUND_UI);
00472 sound_cache_iterator itor = sound_cache.begin();
00473 while(itor != sound_cache.end())
00474 {
00475 if(itor->group == SOUND_UI) {
00476 itor = sound_cache.erase(itor);
00477 } else {
00478 ++itor;
00479 }
00480 }
00481 }
00482 }
00483
00484 void play_music_once(const std::string &file)
00485 {
00486
00487 current_track_list = std::vector<music_track>();
00488 current_track = music_track(file);
00489 play_music();
00490 }
00491
00492 void play_no_music()
00493 {
00494 current_track_list = std::vector<music_track>();
00495 current_track = music_track("");
00496
00497 if (preferences::music_on() && mix_ok && Mix_PlayingMusic()) {
00498 Mix_FadeOutMusic(500);
00499 }
00500 }
00501
00502 void play_music()
00503 {
00504 music_start_time = 1;
00505 want_new_music=true;
00506 no_fading=false;
00507 fadingout_time=current_track.ms_after;
00508 }
00509
00510 static void play_new_music()
00511 {
00512 music_start_time = 0;
00513 want_new_music = true;
00514
00515 if(!preferences::music_on() || !mix_ok || current_track.name.empty())
00516 return;
00517
00518 std::map<std::string,Mix_Music*>::const_iterator itor = music_cache.find(current_track.name);
00519 if(itor == music_cache.end()) {
00520 const std::string& filename = get_binary_file_location("music", current_track.name);
00521
00522 if(filename.empty()) {
00523 ERR_AUDIO << "Could not open track '" << current_track.name << "'\n";
00524 return;
00525 }
00526
00527 Mix_Music* const music = Mix_LoadMUS(filename.c_str());
00528 if(music == NULL) {
00529 ERR_AUDIO << "Could not load music file '" << filename << "': "
00530 << Mix_GetError() << "\n";
00531 return;
00532 }
00533 itor = music_cache.insert(std::pair<std::string,Mix_Music*>(current_track.name,music)).first;
00534 last_track=current_track;
00535 }
00536
00537 LOG_AUDIO << "Playing track '" << current_track.name << "'\n";
00538 int fading_time=current_track.ms_before;
00539 if(no_fading)
00540 {
00541 fading_time=0;
00542 }
00543
00544 const int res = Mix_FadeInMusic(itor->second, 1, fading_time);
00545 if(res < 0)
00546 {
00547 ERR_AUDIO << "Could not play music: " << Mix_GetError() << " " << current_track.name <<" \n";
00548 }
00549
00550 want_new_music=false;
00551 }
00552
00553 void play_music_repeatedly(const std::string &name)
00554 {
00555
00556 if (name.empty())
00557 return;
00558
00559 current_track_list = std::vector<music_track>(1, music_track(name));
00560
00561
00562 if (current_track.name != name) {
00563 current_track = music_track(name);
00564 play_music();
00565 }
00566 }
00567
00568 void play_music_config(const config &music)
00569 {
00570 struct music_track track(music["name"], music["ms_before"], music["ms_after"]);
00571
00572
00573 if (utils::string_bool(music["play_once"])) {
00574 current_track = track;
00575 current_track.once = true;
00576 play_music();
00577 return;
00578 }
00579
00580
00581 if (!utils::string_bool(music["append"])) {
00582 current_track_list = std::vector<music_track>();
00583 }
00584
00585
00586
00587
00588 std::vector<music_track>::const_iterator itor = current_track_list.begin();
00589 while(itor != current_track_list.end()) {
00590 if(itor->name == track.name) break;
00591 ++itor;
00592 }
00593
00594 if(itor == current_track_list.end()) {
00595 current_track_list.push_back(track);
00596 } else {
00597 ERR_AUDIO << "Tried to add duplicate track '" << track.name << "'\n";
00598 }
00599
00600
00601 if (utils::string_bool(music["immediate"])) {
00602 current_track = track;
00603 play_music();
00604 }
00605 }
00606
00607 void music_thinker::process(events::pump_info &info) {
00608 if(preferences::music_on()) {
00609 if(!music_start_time && !current_track_list.empty() && !Mix_PlayingMusic()) {
00610
00611 current_track = choose_track();
00612 music_start_time = info.ticks();
00613 no_fading=true;
00614 fadingout_time=0;
00615 }
00616
00617 if(music_start_time && info.ticks(&music_refresh, music_refresh_rate) >= music_start_time - fadingout_time) {
00618 want_new_music=true;
00619 }
00620
00621 if(want_new_music) {
00622 if(Mix_PlayingMusic()) {
00623 Mix_FadeOutMusic(fadingout_time);
00624 }
00625 play_new_music();
00626 }
00627 }
00628 }
00629
00630 void commit_music_changes()
00631 {
00632 std::vector<music_track>::iterator i;
00633
00634
00635 played_before = std::vector<std::string>();
00636
00637
00638 if (current_track.once)
00639 return;
00640
00641
00642 for (i = current_track_list.begin(); i != current_track_list.end(); i++) {
00643 if (current_track.name == i->name)
00644 return;
00645 }
00646
00647
00648 if (current_track_list.empty())
00649 return;
00650
00651
00652 current_track = choose_track();
00653 play_music();
00654 }
00655
00656 void write_music_play_list(config& snapshot)
00657 {
00658 std::vector<music_track>::iterator i;
00659 bool append = false;
00660
00661
00662 for (i = current_track_list.begin(); i != current_track_list.end(); i++) {
00663 i->write(snapshot, append);
00664 append = true;
00665 }
00666 }
00667
00668 void reposition_sound(int id, unsigned int distance)
00669 {
00670 audio_lock lock();
00671 for(unsigned int ch = 0; ch < channel_ids.size(); ++ch) {
00672 int& ch_id = channel_ids[ch];
00673 if(ch_id == id) {
00674 if(distance >= DISTANCE_SILENT) {
00675 Mix_FadeOutChannel(ch, 100);
00676 }
00677 else {
00678 Mix_SetDistance(ch, distance);
00679 }
00680 }
00681 }
00682 }
00683
00684 bool is_sound_playing(int id)
00685 {
00686 audio_lock lock();
00687 return std::find(channel_ids.begin(), channel_ids.end(), id) != channel_ids.end();
00688 }
00689
00690 void stop_sound(int id)
00691 {
00692 reposition_sound(id, DISTANCE_SILENT);
00693 }
00694
00695 void play_sound_positioned(const std::string &files, int id, int repeats, unsigned int distance)
00696 {
00697 if(preferences::sound_on()) {
00698 play_sound_internal(files, SOUND_SOURCES, repeats, distance, id);
00699 }
00700 }
00701
00702 struct chunk_load_exception { };
00703
00704 static Mix_Chunk* load_chunk(const std::string& file, channel_group group)
00705 {
00706 sound_cache_iterator it_bgn, it_end;
00707 sound_cache_iterator it;
00708
00709 sound_cache_chunk temp_chunk(file);
00710 it_bgn = sound_cache.begin(), it_end = sound_cache.end();
00711 it = std::find(it_bgn, it_end, temp_chunk);
00712
00713 if (it != it_end) {
00714 if(it->group != group) {
00715
00716 it->group = NULL_CHANNEL;
00717 }
00718
00719
00720 sound_cache.splice(it_bgn, sound_cache, it);
00721 } else {
00722
00723 bool cache_full = (sound_cache.size() == max_cached_chunks);
00724 while( cache_full && it != it_bgn ) {
00725
00726 std::vector<Mix_Chunk*>::iterator ch_end = channel_chunks.end();
00727 if(std::find(channel_chunks.begin(), ch_end, (--it)->get_data()) == ch_end) {
00728 sound_cache.erase(it);
00729 cache_full = false;
00730 }
00731 }
00732 if(cache_full) {
00733 LOG_AUDIO << "Maximum sound cache size reached and all are busy, skipping.\n";
00734 throw chunk_load_exception();
00735 }
00736 temp_chunk.group = group;
00737 std::string const &filename = get_binary_file_location("sounds", file);
00738
00739 if (!filename.empty()) {
00740 temp_chunk.set_data(Mix_LoadWAV(filename.c_str()));
00741 } else {
00742 ERR_AUDIO << "Could not load sound with empty filename\n";
00743 throw chunk_load_exception();
00744 }
00745
00746 if (temp_chunk.get_data() == NULL) {
00747 ERR_AUDIO << "Could not load sound file '" << filename << "': "
00748 << Mix_GetError() << "\n";
00749 throw chunk_load_exception();
00750 }
00751
00752 sound_cache.push_front(temp_chunk);
00753 }
00754
00755 return sound_cache.begin()->get_data();
00756 }
00757
00758 void play_sound_internal(const std::string& files, channel_group group, unsigned int repeats,
00759 unsigned int distance, int id, int loop_ticks, int fadein_ticks)
00760 {
00761 if(files.empty() || distance >= DISTANCE_SILENT || !mix_ok) {
00762 return;
00763 }
00764
00765 audio_lock lock();
00766
00767
00768 int channel = Mix_GroupAvailable(group);
00769 if(channel == -1) {
00770 LOG_AUDIO << "All channels dedicated to sound group(" << group << ") are busy, skipping.\n";
00771 return;
00772 }
00773
00774 Mix_Chunk *chunk;
00775 std::string file = pick_one(files);
00776
00777 try {
00778 chunk = load_chunk(file, group);
00779 assert(chunk);
00780 } catch(const chunk_load_exception& e) {
00781 return;
00782 }
00783
00784
00785
00786
00787
00788 if(group != SOUND_UI) {
00789 Mix_SetDistance(channel, distance);
00790 }
00791
00792 int res;
00793 if(loop_ticks > 0) {
00794 if(fadein_ticks > 0) {
00795 res = Mix_FadeInChannelTimed(channel, chunk, -1, fadein_ticks, loop_ticks);
00796 } else {
00797 res = Mix_PlayChannel(channel, chunk, -1);
00798 }
00799
00800 if(res >= 0) {
00801 Mix_ExpireChannel(channel, loop_ticks);
00802 }
00803 } else {
00804 if(fadein_ticks > 0) {
00805 res = Mix_FadeInChannel(channel, chunk, repeats, fadein_ticks);
00806 } else {
00807 res = Mix_PlayChannel(channel, chunk, repeats);
00808 }
00809 }
00810
00811 if(res < 0) {
00812 ERR_AUDIO << "error playing sound effect: " << Mix_GetError() << "\n";
00813
00814 return;
00815 }
00816
00817 channel_ids[channel] = id;
00818
00819
00820 channel_chunks[res] = chunk;
00821 }
00822
00823 void play_sound(const std::string& files, channel_group group, unsigned int repeats)
00824 {
00825 if(preferences::sound_on()) {
00826 play_sound_internal(files, group, repeats);
00827 }
00828 }
00829
00830
00831 void play_bell(const std::string& files)
00832 {
00833 if(preferences::sound_on() && preferences::turn_bell()) {
00834 play_sound_internal(files, SOUND_BELL);
00835 }
00836 }
00837
00838
00839 void play_timer(const std::string& files, int loop_ticks, int fadein_ticks)
00840 {
00841 if(preferences::sound_on()) {
00842 play_sound_internal(files, SOUND_TIMER, 0, 0, -1, loop_ticks, fadein_ticks);
00843 }
00844 }
00845
00846
00847 void play_UI_sound(const std::string& files)
00848 {
00849 if(preferences::UI_sound_on()) {
00850 play_sound_internal(files, SOUND_UI);
00851 }
00852 }
00853
00854
00855 void set_music_volume(int vol)
00856 {
00857 if(mix_ok && vol >= 0) {
00858 if(vol > MIX_MAX_VOLUME)
00859 vol = MIX_MAX_VOLUME;
00860
00861 Mix_VolumeMusic(vol);
00862 }
00863 }
00864
00865 void set_sound_volume(int vol)
00866 {
00867 if(mix_ok && vol >= 0) {
00868 if(vol > MIX_MAX_VOLUME)
00869 vol = MIX_MAX_VOLUME;
00870
00871
00872 for (unsigned i = 0; i < n_of_channels; i++){
00873 if(i != UI_sound_channel && i != bell_channel && i != timer_channel) {
00874 Mix_Volume(i, vol);
00875 }
00876 }
00877 }
00878 }
00879
00880
00881
00882
00883 void set_bell_volume(int vol)
00884 {
00885 if(mix_ok && vol >= 0) {
00886 if(vol > MIX_MAX_VOLUME)
00887 vol = MIX_MAX_VOLUME;
00888
00889 Mix_Volume(bell_channel, vol);
00890 Mix_Volume(timer_channel, vol);
00891 }
00892 }
00893
00894 void set_UI_volume(int vol)
00895 {
00896 if(mix_ok && vol >= 0) {
00897 if(vol > MIX_MAX_VOLUME)
00898 vol = MIX_MAX_VOLUME;
00899
00900 Mix_Volume(UI_sound_channel, vol);
00901 }
00902 }
00903
00904 }