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 // FIXME: This code is taken from MADE and may need more work (e.g. setVolume).
24
25 // MIDI and digital music class
26
27 #include "audio/audiostream.h"
28 #include "audio/mididrv.h"
29 #include "audio/midiparser.h"
30 // Miles Audio for Discworld 1
31 #include "audio/miles.h"
32
33 #include "backends/audiocd/audiocd.h"
34
35 #include "common/config-manager.h"
36 #include "common/file.h"
37 #include "common/memstream.h"
38
39 #include "tinsel/adpcm.h"
40 #include "tinsel/config.h"
41 #include "tinsel/sound.h"
42 #include "tinsel/music.h"
43 #include "tinsel/handle.h"
44 #include "tinsel/sysvar.h"
45
46 enum {
47 MUSIC_JUMP = -1,
48 MUSIC_END = -2,
49
50 BLMAGIC = -3458,
51
52 DIM_SPEED = 8
53 };
54
55 namespace Tinsel {
56
57 //--------------------------- Midi data -------------------------------------
58
59 // sound buffer structure used for MIDI data and samples
60 struct SOUND_BUFFER {
61 uint8 *pDat; // pointer to actual buffer
62 uint32 size; // size of the buffer
63 };
64
65 // FIXME: Avoid non-const global vars
66
67 // MIDI buffer
68 static SOUND_BUFFER g_midiBuffer = { 0, 0 };
69
70 static SCNHANDLE g_currentMidi = 0;
71 static bool g_currentLoop = false;
72
73 // We allocate 155 entries because that's the maximum, used in the SCN version
74 static SCNHANDLE g_midiOffsets[155];
75
76 static const int enhancedAudioGRAVersion[] = {
77 1, 2, 1, 1, 3, 3, 4, 4, 5, 6, // 1-10
78 1, 7, 8, 9, 10, 3, 11, 11, 12, 13, // 11-20
79 13, 13, 13, 13, 14, 13, 13, 15, 16, 17, // 21-30
80 15, 18, 19, 20, 338, 21, 21, 22, 22, 23, // 31-40
81 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, // 41-50
82 34, 35, 35, 36, 37, 38, 39, 39, 39, 39, // 51-60
83 40, 39, 41, 41, 42, 43, 42, 44, 45, 41, // 61-70
84 46, 48, 47, 48, 49, 50, 51, 52, 53, 54, // 71-80
85 55, 56, 57, 58, 59, 60, 61, 62, 63, 61, // 81-90
86 64, 65, 66, 67, 68, 69, 70, 68, 71, 72, // 91-100
87 73, 74, 75, 12, 76, 77, 78, 79, 80, 4, // 101-110
88 81, 82, 83, 82, 81, 84, 85, 86, 87, 88, // 111-120
89 89, 90, 88, 2, 2, 2, 2, 2, 2, 60, // 121-130
90 91, 92, 93, 94, 94, 95, 96, 52, 4, 97, // 131-140
91 98, 99, 99 // 141-143
92 };
93
94 static const int enhancedAudioSCNVersion[] = {
95 301, 302, 2, 1, 1, 301, 302, 3, 3, 4, // 1-10
96 4, 5, 6, 1, 7, 8, 9, 10, 8, 11, // 11-20
97 11, 12, 13, 13, 13, 13, 13, 14, 13, 13, // 21-30
98 15, 16, 17, 15, 18, 19, 20, 338, 21, 21, // 31-40
99 341, 342, 22, 22, 23, 24, 25, 26, 27, 28, // 41-50
100 29, 30, 31, 32, 33, 34, 35, 35, 36, 37, // 51-60
101 38, 39, 39, 39, 39, 40, 39, 41, 41, 42, // 61-70
102 43, 42, 44, 45, 41, 46, 48, 47, 48, 49, // 71-80
103 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, // 81-90
104 60, 61, 62, 63, 61, 64, 65, 66, 67, 68, // 91-100
105 69, 70, 68, 71, 72, 73, 74, 75, 12, 76, // 101-110
106 77, 78, 79, 80, 4, 4, 82, 83, 77, 4, // 111-120
107 84, 85, 86, 3124, 88, 89, 90, 88, 2, 2, // 121-130
108 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 131-140
109 3142, 91, 92, 93, 94, 94, 95, 96, 52, 4, // 141-150
110 97, 98, 99, 99 // 151-154
111 };
112
GetTrackNumber(SCNHANDLE hMidi)113 int GetTrackNumber(SCNHANDLE hMidi) {
114 for (int i = 0; i < ARRAYSIZE(g_midiOffsets); i++) {
115 if (g_midiOffsets[i] == hMidi)
116 return i;
117 }
118
119 return -1;
120 }
121
GetTrackOffset(int trackNumber)122 SCNHANDLE GetTrackOffset(int trackNumber) {
123 assert(trackNumber < ARRAYSIZE(g_midiOffsets));
124 return g_midiOffsets[trackNumber];
125 }
126
127 /**
128 * Plays the specified MIDI sequence through the sound driver.
129 * @param dwFileOffset File offset of MIDI sequence data
130 * @param bLoop Whether to loop the sequence
131 */
PlayMidiSequence(uint32 dwFileOffset,bool bLoop)132 bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
133 g_currentMidi = dwFileOffset;
134 g_currentLoop = bLoop;
135
136 bool mute = false;
137 if (ConfMan.hasKey("mute"))
138 mute = ConfMan.getBool("mute");
139
140 SetMidiVolume(mute ? 0 : _vm->_config->_musicVolume);
141
142 // the index and length of the last tune loaded
143 uint32 dwSeqLen = 0; // length of the sequence
144
145 // Support for external music from the music enhancement project
146 if (_vm->getFeatures() & GF_ENHANCED_AUDIO_SUPPORT) {
147 int trackNumber = GetTrackNumber(dwFileOffset);
148 // Track 8 has been removed in the German CD re-release "Neon Edition"
149 if ((_vm->getFeatures() & GF_ALT_MIDI) && trackNumber >= 8)
150 trackNumber++;
151
152 int track = 0;
153 if (trackNumber >= 0) {
154 if (_vm->getFeatures() & GF_SCNFILES)
155 track = enhancedAudioSCNVersion[trackNumber];
156 else
157 track = enhancedAudioGRAVersion[trackNumber];
158
159 if (track > 0) {
160 StopMidi();
161
162 // StopMidi resets these fields, so set them again
163 g_currentMidi = dwFileOffset;
164 g_currentLoop = bLoop;
165
166 // try to play track, but don't fall back to a true CD
167 g_system->getAudioCDManager()->play(track, bLoop ? -1 : 1, 0, 0, true);
168
169 // Check if an enhanced audio track is being played.
170 // If it is, stop here and don't load a MIDI track
171 if (g_system->getAudioCDManager()->isPlaying()) {
172 return true;
173 }
174 }
175 } else {
176 warning("Unknown MIDI offset %d", dwFileOffset);
177 }
178 }
179
180 if (dwFileOffset == 0)
181 return true;
182
183 Common::File midiStream;
184
185 // open MIDI sequence file in binary mode
186 if (!midiStream.open(MIDI_FILE))
187 error(CANNOT_FIND_FILE, MIDI_FILE);
188
189 // move to correct position in the file
190 midiStream.seek(dwFileOffset, SEEK_SET);
191
192 if (TinselV1Mac) {
193 // The Macintosh version of DW1 uses raw PCM for music
194 dwSeqLen = midiStream.readUint32BE();
195 _vm->_sound->playDW1MacMusic(midiStream, dwSeqLen);
196 } else {
197 dwSeqLen = midiStream.readUint32LE();
198
199 // make sure buffer is large enough for this sequence
200 assert(dwSeqLen > 0 && dwSeqLen <= g_midiBuffer.size);
201
202 // stop any currently playing tune
203 _vm->_midiMusic->stop();
204
205 // read the sequence. This needs to be read again before playSEQ() is
206 // called even if the music is restarting, as playSEQ() reads the file
207 // name off the buffer itself. However, that function adds SMF headers
208 // to the buffer, thus if it's read again, the SMF headers will be read
209 // and the filename will always be 'MThd'.
210 if (midiStream.read(g_midiBuffer.pDat, dwSeqLen) != dwSeqLen)
211 error(FILE_IS_CORRUPT, MIDI_FILE);
212
213 // WORKAROUND for bug #2820054 "DW1: No intro music at first start on Wii",
214 // which actually affects all ports, since it's specific to the GRA version.
215 //
216 // The GRA version does not seem to set the channel volume at all for the first
217 // intro track, thus we need to do that here. We only initialize the channels
218 // used in that sequence. And we are using 127 as default channel volume.
219 //
220 // Only in the GRA version dwFileOffset can be "38888", just to be sure, we
221 // check for the SCN files feature flag not being set though.
222 if (_vm->getGameID() == GID_DW1 && dwFileOffset == 38888 && !(_vm->getFeatures() & GF_SCNFILES)) {
223 _vm->_midiMusic->send(0x7F07B0 | 3);
224 _vm->_midiMusic->send(0x7F07B0 | 5);
225 _vm->_midiMusic->send(0x7F07B0 | 8);
226 _vm->_midiMusic->send(0x7F07B0 | 10);
227 _vm->_midiMusic->send(0x7F07B0 | 13);
228 }
229
230 _vm->_midiMusic->playMIDI(dwSeqLen, bLoop);
231 }
232
233 midiStream.close();
234
235 return true;
236 }
237
238 /**
239 * Returns TRUE if a Midi tune is currently playing.
240 */
MidiPlaying()241 bool MidiPlaying() {
242 if (_vm->getFeatures() & GF_ENHANCED_AUDIO_SUPPORT) {
243 if (g_system->getAudioCDManager()->isPlaying())
244 return true;
245 }
246 return _vm->_midiMusic->isPlaying();
247 }
248
249 /**
250 * Stops any currently playing midi.
251 */
StopMidi()252 bool StopMidi() {
253 g_currentMidi = 0;
254 g_currentLoop = false;
255
256 if (_vm->getFeatures() & GF_ENHANCED_AUDIO_SUPPORT) {
257 g_system->getAudioCDManager()->stop();
258 }
259
260 _vm->_midiMusic->stop();
261 return true;
262 }
263
264
265 /**
266 * Gets the volume of the MIDI music.
267 */
GetMidiVolume()268 int GetMidiVolume() {
269 return _vm->_config->_musicVolume;
270 }
271
272 /**
273 * Sets the volume of the MIDI music.
274 * @param vol New volume - 0..MAXMIDIVOL
275 */
SetMidiVolume(int vol)276 void SetMidiVolume(int vol) {
277 assert(vol >= 0 && vol <= Audio::Mixer::kMaxChannelVolume);
278 _vm->_midiMusic->setVolume(vol);
279 }
280
281 /**
282 * Opens and inits all MIDI sequence files.
283 */
OpenMidiFiles()284 void OpenMidiFiles() {
285 Common::File midiStream;
286
287 if (TinselV0) {
288 // The early demo version of DW1 doesn't have MIDI
289 } else if (TinselV2) {
290 // DW2 uses a different music mechanism
291 } else if (TinselV1Mac) {
292 // open MIDI sequence file in binary mode
293 if (!midiStream.open(MIDI_FILE))
294 error(CANNOT_FIND_FILE, MIDI_FILE);
295
296 uint32 curTrack = 1;
297 uint32 songLength = 0;
298 int32 fileSize = midiStream.size();
299
300 // Init
301 for (int i = 0; i < ARRAYSIZE(g_midiOffsets); i++)
302 g_midiOffsets[i] = 0;
303
304 midiStream.skip(4); // skip file header
305
306 while (!midiStream.eos() && !midiStream.err() && midiStream.pos() != fileSize) {
307 assert(curTrack < ARRAYSIZE(g_midiOffsets));
308 g_midiOffsets[curTrack] = midiStream.pos();
309 //debug("%d: %d", curTrack, g_midiOffsets[curTrack]);
310
311 songLength = midiStream.readUint32BE();
312 midiStream.skip(songLength);
313
314 curTrack++;
315 }
316
317 midiStream.close();
318 } else {
319 if (g_midiBuffer.pDat)
320 // already allocated
321 return;
322
323 // open MIDI sequence file in binary mode
324 if (!midiStream.open(MIDI_FILE))
325 error(CANNOT_FIND_FILE, MIDI_FILE);
326
327 // get length of the largest sequence
328 g_midiBuffer.size = midiStream.readUint32LE();
329 if (midiStream.eos() || midiStream.err())
330 error(FILE_IS_CORRUPT, MIDI_FILE);
331
332 if (g_midiBuffer.size) {
333 // allocate a buffer big enough for the largest MIDI sequence
334 if ((g_midiBuffer.pDat = (uint8 *)malloc(g_midiBuffer.size)) != NULL) {
335 // clear out the buffer
336 memset(g_midiBuffer.pDat, 0, g_midiBuffer.size);
337 }
338 }
339
340 // Now scan through the contents of the MIDI file to find the offset
341 // of each individual track, in order to create a mapping from MIDI
342 // offset to track number, for the enhanced MIDI soundtrack.
343 // The first song is always at position 4. The subsequent ones are
344 // calculated dynamically.
345 uint32 curOffset = 4;
346 uint32 curTrack = 0;
347 uint32 songLength = 0;
348
349 // Init
350 for (int i = 0; i < ARRAYSIZE(g_midiOffsets); i++)
351 g_midiOffsets[i] = 0;
352
353 while (!midiStream.eos() && !midiStream.err()) {
354 if (curOffset + (4 * curTrack) >= (uint32)midiStream.size())
355 break;
356
357 assert(curTrack < ARRAYSIZE(g_midiOffsets));
358 g_midiOffsets[curTrack] = curOffset + (4 * curTrack);
359 //debug("%d: %d", curTrack, midiOffsets[curTrack]);
360
361 songLength = midiStream.readUint32LE();
362 curOffset += songLength;
363 midiStream.skip(songLength);
364
365 curTrack++;
366 }
367
368 midiStream.close();
369 }
370 }
371
DeleteMidiBuffer()372 void DeleteMidiBuffer() {
373 free(g_midiBuffer.pDat);
374 g_midiBuffer.pDat = NULL;
375 }
376
MidiMusicPlayer(TinselEngine * vm)377 MidiMusicPlayer::MidiMusicPlayer(TinselEngine *vm) {
378 _driver = NULL;
379 _milesAudioMode = false;
380 bool milesAudioEnabled = false;
381
382 if (vm->getPlatform() == Common::kPlatformDOS) {
383 // Enable Miles Audio for DOS platform only...
384 switch (vm->getGameID()) {
385 case GID_DW1:
386 if (!vm->getIsADGFDemo()) {
387 // ...for Discworld 1
388 milesAudioEnabled = true;
389 } else {
390 if (vm->isV1CD()) {
391 // ...and for Discworld 1 CD Demo
392 milesAudioEnabled = true;
393 }
394 }
395 break;
396 default:
397 break;
398 }
399 }
400
401 if (milesAudioEnabled) {
402 // Discworld 1 (DOS) uses Miles Audio 3
403 // use our own Miles Audio drivers
404 //
405 // It seems that there are multiple versions of Discworld 1
406 //
407 // Version 1:
408 // Has SAMPLE.AD for AdLib and SAMPLE.OPL for OPL-3
409 // Timbre files are inside a subdirectory of the CD called "/drivers". Main game files are in
410 // another subdirectory, which means the user has to copy those files over.
411 // Installer script copies all drivers directly to harddrive without name changes
412 //
413 // Version 2:
414 // Has FAT.OPL only (gets copied by the installer into MIDPAK.AD or MIDPAK.OPL)
415 // Timbre file is inside subdirectory "drivers" right in the main game directory.
416 // Installer copies FAT.OPL to MIDPAK.AD all the time, even when user selected AWE32
417 //
418 // Neither have timbre data for MT32
419
420 ::MidiDriver::DeviceHandle dev = ::MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
421 ::MusicType musicType = ::MidiDriver::getMusicType(dev);
422 Common::File fileClass;
423
424 switch (musicType) {
425 case MT_ADLIB:
426 if (fileClass.exists("FAT.OPL")) {
427 // Version 2: fat.opl, may be in drivers-subdirectory
428 _driver = Audio::MidiDriver_Miles_AdLib_create("", "FAT.OPL");
429 } else {
430 if (fileClass.exists("MIDPAK.AD")) {
431 // Version 2: drivers got installed and fat.opl got copied over by the user
432 _driver = Audio::MidiDriver_Miles_AdLib_create("MIDPAK.AD", "");
433 } else {
434 if ((fileClass.exists("SAMPLE.AD")) || (fileClass.exists("SAMPLE.OPL"))) {
435 // Version 1: sample.ad / sample.opl, have to be copied over by the user for this version
436 _driver = Audio::MidiDriver_Miles_AdLib_create("SAMPLE.AD", "SAMPLE.OPL");
437 } else {
438 error("MILES-ADLIB: timbre file not found (may be called FAT.OPL, MIDPAK.AD, SAMPLE.AD or SAMPLE.OPL, may be in a subdirectory)");
439 }
440 }
441 }
442 break;
443 case MT_MT32:
444 // Discworld 1 doesn't have a MT32 timbre file
445 _driver = Audio::MidiDriver_Miles_MT32_create("");
446 break;
447 case MT_GM:
448 if (ConfMan.getBool("native_mt32")) {
449 _driver = Audio::MidiDriver_Miles_MT32_create("");
450 musicType = MT_MT32;
451 }
452 break;
453 default:
454 break;
455 }
456 if (!_driver) {
457 // nothing got created yet? -> create default driver
458 MidiPlayer::createDriver();
459 } else {
460 _milesAudioMode = true;
461 }
462
463 } else {
464 MidiPlayer::createDriver();
465 }
466
467 int ret = _driver->open();
468 if (ret == 0) {
469 if (_nativeMT32)
470 _driver->sendMT32Reset();
471 else
472 _driver->sendGMReset();
473
474 _driver->setTimerCallback(this, &timerCallback);
475 }
476 }
477
setVolume(int volume)478 void MidiMusicPlayer::setVolume(int volume) {
479 _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, volume);
480
481 Audio::MidiPlayer::setVolume(volume);
482 }
483
send(uint32 b)484 void MidiMusicPlayer::send(uint32 b) {
485 if (_milesAudioMode) {
486 _driver->send(b);
487 return;
488 }
489
490 Audio::MidiPlayer::send(b);
491
492 byte channel = (byte)(b & 0x0F);
493 if (_channelsTable[channel]) {
494 if ((b & 0xFFF0) == 0x0079B0) {
495 // We've just Reset All Controllers, so we need to
496 // re-adjust the volume. Otherwise, volume is reset to
497 // default whenever the music changes.
498 _channelsTable[channel]->send(0x000007B0 | (((_channelsVolume[channel] * _masterVolume) / 255) << 16) | channel);
499 }
500 }
501 }
502
playMIDI(uint32 size,bool loop)503 void MidiMusicPlayer::playMIDI(uint32 size, bool loop) {
504 Common::StackLock lock(_mutex);
505
506 if (_isPlaying)
507 return;
508
509 stop();
510
511 if (TinselV1PSX)
512 playSEQ(size, loop);
513 else
514 playXMIDI(size, loop);
515 }
516
playXMIDI(uint32 size,bool loop)517 void MidiMusicPlayer::playXMIDI(uint32 size, bool loop) {
518 // It seems like not all music (the main menu music, for instance) set
519 // all the instruments explicitly. That means the music will sound
520 // different, depending on which music played before it. This appears
521 // to be a genuine glitch in the original. For consistency, reset all
522 // instruments to the default one (piano).
523
524 for (int i = 0; i < 16; i++) {
525 _driver->send(0xC0 | i, 0, 0);
526 }
527
528 // Load XMID resource data
529
530 MidiParser *parser = MidiParser::createParser_XMIDI();
531 if (parser->loadMusic(g_midiBuffer.pDat, size)) {
532 parser->setTrack(0);
533 parser->setMidiDriver(this);
534 parser->setTimerRate(getBaseTempo());
535 parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
536 parser->property(MidiParser::mpSendSustainOffOnNotesOff, 1);
537
538 _parser = parser;
539
540 _isLooping = loop;
541 _isPlaying = true;
542 } else {
543 delete parser;
544 }
545 }
546
playSEQ(uint32 size,bool loop)547 void MidiMusicPlayer::playSEQ(uint32 size, bool loop) {
548 // MIDI.DAT holds the file names in DW1 PSX
549 Common::String baseName((char *)g_midiBuffer.pDat, size);
550 Common::String seqName = baseName + ".SEQ";
551
552 // TODO: Load the instrument bank (<baseName>.VB and <baseName>.VH)
553
554 Common::File seqFile;
555 if (!seqFile.open(seqName))
556 error("Failed to open SEQ file '%s'", seqName.c_str());
557
558 if (seqFile.readUint32LE() != MKTAG('S', 'E', 'Q', 'p'))
559 error("Failed to find SEQp tag");
560
561 // Make sure we don't have a SEP file (with multiple SEQ's inside)
562 if (seqFile.readUint32BE() != 1)
563 error("Can only play SEQ files, not SEP");
564
565 uint16 ppqn = seqFile.readUint16BE();
566 uint32 tempo = seqFile.readUint16BE() << 8;
567 tempo |= seqFile.readByte();
568 /* uint16 beat = */ seqFile.readUint16BE();
569
570 // SEQ is directly based on SMF and we'll use that to our advantage here
571 // and convert to SMF and then use the SMF MidiParser.
572
573 // Calculate the SMF size we'll need
574 uint32 dataSize = seqFile.size() - 15;
575 uint32 actualSize = dataSize + 7 + 22;
576
577 // Resize the buffer if necessary
578 if (g_midiBuffer.size < actualSize) {
579 g_midiBuffer.pDat = (byte *)realloc(g_midiBuffer.pDat, actualSize);
580 assert(g_midiBuffer.pDat);
581 }
582
583 // Now construct the header
584 WRITE_BE_UINT32(g_midiBuffer.pDat, MKTAG('M', 'T', 'h', 'd'));
585 WRITE_BE_UINT32(g_midiBuffer.pDat + 4, 6); // header size
586 WRITE_BE_UINT16(g_midiBuffer.pDat + 8, 0); // type 0
587 WRITE_BE_UINT16(g_midiBuffer.pDat + 10, 1); // one track
588 WRITE_BE_UINT16(g_midiBuffer.pDat + 12, ppqn);
589 WRITE_BE_UINT32(g_midiBuffer.pDat + 14, MKTAG('M', 'T', 'r', 'k'));
590 WRITE_BE_UINT32(g_midiBuffer.pDat + 18, dataSize + 7); // SEQ data size + tempo change event size
591
592 // Add in a fake tempo change event
593 WRITE_BE_UINT32(g_midiBuffer.pDat + 22, 0x00FF5103); // no delta, meta event, tempo change, param size = 3
594 WRITE_BE_UINT16(g_midiBuffer.pDat + 26, tempo >> 8);
595 g_midiBuffer.pDat[28] = tempo & 0xFF;
596
597 // Now copy in the rest of the events
598 seqFile.read(g_midiBuffer.pDat + 29, dataSize);
599 seqFile.close();
600
601 MidiParser *parser = MidiParser::createParser_SMF();
602 if (parser->loadMusic(g_midiBuffer.pDat, actualSize)) {
603 parser->setTrack(0);
604 parser->setMidiDriver(this);
605 parser->setTimerRate(getBaseTempo());
606 parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
607 parser->property(MidiParser::mpSendSustainOffOnNotesOff, 1);
608
609 _parser = parser;
610
611 _isLooping = loop;
612 _isPlaying = true;
613 } else {
614 delete parser;
615 }
616 }
617
pause()618 void MidiMusicPlayer::pause() {
619 setVolume(-1);
620 _isPlaying = false;
621 }
622
resume()623 void MidiMusicPlayer::resume() {
624 setVolume(GetMidiVolume());
625 _isPlaying = true;
626 }
627
PCMMusicPlayer()628 PCMMusicPlayer::PCMMusicPlayer() {
629 _silenceSamples = 0;
630
631 _curChunk = 0;
632 _state = S_IDLE;
633 _mState = S_IDLE;
634 _scriptNum = -1;
635 _scriptIndex = 0;
636 _forcePlay = false;
637
638 _volume = 255;
639 _dimmed = false;
640 _dimmedTinsel = false;
641 _dimIteration = 0;
642 _dimmedVolume = 0;
643 _dimPosition = 0;
644
645 _fadeOutVolume = 0;
646 _fadeOutIteration = 0;
647
648 _hScript = _hSegment = 0;
649
650 _end = true;
651
652 _vm->_mixer->playStream(Audio::Mixer::kMusicSoundType,
653 &_handle, this, -1, _volume, 0, DisposeAfterUse::NO, true);
654 }
655
~PCMMusicPlayer()656 PCMMusicPlayer::~PCMMusicPlayer() {
657 _vm->_mixer->stopHandle(_handle);
658 delete _curChunk;
659 }
660
startPlay(int id)661 void PCMMusicPlayer::startPlay(int id) {
662 if (_filename.empty())
663 return;
664
665 debugC(DEBUG_DETAILED, kTinselDebugMusic, "Playing PCM music %s, index %d", _filename.c_str(), id);
666
667 Common::StackLock slock(_mutex);
668
669 stop();
670
671 _scriptNum = id;
672 _scriptIndex = 1;
673 _state = S_NEW;
674
675 play();
676 }
677
stopPlay()678 void PCMMusicPlayer::stopPlay() {
679 Common::StackLock slock(_mutex);
680
681 stop();
682 }
683
readBuffer(int16 * buffer,const int numSamples)684 int PCMMusicPlayer::readBuffer(int16 *buffer, const int numSamples) {
685 Common::StackLock slock(_mutex);
686
687 if (!_curChunk && ((_state == S_IDLE) || (_state == S_STOP)))
688 return 0;
689
690 int samplesLeft = numSamples;
691
692 while (samplesLeft > 0) {
693 if (_silenceSamples > 0) {
694 int n = MIN(_silenceSamples, samplesLeft);
695
696 memset(buffer, 0, n);
697
698 buffer += n;
699 _silenceSamples -= n;
700 samplesLeft -= n;
701
702 } else if (_curChunk &&
703 ((_state == S_MID) || (_state == S_NEXT) || (_state == S_NEW))) {
704 int n = _curChunk->readBuffer(buffer, samplesLeft);
705
706 buffer += n;
707 samplesLeft -= n;
708
709 if (_curChunk->endOfData()) {
710 _state = S_END1;
711
712 delete _curChunk;
713 _curChunk = 0;
714 }
715 } else {
716
717 if (!getNextChunk())
718 break;
719 }
720 }
721
722 return (numSamples - samplesLeft);
723 }
724
isPlaying() const725 bool PCMMusicPlayer::isPlaying() const {
726 return ((_state != S_IDLE) && (_state != S_STOP));
727 }
728
isDimmed() const729 bool PCMMusicPlayer::isDimmed() const {
730 return _dimmed;
731 }
732
getTunePlaying(void * voidPtr,int length)733 void PCMMusicPlayer::getTunePlaying(void *voidPtr, int length) {
734 Common::StackLock lock(_mutex);
735
736 debugC(DEBUG_DETAILED, kTinselDebugMusic, "getTunePlaying");
737
738 assert(length == (3 * sizeof(int32)));
739
740 int32 *p = (int32 *) voidPtr;
741
742 _mState = _state;
743
744 p[0] = (int32) _mState;
745 p[1] = _scriptNum;
746 p[2] = _scriptIndex;
747 }
748
restoreThatTune(void * voidPtr)749 void PCMMusicPlayer::restoreThatTune(void *voidPtr) {
750 Common::StackLock lock(_mutex);
751
752 debugC(DEBUG_DETAILED, kTinselDebugMusic, "restoreThatTune");
753
754 int32 *p = (int32 *) voidPtr;
755
756 _mState = (State) p[0];
757 _scriptNum = p[1];
758 _scriptIndex = p[2];
759
760 if (_mState != S_IDLE)
761 _state = S_NEW;
762
763 delete _curChunk;
764 _curChunk = 0;
765
766 _end = false;
767 }
768
setMusicSceneDetails(SCNHANDLE hScript,SCNHANDLE hSegment,const char * fileName)769 void PCMMusicPlayer::setMusicSceneDetails(SCNHANDLE hScript,
770 SCNHANDLE hSegment, const char *fileName) {
771
772 Common::StackLock lock(_mutex);
773
774 stop();
775
776 debugC(DEBUG_INTERMEDIATE, kTinselDebugMusic, "Setting music scene details: %s", fileName);
777
778 _hScript = hScript;
779 _hSegment = hSegment;
780 _filename = fileName;
781
782 // Start scene with music not dimmed
783 _dimmed = false;
784 _dimmedTinsel = false;
785 _dimIteration = 0;
786 setVol(255);
787 }
788
setVolume(int volume)789 void PCMMusicPlayer::setVolume(int volume) {
790 assert((volume >= 0) && (volume <= 100));
791
792 _dimmed = false;
793 setVol((volume * 255) / 100);
794 }
795
setVol(uint8 volume)796 void PCMMusicPlayer::setVol(uint8 volume) {
797 _volume = volume;
798
799 _vm->_mixer->setChannelVolume(_handle, _volume);
800 }
801
getMusicTinselDimmed() const802 bool PCMMusicPlayer::getMusicTinselDimmed() const {
803 return _dimmedTinsel;
804 }
805
dim(bool bTinselDim)806 void PCMMusicPlayer::dim(bool bTinselDim) {
807 if (_dimmed || (_volume == 0) ||
808 (_state == S_IDLE) || !_curChunk || (SysVar(SV_MUSICDIMFACTOR) == 0))
809 return;
810
811 _dimmed = true;
812 if (bTinselDim)
813 _dimmedTinsel = true;
814
815 _dimmedVolume = _volume - (_volume / SysVar(SV_MUSICDIMFACTOR));
816
817 // Iterate down, negative iteration
818 if (!_dimIteration)
819 _dimPosition = _volume;
820 _dimIteration = (_dimmedVolume - _volume)/DIM_SPEED;
821
822 debugC(DEBUG_DETAILED, kTinselDebugMusic, "Dimming music from %d to %d, steps %d", _dimPosition, _dimmedVolume, _dimIteration);
823
824 // And SFX
825 if (SysVar(SYS_SceneFxDimFactor))
826 _vm->_sound->setSFXVolumes(255 - 255/SysVar(SYS_SceneFxDimFactor));
827 }
828
unDim(bool bTinselUnDim)829 void PCMMusicPlayer::unDim(bool bTinselUnDim) {
830 if (!_dimmed || (_dimmedTinsel && !bTinselUnDim))
831 return; // not dimmed
832
833 _dimmed = _dimmedTinsel = false;
834
835 if ((_volume == 0) || (_state == S_IDLE) || !_curChunk)
836 return;
837
838 // Iterate up, positive iteration
839 if (!_dimIteration)
840 _dimPosition = _dimmedVolume;
841 _dimIteration = (_volume - _dimmedVolume)/DIM_SPEED;
842
843 debugC(DEBUG_DETAILED, kTinselDebugMusic, "UnDimming music from %d to %d, steps %d", _dimPosition, _volume, _dimIteration);
844
845 // And SFX
846 _vm->_sound->setSFXVolumes(255);
847 }
848
dimIteration()849 void PCMMusicPlayer::dimIteration() {
850 if (_dimIteration != 0)
851 {
852 _dimPosition += _dimIteration;
853 if (_dimPosition >= _volume)
854 {
855 _dimPosition = _volume;
856 _dimIteration = 0;
857 }
858 else if (_dimPosition <= _dimmedVolume)
859 {
860 _dimPosition = _dimmedVolume;
861 _dimIteration = 0;
862 }
863
864 _vm->_mixer->setChannelVolume(_handle, _dimPosition);
865 }
866 }
867
startFadeOut(int ticks)868 void PCMMusicPlayer::startFadeOut(int ticks) {
869 if ((_volume == 0) || (_state == S_IDLE) || !_curChunk)
870 return;
871
872 debugC(DEBUG_INTERMEDIATE, kTinselDebugMusic, "Fading out music...");
873
874 if (_dimmed) {
875 // Start from dimmed volume and go from there
876 _dimmed = false;
877 _fadeOutVolume = _volume - _volume/SysVar(SV_MUSICDIMFACTOR);
878 } else
879 _fadeOutVolume = _volume;
880
881 assert(ticks != 0);
882 _fadeOutIteration = _fadeOutVolume / ticks;
883
884 fadeOutIteration();
885 }
886
fadeOutIteration()887 void PCMMusicPlayer::fadeOutIteration() {
888 if ((_volume == 0) || (_state == S_IDLE) || !_curChunk)
889 return;
890
891 _fadeOutVolume = CLIP<int>(_fadeOutVolume -= _fadeOutIteration, 0, 255);
892
893 _vm->_mixer->setChannelVolume(_handle, _fadeOutVolume);
894 }
895
getNextChunk()896 bool PCMMusicPlayer::getNextChunk() {
897 MusicSegment *musicSegments;
898 int32 *script, *scriptBuffer;
899 int id;
900 int snum;
901 uint32 sampleOffset, sampleLength, sampleCLength;
902 Common::File file;
903 byte *buffer;
904 Common::SeekableReadStream *sampleStream;
905
906 switch (_state) {
907 case S_NEW:
908 case S_NEXT:
909 _forcePlay = false;
910
911 script = scriptBuffer = (int32 *)LockMem(_hScript);
912
913 // Set parameters for this chunk of music
914 id = _scriptNum;
915 while (id--)
916 script = scriptBuffer + READ_32(script);
917 snum = FROM_32(script[_scriptIndex++]);
918
919 if (snum == MUSIC_JUMP || snum == MUSIC_END) {
920 // Let usual code sort it out!
921 _scriptIndex--; // Undo increment
922 _forcePlay = true; // Force a Play
923 _state = S_END1; // 'Goto' S_END1
924 break;
925 }
926
927 musicSegments = (MusicSegment *) LockMem(_hSegment);
928
929 assert(FROM_32(musicSegments[snum].numChannels) == 1);
930 assert(FROM_32(musicSegments[snum].bitsPerSample) == 16);
931
932 sampleOffset = FROM_32(musicSegments[snum].sampleOffset);
933 sampleLength = FROM_32(musicSegments[snum].sampleLength);
934 sampleCLength = (((sampleLength + 63) & ~63)*33)/64;
935
936 if (!file.open(_filename))
937 error(CANNOT_FIND_FILE, _filename.c_str());
938
939 file.seek(sampleOffset);
940 if (file.eos() || file.err() || (uint32)file.pos() != sampleOffset)
941 error(FILE_IS_CORRUPT, _filename.c_str());
942
943 buffer = (byte *) malloc(sampleCLength);
944 assert(buffer);
945
946 // read all of the sample
947 if (file.read(buffer, sampleCLength) != sampleCLength)
948 error(FILE_IS_CORRUPT, _filename.c_str());
949
950 debugC(DEBUG_DETAILED, kTinselDebugMusic, "Creating ADPCM music chunk with size %d, "
951 "offset %d (script %d.%d)", sampleCLength, sampleOffset,
952 _scriptNum, _scriptIndex - 1);
953
954 sampleStream = new Common::MemoryReadStream(buffer, sampleCLength, DisposeAfterUse::YES);
955
956 delete _curChunk;
957 _curChunk = new Tinsel8_ADPCMStream(sampleStream, DisposeAfterUse::YES, sampleCLength,
958 22050, 1, 32);
959
960 _state = S_MID;
961 return true;
962
963 case S_END1:
964 debugC(DEBUG_DETAILED, kTinselDebugMusic, "Music reached state S_END1 (script %d.%d)",
965 _scriptNum, _scriptIndex);
966
967 script = scriptBuffer = (int32 *) LockMem(_hScript);
968
969 id = _scriptNum;
970 while (id--)
971 script = scriptBuffer + READ_32(script);
972 snum = FROM_32(script[_scriptIndex]);
973
974 if (snum == MUSIC_END) {
975 _state = S_END2;
976 } else {
977 if (snum == MUSIC_JUMP)
978 _scriptIndex = FROM_32(script[_scriptIndex+1]);
979
980 _state = _forcePlay ? S_NEW : S_NEXT;
981 _forcePlay = false;
982 }
983
984 return true;
985
986 case S_END2:
987 debugC(DEBUG_DETAILED, kTinselDebugMusic, "Music reached state S_END2 (script %d.%d)",
988 _scriptNum, _scriptIndex);
989
990 _silenceSamples = 11025; // Half a second of silence
991 return true;
992
993 case S_END3:
994 debugC(DEBUG_DETAILED, kTinselDebugMusic, "Music reached state S_END3 (script %d.%d)",
995 _scriptNum, _scriptIndex);
996
997 stop();
998 _state = S_IDLE;
999 return false;
1000
1001 case S_IDLE:
1002 return false;
1003
1004 default:
1005 break;
1006 }
1007
1008 return true;
1009 }
1010
play()1011 void PCMMusicPlayer::play() {
1012 if (_curChunk)
1013 return;
1014 if (_scriptNum == -1)
1015 return;
1016
1017 _end = false;
1018
1019 getNextChunk();
1020 }
1021
stop()1022 void PCMMusicPlayer::stop() {
1023 delete _curChunk;
1024 _curChunk = 0;
1025 _scriptNum = -1;
1026 _state = S_IDLE;
1027 _mState = S_IDLE;
1028
1029 _end = true;
1030 }
1031
CurrentMidiFacts(SCNHANDLE * pMidi,bool * pLoop)1032 void CurrentMidiFacts(SCNHANDLE *pMidi, bool *pLoop) {
1033 *pMidi = g_currentMidi;
1034 *pLoop = g_currentLoop;
1035 }
1036
RestoreMidiFacts(SCNHANDLE Midi,bool Loop)1037 void RestoreMidiFacts(SCNHANDLE Midi, bool Loop) {
1038 StopMidi();
1039
1040 g_currentMidi = Midi;
1041 g_currentLoop = Loop;
1042
1043 bool mute = false;
1044 if (ConfMan.hasKey("mute"))
1045 mute = ConfMan.getBool("mute");
1046
1047 PlayMidiSequence(g_currentMidi, true);
1048 SetMidiVolume(mute ? 0 : _vm->_config->_musicVolume);
1049 }
1050
1051 #if 0
1052 // Dumps all of the game's music in external XMIDI *.xmi files
1053 void dumpMusic() {
1054 Common::File midiFile;
1055 Common::DumpFile outFile;
1056 char outName[20];
1057 midiFile.open(MIDI_FILE);
1058 int outFileSize = 0;
1059 char buffer[20000];
1060
1061 const int total = 155; // maximum (SCN version)
1062
1063 for (int i = 0; i < total; i++) {
1064 if (midiOffsets[i] == 0)
1065 break;
1066
1067 sprintf(outName, "track%03d.xmi", i + 1);
1068 outFile.open(outName);
1069
1070 if (i < total - 1)
1071 outFileSize = midiOffsets[i + 1] - midiOffsets[i] - 4;
1072 else
1073 outFileSize = midiFile.size() - midiOffsets[i] - 4;
1074
1075 midiFile.seek(midiOffsets[i] + 4, SEEK_SET);
1076
1077 assert(outFileSize < 20000);
1078 midiFile.read(buffer, outFileSize);
1079 outFile.write(buffer, outFileSize);
1080
1081 outFile.close();
1082 }
1083
1084 midiFile.close();
1085 }
1086 #endif
1087
1088 } // End of namespace Tinsel
1089