initial commit, lordsawar source, slightly modified
[lordsawar] / src / sound.cpp
diff --git a/src/sound.cpp b/src/sound.cpp
new file mode 100644 (file)
index 0000000..7f46c20
--- /dev/null
@@ -0,0 +1,326 @@
+// Copyright (C) 2006 Ulf Lorenz
+// Copyright (C) 2006 Andrea Paternesi
+// Copyright (C) 2006, 2007, 2008 Ben Asselstine
+//
+//  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.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
+//  02110-1301, USA.
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <iostream>
+#include <sigc++/functors/mem_fun.h>
+#include "sound.h"
+#include "File.h"
+#include "Configuration.h"
+#include "defs.h"
+
+#define debug(x) {std::cerr<<__FILE__<<": "<<__LINE__<<": "<<x<<std::endl<<std::flush;}
+//#define debug(x)
+
+
+Sound* Sound::s_instance = 0;
+
+#ifdef FL_SOUND
+// used for fading in a music piece after another faded out
+Mix_Music* _my_toplay = 0;
+
+// used for removing a music piece (we only need to have 1 removed at a time
+Mix_Music* _my_toremove = 0;
+
+// how often the music piece is played
+int loopcount=1;
+
+void _startNext()
+{
+    if (_my_toplay != 0)
+    {
+        Mix_FadeInMusic(_my_toplay, loopcount, 1500);
+        _my_toplay = 0;
+    }
+
+    if (_my_toremove != 0)
+    {
+        Mix_FreeMusic(_my_toremove);
+        _my_toremove = 0;
+    }
+
+    Sound::getInstance()->nextPiece();
+}
+#endif
+
+Sound* Sound::getInstance()
+{
+    if (s_instance == 0)
+        s_instance = new Sound();
+
+    return s_instance;
+}
+
+void Sound::deleteInstance()
+{
+    if (s_instance == 0)
+        return;
+
+    delete s_instance;
+    s_instance = 0;
+}
+
+Sound::Sound()
+    :d_broken(false), d_background(false), d_block(false)
+{
+    debug("Sound constructor")
+#ifdef FL_SOUND
+    d_music = 0;
+
+    if (Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2, 1024) == -1)
+    {
+        std::cerr << "Couldn't initialize SDL-mixer\n";
+        std::cerr <<Mix_GetError() <<std::endl;
+        d_broken = true;
+        return;
+    }
+    
+    XML_Helper helper(File::getMusicFile("music.xml"), std::ios::in, false);
+    helper.registerTag("piece", sigc::mem_fun(this, &Sound::loadMusic));
+
+    if (!helper.parse())
+    {
+        std::cerr<< "Error loading music descriptions; disable music\n";
+        d_broken = true;
+        Mix_CloseAudio();
+       return;
+    }
+
+    Mix_HookMusicFinished(_startNext);
+#endif
+    debug("Music list contains " <<d_musicMap.size() <<" entries.")
+    debug("background list has " <<d_bgMap.size() <<" entries.")
+}
+
+Sound::~Sound()
+{
+    debug("Sound destructor")
+#ifdef FL_SOUND
+    haltMusic(false);
+    disableBackground(false);
+    
+    // remove all music pieces
+    std::map<std::string, MusicItem*>::iterator it;
+    for (it = d_musicMap.begin(); it != d_musicMap.end(); it++)
+        delete (*it).second;
+            
+    if (d_music)
+      {
+        Mix_FreeMusic(d_music);
+       d_music = NULL;
+      }
+    
+    //if (!d_broken)
+        //Mix_CloseAudio();
+#endif
+}
+
+bool Sound::setMusic(bool enable, int volume)
+{
+    if (volume < 0 || volume > 128)
+        return false;
+
+    Configuration::s_musicenable = enable;
+    Configuration::s_musicvolume = volume;
+#ifdef FL_SOUND
+    Mix_VolumeMusic(volume);
+#endif
+
+    return true;
+}
+
+bool Sound::isMusicEnabled()
+{
+    return Configuration::s_musicenable;
+}
+
+int Sound::getMusicVolume()
+{
+    return Configuration::s_musicvolume;
+}
+
+bool Sound::playMusic(std::string piece, int nloops, bool fade)
+{
+    debug("playing Music")
+#ifdef FL_SOUND
+    if (d_broken || !Configuration::s_musicenable)
+        return true;
+
+    // first, load the music piece
+    if (d_musicMap[piece] == 0)
+        return false;
+
+    if (Mix_PlayingMusic() || Mix_FadingMusic())
+        haltMusic(false);
+
+    Mix_Music* music = Mix_LoadMUS(File::getMusicFile(d_musicMap[piece]->file).c_str());
+    if (!music)
+    {
+        //try to load an alternative
+        std::string alias = d_musicMap[piece]->alias;
+        music = Mix_LoadMUS(File::getMusicFile(d_musicMap[alias]->file).c_str());
+        if (!music)
+            return false;
+    }
+
+    // now start to block playing music (well, a mutex/synchronization 
+    // would propably be a good idea here)
+    d_block = true;
+
+    // then end other music pieces and/or start ours.
+    if (Mix_PlayingMusic() || Mix_FadingMusic())
+    {
+        _my_toremove = d_music;
+        _my_toplay = music;
+        d_music = music;
+        loopcount = nloops;
+
+        if (fade)
+            Mix_FadeOutMusic(1500);
+        else
+            Mix_HaltMusic();
+        return true;
+    }
+
+    // no other music playing => start ours.
+    Mix_FadeInMusic(music, nloops, 1500);
+#endif
+    return true;
+}
+
+bool Sound::haltMusic(bool fade)
+{
+    debug("stopping music")
+#ifdef FL_SOUND
+    d_block = false;
+
+    if (Mix_PlayingMusic() || Mix_FadingMusic())
+    {
+        _my_toremove = d_music;
+        d_music = 0;
+
+        if (fade)
+            Mix_FadeOutMusic(1500);
+        else
+            Mix_HaltMusic();
+        return true;
+    }
+
+    // no music is playing => call nextPiece manually
+    nextPiece();
+#endif
+    return true;
+}
+
+void Sound::enableBackground()
+{
+    debug("enabling background music")
+    d_background = true;
+    nextPiece();
+}
+
+void Sound::disableBackground(bool fade)
+{
+    debug("disabling background music")
+    d_background = false;
+
+    if (d_block)
+        return;
+
+#ifdef FL_SOUND
+    if (Mix_PlayingMusic() || Mix_FadingMusic())
+    {
+        _my_toremove = d_music;
+        d_music = 0;
+
+        if (fade)
+            Mix_FadeOutMusic(1500);
+        else
+            Mix_HaltMusic();
+    }
+#endif
+}
+
+void Sound::nextPiece()
+{
+    debug("Sound::nextPiece")
+    if (d_block || !d_background || !isMusicEnabled())
+        return;
+
+#ifdef FL_SOUND
+    if (Mix_PlayingMusic() || Mix_FadingMusic())
+        return;
+
+    // remove the old music
+    if (d_music)
+      {
+        Mix_FreeMusic(d_music);
+       d_music = NULL;
+      }
+
+    // select a random music piece from the list of background pieces
+    while (!d_music && !d_bgMap.empty())
+    {
+        int select = rand() % d_bgMap.size();
+        d_music = Mix_LoadMUS(File::getMusicFile(d_musicMap[d_bgMap[select]]->file).c_str());
+        if (!d_music)
+        {
+            // file does not exist. Remove this entry from the list
+            d_bgMap[select] = d_bgMap[0];
+            d_bgMap.erase(d_bgMap.begin());
+        }
+    }
+
+    if (!d_music)
+        // well, no background music without sound files...
+        return;
+
+    Mix_FadeInMusic(d_music, 1, 1500);
+#endif
+}
+
+bool Sound::loadMusic(std::string tag, XML_Helper* helper)
+{
+    if (tag != "piece")
+    {
+        std::cerr <<"Loading music: Wrong tag name\n";
+        return false;
+    }
+
+    std::string tagname;
+    MusicItem* item = new MusicItem();
+    
+    bool retval = true;
+    retval &= helper->getData(tagname, "name");
+    retval &= helper->getData(item->file, "filename");
+    retval &= helper->getData(item->background, "background");
+    retval &= helper->getData(item->alias, "alias");
+    
+    if (retval)
+    {
+        d_musicMap[tagname] = item;
+        if (item->background)
+            d_bgMap.push_back(tagname);
+    }
+    
+    return retval;
+}