diff options
-rw-r--r-- | engines/kyra/detection.cpp | 40 | ||||
-rw-r--r-- | engines/kyra/kyra.cpp | 29 | ||||
-rw-r--r-- | engines/kyra/kyra_v2.cpp | 54 | ||||
-rw-r--r-- | engines/kyra/kyra_v2.h | 23 | ||||
-rw-r--r-- | engines/kyra/sequences_v2.cpp | 113 | ||||
-rw-r--r-- | engines/kyra/sound.h | 92 | ||||
-rw-r--r-- | engines/kyra/sound_towns.cpp | 620 |
7 files changed, 738 insertions, 233 deletions
diff --git a/engines/kyra/detection.cpp b/engines/kyra/detection.cpp index 67872affd2..7b3d4f26a6 100644 --- a/engines/kyra/detection.cpp +++ b/engines/kyra/detection.cpp @@ -51,6 +51,8 @@ namespace { #define KYRA2_CD_FLAGS FLAGS(false, false, true, false, Kyra::GI_KYRA2) #define KYRA2_DEMO_FLAGS FLAGS(true, false, false, false, Kyra::GI_KYRA2) +#define KYRA2_TOWNS_FLAGS FLAGS(false, false, false, false, Kyra::GI_KYRA2) +#define KYRA2_TOWNS_SJIS_FLAGS FLAGS(false, false, false, true, Kyra::GI_KYRA2) #define KYRA3_CD_FLAGS FLAGS(false, false, true, false, Kyra::GI_KYRA3) @@ -176,7 +178,11 @@ const KYRAGameDescription adGameDescs[] = { { "kyra1", 0, - AD_ENTRY1("TWMUSIC.PAK", "e53bca3a3e3fb49107d59463ec387a59"), + { + { "EMC.PAK", 0, "a046bb0b422061aab8e4c4689400343a", -1 }, + { "TWMUSIC.PAK", 0, "e53bca3a3e3fb49107d59463ec387a59", -1 }, + { NULL, 0, NULL, 0 } + }, Common::EN_ANY, Common::kPlatformFMTowns, Common::ADGF_NO_FLAGS @@ -187,7 +193,11 @@ const KYRAGameDescription adGameDescs[] = { { "kyra1", 0, - AD_ENTRY1("TWMUSIC.PAK", "e53bca3a3e3fb49107d59463ec387a59"), + { + { "JMC.PAK", 0, "9c5707a2a478e8167e44283246612d2c", -1 }, + { "TWMUSIC.PAK", 0, "e53bca3a3e3fb49107d59463ec387a59", -1 }, + { NULL, 0, NULL, 0 } + }, Common::JA_JPN, Common::kPlatformFMTowns, Common::ADGF_NO_FLAGS @@ -330,6 +340,29 @@ const KYRAGameDescription adGameDescs[] = { KYRA2_DEMO_FLAGS }, + { // FM-Towns + { + "kyra2", + 0, + AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"), + Common::EN_ANY, + Common::kPlatformFMTowns, + Common::ADGF_NO_FLAGS + }, + KYRA2_TOWNS_FLAGS + }, + { + { + "kyra2", + 0, + AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"), + Common::JA_JPN, + Common::kPlatformFMTowns, + Common::ADGF_NO_FLAGS + }, + KYRA2_TOWNS_SJIS_FLAGS + }, + { { "kyra3", @@ -402,7 +435,7 @@ bool engineCreateKyra(OSystem *syst, Engine **engine, Common::EncapsulatedADGame bool res = true; Kyra::GameFlags flags = gd->flags; - + flags.lang = gd->desc.language; flags.platform = gd->desc.platform; @@ -440,3 +473,4 @@ ADVANCED_DETECTOR_DEFINE_PLUGIN(KYRA, engineCreateKyra, detectionParams); REGISTER_PLUGIN(KYRA, "Legend of Kyrandia Engine", "The Legend of Kyrandia (C) Westwood Studios"); + diff --git a/engines/kyra/kyra.cpp b/engines/kyra/kyra.cpp index f690fde3cd..c6f9bd5625 100644 --- a/engines/kyra/kyra.cpp +++ b/engines/kyra/kyra.cpp @@ -92,15 +92,31 @@ int KyraEngine::init() { // for now we prefer Adlib over native MIDI int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB/* | MDT_PREFER_MIDI*/); - if (_flags.platform == Common::kPlatformFMTowns) { - // TODO: later on here should be a usage of MixedSoundDriver - _sound = new SoundTowns(this, _mixer); - } else if (_flags.platform == Common::kPlatformPC98) { + if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) { // TODO: currently we don't support the PC98 sound data, // but since it has the FM-Towns data files, we just use the // FM-Towns driver - // TODO: later on here should be a usage of MixedSoundDriver - _sound = new SoundTowns(this, _mixer); + + // Since we handle the volume internally for our FM-Towns driver we set the global + // volume for those sound types to the maximum. + _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, Audio::Mixer::kMaxMixerVolume); + _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, Audio::Mixer::kMaxMixerVolume); + + if (_flags.gameID == GI_KYRA1) { + SoundTowns *snd = new SoundTowns(this, _mixer); + + snd->setMusicVolume(ConfMan.getInt("music_volume")); + snd->setSoundEffectsVolume(ConfMan.getInt("sfx_volume")); + + _sound = snd; + } else { + SoundTowns_v2 *snd = new SoundTowns_v2(this, _mixer); + + snd->setMusicVolume(ConfMan.getInt("music_volume")); + snd->setSoundEffectsVolume(ConfMan.getInt("sfx_volume")); + + _sound = snd; + } } else if (midiDriver == MD_ADLIB) { _sound = new SoundAdlibPC(this, _mixer); assert(_sound); @@ -232,3 +248,4 @@ void KyraEngine::delayWithTicks(int ticks) { } // End of namespace Kyra + diff --git a/engines/kyra/kyra_v2.cpp b/engines/kyra/kyra_v2.cpp index 5b79d4b485..ffb5caab0f 100644 --- a/engines/kyra/kyra_v2.cpp +++ b/engines/kyra/kyra_v2.cpp @@ -127,6 +127,9 @@ int KyraEngine_v2::init() { _screen->setAnimBlockPtr(3504); _screen->setScreenDim(0); + if (!_sound->init()) + error("Couldn't init sound"); + _abortIntroFlag = false; // temporary solution until staticres manager support (kyra.dat) is added for kyra 2 @@ -145,7 +148,7 @@ int KyraEngine_v2::init() { } for (int i = 0; i < 33; i++) - _sequenceStringsDuration[i] = strlen(_sequenceStrings[i]) * 8; + _sequenceStringsDuration[i] = (int) strlen(_sequenceStrings[i]) * 8; // No mouse display in demo if (_flags.isDemo) @@ -164,10 +167,8 @@ int KyraEngine_v2::init() { } int KyraEngine_v2::go() { - // Temporary measure to work around the fact that there's - // several WSA files with identical names in different PAK files. - _res->unloadPakFile("OUTFARM.PAK"); - _res->unloadPakFile("FLYTRAP.PAK"); + if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) + seq_showStarcraftLogo(); seq_playSequences(kSequenceVirgin, kSequenceZanfaun); //seq_playSequences(kSequenceFunters, kSequenceFrash); @@ -175,7 +176,26 @@ int KyraEngine_v2::go() { if (_menuChoice == 1) { // load just the pak files needed for ingame _res->unloadAllPakFiles(); - _res->loadFileList("FILEDATA.FDT"); + if (_flags.platform == Common::kPlatformPC && (_flags.isTalkie || _flags.isDemo)) { + _res->loadFileList("FILEDATA.FDT"); + } else if (_flags.platform == Common::kPlatformPC) { + //TODO + } else if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) { + char tmpfilename[13]; + static const char * pakfiles [] = { "KYRA.DAT", "AUDIO.PAK", "CAULDRON.PAK", + "MISC_CPS.PAK", "MISC_EMC.PAK", "OTHER.PAK", "VOC.PAK", "WSCORE.PAK" }; + for (int i = 0; i < 8; i++) + _res->loadPakFile(pakfiles[i]); + for (int i = 1; i < 10; i++) { + sprintf(tmpfilename, "COST%d_SH.PAK", i); + _res->loadPakFile(tmpfilename); + } + for (int i = 1; i < 6; i++) { + sprintf(tmpfilename, "HOFCH_%d.PAK", i); + _res->loadPakFile(tmpfilename); + } + } + startup(); runLoop(); cleanup(); @@ -187,7 +207,9 @@ int KyraEngine_v2::go() { } void KyraEngine_v2::startup() { - _sound->setSoundFileList(_dosSoundFileList, _dosSoundFileListSize); + snd_assignMusicData(kMusicIngame); + // The track map is exactly the same + // for FM-TOWNS and DOS _trackMap = _dosTrackMap; _trackMapSize = _dosTrackMapSize; @@ -306,7 +328,7 @@ void KyraEngine_v2::runLoop() { // waitTicks(5); // sub_270A0(); //} - + if (_system->getMillis() > _nextIdleAnim) showIdleAnim(); @@ -1540,6 +1562,19 @@ void KyraEngine_v2::snd_loadSoundFile(int id) { _sound->loadSoundFile(file); } +void KyraEngine_v2::snd_assignMusicData(kMusicDataID id) { + if (_flags.platform == Common::kPlatformPC) { + if (id == kMusicIntro) + _sound->setSoundFileList(_dosSoundFileListIntro, 1); + else if (id == kMusicFinale) + _sound->setSoundFileList(_dosSoundFileListFinale, 1); + else + _sound->setSoundFileList(_dosSoundFileList, _dosSoundFileListSize); + } else { + _sound->assignData(id); + } +} + void KyraEngine_v2::playVoice(int high, int low) { int vocFile = high * 10000 + low * 10; snd_playVoiceFile(vocFile); @@ -1632,7 +1667,7 @@ void KyraEngine_v2::updateInvWsa() { case 48: snd_playSoundEffect(0x38); break; - + default: break; } @@ -1904,4 +1939,3 @@ void KyraEngine_v2::setupOpcodeTable() { } // end of namespace Kyra - diff --git a/engines/kyra/kyra_v2.h b/engines/kyra/kyra_v2.h index 4ff2c3061b..fa11952087 100644 --- a/engines/kyra/kyra_v2.h +++ b/engines/kyra/kyra_v2.h @@ -74,6 +74,7 @@ enum kNestedSequences { kSequenceHand4 }; + class WSAMovieV2; class KyraEngine_v2; class TextDisplayer_v2; @@ -143,6 +144,12 @@ struct NestedSequence { uint16 unk1; }; +enum kMusicDataID { + kMusicIntro = 0, + kMusicIngame, + kMusicFinale +}; + class KyraEngine_v2 : public KyraEngine { friend class Debugger_v2; friend class TextDisplayer_v2; @@ -225,6 +232,8 @@ protected: void seq_printCreditsString(uint16 strIndex, int x, int y, uint8 * colorMap, uint8 textcolor); void seq_playWsaSyncDialogue(uint16 strIndex, uint16 vocIndex, int textColor, int x, int y, int width, WSAMovieV2 * wsa, int firstframe, int lastframe, int wsaXpos, int wsaYpos); + int seq_generateFixedRandomValue(int rangeFirst, int rangeLast); + void seq_showStarcraftLogo(); void seq_init(); void seq_uninit(); @@ -246,13 +255,6 @@ protected: static const int8 _dosTrackMap[]; static const int _dosTrackMapSize; - static const char *_introSoundList[]; - static const int _introSoundListSize; - static const char *_introStrings[]; - static const int _introStringsSize; - - int _introStringsDuration[21]; - protected: // game initialization void startup(); @@ -628,6 +630,7 @@ protected: virtual void snd_playVoiceFile(int id); void snd_loadSoundFile(int id); + void snd_assignMusicData(kMusicDataID id); void playVoice(int high, int low); @@ -639,7 +642,7 @@ protected: void timerFunc6(int); void setTimer1DelaySecs(int secs); - + uint32 _nextIdleAnim; int _lastIdleScript; @@ -846,6 +849,8 @@ protected: static const int _sequenceStringsSize_TOWNS_EN; static const char *_sequenceStrings_PC_EN[]; static const int _sequenceStringsSize_PC_EN; + static const char _actorScreenStrings_PC_EN[]; + static const int _actorScreenStringsSize_PC_EN; int _sequenceStringsDuration[33]; @@ -861,6 +866,7 @@ protected: int _seqFrameCounter; int _seqWsaCurrentFrame; bool _seqSpecialFlag; + int _seqRandomizeBase; bool _seqSubframePlaying; uint8 _seqTextColor[2]; uint8 _seqTextColorMap[16]; @@ -883,3 +889,4 @@ protected: #endif + diff --git a/engines/kyra/sequences_v2.cpp b/engines/kyra/sequences_v2.cpp index f6f92eb169..537885f990 100644 --- a/engines/kyra/sequences_v2.cpp +++ b/engines/kyra/sequences_v2.cpp @@ -53,9 +53,7 @@ void KyraEngine_v2::seq_playSequences(int startSeq, int endSeq) { }; _sound->setSoundFileList(soundFileList, 2); } else { - const char *const *soundFileList = - (startSeq > kSequenceZanfaun) ? _dosSoundFileListFinale : _dosSoundFileListIntro; - _sound->setSoundFileList(soundFileList, 1); + snd_assignMusicData((startSeq > kSequenceZanfaun) ? kMusicFinale : kMusicIntro); } _sound->loadSoundFile(0); @@ -1445,7 +1443,6 @@ int KyraEngine_v2::seq_finaleFirates(WSAMovieV2 *wsaObj, int x, int y, int frm) } int KyraEngine_v2::seq_finaleFrash(WSAMovieV2 *wsaObj, int x, int y, int frm) { - //uint32 endtime = 0; int tmp = 0; switch (frm) { @@ -1462,7 +1459,8 @@ int KyraEngine_v2::seq_finaleFrash(WSAMovieV2 *wsaObj, int x, int y, int frm) { case -1: // if (_flags.isTalkie) // seq_finaleActorScreen(); - _seqSpecialFlag = true; + _seqSpecialFlag = _flags.isTalkie; + _seqRandomizeBase = 1; break; case 0: @@ -1480,7 +1478,7 @@ int KyraEngine_v2::seq_finaleFrash(WSAMovieV2 *wsaObj, int x, int y, int frm) { if (_seqFrameCounter < 20 && _seqSpecialFlag) { _seqWsaCurrentFrame = 0; } else { - _seqFrameDelay = 500; + _seqFrameDelay = _flags.isTalkie ? 500 : (300 + seq_generateFixedRandomValue(1, 300)); seq_playTalkText(_flags.isTalkie ? 26 : 22); if (_seqSpecialFlag) { _seqFrameCounter = 3; @@ -1495,7 +1493,7 @@ int KyraEngine_v2::seq_finaleFrash(WSAMovieV2 *wsaObj, int x, int y, int frm) { case 3: seq_playTalkText(_flags.isTalkie ? 27 : 23); - _seqFrameDelay = 500; + _seqFrameDelay = _flags.isTalkie ? 500 : (300 + seq_generateFixedRandomValue(1, 300)); break; case 4: @@ -1506,9 +1504,9 @@ int KyraEngine_v2::seq_finaleFrash(WSAMovieV2 *wsaObj, int x, int y, int frm) { seq_playTalkText(_flags.isTalkie ? 27 : 23); tmp = _seqFrameCounter / 6; if (tmp == 2) - _seqFrameDelay = 7; + _seqFrameDelay = _flags.isTalkie ? 7 : (1 + seq_generateFixedRandomValue(1, 10)); else if (tmp < 2) - _seqFrameDelay = 500; + _seqFrameDelay = _flags.isTalkie ? 500 : (300 + seq_generateFixedRandomValue(1, 300)); break; case 6: @@ -1543,7 +1541,7 @@ void KyraEngine_v2::seq_finaleActorScreen() { _screen->loadBitmap("finale.cps", 3, 3, _screen->_currentPalette); _screen->setFont(Screen::FID_GOLDFONT_FNT); - _sound->setSoundFileList(_dosSoundFileList, _dosSoundFileListSize); + snd_assignMusicData(kMusicIngame); _sound->loadSoundFile(3); _sound->playTrack(3); @@ -1552,7 +1550,7 @@ void KyraEngine_v2::seq_finaleActorScreen() { // TODO - _sound->setSoundFileList(_dosSoundFileListFinale, 1); + snd_assignMusicData(kMusicFinale); _sound->loadSoundFile(0); } @@ -2098,7 +2096,7 @@ void KyraEngine_v2::seq_printCreditsString(uint16 strIndex, int x, int y, uint8 } void KyraEngine_v2::seq_playWsaSyncDialogue(uint16 strIndex, uint16 vocIndex, int textColor, int x, int y, int width, WSAMovieV2 * wsa, int firstframe, int lastframe, int wsaXpos, int wsaYpos) { - int dur = strlen(_sequenceStrings[strIndex]) * (_flags.isTalkie ? 7 : 15); + int dur = int(strlen(_sequenceStrings[strIndex])) * (_flags.isTalkie ? 7 : 15); int entry = seq_setTextEntry(strIndex, x, y, dur, width); _activeText[entry].textcolor = textColor; uint32 chatTimeout = _system->getMillis() + dur * _tickLength; @@ -2139,8 +2137,6 @@ void KyraEngine_v2::seq_playWsaSyncDialogue(uint16 strIndex, uint16 vocIndex, in curframe++; } - - if (lastframe < 0) { int t = ABS(lastframe); if (t < curframe) @@ -2153,11 +2149,81 @@ void KyraEngine_v2::seq_playWsaSyncDialogue(uint16 strIndex, uint16 vocIndex, in _seqWsaCurrentFrame = curframe; } +int KyraEngine_v2::seq_generateFixedRandomValue(int rangeFirst, int rangeLast) { + int result = 0; + if (rangeFirst > rangeFirst) + SWAP(rangeFirst, rangeLast); + int range = (rangeLast - rangeFirst) + 1; + + do { + _seqRandomizeBase = _seqRandomizeBase * 1103515245 + 12345; + result = ((range * ((_seqRandomizeBase % 0x7fffffff) & 0x7fff)) / 32768) + rangeFirst; + } while (rangeLast < result); + + return result; +} + +void KyraEngine_v2::seq_showStarcraftLogo() { + WSAMovieV2 * ci = new WSAMovieV2(this); + assert(ci); + _screen->clearPage(2); + _res->loadPakFile("INTROGEN.PAK"); + int endframe = ci->open("ci.wsa", 0, _screen->_currentPalette); + _res->unloadPakFile("INTROGEN.PAK"); + if (!ci->opened()) { + delete ci; + return; + } + _screen->hideMouse(); + ci->setX(0); + ci->setY(0); + ci->setDrawPage(2); + ci->displayFrame(0, 0); + _screen->copyPage(2, 0); + _screen->fadeFromBlack(); + for (int i = 1; i < endframe; i++) { + if (_skipFlag) + break; + ci->displayFrame(i, 0); + _screen->copyPage(2, 0); + _screen->updateScreen(); + delay(50); + } + if(!_skipFlag) { + ci->displayFrame(0, 0); + _screen->copyPage(2, 0); + _screen->updateScreen(); + delay(50); + } + _screen->fadeToBlack(); + _screen->showMouse(); + + _skipFlag = false; + delete ci; +} + void KyraEngine_v2::seq_init() { _seqProcessedString = new char[200]; _seqWsa = new WSAMovieV2(this); _activeWSA = new ActiveWSA[8]; _activeText = new ActiveText[10]; + + _res->unloadAllPakFiles(); + _res->loadPakFile("KYRA.DAT"); + _res->loadPakFile("AUDIO.PAK"); + _res->loadPakFile("INTROGEN.PAK"); + _res->loadPakFile("OTHER.PAK"); + _res->loadPakFile("VOC.PAK"); + if (_flags.isTalkie) { + _res->loadPakFile("TALKENG.PAK"); + _res->loadPakFile("TALKGER.PAK"); + _res->loadPakFile("TALKFRE.PAK"); + _res->loadPakFile("INTROTLK.PAK"); + } else { + _res->loadPakFile("INTROVOC.PAK"); + if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) + _res->loadPakFile("WSCORE.PAK"); + } } void KyraEngine_v2::seq_uninit() { @@ -2181,7 +2247,7 @@ void KyraEngine_v2::seq_uninit() { void KyraEngine_v2::seq_makeBookOrCauldronAppear(int type) { _screen->hideMouse(); showMessage(0, 0xCF); - + if (type == 1) { seq_makeBookAppear(); } else if (type == 2) { @@ -2190,7 +2256,7 @@ void KyraEngine_v2::seq_makeBookOrCauldronAppear(int type) { _screen->copyRegionToBuffer(2, 0, 0, 320, 200, _screenBuffer); _screen->loadBitmap("_PLAYALL.CPS", 3, 3, 0); - + static int16 bookCauldronRects[] = { 0x46, 0x90, 0x7F, 0x2B, // unknown rect (maybe unused?) 0xCE, 0x90, 0x2C, 0x2C, // book rect @@ -2215,16 +2281,16 @@ void KyraEngine_v2::seq_makeBookOrCauldronAppear(int type) { void KyraEngine_v2::seq_makeBookAppear() { _screen->hideMouse(); - + displayInvWsaLastFrame(); - + showMessage(0, 0xCF); loadInvWsa("BOOK2.WSA", 0, 4, 2, -1, -1, 0); - + uint8 *rect = new uint8[_screen->getRectSize(_invWsa.w, _invWsa.h)]; assert(rect); - + _screen->copyRegionToBuffer(_invWsa.page, _invWsa.x, _invWsa.y, _invWsa.w, _invWsa.h, rect); _invWsa.running = false; @@ -2236,19 +2302,19 @@ void KyraEngine_v2::seq_makeBookAppear() { while (true) { _invWsa.timer = _system->getMillis() + _invWsa.delay * _tickLength; - + _screen->copyBlockToPage(_invWsa.page, _invWsa.x, _invWsa.y, _invWsa.w, _invWsa.h, rect); _invWsa.wsa->displayFrame(_invWsa.curFrame, 0x4000, 0, 0); if (_invWsa.page) _screen->copyRegion(_invWsa.x, _invWsa.y, _invWsa.x, _invWsa.y, _invWsa.w, _invWsa.h, _invWsa.page, 0, Screen::CR_NO_P_CHECK); - + ++_invWsa.curFrame; if (_invWsa.curFrame >= _invWsa.lastFrame && !_quitFlag) break; - + switch (_invWsa.curFrame) { case 39: snd_playSoundEffect(0xCA); @@ -2384,3 +2450,4 @@ const SequenceControl KyraEngine_v2::_wsaControlHand4[] = { } // end of namespace Kyra + diff --git a/engines/kyra/sound.h b/engines/kyra/sound.h index 72ae61595b..9cf712e8ce 100644 --- a/engines/kyra/sound.h +++ b/engines/kyra/sound.h @@ -52,6 +52,7 @@ #include "sound/softsynth/ym2612.h" #include "kyra/kyra.h" +#include "kyra/kyra_v2.h" namespace Audio { class AudioStream; @@ -59,7 +60,7 @@ class AudioStream; namespace Kyra { -/** +/** * Analog audio output device API for Kyrandia games. * It countains functionallity to play music tracks, * sound effects and voices. @@ -109,6 +110,20 @@ public: virtual void setSoundFileList(const char * const *list, uint s) { _soundFileList = list; _soundFileListSize = s; } /** + * Selects preset bundles of music files + * and cd audio tracks the output device will use + * when playing a track and/or sound effect. + * + * TODO: this is just needed for Kyrandia 2 FM-Towns version, + * and is similar to what setSoundFileList is used for, so we + * should think of a better solution than this. + * @see setSoundFileList + * + * @param id kMusicIntro, kMusicIngame or kMusicFinale + */ + virtual void assignData(kMusicDataID id) { _currentTheme = id; } + + /** * Load a specifc sound file for use of * playing music and sound effects. */ @@ -161,7 +176,7 @@ public: * * @param file file to be played */ - void voicePlay(const char *file); + virtual void voicePlay(const char *file); /** * Checks if a voice is being played. @@ -180,6 +195,8 @@ protected: int _musicEnabled; bool _sfxEnabled; + int _currentTheme; + KyraEngine *_vm; Audio::Mixer *_mixer; @@ -213,7 +230,7 @@ class AdlibDriver; * Dune II, Kyrandia 1 and 2. While Dune II and * Kyrandia 1 are using exact the same format, the * one of Kyrandia 2 slightly differs. - * + * * See AdlibDriver for more information. * @see AdlibDriver */ @@ -268,7 +285,7 @@ private: * * Currently it does not initialize the MT-32 output properly, * so MT-32 output does sound a bit odd in some cases. - * + * * TODO: this code needs some serious cleanup and rework * to support MT-32 and GM properly. */ @@ -341,7 +358,7 @@ private: Common::Mutex _mutex; }; -class FMT_EuphonyDriver; +class SoundTowns_EuphonyDriver; class SoundTowns : public MidiDriver, public Sound { public: SoundTowns(KyraEngine *vm, Audio::Mixer *mixer); @@ -350,8 +367,14 @@ public: bool init(); void process(); - void setVolume(int) { /* TODO */ } - int getVolume() { return 255; /* TODO */ } + void setVolume(int) {} + int getVolume() { return 255; } + + // TODO: this should be moved to Sound or at least + // supplied by Sound as a pure virtual method. + // TODO: define ranges for those two functions + void setMusicVolume(int volume); + void setSoundEffectsVolume(int volume) { _sfxVolume = CLIP(volume, 0, 255); } void loadSoundFile(uint file); @@ -387,22 +410,67 @@ private: Audio::AudioStream *_currentSFX; Audio::SoundHandle _sfxHandle; - int _currentTrackTable; uint _sfxFileIndex; uint8 *_sfxFileData; + uint8 _sfxVolume; - FMT_EuphonyDriver * _driver; + SoundTowns_EuphonyDriver * _driver; MidiParser * _parser; - uint8 *_musicTrackData; Common::Mutex _mutex; - static const char *_sfxFiles[]; - static const int _sfxFilenum; static const uint8 _sfxBTTable[256]; const uint8 *_sfxWDTable; }; +//class SoundTowns_v2_TwnDriver; +class SoundTowns_v2 : public Sound { +public: + SoundTowns_v2(KyraEngine *vm, Audio::Mixer *mixer); + ~SoundTowns_v2(); + + bool init(); + void process(); + + void setVolume(int) {} + int getVolume() { return 255; } + + // TODO: this should be moved to Sound or at least + // supplied by Sound as a pure virtual method. + // TODO: define ranges for those two functions + void setMusicVolume(int volume); + void setSoundEffectsVolume(int volume) { _sfxVolume = CLIP(volume, 0, 255); } + + void loadSoundFile(uint file) {} + + void playTrack(uint8 track); + void haltTrack(); + void beginFadeOut(); + + void voicePlay(const char *file); + void playSoundEffect(uint8) {} + +private: + int _lastTrack; + + Audio::AudioStream *_currentSFX; + Audio::SoundHandle _sfxHandle; + uint8 _sfxVolume; + + //SoundTowns_v2_TwnDriver * _driver; + uint8 * _twnTrackData; + + static const uint8 _cdaTrackTableK2Intro[]; + static const uint8 _cdaTrackTableK2Ingame[]; + static const uint8 _cdaTrackTableK2Finale[]; + + static const struct Kyra2AudioThemes { + const uint8 * cdaTable; + const uint8 cdaTableSize; + const char * twnFilename; + } _themes[]; +}; + class MixedSoundDriver : public Sound { public: MixedSoundDriver(KyraEngine *vm, Audio::Mixer *mixer, Sound *music, Sound *sfx) : Sound(vm, mixer), _music(music), _sfx(sfx) {} diff --git a/engines/kyra/sound_towns.cpp b/engines/kyra/sound_towns.cpp index e0a0c2146b..d77bd87efc 100644 --- a/engines/kyra/sound_towns.cpp +++ b/engines/kyra/sound_towns.cpp @@ -33,11 +33,14 @@ #include "sound/audiostream.h" #include "common/util.h" + #include <math.h> +#define EUPHONY_FADEOUT_TICKS 600 + namespace Kyra { -enum EuD_ChannelState { _s_ready, _s_attacking, _s_decaying, _s_sustaining, _s_releasing }; +enum ChannelState { _s_ready, _s_attacking, _s_decaying, _s_sustaining, _s_releasing }; class MidiChannel_EuD : public MidiChannel { public: @@ -125,7 +128,7 @@ protected: const int8 *_samples; } * _snd[8]; struct Env { - EuD_ChannelState state; + ChannelState state; int32 currentLevel; int32 rate; int32 tickCount; @@ -142,16 +145,43 @@ protected: } * _voice; }; -class MidiParser_EuD : public MidiParser { +class SoundTowns_EuphonyTrackQueue { public: - MidiParser_EuD(); + SoundTowns_EuphonyTrackQueue(SoundTowns_EuphonyDriver *driver, SoundTowns_EuphonyTrackQueue *last); + ~SoundTowns_EuphonyTrackQueue() {} + + void release(); + void initDriver(); + void loadDataToCurrentPosition(uint8 * trackdata, uint32 size, bool loop = 0); + void loadDataToEndOfQueue(uint8 * trackdata, uint32 size, bool loop = 0); + void setPlayBackStatus(bool playing); + SoundTowns_EuphonyTrackQueue * reset(); + bool isPlaying() {return _playing; } + uint8 * trackData() {return _trackData; } + + bool _loop; + SoundTowns_EuphonyTrackQueue * _next; + +private: + uint8 * _trackData; + uint8 * _used; + uint8 * _fchan; + uint8 * _wchan; + bool _playing; + SoundTowns_EuphonyDriver * _driver; + SoundTowns_EuphonyTrackQueue * _last; +}; - bool loadMusic (byte *data, uint32 unused = 0); +class MidiParser_EuD : public MidiParser { +public: + MidiParser_EuD(SoundTowns_EuphonyTrackQueue * queue); + bool loadMusic (byte *data, uint32 size); int32 calculateTempo(int16 val); protected: void parseNextEvent (EventInfo &info); void resetTracking(); + void setup(); byte * _enable; byte * _mode; @@ -159,18 +189,19 @@ protected: byte * _adjVelo; int8 * _adjNote; - byte _tempo[3]; - uint8 _firstBaseTickStep; uint8 _nextBaseTickStep; uint32 _initialTempo; uint32 _baseTick; + + byte _tempo[3]; + SoundTowns_EuphonyTrackQueue * _queue; }; -class FMT_EuphonyDriver : public MidiDriver_Emulated { +class SoundTowns_EuphonyDriver : public MidiDriver_Emulated { public: - FMT_EuphonyDriver(Audio::Mixer *mixer); - virtual ~FMT_EuphonyDriver(); + SoundTowns_EuphonyDriver(Audio::Mixer *mixer); + virtual ~SoundTowns_EuphonyDriver(); int open(); void close(); @@ -179,10 +210,11 @@ public: uint32 property(int prop, uint32 param) { return 0; } void setPitchBendRange(byte channel, uint range) { } - //void sysEx(const byte *msg, uint16 length); void loadFmInstruments(const byte *instr); void loadWaveInstruments(const byte *instr); + SoundTowns_EuphonyTrackQueue * queue() { return _queue; } + MidiChannel *allocateChannel() { return 0; } MidiChannel *getPercussionChannel() { return 0; } @@ -190,6 +222,9 @@ public: void assignWaveChannel(uint8 midiChannelNumber, uint8 waveChannelNumber); void removeChannel(uint8 midiChannelNumber); + void setVolume(int val = -1) { if (val >= 0) _volume = val; } + int getVolume(int val = -1) { return _volume; } + // AudioStream API bool isStereo() const { return true; } int getRate() const { return _mixer->getOutputRate(); } @@ -198,7 +233,6 @@ public: protected: void nextTick(int16 *buf1, int buflen); - int volume(int val = -1) { if (val >= 0) _volume = val; return _volume; } void rate(uint16 r); void generateSamples(int16 *buf, int len); @@ -206,6 +240,7 @@ protected: MidiChannel_EuD_FM *_fChannel[6]; MidiChannel_EuD_WAVE *_wChannel[8]; MidiChannel_EuD * _channel[16]; + SoundTowns_EuphonyTrackQueue * _queue; int _volume; bool _fading; @@ -532,11 +567,11 @@ void MidiChannel_EuD_WAVE::velocity(int velo) { _velocity = velo; } -FMT_EuphonyDriver::FMT_EuphonyDriver(Audio::Mixer *mixer) -: MidiDriver_Emulated(mixer) { - +SoundTowns_EuphonyDriver::SoundTowns_EuphonyDriver(Audio::Mixer *mixer) + : MidiDriver_Emulated(mixer) { _volume = 255; - _fadestate = 300; + _fadestate = EUPHONY_FADEOUT_TICKS; + _queue = 0; MidiDriver_YM2612::createLookupTables(); @@ -551,9 +586,11 @@ FMT_EuphonyDriver::FMT_EuphonyDriver(Audio::Mixer *mixer) rate(getRate()); fading(0); + + _queue = new SoundTowns_EuphonyTrackQueue(this, 0); } -FMT_EuphonyDriver::~FMT_EuphonyDriver() { +SoundTowns_EuphonyDriver::~SoundTowns_EuphonyDriver() { for (int i = 0; i < 6; i++) delete _fChannel[i]; for (int i = 0; i < 8; i++) @@ -577,33 +614,37 @@ FMT_EuphonyDriver::~FMT_EuphonyDriver() { _waveSounds[i] = 0; } } + + if (_queue) { + _queue->release(); + delete _queue; + _queue = 0; + } } -int FMT_EuphonyDriver::open() { +int SoundTowns_EuphonyDriver::open() { if (_isOpen) return MERR_ALREADY_OPEN; - MidiDriver_Emulated::open(); _mixer->playInputStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, false, true); + return 0; } -void FMT_EuphonyDriver::close() { +void SoundTowns_EuphonyDriver::close() { if (!_isOpen) return; _isOpen = false; _mixer->stopHandle(_mixerSoundHandle); } -void FMT_EuphonyDriver::send(uint32 b) { +void SoundTowns_EuphonyDriver::send(uint32 b) { send(b & 0xF, b & 0xFFFFFFF0); } -void FMT_EuphonyDriver::send(byte chan, uint32 b) { - //byte param3 = (byte) ((b >> 24) & 0xFF); - +void SoundTowns_EuphonyDriver::send(byte chan, uint32 b) { byte param2 = (byte) ((b >> 16) & 0xFF); byte param1 = (byte) ((b >> 8) & 0xFF); byte cmd = (byte) (b & 0xF0); @@ -662,18 +703,18 @@ void FMT_EuphonyDriver::send(byte chan, uint32 b) { _channel[chan]->pitchBend((param1 | (param2 << 7)) - 0x2000); break; default: - warning("FMT_EuphonyDriver: Unknown send() command 0x%02X", cmd); + warning("SoundTowns_EuphonyDriver: Unknown send() command 0x%02X", cmd); } } -void FMT_EuphonyDriver::loadFmInstruments(const byte *instr) { +void SoundTowns_EuphonyDriver::loadFmInstruments(const byte *instr) { if (_fmInstruments) delete [] _fmInstruments; _fmInstruments = new uint8[0x1800]; memcpy(_fmInstruments, instr, 0x1800); } -void FMT_EuphonyDriver::loadWaveInstruments(const byte *instr) { +void SoundTowns_EuphonyDriver::loadWaveInstruments(const byte *instr) { if (_waveInstruments) delete [] _waveInstruments; _waveInstruments = new uint8[0x1000]; @@ -698,24 +739,24 @@ void FMT_EuphonyDriver::loadWaveInstruments(const byte *instr) { } -void FMT_EuphonyDriver::assignFmChannel(uint8 midiChannelNumber, uint8 fmChannelNumber) { +void SoundTowns_EuphonyDriver::assignFmChannel(uint8 midiChannelNumber, uint8 fmChannelNumber) { _channel[midiChannelNumber] = _fChannel[fmChannelNumber]; } -void FMT_EuphonyDriver::assignWaveChannel(uint8 midiChannelNumber, uint8 waveChannelNumber) { +void SoundTowns_EuphonyDriver::assignWaveChannel(uint8 midiChannelNumber, uint8 waveChannelNumber) { _channel[midiChannelNumber] = _wChannel[waveChannelNumber]; } -void FMT_EuphonyDriver::removeChannel(uint8 midiChannelNumber) { +void SoundTowns_EuphonyDriver::removeChannel(uint8 midiChannelNumber) { _channel[midiChannelNumber] = 0; } -void FMT_EuphonyDriver::generateSamples(int16 *data, int len) { +void SoundTowns_EuphonyDriver::generateSamples(int16 *data, int len) { memset(data, 0, 2 * sizeof(int16) * len); nextTick(data, len); } -void FMT_EuphonyDriver::nextTick(int16 *buf1, int buflen) { +void SoundTowns_EuphonyDriver::nextTick(int16 *buf1, int buflen) { int32 *buf0 = (int32 *)buf1; for (int i = 0; i < ARRAYSIZE(_channel); i++) { @@ -724,39 +765,63 @@ void FMT_EuphonyDriver::nextTick(int16 *buf1, int buflen) { } for (int i = 0; i < buflen; ++i) { - int s = int( float(buf0[i] * volume()) * float((float)_fadestate / 300) ); + int s = int( float(buf0[i] * _volume) * float((float)_fadestate / EUPHONY_FADEOUT_TICKS) ); buf1[i*2] = buf1[i*2+1] = (s >> 9) & 0xffff; } if (_fading) { - if (_fadestate) + if (_fadestate) { _fadestate--; - else + } else { _fading = false; + _queue->setPlayBackStatus(false); + } } } -void FMT_EuphonyDriver::rate(uint16 r) { +void SoundTowns_EuphonyDriver::rate(uint16 r) { for (uint8 i = 0; i < 16; i++) { if (_channel[i]) _channel[i]->rate(r); } } -void FMT_EuphonyDriver::fading(bool status) { +void SoundTowns_EuphonyDriver::fading(bool status) { _fading = status; if (!_fading) - _fadestate = 300; + _fadestate = EUPHONY_FADEOUT_TICKS; } -MidiParser_EuD::MidiParser_EuD() : MidiParser(), +MidiParser_EuD::MidiParser_EuD(SoundTowns_EuphonyTrackQueue * queue) : MidiParser(), _firstBaseTickStep(0x33), _nextBaseTickStep(0x33) { _initialTempo = calculateTempo(0x5a); + _queue = queue; } void MidiParser_EuD::parseNextEvent(EventInfo &info) { byte *pos = _position._play_pos; + if (_queue->_next) { + if (info.ext.type == 0x2F) { + unloadMusic(); + memset(&info, 0, sizeof(EventInfo)); + pos = _position._play_pos = _tracks[0] = _queue->trackData() + 0x806; + } else if (_active_track == 255) { + _queue = _queue->_next; + setup(); + setTrack(0); + _queue->setPlayBackStatus(true); + return; + } else if (!_queue->isPlaying()) { + unloadMusic(); + _queue = _queue->_next; + setup(); + setTrack(0); + _queue->setPlayBackStatus(true); + return; + } + } + while (true) { byte cmd = *pos; byte evt = (cmd & 0xF0); @@ -830,10 +895,13 @@ void MidiParser_EuD::parseNextEvent(EventInfo &info) { break; } else if (cmd == 0xFD || cmd == 0xFE) { // End of track. - if (_autoLoop) + if (_autoLoop) { + unloadMusic(); + _queue->setPlayBackStatus(true); pos = info.start = _tracks[0]; - else + } else { info.start = pos; + } uint32 last = _position._last_event_tick; uint16 tick = (pos[2] | ((uint16) pos[3] << 7)) + _baseTick; @@ -852,25 +920,20 @@ void MidiParser_EuD::parseNextEvent(EventInfo &info) { _position._play_pos = pos; } -bool MidiParser_EuD::loadMusic(byte *data, uint32) { - unloadMusic(); - - _enable = data + 0x354; - _mode = data + 0x374; - _channel = data + 0x394; - _adjVelo = data + 0x3B4; - _adjNote = (int8*) data + 0x3D4; - - _firstBaseTickStep = data[0x804]; - _initialTempo = calculateTempo((data[0x805] > 0xfc) ? 0x5a : data[0x805]); - - _num_tracks = 1; - _ppqn = 120; - _tracks[0] = data + 0x806; - - resetTracking(); - setTrack (0); +bool MidiParser_EuD::loadMusic(byte *data, uint32 size) { + bool loop = _autoLoop; + if (_queue->isPlaying() && !_queue->_loop) { + _queue->loadDataToEndOfQueue(data, size, loop); + } else { + unloadMusic(); + _queue = _queue->reset(); + _queue->release(); + _queue->loadDataToCurrentPosition(data, size, loop); + setup(); + setTrack(0); + _queue->setPlayBackStatus(true); + } return true; } @@ -892,15 +955,139 @@ int32 MidiParser_EuD::calculateTempo(int16 val) { void MidiParser_EuD::resetTracking() { MidiParser::resetTracking(); + _nextBaseTickStep = _firstBaseTickStep; _baseTick = 0; setTempo(_initialTempo); + _queue->setPlayBackStatus(false); +} + +void MidiParser_EuD::setup() { + uint8 *data = _queue->trackData(); + if (!data) + return; + _queue->initDriver(); + + _enable = data + 0x354; + _mode = data + 0x374; + _channel = data + 0x394; + _adjVelo = data + 0x3B4; + _adjNote = (int8*) data + 0x3D4; + + _nextBaseTickStep = _firstBaseTickStep = data[0x804]; + _initialTempo = calculateTempo((data[0x805] > 0xfc) ? 0x5a : data[0x805]); + + property(MidiParser::mpAutoLoop, _queue->_loop); + + _num_tracks = 1; + _ppqn = 120; + _tracks[0] = data + 0x806; +} + +SoundTowns_EuphonyTrackQueue::SoundTowns_EuphonyTrackQueue(SoundTowns_EuphonyDriver * driver, SoundTowns_EuphonyTrackQueue * last) { + _trackData = 0; + _next = 0; + _driver = driver; + _last = last; + _used = _fchan = _wchan = 0; + _playing = false; +} + +void SoundTowns_EuphonyTrackQueue::setPlayBackStatus(bool playing) { + SoundTowns_EuphonyTrackQueue * i = this; + do { + i->_playing = playing; + i = i->_next; + } while (i); +} + +SoundTowns_EuphonyTrackQueue * SoundTowns_EuphonyTrackQueue::reset() { + SoundTowns_EuphonyTrackQueue * i = this; + while (i->_last) + i = i->_last; + return i; +} + +void SoundTowns_EuphonyTrackQueue::loadDataToCurrentPosition(uint8 * trackdata, uint32 size, bool loop) { + if (_trackData) + delete [] _trackData; + _trackData = new uint8[0xC58A]; + memset(_trackData, 0, 0xC58A); + Screen::decodeFrame4(trackdata, _trackData, size); + + _used = _trackData + 0x374; + _fchan = _trackData + 0x6d4; + _wchan = _trackData + 0x6dA; + _loop = loop; + _playing = false; +} + +void SoundTowns_EuphonyTrackQueue::loadDataToEndOfQueue(uint8 * trackdata, uint32 size, bool loop) { + if (!_trackData) { + loadDataToCurrentPosition(trackdata, size, loop); + return; + } + + SoundTowns_EuphonyTrackQueue * i = this; + while (i->_next) + i = i->_next; + + i = i->_next = new SoundTowns_EuphonyTrackQueue(_driver, i); + i->_trackData = new uint8[0xC58A]; + memset(i->_trackData, 0, 0xC58A); + Screen::decodeFrame4(trackdata, i->_trackData, size); + + i->_used = i->_trackData + 0x374; + i->_fchan = i->_trackData + 0x6d4; + i->_wchan = i->_trackData + 0x6dA; + i->_loop = loop; + i->_playing = _playing; +} + +void SoundTowns_EuphonyTrackQueue::release() { + SoundTowns_EuphonyTrackQueue * i = _next; + _next = 0; + _playing = false; + _used = _fchan = _wchan = 0; + + if (_trackData) { + delete [] _trackData; + _trackData = 0; + } + + while (i) { + if (i->_trackData) { + delete [] i->_trackData; + i->_trackData = 0; + } + i = i->_next; + if (i) + delete i->_last; + } +} + +void SoundTowns_EuphonyTrackQueue::initDriver() { + for (uint8 i = 0; i < 6; i++) { + if (_used[_fchan[i]]) + _driver->assignFmChannel(_fchan[i], i); + } + + for (uint8 i = 0; i < 8; i++) { + if (_used[_wchan[i]]) + _driver->assignWaveChannel(_wchan[i], i); + } + + for (uint8 i = 0; i < 16; i++) { + if (!_used[i]) + _driver->removeChannel(i); + } + _driver->send(0x79B0); } SoundTowns::SoundTowns(KyraEngine *vm, Audio::Mixer *mixer) : Sound(vm, mixer), _lastTrack(-1), - _currentSFX(0), _sfxFileData(0), _sfxFileIndex((uint)-1), _sfxWDTable(0), _parser(0), _musicTrackData(0) { + _currentSFX(0), _sfxFileData(0), _sfxFileIndex((uint)-1), _sfxWDTable(0), _parser(0), _sfxVolume(255) { - _driver = new FMT_EuphonyDriver(_mixer); + _driver = new SoundTowns_EuphonyDriver(_mixer); int ret = open(); if (ret != MERR_ALREADY_OPEN && ret != 0) { error("couldn't open midi driver"); @@ -916,9 +1103,6 @@ SoundTowns::~SoundTowns() { _driver->setTimerCallback(0, 0); close(); - if (_musicTrackData) - delete [] _musicTrackData; - _driver = 0; } @@ -950,65 +1134,26 @@ void SoundTowns::playTrack(uint8 track) { track -= 2; static const CDTrackTable tTable[] = { - { 0x04000, 1, 0 }, - { 0x05480, 1, 6 }, - { 0x05E70, 0, 1 }, - { 0x06D90, 1, 3 }, - { 0x072C0, 0, -1 }, - { 0x075F0, 1, -1 }, - { 0x07880, 1, -1 }, - { 0x089C0, 0, -1 }, - { 0x09080, 0, -1 }, - { 0x091D0, 1, 4 }, - { 0x0A880, 1, 5 }, - { 0x0AF50, 0, -1 }, - { 0x0B1A0, 1, -1 }, - { 0x0B870, 0, -1 }, - { 0x0BCF0, 1, -1 }, - { 0x0C5D0, 1, 7 }, - { 0x0D3E0, 1, 8 }, - { 0x0e7b0, 1, 2 }, - { 0x0edc0, 0, -1 }, - { 0x0eef0, 1, 9 }, - { 0x10540, 1, 10 }, - { 0x10d80, 0, -1 }, - { 0x10E30, 0, -1 }, - { 0x10FC0, 0, -1 }, - { 0x11310, 1, -1 }, - { 0x11A20, 1, -1 }, - { 0x12380, 0, -1 }, - { 0x12540, 1, -1 }, - { 0x12730, 1, -1 }, - { 0x12A90, 1, 11 }, - { 0x134D0, 0, -1 }, - { 0x00000, 0, -1 }, - { 0x13770, 0, -1 }, - { 0x00000, 0, -1 }, - { 0x00000, 0, -1 }, - { 0x00000, 0, -1 }, - { 0x00000, 0, -1 }, - { 0x14710, 1, 12 }, - { 0x15DF0, 1, 13 }, - { 0x16030, 1, 14 }, - { 0x17030, 0, -1 }, - { 0x17650, 0, -1 }, - { 0x134D0, 0, -1 }, - { 0x178E0, 1, -1 }, - { 0x18200, 0, -1 }, - { 0x18320, 0, -1 }, - { 0x184A0, 0, -1 }, - { 0x18BB0, 0, -1 }, - { 0x19040, 0, 19 }, - { 0x19B50, 0, 20 }, - { 0x17650, 0, -1 }, - { 0x1A730, 1, 21 }, - { 0x00000, 0, -1 }, - { 0x12380, 0, -1 }, - { 0x1B810, 0, -1 }, - { 0x1BA50, 0, 15 }, - { 0x1C190, 0, 16 }, - { 0x1CA50, 0, 17 }, - { 0x1D100, 0, 18 }, + { 0x04000, 1, 0 }, { 0x05480, 1, 6 }, { 0x05E70, 0, 1 }, + { 0x06D90, 1, 3 }, { 0x072C0, 0, -1 }, { 0x075F0, 1, -1 }, + { 0x07880, 1, -1 }, { 0x089C0, 0, -1 }, { 0x09080, 0, -1 }, + { 0x091D0, 1, 4 }, { 0x0A880, 1, 5 }, { 0x0AF50, 0, -1 }, + { 0x0B1A0, 1, -1 }, { 0x0B870, 0, -1 }, { 0x0BCF0, 1, -1 }, + { 0x0C5D0, 1, 7 }, { 0x0D3E0, 1, 8 }, { 0x0e7b0, 1, 2 }, + { 0x0edc0, 0, -1 }, { 0x0eef0, 1, 9 }, { 0x10540, 1, 10 }, + { 0x10d80, 0, -1 }, { 0x10E30, 0, -1 }, { 0x10FC0, 0, -1 }, + { 0x11310, 1, -1 }, { 0x11A20, 1, -1 }, { 0x12380, 0, -1 }, + { 0x12540, 1, -1 }, { 0x12730, 1, -1 }, { 0x12A90, 1, 11 }, + { 0x134D0, 0, -1 }, { 0x00000, 0, -1 }, { 0x13770, 0, -1 }, + { 0x00000, 0, -1 }, { 0x00000, 0, -1 }, { 0x00000, 0, -1 }, + { 0x00000, 0, -1 }, { 0x14710, 1, 12 }, { 0x15DF0, 1, 13 }, + { 0x16030, 1, 14 }, { 0x17030, 0, -1 }, { 0x17650, 0, -1 }, + { 0x134D0, 0, -1 }, { 0x178E0, 1, -1 }, { 0x18200, 0, -1 }, + { 0x18320, 0, -1 }, { 0x184A0, 0, -1 }, { 0x18BB0, 0, -1 }, + { 0x19040, 0, 19 }, { 0x19B50, 0, 20 }, { 0x17650, 0, -1 }, + { 0x1A730, 1, 21 }, { 0x00000, 0, -1 }, { 0x12380, 0, -1 }, + { 0x1B810, 0, -1 }, { 0x1BA50, 0, 15 }, { 0x1C190, 0, 16 }, + { 0x1CA50, 0, 17 }, { 0x1D100, 0, 18 } }; int trackNum = tTable[track].track; @@ -1017,7 +1162,7 @@ void SoundTowns::playTrack(uint8 track) { if (track == _lastTrack && _musicEnabled) return; - haltTrack(); + beginFadeOut(); if (_musicEnabled == 2 && trackNum != -1) { AudioCD.play(trackNum+1, loop ? -1 : 1, 0, 0); @@ -1035,16 +1180,21 @@ void SoundTowns::haltTrack() { AudioCD.updateCD(); if (_parser) { Common::StackLock lock(_mutex); - _parser->setTrack(0); _parser->jumpToTick(0); - _parser->unloadMusic(); delete _parser; _parser = 0; - setVolume(255); } + _driver->queue()->release(); +} + +void SoundTowns::setMusicVolume(int volume) { + volume = CLIP<int>(volume, 0, Audio::Mixer::kMaxMixerVolume); + + _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, volume); + _driver->setVolume(255); } void SoundTowns::loadSoundFile(uint file) { @@ -1143,7 +1293,7 @@ void SoundTowns::playSoundEffect(uint8 track) { _currentSFX = Audio::makeLinearInputStream(sfxPlaybackBuffer, playbackBufferSize, outputRate, Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_LITTLE_ENDIAN | Audio::Mixer::FLAG_AUTOFREE, 0, 0); - _mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_sfxHandle, _currentSFX); + _mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_sfxHandle, _currentSFX, -1, _sfxVolume); } void SoundTowns::beginFadeOut() { @@ -1181,65 +1331,33 @@ uint32 SoundTowns::getBaseTempo(void) { } bool SoundTowns::loadInstruments() { - if (!_musicTrackData) - _musicTrackData = new uint8[0xC58A]; - - memset(_musicTrackData, 0, 0xC58A); uint8 * twm = _vm->resource()->fileData("twmusic.pak", 0); if (!twm) return false; - Screen::decodeFrame4(twm, _musicTrackData, 0x8BF0); - _driver->loadFmInstruments(_musicTrackData + 8); + _driver->queue()->loadDataToCurrentPosition(twm, 0x8BF0); + _driver->loadFmInstruments(_driver->queue()->trackData() + 8); - memset (_musicTrackData, 0, 0xC58A); - Screen::decodeFrame4(twm + 0x0CA0, _musicTrackData, 0xC58A); + _driver->queue()->loadDataToCurrentPosition(twm + 0x0CA0, 0xC58A); + _driver->loadWaveInstruments(_driver->queue()->trackData() + 8); delete [] twm; - _driver->loadWaveInstruments(_musicTrackData + 8); + _driver->queue()->release(); return true; } void SoundTowns::playEuphonyTrack(uint32 offset, int loop) { - if (!_musicTrackData) - _musicTrackData = new uint8[0xC58A]; - - memset(_musicTrackData, 0, 0xC58A); uint8 * twm = _vm->resource()->fileData("twmusic.pak", 0); - Screen::decodeFrame4(twm + 0x4b70 + offset, _musicTrackData, 0xC58A); - delete [] twm; - - Common::StackLock lock(_mutex); - - uint8 * used = _musicTrackData + 0x374; - uint8 * fchan = _musicTrackData + 0x6d4; - uint8 * wchan = _musicTrackData + 0x6dA; - - for (uint8 i = 0; i < 6; i++) { - if (used[fchan[i]]) - _driver->assignFmChannel(fchan[i], i); - } - - for (uint8 i = 0; i < 8; i++) { - if (used[wchan[i]]) - _driver->assignWaveChannel(wchan[i], i); - } - for (uint8 i = 0; i < 16; i++) { - if (!used[i]) - _driver->removeChannel(i); + if (!_parser) { + _parser = new MidiParser_EuD(_driver->queue()); + _parser->setMidiDriver(this); + _parser->setTimerRate(getBaseTempo()); } - _driver->send(0x79B0); - if (_parser) - delete _parser; - - _parser = new MidiParser_EuD; _parser->property(MidiParser::mpAutoLoop, loop); - _parser->loadMusic(_musicTrackData, 0); - _parser->jumpToTick(0); + _parser->loadMusic(twm + 0x4b70 + offset, 0xC58A); - _parser->setMidiDriver(this); - _parser->setTimerRate(getBaseTempo()); + delete [] twm; } void SoundTowns::onTimer(void * data) { @@ -1302,5 +1420,165 @@ const uint8 SoundTowns::_sfxBTTable[256] = { 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 }; +// KYRA 2 + +SoundTowns_v2::SoundTowns_v2(KyraEngine *vm, Audio::Mixer *mixer) : + Sound(vm, mixer), _lastTrack(-1), _currentSFX(0), /*_driver(0),*/ + _twnTrackData(0), _sfxVolume(255) { +} + +SoundTowns_v2::~SoundTowns_v2() { + /*if (_driver) + delete _driver;*/ + if (_twnTrackData) + delete [] _twnTrackData; +} + +bool SoundTowns_v2::init() { + //_driver = new SoundTowns_v2_TwnDriver(_mixer); + _vm->checkCD(); + Common::File f; + if (_musicEnabled && (f.exists("track1.mp3") || + f.exists("track1.ogg") || f.exists("track1.flac") || f.exists("track1.fla"))) + _musicEnabled = 2; + return true;//_driver->init(); +} + +void SoundTowns_v2::process() { + AudioCD.updateCD(); +} + +void SoundTowns_v2::setMusicVolume(int volume) { + /* TODO */ +} + +void SoundTowns_v2::playTrack(uint8 track) { + if (track == _lastTrack && _musicEnabled) + return; + + int trackNum = -1; + for (int i = 0; i < _themes[_currentTheme].cdaTableSize; i++) { + if (track == _themes[_currentTheme].cdaTable[i * 2]) { + trackNum = _themes[_currentTheme].cdaTable[i * 2 + 1] - 1; + break; + } + } + + haltTrack(); + + // TODO: figure out when to loop and when not for CD Audio + bool loop = false; + + if (_musicEnabled == 2 && trackNum != -1) { + AudioCD.play(trackNum+1, loop ? -1 : 1, 0, 0); + AudioCD.updateCD(); + } else if (_musicEnabled) { + char musicfile[13]; + sprintf(musicfile, "%s%d.twn", _themes[_currentTheme].twnFilename, track); + if (_twnTrackData) + delete [] _twnTrackData; + _twnTrackData = _vm->resource()->fileData(musicfile, 0); + //_driver->loadData(_twnTrackData); + } + + _lastTrack = track; +} + +void SoundTowns_v2::haltTrack() { + _lastTrack = -1; + AudioCD.stop(); + AudioCD.updateCD(); + //_driver->reset(); +} + +void SoundTowns_v2::voicePlay(const char *file) { + static const uint16 rates[] = { 0x10E1, 0x0CA9, 0x0870, 0x0654, 0x0438, 0x032A, 0x021C, 0x0194 }; + + uint8 * data = _vm->resource()->fileData(file, 0); + uint8 * src = data; + + uint16 sfxRate = rates[READ_LE_UINT16(src)]; + src += 2; + bool compressed = (READ_LE_UINT16(src) & 1) ? true : false; + src += 2; + uint32 outsize = READ_LE_UINT32(src); + uint8 *sfx = (uint8*) malloc(outsize); + uint8 *dst = sfx; + src += 4; + + if (compressed) { + for (uint32 i = outsize; i;) { + uint8 cnt = *src++; + if (cnt & 0x80) { + cnt &= 0x7F; + memset(dst, *src++, cnt); + } else { + memcpy(dst, src, cnt); + src += cnt; + } + dst += cnt; + i -= cnt; + } + } else { + memcpy(dst, src, outsize); + } + + for (uint32 i = 0; i < outsize; i++) { + uint8 cmd = sfx[i]; + if (cmd & 0x80) { + cmd = ~cmd; + } else { + cmd |= 0x80; + if (cmd == 0xff) + cmd--; + } + if (cmd < 0x80) + cmd = 0x80 - cmd; + sfx[i] = cmd; + } + + uint32 outputRate = uint32(11025 * SoundTowns::semitoneAndSampleRate_to_sampleStep(0x3c, 0x3c, sfxRate, 11025, 0x2000)); + + _currentSFX = Audio::makeLinearInputStream(sfx, outsize, outputRate, + Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_LITTLE_ENDIAN | Audio::Mixer::FLAG_AUTOFREE, 0, 0); + _mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_sfxHandle, _currentSFX, -1, _sfxVolume); + + delete [] data; +} + +void SoundTowns_v2::beginFadeOut() { + //_driver->fadeOut(); + haltTrack(); +} + +const uint8 SoundTowns_v2::_cdaTrackTableK2Intro[] = { + 0x03, 0x01, 0x04, 0x02, 0x05, 0x03, 0x06, 0x04, 0x07, 0x05, 0x08, 0x06 +}; + +const uint8 SoundTowns_v2::_cdaTrackTableK2Ingame[] = { + 0x02, 0x07, 0x03, 0x08, 0x04, 0x09, 0x07, 0x0A, 0x0C, 0x0B, 0x0D, 0x0C, 0x0E, 0x0D, 0x0F, 0x0E, + 0x10, 0x0F, 0x12, 0x10, 0x13, 0x11, 0x15, 0x12, 0x17, 0x13, 0x18, 0x14, 0x19, 0x15, 0x1A, 0x16, + 0x1B, 0x17, 0x1C, 0x18, 0x1D, 0x19, 0x1E, 0x1A, 0x1F, 0x1B, 0x21, 0x1C, 0x22, 0x1D, 0x23, 0x1E, + 0x24, 0x1F, 0x25, 0x20, 0x26, 0x21, 0x27, 0x22, 0x28, 0x23, 0x29, 0x24, 0x2A, 0x25, 0x2B, 0x26, + 0x2C, 0x27, 0x2D, 0x28, 0x2E, 0x29, 0x2F, 0x2A, 0x30, 0x2B, 0x31, 0x2C, 0x32, 0x2D, 0x33, 0x2E, + 0x34, 0x2F, 0x35, 0x30, 0x36, 0x31, 0x37, 0x32, 0x38, 0x33, 0x39, 0x34, 0x3A, 0x35, 0x3B, 0x36, + 0x3C, 0x37, 0x3D, 0x38, 0x3E, 0x39, 0x3F, 0x3A, 0x40, 0x3B, 0x41, 0x3C, 0x42, 0x3D, 0x43, 0x3E, + 0x44, 0x3F, 0x45, 0x40, 0x46, 0x41, 0x47, 0x42, 0x48, 0x43, 0x49, 0x44, 0x4A, 0x45, 0x4B, 0x46, + 0x4C, 0x47, 0x4D, 0x48, 0x4E, 0x49, 0x4F, 0x4A, 0x50, 0x4B, 0x51, 0x4C, 0x52, 0x4D, 0x53, 0x4E, + 0x54, 0x4F, 0x55, 0x50, 0x56, 0x51, 0x57, 0x52 +}; + +const uint8 SoundTowns_v2::_cdaTrackTableK2Finale[] = { + 0x03, 0x53, 0x04, 0x54 +}; + +const SoundTowns_v2::Kyra2AudioThemes SoundTowns_v2::_themes[] = { + { _cdaTrackTableK2Intro, ARRAYSIZE(_cdaTrackTableK2Intro) >> 1, "intro" }, + { _cdaTrackTableK2Ingame, ARRAYSIZE(_cdaTrackTableK2Ingame) >> 1, "k2" }, + { _cdaTrackTableK2Finale, ARRAYSIZE(_cdaTrackTableK2Finale) >> 1, "finale" } +}; + } // end of namespace Kyra +#undef EUPHONY_FADEOUT_TICKS + |