CMusicHandler.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. #include "../stdafx.h"
  2. #include <sstream>
  3. #include <boost/assign/std/vector.hpp>
  4. #include <boost/assign/list_of.hpp>
  5. #include <SDL_mixer.h>
  6. #include "CSndHandler.h"
  7. #include "CMusicHandler.h"
  8. #include "CCreatureHandler.h"
  9. #include "../CGameInfo.h"
  10. /*
  11. * CMusicHandler.cpp, part of VCMI engine
  12. *
  13. * Authors: listed in file AUTHORS in main folder
  14. *
  15. * License: GNU General Public License v2.0 or later
  16. * Full text of license available in license.txt file, in main folder
  17. *
  18. */
  19. using namespace boost::assign;
  20. void CMusicHandler::initMusics()
  21. {
  22. if(Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024)==-1)
  23. {
  24. printf("Mix_OpenAudio error: %s!!!\n", Mix_GetError());
  25. return;
  26. }
  27. atexit(Mix_CloseAudio);
  28. // Map sound names
  29. #define VCMI_SOUND_NAME(x) ( soundBase::x,
  30. #define VCMI_SOUND_FILE(y) cachedSounds(#y, 0) )
  31. sounds = boost::assign::map_list_of
  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. // Create reverse map. It's used during game init to map names to internal IDs
  44. std::map<soundBase::soundNames, cachedSounds>::iterator it;
  45. for ( it=sounds.begin() ; it != sounds.end(); it++ )
  46. reverse_sounds[(*it).second.filename] = (*it).first;
  47. //AITheme0 = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "AITheme0.mp3");
  48. //AITheme1 = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "AITHEME1.mp3");
  49. //AITheme2 = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "AITHEME2.mp3");
  50. //buildTown = Mix_LoadWAV("MP3" PATHSEPARATOR "BUILDTWN.wav");
  51. //combat1 = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "COMBAT01.mp3");
  52. //combat2 = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "COMBAT02.mp3");
  53. //combat3 = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "COMBAT03.mp3");
  54. //combat4 = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "COMBAT04.mp3");
  55. //castleTown = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "CstleTown.mp3");
  56. //defendCastle = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "Defend Castle.mp3");
  57. //dirt = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "DIRT.mp3");
  58. //dungeon = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "DUNGEON.mp3");
  59. //elemTown = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "ElemTown.mp3");
  60. //evilTheme = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "EvilTheme.mp3");
  61. //fortressTown = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "FortressTown.mp3");
  62. //goodTheme = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "GoodTheme.mp3");
  63. //grass = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "GRASS.mp3");
  64. //infernoTown = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "InfernoTown.mp3");
  65. //lava = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "LAVA.mp3");
  66. //loopLepr = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "LoopLepr.mp3");
  67. //loseCampain = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "Lose Campain.mp3");
  68. //loseCastle = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "LoseCastle.mp3");
  69. //loseCombat = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "LoseCombat.mp3");
  70. //mainMenu = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "MAINMENU.mp3");
  71. //mainMenuWoG = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "MainMenuWoG.mp3");
  72. //necroTown = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "necroTown.mp3");
  73. //neutralTheme = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "NeutralTheme.mp3");
  74. //rampart = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "RAMPART.mp3");
  75. //retreatBattle = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "Retreat Battle.mp3");
  76. //rough = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "ROUGH.mp3");
  77. //sand = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "SAND.mp3");
  78. //secretTheme = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "SecretTheme.mp3");
  79. //snow = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "SNOW.mp3");
  80. //stronghold = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "StrongHold.mp3");
  81. //surrenderBattle = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "Surrender Battle.mp3");
  82. //swamp = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "SWAMP.mp3");
  83. //towerTown = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "TowerTown.mp3");
  84. //ultimateLose = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "UltimateLose.mp3");
  85. //underground = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "Underground.mp3");
  86. //water = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "WATER.mp3");
  87. //winBattle = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "Win Battle.mp3");
  88. //winScenario = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "Win Scenario.mp3");
  89. // Load sounds
  90. sndh = new CSndHandler(std::string(DATA_DIR "Data" PATHSEPARATOR "Heroes3.snd"));
  91. }
  92. // Return an SDL chunk. Allocate if it is not cached yet.
  93. Mix_Chunk *CMusicHandler::GetSoundChunk(std::string srcName)
  94. {
  95. int size;
  96. const char *data = sndh->extract(srcName, size);
  97. SDL_RWops *ops;
  98. Mix_Chunk *chunk;
  99. if (!data)
  100. return NULL;
  101. ops = SDL_RWFromConstMem(data, size);
  102. chunk = Mix_LoadWAV_RW(ops, 1); // will free ops
  103. if (!chunk)
  104. fprintf(stderr, "Unable to mix: %s\n",
  105. Mix_GetError());
  106. return chunk;
  107. }
  108. soundBase::soundNames CMusicHandler::getSoundID(std::string &fileName)
  109. {
  110. std::map<std::string, soundBase::soundNames>::iterator it;
  111. it = reverse_sounds.find(fileName);
  112. if (it == reverse_sounds.end())
  113. return soundBase::invalid;
  114. else
  115. return it->second;
  116. }
  117. void CMusicHandler::initCreaturesSounds(std::vector<CCreature> &creatures)
  118. {
  119. tlog5 << "\t\tReading config/cr_sounds.txt" << std::endl;
  120. std::ifstream ifs("config/cr_sounds.txt");
  121. std::string line;
  122. while(getline(ifs, line))
  123. {
  124. std::string cname="", attack="", defend="", killed="", move="",
  125. shoot="", wince="", ext1="", ext2="";
  126. std::stringstream str(line);
  127. str >> cname >> attack >> defend >> killed >> move >> shoot >> wince >> ext1 >> ext2;
  128. if (cname[0] == '#')
  129. // That's a comment. Discard.
  130. continue;
  131. if (str.good() || (str.eof() && wince != ""))
  132. {
  133. int id = CGI->creh->nameToID[cname];
  134. CCreature &c = CGI->creh->creatures[id];
  135. if (c.sounds.killed != soundBase::invalid)
  136. tlog1 << "Creature << " << cname << " already has sounds" << std::endl;
  137. c.sounds.attack = getSoundID(attack);
  138. c.sounds.defend = getSoundID(defend);
  139. c.sounds.killed = getSoundID(killed);
  140. c.sounds.move = getSoundID(move);
  141. c.sounds.shoot = getSoundID(shoot);
  142. c.sounds.wince = getSoundID(wince);
  143. c.sounds.ext1 = getSoundID(ext1);
  144. c.sounds.ext2 = getSoundID(ext2);
  145. }
  146. }
  147. ifs.close();
  148. ifs.clear();
  149. // Find creatures without sounds
  150. for(unsigned int i=0;i<CGI->creh->creatures.size();i++)
  151. {
  152. // Note: this will exclude war machines, but it's better
  153. // than nothing.
  154. if (vstd::contains(CGI->creh->notUsedMonsters, i))
  155. continue;
  156. CCreature &c = CGI->creh->creatures[i];
  157. if (c.sounds.killed == soundBase::invalid)
  158. tlog1 << "creature " << c.idNumber << " doesn't have sounds" << std::endl;
  159. }
  160. }
  161. // Plays a sound, and return its channel so we can fade it out later
  162. int CMusicHandler::playSound(soundBase::soundNames soundID, int repeats)
  163. {
  164. int channel;
  165. if (!sndh)
  166. return -1;
  167. std::map<soundBase::soundNames, cachedSounds>::iterator it;
  168. it = sounds.find(soundID);
  169. if (it == sounds.end())
  170. return -1;
  171. class cachedSounds sound = it->second;
  172. if (!sound.chunk) {
  173. sound.chunk = GetSoundChunk(sound.filename);
  174. }
  175. if (sound.chunk)
  176. {
  177. channel = Mix_PlayChannel(-1, sound.chunk, repeats);
  178. if(channel == -1)
  179. {
  180. fprintf(stderr, "Unable to play WAV file("DATA_DIR "Data" PATHSEPARATOR "Heroes3.wav::%s): %s\n",
  181. sound.filename.c_str(),Mix_GetError());
  182. }
  183. }
  184. return channel;
  185. }
  186. // Helper. Randomly select a sound from an array and play it
  187. int CMusicHandler::playSoundFromSet(std::vector<soundBase::soundNames> &sound_vec)
  188. {
  189. return playSound(sound_vec[rand() % sound_vec.size()]);
  190. }
  191. void CMusicHandler::stopSound( int handler )
  192. {
  193. if (handler != -1)
  194. Mix_HaltChannel(handler);
  195. }