File: CMusicHandler.h

package info (click to toggle)
vcmi 1.1.0%2Bdfsg-1
  • links: PTS, VCS
  • area: contrib
  • in suites: bookworm
  • size: 14,672 kB
  • sloc: cpp: 181,738; sh: 220; python: 178; ansic: 69; objc: 66; xml: 59; makefile: 34
file content (164 lines) | stat: -rw-r--r-- 4,903 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/*
 * CMusicHandler.h, part of VCMI engine
 *
 * Authors: listed in file AUTHORS in main folder
 *
 * License: GNU General Public License v2.0 or later
 * Full text of license available in license.txt file, in main folder
 *
 */
#pragma once

#include "../lib/CConfigHandler.h"
#include "../lib/CSoundBase.h"
#include "../lib/Terrain.h"

struct _Mix_Music;
struct SDL_RWops;
typedef struct _Mix_Music Mix_Music;
struct Mix_Chunk;

class CAudioBase {
protected:
	boost::mutex mutex;
	bool initialized;
	int volume;					// from 0 (mute) to 100

public:
	CAudioBase(): initialized(false), volume(0) {};
	virtual void init() = 0;
	virtual void release() = 0;

	virtual void setVolume(ui32 percent);
	ui32 getVolume() const { return volume; };
};

class CSoundHandler: public CAudioBase
{
private:
	//soundBase::soundID getSoundID(const std::string &fileName);
	//update volume on configuration change
	SettingsListener listener;
	void onVolumeChange(const JsonNode &volumeNode);

	using CachedChunk = std::pair<Mix_Chunk *, std::unique_ptr<ui8[]>>;
	std::map<std::string, CachedChunk> soundChunks;

	Mix_Chunk *GetSoundChunk(std::string &sound, bool cache);

	//have entry for every currently active channel
	//std::function will be nullptr if callback was not set
	std::map<int, std::function<void()> > callbacks;

	int ambientDistToVolume(int distance) const;
	void ambientStopSound(std::string soundId);

	const JsonNode ambientConfig;
	bool allTilesSource;
	std::map<std::string, int> ambientChannels;

public:
	CSoundHandler();

	void init() override;
	void loadHorseSounds();
	void release() override;

	void setVolume(ui32 percent) override;
	void setChannelVolume(int channel, ui32 percent);

	// Sounds
	int playSound(soundBase::soundID soundID, int repeats=0);
	int playSound(std::string sound, int repeats=0, bool cache=false);
	int playSoundFromSet(std::vector<soundBase::soundID> &sound_vec);
	void stopSound(int handler);

	void setCallback(int channel, std::function<void()> function);
	void soundFinishedCallback(int channel);

	int ambientGetRange() const;
	bool ambientCheckVisitable() const;
	void ambientUpdateChannels(std::map<std::string, int> currentSounds);
	void ambientStopAllChannels();

	// Sets
	std::vector<soundBase::soundID> pickupSounds;
	std::vector<soundBase::soundID> battleIntroSounds;
	std::map<TerrainId, soundBase::soundID> horseSounds;
};

// Helper //now it looks somewhat useless
#define battle_sound(creature,what_sound) creature->sounds.what_sound

class CMusicHandler;

//Class for handling one music file
class MusicEntry
{
	CMusicHandler *owner;
	Mix_Music *music;

	int loop; // -1 = indefinite
	bool fromStart;
	bool playing;
	uint32_t startTime;
	uint32_t startPosition;
	//if not null - set from which music will be randomly selected
	std::string setName;
	std::string currentName;

	void load(std::string musicURI);

public:
	MusicEntry(CMusicHandler *owner, std::string setName, std::string musicURI, bool looped, bool fromStart);
	~MusicEntry();

	bool isSet(std::string setName);
	bool isTrack(std::string trackName);
	bool isPlaying();

	bool play();
	bool stop(int fade_ms=0);
};

class CMusicHandler: public CAudioBase
{
private:
	//update volume on configuration change
	SettingsListener listener;
	void onVolumeChange(const JsonNode &volumeNode);

	std::unique_ptr<MusicEntry> current;
	std::unique_ptr<MusicEntry> next;

	void queueNext(CMusicHandler *owner, const std::string & setName, const std::string & musicURI, bool looped, bool fromStart);
	void queueNext(std::unique_ptr<MusicEntry> queued);
	void musicFinishedCallback();

	/// map <set name> -> <list of URI's to tracks belonging to the said set>
	std::map<std::string, std::vector<std::string>> musicsSet;
	/// stored position, in seconds at which music player should resume playing this track
	std::map<std::string, float> trackPositions;

public:
	CMusicHandler();

	/// add entry with URI musicURI in set. Track will have ID musicID
	void addEntryToSet(const std::string & set, const std::string & musicURI);

	void init() override;
	void loadTerrainMusicThemes();
	void release() override;
	void setVolume(ui32 percent) override;

	/// play track by URI, if loop = true music will be looped
	void playMusic(const std::string & musicURI, bool loop, bool fromStart);
	/// play random track from this set
	void playMusicFromSet(const std::string & musicSet, bool loop, bool fromStart);
	/// play random track from set (musicSet, entryID)
	void playMusicFromSet(const std::string & musicSet, const std::string & entryID, bool loop, bool fromStart);
	/// stops currently playing music by fading out it over fade_ms and starts next scheduled track, if any
	void stopMusic(int fade_ms=1000);

	friend class MusicEntry;
};