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 #include "common/config-manager.h"
24 #include "common/timer.h"
25 
26 #include "scumm/actor.h"
27 #include "scumm/scumm_v7.h"
28 #include "scumm/sound.h"
29 #include "scumm/imuse_digi/dimuse.h"
30 #include "scumm/imuse_digi/dimuse_bndmgr.h"
31 #include "scumm/imuse_digi/dimuse_track.h"
32 
33 #include "audio/audiostream.h"
34 #include "audio/mixer.h"
35 
36 namespace Scumm {
37 
allocSlot(int priority)38 int IMuseDigital::allocSlot(int priority) {
39 	int l, lowest_priority = 127;
40 	int trackId = -1;
41 
42 	for (l = 0; l < MAX_DIGITAL_TRACKS; l++) {
43 		if (!_track[l]->used) {
44 			trackId = l;
45 			break;
46 		}
47 	}
48 
49 	if (trackId == -1) {
50 		debug(5, "IMuseDigital::allocSlot(): All slots are full");
51 		for (l = 0; l < MAX_DIGITAL_TRACKS; l++) {
52 			Track *track = _track[l];
53 			if (track->used && !track->toBeRemoved &&
54 					(lowest_priority > track->soundPriority) && !track->souStreamUsed) {
55 				lowest_priority = track->soundPriority;
56 				trackId = l;
57 			}
58 		}
59 		if (lowest_priority <= priority) {
60 			assert(trackId != -1);
61 			Track *track = _track[trackId];
62 
63 			// Stop the track immediately
64 			_mixer->stopHandle(track->mixChanHandle);
65 			if (track->soundDesc) {
66 				_sound->closeSound(track->soundDesc);
67 			}
68 
69 			// Mark it as unused
70 			track->reset();
71 
72 			debug(5, "IMuseDigital::allocSlot(): Removed sound %d from track %d", _track[trackId]->soundId, trackId);
73 		} else {
74 			debug(5, "IMuseDigital::allocSlot(): Priority sound too low");
75 			return -1;
76 		}
77 	}
78 
79 	return trackId;
80 }
81 
startSound(int soundId,const char * soundName,int soundType,int volGroupId,Audio::AudioStream * input,int hookId,int volume,int priority,Track * otherTrack)82 void IMuseDigital::startSound(int soundId, const char *soundName, int soundType, int volGroupId, Audio::AudioStream *input, int hookId, int volume, int priority, Track *otherTrack) {
83 	Common::StackLock lock(_mutex, "IMuseDigital::startSound()");
84 	debug(5, "IMuseDigital::startSound(%d) - begin func", soundId);
85 
86 	int l = allocSlot(priority);
87 	if (l == -1) {
88 		warning("IMuseDigital::startSound() Can't start sound - no free slots");
89 		return;
90 	}
91 	debug(5, "IMuseDigital::startSound(%d, trackId:%d)", soundId, l);
92 
93 	Track *track = _track[l];
94 
95 	// Reset the track
96 	track->reset();
97 
98 	track->pan = 64;
99 	track->vol = volume * 1000;
100 	track->soundId = soundId;
101 	track->volGroupId = volGroupId;
102 	track->curHookId = hookId;
103 	track->soundPriority = priority;
104 	track->curRegion = -1;
105 	track->soundType = soundType;
106 	track->trackId = l;
107 
108 	int bits = 0, freq = 0, channels = 0;
109 
110 	track->souStreamUsed = (input != 0);
111 
112 	if (track->souStreamUsed) {
113 		_mixer->playStream(track->getType(), &track->mixChanHandle, input, -1, track->getVol(), track->getPan());
114 	} else {
115 		strcpy(track->soundName, soundName);
116 		track->soundDesc = _sound->openSound(soundId, soundName, soundType, volGroupId, -1);
117 		if (!track->soundDesc)
118 			track->soundDesc = _sound->openSound(soundId, soundName, soundType, volGroupId, 1);
119 		if (!track->soundDesc)
120 			track->soundDesc = _sound->openSound(soundId, soundName, soundType, volGroupId, 2);
121 
122 		if (!track->soundDesc)
123 			return;
124 
125 		track->sndDataExtComp = _sound->isSndDataExtComp(track->soundDesc);
126 
127 		bits = _sound->getBits(track->soundDesc);
128 		channels = _sound->getChannels(track->soundDesc);
129 		freq = _sound->getFreq(track->soundDesc);
130 
131 		if ((soundId == kTalkSoundID) && (soundType == IMUSE_BUNDLE)) {
132 			if (_vm->_actorToPrintStrFor != 0xFF && _vm->_actorToPrintStrFor != 0) {
133 				Actor *a = _vm->derefActor(_vm->_actorToPrintStrFor, "IMuseDigital::startSound");
134 				freq = (freq * a->_talkFrequency) / 256;
135 				track->pan = a->_talkPan;
136 				track->vol = a->_talkVolume * 1000;
137 			}
138 
139 			// The volume is set to zero, when using subtitles only setting in COMI
140 			if (ConfMan.getBool("speech_mute") || _vm->VAR(_vm->VAR_VOICE_MODE) == 2) {
141 				track->vol = 0;
142 			}
143 		}
144 
145 		assert(bits == 8 || bits == 12 || bits == 16);
146 		assert(channels == 1 || channels == 2);
147 		assert(0 < freq && freq <= 65535);
148 
149 		track->feedSize = freq * channels;
150 		if (channels == 2)
151 			track->mixerFlags = kFlagStereo;
152 
153 		if ((bits == 12) || (bits == 16)) {
154 			track->mixerFlags |= kFlag16Bits;
155 			track->feedSize *= 2;
156 		} else if (bits == 8) {
157 			track->mixerFlags |= kFlagUnsigned;
158 		} else
159 			error("IMuseDigital::startSound(): Can't handle %d bit samples", bits);
160 
161 		if (otherTrack && otherTrack->used && !otherTrack->toBeRemoved) {
162 			track->curRegion = otherTrack->curRegion;
163 			track->dataOffset = otherTrack->dataOffset;
164 			track->regionOffset = otherTrack->regionOffset;
165 			track->dataMod12Bit = otherTrack->dataMod12Bit;
166 		}
167 
168 		track->stream = Audio::makeQueuingAudioStream(freq, track->mixerFlags & kFlagStereo);
169 		_mixer->playStream(track->getType(), &track->mixChanHandle, track->stream, -1, track->getVol(), track->getPan());
170 	}
171 
172 	track->used = true;
173 }
174 
setPriority(int soundId,int priority)175 void IMuseDigital::setPriority(int soundId, int priority) {
176 	Common::StackLock lock(_mutex, "IMuseDigital::setPriority()");
177 	debug(5, "IMuseDigital::setPriority(%d, %d)", soundId, priority);
178 	assert ((priority >= 0) && (priority <= 127));
179 
180 	for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
181 		Track *track = _track[l];
182 		if (track->used && !track->toBeRemoved && (track->soundId == soundId)) {
183 			debug(5, "IMuseDigital::setPriority(%d) - setting", soundId);
184 			track->soundPriority = priority;
185 		}
186 	}
187 }
188 
setVolume(int soundId,int volume)189 void IMuseDigital::setVolume(int soundId, int volume) {
190 	Common::StackLock lock(_mutex, "IMuseDigital::setVolume()");
191 	debug(5, "IMuseDigital::setVolume(%d, %d)", soundId, volume);
192 
193 	for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
194 		Track *track = _track[l];
195 		if (track->used && !track->toBeRemoved && (track->soundId == soundId)) {
196 			debug(5, "IMuseDigital::setVolume(%d) - setting", soundId);
197 			track->vol = volume * 1000;
198 		}
199 	}
200 }
201 
setHookId(int soundId,int hookId)202 void IMuseDigital::setHookId(int soundId, int hookId) {
203 	Common::StackLock lock(_mutex, "IMuseDigital::setHookId()");
204 
205 	for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
206 		Track *track = _track[l];
207 		if (track->used && !track->toBeRemoved && (track->soundId == soundId)) {
208 			track->curHookId = hookId;
209 		}
210 	}
211 }
212 
getCurMusicSoundId()213 int IMuseDigital::getCurMusicSoundId() {
214 	Common::StackLock lock(_mutex, "IMuseDigital::getCurMusicSoundId()");
215 	int soundId = -1;
216 
217 	for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
218 		Track *track = _track[l];
219 		if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
220 			soundId = track->soundId;
221 			break;
222 		}
223 	}
224 
225 	return soundId;
226 }
227 
setPan(int soundId,int pan)228 void IMuseDigital::setPan(int soundId, int pan) {
229 	Common::StackLock lock(_mutex, "IMuseDigital::setPan()");
230 	debug(5, "IMuseDigital::setPan(%d, %d)", soundId, pan);
231 
232 	for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
233 		Track *track = _track[l];
234 		if (track->used && !track->toBeRemoved && (track->soundId == soundId)) {
235 			debug(5, "IMuseDigital::setPan(%d) - setting", soundId);
236 			track->pan = pan;
237 		}
238 	}
239 }
240 
selectVolumeGroup(int soundId,int volGroupId)241 void IMuseDigital::selectVolumeGroup(int soundId, int volGroupId) {
242 	Common::StackLock lock(_mutex, "IMuseDigital::selectVolumeGroup()");
243 	debug(5, "IMuseDigital::setGroupVolume(%d, %d)", soundId, volGroupId);
244 	assert((volGroupId >= 1) && (volGroupId <= 4));
245 
246 	if (volGroupId == 4)
247 		volGroupId = 3;
248 
249 	for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
250 		Track *track = _track[l];
251 		if (track->used && !track->toBeRemoved && (track->soundId == soundId)) {
252 			debug(5, "IMuseDigital::setVolumeGroup(%d) - setting", soundId);
253 			track->volGroupId = volGroupId;
254 		}
255 	}
256 }
257 
setFade(int soundId,int destVolume,int delay60HzTicks)258 void IMuseDigital::setFade(int soundId, int destVolume, int delay60HzTicks) {
259 	Common::StackLock lock(_mutex, "IMuseDigital::setFade()");
260 	debug(5, "IMuseDigital::setFade(%d, %d, %d)", soundId, destVolume, delay60HzTicks);
261 
262 	for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
263 		Track *track = _track[l];
264 		if (track->used && !track->toBeRemoved && (track->soundId == soundId)) {
265 			debug(5, "IMuseDigital::setFade(%d) - setting", soundId);
266 			track->volFadeDelay = delay60HzTicks;
267 			track->volFadeDest = destVolume * 1000;
268 			track->volFadeStep = (track->volFadeDest - track->vol) * 60 * (1000 / _callbackFps) / (1000 * delay60HzTicks);
269 			track->volFadeUsed = true;
270 		}
271 	}
272 }
273 
fadeOutMusicAndStartNew(int fadeDelay,const char * filename,int soundId)274 void IMuseDigital::fadeOutMusicAndStartNew(int fadeDelay, const char *filename, int soundId) {
275 	Common::StackLock lock(_mutex, "IMuseDigital::fadeOutMusicAndStartNew()");
276 	debug(5, "IMuseDigital::fadeOutMusicAndStartNew(fade:%d, file:%s, sound:%d)", fadeDelay, filename, soundId);
277 
278 	for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
279 		Track *track = _track[l];
280 		if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
281 			debug(5, "IMuseDigital::fadeOutMusicAndStartNew(sound:%d) - starting", soundId);
282 			startMusicWithOtherPos(filename, soundId, 0, 127, track);
283 			cloneToFadeOutTrack(track, fadeDelay);
284 			flushTrack(track);
285 			break;
286 		}
287 	}
288 }
289 
fadeOutMusic(int fadeDelay)290 void IMuseDigital::fadeOutMusic(int fadeDelay) {
291 	Common::StackLock lock(_mutex, "IMuseDigital::fadeOutMusic()");
292 	debug(5, "IMuseDigital::fadeOutMusic(fade:%d)", fadeDelay);
293 
294 	for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
295 		Track *track = _track[l];
296 		if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
297 			debug(5, "IMuseDigital::fadeOutMusic(fade:%d, sound:%d)", fadeDelay, track->soundId);
298 			cloneToFadeOutTrack(track, fadeDelay);
299 			flushTrack(track);
300 			break;
301 		}
302 	}
303 }
304 
setHookIdForMusic(int hookId)305 void IMuseDigital::setHookIdForMusic(int hookId) {
306 	Common::StackLock lock(_mutex, "IMuseDigital::setHookIdForMusic()");
307 	debug(5, "IMuseDigital::setHookIdForMusic(hookId:%d)", hookId);
308 
309 	for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
310 		Track *track = _track[l];
311 		if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
312 			debug(5, "IMuseDigital::setHookIdForMusic - setting for sound:%d", track->soundId);
313 			track->curHookId = hookId;
314 			break;
315 		}
316 	}
317 }
318 
setTrigger(TriggerParams * trigger)319 void IMuseDigital::setTrigger(TriggerParams *trigger) {
320 	Common::StackLock lock(_mutex, "IMuseDigital::setTrigger()");
321 	debug(5, "IMuseDigital::setTrigger(%s)", trigger->filename);
322 
323 	memcpy(&_triggerParams, trigger, sizeof(TriggerParams));
324 	_triggerUsed = true;
325 }
326 
cloneToFadeOutTrack(Track * track,int fadeDelay)327 Track *IMuseDigital::cloneToFadeOutTrack(Track *track, int fadeDelay) {
328 	assert(track);
329 	Track *fadeTrack;
330 
331 	debug(5, "cloneToFadeOutTrack(soundId:%d, fade:%d) - begin of func", track->trackId, fadeDelay);
332 
333 	if (track->toBeRemoved) {
334 		error("cloneToFadeOutTrack: Tried to clone a track to be removed, please bug report");
335 		return NULL;
336 	}
337 
338 	assert(track->trackId < MAX_DIGITAL_TRACKS);
339 	fadeTrack = _track[track->trackId + MAX_DIGITAL_TRACKS];
340 
341 	if (fadeTrack->used) {
342 		debug(5, "cloneToFadeOutTrack: No free fade track, force flush fade soundId:%d", fadeTrack->soundId);
343 		flushTrack(fadeTrack);
344 		_mixer->stopHandle(fadeTrack->mixChanHandle);
345 	}
346 
347 	// Clone the settings of the given track
348 	memcpy(fadeTrack, track, sizeof(Track));
349 	fadeTrack->trackId = track->trackId + MAX_DIGITAL_TRACKS;
350 
351 	// Clone the sound.
352 	// leaving bug number for now #1635361
353 	ImuseDigiSndMgr::SoundDesc *soundDesc = _sound->cloneSound(track->soundDesc);
354 	if (!soundDesc) {
355 		// it fail load open old song after switch to different CDs
356 		// so gave up
357 		error("Game not supported while playing on 2 different CDs");
358 	}
359 	track->soundDesc = soundDesc;
360 
361 	// Set the volume fading parameters to indicate a fade out
362 	fadeTrack->volFadeDelay = fadeDelay;
363 	fadeTrack->volFadeDest = 0;
364 	fadeTrack->volFadeStep = (fadeTrack->volFadeDest - fadeTrack->vol) * 60 * (1000 / _callbackFps) / (1000 * fadeDelay);
365 	fadeTrack->volFadeUsed = true;
366 
367 	// Create an appendable output buffer
368 	fadeTrack->stream = Audio::makeQueuingAudioStream(_sound->getFreq(fadeTrack->soundDesc), track->mixerFlags & kFlagStereo);
369 	_mixer->playStream(track->getType(), &fadeTrack->mixChanHandle, fadeTrack->stream, -1, fadeTrack->getVol(), fadeTrack->getPan());
370 	fadeTrack->used = true;
371 
372 	debug(5, "cloneToFadeOutTrack() - end of func, soundId %d, fade soundId %d", track->soundId, fadeTrack->soundId);
373 
374 	return fadeTrack;
375 }
376 
377 } // End of namespace Scumm
378