CMusicHandler.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. #include "../stdafx.h"
  2. #include <sstream>
  3. #include <boost/assign/std/vector.hpp>
  4. #include <boost/assign/list_of.hpp>
  5. #include <boost/bimap.hpp>
  6. #include <SDL_mixer.h>
  7. #include "CSndHandler.h"
  8. #include "CMusicHandler.h"
  9. #include "CCreatureHandler.h"
  10. #include "../CGameInfo.h"
  11. /*
  12. * CMusicHandler.cpp, part of VCMI engine
  13. *
  14. * Authors: listed in file AUTHORS in main folder
  15. *
  16. * License: GNU General Public License v2.0 or later
  17. * Full text of license available in license.txt file, in main folder
  18. *
  19. */
  20. using namespace boost::assign;
  21. static boost::bimap<soundBase::soundID, std::string> sounds;
  22. // Not pretty, but there's only one music handler object in the game.
  23. static void musicFinishedCallbackC(void) {
  24. CGI->audioh->musicFinishedCallback();
  25. }
  26. void CSoundHandler::initSounds()
  27. {
  28. // Map sound names
  29. #define VCMI_SOUND_NAME(x) ( soundBase::x,
  30. #define VCMI_SOUND_FILE(y) #y )
  31. sounds = boost::assign::list_of<boost::bimap<soundBase::soundID, std::string>::relation>
  32. VCMI_SOUND_LIST;
  33. #undef VCMI_SOUND_NAME
  34. #undef VCMI_SOUND_FILE
  35. // Vectors for helper(s)
  36. pickup_sounds += soundBase::pickup01, soundBase::pickup02, soundBase::pickup03,
  37. soundBase::pickup04, soundBase::pickup05, soundBase::pickup06, soundBase::pickup07;
  38. horseSounds += // must be the same order as terrains (see EtrrainType);
  39. soundBase::horseDirt, soundBase::horseSand, soundBase::horseGrass,
  40. soundBase::horseSnow, soundBase::horseSwamp, soundBase::horseRough,
  41. soundBase::horseSubterranean, soundBase::horseLava,
  42. soundBase::horseWater, soundBase::horseRock;
  43. // Load sounds
  44. sndh = new CSndHandler(std::string(DATA_DIR "Data" PATHSEPARATOR "Heroes3.snd"));
  45. }
  46. void CSoundHandler::freeSounds()
  47. {
  48. Mix_HaltChannel(-1);
  49. delete sndh;
  50. std::map<soundBase::soundID, Mix_Chunk *>::iterator it;
  51. for (it=soundChunks.begin(); it != soundChunks.end(); it++) {
  52. if (it->second)
  53. Mix_FreeChunk(it->second);
  54. }
  55. }
  56. // Allocate an SDL chunk and cache it.
  57. Mix_Chunk *CSoundHandler::GetSoundChunk(soundBase::soundID soundID)
  58. {
  59. // Find its name
  60. boost::bimap<soundBase::soundID, std::string>::left_iterator it;
  61. it = sounds.left.find(soundID);
  62. if (it == sounds.left.end())
  63. return NULL;
  64. // Load and insert
  65. int size;
  66. const char *data = sndh->extract(it->second, size);
  67. if (!data)
  68. return NULL;
  69. SDL_RWops *ops = SDL_RWFromConstMem(data, size);
  70. Mix_Chunk *chunk;
  71. chunk = Mix_LoadWAV_RW(ops, 1); // will free ops
  72. if (!chunk) {
  73. tlog1 << "Unable to mix sound" << it->second << "(" << Mix_GetError() << ")" << std::endl;
  74. return NULL;
  75. }
  76. soundChunks.insert(std::pair<soundBase::soundID, Mix_Chunk *>(soundID, chunk));
  77. return chunk;
  78. }
  79. // Get a soundID given a filename
  80. soundBase::soundID CSoundHandler::getSoundID(std::string &fileName)
  81. {
  82. boost::bimap<soundBase::soundID, std::string>::right_iterator it;
  83. it = sounds.right.find(fileName);
  84. if (it == sounds.right.end())
  85. return soundBase::invalid;
  86. else
  87. return it->second;
  88. }
  89. void CSoundHandler::initCreaturesSounds(std::vector<CCreature> &creatures)
  90. {
  91. tlog5 << "\t\tReading config/cr_sounds.txt" << std::endl;
  92. std::ifstream ifs("config/cr_sounds.txt");
  93. std::string line;
  94. while(getline(ifs, line))
  95. {
  96. std::string cname="", attack="", defend="", killed="", move="",
  97. shoot="", wince="", ext1="", ext2="";
  98. std::stringstream str(line);
  99. str >> cname >> attack >> defend >> killed >> move >> shoot >> wince >> ext1 >> ext2;
  100. if (cname[0] == '#')
  101. // That's a comment. Discard.
  102. continue;
  103. if (str.good() || (str.eof() && wince != ""))
  104. {
  105. int id = CGI->creh->nameToID[cname];
  106. CCreature &c = CGI->creh->creatures[id];
  107. if (c.sounds.killed != soundBase::invalid)
  108. tlog1 << "Creature << " << cname << " already has sounds" << std::endl;
  109. c.sounds.attack = getSoundID(attack);
  110. c.sounds.defend = getSoundID(defend);
  111. c.sounds.killed = getSoundID(killed);
  112. c.sounds.move = getSoundID(move);
  113. c.sounds.shoot = getSoundID(shoot);
  114. c.sounds.wince = getSoundID(wince);
  115. c.sounds.ext1 = getSoundID(ext1);
  116. c.sounds.ext2 = getSoundID(ext2);
  117. // Special creatures
  118. if (c.idNumber == 55 || // Archdevil
  119. c.idNumber == 62 || // Vampire
  120. c.idNumber == 62) // Vampire Lord
  121. {
  122. c.sounds.startMoving = c.sounds.ext1;
  123. c.sounds.endMoving = c.sounds.ext2;
  124. }
  125. }
  126. }
  127. ifs.close();
  128. ifs.clear();
  129. // Find creatures without sounds
  130. for(unsigned int i=0;i<CGI->creh->creatures.size();i++)
  131. {
  132. // Note: this will exclude war machines, but it's better
  133. // than nothing.
  134. if (vstd::contains(CGI->creh->notUsedMonsters, i))
  135. continue;
  136. CCreature &c = CGI->creh->creatures[i];
  137. if (c.sounds.killed == soundBase::invalid)
  138. tlog1 << "creature " << c.idNumber << " doesn't have sounds" << std::endl;
  139. }
  140. }
  141. // Plays a sound, and return its channel so we can fade it out later
  142. int CSoundHandler::playSound(soundBase::soundID soundID, int repeats)
  143. {
  144. int channel;
  145. Mix_Chunk *chunk;
  146. std::map<soundBase::soundID, Mix_Chunk *>::iterator it;
  147. if (!sndh)
  148. return -1;
  149. chunk = GetSoundChunk(soundID);
  150. if (chunk)
  151. {
  152. channel = Mix_PlayChannel(-1, chunk, repeats);
  153. if (channel == -1)
  154. tlog1 << "Unable to play sound file " << soundID << std::endl;
  155. } else {
  156. channel = -1;
  157. }
  158. return channel;
  159. }
  160. // Helper. Randomly select a sound from an array and play it
  161. int CSoundHandler::playSoundFromSet(std::vector<soundBase::soundID> &sound_vec)
  162. {
  163. return playSound(sound_vec[rand() % sound_vec.size()]);
  164. }
  165. void CSoundHandler::stopSound( int handler )
  166. {
  167. if (handler != -1)
  168. Mix_HaltChannel(handler);
  169. }
  170. void CMusicHandler::initMusics()
  171. {
  172. // Map music IDs
  173. #define VCMI_MUSIC_ID(x) ( musicBase::x ,
  174. #define VCMI_MUSIC_FILE(y) y )
  175. musics = map_list_of
  176. VCMI_MUSIC_LIST;
  177. #undef VCMI_MUSIC_NAME
  178. #undef VCMI_MUSIC_FILE
  179. Mix_HookMusicFinished(musicFinishedCallbackC);
  180. // Vector for helper
  181. battleMusics += musicBase::combat1, musicBase::combat2,
  182. musicBase::combat3, musicBase::combat4;
  183. }
  184. void CMusicHandler::freeMusics()
  185. {
  186. Mix_HookMusicFinished(NULL);
  187. musicMutex.lock();
  188. if (currentMusic) {
  189. Mix_HaltMusic();
  190. Mix_FreeMusic(currentMusic);
  191. }
  192. if (nextMusic)
  193. Mix_FreeMusic(nextMusic);
  194. musicMutex.unlock();
  195. }
  196. // Plays a music
  197. // loop: -1 always repeats, 0=do not play, 1+=number of loops
  198. void CMusicHandler::playMusic(musicBase::musicID musicID, int loop)
  199. {
  200. std::string filename = DATA_DIR "Mp3" PATHSEPARATOR;
  201. filename += musics[musicID];
  202. musicMutex.lock();
  203. if (nextMusic) {
  204. // There's already a music queued, so remove it
  205. Mix_FreeMusic(nextMusic);
  206. nextMusic = NULL;
  207. }
  208. if (currentMusic) {
  209. // A music is already playing. Stop it and the callback will
  210. // start the new one
  211. nextMusic = Mix_LoadMUS(filename.c_str());
  212. nextMusicLoop = loop;
  213. Mix_FadeOutMusic(1000);
  214. } else {
  215. currentMusic = Mix_LoadMUS(filename.c_str());
  216. if (Mix_PlayMusic(currentMusic, loop) == -1)
  217. tlog1 << "Unable to play sound file " << musicID << "(" << Mix_GetError() << ")" << std::endl;
  218. }
  219. musicMutex.unlock();
  220. }
  221. // Helper. Randomly select a music from an array and play it
  222. void CMusicHandler::playMusicFromSet(std::vector<musicBase::musicID> &music_vec, int loop)
  223. {
  224. playMusic(music_vec[rand() % music_vec.size()], loop);
  225. }
  226. // Stop and free the current music
  227. void CMusicHandler::stopMusic(int fade_ms)
  228. {
  229. musicMutex.lock();
  230. if (currentMusic) {
  231. Mix_FadeOutMusic(fade_ms);
  232. }
  233. musicMutex.unlock();
  234. }
  235. // Called by SDL when a music finished.
  236. void CMusicHandler::musicFinishedCallback(void)
  237. {
  238. musicMutex.lock();
  239. if (currentMusic) {
  240. Mix_FreeMusic(currentMusic);
  241. currentMusic = NULL;
  242. }
  243. if (nextMusic) {
  244. currentMusic = nextMusic;
  245. nextMusic = NULL;
  246. if (Mix_PlayMusic(currentMusic, nextMusicLoop) == -1)
  247. tlog1 << "Unable to play music (" << Mix_GetError() << ")" << std::endl;
  248. }
  249. musicMutex.unlock();
  250. }
  251. void CAudioHandler::initAudio()
  252. {
  253. if (audioInitialized)
  254. return;
  255. if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024)==-1)
  256. {
  257. tlog1 << "Mix_OpenAudio error: %s!!!" << Mix_GetError() << std::endl;
  258. return;
  259. }
  260. audioInitialized = true;
  261. initSounds();
  262. initMusics();
  263. }
  264. CAudioHandler::~CAudioHandler()
  265. {
  266. if (!audioInitialized)
  267. return;
  268. freeSounds();
  269. freeMusics();
  270. Mix_CloseAudio();
  271. }