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), _midiInit(false), _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 	updateVolume();
62 
63 	// Play the referenced file once
64 	play(fileref, false);
65 }
66 
setBackgroundSong(uint32 fileref)67 void MusicPlayer::setBackgroundSong(uint32 fileref) {
68 	Common::StackLock lock(_mutex);
69 
70 	debugC(1, kDebugMIDI, "Groovie::Music: Changing the background song: %04X", fileref);
71 	_backgroundFileRef = fileref;
72 }
73 
frameTick()74 void MusicPlayer::frameTick() {
75 	if (_backgroundDelay > 0) {
76 		_backgroundDelay--;
77 		if (_backgroundDelay == 0)
78 			playSong(_backgroundFileRef);
79 	}
80 }
81 
setBackgroundDelay(uint16 delay)82 void MusicPlayer::setBackgroundDelay(uint16 delay) {
83 	_backgroundDelay = delay;
84 }
85 
playCD(uint8 track)86 void MusicPlayer::playCD(uint8 track) {
87 	int startms = 0;
88 
89 	// Stop the MIDI playback
90 	unload();
91 
92 	debugC(1, kDebugMIDI, "Groovie::Music: Playing CD track %d", track);
93 
94 	if (track == 3) {
95 		// This is the credits song, start at 23:20
96 		startms = 1400000;
97 		// TODO: If we want to play it directly from the CD, we should decrement
98 		// the song number (it's track 2 on the 2nd CD)
99 	} else if ((track == 98) && (_prevCDtrack == 3)) {
100 		// Track 98 is used as a hack to stop the credits song
101 		g_system->getAudioCDManager()->stop();
102 		stopCreditsIOS();
103 		return;
104 	}
105 
106 	// Save the playing track in order to be able to stop the credits song
107 	_prevCDtrack = track;
108 
109 	// Wait until the CD stops playing the current song
110 	// It was in the original interpreter, but it introduces a big delay
111 	// in the middle of the introduction, so it's disabled right now
112 	/*
113 	g_system->getAudioCDManager()->updateCD();
114 	while (g_system->getAudioCDManager()->isPlaying()) {
115 		// Wait a bit and try again
116 		_vm->_system->delayMillis(100);
117 		g_system->getAudioCDManager()->updateCD();
118 	}
119 	*/
120 
121 	// Play the track starting at the requested offset (1000ms = 75 frames)
122 	g_system->getAudioCDManager()->play(track - 1, 1, startms * 75 / 1000, 0);
123 
124 	// If the audio is not playing from the CD, play the "fallback" MIDI.
125 	// The Mac version has no CD tracks, so it will always use the MIDI.
126 	if (!g_system->getAudioCDManager()->isPlaying()) {
127 		if (track == 2) {
128 			// Intro MIDI fallback
129 			if (_vm->getPlatform() == Common::kPlatformMacintosh)
130 				playSong(70);
131 			else
132 				playSong((19 << 10) | 36); // XMI.GJD, file 36
133 		} else if (track == 3) {
134 			// TODO: Credits MIDI fallback
135 			if (_vm->getPlatform() == Common::kPlatformIOS)
136 				playCreditsIOS();
137 		}
138 	}
139 }
140 
startBackground()141 void MusicPlayer::startBackground() {
142 	debugC(3, kDebugMIDI, "Groovie::Music: startBackground()");
143 	if (!_isPlaying && _backgroundFileRef) {
144 		debugC(3, kDebugMIDI, "Groovie::Music: Starting the background song (0x%4X)", _backgroundFileRef);
145 		play(_backgroundFileRef, true);
146 	}
147 }
148 
setUserVolume(uint16 volume)149 void MusicPlayer::setUserVolume(uint16 volume) {
150 	Common::StackLock lock(_mutex);
151 
152 	// Save the new user volume
153 	_userVolume = volume;
154 	if (_userVolume > 0x100)
155 		_userVolume = 0x100;
156 
157 	// Apply it
158 	updateVolume();
159 }
160 
setGameVolume(uint16 volume,uint16 time)161 void MusicPlayer::setGameVolume(uint16 volume, uint16 time) {
162 	Common::StackLock lock(_mutex);
163 
164 	debugC(1, kDebugMIDI, "Groovie::Music: Setting game volume from %d to %d in %dms", _gameVolume, volume, time);
165 
166 	// Save the start parameters of the fade
167 	_fadingStartTime = _vm->_system->getMillis();
168 	_fadingStartVolume = _gameVolume;
169 	_fadingDuration = time;
170 
171 	// Save the new game volume
172 	_fadingEndVolume = volume;
173 	if (_fadingEndVolume > 100)
174 		_fadingEndVolume = 100;
175 }
176 
play(uint32 fileref,bool loop)177 bool MusicPlayer::play(uint32 fileref, bool loop) {
178 	// Unload the previous song
179 	unload();
180 
181 	// Set the new state
182 	_isPlaying = true;
183 
184 	// Load the new file
185 	return load(fileref, loop);
186 }
187 
stop()188 void MusicPlayer::stop() {
189 	_backgroundFileRef = 0;
190 	unload();
191 }
192 
applyFading()193 void MusicPlayer::applyFading() {
194 	debugC(6, kDebugMIDI, "Groovie::Music: applyFading() _fadingStartTime = %d, _fadingDuration = %d, _fadingStartVolume = %d, _fadingEndVolume = %d", _fadingStartTime, _fadingDuration, _fadingStartVolume, _fadingEndVolume);
195 	Common::StackLock lock(_mutex);
196 
197 	// Calculate the passed time
198 	uint32 time = _vm->_system->getMillis() - _fadingStartTime;
199 	debugC(6, kDebugMIDI, "Groovie::Music: time = %d, _gameVolume = %d", time, _gameVolume);
200 	if (time >= _fadingDuration) {
201 		// Set the end volume
202 		_gameVolume = _fadingEndVolume;
203 	} else {
204 		// Calculate the interpolated volume for the current time
205 		_gameVolume = (_fadingStartVolume * (_fadingDuration - time) +
206 			_fadingEndVolume * time) / _fadingDuration;
207 	}
208 	if (_gameVolume == _fadingEndVolume) {
209 		// If we were fading to 0, stop the playback and restore the volume
210 		if (_fadingEndVolume == 0) {
211 			debugC(1, kDebugMIDI, "Groovie::Music: Faded to zero: end of song. _fadingEndVolume set to 100");
212 			// WORKAROUND The original interpreter would keep playing a track
213 			// at volume 0 after it has faded out. When a new track was
214 			// started, it would restore the volume first and a short part of
215 			// the old track would be heard before the new track would start.
216 			// To prevent this, playback is actually stopped after fading out,
217 			// but _isPlaying remains true. This keeps the original
218 			// interpreter behavior of not starting the background music, but
219 			// it prevents the issue when starting playback of a new track.
220 			unload(false);
221 		}
222 	}
223 
224 	// Apply it
225 	updateVolume();
226 }
227 
onTimer(void * refCon)228 void MusicPlayer::onTimer(void *refCon) {
229 	debugC(9, kDebugMIDI, "Groovie::Music: onTimer()");
230 	MusicPlayer *music = (MusicPlayer *)refCon;
231 	Common::StackLock lock(music->_mutex);
232 
233 	// Apply the game volume fading
234 	if (music->_gameVolume != music->_fadingEndVolume) {
235 		// Apply the next step of the fading
236 		music->applyFading();
237 	}
238 
239 	// If the game is accepting user input, start the background music if necessary
240 	if (music->_vm->isWaitingForInput())
241 		music->startBackground();
242 
243 	// Handle internal timed events
244 	music->onTimerInternal();
245 }
246 
unload(bool updateState)247 void MusicPlayer::unload(bool updateState) {
248 	debugC(1, kDebugMIDI, "Groovie::Music: Stopping the playback");
249 
250 	if (updateState)
251 		// Set the new state
252 		_isPlaying = false;
253 }
254 
playCreditsIOS()255 void MusicPlayer::playCreditsIOS() {
256 	Audio::AudioStream *stream = Audio::SeekableAudioStream::openStreamFile("7th_Guest_Dolls_from_Hell_OC_ReMix");
257 
258 	if (!stream) {
259 		warning("Could not find '7th_Guest_Dolls_from_Hell_OC_ReMix' audio file");
260 		return;
261 	}
262 
263 	_vm->_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_handleCreditsIOS, stream);
264 }
265 
stopCreditsIOS()266 void MusicPlayer::stopCreditsIOS() {
267 	_vm->_system->getMixer()->stopHandle(_handleCreditsIOS);
268 }
269 
270 // MusicPlayerMidi
271 
MusicPlayerMidi(GroovieEngine * vm)272 MusicPlayerMidi::MusicPlayerMidi(GroovieEngine *vm) :
273 	MusicPlayer(vm), _midiParser(NULL), _data(NULL), _driver(NULL) {
274 	// Initialize the channel volumes
275 	for (int i = 0; i < 0x10; i++) {
276 		_chanVolumes[i] = 0x7F;
277 	}
278 }
279 
~MusicPlayerMidi()280 MusicPlayerMidi::~MusicPlayerMidi() {
281 	// Stop the callback
282 	if (_driver)
283 		_driver->setTimerCallback(NULL, NULL);
284 
285 	Common::StackLock lock(_mutex);
286 
287 	// Unload the parser
288 	unload();
289 	delete _midiParser;
290 
291 	// Unload the MIDI Driver
292 	if (_driver) {
293 		_driver->close();
294 		delete _driver;
295 	}
296 }
297 
send(uint32 b)298 void MusicPlayerMidi::send(uint32 b) {
299 	if ((b & 0xFFF0) == 0x07B0) { // Volume change
300 		// Save the specific channel volume
301 		byte chan = b & 0xF;
302 		_chanVolumes[chan] = (b >> 16) & 0x7F;
303 
304 		// Send the updated value
305 		updateChanVolume(chan);
306 
307 		return;
308 	}
309 	if (_driver)
310 		_driver->send(b);
311 }
312 
sysEx(const byte * msg,uint16 length)313 void MusicPlayerMidi::sysEx(const byte *msg, uint16 length) {
314 	if (_driver)
315 		_driver->sysEx(msg, length);
316 }
317 
sysExNoDelay(const byte * msg,uint16 length)318 uint16 MusicPlayerMidi::sysExNoDelay(const byte *msg, uint16 length) {
319 	return _driver ? _driver->sysExNoDelay(msg, length) : 0;
320 }
321 
metaEvent(byte type,byte * data,uint16 length)322 void MusicPlayerMidi::metaEvent(byte type, byte *data, uint16 length) {
323 	switch (type) {
324 	case 0x2F:
325 		// End of Track, play the background song
326 		endTrack();
327 		break;
328 	default:
329 		if (_driver)
330 			_driver->metaEvent(type, data, length);
331 		break;
332 	}
333 }
334 
pause(bool pause)335 void MusicPlayerMidi::pause(bool pause) {
336 	if (_midiParser) {
337 		if (pause) {
338 			_midiParser->pausePlaying();
339 		} else {
340 			_midiParser->resumePlaying();
341 		}
342 	}
343 }
344 
updateChanVolume(byte channel)345 void MusicPlayerMidi::updateChanVolume(byte channel) {
346 	// Generate a MIDI Control change message for the volume
347 	uint32 b = 0x7B0;
348 
349 	// Specify the channel
350 	b |= (channel & 0xF);
351 
352 	// Scale by the user and game volumes
353 	uint32 val = (_chanVolumes[channel] * _userVolume * _gameVolume) / 0x100 / 100;
354 	val &= 0x7F;
355 
356 	// Send it to the driver
357 	if (_driver)
358 		_driver->send(b | (val << 16));
359 }
360 
endTrack()361 void MusicPlayerMidi::endTrack() {
362 	debugC(3, kDebugMIDI, "Groovie::Music: endTrack()");
363 	unload();
364 }
365 
onTimerInternal()366 void MusicPlayerMidi::onTimerInternal() {
367 	// TODO: We really only need to call this while music is playing.
368 	if (_midiParser)
369 		_midiParser->onTimer();
370 }
371 
updateVolume()372 void MusicPlayerMidi::updateVolume() {
373 	// Apply it to all the channels
374 	for (int i = 0; i < 0x10; i++) {
375 		updateChanVolume(i);
376 	}
377 }
378 
unload(bool updateState)379 void MusicPlayerMidi::unload(bool updateState) {
380 	MusicPlayer::unload(updateState);
381 
382 	// Unload the parser data
383 	if (_midiParser)
384 		_midiParser->unloadMusic();
385 
386 	// Unload the data
387 	delete[] _data;
388 	_data = NULL;
389 }
390 
loadParser(Common::SeekableReadStream * stream,bool loop)391 bool MusicPlayerMidi::loadParser(Common::SeekableReadStream *stream, bool loop) {
392 	if (!_midiParser)
393 		return false;
394 
395 	// Read the whole file to memory
396 	int length = stream->size();
397 	_data = new byte[length];
398 	stream->read(_data, length);
399 	delete stream;
400 
401 	// Set the looping option
402 	_midiParser->property(MidiParser::mpAutoLoop, loop);
403 
404 	// Start parsing the data
405 	if (!_midiParser->loadMusic(_data, length)) {
406 		error("Groovie::Music: Couldn't parse the data");
407 		return false;
408 	}
409 
410 	// Activate the timer source
411 	if (_driver)
412 		_driver->setTimerCallback(this, &onTimer);
413 
414 	return true;
415 }
416 
417 
418 // MusicPlayerXMI
419 
MusicPlayerXMI(GroovieEngine * vm,const Common::String & gtlName)420 MusicPlayerXMI::MusicPlayerXMI(GroovieEngine *vm, const Common::String &gtlName) :
421 		MusicPlayerMidi(vm), _multisourceDriver(0), _milesXmidiTimbres(0) {
422 
423 	// Create the driver
424 	MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
425 	MusicType musicType = MidiDriver::getMusicType(dev);
426 	if (musicType == MT_GM && ConfMan.getBool("native_mt32"))
427 		musicType = MT_MT32;
428 	_driver = NULL;
429 
430 	_musicType = 0;
431 
432 	// 7th Guest uses FAT.AD/FAT.OPL/FAT.MT
433 	// 11th Hour uses SAMPLE.AD/SAMPLE.OPL/SAMPLE.MT
434 	switch (musicType) {
435 	case MT_ADLIB:
436 		_driver = _multisourceDriver = Audio::MidiDriver_Miles_AdLib_create(gtlName + ".AD", gtlName + ".OPL");
437 		break;
438 	case MT_MT32:
439 		Audio::MidiDriver_Miles_Midi *milesDriver;
440 		milesDriver = Audio::MidiDriver_Miles_MIDI_create(musicType, gtlName + ".MT");
441 		_milesXmidiTimbres = milesDriver;
442 		_driver = _multisourceDriver = milesDriver;
443 		break;
444 	case MT_GM:
445 		_driver = _multisourceDriver = Audio::MidiDriver_Miles_MIDI_create(musicType, "");
446 		break;
447 	case MT_NULL:
448 		_driver = _multisourceDriver = new MidiDriver_NULL_Multisource();
449 		break;
450 	default:
451 		break;
452 	}
453 	_musicType = musicType;
454 
455 	assert(_driver);
456 
457 	// Create the parser
458 	_midiParser = MidiParser::createParser_XMIDI(NULL, NULL, 0);
459 
460 	int result = _driver->open();
461 	if (result > 0 && result != MidiDriver::MERR_ALREADY_OPEN)
462 		error("Opening MidiDriver failed with error code %i", result);
463 
464 	_multisourceDriver->setSourceNeutralVolume(0, 100);
465 	_multisourceDriver->property(MidiDriver::PROP_USER_VOLUME_SCALING, true);
466 
467 	// Set the parser's driver
468 	_midiParser->setMidiDriver(this);
469 
470 	// Set the timer rate
471 	_midiParser->setTimerRate(_driver->getBaseTempo());
472 }
473 
~MusicPlayerXMI()474 MusicPlayerXMI::~MusicPlayerXMI() {
475 	_midiParser->stopPlaying();
476 }
477 
send(int8 source,uint32 b)478 void MusicPlayerXMI::send(int8 source, uint32 b) {
479 	_multisourceDriver->send(source, b);
480 }
481 
metaEvent(int8 source,byte type,byte * data,uint16 length)482 void MusicPlayerXMI::metaEvent(int8 source, byte type, byte *data, uint16 length) {
483 	if (type == 0x2F) // End Of Track
484 		MusicPlayerMidi::endTrack();
485 	_multisourceDriver->metaEvent(source, type, data, length);
486 }
487 
stopAllNotes(bool stopSustainedNotes)488 void MusicPlayerXMI::stopAllNotes(bool stopSustainedNotes) {
489 	if (_driver)
490 		_driver->stopAllNotes(stopSustainedNotes);
491 }
492 
isReady()493 bool MusicPlayerXMI::isReady() {
494 	return _driver ? _driver->isReady() : false;
495 }
496 
updateVolume()497 void MusicPlayerXMI::updateVolume() {
498 	_multisourceDriver->setSourceVolume(0, _gameVolume);
499 }
500 
setUserVolume(uint16 volume)501 void MusicPlayerXMI::setUserVolume(uint16 volume) {
502 	_multisourceDriver->syncSoundSettings();
503 }
504 
load(uint32 fileref,bool loop)505 bool MusicPlayerXMI::load(uint32 fileref, bool loop) {
506 	debugC(1, kDebugMIDI, "Groovie::Music: Starting the playback of song: %04X", fileref);
507 
508 	// Open the song resource
509 	Common::SeekableReadStream *file = _vm->_resMan->open(fileref);
510 	if (!file) {
511 		error("Groovie::Music: Couldn't find resource 0x%04X", fileref);
512 		return false;
513 	}
514 
515 	return loadParser(file, loop);
516 }
517 
unload(bool updateState)518 void MusicPlayerXMI::unload(bool updateState) {
519 	MusicPlayerMidi::unload(updateState);
520 	_multisourceDriver->deinitSource(0);
521 }
522 
523 // MusicPlayerMac_t7g
524 
MusicPlayerMac_t7g(GroovieEngine * vm)525 MusicPlayerMac_t7g::MusicPlayerMac_t7g(GroovieEngine *vm) : MusicPlayerMidi(vm) {
526 	// Create the parser
527 	_midiParser = MidiParser::createParser_SMF();
528 
529 	// Create the driver
530 	MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
531 	_driver = MidiDriver::createMidi(dev);
532 	assert(_driver);
533 
534 	_driver->open();	// TODO: Handle return value != 0 (indicating an error)
535 
536 	// Set the parser's driver
537 	_midiParser->setMidiDriver(this);
538 
539 	// Set the timer rate
540 	_midiParser->setTimerRate(_driver->getBaseTempo());
541 
542 	// Sanity check
543 	assert(_vm->_macResFork);
544 }
545 
load(uint32 fileref,bool loop)546 bool MusicPlayerMac_t7g::load(uint32 fileref, bool loop) {
547 	debugC(1, kDebugMIDI, "Groovie::Music: Starting the playback of song: %04X", fileref);
548 
549 	// First try for compressed MIDI
550 	Common::SeekableReadStream *file = _vm->_macResFork->getResource(MKTAG('c','m','i','d'), fileref & 0x3FF);
551 
552 	if (file) {
553 		// Found the resource, decompress it
554 		Common::SeekableReadStream *tmp = decompressMidi(file);
555 		delete file;
556 		file = tmp;
557 	} else {
558 		// Otherwise, it's uncompressed
559 		file = _vm->_macResFork->getResource(MKTAG('M','i','d','i'), fileref & 0x3FF);
560 		if (!file)
561 			error("Groovie::Music: Couldn't find resource 0x%04X", fileref);
562 	}
563 
564 	return loadParser(file, loop);
565 }
566 
decompressMidi(Common::SeekableReadStream * stream)567 Common::SeekableReadStream *MusicPlayerMac_t7g::decompressMidi(Common::SeekableReadStream *stream) {
568 	// Initialize an output buffer of the given size
569 	uint32 size = stream->readUint32BE();
570 	byte *output = (byte *)malloc(size);
571 
572 	byte *current = output;
573 	uint32 decompBytes = 0;
574 	while ((decompBytes < size) && !stream->eos()) {
575 		// 8 flags
576 		byte flags = stream->readByte();
577 
578 		for (byte i = 0; (i < 8) && !stream->eos(); i++) {
579 			if (flags & 1) {
580 				// 1: Next byte is a literal
581 				*(current++) = stream->readByte();
582 				if (stream->eos())
583 					continue;
584 				decompBytes++;
585 			} else {
586 				// 0: It's a reference to part of the history
587 				uint16 args = stream->readUint16BE();
588 				if (stream->eos())
589 					continue;
590 
591 				// Length = 4bit unsigned (3 minimal)
592 				uint8 length = (args >> 12) + 3;
593 
594 				// Offset = 12bit signed (all values are negative)
595 				int16 offset = (args & 0xFFF) | 0xF000;
596 
597 				// Copy from the past decompressed bytes
598 				decompBytes += length;
599 				while (length > 0) {
600 					*(current) = *(current + offset);
601 					current++;
602 					length--;
603 				}
604 			}
605 			flags = flags >> 1;
606 		}
607 	}
608 
609 	// Return the output buffer wrapped in a MemoryReadStream
610 	return new Common::MemoryReadStream(output, size, DisposeAfterUse::YES);
611 }
612 
613 // MusicPlayerMac_v2
614 
MusicPlayerMac_v2(GroovieEngine * vm)615 MusicPlayerMac_v2::MusicPlayerMac_v2(GroovieEngine *vm) : MusicPlayerMidi(vm) {
616 	// Create the parser
617 	_midiParser = MidiParser::createParser_QT();
618 
619 	// Create the driver
620 	MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
621 	_driver = MidiDriver::createMidi(dev);
622 	assert(_driver);
623 
624 	_driver->open();	// TODO: Handle return value != 0 (indicating an error)
625 
626 	// Set the parser's driver
627 	_midiParser->setMidiDriver(this);
628 
629 	// Set the timer rate
630 	_midiParser->setTimerRate(_driver->getBaseTempo());
631 }
632 
load(uint32 fileref,bool loop)633 bool MusicPlayerMac_v2::load(uint32 fileref, bool loop) {
634 	debugC(1, kDebugMIDI, "Groovie::Music: Starting the playback of song: %04X", fileref);
635 
636 	// Find correct filename
637 	ResInfo info;
638 	_vm->_resMan->getResInfo(fileref, info);
639 	uint len = info.filename.size();
640 	if (len < 4)
641 		return false;	// This shouldn't actually occur
642 
643 	// Remove the extension and add ".mov"
644 	info.filename.deleteLastChar();
645 	info.filename.deleteLastChar();
646 	info.filename.deleteLastChar();
647 	info.filename += "mov";
648 
649 	Common::SeekableReadStream *file = SearchMan.createReadStreamForMember(info.filename);
650 
651 	if (!file) {
652 		warning("Could not find file '%s'", info.filename.c_str());
653 		return false;
654 	}
655 
656 	return loadParser(file, loop);
657 }
658 
MusicPlayerIOS(GroovieEngine * vm)659 MusicPlayerIOS::MusicPlayerIOS(GroovieEngine *vm) : MusicPlayer(vm) {
660 	vm->getTimerManager()->installTimerProc(&onTimer, 50 * 1000, this, "groovieMusic");
661 }
662 
~MusicPlayerIOS()663 MusicPlayerIOS::~MusicPlayerIOS() {
664 	_vm->getTimerManager()->removeTimerProc(&onTimer);
665 }
666 
updateVolume()667 void MusicPlayerIOS::updateVolume() {
668 	// Just set the mixer volume for the music sound type
669 	_vm->_system->getMixer()->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, _userVolume * _gameVolume / 100);
670 }
671 
unload(bool updateState)672 void MusicPlayerIOS::unload(bool updateState) {
673 	MusicPlayer::unload(updateState);
674 
675 	_vm->_system->getMixer()->stopHandle(_handle);
676 }
677 
load(uint32 fileref,bool loop)678 bool MusicPlayerIOS::load(uint32 fileref, bool loop) {
679 	// Find correct filename
680 	ResInfo info;
681 	_vm->_resMan->getResInfo(fileref, info);
682 	uint len = info.filename.size();
683 	if (len < 4)
684 		return false;	// This shouldn't actually occur
685 	/*
686 	19462 door
687 	19463 ??
688 	19464 ??
689 	19465 puzzle?
690 	19466 cake
691 	19467 maze
692 	19468 ambient  (but not 69, amb b.  odd)
693 	19470 puzzle
694 	19471
695 	19473
696 	19475 coffins or blood pump
697 	19476 blood pump or coffins
698 	19493
699 	19499 chapel
700 	19509 downstair ambient
701 	19510 bedroom 'skip 3 and 5' puzzle (should loop from partway?)
702 	19514
703 	19515 bathroom drain teeth
704 	*/
705 	if ((fileref >= 19462 && fileref <= 19468) ||
706 		fileref == 19470 || fileref == 19471 ||
707 		fileref == 19473 || fileref == 19475 ||
708 		fileref == 19476 || fileref == 19493 ||
709 		fileref == 19499 || fileref == 19509 ||
710 		fileref == 19510 || fileref == 19514 ||
711 		fileref == 19515)
712 		loop = true; // XMIs for these refs self-loop
713 
714 	// iOS port provides alternative intro sequence music
715 	if (info.filename == "gu39.xmi") {
716 		info.filename = "intro";
717 	} else if (info.filename == "gu32.xmi") {
718 		info.filename = "foyer";
719 	} else {
720 		// Remove the extension
721 		info.filename.deleteLastChar();
722 		info.filename.deleteLastChar();
723 		info.filename.deleteLastChar();
724 		info.filename.deleteLastChar();
725 	}
726 
727 	// Create the audio stream
728 	Audio::SeekableAudioStream *seekStream = Audio::SeekableAudioStream::openStreamFile(info.filename);
729 
730 	if (!seekStream) {
731 		warning("Could not play audio file '%s'", info.filename.c_str());
732 		return false;
733 	}
734 
735 	Audio::AudioStream *audStream = seekStream;
736 
737 	// Loop if requested
738 	if (loop)
739 		audStream = Audio::makeLoopingAudioStream(seekStream, 0);
740 
741 	// MIDI player handles volume reset on load, IOS player doesn't - force update here
742 	updateVolume();
743 
744 	// Play!
745 	_vm->_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_handle, audStream);
746 	return true;
747 }
748 
749 } // End of Groovie namespace
750