SHERLOCK: add music + sync to 3DO intro

This commit is contained in:
Martin Kiewitz 2015-06-08 20:09:29 +02:00
parent 55551cfbd5
commit 0d09049562
3 changed files with 204 additions and 85 deletions

View File

@ -24,6 +24,8 @@
#include "sherlock/sherlock.h"
#include "sherlock/music.h"
#include "sherlock/scalpel/drivers/mididriver.h"
// for 3DO digital music
#include "audio/decoders/aiff.h"
namespace Sherlock {
@ -190,16 +192,15 @@ bool MidiParser_SH::loadMusic(byte *data, uint32 size) {
/*----------------------------------------------------------------*/
Music::Music(SherlockEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
_midiDriver = NULL;
_midiParser = NULL;
_musicType = MT_NULL;
_musicPlaying = false;
_musicOn = true;
_musicOn = false;
if (_vm->getPlatform() == Common::kPlatform3DO) {
// 3DO - disable music
// TODO: Implement music support
_driver = NULL;
_midiParser = NULL;
_musicType = MT_NULL;
_musicOn = false;
// 3DO - uses digital samples for music
_musicOn = true;
return;
}
@ -211,18 +212,16 @@ Music::Music(SherlockEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MT32);
_musicType = MidiDriver::getMusicType(dev);
_driver = NULL;
switch (_musicType) {
case MT_ADLIB:
_driver = MidiDriver_AdLib_create();
_midiDriver = MidiDriver_AdLib_create();
break;
case MT_MT32:
_driver = MidiDriver_MT32_create();
_midiDriver = MidiDriver_MT32_create();
break;
case MT_GM:
if (ConfMan.getBool("native_mt32")) {
_driver = MidiDriver_MT32_create();
_midiDriver = MidiDriver_MT32_create();
_musicType = MT_MT32;
}
break;
@ -233,16 +232,14 @@ Music::Music(SherlockEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
break;
}
if (_driver) {
assert(_driver);
int ret = _driver->open();
if (_midiDriver) {
int ret = _midiDriver->open();
if (ret == 0) {
// Reset is done inside our MIDI driver
_driver->setTimerCallback(_midiParser, &_midiParser->timerCallback);
_midiDriver->setTimerCallback(_midiParser, &_midiParser->timerCallback);
}
_midiParser->setMidiDriver(_driver);
_midiParser->setTimerRate(_driver->getBaseTempo());
_midiParser->setMidiDriver(_midiDriver);
_midiParser->setTimerRate(_midiDriver->getBaseTempo());
if (_musicType == MT_MT32) {
// Upload patches
@ -259,12 +256,11 @@ Music::Music(SherlockEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
byte *MT32driverDataPtr = MT32driverData + 12;
MT32driverDataSize -= 12;
MidiDriver_MT32_uploadPatches(_driver, MT32driverDataPtr, MT32driverDataSize);
MidiDriver_MT32_uploadPatches(_midiDriver, MT32driverDataPtr, MT32driverDataSize);
delete[] MT32driverData;
}
} else {
// no driver, bye bye music
_musicOn = false;
_musicOn = true;
}
}
@ -274,9 +270,9 @@ Music::~Music() {
_midiParser->stopPlaying();
delete _midiParser;
}
if (_driver) {
_driver->close();
delete _driver;
if (_midiDriver) {
_midiDriver->close();
delete _midiDriver;
}
}
@ -321,66 +317,92 @@ void Music::syncMusicSettings() {
}
bool Music::playMusic(const Common::String &name) {
if (!_driver)
return false;
if (!_musicOn)
return false;
debugC(kDebugLevelMusic, "Music: playMusic('%s')", name.c_str());
Common::SeekableReadStream *stream = _vm->_res->load(name, "MUSIC.LIB");
byte *data = new byte[stream->size()];
int32 dataSize = stream->size();
assert(data);
if (_vm->getPlatform() != Common::kPlatform3DO) {
// MIDI based
if (!_midiDriver)
return false;
stream->read(data, dataSize);
delete stream;
Common::SeekableReadStream *stream = _vm->_res->load(name, "MUSIC.LIB");
// for dumping the music tracks
byte *data = new byte[stream->size()];
int32 dataSize = stream->size();
assert(data);
stream->read(data, dataSize);
delete stream;
// for dumping the music tracks
#if 0
Common::DumpFile outFile;
outFile.open(name + ".RAW");
outFile.write(data, stream->size());
outFile.flush();
outFile.close();
Common::DumpFile outFile;
outFile.open(name + ".RAW");
outFile.write(data, stream->size());
outFile.flush();
outFile.close();
#endif
if (dataSize < 14) {
warning("Music: not enough data in music file");
return false;
}
if (dataSize < 14) {
warning("Music: not enough data in music file");
return false;
}
byte *dataPos = data;
if (memcmp(" ", dataPos, 12)) {
warning("Music: expected header not found in music file");
return false;
}
dataPos += 12;
dataSize -= 12;
byte *dataPos = data;
if (memcmp(" ", dataPos, 12)) {
warning("Music: expected header not found in music file");
return false;
}
dataPos += 12;
dataSize -= 12;
uint16 headerSize = READ_LE_UINT16(dataPos);
if (headerSize != 0x7F) {
warning("Music: header is not as expected");
return false;
}
uint16 headerSize = READ_LE_UINT16(dataPos);
if (headerSize != 0x7F) {
warning("Music: header is not as expected");
return false;
}
if (_driver) {
switch (_musicType) {
case MT_ADLIB:
MidiDriver_AdLib_newMusicData(_driver, dataPos, dataSize);
MidiDriver_AdLib_newMusicData(_midiDriver, dataPos, dataSize);
break;
case MT_MT32:
MidiDriver_MT32_newMusicData(_driver, dataPos, dataSize);
MidiDriver_MT32_newMusicData(_midiDriver, dataPos, dataSize);
break;
default:
// should never happen
break;
}
}
_midiParser->loadMusic(dataPos, dataSize);
_midiParser->loadMusic(dataPos, dataSize);
} else {
// 3DO: sample based
Audio::AudioStream *musicStream;
Common::String digitalMusicName = "music/" + name + "_MW22.aifc";
if (isPlaying()) {
_mixer->stopHandle(_digitalMusicHandle);
}
Common::File *digitalMusicFile = new Common::File();
if (!digitalMusicFile->open(digitalMusicName)) {
warning("playMusic: can not open 3DO music '%s'", digitalMusicName.c_str());
return false;
}
// Try to load the given file as AIFF/AIFC
musicStream = Audio::makeAIFFStream(digitalMusicFile, DisposeAfterUse::YES);
if (!musicStream) {
warning("playMusic: can not load 3DO music '%s'", digitalMusicName.c_str());
return false;
}
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_digitalMusicHandle, musicStream);
}
return true;
}
@ -388,6 +410,15 @@ void Music::stopMusic() {
// TODO
warning("TODO: Sound::stopMusic");
if (_vm->getPlatform() != Common::kPlatform3DO) {
// TODO
} else {
// 3DO
if (isPlaying()) {
_mixer->stopHandle(_digitalMusicHandle);
}
}
_musicPlaying = false;
}
@ -410,6 +441,27 @@ void Music::waitTimerRoland(uint time) {
warning("TODO: Sound::waitTimerRoland");
}
bool Music::isPlaying() {
if (_vm->getPlatform() != Common::kPlatform3DO) {
// MIDI based
return _midiParser->isPlaying();
} else {
// 3DO: sample based
return _mixer->isSoundHandleActive(_digitalMusicHandle);
}
}
// Returns the current music position in milliseconds
uint32 Music::getCurrentPosition() {
if (_vm->getPlatform() != Common::kPlatform3DO) {
// MIDI based
return (_midiParser->getTick() * 1000) / 60; // translate tick to millisecond
} else {
// 3DO: sample based
return _mixer->getSoundElapsedTime(_digitalMusicHandle);
}
}
// This is used to wait for the music in certain situations like especially the intro
// Note: the original game didn't do this, instead it just waited for certain amounts of time
// We do this, so that the intro graphics + music work together even on faster/slower hardware.
@ -445,5 +497,39 @@ bool Music::waitUntilTick(uint32 tick, uint32 maxTick, uint32 additionalDelay, u
}
}
} // End of namespace Sherlock
// This is used to wait for the music in certain situations like especially the intro
// Note: the original game didn't do this, instead it just waited for certain amounts of time
// We do this, so that the intro graphics + music work together even on faster/slower hardware.
bool Music::waitUntilMSec(uint32 msecTarget, uint32 msecMax, uint32 additionalDelay, uint32 noMusicDelay) {
uint32 msecCurrent = 0;
if (!isPlaying()) {
return _vm->_events->delay(noMusicDelay, true);
}
while (1) {
if (!isPlaying()) { // Music is not playing anymore -> we are done
if (additionalDelay > 0) {
if (!_vm->_events->delay(additionalDelay, true))
return false;
}
return true;
}
msecCurrent = getCurrentPosition();
//warning("waitUntilMSec: %lx", msecCurrent);
if ((!msecMax) || (msecCurrent <= msecMax)) {
if (msecCurrent >= msecTarget) {
if (additionalDelay > 0) {
if (!_vm->_events->delay(additionalDelay, true))
return false;
}
return true;
}
}
if (!_vm->_events->delay(10, true))
return false;
}
}
} // End of namespace Sherlock

View File

@ -27,6 +27,9 @@
#include "audio/midiparser.h"
//#include "audio/mididrv.h"
#include "sherlock/scalpel/drivers/mididriver.h"
// for 3DO digital music
#include "audio/audiostream.h"
#include "audio/mixer.h"
namespace Sherlock {
@ -58,7 +61,8 @@ private:
SherlockEngine *_vm;
Audio::Mixer *_mixer;
MidiParser_SH *_midiParser;
MidiDriver *_driver;
MidiDriver *_midiDriver;
Audio::SoundHandle _digitalMusicHandle;
public:
bool _musicPlaying;
@ -105,7 +109,12 @@ public:
void waitTimerRoland(uint time);
bool isPlaying();
uint32 getCurrentPosition();
bool waitUntilTick(uint32 tick, uint32 maxTick, uint32 additionalDelay, uint32 noMusicDelay);
bool waitUntilMSec(uint32 msecTarget, uint32 maxMSec, uint32 additionalDelay, uint32 noMusicDelay);
};
} // End of namespace Sherlock

View File

@ -531,6 +531,12 @@ bool ScalpelEngine::showOfficeCutscene() {
bool ScalpelEngine::showCityCutscene3DO() {
_animation->_soundLibraryFilename = "TITLE.SND";
// Play intro music
_music->playMusic("prolog");
// rain.aiff seems to be playing in an endless loop until
// sherlock logo fades away TODO
bool finished = _animation->play3DO("26open1", true, 1, 255, 2);
if (finished) {
@ -548,8 +554,8 @@ bool ScalpelEngine::showCityCutscene3DO() {
ImageFile3DO titleImage_November("title2b.cel");
_screen->transBlitFromUnscaled3DO(titleImage_November[0]._frame, Common::Point(101, 100));
finished = _events->delay(5000, true);
finished = _music->waitUntilMSec(14700, 0, 0, 5000);
}
if (finished) {
@ -562,8 +568,6 @@ bool ScalpelEngine::showCityCutscene3DO() {
finished = _animation->play3DO("26open2", true, 1, 0, 2);
if (finished) {
_screen->_backBuffer2.blitFrom(*_screen);
// "Sherlock Holmes" (title)
ImageFile3DO titleImage_SherlockHolmesTitle("title1ab.cel");
@ -578,14 +582,10 @@ bool ScalpelEngine::showCityCutscene3DO() {
finished = _events->delay(3500, true);
}
// Title is supposed to get faded away after that
if (finished) {
// Restore screen
_screen->blitFrom(_screen->_backBuffer2);
}
}
if (finished)
finished = _events->delay(2000);
finished = _music->waitUntilMSec(33600, 0, 0, 2000);
if (finished) {
// TODO: fade to black
@ -598,44 +598,56 @@ bool ScalpelEngine::showCityCutscene3DO() {
_screen->transBlitFromUnscaled3DO(titleImage_InTheAlley[0]._frame, Common::Point(72, 51));
// TODO: Supposed to get faded in and out
finished = _events->delay(2500, true);
finished = _music->waitUntilMSec(39900, 0, 0, 2500);
// Fade out
_screen->clear();
}
return finished;
}
bool ScalpelEngine::showAlleyCutscene3DO() {
bool finished = _animation->play3DO("27PRO1", true, 1, 3, 2);
bool finished = _music->waitUntilMSec(44000, 0, 0, 1000);
if (finished)
finished = _animation->play3DO("27PRO1", true, 1, 3, 2);
if (finished) {
// Fade out...
_screen->clear();
finished = _events->delay(1000, true);
finished = _music->waitUntilMSec(66700, 0, 0, 1000);
}
if (finished)
finished = _animation->play3DO("27PRO2", true, 1, 0, 2);
if (finished) {
// Fade out
_screen->clear();
finished = _music->waitUntilMSec(76000, 0, 0, 1000);
}
if (finished) {
// Show screaming victim
ImageFile3DO titleImage_ScreamingVictim("scream.cel");
_screen->clear();
_screen->transBlitFromUnscaled3DO(titleImage_ScreamingVictim[0]._frame, Common::Point(0, 0));
// Play "scream.aiff"
if (_sound->_voices)
_sound->playSound("prologue/sounds/scream.aiff", WAIT_RETURN_IMMEDIATELY, 100);
finished = _events->delay(6000, true);
finished = _music->waitUntilMSec(81600, 0, 0, 6000);
}
if (finished) {
// TODO: quick fade out
_screen->clear();
finished = _events->delay(2000, true);
finished = _music->waitUntilMSec(84400, 0, 0, 2000);
}
if (finished)
@ -653,22 +665,21 @@ bool ScalpelEngine::showAlleyCutscene3DO() {
_screen->transBlitFromUnscaled3DO(titleImage_EarlyTheFollowingMorning[0]._frame, Common::Point(35, 51));
// TODO: Fade in
finished = _events->delay(3000, true);
finished = _music->waitUntilMSec(96700, 0, 0, 3000);
}
return finished;
}
bool ScalpelEngine::showStreetCutscene3DO() {
// wait a bit
bool finished = _events->delay(500);
bool finished = true;
if (finished) {
// fade out "Early the following morning..."
_screen->clear();
// wait for music a bit
finished = _events->delay(1000, true);
finished = _music->waitUntilMSec(100300, 0, 0, 1000);
}
finished = _animation->play3DO("14KICK", true, 1, 3, 2);
@ -677,16 +688,25 @@ bool ScalpelEngine::showStreetCutscene3DO() {
finished = _animation->play3DO("14NOTE", true, 1, 0, 3);
// TODO: fade out
_screen->clear();
return finished;
}
bool ScalpelEngine::showOfficeCutscene3DO() {
bool finished = _animation->play3DO("COFF1", true, 1, 3, 3);
bool finished = true;
finished = _music->waitUntilMSec(151000, 0, 0, 1000);
if (finished)
_animation->play3DO("COFF1", true, 1, 3, 3);
if (finished)
finished = _animation->play3DO("COFF2", true, 1, 0, 3);
if (finished)
finished = _music->waitUntilMSec(182400, 0, 0, 1000);
if (finished) {
// Show the note
ImageFile3DO titleImage_CoffeeNote("note.cel");
@ -699,12 +719,16 @@ bool ScalpelEngine::showOfficeCutscene3DO() {
} else
finished = _events->delay(19000);
if (finished) {
_events->clearEvents();
finished = _events->delay(500);
}
if (finished)
finished = _music->waitUntilMSec(218800, 0, 0, 1000);
// Fade out
_screen->clear();
}
if (finished)
finished = _music->waitUntilMSec(222200, 0, 0, 1000);
if (finished)
finished = _animation->play3DO("COFF3", true, 1, 0, 3);