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 >lName) :
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