diff options
author | Paul Gilbert | 2011-06-01 21:02:00 +1000 |
---|---|---|
committer | Paul Gilbert | 2011-06-01 21:02:00 +1000 |
commit | 12464b101fc92f0f148f59e906829c568280a455 (patch) | |
tree | 745ec14cd522ac809bd70650ebb876ff55dc8dd3 | |
parent | 93f92a72dcb311bdfc5be7ccc891f10b2b4d6881 (diff) | |
download | scummvm-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.cpp | 659 | ||||
-rw-r--r-- | engines/tsage/sound.h | 175 |
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, ¬e, &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 |