Reformat sound code

This commit is contained in:
Charles Dang 2017-05-08 06:05:39 +11:00
parent 079b60acbd
commit 23f4eab5db

View file

@ -12,32 +12,32 @@
See the COPYING file for more details.
*/
#include "sound.hpp"
#include "config.hpp"
#include "filesystem.hpp"
#include "preferences/game.hpp"
#include "log.hpp"
#include "preferences/game.hpp"
#include "serialization/string_utils.hpp"
#include "sound.hpp"
#include "sound_music_track.hpp"
#include <SDL_mixer.h>
#include <SDL.h> // Travis doesn't like this, although it works on my machine -> '#include <SDL_sound.h>
#include <SDL_mixer.h>
#include <list>
#include <string>
#include <sstream>
#include <string>
static lg::log_domain log_audio("audio");
#define DBG_AUDIO LOG_STREAM(debug, log_audio)
#define LOG_AUDIO LOG_STREAM(info, log_audio)
#define ERR_AUDIO LOG_STREAM(err, log_audio)
#if (MIX_MAJOR_VERSION < 1) || (MIX_MAJOR_VERSION == 1) && ((MIX_MINOR_VERSION < 2) || (MIX_MINOR_VERSION == 2) && (MIX_PATCHLEVEL <= 11))
#error "Please upgrade to SDL mixer version >= 1.2.12, we don't support older versions anymore."
#endif
namespace sound {
namespace sound
{
// Channel-chunk mapping lets us know, if we can safely free a given chunk
static std::vector<Mix_Chunk*> channel_chunks;
@ -45,18 +45,23 @@ static std::vector<Mix_Chunk*> channel_chunks;
// is playing on a channel for fading/panning)
static std::vector<int> channel_ids;
static void play_sound_internal(const std::string& files, channel_group group, unsigned int repeats=0,
unsigned int distance=0, int id=-1, int loop_ticks=0, int fadein_ticks=0);
static void play_sound_internal(const std::string& files,
channel_group group,
unsigned int repeats = 0,
unsigned int distance = 0,
int id = -1,
int loop_ticks = 0,
int fadein_ticks = 0);
}
namespace {
namespace
{
bool mix_ok = false;
int music_start_time = 0;
unsigned music_refresh = 0;
unsigned music_refresh_rate = 20;
bool want_new_music = false;
int fadingout_time=5000;
int fadingout_time = 5000;
bool no_fading = false;
bool unload_music = false;
@ -80,17 +85,21 @@ const size_t n_reserved_channels = UI_sound_channel_last + 1; // sources, bell,
// Keep this above number of available channels to avoid busy-looping
unsigned max_cached_chunks = 256;
std::map< Mix_Chunk*, int > chunk_usage;
std::map<Mix_Chunk*, int> chunk_usage;
} // end anon namespace
}
static void increment_chunk_usage(Mix_Chunk* mcp) {
static void increment_chunk_usage(Mix_Chunk* mcp)
{
++(chunk_usage[mcp]);
}
static void decrement_chunk_usage(Mix_Chunk* mcp) {
if(mcp == nullptr) return;
std::map< Mix_Chunk*, int >::iterator this_usage = chunk_usage.find(mcp);
static void decrement_chunk_usage(Mix_Chunk* mcp)
{
if(mcp == nullptr) {
return;
}
std::map<Mix_Chunk*, int>::iterator this_usage = chunk_usage.find(mcp);
assert(this_usage != chunk_usage.end());
if(--(this_usage->second) == 0) {
Mix_FreeChunk(mcp);
@ -98,13 +107,21 @@ static void decrement_chunk_usage(Mix_Chunk* mcp) {
}
}
namespace {
class sound_cache_chunk {
namespace
{
class sound_cache_chunk
{
public:
sound_cache_chunk(const std::string& f) : group(sound::NULL_CHANNEL), file(f), data_(nullptr) {}
sound_cache_chunk(const std::string& f)
: group(sound::NULL_CHANNEL)
, file(f)
, data_(nullptr)
{
}
sound_cache_chunk(const sound_cache_chunk& scc)
: group(scc.group), file(scc.file), data_(scc.data_)
: group(scc.group)
, file(scc.file)
, data_(scc.data_)
{
increment_chunk_usage(data_);
}
@ -117,23 +134,30 @@ public:
sound::channel_group group;
std::string file;
void set_data(Mix_Chunk* d) {
void set_data(Mix_Chunk* d)
{
increment_chunk_usage(d);
decrement_chunk_usage(data_);
data_ = d;
}
Mix_Chunk* get_data() const {
Mix_Chunk* get_data() const
{
return data_;
}
bool operator==(sound_cache_chunk const &scc) const {
bool operator==(sound_cache_chunk const& scc) const
{
return file == scc.file;
}
bool operator!=(sound_cache_chunk const &scc) const { return !operator==(scc); }
bool operator!=(sound_cache_chunk const& scc) const
{
return !operator==(scc);
}
sound_cache_chunk& operator=(const sound_cache_chunk& scc) {
sound_cache_chunk& operator=(const sound_cache_chunk& scc)
{
file = scc.file;
group = scc.group;
set_data(scc.get_data());
@ -144,8 +168,8 @@ private:
Mix_Chunk* data_;
};
std::list< sound_cache_chunk > sound_cache;
typedef std::list< sound_cache_chunk >::iterator sound_cache_iterator;
std::list<sound_cache_chunk> sound_cache;
typedef std::list<sound_cache_chunk>::iterator sound_cache_iterator;
std::map<std::string, std::shared_ptr<Mix_Music>> music_cache;
std::vector<std::string> played_before;
@ -162,66 +186,80 @@ std::vector<std::shared_ptr<sound::music_track>> current_track_list;
std::shared_ptr<sound::music_track> current_track;
unsigned int current_track_index = 0;
std::vector<std::shared_ptr<sound::music_track>>::const_iterator find_track(const sound::music_track& track) {
return std::find_if(current_track_list.begin(), current_track_list.end(), [&track](const std::shared_ptr<const sound::music_track>& ptr) {
return *ptr == track;
});
std::vector<std::shared_ptr<sound::music_track>>::const_iterator find_track(const sound::music_track& track)
{
return std::find_if(current_track_list.begin(), current_track_list.end(),
[&track](const std::shared_ptr<const sound::music_track>& ptr) { return *ptr == track; }
);
}
} // end anon namespace
namespace sound
{
unsigned int get_current_track()
{
return current_track_index;
}
namespace sound {
unsigned int get_current_track() {
return current_track_index;
unsigned int get_num_tracks()
{
return current_track_list.size();
}
std::shared_ptr<music_track> get_track(unsigned int i)
{
if(i < current_track_list.size()) {
return current_track_list[i];
}
unsigned int get_num_tracks() {
return current_track_list.size();
if(i == current_track_list.size()) {
return current_track;
}
std::shared_ptr<music_track> get_track(unsigned int i) {
if(i < current_track_list.size()) {
return current_track_list[i];
}
if(i == current_track_list.size()) {
return current_track;
}
return nullptr;
}
return nullptr;
}
void set_track(unsigned int i, const std::shared_ptr<music_track>& to) {
if(i < current_track_list.size() && find_track(*to) != current_track_list.end()) {
current_track_list[i] = std::make_shared<music_track>(*to);
}
}
void remove_track(unsigned int i) {
if(i >= current_track_list.size()) {
return;
}
if(i == current_track_index) {
// Let the track finish playing
current_track->set_play_once(true);
// Set current index to the new size of the list
current_track_index = current_track_list.size() - 1;
} else if(i < current_track_index) {
current_track_index--;
}
current_track_list.erase(current_track_list.begin() + i);
void set_track(unsigned int i, const std::shared_ptr<music_track>& to)
{
if(i < current_track_list.size() && find_track(*to) != current_track_list.end()) {
current_track_list[i] = std::make_shared<music_track>(*to);
}
}
void remove_track(unsigned int i)
{
if(i >= current_track_list.size()) {
return;
}
if(i == current_track_index) {
// Let the track finish playing
current_track->set_play_once(true);
// Set current index to the new size of the list
current_track_index = current_track_list.size() - 1;
} else if(i < current_track_index) {
current_track_index--;
}
current_track_list.erase(current_track_list.begin() + i);
}
} // end namespace sound
static bool track_ok(const std::string& id)
{
LOG_AUDIO << "Considering " << id << "\n";
// If they committed changes to list, we forget previous plays, but
// still *never* repeat same track twice if we have an option.
if (id == current_track->file_path())
if(id == current_track->file_path()) {
return false;
}
if (current_track_list.size() <= 3)
if(current_track_list.size() <= 3) {
return true;
}
// Timothy Pinkham says:
// 1) can't be repeated without 2 other pieces have already played
@ -235,29 +273,29 @@ static bool track_ok(const std::string& id)
std::set<std::string> played;
std::vector<std::string>::reverse_iterator i;
for (i = played_before.rbegin(); i != played_before.rend(); ++i) {
if (*i == id) {
for(i = played_before.rbegin(); i != played_before.rend(); ++i) {
if(*i == id) {
++num_played;
if (num_played == 2)
if(num_played == 2) {
break;
}
} else {
played.insert(*i);
}
}
// If we've played this twice, must have played every other track.
if (num_played == 2 && played.size() != current_track_list.size() - 1) {
LOG_AUDIO << "Played twice with only " << played.size()
<< " tracks between\n";
if(num_played == 2 && played.size() != current_track_list.size() - 1) {
LOG_AUDIO << "Played twice with only " << played.size() << " tracks between\n";
return false;
}
// Check previous previous track not same.
i = played_before.rbegin();
if (i != played_before.rend()) {
if(i != played_before.rend()) {
++i;
if (i != played_before.rend()) {
if (*i == id) {
if(i != played_before.rend()) {
if(*i == id) {
LOG_AUDIO << "Played just before previous\n";
return false;
}
@ -267,60 +305,64 @@ static bool track_ok(const std::string& id)
return true;
}
static std::shared_ptr<sound::music_track> choose_track()
{
assert(!current_track_list.empty());
if (current_track_index >= current_track_list.size()) {
if(current_track_index >= current_track_list.size()) {
current_track_index = 0;
}
if (current_track_list[current_track_index]->shuffle()) {
if(current_track_list[current_track_index]->shuffle()) {
unsigned int track = 0;
if (current_track_list.size() > 1) {
if(current_track_list.size() > 1) {
do {
track = rand()%current_track_list.size();
} while (!track_ok( current_track_list[track]->file_path() ));
track = rand() % current_track_list.size();
} while(!track_ok(current_track_list[track]->file_path()));
}
current_track_index = track;
}
DBG_AUDIO << "Next track will be " << current_track_list[current_track_index]->file_path() << "\n";
played_before.push_back( current_track_list[current_track_index]->file_path() );
played_before.push_back(current_track_list[current_track_index]->file_path());
return current_track_list[current_track_index];
}
static std::string pick_one(const std::string &files)
static std::string pick_one(const std::string& files)
{
std::vector<std::string> ids = utils::square_parenthetical_split(files,',',"[","]");
std::vector<std::string> ids = utils::square_parenthetical_split(files, ',', "[", "]");
if (ids.empty())
if(ids.empty()) {
return "";
if (ids.size() == 1)
}
if(ids.size() == 1) {
return ids[0];
}
// We avoid returning same choice twice if we can avoid it.
static std::map<std::string,unsigned int> prev_choices;
static std::map<std::string, unsigned int> prev_choices;
unsigned int choice;
if (prev_choices.find(files) != prev_choices.end()) {
choice = rand()%(ids.size()-1);
if (choice >= prev_choices[files])
if(prev_choices.find(files) != prev_choices.end()) {
choice = rand() % (ids.size() - 1);
if(choice >= prev_choices[files]) {
++choice;
}
prev_choices[files] = choice;
} else {
choice = rand()%ids.size();
prev_choices.emplace(files,choice);
choice = rand() % ids.size();
prev_choices.emplace(files, choice);
}
return ids[choice];
}
namespace {
namespace
{
struct audio_lock
{
audio_lock()
@ -336,9 +378,8 @@ struct audio_lock
} // end of anonymous namespace
namespace sound {
namespace sound
{
// Removes channel-chunk and channel-id mapping
static void channel_finished_hook(int channel)
{
@ -346,11 +387,14 @@ static void channel_finished_hook(int channel)
channel_ids[channel] = -1;
}
bool init_sound() {
bool init_sound()
{
LOG_AUDIO << "Initializing audio...\n";
if(SDL_WasInit(SDL_INIT_AUDIO) == 0)
if(SDL_InitSubSystem(SDL_INIT_AUDIO) == -1)
if(SDL_WasInit(SDL_INIT_AUDIO) == 0) {
if(SDL_InitSubSystem(SDL_INIT_AUDIO) == -1) {
return false;
}
}
if(!mix_ok) {
if(Mix_OpenAudio(preferences::sample_rate(), MIX_DEFAULT_FORMAT, 2, preferences::sound_buffer_size()) == -1) {
@ -383,18 +427,20 @@ bool init_sound() {
LOG_AUDIO << "Audio initialized.\n";
DBG_AUDIO << "Channel layout: " << n_of_channels << " channels (" << n_reserved_channels << " reserved)\n"
<< " " << bell_channel << " - bell\n"
<< " " << timer_channel << " - timer\n"
<< " " << source_channel_start << ".." << source_channel_last << " - sound sources\n"
<< " " << UI_sound_channel_start << ".." << UI_sound_channel_last << " - UI\n"
<< " " << UI_sound_channel_last + 1 << ".." << n_of_channels - 1 << " - sound effects\n";
<< " " << bell_channel << " - bell\n"
<< " " << timer_channel << " - timer\n"
<< " " << source_channel_start << ".." << source_channel_last << " - sound sources\n"
<< " " << UI_sound_channel_start << ".." << UI_sound_channel_last << " - UI\n"
<< " " << UI_sound_channel_last + 1 << ".." << n_of_channels - 1 << " - sound effects\n";
play_music();
}
return true;
}
void close_sound() {
void close_sound()
{
int frequency, channels;
Uint16 format;
@ -410,53 +456,66 @@ void close_sound() {
if(numtimesopened == 0) {
ERR_AUDIO << "Error closing audio device: " << Mix_GetError() << std::endl;
}
while (numtimesopened) {
while(numtimesopened) {
Mix_CloseAudio();
--numtimesopened;
}
}
if(SDL_WasInit(SDL_INIT_AUDIO) != 0)
if(SDL_WasInit(SDL_INIT_AUDIO) != 0) {
SDL_QuitSubSystem(SDL_INIT_AUDIO);
}
LOG_AUDIO << "Audio device released.\n";
}
void reset_sound() {
void reset_sound()
{
bool music = preferences::music_on();
bool sound = preferences::sound_on();
bool UI_sound = preferences::UI_sound_on();
bool bell = preferences::turn_bell();
if (music || sound || bell || UI_sound) {
if(music || sound || bell || UI_sound) {
sound::close_sound();
if (!sound::init_sound()) {
if(!sound::init_sound()) {
ERR_AUDIO << "Error initializing audio device: " << Mix_GetError() << std::endl;
}
if (!music)
if(!music) {
sound::stop_music();
if (!sound)
}
if(!sound) {
sound::stop_sound();
if (!UI_sound)
}
if(!UI_sound) {
sound::stop_UI_sound();
if (!bell)
}
if(!bell) {
sound::stop_bell();
}
}
}
void stop_music() {
void stop_music()
{
if(mix_ok) {
Mix_FadeOutMusic(500);
Mix_HookMusicFinished([](){ unload_music = true; });
Mix_HookMusicFinished([]() { unload_music = true; });
}
}
void stop_sound() {
void stop_sound()
{
if(mix_ok) {
Mix_HaltGroup(SOUND_SOURCES);
Mix_HaltGroup(SOUND_FX);
sound_cache_iterator itor = sound_cache.begin();
while(itor != sound_cache.end())
{
while(itor != sound_cache.end()) {
if(itor->group == SOUND_SOURCES || itor->group == SOUND_FX) {
itor = sound_cache.erase(itor);
} else {
@ -469,13 +528,13 @@ void stop_sound() {
/*
* For the purpose of channel manipulation, we treat turn timer the same as bell
*/
void stop_bell() {
void stop_bell()
{
if(mix_ok) {
Mix_HaltGroup(SOUND_BELL);
Mix_HaltGroup(SOUND_TIMER);
sound_cache_iterator itor = sound_cache.begin();
while(itor != sound_cache.end())
{
while(itor != sound_cache.end()) {
if(itor->group == SOUND_BELL || itor->group == SOUND_TIMER) {
itor = sound_cache.erase(itor);
} else {
@ -485,12 +544,12 @@ void stop_bell() {
}
}
void stop_UI_sound() {
void stop_UI_sound()
{
if(mix_ok) {
Mix_HaltGroup(SOUND_UI);
sound_cache_iterator itor = sound_cache.begin();
while(itor != sound_cache.end())
{
while(itor != sound_cache.end()) {
if(itor->group == SOUND_UI) {
itor = sound_cache.erase(itor);
} else {
@ -500,7 +559,7 @@ void stop_UI_sound() {
}
}
void play_music_once(const std::string &file)
void play_music_once(const std::string& file)
{
current_track = std::make_shared<music_track>(file);
current_track->set_play_once(true);
@ -518,13 +577,15 @@ void play_music()
if(!current_track) {
return;
}
music_start_time = 1; //immediate (same as effect as SDL_GetTicks())
want_new_music=true;
no_fading=false;
music_start_time = 1; // immediate (same as effect as SDL_GetTicks())
want_new_music = true;
no_fading = false;
fadingout_time = current_track->ms_after();
}
void play_track(unsigned int i) {
void play_track(unsigned int i)
{
if(i >= current_track_list.size()) {
current_track = choose_track();
} else {
@ -536,7 +597,7 @@ void play_track(unsigned int i) {
static void play_new_music()
{
music_start_time = 0; //reset status: no start time
music_start_time = 0; // reset status: no start time
want_new_music = true;
if(!preferences::music_on() || !mix_ok || !current_track->valid()) {
@ -549,38 +610,38 @@ static void play_new_music()
if(itor == music_cache.end()) {
LOG_AUDIO << "attempting to insert track '" << filename << "' into cache\n";
SDL_RWops *rwops = filesystem::load_RWops(filename);
const std::shared_ptr<Mix_Music> music(Mix_LoadMUSType_RW(rwops, MUS_NONE, true), &Mix_FreeMusic); // SDL takes ownership of rwops
SDL_RWops* rwops = filesystem::load_RWops(filename);
// SDL takes ownership of rwops
const std::shared_ptr<Mix_Music> music(Mix_LoadMUSType_RW(rwops, MUS_NONE, true), &Mix_FreeMusic);
if(music == nullptr) {
ERR_AUDIO << "Could not load music file '" << filename << "': "
<< Mix_GetError() << "\n";
ERR_AUDIO << "Could not load music file '" << filename << "': " << Mix_GetError() << "\n";
return;
}
itor = music_cache.emplace(filename, music).first;
}
LOG_AUDIO << "Playing track '" << filename << "'\n";
int fading_time = current_track->ms_before();
if(no_fading)
{
fading_time=0;
if(no_fading) {
fading_time = 0;
}
const int res = Mix_FadeInMusic(itor->second.get(), 1, fading_time);
if(res < 0)
{
ERR_AUDIO << "Could not play music: " << Mix_GetError() << " " << filename <<" " << std::endl;
if(res < 0) {
ERR_AUDIO << "Could not play music: " << Mix_GetError() << " " << filename << " " << std::endl;
}
want_new_music=false;
want_new_music = false;
}
void play_music_repeatedly(const std::string &id)
void play_music_repeatedly(const std::string& id)
{
// Can happen if scenario doesn't specify.
if (id.empty())
if(id.empty()) {
return;
}
current_track_list.clear();
current_track_list.emplace_back(new music_track(id));
@ -595,16 +656,23 @@ void play_music_repeatedly(const std::string &id)
}
}
void play_music_config(const config &music_node, int i)
void play_music_config(const config& music_node, int i)
{
music_track track( music_node );
//
// FIXME: there is a memory leak somewhere in this function, seemingly related to the shared_ptrs
// stored in current_track_list.
//
// vultraz 5/8/2017
//
if (!track.valid() && !track.id().empty()) {
music_track track(music_node);
if(!track.valid() && !track.id().empty()) {
ERR_AUDIO << "cannot open track '" << track.id() << "'; disabled in this playlist." << std::endl;
}
// If they say play once, we don't alter playlist.
if (track.play_once()) {
if(track.play_once()) {
current_track = std::make_shared<music_track>(track);
current_track_index = current_track_list.size();
play_music();
@ -612,7 +680,7 @@ void play_music_config(const config &music_node, int i)
}
// Clear play list unless they specify append.
if (!track.append()) {
if(!track.append()) {
current_track_list.clear();
}
@ -639,27 +707,28 @@ void play_music_config(const config &music_node, int i)
}
// They can tell us to start playing this list immediately.
if (track.immediate()) {
if(track.immediate()) {
current_track = *iter;
current_track_index = iter - current_track_list.begin();
play_music();
} else if (!track.append()) { // Make sure the current track is finished
} else if(!track.append()) { // Make sure the current track is finished
current_track->set_play_once(true);
}
}
void music_thinker::process(events::pump_info &info) {
void music_thinker::process(events::pump_info& info)
{
if(preferences::music_on()) {
if(!music_start_time && !current_track_list.empty() && !Mix_PlayingMusic()) {
// Pick next track, add ending time to its start time.
current_track = choose_track();
music_start_time = info.ticks();
no_fading=true;
fadingout_time=0;
no_fading = true;
fadingout_time = 0;
}
if(music_start_time && info.ticks(&music_refresh, music_refresh_rate) >= music_start_time - fadingout_time) {
want_new_music=true;
want_new_music = true;
}
if(want_new_music) {
@ -672,7 +741,7 @@ void music_thinker::process(events::pump_info &info) {
}
}
if (unload_music) {
if(unload_music) {
// The custom shared_ptr deleter (Mix_FreeMusic) will handle the freeing of each track.
music_cache.clear();
@ -682,24 +751,19 @@ void music_thinker::process(events::pump_info &info) {
}
}
music_muter::music_muter() :
events::sdl_handler(false)
music_muter::music_muter()
: events::sdl_handler(false)
{
join_global();
}
void music_muter::handle_window_event(const SDL_Event& event)
{
if (preferences::stop_music_in_background() && preferences::music_on())
{
if (event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED)
{
if(preferences::stop_music_in_background() && preferences::music_on()) {
if(event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) {
Mix_ResumeMusic();
}
else if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST)
{
if (Mix_PlayingMusic())
{
} else if(event.window.event == SDL_WINDOWEVENT_FOCUS_LOST) {
if(Mix_PlayingMusic()) {
Mix_PauseMusic();
}
}
@ -711,8 +775,9 @@ void commit_music_changes()
played_before.clear();
// Play-once is OK if still playing.
if (current_track->play_once())
if(current_track->play_once()) {
return;
}
// If current track no longer on playlist, change it.
for(auto m : current_track_list) {
@ -722,8 +787,9 @@ void commit_music_changes()
}
// Victory empties playlist: if next scenario doesn't specify one...
if (current_track_list.empty())
if(current_track_list.empty()) {
return;
}
// FIXME: we don't pause ms_before on this first track. Should we?
current_track = choose_track();
@ -743,17 +809,19 @@ void write_music_play_list(config& snapshot)
void reposition_sound(int id, unsigned int distance)
{
audio_lock lock;
for (unsigned ch = 0; ch < channel_ids.size(); ++ch)
{
if (channel_ids[ch] != id) continue;
if (distance >= DISTANCE_SILENT) {
for(unsigned ch = 0; ch < channel_ids.size(); ++ch) {
if(channel_ids[ch] != id) {
continue;
}
if(distance >= DISTANCE_SILENT) {
// Don't call Mix_FadeOutChannel if the channel's volume is set to
// zero. It doesn't do anything in that case and the channel will
// resume playing as soon as its volume is reset to a non-zero
// value, which results in issues like sound sources deleted while
// their volume is zero coming back to life and escaping Wesnoth's
// sound source management code.
if (Mix_Volume(ch, -1) == 0) {
if(Mix_Volume(ch, -1) == 0) {
Mix_HaltChannel(ch);
} else {
Mix_FadeOutChannel(ch, 100);
@ -775,14 +843,16 @@ void stop_sound(int id)
reposition_sound(id, DISTANCE_SILENT);
}
void play_sound_positioned(const std::string &files, int id, int repeats, unsigned int distance)
void play_sound_positioned(const std::string& files, int id, int repeats, unsigned int distance)
{
if(preferences::sound_on()) {
play_sound_internal(files, SOUND_SOURCES, repeats, distance, id);
}
}
struct chunk_load_exception { };
struct chunk_load_exception
{
};
static Mix_Chunk* load_chunk(const std::string& file, channel_group group)
{
@ -793,18 +863,18 @@ static Mix_Chunk* load_chunk(const std::string& file, channel_group group)
it_bgn = sound_cache.begin(), it_end = sound_cache.end();
it = std::find(it_bgn, it_end, temp_chunk);
if (it != it_end) {
if(it != it_end) {
if(it->group != group) {
// cached item has been used in multiple sound groups
it->group = NULL_CHANNEL;
}
//splice the most recently used chunk to the front of the cache
// splice the most recently used chunk to the front of the cache
sound_cache.splice(it_bgn, sound_cache, it);
} else {
// remove the least recently used chunk from cache if it's full
bool cache_full = (sound_cache.size() == max_cached_chunks);
while( cache_full && it != it_bgn ) {
while(cache_full && it != it_bgn) {
// make sure this chunk is not being played before freeing it
std::vector<Mix_Chunk*>::iterator ch_end = channel_chunks.end();
if(std::find(channel_chunks.begin(), ch_end, (--it)->get_data()) == ch_end) {
@ -812,24 +882,25 @@ static Mix_Chunk* load_chunk(const std::string& file, channel_group group)
cache_full = false;
}
}
if(cache_full) {
LOG_AUDIO << "Maximum sound cache size reached and all are busy, skipping.\n";
throw chunk_load_exception();
}
temp_chunk.group = group;
const std::string& filename = filesystem::get_binary_file_location("sounds", file);
if (!filename.empty()) {
SDL_RWops *rwops = filesystem::load_RWops(filename);
if(!filename.empty()) {
SDL_RWops* rwops = filesystem::load_RWops(filename);
temp_chunk.set_data(Mix_LoadWAV_RW(rwops, true)); // SDL takes ownership of rwops
} else {
ERR_AUDIO << "Could not load sound file '" << file << "'." << std::endl;
throw chunk_load_exception();
}
if (temp_chunk.get_data() == nullptr) {
ERR_AUDIO << "Could not load sound file '" << filename << "': "
<< Mix_GetError() << "\n";
if(temp_chunk.get_data() == nullptr) {
ERR_AUDIO << "Could not load sound file '" << filename << "': " << Mix_GetError() << "\n";
throw chunk_load_exception();
}
@ -839,8 +910,13 @@ static Mix_Chunk* load_chunk(const std::string& file, channel_group group)
return sound_cache.begin()->get_data();
}
void play_sound_internal(const std::string& files, channel_group group, unsigned int repeats,
unsigned int distance, int id, int loop_ticks, int fadein_ticks)
void play_sound_internal(const std::string& files,
channel_group group,
unsigned int repeats,
unsigned int distance,
int id,
int loop_ticks,
int fadein_ticks)
{
if(files.empty() || distance >= DISTANCE_SILENT || !mix_ok) {
return;
@ -855,7 +931,7 @@ void play_sound_internal(const std::string& files, channel_group group, unsigned
return;
}
Mix_Chunk *chunk;
Mix_Chunk* chunk;
std::string file = pick_one(files);
try {
@ -894,13 +970,13 @@ void play_sound_internal(const std::string& files, channel_group group, unsigned
if(res < 0) {
ERR_AUDIO << "error playing sound effect: " << Mix_GetError() << std::endl;
//still keep it in the sound cache, in case we want to try again later
// still keep it in the sound cache, in case we want to try again later
return;
}
channel_ids[channel] = id;
//reserve the channel's chunk from being freed, since it is playing
// reserve the channel's chunk from being freed, since it is playing
channel_chunks[res] = chunk;
}
@ -914,7 +990,7 @@ void play_sound(const std::string& files, channel_group group, unsigned int repe
// Play bell with separate volume setting
void play_bell(const std::string& files)
{
if (preferences::turn_bell()) {
if(preferences::turn_bell()) {
play_sound_internal(files, SOUND_BELL);
}
}
@ -940,14 +1016,16 @@ int get_music_volume()
if(mix_ok) {
return Mix_VolumeMusic(-1);
}
return 0;
}
void set_music_volume(int vol)
{
if(mix_ok && vol >= 0) {
if(vol > MIX_MAX_VOLUME)
if(vol > MIX_MAX_VOLUME) {
vol = MIX_MAX_VOLUME;
}
Mix_VolumeMusic(vol);
}
@ -965,14 +1043,14 @@ int get_sound_volume()
void set_sound_volume(int vol)
{
if(mix_ok && vol >= 0) {
if(vol > MIX_MAX_VOLUME)
if(vol > MIX_MAX_VOLUME) {
vol = MIX_MAX_VOLUME;
}
// Bell, timer and UI have separate channels which we can't set up from this
for (unsigned i = 0; i < n_of_channels; ++i){
if(!(i >= UI_sound_channel_start && i <= UI_sound_channel_last)
&& i != bell_channel && i != timer_channel)
{
for(unsigned i = 0; i < n_of_channels; ++i) {
if(!(i >= UI_sound_channel_start && i <= UI_sound_channel_last) && i != bell_channel
&& i != timer_channel) {
Mix_Volume(i, vol);
}
}
@ -985,8 +1063,9 @@ void set_sound_volume(int vol)
void set_bell_volume(int vol)
{
if(mix_ok && vol >= 0) {
if(vol > MIX_MAX_VOLUME)
if(vol > MIX_MAX_VOLUME) {
vol = MIX_MAX_VOLUME;
}
Mix_Volume(bell_channel, vol);
Mix_Volume(timer_channel, vol);
@ -996,13 +1075,14 @@ void set_bell_volume(int vol)
void set_UI_volume(int vol)
{
if(mix_ok && vol >= 0) {
if(vol > MIX_MAX_VOLUME)
if(vol > MIX_MAX_VOLUME) {
vol = MIX_MAX_VOLUME;
}
for (unsigned i = UI_sound_channel_start; i <= UI_sound_channel_last; ++i) {
for(unsigned i = UI_sound_channel_start; i <= UI_sound_channel_last; ++i) {
Mix_Volume(i, vol);
}
}
}
} // end of sound namespace
} // end namespace sound