/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "audio/audiostream.h" #include "audio/decoders/raw.h" #include "common/algorithm.h" #include "common/debug.h" #include "common/memstream.h" #include "mads/sound.h" #include "mads/nebular/sound_nebular.h" namespace MADS { namespace Nebular { bool AdlibChannel::_channelsEnabled; AdlibChannel::AdlibChannel() { _activeCount = 0; _field1 = 0; _field2 = 0; _field3 = 0; _field4 = 0; _sampleIndex = 0; _volume = 0; _field7 = 0; _field8 = 0; _field9 = 0; _fieldA = 0; _fieldB = 0; _fieldC = 0; _fieldD = 0; _fieldE = 0; _ptr1 = nullptr; _pSrc = nullptr; _ptr3 = nullptr; _ptr4 = nullptr; _field17 = 0; _field19 = 0; _soundData = nullptr; _field1D = 0; _field1E = 0; _field1F = 0; _field20 = 0; } void AdlibChannel::reset() { _activeCount = 0; _field1 = 0; _field2 = 0; _field3 = 0; } void AdlibChannel::enable(int flag) { if (_activeCount) { _fieldE = flag; // WORKAROUND: Original set _soundData pointer to flag. Since this seems // just intended to invalidate any prior pointer, I've replaced it with // a simple null pointer _soundData = nullptr; } _channelsEnabled = true; } void AdlibChannel::setPtr2(byte *pData) { _pSrc = pData; _field2 = 0xFF; _fieldA = 1; _field9 = 1; } void AdlibChannel::load(byte *pData) { _ptr1 = _pSrc = _ptr3 = pData; _ptr4 = _soundData = pData; _fieldA = 0xFF; _activeCount = 1; _fieldD = 64; _field1 = 0; _field1F = 0; _field2 = _field3 = 0; _volume = _field7 = 0; _field1D = _field1E = 0; _fieldE = 0; _field9 = 0; _fieldB = 0; _field17 = 0; _field19 = 0; } void AdlibChannel::check(byte *nullPtr) { if (_activeCount && _fieldE) { if (!_field1E) { _pSrc = nullPtr; _fieldE = 0; } else { _field2 = 0xFF; _fieldA = 4; if (!_field9) _field9 = 1; } } } /*-----------------------------------------------------------------------*/ AdlibSample::AdlibSample(Common::SeekableReadStream &s) { _attackRate = s.readByte(); _decayRate = s.readByte(); _sustainLevel = s.readByte(); _releaseRate = s.readByte(); _egTyp = s.readByte() != 0; _ksr = s.readByte() != 0; _totalLevel = s.readByte(); _scalingLevel = s.readByte(); _waveformSelect = s.readByte(); _freqMultiple = s.readByte(); _feedback = s.readByte(); _ampMod = s.readByte() != 0; _vib = s.readByte(); _alg = s.readByte(); _fieldE = s.readByte(); s.skip(1); _freqMask = s.readUint16LE(); _freqBase = s.readUint16LE(); _field14 = s.readUint16LE(); } /*-----------------------------------------------------------------------*/ ASound::ASound(Audio::Mixer *mixer, const Common::String &filename, int dataOffset) { // Open up the appropriate sound file if (!_soundFile.open(filename)) error("Could not open file - %s", filename.c_str()); // Initialise fields _commandParam = 0; _activeChannelPtr = nullptr; _samplePtr = nullptr; _frameCounter = 0; _isDisabled = false; _v1 = 0; _v2 = 0; _activeChannelNumber = 0; _freqMask1 = _freqMask2 = 0; _freqBase1 = _freqBase2 = 0; _channelNum1 = _channelNum2 = 0; _v7 = 0; _v8 = 0; _v9 = 0; _v10 = 0; _pollResult = 0; _resultFlag = 0; _nullData[0] = _nullData[1] = 0; Common::fill(&_ports[0], &_ports[256], 0); _stateFlag = false; _activeChannelReg = 0; _v11 = 0; _randomSeed = 1234; _amDep = _vibDep = _splitPoint = true; _samplesTillCallback = 0; _samplesTillCallbackRemainder = 0; _samplesPerCallback = getRate() / CALLBACKS_PER_SECOND; _samplesPerCallbackRemainder = getRate() % CALLBACKS_PER_SECOND; AdlibChannel::_channelsEnabled = false; // Store passed parameters, and setup OPL _dataOffset = dataOffset; _mixer = mixer; _opl = OPL::Config::create(); assert(_opl); _opl->init(getRate()); _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); // Initialise the Adlib adlibInit(); // Reset the adlib command0(); } ASound::~ASound() { Common::List::iterator i; for (i = _dataCache.begin(); i != _dataCache.end(); ++i) delete[] (*i)._data; _mixer->stopHandle(_soundHandle); delete _opl; } void ASound::adlibInit() { write(4, 0x60); write(4, 0x80); write(2, 0xff); write(4, 0x21); write(4, 0x60); write(4, 0x80); } int ASound::stop() { command0(); int result = _pollResult; _pollResult = 0; return result; } int ASound::poll() { // Update any playing sounds update(); // Return result int result = _pollResult; _pollResult = 0; return result; } void ASound::noise() { int randomVal = getRandomNumber(); if (_v1) { setFrequency(_channelNum1, ((randomVal ^ 0xFFFF) & _freqMask1) + _freqBase1); } if (_v2) { setFrequency(_channelNum2, (randomVal & _freqMask2) + _freqBase2); } } void ASound::write(int reg, int val) { _queue.push(RegisterValue(reg, val)); } int ASound::write2(int state, int reg, int val) { // TODO: Original has a state parameter, not used when in Adlib mode? _ports[reg] = val; write(reg, val); return state; } void ASound::flush() { Common::StackLock slock(_driverMutex); while (!_queue.empty()) { RegisterValue v = _queue.pop(); _opl->writeReg(v._regNum, v._value); } } void ASound::channelOn(int reg, int volume) { write2(8, reg, (_ports[reg] & 0xC0) | (volume & 0x3F)); } void ASound::channelOff(int reg) { write2(8, reg, _ports[reg] | 0x3F); } void ASound::resultCheck() { if (_resultFlag != 1) { _resultFlag = 1; _pollResult = 1; } } byte *ASound::loadData(int offset, int size) { // First scan for an existing copy Common::List::iterator i; for (i = _dataCache.begin(); i != _dataCache.end(); ++i) { CachedDataEntry &e = *i; if (e._offset == offset) return e._data; } // No existing entry found, so load up data and store as a new entry CachedDataEntry rec; rec._offset = offset; rec._data = new byte[size]; _soundFile.seek(_dataOffset + offset); _soundFile.read(rec._data, size); _dataCache.push_back(rec); // Return the data return rec._data; } void ASound::playSound(int offset, int size) { // Load the specified data block playSoundData(loadData(offset, size)); } void ASound::playSoundData(byte *pData, int startingChannel) { // Scan for a high level free channel for (int i = startingChannel; i < ADLIB_CHANNEL_COUNT; ++i) { if (!_channels[i]._activeCount) { _channels[i].load(pData); return; } } // None found, do a secondary scan for an interruptable channel for (int i = ADLIB_CHANNEL_COUNT - 1; i >= startingChannel; --i) { if (_channels[i]._fieldE == 0xFF) { _channels[i].load(pData); return; } } } bool ASound::isSoundActive(byte *pData) { for (int i = 0; i < ADLIB_CHANNEL_MIDWAY; ++i) { if (_channels[i]._activeCount && _channels[i]._soundData == pData) return true; } return false; } void ASound::setFrequency(int channel, int freq) { write2(8, 0xA0 + channel, freq & 0xFF); write2(8, 0xB0 + channel, (freq >> 8) | 0x20); } int ASound::getRandomNumber() { int v = 0x9248 + (int)_randomSeed; _randomSeed = ((v >> 3) | (v << 13)) & 0xFFFF; return _randomSeed; } void ASound::update() { getRandomNumber(); if (_isDisabled) return; ++_frameCounter; pollChannels(); checkChannels(); if (_v1 == _v2) { if (_resultFlag != -1) { _resultFlag = -1; _pollResult = -1; } } else { if (_v1) { _freqBase1 += _v7; if (!--_v1) { if (!_v2 || _channelNum1 != _channelNum2) { write2(8, 0xA0 + _channelNum1, 0); write2(8, 0xB0 + _channelNum1, 0); } } } if (_v2) { _freqBase2 += _v8; if (!--_v2) { if (!_v1 || _channelNum2 != _channelNum1) { write2(8, 0xA0 + _channelNum2, 0); write2(8, 0xB0 + _channelNum2, 0); } } } } } void ASound::pollChannels() { _activeChannelNumber = 0; for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i) { _activeChannelPtr = &_channels[i]; pollActiveChannel(); } } void ASound::checkChannels() { if (AdlibChannel::_channelsEnabled) { for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i) _channels[i].check(_nullData); } } void ASound::pollActiveChannel() { AdlibChannel *chan = _activeChannelPtr; bool updateFlag = true; if (chan->_activeCount) { if (chan->_field8 > 0 && --chan->_field8 == 0) updateOctave(); if (--_activeChannelPtr->_activeCount <= 0) { for (;;) { byte *pSrc = chan->_pSrc; if (!(*pSrc & 0x80) || (*pSrc <= 0xF0)) { if (updateFlag) updateActiveChannel(); chan->_field4 = *pSrc++; chan->_activeCount = *pSrc++; chan->_pSrc += 2; if (!chan->_field4 || !chan->_activeCount) { updateOctave(); } else { chan->_field8 = chan->_activeCount - chan->_field7; updateChannelState(); } // Break out of processing loop break; } else { updateFlag = false; switch ((~*pSrc) & 0xF) { case 0: if (!chan->_field17) { if (*++pSrc == 0) { chan->_pSrc += 2; chan->_ptr3 = chan->_pSrc; chan->_field17 = 0; } else { chan->_field17 = *pSrc; chan->_pSrc = chan->_ptr3; } } else if (--chan->_field17) { chan->_pSrc = chan->_ptr3; } else { chan->_pSrc += 2; chan->_ptr3 = chan->_pSrc; } break; case 1: if (!chan->_field19) { if (*++pSrc == 0) { chan->_pSrc += 2; chan->_ptr4 = chan->_pSrc; chan->_ptr3 = chan->_pSrc; chan->_field17 = 0; chan->_field19 = 0; } else { chan->_field19 = *pSrc; chan->_pSrc = chan->_ptr4; chan->_ptr3 = chan->_ptr4; } } else if (--chan->_field19) { chan->_ptr4 = chan->_pSrc; chan->_ptr3 = chan->_pSrc; } else { chan->_pSrc += 2; chan->_ptr4 = chan->_pSrc; chan->_ptr3 = chan->_pSrc; } break; case 2: // Loop sound data chan->_field1 = 0; chan->_field2 = chan->_field3 = 0; chan->_volume = chan->_field7 = 0; chan->_field1D = chan->_field1E = 0; chan->_field8 = 0; chan->_field9 = 0; chan->_fieldB = 0; chan->_field17 = 0; chan->_field19 = 0; chan->_fieldD = 0x40; chan->_ptr1 = chan->_soundData; chan->_pSrc = chan->_soundData; chan->_ptr3 = chan->_soundData; chan->_ptr4 = chan->_soundData; chan->_pSrc += 2; break; case 3: chan->_sampleIndex = *++pSrc; chan->_pSrc += 2; loadSample(chan->_sampleIndex); break; case 4: chan->_field7 = *++pSrc; chan->_pSrc += 2; break; case 5: chan->_field1 = *++pSrc; chan->_pSrc += 2; break; case 6: ++pSrc; if (chan->_fieldE) { chan->_pSrc += 2; } else { chan->_volume = *pSrc >> 1; updateFlag = true; chan->_pSrc += 2; } break; case 7: ++pSrc; if (!chan->_fieldE) { chan->_fieldA = *pSrc; chan->_field2 = *++pSrc; chan->_field9 = 1; } chan->_pSrc += 3; break; case 8: chan->_field1D = *++pSrc; chan->_pSrc += 2; break; case 9: { int v1 = *++pSrc; ++pSrc; int v2 = (v1 - 1) & getRandomNumber(); int v3 = pSrc[v2]; int v4 = pSrc[v1]; pSrc[v4 + v1 + 1] = v3; chan->_pSrc += v1 + 3; break; } case 10: ++pSrc; if (chan->_fieldE) { chan->_pSrc += 2; } else { chan->_field1E = *pSrc >> 1; updateFlag = true; chan->_pSrc += 2; } break; case 11: chan->_fieldD = *++pSrc; updateFlag = true; chan->_pSrc += 2; break; case 12: chan->_fieldC = *++pSrc; chan->_field3 = *++pSrc; chan->_fieldB = 1; chan->_pSrc += 2; break; case 13: ++pSrc; chan->_pSrc += 2; break; case 14: chan->_field1F = *++pSrc; chan->_pSrc += 2; break; default: break; } } } } if (chan->_field1) updateFNumber(); updateFlag = false; if (chan->_field9 || chan->_fieldB) { if (!--chan->_field9) { chan->_field9 = chan->_fieldA; if (chan->_field2) { int8 newVal = (int8)chan->_field2 + (int8)chan->_field1E; if (newVal < 0) { chan->_field9 = 0; newVal = 0; } else if (newVal > 63) { chan->_field9 = 0; newVal = 63; } chan->_field1E = newVal; updateFlag = true; } } if (!--chan->_fieldB) { chan->_fieldB = chan->_fieldC; if (chan->_field3) { chan->_fieldD = chan->_field3; updateFlag = true; } } if (updateFlag) updateActiveChannel(); } } ++_activeChannelNumber; } void ASound::updateOctave() { int reg = 0xB0 + _activeChannelNumber; write2(8, reg, _ports[reg] & 0xDF); } static int _vList1[] = { 0x200, 0x21E, 0x23F, 0x261, 0x285, 0x2AB, 0x2D4, 0x2FF, 0x32D, 0x35D, 0x390, 0x3C7 }; void ASound::updateChannelState() { updateActiveChannel(); if (_channelData[_activeChannelNumber]._field0) { if (_channelNum1 == _activeChannelNumber) _stateFlag = 0; if (_channelNum2 == _activeChannelNumber) _stateFlag = 1; if (!_stateFlag) { _stateFlag = 1; if (_v1) write2(8, 0xB0 + _channelNum1, _ports[0xB0 + _channelNum1] & 0xDF); _channelNum1 = _activeChannelNumber; _v1 = _channelData[_channelNum1]._field0; _freqMask1 = _channelData[_channelNum1]._freqMask; _freqBase1 = _channelData[_channelNum1]._freqBase; _v7 = _channelData[_channelNum1]._field6; } else { _stateFlag = 0; if (_v2) write2(8, 0xB0 + _channelNum2, _ports[0xB0 + _channelNum2] & 0xDF); _channelNum2 = _activeChannelNumber; _v2 = _channelData[_channelNum2]._field0; _freqMask2 = _channelData[_channelNum2]._freqMask; _freqBase2 = _channelData[_channelNum2]._freqBase; _v8 = _channelData[_channelNum2]._field6; } resultCheck(); } else { int reg = 0xA0 + _activeChannelNumber; int vTimes = (_activeChannelPtr->_field4 + _activeChannelPtr->_field1F) / 12; int vOffset = (_activeChannelPtr->_field4 + _activeChannelPtr->_field1F) % 12; int val = _vList1[vOffset] + _activeChannelPtr->_field1D; write2(8, reg, val & 0xFF); reg += 0x10; write2(8, reg, (_ports[reg] & 0x20) | (vTimes << 2) | (val >> 8)); write2(8, reg, _ports[reg] | 0x20); } } static const int outputIndexes[] = { 0, 3, 1, 4, 2, 5, 6, 9, 7, 10, 8, 11, 12, 15, 13, 16, 14, 17 }; static const int outputChannels[] = { 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21, 0 }; static const int volumeList[] = { 0x3F, 0x3F, 0x36, 0x31, 0x2D, 0x2A, 0x28, 0x26, 0x24, 0x22, 0x21, 0x20, 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x19, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x14, 0x14, 0x13, 0x12, 0x12, 0x11, 0x11, 0x10, 0x10, 0x0F, 0x0F, 0x0E, 0x0E, 0x0D, 0x0D, 0x0C, 0x0C, 0x0B, 0x0B, 0x0A, 0x0A, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; void ASound::updateActiveChannel() { int reg = 0x40 + outputChannels[outputIndexes[_activeChannelNumber * 2 + 1]]; int portVal = _ports[reg] & 0xFFC0; int newVolume = CLIP(_activeChannelPtr->_volume + _activeChannelPtr->_field1E, 0, 63); // Note: Original had a whole block not seeming to be used, since the initialisation // sets a variable to 5660h, and doesn't change it, so the branch is never taken int val = CLIP(newVolume - volumeList[_activeChannelPtr->_fieldD], 0, 63); val = (63 - val) | portVal; int val2 = CLIP(newVolume - volumeList[-(_activeChannelPtr->_fieldD - 127)], 0, 63); val2 = (63 - val2) | portVal; write2(0, reg, val); write2(2, reg, val2); } void ASound::loadSample(int sampleIndex) { _activeChannelReg = 0xB0 + _activeChannelNumber; write2(8, _activeChannelReg, _ports[_activeChannelReg] & 0xDF); _activeChannelReg = _activeChannelNumber; _samplePtr = &_samples[sampleIndex * 2]; _v11 = outputChannels[outputIndexes[_activeChannelReg * 2]]; processSample(); AdlibChannelData &cd = _channelData[_activeChannelNumber]; cd._field6 = _samplePtr->_field14; cd._freqBase = _samplePtr->_freqBase; cd._freqMask = _samplePtr->_freqMask; cd._field0 = _samplePtr->_fieldE; _samplePtr = &_samples[sampleIndex * 2 + 1]; _v11 = outputChannels[outputIndexes[_activeChannelReg * 2 + 1]]; processSample(); } void ASound::processSample() { // Write out vib flags and split point write2(8, 0x40 + _v11, 0x3F); int depthRhythm = (_ports[0xBD] & 0x3F) | (_amDep ? 0x80 : 0) | (_vibDep ? 0x40 : 0); write2(8, 0xBD, depthRhythm); write2(8, 8, _splitPoint ? 0x40 : 0); // Write out feedback & Alg int val = (_samplePtr->_feedback << 1) | (1 - _samplePtr->_alg); write2(8, 0xC0 + _activeChannelReg, val); // Write out attack/decay rate val = (_samplePtr->_attackRate << 4) | (_samplePtr->_decayRate & 0xF); write2(8, 0x60 + _v11, val); // Write out sustain level/release rate val = (_samplePtr->_sustainLevel << 4) | (_samplePtr->_releaseRate & 0xF); write2(8, 0x80 + _v11, val); // Write out misc flags val = (_samplePtr->_ampMod ? 0x80 : 0) | (_samplePtr->_vib ? 0x40 : 0) | (_samplePtr->_egTyp ? 0x20 : 0) | (_samplePtr->_ksr ? 0x10 : 0) | (_samplePtr->_freqMultiple & 0xF); write2(8, 0x20 + _v11, val); // Write out waveform select write2(8, 0xE0 + _v11, _samplePtr->_waveformSelect & 3); // Write out total level & scaling level val = -((_samplePtr->_totalLevel & 0x3F) - 0x3F) | (_samplePtr->_scalingLevel << 6); write2(8, 0x40 + _v11, val); } void ASound::updateFNumber() { int loReg = 0xA0 + _activeChannelNumber; int hiReg = 0xB0 + _activeChannelNumber; int val1 = (_ports[hiReg] & 0x1F) << 8; val1 += _ports[loReg] + _activeChannelPtr->_field1; write2(8, loReg, val1); int val2 = (_ports[hiReg] & 0x20) | (val1 >> 8); write2(8, hiReg, val2); } int ASound::readBuffer(int16 *buffer, const int numSamples) { Common::StackLock slock(_driverMutex); int32 samplesLeft = numSamples; memset(buffer, 0, sizeof(int16) * numSamples); while (samplesLeft) { if (!_samplesTillCallback) { poll(); flush(); _samplesTillCallback = _samplesPerCallback; _samplesTillCallbackRemainder += _samplesPerCallbackRemainder; if (_samplesTillCallbackRemainder >= CALLBACKS_PER_SECOND) { _samplesTillCallback++; _samplesTillCallbackRemainder -= CALLBACKS_PER_SECOND; } } int32 render = MIN(samplesLeft, _samplesTillCallback); samplesLeft -= render; _samplesTillCallback -= render; _opl->readBuffer(buffer, render); buffer += render; } return numSamples; } int ASound::command0() { bool isDisabled = _isDisabled; _isDisabled = true; for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i) _channels[i].reset(); _v1 = 0; _v2 = 0; _freqMask1 = _freqMask2 = 0; _freqBase1 = _freqBase2 = 0; _v7 = 0; _v8 = 0; // Reset Adlib port registers for (int reg = 0x4F; reg >= 0x40; --reg) write2(8, reg, 0x3F); for (int reg = 0xFF; reg >= 0x60; --reg) write2(8, reg, 0); for (int reg = 0x3F; reg > 0; --reg) write2(8, reg, 0); write2(8, 1, 0x20); _isDisabled = isDisabled; return 0; } int ASound::command1() { for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i) _channels[i].enable(0xFF); return 0; } int ASound::command2() { for (int i = 0; i < ADLIB_CHANNEL_MIDWAY; ++i) _channels[i].setPtr2(_nullData); return 0; } int ASound::command3() { for (int i = 0; i < ADLIB_CHANNEL_MIDWAY; ++i) _channels[i].enable(0xFF); return 0; } int ASound::command4() { for (int i = ADLIB_CHANNEL_MIDWAY; i < ADLIB_CHANNEL_COUNT; ++i) _channels[i].setPtr2(_nullData); return 0; } int ASound::command5() { for (int i = 5; i < ADLIB_CHANNEL_COUNT; ++i) _channels[i].enable(0xFF); return 0; } int ASound::command6() { _v9 = _v1; _v1 = 0; _v10 = _v2; _v2 = 0; channelOff(0x43); channelOff(0x44); channelOff(0x45); channelOff(0x4B); channelOff(0x4C); channelOff(0x4D); channelOff(0x53); channelOff(0x54); channelOff(0x55); return 0; } int ASound::command7() { channelOn(0x43, _channels[0]._volume); channelOn(0x44, _channels[1]._volume); channelOn(0x45, _channels[2]._volume); channelOn(0x4B, _channels[3]._volume); channelOn(0x4C, _channels[4]._volume); channelOn(0x4D, _channels[5]._volume); _v1 = _v9; _v2 = _v10; if (_v9 != _v10) resultCheck(); _isDisabled = 0; return _v10; } int ASound::command8() { int result = 0; for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i) result |= _channels[i]._activeCount; return result; } /*-----------------------------------------------------------------------*/ const ASound1::CommandPtr ASound1::_commandList[42] = { &ASound1::command0, &ASound1::command1, &ASound1::command2, &ASound1::command3, &ASound1::command4, &ASound1::command5, &ASound1::command6, &ASound1::command7, &ASound1::command8, &ASound1::command9, &ASound1::command10, &ASound1::command11, &ASound1::command12, &ASound1::command13, &ASound1::command14, &ASound1::command15, &ASound1::command16, &ASound1::command17, &ASound1::command18, &ASound1::command19, &ASound1::command20, &ASound1::command21, &ASound1::command22, &ASound1::command23, &ASound1::command24, &ASound1::command25, &ASound1::command26, &ASound1::command27, &ASound1::command28, &ASound1::command29, &ASound1::command30, &ASound1::command31, &ASound1::command32, &ASound1::command33, &ASound1::command34, &ASound1::command35, &ASound1::command36, &ASound1::command37, &ASound1::command38, &ASound1::command39, &ASound1::command40, &ASound1::command41 }; ASound1::ASound1(Audio::Mixer *mixer): ASound(mixer, "asound.001", 0x1520) { _cmd23Toggle = false; // Load sound samples _soundFile.seek(_dataOffset + 0x12C); for (int i = 0; i < 98; ++i) _samples.push_back(AdlibSample(_soundFile)); } int ASound1::command(int commandId, int param) { if (commandId > 41) return 0; _commandParam = param; _frameCounter = 0; return (this->*_commandList[commandId])(); } int ASound1::command9() { playSound(0xC68, 12); return 0; } int ASound1::command10() { byte *pData1 = loadData(0x130E, 48); if (!isSoundActive(pData1)) { command1(); _channels[0].load(pData1); _channels[1].load(loadData(0x133E, 392)); _channels[2].load(loadData(0x14C6, 46)); _channels[3].load(loadData(0x14F4, 48)); } return 0; } int ASound1::command11() { command111213(); _channels[0]._field1E = 0; _channels[1]._field1E = 0; return 0; } int ASound1::command12() { command111213(); _channels[0]._field1E = 40; _channels[1]._field1E = 0; return 0; } int ASound1::command13() { command111213(); _channels[0]._field1E = 40; _channels[1]._field1E = 50; return 0; } int ASound1::command14() { playSound(0x1216, 248); return 0; } int ASound1::command15() { byte *pData1 = loadData(0x1524, 152); if (!isSoundActive(pData1)) { command1(); _channels[4].load(pData1); _channels[5].load(loadData(0x15BC, 94)); _channels[6].load(loadData(0x161A, 94)); _channels[7].load(loadData(0x1678, 42)); _channels[8].load(loadData(0x16A2, 42)); } return 0; } int ASound1::command16() { playSound(0xC74, 14); return 0; } int ASound1::command17() { playSound(0xE9A, 10); return 0; } int ASound1::command18() { command1(); playSound(0xCA6, 20); return 0; } int ASound1::command19() { command1(); playSound(0xCBA, 74); return 0; } int ASound1::command20() { byte *pData = loadData(0xD18, 28); if (!isSoundActive(pData)) playSoundData(pData); return 0; } int ASound1::command21() { playSound(0xD04, 20); return 0; } int ASound1::command22() { byte *pData = loadData(0xD34, 10); pData[6] = (getRandomNumber() & 7) + 85; if (!isSoundActive(pData)) playSoundData(pData); return 0; } int ASound1::command23() { _cmd23Toggle = !_cmd23Toggle; playSound(_cmd23Toggle ? 0xD3E : 0xD46, 8); return 0; } int ASound1::command24() { playSound(0xD4E, 18); playSound(0xD60, 20); playSound(0xD74, 14); return 0; } int ASound1::command25() { byte *pData = loadData(0xD82, 16); if (!isSoundActive(pData)) playSoundData(pData); return 0; } int ASound1::command26() { byte *pData = loadData(0xEEC, 10); pData[5] = (command2627293032() + 0x7F) & 0xFF; if (!isSoundActive(pData)) _channels[6].load(pData); return 0; } int ASound1::command27() { byte *pData = loadData(0xEE2, 10); pData[5] = (command2627293032() + 0x40) & 0xFF; if (!isSoundActive(pData)) _channels[7].load(pData); return 0; } int ASound1::command28() { playSound(0xD92, 28); return 0; } int ASound1::command29() { byte *pData = loadData(0xC82, 36); byte v = (command2627293032() + 0x40) & 0xFF; pData[7] = pData[13] = pData[21] = pData[27] = v; if (!isSoundActive(pData)) playSoundData(pData, 0); return 0; } int ASound1::command30() { byte *pData = loadData(0xEA6, 16); pData[7] = (command2627293032() + 0x40) & 0xFF; if (!isSoundActive(pData)) playSoundData(pData, 0); return 0; } int ASound1::command31() { byte *pData = loadData(0xDAE, 14); if (!isSoundActive(pData)) playSoundData(pData); return 0; } int ASound1::command32() { byte *pData = loadData(0xEB4, 46); int v = command2627293032() + 0x40; pData[9] = pData[17] = pData[25] = pData[33] = v & 0xFF; pData[11] = pData[19] = pData[27] = pData[35] = v >> 8; if (!isSoundActive(pData)) playSoundData(pData, 0); return 0; } int ASound1::command33() { playSound(0xDBC, 10); playSound(0xDC6, 10); return 0; } int ASound1::command34() { int v = getRandomNumber() & 0x20; if (!v) v = 0x60; byte *pData = loadData(0xDD0, 22); pData[8] = pData[15] = v; playSoundData(pData); return 0; } int ASound1::command35() { playSound(0xDE6, 16); return 0; } int ASound1::command36() { playSound(0xE10, 10); command34(); return 0; } int ASound1::command37() { playSound(0xE1A, 14); return 0; } int ASound1::command38() { playSound(0xE28, 114); return 0; } int ASound1::command39() { byte *pData1 = loadData(0x16CC, 82); if (!isSoundActive(pData1)) { _channels[5].load(pData1); _channels[6].load(loadData(0x171E, 30)); _channels[7].load(loadData(0x173C, 40)); _channels[8].load(loadData(0x1764, 64)); } return 0; } int ASound1::command40() { playSound(0xDF6, 26); return 0; } int ASound1::command41() { playSound(0xC32, 34); playSound(0xC54, 20); return 0; } void ASound1::command111213() { byte *pData1 = loadData(0xEF6, 408); if (!isSoundActive(pData1)) { command1(); _channels[0].load(pData1); _channels[1].load(loadData(0x108E, 266)); _channels[2].load(loadData(0x1198, 66)); _channels[2].load(loadData(0x11DA, 60)); } } int ASound1::command2627293032() { return (_commandParam > 0x40) ? _commandParam - 0x40 : _commandParam & 0xff00; } /*-----------------------------------------------------------------------*/ const ASound2::CommandPtr ASound2::_commandList[44] = { &ASound2::command0, &ASound2::command1, &ASound2::command2, &ASound2::command3, &ASound2::command4, &ASound2::command5, &ASound2::command6, &ASound2::command7, &ASound2::command8, &ASound2::command9, &ASound2::command10, &ASound2::command11, &ASound2::command12, &ASound2::command13, &ASound2::command14, &ASound2::command15, &ASound2::command16, &ASound2::command17, &ASound2::command18, &ASound2::command19, &ASound2::command20, &ASound2::command21, &ASound2::command22, &ASound2::command23, &ASound2::command24, &ASound2::command25, &ASound2::command26, &ASound2::command27, &ASound2::command28, &ASound2::command29, &ASound2::command30, &ASound2::command31, &ASound2::command32, &ASound2::command33, &ASound2::command34, &ASound2::command35, &ASound2::command36, &ASound2::command37, &ASound2::command38, &ASound2::command39, &ASound2::command40, &ASound2::command41, &ASound2::command42, &ASound2::command43 }; ASound2::ASound2(Audio::Mixer *mixer) : ASound(mixer, "asound.002", 0x15E0) { _command12Param = 0xFD; // Load sound samples _soundFile.seek(_dataOffset + 0x144); for (int i = 0; i < 164; ++i) _samples.push_back(AdlibSample(_soundFile)); } int ASound2::command(int commandId, int param) { if (commandId > 43) return 0; _frameCounter = 0; return (this->*_commandList[commandId])(); } int ASound2::command0() { _command12Param = 0xFD; return ASound::command0(); } int ASound2::command9() { byte *pData1 = loadData(0x1094, 376); if (!isSoundActive(pData1)) { command1(); _channels[0].load(pData1); _channels[2].load(loadData(0x123E, 130)); command9Randomize(); _channels[1].load(loadData(0x120C, 50)); } return 0; } void ASound2::command9Randomize() { // Randomization int v; while (((v = getRandomNumber()) & 0x3F) > 36) ; byte *pData = loadData(0x120C, 50); command9Apply(pData, v + 20, -1); command9Apply(pData + 1, 10 - ((v + 1) / 6), 1); } void ASound2::command9Apply(byte *data, int val, int incr) { data += 8; for (int ctr = 0; ctr < 10; ++ctr, data += 4, val += incr) { *data = val; } } int ASound2::command10() { byte *pData1 = loadData(0x12C0, 60); if (!isSoundActive(pData1)) { command1(); _channels[0].load(pData1); _channels[1].load(loadData(0x12FC, 318)); _channels[2].load(loadData(0x143A, 110)); } return 0; } int ASound2::command11() { byte *pData = loadData(0x14A8, 170); if (!isSoundActive(pData)) { playSoundData(pData); playSoundData(loadData(0x1552, 1802)); playSoundData(loadData(0x1C5C, 716)); playSoundData(loadData(0x1F28, 106)); } return 0; } int ASound2::command12() { _command12Param += 26; byte v = _command12Param & 0x7f; byte *pData = loadData(0x4A5E, 38); pData[5] = pData[20] = v; playSoundData(pData); pData = loadData(0x4A84, 30); pData[5] = pData[18] = v; playSoundData(pData); return 0; } int ASound2::command13() { playSoundData(loadData(0x4AA2, 20)); playSoundData(loadData(0x4AB6, 20)); return 0; } int ASound2::command14() { playSound(0x4ACA, 40); playSound(0x4AF2, 42); return 0; } int ASound2::command15() { byte *pData1 = loadData(0x1F92, 1074); if (!isSoundActive(pData1)) { command1(); playSoundData(pData1); playSound(0x23C4, 1050); playSound(0x27DE, 58); playSound(0x2818, 712); playSound(0x2AE0, 256); } return 0; } int ASound2::command16() { byte *pData1 = loadData(0x3960, 280); if (!isSoundActive(pData1)) { command1(); playSoundData(pData1); playSound(0x3A78, 266); playSound(0x3B72, 322); playSound(0x3CC4, 488); playSound(0x3EAC, 104); playSound(0x3F14, 104); } return 0; } int ASound2::command17() { byte *pData1 = loadData(0x3F7C, 432); if (!isSoundActive(pData1)) { command1(); playSoundData(pData1); playSound(0x412C, 422); playSound(0x42D2, 424); playSound(0x447A, 418); } return 0; } static const int command18_list[16][2] = { { 0x337C, 28 }, { 0x3398, 26 }, { 0x33B2, 26 }, { 0x33CC, 26 }, { 0x33E6, 56 }, { 0x341E, 46 }, { 0x344C, 56 }, { 0x3484, 22 }, { 0x349A, 38 }, { 0x34C0, 62 }, { 0x34FE, 26 }, { 0x3518, 26 }, { 0x3532, 26 }, { 0x354C, 26 }, { 0x3566, 32 }, { 0x3586, 24 } }; int ASound2::command18() { if (_channels[3]._activeCount == 0) { int idx = (getRandomNumber() & 0x1E) >> 1; byte *pData = loadData(command18_list[idx][0], command18_list[idx][1]); _channels[3].load(pData); } return 0; } int ASound2::command19() { byte *pData1 = loadData(0x2BE0, 366); if (!isSoundActive(pData1)) { command1(); playSoundData(pData1); playSound(0x2D4E, 460); playSound(0x2F1A, 266); playSound(0x3024, 328); playSound(0x316C, 162); playSound(0x320E, 366); } return 0; } int ASound2::command20() { playSound(0x4A36, 40); return 0; } int ASound2::command21() { playSound(0x49DE, 16); playSound(0x49EE, 16); playSound(0x49FF, 16); return 0; } int ASound2::command22() { playSound(0x4A0E, 24); playSound(0x4A26, 16); return 0; } int ASound2::command23() { playSound(0x49B6, 16); return 0; } int ASound2::command24() { playSound(0x49C6, 24); return 0; } int ASound2::command25() { playSound(0x49AC, 10); return 0; } int ASound2::command26() { playSound(0x498A, 14); playSound(0x4998, 20); return 0; } int ASound2::command27() { playSound(0x4912, 80); playSound(0x4962, 40); return 0; } int ASound2::command28() { playSound(0x48E8, 28); playSound(0x4904, 14); return 0; } int ASound2::command29() { playSound(0x48B2, 22); return 0; } int ASound2::command30() { playSound(0x4870, 22); playSound(0x4886, 22); playSound(0x489C, 22); return 0; } int ASound2::command31() { playSound(0x482E, 22); playSound(0x4844, 22); playSound(0x489C, 22); return 0; } int ASound2::command32() { playSound(0x46E8, 10); return 0; } int ASound2::command33() { playSound(0x46D8, 16); return 0; } int ASound2::command34() { playSound(0x46C8, 16); return 0; } int ASound2::command35() { playSound(0x46B2, 22); return 0; } int ASound2::command36() { playSound(0x4624, 16); return 0; } int ASound2::command37() { playSound(0x4674, 20); playSound(0x4688, 32); playSound(0x46A8, 10); return 0; } int ASound2::command38() { byte *pData1 = loadData(0x359E, 202); if (!isSoundActive(pData1)) { command1(); playSoundData(pData1); playSound(0x3668, 220); playSound(0x3744, 124); playSound(0x37C0, 162); playSound(0x3862, 78); playSound(0x38B0, 176); } return 0; } int ASound2::command39() { byte *pData = loadData(0x466A, 10); pData[6] = (getRandomNumber() & 7) + 85; playSoundData(pData); return 0; } int ASound2::command40() { playSound(0x4634, 34); playSound(0x4656, 20); return 0; } int ASound2::command41() { playSound(0x48C8, 32); return 0; } int ASound2::command42() { playSound(0x46F2, 156); playSound(0x478E, 160); return 0; } int ASound2::command43() { playSound(0x4B1C, 40); playSound(0x4B44, 41); return 0; } /*-----------------------------------------------------------------------*/ const ASound3::CommandPtr ASound3::_commandList[61] = { &ASound3::command0, &ASound3::command1, &ASound3::command2, &ASound3::command3, &ASound3::command4, &ASound3::command5, &ASound3::command6, &ASound3::command7, &ASound3::command8, &ASound3::command9, &ASound3::command10, &ASound3::command11, &ASound3::nullCommand, &ASound3::command13, &ASound3::command14, &ASound3::command15, &ASound3::command16, &ASound3::command17, &ASound3::command18, &ASound3::command19, &ASound3::command20, &ASound3::command21, &ASound3::command22, &ASound3::command23, &ASound3::command24, &ASound3::command25, &ASound3::command26, &ASound3::command27, &ASound3::command28, &ASound3::command29, &ASound3::command30, &ASound3::command31, &ASound3::command32, &ASound3::command33, &ASound3::command34, &ASound3::command35, &ASound3::command36, &ASound3::command37, &ASound3::command38, &ASound3::command39, &ASound3::command40, &ASound3::command41, &ASound3::command42, &ASound3::command43, &ASound3::command44, &ASound3::command45, &ASound3::command46, &ASound3::command47, &ASound3::nullCommand, &ASound3::command49, &ASound3::command50, &ASound3::command51, &ASound3::nullCommand, &ASound3::nullCommand, &ASound3::nullCommand, &ASound3::nullCommand, &ASound3::nullCommand, &ASound3::command57, &ASound3::nullCommand, &ASound3::command59, &ASound3::command60 }; ASound3::ASound3(Audio::Mixer *mixer) : ASound(mixer, "asound.003", 0x15B0) { _command39Flag = false; // Load sound samples _soundFile.seek(_dataOffset + 0x122); for (int i = 0; i < 192; ++i) _samples.push_back(AdlibSample(_soundFile)); } int ASound3::command(int commandId, int param) { if (commandId > 60) return 0; _commandParam = param; _frameCounter = 0; return (this->*_commandList[commandId])(); } int ASound3::command9() { AdlibChannel::_channelsEnabled = _commandParam != 0; return 0; } int ASound3::command10() { byte *pData1 = loadData(0x13EA, 254); if (!isSoundActive(pData1)) { command1(); _channels[0].load(pData1); _channels[1].load(loadData(0X14E8, 452)); _channels[2].load(loadData(0x16AC, 396)); _channels[3].load(loadData(0x1838, 118)); _channels[4].load(loadData(0x18AE, 74)); } return 0; } int ASound3::command11() { byte *pData1 = loadData(0x2B84, 596); if (!isSoundActive(pData1)) { command1(); _channels[0].load(pData1); _channels[1].load(loadData(0x2DD8, 562)); _channels[2].load(loadData(0x300A, 1694)); _channels[3].load(loadData(0x36A8, 1100)); _channels[4].load(loadData(0x3AF4, 420)); _channels[5].load(loadData(0x3C98, 1516)); } return 0; } int ASound3::command13() { byte *pData1 = loadData(0x4470, 64); if (!isSoundActive(pData1)) { command1(); playSoundData(pData1); playSound(0x44B0, 64); playSound(0x44F0, 64); playSound(0x4530, 64); playSound(0x4570, 64); playSound(0X45b0, 64); } return 0; } int ASound3::command14() { byte *pData1 = loadData(0X45F0, 36); if (!isSoundActive(pData1)) { command1(); playSoundData(pData1); playSound(0x4614, 36); playSound(0x4638, 36); playSound(0x465C, 32); playSound(0x467C, 76); playSound(0x46C8, 74); } return 0; } int ASound3::command15() { _channels[3].load(loadData(0x36A8, 1100)); _channels[4].load(loadData(0x3AF4, 420)); _channels[5].load(loadData(0x3C98, 1516)); _channels[3]._field20 = 0xDD; _channels[4]._field20 = 0xDD; _channels[5]._field20 = 0xDD; return 0; } int ASound3::command16() { byte *pData1 = loadData(0x4712, 398); if (!isSoundActive(pData1)) { command1(); _channels[0].load(pData1); _channels[1].load(loadData(0x48A0, 354)); _channels[2].load(loadData(0x4A02, 410)); _channels[3].load(loadData(0x4B9C, 392)); } return 0; } int ASound3::command17() { byte *pData1 = loadData(0x18F8, 400); if (!isSoundActive(pData1)) { command1(); _channels[0].load(pData1); _channels[1].load(loadData(0x1A88, 680)); _channels[2].load(loadData(0x1D30, 478)); _channels[3].load(loadData(0x1F0E, 1146)); _channels[4].load(loadData(0x2388, 1006)); _channels[5].load(loadData(0x2776, 1038)); } return 0; } int ASound3::command18() { byte *pData1 = loadData(0x4284, 142); if (!isSoundActive(pData1)) { command1(); playSoundData(pData1); playSound(0x4312, 172); playSound(0x43BE, 88); playSound(0x4416, 90); } return 0; } int ASound3::command19() { playSound(0x4F6, 8); return 0; } int ASound3::command20() { playSound(0x4F1C, 10); return 0; } int ASound3::command21() { playSound(0x4F2E, 8); return 0; } int ASound3::command22() { playSound(0x4F36, 16); return 0; } int ASound3::command23() { playSound(0x4F50, 10); playSound(0x4F46, 10); return 0; } int ASound3::command24() { // WORKAROUND: Original calls isSoundActive without loading data pointer byte *pData = loadData(0x4EFC, 12); if (!isSoundActive(pData)) { int v; while ((v = (getRandomNumber() & 0x3F)) > 45) ; pData[6] = v + 19; playSoundData(pData); } return 0; } int ASound3::command25() { playSound(0x4EE6, 22); return 0; } int ASound3::command26() { playSound(0x4F5A, 8); return 0; } int ASound3::command27() { playSound(0x4DA2, 34); playSound(0x4DC4, 20); return 0; } int ASound3::command28() { playSound(0x4F72, 10); playSound(0x4F72, 10); return 0; } int ASound3::command29() { playSound(0x4F72, 10); playSound(0x4F72, 10); return 0; } int ASound3::command30() { playSound(0x4E5A, 22); playSound(0x4E70, 22); playSound(0x4E86, 22); return 0; } int ASound3::command31() { playSound(0x4F7C, 40); return 0; } int ASound3::command32() { playSound(0x4ED2, 10); return 0; } int ASound3::command33() { playSound(0x4EC2, 16); return 0; } int ASound3::command34() { playSound(0x4EB2, 16); return 0; } int ASound3::command35() { playSound(0x4E9C, 22); return 0; } int ASound3::command36() { playSound(0x4D2C, 16); return 0; } int ASound3::command37() { playSound(0x4E1E, 20); playSound(0x4E32, 30); playSound(0x4E50, 10); return 0; } int ASound3::command38() { playSound(0x4FAC, 10); return 0; } int ASound3::command39() { _command39Flag = !_command39Flag; if (_command39Flag) { playSound(0x4FD0, 8); } else { playSound(0x4FD8, 8); } return 0; } int ASound3::command40() { _command39Flag = !_command39Flag; if (_command39Flag) { playSound(0x4EE0, 8); } else { playSound(0x4EE8, 8); } return 0; } int ASound3::command41() { playSound(0x4F08, 20); return 0; } int ASound3::command42() { playSound(0x4DD8, 28); playSound(0x4DF4, 42); return 0; } int ASound3::command43() { playSound(0x4FB6, 12); playSound(0x4FC2, 14); return 0; } int ASound3::command44() { playSound(0x4FFE, 14); return 0; } int ASound3::command45() { playSound(0x500C, 14); return 0; } int ASound3::command46() { playSound(0x4D78, 14); playSound(0x4D86, 14); playSound(0x4D94, 14); return 0; } int ASound3::command47() { playSound(0x4D62, 8); playSound(0x4D6A, 14); return 0; } int ASound3::command49() { playSound(0x4D62, 8); playSound(0x4D6A, 14); return 0; } int ASound3::command50() { playSound(0x4D3C, 14); playSound(0x4D4A, 14); playSound(0x4D58, 10); return 0; } int ASound3::command51() { playSound(0x4FF0, 14); return 0; } int ASound3::command57() { byte *pData = loadData(0x4EDC, 10); pData[6] = (getRandomNumber() & 7) + 85; playSoundData(pData); return 0; } int ASound3::command59() { playSound(0x4F62, 16); return 0; } int ASound3::command60() { playSound(0x4FA4, 8); return 0; } /*-----------------------------------------------------------------------*/ const ASound4::CommandPtr ASound4::_commandList[61] = { &ASound4::command0, &ASound4::command1, &ASound4::command2, &ASound4::command3, &ASound4::command4, &ASound4::command5, &ASound4::command6, &ASound4::command7, &ASound4::command8, &ASound4::nullCommand, &ASound4::command10, &ASound4::nullCommand, &ASound4::command12, &ASound4::nullCommand, &ASound4::nullCommand, &ASound4::nullCommand, &ASound4::nullCommand, &ASound4::nullCommand, &ASound4::nullCommand, &ASound4::command19, &ASound4::command20, &ASound4::command21, &ASound4::nullCommand, &ASound4::nullCommand, &ASound4::command24, &ASound4::nullCommand, &ASound4::nullCommand, &ASound4::command27, &ASound4::nullCommand, &ASound4::nullCommand, &ASound4::command30, &ASound4::nullCommand, &ASound4::command32, &ASound4::command33, &ASound4::command34, &ASound4::command35, &ASound4::command36, &ASound4::command37, &ASound4::command38, &ASound4::nullCommand, &ASound4::nullCommand, &ASound4::nullCommand, &ASound4::nullCommand, &ASound4::command43, &ASound4::nullCommand, &ASound4::nullCommand, &ASound4::nullCommand, &ASound4::nullCommand, &ASound4::nullCommand, &ASound4::nullCommand, &ASound4::nullCommand, &ASound4::nullCommand, &ASound4::nullCommand, &ASound4::nullCommand, &ASound4::nullCommand, &ASound4::nullCommand, &ASound4::nullCommand, &ASound4::command57, &ASound4::nullCommand, &ASound4::command59, &ASound4::command60 }; ASound4::ASound4(Audio::Mixer *mixer) : ASound(mixer, "asound.004", 0x14F0) { // Load sound samples _soundFile.seek(_dataOffset + 0x122); for (int i = 0; i < 210; ++i) _samples.push_back(AdlibSample(_soundFile)); } int ASound4::command(int commandId, int param) { if (commandId > 60) return 0; _commandParam = param; _frameCounter = 0; return (this->*_commandList[commandId])(); } int ASound4::command10() { byte *pData = loadData(0x22AA, 254); if (!isSoundActive(pData)) { command1(); _channels[0].load(pData); _channels[1].load(loadData(0x23A8, 452)); _channels[2].load(loadData(0x256C, 396)); _channels[3].load(loadData(0x26F8, 118)); _channels[4].load(loadData(0x276E, 74)); } return 0; } int ASound4::command12() { byte *pData = loadData(0x16A8, 550); if (!isSoundActive(pData)) { command1(); _channels[0].load(pData); _channels[1].load(loadData(0x18CE, 442)); _channels[2].load(loadData(0x1A88, 298)); _channels[3].load(loadData(0x1BB2, 354)); _channels[4].load(loadData(0x1D14, 572)); _channels[4].load(loadData(0x1F50, 560)); } int v = (_commandParam > 0x40) ? _commandParam - 0x40 : 0; v += 0xB5; for (int channelNum = 0; channelNum < 6; ++channelNum) _channels[channelNum]._field20 = v; return 0; } int ASound4::command19() { playSound(0x28EC, 8); return 0; } int ASound4::command20() { playSound(0x28E2, 10); return 0; } int ASound4::command21() { playSound(0x27C0, 8); return 0; } int ASound4::command24() { int v; while ((v = (getRandomNumber() & 0x3F)) > 45) ; byte *pData = loadData(0x28D6, 12); pData[6] = v + 19; playSoundData(pData); return 0; } int ASound4::command27() { playSound(0x27D8, 34); playSound(0x27FA, 20); return 0; } int ASound4::command30() { playSound(0x284A, 22); playSound(0x2860, 22); playSound(0x2876, 22); return 0; } int ASound4::command32() { playSound(0x28C2, 10); return 0; } int ASound4::command33() { playSound(0x28B2, 16); return 0; } int ASound4::command34() { playSound(0x28A2, 16); return 0; } int ASound4::command35() { playSound(0x288C, 22); return 0; } int ASound4::command36() { playSound(0x27C8, 16); return 0; } int ASound4::command37() { playSound(0x280E, 20); playSound(0x2822, 30); playSound(0x2840, 10); return 0; } int ASound4::command38() { playSound(0x2904, 10); return 0; } int ASound4::command43() { playSound(0x290E, 12); playSound(0x291A, 14); return 0; } int ASound4::command52() { byte *pData = loadData(0x23A8, 452); if (_channels[1]._ptr1 == pData) { pData = loadData(0x146E, 570); if (!isSoundActive(pData)) { _channels[0].load(pData); _channels[1]._field20 = 0xD8; _channels[2]._field20 = 0xD8; } } return 0; } int ASound4::command53() { method1(); _channels[0]._field20 = 0; return 0; } int ASound4::command54() { method1(); _channels[1]._field20 = 0; _channels[2]._field20 = 0; return 0; } int ASound4::command55() { method1(); _channels[3]._field20 = 0; _channels[4]._field20 = 0; return 0; } int ASound4::command56() { method1(); _channels[5]._field20 = 0; return 0; } int ASound4::command57() { int v = (getRandomNumber() & 7) + 85; byte *pData = loadData(0x28CC, 10); pData[6] = v; playSoundData(pData); return 0; } int ASound4::command58() { byte *pData = loadData(0x146E, 570); if (_channels[1]._ptr1 == pData) { _channels[0].load(loadData(0x22AA, 254)); _channels[1]._field20 = 0; _channels[2]._field20 = 0; } return 0; } int ASound4::command59() { playSound(0x28F4, 8); return 0; } int ASound4::command60() { playSound(0x28FC, 8); return 0; } void ASound4::method1() { byte *pData = loadData(0x2180, 58); if (!isSoundActive(pData)) { command1(); _channels[0].load(pData); _channels[1].load(loadData(0x21BA, 48)); _channels[2].load(loadData(0x21EA, 50)); _channels[3].load(loadData(0x221C, 40)); _channels[4].load(loadData(0x2244, 28)); _channels[5].load(loadData(0x2260, 74)); for (int channel = 0; channel < 6; ++channel) _channels[channel]._field20 = 0xB5; } } /*-----------------------------------------------------------------------*/ const ASound5::CommandPtr ASound5::_commandList[42] = { &ASound5::command0, &ASound5::command1, &ASound5::command2, &ASound5::command3, &ASound5::command4, &ASound5::command5, &ASound5::command6, &ASound5::command7, &ASound5::command8, &ASound5::command9, &ASound5::command10, &ASound5::command11, &ASound5::command11, &ASound5::command13, &ASound5::command14, &ASound5::command15, &ASound5::command16, &ASound5::command17, &ASound5::command18, &ASound5::command19, &ASound5::command20, &ASound5::command21, &ASound5::command22, &ASound5::command23, &ASound5::command11, &ASound5::command11, &ASound5::command26, &ASound5::command27, &ASound5::command28, &ASound5::command29, &ASound5::command30, &ASound5::command31, &ASound5::command32, &ASound5::command33, &ASound5::command34, &ASound5::command35, &ASound5::command36, &ASound5::command37, &ASound5::command38, &ASound5::command39, &ASound5::command40, &ASound5::command41 }; ASound5::ASound5(Audio::Mixer *mixer) : ASound(mixer, "asound.002", 0x15E0) { // Load sound samples _soundFile.seek(_dataOffset + 0x144); for (int i = 0; i < 164; ++i) _samples.push_back(AdlibSample(_soundFile)); } int ASound5::command(int commandId, int param) { if (commandId > 41) return 0; _frameCounter = 0; return (this->*_commandList[commandId])(); } int ASound5::command9() { byte *pData = loadData(0x2114, 10); pData[6] = (getRandomNumber() & 7) + 85; playSoundData(pData); return 0; } int ASound5::command10() { playSound(0x211E, 10); return 0; } int ASound5::command11() { playSound(0x2016, 10); return 0; } int ASound5::command13() { playSound(0x2154, 10); return 0; } int ASound5::command14() { playSound(0x21DC, 22); return 0; } int ASound5::command15() { byte *pData = loadData(0x21DC, 22); if (_channels[0]._ptr1 == pData) { pData = loadData(0x1F2, 12); _channels[0]._soundData = pData; _channels[0]._field17 = 1; _channels[0]._field19 = 1; } return 0; } int ASound5::command16() { playSound(0x214C, 8); return 0; } int ASound5::command17() { playSound(0x2142, 10); return 0; } int ASound5::command18() { playSound(0x21A2, 22); return 0; } int ASound5::command19() { playSound(0x2190, 18); return 0; } int ASound5::command20() { playSound(0x2170, 16); return 0; } int ASound5::command21() { playSound(0x2180, 16); return 0; } int ASound5::command22() { playSound(0x2168, 8); return 0; } int ASound5::command23() { playSound(0x215E, 10); return 0; } int ASound5::command26() { playSound(0x21B8, 12); return 0; } int ASound5::command27() { playSound(0x21C4, 24); return 0; } int ASound5::command28() { playSound(0x2020, 34); playSound(0x4904, 20); return 0; } int ASound5::command29() { byte *pData = loadData(0x17C, 312); if (!isSoundActive(pData)) { command1(); _channels[0].load(pData); _channels[1].load(loadData(0x1864, 304)); _channels[2].load(loadData(0x1994, 222)); _channels[3].load(loadData(0x1864, 304)); _channels[4].load(loadData(0x1994, 222)); } return 0; } int ASound5::command30() { playSound(0x2092, 22); playSound(0x20A8, 22); playSound(0x20BE, 22); return 0; } int ASound5::command31() { playSound(0x2128, 22); playSound(0x2134, 14); return 0; } int ASound5::command32() { playSound(0x210A, 10); return 0; } int ASound5::command33() { playSound(0x20FA, 16); return 0; } int ASound5::command34() { playSound(0x20EA, 16); return 0; } int ASound5::command35() { playSound(0x20D4, 22); return 0; } int ASound5::command36() { playSound(0x2006, 16); return 0; } int ASound5::command37() { playSound(0x2056, 20); playSound(0x206A, 30); playSound(0x2088, 10); return 0; } int ASound5::command38() { byte *pData1 = loadData(0x14F2, 570); if (_channels[3]._ptr1 == pData1) { _channels[3].load(loadData(0x1A72, 522)); _channels[3].load(loadData(0x1C7C, 874)); } return 0; } int ASound5::command39() { playSound(0x1FEE, 8); return 0; } int ASound5::command40() { playSound(0x1FF6, 16); return 0; } int ASound5::command41() { byte *pData1 = loadData(0x14F2, 570); if (!isSoundActive(pData1)) { byte *pData2 = loadData(0x1A72, 522); if (_channels[3]._ptr1 == pData2) { _channels[3].load(pData1); _channels[4].load(loadData(0x1FE6, 8)); } } return 0; } /*-----------------------------------------------------------------------*/ const ASound6::CommandPtr ASound6::_commandList[30] = { &ASound6::command0, &ASound6::command1, &ASound6::command2, &ASound6::command3, &ASound6::command4, &ASound6::command5, &ASound6::command6, &ASound6::command7, &ASound6::command8, &ASound6::command9, &ASound6::command10, &ASound6::command11, &ASound6::command11, &ASound6::command13, &ASound6::command14, &ASound6::command15, &ASound6::command16, &ASound6::command17, &ASound6::command18, &ASound6::command19, &ASound6::command20, &ASound6::command21, &ASound6::command22, &ASound6::command23, &ASound6::command11, &ASound6::command11, &ASound6::nullCommand, &ASound6::nullCommand, &ASound6::nullCommand, &ASound6::command29 }; ASound6::ASound6(Audio::Mixer *mixer) : ASound(mixer, "asound.006", 0x1390) { // Load sound samples _soundFile.seek(_dataOffset + 0x122); for (int i = 0; i < 200; ++i) _samples.push_back(AdlibSample(_soundFile)); } int ASound6::command(int commandId, int param) { if (commandId > 29) return 0; _frameCounter = 0; return (this->*_commandList[commandId])(); } int ASound6::command9() { byte *pData = loadData(0x2194, 10); pData[6] = (getRandomNumber() & 7) + 85; playSoundData(pData); return 0; } int ASound6::command10() { playSound(0x2224, 24); return 0; } int ASound6::command11() { playSound(0x2202, 34); return 0; } int ASound6::command12() { playSound(0x2246, 8); return 0; } int ASound6::command13() { playSound(0x2298, 28); return 0; } int ASound6::command14() { playSound(0x22B4, 27); return 0; } int ASound6::command15() { playSound(0x219E, 12); return 0; } int ASound6::command16() { playSound(0x21AA, 22); playSound(0x21C0, 12); return 0; } int ASound6::command17() { playSound(0x21CC, 54); return 0; } int ASound6::command18() { playSound(0x2270, 16); return 0; } int ASound6::command19() { playSound(0x2280, 16); return 0; } int ASound6::command20() { playSound(0x223C, 10); return 0; } int ASound6::command21() { playSound(0x224E, 34); return 0; } int ASound6::command22() { playSound(0x2290, 8); return 0; } int ASound6::command23() { playSound(0x215E, 34); playSound(0x2180, 20); return 0; } int ASound6::command24() { byte *pData1 = loadData(0x1D54, 540); if (!isSoundActive(pData1)) { command1(); _channels[0].load(pData1); _channels[1].load(loadData(0x1F70, 52)); _channels[2].load(loadData(0x1FA4, 430)); } return 0; } int ASound6::command25() { playSound(0x2152, 12); return 0; } int ASound6::command29() { byte *pData1 = loadData(0x149A, 312); if (!isSoundActive(pData1)) { command1(); _channels[0].load(pData1); _channels[1].load(loadData(0x15D2, 304)); _channels[2].load(loadData(0x1702, 222)); _channels[3].load(loadData(0x17E0, 522)); _channels[4].load(loadData(0x19EA, 874)); } return 0; } /*-----------------------------------------------------------------------*/ const ASound7::CommandPtr ASound7::_commandList[38] = { &ASound7::command0, &ASound7::command1, &ASound7::command2, &ASound7::command3, &ASound7::command4, &ASound7::command5, &ASound7::command6, &ASound7::command7, &ASound7::command8, &ASound7::command9, &ASound7::nullCommand, &ASound7::nullCommand, &ASound7::nullCommand, &ASound7::nullCommand, &ASound7::nullCommand, &ASound7::command15, &ASound7::command16, &ASound7::command16, &ASound7::command18, &ASound7::command19, &ASound7::command20, &ASound7::command21, &ASound7::command22, &ASound7::command23, &ASound7::command24, &ASound7::command25, &ASound7::command26, &ASound7::command27, &ASound7::command28, &ASound7::nullCommand, &ASound7::command30, &ASound7::nullCommand, &ASound7::command32, &ASound7::command33, &ASound7::command34, &ASound7::command35, &ASound7::command36, &ASound7::command37 }; ASound7::ASound7(Audio::Mixer *mixer) : ASound(mixer, "asound.007", 0x1460) { // Load sound samples _soundFile.seek(_dataOffset + 0x122); for (int i = 0; i < 214; ++i) _samples.push_back(AdlibSample(_soundFile)); } int ASound7::command(int commandId, int param) { if (commandId > 38) return 0; _frameCounter = 0; return (this->*_commandList[commandId])(); } int ASound7::command9() { byte *pData1 = loadData(0x2992, 122); if (!isSoundActive(pData1)) { _channels[0].load(pData1); _channels[1].load(loadData(0x2A0C, 76)); _channels[2].load(loadData(0x2A58, 122)); _channels[3].load(loadData(0x2AD2, 38)); } return 0; } int ASound7::command15() { byte *pData = loadData(0x2B3E, 10); pData[6] = (getRandomNumber() & 7) + 85; return 0; } int ASound7::command16() { playSound(0x2CE2, 8); return 0; } int ASound7::command18() { playSound(0x2C94, 22); return 0; } int ASound7::command19() { byte *pData1 = loadData(0x2C94, 22); byte *pData2 = loadData(0x2CAA, 16); if (_channels[8]._ptr1 == pData1 || _channels[8]._ptr1 == pData2) { _channels[8]._soundData = loadData(0x2CBA, 12); _channels[8]._field17 = 1; _channels[8]._field19 = 1; } return 0; } int ASound7::command20() { playSound(0x2CD0, 18); return 0; } int ASound7::command21() { playSound(0x2CC6, 10); return 0; } int ASound7::command22() { playSound(0x2C08, 140); return 0; } int ASound7::command23() { playSound(0x2B08, 34); playSound(0x2B2A, 20); return 0; } int ASound7::command24() { byte *pData1 = loadData(0x14C6, 144); if (!isSoundActive(pData1)) { command1(); _channels[0].load(pData1); _channels[1].load(loadData(0x1556, 146)); _channels[2].load(loadData(0x15E8, 138)); _channels[3].load(loadData(0x1672, 122)); _channels[4].load(loadData(0x16EC, 74)); } return 0; } int ASound7::command25() { byte *pData1 = loadData(0x1DBE, 182); if (!isSoundActive(pData1)) { command1(); _channels[0].load(pData1); _channels[1].load(loadData(0x1E74, 182)); _channels[2].load(loadData(0x1F2A, 186)); _channels[3].load(loadData(0x1FE4, 244)); } return 0; } int ASound7::command26() { byte *pData1 = loadData(0x20D8, 312); if (!isSoundActive(pData1)) { command1(); _channels[0].load(pData1); _channels[1].load(loadData(0x2210, 304)); _channels[2].load(loadData(0x2340, 222)); _channels[3].load(loadData(0x241E, 522)); _channels[4].load(loadData(0x2628, 874)); } return 0; } int ASound7::command27() { byte *pData1 = loadData(0x1736, 158); if (!isSoundActive(pData1)) { command1(); _channels[0].load(pData1); _channels[1].load(loadData(0x17D4, 288)); _channels[2].load(loadData(0x18F4, 290)); _channels[3].load(loadData(0x1A16, 396)); _channels[4].load(loadData(0x1BA2, 380)); _channels[5].load(loadData(0x1D1E, 160)); } return 0; } int ASound7::command28() { playSound(0x2CAA, 16); return 0; } int ASound7::command30() { playSound(0x2B86, 22); playSound(0x2B9C, 22); playSound(0x2BB2, 22); return 0; } int ASound7::command32() { playSound(0x2BFE, 10); return 0; } int ASound7::command33() { playSound(0x2BEE, 16); return 0; } int ASound7::command34() { playSound(0x2BDE, 16); return 0; } int ASound7::command35() { playSound(0x2BC8, 22); return 0; } int ASound7::command36() { playSound(0x2AF8, 16); return 0; } int ASound7::command37() { playSound(0x2B48, 20); playSound(0x2B5C, 32); playSound(0x2B7C, 10); return 0; } } // End of namespace Nebular } // End of namespace MADS