aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Gilbert2011-06-01 21:02:00 +1000
committerPaul Gilbert2011-06-01 21:02:00 +1000
commit12464b101fc92f0f148f59e906829c568280a455 (patch)
tree745ec14cd522ac809bd70650ebb876ff55dc8dd3
parent93f92a72dcb311bdfc5be7ccc891f10b2b4d6881 (diff)
downloadscummvm-rg350-12464b101fc92f0f148f59e906829c568280a455.tar.gz
scummvm-rg350-12464b101fc92f0f148f59e906829c568280a455.tar.bz2
scummvm-rg350-12464b101fc92f0f148f59e906829c568280a455.zip
TSAGE: Added sound driver classes copied from CRUISE engine
-rw-r--r--engines/tsage/sound.cpp659
-rw-r--r--engines/tsage/sound.h175
2 files changed, 787 insertions, 47 deletions
diff --git a/engines/tsage/sound.cpp b/engines/tsage/sound.cpp
index 9ed4041148..ac77edd7d9 100644
--- a/engines/tsage/sound.cpp
+++ b/engines/tsage/sound.cpp
@@ -586,53 +586,41 @@ void SoundManager::_sfRethinkVoiceTypes() {
++sfManager()._suspendCtr;
_sfDereferenceAll();
- for (int voiceIndex = 0; voiceIndex < SOUND_ARR_SIZE; ++voiceIndex) {
- VoiceStruct *vs = sfManager()._voiceStructPtrs[voiceIndex];
- if (!vs)
- continue;
-
- if (vs->_voiceType == VOICETYPE_0) {
- for (uint idx = 0; idx < vs->_entries.size(); ++idx) {
- VoiceStructEntry &vse = vs->_entries[idx];
- vse._field14 = vse._field4;
- vse._field16 = vse._field6;
- vse._field18 = vse._field8;
- vse._field19 = vse._field9;
- vse._field1A = vse._fieldA;
- vse._field4 = 0;
- vse._field6 = 0;
- vse._field8 = 0;
- vse._field9 = 0;
- vse._fieldA = 0;
- vse._fieldC = 0;
- vse._fieldE = 0;
- vse._field10 = 0;
- vse._field11 = 0;
- vse._field12 = 0;
- }
- } else {
- for (uint idx = 0; idx < vs->_entries.size(); ++idx) {
- VoiceStructEntry &vse = vs->_entries[idx];
- vse._field14 = vse._field8;
- vse._field16 = vse._fieldA;
- vse._field18 = vse._fieldC;
- vse._field19 = vse._fieldD;
- vse._field8 = 0;
- vse._fieldA = 0;
- vse._fieldC = 0;
- vse._fieldD = 0;
- vse._fieldE = 0;
- vse._field10 = 0;
- vse._field12 = 0;
- }
+ // Check for any active sound currently playing
+ for (Common::List<Sound *>::iterator playIterator = sfManager()._playList.begin();
+ playIterator != sfManager()._playList.end(); ++playIterator) {
+ Sound *sound = *playIterator;
+ if (sound->getCueValue() >= 0) {
+ // Currently playing sound
+ // TODO: Figure out how to determine when raw playback has ended
+ return;
}
}
-// int var2 = 0;
+ // No currently playing sound, so look for any queued sounds to play
for (Common::List<Sound *>::iterator playIterator = sfManager()._playList.begin();
playIterator != sfManager()._playList.end(); ++playIterator) {
+ Sound *sound = *playIterator;
+ if (sound->getCueValue() == -1) {
+ // Found a sound to start playing
+
+ // Get the first sound driver
+ assert(sfManager()._installedDrivers.size() > 0);
+ SoundDriver *driver = *sfManager()._installedDrivers.begin();
+
+ // Start each channel of the sound
+ for (int channelNum = 0; channelNum < sound->_trackInfo._count; ++channelNum) {
+ const byte *data = sound->_trackInfo._channelData[channelNum];
+ int dataSize = _vm->_memoryManager.getSize(data);
+
+ driver->play(data, dataSize, channelNum, sfManager()._volume);
+ }
+ sound->_cueValue = 0;
+ return;
+ }
}
+
}
void SoundManager::_sfUpdateVolume(Sound *sound) {
@@ -885,7 +873,7 @@ void Sound::_prime(int soundNum, bool queFlag) {
_soundManager->extractTrackInfo(&_trackInfo, soundData, _groupNum);
for (int idx = 0; idx < _trackInfo._count; ++idx) {
- _trackInfo._handleList[idx] = _resourceManager->getResource(RES_SOUND, soundNum, _trackInfo._rlbList[idx]);
+ _trackInfo._channelData[idx] = _resourceManager->getResource(RES_SOUND, soundNum, _trackInfo._rlbList[idx]);
}
DEALLOCATE(soundData);
@@ -896,7 +884,7 @@ void Sound::_prime(int soundNum, bool queFlag) {
_soundPriority = 0;
_loop = 0;
_trackInfo._count = 0;
- _trackInfo._handleList[0] = ALLOCATE(200);
+ _trackInfo._channelData[0] = ALLOCATE(200);
_field26E = ALLOCATE(200);
}
@@ -909,12 +897,12 @@ void Sound::_prime(int soundNum, bool queFlag) {
void Sound::_unPrime() {
if (_primed) {
if (_isEmpty) {
- DEALLOCATE(_trackInfo._handleList[0]);
+ DEALLOCATE(_trackInfo._channelData[0]);
DEALLOCATE(_field26E);
_field26E = NULL;
} else {
for (int idx = 0; idx < _trackInfo._count; ++idx) {
- DEALLOCATE(_trackInfo._handleList[idx]);
+ DEALLOCATE(_trackInfo._channelData[idx]);
}
}
@@ -931,7 +919,7 @@ void Sound::orientAfterDriverChange() {
int timeIndex = getTimeIndex();
for (int idx = 0; idx < _trackInfo._count; ++idx)
- DEALLOCATE(_trackInfo._handleList[idx]);
+ DEALLOCATE(_trackInfo._channelData[idx]);
_trackInfo._count = 0;
_primed = false;
@@ -1167,17 +1155,596 @@ SoundDriver::SoundDriver() {
/*--------------------------------------------------------------------------*/
+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);
+
+void PCSoundDriver::setUpdateCallback(UpdateCallback upCb, void *ref) {
+ _upCb = upCb;
+ _upRef = ref;
+}
+
+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;
+ }
+ }
+
+ *oct = *note / 12;
+ *note %= 12;
+}
+
+void PCSoundDriver::resetChannel(int channel) {
+ stopChannel(channel);
+ stopAll();
+}
+
+void PCSoundDriver::syncSounds() {
+ bool mute = false;
+ if (ConfMan.hasKey("mute"))
+ mute = ConfMan.getBool("mute");
+
+ bool music_mute = mute;
+ bool sfx_mute = mute;
+
+ if (!mute) {
+ music_mute = ConfMan.getBool("music_mute");
+ sfx_mute = ConfMan.getBool("sfx_mute");
+ }
+
+ // Get the new music and sfx volumes
+ _musicVolume = music_mute ? 0 : MIN(255, ConfMan.getInt("music_volume"));
+ _sfxVolume = sfx_mute ? 0 : MIN(255, ConfMan.getInt("sfx_volume"));
+}
+
+/*--------------------------------------------------------------------------*/
+
+const int AdlibDriverBase::_freqTable[] = {
+ 0x157, 0x16C, 0x181, 0x198, 0x1B1, 0x1CB,
+ 0x1E6, 0x203, 0x222, 0x243, 0x266, 0x28A
+};
+
+const int AdlibDriverBase::_freqTableCount = ARRAYSIZE(_freqTable);
+
+const int AdlibDriverBase::_operatorsTable[] = {
+ 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21
+};
+
+const int AdlibDriverBase::_operatorsTableCount = ARRAYSIZE(_operatorsTable);
+
+const int AdlibDriverBase::_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 AdlibDriverBase::_voiceOperatorsTableCount = ARRAYSIZE(_voiceOperatorsTable);
+
+
+AdlibDriverBase::AdlibDriverBase(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->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, 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"));
+}
+
+AdlibDriverBase::~AdlibDriverBase() {
+ _mixer->stopHandle(_soundHandle);
+ OPLDestroy(_opl);
+}
+
+void AdlibDriverBase::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 AdlibDriverBase::adjustVolume(int channel, int volume) {
+ _channelsVolumeTable[channel].original = volume;
+
+ if (volume > 80) {
+ volume = 80;
+ } else if (volume < 0) {
+ volume = 0;
+ }
+ volume += volume / 4;
+ if (volume > 127) {
+ volume = 127;
+ }
+
+ int volAdjust = (channel == 4) ? _sfxVolume : _musicVolume;
+ volume = (volume * volAdjust) / 128;
+
+ if (volume > 127)
+ volume = 127;
+
+ _channelsVolumeTable[channel].adjusted = volume;
+}
+
+void AdlibDriverBase::setupChannel(int channel, const byte *data, int instrument, int volume) {
+ assert(channel < 5);
+ if (data) {
+ adjustVolume(channel, volume);
+ setupInstrument(data, channel);
+ }
+}
+
+void AdlibDriverBase::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);
+ }
+}
+
+void AdlibDriverBase::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);
+}
+
+int AdlibDriverBase::readBuffer(int16 *buffer, const int numSamples) {
+ update(buffer, numSamples);
+ return numSamples;
+}
+
+void AdlibDriverBase::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);
+ }
+
+ for (int j = 0; j < 5; j++) {
+ for (int i = 0; i < 18; ++i) {
+ OPLWriteReg(_opl, oplRegs[j] | _operatorsTable[i], 0);
+ }
+ }
+
+ OPLWriteReg(_opl, 1, 0x20);
+ OPLWriteReg(_opl, 1, 0);
+}
+
+void AdlibDriverBase::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;
+ }
+}
+
+void AdlibDriverBase::setupInstrument(const byte *data, int channel) {
+ AdLibSoundInstrument *ins = &_instrumentsTable[channel];
+ loadInstrument(data, ins);
+
+ setupInstrument(ins, channel);
+}
+
+void AdlibDriverBase::setupInstrument(const AdLibSoundInstrument *ins, int channel) {
+ int mod, car, tmp;
+ const AdLibRegisterSoundInstrument *reg;
+
+ 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]];
+ }
+
+ 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);
+ }
+
+ 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);
+}
+
+void AdlibDriverBase::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
+
+ reg->attackDecay = READ_LE_UINT16(data + 6) << 4; // attack rate
+ reg->attackDecay |= READ_LE_UINT16(data + 12) & 0xF; // decay rate
+
+ reg->sustainRelease = READ_LE_UINT16(data + 8) << 4; // sustain level
+ reg->sustainRelease |= READ_LE_UINT16(data + 14) & 0xF; // release rate
+
+ 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, &note, &oct);
+
+ 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 AdlibSoundDriverADL::playSample(const byte *data, int size, int channel, int volume) {
+ 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);
+}
+
+PCSoundFxPlayer::~PCSoundFxPlayer() {
+ _driver->setUpdateCallback(NULL, NULL);
+ stop();
+}
+
+bool PCSoundFxPlayer::load(const char *song) {
+ /*
+ debug(9, "PCSoundFxPlayer::load('%s')", song);
+
+ // 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';
+
+ 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 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 = 5; // TODO: What to do with this?
+ _updateTicksCounter = 0;
+ _playing = true;
+ }
+}
+
+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();
+}
+
+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;
+
+ for (int i = 0; i < 4; ++i) {
+ handlePattern(i, patternData + patternNum + _currentPos);
+ patternData += 4;
+ }
+
+ if (_fadeOutCounter != 0 && _fadeOutCounter < 100) {
+ _fadeOutCounter += 2;
+ }
+ if (_fadeOutCounter >= 100) {
+ stop();
+ return;
+ }
+
+ _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;
+ }
+
+ _numOrders = _sfxData[470];
+ _eventsDelay = (244 - _sfxData[471]) * 100 / 1060;
+ _updateTicksCounter = 0;
+ }
+
+ s.syncAsSint16LE(_songPlayed);
+ s.syncAsSint16LE(_looping);
+ s.syncAsSint16LE(_currentPos);
+ s.syncAsSint16LE(_currentOrder);
+ s.syncAsSint16LE(_playing);
+}
+
+/*--------------------------------------------------------------------------*/
+
const byte adlib_group_data[] = { 1, 1, 9, 1, 0xff };
AdlibSoundDriver::AdlibSoundDriver() {
_minVersion = 0x102;
_maxVersion = 0x10A;
-
_groupData.groupMask = 9;
_groupData.v1 = 0x46;
_groupData.v2 = 0;
_groupData.pData = &adlib_group_data[0];
+
+ _mixer = _vm->_mixer;
+ _soundDriver = new AdlibSoundDriverADL(_mixer);
+ _player = new PCSoundFxPlayer(_soundDriver);
}
} // End of namespace tSage
diff --git a/engines/tsage/sound.h b/engines/tsage/sound.h
index eebb549a76..7ad4da5755 100644
--- a/engines/tsage/sound.h
+++ b/engines/tsage/sound.h
@@ -24,6 +24,9 @@
#define TSAGE_SOUND_H
#include "common/scummsys.h"
+#include "audio/audiostream.h"
+#include "audio/fmopl.h"
+#include "audio/mixer.h"
#include "common/list.h"
#include "tsage/saveload.h"
#include "tsage/core.h"
@@ -40,7 +43,7 @@ struct trackInfoStruct {
int _count;
int _rlbList[SOUND_ARR_SIZE];
int _arr2[SOUND_ARR_SIZE];
- byte *_handleList[SOUND_ARR_SIZE];
+ byte *_channelData[SOUND_ARR_SIZE];
int field82[SOUND_ARR_SIZE];
int field92[SOUND_ARR_SIZE];
int fielda2[SOUND_ARR_SIZE];
@@ -90,6 +93,7 @@ public:
virtual void installPatchBank(const byte *data) {}
virtual void setVolume0(int channel, int v2, int v3, int volume) {}
virtual void setVolume1(int channel, int v2, int v3, int volume) {}
+ virtual void play(const byte *data, int size, int channel, int volume) {}
virtual void poll() {}
};
@@ -337,15 +341,184 @@ public:
void release() { _sound.release(); }
};
+/*--------------------------------------------------------------------------
+ * Adlib related classes
+ *--------------------------------------------------------------------------
+ */
+
+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 PCSoundDriver {
+public:
+ typedef void (*UpdateCallback)(void *);
+
+ PCSoundDriver() { _upCb = NULL, _upRef = NULL, _musicVolume = 0, _sfxVolume = 0; }
+ 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;
+};
+
+class AdlibDriverBase : public PCSoundDriver, Audio::AudioStream {
+public:
+ AdlibDriverBase(Audio::Mixer *mixer);
+ virtual ~AdlibDriverBase();
+
+ // 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[10];
+ AdLibSoundInstrument _instrumentsTable[10];
+
+ static const int _freqTable[];
+ static const int _freqTableCount;
+ static const int _operatorsTable[];
+ static const int _operatorsTableCount;
+ static const int _voiceOperatorsTable[];
+ static const int _voiceOperatorsTableCount;
+};
+
+class AdlibSoundDriverADL : public AdlibDriverBase {
+public:
+ AdlibSoundDriverADL(Audio::Mixer *mixer) : AdlibDriverBase(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; }
+};
+
class AdlibSoundDriver: public SoundDriver {
private:
GroupData _groupData;
+ Audio::Mixer *_mixer;
+ PCSoundDriver *_soundDriver;
+ PCSoundFxPlayer *_player;
public:
AdlibSoundDriver();
virtual void setVolume(int volume) {}
virtual void installPatchBank(const byte *data) {}
virtual const GroupData *getGroupData() { return &_groupData; }
+ virtual void play(const byte *data, int size, int channel, int volume) {
+ _soundDriver->playSample(data, size, channel, volume);
+ }
};
} // End of namespace tSage