1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #ifndef DIRECTOR_SOUND_H
24 #define DIRECTOR_SOUND_H
25 
26 #include "audio/mixer.h"
27 
28 namespace Audio {
29 	class AudioStream;
30 	class SoundHandle;
31 	class PCSpeaker;
32 	class RewindableAudioStream;
33 }
34 
35 namespace Director {
36 
37 class AudioDecoder;
38 
39 struct FadeParams {
40 	int startVol;
41 	int targetVol;
42 	int totalTicks;
43 	int startTicks;
44 	int lapsedTicks;
45 	bool fadeIn;
46 
FadeParamsFadeParams47 	FadeParams(int sv, int tv, int tt, int st, bool f) :
48 		startVol(sv), targetVol(tv), totalTicks(tt), startTicks(st), lapsedTicks(0), fadeIn(f) {}
49 };
50 
51 const uint16 kMinSampledMenu = 10;
52 const uint16 kMaxSampledMenu = 15;
53 const uint16 kNumSampledMenus = kMaxSampledMenu - kMinSampledMenu + 1;
54 
55 struct ExternalSoundID {
56 	uint16 menu;
57 	uint16 submenu;
58 
ExternalSoundIDExternalSoundID59 	ExternalSoundID() : menu(0), submenu(0) {}
ExternalSoundIDExternalSoundID60 	ExternalSoundID(uint16 menuID, uint16 submenuID)
61 		: menu(menuID), submenu(submenuID) {}
62 
63 	bool operator==(const ExternalSoundID &b) {
64 		return menu == b.menu && submenu == b.submenu;
65 	}
66 	bool operator!=(const ExternalSoundID &b) {
67 		return !(*this == b);
68 	}
69 };
70 
71 enum SoundIDType {
72 	kSoundCast,
73 	kSoundExternal
74 };
75 
76 struct SoundID {
77 	SoundIDType type;
78 	union {
79 		struct {
80 			int member;
81 			int castLib;
82 		} cast;
83 		struct {
84 			uint16 menu;
85 			uint16 submenu;
86 		} external;
87 	} u;
88 
SoundIDSoundID89 	SoundID() {
90 		type = kSoundCast;
91 		u.cast.member = 0;
92 		u.cast.castLib = 0;
93 	}
SoundIDSoundID94 	SoundID(SoundIDType type_, int a, int b) {
95 		type = type_;
96 		switch (type) {
97 		case kSoundCast:
98 			u.cast.member = a;
99 			u.cast.castLib = b;
100 			break;
101 		case kSoundExternal:
102 			u.external.menu = a;
103 			u.external.submenu = b;
104 		}
105 	}
SoundIDSoundID106 	SoundID(CastMemberID memberID) {
107 		type = kSoundCast;
108 		u.cast.member = memberID.member;
109 		u.cast.castLib = memberID.castLib;
110 	}
111 
112 	bool operator==(const SoundID &b) {
113 		if (type != b.type)
114 			return false;
115 
116 		switch (type) {
117 		case kSoundCast:
118 			return u.cast.member == b.u.cast.member && u.cast.castLib == b.u.cast.castLib;
119 		case kSoundExternal:
120 			return u.external.menu == b.u.external.menu && u.external.submenu == b.u.external.submenu;
121 		}
122 
123 		return false;
124 	}
125 	bool operator!=(const SoundID &b) {
126 		return !(*this == b);
127 	}
128 };
129 
130 struct SoundChannel {
131 	Audio::SoundHandle handle;
132 	SoundID lastPlayedSound;
133 	bool stopOnZero; // Should the sound be stopped when the channel contains cast member 0?
134 	byte volume;
135 	FadeParams *fade;
136 
137 	// a non-zero sound ID if the channel is a puppet. i.e. it's controlled by lingo
138 	SoundID puppet;
139 	bool newPuppet;
140 
141 	// this indicate whether the sound is playing across the movie. Because the cast name may be the same while the actual sounds are changing.
142 	// And we will override the sound when ever the sound is changing. thus we use a flag to indicate whether the movie is changed.
143 	bool movieChanged;
144 
SoundChannelSoundChannel145 	SoundChannel(): handle(), lastPlayedSound(SoundID()), stopOnZero(true), volume(255), fade(nullptr), puppet(SoundID()), newPuppet(false), movieChanged(false) {}
146 };
147 
148 class DirectorSound {
149 
150 private:
151 	Window *_window;
152 	Common::Array<SoundChannel> _channels;
153 	Audio::SoundHandle _scriptSound;
154 	Audio::Mixer *_mixer;
155 	Audio::PCSpeaker *_speaker;
156 	Audio::SoundHandle _pcSpeakerHandle;
157 
158 	// these two were used in fplay xobj
159 	Common::Queue<Common::String> _fplayQueue;
160 	Common::String _currentSoundName;
161 
162 	bool _enable;
163 
164 	Common::Array<AudioDecoder *> _sampleSounds[kNumSampledMenus];
165 
166 public:
167 	DirectorSound(Window *window);
168 	~DirectorSound();
169 
170 	SoundChannel *getChannel(uint8 soundChannel);
171 	void playFile(Common::String filename, uint8 soundChannel);
172 	void playMCI(Audio::AudioStream &stream, uint32 from, uint32 to);
173 	void playStream(Audio::AudioStream &stream, uint8 soundChannel);
174 	void playSound(SoundID soundId, uint8 soundChannel, bool forPuppet = false);
175 	void playCastMember(CastMemberID memberID, uint8 soundChannel, bool forPuppet = false);
176 	void playExternalSound(uint16 menu, uint16 submenu, uint8 soundChannel);
177 	void playFPlaySound(const Common::Array<Common::String> &fplayList);
178 	void playFPlaySound();
179 	void setSouldLevel(int channel, uint8 soundLevel);
180 	uint8 getSoundLevel(uint8 soundChannel);
181 	void setSoundEnabled(bool enabled);
182 	void systemBeep();
183 	void changingMovie();
184 
185 	void loadSampleSounds(uint type);
186 	void unloadSampleSounds();
187 
188 	void setLastPlayedSound(uint8 soundChannel, SoundID soundId, bool stopOnZero = true);
189 	bool isLastPlayedSound(uint8 soundChannel, const SoundID &soundId);
190 	bool shouldStopOnZero(uint8 soundChannel);
191 
192 	bool isChannelPuppet(uint8 soundChannel);
193 	void setPuppetSound(SoundID soundId, uint8 soundChannel);
194 	void playPuppetSound(uint8 soundChannel);
195 
getSoundEnabled()196 	bool getSoundEnabled() { return _enable; }
197 
getCurrentSound()198 	Common::String getCurrentSound() { return _currentSoundName; }
199 
200 	void registerFade(uint8 soundChannel, bool fadeIn, int ticks);
201 	bool fadeChannel(uint8 soundChannel);
202 
203 	bool isChannelActive(uint8 soundChannel);
204 	void stopSound(uint8 soundChannel);
205 	void stopSound();
206 
207 private:
208 	uint8 getChannelVolume(uint8 soundChannel);
209 	void setSoundLevelInternal(uint8 soundChannel, uint8 soundLevel);
210 	bool isChannelValid(uint8 soundChannel);
211 	void cancelFade(uint8 soundChannel);
212 };
213 
214 class AudioDecoder {
215 public:
AudioDecoder()216 	AudioDecoder() {};
~AudioDecoder()217 	virtual ~AudioDecoder() {};
218 public:
219 	virtual Audio::AudioStream *getAudioStream(bool looping = false, bool forPuppet = false, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES) { return nullptr; }
220 };
221 
222 class SNDDecoder : public AudioDecoder {
223 public:
224 	SNDDecoder();
225 	~SNDDecoder();
226 
227 	bool loadStream(Common::SeekableReadStreamEndian &stream);
228 	void loadExternalSoundStream(Common::SeekableReadStreamEndian &stream);
229 	bool processCommands(Common::SeekableReadStreamEndian &stream);
230 	bool processBufferCommand(Common::SeekableReadStreamEndian &stream);
231 	Audio::AudioStream *getAudioStream(bool looping = false, bool forPuppet = false, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES) override;
232 	bool hasLoopBounds();
233 
234 private:
235 	byte *_data;
236 	uint16 _channels;
237 	uint32 _size;
238 	uint16 _rate;
239 	byte _flags;
240 	uint32 _loopStart;
241 	uint32 _loopEnd;
242 };
243 
244 class AudioFileDecoder : public AudioDecoder {
245 public:
246 	AudioFileDecoder(Common::String &path);
~AudioFileDecoder()247 	~AudioFileDecoder() {};
248 
249 	void setPath(Common::String &path);
250 
251 	Audio::AudioStream *getAudioStream(bool looping = false, bool forPuppet = false, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES) override;
252 
253 private:
254 	Common::String _path;
255 };
256 
257 } // End of namespace Director
258 
259 #endif
260