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
24 #include "scumm/sound.h"
25 #include "scumm/players/player_towns.h"
26
27 namespace Scumm {
28
Player_Towns(ScummEngine * vm,bool isVersion2)29 Player_Towns::Player_Towns(ScummEngine *vm, bool isVersion2) : _vm(vm), _v2(isVersion2), _intf(0), _numSoundMax(isVersion2 ? 256 : 200), _unkFlags(0x33) {
30 memset(_pcmCurrentSound, 0, sizeof(_pcmCurrentSound));
31 }
32
setSfxVolume(int vol)33 void Player_Towns::setSfxVolume(int vol) {
34 if (!_intf)
35 return;
36 _intf->setSoundEffectVolume(vol);
37 }
38
getSoundStatus(int sound) const39 int Player_Towns::getSoundStatus(int sound) const {
40 if (!_intf)
41 return 0;
42 for (int i = 1; i < 9; i++) {
43 if (_pcmCurrentSound[i].index == sound)
44 return _intf->callback(40, 0x3f + i) ? 1 : 0;
45 }
46 return 0;
47 }
48
syncWithSerializer(Common::Serializer & s,Player_Towns::PcmCurrentSound & pcs)49 void syncWithSerializer(Common::Serializer &s, Player_Towns::PcmCurrentSound &pcs) {
50 s.syncAsSint16LE(pcs.index, VER(81));
51 s.syncAsSint16LE(pcs.chan, VER(81));
52 s.syncAsByte(pcs.note, VER(81));
53 s.syncAsByte(pcs.velo, VER(81));
54 s.syncAsByte(pcs.pan, VER(81));
55 s.syncAsByte(pcs.paused, VER(81));
56 s.syncAsByte(pcs.looping, VER(81));
57 s.syncAsUint32LE(pcs.priority, VER(81));
58 }
59
saveLoadWithSerializer(Common::Serializer & s)60 void Player_Towns::saveLoadWithSerializer(Common::Serializer &s) {
61 for (int i = 1; i < 9; i++) {
62 if (!_pcmCurrentSound[i].index)
63 continue;
64
65 if (_intf->callback(40, i + 0x3f))
66 continue;
67
68 _intf->callback(39, i + 0x3f);
69
70 _pcmCurrentSound[i].index = 0;
71 }
72
73 s.syncArray(_pcmCurrentSound, 9, syncWithSerializer);
74 }
75
restoreAfterLoad()76 void Player_Towns::restoreAfterLoad() {
77 Common::Array<uint16> restoredSounds;
78
79 for (int i = 1; i < 9; i++) {
80 if (!_pcmCurrentSound[i].index || _pcmCurrentSound[i].index == 0xffff)
81 continue;
82
83 // Don't restart multichannel sounds more than once
84 if (Common::find(restoredSounds.begin(), restoredSounds.end(), _pcmCurrentSound[i].index) != restoredSounds.end())
85 continue;
86
87 if (!_v2)
88 restoredSounds.push_back(_pcmCurrentSound[i].index);
89
90 uint8 *ptr = _vm->getResourceAddress(rtSound, _pcmCurrentSound[i].index);
91 if (!ptr)
92 continue;
93
94 if (_vm->_game.version != 3)
95 ptr += 2;
96
97 if (ptr[13])
98 continue;
99
100 playPcmTrack(_pcmCurrentSound[i].index, ptr + 6, _pcmCurrentSound[i].velo, _pcmCurrentSound[i].pan, _pcmCurrentSound[i].note, _pcmCurrentSound[i].priority);
101 }
102 }
103
playPcmTrack(int sound,const uint8 * data,int velo,int pan,int note,int priority)104 void Player_Towns::playPcmTrack(int sound, const uint8 *data, int velo, int pan, int note, int priority) {
105 if (!_intf)
106 return;
107
108 const uint8 *sfxData = data + 16;
109
110 int numChan = _v2 ? 1 : data[14];
111 for (int i = 0; i < numChan; i++) {
112 int chan = allocatePcmChannel(sound, i, priority);
113 if (!chan)
114 return;
115
116 _intf->callback(70, _unkFlags);
117 _intf->callback(3, chan + 0x3f, pan);
118 _intf->callback(37, chan + 0x3f, note, velo, sfxData);
119
120 _pcmCurrentSound[chan].note = note;
121 _pcmCurrentSound[chan].velo = velo;
122 _pcmCurrentSound[chan].pan = pan;
123 _pcmCurrentSound[chan].paused = 0;
124 _pcmCurrentSound[chan].looping = READ_LE_UINT32(&sfxData[20]) ? 1 : 0;
125
126 sfxData += (READ_LE_UINT32(&sfxData[12]) + 32);
127 }
128 }
129
stopPcmTrack(int sound)130 void Player_Towns::stopPcmTrack(int sound) {
131 if (!_intf)
132 return;
133
134 for (int i = 1; i < 9; i++) {
135 if (sound == _pcmCurrentSound[i].index || !sound) {
136 _intf->callback(39, i + 0x3f);
137 _pcmCurrentSound[i].index = 0;
138 }
139 }
140 }
141
allocatePcmChannel(int sound,int sfxChanRelIndex,uint32 priority)142 int Player_Towns::allocatePcmChannel(int sound, int sfxChanRelIndex, uint32 priority) {
143 if (!_intf)
144 return 0;
145
146 int chan = 0;
147
148 if (_v2 && priority > 255) {
149 chan = 8;
150 if (_intf->callback(40, 0x47))
151 _intf->callback(39, 0x47);
152 } else {
153 for (int i = 8; i; i--) {
154 if (!_pcmCurrentSound[i].index) {
155 chan = i;
156 continue;
157 }
158
159 if (_intf->callback(40, i + 0x3f))
160 continue;
161
162 chan = i;
163 if (_pcmCurrentSound[chan].index == 0xffff)
164 _intf->callback(39, chan + 0x3f);
165 else
166 _vm->_sound->stopSound(_pcmCurrentSound[chan].index);
167 }
168
169 if (!chan) {
170 for (int i = 1; i < 9; i++) {
171 if (priority >= _pcmCurrentSound[i].priority)
172 chan = i;
173 }
174 if (_pcmCurrentSound[chan].index == 0xffff)
175 _intf->callback(39, chan + 0x3f);
176 else
177 _vm->_sound->stopSound(_pcmCurrentSound[chan].index);
178 }
179 }
180
181 if (chan) {
182 _pcmCurrentSound[chan].index = sound;
183 _pcmCurrentSound[chan].chan = sfxChanRelIndex;
184 _pcmCurrentSound[chan].priority = priority;
185 }
186
187 return chan;
188 }
189
Player_Towns_v1(ScummEngine * vm,Audio::Mixer * mixer)190 Player_Towns_v1::Player_Towns_v1(ScummEngine *vm, Audio::Mixer *mixer) : Player_Towns(vm, false) {
191 _soundOverride = 0;
192 _cdaCurrentSound = _eupCurrentSound = _cdaNumLoops = 0;
193 _cdaForceRestart = 0;
194 _cdaVolLeft = _cdaVolRight = 0;
195
196 _eupVolLeft = _eupVolRight = 0;
197 _eupLooping = false;
198
199 if (_vm->_game.version == 3) {
200 _soundOverride = new SoundOvrParameters[_numSoundMax];
201 memset(_soundOverride, 0, _numSoundMax * sizeof(SoundOvrParameters));
202 }
203
204 _player = new EuphonyPlayer(mixer);
205 _intf = new TownsAudioInterface(mixer, 0);
206 }
207
~Player_Towns_v1()208 Player_Towns_v1::~Player_Towns_v1() {
209 delete _intf;
210 delete _player;
211 delete[] _soundOverride;
212 }
213
init()214 bool Player_Towns_v1::init() {
215 if (!_player)
216 return false;
217
218 if (!_player->init())
219 return false;
220
221 _player->driver()->reserveSoundEffectChannels(8);
222
223 // Treat all 6 fm channels and all 8 pcm channels as sound effect channels
224 // since music seems to exist as CD audio only in the games which use this
225 // MusicEngine implementation.
226 _intf->setSoundEffectChanMask(-1);
227
228 setVolumeCD(255, 255);
229
230 return true;
231 }
232
setMusicVolume(int vol)233 void Player_Towns_v1::setMusicVolume(int vol) {
234 _player->driver()->setMusicVolume(vol);
235 }
236
startSound(int sound)237 void Player_Towns_v1::startSound(int sound) {
238 uint8 *ptr = _vm->getResourceAddress(rtSound, sound);
239 assert(ptr);
240
241 if (_vm->_game.version != 3)
242 ptr += 2;
243
244 int type = ptr[13];
245
246 if (type == 0) {
247 uint8 velocity = 0;
248 uint8 note = 0;
249
250 if (_vm->_game.version == 3) {
251 velocity = (_soundOverride[sound].vLeft + _soundOverride[sound].vRight);
252 note = _soundOverride[sound].note;
253 }
254
255 velocity = velocity ? velocity >> 2 : ptr[14] >> 1;
256 uint16 len = READ_LE_UINT16(ptr) + 2;
257 playPcmTrack(sound, ptr + 6, velocity, 64, note ? note : (len > 50 ? ptr[50] : 60), READ_LE_UINT16(ptr + 10));
258 } else if (type == 1 || (_vm->_game.id == GID_INDY3 && sound == 40)) {
259 // WORKAROUND: Indy 3 FMTOWNS: No/distorted music in Venice
260 // The Venice music does not exist as CD audio and the original doesn't feature music
261 // in this scene. It does, however, exist as Euphony track albeit with an odd sound
262 // type (255 instead of 1).
263 // It doesn't sound great but we'll enable it to have music at all in this scene.
264 // See Trac#1873 and Trac#10561.
265 playEuphonyTrack(sound, ptr + 6);
266
267 } else if (type == 2) {
268 playCdaTrack(sound, ptr + 6);
269 }
270
271 if (_vm->_game.version == 3)
272 _soundOverride[sound].vLeft = _soundOverride[sound].vRight = _soundOverride[sound].note = 0;
273 }
274
stopSound(int sound)275 void Player_Towns_v1::stopSound(int sound) {
276 if (sound == 0 || sound == _cdaCurrentSound) {
277 _cdaCurrentSound = 0;
278 _vm->_sound->stopCD();
279 _vm->_sound->stopCDTimer();
280 }
281
282 if (sound != 0 && sound == _eupCurrentSound) {
283 _eupCurrentSound = 0;
284 _eupLooping = false;
285 _player->stop();
286 }
287
288 stopPcmTrack(sound);
289 }
290
stopAllSounds()291 void Player_Towns_v1::stopAllSounds() {
292 _cdaCurrentSound = 0;
293 _vm->_sound->stopCD();
294 _vm->_sound->stopCDTimer();
295
296 _eupCurrentSound = 0;
297 _eupLooping = false;
298 _player->stop();
299
300 stopPcmTrack(0);
301 }
302
getSoundStatus(int sound) const303 int Player_Towns_v1::getSoundStatus(int sound) const {
304 if (sound == _cdaCurrentSound)
305 return _vm->_sound->pollCD();
306 if (sound == _eupCurrentSound)
307 return _player->isPlaying() ? 1 : 0;
308 return Player_Towns::getSoundStatus(sound);
309 }
310
doCommand(int numargs,int args[])311 int32 Player_Towns_v1::doCommand(int numargs, int args[]) {
312 int32 res = 0;
313
314 switch (args[0]) {
315 case 2:
316 _player->driver()->cdaToggle(0);
317 break;
318
319 case 3:
320 restartLoopingSounds();
321 break;
322
323 case 8:
324 startSound(args[1]);
325 break;
326
327 case 9:
328 _vm->_sound->stopSound(args[1]);
329 break;
330
331 case 11:
332 stopPcmTrack(0);
333 break;
334
335 case 14:
336 startSoundEx(args[1], args[2], args[3], args[4]);
337 break;
338
339 case 15:
340 stopSoundSuspendLooping(args[1]);
341 break;
342
343 default:
344 warning("Player_Towns_v1::doCommand: Unknown command %d", args[0]);
345 break;
346 }
347
348 return res;
349 }
350
setVolumeCD(int left,int right)351 void Player_Towns_v1::setVolumeCD(int left, int right) {
352 _cdaVolLeft = left & 0xff;
353 _cdaVolRight = right & 0xff;
354 _player->driver()->setOutputVolume(1, left >> 1, right >> 1);
355 }
356
setSoundVolume(int sound,int left,int right)357 void Player_Towns_v1::setSoundVolume(int sound, int left, int right) {
358 if (_soundOverride && sound > 0 && sound < _numSoundMax) {
359 _soundOverride[sound].vLeft = left;
360 _soundOverride[sound].vRight = right;
361 }
362 }
363
setSoundNote(int sound,int note)364 void Player_Towns_v1::setSoundNote(int sound, int note) {
365 if (_soundOverride && sound > 0 && sound < _numSoundMax)
366 _soundOverride[sound].note = note;
367 }
368
saveLoadWithSerializer(Common::Serializer & s)369 void Player_Towns_v1::saveLoadWithSerializer(Common::Serializer &s) {
370 _cdaCurrentSoundTemp = (_vm->_sound->pollCD() && _cdaNumLoops > 1) ? _cdaCurrentSound & 0xff : 0;
371 _cdaNumLoopsTemp = _cdaNumLoops & 0xff;
372
373 s.syncAsByte(_cdaCurrentSoundTemp, VER(81));
374 s.syncAsByte(_cdaNumLoopsTemp, VER(81));
375 s.syncAsByte(_cdaVolLeft, VER(81));
376 s.syncAsByte(_cdaVolRight, VER(81));
377
378 if (!_eupLooping && !_player->isPlaying())
379 _eupCurrentSound = 0;
380
381 s.syncAsByte(_eupCurrentSound, VER(81));
382 s.syncAsByte(_eupLooping, VER(81));
383 s.syncAsByte(_eupVolLeft, VER(81));
384 s.syncAsByte(_eupVolRight, VER(81));
385
386 Player_Towns::saveLoadWithSerializer(s);
387 }
388
restoreAfterLoad()389 void Player_Towns_v1::restoreAfterLoad() {
390 setVolumeCD(_cdaVolLeft, _cdaVolRight);
391
392 if (_cdaCurrentSoundTemp) {
393 uint8 *ptr = _vm->getResourceAddress(rtSound, _cdaCurrentSoundTemp) + 6;
394 if (_vm->_game.version != 3)
395 ptr += 2;
396
397 if (ptr[7] == 2) {
398 playCdaTrack(_cdaCurrentSoundTemp, ptr, true);
399 _cdaCurrentSound = _cdaCurrentSoundTemp;
400 _cdaNumLoops = _cdaNumLoopsTemp;
401 }
402 }
403
404 if (_eupCurrentSound) {
405 uint8 *ptr = _vm->getResourceAddress(rtSound, _eupCurrentSound) + 6;
406 if (_vm->_game.version != 3)
407 ptr += 2;
408
409 // WORKAROUND for bug #1873 INDY3 FMTOWNS: Music in Venice is distorted
410 // The resource for sound 40 accidently sets the sound type to 255 instead of 1.
411 if (ptr[7] == 1 || (_vm->_game.id == GID_INDY3 && _eupCurrentSound == 40)) {
412 setSoundVolume(_eupCurrentSound, _eupVolLeft, _eupVolRight);
413 playEuphonyTrack(_eupCurrentSound, ptr);
414 }
415 }
416
417 Player_Towns::restoreAfterLoad();
418 }
419
restartLoopingSounds()420 void Player_Towns_v1::restartLoopingSounds() {
421 if (_cdaNumLoops && !_cdaForceRestart)
422 _cdaForceRestart = 1;
423
424 for (int i = 1; i < 9; i++) {
425 if (!_pcmCurrentSound[i].paused)
426 continue;
427
428 _pcmCurrentSound[i].paused = 0;
429
430 uint8 *ptr = _vm->getResourceAddress(rtSound, _pcmCurrentSound[i].index);
431 if (!ptr)
432 continue;
433 ptr += 24;
434
435 int c = 1;
436 while (_pcmCurrentSound[i].chan != c) {
437 ptr = ptr + READ_LE_UINT32(&ptr[12]) + 32;
438 c++;
439 }
440
441 _player->driver()->playSoundEffect(i + 0x3f, _pcmCurrentSound[i].note, _pcmCurrentSound[i].velo, ptr);
442 }
443
444 _player->driver()->cdaToggle(1);
445 }
446
startSoundEx(int sound,int velo,int pan,int note)447 void Player_Towns_v1::startSoundEx(int sound, int velo, int pan, int note) {
448 uint8 *ptr = _vm->getResourceAddress(rtSound, sound) + 2;
449
450 if (pan > 99)
451 pan = 99;
452
453 velo = velo ? (velo * ptr[14] + 50) / 100 : ptr[14];
454 velo = CLIP(velo, 1, 255);
455 uint16 pri = READ_LE_UINT16(ptr + 10);
456
457 if (ptr[13] == 0) {
458 velo >>= 1;
459
460 if (!velo)
461 velo = 1;
462
463 pan = pan ? (((pan << 7) - pan) + 50) / 100 : 64;
464
465 playPcmTrack(sound, ptr + 6, velo ? velo : ptr[14] >> 1, pan, note ? note : ptr[50], pri);
466
467 } else if (ptr[13] == 2) {
468 int volLeft = velo;
469 int volRight = velo;
470
471 if (pan < 50)
472 volRight = ((pan * 2 + 1) * velo + 50) / 100;
473 else if (pan > 50)
474 volLeft = (((99 - pan) * 2 + 1) * velo + 50) / 100;
475
476 setVolumeCD(volLeft, volRight);
477
478 if (!_cdaForceRestart && sound == _cdaCurrentSound)
479 return;
480
481 playCdaTrack(sound, ptr + 6, true);
482 }
483 }
484
stopSoundSuspendLooping(int sound)485 void Player_Towns_v1::stopSoundSuspendLooping(int sound) {
486 if (!sound) {
487 return;
488 } else if (sound == _cdaCurrentSound) {
489 if (_cdaNumLoops && _cdaForceRestart)
490 _cdaForceRestart = 1;
491 } else {
492 for (int i = 1; i < 9; i++) {
493 if (sound == _pcmCurrentSound[i].index) {
494 if (!_player->driver()->soundEffectIsPlaying(i + 0x3f))
495 continue;
496 _player->driver()->stopSoundEffect(i + 0x3f);
497 if (_pcmCurrentSound[i].looping)
498 _pcmCurrentSound[i].paused = 1;
499 else
500 _pcmCurrentSound[i].index = 0;
501 }
502 }
503 }
504 }
505
playEuphonyTrack(int sound,const uint8 * data)506 void Player_Towns_v1::playEuphonyTrack(int sound, const uint8 *data) {
507 const uint8 *pos = data + 16;
508 const uint8 *src = pos + data[14] * 48;
509 const uint8 *trackData = src + 150;
510
511 for (int i = 0; i < 32; i++)
512 _player->configPart_enable(i, *src++);
513 for (int i = 0; i < 32; i++)
514 _player->configPart_setType(i, 0xff);
515 for (int i = 0; i < 32; i++)
516 _player->configPart_remap(i, *src++);
517 for (int i = 0; i < 32; i++)
518 _player->configPart_adjustVolume(i, *src++);
519 for (int i = 0; i < 32; i++)
520 _player->configPart_setTranspose(i, *src++);
521
522 src += 8;
523 for (int i = 0; i < 6; i++)
524 _player->driver()->assignPartToChannel(i, *src++);
525
526 for (int i = 0; i < data[14]; i++) {
527 _player->driver()->loadInstrument(i, i, pos + i * 48);
528 _player->driver()->setInstrument(i, i);
529 }
530
531 _eupVolLeft = _soundOverride[sound].vLeft;
532 _eupVolRight = _soundOverride[sound].vRight;
533 int lvl = _soundOverride[sound].vLeft + _soundOverride[sound].vRight;
534 if (!lvl)
535 lvl = data[8] + data[9];
536 lvl >>= 2;
537
538 for (int i = 0; i < 6; i++)
539 _player->driver()->channelVolume(i, lvl);
540
541 uint32 trackSize = READ_LE_UINT32(src);
542 src += 4;
543 uint8 startTick = *src++;
544
545 _player->setTempo(*src++);
546 _player->startTrack(trackData, trackSize, startTick);
547
548 _eupLooping = (*src != 1) ? 1 : 0;
549 _player->setLoopStatus(_eupLooping != 0);
550 _player->resume();
551 _eupCurrentSound = sound;
552 }
553
playCdaTrack(int sound,const uint8 * data,bool skipTrackVelo)554 void Player_Towns_v1::playCdaTrack(int sound, const uint8 *data, bool skipTrackVelo) {
555 const uint8 *ptr = data;
556
557 if (!sound)
558 return;
559
560 if (!skipTrackVelo) {
561 if (_vm->_game.version == 3) {
562 if (_soundOverride[sound].vLeft + _soundOverride[sound].vRight)
563 setVolumeCD(_soundOverride[sound].vLeft, _soundOverride[sound].vRight);
564 else
565 setVolumeCD(ptr[8], ptr[9]);
566 } else {
567 setVolumeCD(ptr[8], ptr[9]);
568 }
569 }
570
571 if (sound == _cdaCurrentSound && _vm->_sound->pollCD() == 1)
572 return;
573
574 ptr += 16;
575
576 int track = ptr[0];
577 _cdaNumLoops = ptr[1];
578 int start = (ptr[2] * 60 + ptr[3]) * 75 + ptr[4];
579 int end = (ptr[5] * 60 + ptr[6]) * 75 + ptr[7];
580
581 _vm->_sound->playCDTrack(track, _cdaNumLoops == 0xff ? -1 : _cdaNumLoops, start, end <= start ? 0 : end - start);
582 _cdaForceRestart = 0;
583 _cdaCurrentSound = sound;
584 }
585
Player_Towns_v2(ScummEngine * vm,Audio::Mixer * mixer,IMuse * imuse,bool disposeIMuse)586 Player_Towns_v2::Player_Towns_v2(ScummEngine *vm, Audio::Mixer *mixer, IMuse *imuse, bool disposeIMuse) : Player_Towns(vm, true), _imuse(imuse), _imuseDispose(disposeIMuse), _sblData(0) {
587 _soundOverride = new SoundOvrParameters[_numSoundMax];
588 memset(_soundOverride, 0, _numSoundMax * sizeof(SoundOvrParameters));
589 _intf = new TownsAudioInterface(mixer, 0, true);
590 }
591
~Player_Towns_v2()592 Player_Towns_v2::~Player_Towns_v2() {
593 delete _intf;
594 _intf = 0;
595
596 if (_imuseDispose)
597 delete _imuse;
598
599 delete[] _sblData;
600 delete[] _soundOverride;
601 }
602
init()603 bool Player_Towns_v2::init() {
604 if (!_intf)
605 return false;
606
607 if (!_intf->init())
608 return false;
609
610 _intf->callback(33, 8);
611 _intf->setSoundEffectChanMask(~0x3f);
612
613 return true;
614 }
615
setMusicVolume(int vol)616 void Player_Towns_v2::setMusicVolume(int vol) {
617 _imuse->setMusicVolume(vol);
618 }
619
getSoundStatus(int sound) const620 int Player_Towns_v2::getSoundStatus(int sound) const {
621 if (_soundOverride[sound].type == 7)
622 return Player_Towns::getSoundStatus(sound);
623 return _imuse->getSoundStatus(sound);
624 }
625
startSound(int sound)626 void Player_Towns_v2::startSound(int sound) {
627 uint8 *ptr = _vm->getResourceAddress(rtSound, sound);
628 assert(ptr);
629
630 if (READ_BE_UINT32(ptr) == MKTAG('T','O','W','S')) {
631 _soundOverride[sound].type = 7;
632 uint8 velo = _soundOverride[sound].velo ? _soundOverride[sound].velo - 1: (ptr[10] + ptr[11] + 1) >> 1;
633 uint8 pan = _soundOverride[sound].pan ? _soundOverride[sound].pan - 1 : 64;
634 uint8 pri = ptr[9];
635 _soundOverride[sound].velo = _soundOverride[sound].pan = 0;
636 playPcmTrack(sound, ptr + 8, velo, pan, ptr[52], pri);
637
638 } else if (READ_BE_UINT32(ptr) == MKTAG('S','B','L',' ')) {
639 _soundOverride[sound].type = 5;
640 playVocTrack(ptr + 27);
641
642 } else {
643 _soundOverride[sound].type = 3;
644 _imuse->startSound(sound);
645 }
646 }
647
stopSound(int sound)648 void Player_Towns_v2::stopSound(int sound) {
649 if (_soundOverride[sound].type == 7) {
650 stopPcmTrack(sound);
651 } else {
652 _imuse->stopSound(sound);
653 }
654 }
655
stopAllSounds()656 void Player_Towns_v2::stopAllSounds() {
657 stopPcmTrack(0);
658 _imuse->stopAllSounds();
659 }
660
doCommand(int numargs,int args[])661 int32 Player_Towns_v2::doCommand(int numargs, int args[]) {
662 int32 res = -1;
663 uint8 *ptr = 0;
664
665 switch (args[0]) {
666 case 8:
667 startSound(args[1]);
668 res = 0;
669 break;
670
671 case 9:
672 case 15:
673 stopSound(args[1]);
674 res = 0;
675 break;
676
677 case 11:
678 stopPcmTrack(0);
679 break;
680
681 case 13:
682 res = getSoundStatus(args[1]);
683 break;
684
685 case 258:
686 if (_soundOverride[args[1]].type == 0) {
687 ptr = _vm->getResourceAddress(rtSound, args[1]);
688 if (READ_BE_UINT32(ptr) == MKTAG('T','O','W','S'))
689 _soundOverride[args[1]].type = 7;
690 }
691 if (_soundOverride[args[1]].type == 7) {
692 _soundOverride[args[1]].velo = args[2] + 1;
693 res = 0;
694 }
695 break;
696
697 case 259:
698 if (_soundOverride[args[1]].type == 0) {
699 ptr = _vm->getResourceAddress(rtSound, args[1]);
700 if (READ_BE_UINT32(ptr) == MKTAG('T','O','W','S'))
701 _soundOverride[args[1]].type = 7;
702 }
703 if (_soundOverride[args[1]].type == 7) {
704 _soundOverride[args[1]].pan = 64 - CLIP<int>(args[2], -63, 63);
705 res = 0;
706 }
707 break;
708
709 default:
710 break;
711 }
712
713 if (res == -1)
714 return _imuse->doCommand(numargs, args);
715
716 return res;
717 }
718
saveLoadWithSerializer(Common::Serializer & s)719 void Player_Towns_v2::saveLoadWithSerializer(Common::Serializer &s) {
720 if (s.getVersion() >= VER(83))
721 Player_Towns::saveLoadWithSerializer(s);
722 }
723
playVocTrack(const uint8 * data)724 void Player_Towns_v2::playVocTrack(const uint8 *data) {
725 static const uint8 header[] = {
726 0x54, 0x61, 0x6C, 0x6B, 0x69, 0x65, 0x20, 0x20,
727 0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00,
728 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
729 0x36, 0x04, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00
730 };
731
732 uint32 len = (READ_LE_UINT32(data) >> 8) - 2;
733
734 int chan = allocatePcmChannel(0xffff, 0, 0x1000);
735 if (!chan)
736 return;
737
738 delete[] _sblData;
739 _sblData = new uint8[len + 32];
740
741 memcpy(_sblData, header, 32);
742 WRITE_LE_UINT32(_sblData + 12, len);
743
744 const uint8 *src = data + 6;
745 uint8 *dst = _sblData + 32;
746 for (uint32 i = 0; i < len; i++)
747 *dst++ = *src & 0x80 ? (*src++ & 0x7f) : -*src++;
748
749 _intf->callback(37, 0x3f + chan, 60, 127, _sblData);
750 _pcmCurrentSound[chan].paused = 0;
751 }
752
753 } // End of namespace Scumm
754