diff options
Diffstat (limited to 'engines/cruise')
-rw-r--r-- | engines/cruise/cruise.cpp | 21 | ||||
-rw-r--r-- | engines/cruise/cruise.h | 11 | ||||
-rw-r--r-- | engines/cruise/cruise_main.cpp | 26 | ||||
-rw-r--r-- | engines/cruise/dataLoader.cpp | 42 | ||||
-rw-r--r-- | engines/cruise/dataLoader.h | 1 | ||||
-rw-r--r-- | engines/cruise/detection.cpp | 3 | ||||
-rw-r--r-- | engines/cruise/function.cpp | 100 | ||||
-rw-r--r-- | engines/cruise/menu.cpp | 3 | ||||
-rw-r--r-- | engines/cruise/saveload.cpp | 7 | ||||
-rw-r--r-- | engines/cruise/sound.cpp | 913 | ||||
-rw-r--r-- | engines/cruise/sound.h | 89 | ||||
-rw-r--r-- | engines/cruise/vars.cpp | 2 | ||||
-rw-r--r-- | engines/cruise/vars.h | 2 | ||||
-rw-r--r-- | engines/cruise/volume.cpp | 37 |
14 files changed, 963 insertions, 294 deletions
diff --git a/engines/cruise/cruise.cpp b/engines/cruise/cruise.cpp index 734f4b95c5..4656704cb8 100644 --- a/engines/cruise/cruise.cpp +++ b/engines/cruise/cruise.cpp @@ -52,26 +52,24 @@ CruiseEngine::CruiseEngine(OSystem * syst, const CRUISEGameDescription *gameDesc _currentVolumeFile = new Common::File(); #endif - Common::addDebugChannel(kCruiseDebugScript, "Script", - "Script debug level"); + Common::addDebugChannel(kCruiseDebugScript, "scripts", "Scripts debug level"); + Common::addDebugChannel(kCruiseDebugSound, "sound", "Sound debug level"); // Setup mixer _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, - ConfMan.getInt("sfx_volume")); + ConfMan.getInt("sfx_volume")); _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, - ConfMan.getInt("music_volume")); + ConfMan.getInt("music_volume")); _vm = this; _debugger = new Debugger(); - _music = new MusicPlayer(); - _sound = new SoundPlayer(); + _sound = new PCSound(_mixer, this); syst->getEventManager()->registerRandomSource(_rnd, "cruise"); } CruiseEngine::~CruiseEngine() { delete _debugger; - delete _music; delete _sound; freeSystem(); @@ -126,9 +124,8 @@ void CruiseEngine::initialize() { readVolCnf(); // Setup mixer - _musicVolume = ConfMan.getInt("music_volume"); - _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume")); - _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume")); +// _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume")); +// _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume")); int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI); _mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32")); @@ -230,4 +227,8 @@ const char *CruiseEngine::getSavegameFile(int saveGameIdx) { return buffer; } +void CruiseEngine::syncSoundSettings() { + _sound->syncSounds(); +} + } // End of namespace Cruise diff --git a/engines/cruise/cruise.h b/engines/cruise/cruise.h index 2fc69acea0..af098b4997 100644 --- a/engines/cruise/cruise.h +++ b/engines/cruise/cruise.h @@ -57,10 +57,8 @@ private: bool _preLoad; Debugger *_debugger; MidiDriver *_driver; - MusicPlayer *_music; - SoundPlayer *_sound; + PCSound *_sound; bool _mt32, _adlib; - int _musicVolume; Common::StringList _langStrings; CursorType _savedCursor; uint32 lastTick, lastTickDebug; @@ -90,8 +88,7 @@ public: uint32 getFeatures() const; Common::Language getLanguage() const; Common::Platform getPlatform() const; - MusicPlayer &music() { return *_music; } - SoundPlayer &sound() { return *_sound; } + PCSound &sound() { return *_sound; } bool mt32() const { return _mt32; } bool adlib() const { return _adlib; } virtual GUI::Debugger *getDebugger() { return _debugger; } @@ -103,6 +100,7 @@ public: virtual bool canLoadGameStateCurrently(); virtual Common::Error saveGameState(int slot, const char *desc); virtual bool canSaveGameStateCurrently(); + virtual void syncSoundSettings(); const CRUISEGameDescription *_gameDescription; void initAllData(void); @@ -128,7 +126,8 @@ enum { }; enum { - kCruiseDebugScript = 1 << 0 + kCruiseDebugScript = 1 << 0, + kCruiseDebugSound = 1 << 1 }; enum { diff --git a/engines/cruise/cruise_main.cpp b/engines/cruise/cruise_main.cpp index 136aa92b81..938e823fe9 100644 --- a/engines/cruise/cruise_main.cpp +++ b/engines/cruise/cruise_main.cpp @@ -81,12 +81,11 @@ int getNumObjectsByClass(int scriptIdx, int param) { return (counter); } -void resetFileEntryRange(int param1, int param2) { +void resetFileEntryRange(int start, int count) { int i; - for (i = param1; i < param2; i++) { - resetFileEntry(i); - } + for (i = 0; i < count; ++i) + resetFileEntry(start + i); } int getProcParam(int overlayIdx, int param2, const char *name) { @@ -294,15 +293,15 @@ int loadFileSub1(uint8 **ptr, const char *name, uint8 *ptr2) { if (!strcmp(buffer, ".SPL")) { removeExtention(name, buffer); - // if (useH32) - { - strcat(buffer, ".H32"); - } - /* else + /* if (useH32) + *{ + * strcat(buffer, ".H32"); + *} + * else * if (useAdlib) - * { - * strcatuint8(buffer,".ADL"); - * } + * { */ + strcat(buffer,".ADL"); + /* } * else * { * strcatuint8(buffer,".HP"); @@ -1739,6 +1738,9 @@ void CruiseEngine::mainLoop(void) { int quitValue2 = 1; int quitValue = 0; + if (ConfMan.hasKey("save_slot")) + loadGameState(ConfMan.getInt("save_slot")); + do { // Handle frame delay uint32 currentTick = g_system->getMillis(); diff --git a/engines/cruise/dataLoader.cpp b/engines/cruise/dataLoader.cpp index d725e1bb89..3cf3957703 100644 --- a/engines/cruise/dataLoader.cpp +++ b/engines/cruise/dataLoader.cpp @@ -257,13 +257,15 @@ int loadFile(const char* name, int idx, int destIdx) { return loadFNTSub(ptr, idx); break; } - case type_UNK: { - break; - } case type_SPL: { + // Sound file + loadSPLSub(ptr, idx); break; } + default: + error("Unknown fileType in loadFile"); } + return -1; } @@ -293,13 +295,15 @@ int loadFileRange(const char *name, int startIdx, int currentEntryIdx, int numId loadFNTSub(ptr, startIdx); break; } - case type_UNK: { - break; - } case type_SPL: { + // Sound file + loadSPLSub(ptr, startIdx); break; } + default: + error("Unknown fileType in loadFileRange"); } + return 0; } @@ -316,6 +320,7 @@ int loadFullBundle(const char *name, int startIdx) { switch (fileType) { case type_SET: { + // Sprite set int i; int numMaxEntriesInSet; @@ -328,15 +333,17 @@ int loadFullBundle(const char *name, int startIdx) { break; } case type_FNT: { + // Font file loadFNTSub(ptr, startIdx); break; } - case type_UNK: { - break; - } case type_SPL: { + // Sound file + loadSPLSub(ptr, startIdx); break; } + default: + error("Unknown fileType in loadFullBundle"); } return 0; @@ -387,6 +394,23 @@ int loadFNTSub(uint8 *ptr, int destIdx) { return 1; } +int loadSPLSub(uint8 *ptr, int destIdx) { + uint8 *destPtr; + int fileIndex; + + if (destIdx == -1) { + fileIndex = createResFileEntry(loadFileVar1, 1, loadFileVar1, 1); + } else { + fileIndex = updateResFileEntry(loadFileVar1, 1, loadFileVar1, destIdx, 1); + } + + destPtr = filesDatabase[fileIndex].subData.ptr; + memcpy(destPtr, ptr, loadFileVar1); + + return 1; +} + + int loadSetEntry(const char *name, uint8 *ptr, int currentEntryIdx, int currentDestEntry) { uint8 *ptr3; int offset; diff --git a/engines/cruise/dataLoader.h b/engines/cruise/dataLoader.h index 2e433acfcb..f6bea29cde 100644 --- a/engines/cruise/dataLoader.h +++ b/engines/cruise/dataLoader.h @@ -29,6 +29,7 @@ namespace Cruise { int loadFNTSub(uint8 *ptr, int destIdx); +int loadSPLSub(uint8 *ptr, int destIdx); int loadSetEntry(const char *name, uint8 *ptr, int currentEntryIdx, int currentDestEntry); int loadFile(const char* name, int idx, int destIdx); int loadData(const char * name, int startIdx); diff --git a/engines/cruise/detection.cpp b/engines/cruise/detection.cpp index a0a8cce970..879fad0210 100644 --- a/engines/cruise/detection.cpp +++ b/engines/cruise/detection.cpp @@ -209,7 +209,8 @@ bool CruiseMetaEngine::hasFeature(MetaEngineFeature f) const { (f == kSupportsListSaves) || (f == kSupportsDeleteSave) || (f == kSavesSupportMetaInfo) || - (f == kSavesSupportThumbnail); + (f == kSavesSupportThumbnail) || + (f == kSupportsLoadingDuringStartup); } SaveStateList CruiseMetaEngine::listSaves(const char *target) const { diff --git a/engines/cruise/function.cpp b/engines/cruise/function.cpp index 7789cb6fb1..eab69c6846 100644 --- a/engines/cruise/function.cpp +++ b/engines/cruise/function.cpp @@ -32,6 +32,10 @@ namespace Cruise { +uint32 Period(uint32 hz) { + return ((uint32)(100000000L / ((uint32)hz * 28L))); +} + //#define FUNCTION_DEBUG int16 Op_LoadOverlay(void) { @@ -198,15 +202,15 @@ int16 Op_Random(void) { int16 Op_PlayFX(void) { int volume = popVar(); int speed = popVar(); - int channelNum = popVar(); + /*int channelNum = */popVar(); int sampleNum = popVar(); if ((sampleNum >= 0) && (sampleNum < NUM_FILE_ENTRIES) && (filesDatabase[sampleNum].subData.ptr)) { if (speed == -1) speed = filesDatabase[sampleNum].subData.transparency; - _vm->sound().startSound(channelNum, filesDatabase[sampleNum].subData.ptr, - filesDatabase[sampleNum].width, speed, volume, false); + _vm->sound().playSound(filesDatabase[sampleNum].subData.ptr, + filesDatabase[sampleNum].width, volume); } return (0); @@ -215,15 +219,15 @@ int16 Op_PlayFX(void) { int16 Op_LoopFX(void) { int volume = popVar(); int speed = popVar(); - int channelNum = popVar(); + /*int channelNum = */popVar(); int sampleNum = popVar(); if ((sampleNum >= 0) && (sampleNum < NUM_FILE_ENTRIES) && (filesDatabase[sampleNum].subData.ptr)) { if (speed == -1) speed = filesDatabase[sampleNum].subData.transparency; - _vm->sound().startSound(channelNum, filesDatabase[sampleNum].subData.ptr, - filesDatabase[sampleNum].width, speed, volume, true); + _vm->sound().playSound(filesDatabase[sampleNum].subData.ptr, + filesDatabase[sampleNum].width, volume); } return (0); @@ -246,15 +250,14 @@ int16 Op_StopFX(void) { int16 Op_FreqFX(void) { int volume = popVar(); - int speed = popVar(); + int freq2 = popVar(); int channelNum = popVar(); int sampleNum = popVar(); if ((sampleNum >= 0) && (sampleNum < NUM_FILE_ENTRIES) && (filesDatabase[sampleNum].subData.ptr)) { - if (speed == -1) - speed = filesDatabase[sampleNum].subData.transparency; - - _vm->sound().startNote(channelNum, volume, speed); + int freq = Period(freq2 * 1000); + + _vm->sound().startNote(channelNum, volume, freq); } return (0); @@ -357,10 +360,10 @@ int16 Op_FindSet(void) { } int16 Op_RemoveFrame(void) { - int var1 = popVar(); - int var2 = popVar(); + int count = popVar(); + int start = popVar(); - resetFileEntryRange(var2, var1); + resetFileEntryRange(start, count); return (0); } @@ -563,26 +566,22 @@ int16 Op_LoadFrame(void) { } int16 Op_LoadAbs(void) { - int param1; -// int param2; -// int param3; + int slot; char name[36] = ""; char *ptr; int result = 0; ptr = (char *) popPtr(); + slot = popVar(); - strcpy(name, ptr); - - param1 = popVar(); - - if (param1 >= 0 || param1 < NUM_FILE_ENTRIES) { + if ((slot >= 0) && (slot < NUM_FILE_ENTRIES)) { + strcpy(name, ptr); strToUpper(name); gfxModuleData_gfxWaitVSync(); gfxModuleData_gfxWaitVSync(); - result = loadFullBundle(name, param1); + result = loadFullBundle(name, slot); } changeCursor(CURSOR_NORMAL); @@ -774,8 +773,6 @@ int16 Op_UnfreezeParent(void) { return 0; } -int16 protectionCode = 0; - int16 Op_ProtectionFlag(void) { int16 temp = protectionCode; int16 newVar; @@ -1356,22 +1353,22 @@ int16 Op_LoadSong(void) { strcpy(buffer, ptr); strToUpper(buffer); - _vm->music().loadSong(buffer); + _vm->sound().loadMusic(buffer); changeCursor(CURSOR_NORMAL); return 0; } int16 Op_PlaySong(void) { - if (_vm->music().songLoaded() && !_vm->music().songPlayed()) - _vm->music().startSong(); + if (_vm->sound().songLoaded() && !_vm->sound().songPlayed()) + _vm->sound().playMusic(); return 0; } int16 Op_StopSong(void) { - if (_vm->music().isPlaying()) - _vm->music().stop(); + if (_vm->sound().isPlaying()) + _vm->sound().stopMusic(); return 0; } @@ -1385,12 +1382,12 @@ int16 Op_RestoreSong(void) { int16 Op_SongSize(void) { int size, oldSize; - if (_vm->music().songLoaded()) { - byte *pSize = _vm->music().songData() + 470; - oldSize = *pSize; + if (_vm->sound().songLoaded()) { + oldSize = _vm->sound().numOrders(); + size = popVar(); if ((size >= 1) && (size < 128)) - *pSize = size; + _vm->sound().setNumOrders(size); } else oldSize = 0; @@ -1401,35 +1398,34 @@ int16 Op_SetPattern(void) { int value = popVar(); int offset = popVar(); - if (_vm->music().songLoaded()) { - byte *pData = _vm->music().songData(); - *(pData + 472 + offset) = (byte)value; + if (_vm->sound().songLoaded()) { + _vm->sound().setPattern(offset, value); } return 0; } int16 Op_FadeSong(void) { - _vm->music().fadeSong(); + _vm->sound().fadeSong(); return 0; } int16 Op_FreeSong(void) { - _vm->music().stop(); - _vm->music().removeSong(); + _vm->sound().stopMusic(); + _vm->sound().removeMusic(); return 0; } int16 Op_SongLoop(void) { - bool oldLooping = _vm->music().looping(); - _vm->music().setLoop(popVar() != 0); + bool oldLooping = _vm->sound().musicLooping(); + _vm->sound().musicLoop(popVar() != 0); return oldLooping; } int16 Op_SongPlayed(void) { - return _vm->music().songPlayed(); + return _vm->sound().songPlayed(); } void setVar49Value(int value) { @@ -1634,25 +1630,29 @@ int16 Op_GetNodeY(void) { } int16 Op_SetVolume(void) { - int oldVolume = _vm->music().getVolume() >> 2; + int oldVolume = _vm->sound().getVolume(); int newVolume = popVar(); - // TODO: The game seems to expect the volume will only range from 0 - 63, so for now - // I'm doing a translation of the full range 0 - 255 to the 0 - 63 for this script. - // Need to verify at some point that there's no problem with this if (newVolume > 63) newVolume = 63; if (newVolume >= 0) { int volume = 63 - newVolume; - _vm->music().setVolume(volume << 2); + _vm->sound().setVolume(volume); } return oldVolume >> 2; } int16 Op_SongExist(void) { - char* songName = (char*)popPtr(); + const char *songName = (char*)popPtr(); + + if (songName) { + char name[33]; + strcpy(name, songName); + strToUpper(name); - warning("Unimplemented \"Op_SongExist\": %s", songName); + if (!strcmp(_vm->sound().musicName(), name)) + return 1; + } return 0; } diff --git a/engines/cruise/menu.cpp b/engines/cruise/menu.cpp index 136bd281dc..0e9ab55c82 100644 --- a/engines/cruise/menu.cpp +++ b/engines/cruise/menu.cpp @@ -207,7 +207,7 @@ int playerMenu(int menuX, int menuY) { if (playerMenuEnabled && displayOn) { if (remdo) { - _vm->music().removeSong(); + _vm->sound().stopMusic(); freeStuff2(); } /* @@ -261,6 +261,7 @@ int playerMenu(int menuX, int menuY) { loadSavegameData(0); break; case 6: // restart + _vm->sound().fadeOutMusic(); Op_FadeOut(); memset(globalScreen, 0, 320 * 200); initVars(); diff --git a/engines/cruise/saveload.cpp b/engines/cruise/saveload.cpp index 0084b10cb9..d151950c5e 100644 --- a/engines/cruise/saveload.cpp +++ b/engines/cruise/saveload.cpp @@ -25,6 +25,7 @@ #include "cruise/cruise_main.h" #include "cruise/cruise.h" +#include "cruise/vars.h" #include "common/serializer.h" #include "common/savefile.h" @@ -143,6 +144,7 @@ static void syncBasicInfo(Common::Serializer &s) { s.syncAsSint16LE(flagCt); s.syncAsSint16LE(var41); s.syncAsSint16LE(playerMenuEnabled); + s.syncAsSint16LE(protectionCode); } static void syncBackgroundTable(Common::Serializer &s) { @@ -608,7 +610,7 @@ static void syncCT(Common::Serializer &s) { static void DoSync(Common::Serializer &s) { syncBasicInfo(s); - _vm->music().doSync(s); + _vm->sound().doSync(s); syncPalette(s, newPal); syncPalette(s, workpal); @@ -818,6 +820,7 @@ Common::Error loadSavegameData(int saveGameIdx) { printInfoBlackBox("Loading in progress..."); initVars(); + _vm->sound().stopMusic(); // Skip over the savegame header CruiseSavegameHeader header; @@ -926,8 +929,6 @@ Common::Error loadSavegameData(int saveGameIdx) { currentcellHead = currentcellHead->next; } - //TODO: here, restart music - if (strlen(currentCtpName)) { loadCtFromSave = 1; initCt(currentCtpName); diff --git a/engines/cruise/sound.cpp b/engines/cruise/sound.cpp index fd638e6be5..92c2ac6256 100644 --- a/engines/cruise/sound.cpp +++ b/engines/cruise/sound.cpp @@ -30,199 +30,856 @@ #include "cruise/sound.h" #include "cruise/volume.h" +#include "sound/audiostream.h" +#include "sound/fmopl.h" +#include "sound/mods/soundfx.h" + namespace Cruise { -MusicPlayer::MusicPlayer(): _looping(false), _isPlaying(false), _songPlayed(false) { - _songPointer = NULL; - _masterVolume = 0; +class PCSoundDriver { +public: + typedef void (*UpdateCallback)(void *); + + virtual ~PCSoundDriver() {} + + virtual void setupChannel(int channel, const byte *data, int instrument, int volume) = 0; + virtual void setChannelFrequency(int channel, int frequency) = 0; + virtual void stopChannel(int channel) = 0; + virtual void playSample(const byte *data, int size, int channel, int volume) = 0; + virtual void stopAll() = 0; + virtual const char *getInstrumentExtension() const { return ""; } + virtual void syncSounds(); + + void setUpdateCallback(UpdateCallback upCb, void *ref); + void resetChannel(int channel); + void findNote(int freq, int *note, int *oct) const; + + +protected: + UpdateCallback _upCb; + void *_upRef; + uint8 _musicVolume; + uint8 _sfxVolume; + + static const int _noteTable[]; + static const int _noteTableCount; +}; + +const int PCSoundDriver::_noteTable[] = { + 0xEEE, 0xE17, 0xD4D, 0xC8C, 0xBD9, 0xB2F, 0xA8E, 0x9F7, + 0x967, 0x8E0, 0x861, 0x7E8, 0x777, 0x70B, 0x6A6, 0x647, + 0x5EC, 0x597, 0x547, 0x4FB, 0x4B3, 0x470, 0x430, 0x3F4, + 0x3BB, 0x385, 0x353, 0x323, 0x2F6, 0x2CB, 0x2A3, 0x27D, + 0x259, 0x238, 0x218, 0x1FA, 0x1DD, 0x1C2, 0x1A9, 0x191, + 0x17B, 0x165, 0x151, 0x13E, 0x12C, 0x11C, 0x10C, 0x0FD, + 0x0EE, 0x0E1, 0x0D4, 0x0C8, 0x0BD, 0x0B2, 0x0A8, 0x09F, + 0x096, 0x08E, 0x086, 0x07E, 0x077, 0x070, 0x06A, 0x064, + 0x05E, 0x059, 0x054, 0x04F, 0x04B, 0x047, 0x043, 0x03F, + 0x03B, 0x038, 0x035, 0x032, 0x02F, 0x02C, 0x02A, 0x027, + 0x025, 0x023, 0x021, 0x01F, 0x01D, 0x01C, 0x01A, 0x019, + 0x017, 0x016, 0x015, 0x013, 0x012, 0x011, 0x010, 0x00F +}; + +const int PCSoundDriver::_noteTableCount = ARRAYSIZE(_noteTable); + +struct AdlibRegisterSoundInstrument { + uint8 vibrato; + uint8 attackDecay; + uint8 sustainRelease; + uint8 feedbackStrength; + uint8 keyScaling; + uint8 outputLevel; + uint8 freqMod; +}; + +struct AdlibSoundInstrument { + byte mode; + byte channel; + AdlibRegisterSoundInstrument regMod; + AdlibRegisterSoundInstrument regCar; + byte waveSelectMod; + byte waveSelectCar; + byte amDepth; +}; + +struct VolumeEntry { + int original; + int adjusted; +}; + +class AdlibSoundDriver : public PCSoundDriver, Audio::AudioStream { +public: + AdlibSoundDriver(Audio::Mixer *mixer); + virtual ~AdlibSoundDriver(); + + // PCSoundDriver interface + virtual void setupChannel(int channel, const byte *data, int instrument, int volume); + virtual void stopChannel(int channel); + virtual void stopAll(); + + // AudioStream interface + virtual int readBuffer(int16 *buffer, const int numSamples); + virtual bool isStereo() const { return false; } + virtual bool endOfData() const { return false; } + virtual int getRate() const { return _sampleRate; } + + void initCard(); + void update(int16 *buf, int len); + void setupInstrument(const byte *data, int channel); + void setupInstrument(const AdlibSoundInstrument *ins, int channel); + void loadRegisterInstrument(const byte *data, AdlibRegisterSoundInstrument *reg); + virtual void loadInstrument(const byte *data, AdlibSoundInstrument *asi) = 0; + virtual void syncSounds(); + + void adjustVolume(int channel, int volume); + +protected: + FM_OPL *_opl; + int _sampleRate; + Audio::Mixer *_mixer; + Audio::SoundHandle _soundHandle; + + byte _vibrato; + VolumeEntry _channelsVolumeTable[5]; + AdlibSoundInstrument _instrumentsTable[5]; + + static const int _freqTable[]; + static const int _freqTableCount; + static const int _operatorsTable[]; + static const int _operatorsTableCount; + static const int _voiceOperatorsTable[]; + static const int _voiceOperatorsTableCount; +}; + +const int AdlibSoundDriver::_freqTable[] = { + 0x157, 0x16C, 0x181, 0x198, 0x1B1, 0x1CB, + 0x1E6, 0x203, 0x222, 0x243, 0x266, 0x28A +}; + +const int AdlibSoundDriver::_freqTableCount = ARRAYSIZE(_freqTable); + +const int AdlibSoundDriver::_operatorsTable[] = { + 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21 +}; + +const int AdlibSoundDriver::_operatorsTableCount = ARRAYSIZE(_operatorsTable); + +const int AdlibSoundDriver::_voiceOperatorsTable[] = { + 0, 3, 1, 4, 2, 5, 6, 9, 7, 10, 8, 11, 12, 15, 16, 16, 14, 14, 17, 17, 13, 13 +}; + +const int AdlibSoundDriver::_voiceOperatorsTableCount = ARRAYSIZE(_voiceOperatorsTable); + +class AdlibSoundDriverADL : public AdlibSoundDriver { +public: + AdlibSoundDriverADL(Audio::Mixer *mixer) : AdlibSoundDriver(mixer) {} + virtual const char *getInstrumentExtension() const { return ".ADL"; } + virtual void loadInstrument(const byte *data, AdlibSoundInstrument *asi); + virtual void setChannelFrequency(int channel, int frequency); + virtual void playSample(const byte *data, int size, int channel, int volume); +}; + +class PCSoundFxPlayer { +private: + enum { + NUM_INSTRUMENTS = 15, + NUM_CHANNELS = 4 + }; + + void update(); + void handleEvents(); + void handlePattern(int channel, const byte *patternData); + + char _musicName[33]; + bool _playing; + bool _songPlayed; + int _currentPos; + int _currentOrder; + int _numOrders; + int _eventsDelay; + bool _looping; + int _fadeOutCounter; + int _updateTicksCounter; + int _instrumentsChannelTable[NUM_CHANNELS]; + byte *_sfxData; + byte *_instrumentsData[NUM_INSTRUMENTS]; + PCSoundDriver *_driver; + +public: + PCSoundFxPlayer(PCSoundDriver *driver); + ~PCSoundFxPlayer(); + + bool load(const char *song); + void play(); + void stop(); + void unload(); + void fadeOut(); + void doSync(Common::Serializer &s); + + static void updateCallback(void *ref); + + bool songLoaded() const { return _sfxData != NULL; } + bool songPlayed() const { return _songPlayed; } + bool playing() const { return _playing; } + uint8 numOrders() const { assert(_sfxData); return _sfxData[470]; } + void setNumOrders(uint8 v) { assert(_sfxData); _sfxData[470] = v; } + void setPattern(int offset, uint8 value) { assert(_sfxData); _sfxData[472 + offset] = value; } + const char *musicName() { return _musicName; } + + // Note: Original game never actually uses looping variable. Songs are hardcoded to loop + bool looping() const { return _looping; } + void setLooping(bool v) { _looping = v; } +}; + +byte *readBundleSoundFile(const char *name) { + // Load the correct file + int fileIdx = findFileInDisks(name); + if (fileIdx < 0) return NULL; + + int unpackedSize = volumePtrToFileDescriptor[fileIdx].extSize + 2; + byte *data = (byte *)malloc(unpackedSize); + assert(data); + + if (volumePtrToFileDescriptor[fileIdx].size + 2 != unpackedSize) { + uint8 *packedBuffer = (uint8 *)mallocAndZero(volumePtrToFileDescriptor[fileIdx].size + 2); + + loadPackedFileToMem(fileIdx, packedBuffer); + + //uint32 realUnpackedSize = READ_BE_UINT32(packedBuffer + volumePtrToFileDescriptor[fileIdx].size - 4); + + delphineUnpack(data, packedBuffer, volumePtrToFileDescriptor[fileIdx].size); + + free(packedBuffer); + } else { + loadPackedFileToMem(fileIdx, data); + } + + return data; } -MusicPlayer::~MusicPlayer() { - stop(); - if (_songPointer != NULL) - free(_songPointer); + +void PCSoundDriver::setUpdateCallback(UpdateCallback upCb, void *ref) { + _upCb = upCb; + _upRef = ref; } -void MusicPlayer::setVolume(int volume) { - _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, volume); +void PCSoundDriver::findNote(int freq, int *note, int *oct) const { + *note = _noteTableCount - 1; + for (int i = 0; i < _noteTableCount; ++i) { + if (_noteTable[i] <= freq) { + *note = i; + break; + } + } - if (_masterVolume == volume) - return; + *oct = *note / 12; + *note %= 12; +} - _masterVolume = volume; +void PCSoundDriver::resetChannel(int channel) { + stopChannel(channel); + stopAll(); } -void MusicPlayer::stop() { - _isPlaying = false; - _songPlayed = songLoaded(); +void PCSoundDriver::syncSounds() { + // Get the new music and sfx volumes + _musicVolume = ConfMan.getBool("music_mute") ? 0 : MIN(255, ConfMan.getInt("music_volume")); + _sfxVolume = ConfMan.getBool("sfx_mute") ? 0 : MIN(255, ConfMan.getInt("sfx_volume")); } -void MusicPlayer::pause() { - setVolume(-1); - _isPlaying = false; +AdlibSoundDriver::AdlibSoundDriver(Audio::Mixer *mixer) + : _mixer(mixer) { + _sampleRate = _mixer->getOutputRate(); + _opl = makeAdlibOPL(_sampleRate); + + for (int i = 0; i < 5; ++i) { + _channelsVolumeTable[i].original = 0; + _channelsVolumeTable[i].adjusted = 0; + } + memset(_instrumentsTable, 0, sizeof(_instrumentsTable)); + initCard(); + _mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, false, true); + + _musicVolume = ConfMan.getBool("music_mute") ? 0 : MIN(255, ConfMan.getInt("music_volume")); + _sfxVolume = ConfMan.getBool("sfx_mute") ? 0 : MIN(255, ConfMan.getInt("sfx_volume")); } -void MusicPlayer::resume() { - setVolume(_masterVolume); - _isPlaying = true; - _songPlayed = false; +AdlibSoundDriver::~AdlibSoundDriver() { + _mixer->stopHandle(_soundHandle); + OPLDestroy(_opl); } -void MusicPlayer::doSync(Common::Serializer &s) { - // synchronise current music name, if any, state, and position - s.syncBytes((byte *)_musicName, 33); - uint16 v = (uint16)songLoaded(); - s.syncAsSint16LE(v); - s.syncAsSint16LE(_songPlayed); - s.syncAsSint16LE(_looping); +void AdlibSoundDriver::syncSounds() { + PCSoundDriver::syncSounds(); + + // Force all instruments to reload on the next playing point + for (int i = 0; i < 5; ++i) { + adjustVolume(i, _channelsVolumeTable[i].original); + AdlibSoundInstrument *ins = &_instrumentsTable[i]; + setupInstrument(ins, i); + } } -void MusicPlayer::loadSong(const char *name) { - char tempName[20], baseName[20]; - uint8 *sampleData; +void AdlibSoundDriver::adjustVolume(int channel, int volume) { + _channelsVolumeTable[channel].original = volume; - if (songLoaded()) - removeSong(); + if (volume > 80) { + volume = 80; + } else if (volume < 0) { + volume = 0; + } + volume += volume / 4; + if (volume > 127) { + volume = 127; + } - // Load the correct file - int fileIdx = findFileInDisks(name); - if (fileIdx < 0) return; + int volAdjust = (channel == 4) ? _sfxVolume : _musicVolume; + volume = (volume * volAdjust) / 128; - int unpackedSize = volumePtrToFileDescriptor[fileIdx].extSize + 2; - _songPointer = (byte *)malloc(unpackedSize); - assert(_songPointer); + if (volume > 127) + volume = 127; - if (volumePtrToFileDescriptor[fileIdx].size + 2 != unpackedSize) { - uint8 *packedBuffer = (uint8 *)mallocAndZero(volumePtrToFileDescriptor[fileIdx].size + 2); + _channelsVolumeTable[channel].adjusted = volume; +} - loadPackedFileToMem(fileIdx, packedBuffer); +void AdlibSoundDriver::setupChannel(int channel, const byte *data, int instrument, int volume) { + assert(channel < 5); + if (data) { + adjustVolume(channel, volume); + setupInstrument(data, channel); + } +} - uint32 realUnpackedSize = READ_BE_UINT32(packedBuffer + volumePtrToFileDescriptor[fileIdx].size - 4); +void AdlibSoundDriver::stopChannel(int channel) { + assert(channel < 5); + AdlibSoundInstrument *ins = &_instrumentsTable[channel]; + if (ins->mode != 0 && ins->channel == 6) { + channel = 6; + } + if (ins->mode == 0 || channel == 6) { + OPLWriteReg(_opl, 0xB0 | channel, 0); + } + if (ins->mode != 0) { + _vibrato &= ~(1 << (10 - ins->channel)); + OPLWriteReg(_opl, 0xBD, _vibrato); + } +} - delphineUnpack(_songPointer, packedBuffer, volumePtrToFileDescriptor[fileIdx].size); - _songSize = realUnpackedSize; +void AdlibSoundDriver::stopAll() { + int i; + for (i = 0; i < 18; ++i) { + OPLWriteReg(_opl, 0x40 | _operatorsTable[i], 63); + } + for (i = 0; i < 9; ++i) { + OPLWriteReg(_opl, 0xB0 | i, 0); + } + OPLWriteReg(_opl, 0xBD, 0); +} - free(packedBuffer); - } else { - loadPackedFileToMem(fileIdx, _songPointer); - _songSize = unpackedSize; +int AdlibSoundDriver::readBuffer(int16 *buffer, const int numSamples) { + update(buffer, numSamples); + return numSamples; +} + +void AdlibSoundDriver::initCard() { + _vibrato = 0x20; + OPLWriteReg(_opl, 0xBD, _vibrato); + OPLWriteReg(_opl, 0x08, 0x40); + + static const int oplRegs[] = { 0x40, 0x60, 0x80, 0x20, 0xE0 }; + + for (int i = 0; i < 9; ++i) { + OPLWriteReg(_opl, 0xB0 | i, 0); + } + for (int i = 0; i < 9; ++i) { + OPLWriteReg(_opl, 0xC0 | i, 0); } - strcpy(_musicName, name); + for (int j = 0; j < 5; j++) { + for (int i = 0; i < 18; ++i) { + OPLWriteReg(_opl, oplRegs[j] | _operatorsTable[i], 0); + } + } - // Get the details of the song - // TODO: Figure this out for sure for use in actually playing song - //int size = *(_songPointer + 470); - //int speed = 244 - *(_songPointer + 471); - //int musicSpeed = (speed * 100) / 1060; + OPLWriteReg(_opl, 1, 0x20); + OPLWriteReg(_opl, 1, 0); +} +void AdlibSoundDriver::update(int16 *buf, int len) { + static int samplesLeft = 0; + while (len != 0) { + int count = samplesLeft; + if (count > len) { + count = len; + } + samplesLeft -= count; + len -= count; + YM3812UpdateOne(_opl, buf, count); + if (samplesLeft == 0) { + if (_upCb) { + (*_upCb)(_upRef); + } + samplesLeft = _sampleRate / 50; + } + buf += count; + } +} - // Get the file without the extension - strcpy(baseName, name); - char *p = strchr(baseName, '.'); - if (p) - *p = '\0'; +void AdlibSoundDriver::setupInstrument(const byte *data, int channel) { + assert(channel < 5); + AdlibSoundInstrument *ins = &_instrumentsTable[channel]; + loadInstrument(data, ins); - // Get the instruments states file - strcpy(tempName, baseName); - strcat(tempName, ".IST"); + setupInstrument(ins, channel); +} + +void AdlibSoundDriver::setupInstrument(const AdlibSoundInstrument *ins, int channel) { + int mod, car, tmp; + const AdlibRegisterSoundInstrument *reg; - fileIdx = findFileInDisks(tempName); - if (fileIdx >= 0) { - // TODO: Figure out instrument state usage - uint8 instrumentState[15]; - loadPackedFileToMem(fileIdx, instrumentState); + if (ins->mode != 0) { + mod = _operatorsTable[_voiceOperatorsTable[2 * ins->channel + 0]]; + car = _operatorsTable[_voiceOperatorsTable[2 * ins->channel + 1]]; + } else { + mod = _operatorsTable[_voiceOperatorsTable[2 * channel + 0]]; + car = _operatorsTable[_voiceOperatorsTable[2 * channel + 1]]; } - for (int instrumentCtr = 0; instrumentCtr < 15; ++instrumentCtr) { - if (_vm->mt32()) { - // Handle loading Roland instrument data - strcpy(tempName, baseName); - strcat(tempName, ".H32"); + if (ins->mode == 0 || ins->channel == 6) { + reg = &ins->regMod; + OPLWriteReg(_opl, 0x20 | mod, reg->vibrato); + if (reg->freqMod) { + tmp = reg->outputLevel & 0x3F; + } else { + tmp = (63 - (reg->outputLevel & 0x3F)) * _channelsVolumeTable[channel].adjusted; + tmp = 63 - (2 * tmp + 127) / (2 * 127); + } + OPLWriteReg(_opl, 0x40 | mod, tmp | (reg->keyScaling << 6)); + OPLWriteReg(_opl, 0x60 | mod, reg->attackDecay); + OPLWriteReg(_opl, 0x80 | mod, reg->sustainRelease); + if (ins->mode != 0) { + OPLWriteReg(_opl, 0xC0 | ins->channel, reg->feedbackStrength); + } else { + OPLWriteReg(_opl, 0xC0 | channel, reg->feedbackStrength); + } + OPLWriteReg(_opl, 0xE0 | mod, ins->waveSelectMod); + } - sampleData = loadInstrument(tempName, instrumentCtr); - if (sampleData) { - int v = *sampleData; - if ((v >= 128) && (v < 192)) - patchMidi(0x80000L + (instrumentCtr * 512), sampleData + 1, 254); + reg = &ins->regCar; + OPLWriteReg(_opl, 0x20 | car, reg->vibrato); + tmp = (63 - (reg->outputLevel & 0x3F)) * _channelsVolumeTable[channel].adjusted; + tmp = 63 - (2 * tmp + 127) / (2 * 127); + OPLWriteReg(_opl, 0x40 | car, tmp | (reg->keyScaling << 6)); + OPLWriteReg(_opl, 0x60 | car, reg->attackDecay); + OPLWriteReg(_opl, 0x80 | car, reg->sustainRelease); + OPLWriteReg(_opl, 0xE0 | car, ins->waveSelectCar); +} - // TODO: Currently I'm freeing the instrument data immediately. The original - // holds onto the sample data, so it may actually still be needed - free(sampleData); - } - } else if (_vm->adlib()) { - // Handle loading Adlib instrument data - strcpy(tempName, baseName); - strcat(tempName, ".ADL"); +void AdlibSoundDriver::loadRegisterInstrument(const byte *data, AdlibRegisterSoundInstrument *reg) { + reg->vibrato = 0; + if (READ_LE_UINT16(data + 18)) { // amplitude vibrato + reg->vibrato |= 0x80; + } + if (READ_LE_UINT16(data + 20)) { // frequency vibrato + reg->vibrato |= 0x40; + } + if (READ_LE_UINT16(data + 10)) { // sustaining sound + reg->vibrato |= 0x20; + } + if (READ_LE_UINT16(data + 22)) { // envelope scaling + reg->vibrato |= 0x10; + } + reg->vibrato |= READ_LE_UINT16(data + 2) & 0xF; // frequency multiplier - fileIdx = findFileInDisks(tempName); - if (fileIdx >= 0) { - sampleData = (byte *)malloc(volumePtrToFileDescriptor[fileIdx].extSize + 2); - assert(sampleData); - loadPackedFileToMem(fileIdx, sampleData); + reg->attackDecay = READ_LE_UINT16(data + 6) << 4; // attack rate + reg->attackDecay |= READ_LE_UINT16(data + 12) & 0xF; // decay rate - // TODO: Make use of sample data + reg->sustainRelease = READ_LE_UINT16(data + 8) << 4; // sustain level + reg->sustainRelease |= READ_LE_UINT16(data + 14) & 0xF; // release rate - free(sampleData); - } + reg->feedbackStrength = READ_LE_UINT16(data + 4) << 1; // feedback + if (READ_LE_UINT16(data + 24) == 0) { // frequency modulation + reg->feedbackStrength |= 1; + } + + reg->keyScaling = READ_LE_UINT16(data); + reg->outputLevel = READ_LE_UINT16(data + 16); + reg->freqMod = READ_LE_UINT16(data + 24); +} + +void AdlibSoundDriverADL::loadInstrument(const byte *data, AdlibSoundInstrument *asi) { + asi->mode = *data++; + asi->channel = *data++; + asi->waveSelectMod = *data++ & 3; + asi->waveSelectCar = *data++ & 3; + asi->amDepth = *data++; + ++data; + loadRegisterInstrument(data, &asi->regMod); data += 26; + loadRegisterInstrument(data, &asi->regCar); data += 26; +} + +void AdlibSoundDriverADL::setChannelFrequency(int channel, int frequency) { + assert(channel < 5); + AdlibSoundInstrument *ins = &_instrumentsTable[channel]; + if (ins->mode != 0) { + channel = ins->channel; + if (channel == 9) { + channel = 8; + } else if (channel == 10) { + channel = 7; } } + int freq, note, oct; + findNote(frequency, ¬e, &oct); - _songPlayed = false; - _isPlaying = false; + note += oct * 12; + if (ins->amDepth) { + note = ins->amDepth; + } + if (note < 0) { + note = 0; + } + + freq = _freqTable[note % 12]; + OPLWriteReg(_opl, 0xA0 | channel, freq); + freq = ((note / 12) << 2) | ((freq & 0x300) >> 8); + if (ins->mode == 0) { + freq |= 0x20; + } + OPLWriteReg(_opl, 0xB0 | channel, freq); + if (ins->mode != 0) { + _vibrato |= 1 << (10 - channel); + OPLWriteReg(_opl, 0xBD, _vibrato); + } } -void MusicPlayer::startSong() { - if (songLoaded()) { - // Start playing song here +void AdlibSoundDriverADL::playSample(const byte *data, int size, int channel, int volume) { + assert(channel < 5); + adjustVolume(channel, 127); + + setupInstrument(data, channel); + AdlibSoundInstrument *ins = &_instrumentsTable[channel]; + if (ins->mode != 0 && ins->channel == 6) { + OPLWriteReg(_opl, 0xB0 | channel, 0); + } + if (ins->mode != 0) { + _vibrato &= ~(1 << (10 - ins->channel)); + OPLWriteReg(_opl, 0xBD, _vibrato); } + if (ins->mode != 0) { + channel = ins->channel; + if (channel == 9) { + channel = 8; + } else if (channel == 10) { + channel = 7; + } + } + uint16 note = 48; + if (ins->amDepth) { + note = ins->amDepth; + } + int freq = _freqTable[note % 12]; + OPLWriteReg(_opl, 0xA0 | channel, freq); + freq = ((note / 12) << 2) | ((freq & 0x300) >> 8); + if (ins->mode == 0) { + freq |= 0x20; + } + OPLWriteReg(_opl, 0xB0 | channel, freq); + if (ins->mode != 0) { + _vibrato |= 1 << (10 - channel); + OPLWriteReg(_opl, 0xBD, _vibrato); + } +} + +PCSoundFxPlayer::PCSoundFxPlayer(PCSoundDriver *driver) + : _playing(false), _songPlayed(false), _driver(driver) { + memset(_instrumentsData, 0, sizeof(_instrumentsData)); + _sfxData = NULL; + _fadeOutCounter = 0; + _driver->setUpdateCallback(updateCallback, this); } -void MusicPlayer::removeSong() { - if (isPlaying()) +PCSoundFxPlayer::~PCSoundFxPlayer() { + _driver->setUpdateCallback(NULL, NULL); + if (_playing) { stop(); + } +} + +bool PCSoundFxPlayer::load(const char *song) { + debug(9, "PCSoundFxPlayer::load('%s')", song); - if (_songPointer) { - free(_songPointer); - _songPointer = NULL; + /* stop (w/ fade out) the previous song */ + while (_fadeOutCounter != 0 && _fadeOutCounter < 100) { + g_system->delayMillis(50); + } + _fadeOutCounter = 0; + + if (_playing) { + stop(); } + strcpy(_musicName, song); _songPlayed = false; + _looping = false; + _sfxData = readBundleSoundFile(song); + if (!_sfxData) { + warning("Unable to load soundfx module '%s'", song); + return 0; + } + + for (int i = 0; i < NUM_INSTRUMENTS; ++i) { + _instrumentsData[i] = NULL; + + char instrument[64]; + memset(instrument, 0, 64); // Clear the data first + memcpy(instrument, _sfxData + 20 + i * 30, 12); + instrument[63] = '\0'; - strcpy(_musicName, ""); + if (strlen(instrument) != 0) { + char *dot = strrchr(instrument, '.'); + if (dot) { + *dot = '\0'; + } + strcat(instrument, _driver->getInstrumentExtension()); + _instrumentsData[i] = readBundleSoundFile(instrument); + if (!_instrumentsData[i]) { + warning("Unable to load soundfx instrument '%s'", instrument); + } + } + } + return 1; } -void MusicPlayer::fadeSong() { - // TODO: Implement fading properly - stop(); +void PCSoundFxPlayer::play() { + debug(9, "PCSoundFxPlayer::play()"); + if (_sfxData) { + for (int i = 0; i < NUM_CHANNELS; ++i) { + _instrumentsChannelTable[i] = -1; + } + _currentPos = 0; + _currentOrder = 0; + _numOrders = _sfxData[470]; + _eventsDelay = (244 - _sfxData[471]) * 100 / 1060; + _updateTicksCounter = 0; + _playing = true; + } } -void MusicPlayer::patchMidi(uint32 adr, const byte *data, int size) { - // TODO: Handle patching midi +void PCSoundFxPlayer::stop() { + if (_playing || _fadeOutCounter != 0) { + _fadeOutCounter = 0; + _playing = false; + for (int i = 0; i < NUM_CHANNELS; ++i) { + _driver->stopChannel(i); + } + _driver->stopAll(); + unload(); + } } -byte *MusicPlayer::loadInstrument(const char *name, int i) { - // Find the resource - int fileIdx = findFileInDisks(name); - if (fileIdx < 0) { - warning("Instrument '%s' not found", name); - return NULL; +void PCSoundFxPlayer::fadeOut() { + if (_playing) { + _fadeOutCounter = 1; + _playing = false; + } +} + +void PCSoundFxPlayer::updateCallback(void *ref) { + ((PCSoundFxPlayer *)ref)->update(); +} + +void PCSoundFxPlayer::update() { + if (_playing || (_fadeOutCounter != 0 && _fadeOutCounter < 100)) { + ++_updateTicksCounter; + if (_updateTicksCounter > _eventsDelay) { + handleEvents(); + _updateTicksCounter = 0; + } } +} + +void PCSoundFxPlayer::handleEvents() { + const byte *patternData = _sfxData + 600 + 1800; + const byte *orderTable = _sfxData + 472; + uint16 patternNum = orderTable[_currentOrder] * 1024; - int size = volumePtrToFileDescriptor[fileIdx].extSize; + for (int i = 0; i < 4; ++i) { + handlePattern(i, patternData + patternNum + _currentPos); + patternData += 4; + } - // Get the data - byte *tmp = (byte *)malloc(size); - assert(tmp); - loadPackedFileToMem(fileIdx, tmp); + if (_fadeOutCounter != 0 && _fadeOutCounter < 100) { + _fadeOutCounter += 2; + } + if (_fadeOutCounter >= 100) { + stop(); + return; + } - // Create a copy of the resource that's 22 bytes smaller - byte *result = (byte *)malloc(size - 22); - assert(result); - Common::copy(tmp, tmp + size - 22, result); + _currentPos += 16; + if (_currentPos >= 1024) { + _currentPos = 0; + ++_currentOrder; + if (_currentOrder == _numOrders) { + _currentOrder = 0; + } + } + debug(7, "_currentOrder=%d/%d _currentPos=%d", _currentOrder, _numOrders, _currentPos); +} + +void PCSoundFxPlayer::handlePattern(int channel, const byte *patternData) { + int instrument = patternData[2] >> 4; + if (instrument != 0) { + --instrument; + if (_instrumentsChannelTable[channel] != instrument || _fadeOutCounter != 0) { + _instrumentsChannelTable[channel] = instrument; + const int volume = _sfxData[instrument] - _fadeOutCounter; + _driver->setupChannel(channel, _instrumentsData[instrument], instrument, volume); + } + } + int16 freq = (int16)READ_BE_UINT16(patternData); + if (freq > 0) { + _driver->stopChannel(channel); + _driver->setChannelFrequency(channel, freq); + } +} + +void PCSoundFxPlayer::unload() { + for (int i = 0; i < NUM_INSTRUMENTS; ++i) { + free(_instrumentsData[i]); + _instrumentsData[i] = NULL; + } + free(_sfxData); + _sfxData = NULL; + _songPlayed = true; +} + +void PCSoundFxPlayer::doSync(Common::Serializer &s) { + s.syncBytes((byte *)_musicName, 33); + uint16 v = (uint16)songLoaded(); + s.syncAsSint16LE(v); + + if (s.isLoading() && v) { + load(_musicName); + + for (int i = 0; i < NUM_CHANNELS; ++i) { + _instrumentsChannelTable[i] = -1; + } + } + + s.syncAsSint16LE(_songPlayed); + s.syncAsSint16LE(_looping); + s.syncAsSint16LE(_currentPos); + s.syncAsSint16LE(_currentOrder); + s.syncAsSint16LE(_playing); +} + +PCSound::PCSound(Audio::Mixer *mixer, CruiseEngine *vm) { + _vm = vm; + _mixer = mixer; + _soundDriver = new AdlibSoundDriverADL(_mixer); + _player = new PCSoundFxPlayer(_soundDriver); +} + +PCSound::~PCSound() { + delete _player; + delete _soundDriver; +} + +void PCSound::loadMusic(const char *name) { + debugC(5, kCruiseDebugSound, "PCSound::loadMusic('%s')", name); + _player->load(name); +} + +void PCSound::playMusic() { + debugC(5, kCruiseDebugSound, "PCSound::playMusic()"); + _player->play(); +} + +void PCSound::stopMusic() { + debugC(5, kCruiseDebugSound, "PCSound::stopMusic()"); + _player->stop(); +} + +void PCSound::removeMusic() { + debugC(5, kCruiseDebugSound, "PCSound::removeMusic()"); + _player->unload(); +} + +void PCSound::fadeOutMusic() { + debugC(5, kCruiseDebugSound, "PCSound::fadeOutMusic()"); + _player->fadeOut(); +} + +void PCSound::playSound(const uint8 *data, int size, int volume) { + debugC(5, kCruiseDebugSound, "PCSound::playSound() channel %d size %d", 4, size); + _soundDriver->playSample(data, size, 4, volume); +} + +void PCSound::stopSound(int channel) { + debugC(5, kCruiseDebugSound, "PCSound::stopSound() channel %d", channel); + _soundDriver->resetChannel(channel); +} + +void PCSound::stopChannel(int channel) { + debugC(5, kCruiseDebugSound, "PCSound::stopChannel() channel %d", channel); + _soundDriver->stopChannel(channel); +} + +bool PCSound::isPlaying() const { + return _player->playing(); +} + +bool PCSound::songLoaded() const { + return _player->songLoaded(); +} + +bool PCSound::songPlayed() const { + return _player->songPlayed(); +} + +void PCSound::fadeSong() { + _player->fadeOut(); +} + +uint8 PCSound::numOrders() const { + return _player->numOrders(); +} + +void PCSound::setNumOrders(uint8 v) { + _player->setNumOrders(v); +} + +void PCSound::setPattern(int offset, uint8 value) { + _player->setPattern(offset, value); +} + +bool PCSound::musicLooping() const { + return _player->looping(); +} + +void PCSound::musicLoop(bool v) { + _player->setLooping(v); +} + +void PCSound::startNote(int channel, int volume, int freq) { + warning("TODO: startNote"); +// _soundDriver->setVolume(channel, volume); + _soundDriver->setChannelFrequency(channel, freq); +} + +void PCSound::doSync(Common::Serializer &s) { + _player->doSync(s); + s.syncAsSint16LE(_genVolume); +} + +const char *PCSound::musicName() { + return _player->musicName(); +} - free(tmp); - return result; +void PCSound::syncSounds() { + _soundDriver->syncSounds(); } } // End of namespace Cruise diff --git a/engines/cruise/sound.h b/engines/cruise/sound.h index faf3df995d..13a6b2ac5a 100644 --- a/engines/cruise/sound.h +++ b/engines/cruise/sound.h @@ -29,62 +29,55 @@ #include "sound/mididrv.h" #include "sound/midiparser.h" #include "sound/mixer.h" + +#include "common/config-manager.h" #include "common/serializer.h" namespace Cruise { -class MusicPlayer { -private: - byte _channelVolume[16]; - int _fadeVolume; - char _musicName[33]; - - bool _isPlaying; - bool _songPlayed; - bool _looping; - byte _masterVolume; - - byte *_songPointer; - // TODO: lib_SongSize - int _songSize; +class CruiseEngine; +class PCSoundDriver; +class PCSoundFxPlayer; - void patchMidi(uint32 adr, const byte *data, int size); - byte *loadInstrument(const char *name, int i); +class PCSound { +private: + Audio::Mixer *_mixer; + CruiseEngine *_vm; + int _genVolume; +protected: + PCSoundDriver *_soundDriver; + PCSoundFxPlayer *_player; public: - MusicPlayer(); - ~MusicPlayer(); - - void setVolume(int volume); - int getVolume() const { return _masterVolume; } - - void stop(); - void pause(); - void resume(); - - // Common public access methods + PCSound(Audio::Mixer *mixer, CruiseEngine *vm); + virtual ~PCSound(); + + virtual void loadMusic(const char *name); + virtual void playMusic(); + virtual void stopMusic(); + virtual void removeMusic(); + virtual void fadeOutMusic(); + + virtual void playSound(const uint8 *data, int size, int volume); + virtual void stopSound(int channel); + void doSync(Common::Serializer &s); - void loadSong(const char *name); - void startSong(); - void stopSong(); - void removeSong(); + const char *musicName(); + void stopChannel(int channel); + bool isPlaying() const; + bool songLoaded() const; + bool songPlayed() const; void fadeSong(); - - bool songLoaded() const { return _songPointer != NULL; } - bool songPlayed() const { return _songPlayed; } - bool isPlaying() const { return _isPlaying; } - bool looping() const { return _looping; } - byte *songData() { return _songPointer; } - void setPlaying(bool playing) { _isPlaying = playing; } - void setLoop(bool loop) { _looping = loop; } -}; - -class SoundPlayer { -public: - SoundPlayer() {} - - void startSound(int channelNum, const byte *ptr, int size, int speed, int volume, bool loop) {} - void startNote(int channelNum, int speed, int volume) {} - void stopChannel(int channelNum) {} + uint8 numOrders() const; + void setNumOrders(uint8 v); + void setPattern(int offset, uint8 value); + bool musicLooping() const; + void musicLoop(bool v); + void startNote(int channel, int volume, int freq); + void syncSounds(); + + // Note: Volume variable accessed by these methods is never actually used in original game + void setVolume(int volume) { _genVolume = volume; } + uint8 getVolume() const { return _genVolume; } }; } // End of namespace Cruise diff --git a/engines/cruise/vars.cpp b/engines/cruise/vars.cpp index 3ea591ed43..94fd00cbfd 100644 --- a/engines/cruise/vars.cpp +++ b/engines/cruise/vars.cpp @@ -79,6 +79,8 @@ int16 volumeNumberOfEntry; int16 displayOn = 1; +int16 protectionCode = 0; + int16 globalVars[2000]; dataFileEntry filesDatabase[NUM_FILE_ENTRIES]; diff --git a/engines/cruise/vars.h b/engines/cruise/vars.h index 7208a8bea0..4bb94ff691 100644 --- a/engines/cruise/vars.h +++ b/engines/cruise/vars.h @@ -183,6 +183,8 @@ extern int16 volumeNumberOfEntry; extern int16 displayOn; +extern int16 protectionCode; + #define NUM_FILE_ENTRIES 257 extern int16 globalVars[2000]; diff --git a/engines/cruise/volume.cpp b/engines/cruise/volume.cpp index fda3b6bc1f..2b8cce0f05 100644 --- a/engines/cruise/volume.cpp +++ b/engines/cruise/volume.cpp @@ -107,11 +107,8 @@ int getVolumeDataEntry(volumeDataStruct *entry) { changeCursor(CURSOR_DISK); - currentVolumeFile.read(&volumeNumberOfEntry, 2); - currentVolumeFile.read(&volumeSizeOfEntry, 2); - - bigEndianShortToNative(&volumeNumberOfEntry); - bigEndianShortToNative(&volumeSizeOfEntry); + volumeNumberOfEntry = currentVolumeFile.readSint16BE(); + volumeSizeOfEntry = currentVolumeFile.readSint16BE(); volumeNumEntry = volumeNumberOfEntry; @@ -129,16 +126,10 @@ int getVolumeDataEntry(volumeDataStruct *entry) { for (i = 0; i < volumeNumEntry; i++) { currentVolumeFile.read(&volumePtrToFileDescriptor[i].name, 14); - currentVolumeFile.read(&volumePtrToFileDescriptor[i].offset, 4); - currentVolumeFile.read(&volumePtrToFileDescriptor[i].size, 4); - currentVolumeFile.read(&volumePtrToFileDescriptor[i].extSize, 4); - currentVolumeFile.read(&volumePtrToFileDescriptor[i].unk3, 4); - } - - for (i = 0; i < volumeNumEntry; i++) { - bigEndianLongToNative(&volumePtrToFileDescriptor[i].offset); - bigEndianLongToNative(&volumePtrToFileDescriptor[i].size); - bigEndianLongToNative(&volumePtrToFileDescriptor[i].extSize); + volumePtrToFileDescriptor[i].offset = currentVolumeFile.readSint32BE(); + volumePtrToFileDescriptor[i].size = currentVolumeFile.readSint32BE(); + volumePtrToFileDescriptor[i].extSize = currentVolumeFile.readSint32BE(); + volumePtrToFileDescriptor[i].unk3 = currentVolumeFile.readSint32BE(); } strcpy(currentBaseName, entry->ident); @@ -355,29 +346,23 @@ int16 readVolCnf(void) { return (0); } - fileHandle.read(&numOfDisks, 2); - bigEndianShortToNative(&numOfDisks); - - fileHandle.read(&sizeHEntry, 2); - bigEndianShortToNative(&sizeHEntry); // size of one header entry - 20 bytes + numOfDisks = fileHandle.readSint16BE(); + sizeHEntry = fileHandle.readSint16BE(); // size of one header entry - 20 bytes for (i = 0; i < numOfDisks; i++) { // fread(&volumeData[i],20,1,fileHandle); fileHandle.read(&volumeData[i].ident, 10); fileHandle.read(&volumeData[i].ptr, 4); - fileHandle.read(&volumeData[i].diskNumber, 2); - fileHandle.read(&volumeData[i].size, 4); + volumeData[i].diskNumber = fileHandle.readSint16BE(); + volumeData[i].size = fileHandle.readSint32BE(); - bigEndianShortToNative(&volumeData[i].diskNumber); debug(1, "Disk number: %d", volumeData[i].diskNumber); - bigEndianLongToNative(&volumeData[i].size); } for (i = 0; i < numOfDisks; i++) { dataFileName *ptr; - fileHandle.read(&volumeData[i].size, 4); - bigEndianLongToNative(&volumeData[i].size); + volumeData[i].size = fileHandle.readSint32BE(); ptr = (dataFileName *) mallocAndZero(volumeData[i].size); |