Преглед изворни кода

Sound patches from Ubuntux #7 - #10:
* Some sound code cleanups
* Renamed soundBase::soundNames into soundBase::soundID
* Add a music handler destructor
* Add Archdevil and vampire pre and post movement sounds

I've applied minor change to fix CMusicHandler - GeniusAI conflict: moved sounds bimap to the .cpp file.

(vcmi_sounds_cleanup.diff
vcmi_sounds_cleanup2.diff
vcmi_sounds_cleanup3.diff
vcmi_add_sounds.diff)

Michał W. Urbańczyk пре 16 година
родитељ
комит
df25dd7efb

+ 1 - 1
AI/EmptyAI/CEmptyAI.h

@@ -15,7 +15,7 @@ public:
 	void showSelDialog(std::string text, std::vector<CSelectableComponent*> & components, int askID){};
 	void tileRevealed(int3 pos){};
 	void tileHidden(int3 pos){};
-	void showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, soundBase::soundNames soundID, bool selection, bool cancel){};
+	void showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, soundBase::soundID soundID, bool selection, bool cancel){};
 	void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, boost::function<void()> &onEnd){};
 	void heroGotLevel(const CGHeroInstance *hero, int pskill, std::vector<ui16> &skills, boost::function<void(ui32)> &callback);
 };

+ 1 - 1
AI/GeniusAI/CGeniusAI.cpp

@@ -69,7 +69,7 @@ void GeniusAI::CGeniusAI::showGarrisonDialog( const CArmedInstance *up, const CG
 	onEnd();
 }
 
-void CGeniusAI::showBlockingDialog( const std::string &text, const std::vector<Component> &components, ui32 askID, soundBase::soundNames soundID, bool selection, bool cancel )
+void CGeniusAI::showBlockingDialog( const std::string &text, const std::vector<Component> &components, ui32 askID, soundBase::soundID soundID, bool selection, bool cancel )
 {
 	m_cb->selectionMade(cancel ? 0 : 1, askID);
 }

+ 1 - 1
AI/GeniusAI/CGeniusAI.h

@@ -190,7 +190,7 @@ public:
 	virtual void heroMoved(const HeroMoveDetails &);
 	virtual void heroPrimarySkillChanged(const CGHeroInstance * hero, int which, int val) {};
 	virtual void showSelDialog(std::string text, std::vector<CSelectableComponent*> & components, int askID){};
-	virtual void showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, soundBase::soundNames soundID, bool selection, bool cancel);
+	virtual void showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, soundBase::soundID soundID, bool selection, bool cancel);
 	virtual void tileRevealed(int3 pos){};
 	virtual void tileHidden(int3 pos){};
 	virtual void heroGotLevel(const CGHeroInstance *hero, int pskill, std::vector<ui16> &skills, boost::function<void(ui32)> &callback);

+ 6 - 0
CBattleInterface.cpp

@@ -1095,6 +1095,8 @@ void CBattleInterface::stackMoved(int number, int destHex, bool endMoving, int d
 
 	if(startMoving) //animation of starting move; some units don't have this animation (ie. halberdier)
 	{
+		if (movedStack->creature->sounds.startMoving)
+			CGI->mush->playSound(movedStack->creature->sounds.startMoving);
 		handleStartMoving(number);
 	}
 	if(moveStarted)
@@ -1194,6 +1196,10 @@ void CBattleInterface::stackMoved(int number, int destHex, bool endMoving, int d
 	{
 		if(creAnims[number]->framesInGroup(21)!=0) // some units don't have this animation (ie. halberdier)
 		{
+			if (movedStack->creature->sounds.endMoving) {
+				CGI->mush->playSound(movedStack->creature->sounds.endMoving);
+			}
+
 			creAnims[number]->setType(21);
 
 			//for(int i=0; i<creAnims[number]->framesInGroup(21)*getAnimSpeedMultiplier()-1; ++i)

+ 2 - 2
CGameInterface.h

@@ -80,10 +80,10 @@ public:
 	virtual void heroVisitsTown(const CGHeroInstance* hero, const CGTownInstance * town){};
 	virtual void init(ICallback * CB){};
 	virtual void receivedResource(int type, int val){};
-	virtual void showInfoDialog(const std::string &text, const std::vector<Component*> &components, soundBase::soundNames soundID){};
+	virtual void showInfoDialog(const std::string &text, const std::vector<Component*> &components, soundBase::soundID soundID){};
 	//virtual void showSelDialog(const std::string &text, const std::vector<Component*> &components, ui32 askID){};
 	//virtual void showYesNoDialog(const std::string &text, const std::vector<Component*> &components, ui32 askID){};
-	virtual void showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, const soundBase::soundNames soundID, bool selection, bool cancel) = 0; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
+	virtual void showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, const soundBase::soundID soundID, bool selection, bool cancel) = 0; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
 	virtual void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, boost::function<void()> &onEnd) = 0; //all stacks operations between these objects become allowed, interface has to call onEnd when done
 	virtual void tileHidden(const std::set<int3> &pos){};
 	virtual void tileRevealed(const std::set<int3> &pos){};

+ 3 - 3
CPlayerInterface.cpp

@@ -2409,7 +2409,7 @@ void CPlayerInterface::showComp(SComponent comp)
 	adventureInt->infoBar.showComp(&comp,4000);
 }
 
-void CPlayerInterface::showInfoDialog(const std::string &text, const std::vector<Component*> &components, soundBase::soundNames soundID)
+void CPlayerInterface::showInfoDialog(const std::string &text, const std::vector<Component*> &components, soundBase::soundID soundID)
 {
 	std::vector<SComponent*> intComps;
 	for(int i=0;i<components.size();i++)
@@ -2417,7 +2417,7 @@ void CPlayerInterface::showInfoDialog(const std::string &text, const std::vector
 	showInfoDialog(text,intComps,soundID);
 }
 
-void CPlayerInterface::showInfoDialog(const std::string &text, const std::vector<SComponent*> & components, soundBase::soundNames soundID)
+void CPlayerInterface::showInfoDialog(const std::string &text, const std::vector<SComponent*> & components, soundBase::soundID soundID)
 {
 	{
 		boost::unique_lock<boost::mutex> un(showingDialog->mx);
@@ -2463,7 +2463,7 @@ void CPlayerInterface::showYesNoDialog(const std::string &text, const std::vecto
 	LOCPLINT->pushInt(temp);
 }
 
-void CPlayerInterface::showBlockingDialog( const std::string &text, const std::vector<Component> &components, ui32 askID, soundBase::soundNames soundID, bool selection, bool cancel )
+void CPlayerInterface::showBlockingDialog( const std::string &text, const std::vector<Component> &components, ui32 askID, soundBase::soundID soundID, bool selection, bool cancel )
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
 

+ 3 - 3
CPlayerInterface.h

@@ -592,10 +592,10 @@ public:
 	void heroMovePointsChanged(const CGHeroInstance * hero);
 	void heroVisitsTown(const CGHeroInstance* hero, const CGTownInstance * town);
 	void receivedResource(int type, int val);
-	void showInfoDialog(const std::string &text, const std::vector<Component*> &components, soundBase::soundNames soundID);
+	void showInfoDialog(const std::string &text, const std::vector<Component*> &components, soundBase::soundID soundID);
 	//void showSelDialog(const std::string &text, const std::vector<Component*> &components, ui32 askID);
 	//void showYesNoDialog(const std::string &text, const std::vector<Component*> &components, ui32 askID);
-	void showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, soundBase::soundNames soundID, bool selection, bool cancel); //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
+	void showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, soundBase::soundID soundID, bool selection, bool cancel); //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
 	void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, boost::function<void()> &onEnd);
 	void tileHidden(const std::set<int3> &pos);
 	void tileRevealed(const std::set<int3> &pos);
@@ -636,7 +636,7 @@ public:
 	void handleMouseMotion(SDL_Event *sEvent);
 	void init(ICallback * CB);
 	int3 repairScreenPos(int3 pos); //returns position closest to pos we can center screen on
-	void showInfoDialog(const std::string &text, const std::vector<SComponent*> & components, soundBase::soundNames soundID);
+	void showInfoDialog(const std::string &text, const std::vector<SComponent*> & components, soundBase::soundID soundID);
 	void showYesNoDialog(const std::string &text, const std::vector<SComponent*> & components, CFunctionList<void()> onYes, CFunctionList<void()> onNo, bool DelComps); //deactivateCur - whether current main interface should be deactivated; delComps - if components will be deleted on window close
 	bool moveHero(const CGHeroInstance *h, CPath path);
 

+ 2 - 2
client/NetPacksClient.cpp

@@ -266,7 +266,7 @@ void InfoWindow::applyCl( CClient *cl )
 	std::string str = toString(text);
 
 	if(vstd::contains(cl->playerint,player))
-		cl->playerint[player]->showInfoDialog(str,comps,(soundBase::soundNames)soundID);
+		cl->playerint[player]->showInfoDialog(str,comps,(soundBase::soundID)soundID);
 	else
 		tlog2 << "We received InfoWindow for not our player...\n";
 }
@@ -285,7 +285,7 @@ void BlockingDialog::applyCl( CClient *cl )
 {
 	std::string str = toString(text);
 	if(vstd::contains(cl->playerint,player))
-		cl->playerint[player]->showBlockingDialog(str,components,id,(soundBase::soundNames)soundID,selection(),cancel());
+		cl->playerint[player]->showBlockingDialog(str,components,id,(soundBase::soundID)soundID,selection(),cancel());
 	else
 		tlog2 << "We received YesNoDialog for not our player...\n";
 }

+ 1 - 1
config/cr_sounds.txt

@@ -96,7 +96,7 @@ Monk MONKATTK.wav MONKDFND.wav MONKKILL.wav MONKMOVE.wav MONKSHOT.wav MONKWNCE.w
 Mummy MUMYATTK.wav MUMYDFND.wav MUMYKILL.wav MUMYMOVE.wav invalid MUMYWNCE.wav
 NagaGuardian NGRDATTK.wav NGRDDFND.wav NGRDKILL.wav NGRDMOVE.wav invalid NGRDWNCE.wav
 NagaSentinel NSENATTK.wav NSENDFND.wav NSENKILL.wav NSENMOVE.wav invalid NSENWNCE.wav
-Nosferatu NOSFATTK.wav NOSFDFND.wav NOSFEXT1.wav NOSFEXT2.wav NOSFKILL.wav NOSFMOVE.wav NOSFSHOT.wav NOSFWNCE.wav
+Nosferatu NOSFATTK.wav NOSFDFND.wav NOSFKILL.wav NOSFMOVE.wav NOSFSHOT.wav NOSFWNCE.wav NOSFEXT1.wav NOSFEXT2.wav 
 ObsidianGargoyle OGRGATTK.wav OGRGDFND.wav OGRGKILL.wav OGRGMOVE.wav invalid OGRGWNCE.wav
 Ogre OGREATTK.wav OGREDFND.wav OGREKILL.wav OGREMOVE.wav invalid OGREWNCE.wav
 OgreMage OGRMATTK.wav OGRMDFND.wav OGRMKILL.wav OGRMMOVE.wav OGRMSHOT.wav OGRMWNCE.wav

+ 2 - 0
hch/CCreatureHandler.cpp

@@ -107,6 +107,8 @@ void CCreatureHandler::loadCreatures()
 		ncre.sounds.wince = soundBase::invalid;
 		ncre.sounds.ext1 = soundBase::invalid;
 		ncre.sounds.ext2 = soundBase::invalid;
+		ncre.sounds.startMoving = soundBase::invalid;
+		ncre.sounds.endMoving = soundBase::invalid;
 
 		int befi=i;
 		for(i; i<andame; ++i)

+ 10 - 8
hch/CCreatureHandler.h

@@ -46,14 +46,16 @@ public:
 
 	// Sound infos
 	struct {
-		soundBase::soundNames attack;
-		soundBase::soundNames defend;
-		soundBase::soundNames killed; // was killed died
-		soundBase::soundNames move;
-		soundBase::soundNames shoot; // range attack
-		soundBase::soundNames wince; // attacked but did not die
-		soundBase::soundNames ext1;	 // creature specific extension
-		soundBase::soundNames ext2;	 // creature specific extension
+		soundBase::soundID attack;
+		soundBase::soundID defend;
+		soundBase::soundID killed; // was killed died
+		soundBase::soundID move;
+		soundBase::soundID shoot; // range attack
+		soundBase::soundID wince; // attacked but did not die
+		soundBase::soundID ext1;  // creature specific extension
+		soundBase::soundID ext2;  // creature specific extension
+		soundBase::soundID startMoving; // usually same as ext1
+		soundBase::soundID endMoving;	// usually same as ext2
 	} sounds;
 
 	bool isDoubleWide() const; //returns true if unit is double wide on battlefield

+ 73 - 42
hch/CMusicHandler.cpp

@@ -3,6 +3,7 @@
 #include <sstream>
 #include <boost/assign/std/vector.hpp> 
 #include <boost/assign/list_of.hpp>
+#include <boost/bimap.hpp>
 
 #include <SDL_mixer.h>
 
@@ -23,19 +24,44 @@
 
 using namespace boost::assign;
 
+boost::bimap<soundBase::soundID, std::string> sounds;
+
+CMusicHandler::~CMusicHandler()
+{
+	if (!audioInit)
+		return;
+
+	if (sndh) {
+		Mix_HaltChannel(-1);
+		delete sndh;
+	}
+
+	std::map<soundBase::soundID, Mix_Chunk *>::iterator it;
+	for (it=soundChunks.begin(); it != soundChunks.end(); it++) {
+		if (it->second)
+			Mix_FreeChunk(it->second);
+	}
+
+	Mix_CloseAudio();
+}
+
 void CMusicHandler::initMusics()
 {
+	if (audioInit)
+		return;
+
 	if(Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024)==-1)
 	{
 		printf("Mix_OpenAudio error: %s!!!\n", Mix_GetError());
 		return;
 	}
-	atexit(Mix_CloseAudio);
+
+	audioInit = true;
 
 	// Map sound names
 #define VCMI_SOUND_NAME(x) ( soundBase::x,
-#define VCMI_SOUND_FILE(y) cachedSounds(#y, 0) )
-	sounds = boost::assign::map_list_of
+#define VCMI_SOUND_FILE(y) #y )
+	sounds = boost::assign::list_of<boost::bimap<soundBase::soundID, std::string>::relation>
 		VCMI_SOUND_LIST;
 #undef VCMI_SOUND_NAME
 #undef VCMI_SOUND_FILE
@@ -49,11 +75,6 @@ void CMusicHandler::initMusics()
 		soundBase::horseSubterranean, soundBase::horseLava,
 		soundBase::horseWater, soundBase::horseRock;
 
-	// Create reverse map. It's used during game init to map names to internal IDs
-	std::map<soundBase::soundNames, cachedSounds>::iterator it;
-	for ( it=sounds.begin() ; it != sounds.end(); it++ )
-		reverse_sounds[(*it).second.filename] = (*it).first;
-
 	//AITheme0 = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "AITheme0.mp3");
 	//AITheme1 = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "AITHEME1.mp3");
 	//AITheme2 = Mix_LoadMUS(DATA_DIR "MP3" PATHSEPARATOR "AITHEME2.mp3");
@@ -101,33 +122,42 @@ void CMusicHandler::initMusics()
 	sndh = new CSndHandler(std::string(DATA_DIR "Data" PATHSEPARATOR "Heroes3.snd"));
 }
 
-// Return an SDL chunk. Allocate if it is not cached yet.
-Mix_Chunk *CMusicHandler::GetSoundChunk(std::string srcName)
+// Allocate an SDL chunk and cache it.
+Mix_Chunk *CMusicHandler::GetSoundChunk(soundBase::soundID soundID)
 {
-	int size;
-	const char *data = sndh->extract(srcName, size);
-	SDL_RWops *ops;
-	Mix_Chunk *chunk;
+	// Find its name
+	boost::bimap<soundBase::soundID, std::string>::left_iterator it;
+	it = sounds.left.find(soundID);
+	if (it == sounds.left.end())
+		return NULL;
 
+	// Load and insert
+	int size;
+	const char *data = sndh->extract(it->second, size);
 	if (!data)
 		return NULL;
 
-	ops = SDL_RWFromConstMem(data, size);
+	SDL_RWops *ops = SDL_RWFromConstMem(data, size);
+	Mix_Chunk *chunk;
 	chunk = Mix_LoadWAV_RW(ops, 1);	// will free ops
 
-	if (!chunk)
-		fprintf(stderr, "Unable to mix: %s\n",
-				Mix_GetError());
+	if (!chunk) {
+		tlog1 << "Unable to mix sound" << it->second << "(" << Mix_GetError() << ")" << std::endl;
+		return NULL;
+	}
+
+	soundChunks.insert(std::pair<soundBase::soundID, Mix_Chunk *>(soundID, chunk));
 
 	return chunk;
 }
 
-soundBase::soundNames CMusicHandler::getSoundID(std::string &fileName)
+// Get a soundID given a filename
+soundBase::soundID CMusicHandler::getSoundID(std::string &fileName)
 {
-	std::map<std::string, soundBase::soundNames>::iterator it;
+	boost::bimap<soundBase::soundID, std::string>::right_iterator it;
 
-	it = reverse_sounds.find(fileName);
-	if (it == reverse_sounds.end())
+	it = sounds.right.find(fileName);
+	if (it == sounds.right.end())
 		return soundBase::invalid;
 	else
 		return it->second;
@@ -167,6 +197,15 @@ void CMusicHandler::initCreaturesSounds(std::vector<CCreature> &creatures)
 			c.sounds.wince = getSoundID(wince);
 			c.sounds.ext1 = getSoundID(ext1);
 			c.sounds.ext2 = getSoundID(ext2);
+
+			// Special creatures
+			if (c.idNumber == 55 || // Archdevil
+				c.idNumber == 62 || // Vampire
+				c.idNumber == 62)	// Vampire Lord
+			{
+				c.sounds.startMoving = c.sounds.ext1;
+				c.sounds.endMoving = c.sounds.ext2;
+			}
 		}
 	}
 	ifs.close();
@@ -187,40 +226,32 @@ void CMusicHandler::initCreaturesSounds(std::vector<CCreature> &creatures)
 }
 
 // Plays a sound, and return its channel so we can fade it out later
-int CMusicHandler::playSound(soundBase::soundNames soundID, int repeats)
+int CMusicHandler::playSound(soundBase::soundID soundID, int repeats)
 {
 	int channel;
+	Mix_Chunk *chunk;
+	std::map<soundBase::soundID, Mix_Chunk *>::iterator it;
 
 	if (!sndh)
 		return -1;
 
-	std::map<soundBase::soundNames, cachedSounds>::iterator it;
+	chunk = GetSoundChunk(soundID);
 
-	it = sounds.find(soundID);
-	if (it == sounds.end())
-		return -1;
-
-	class cachedSounds sound = it->second;
-
-	if (!sound.chunk) {
-		sound.chunk = GetSoundChunk(sound.filename);
-	}
-
-	if (sound.chunk)
+	if (chunk)
 	{
-		channel = Mix_PlayChannel(-1, sound.chunk, repeats);
-		if(channel == -1)
-		{
-			fprintf(stderr, "Unable to play WAV file("DATA_DIR "Data" PATHSEPARATOR "Heroes3.wav::%s): %s\n",
-					sound.filename.c_str(),Mix_GetError());
-		}
+		channel = Mix_PlayChannel(-1, chunk, repeats);
+		if (channel == -1)
+			tlog1 << "Unable to play sound file " << soundID << std::endl;
+		
+	} else {
+		channel = -1;
 	}
 
 	return channel;
 }
 
 // Helper. Randomly select a sound from an array and play it
-int CMusicHandler::playSoundFromSet(std::vector<soundBase::soundNames> &sound_vec)
+int CMusicHandler::playSoundFromSet(std::vector<soundBase::soundID> &sound_vec)
 {
 	return playSound(sound_vec[rand() % sound_vec.size()]);
 }

+ 10 - 19
hch/CMusicHandler.h

@@ -20,38 +20,29 @@ class CMusicHandler
 {
 private:
 	CSndHandler *sndh;
-	soundBase::soundNames getSoundID(std::string &fileName);
+	soundBase::soundID getSoundID(std::string &fileName);
 
-	class cachedSounds {
-	public:
-		std::string filename;
-		Mix_Chunk *chunk;
+	std::map<soundBase::soundID, Mix_Chunk *> soundChunks;
 
-		// This is some horrible C++ abuse. Isn't there any way to do
-		// something simplier to init sounds?
-		cachedSounds(std::string filename_in, Mix_Chunk *chunk_in):
-		filename(filename_in), chunk(chunk_in) {};
-	};
+	Mix_Chunk *GetSoundChunk(soundBase::soundID soundID);
 
-	std::map<soundBase::soundNames, cachedSounds> sounds;
-	std::map<std::string, soundBase::soundNames> reverse_sounds;
-
-	Mix_Chunk *GetSoundChunk(std::string srcName);
+	bool audioInit;
 
 public:
-	CMusicHandler(): sndh(NULL) {};
+	CMusicHandler(): sndh(NULL), audioInit(false) {};
+	~CMusicHandler();
 
 	void initMusics();
 	void initCreaturesSounds(std::vector<CCreature> &creatures);
 
 	// Sounds
-	int playSound(soundBase::soundNames soundID, int repeats=0);
-	int playSoundFromSet(std::vector<soundBase::soundNames> &sound_vec);
+	int playSound(soundBase::soundID soundID, int repeats=0);
+	int playSoundFromSet(std::vector<soundBase::soundID> &sound_vec);
 	void stopSound(int handler);
 
 	// Sets
-	std::vector<soundBase::soundNames> pickup_sounds;
-	std::vector<soundBase::soundNames> horseSounds;
+	std::vector<soundBase::soundID> pickup_sounds;
+	std::vector<soundBase::soundID> horseSounds;
 };
 
 #endif // __CMUSICHANDLER_H__

+ 1 - 1
hch/CSoundBase.h

@@ -1026,7 +1026,7 @@ public:
 	// We must keep an entry 0 for an invalid or no sound.
 #define VCMI_SOUND_NAME(x) x,
 #define VCMI_SOUND_FILE(y)
-	enum soundNames {
+	enum soundID {
 		invalid=0,
 		sound_todo=1,			// temp entry until code is fixed
 		VCMI_SOUND_LIST