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 "audio/mididrv.h"
24 #include "audio/mixer.h"
25
26 #include "groovie/music.h"
27 #include "groovie/groovie.h"
28 #include "groovie/resource.h"
29
30 #include "backends/audiocd/audiocd.h"
31 #include "common/config-manager.h"
32 #include "common/debug.h"
33 #include "common/file.h"
34 #include "common/macresman.h"
35 #include "common/memstream.h"
36 #include "common/textconsole.h"
37 #include "audio/audiostream.h"
38 #include "audio/midiparser.h"
39 #include "audio/miles.h"
40
41 namespace Groovie {
42
43 // MusicPlayer
44
MusicPlayer(GroovieEngine * vm)45 MusicPlayer::MusicPlayer(GroovieEngine *vm) :
46 _vm(vm), _isPlaying(false), _backgroundFileRef(0), _gameVolume(100),
47 _prevCDtrack(0), _backgroundDelay(0), _fadingStartTime(0), _fadingStartVolume(0),
48 _fadingEndVolume(0), _fadingDuration(0), _userVolume(0) {
49 }
50
~MusicPlayer()51 MusicPlayer::~MusicPlayer() {
52 g_system->getAudioCDManager()->stop();
53 }
54
playSong(uint32 fileref)55 void MusicPlayer::playSong(uint32 fileref) {
56 Common::StackLock lock(_mutex);
57
58 // Set the volumes
59 _fadingEndVolume = 100;
60 _gameVolume = 100;
61
62 // Play the referenced file once
63 play(fileref, false);
64 }
65
setBackgroundSong(uint32 fileref)66 void MusicPlayer::setBackgroundSong(uint32 fileref) {
67 Common::StackLock lock(_mutex);
68
69 debugC(1, kDebugMIDI, "Groovie::Music: Changing the background song: %04X", fileref);
70 _backgroundFileRef = fileref;
71 }
72
frameTick()73 void MusicPlayer::frameTick() {
74 if (_backgroundDelay > 0) {
75 _backgroundDelay--;
76 if (_backgroundDelay == 0)
77 playSong(_backgroundFileRef);
78 }
79 }
80
setBackgroundDelay(uint16 delay)81 void MusicPlayer::setBackgroundDelay(uint16 delay) {
82 _backgroundDelay = delay;
83 }
84
playCD(uint8 track)85 void MusicPlayer::playCD(uint8 track) {
86 int startms = 0;
87
88 // Stop the MIDI playback
89 unload();
90
91 debugC(1, kDebugMIDI, "Groovie::Music: Playing CD track %d", track);
92
93 if (track == 3) {
94 // This is the credits song, start at 23:20
95 startms = 1400000;
96 // TODO: If we want to play it directly from the CD, we should decrement
97 // the song number (it's track 2 on the 2nd CD)
98 } else if ((track == 98) && (_prevCDtrack == 3)) {
99 // Track 98 is used as a hack to stop the credits song
100 g_system->getAudioCDManager()->stop();
101 stopCreditsIOS();
102 return;
103 }
104
105 // Save the playing track in order to be able to stop the credits song
106 _prevCDtrack = track;
107
108 // Wait until the CD stops playing the current song
109 // It was in the original interpreter, but it introduces a big delay
110 // in the middle of the introduction, so it's disabled right now
111 /*
112 g_system->getAudioCDManager()->updateCD();
113 while (g_system->getAudioCDManager()->isPlaying()) {
114 // Wait a bit and try again
115 _vm->_system->delayMillis(100);
116 g_system->getAudioCDManager()->updateCD();
117 }
118 */
119
120 // Play the track starting at the requested offset (1000ms = 75 frames)
121 g_system->getAudioCDManager()->play(track - 1, 1, startms * 75 / 1000, 0);
122
123 // If the audio is not playing from the CD, play the "fallback" MIDI.
124 // The Mac version has no CD tracks, so it will always use the MIDI.
125 if (!g_system->getAudioCDManager()->isPlaying()) {
126 if (track == 2) {
127 // Intro MIDI fallback
128 if (_vm->getPlatform() == Common::kPlatformMacintosh)
129 playSong(70);
130 else
131 playSong((19 << 10) | 36); // XMI.GJD, file 36
132 } else if (track == 3) {
133 // TODO: Credits MIDI fallback
134 if (_vm->getPlatform() == Common::kPlatformIOS)
135 playCreditsIOS();
136 }
137 }
138 }
139
startBackground()140 void MusicPlayer::startBackground() {
141 debugC(3, kDebugMIDI, "Groovie::Music: startBackground()");
142 if (!_isPlaying && _backgroundFileRef) {
143 debugC(3, kDebugMIDI, "Groovie::Music: Starting the background song (0x%4X)", _backgroundFileRef);
144 play(_backgroundFileRef, true);
145 }
146 }
147
setUserVolume(uint16 volume)148 void MusicPlayer::setUserVolume(uint16 volume) {
149 Common::StackLock lock(_mutex);
150
151 // Save the new user volume
152 _userVolume = volume;
153 if (_userVolume > 0x100)
154 _userVolume = 0x100;
155
156 // Apply it
157 updateVolume();
158 }
159
setGameVolume(uint16 volume,uint16 time)160 void MusicPlayer::setGameVolume(uint16 volume, uint16 time) {
161 Common::StackLock lock(_mutex);
162
163 debugC(1, kDebugMIDI, "Groovie::Music: Setting game volume from %d to %d in %dms", _gameVolume, volume, time);
164
165 // Save the start parameters of the fade
166 _fadingStartTime = _vm->_system->getMillis();
167 _fadingStartVolume = _gameVolume;
168 _fadingDuration = time;
169
170 // Save the new game volume
171 _fadingEndVolume = volume;
172 if (_fadingEndVolume > 100)
173 _fadingEndVolume = 100;
174 }
175
play(uint32 fileref,bool loop)176 bool MusicPlayer::play(uint32 fileref, bool loop) {
177 // Unload the previous song
178 unload();
179
180 // Set the new state
181 _isPlaying = true;
182
183 // Load the new file
184 return load(fileref, loop);
185 }
186
applyFading()187 void MusicPlayer::applyFading() {
188 debugC(6, kDebugMIDI, "Groovie::Music: applyFading() _fadingStartTime = %d, _fadingDuration = %d, _fadingStartVolume = %d, _fadingEndVolume = %d", _fadingStartTime, _fadingDuration, _fadingStartVolume, _fadingEndVolume);
189 Common::StackLock lock(_mutex);
190
191 // Calculate the passed time
192 uint32 time = _vm->_system->getMillis() - _fadingStartTime;
193 debugC(6, kDebugMIDI, "Groovie::Music: time = %d, _gameVolume = %d", time, _gameVolume);
194 if (time >= _fadingDuration) {
195 // Set the end volume
196 _gameVolume = _fadingEndVolume;
197 } else {
198 // Calculate the interpolated volume for the current time
199 _gameVolume = (_fadingStartVolume * (_fadingDuration - time) +
200 _fadingEndVolume * time) / _fadingDuration;
201 }
202 if (_gameVolume == _fadingEndVolume) {
203 // If we were fading to 0, stop the playback and restore the volume
204 if (_fadingEndVolume == 0) {
205 debugC(1, kDebugMIDI, "Groovie::Music: Faded to zero: end of song. _fadingEndVolume set to 100");
206 unload();
207 }
208 }
209
210 // Apply it
211 updateVolume();
212 }
213
onTimer(void * refCon)214 void MusicPlayer::onTimer(void *refCon) {
215 debugC(9, kDebugMIDI, "Groovie::Music: onTimer()");
216 MusicPlayer *music = (MusicPlayer *)refCon;
217 Common::StackLock lock(music->_mutex);
218
219 // Apply the game volume fading
220 if (music->_gameVolume != music->_fadingEndVolume) {
221 // Apply the next step of the fading
222 music->applyFading();
223 }
224
225 // Handle internal timed events
226 music->onTimerInternal();
227 }
228
unload()229 void MusicPlayer::unload() {
230 debugC(1, kDebugMIDI, "Groovie::Music: Stopping the playback");
231
232 // Set the new state
233 _isPlaying = false;
234 }
235
playCreditsIOS()236 void MusicPlayer::playCreditsIOS() {
237 Audio::AudioStream *stream = Audio::SeekableAudioStream::openStreamFile("7th_Guest_Dolls_from_Hell_OC_ReMix");
238
239 if (!stream) {
240 warning("Could not find '7th_Guest_Dolls_from_Hell_OC_ReMix' audio file");
241 return;
242 }
243
244 _vm->_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_handleCreditsIOS, stream);
245 }
246
stopCreditsIOS()247 void MusicPlayer::stopCreditsIOS() {
248 _vm->_system->getMixer()->stopHandle(_handleCreditsIOS);
249 }
250
251 // MusicPlayerMidi
252
MusicPlayerMidi(GroovieEngine * vm)253 MusicPlayerMidi::MusicPlayerMidi(GroovieEngine *vm) :
254 MusicPlayer(vm), _midiParser(NULL), _data(NULL), _driver(NULL) {
255 // Initialize the channel volumes
256 for (int i = 0; i < 0x10; i++) {
257 _chanVolumes[i] = 0x7F;
258 }
259 }
260
~MusicPlayerMidi()261 MusicPlayerMidi::~MusicPlayerMidi() {
262 // Stop the callback
263 if (_driver)
264 _driver->setTimerCallback(NULL, NULL);
265
266 Common::StackLock lock(_mutex);
267
268 // Unload the parser
269 unload();
270 delete _midiParser;
271
272 // Unload the MIDI Driver
273 if (_driver) {
274 _driver->close();
275 delete _driver;
276 }
277 }
278
send(uint32 b)279 void MusicPlayerMidi::send(uint32 b) {
280 if ((b & 0xFFF0) == 0x07B0) { // Volume change
281 // Save the specific channel volume
282 byte chan = b & 0xF;
283 _chanVolumes[chan] = (b >> 16) & 0x7F;
284
285 // Send the updated value
286 updateChanVolume(chan);
287
288 return;
289 }
290 if (_driver)
291 _driver->send(b);
292 }
293
metaEvent(byte type,byte * data,uint16 length)294 void MusicPlayerMidi::metaEvent(byte type, byte *data, uint16 length) {
295 switch (type) {
296 case 0x2F:
297 // End of Track, play the background song
298 endTrack();
299 break;
300 default:
301 if (_driver)
302 _driver->metaEvent(type, data, length);
303 break;
304 }
305 }
306
updateChanVolume(byte channel)307 void MusicPlayerMidi::updateChanVolume(byte channel) {
308 // Generate a MIDI Control change message for the volume
309 uint32 b = 0x7B0;
310
311 // Specify the channel
312 b |= (channel & 0xF);
313
314 // Scale by the user and game volumes
315 uint32 val = (_chanVolumes[channel] * _userVolume * _gameVolume) / 0x100 / 100;
316 val &= 0x7F;
317
318 // Send it to the driver
319 if (_driver)
320 _driver->send(b | (val << 16));
321 }
322
endTrack()323 void MusicPlayerMidi::endTrack() {
324 debugC(3, kDebugMIDI, "Groovie::Music: endTrack()");
325 unload();
326 }
327
onTimerInternal()328 void MusicPlayerMidi::onTimerInternal() {
329 // TODO: We really only need to call this while music is playing.
330 if (_midiParser)
331 _midiParser->onTimer();
332 }
333
updateVolume()334 void MusicPlayerMidi::updateVolume() {
335 // Apply it to all the channels
336 for (int i = 0; i < 0x10; i++) {
337 updateChanVolume(i);
338 }
339 }
340
unload()341 void MusicPlayerMidi::unload() {
342 MusicPlayer::unload();
343
344 // Unload the parser data
345 if (_midiParser)
346 _midiParser->unloadMusic();
347
348 // Unload the data
349 delete[] _data;
350 _data = NULL;
351 }
352
loadParser(Common::SeekableReadStream * stream,bool loop)353 bool MusicPlayerMidi::loadParser(Common::SeekableReadStream *stream, bool loop) {
354 if (!_midiParser)
355 return false;
356
357 // Read the whole file to memory
358 int length = stream->size();
359 _data = new byte[length];
360 stream->read(_data, length);
361 delete stream;
362
363 // Set the looping option
364 _midiParser->property(MidiParser::mpAutoLoop, loop);
365
366 // Start parsing the data
367 if (!_midiParser->loadMusic(_data, length)) {
368 error("Groovie::Music: Couldn't parse the data");
369 return false;
370 }
371
372 // Activate the timer source
373 if (_driver)
374 _driver->setTimerCallback(this, &onTimer);
375
376 return true;
377 }
378
379
380 // MusicPlayerXMI
381
MusicPlayerXMI(GroovieEngine * vm,const Common::String & gtlName)382 MusicPlayerXMI::MusicPlayerXMI(GroovieEngine *vm, const Common::String >lName) :
383 MusicPlayerMidi(vm) {
384
385 // Create the driver
386 MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
387 MusicType musicType = MidiDriver::getMusicType(dev);
388 _driver = NULL;
389
390 // new Miles Audio support, to disable set milesAudioEnabled to false
391 _milesAudioMode = false;
392 bool milesAudioEnabled = true;
393 MidiParser::XMidiNewTimbreListProc newTimbreListProc = NULL;
394
395 _musicType = 0;
396
397 if (milesAudioEnabled) {
398 // 7th Guest uses FAT.AD/FAT.OPL/FAT.MT
399 // 11th Hour uses SAMPLE.AD/SAMPLE.OPL/SAMPLE.MT
400 switch (musicType) {
401 case MT_ADLIB:
402 _driver = Audio::MidiDriver_Miles_AdLib_create(gtlName + ".AD", gtlName + ".OPL");
403 break;
404 case MT_MT32:
405 _driver = Audio::MidiDriver_Miles_MT32_create(gtlName + ".MT");
406 break;
407 case MT_GM:
408 if (ConfMan.getBool("native_mt32")) {
409 _driver = Audio::MidiDriver_Miles_MT32_create(gtlName + ".MT");
410 musicType = MT_MT32;
411 }
412 break;
413 default:
414 break;
415 }
416
417 if (musicType == MT_MT32) {
418 newTimbreListProc = Audio::MidiDriver_Miles_MT32_processXMIDITimbreChunk;
419 }
420 }
421
422 if (_driver) {
423 _milesAudioMode = true;
424 }
425
426 if (!_driver) {
427 // No driver yet? create a generic one
428 _driver = MidiDriver::createMidi(dev);
429 }
430
431 assert(_driver);
432
433 // Create the parser
434 _midiParser = MidiParser::createParser_XMIDI(NULL, NULL, newTimbreListProc, _driver);
435
436 _driver->open(); // TODO: Handle return value != 0 (indicating an error)
437
438 // Set the parser's driver
439 _midiParser->setMidiDriver(this);
440
441 // Set the timer rate
442 _midiParser->setTimerRate(_driver->getBaseTempo());
443
444 // Initialize the channel banks
445 for (int i = 0; i < 0x10; i++) {
446 _chanBanks[i] = 0;
447 }
448
449 if (_milesAudioMode)
450 return;
451
452 // Load the Global Timbre Library
453 if (MidiDriver::getMusicType(dev) == MT_ADLIB) {
454 // MIDI through AdLib
455 _musicType = MT_ADLIB;
456 loadTimbres(gtlName + ".ad");
457
458 // Setup the percussion channel
459 for (uint i = 0; i < _timbres.size(); i++) {
460 if (_timbres[i].bank == 0x7F)
461 setTimbreAD(9, _timbres[i]);
462 }
463 } else if ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32")) {
464 _driver->sendMT32Reset();
465
466 // MT-32
467 _musicType = MT_MT32;
468 loadTimbres(gtlName + ".mt");
469 } else {
470 _driver->sendGMReset();
471
472 // GM
473 _musicType = 0;
474 }
475 }
476
~MusicPlayerXMI()477 MusicPlayerXMI::~MusicPlayerXMI() {
478 //~MusicPlayer();
479
480 // Unload the timbres
481 clearTimbres();
482 }
483
send(uint32 b)484 void MusicPlayerXMI::send(uint32 b) {
485 if (_milesAudioMode) {
486 MusicPlayerMidi::send(b);
487 return;
488 }
489
490 if ((b & 0xFFF0) == 0x72B0) { // XMIDI Patch Bank Select 114
491 // From AIL2's documentation: XMIDI Patch Bank Select controller (114)
492 // selects a bank to be used when searching the next patches
493 byte chan = b & 0xF;
494 byte bank = (b >> 16) & 0xFF;
495
496 debugC(5, kDebugMIDI, "Groovie::Music: Selecting bank %X for channel %X", bank, chan);
497 _chanBanks[chan] = bank;
498 return;
499 } else if ((b & 0xF0) == 0xC0) { // Program change
500 // We intercept the program change when using AdLib or MT32 drivers,
501 // since we have custom timbres for them. The command is sent
502 // unchanged to GM drivers.
503 if (_musicType != 0) {
504 byte chan = b & 0xF;
505 byte patch = (b >> 8) & 0xFF;
506
507 debugC(5, kDebugMIDI, "Groovie::Music: Setting custom patch %X from bank %X to channel %X", patch, _chanBanks[chan], chan);
508
509 // Try to find the requested patch from the previously
510 // specified bank
511 int numTimbres = _timbres.size();
512 for (int i = 0; i < numTimbres; i++) {
513 if ((_timbres[i].bank == _chanBanks[chan]) &&
514 (_timbres[i].patch == patch)) {
515 if (_musicType == MT_ADLIB) {
516 setTimbreAD(chan, _timbres[i]);
517 } else if (_musicType == MT_MT32) {
518 setTimbreMT(chan, _timbres[i]);
519 }
520 return;
521 }
522 }
523
524 // If we got here we couldn't find the patch, and the
525 // received message will be sent unchanged.
526 }
527 }
528 MusicPlayerMidi::send(b);
529 }
530
load(uint32 fileref,bool loop)531 bool MusicPlayerXMI::load(uint32 fileref, bool loop) {
532 debugC(1, kDebugMIDI, "Groovie::Music: Starting the playback of song: %04X", fileref);
533
534 // Open the song resource
535 Common::SeekableReadStream *file = _vm->_resMan->open(fileref);
536 if (!file) {
537 error("Groovie::Music: Couldn't find resource 0x%04X", fileref);
538 return false;
539 }
540
541 return loadParser(file, loop);
542 }
543
loadTimbres(const Common::String & filename)544 void MusicPlayerXMI::loadTimbres(const Common::String &filename) {
545 // Load the Global Timbre Library format as documented in AIL2
546 debugC(1, kDebugMIDI, "Groovie::Music: Loading the GTL file %s", filename.c_str());
547
548 // Does it exist?
549 if (!Common::File::exists(filename)) {
550 error("Groovie::Music: %s not found", filename.c_str());
551 return;
552 }
553
554 // Open the GTL
555 Common::File *gtl = new Common::File();
556 if (!gtl->open(filename.c_str())) {
557 delete gtl;
558 error("Groovie::Music: Couldn't open %s", filename.c_str());
559 return;
560 }
561
562 // Clear the old timbres before loading the new ones
563 clearTimbres();
564
565 // Get the list of timbres
566 while (true) {
567 Timbre t;
568 t.patch = gtl->readByte();
569 t.bank = gtl->readByte();
570 if ((t.patch == 0xFF) && (t.bank == 0xFF)) {
571 // End of list
572 break;
573 }
574 // We temporarily use the size field to store the offset
575 t.size = gtl->readUint32LE();
576
577 // Add it to the list
578 _timbres.push_back(t);
579 }
580
581 // Read the timbres
582 for (unsigned int i = 0; i < _timbres.size(); i++) {
583 // Seek to the start of the timbre
584 gtl->seek(_timbres[i].size);
585
586 // Read the size
587 _timbres[i].size = gtl->readUint16LE() - 2;
588
589 // Allocate memory for the timbre data
590 _timbres[i].data = new byte[_timbres[i].size];
591
592 // Read the timbre data
593 gtl->read(_timbres[i].data, _timbres[i].size);
594 debugC(5, kDebugMIDI, "Groovie::Music: Loaded patch %x in bank %x with size %d",
595 _timbres[i].patch, _timbres[i].bank, _timbres[i].size);
596 }
597
598 // Close the file
599 delete gtl;
600 }
601
clearTimbres()602 void MusicPlayerXMI::clearTimbres() {
603 // Delete the allocated data
604 int num = _timbres.size();
605 for (int i = 0; i < num; i++) {
606 delete[] _timbres[i].data;
607 }
608
609 // Erase the array entries
610 _timbres.clear();
611 }
612
setTimbreAD(byte channel,const Timbre & timbre)613 void MusicPlayerXMI::setTimbreAD(byte channel, const Timbre &timbre) {
614 // Verify the timbre size
615 if (timbre.size != 12) {
616 error("Groovie::Music: Invalid size for an AdLib timbre: %d", timbre.size);
617 }
618
619 // Prepare the AdLib Instrument array from the GTL entry
620 //
621 // struct AdLibInstrument used by our AdLib MIDI synth is 30 bytes.
622 // Since we pass data + 2 for non percussion instruments we need to
623 // have a buffer of size 32, so there are no invalid memory reads,
624 // when setting up an AdLib instrument.
625 byte data[32];
626 memset(data, 0, sizeof(data));
627
628 data[2] = timbre.data[1]; // mod_characteristic
629 data[3] = timbre.data[2] ^ 0x3F; // mod_scalingOutputLevel
630 data[4] = ~timbre.data[3]; // mod_attackDecay
631 data[5] = ~timbre.data[4]; // mod_sustainRelease
632 data[6] = timbre.data[5]; // mod_waveformSelect
633 data[7] = timbre.data[7]; // car_characteristic
634 data[8] = timbre.data[8] ^ 0x3F; // car_scalingOutputLevel
635 data[9] = ~timbre.data[9]; // car_attackDecay
636 data[10] = ~timbre.data[10]; // car_sustainRelease
637 data[11] = timbre.data[11]; // car_waveformSelect
638 data[12] = timbre.data[6]; // feedback
639
640 // Send the instrument to the driver
641 if (timbre.bank == 0x7F) {
642 // This is a Percussion instrument, this will always be set on the same note
643 data[0] = timbre.patch;
644
645 // From AIL2's documentation: If the instrument is to be played in MIDI
646 // channel 10, num specifies its desired absolute MIDI note number.
647 data[1] = timbre.data[0];
648
649 _driver->getPercussionChannel()->sysEx_customInstrument('ADLP', data);
650 } else {
651 // Some tweaks for non-percussion instruments
652 byte mult1 = timbre.data[1] & 0xF;
653 if (mult1 < 4)
654 mult1 = 1 << mult1;
655 data[2] = (timbre.data[1] & 0xF0) + (mult1 & 0xF);
656 byte mult2 = timbre.data[7] & 0xF;
657 if (mult2 < 4)
658 mult2 = 1 << mult2;
659 data[7] = (timbre.data[7] & 0xF0) + (mult2 & 0xF);
660 // TODO: Fix CHARACTERISTIC: 0xF0: pitch_vib, amp_vib, sustain_sound, env_scaling 0xF: freq_mult
661 // TODO: Fix KSL_TL: 0xC: key_scale_lvl 0x3F: out_lvl
662
663 // From AIL2's documentation: num specifies the number of semitones
664 // by which to transpose notes played with the instrument.
665 if (timbre.data[0] != 0)
666 warning("Groovie::Music: AdLib instrument's transposing not supported");
667
668 _driver->sysEx_customInstrument(channel, 'ADL ', data + 2);
669 }
670 }
671
672
673 #include "common/pack-start.h" // START STRUCT PACKING
674
675 struct RolandInstrumentSysex {
676 byte roland_id;
677 byte device_id;
678 byte model_id;
679 byte command;
680 byte address[3];
681 byte instrument[0xF6];
682 byte checksum;
683 } PACKED_STRUCT;
684
685 #include "common/pack-end.h" // END STRUCT PACKING
686
setRolandInstrument(MidiDriver * drv,byte channel,byte * instrument)687 void setRolandInstrument(MidiDriver *drv, byte channel, byte *instrument) {
688 RolandInstrumentSysex sysex;
689 memcpy(&sysex.instrument, instrument, 0xF6);
690
691 // Show the timbre name as extra debug information
692 Common::String name((char *)instrument, 10);
693 debugC(5, kDebugMIDI, "Groovie::Music: Setting MT32 timbre '%s' to channel %d", name.c_str(), channel);
694
695 sysex.roland_id = 0x41;
696 sysex.device_id = channel; // Unit#
697 sysex.model_id = 0x16; // MT32
698 sysex.command = 0x12; // Data set
699
700 // Remap instrument to appropriate address space.
701 int address = 0x008000;
702 sysex.address[0] = (address >> 14) & 0x7F;
703 sysex.address[1] = (address >> 7) & 0x7F;
704 sysex.address[2] = (address ) & 0x7F;
705
706 // Compute the checksum.
707 byte checksum = 0;
708 byte *ptr = sysex.address;
709 for (int i = 4; i < (int)sizeof(RolandInstrumentSysex) - 1; ++i)
710 checksum -= *ptr++;
711 sysex.checksum = checksum & 0x7F;
712
713 // Send sysex
714 drv->sysEx((byte *)&sysex, sizeof(RolandInstrumentSysex));
715
716
717 // Wait the time it takes to send the SysEx data
718 uint32 delay = (sizeof(RolandInstrumentSysex) + 2) * 1000 / 3125;
719
720 // Plus an additional delay for the MT-32 rev00
721 delay += 40;
722
723 g_system->delayMillis(delay);
724 }
725
setTimbreMT(byte channel,const Timbre & timbre)726 void MusicPlayerXMI::setTimbreMT(byte channel, const Timbre &timbre) {
727 // Verify the timbre size
728 if (timbre.size != 0xF6)
729 error("Groovie::Music: Invalid size for an MT-32 timbre: %d", timbre.size);
730
731 setRolandInstrument(_driver, channel, timbre.data);
732 }
733
734
735 // MusicPlayerMac_t7g
736
MusicPlayerMac_t7g(GroovieEngine * vm)737 MusicPlayerMac_t7g::MusicPlayerMac_t7g(GroovieEngine *vm) : MusicPlayerMidi(vm) {
738 // Create the parser
739 _midiParser = MidiParser::createParser_SMF();
740
741 // Create the driver
742 MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
743 _driver = MidiDriver::createMidi(dev);
744 assert(_driver);
745
746 _driver->open(); // TODO: Handle return value != 0 (indicating an error)
747
748 // Set the parser's driver
749 _midiParser->setMidiDriver(this);
750
751 // Set the timer rate
752 _midiParser->setTimerRate(_driver->getBaseTempo());
753
754 // Sanity check
755 assert(_vm->_macResFork);
756 }
757
load(uint32 fileref,bool loop)758 bool MusicPlayerMac_t7g::load(uint32 fileref, bool loop) {
759 debugC(1, kDebugMIDI, "Groovie::Music: Starting the playback of song: %04X", fileref);
760
761 // First try for compressed MIDI
762 Common::SeekableReadStream *file = _vm->_macResFork->getResource(MKTAG('c','m','i','d'), fileref & 0x3FF);
763
764 if (file) {
765 // Found the resource, decompress it
766 Common::SeekableReadStream *tmp = decompressMidi(file);
767 delete file;
768 file = tmp;
769 } else {
770 // Otherwise, it's uncompressed
771 file = _vm->_macResFork->getResource(MKTAG('M','i','d','i'), fileref & 0x3FF);
772 if (!file)
773 error("Groovie::Music: Couldn't find resource 0x%04X", fileref);
774 }
775
776 return loadParser(file, loop);
777 }
778
decompressMidi(Common::SeekableReadStream * stream)779 Common::SeekableReadStream *MusicPlayerMac_t7g::decompressMidi(Common::SeekableReadStream *stream) {
780 // Initialize an output buffer of the given size
781 uint32 size = stream->readUint32BE();
782 byte *output = (byte *)malloc(size);
783
784 byte *current = output;
785 uint32 decompBytes = 0;
786 while ((decompBytes < size) && !stream->eos()) {
787 // 8 flags
788 byte flags = stream->readByte();
789
790 for (byte i = 0; (i < 8) && !stream->eos(); i++) {
791 if (flags & 1) {
792 // 1: Next byte is a literal
793 *(current++) = stream->readByte();
794 if (stream->eos())
795 continue;
796 decompBytes++;
797 } else {
798 // 0: It's a reference to part of the history
799 uint16 args = stream->readUint16BE();
800 if (stream->eos())
801 continue;
802
803 // Length = 4bit unsigned (3 minimal)
804 uint8 length = (args >> 12) + 3;
805
806 // Offset = 12bit signed (all values are negative)
807 int16 offset = (args & 0xFFF) | 0xF000;
808
809 // Copy from the past decompressed bytes
810 decompBytes += length;
811 while (length > 0) {
812 *(current) = *(current + offset);
813 current++;
814 length--;
815 }
816 }
817 flags = flags >> 1;
818 }
819 }
820
821 // Return the output buffer wrapped in a MemoryReadStream
822 return new Common::MemoryReadStream(output, size, DisposeAfterUse::YES);
823 }
824
825 // MusicPlayerMac_v2
826
MusicPlayerMac_v2(GroovieEngine * vm)827 MusicPlayerMac_v2::MusicPlayerMac_v2(GroovieEngine *vm) : MusicPlayerMidi(vm) {
828 // Create the parser
829 _midiParser = MidiParser::createParser_QT();
830
831 // Create the driver
832 MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
833 _driver = MidiDriver::createMidi(dev);
834 assert(_driver);
835
836 _driver->open(); // TODO: Handle return value != 0 (indicating an error)
837
838 // Set the parser's driver
839 _midiParser->setMidiDriver(this);
840
841 // Set the timer rate
842 _midiParser->setTimerRate(_driver->getBaseTempo());
843 }
844
load(uint32 fileref,bool loop)845 bool MusicPlayerMac_v2::load(uint32 fileref, bool loop) {
846 debugC(1, kDebugMIDI, "Groovie::Music: Starting the playback of song: %04X", fileref);
847
848 // Find correct filename
849 ResInfo info;
850 _vm->_resMan->getResInfo(fileref, info);
851 uint len = info.filename.size();
852 if (len < 4)
853 return false; // This shouldn't actually occur
854
855 // Remove the extension and add ".mov"
856 info.filename.deleteLastChar();
857 info.filename.deleteLastChar();
858 info.filename.deleteLastChar();
859 info.filename += "mov";
860
861 Common::SeekableReadStream *file = SearchMan.createReadStreamForMember(info.filename);
862
863 if (!file) {
864 warning("Could not find file '%s'", info.filename.c_str());
865 return false;
866 }
867
868 return loadParser(file, loop);
869 }
870
MusicPlayerIOS(GroovieEngine * vm)871 MusicPlayerIOS::MusicPlayerIOS(GroovieEngine *vm) : MusicPlayer(vm) {
872 vm->getTimerManager()->installTimerProc(&onTimer, 50 * 1000, this, "groovieMusic");
873 }
874
~MusicPlayerIOS()875 MusicPlayerIOS::~MusicPlayerIOS() {
876 _vm->getTimerManager()->removeTimerProc(&onTimer);
877 }
878
updateVolume()879 void MusicPlayerIOS::updateVolume() {
880 // Just set the mixer volume for the music sound type
881 _vm->_system->getMixer()->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, _userVolume * _gameVolume / 100);
882 }
883
unload()884 void MusicPlayerIOS::unload() {
885 MusicPlayer::unload();
886
887 _vm->_system->getMixer()->stopHandle(_handle);
888 }
889
load(uint32 fileref,bool loop)890 bool MusicPlayerIOS::load(uint32 fileref, bool loop) {
891 // Find correct filename
892 ResInfo info;
893 _vm->_resMan->getResInfo(fileref, info);
894 uint len = info.filename.size();
895 if (len < 4)
896 return false; // This shouldn't actually occur
897 /*
898 19462 door
899 19463 ??
900 19464 ??
901 19465 puzzle?
902 19466 cake
903 19467 maze
904 19468 ambient (but not 69, amb b. odd)
905 19470 puzzle
906 19471
907 19473
908 19475 coffins or blood pump
909 19476 blood pump or coffins
910 19493
911 19499 chapel
912 19509 downstair ambient
913 19510 bedroom 'skip 3 and 5' puzzle (should loop from partway?)
914 19514
915 19515 bathroom drain teeth
916 */
917 if ((fileref >= 19462 && fileref <= 19468) ||
918 fileref == 19470 || fileref == 19471 ||
919 fileref == 19473 || fileref == 19475 ||
920 fileref == 19476 || fileref == 19493 ||
921 fileref == 19499 || fileref == 19509 ||
922 fileref == 19510 || fileref == 19514 ||
923 fileref == 19515)
924 loop = true; // XMIs for these refs self-loop
925
926 // iOS port provides alternative intro sequence music
927 if (info.filename == "gu39.xmi") {
928 info.filename = "intro";
929 } else if (info.filename == "gu32.xmi") {
930 info.filename = "foyer";
931 } else {
932 // Remove the extension
933 info.filename.deleteLastChar();
934 info.filename.deleteLastChar();
935 info.filename.deleteLastChar();
936 info.filename.deleteLastChar();
937 }
938
939 // Create the audio stream
940 Audio::SeekableAudioStream *seekStream = Audio::SeekableAudioStream::openStreamFile(info.filename);
941
942 if (!seekStream) {
943 warning("Could not play audio file '%s'", info.filename.c_str());
944 return false;
945 }
946
947 Audio::AudioStream *audStream = seekStream;
948
949 // Loop if requested
950 if (loop)
951 audStream = Audio::makeLoopingAudioStream(seekStream, 0);
952
953 // MIDI player handles volume reset on load, IOS player doesn't - force update here
954 updateVolume();
955
956 // Play!
957 _vm->_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_handle, audStream);
958 return true;
959 }
960
961 } // End of Groovie namespace
962