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 #include "audio/soundfont/rawfile.h"
23 #include "audio/soundfont/vab/vab.h"
24 #include "audio/soundfont/vgmcoll.h"
25 #include "audio/mixer.h"
26 #include "audio/audiostream.h"
27 #include "audio/decoders/raw.h"
28 #include "audio/decoders/xa.h"
29 #include "common/config-manager.h"
30 #include "common/file.h"
31 #include "common/memstream.h"
32 #include "dragons/dragons.h"
33 #include "dragons/sound.h"
34 #include "dragons/bigfile.h"
35 #include "dragons/dragonrms.h"
36 #include "dragons/vabsound.h"
37
38 #define RAW_CD_SECTOR_SIZE 2352
39
40 #define CDXA_TYPE_MASK 0x0E
41 #define CDXA_TYPE_DATA 0x08
42 #define CDXA_TYPE_AUDIO 0x04
43
44 namespace Dragons {
45
46 struct SpeechLocation {
47 uint32 talkId;
48 uint16 sectorStart;
49 int8 startOffset;
50 uint16 sectorEnd;
51 } SpeechLocation;
52
CdIntToPos_0(uint32 param_1)53 void CdIntToPos_0(uint32 param_1) { //, byte *param_2)
54 int iVar1;
55 int iVar2;
56 int iVar3;
57 uint8 minute;
58 uint8 second;
59 uint8 sector;
60
61 iVar3 = (param_1 + 0x96) / 0x4b;
62 iVar2 = (param_1 + 0x96) % 0x4b;
63 iVar1 = iVar3 / 0x3c;
64 iVar3 = iVar3 % 0x3c;
65 second = (char)iVar3 + (char)(iVar3 / 10) * 6;
66 sector = (char)iVar2 + (char)(iVar2 / 10) * 6;
67 minute = (char)iVar1 + (char)(iVar1 / 10) * 6;
68
69
70 uint32 out = (((uint)(minute >> 4) * 10 + ((uint)minute & 0xf)) * 0x3c +
71 (uint)(second >> 4) * 10 + ((uint)second & 0xf)) * 0x4b +
72 (uint)(sector >> 4) * 10 + ((uint)sector & 0xf) + -0x96;
73
74 debug(3, "Seek Audio %2X:%2X:%2X in: %d out %d", minute, second, sector, param_1, out);
75
76 return;
77 }
78
playSpeech(uint32 textIndex)79 void SoundManager::playSpeech(uint32 textIndex) {
80 if (isSpeechPlaying()) {
81 _vm->_mixer->stopHandle(_speechHandle);
82 }
83
84 // Reduce music volume while playing dialog.
85 _midiPlayer->setVolume(_musicVolume / 2);
86
87 struct SpeechLocation speechLocation;
88 if (!getSpeechLocation(textIndex, &speechLocation)) {
89 return;
90 }
91
92 Common::File *fd = new Common::File();
93 if (!fd->open("dtspeech.xa")) {
94 error("Failed to open dtspeech.xa");
95 }
96 CdIntToPos_0(speechLocation.sectorStart * 32);
97 PSXAudioTrack *_audioTrack = new PSXAudioTrack();
98
99 _vm->setFlags(ENGINE_FLAG_8000);
100 _vm->_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, _audioTrack->createNewAudioStream(fd, speechLocation.sectorStart, speechLocation.startOffset, speechLocation.sectorEnd), -1, _speechVolume);
101 fd->close();
102 delete fd;
103 delete _audioTrack;
104 }
105
isSpeechPlaying()106 bool SoundManager::isSpeechPlaying() {
107 return _vm->_mixer->isSoundHandleActive(_speechHandle);
108 }
109
getSpeechLocation(uint32 talkId,struct SpeechLocation * location)110 bool SoundManager::getSpeechLocation(uint32 talkId, struct SpeechLocation *location) {
111 Common::File *fd = new Common::File();
112 if (!fd->open("dragon.exe")) {
113 error("Failed to open dragon.exe");
114 }
115 fd->seek(_vm->getSpeechTblOffsetFromDragonEXE());
116 bool foundId = false;
117 for (int i = 0; i < 2272; i++) { //TODO check that the number of speech audio tracks is the same across game variants
118 uint32 id = (fd->readUint32LE() & 0xffffff);
119 fd->seek(-1, SEEK_CUR);
120 int8 startOffset = fd->readSByte();
121 uint16 start = fd->readUint16LE();
122 uint16 end = fd->readUint16LE();
123 if (id == talkId) {
124 location->talkId = id;
125 location->sectorStart = start;
126 location->startOffset = startOffset;
127 location->sectorEnd = end;
128 foundId = true;
129 debug(3, "sectors [%d-%d] unk byte = %d", start * 32, end * 32, startOffset);
130 break;
131 }
132 }
133
134 fd->close();
135 delete fd;
136
137 return foundId;
138 }
139
resumeMusic()140 void SoundManager::resumeMusic() {
141 if (isSpeechPlaying()) {
142 _vm->_mixer->stopHandle(_speechHandle);
143 _vm->clearFlags(ENGINE_FLAG_8000);
144 }
145 if (_currentSong != -1) {
146 _midiPlayer->resume();
147 }
148 }
149
PSXAudioTrack()150 SoundManager::PSXAudioTrack::PSXAudioTrack() {
151 memset(&_adpcmStatus, 0, sizeof(_adpcmStatus));
152 }
153
154 // Ha! It's palindromic!
155 #define AUDIO_DATA_CHUNK_SIZE 2304
156 #define AUDIO_DATA_SAMPLE_COUNT 4032
157
158 static const int s_xaTable[5][2] = {
159 { 0, 0 },
160 { 60, 0 },
161 { 115, -52 },
162 { 98, -55 },
163 { 122, -60 }
164 };
165
queueAudioFromSector(Audio::QueuingAudioStream * audStream,Common::SeekableReadStream * sector)166 void SoundManager::PSXAudioTrack::queueAudioFromSector(Audio::QueuingAudioStream *audStream, Common::SeekableReadStream *sector) {
167 sector->skip(24);
168
169 // This XA audio is different (yet similar) from normal XA audio! Watch out!
170 // TODO: It's probably similar enough to normal XA that we can merge it somehow...
171 // TODO: RTZ PSX needs the same audio code in a regular AudioStream class. Probably
172 // will do something similar to QuickTime and creating a base class 'ISOMode2Parser'
173 // or something similar.
174 byte *buf = new byte[AUDIO_DATA_CHUNK_SIZE];
175 sector->read(buf, AUDIO_DATA_CHUNK_SIZE);
176
177 int channels = audStream->isStereo() ? 2 : 1;
178 int16 *dst = new int16[AUDIO_DATA_SAMPLE_COUNT];
179 int16 *leftChannel = dst;
180 int16 *rightChannel = dst + 1;
181
182 for (byte *src = buf; src < buf + AUDIO_DATA_CHUNK_SIZE; src += 128) {
183 for (int i = 0; i < 4; i++) {
184 int shift = 12 - (src[4 + i * 2] & 0xf);
185 int filter = src[4 + i * 2] >> 4;
186 int f0 = s_xaTable[filter][0];
187 int f1 = s_xaTable[filter][1];
188 int16 s_1 = _adpcmStatus[0].sample[0];
189 int16 s_2 = _adpcmStatus[0].sample[1];
190
191 for (int j = 0; j < 28; j++) {
192 byte d = src[16 + i + j * 4];
193 int t = (int8)(d << 4) >> 4;
194 int s = (t << shift) + ((s_1 * f0 + s_2 * f1 + 32) >> 6);
195 s_2 = s_1;
196 s_1 = CLIP<int>(s, -32768, 32767);
197 *leftChannel = s_1;
198 leftChannel += channels;
199 }
200
201 if (channels == 2) {
202 _adpcmStatus[0].sample[0] = s_1;
203 _adpcmStatus[0].sample[1] = s_2;
204 s_1 = _adpcmStatus[1].sample[0];
205 s_2 = _adpcmStatus[1].sample[1];
206 }
207
208 shift = 12 - (src[5 + i * 2] & 0xf);
209 filter = src[5 + i * 2] >> 4;
210 f0 = s_xaTable[filter][0];
211 f1 = s_xaTable[filter][1];
212
213 for (int j = 0; j < 28; j++) {
214 byte d = src[16 + i + j * 4];
215 int t = (int8)d >> 4;
216 int s = (t << shift) + ((s_1 * f0 + s_2 * f1 + 32) >> 6);
217 s_2 = s_1;
218 s_1 = CLIP<int>(s, -32768, 32767);
219
220 if (channels == 2) {
221 *rightChannel = s_1;
222 rightChannel += 2;
223 } else {
224 *leftChannel++ = s_1;
225 }
226 }
227
228 if (channels == 2) {
229 _adpcmStatus[1].sample[0] = s_1;
230 _adpcmStatus[1].sample[1] = s_2;
231 } else {
232 _adpcmStatus[0].sample[0] = s_1;
233 _adpcmStatus[0].sample[1] = s_2;
234 }
235 }
236 }
237
238 int flags = Audio::FLAG_16BITS;
239
240 if (audStream->isStereo())
241 flags |= Audio::FLAG_STEREO;
242
243 #ifdef SCUMM_LITTLE_ENDIAN
244 flags |= Audio::FLAG_LITTLE_ENDIAN;
245 #endif
246
247 audStream->queueBuffer((byte *)dst, AUDIO_DATA_SAMPLE_COUNT * 2, DisposeAfterUse::YES, flags);
248 delete[] buf;
249 }
250
createNewAudioStream(Common::File * fd,uint16 sectorStart,int8 startOffset,uint16 sectorEnd)251 Audio::QueuingAudioStream *SoundManager::PSXAudioTrack::createNewAudioStream(Common::File *fd, uint16 sectorStart, int8 startOffset, uint16 sectorEnd) {
252 fd->seek(((sectorStart * 32) + startOffset) * RAW_CD_SECTOR_SIZE);
253 fd->skip(19);
254 byte format = fd->readByte();
255 bool stereo = (format & (1 << 0)) != 0;
256 uint rate = (format & (1 << 2)) ? 18900 : 37800;
257
258 Audio::QueuingAudioStream *audStream = Audio::makeQueuingAudioStream(rate, stereo);
259 for (int i = 0x0; i < sectorEnd - sectorStart; i++) {
260 fd->seek(((sectorStart * 32) + startOffset + i * 32) * RAW_CD_SECTOR_SIZE);
261 queueAudioFromSector(audStream, fd);
262 }
263 audStream->finish();
264 return audStream;
265 }
266
SoundManager(DragonsEngine * vm,BigfileArchive * bigFileArchive,DragonRMS * dragonRMS)267 SoundManager::SoundManager(DragonsEngine *vm, BigfileArchive *bigFileArchive, DragonRMS *dragonRMS)
268 : _vm(vm),
269 _sfxVolume(0),
270 _musicVolume(0),
271 _speechVolume(0),
272 _bigFileArchive(bigFileArchive),
273 _dragonRMS(dragonRMS) {
274 _dat_8006bb60_sound_related = 0;
275 _currentSong = -1;
276
277 bool allSoundIsMuted = false;
278 if (ConfMan.hasKey("mute")) {
279 allSoundIsMuted = ConfMan.getBool("mute");
280 }
281
282 if (ConfMan.hasKey("speech_mute") && !allSoundIsMuted) {
283 _vm->_mixer->muteSoundType(_vm->_mixer->kSpeechSoundType, ConfMan.getBool("speech_mute"));
284 }
285
286 if (ConfMan.hasKey("sfx_mute") && !allSoundIsMuted) {
287 _vm->_mixer->muteSoundType(_vm->_mixer->kSFXSoundType, ConfMan.getBool("sfx_mute"));
288 }
289
290 if (ConfMan.hasKey("music_mute") && !allSoundIsMuted) {
291 _vm->_mixer->muteSoundType(_vm->_mixer->kMusicSoundType, ConfMan.getBool("music_mute"));
292 }
293
294 SomeInitSound_FUN_8003f64c();
295 initVabData();
296 _midiPlayer = new MidiMusicPlayer(_bigFileArchive);
297
298 syncSoundSettings();
299 }
300
~SoundManager()301 SoundManager::~SoundManager() {
302 if (isSpeechPlaying()) {
303 _vm->_mixer->stopHandle(_speechHandle);
304 }
305
306 stopAllVoices();
307
308 _midiPlayer->stop();
309
310 delete _midiPlayer;
311 delete _vabMusx;
312 delete _vabMsf;
313 delete _vabGlob;
314 }
315
SomeInitSound_FUN_8003f64c()316 void SoundManager::SomeInitSound_FUN_8003f64c() {
317 // TODO: Check if this changes on different game versions?
318 memset(_sfxVolumeTbl, 0x10, sizeof(_sfxVolumeTbl));
319
320 _sfxVolumeTbl[192] = 0x0b;
321 _sfxVolumeTbl[193] = 0x0b;
322 _sfxVolumeTbl[226] = _sfxVolumeTbl[226] | 0x80u;
323 _sfxVolumeTbl[229] = 0x0b;
324 _sfxVolumeTbl[230] = 0x0b;
325 _sfxVolumeTbl[450] = 0x0b;
326 _sfxVolumeTbl[451] = 0x0b;
327 _sfxVolumeTbl[514] = 0x8b;
328 _sfxVolumeTbl[515] = 0x0b;
329 _sfxVolumeTbl[516] = 0x0b;
330 _sfxVolumeTbl[578] = 0x0b;
331 _sfxVolumeTbl[579] = 0x0b;
332 _sfxVolumeTbl[580] = 0x0b;
333 _sfxVolumeTbl[611] = 0x0b;
334 _sfxVolumeTbl[674] = 0x8b;
335 _sfxVolumeTbl[675] = 0x88;
336 _sfxVolumeTbl[711] = 0x08;
337 _sfxVolumeTbl[866] = 0x0b;
338 _sfxVolumeTbl[896] = 0x0b;
339 _sfxVolumeTbl[897] = _sfxVolumeTbl[897] | 0x80u;
340 _sfxVolumeTbl[930] = _sfxVolumeTbl[930] | 0x80u;
341 _sfxVolumeTbl[934] = 0x8b;
342 _sfxVolumeTbl[935] = 0x8b;
343 _sfxVolumeTbl[936] = 0x0b;
344 _sfxVolumeTbl[937] = 0x88;
345 _sfxVolumeTbl[941] = 0x0b;
346 _sfxVolumeTbl[964] = 0x0b;
347 _sfxVolumeTbl[995] = _sfxVolumeTbl[995] | 0x80u;
348 _sfxVolumeTbl[1027] = 0x08;
349 _sfxVolumeTbl[1056] = 0x8b;
350 _sfxVolumeTbl[1059] = _sfxVolumeTbl[1059] | 0x80u;
351 _sfxVolumeTbl[1122] = 0x0b;
352 _sfxVolumeTbl[1250] = 0x08;
353 _sfxVolumeTbl[1252] = 0x0b;
354 _sfxVolumeTbl[1256] = 0x0b;
355 _sfxVolumeTbl[1257] = 0x08;
356 _sfxVolumeTbl[1258] = 0x0b;
357 _sfxVolumeTbl[1284] = 0x0b;
358 _sfxVolumeTbl[1378] = 0x0b;
359 _sfxVolumeTbl[1379] = _sfxVolumeTbl[1379] | 0x80u;
360 _sfxVolumeTbl[1380] = 0x0b;
361 _sfxVolumeTbl[1385] = 0x0b;
362 _sfxVolumeTbl[1443] = 0x8b;
363 _sfxVolumeTbl[1444] = _sfxVolumeTbl[1444] | 0x80u;
364 _sfxVolumeTbl[1445] = _sfxVolumeTbl[1445] | 0x80u;
365 _sfxVolumeTbl[1446] = 0x8b;
366 _sfxVolumeTbl[1472] = 0x8b;
367 _sfxVolumeTbl[1508] = _sfxVolumeTbl[1508] | 0x80u;
368 _sfxVolumeTbl[1575] = 0x08;
369 _sfxVolumeTbl[1576] = 0x08;
370 _sfxVolumeTbl[1577] = 0x08;
371 _sfxVolumeTbl[1604] = 0x08;
372 _sfxVolumeTbl[1605] = 0x08;
373 _sfxVolumeTbl[1610] = 0x0b;
374 _sfxVolumeTbl[1611] = 0x0b;
375 _sfxVolumeTbl[1612] = 0x0b;
376 }
377
initVabData()378 void SoundManager::initVabData() {
379 _vabMusx = loadVab("musx.vh", "musx.vb");
380 _vabMsf = loadVab("musx.vh", "musx.vb");
381 _vabGlob = loadVab("glob.vh", "glob.vb");
382 }
383
loadVab(const char * headerFilename,const char * bodyFilename)384 VabSound * SoundManager::loadVab(const char *headerFilename, const char *bodyFilename) {
385 uint32 headSize, bodySize;
386
387 byte *headData = _bigFileArchive->load(headerFilename, headSize);
388 byte *bodyData = _bigFileArchive->load(bodyFilename, bodySize);
389
390 Common::SeekableReadStream *headStream = new Common::MemoryReadStream(headData, headSize, DisposeAfterUse::YES);
391 Common::SeekableReadStream *bodyStream = new Common::MemoryReadStream(bodyData, bodySize, DisposeAfterUse::YES);
392
393 return new VabSound(headStream, bodyStream);
394 }
395
396 /**
397 *
398 * @param soundId Bit 0x4000 set indicates STOP SOUND, bit 0x8000 set indicates SOUND IS GLOBAL (comes from glob.v[hb])
399 */
playOrStopSound(uint16 soundId)400 void SoundManager::playOrStopSound(uint16 soundId) {
401 uint16 volumeId;
402 if ((soundId & 0x8000u) == 0) {
403 volumeId = (soundId & ~0x4000u) + _vm->getCurrentSceneId() * 0x20;
404 } else {
405 volumeId = soundId & ~(0x4000u | 0x8000u);
406 }
407
408 if ((soundId & 0x4000u) == 0) {
409 playSound(soundId, volumeId);
410 } else {
411 stopSound(soundId, volumeId);
412 }
413 }
414
playSound(uint16 soundId,uint16 volumeId)415 void SoundManager::playSound(uint16 soundId, uint16 volumeId) {
416 byte volume = 0;
417
418 volume = _sfxVolumeTbl[volumeId] & 0x1fu;
419 _sfxVolumeTbl[volumeId] = _sfxVolumeTbl[volumeId] | 0x40u; // Set bit 0x40
420
421 VabSound *vabSound = ((soundId & 0x8000u) != 0) ? _vabGlob : _vabMsf;
422
423 uint16 realId = soundId & 0x7fffu;
424
425 uint16 program = realId >> 4u;
426 uint16 key = ((realId & 0xfu) << 1u | 0x40u);
427
428 if (isVoicePlaying(soundId)) {
429 stopVoicePlaying(soundId);
430 }
431
432 if (vabSound->hasSound(program, key)) {
433 Audio::SoundHandle *handle = getVoiceHandle(soundId);
434 if (handle) {
435 uint8 adjustedVolume = (uint8)((float)_sfxVolume * ((float)volume / 31));
436 debug(3, "Playing SFX: Master Volume %d Adjusted Volume %d diff %f%%", _sfxVolume, adjustedVolume, 100 * ((float)volume / 31));
437 Audio::AudioStream *audioStream = vabSound->getAudioStream(program, key);
438 if (audioStream) {
439 _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, handle, audioStream, -1, adjustedVolume);
440 }
441 }
442 } else {
443 warning("Sound not found Program: %d, key %d", program, key);
444 }
445 }
446
stopSound(uint16 soundId,uint16 volumeId)447 void SoundManager::stopSound(uint16 soundId, uint16 volumeId) {
448 _sfxVolumeTbl[volumeId] = _sfxVolumeTbl[volumeId] & 0xbfu; // Clear bit 0x40
449
450 // uint16 vabId = getVabFromSoundId(soundId);
451
452 stopVoicePlaying(soundId & ~0x4000u);
453 }
454
getVabFromSoundId(uint16 soundId)455 uint16 SoundManager::getVabFromSoundId(uint16 soundId) {
456 // TODO
457 return 0;
458 }
459
loadMsf(uint32 sceneId)460 void SoundManager::loadMsf(uint32 sceneId) {
461 char msfFileName[] = "XXXX.MSF";
462 memcpy(msfFileName, _dragonRMS->getSceneName(sceneId), 4);
463
464 debug(3, "Loading SFX file %s", msfFileName);
465
466 if (_bigFileArchive->doesFileExist(msfFileName)) {
467 uint32 msfSize;
468 byte *msfData = _bigFileArchive->load(msfFileName, msfSize);
469
470 Common::SeekableReadStream *msfStream = new Common::MemoryReadStream(msfData, msfSize, DisposeAfterUse::YES);
471
472 stopAllVoices();
473
474 delete _vabMsf;
475 _vabMsf = new VabSound(msfStream, _vm);
476 }
477 }
478
isVoicePlaying(uint16 soundID)479 bool SoundManager::isVoicePlaying(uint16 soundID) {
480 for (int i = 0; i < NUM_VOICES; i++) {
481 if (_voice[i].soundID == soundID && _vm->_mixer->isSoundHandleActive(_voice[i].handle)) {
482 return true;
483 }
484 }
485 return false;
486 }
487
getVoiceHandle(uint16 soundID)488 Audio::SoundHandle *SoundManager::getVoiceHandle(uint16 soundID) {
489 for (int i = 0; i < NUM_VOICES; i++) {
490 if (!_vm->_mixer->isSoundHandleActive(_voice[i].handle)) {
491 _voice[i].soundID = soundID & ~0x4000u;
492 return &_voice[i].handle;
493 }
494 }
495 return nullptr;
496 }
497
stopVoicePlaying(uint16 soundID)498 void SoundManager::stopVoicePlaying(uint16 soundID) {
499 for (int i = 0; i < NUM_VOICES; i++) {
500 if (_voice[i].soundID == soundID) {
501 _vm->_mixer->stopHandle(_voice[i].handle);
502 return;
503 }
504 }
505 }
506
stopAllVoices()507 void SoundManager::stopAllVoices() {
508 for (int i = 0; i < NUM_VOICES; i++) {
509 _vm->_mixer->stopHandle(_voice[i].handle);
510 }
511 }
512
playMusic(int16 song)513 void SoundManager::playMusic(int16 song) {
514 char sceneName[5] = "nnnn";
515 char filename[12] = "xxxxznn.msq";
516
517 if (_currentSong == song) {
518 return;
519 }
520
521 _currentSong = song;
522
523 memcpy(sceneName, _vm->_dragonRMS->getSceneName(_vm->getCurrentSceneId()), 4);
524 snprintf(filename, 12, "%sz%02d.msq", sceneName, song);
525 debug(1, "Load music file %s", filename);
526
527 if (!_bigFileArchive->doesFileExist(filename)) {
528 warning("Could not find music file %s", filename);
529 return;
530 }
531
532 uint32 dataSize;
533 byte *seqData = _bigFileArchive->load(filename, dataSize);
534 Common::MemoryReadStream *seq = new Common::MemoryReadStream(seqData, dataSize, DisposeAfterUse::YES);
535 _midiPlayer->playSong(seq);
536 delete seq;
537 }
538
syncSoundSettings()539 void SoundManager::syncSoundSettings() {
540 _musicVolume = CLIP<int>(ConfMan.getInt("music_volume"), 0, 255);
541 _sfxVolume = CLIP<int>(ConfMan.getInt("sfx_volume"), 0, 255);
542 _speechVolume = CLIP<int>(ConfMan.getInt("speech_volume"), 0, 255);
543
544 _midiPlayer->setVolume(_musicVolume);
545 }
546
547 } // End of namespace Dragons
548