aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/kyra/detection.cpp40
-rw-r--r--engines/kyra/kyra.cpp29
-rw-r--r--engines/kyra/kyra_v2.cpp54
-rw-r--r--engines/kyra/kyra_v2.h23
-rw-r--r--engines/kyra/sequences_v2.cpp113
-rw-r--r--engines/kyra/sound.h92
-rw-r--r--engines/kyra/sound_towns.cpp620
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
+