diff options
-rw-r--r-- | kyra/kyra.cpp | 76 | ||||
-rw-r--r-- | kyra/kyra.h | 17 | ||||
-rw-r--r-- | kyra/sound.cpp | 165 | ||||
-rw-r--r-- | kyra/sound.h | 88 | ||||
-rw-r--r-- | kyra/staticres.cpp | 15 | ||||
-rw-r--r-- | sound/midiparser_xmidi.cpp | 6 |
6 files changed, 298 insertions, 69 deletions
diff --git a/kyra/kyra.cpp b/kyra/kyra.cpp index ea67e701e1..ec4caac12f 100644 --- a/kyra/kyra.cpp +++ b/kyra/kyra.cpp @@ -142,7 +142,8 @@ int KyraEngine::init(GameDetector &detector) { _system->initSize(320, 200); _system->endGFXTransaction(); - int midiDrv = MidiDriver::detectMusicDriver(MDT_NATIVE | MDT_ADLIB | MDT_PREFER_NATIVE); + // for now we prefer MIDI-to-Adlib conversion over native midi + int midiDrv = MidiDriver::detectMusicDriver(MDT_NATIVE | MDT_ADLIB/* | MDT_PREFER_NATIVE*/); bool native_mt32 = (ConfMan.getBool("native_mt32") || (midiDrv == MD_MT32)); MidiDriver *driver = MidiDriver::createMidi(midiDrv); @@ -158,7 +159,7 @@ int KyraEngine::init(GameDetector &detector) { assert(_midi); _midi->hasNativeMT32(native_mt32); _midi->setVolume(255); - + _res = new Resource(this); assert(_res); _screen = new Screen(this, _system); @@ -504,13 +505,14 @@ void KyraEngine::seq_intro() { _skipIntroFlag = true; // only true if user already played the game once _seq_copyViewOffs = true; _screen->setFont(Screen::FID_8_FNT); -// snd_kyraPlayTheme(0); + snd_playTheme(MUSIC_INTRO, 2); + snd_setSoundEffectFile(MUSIC_INTRO); setTalkCoords(144); for (int i = 0; i < ARRAYSIZE(introProcTable) && !seq_skipSequence(); ++i) { (this->*introProcTable[i])(); } setTalkCoords(136); - waitTicks(0x1E); + waitTicks(30); _seq_copyViewOffs = false; } @@ -947,13 +949,14 @@ bool KyraEngine::seq_playSpecialSequence(const uint8 *seqData, bool skipSeq) { } break; case 24: { // sound related - seqData++; - warning("Sequence opcode 24 skipped"); + uint8 param = *seqData++; + waitTicks(3); + snd_playSoundEffect(param); } break; case 25: { // sound related - seqData++; - warning("Sequence opcode 25 skipped"); + uint8 param = *seqData++; + snd_seqMessage(param); } break; case 26: @@ -981,4 +984,61 @@ bool KyraEngine::seq_playSpecialSequence(const uint8 *seqData, bool skipSeq) { return seqSkippedFlag; } +void KyraEngine::snd_playTheme(int file, int track) { + debug(9, "KyraEngine::snd_playTheme(%d)", file); + assert(file < _xmidiFilesCount); + _midi->playMusic("INTRO.XMI"); + _midi->playTrack(track, false); +} + +void KyraEngine::snd_playTrack(int track) { + debug(9, "KyraEngine::snd_playTrack(%d)", track); + _midi->playTrack(track, false); +} + +void KyraEngine::snd_setSoundEffectFile(int file) { + debug(9, "KyraEngine::snd_setSoundEffectFile(%d)", file); + assert(file < _xmidiFilesCount); + _midi->loadSoundEffectFile(_xmidiFiles[file]); +} + +void KyraEngine::snd_playSoundEffect(int track) { + debug(9, "KyraEngine::snd_playSoundEffect(%d)", track); + _midi->playSoundEffect(track); +} + +void KyraEngine::snd_startTrack() { + debug(9, "KyraEngine::snd_startTrack()"); + _midi->startTrack(); +} + +void KyraEngine::snd_haltTrack() { + debug(9, "KyraEngine::snd_haltTrack()"); + _midi->haltTrack(); +} + +void KyraEngine::snd_seqMessage(int msg) { + debug(9, "KyraEngine::snd_seqMessage(%.02d)", msg); + switch (msg) { + case 0: + // nothing to do here... + break; + case 1: + _midi->beginFadeOut(); + break; + case 56: + snd_playTheme(MUSIC_INTRO, 3); + break; + case 57: + snd_playTheme(MUSIC_INTRO, 4); + break; + case 58: + snd_playTheme(MUSIC_INTRO, 5); + break; + default: + warning("Unknown seq. message: %.02d", msg); + break; + } +} + } // End of namespace Kyra diff --git a/kyra/kyra.h b/kyra/kyra.h index d080cc9bb3..4463a5e517 100644 --- a/kyra/kyra.h +++ b/kyra/kyra.h @@ -88,12 +88,17 @@ class Resource; class Screen; class KyraEngine : public Engine { + friend class MusicPlayer; public: enum { TALK_SUBSTRING_LEN = 80, TALK_SUBSTRING_NUM = 3 }; + + enum { + MUSIC_INTRO = 0 + }; KyraEngine(GameDetector *detector, OSystem *system); ~KyraEngine(); @@ -149,6 +154,14 @@ protected: uint16 wsa_getNumFrames(WSAMovieV1 *wsa) const; void wsa_play(WSAMovieV1 *wsa, int frameNum, int x, int y, int pageNum); void wsa_processFrame(WSAMovieV1 *wsa, int frameNum, uint8 *dst); + + void snd_playTheme(int file, int track = 0); + void snd_playTrack(int track); + void snd_startTrack(); + void snd_haltTrack(); + void snd_setSoundEffectFile(int file); + void snd_playSoundEffect(int track); + void snd_seqMessage(int msg); uint8 _game; bool _fastMode; @@ -191,7 +204,9 @@ protected: static const char *_seq_WSATable[]; static const char *_seq_CPSTable[]; static const char *_seq_COLTable[]; - static const char *_seq_textsTableEN[]; + static const char *_seq_textsTableEN[]; + static const char *_xmidiFiles[]; + static const int _xmidiFilesCount; }; } // End of namespace Kyra diff --git a/kyra/sound.cpp b/kyra/sound.cpp index 8448726c75..6b75e1033e 100644 --- a/kyra/sound.cpp +++ b/kyra/sound.cpp @@ -20,19 +20,28 @@ */ #include "common/stdafx.h" +#include "common/system.h" #include "kyra/resource.h" #include "kyra/sound.h" namespace Kyra { -MusicPlayer::MusicPlayer(MidiDriver* driver, KyraEngine* engine) { +MusicPlayer::MusicPlayer(MidiDriver *driver, KyraEngine *engine) { _engine = engine; _driver = driver; _passThrough = false; - _isPlaying = _nativeMT32 = false; + _eventFromMusic = false; + _fadeMusicOut = _sfxIsPlaying = false; + _isPlaying = _isLooping = _nativeMT32 = false; + _soundEffect = _parser = 0; + _soundEffectSource = _parserSource = 0; - memset(_channel, 0, sizeof(MidiChannel*) * 16); - memset(_channelVolume, 255, sizeof(uint8) * 16); + memset(_channel, 0, sizeof(MidiChannel*) * 32); + memset(_channelVolume, 50, sizeof(uint8) * 16); + _channelVolume[10] = 100; + for (int i = 0; i < 16; ++i) { + _virChannel[i] = i; + } _volume = 0; int ret = open(); @@ -56,9 +65,13 @@ void MusicPlayer::setVolume(int volume) { return; _volume = volume; - for (int i = 0; i < 16; ++i) { + for (int i = 0; i < 32; ++i) { if (_channel[i]) { - _channel[i]->volume(_channelVolume[i] * _volume / 255); + if (i >= 16) { + _channel[i]->volume(_channelVolume[i - 16] * _volume / 255); + } else { + _channel[i]->volume(_channelVolume[i] * _volume / 255); + } } } } @@ -84,11 +97,23 @@ void MusicPlayer::close() { void MusicPlayer::send(uint32 b) { if (_passThrough) { + if ((b & 0xFFF0) == 0x007BB0) + return; _driver->send(b); return; } uint8 channel = (byte)(b & 0x0F); + if (((b & 0xFFF0) == 0x6FB0 || (b & 0xFFF0) == 0x6EB0) && channel != 9) { + if (_virChannel[channel] == channel) { + _virChannel[channel] = channel + 16; + if (!_channel[_virChannel[channel]]) + _channel[_virChannel[channel]] = _driver->allocateChannel(); + _channel[_virChannel[channel]]->volume(_channelVolume[channel] * _volume / 255); + } + return; + } + if ((b & 0xFFF0) == 0x07B0) { // Adjust volume changes by master volume uint8 volume = (uint8)((b >> 16) & 0x7F); @@ -104,24 +129,36 @@ void MusicPlayer::send(uint32 b) { return; } - if (!_channel[channel]) - _channel[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel(); - if (_channel[channel]) - _channel[channel]->send(b); + if (!_channel[_virChannel[channel]]) { + _channel[_virChannel[channel]] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel(); + _channel[_virChannel[channel]]->volume(_channelVolume[channel] * _volume / 255); + } + if (_channel[_virChannel[channel]]) + _channel[_virChannel[channel]]->send(b); } void MusicPlayer::metaEvent(byte type, byte *data, uint16 length) { switch (type) { case 0x2F: // End of Track - _parser->jumpToTick(0); + if (_eventFromMusic) { + if (!_isLooping) { + _isPlaying = false; + } + // remap all channels + for (int i = 0; i < 16; ++i) { + _virChannel[i] = i; + } + } else { + _sfxIsPlaying = false; + } break; default: - warning("Unhandled meta event: 0x%02x", type); + _driver->metaEvent(type, data, length); break; } } -void MusicPlayer::playMusic(const char* file) { +void MusicPlayer::playMusic(const char *file) { uint32 size; uint8 *data = (_engine->resource())->fileData(file, &size); @@ -133,10 +170,10 @@ void MusicPlayer::playMusic(const char* file) { playMusic(data, size); } -void MusicPlayer::playMusic(uint8* data, uint32 size) { - if (_isPlaying) - stopMusic(); +void MusicPlayer::playMusic(uint8 *data, uint32 size) { + stopMusic(); + _parserSource = data; _parser = MidiParser::createParser_XMIDI(); assert(_parser); @@ -150,31 +187,117 @@ void MusicPlayer::playMusic(uint8* data, uint32 size) { _parser->setTrack(0); _parser->setMidiDriver(this); _parser->setTimerRate(getBaseTempo()); + _parser->property(MidiParser::mpAutoLoop, false); +} - _isPlaying = true; +void MusicPlayer::loadSoundEffectFile(const char *file) { + uint32 size; + uint8 *data = (_engine->resource())->fileData(file, &size); + + if (!data) { + warning("couldn't load '%s'", file); + return; + } + + loadSoundEffectFile(data, size); +} + +void MusicPlayer::loadSoundEffectFile(uint8 *data, uint32 size) { + stopSoundEffect(); + + _soundEffectSource = data; + _soundEffect = MidiParser::createParser_XMIDI(); + assert(_soundEffect); + + if (!_soundEffect->loadMusic(data, size)) { + warning("Error reading track!"); + delete _parser; + _parser = 0; + return; + } + + _soundEffect->setTrack(0); + _soundEffect->setMidiDriver(this); + _soundEffect->setTimerRate(getBaseTempo()); + _soundEffect->property(MidiParser::mpAutoLoop, false); } void MusicPlayer::stopMusic() { + _isLooping = false; _isPlaying = false; if (_parser) { _parser->unloadMusic(); delete _parser; - _parser = NULL; + _parser = 0; + delete [] _parserSource; + _parserSource = 0; + } +} + +void MusicPlayer::stopSoundEffect() { + _sfxIsPlaying = false; + if (_soundEffect) { + _soundEffect->unloadMusic(); + delete _soundEffect; + _soundEffect = 0; + delete [] _soundEffectSource; + _soundEffectSource = 0; } } void MusicPlayer::onTimer(void *refCon) { MusicPlayer *music = (MusicPlayer *)refCon; - if (music->_isPlaying) - music->_parser->onTimer(); + + // this should be set to the fadeToBlack value + const static uint32 musicFadeTime = 2 * 1000; + if (music->_fadeMusicOut && music->_fadeStartTime + musicFadeTime > music->_engine->_system->getMillis()) { + byte volume = (byte)((musicFadeTime - (music->_engine->_system->getMillis() - music->_fadeStartTime)) * 255 / musicFadeTime); + music->setVolume(volume); + } else if(music->_fadeStartTime) { + music->setVolume(255); + music->_fadeStartTime = 0; + music->_fadeMusicOut = false; + } + + if (music->_isPlaying) { + if (music->_parser) { + music->_eventFromMusic = true; + music->_parser->onTimer(); + } + } + + if (music->_sfxIsPlaying) { + if (music->_soundEffect) { + music->_eventFromMusic = false; + music->_soundEffect->onTimer(); + } + } } -void MusicPlayer::playTrack(uint8 track) { +void MusicPlayer::playTrack(uint8 track, bool loop) { if (_parser) { _isPlaying = true; + _isLooping = loop; _parser->setTrack(track); _parser->jumpToTick(0); + _parser->setTempo(1); + _parser->property(MidiParser::mpAutoLoop, loop); } } +void MusicPlayer::playSoundEffect(uint8 track) { + if (_soundEffect) { + _sfxIsPlaying = true; + _soundEffect->setTrack(track); + _soundEffect->jumpToTick(0); + _soundEffect->property(MidiParser::mpAutoLoop, false); + } +} + +void MusicPlayer::beginFadeOut() { + // this should be something like fade out... + _fadeMusicOut = true; + _fadeStartTime = _engine->_system->getMillis(); +} + } // end of namespace Kyra diff --git a/kyra/sound.h b/kyra/sound.h index cf262515cd..e5e1dc0ed2 100644 --- a/kyra/sound.h +++ b/kyra/sound.h @@ -29,53 +29,73 @@ #include "kyra/kyra.h" namespace Kyra { - class MusicPlayer : public MidiDriver { - public: +class MusicPlayer : public MidiDriver { +public: - MusicPlayer(MidiDriver* driver, KyraEngine* engine); - ~MusicPlayer(); + MusicPlayer(MidiDriver *driver, KyraEngine *engine); + ~MusicPlayer(); - void setVolume(int volume); - int getVolume() { return _volume; } + void setVolume(int volume); + int getVolume() { return _volume; } - void hasNativeMT32(bool nativeMT32) { _nativeMT32 = nativeMT32; } + void hasNativeMT32(bool nativeMT32) { _nativeMT32 = nativeMT32; } + bool isMT32() { return _nativeMT32; } - void playMusic(const char* file); - void playMusic(uint8* data, uint32 size); - void stopMusic(); + void playMusic(const char* file); + void playMusic(uint8* data, uint32 size); + void stopMusic(); - void playTrack(uint8 track); - void setPassThrough(bool b) { _passThrough = b; } + void playTrack(uint8 track, bool looping = true); + void haltTrack() { _isPlaying = false; } + void startTrack() { _isPlaying = true; } + void setPassThrough(bool b) { _passThrough = b; } - //MidiDriver interface implementation - int open(); - void close(); - void send(uint32 b); - void metaEvent(byte type, byte *data, uint16 length); + void loadSoundEffectFile(const char* file); + void loadSoundEffectFile(uint8* data, uint32 size); + void stopSoundEffect(); - void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { } - uint32 getBaseTempo(void) { return _driver ? _driver->getBaseTempo() : 0; } + void playSoundEffect(uint8 track); - //Channel allocation functions - MidiChannel *allocateChannel() { return 0; } - MidiChannel *getPercussionChannel() { return 0; } + void beginFadeOut(); - protected: + //MidiDriver interface implementation + int open(); + void close(); + void send(uint32 b); + void metaEvent(byte type, byte *data, uint16 length); - static void onTimer(void *data); + void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { } + uint32 getBaseTempo(void) { return _driver ? _driver->getBaseTempo() : 0; } - MidiChannel* _channel[16]; - uint8 _channelVolume[16]; - MidiDriver* _driver; - bool _nativeMT32; - bool _passThrough; - uint8 _volume; - bool _isPlaying; - MidiParser* _parser; - KyraEngine* _engine; + //Channel allocation functions + MidiChannel *allocateChannel() { return 0; } + MidiChannel *getPercussionChannel() { return 0; } + +protected: + + static void onTimer(void *data); + + MidiChannel* _channel[32]; + int _virChannel[16]; + uint8 _channelVolume[16]; + MidiDriver* _driver; + bool _nativeMT32; + bool _passThrough; + uint8 _volume; + bool _isPlaying; + bool _sfxIsPlaying; + uint32 _fadeStartTime; + bool _fadeMusicOut; + bool _isLooping; + bool _eventFromMusic; + MidiParser *_parser; + byte *_parserSource; + MidiParser *_soundEffect; + byte *_soundEffectSource; + KyraEngine *_engine; +}; - }; } // end of namespace Kyra #endif diff --git a/kyra/staticres.cpp b/kyra/staticres.cpp index 7f24f915f3..d11323a025 100644 --- a/kyra/staticres.cpp +++ b/kyra/staticres.cpp @@ -452,4 +452,19 @@ const char *KyraEngine::_seq_textsTableEN[] = { "" }; +const char *KyraEngine::_xmidiFiles[] = { + "INTRO.XMI", + "KYRA1A.XMI", + "KYRA1B.XMI", + "KYRA2A.XMI", + "KYRA3A.XMI", + "KYRA4A.XMI", + "KYRA4B.XMI", + "KYRA5A.XMI", + "KYRA5B.XMI", + "KYRAMISC.XMI" +}; + +const int KyraEngine::_xmidiFilesCount = ARRAYSIZE(_xmidiFiles); + } // End of namespace Kyra diff --git a/sound/midiparser_xmidi.cpp b/sound/midiparser_xmidi.cpp index 3ee08ccb7d..9ad5bfafb9 100644 --- a/sound/midiparser_xmidi.cpp +++ b/sound/midiparser_xmidi.cpp @@ -51,11 +51,7 @@ public: // This is a special XMIDI variable length quantity uint32 MidiParser_XMIDI::readVLQ2(byte * &pos) { uint32 value = 0; - int i; - - for (i = 0; i < 4; ++i) { - if (pos[0] & 0x80) - break; + while (!(pos[0] & 0x80)) { value += *pos++; } return value; |