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