diff options
Diffstat (limited to 'sound/softsynth')
-rw-r--r-- | sound/softsynth/adlib.cpp | 16 | ||||
-rw-r--r-- | sound/softsynth/fluidsynth.cpp | 13 | ||||
-rw-r--r-- | sound/softsynth/fmtowns_pc98/towns_audio.cpp | 1578 | ||||
-rw-r--r-- | sound/softsynth/fmtowns_pc98/towns_audio.h | 178 | ||||
-rw-r--r-- | sound/softsynth/fmtowns_pc98/towns_euphony.cpp | 907 | ||||
-rw-r--r-- | sound/softsynth/fmtowns_pc98/towns_euphony.h | 187 | ||||
-rw-r--r-- | sound/softsynth/fmtowns_pc98/towns_pc98_driver.cpp | 1403 | ||||
-rw-r--r-- | sound/softsynth/fmtowns_pc98/towns_pc98_driver.h | 129 | ||||
-rw-r--r-- | sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp | 1513 | ||||
-rw-r--r-- | sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h | 177 | ||||
-rw-r--r-- | sound/softsynth/mt32.cpp | 27 | ||||
-rw-r--r-- | sound/softsynth/opl/dbopl.cpp | 2 | ||||
-rw-r--r-- | sound/softsynth/opl/mame.cpp | 4 | ||||
-rw-r--r-- | sound/softsynth/pcspk.cpp | 57 | ||||
-rw-r--r-- | sound/softsynth/sid.cpp | 15 | ||||
-rw-r--r-- | sound/softsynth/sid.h | 13 | ||||
-rw-r--r-- | sound/softsynth/wave6581.cpp | 15 | ||||
-rw-r--r-- | sound/softsynth/ym2612.cpp | 16 |
18 files changed, 6165 insertions, 85 deletions
diff --git a/sound/softsynth/adlib.cpp b/sound/softsynth/adlib.cpp index 6697cef646..ffb359e816 100644 --- a/sound/softsynth/adlib.cpp +++ b/sound/softsynth/adlib.cpp @@ -27,6 +27,7 @@ #include "common/util.h" #include "sound/fmopl.h" #include "sound/musicplugin.h" +#include "common/translation.h" #ifdef DEBUG_ADLIB static int tick; @@ -1586,7 +1587,7 @@ void MidiDriver_ADLIB::adlib_note_on(int chan, byte note, int mod) { class AdLibEmuMusicPlugin : public MusicPluginObject { public: const char *getName() const { - return "AdLib Emulator"; + return _s("AdLib Emulator"); } const char *getId() const { @@ -1594,7 +1595,7 @@ public: } MusicDevices getDevices() const; - Common::Error createInstance(MidiDriver **mididriver) const; + Common::Error createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle = 0) const; }; MusicDevices AdLibEmuMusicPlugin::getDevices() const { @@ -1603,21 +1604,12 @@ MusicDevices AdLibEmuMusicPlugin::getDevices() const { return devices; } -Common::Error AdLibEmuMusicPlugin::createInstance(MidiDriver **mididriver) const { +Common::Error AdLibEmuMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const { *mididriver = new MidiDriver_ADLIB(g_system->getMixer()); return Common::kNoError; } -MidiDriver *MidiDriver_ADLIB_create() { - MidiDriver *mididriver; - - AdLibEmuMusicPlugin p; - p.createInstance(&mididriver); - - return mididriver; -} - //#if PLUGIN_ENABLED_DYNAMIC(ADLIB) //REGISTER_PLUGIN_DYNAMIC(ADLIB, PLUGIN_TYPE_MUSIC, AdLibEmuMusicPlugin); //#else diff --git a/sound/softsynth/fluidsynth.cpp b/sound/softsynth/fluidsynth.cpp index c3bd782cc1..fcb4591a20 100644 --- a/sound/softsynth/fluidsynth.cpp +++ b/sound/softsynth/fluidsynth.cpp @@ -230,7 +230,7 @@ public: } MusicDevices getDevices() const; - Common::Error createInstance(MidiDriver **mididriver) const; + Common::Error createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle = 0) const; }; MusicDevices FluidSynthMusicPlugin::getDevices() const { @@ -239,21 +239,12 @@ MusicDevices FluidSynthMusicPlugin::getDevices() const { return devices; } -Common::Error FluidSynthMusicPlugin::createInstance(MidiDriver **mididriver) const { +Common::Error FluidSynthMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const { *mididriver = new MidiDriver_FluidSynth(g_system->getMixer()); return Common::kNoError; } -MidiDriver *MidiDriver_FluidSynth_create() { - MidiDriver *mididriver; - - FluidSynthMusicPlugin p; - p.createInstance(&mididriver); - - return mididriver; -} - //#if PLUGIN_ENABLED_DYNAMIC(FLUIDSYNTH) //REGISTER_PLUGIN_DYNAMIC(FLUIDSYNTH, PLUGIN_TYPE_MUSIC, FluidSynthMusicPlugin); //#else diff --git a/sound/softsynth/fmtowns_pc98/towns_audio.cpp b/sound/softsynth/fmtowns_pc98/towns_audio.cpp new file mode 100644 index 0000000000..c9ba03ea27 --- /dev/null +++ b/sound/softsynth/fmtowns_pc98/towns_audio.cpp @@ -0,0 +1,1578 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#include "sound/softsynth/fmtowns_pc98/towns_audio.h" +#include "sound/audiocd.h" +#include "common/endian.h" + + +class TownsAudio_PcmChannel { +friend class TownsAudioInterface; +public: + TownsAudio_PcmChannel(); + ~TownsAudio_PcmChannel(); + +private: + void loadExtData(uint8 *buffer, uint32 size); + void setupLoop(uint32 start, uint32 len); + void clear(); + + void envAttack(); + void envDecay(); + void envSustain(); + void envRelease(); + + uint8 *curInstrument; + uint8 note; + uint8 velo; + + int8 *data; + int8 *dataEnd; + + int8 *loopEnd; + uint32 loopLen; + + uint16 stepNote; + uint16 stepPitch; + uint16 step; + + uint8 panLeft; + uint8 panRight; + + uint32 pos; + + uint8 envTotalLevel; + uint8 envAttackRate; + uint8 envDecayRate; + uint8 envSustainLevel; + uint8 envSustainRate; + uint8 envReleaseRate; + + int16 envStep; + int16 envCurrentLevel; + + EnvelopeState envState; + + int8 *extData; +}; + +class TownsAudio_WaveTable { +friend class TownsAudioInterface; +public: + TownsAudio_WaveTable(); + ~TownsAudio_WaveTable(); + +private: + void readHeader(const uint8 *buffer); + void readData(const uint8 *buffer); + void clear(); + + char name[9]; + int32 id; + uint32 size; + uint32 loopStart; + uint32 loopLen; + uint16 rate; + uint16 rateOffs; + uint16 baseNote; + int8 *data; +}; + +TownsAudioInterface::TownsAudioInterface(Audio::Mixer *mixer, TownsAudioInterfacePluginDriver *driver) : TownsPC98_FmSynth(mixer, kTypeTowns), + _fmInstruments(0), _pcmInstruments(0), _pcmChan(0), _waveTables(0), _waveTablesTotalDataSize(0), + _baserate(55125.0f / (float)mixer->getOutputRate()), _tickLength(0), _timer(0), _drv(driver), + _pcmSfxChanMask(0), _musicVolume(Audio::Mixer::kMaxMixerVolume), _sfxVolume(Audio::Mixer::kMaxMixerVolume), + _outputVolumeFlags(0), _outputMuteFlags(0), _ready(false) { + +#define INTCB(x) &TownsAudioInterface::intf_##x + static const TownsAudioIntfCallback intfCb[] = { + // 0 + INTCB(reset), + INTCB(keyOn), + INTCB(keyOff), + INTCB(setPanPos), + // 4 + INTCB(setInstrument), + INTCB(loadInstrument), + INTCB(notImpl), + INTCB(setPitch), + // 8 + INTCB(setLevel), + INTCB(chanOff), + INTCB(notImpl), + INTCB(notImpl), + // 12 + INTCB(notImpl), + INTCB(notImpl), + INTCB(notImpl), + INTCB(notImpl), + // 16 + INTCB(notImpl), + INTCB(writeReg), + INTCB(notImpl), + INTCB(writeRegBuffer), + // 20 + INTCB(readRegBuffer), + INTCB(setTimerA), + INTCB(setTimerB), + INTCB(enableTimerA), + // 24 + INTCB(enableTimerB), + INTCB(notImpl), + INTCB(notImpl), + INTCB(notImpl), + // 28 + INTCB(notImpl), + INTCB(notImpl), + INTCB(notImpl), + INTCB(notImpl), + // 32 + INTCB(loadSamples), + INTCB(reserveEffectChannels), + INTCB(loadWaveTable), + INTCB(unloadWaveTable), + // 36 + INTCB(notImpl), + INTCB(pcmPlayEffect), + INTCB(notImpl), + INTCB(pcmChanOff), + // 40 + INTCB(pcmEffectPlaying), + INTCB(notImpl), + INTCB(notImpl), + INTCB(notImpl), + // 44 + INTCB(notImpl), + INTCB(notImpl), + INTCB(notImpl), + INTCB(notImpl), + // 48 + INTCB(notImpl), + INTCB(notImpl), + INTCB(fmKeyOn), + INTCB(fmKeyOff), + // 52 + INTCB(fmSetPanPos), + INTCB(fmSetInstrument), + INTCB(fmLoadInstrument), + INTCB(notImpl), + // 56 + INTCB(fmSetPitch), + INTCB(fmSetLevel), + INTCB(fmReset), + INTCB(notImpl), + // 60 + INTCB(notImpl), + INTCB(notImpl), + INTCB(notImpl), + INTCB(notImpl), + // 64 + INTCB(notImpl), + INTCB(notImpl), + INTCB(notImpl), + INTCB(setOutputVolume), + // 68 + INTCB(resetOutputVolume), + INTCB(notImpl), + INTCB(updateOutputVolume), + INTCB(notImpl), + // 72 + INTCB(notImpl), + INTCB(notImpl), + INTCB(notImpl), + INTCB(notImpl), + // 76 + INTCB(notImpl), + INTCB(notImpl), + INTCB(notImpl), + INTCB(notImpl), + // 80 + INTCB(pcmUpdateEnvelopeGenerator), + INTCB(notImpl) + + }; +#undef INTCB + + _intfOpcodes = intfCb; + + memset(_fmSaveReg, 0, sizeof(_fmSaveReg)); + memset(_outputLevel, 0, sizeof(_outputLevel)); + + _timerBase = (uint32)(_baserate * 1000000.0f); + _tickLength = 2 * _timerBase; +} + +TownsAudioInterface::~TownsAudioInterface() { + delete[] _fmSaveReg[0]; + delete[] _fmSaveReg[1]; + delete[] _fmInstruments; + delete[] _pcmInstruments; + delete[] _waveTables; + delete[] _pcmChan; +} + +bool TownsAudioInterface::init() { + if (_ready) + return true; + + if (!_drv) + return false; + + if (!TownsPC98_FmSynth::init()) + return false; + + _fmSaveReg[0] = new uint8[256]; + _fmSaveReg[1] = new uint8[256]; + _fmInstruments = new uint8[128 * 48]; + _pcmInstruments = new uint8[32 * 128]; + _waveTables = new TownsAudio_WaveTable[128]; + _pcmChan = new TownsAudio_PcmChannel[8]; + + _timer = 0; + + setVolumeChannelMasks(-1, 0); + + callback(0); + + _ready = true; + return true; +} + +int TownsAudioInterface::callback(int command, ...) { + if (!_ready) + return 1; + + va_list args; + va_start(args, command); + + if (command > 81) { + va_end(args); + return 4; + } + + int res = (this->*_intfOpcodes[command])(args); + + va_end(args); + return res; +} + +void TownsAudioInterface::setMusicVolume(int volume) { + _musicVolume = CLIP<uint16>(volume, 0, Audio::Mixer::kMaxMixerVolume); + setVolumeIntern(_musicVolume, _sfxVolume); +} + +void TownsAudioInterface::setSoundEffectVolume(int volume) { + _sfxVolume = CLIP<uint16>(volume, 0, Audio::Mixer::kMaxMixerVolume); + setVolumeIntern(_musicVolume, _sfxVolume); +} + +void TownsAudioInterface::setSoundEffectChanMask(int mask) { + _pcmSfxChanMask = mask >> 6; + mask &= 0x3f; + setVolumeChannelMasks(~mask, mask); +} + +void TownsAudioInterface::nextTickEx(int32 *buffer, uint32 bufferSize) { + if (!_ready) + return; + + for (uint32 i = 0; i < bufferSize; i++) { + _timer += _tickLength; + while (_timer > 0x514767) { + _timer -= 0x514767; + + for (int ii = 0; ii < 8; ii++) { + if ((_pcmChanKeyPlaying & _chanFlags[ii]) || (_pcmChanEffectPlaying & _chanFlags[ii])) { + TownsAudio_PcmChannel *s = &_pcmChan[ii]; + s->pos += s->step; + + if (&s->data[s->pos >> 11] >= s->loopEnd) { + if (s->loopLen) { + s->pos -= s->loopLen; + } else { + s->pos = 0; + _pcmChanEffectPlaying &= ~_chanFlags[ii]; + _pcmChanKeyPlaying &= ~_chanFlags[ii]; + } + } + } + } + } + + int32 finOutL = 0; + int32 finOutR = 0; + + for (int ii = 0; ii < 8; ii++) { + if (_pcmChanOut & _chanFlags[ii]) { + int32 o = _pcmChan[ii].data[_pcmChan[ii].pos >> 11] * _pcmChan[ii].velo; + if ((1 << ii) & (~_pcmSfxChanMask)) + o = (o * _musicVolume) / Audio::Mixer::kMaxMixerVolume; + if ((1 << ii) & _pcmSfxChanMask) + o = (o * _sfxVolume) / Audio::Mixer::kMaxMixerVolume; + if (_pcmChan[ii].panLeft) + finOutL += ((o * _pcmChan[ii].panLeft) >> 3); + if (_pcmChan[ii].panRight) + finOutR += ((o * _pcmChan[ii].panRight) >> 3); + if (!((_pcmChanKeyPlaying & _chanFlags[ii]) || (_pcmChanEffectPlaying & _chanFlags[ii]))) + _pcmChanOut &= ~_chanFlags[ii]; + } + } + + buffer[i << 1] += finOutL; + buffer[(i << 1) + 1] += finOutR; + } +} + +void TownsAudioInterface::timerCallbackA() { + Common::StackLock lock(_mutex); + if (_drv && _ready) + _drv->timerCallback(0); +} + +void TownsAudioInterface::timerCallbackB() { + Common::StackLock lock(_mutex); + if (_drv && _ready) { + _drv->timerCallback(1); + callback(80); + } +} + +int TownsAudioInterface::intf_reset(va_list &args) { + Common::StackLock lock(_mutex); + fmReset(); + pcmReset(); + callback(68); + return 0; +} + +int TownsAudioInterface::intf_keyOn(va_list &args) { + int chan = va_arg(args, int); + int note = va_arg(args, int); + int velo = va_arg(args, int); + return (chan & 0x40) ? pcmKeyOn(chan, note, velo) : fmKeyOn(chan, note, velo); +} + +int TownsAudioInterface::intf_keyOff(va_list &args) { + int chan = va_arg(args, int); + return (chan & 0x40) ? pcmKeyOff(chan) : fmKeyOff(chan); +} + +int TownsAudioInterface::intf_setPanPos(va_list &args) { + int chan = va_arg(args, int); + int mode = va_arg(args, int); + return (chan & 0x40) ? pcmSetPanPos(chan, mode) : fmSetPanPos(chan, mode); +} + +int TownsAudioInterface::intf_setInstrument(va_list &args) { + int chan = va_arg(args, int); + int instrId = va_arg(args, int); + return (chan & 0x40) ? pcmSetInstrument(chan, instrId) : fmSetInstrument(chan, instrId); +} + +int TownsAudioInterface::intf_loadInstrument(va_list &args) { + int chanType = va_arg(args, int); + int instrId = va_arg(args, int); + uint8 *instrData = va_arg(args, uint8 *); + return (chanType & 0x40) ? pcmLoadInstrument(instrId, instrData) : fmLoadInstrument(instrId, instrData); +} + +int TownsAudioInterface::intf_setPitch(va_list &args) { + int chan = va_arg(args, int); + int16 pitch = (int16)(va_arg(args, int) & 0xffff); + return (chan & 0x40) ? pcmSetPitch(chan, pitch) : fmSetPitch(chan, pitch); +} + +int TownsAudioInterface::intf_setLevel(va_list &args) { + int chan = va_arg(args, int); + int lvl = va_arg(args, int); + return (chan & 0x40) ? pcmSetLevel(chan, lvl) : fmSetLevel(chan, lvl); +} + +int TownsAudioInterface::intf_chanOff(va_list &args) { + int chan = va_arg(args, int); + return (chan & 0x40) ? pcmChanOff(chan) : fmChanOff(chan); +} + +int TownsAudioInterface::intf_writeReg(va_list &args) { + int part = va_arg(args, int) ? 1 : 0; + int reg = va_arg(args, int); + int val = va_arg(args, int); + if ((!part && reg < 0x20) || (part && reg < 0x30) || (reg > 0xb6)) + return 3; + + bufferedWriteReg(part, reg, val); + return 0; +} + +int TownsAudioInterface::intf_writeRegBuffer(va_list &args) { + int part = va_arg(args, int) ? 1 : 0; + int reg = va_arg(args, int); + int val = va_arg(args, int); + + if ((!part && reg < 0x20) || (part && reg < 0x30) || (reg > 0xef)) + return 3; + + _fmSaveReg[part][reg] = val; + return 0; +} + +int TownsAudioInterface::intf_readRegBuffer(va_list &args) { + int part = va_arg(args, int) ? 1 : 0; + int reg = va_arg(args, int); + uint8 *dst = va_arg(args, uint8 *); + *dst = 0; + + if ((!part && reg < 0x20) || (part && reg < 0x30) || (reg > 0xef)) + return 3; + + *dst = _fmSaveReg[part][reg]; + return 0; +} + +int TownsAudioInterface::intf_setTimerA(va_list &args) { + int enable = va_arg(args, int); + int tempo = va_arg(args, int); + + if (enable) { + bufferedWriteReg(0, 0x25, tempo & 3); + bufferedWriteReg(0, 0x24, (tempo >> 2) & 0xff); + bufferedWriteReg(0, 0x27, _fmSaveReg[0][0x27] | 0x05); + } else { + bufferedWriteReg(0, 0x27, (_fmSaveReg[0][0x27] & 0xfa) | 0x10); + } + + return 0; +} + +int TownsAudioInterface::intf_setTimerB(va_list &args) { + int enable = va_arg(args, int); + int tempo = va_arg(args, int); + + if (enable) { + bufferedWriteReg(0, 0x26, tempo & 0xff); + bufferedWriteReg(0, 0x27, _fmSaveReg[0][0x27] | 0x0A); + } else { + bufferedWriteReg(0, 0x27, (_fmSaveReg[0][0x27] & 0xf5) | 0x20); + } + + return 0; +} + +int TownsAudioInterface::intf_enableTimerA(va_list &args) { + bufferedWriteReg(0, 0x27, _fmSaveReg[0][0x27] | 0x15); + return 0; +} + +int TownsAudioInterface::intf_enableTimerB(va_list &args) { + bufferedWriteReg(0, 0x27, _fmSaveReg[0][0x27] | 0x2a); + return 0; +} + +int TownsAudioInterface::intf_loadSamples(va_list &args) { + uint32 dest = va_arg(args, uint32); + int size = va_arg(args, int); + uint8 *src = va_arg(args, uint8*); + + if (dest >= 65536 || size == 0 || size > 65536) + return 3; + if (size + dest > 65536) + return 5; + + int dwIndex = _numWaveTables - 1; + for (uint32 t = _waveTablesTotalDataSize; dwIndex && (dest < t); dwIndex--) + t -= _waveTables[dwIndex].size; + + TownsAudio_WaveTable *s = &_waveTables[dwIndex]; + _waveTablesTotalDataSize -= s->size; + s->size = size; + s->readData(src); + _waveTablesTotalDataSize += s->size; + + return 0; +} + +int TownsAudioInterface::intf_reserveEffectChannels(va_list &args) { + int numChan = va_arg(args, int); + if (numChan > 8) + return 3; + if ((numChan << 13) + _waveTablesTotalDataSize > 65536) + return 5; + + if (numChan == _numReservedChannels) + return 0; + + if (numChan < _numReservedChannels) { + int c = 8 - _numReservedChannels; + for (int i = numChan; i; i--) { + uint8 f = ~_chanFlags[c--]; + _pcmChanEffectPlaying &= f; + } + } else { + int c = 7 - _numReservedChannels; + for (int i = numChan - _numReservedChannels; i; i--) { + uint8 f = ~_chanFlags[c--]; + _pcmChanKeyPressed &= f; + _pcmChanKeyPlaying &= f; + } + } + + static const uint8 reserveChanFlags[] = { 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF }; + _numReservedChannels = numChan; + _pcmChanReserved = reserveChanFlags[_numReservedChannels]; + + return 0; +} + +int TownsAudioInterface::intf_loadWaveTable(va_list &args) { + uint8 *data = va_arg(args, uint8 *); + if (_numWaveTables > 127) + return 3; + + TownsAudio_WaveTable w; + w.readHeader(data); + if (!w.size) + return 6; + + if (_waveTablesTotalDataSize + w.size > 65504) + return 5; + + for (int i = 0; i < _numWaveTables; i++) { + if (_waveTables[i].id == w.id) + return 10; + } + + TownsAudio_WaveTable *s = &_waveTables[_numWaveTables++]; + s->readHeader(data); + + _waveTablesTotalDataSize += s->size; + callback(32, _waveTablesTotalDataSize, s->size, data + 32); + + return 0; +} + +int TownsAudioInterface::intf_unloadWaveTable(va_list &args) { + int id = va_arg(args, int); + + if (id == -1) { + for (int i = 0; i < 128; i++) + _waveTables[i].clear(); + _numWaveTables = 0; + _waveTablesTotalDataSize = 0; + } else { + if (_waveTables) { + for (int i = 0; i < _numWaveTables; i++) { + if (_waveTables[i].id == id) { + _numWaveTables--; + _waveTablesTotalDataSize -= _waveTables[i].size; + _waveTables[i].clear(); + for (; i < _numWaveTables; i++) + memcpy(&_waveTables[i], &_waveTables[i + 1], sizeof(TownsAudio_WaveTable)); + return 0; + } + return 9; + } + } + } + + return 0; +} + +int TownsAudioInterface::intf_pcmPlayEffect(va_list &args) { + int chan = va_arg(args, int); + int note = va_arg(args, int); + int velo = va_arg(args, int); + uint8 *data = va_arg(args, uint8 *); + + if (chan < 0x40 || chan > 0x47) + return 1; + + if (note & 0x80 || velo & 0x80) + return 3; + + chan -= 0x40; + + if (!(_pcmChanReserved & _chanFlags[chan])) + return 7; + + if ((_pcmChanEffectPlaying & _chanFlags[chan])) + return 2; + + TownsAudio_WaveTable w; + w.readHeader(data); + + if (w.size < (w.loopStart + w.loopLen)) + return 13; + + if (!w.size) + return 6; + + TownsAudio_PcmChannel *p = &_pcmChan[chan]; + + _pcmChanNote[chan] = note; + _pcmChanVelo[chan] = velo; + + p->note = note; + p->velo = velo << 1; + + p->loadExtData(data + 32, w.size); + p->setupLoop(w.loopStart, w.loopLen); + + pcmCalcPhaseStep(p, &w); + if (p->step > 2048) + p->step = 2048; + + _pcmChanEffectPlaying |= _chanFlags[chan]; + _pcmChanOut |= _chanFlags[chan]; + + return 0; +} + +int TownsAudioInterface::intf_pcmChanOff(va_list &args) { + int chan = va_arg(args, int); + pcmChanOff(chan); + return 0; +} + +int TownsAudioInterface::intf_pcmEffectPlaying(va_list &args) { + int chan = va_arg(args, int); + if (chan < 0x40 || chan > 0x47) + return 1; + chan -= 0x40; + return (_pcmChanEffectPlaying & _chanFlags[chan]) ? true : false; +} + +int TownsAudioInterface::intf_fmKeyOn(va_list &args) { + int chan = va_arg(args, int); + int note = va_arg(args, int); + int velo = va_arg(args, int); + return fmKeyOn(chan, note, velo); +} + +int TownsAudioInterface::intf_fmKeyOff(va_list &args) { + int chan = va_arg(args, int); + return fmKeyOff(chan); +} + +int TownsAudioInterface::intf_fmSetPanPos(va_list &args) { + int chan = va_arg(args, int); + int mode = va_arg(args, int); + return fmSetPanPos(chan, mode); +} + +int TownsAudioInterface::intf_fmSetInstrument(va_list &args) { + int chan = va_arg(args, int); + int instrId = va_arg(args, int); + return fmSetInstrument(chan, instrId); +} + +int TownsAudioInterface::intf_fmLoadInstrument(va_list &args) { + int instrId = va_arg(args, int); + uint8 *instrData = va_arg(args, uint8 *); + return fmLoadInstrument(instrId, instrData); +} + +int TownsAudioInterface::intf_fmSetPitch(va_list &args) { + int chan = va_arg(args, int); + uint16 freq = va_arg(args, int) & 0xffff; + return fmSetPitch(chan, freq); +} + +int TownsAudioInterface::intf_fmSetLevel(va_list &args) { + int chan = va_arg(args, int); + int lvl = va_arg(args, int); + return fmSetLevel(chan, lvl); +} + +int TownsAudioInterface::intf_fmReset(va_list &args) { + fmReset(); + return 0; +} + +int TownsAudioInterface::intf_setOutputVolume(va_list &args) { + int chanType = va_arg(args, int); + int left = va_arg(args, int); + int right = va_arg(args, int); + + if (left & 0xff80 || right & 0xff80) + return 3; + + static const uint8 flags[] = { 0x0C, 0x30, 0x40, 0x80 }; + + uint8 chan = (chanType & 0x40) ? 8 : 12; + + chanType &= 3; + left = (left & 0x7e) >> 1; + right = (right & 0x7e) >> 1; + + if (chan) + _outputVolumeFlags |= flags[chanType]; + else + _outputVolumeFlags &= ~flags[chanType]; + + if (chanType > 1) { + _outputLevel[chan + chanType] = left; + } else { + if (chanType == 0) + chan -= 8; + _outputLevel[chan] = left; + _outputLevel[chan + 1] = right; + } + + updateOutputVolume(); + + return 0; +} + +int TownsAudioInterface::intf_resetOutputVolume(va_list &args) { + memset(_outputLevel, 0, sizeof(_outputLevel)); + _outputMuteFlags = 0; + _outputVolumeFlags = 0; + updateOutputVolume(); + return 0; +} + +int TownsAudioInterface::intf_updateOutputVolume(va_list &args) { + int flags = va_arg(args, int); + _outputMuteFlags = flags & 3; + updateOutputVolume(); + return 0; +} + +int TownsAudioInterface::intf_pcmUpdateEnvelopeGenerator(va_list &args) { + for (int i = 0; i < 8; i++) + pcmUpdateEnvelopeGenerator(i); + return 0; +} + +int TownsAudioInterface::intf_notImpl(va_list &args) { + return 4; +} + +void TownsAudioInterface::fmReset() { + TownsPC98_FmSynth::reset(); + + _fmChanPlaying = 0; + memset(_fmChanNote, 0, sizeof(_fmChanNote)); + memset(_fmChanPitch, 0, sizeof(_fmChanPitch)); + + memset(_fmSaveReg[0], 0, 240); + memset(&_fmSaveReg[0][240], 0x7f, 16); + memset(_fmSaveReg[1], 0, 256); + memset(&_fmSaveReg[1][240], 0x7f, 16); + _fmSaveReg[0][243] = _fmSaveReg[0][247] = _fmSaveReg[0][251] = _fmSaveReg[0][255] = + _fmSaveReg[1][243] = _fmSaveReg[1][247] = _fmSaveReg[1][251] = _fmSaveReg[1][255] = 0xff; + + for (int i = 0; i < 128; i++) + fmLoadInstrument(i, _fmDefaultInstrument); + + bufferedWriteReg(0, 0x21, 0); + bufferedWriteReg(0, 0x2C, 0x80); + bufferedWriteReg(0, 0x2B, 0); + bufferedWriteReg(0, 0x27, 0x30); + + for (int i = 0; i < 6; i++) { + fmKeyOff(i); + fmSetInstrument(i, 0); + fmSetLevel(i, 127); + } +} + +int TownsAudioInterface::fmKeyOn(int chan, int note, int velo) { + if (chan > 5) + return 1; + if (note < 12 || note > 107 || (velo & 0x80)) + return 3; + if (_fmChanPlaying & _chanFlags[chan]) + return 2; + + _fmChanPlaying |= _chanFlags[chan]; + note -= 12; + + _fmChanNote[chan] = note; + int16 pitch = _fmChanPitch[chan]; + + uint8 part = chan > 2 ? 1 : 0; + if (chan > 2) + chan -= 3; + + int frq = 0; + uint8 bl = 0; + + if (note) { + frq = _frequency[(note - 1) % 12]; + bl = (note - 1) / 12; + } else { + frq = 616; + } + + frq += pitch; + + if (frq < 616) { + if (!bl) { + frq = 616; + } else { + frq += 616; + --bl; + } + } else if (frq > 1232) { + if (bl == 7) { + frq = 15500; + } else { + frq -= 616; + ++bl; + } + } + + frq |= (bl << 11); + + bufferedWriteReg(part, chan + 0xa4, (frq >> 8) & 0xff); + bufferedWriteReg(part, chan + 0xa0, frq & 0xff); + + velo = (velo >> 2) + 96; + uint16 c = _carrier[_fmSaveReg[part][0xb0 + chan] & 7]; + _fmSaveReg[part][0xe0 + chan] = velo; + + for (uint8 reg = 0x40 + chan; reg < 0x50; reg += 4) { + c += c; + if (c & 0x100) { + c &= 0xff; + bufferedWriteReg(part, reg, (((((((_fmSaveReg[part][0x80 + reg] ^ 0x7f) * velo) >> 7) + 1) * _fmSaveReg[part][0xd0 + chan]) >> 7) + 1) ^ 0x7f); + } + } + + uint8 v = chan; + if (part) + v |= 4; + + for (uint8 reg = 0x80 + chan; reg < 0x90; reg += 4) + writeReg(part, reg, _fmSaveReg[part][reg] | 0x0f); + + writeReg(0, 0x28, v); + + for (uint8 reg = 0x80 + chan; reg < 0x90; reg += 4) + writeReg(part, reg, _fmSaveReg[part][reg]); + + bufferedWriteReg(0, 0x28, v | 0xf0); + + return 0; +} + +int TownsAudioInterface::fmKeyOff(int chan) { + if (chan > 5) + return 1; + _fmChanPlaying &= ~_chanFlags[chan]; + if (chan > 2) + chan++; + bufferedWriteReg(0, 0x28, chan); + return 0; +} + +int TownsAudioInterface::fmChanOff(int chan) { + if (chan > 5) + return 1; + _fmChanPlaying &= ~_chanFlags[chan]; + + uint8 part = chan > 2 ? 1 : 0; + if (chan > 2) + chan -= 3; + + for (uint8 reg = 0x80 + chan; reg < 0x90; reg += 4) + writeReg(part, reg, _fmSaveReg[part][reg] | 0x0f); + + if (part) + chan += 4; + writeReg(0, 0x28, chan); + return 0; +} + +int TownsAudioInterface::fmSetPanPos(int chan, int value) { + if (chan > 5) + return 1; + + uint8 part = chan > 2 ? 1 : 0; + if (chan > 2) + chan -= 3; + + if (value > 0x40) + value = 0x40; + else if (value < 0x40) + value = 0x80; + else + value = 0xC0; + + bufferedWriteReg(part, 0xb4 + chan, (_fmSaveReg[part][0xb4 + chan] & 0x3f) | value); + return 0; +} + +int TownsAudioInterface::fmSetInstrument(int chan, int instrId) { + if (chan > 5) + return 1; + if (instrId > 127) + return 3; + + uint8 part = chan > 2 ? 1 : 0; + if (chan > 2) + chan -= 3; + + uint8 *src = &_fmInstruments[instrId * 48 + 8]; + + uint16 c = _carrier[src[24] & 7]; + uint8 reg = 0x30 + chan; + + for (; reg < 0x40; reg += 4) + bufferedWriteReg(part, reg, *src++); + + for (; reg < 0x50; reg += 4) { + uint8 v = *src++; + _fmSaveReg[part][0x80 + reg] = _fmSaveReg[part][reg] = v; + c += c; + if (c & 0x100) { + c &= 0xff; + v = 127; + } + writeReg(part, reg, v); + } + + for (; reg < 0x90; reg += 4) + bufferedWriteReg(part, reg, *src++); + + reg += 0x20; + bufferedWriteReg(part, reg, *src++); + + uint8 v = *src++; + reg += 4; + if (v < 64) + v |= (_fmSaveReg[part][reg] & 0xc0); + bufferedWriteReg(part, reg, v); + + return 0; +} + +int TownsAudioInterface::fmLoadInstrument(int instrId, const uint8 *data) { + if (instrId > 127) + return 3; + assert(data); + memcpy(&_fmInstruments[instrId * 48], data, 48); + return 0; +} + +int TownsAudioInterface::fmSetPitch(int chan, int pitch) { + if (chan > 5) + return 1; + + uint8 bl = _fmChanNote[chan]; + int frq = 0; + + if (pitch < 0) { + if (bl) { + if (pitch < -8008) + pitch = -8008; + pitch *= -1; + pitch /= 13; + frq = _frequency[(bl - 1) % 12] - pitch; + bl = (bl - 1) / 12; + _fmChanPitch[chan] = -pitch; + + if (frq < 616) { + if (bl) { + frq += 616; + bl--; + } else { + frq = 616; + bl = 0; + } + } + } else { + frq = 616; + bl = 0; + } + + } else if (pitch > 0) { + if (bl < 96) { + if (pitch > 8008) + pitch = 8008; + pitch /= 13; + + if (bl) { + frq = _frequency[(bl - 1) % 12] + pitch; + bl = (bl - 1) / 12; + } else { + frq = 616; + bl = 0; + } + + _fmChanPitch[chan] = pitch; + + if (frq > 1232) { + if (bl < 7) { + frq -= 616; + bl++; + } else { + frq = 1164; + bl = 7; + } + } else { + if (bl >= 7 && frq > 1164) + frq = 1164; + } + + } else { + frq = 1164; + bl = 7; + } + } else { + _fmChanPitch[chan] = 0; + if (bl) { + frq = _frequency[(bl - 1) % 12]; + bl = (bl - 1) / 12; + } else { + frq = 616; + bl = 0; + } + } + + uint8 part = chan > 2 ? 1 : 0; + if (chan > 2) + chan -= 3; + + frq |= (bl << 11); + + bufferedWriteReg(part, chan + 0xa4, (frq >> 8)); + bufferedWriteReg(part, chan + 0xa0, (frq & 0xff)); + + return 0; +} + +int TownsAudioInterface::fmSetLevel(int chan, int lvl) { + if (chan > 5) + return 1; + if (lvl > 127) + return 3; + + uint8 part = chan > 2 ? 1 : 0; + if (chan > 2) + chan -= 3; + + uint16 c = _carrier[_fmSaveReg[part][0xb0 + chan] & 7]; + _fmSaveReg[part][0xd0 + chan] = lvl; + + for (uint8 reg = 0x40 + chan; reg < 0x50; reg += 4) { + c += c; + if (c & 0x100) { + c &= 0xff; + bufferedWriteReg(part, reg, (((((((_fmSaveReg[part][0x80 + reg] ^ 0x7f) * lvl) >> 7) + 1) * _fmSaveReg[part][0xe0 + chan]) >> 7) + 1) ^ 0x7f); + } + } + return 0; +} + +void TownsAudioInterface::bufferedWriteReg(uint8 part, uint8 regAddress, uint8 value) { + _fmSaveReg[part][regAddress] = value; + writeReg(part, regAddress, value); +} + +void TownsAudioInterface::pcmReset() { + _pcmChanOut = 0; + _pcmChanReserved = _pcmChanKeyPressed = _pcmChanEffectPlaying = _pcmChanKeyPlaying = 0; + _numReservedChannels = 0; + + memset(_pcmChanNote, 0, 8); + memset(_pcmChanVelo, 0, 8); + memset(_pcmChanLevel, 0, 8); + + for (int i = 0; i < 8; i++) + _pcmChan[i].clear(); + + memset(_pcmInstruments, 0, 128 * 32); + static uint8 name[] = { 0x4E, 0x6F, 0x20, 0x44, 0x61, 0x74, 0x61, 0x21 }; + for (int i = 0; i < 32; i++) + memcpy(_pcmInstruments + i * 128, name, 8); + + for (int i = 0; i < 128; i++) + _waveTables[i].clear(); + _numWaveTables = 0; + _waveTablesTotalDataSize = 0; + + for (int i = 0x40; i < 0x48; i++) { + pcmSetInstrument(i, 0); + pcmSetLevel(i, 127); + } +} + +int TownsAudioInterface::pcmKeyOn(int chan, int note, int velo) { + if (chan < 0x40 || chan > 0x47) + return 1; + + if (note & 0x80 || velo & 0x80) + return 3; + + chan -= 0x40; + + if ((_pcmChanReserved & _chanFlags[chan]) || (_pcmChanKeyPressed & _chanFlags[chan])) + return 2; + + _pcmChanNote[chan] = note; + _pcmChanVelo[chan] = velo; + + TownsAudio_PcmChannel *p = &_pcmChan[chan]; + p->note = note; + + uint8 *instr = _pcmChan[chan].curInstrument; + int i = 0; + for (; i < 8; i++) { + if (note <= instr[16 + 2 * i]) + break; + } + + if (i == 8) + return 8; + + int il = i << 3; + p->note += instr[il + 70]; + + p->envTotalLevel = instr[il + 64]; + p->envAttackRate = instr[il + 65]; + p->envDecayRate = instr[il + 66]; + p->envSustainLevel = instr[il + 67]; + p->envSustainRate = instr[il + 68]; + p->envReleaseRate = instr[il + 69]; + p->envStep = 0; + + int32 id = (int32)READ_LE_UINT32(&instr[i * 4 + 32]); + + for (i = 0; i < _numWaveTables; i++) { + if (id == _waveTables[i].id) + break; + } + + if (i == _numWaveTables) + return 9; + + TownsAudio_WaveTable *w = &_waveTables[i]; + + p->data = w->data; + p->dataEnd = w->data + w->size; + p->setupLoop(w->loopStart, w->loopLen); + + pcmCalcPhaseStep(p, w); + + uint32 lvl = _pcmChanLevel[chan] * _pcmChanVelo[chan]; + p->envTotalLevel = ((p->envTotalLevel * lvl) >> 14) & 0xff; + p->envSustainLevel = ((p->envSustainLevel * lvl) >> 14) & 0xff; + + p->envAttack(); + p->velo = (p->envCurrentLevel >> 8) << 1; + + _pcmChanKeyPressed |= _chanFlags[chan]; + _pcmChanKeyPlaying |= _chanFlags[chan]; + _pcmChanOut |= _chanFlags[chan]; + + return 0; +} + +int TownsAudioInterface::pcmKeyOff(int chan) { + if (chan < 0x40 || chan > 0x47) + return 1; + + chan -= 0x40; + _pcmChanKeyPressed &= ~_chanFlags[chan]; + _pcmChan[chan].envRelease(); + return 0; +} + +int TownsAudioInterface::pcmChanOff(int chan) { + if (chan < 0x40 || chan > 0x47) + return 1; + + chan -= 0x40; + + _pcmChanKeyPressed &= ~_chanFlags[chan]; + _pcmChanEffectPlaying &= ~_chanFlags[chan]; + _pcmChanKeyPlaying &= ~_chanFlags[chan]; + _pcmChanOut &= ~_chanFlags[chan]; + + return 0; +} + +int TownsAudioInterface::pcmSetPanPos(int chan, int mode) { + if (chan > 0x47) + return 1; + if (mode & 0x80) + return 3; + + chan -= 0x40; + uint8 blc = 0x77; + + if (mode > 64) { + mode -= 64; + blc = ((blc ^ (mode >> 3)) + (mode << 4)) & 0xff; + } else if (mode < 64) { + mode = (mode >> 3) ^ 7; + blc = ((119 + mode) ^ (mode << 4)) & 0xff; + } + + _pcmChan[chan].panLeft = blc & 0x0f; + _pcmChan[chan].panRight = blc >> 4; + + return 0; +} + +int TownsAudioInterface::pcmSetInstrument(int chan, int instrId) { + if (chan > 0x47) + return 1; + if (instrId > 31) + return 3; + chan -= 0x40; + _pcmChan[chan].curInstrument = &_pcmInstruments[instrId * 128]; + return 0; +} + +int TownsAudioInterface::pcmLoadInstrument(int instrId, const uint8 *data) { + if (instrId > 31) + return 3; + assert(data); + memcpy(&_pcmInstruments[instrId * 128], data, 128); + return 0; +} + +int TownsAudioInterface::pcmSetPitch(int chan, int pitch) { + if (chan > 0x47) + return 1; + + if (pitch < -8192 || pitch > 8191) + return 3; + + chan -= 0x40; + TownsAudio_PcmChannel *p = &_pcmChan[chan]; + + uint32 pts = 0x4000; + + if (pitch < 0) + pts = (0x20000000 / (-pitch + 0x2001)) >> 2; + else if (pitch > 0) + pts = (((pitch + 0x2001) << 16) / 0x2000) >> 2; + + p->stepPitch = pts & 0xffff; + p->step = (p->stepNote * p->stepPitch) >> 14; + +// if (_pcmChanUnkFlag & _chanFlags[chan]) +// unk[chan] = (((p->step * 1000) << 11) / 98) / 20833; + + /*else*/ + if ((_pcmChanEffectPlaying & _chanFlags[chan]) && (p->step > 2048)) + p->step = 2048; + + return 0; +} + +int TownsAudioInterface::pcmSetLevel(int chan, int lvl) { + if (chan > 0x47) + return 1; + + if (lvl & 0x80) + return 3; + + chan -= 0x40; + TownsAudio_PcmChannel *p = &_pcmChan[chan]; + + if (_pcmChanReserved & _chanFlags[chan]) { + _pcmChanVelo[chan] = lvl; + p->velo = lvl << 1; + } else { + int32 t = p->envStep * lvl; + if (_pcmChanLevel[chan]) + t /= _pcmChanLevel[chan]; + p->envStep = t; + t = p->envCurrentLevel * lvl; + if (_pcmChanLevel[chan]) + t /= _pcmChanLevel[chan]; + p->envCurrentLevel = t; + _pcmChanLevel[chan] = lvl; + p->velo = p->envCurrentLevel >> 8; + } + + return 0; +} + +void TownsAudioInterface::pcmUpdateEnvelopeGenerator(int chan) { + TownsAudio_PcmChannel *p = &_pcmChan[chan]; + if (!p->envCurrentLevel) { + _pcmChanKeyPlaying &= ~_chanFlags[chan]; + p->envState = kEnvReady; + } + + if (!(_pcmChanKeyPlaying & _chanFlags[chan])) + return; + + switch (p->envState) { + case kEnvAttacking: + if (((p->envCurrentLevel + p->envStep) >> 8) > p->envTotalLevel) { + p->envDecay(); + return; + } else { + p->envCurrentLevel += p->envStep; + } + break; + + case kEnvDecaying: + if (((p->envCurrentLevel - p->envStep) >> 8) < p->envSustainLevel) { + p->envSustain(); + return; + } else { + p->envCurrentLevel -= p->envStep; + } + break; + + case kEnvSustaining: + case kEnvReleasing: + p->envCurrentLevel -= p->envStep; + if (p->envCurrentLevel <= 0) + p->envCurrentLevel = 0; + break; + + default: + break; + } + p->velo = (p->envCurrentLevel >> 8) << 1; +} + +void TownsAudioInterface::pcmCalcPhaseStep(TownsAudio_PcmChannel *p, TownsAudio_WaveTable *w) { + int8 diff = p->note - w->baseNote; + uint16 r = w->rate + w->rateOffs; + uint16 bl = 0; + uint32 s = 0; + + if (diff < 0) { + diff *= -1; + bl = diff % 12; + diff /= 12; + s = (r >> diff); + if (bl) + s = (s * _pcmPhase2[bl]) >> 16; + + } else if (diff > 0) { + bl = diff % 12; + diff /= 12; + s = (r << diff); + if (bl) + s += ((s * _pcmPhase1[bl]) >> 16); + + } else { + s = r; + } + + p->stepNote = s & 0xffff; + p->step = (s * p->stepPitch) >> 14; +} + +void TownsAudioInterface::updateOutputVolume() { + // FM Towns seems to support volumes of 0 - 63 for each channel. + // We recalculate sane values for our 0 to 255 volume range and + // balance values for our -128 to 127 volume range + + // CD-AUDIO + int vl = (int)(((float)_outputLevel[12] * 127.0f) / 63.0f); + int vr = (int)(((float)_outputLevel[13] * 127.0f) / 63.0f); + int8 balance = vr - vl; + vl = (int)(((float)_outputLevel[12] * 255.0f) / 63.0f); + vr = (int)(((float)_outputLevel[13] * 255.0f) / 63.0f); + AudioCD.setVolume((vl + vr) >> 1); + AudioCD.setBalance(balance); +} + +const uint8 TownsAudioInterface::_chanFlags[] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 +}; + +const uint16 TownsAudioInterface::_frequency[] = { + 0x028C, 0x02B4, 0x02DC, 0x030A, 0x0338, 0x0368, 0x039C, 0x03D4, 0x040E, 0x044A, 0x048C, 0x04D0 +}; + +const uint8 TownsAudioInterface::_carrier[] = { + 0x10, 0x10, 0x10, 0x10, 0x30, 0x70, 0x70, 0xF0 +}; + +const uint8 TownsAudioInterface::_fmDefaultInstrument[] = { + 0x45, 0x4C, 0x45, 0x50, 0x49, 0x41, 0x4E, 0x4F, 0x01, 0x0A, 0x02, 0x01, + 0x1E, 0x32, 0x05, 0x00, 0x9C, 0xDC, 0x9C, 0xDC, 0x07, 0x03, 0x14, 0x08, + 0x00, 0x03, 0x05, 0x05, 0x55, 0x45, 0x27, 0xA7, 0x04, 0xC0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +const uint16 TownsAudioInterface::_pcmPhase1[] = { + 0x879B, 0x0F37, 0x1F58, 0x306E, 0x4288, 0x55B6, 0x6A08, 0x7F8F, 0x965E, 0xAE88, 0xC882, 0xE341 +}; + +const uint16 TownsAudioInterface::_pcmPhase2[] = { + 0xFEFE, 0xF1A0, 0xE411, 0xD744, 0xCB2F, 0xBFC7, 0xB504, 0xAAE2, 0xA144, 0x9827, 0x8FAC +}; + +TownsAudio_PcmChannel::TownsAudio_PcmChannel() { + extData = 0; + clear(); +} + +TownsAudio_PcmChannel::~TownsAudio_PcmChannel() { + clear(); +} + +void TownsAudio_PcmChannel::loadExtData(uint8 *buffer, uint32 size) { + delete[] extData; + extData = new int8[size]; + int8 *src = (int8 *)buffer; + int8 *dst = extData; + for (uint32 i = 0; i < size; i++) + *dst++ = *src & 0x80 ? (*src++ & 0x7f) : -*src++; + + data = extData; + dataEnd = extData + size; + pos = 0; +} + +void TownsAudio_PcmChannel::setupLoop(uint32 start, uint32 len) { + loopLen = len << 11; + loopEnd = loopLen ? &data[(start + loopLen) >> 11] : dataEnd; + pos = start; +} + +void TownsAudio_PcmChannel::clear() { + curInstrument = 0; + note = 0; + velo = 0; + + data = 0; + dataEnd = 0; + loopLen = 0; + + pos = 0; + loopEnd = 0; + + step = 0; + stepNote = 0x4000; + stepPitch = 0x4000; + + panLeft = panRight = 7; + + envTotalLevel = envAttackRate = envDecayRate = envSustainLevel = envSustainRate = envReleaseRate = 0; + envStep = envCurrentLevel = 0; + + envState = kEnvReady; + + delete[] extData; + extData = 0; +} + +void TownsAudio_PcmChannel::envAttack() { + envState = kEnvAttacking; + int16 t = envTotalLevel << 8; + if (envAttackRate == 127) { + envStep = 0; + } else if (envAttackRate) { + envStep = t / envAttackRate; + envCurrentLevel = 1; + } else { + envCurrentLevel = t; + envDecay(); + } +} + +void TownsAudio_PcmChannel::envDecay() { + envState = kEnvDecaying; + int16 t = envTotalLevel - envSustainLevel; + if (t < 0 || envDecayRate == 127) { + envStep = 0; + } else if (envDecayRate) { + envStep = (t << 8) / envDecayRate; + } else { + envCurrentLevel = envSustainLevel << 8; + envSustain(); + } +} + +void TownsAudio_PcmChannel::envSustain() { + envState = kEnvSustaining; + if (envSustainLevel && envSustainRate) + envStep = (envSustainRate == 127) ? 0 : (envCurrentLevel / envSustainRate) >> 1; + else + envStep = envCurrentLevel = 1; +} + +void TownsAudio_PcmChannel::envRelease() { + envState = kEnvReleasing; + if (envReleaseRate == 127) + envStep = 0; + else if (envReleaseRate) + envStep = envCurrentLevel / envReleaseRate; + else + envStep = envCurrentLevel = 1; +} + +TownsAudio_WaveTable::TownsAudio_WaveTable() { + data = 0; + clear(); +} + +TownsAudio_WaveTable::~TownsAudio_WaveTable() { + clear(); +} + +void TownsAudio_WaveTable::readHeader(const uint8 *buffer) { + memcpy(name, buffer, 8); + name[8] = 0; + id = READ_LE_UINT32(&buffer[8]); + size = READ_LE_UINT32(&buffer[12]); + loopStart = READ_LE_UINT32(&buffer[16]); + loopLen = READ_LE_UINT32(&buffer[20]); + rate = READ_LE_UINT16(&buffer[24]); + rateOffs = READ_LE_UINT16(&buffer[26]); + baseNote = READ_LE_UINT32(&buffer[28]); +} + +void TownsAudio_WaveTable::readData(const uint8 *buffer) { + if (!size) + return; + + delete[] data; + data = new int8[size]; + + const int8 *src = (const int8 *)buffer; + int8 *dst = data; + for (uint32 i = 0; i < size; i++) + *dst++ = *src & 0x80 ? (*src++ & 0x7f) : -*src++; +} + +void TownsAudio_WaveTable::clear() { + name[0] = name[8] = 0; + id = -1; + size = 0; + loopStart = 0; + loopLen = 0; + rate = 0; + rateOffs = 0; + baseNote = 0; + delete[] data; + data = 0; +} + diff --git a/sound/softsynth/fmtowns_pc98/towns_audio.h b/sound/softsynth/fmtowns_pc98/towns_audio.h new file mode 100644 index 0000000000..950c016b4e --- /dev/null +++ b/sound/softsynth/fmtowns_pc98/towns_audio.h @@ -0,0 +1,178 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef TOWNS_AUDIO_H +#define TOWNS_AUDIO_H + +#include "sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h" + +class TownsAudioInterfacePluginDriver { +public: + virtual ~TownsAudioInterfacePluginDriver() {} + virtual void timerCallback(int timerId) = 0; +}; + +class TownsAudio_PcmChannel; +class TownsAudio_WaveTable; + +class TownsAudioInterface : public TownsPC98_FmSynth { +public: + TownsAudioInterface(Audio::Mixer *mixer, TownsAudioInterfacePluginDriver *driver); + ~TownsAudioInterface(); + + bool init(); + + int callback(int command, ...); + + void setMusicVolume(int volume); + void setSoundEffectVolume(int volume); + // Defines the channels used as sound effect channels for the purpose of ScummVM GUI volume control. + // The first 6 bits are the 6 fm channels. The next 8 bits are pcm channels. + void setSoundEffectChanMask(int mask); + +private: + void nextTickEx(int32 *buffer, uint32 bufferSize); + + void timerCallbackA(); + void timerCallbackB(); + + typedef int (TownsAudioInterface::*TownsAudioIntfCallback)(va_list &); + const TownsAudioIntfCallback *_intfOpcodes; + + int intf_reset(va_list &args); + int intf_keyOn(va_list &args); + int intf_keyOff(va_list &args); + int intf_setPanPos(va_list &args); + int intf_setInstrument(va_list &args); + int intf_loadInstrument(va_list &args); + int intf_setPitch(va_list &args); + int intf_setLevel(va_list &args); + int intf_chanOff(va_list &args); + int intf_writeReg(va_list &args); + int intf_writeRegBuffer(va_list &args); + int intf_readRegBuffer(va_list &args); + int intf_setTimerA(va_list &args); + int intf_setTimerB(va_list &args); + int intf_enableTimerA(va_list &args); + int intf_enableTimerB(va_list &args); + int intf_loadSamples(va_list &args); + int intf_reserveEffectChannels(va_list &args); + int intf_loadWaveTable(va_list &args); + int intf_unloadWaveTable(va_list &args); + int intf_pcmPlayEffect(va_list &args); + int intf_pcmChanOff(va_list &args); + int intf_pcmEffectPlaying(va_list &args); + int intf_fmKeyOn(va_list &args); + int intf_fmKeyOff(va_list &args); + int intf_fmSetPanPos(va_list &args); + int intf_fmSetInstrument(va_list &args); + int intf_fmLoadInstrument(va_list &args); + int intf_fmSetPitch(va_list &args); + int intf_fmSetLevel(va_list &args); + int intf_fmReset(va_list &args); + int intf_setOutputVolume(va_list &args); + int intf_resetOutputVolume(va_list &args); + int intf_updateOutputVolume(va_list &args); + int intf_pcmUpdateEnvelopeGenerator(va_list &args); + + int intf_notImpl(va_list &args); + + void fmReset(); + int fmKeyOn(int chan, int note, int velo); + int fmKeyOff(int chan); + int fmChanOff(int chan); + int fmSetPanPos(int chan, int mode); + int fmSetInstrument(int chan, int instrId); + int fmLoadInstrument(int instrId, const uint8 *data); + int fmSetPitch(int chan, int pitch); + int fmSetLevel(int chan, int lvl); + + void bufferedWriteReg(uint8 part, uint8 regAddress, uint8 value); + + uint8 _fmChanPlaying; + uint8 _fmChanNote[6]; + int16 _fmChanPitch[6]; + + uint8 *_fmSaveReg[2]; + uint8 *_fmInstruments; + + void pcmReset(); + int pcmKeyOn(int chan, int note, int velo); + int pcmKeyOff(int chan); + int pcmChanOff(int chan); + int pcmSetPanPos(int chan, int mode); + int pcmSetInstrument(int chan, int instrId); + int pcmLoadInstrument(int instrId, const uint8 *data); + int pcmSetPitch(int chan, int pitch); + int pcmSetLevel(int chan, int lvl); + void pcmUpdateEnvelopeGenerator(int chan); + + TownsAudio_PcmChannel *_pcmChan; + uint8 _pcmChanOut; + uint8 _pcmChanReserved; + uint8 _pcmChanKeyPressed; + uint8 _pcmChanEffectPlaying; + uint8 _pcmChanKeyPlaying; + + uint8 _pcmChanNote[8]; + uint8 _pcmChanVelo[8]; + uint8 _pcmChanLevel[8]; + + uint8 _numReservedChannels; + uint8 *_pcmInstruments; + + TownsAudio_WaveTable *_waveTables; + uint8 _numWaveTables; + uint32 _waveTablesTotalDataSize; + + void pcmCalcPhaseStep(TownsAudio_PcmChannel *p, TownsAudio_WaveTable *w); + + void updateOutputVolume(); + uint8 _outputVolumeFlags; + uint8 _outputLevel[16]; + uint8 _outputMuteFlags; + + const float _baserate; + uint32 _timerBase; + uint32 _tickLength; + uint32 _timer; + + uint16 _musicVolume; + uint16 _sfxVolume; + int _pcmSfxChanMask; + + TownsAudioInterfacePluginDriver *_drv; + bool _ready; + + static const uint8 _chanFlags[]; + static const uint16 _frequency[]; + static const uint8 _carrier[]; + static const uint8 _fmDefaultInstrument[]; + static const uint16 _pcmPhase1[]; + static const uint16 _pcmPhase2[]; +}; + +#endif + diff --git a/sound/softsynth/fmtowns_pc98/towns_euphony.cpp b/sound/softsynth/fmtowns_pc98/towns_euphony.cpp new file mode 100644 index 0000000000..0c0c203cc9 --- /dev/null +++ b/sound/softsynth/fmtowns_pc98/towns_euphony.cpp @@ -0,0 +1,907 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#include "sound/softsynth/fmtowns_pc98/towns_euphony.h" +#include "common/endian.h" + +TownsEuphonyDriver::TownsEuphonyDriver(Audio::Mixer *mixer) : _activeChannels(0), _sustainChannels(0), + _assignedChannels(0), _paraCount(0), _command(0), _tEnable(0), _tMode(0), _tOrdr(0), _tLevel(0), + _tTranspose(0), _musicPos(0), _musicStart(0), _playing(false), _eventBuffer(0), _bufferedEventsCount(0), + _tempoControlMode(0) { + _para[0] = _para[1] = 0; + _intf = new TownsAudioInterface(mixer, this); + resetTempo(); +} + +TownsEuphonyDriver::~TownsEuphonyDriver() { + delete[] _activeChannels; + delete[] _sustainChannels; + delete[] _assignedChannels; + + delete[] _tEnable; + delete[] _tMode; + delete[] _tOrdr; + delete[] _tLevel; + delete[] _tTranspose; + + delete _intf; +} + +bool TownsEuphonyDriver::init() { + if (!_intf->init()) + return false; + + _activeChannels = new int8[16]; + _sustainChannels = new int8[16]; + _assignedChannels = new ActiveChannel[128]; + _eventBuffer = new DlEvent[64]; + + _tEnable = new uint8[32]; + _tMode = new uint8[32]; + _tOrdr = new uint8[32]; + _tLevel = new int8[32]; + _tTranspose = new int8[32]; + + reset(); + + return true; +} + +void TownsEuphonyDriver::reset() { + _intf->callback(0); + + _intf->callback(74); + _intf->callback(70); + _intf->callback(75, 3); + + setTimerA(true, 1); + setTimerA(false, 1); + setTimerB(true, 221); + + _paraCount = _command = _para[0] = _para[1] = 0; + memset(_sustainChannels, 0, 16); + memset(_activeChannels, -1, 16); + for (int i = 0; i < 128; i++) { + _assignedChannels[i].chan = _assignedChannels[i].next = -1; + _assignedChannels[i].note = _assignedChannels[i].sub = 0; + } + + int e = 0; + for (int i = 0; i < 6; i++) + assignChannel(i, e++); + for (int i = 0x40; i < 0x48; i++) + assignChannel(i, e++); + + resetTables(); + + memset(_eventBuffer, 0, 64 * sizeof(DlEvent)); + _bufferedEventsCount = 0; + + _playing = _endOfTrack = _suspendParsing = _loop = false; + _elapsedEvents = 0; + _tempoDiff = 0; + + resetTempo(); + + if (_tempoControlMode == 1) { + //if (///) + // return; + setTempoIntern(_defaultTempo); + } else { + setTempoIntern(_defaultTempo); + } + + resetControl(); +} + +void TownsEuphonyDriver::loadInstrument(int chanType, int id, const uint8 *data) { + _intf->callback(5, chanType, id, data); +} + +void TownsEuphonyDriver::loadWaveTable(const uint8 *data) { + _intf->callback(34, data); +} + +void TownsEuphonyDriver::unloadWaveTable(int id) { + _intf->callback(35, id); +} + +void TownsEuphonyDriver::reserveSoundEffectChannels(int num) { + _intf->callback(33, num); + uint32 volMask = 0; + + if (num > 8) + return; + + for (uint32 v = 1 << 13; num; num--) { + volMask |= v; + v >>= 1; + } + + _intf->setSoundEffectChanMask(volMask); +} + +int TownsEuphonyDriver::setMusicTempo(int tempo) { + if (tempo > 250) + return 3; + _defaultTempo = tempo; + _trackTempo = tempo; + setTempoIntern(tempo); + return 0; +} + +int TownsEuphonyDriver::startMusicTrack(const uint8 *data, int trackSize, int startTick) { + if (_playing) + return 2; + + _musicPos = _musicStart = data; + _defaultBaseTickLen = _baseTickLen = startTick; + _musicTrackSize = trackSize; + _timeStampBase = _timeStampDest = 0; + _tickCounter = 0; + _playing = true; + + return 0; +} + +void TownsEuphonyDriver::setMusicLoop(bool loop) { + _loop = loop; +} + +void TownsEuphonyDriver::stopParser() { + if (_playing) { + _playing = false; + _pulseCount = 0; + _endOfTrack = false; + flushEventBuffer(); + resetControl(); + } +} + +void TownsEuphonyDriver::continueParsing() { + _suspendParsing = false; +} + +void TownsEuphonyDriver::playSoundEffect(int chan, int note, int velo, const uint8 *data) { + _intf->callback(37, chan, note, velo, data); +} + +void TownsEuphonyDriver::stopSoundEffect(int chan) { + _intf->callback(39, chan); +} + +bool TownsEuphonyDriver::soundEffectIsPlaying(int chan) { + return _intf->callback(40, chan) ? true : false; +} + +void TownsEuphonyDriver::chanPanPos(int chan, int mode) { + _intf->callback(3, chan, mode); +} + +void TownsEuphonyDriver::chanPitch(int chan, int pitch) { + _intf->callback(7, chan, pitch); +} + +void TownsEuphonyDriver::chanVolume(int chan, int vol) { + _intf->callback(8, chan, vol); +} + +void TownsEuphonyDriver::setOutputVolume(int mode, int volLeft, int volRight) { + _intf->callback(67, mode, volLeft, volRight); +} + +int TownsEuphonyDriver::chanEnable(int tableEntry, int val) { + if (tableEntry > 31) + return 3; + _tEnable[tableEntry] = val; + return 0; +} + +int TownsEuphonyDriver::chanMode(int tableEntry, int val) { + if (tableEntry > 31) + return 3; + _tMode[tableEntry] = val; + return 0; +} + +int TownsEuphonyDriver::chanOrdr(int tableEntry, int val) { + if (tableEntry > 31) + return 3; + if (val < 16) + _tOrdr[tableEntry] = val; + return 0; +} + +int TownsEuphonyDriver::chanVolumeShift(int tableEntry, int val) { + if (tableEntry > 31) + return 3; + if (val <= 40) + _tLevel[tableEntry] = (int8)(val & 0xff); + return 0; +} + +int TownsEuphonyDriver::chanNoteShift(int tableEntry, int val) { + if (tableEntry > 31) + return 3; + if (val <= 40) + _tTranspose[tableEntry] = (int8)(val & 0xff); + return 0; +} + +int TownsEuphonyDriver::assignChannel(int chan, int tableEntry) { + if (tableEntry > 15 || chan > 127 || chan < 0) + return 3; + + ActiveChannel *a = &_assignedChannels[chan]; + if (a->chan == tableEntry) + return 0; + + if (a->chan != -1) { + int8 *b = &_activeChannels[a->chan]; + while (*b != chan) { + b = &_assignedChannels[*b].next; + if (*b == -1 && *b != chan) + return 3; + } + + *b = a->next; + + if (a->note) + _intf->callback(2, chan); + + a->chan = a->next = -1; + a->note = 0; + } + + a->next = _activeChannels[tableEntry]; + _activeChannels[tableEntry] = chan; + a->chan = tableEntry; + a->note = a->sub = 0; + + return 0; +} + +void TownsEuphonyDriver::timerCallback(int timerId) { + switch (timerId) { + case 0: + updatePulseCount(); + while (_pulseCount > 0) { + --_pulseCount; + updateTimeStampBase(); + if (!_playing) + continue; + updateEventBuffer(); + updateParser(); + updateCheckEot(); + } + break; + default: + break; + } +} + +void TownsEuphonyDriver::setMusicVolume(int volume) { + _intf->setMusicVolume(volume); +} + +void TownsEuphonyDriver::setSoundEffectVolume(int volume) { + _intf->setSoundEffectVolume(volume); +} + +void TownsEuphonyDriver::resetTables() { + memset(_tEnable, 0xff, 32); + memset(_tMode, 0xff, 16); + memset(_tMode + 16, 0, 16); + for (int i = 0; i < 32; i++) + _tOrdr[i] = i & 0x0f; + memset(_tLevel, 0, 32); + memset(_tTranspose, 0, 32); +} + +void TownsEuphonyDriver::resetTempo() { + _defaultBaseTickLen = _baseTickLen = 0x33; + _pulseCount = 0; + _extraTimingControlRemainder = 0; + _extraTimingControl = 16; + _tempoModifier = 0; + _timeStampDest = 0; + _deltaTicks = 0; + _tickCounter = 0; + _defaultTempo = 90; + _trackTempo = 90; +} + +void TownsEuphonyDriver::setTempoIntern(int tempo) { + tempo = CLIP(tempo + _tempoModifier, 0, 500); + if (_tempoControlMode == 0) { + _timerSetting = 34750 / (tempo + 30); + _extraTimingControl = 16; + + while (_timerSetting < 126) { + _timerSetting <<= 1; + _extraTimingControl <<= 1; + } + + while (_timerSetting > 383) { + _timerSetting >>= 1; + _extraTimingControl >>= 1; + } + + setTimerA(true, -(_timerSetting - 2)); + + } else if (_tempoControlMode == 1) { + _timerSetting = 312500 / (tempo + 30); + _extraTimingControl = 16; + while (_timerSetting < 1105) { + _timerSetting <<= 1; + _extraTimingControl <<= 1; + } + + } else if (_tempoControlMode == 2) { + _timerSetting = 625000 / (tempo + 30); + _extraTimingControlRemainder = 0; + } +} + +void TownsEuphonyDriver::setTimerA(bool enable, int tempo) { + _intf->callback(21, enable ? 255 : 0, tempo); +} + +void TownsEuphonyDriver::setTimerB(bool enable, int tempo) { + _intf->callback(22, enable ? 255 : 0, tempo); +} + +void TownsEuphonyDriver::updatePulseCount() { + int tc = _extraTimingControl + _extraTimingControlRemainder; + _extraTimingControlRemainder = tc & 0x0f; + tc >>= 4; + _tempoDiff -= tc; + + while (_tempoDiff < 0) { + _elapsedEvents++; + _tempoDiff += 4; + } + + if (_playing && !_suspendParsing) + _pulseCount += tc; +} + +void TownsEuphonyDriver::updateTimeStampBase() { + static const uint16 table[] = { 0x180, 0xC0, 0x80, 0x60, 0x40, 0x30, 0x20, 0x18 }; + if ((uint32)(table[_baseTickLen >> 4] * ((_baseTickLen & 0x0f) + 1)) > ++_tickCounter) + return; + ++_timeStampDest; + _tickCounter = 0; + _deltaTicks = 0; +} + +void TownsEuphonyDriver::updateParser() { + for (bool loop = true; loop;) { + uint8 cmd = _musicPos[0]; + + if (cmd == 0xff || cmd == 0xf7) { + jumpNextLoop(); + + } else if (cmd < 0x90) { + _endOfTrack = true; + flushEventBuffer(); + loop = false; + + } else if (_timeStampBase > _timeStampDest) { + loop = false; + + } else { + if (_timeStampBase == _timeStampDest) { + uint16 timeStamp = READ_LE_UINT16(&_musicPos[2]); + uint8 l = (timeStamp & 0xff) + (timeStamp & 0xff); + timeStamp = ((timeStamp & 0xff00) | l) >> 1; + if (timeStamp > _tickCounter) + loop = false; + } + + if (loop) { + if (parseNext()) + loop = false; + } + } + } +} + +void TownsEuphonyDriver::updateCheckEot() { + if (!_endOfTrack || _bufferedEventsCount) + return; + stopParser(); +} + +bool TownsEuphonyDriver::parseNext() { +#define OPC(x) &TownsEuphonyDriver::evt##x + static const EuphonyOpcode opcodes[] = { + OPC(NotImpl), + OPC(SetupNote), + OPC(PolyphonicAftertouch), + OPC(ControlPitch), + OPC(InstrumentChanAftertouch), + OPC(InstrumentChanAftertouch), + OPC(ControlPitch) + }; +#undef OPC + + uint cmd = _musicPos[0]; + if (cmd != 0xfe && cmd != 0xfd) { + if (cmd >= 0xf0) { + cmd &= 0x0f; + if (cmd == 0) + evtLoadInstrument(); + else if (cmd == 2) + evtAdvanceTimestampOffset(); + else if (cmd == 8) + evtTempo(); + else if (cmd == 12) + evtModeOrdrChange(); + jumpNextLoop(); + return false; + + } else if (!(this->*opcodes[(cmd - 0x80) >> 4])()) { + jumpNextLoop(); + return false; + } + } + + if (cmd == 0xfd) { + _suspendParsing = true; + return true; + } + + if (!_loop) { + _endOfTrack = true; + return true; + } + + _endOfTrack = false; + _musicPos = _musicStart; + _timeStampBase = _timeStampDest = _tickCounter = 0; + _baseTickLen = _defaultBaseTickLen; + + return false; +} + +void TownsEuphonyDriver::jumpNextLoop() { + _musicPos += 6; + if (_musicPos >= _musicStart + _musicTrackSize) + _musicPos = _musicStart; +} + +void TownsEuphonyDriver::updateEventBuffer() { + DlEvent *e = _eventBuffer; + for (int i = _bufferedEventsCount; i; e++) { + if (e->evt == 0) + continue; + if (--e->len) { + --i; + continue; + } + processBufferNote(e->mode, e->evt, e->note, e->velo); + e->evt = 0; + --i; + --_bufferedEventsCount; + } +} + +void TownsEuphonyDriver::flushEventBuffer() { + DlEvent *e = _eventBuffer; + for (int i = _bufferedEventsCount; i; e++) { + if (e->evt == 0) + continue; + processBufferNote(e->mode, e->evt, e->note, e->velo); + e->evt = 0; + --i; + --_bufferedEventsCount; + } +} + +void TownsEuphonyDriver::processBufferNote(int mode, int evt, int note, int velo) { + if (!velo) + evt &= 0x8f; + sendEvent(mode, evt); + sendEvent(mode, note); + sendEvent(mode, velo); +} + +void TownsEuphonyDriver::resetControl() { + for (int i = 0; i < 32; i++) { + if (_tOrdr[i] > 15) { + for (int ii = 0; ii < 16; ii++) + resetControlIntern(_tMode[i], ii); + } else { + resetControlIntern(_tMode[i], _tOrdr[i]); + } + } +} + +void TownsEuphonyDriver::resetControlIntern(int mode, int chan) { + sendEvent(mode, 0xb0 | chan); + sendEvent(mode, 0x40); + sendEvent(mode, 0); + sendEvent(mode, 0xb0 | chan); + sendEvent(mode, 0x7b); + sendEvent(mode, 0); + sendEvent(mode, 0xb0 | chan); + sendEvent(mode, 0x79); + sendEvent(mode, 0x40); +} + +uint8 TownsEuphonyDriver::appendEvent(uint8 evt, uint8 chan) { + if (evt >= 0x80 && evt < 0xf0 && _tOrdr[chan] < 16) + return (evt & 0xf0) | _tOrdr[chan]; + return evt; +} + +void TownsEuphonyDriver::sendEvent(uint8 mode, uint8 command) { + if (mode == 0) { + warning("TownsEuphonyDriver: Mode 0 not implemented."); + + } else if (mode == 0x10) { + warning("TownsEuphonyDriver: Mode 0x10 not implemented."); + + } else if (mode == 0xff) { + if (command >= 0xf0) { + _paraCount = 1; + _command = 0; + } else if (command >= 0x80) { + _paraCount = 1; + _command = command; + } else if (_command >= 0x80) { + switch ((_command - 0x80) >> 4) { + case 0: + if (_paraCount < 2) { + _paraCount++; + _para[0] = command; + } else { + _paraCount = 1; + _para[1] = command; + sendNoteOff(); + } + break; + + case 1: + if (_paraCount < 2) { + _paraCount++; + _para[0] = command; + } else { + _paraCount = 1; + _para[1] = command; + if (command) + sendNoteOn(); + else + sendNoteOff(); + } + break; + + case 2: + if (_paraCount < 2) { + _paraCount++; + _para[0] = command; + } else { + _paraCount = 1; + } + break; + + case 3: + if (_paraCount < 2) { + _paraCount++; + _para[0] = command; + } else { + _paraCount = 1; + _para[1] = command; + + if (_para[0] == 7) + sendChanVolume(); + else if (_para[0] == 10) + sendPanPosition(); + else if (_para[0] == 64) + sendAllNotesOff(); + } + break; + + case 4: + _paraCount = 1; + _para[0] = command; + sendSetInstrument(); + break; + + case 5: + _paraCount = 1; + _para[0] = command; + break; + + case 6: + if (_paraCount < 2) { + _paraCount++; + _para[0] = command; + } else { + _paraCount = 1; + _para[1] = command; + sendPitch(); + } + break; + } + } + } +} + +bool TownsEuphonyDriver::evtSetupNote() { + if (_musicPos[1] > 31) + return false; + if (!_tEnable[_musicPos[1]]) { + jumpNextLoop(); + return (_musicPos[0] == 0xfe || _musicPos[0] == 0xfd) ? true : false; + } + uint8 evt = appendEvent(_musicPos[0], _musicPos[1]); + uint8 mode = _tMode[_musicPos[1]]; + uint8 note = _musicPos[4]; + uint8 velo = _musicPos[5]; + + sendEvent(mode, evt); + sendEvent(mode, applyNoteShift(note)); + sendEvent(mode, applyVolumeShift(velo)); + + jumpNextLoop(); + if (_musicPos[0] == 0xfe || _musicPos[0] == 0xfd) + return true; + + velo = _musicPos[5]; + uint16 len = ((((_musicPos[1] << 4) | (_musicPos[2] << 8)) >> 4) & 0xff) | ((((_musicPos[3] << 4) | (_musicPos[4] << 8)) >> 4) << 8); + + int i = 0; + for (; i < 64; i++) { + if (_eventBuffer[i].evt == 0) + break; + } + + if (i == 64) { + processBufferNote(mode, evt, note, velo); + } else { + _eventBuffer[i].evt = evt; + _eventBuffer[i].mode = mode; + _eventBuffer[i].note = note; + _eventBuffer[i].velo = velo; + _eventBuffer[i].len = len ? len : 1; + _bufferedEventsCount++; + } + + return false; +} + +bool TownsEuphonyDriver::evtPolyphonicAftertouch() { + if (_musicPos[1] > 31) + return false; + if (!_tEnable[_musicPos[1]]) + return false; + + uint8 evt = appendEvent(_musicPos[0], _musicPos[1]); + uint8 mode = _tMode[_musicPos[1]]; + + sendEvent(mode, evt); + sendEvent(mode, applyNoteShift(_musicPos[4])); + sendEvent(mode, _musicPos[5]); + + return false; +} + +bool TownsEuphonyDriver::evtControlPitch() { + if (_musicPos[1] > 31) + return false; + if (!_tEnable[_musicPos[1]]) + return false; + + uint8 evt = appendEvent(_musicPos[0], _musicPos[1]); + uint8 mode = _tMode[_musicPos[1]]; + + sendEvent(mode, evt); + sendEvent(mode, _musicPos[4]); + sendEvent(mode, _musicPos[5]); + + return false; +} + +bool TownsEuphonyDriver::evtInstrumentChanAftertouch() { + if (_musicPos[1] > 31) + return false; + if (!_tEnable[_musicPos[1]]) + return false; + + uint8 evt = appendEvent(_musicPos[0], _musicPos[1]); + uint8 mode = _tMode[_musicPos[1]]; + + sendEvent(mode, evt); + sendEvent(mode, _musicPos[4]); + + return false; +} + +bool TownsEuphonyDriver::evtLoadInstrument() { + return false; +} + +bool TownsEuphonyDriver::evtAdvanceTimestampOffset() { + ++_timeStampBase; + _baseTickLen = _musicPos[1]; + return false; +} + +bool TownsEuphonyDriver::evtTempo() { + uint8 l = _musicPos[4] << 1; + _trackTempo = (l | (_musicPos[5] << 8)) >> 1; + setTempoIntern(_trackTempo); + return false; +} + +bool TownsEuphonyDriver::evtModeOrdrChange() { + if (_musicPos[1] > 31) + return false; + if (!_tEnable[_musicPos[1]]) + return false; + + if (_musicPos[4] == 1) + _tMode[_musicPos[1]] = _musicPos[5]; + else if (_musicPos[4] == 2) + _tOrdr[_musicPos[1]] = _musicPos[5]; + + return false; +} + +uint8 TownsEuphonyDriver::applyNoteShift(uint8 in) { + int out = _tTranspose[_musicPos[1]]; + if (!out) + return in; + out += (in & 0x7f); + + if (out > 127) + out -= 12; + + if (out < 0) + out += 12; + + return out & 0xff; +} + +uint8 TownsEuphonyDriver::applyVolumeShift(uint8 in) { + int out = _tLevel[_musicPos[1]]; + out += (in & 0x7f); + out = CLIP(out, 1, 127); + + return out & 0xff; +} + +void TownsEuphonyDriver::sendNoteOff() { + int8 *chan = &_activeChannels[_command & 0x0f]; + if (*chan == -1) + return; + + while (_assignedChannels[*chan].note != _para[0]) { + chan = &_assignedChannels[*chan].next; + if (*chan == -1) + return; + } + + if (_sustainChannels[_command & 0x0f]) { + _assignedChannels[*chan].note |= 0x80; + } else { + _assignedChannels[*chan].note = 0; + _intf->callback(2, *chan); + } +} + +void TownsEuphonyDriver::sendNoteOn() { + if (!_para[0]) + return; + int8 *chan = &_activeChannels[_command & 0x0f]; + if (*chan == -1) + return; + + do { + _assignedChannels[*chan].sub++; + chan = &_assignedChannels[*chan].next; + } while (*chan != -1); + + chan = &_activeChannels[_command & 0x0f]; + + int d = 0; + int c = 0; + bool found = false; + + do { + if (!_assignedChannels[*chan].note) { + found = true; + break; + } + if (d <= _assignedChannels[*chan].sub) { + c = *chan; + d = _assignedChannels[*chan].sub; + } + chan = &_assignedChannels[*chan].next; + } while (*chan != -1); + + if (found) + c = *chan; + else + _intf->callback(2, c); + + _assignedChannels[c].note = _para[0]; + _assignedChannels[c].sub = 0; + _intf->callback(1, c, _para[0], _para[1]); +} + +void TownsEuphonyDriver::sendChanVolume() { + int8 *chan = &_activeChannels[_command & 0x0f]; + while (*chan != -1) { + _intf->callback(8, *chan, _para[1] & 0x7f); + chan = &_assignedChannels[*chan].next; + }; +} + +void TownsEuphonyDriver::sendPanPosition() { + int8 *chan = &_activeChannels[_command & 0x0f]; + while (*chan != -1) { + _intf->callback(3, *chan, _para[1] & 0x7f); + chan = &_assignedChannels[*chan].next; + }; +} + +void TownsEuphonyDriver::sendAllNotesOff() { + if (_para[1] > 63) { + _sustainChannels[_command & 0x0f] = -1; + return; + } + + _sustainChannels[_command & 0x0f] = 0; + int8 *chan = &_activeChannels[_command & 0x0f]; + while (*chan != -1) { + if (_assignedChannels[*chan].note & 0x80) { + _assignedChannels[*chan].note = 0; + _intf->callback(2, *chan); + } + chan = &_assignedChannels[*chan].next; + }; +} + +void TownsEuphonyDriver::sendSetInstrument() { + int8 *chan = &_activeChannels[_command & 0x0f]; + while (*chan != -1) { + _intf->callback(4, *chan, _para[0]); + _intf->callback(7, *chan, 0); + chan = &_assignedChannels[*chan].next; + }; +} + +void TownsEuphonyDriver::sendPitch() { + int8 *chan = &_activeChannels[_command & 0x0f]; + while (*chan != -1) { + _para[0] += _para[0]; + int16 pitch = (((READ_LE_UINT16(_para)) >> 1) & 0x3fff) - 0x2000; + _intf->callback(7, *chan, pitch); + chan = &_assignedChannels[*chan].next; + }; +} diff --git a/sound/softsynth/fmtowns_pc98/towns_euphony.h b/sound/softsynth/fmtowns_pc98/towns_euphony.h new file mode 100644 index 0000000000..fa1f8ba496 --- /dev/null +++ b/sound/softsynth/fmtowns_pc98/towns_euphony.h @@ -0,0 +1,187 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef TOWNS_EUP_H +#define TOWNS_EUP_H + +#include "sound/softsynth/fmtowns_pc98/towns_audio.h" + +class TownsEuphonyDriver : public TownsAudioInterfacePluginDriver { +public: + TownsEuphonyDriver(Audio::Mixer *mixer); + virtual ~TownsEuphonyDriver(); + + bool init(); + void reset(); + + void loadInstrument(int chanType, int id, const uint8 *data); + void loadWaveTable(const uint8 *data); + void unloadWaveTable(int id); + void reserveSoundEffectChannels(int num); + + int setMusicTempo(int tempo); + int startMusicTrack(const uint8 *data, int trackSize, int startTick); + void setMusicLoop(bool loop); + void stopParser(); + bool parserIsPlaying() {return _playing; } + void continueParsing(); + + void playSoundEffect(int chan, int note, int velo, const uint8 *data); + void stopSoundEffect(int chan); + bool soundEffectIsPlaying(int chan); + + void chanPanPos(int chan, int mode); + void chanPitch(int chan, int pitch); + void chanVolume(int chan, int vol); + + void setOutputVolume(int chanType, int volLeft, int volRight); + + int chanEnable(int tableEntry, int val); + int chanMode(int tableEntry, int val); + int chanOrdr(int tableEntry, int val); + int chanVolumeShift(int tableEntry, int val); + int chanNoteShift(int tableEntry, int val); + + int assignChannel(int chan, int tableEntry); + + void timerCallback(int timerId); + + void setMusicVolume(int volume); + void setSoundEffectVolume(int volume); + + TownsAudioInterface *intf() { + return _intf; + } + +private: + void resetTables(); + + void resetTempo(); + void setTempoIntern(int tempo); + void setTimerA(bool enable, int tempo); + void setTimerB(bool enable, int tempo); + + void updatePulseCount(); + void updateTimeStampBase(); + void updateParser(); + void updateCheckEot(); + + bool parseNext(); + void jumpNextLoop(); + + void updateEventBuffer(); + void flushEventBuffer(); + void processBufferNote(int mode, int evt, int note, int velo); + + void resetControl(); + void resetControlIntern(int mode, int chan); + uint8 appendEvent(uint8 evt, uint8 chan); + + void sendEvent(uint8 mode, uint8 command); + + typedef bool(TownsEuphonyDriver::*EuphonyOpcode)(); + bool evtSetupNote(); + bool evtPolyphonicAftertouch(); + bool evtControlPitch(); + bool evtInstrumentChanAftertouch(); + bool evtLoadInstrument(); + bool evtAdvanceTimestampOffset(); + bool evtTempo(); + bool evtModeOrdrChange(); + bool evtNotImpl() { + return false; + } + + uint8 applyNoteShift(uint8 in); + uint8 applyVolumeShift(uint8 in); + + void sendNoteOff(); + void sendNoteOn(); + void sendChanVolume(); + void sendPanPosition(); + void sendAllNotesOff(); + void sendSetInstrument(); + void sendPitch(); + + int8 *_activeChannels; + int8 *_sustainChannels; + + struct ActiveChannel { + int8 chan; + int8 next; + uint8 note; + uint8 sub; + } *_assignedChannels; + + uint8 *_tEnable; + uint8 *_tMode; + uint8 *_tOrdr; + int8 *_tLevel; + int8 *_tTranspose; + + struct DlEvent { + uint8 evt; + uint8 mode; + uint8 note; + uint8 velo; + uint16 len; + } *_eventBuffer; + int _bufferedEventsCount; + + uint8 _para[2]; + uint8 _paraCount; + uint8 _command; + + uint8 _defaultBaseTickLen; + uint8 _baseTickLen; + uint32 _pulseCount; + int _tempoControlMode; + int _extraTimingControlRemainder; + int _extraTimingControl; + int _timerSetting; + int8 _tempoDiff; + int _tempoModifier; + uint32 _timeStampDest; + uint32 _timeStampBase; + int8 _elapsedEvents; + uint8 _deltaTicks; + uint32 _tickCounter; + uint8 _defaultTempo; + int _trackTempo; + + bool _loop; + bool _playing; + bool _endOfTrack; + bool _suspendParsing; + + const uint8 *_musicStart; + const uint8 *_musicPos; + uint32 _musicTrackSize; + + TownsAudioInterface *_intf; +}; + +#endif + diff --git a/sound/softsynth/fmtowns_pc98/towns_pc98_driver.cpp b/sound/softsynth/fmtowns_pc98/towns_pc98_driver.cpp new file mode 100644 index 0000000000..82d0bd0438 --- /dev/null +++ b/sound/softsynth/fmtowns_pc98/towns_pc98_driver.cpp @@ -0,0 +1,1403 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#include "sound/softsynth/fmtowns_pc98/towns_pc98_driver.h" +#include "common/endian.h" + +class TownsPC98_MusicChannel { +public: + TownsPC98_MusicChannel(TownsPC98_AudioDriver *driver, uint8 regOffs, uint8 flgs, uint8 num, + uint8 key, uint8 prt, uint8 id); + virtual ~TownsPC98_MusicChannel(); + virtual void init(); + + typedef enum channelState { + CHS_RECALCFREQ = 0x01, + CHS_KEYOFF = 0x02, + CHS_SSGOFF = 0x04, + CHS_VBROFF = 0x08, + CHS_ALLOFF = 0x0f, + CHS_PROTECT = 0x40, + CHS_EOT = 0x80 + } ChannelState; + + virtual void loadData(uint8 *data); + virtual void processEvents(); + virtual void processFrequency(); + virtual bool processControlEvent(uint8 cmd); + + virtual void keyOn(); + void keyOff(); + + void setOutputLevel(); + virtual void fadeStep(); + virtual void reset(); + + const uint8 _idFlag; + +protected: + void setupVibrato(); + bool processVibrato(); + + bool control_dummy(uint8 para); + bool control_f0_setPatch(uint8 para); + bool control_f1_presetOutputLevel(uint8 para); + bool control_f2_setKeyOffTime(uint8 para); + bool control_f3_setFreqLSB(uint8 para); + bool control_f4_setOutputLevel(uint8 para); + bool control_f5_setTempo(uint8 para); + bool control_f6_repeatSection(uint8 para); + bool control_f7_setupVibrato(uint8 para); + bool control_f8_toggleVibrato(uint8 para); + bool control_fa_writeReg(uint8 para); + virtual bool control_fb_incOutLevel(uint8 para); + virtual bool control_fc_decOutLevel(uint8 para); + bool control_fd_jump(uint8 para); + virtual bool control_ff_endOfTrack(uint8 para); + + uint8 _ticksLeft; + uint8 _algorithm; + uint8 _instr; + uint8 _totalLevel; + uint8 _frqBlockMSB; + int8 _frqLSB; + uint8 _keyOffTime; + bool _hold; + uint8 *_dataPtr; + uint8 _vbrInitDelayHi; + uint8 _vbrInitDelayLo; + int16 _vbrModInitVal; + uint8 _vbrDuration; + uint8 _vbrCurDelay; + int16 _vbrModCurVal; + uint8 _vbrDurLeft; + uint16 _frequency; + uint8 _block; + uint8 _regOffset; + uint8 _flags; + uint8 _ssgTl; + uint8 _ssgStep; + uint8 _ssgTicksLeft; + uint8 _ssgTargetLvl; + uint8 _ssgStartLvl; + + const uint8 _chanNum; + const uint8 _keyNum; + const uint8 _part; + + TownsPC98_AudioDriver *_drv; + + typedef bool (TownsPC98_MusicChannel::*ControlEventFunc)(uint8 para); + const ControlEventFunc *controlEvents; +}; + +class TownsPC98_MusicChannelSSG : public TownsPC98_MusicChannel { +public: + TownsPC98_MusicChannelSSG(TownsPC98_AudioDriver *driver, uint8 regOffs, + uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id); + virtual ~TownsPC98_MusicChannelSSG() {} + void init(); + + virtual void loadData(uint8 *data); + void processEvents(); + void processFrequency(); + bool processControlEvent(uint8 cmd); + + void keyOn(); + void nextShape(); + + void protect(); + void restore(); + virtual void reset(); + + void fadeStep(); + +protected: + void setOutputLevel(uint8 lvl); + + bool control_f0_setPatch(uint8 para); + bool control_f1_setTotalLevel(uint8 para); + bool control_f4_setAlgorithm(uint8 para); + bool control_f9_loadCustomPatch(uint8 para); + bool control_fb_incOutLevel(uint8 para); + bool control_fc_decOutLevel(uint8 para); + bool control_ff_endOfTrack(uint8 para); + + typedef bool (TownsPC98_MusicChannelSSG::*ControlEventFunc)(uint8 para); + const ControlEventFunc *controlEvents; +}; + +class TownsPC98_SfxChannel : public TownsPC98_MusicChannelSSG { +public: + TownsPC98_SfxChannel(TownsPC98_AudioDriver *driver, uint8 regOffs, + uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) : + TownsPC98_MusicChannelSSG(driver, regOffs, flgs, num, key, prt, id) {} + ~TownsPC98_SfxChannel() {} + + void loadData(uint8 *data); + void reset(); +}; + +class TownsPC98_MusicChannelPCM : public TownsPC98_MusicChannel { +public: + TownsPC98_MusicChannelPCM(TownsPC98_AudioDriver *driver, uint8 regOffs, + uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id); + ~TownsPC98_MusicChannelPCM() {} + void init(); + + void loadData(uint8 *data); + void processEvents(); + bool processControlEvent(uint8 cmd); + +private: + bool control_f1_prcStart(uint8 para); + bool control_ff_endOfTrack(uint8 para); + + typedef bool (TownsPC98_MusicChannelPCM::*ControlEventFunc)(uint8 para); + const ControlEventFunc *controlEvents; +}; + +TownsPC98_MusicChannel::TownsPC98_MusicChannel(TownsPC98_AudioDriver *driver, uint8 regOffs, uint8 flgs, uint8 num, + uint8 key, uint8 prt, uint8 id) : _drv(driver), _regOffset(regOffs), _flags(flgs), _chanNum(num), _keyNum(key), + _part(prt), _idFlag(id), controlEvents(0) { + + _ticksLeft = _algorithm = _instr = _totalLevel = _frqBlockMSB = _keyOffTime = 0; + _ssgStartLvl = _ssgTl = _ssgStep = _ssgTicksLeft = _ssgTargetLvl = _block = 0; + _vbrInitDelayHi = _vbrInitDelayLo = _vbrDuration = _vbrCurDelay = _vbrDurLeft = 0; + _frqLSB = 0; + _hold = false; + _dataPtr = 0; + _vbrModInitVal = _vbrModCurVal = 0; + _frequency = 0; +} + +TownsPC98_MusicChannel::~TownsPC98_MusicChannel() { +} + +void TownsPC98_MusicChannel::init() { +#define Control(x) &TownsPC98_MusicChannel::control_##x + static const ControlEventFunc ctrlEvents[] = { + Control(f0_setPatch), + Control(f1_presetOutputLevel), + Control(f2_setKeyOffTime), + Control(f3_setFreqLSB), + Control(f4_setOutputLevel), + Control(f5_setTempo), + Control(f6_repeatSection), + Control(f7_setupVibrato), + Control(f8_toggleVibrato), + Control(dummy), + Control(fa_writeReg), + Control(fb_incOutLevel), + Control(fc_decOutLevel), + Control(fd_jump), + Control(dummy), + Control(ff_endOfTrack) + }; +#undef Control + + controlEvents = ctrlEvents; +} + +void TownsPC98_MusicChannel::keyOff() { + // all operators off + uint8 value = _keyNum & 0x0f; + if (_part) + value |= 4; + uint8 regAddress = 0x28; + _drv->writeReg(0, regAddress, value); + _flags |= CHS_KEYOFF; +} + +void TownsPC98_MusicChannel::keyOn() { + // all operators on + uint8 value = _keyNum | 0xf0; + if (_part) + value |= 4; + uint8 regAddress = 0x28; + _drv->writeReg(0, regAddress, value); +} + +void TownsPC98_MusicChannel::loadData(uint8 *data) { + _flags = (_flags & ~CHS_EOT) | CHS_ALLOFF; + _ticksLeft = 1; + _dataPtr = data; + _totalLevel = 0x7F; + + uint8 *tmp = _dataPtr; + for (bool loop = true; loop;) { + uint8 cmd = *tmp++; + if (cmd < 0xf0) { + tmp++; + } else if (cmd == 0xff) { + if (READ_LE_UINT16(tmp)) { + _drv->_looping |= _idFlag; + tmp += _drv->_opnFxCmdLen[cmd - 240]; + } else + loop = false; + } else if (cmd == 0xf6) { + // reset repeat section countdown + tmp[0] = tmp[1]; + tmp += 4; + } else { + tmp += _drv->_opnFxCmdLen[cmd - 240]; + } + } +} + +void TownsPC98_MusicChannel::processEvents() { + if (_flags & CHS_EOT) + return; + + if (!_hold && _ticksLeft == _keyOffTime) + keyOff(); + + if (--_ticksLeft) + return; + + if (!_hold) + keyOff(); + + uint8 cmd = 0; + bool loop = true; + + while (loop) { + cmd = *_dataPtr++; + if (cmd < 0xf0) + loop = false; + else if (!processControlEvent(cmd)) + return; + } + + uint8 para = *_dataPtr++; + + if (cmd == 0x80) { + keyOff(); + _hold = false; + } else { + keyOn(); + + if (_hold == false || cmd != _frqBlockMSB) + _flags |= CHS_RECALCFREQ; + + _hold = (para & 0x80) ? true : false; + _frqBlockMSB = cmd; + } + + _ticksLeft = para & 0x7f; +} + +void TownsPC98_MusicChannel::processFrequency() { + if (_flags & CHS_RECALCFREQ) { + + _frequency = (((const uint16 *)_drv->_opnFreqTable)[_frqBlockMSB & 0x0f] + _frqLSB) | (((_frqBlockMSB & 0x70) >> 1) << 8); + + _drv->writeReg(_part, _regOffset + 0xa4, (_frequency >> 8)); + _drv->writeReg(_part, _regOffset + 0xa0, (_frequency & 0xff)); + + setupVibrato(); + } + + if (!(_flags & CHS_VBROFF)) { + if (!processVibrato()) + return; + + _drv->writeReg(_part, _regOffset + 0xa4, (_frequency >> 8)); + _drv->writeReg(_part, _regOffset + 0xa0, (_frequency & 0xff)); + } +} + +void TownsPC98_MusicChannel::setupVibrato() { + _vbrCurDelay = _vbrInitDelayHi; + if (_flags & CHS_KEYOFF) { + _vbrModCurVal = _vbrModInitVal; + _vbrCurDelay += _vbrInitDelayLo; + } + _vbrDurLeft = (_vbrDuration >> 1); + _flags &= ~(CHS_KEYOFF | CHS_RECALCFREQ); +} + +bool TownsPC98_MusicChannel::processVibrato() { + if (--_vbrCurDelay) + return false; + + _vbrCurDelay = _vbrInitDelayHi; + _frequency += _vbrModCurVal; + + if (!--_vbrDurLeft) { + _vbrDurLeft = _vbrDuration; + _vbrModCurVal = -_vbrModCurVal; + } + + return true; +} + +bool TownsPC98_MusicChannel::processControlEvent(uint8 cmd) { + uint8 para = *_dataPtr++; + return (this->*controlEvents[cmd & 0x0f])(para); +} + +void TownsPC98_MusicChannel::setOutputLevel() { + uint8 outopr = _drv->_opnCarrier[_algorithm]; + uint8 reg = 0x40 + _regOffset; + + for (int i = 0; i < 4; i++) { + if (outopr & 1) + _drv->writeReg(_part, reg, _totalLevel); + outopr >>= 1; + reg += 4; + } +} + +void TownsPC98_MusicChannel::fadeStep() { + _totalLevel += 3; + if (_totalLevel > 0x7f) + _totalLevel = 0x7f; + setOutputLevel(); +} + +void TownsPC98_MusicChannel::reset() { + _hold = false; + _keyOffTime = 0; + _ticksLeft = 1; + + _flags = (_flags & ~CHS_EOT) | CHS_ALLOFF; + + _totalLevel = 0; + _algorithm = 0; + _flags = CHS_EOT; + _algorithm = 0; + + _block = 0; + _frequency = 0; + _frqBlockMSB = 0; + _frqLSB = 0; + + _ssgTl = 0; + _ssgStartLvl = 0; + _ssgTargetLvl = 0; + _ssgStep = 0; + _ssgTicksLeft = 0; + + _vbrInitDelayHi = 0; + _vbrInitDelayLo = 0; + _vbrModInitVal = 0; + _vbrDuration = 0; + _vbrCurDelay = 0; + _vbrModCurVal = 0; + _vbrDurLeft = 0; +} + +bool TownsPC98_MusicChannel::control_f0_setPatch(uint8 para) { + _instr = para; + uint8 reg = _regOffset + 0x80; + + for (int i = 0; i < 4; i++) { + // set release rate for each operator + _drv->writeReg(_part, reg, 0x0f); + reg += 4; + } + + const uint8 *tptr = _drv->_patches + ((uint32)_instr << 5); + reg = _regOffset + 0x30; + + // write registers 0x30 to 0x8f + for (int i = 0; i < 6; i++) { + _drv->writeReg(_part, reg, tptr[0]); + reg += 4; + _drv->writeReg(_part, reg, tptr[2]); + reg += 4; + _drv->writeReg(_part, reg, tptr[1]); + reg += 4; + _drv->writeReg(_part, reg, tptr[3]); + reg += 4; + tptr += 4; + } + + reg = _regOffset + 0xB0; + _algorithm = tptr[0] & 7; + // set feedback and algorithm + _drv->writeReg(_part, reg, tptr[0]); + + setOutputLevel(); + return true; +} + +bool TownsPC98_MusicChannel::control_f1_presetOutputLevel(uint8 para) { + if (_drv->_fading) + return true; + + _totalLevel = _drv->_opnLvlPresets[para]; + setOutputLevel(); + return true; +} + +bool TownsPC98_MusicChannel::control_f2_setKeyOffTime(uint8 para) { + _keyOffTime = para; + return true; +} + +bool TownsPC98_MusicChannel::control_f3_setFreqLSB(uint8 para) { + _frqLSB = (int8) para; + return true; +} + +bool TownsPC98_MusicChannel::control_f4_setOutputLevel(uint8 para) { + if (_drv->_fading) + return true; + + _totalLevel = para; + setOutputLevel(); + return true; +} + +bool TownsPC98_MusicChannel::control_f5_setTempo(uint8 para) { + _drv->setMusicTempo(para); + return true; +} + +bool TownsPC98_MusicChannel::control_f6_repeatSection(uint8 para) { + _dataPtr--; + _dataPtr[0]--; + + if (*_dataPtr) { + // repeat section until counter has reached zero + _dataPtr = _drv->_trackPtr + READ_LE_UINT16(_dataPtr + 2); + } else { + // reset counter, advance to next section + _dataPtr[0] = _dataPtr[1]; + _dataPtr += 4; + } + return true; +} + +bool TownsPC98_MusicChannel::control_f7_setupVibrato(uint8 para) { + _vbrInitDelayHi = _dataPtr[0]; + _vbrInitDelayLo = para; + _vbrModInitVal = (int16) READ_LE_UINT16(_dataPtr + 1); + _vbrDuration = _dataPtr[3]; + _dataPtr += 4; + _flags = (_flags & ~CHS_VBROFF) | CHS_KEYOFF | CHS_RECALCFREQ; + return true; +} + +bool TownsPC98_MusicChannel::control_f8_toggleVibrato(uint8 para) { + if (para == 0x10) { + if (*_dataPtr++) { + _flags = (_flags & ~CHS_VBROFF) | CHS_KEYOFF; + } else { + _flags |= CHS_VBROFF; + } + } else { + /* NOT IMPLEMENTED + uint8 skipChannels = para / 36; + uint8 entry = para % 36; + TownsPC98_AudioDriver::TownsPC98_MusicChannel *t = &chan[skipChannels]; + + t->unnamedEntries[entry] = *_dataPtr++;*/ + } + return true; +} + +bool TownsPC98_MusicChannel::control_fa_writeReg(uint8 para) { + _drv->writeReg(_part, para, *_dataPtr++); + return true; +} + +bool TownsPC98_MusicChannel::control_fb_incOutLevel(uint8 para) { + _dataPtr--; + if (_drv->_fading) + return true; + + uint8 val = (_totalLevel + 3); + if (val > 0x7f) + val = 0x7f; + + _totalLevel = val; + setOutputLevel(); + return true; +} + +bool TownsPC98_MusicChannel::control_fc_decOutLevel(uint8 para) { + _dataPtr--; + if (_drv->_fading) + return true; + + int8 val = (int8)(_totalLevel - 3); + if (val < 0) + val = 0; + + _totalLevel = (uint8) val; + setOutputLevel(); + return true; +} + +bool TownsPC98_MusicChannel::control_fd_jump(uint8 para) { + uint8 *tmp = _drv->_trackPtr + READ_LE_UINT16(_dataPtr - 1); + _dataPtr = (tmp[1] == 1) ? tmp : (_dataPtr + 1); + return true; +} + +bool TownsPC98_MusicChannel::control_dummy(uint8 para) { + _dataPtr--; + return true; +} + +bool TownsPC98_MusicChannel::control_ff_endOfTrack(uint8 para) { + uint16 val = READ_LE_UINT16(--_dataPtr); + if (val) { + // loop + _dataPtr = _drv->_trackPtr + val; + return true; + } else { + // quit parsing for active channel + --_dataPtr; + _flags |= CHS_EOT; + _drv->_finishedChannelsFlag |= _idFlag; + keyOff(); + return false; + } +} + +TownsPC98_MusicChannelSSG::TownsPC98_MusicChannelSSG(TownsPC98_AudioDriver *driver, uint8 regOffs, + uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) : + TownsPC98_MusicChannel(driver, regOffs, flgs, num, key, prt, id), controlEvents(0) { +} + +void TownsPC98_MusicChannelSSG::init() { + _algorithm = 0x80; + +#define Control(x) &TownsPC98_MusicChannelSSG::control_##x + static const ControlEventFunc ctrlEventsSSG[] = { + Control(f0_setPatch), + Control(f1_setTotalLevel), + Control(f2_setKeyOffTime), + Control(f3_setFreqLSB), + Control(f4_setAlgorithm), + Control(f5_setTempo), + Control(f6_repeatSection), + Control(f7_setupVibrato), + Control(f8_toggleVibrato), + Control(f9_loadCustomPatch), + Control(fa_writeReg), + Control(fb_incOutLevel), + Control(fc_decOutLevel), + Control(fd_jump), + Control(dummy), + Control(ff_endOfTrack) + }; +#undef Control + + controlEvents = ctrlEventsSSG; +} + +void TownsPC98_MusicChannelSSG::processEvents() { + if (_flags & CHS_EOT) + return; + + _drv->toggleRegProtection(_flags & CHS_PROTECT ? true : false); + + if (!_hold && _ticksLeft == _keyOffTime) + nextShape(); + + if (!--_ticksLeft) { + + uint8 cmd = 0; + bool loop = true; + + while (loop) { + cmd = *_dataPtr++; + if (cmd < 0xf0) + loop = false; + else if (!processControlEvent(cmd)) + return; + } + + uint8 para = *_dataPtr++; + + if (cmd == 0x80) { + nextShape(); + _hold = false; + } else { + if (!_hold) { + _instr &= 0xf0; + _ssgStep = _drv->_ssgPatches[_instr]; + _ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f; + _ssgTargetLvl = _drv->_ssgPatches[_instr + 2]; + _ssgStartLvl = _drv->_ssgPatches[_instr + 3]; + _flags = (_flags & ~CHS_SSGOFF) | CHS_KEYOFF; + } + + keyOn(); + + if (_hold == false || cmd != _frqBlockMSB) + _flags |= CHS_RECALCFREQ; + + _hold = (para & 0x80) ? true : false; + _frqBlockMSB = cmd; + } + + _ticksLeft = para & 0x7f; + } + + if (!(_flags & CHS_SSGOFF)) { + if (--_ssgTicksLeft) { + if (!_drv->_fading) + setOutputLevel(_ssgStartLvl); + return; + } + + _ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f; + + if (_drv->_ssgPatches[_instr + 1] & 0x80) { + uint8 t = _ssgStartLvl - _ssgStep; + + if (_ssgStep <= _ssgStartLvl && _ssgTargetLvl < t) { + if (!_drv->_fading) + setOutputLevel(t); + return; + } + } else { + int t = _ssgStartLvl + _ssgStep; + uint8 p = (uint8)(t & 0xff); + + if (t < 256 && _ssgTargetLvl > p) { + if (!_drv->_fading) + setOutputLevel(p); + return; + } + } + + setOutputLevel(_ssgTargetLvl); + if (_ssgStartLvl && !(_instr & 8)) { + _instr += 4; + _ssgStep = _drv->_ssgPatches[_instr]; + _ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f; + _ssgTargetLvl = _drv->_ssgPatches[_instr + 2]; + } else { + _flags |= CHS_SSGOFF; + setOutputLevel(0); + } + } +} + +void TownsPC98_MusicChannelSSG::processFrequency() { + if (_algorithm & 0x40) + return; + + if (_flags & CHS_RECALCFREQ) { + _block = _frqBlockMSB >> 4; + _frequency = ((const uint16 *)_drv->_opnFreqTableSSG)[_frqBlockMSB & 0x0f] + _frqLSB; + + uint16 f = _frequency >> _block; + _drv->writeReg(_part, _regOffset << 1, f & 0xff); + _drv->writeReg(_part, (_regOffset << 1) + 1, f >> 8); + + setupVibrato(); + } + + if (!(_flags & (CHS_EOT | CHS_VBROFF | CHS_SSGOFF))) { + if (!processVibrato()) + return; + + uint16 f = _frequency >> _block; + _drv->writeReg(_part, _regOffset << 1, f & 0xff); + _drv->writeReg(_part, (_regOffset << 1) + 1, f >> 8); + } +} + +bool TownsPC98_MusicChannelSSG::processControlEvent(uint8 cmd) { + uint8 para = *_dataPtr++; + return (this->*controlEvents[cmd & 0x0f])(para); +} + +void TownsPC98_MusicChannelSSG::nextShape() { + _instr = (_instr & 0xf0) + 0x0c; + _ssgStep = _drv->_ssgPatches[_instr]; + _ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f; + _ssgTargetLvl = _drv->_ssgPatches[_instr + 2]; +} + +void TownsPC98_MusicChannelSSG::keyOn() { + uint8 c = 0x7b; + uint8 t = (_algorithm & 0xC0) << 1; + if (_algorithm & 0x80) + t |= 4; + + c = (c << (_regOffset + 1)) | (c >> (7 - _regOffset)); + t = (t << (_regOffset + 1)) | (t >> (7 - _regOffset)); + + if (!(_algorithm & 0x80)) + _drv->writeReg(_part, 6, _algorithm & 0x7f); + + uint8 e = (_drv->readSSGStatus() & c) | t; + _drv->writeReg(_part, 7, e); +} + +void TownsPC98_MusicChannelSSG::protect() { + _flags |= CHS_PROTECT; +} + +void TownsPC98_MusicChannelSSG::restore() { + _flags &= ~CHS_PROTECT; + keyOn(); + _drv->writeReg(_part, 8 + _regOffset, _ssgTl); + uint16 f = _frequency >> _block; + _drv->writeReg(_part, _regOffset << 1, f & 0xff); + _drv->writeReg(_part, (_regOffset << 1) + 1, f >> 8); +} + +void TownsPC98_MusicChannelSSG::loadData(uint8 *data) { + _drv->toggleRegProtection(_flags & CHS_PROTECT ? true : false); + TownsPC98_MusicChannel::loadData(data); + setOutputLevel(0); + _algorithm = 0x80; +} + +void TownsPC98_MusicChannelSSG::setOutputLevel(uint8 lvl) { + _ssgStartLvl = lvl; + uint16 newTl = (((uint16)_totalLevel + 1) * (uint16)lvl) >> 8; + if (newTl == _ssgTl) + return; + _ssgTl = newTl; + _drv->writeReg(_part, 8 + _regOffset, _ssgTl); +} + +void TownsPC98_MusicChannelSSG::reset() { + TownsPC98_MusicChannel::reset(); + + // Unlike the original we restore the default patch data. This fixes a bug + // where certain sound effects would bring each other out of tune (e.g. the + // dragon's fire in Darm's house in Kyra 1 would sound different each time + // you triggered another sfx by dropping an item etc.) + uint8 i = (10 + _regOffset) << 4; + const uint8 *src = &_drv->_drvTables[156]; + _drv->_ssgPatches[i] = src[i]; + _drv->_ssgPatches[i + 3] = src[i + 3]; + _drv->_ssgPatches[i + 4] = src[i + 4]; + _drv->_ssgPatches[i + 6] = src[i + 6]; + _drv->_ssgPatches[i + 8] = src[i + 8]; + _drv->_ssgPatches[i + 12] = src[i + 12]; +} + +void TownsPC98_MusicChannelSSG::fadeStep() { + _totalLevel--; + if ((int8)_totalLevel < 0) + _totalLevel = 0; + setOutputLevel(_ssgStartLvl); +} + +bool TownsPC98_MusicChannelSSG::control_f0_setPatch(uint8 para) { + _instr = para << 4; + para = (para >> 3) & 0x1e; + if (para) + return control_f4_setAlgorithm(para | 0x40); + return true; +} + +bool TownsPC98_MusicChannelSSG::control_f1_setTotalLevel(uint8 para) { + if (!_drv->_fading) + _totalLevel = para; + return true; +} + +bool TownsPC98_MusicChannelSSG::control_f4_setAlgorithm(uint8 para) { + _algorithm = para; + return true; +} + +bool TownsPC98_MusicChannelSSG::control_f9_loadCustomPatch(uint8 para) { + _instr = (_drv->_sfxOffs + 10 + _regOffset) << 4; + _drv->_ssgPatches[_instr] = *_dataPtr++; + _drv->_ssgPatches[_instr + 3] = para; + _drv->_ssgPatches[_instr + 4] = *_dataPtr++; + _drv->_ssgPatches[_instr + 6] = *_dataPtr++; + _drv->_ssgPatches[_instr + 8] = *_dataPtr++; + _drv->_ssgPatches[_instr + 12] = *_dataPtr++; + return true; +} + +bool TownsPC98_MusicChannelSSG::control_fb_incOutLevel(uint8 para) { + _dataPtr--; + if (_drv->_fading) + return true; + + _totalLevel--; + if ((int8)_totalLevel < 0) + _totalLevel = 0; + + return true; +} + +bool TownsPC98_MusicChannelSSG::control_fc_decOutLevel(uint8 para) { + _dataPtr--; + if (_drv->_fading) + return true; + + if (_totalLevel + 1 < 0x10) + _totalLevel++; + + return true; +} + +bool TownsPC98_MusicChannelSSG::control_ff_endOfTrack(uint8 para) { + if (!_drv->_sfxOffs) { + uint16 val = READ_LE_UINT16(--_dataPtr); + if (val) { + // loop + _dataPtr = _drv->_trackPtr + val; + return true; + } else { + // stop parsing + if (!_drv->_fading) + setOutputLevel(0); + --_dataPtr; + _flags |= CHS_EOT; + _drv->_finishedSSGFlag |= _idFlag; + } + } else { + // end of sfx track - restore ssg music channel + _flags |= CHS_EOT; + _drv->_finishedSfxFlag |= _idFlag; + _drv->_ssgChannels[_chanNum]->restore(); + } + + return false; +} + +void TownsPC98_SfxChannel::loadData(uint8 *data) { + _flags = CHS_ALLOFF; + _ticksLeft = 1; + _dataPtr = data; + _ssgTl = 0xff; + _algorithm = 0x80; + + uint8 *tmp = _dataPtr; + for (bool loop = true; loop;) { + uint8 cmd = *tmp++; + if (cmd < 0xf0) { + tmp++; + } else if (cmd == 0xff) { + loop = false; + } else if (cmd == 0xf6) { + // reset repeat section countdown + tmp[0] = tmp[1]; + tmp += 4; + } else { + tmp += _drv->_opnFxCmdLen[cmd - 240]; + } + } +} + +void TownsPC98_SfxChannel::reset() { + TownsPC98_MusicChannel::reset(); + + // Unlike the original we restore the default patch data. This fixes a bug + // where certain sound effects would bring each other out of tune (e.g. the + // dragon's fire in Darm's house in Kyra 1 would sound different each time + // you triggered another sfx by dropping an item etc.) + uint8 i = (13 + _regOffset) << 4; + const uint8 *src = &_drv->_drvTables[156]; + _drv->_ssgPatches[i] = src[i]; + _drv->_ssgPatches[i + 3] = src[i + 3]; + _drv->_ssgPatches[i + 4] = src[i + 4]; + _drv->_ssgPatches[i + 6] = src[i + 6]; + _drv->_ssgPatches[i + 8] = src[i + 8]; + _drv->_ssgPatches[i + 12] = src[i + 12]; +} + +TownsPC98_MusicChannelPCM::TownsPC98_MusicChannelPCM(TownsPC98_AudioDriver *driver, uint8 regOffs, + uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) : + TownsPC98_MusicChannel(driver, regOffs, flgs, num, key, prt, id), controlEvents(0) { +} + +void TownsPC98_MusicChannelPCM::init() { + _algorithm = 0x80; + +#define Control(x) &TownsPC98_MusicChannelPCM::control_##x + static const ControlEventFunc ctrlEventsPCM[] = { + Control(dummy), + Control(f1_prcStart), + Control(dummy), + Control(dummy), + Control(dummy), + Control(dummy), + Control(f6_repeatSection), + Control(dummy), + Control(dummy), + Control(dummy), + Control(fa_writeReg), + Control(dummy), + Control(dummy), + Control(dummy), + Control(dummy), + Control(ff_endOfTrack) + }; +#undef Control + + controlEvents = ctrlEventsPCM; +} + +void TownsPC98_MusicChannelPCM::loadData(uint8 *data) { + _flags = (_flags & ~CHS_EOT) | CHS_ALLOFF; + _ticksLeft = 1; + _dataPtr = data; + _totalLevel = 0x7F; +} + +void TownsPC98_MusicChannelPCM::processEvents() { + if (_flags & CHS_EOT) + return; + + if (--_ticksLeft) + return; + + uint8 cmd = 0; + bool loop = true; + + while (loop) { + cmd = *_dataPtr++; + if (cmd == 0x80) { + loop = false; + } else if (cmd < 0xf0) { + _drv->writeReg(_part, 0x10, cmd); + } else if (!processControlEvent(cmd)) { + return; + } + } + + _ticksLeft = *_dataPtr++; +} + +bool TownsPC98_MusicChannelPCM::processControlEvent(uint8 cmd) { + uint8 para = *_dataPtr++; + return (this->*controlEvents[cmd & 0x0f])(para); +} + +bool TownsPC98_MusicChannelPCM::control_f1_prcStart(uint8 para) { + _totalLevel = para; + _drv->writeReg(_part, 0x11, para); + return true; +} + +bool TownsPC98_MusicChannelPCM::control_ff_endOfTrack(uint8 para) { + uint16 val = READ_LE_UINT16(--_dataPtr); + if (val) { + // loop + _dataPtr = _drv->_trackPtr + val; + return true; + } else { + // quit parsing for active channel + --_dataPtr; + _flags |= CHS_EOT; + _drv->_finishedRhythmFlag |= _idFlag; + return false; + } +} + +TownsPC98_AudioDriver::TownsPC98_AudioDriver(Audio::Mixer *mixer, EmuType type) : TownsPC98_FmSynth(mixer, type), + _channels(0), _ssgChannels(0), _sfxChannels(0), _rhythmChannel(0), + _trackPtr(0), _sfxData(0), _sfxOffs(0), _ssgPatches(0), + _patches(0), _sfxBuffer(0), _musicBuffer(0), + + _opnCarrier(_drvTables + 76), _opnFreqTable(_drvTables + 108), _opnFreqTableSSG(_drvTables + 132), + _opnFxCmdLen(_drvTables + 36), _opnLvlPresets(_drvTables + (type == kTypeTowns ? 52 : 84)), + + _updateChannelsFlag(type == kType26 ? 0x07 : 0x3F), _finishedChannelsFlag(0), + _updateSSGFlag(type == kTypeTowns ? 0x00 : 0x07), _finishedSSGFlag(0), + _updateRhythmFlag(type == kType86 ? 0x01 : 0x00), _finishedRhythmFlag(0), + _updateSfxFlag(0), _finishedSfxFlag(0), + + _musicTickCounter(0), + + _musicVolume(255), _sfxVolume(255), + + _musicPlaying(false), _sfxPlaying(false), _fading(false), _looping(0), _ready(false) { + + _sfxOffsets[0] = _sfxOffsets[1] = 0; +} + +TownsPC98_AudioDriver::~TownsPC98_AudioDriver() { + reset(); + + if (_channels) { + for (int i = 0; i < _numChan; i++) + delete _channels[i]; + delete[] _channels; + } + + if (_ssgChannels) { + for (int i = 0; i < _numSSG; i++) + delete _ssgChannels[i]; + delete[] _ssgChannels; + } + + if (_sfxChannels) { + for (int i = 0; i < 2; i++) + delete _sfxChannels[i]; + delete[] _sfxChannels; + } + + delete _rhythmChannel; + + delete[] _ssgPatches; +} + +bool TownsPC98_AudioDriver::init() { + if (_ready) { + reset(); + return true; + } + + TownsPC98_FmSynth::init(); + + setVolumeChannelMasks(-1, 0); + + _channels = new TownsPC98_MusicChannel *[_numChan]; + for (int i = 0; i < _numChan; i++) { + int ii = i * 6; + _channels[i] = new TownsPC98_MusicChannel(this, _drvTables[ii], _drvTables[ii + 1], + _drvTables[ii + 2], _drvTables[ii + 3], _drvTables[ii + 4], _drvTables[ii + 5]); + _channels[i]->init(); + } + + if (_numSSG) { + _ssgPatches = new uint8[256]; + memcpy(_ssgPatches, _drvTables + 156, 256); + + _ssgChannels = new TownsPC98_MusicChannelSSG *[_numSSG]; + for (int i = 0; i < _numSSG; i++) { + int ii = i * 6; + _ssgChannels[i] = new TownsPC98_MusicChannelSSG(this, _drvTables[ii], _drvTables[ii + 1], + _drvTables[ii + 2], _drvTables[ii + 3], _drvTables[ii + 4], _drvTables[ii + 5]); + _ssgChannels[i]->init(); + } + + _sfxChannels = new TownsPC98_SfxChannel *[2]; + for (int i = 0; i < 2; i++) { + int ii = (i + 1) * 6; + _sfxChannels[i] = new TownsPC98_SfxChannel(this, _drvTables[ii], _drvTables[ii + 1], + _drvTables[ii + 2], _drvTables[ii + 3], _drvTables[ii + 4], _drvTables[ii + 5]); + _sfxChannels[i]->init(); + } + } + + if (_hasPercussion) { + _rhythmChannel = new TownsPC98_MusicChannelPCM(this, 0, 0, 0, 0, 0, 1); + _rhythmChannel->init(); + } + + setMusicTempo(84); + setSfxTempo(654); + + _ready = true; + + return true; +} + +void TownsPC98_AudioDriver::loadMusicData(uint8 *data, bool loadPaused) { + if (!_ready) { + warning("TownsPC98_AudioDriver: Driver must be initialized before loading data"); + return; + } + + if (!data) { + warning("TownsPC98_AudioDriver: Invalid music file data"); + return; + } + + reset(); + + Common::StackLock lock(_mutex); + uint8 *src_a = _trackPtr = _musicBuffer = data; + + for (uint8 i = 0; i < 3; i++) { + _channels[i]->loadData(data + READ_LE_UINT16(src_a)); + src_a += 2; + } + + for (int i = 0; i < _numSSG; i++) { + _ssgChannels[i]->loadData(data + READ_LE_UINT16(src_a)); + src_a += 2; + } + + for (uint8 i = 3; i < _numChan; i++) { + _channels[i]->loadData(data + READ_LE_UINT16(src_a)); + src_a += 2; + } + + if (_hasPercussion) { + _rhythmChannel->loadData(data + READ_LE_UINT16(src_a)); + src_a += 2; + } + + toggleRegProtection(false); + + _patches = src_a + 4; + _finishedChannelsFlag = _finishedSSGFlag = _finishedRhythmFlag = 0; + + _musicPlaying = (loadPaused ? false : true); +} + +void TownsPC98_AudioDriver::loadSoundEffectData(uint8 *data, uint8 trackNum) { + if (!_ready) { + warning("TownsPC98_AudioDriver: Driver must be initialized before loading data"); + return; + } + + if (!_sfxChannels) { + warning("TownsPC98_AudioDriver: Sound effects not supported by this configuration"); + return; + } + + if (!data) { + warning("TownsPC98_AudioDriver: Invalid sound effects file data"); + return; + } + + Common::StackLock lock(_mutex); + _sfxData = _sfxBuffer = data; + _sfxOffsets[0] = READ_LE_UINT16(&_sfxData[(trackNum << 2)]); + _sfxOffsets[1] = READ_LE_UINT16(&_sfxData[(trackNum << 2) + 2]); + _sfxPlaying = true; + _finishedSfxFlag = 0; +} + +void TownsPC98_AudioDriver::reset() { + Common::StackLock lock(_mutex); + + _musicPlaying = false; + _sfxPlaying = false; + _fading = false; + _looping = 0; + _musicTickCounter = 0; + _sfxData = 0; + + TownsPC98_FmSynth::reset(); + + for (int i = 0; i < _numChan; i++) + _channels[i]->reset(); + for (int i = 0; i < _numSSG; i++) + _ssgChannels[i]->reset(); + + if (_numSSG) { + for (int i = 0; i < 2; i++) + _sfxChannels[i]->reset(); + + memcpy(_ssgPatches, _drvTables + 156, 256); + } + + if (_rhythmChannel) + _rhythmChannel->reset(); +} + +void TownsPC98_AudioDriver::fadeStep() { + if (!_musicPlaying) + return; + + Common::StackLock lock(_mutex); + for (int j = 0; j < _numChan; j++) { + if (_updateChannelsFlag & _channels[j]->_idFlag) + _channels[j]->fadeStep(); + } + + for (int j = 0; j < _numSSG; j++) { + if (_updateSSGFlag & _ssgChannels[j]->_idFlag) + _ssgChannels[j]->fadeStep(); + } + + if (!_fading) { + _fading = 19; + if (_hasPercussion) { + if (_updateRhythmFlag & _rhythmChannel->_idFlag) + _rhythmChannel->reset(); + } + } else { + if (!--_fading) + reset(); + } +} + +void TownsPC98_AudioDriver::timerCallbackB() { + _sfxOffs = 0; + + if (_musicPlaying) { + _musicTickCounter++; + + for (int i = 0; i < _numChan; i++) { + if (_updateChannelsFlag & _channels[i]->_idFlag) { + _channels[i]->processEvents(); + _channels[i]->processFrequency(); + } + } + + for (int i = 0; i < _numSSG; i++) { + if (_updateSSGFlag & _ssgChannels[i]->_idFlag) { + _ssgChannels[i]->processEvents(); + _ssgChannels[i]->processFrequency(); + } + } + + if (_hasPercussion) + if (_updateRhythmFlag & _rhythmChannel->_idFlag) + _rhythmChannel->processEvents(); + } + + toggleRegProtection(false); + + if (_finishedChannelsFlag == _updateChannelsFlag && _finishedSSGFlag == _updateSSGFlag && _finishedRhythmFlag == _updateRhythmFlag) + _musicPlaying = false; +} + +void TownsPC98_AudioDriver::timerCallbackA() { + if (_sfxChannels && _sfxPlaying) { + if (_sfxData) + startSoundEffect(); + + _sfxOffs = 3; + _trackPtr = _sfxBuffer; + + for (int i = 0; i < 2; i++) { + if (_updateSfxFlag & _sfxChannels[i]->_idFlag) { + _sfxChannels[i]->processEvents(); + _sfxChannels[i]->processFrequency(); + } + } + + _trackPtr = _musicBuffer; + } + + if (_updateSfxFlag && _finishedSfxFlag == _updateSfxFlag) { + _sfxPlaying = false; + _updateSfxFlag = 0; + setVolumeChannelMasks(-1, 0); + } +} + +void TownsPC98_AudioDriver::setMusicTempo(uint8 tempo) { + writeReg(0, 0x26, tempo); + writeReg(0, 0x27, 0x33); +} + +void TownsPC98_AudioDriver::setSfxTempo(uint16 tempo) { + writeReg(0, 0x24, tempo & 0xff); + writeReg(0, 0x25, tempo >> 8); + writeReg(0, 0x27, 0x33); +} + +void TownsPC98_AudioDriver::startSoundEffect() { + int volFlags = 0; + + for (int i = 0; i < 2; i++) { + if (_sfxOffsets[i]) { + _ssgChannels[i + 1]->protect(); + _sfxChannels[i]->reset(); + _sfxChannels[i]->loadData(_sfxData + _sfxOffsets[i]); + _updateSfxFlag |= _sfxChannels[i]->_idFlag; + volFlags |= (_sfxChannels[i]->_idFlag << _numChan); + } else { + _ssgChannels[i + 1]->restore(); + _updateSfxFlag &= ~_sfxChannels[i]->_idFlag; + } + } + + setVolumeChannelMasks(~volFlags, volFlags); + _sfxData = 0; +} + +const uint8 TownsPC98_AudioDriver::_drvTables[] = { + // channel presets + 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x80, 0x01, 0x01, 0x00, 0x02, + 0x02, 0x80, 0x02, 0x02, 0x00, 0x04, + 0x00, 0x80, 0x03, 0x04, 0x01, 0x08, + 0x01, 0x80, 0x04, 0x05, 0x01, 0x10, + 0x02, 0x80, 0x05, 0x06, 0x01, 0x20, + + // control event size + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x05, + 0x02, 0x06, 0x02, 0x00, 0x00, 0x02, 0x00, 0x02, + + // fmt level presets + 0x54, 0x50, 0x4C, 0x48, 0x44, 0x40, 0x3C, 0x38, + 0x34, 0x30, 0x2C, 0x28, 0x24, 0x20, 0x1C, 0x18, + 0x14, 0x10, 0x0C, 0x08, 0x04, 0x90, 0x90, 0x90, + + // carriers + 0x08, 0x08, 0x08, 0x08, 0x0C, 0x0E, 0x0E, 0x0F, + + // pc98 level presets + 0x40, 0x3B, 0x38, 0x34, 0x30, 0x2A, 0x28, 0x25, + 0x22, 0x20, 0x1D, 0x1A, 0x18, 0x15, 0x12, 0x10, + 0x0D, 0x0A, 0x08, 0x05, 0x02, 0x90, 0x90, 0x90, + + // frequencies + 0x6A, 0x02, 0x8F, 0x02, 0xB6, 0x02, 0xDF, 0x02, + 0x0B, 0x03, 0x39, 0x03, 0x6A, 0x03, 0x9E, 0x03, + 0xD5, 0x03, 0x10, 0x04, 0x4E, 0x04, 0x8F, 0x04, + + // ssg frequencies + 0xE8, 0x0E, 0x12, 0x0E, 0x48, 0x0D, 0x89, 0x0C, + 0xD5, 0x0B, 0x2B, 0x0B, 0x8A, 0x0A, 0xF3, 0x09, + 0x64, 0x09, 0xDD, 0x08, 0x5E, 0x08, 0xE6, 0x07, + + // ssg patch data + 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00, + 0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0x37, 0x81, 0xC8, 0x00, + 0x00, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0x37, 0x81, 0xC8, 0x00, + 0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00, + 0x00, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00, + 0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00, + 0x04, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00, + 0x0A, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0x01, 0x00, + 0xFF, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00, + 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0x00, + 0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, + 0x64, 0x01, 0xFF, 0x64, 0xFF, 0x81, 0xFF, 0x00, + 0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, + + 0x02, 0x01, 0xFF, 0x28, 0xFF, 0x81, 0xF0, 0x00, + 0x00, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xC8, 0x00, + 0x01, 0x81, 0x00, 0x00, 0x28, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0x78, 0x5F, 0x81, 0xA0, 0x00, + 0x05, 0x81, 0x00, 0x00, 0x28, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00, + 0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00, + 0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00, + 0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00 +}; + +#undef EUPHONY_FADEOUT_TICKS + diff --git a/sound/softsynth/fmtowns_pc98/towns_pc98_driver.h b/sound/softsynth/fmtowns_pc98/towns_pc98_driver.h new file mode 100644 index 0000000000..18daee1e72 --- /dev/null +++ b/sound/softsynth/fmtowns_pc98/towns_pc98_driver.h @@ -0,0 +1,129 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef TOWNS_PC98_AUDIODRIVER_H +#define TOWNS_PC98_AUDIODRIVER_H + +#include "sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h" + +class TownsPC98_MusicChannel; +class TownsPC98_MusicChannelSSG; +class TownsPC98_SfxChannel; +class TownsPC98_MusicChannelPCM; + +class TownsPC98_AudioDriver : public TownsPC98_FmSynth { +friend class TownsPC98_MusicChannel; +friend class TownsPC98_MusicChannelSSG; +friend class TownsPC98_SfxChannel; +friend class TownsPC98_MusicChannelPCM; +public: + TownsPC98_AudioDriver(Audio::Mixer *mixer, EmuType type); + ~TownsPC98_AudioDriver(); + + void loadMusicData(uint8 *data, bool loadPaused = false); + void loadSoundEffectData(uint8 *data, uint8 trackNum); + bool init(); + void reset(); + + void fadeStep(); + + void pause() { + _musicPlaying = false; + } + void cont() { + _musicPlaying = true; + } + + void timerCallbackB(); + void timerCallbackA(); + + bool looping() { + return _looping == _updateChannelsFlag ? true : false; + } + bool musicPlaying() { + return _musicPlaying; + } + + void setMusicVolume(int volume) { + _musicVolume = volume; + setVolumeIntern(_musicVolume, _sfxVolume); + } + void setSoundEffectVolume(int volume) { + _sfxVolume = volume; + setVolumeIntern(_musicVolume, _sfxVolume); + } + +protected: + void startSoundEffect(); + + void setMusicTempo(uint8 tempo); + void setSfxTempo(uint16 tempo); + + TownsPC98_MusicChannel **_channels; + TownsPC98_MusicChannelSSG **_ssgChannels; + TownsPC98_SfxChannel **_sfxChannels; + TownsPC98_MusicChannelPCM *_rhythmChannel; + + const uint8 *_opnCarrier; + const uint8 *_opnFreqTable; + const uint8 *_opnFreqTableSSG; + const uint8 *_opnFxCmdLen; + const uint8 *_opnLvlPresets; + + uint8 *_musicBuffer; + uint8 *_sfxBuffer; + uint8 *_trackPtr; + uint8 *_patches; + uint8 *_ssgPatches; + + uint8 _updateChannelsFlag; + uint8 _updateSSGFlag; + uint8 _updateRhythmFlag; + uint8 _updateSfxFlag; + uint8 _finishedChannelsFlag; + uint8 _finishedSSGFlag; + uint8 _finishedRhythmFlag; + uint8 _finishedSfxFlag; + + bool _musicPlaying; + bool _sfxPlaying; + uint8 _fading; + uint8 _looping; + uint32 _musicTickCounter; + + int _sfxOffs; + uint8 *_sfxData; + uint16 _sfxOffsets[2]; + + uint16 _musicVolume; + uint16 _sfxVolume; + + static const uint8 _drvTables[]; + + bool _ready; +}; + +#endif + diff --git a/sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp b/sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp new file mode 100644 index 0000000000..241b9bde50 --- /dev/null +++ b/sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp @@ -0,0 +1,1513 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#include "sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h" +#include "common/endian.h" + +class TownsPC98_FmSynthOperator { +public: + TownsPC98_FmSynthOperator(const uint32 timerbase, const uint32 rtt, const uint8 *rateTable, + const uint8 *shiftTable, const uint8 *attackDecayTable, const uint32 *frqTable, + const uint32 *sineTable, const int32 *tlevelOut, const int32 *detuneTable); + ~TownsPC98_FmSynthOperator() {} + + void keyOn(); + void keyOff(); + void frequency(int freq); + void updatePhaseIncrement(); + void recalculateRates(); + void generateOutput(int32 phasebuf, int32 *feedbuf, int32 &out); + + void feedbackLevel(int32 level) { + _feedbackLevel = level ? level + 6 : 0; + } + void detune(int value) { + _detn = &_detnTbl[value << 5]; + } + void multiple(uint32 value) { + _multiple = value ? (value << 1) : 1; + } + void attackRate(uint32 value) { + _specifiedAttackRate = value; + } + bool scaleRate(uint8 value); + void decayRate(uint32 value) { + _specifiedDecayRate = value; + recalculateRates(); + } + void sustainRate(uint32 value) { + _specifiedSustainRate = value; + recalculateRates(); + } + void sustainLevel(uint32 value) { + _sustainLevel = (value == 0x0f) ? 0x3e0 : value << 5; + } + void releaseRate(uint32 value) { + _specifiedReleaseRate = value; + recalculateRates(); + } + void totalLevel(uint32 value) { + _totalLevel = value << 3; + } + void ampModulation(bool enable) { + _ampMod = enable; + } + void reset(); + +protected: + EnvelopeState _state; + bool _playing; + uint32 _feedbackLevel; + uint32 _multiple; + uint32 _totalLevel; + uint8 _keyScale1; + uint8 _keyScale2; + uint32 _specifiedAttackRate; + uint32 _specifiedDecayRate; + uint32 _specifiedSustainRate; + uint32 _specifiedReleaseRate; + uint32 _tickCount; + uint32 _sustainLevel; + + bool _ampMod; + uint32 _frequency; + uint8 _kcode; + uint32 _phase; + uint32 _phaseIncrement; + const int32 *_detn; + + const uint8 *_rateTbl; + const uint8 *_rshiftTbl; + const uint8 *_adTbl; + const uint32 *_fTbl; + const uint32 *_sinTbl; + const int32 *_tLvlTbl; + const int32 *_detnTbl; + + const uint32 _tickLength; + uint32 _timer; + const uint32 _rtt; + int32 _currentLevel; + + struct EvpState { + uint8 rate; + uint8 shift; + } fs_a, fs_d, fs_s, fs_r; +}; + +TownsPC98_FmSynthOperator::TownsPC98_FmSynthOperator(const uint32 timerbase, const uint32 rtt, + const uint8 *rateTable, const uint8 *shiftTable, const uint8 *attackDecayTable, + const uint32 *frqTable, const uint32 *sineTable, const int32 *tlevelOut, const int32 *detuneTable) : + _rtt(rtt), _rateTbl(rateTable), _rshiftTbl(shiftTable), _adTbl(attackDecayTable), _fTbl(frqTable), + _sinTbl(sineTable), _tLvlTbl(tlevelOut), _detnTbl(detuneTable), _tickLength(timerbase * 2), + _specifiedAttackRate(0), _specifiedDecayRate(0), _specifiedReleaseRate(0), _specifiedSustainRate(0), + _phase(0), _state(kEnvReady), _playing(false), _timer(0), _keyScale1(0), + _keyScale2(0), _currentLevel(1023), _ampMod(false), _tickCount(0) { + + fs_a.rate = fs_a.shift = fs_d.rate = fs_d.shift = fs_s.rate = fs_s.shift = fs_r.rate = fs_r.shift = 0; + + reset(); +} + +void TownsPC98_FmSynthOperator::keyOn() { + if (_playing) + return; + + _playing = true; + _state = kEnvAttacking; + _phase = 0; +} + +void TownsPC98_FmSynthOperator::keyOff() { + if (!_playing) + return; + + _playing = false; + if (_state != kEnvReady) + _state = kEnvReleasing; +} + +void TownsPC98_FmSynthOperator::frequency(int freq) { + uint8 block = (freq >> 11); + uint16 pos = (freq & 0x7ff); + uint8 c = pos >> 7; + + _kcode = (block << 2) | ((c < 7) ? 0 : ((c > 8) ? 3 : c - 6)); + _frequency = _fTbl[pos << 1] >> (7 - block); +} + +void TownsPC98_FmSynthOperator::updatePhaseIncrement() { + _phaseIncrement = ((_frequency + _detn[_kcode]) * _multiple) >> 1; + uint8 keyscale = _kcode >> _keyScale1; + if (_keyScale2 != keyscale) { + _keyScale2 = keyscale; + recalculateRates(); + } +} + +void TownsPC98_FmSynthOperator::recalculateRates() { + int k = _keyScale2; + int r = _specifiedAttackRate ? (_specifiedAttackRate << 1) + 0x20 : 0; + fs_a.rate = ((r + k) < 94) ? _rateTbl[r + k] : 136; + fs_a.shift = ((r + k) < 94) ? _rshiftTbl[r + k] : 0; + + r = _specifiedDecayRate ? (_specifiedDecayRate << 1) + 0x20 : 0; + fs_d.rate = _rateTbl[r + k]; + fs_d.shift = _rshiftTbl[r + k]; + + r = _specifiedSustainRate ? (_specifiedSustainRate << 1) + 0x20 : 0; + fs_s.rate = _rateTbl[r + k]; + fs_s.shift = _rshiftTbl[r + k]; + + r = (_specifiedReleaseRate << 2) + 0x22; + fs_r.rate = _rateTbl[r + k]; + fs_r.shift = _rshiftTbl[r + k]; +} + +void TownsPC98_FmSynthOperator::generateOutput(int32 phasebuf, int32 *feed, int32 &out) { + if (_state == kEnvReady) + return; + + _timer += _tickLength; + while (_timer > _rtt) { + _timer -= _rtt; + ++_tickCount; + + int32 levelIncrement = 0; + uint32 targetTime = 0; + int32 targetLevel = 0; + EnvelopeState nextState = kEnvReady; + + switch (_state) { + case kEnvReady: + return; + case kEnvAttacking: + targetLevel = 0; + nextState = kEnvDecaying; + if ((_specifiedAttackRate << 1) + _keyScale2 < 64) { + targetTime = (1 << fs_a.shift) - 1; + levelIncrement = (~_currentLevel * _adTbl[fs_a.rate + ((_tickCount >> fs_a.shift) & 7)]) >> 4; + break; + } else { + _currentLevel = targetLevel; + _state = nextState; + } + // Fall through + case kEnvDecaying: + targetTime = (1 << fs_d.shift) - 1; + nextState = kEnvSustaining; + targetLevel = _sustainLevel; + levelIncrement = _adTbl[fs_d.rate + ((_tickCount >> fs_d.shift) & 7)]; + break; + case kEnvSustaining: + targetTime = (1 << fs_s.shift) - 1; + nextState = kEnvSustaining; + targetLevel = 1023; + levelIncrement = _adTbl[fs_s.rate + ((_tickCount >> fs_s.shift) & 7)]; + break; + case kEnvReleasing: + targetTime = (1 << fs_r.shift) - 1; + nextState = kEnvReady; + targetLevel = 1023; + levelIncrement = _adTbl[fs_r.rate + ((_tickCount >> fs_r.shift) & 7)]; + break; + } + + if (!(_tickCount & targetTime)) { + _currentLevel += levelIncrement; + if ((_state == kEnvAttacking && _currentLevel <= targetLevel) || (_state != kEnvAttacking && _currentLevel >= targetLevel)) { + if (_state != kEnvDecaying) + _currentLevel = targetLevel; + _state = nextState; + } + } + } + + uint32 lvlout = _totalLevel + (uint32) _currentLevel; + + + int32 outp = 0; + int32 *i = &outp, *o = &outp; + int phaseShift = 0; + + if (feed) { + o = &feed[0]; + i = &feed[1]; + phaseShift = _feedbackLevel ? ((*o + *i) << _feedbackLevel) : 0; + *o = *i; + } else { + phaseShift = phasebuf << 15; + } + + if (lvlout < 832) { + uint32 index = (lvlout << 3) + _sinTbl[(((int32)((_phase & 0xffff0000) + + phaseShift)) >> 16) & 0x3ff]; + *i = ((index < 6656) ? _tLvlTbl[index] : 0); + } else { + *i = 0; + } + + _phase += _phaseIncrement; + out += *o; +} + +void TownsPC98_FmSynthOperator::reset() { + keyOff(); + _timer = 0; + _keyScale2 = 0; + _currentLevel = 1023; + + frequency(0); + detune(0); + scaleRate(0); + multiple(0); + updatePhaseIncrement(); + attackRate(0); + decayRate(0); + releaseRate(0); + sustainRate(0); + feedbackLevel(0); + totalLevel(127); + ampModulation(false); +} + +bool TownsPC98_FmSynthOperator::scaleRate(uint8 value) { + value = 3 - value; + if (_keyScale1 != value) { + _keyScale1 = value; + return true; + } + + int k = _keyScale2; + int r = _specifiedAttackRate ? (_specifiedAttackRate << 1) + 0x20 : 0; + fs_a.rate = ((r + k) < 94) ? _rateTbl[r + k] : 136; + fs_a.shift = ((r + k) < 94) ? _rshiftTbl[r + k] : 0; + return false; +} + +class TownsPC98_FmSynthSquareSineSource { +public: + TownsPC98_FmSynthSquareSineSource(const uint32 timerbase, const uint32 rtt); + ~TownsPC98_FmSynthSquareSineSource(); + + void init(const int *rsTable, const int *rseTable); + void reset(); + void writeReg(uint8 address, uint8 value, bool force = false); + + void nextTick(int32 *buffer, uint32 bufferSize); + + void setVolumeIntern(int volA, int volB) { + _volumeA = volA; + _volumeB = volB; + } + void setVolumeChannelMasks(int channelMaskA, int channelMaskB) { + _volMaskA = channelMaskA; + _volMaskB = channelMaskB; + } + + uint8 chanEnable() const { + return _chanEnable; + } +private: + void updateRegs(); + + uint8 _updateRequestBuf[64]; + int _updateRequest; + int _rand; + + int8 _evpTimer; + uint32 _pReslt; + uint8 _attack; + + bool _evpUpdate, _cont; + + int _evpUpdateCnt; + uint8 _outN; + int _nTick; + + int32 *_tlTable; + int32 *_tleTable; + + const uint32 _tickLength; + uint32 _timer; + const uint32 _rtt; + + struct Channel { + int tick; + uint8 smp; + uint8 out; + + uint8 frqL; + uint8 frqH; + uint8 vol; + } _channels[3]; + + uint8 _noiseGenerator; + uint8 _chanEnable; + + uint8 **_reg; + + uint16 _volumeA; + uint16 _volumeB; + int _volMaskA; + int _volMaskB; + + bool _ready; +}; + +class TownsPC98_FmSynthPercussionSource { +public: + TownsPC98_FmSynthPercussionSource(const uint32 timerbase, const uint32 rtt); + ~TownsPC98_FmSynthPercussionSource() { + delete[] _reg; + } + + void init(const uint8 *instrData = 0); + void reset(); + void writeReg(uint8 address, uint8 value); + + void nextTick(int32 *buffer, uint32 bufferSize); + + void setVolumeIntern(int volA, int volB) { + _volumeA = volA; + _volumeB = volB; + } + void setVolumeChannelMasks(int channelMaskA, int channelMaskB) { + _volMaskA = channelMaskA; + _volMaskB = channelMaskB; + } + +private: + struct RhtChannel { + const uint8 *data; + + const uint8 *start; + const uint8 *end; + const uint8 *pos; + uint32 size; + bool active; + uint8 level; + + int8 decState; + uint8 decStep; + + int16 samples[2]; + int out; + + uint8 startPosH; + uint8 startPosL; + uint8 endPosH; + uint8 endPosL; + }; + + void recalcOuput(RhtChannel *ins); + void advanceInput(RhtChannel *ins); + + RhtChannel _rhChan[6]; + + uint8 _totalLevel; + + const uint32 _tickLength; + uint32 _timer; + const uint32 _rtt; + + uint8 **_reg; + + uint16 _volumeA; + uint16 _volumeB; + int _volMaskA; + int _volMaskB; + + bool _ready; +}; + +TownsPC98_FmSynthSquareSineSource::TownsPC98_FmSynthSquareSineSource(const uint32 timerbase, const uint32 rtt) : _tlTable(0), + _rtt(rtt), _tleTable(0), _updateRequest(-1), _tickLength(timerbase * 27), _ready(0), _reg(0), _rand(1), _outN(1), + _nTick(0), _evpUpdateCnt(0), _evpTimer(0x1f), _pReslt(0x1f), _attack(0), _cont(false), _evpUpdate(true), + _timer(0), _noiseGenerator(0), _chanEnable(0), + _volMaskA(0), _volMaskB(0), _volumeA(Audio::Mixer::kMaxMixerVolume), _volumeB(Audio::Mixer::kMaxMixerVolume) { + + memset(_channels, 0, sizeof(_channels)); + memset(_updateRequestBuf, 0, sizeof(_updateRequestBuf)); + _reg = new uint8 *[11]; + + _reg[0] = &_channels[0].frqL; + _reg[1] = &_channels[0].frqH; + _reg[2] = &_channels[1].frqL; + _reg[3] = &_channels[1].frqH; + _reg[4] = &_channels[2].frqL; + _reg[5] = &_channels[2].frqH; + _reg[6] = &_noiseGenerator; + _reg[7] = &_chanEnable; + _reg[8] = &_channels[0].vol; + _reg[9] = &_channels[1].vol; + _reg[10] = &_channels[2].vol; + + reset(); +} + +TownsPC98_FmSynthSquareSineSource::~TownsPC98_FmSynthSquareSineSource() { + delete[] _tlTable; + delete[] _tleTable; + delete[] _reg; +} + +void TownsPC98_FmSynthSquareSineSource::init(const int *rsTable, const int *rseTable) { + if (_ready) { + reset(); + return; + } + + delete[] _tlTable; + delete[] _tleTable; + _tlTable = new int32[16]; + _tleTable = new int32[32]; + float a, b, d; + d = 801.0f; + + for (int i = 0; i < 16; i++) { + b = 1.0f / rsTable[i]; + a = 1.0f / d + b + 1.0f / 1000.0f; + float v = (b / a) * 32767.0f; + _tlTable[i] = (int32) v; + + b = 1.0f / rseTable[i]; + a = 1.0f / d + b + 1.0f / 1000.0f; + v = (b / a) * 32767.0f; + _tleTable[i] = (int32) v; + } + + for (int i = 16; i < 32; i++) { + b = 1.0f / rseTable[i]; + a = 1.0f / d + b + 1.0f / 1000.0f; + float v = (b / a) * 32767.0f; + _tleTable[i] = (int32) v; + } + + _ready = true; +} + +void TownsPC98_FmSynthSquareSineSource::reset() { + _rand = 1; + _outN = 1; + _updateRequest = -1; + _nTick = _evpUpdateCnt = 0; + _evpTimer = 0x1f; + _pReslt = 0x1f; + _attack = 0; + _cont = false; + _evpUpdate = true; + _timer = 0; + + for (int i = 0; i < 3; i++) { + _channels[i].tick = 0; + _channels[i].smp = _channels[i].out = 0; + } + + for (int i = 0; i < 14; i++) + writeReg(i, 0, true); + + writeReg(7, 0xbf, true); +} + +void TownsPC98_FmSynthSquareSineSource::writeReg(uint8 address, uint8 value, bool force) { + if (!_ready) + return; + + if (address > 10 || *_reg[address] == value) { + if ((address == 11 || address == 12 || address == 13) && value) + warning("TownsPC98_FmSynthSquareSineSource: unsupported reg address: %d", address); + return; + } + + if (!force) { + if (_updateRequest >= 63) { + warning("TownsPC98_FmSynthSquareSineSource: event buffer overflow"); + _updateRequest = -1; + } + _updateRequestBuf[++_updateRequest] = value; + _updateRequestBuf[++_updateRequest] = address; + return; + } + + *_reg[address] = value; +} + +void TownsPC98_FmSynthSquareSineSource::nextTick(int32 *buffer, uint32 bufferSize) { + if (!_ready) + return; + + for (uint32 i = 0; i < bufferSize; i++) { + _timer += _tickLength; + while (_timer > _rtt) { + _timer -= _rtt; + + if (++_nTick >= (_noiseGenerator & 0x1f)) { + if ((_rand + 1) & 2) + _outN ^= 1; + + _rand = (((_rand & 1) ^ ((_rand >> 3) & 1)) << 16) | (_rand >> 1); + _nTick = 0; + } + + for (int ii = 0; ii < 3; ii++) { + if (++_channels[ii].tick >= (((_channels[ii].frqH & 0x0f) << 8) | _channels[ii].frqL)) { + _channels[ii].tick = 0; + _channels[ii].smp ^= 1; + } + _channels[ii].out = (_channels[ii].smp | ((_chanEnable >> ii) & 1)) & (_outN | ((_chanEnable >> (ii + 3)) & 1)); + } + + if (_evpUpdate) { + if (++_evpUpdateCnt >= 0) { + _evpUpdateCnt = 0; + + if (--_evpTimer < 0) { + if (_cont) { + _evpTimer &= 0x1f; + } else { + _evpUpdate = false; + _evpTimer = 0; + } + } + } + } + _pReslt = _evpTimer ^ _attack; + updateRegs(); + } + + int32 finOut = 0; + for (int ii = 0; ii < 3; ii++) { + int32 finOutTemp = ((_channels[ii].vol >> 4) & 1) ? _tleTable[_channels[ii].out ? _pReslt : 0] : _tlTable[_channels[ii].out ? (_channels[ii].vol & 0x0f) : 0]; + + if ((1 << ii) & _volMaskA) + finOutTemp = (finOutTemp * _volumeA) / Audio::Mixer::kMaxMixerVolume; + + if ((1 << ii) & _volMaskB) + finOutTemp = (finOutTemp * _volumeB) / Audio::Mixer::kMaxMixerVolume; + + finOut += finOutTemp; + } + + finOut /= 3; + + buffer[i << 1] += finOut; + buffer[(i << 1) + 1] += finOut; + } +} + +void TownsPC98_FmSynthSquareSineSource::updateRegs() { + for (int i = 0; i < _updateRequest;) { + uint8 b = _updateRequestBuf[i++]; + uint8 a = _updateRequestBuf[i++]; + writeReg(a, b, true); + } + _updateRequest = -1; +} + +TownsPC98_FmSynthPercussionSource::TownsPC98_FmSynthPercussionSource(const uint32 timerbase, const uint32 rtt) : + _rtt(rtt), _tickLength(timerbase * 2), _timer(0), _ready(false), _volMaskA(0), _volMaskB(0), _volumeA(Audio::Mixer::kMaxMixerVolume), _volumeB(Audio::Mixer::kMaxMixerVolume) { + + memset(_rhChan, 0, sizeof(RhtChannel) * 6); + _reg = new uint8 *[40]; + + _reg[0] = _reg[1] = _reg[2] = _reg[3] = _reg[4] = _reg[5] = _reg[6] = _reg[7] = _reg[8] = _reg[9] = _reg[10] = _reg[11] = _reg[12] = _reg[13] = _reg[14] = _reg[15] = 0; + _reg[16] = &_rhChan[0].startPosL; + _reg[17] = &_rhChan[1].startPosL; + _reg[18] = &_rhChan[2].startPosL; + _reg[19] = &_rhChan[3].startPosL; + _reg[20] = &_rhChan[4].startPosL; + _reg[21] = &_rhChan[5].startPosL; + _reg[22] = &_rhChan[0].startPosH; + _reg[23] = &_rhChan[1].startPosH; + _reg[24] = &_rhChan[2].startPosH; + _reg[25] = &_rhChan[3].startPosH; + _reg[26] = &_rhChan[4].startPosH; + _reg[27] = &_rhChan[5].startPosH; + _reg[28] = &_rhChan[0].endPosL; + _reg[29] = &_rhChan[1].endPosL; + _reg[30] = &_rhChan[2].endPosL; + _reg[31] = &_rhChan[3].endPosL; + _reg[32] = &_rhChan[4].endPosL; + _reg[33] = &_rhChan[5].endPosL; + _reg[34] = &_rhChan[0].endPosH; + _reg[35] = &_rhChan[1].endPosH; + _reg[36] = &_rhChan[2].endPosH; + _reg[37] = &_rhChan[3].endPosH; + _reg[38] = &_rhChan[4].endPosH; + _reg[39] = &_rhChan[5].endPosH; +} + +void TownsPC98_FmSynthPercussionSource::init(const uint8 *instrData) { + if (_ready) { + reset(); + return; + } + + const uint8 *start = instrData; + const uint8 *pos = start; + + if (instrData) { + for (int i = 0; i < 6; i++) { + _rhChan[i].data = start + READ_BE_UINT16(pos); + pos += 2; + _rhChan[i].size = READ_BE_UINT16(pos); + pos += 2; + } + reset(); + _ready = true; + } else { + memset(_rhChan, 0, sizeof(RhtChannel) * 6); + _ready = false; + } +} + +void TownsPC98_FmSynthPercussionSource::reset() { + _timer = 0; + _totalLevel = 63; + + for (int i = 0; i < 6; i++) { + RhtChannel *s = &_rhChan[i]; + s->pos = s->start = s->data; + s->end = s->data + s->size; + s->active = false; + s->level = 0; + s->out = 0; + s->decStep = 1; + s->decState = 0; + s->samples[0] = s->samples[1] = 0; + s->startPosH = s->startPosL = s->endPosH = s->endPosL = 0; + } +} + +void TownsPC98_FmSynthPercussionSource::writeReg(uint8 address, uint8 value) { + if (!_ready) + return; + + uint8 h = address >> 4; + uint8 l = address & 15; + + if (address > 15) + *_reg[address] = value; + + if (address == 0) { + if (value & 0x80) { + //key off + for (int i = 0; i < 6; i++) { + if ((value >> i) & 1) + _rhChan[i].active = false; + } + } else { + //key on + for (int i = 0; i < 6; i++) { + if ((value >> i) & 1) { + RhtChannel *s = &_rhChan[i]; + s->pos = s->start; + s->active = true; + s->out = 0; + s->samples[0] = s->samples[1] = 0; + s->decStep = 1; + s->decState = 0; + } + } + } + } else if (address == 1) { + // total level + _totalLevel = (value & 63) ^ 63; + for (int i = 0; i < 6; i++) + recalcOuput(&_rhChan[i]); + } else if (!h && l & 8) { + // instrument level + l &= 7; + _rhChan[l].level = (value & 0x1f) ^ 0x1f; + recalcOuput(&_rhChan[l]); + } else if (h & 3) { + l &= 7; + if (h == 1) { + // set start offset + _rhChan[l].start = _rhChan[l].data + ((_rhChan[l].startPosH << 8 | _rhChan[l].startPosL) << 8); + } else if (h == 2) { + // set end offset + _rhChan[l].end = _rhChan[l].data + ((_rhChan[l].endPosH << 8 | _rhChan[l].endPosL) << 8) + 255; + } + } +} + +void TownsPC98_FmSynthPercussionSource::nextTick(int32 *buffer, uint32 bufferSize) { + if (!_ready) + return; + + for (uint32 i = 0; i < bufferSize; i++) { + _timer += _tickLength; + while (_timer > _rtt) { + _timer -= _rtt; + + for (int ii = 0; ii < 6; ii++) { + RhtChannel *s = &_rhChan[ii]; + if (s->active) { + recalcOuput(s); + if (s->decStep) { + advanceInput(s); + if (s->pos == s->end) + s->active = false; + } + s->decStep ^= 1; + } + } + } + + int32 finOut = 0; + + for (int ii = 0; ii < 6; ii++) { + if (_rhChan[ii].active) + finOut += _rhChan[ii].out; + } + + finOut <<= 1; + + if (1 & _volMaskA) + finOut = (finOut * _volumeA) / Audio::Mixer::kMaxMixerVolume; + + if (1 & _volMaskB) + finOut = (finOut * _volumeB) / Audio::Mixer::kMaxMixerVolume; + + buffer[i << 1] += finOut; + buffer[(i << 1) + 1] += finOut; + } +} + +void TownsPC98_FmSynthPercussionSource::recalcOuput(RhtChannel *ins) { + uint32 s = _totalLevel + ins->level; + uint32 x = s > 62 ? 0 : (1 + (s >> 3)); + int32 y = s > 62 ? 0 : (15 - (s & 7)); + ins->out = ((ins->samples[ins->decStep] * y) >> x) & ~3; +} + +void TownsPC98_FmSynthPercussionSource::advanceInput(RhtChannel *ins) { + static const int8 adjustIndex[] = { -1, -1, -1, -1, 2, 5, 7, 9 }; + + static const int16 stepTable[] = { + 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, + 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, + 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552 + }; + + uint8 cur = (int8)*ins->pos++; + + for (int i = 0; i < 2; i++) { + int b = (2 * (cur & 7) + 1) * stepTable[ins->decState] / 8; + ins->samples[i] = CLIP<int16>(ins->samples[i ^ 1] + (cur & 8 ? b : -b), -2048, 2047); + ins->decState = CLIP<int8>(ins->decState + adjustIndex[cur & 7], 0, 48); + cur >>= 4; + } +} + +TownsPC98_FmSynth::TownsPC98_FmSynth(Audio::Mixer *mixer, EmuType type) : + _mixer(mixer), + _chanInternal(0), _ssg(0), _prc(0), + _numChan(type == kType26 ? 3 : 6), _numSSG(type == kTypeTowns ? 0 : 3), _hasPercussion(type == kType86 ? true : false), + _oprRates(0), _oprRateshift(0), _oprAttackDecay(0), _oprFrq(0), _oprSinTbl(0), _oprLevelOut(0), _oprDetune(0), + _rtt(type == kTypeTowns ? 0x514767 : 0x5B8D80), _baserate(55125.0f / (float)mixer->getOutputRate()), + _volMaskA(0), _volMaskB(0), _volumeA(255), _volumeB(255), + _regProtectionFlag(false), _ready(false) { + + memset(&_timers[0], 0, sizeof(ChipTimer)); + memset(&_timers[1], 0, sizeof(ChipTimer)); + _timers[0].cb = &TownsPC98_FmSynth::timerCallbackA; + _timers[1].cb = &TownsPC98_FmSynth::timerCallbackB; + _timerbase = (uint32)(_baserate * 1000000.0f); +} + +TownsPC98_FmSynth::~TownsPC98_FmSynth() { + Common::StackLock lock(_mutex); + _mixer->stopHandle(_soundHandle); + delete _ssg; + delete _prc; + delete[] _chanInternal; + + delete[] _oprRates; + delete[] _oprRateshift; + delete[] _oprFrq; + delete[] _oprAttackDecay; + delete[] _oprSinTbl; + delete[] _oprLevelOut; + delete[] _oprDetune; +} + +bool TownsPC98_FmSynth::init() { + if (_ready) { + reset(); + return true; + } + + generateTables(); + + _chanInternal = new ChanInternal[_numChan]; + for (int i = 0; i < _numChan; i++) { + memset(&_chanInternal[i], 0, sizeof(ChanInternal)); + for (int j = 0; j < 4; ++j) + _chanInternal[i].opr[j] = new TownsPC98_FmSynthOperator(_timerbase, _rtt, _oprRates, _oprRateshift, _oprAttackDecay, _oprFrq, _oprSinTbl, _oprLevelOut, _oprDetune); + } + + if (_numSSG) { + _ssg = new TownsPC98_FmSynthSquareSineSource(_timerbase, _rtt); + _ssg->init(&_ssgTables[0], &_ssgTables[16]); + } + + if (_hasPercussion) { + _prc = new TownsPC98_FmSynthPercussionSource(_timerbase, _rtt); + _prc->init(_percussionData); + } + + _mixer->playStream(Audio::Mixer::kPlainSoundType, + &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); + + _ready = true; + + return true; +} + +void TownsPC98_FmSynth::reset() { + for (int i = 0; i < _numChan; i++) { + for (int ii = 0; ii < 4; ii++) + _chanInternal[i].opr[ii]->reset(); + memset(_chanInternal[i].feedbuf, 0, 3); + _chanInternal[i].algorithm = 0; + _chanInternal[i].frqTemp = 0; + _chanInternal[i].enableLeft = _chanInternal[i].enableRight = true; + _chanInternal[i].updateEnvelopeParameters = false; + } + + writeReg(0, 0x27, 0x33); + + if (_ssg) + _ssg->reset(); + + if (_prc) + _prc->reset(); +} + +void TownsPC98_FmSynth::writeReg(uint8 part, uint8 regAddress, uint8 value) { + if (_regProtectionFlag || !_ready) + return; + + static const uint8 oprOrdr[] = { 0, 2, 1, 3 }; + + Common::StackLock lock(_mutex); + + uint8 h = regAddress & 0xf0; + uint8 l = (regAddress & 0x0f); + + ChanInternal *c = 0; + TownsPC98_FmSynthOperator **co = 0; + TownsPC98_FmSynthOperator *o = 0; + + if (regAddress > 0x2F) { + c = &_chanInternal[(l & 3) + 3 * part]; + co = c->opr; + o = c->opr[oprOrdr[(l - (l & 3)) >> 2]]; + } else if (regAddress == 0x28) { + c = &_chanInternal[(value & 3) + ((value & 4) ? 3 : 0)]; + co = c->opr; + } + + switch (h) { + case 0x00: + // ssg + if (_ssg) + _ssg->writeReg(l, value); + break; + case 0x10: + // pcm rhythm channel + if (_prc) + _prc->writeReg(l, value); + break; + case 0x20: + if (l == 8) { + // Key on/off + for (int i = 0; i < 4; i++) { + if ((value >> (4 + i)) & 1) + co[oprOrdr[i]]->keyOn(); + else + co[oprOrdr[i]]->keyOff(); + } + } else if (l == 4) { + // Timer A + _timers[0].value = (_timers[0].value & 3) | (value << 2); + } else if (l == 5) { + // Timer A + _timers[0].value = (_timers[0].value & 0x3fc) | (value & 3); + } else if (l == 6) { + // Timer B + _timers[1].value = value & 0xff; + } else if (l == 7) { + if (value & 1) { + float spc = (float)(0x400 - _timers[0].value) / _baserate; + if (spc < 1) { + warning("TownsPC98_FmSynth: Invalid Timer A setting: %d", _timers[0].value); + spc = 1; + } + + _timers[0].smpPerCb = (int32) spc; + _timers[0].smpPerCbRem = (uint32)((spc - (float)_timers[0].smpPerCb) * 1000000.0f); + _timers[0].smpTillCb = _timers[0].smpPerCb; + _timers[0].smpTillCbRem = _timers[0].smpPerCbRem; + _timers[0].enabled = true; + } else { + _timers[0].enabled = false; + } + + if (value & 2) { + float spc = (float)(0x100 - _timers[1].value) * 16.0f / _baserate; + if (spc < 1) { + warning("TownsPC98_FmSynth: Invalid Timer B setting: %d", _timers[1].value); + spc = 1; + } + + _timers[1].smpPerCb = (int32) spc; + _timers[1].smpPerCbRem = (uint32)((spc - (float)_timers[1].smpPerCb) * 1000000.0f); + _timers[1].smpTillCb = _timers[1].smpPerCb; + _timers[1].smpTillCbRem = _timers[1].smpPerCbRem; + _timers[1].enabled = true; + } else { + _timers[1].enabled = false; + } + + if (value & 0x10) { + _timers[0].smpTillCb = _timers[0].smpPerCb; + _timers[0].smpTillCbRem = _timers[0].smpTillCbRem; + } + + if (value & 0x20) { + _timers[1].smpTillCb = _timers[1].smpPerCb; + _timers[1].smpTillCbRem = _timers[1].smpTillCbRem; + } + } else if (l == 2) { + // LFO + if (value & 8) + warning("TownsPC98_FmSynth: TRYING TO USE LFO (NOT SUPPORTED)"); + } else if (l == 10 || l == 11) { + // DAC + if (l == 11 && (value & 0x80)) + warning("TownsPC98_FmSynth: TRYING TO USE DAC (NOT SUPPORTED)"); + } + break; + + case 0x30: + // detune, multiple + o->detune((value >> 4) & 7); + o->multiple(value & 0x0f); + c->updateEnvelopeParameters = true; + break; + + case 0x40: + // total level + o->totalLevel(value & 0x7f); + break; + + case 0x50: + // rate scaling, attack rate + o->attackRate(value & 0x1f); + if (o->scaleRate(value >> 6)) + c->updateEnvelopeParameters = true; + break; + + case 0x60: + // first decay rate, amplitude modulation + o->decayRate(value & 0x1f); + o->ampModulation(value & 0x80 ? true : false); + break; + + case 0x70: + // secondary decay rate + o->sustainRate(value & 0x1f); + break; + + case 0x80: + // secondary amplitude, release rate; + o->sustainLevel(value >> 4); + o->releaseRate(value & 0x0f); + break; + + case 0x90: + warning("TownsPC98_FmSynth: TRYING TO USE SSG ENVELOPE SHAPES (NOT SUPPORTED)"); + break; + + case 0xa0: + // frequency + l &= ~3; + if (l == 0) { + c->frqTemp = (c->frqTemp & 0xff00) | value; + c->updateEnvelopeParameters = true; + for (int i = 0; i < 4; i++) + co[i]->frequency(c->frqTemp); + } else if (l == 4) { + c->frqTemp = (c->frqTemp & 0xff) | (value << 8); + } else if (l == 8) { + // Ch 3/6 special mode frq + warning("TownsPC98_FmSynth: TRYING TO USE CH 3/6 SPECIAL MODE FREQ (NOT SUPPORTED)"); + } else if (l == 12) { + // Ch 3/6 special mode frq + warning("TownsPC98_FmSynth: TRYING TO USE CH 3/6 SPECIAL MODE FREQ (NOT SUPPORTED)"); + } + break; + + case 0xb0: + l &= ~3; + if (l == 0) { + // feedback, _algorithm + co[0]->feedbackLevel((value >> 3) & 7); + c->algorithm = value & 7; + } else if (l == 4) { + // stereo, LFO sensitivity + c->enableLeft = value & 0x80 ? true : false; + c->enableRight = value & 0x40 ? true : false; + c->ampModSensitivity((value & 0x30) >> 4); + c->frqModSensitivity(value & 3); + } + break; + + default: + warning("TownsPC98_FmSynth: UNKNOWN ADDRESS %d", regAddress); + } +} + +int TownsPC98_FmSynth::readBuffer(int16 *buffer, const int numSamples) { + Common::StackLock lock(_mutex); + + memset(buffer, 0, sizeof(int16) * numSamples); + int32 *tmp = new int32[numSamples]; + int32 *tmpStart = tmp; + memset(tmp, 0, sizeof(int32) * numSamples); + int32 samplesLeft = numSamples >> 1; + + while (samplesLeft) { + int32 render = samplesLeft; + + for (int i = 0; i < 2; i++) { + if (_timers[i].enabled && _timers[i].cb) { + if (!_timers[i].smpTillCb) { + (this->*_timers[i].cb)(); + _timers[i].smpTillCb = _timers[i].smpPerCb; + + _timers[i].smpTillCbRem += _timers[i].smpPerCbRem; + if (_timers[i].smpTillCbRem >= _timerbase) { + _timers[i].smpTillCb++; + _timers[i].smpTillCbRem -= _timerbase; + } + } + render = MIN(render, _timers[i].smpTillCb); + } + } + + samplesLeft -= render; + + for (int i = 0; i < 2; i++) { + if (_timers[i].enabled && _timers[i].cb) { + _timers[i].smpTillCb -= render; + } + } + + nextTick(tmp, render); + + if (_ssg) + _ssg->nextTick(tmp, render); + if (_prc) + _prc->nextTick(tmp, render); + + nextTickEx(tmp, render); + + for (int i = 0; i < render; ++i) { + int32 l = CLIP<int32>(tmp[i << 1], -32767, 32767); + buffer[i << 1] = (int16) l; + int32 r = CLIP<int32>(tmp[(i << 1) + 1], -32767, 32767); + buffer[(i << 1) + 1] = (int16) r; + } + + buffer += (render << 1); + tmp += (render << 1); + } + + delete[] tmpStart; + return numSamples; +} + +uint8 TownsPC98_FmSynth::readSSGStatus() { + return _ssg->chanEnable(); +} + +void TownsPC98_FmSynth::setVolumeIntern(int volA, int volB) { + Common::StackLock lock(_mutex); + _volumeA = CLIP<uint16>(volA, 0, Audio::Mixer::kMaxMixerVolume); + _volumeB = CLIP<uint16>(volB, 0, Audio::Mixer::kMaxMixerVolume); + if (_ssg) + _ssg->setVolumeIntern(_volumeA, _volumeB); + if (_prc) + _prc->setVolumeIntern(_volumeA, _volumeB); +} + +void TownsPC98_FmSynth::setVolumeChannelMasks(int channelMaskA, int channelMaskB) { + Common::StackLock lock(_mutex); + _volMaskA = channelMaskA; + _volMaskB = channelMaskB; + if (_ssg) + _ssg->setVolumeChannelMasks(_volMaskA >> _numChan, _volMaskB >> _numChan); + if (_prc) + _prc->setVolumeChannelMasks(_volMaskA >> (_numChan + _numSSG), _volMaskB >> (_numChan + _numSSG)); +} + +void TownsPC98_FmSynth::generateTables() { + delete[] _oprRates; + _oprRates = new uint8[128]; + + WRITE_BE_UINT32(_oprRates + 32, _numChan == 6 ? 0x90900000 : 0x00081018); + WRITE_BE_UINT32(_oprRates + 36, _numChan == 6 ? 0x00001010 : 0x00081018); + memset(_oprRates, 0x90, 32); + memset(_oprRates + 96, 0x80, 32); + uint8 *dst = (uint8 *)_oprRates + 40; + for (int i = 0; i < 40; i += 4) + WRITE_BE_UINT32(dst + i, 0x00081018); + for (int i = 0; i < 48; i += 4) + WRITE_BE_UINT32(dst + i, 0x00081018); + dst += 40; + for (uint8 i = 0; i < 16; i ++) { + uint8 v = (i < 12) ? i : 12; + *dst++ = ((4 + v) << 3); + } + + delete[] _oprRateshift; + _oprRateshift = new uint8[128]; + memset(_oprRateshift, 0, 128); + dst = (uint8 *)_oprRateshift + 32; + for (int i = 11; i; i--) { + memset(dst, i, 4); + dst += 4; + } + + delete[] _oprFrq; + _oprFrq = new uint32[0x1000]; + for (uint32 i = 0; i < 0x1000; i++) + _oprFrq[i] = (uint32)(_baserate * (float)(i << 11)); + + delete[] _oprAttackDecay; + _oprAttackDecay = new uint8[152]; + memset(_oprAttackDecay, 0, 152); + for (int i = 0; i < 36; i++) + WRITE_BE_UINT32(_oprAttackDecay + (i << 2), _adtStat[i]); + + delete[] _oprSinTbl; + _oprSinTbl = new uint32[1024]; + for (int i = 0; i < 1024; i++) { + double val = sin((double)(((i << 1) + 1) * PI / 1024.0)); + double d_dcb = log(1.0 / (double)ABS(val)) / log(2.0) * 256.0; + int32 i_dcb = (int32)(2.0 * d_dcb); + i_dcb = (i_dcb & 1) ? (i_dcb >> 1) + 1 : (i_dcb >> 1); + _oprSinTbl[i] = (i_dcb << 1) + (val >= 0.0 ? 0 : 1); + } + + delete[] _oprLevelOut; + _oprLevelOut = new int32[0x1a00]; + for (int i = 0; i < 256; i++) { + double val = floor(65536.0 / pow(2.0, 0.00390625 * (double)(1 + i))); + int32 val_int = ((int32) val) >> 4; + _oprLevelOut[i << 1] = (val_int & 1) ? ((val_int >> 1) + 1) << 2 : (val_int >> 1) << 2; + _oprLevelOut[(i << 1) + 1] = -_oprLevelOut[i << 1]; + for (int ii = 1; ii < 13; ++ii) { + _oprLevelOut[(i << 1) + (ii << 9)] = _oprLevelOut[i << 1] >> ii; + _oprLevelOut[(i << 1) + (ii << 9) + 1] = -_oprLevelOut[(i << 1) + (ii << 9)]; + } + } + + uint8 *dtt = new uint8[128]; + memset(dtt, 0, 36); + memset(dtt + 36, 1, 8); + memcpy(dtt + 44, _detSrc, 84); + + delete[] _oprDetune; + _oprDetune = new int32[256]; + for (int i = 0; i < 128; i++) { + _oprDetune[i] = (int32)((float)dtt[i] * _baserate * 64.0); + _oprDetune[i + 128] = -_oprDetune[i]; + } + + delete[] dtt; +} + +void TownsPC98_FmSynth::nextTick(int32 *buffer, uint32 bufferSize) { + if (!_ready) + return; + + for (int i = 0; i < _numChan; i++) { + TownsPC98_FmSynthOperator **o = _chanInternal[i].opr; + + if (_chanInternal[i].updateEnvelopeParameters) { + _chanInternal[i].updateEnvelopeParameters = false; + for (int ii = 0; ii < 4 ; ii++) + o[ii]->updatePhaseIncrement(); + } + + for (uint32 ii = 0; ii < bufferSize ; ii++) { + int32 phbuf1, phbuf2, output; + phbuf1 = phbuf2 = output = 0; + + int32 *leftSample = &buffer[ii * 2]; + int32 *rightSample = &buffer[ii * 2 + 1]; + int32 *del = &_chanInternal[i].feedbuf[2]; + int32 *feed = _chanInternal[i].feedbuf; + + switch (_chanInternal[i].algorithm) { + case 0: + o[0]->generateOutput(0, feed, phbuf1); + o[2]->generateOutput(*del, 0, phbuf2); + *del = 0; + o[1]->generateOutput(phbuf1, 0, *del); + o[3]->generateOutput(phbuf2, 0, output); + break; + case 1: + o[0]->generateOutput(0, feed, phbuf1); + o[2]->generateOutput(*del, 0, phbuf2); + o[1]->generateOutput(0, 0, phbuf1); + o[3]->generateOutput(phbuf2, 0, output); + *del = phbuf1; + break; + case 2: + o[0]->generateOutput(0, feed, phbuf2); + o[2]->generateOutput(*del, 0, phbuf2); + o[1]->generateOutput(0, 0, phbuf1); + o[3]->generateOutput(phbuf2, 0, output); + *del = phbuf1; + break; + case 3: + o[0]->generateOutput(0, feed, phbuf2); + o[2]->generateOutput(0, 0, *del); + o[1]->generateOutput(phbuf2, 0, phbuf1); + o[3]->generateOutput(*del, 0, output); + *del = phbuf1; + break; + case 4: + o[0]->generateOutput(0, feed, phbuf1); + o[2]->generateOutput(0, 0, phbuf2); + o[1]->generateOutput(phbuf1, 0, output); + o[3]->generateOutput(phbuf2, 0, output); + *del = 0; + break; + case 5: + o[0]->generateOutput(0, feed, phbuf1); + o[2]->generateOutput(*del, 0, output); + o[1]->generateOutput(phbuf1, 0, output); + o[3]->generateOutput(phbuf1, 0, output); + *del = phbuf1; + break; + case 6: + o[0]->generateOutput(0, feed, phbuf1); + o[2]->generateOutput(0, 0, output); + o[1]->generateOutput(phbuf1, 0, output); + o[3]->generateOutput(0, 0, output); + *del = 0; + break; + case 7: + o[0]->generateOutput(0, feed, output); + o[2]->generateOutput(0, 0, output); + o[1]->generateOutput(0, 0, output); + o[3]->generateOutput(0, 0, output); + *del = 0; + break; + }; + + int32 finOut = (output << 2) / ((_numChan + _numSSG - 3) / 3); + + if ((1 << i) & _volMaskA) + finOut = (finOut * _volumeA) / Audio::Mixer::kMaxMixerVolume; + + if ((1 << i) & _volMaskB) + finOut = (finOut * _volumeB) / Audio::Mixer::kMaxMixerVolume; + + if (_chanInternal[i].enableLeft) + *leftSample += finOut; + + if (_chanInternal[i].enableRight) + *rightSample += finOut; + } + } +} + +const uint32 TownsPC98_FmSynth::_adtStat[] = { + 0x00010001, 0x00010001, 0x00010001, 0x01010001, + 0x00010101, 0x00010101, 0x00010101, 0x01010101, + 0x01010101, 0x01010101, 0x01010102, 0x01010102, + 0x01020102, 0x01020102, 0x01020202, 0x01020202, + 0x02020202, 0x02020202, 0x02020204, 0x02020204, + 0x02040204, 0x02040204, 0x02040404, 0x02040404, + 0x04040404, 0x04040404, 0x04040408, 0x04040408, + 0x04080408, 0x04080408, 0x04080808, 0x04080808, + 0x08080808, 0x08080808, 0x10101010, 0x10101010 +}; + +const uint8 TownsPC98_FmSynth::_detSrc[] = { + 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, + 0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, + 0x08, 0x08, 0x08, 0x08, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, + 0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, + 0x08, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x10, 0x10, 0x10, 0x10, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, + 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0a, + 0x0b, 0x0c, 0x0d, 0x0e, 0x10, 0x11, 0x13, 0x14, + 0x16, 0x16, 0x16, 0x16 +}; + +const int TownsPC98_FmSynth::_ssgTables[] = { + 0x01202A, 0x0092D2, 0x006B42, 0x0053CB, 0x003DF8, 0x003053, 0x0022DA, 0x001A8C, + 0x00129B, 0x000DC1, 0x000963, 0x0006C9, 0x000463, 0x0002FA, 0x0001B6, 0x0000FB, + 0x0193B6, 0x01202A, 0x00CDB1, 0x0092D2, 0x007D7D, 0x006B42, 0x005ECD, 0x0053CB, + 0x00480F, 0x003DF8, 0x0036B9, 0x003053, 0x00290A, 0x0022DA, 0x001E6B, 0x001A8C, + 0x001639, 0x00129B, 0x000FFF, 0x000DC1, 0x000B5D, 0x000963, 0x0007FB, 0x0006C9, + 0x000575, 0x000463, 0x00039D, 0x0002FA, 0x000242, 0x0001B6, 0x00014C, 0x0000FB +}; + +const uint8 TownsPC98_FmSynth::_percussionData[] = { + 0, 24, 1, 192, 1, 216, 2, 128, 4, 88, 23, 64, 27, 152, 1, 128, 29, 24, 2, 128, 31, 152, 0, 128, 136, 128, 128, 128, 0, 136, 97, 103, 153, 139, 34, 163, 72, 195, 27, 69, 1, 154, 137, 35, 8, 51, 169, 122, 164, 75, 133, 203, 81, 146, 168, 121, 185, 68, 202, 8, 33, 237, 49, 177, 12, 133, 140, 17, 160, 42, 161, 10, 0, 137, 176, 57, + 233, 41, 160, 136, 235, 65, 177, 137, 128, 26, 164, 28, 3, 157, 51, 137, 1, 152, 113, 161, 40, 146, 115, 192, 56, 5, 169, 66, 161, 56, 1, 50, 145, 59, 39, 168, 97, 1, 160, 57, 7, 153, 50, 153, 32, 2, 25, 129, 32, 20, 186, 66, 129, 24, 153, 164, 142, 130, 169, 153, 26, 242, 138, 217, 9, 128, 204, 58, 209, 172, 40, 176, 141, + 128, 155, 144, 203, 139, 0, 235, 9, 177, 172, 0, 185, 168, 138, 25, 240, 59, 211, 139, 19, 176, 90, 160, 17, 26, 132, 41, 1, 5, 25, 3, 50, 144, 115, 147, 42, 39, 152, 41, 3, 56, 193, 105, 130, 155, 66, 200, 26, 19, 218, 154, 49, 201, 171, 138, 176, 251, 139, 185, 172, 136, 189, 139, 145, 207, 41, 160, 171, 152, 186, 139, + 186, 141, 128, 218, 171, 51, 217, 170, 56, 163, 12, 4, 155, 81, 147, 42, 37, 152, 32, 54, 136, 49, 50, 48, 37, 32, 69, 0, 17, 50, 50, 83, 2, 16, 68, 20, 8, 66, 4, 154, 84, 145, 24, 33, 24, 32, 17, 18, 145, 32, 22, 168, 49, 163, 1, 33, 50, 184, 115, 129, 25, 66, 1, 24, 67, 2, 80, 35, 40, 53, 2, 65, 51, 19, 67, 37, 0, 52, 35, 49, 37, + 34, 49, 37, 17, 52, 17, 35, 35, 35, 34, 32, 49, 33, 152, 34, 145, 24, 24, 128, 138, 128, 184, 9, 177, 171, 168, 185, 155, 152, 172, 155, 186, 172, 185, 172, 155, 186, 173, 153, 202, 187, 185, 202, 170, 171, 202, 186, 169, 170, 170, 171, 139, 154, 171, 153, 154, 169, 10, 168, 154, 128, 168, 154, 0, 153, 152, 136, 137, + 128, 153, 0, 152, 8, 128, 137, 0, 136, 136, 8, 9, 8, 9, 8, 24, 153, 128, 136, 153, 144, 0, 161, 138, 1, 169, 136, 128, 160, 168, 152, 153, 138, 137, 154, 153, 153, 154, 153, 170, 168, 170, 185, 168, 169, 154, 169, 171, 153, 169, 170, 153, 152, 154, 153, 137, 169, 137, 136, 144, 152, 144, 128, 128, 144, 129, 129, 0, 33, + 0, 17, 17, 17, 33, 33, 18, 18, 34, 34, 34, 34, 34, 34, 35, 19, 35, 19, 35, 35, 18, 19, 18, 35, 18, 33, 0, 8, 8, 8, 8, 8, 8, 8, 160, 205, 65, 176, 171, 203, 16, 240, 95, 242, 120, 145, 156, 66, 177, 26, 19, 153, 9, 35, 35, 239, 56, 132, 138, 154, 50, 145, 203, 25, 32, 20, 237, 24, 130, 138, 160, 27, 39, 173, 50, 203, 64, 145, 139, + 18, 168, 48, 146, 171, 65, 18, 176, 12, 52, 128, 25, 5, 57, 240, 104, 161, 25, 129, 18, 188, 114, 160, 26, 36, 200, 154, 18, 1, 128, 186, 73, 162, 173, 32, 184, 25, 144, 137, 234, 8, 154, 32, 160, 158, 18, 187, 81, 2, 235, 41, 36, 144, 154, 17, 67, 128, 33, 160, 114, 146, 26, 37, 33, 232, 41, 130, 41, 178, 29, 50, 251, 24, + 1, 153, 138, 160, 76, 179, 155, 11, 0, 38, 252, 41, 146, 41, 178, 27, 193, 43, 39, 170, 136, 17, 129, 8, 49, 233, 48, 129, 11, 6, 26, 130, 136, 128, 64, 1, 248, 105, 145, 9, 16, 144, 140, 5, 25, 168, 16, 186, 48, 5, 171, 217, 57, 134, 171, 8, 34, 188, 20, 203, 41, 6, 155, 161, 89, 164, 140, 2, 136, 51, 202, 41, 131, 56, 144, + 8, 97, 144, 146, 13, 69, 200, 42, 130, 25, 152, 57, 6, 220, 88, 177, 26, 148, 9, 168, 8, 67, 192, 156, 65, 145, 137, 10, 4, 154, 18, 157, 67, 160, 154, 1, 50, 188, 82, 170, 82, 185, 49, 220, 97, 144, 10, 8, 16, 145, 9, 136, 18, 202, 51, 184, 141, 114, 179, 139, 24, 19, 8, 250, 121, 160, 40, 160, 10, 18, 152, 168, 42, 35, 216, + 187, 120, 145, 18, 156, 203, 84, 144, 9, 144, 26, 66, 161, 13, 1, 128, 17, 154, 18, 142, 6, 154, 65, 192, 29, 35, 186, 64, 192, 24, 9, 146, 56, 185, 16, 248, 121, 176, 40, 129, 136, 171, 96, 147, 140, 50, 203, 64, 144, 41, 128, 161, 187, 71, 200, 24, 129, 24, 217, 56, 20, 220, 24, 4, 169, 9, 1, 33, 201, 26, 134, 141, 51, 201, + 25, 16, 33, 235, 32, 144, 33, 153, 169, 99, 160, 11, 3, 136, 58, 210, 33, 203, 48, 163, 17, 219, 128, 140, 38, 8, 184, 141, 50, 131, 159, 33, 128, 153, 25, 18, 153, 88, 242, 43, 3, 9, 136, 157, 53, 202, 40, 145, 25, 2, 204, 105, 146, 156, 66, 152, 8, 153, 33, 128, 129, 136, 153, 50, 186, 55, 188, 51, 249, 64, 178, 27, 128, + 48, 177, 156, 18, 35, 175, 51, 189, 32, 51, 234, 155, 69, 184, 26, 2, 152, 9, 17, 136, 144, 137, 50, 235, 115, 216, 24, 2, 170, 67, 187, 49, 129, 155, 4, 27, 129, 56, 232, 43, 39, 203, 40, 3, 154, 169, 66, 184, 114, 224, 25, 2, 9, 128, 11, 35, 155, 18, 11, 202, 84, 169, 26, 5, 154, 8, 160, 98, 185, 17, 187, 50, 23, 188, 33, + 1, 139, 4, 154, 90, 147, 12, 3, 43, 2, 170, 171, 103, 193, 28, 132, 137, 8, 129, 24, 170, 50, 201, 42, 35, 202, 169, 52, 201, 33, 218, 40, 39, 203, 0, 40, 147, 29, 163, 139, 83, 185, 1, 4, 159, 34, 160, 12, 21, 155, 40, 129, 137, 58, 151, 13, 2, 136, 144, 16, 153, 40, 17, 131, 207, 51, 144, 140, 4, 154, 17, 146, 170, 73, 163, + 44, 164, 12, 152, 37, 203, 17, 128, 144, 139, 23, 154, 128, 138, 38, 216, 41, 1, 0, 233, 73, 131, 171, 49, 136, 9, 164, 46, 3, 171, 32, 0, 145, 157, 38, 187, 64, 176, 58, 134, 155, 18, 136, 217, 64, 1, 200, 140, 38, 153, 170, 66, 161, 8, 169, 65, 185, 98, 200, 41, 3, 155, 144, 58, 23, 187, 1, 145, 40, 147, 189, 32, 68, 249, + 1, 112, 255, 199, 195, 19, 108, 76, 187, 247, 247, 183, 40, 168, 212, 245, 199, 227, 68, 45, 59, 10, 145, 177, 198, 24, 130, 76, 26, 193, 180, 129, 0, 162, 42, 160, 199, 162, 0, 16, 152, 137, 132, 168, 195, 130, 162, 181, 227, 163, 161, 179, 211, 180, 179, 164, 128, 162, 161, 194, 164, 179, 40, 153, 195, 213, 146, 178, + 147, 176, 50, 186, 161, 196, 151, 58, 16, 28, 162, 160, 131, 122, 155, 33, 241, 146, 128, 40, 26, 128, 154, 36, 170, 89, 59, 9, 24, 144, 77, 161, 8, 177, 112, 139, 33, 232, 148, 24, 41, 61, 9, 26, 162, 32, 30, 58, 153, 32, 59, 73, 59, 11, 79, 137, 57, 9, 49, 30, 24, 153, 131, 25, 106, 61, 153, 73, 28, 56, 27, 41, 137, 148, + 76, 43, 74, 58, 13, 161, 3, 171, 149, 32, 77, 10, 74, 42, 168, 16, 0, 123, 138, 129, 162, 178, 225, 50, 140, 161, 0, 147, 10, 129, 41, 244, 210, 165, 1, 152, 24, 162, 184, 166, 32, 144, 59, 216, 132, 177, 8, 145, 67, 143, 146, 160, 183, 162, 130, 24, 192, 32, 225, 146, 144, 33, 44, 73, 30, 129, 137, 32, 76, 152, 25, 161, + 2, 154, 32, 177, 132, 232, 2, 136, 210, 128, 149, 177, 32, 58, 27, 168, 225, 133, 8, 44, 107, 136, 25, 136, 17, 26, 58, 46, 16, 11, 145, 17, 144, 79, 136, 144, 136, 145, 152, 33, 31, 162, 130, 200, 82, 153, 74, 137, 147, 26, 0, 13, 133, 170, 149, 16, 192, 0, 178, 0, 128, 152, 182, 150, 9, 16, 9, 137, 33, 59, 63, 10, 152, 32, + 179, 192, 5, 154, 228, 182, 145, 130, 144, 42, 128, 242, 2, 136, 41, 168, 17, 76, 57, 31, 129, 136, 17, 47, 8, 41, 138, 32, 138, 123, 59, 58, 10, 136, 161, 4, 46, 25, 145, 136, 129, 25, 56, 28, 91, 41, 154, 108, 9, 16, 44, 24, 137, 48, 15, 0, 194, 162, 41, 194, 56, 241, 163, 146, 0, 139, 7, 186, 150, 129, 152, 1, 208, 33, 176, + 136, 164, 163, 185, 7, 138, 130, 242, 162, 163, 177, 88, 136, 184, 166, 146, 0, 25, 25, 177, 199, 146, 16, 136, 9, 145, 178, 178, 0, 147, 138, 229, 18, 152, 25, 144, 163, 246, 162, 129, 129, 184, 5, 152, 178, 145, 148, 136, 146, 95, 152, 128, 144, 33, 170, 81, 11, 40, 202, 131, 0, 243, 24, 1, 11, 148, 42, 24, 163, 140, + 120, 9, 76, 58, 153, 145, 56, 30, 72, 46, 42, 9, 8, 57, 91, 76, 59, 26, 160, 129, 41, 76, 10, 57, 192, 163, 129, 16, 225, 2, 27, 40, 200, 48, 91, 226, 40, 145, 43, 177, 177, 182, 196, 145, 33, 184, 165, 17, 192, 163, 194, 129, 211, 128, 162, 197, 129, 0, 136, 211, 146, 8, 162, 144, 0, 167, 160, 1, 176, 150, 137, 1, 24, 243, + 0, 129, 145, 25, 123, 169, 130, 168, 132, 41, 63, 42, 136, 137, 120, 26, 136, 8, 24, 89, 29, 58, 177, 193, 147, 1, 26, 162, 176, 167, 180, 8, 49, 28, 29, 178, 162, 88, 43, 42, 57, 43, 61, 8, 29, 129, 128, 128, 123, 137, 24, 243, 16, 136, 16, 46, 0, 169, 149, 128, 1, 60, 153, 72, 154, 90, 25, 25, 25, 8, 91, 73, 12, 16, 137, 144, + 72, 11, 8, 167, 128, 129, 9, 138, 166, 193, 147, 162, 123, 137, 145, 1, 162, 26, 1, 219, 147, 129, 210, 147, 243, 1, 243, 16, 144, 145, 160, 131, 200, 4, 59, 75, 57, 218, 2, 178, 77, 24, 60, 11, 147, 10, 50, 141, 64, 27, 185, 122, 161, 41, 128, 90, 136, 24, 46, 16, 139, 16, 24, 28, 124, 9, 41, 8, 26, 121, 10, 42, 40, 139, 129, + 0, 201, 135, 137, 56, 176, 176, 35, 215, 145, 1, 26, 145, 144, 160, 135, 138, 1, 177, 146, 146, 161, 65, 242, 136, 164, 177, 1, 1, 186, 151, 208, 148, 129, 10, 32, 241, 145, 163, 178, 17, 168, 136, 151, 168, 2, 148, 185, 133, 176, 130, 129, 154, 163, 215, 0, 146, 136, 40, 211, 161, 131, 171, 81, 144, 170, 21, 184, 56, + 195, 168, 133, 177, 91, 16, 187, 5, 145, 153, 66, 172, 18, 177, 42, 120, 138, 27, 134, 26, 106, 42, 138, 146, 184, 66, 75, 46, 41, 168, 0, 145, 57, 91, 75, 27, 24, 27, 48, 169, 40, 122, 9, 109, 10, 8, 177, 146, 16, 74, 30, 129, 160, 162, 146, 41, 124, 138, 24, 145, 152, 3, 1, 14, 3, 139, 1, 192, 161, 151, 177, 122, 8, 10, 0, + 176, 130, 129, 27, 88, 225, 0, 2, 154, 129, 129, 193, 49, 203, 81, 153, 226, 33, 0, 30, 0, 176, 179, 18, 9, 96, 156, 162, 148, 160, 129, 2, 29, 195, 128, 0, 56, 156, 20, 232, 129, 128, 32, 10, 144, 74, 183, 9, 145, 162, 1, 162, 138, 23, 171, 1, 164, 224, 34, 43, 43, 177, 200, 135, 161, 91, 57, 154, 177, 148, 145, 146, 58, + 108, 136, 170, 35, 208, 177, 34, 128, 44, 129, 155, 151, 243, 16, 1, 154, 72, 193, 144, 18, 11, 122, 160, 153, 5, 192, 24, 130, 184, 132, 226, 0, 128, 153, 131, 181, 136, 65, 154, 128, 17, 170, 39, 28, 59, 144, 168, 80, 25, 47, 24, 26, 144, 32, 47, 41, 153, 161, 148, 8, 92, 9, 9, 129, 144, 33, 26, 47, 24, 137, 108, 25, 10, + 17, 10, 73, 75, 47, 24, 184, 48, 8, 45, 57, 138, 136, 150, 10, 48, 139, 136, 35, 203, 121, 8, 27, 179, 161, 106, 0, 29, 16, 176, 179, 3, 185, 19, 227, 41, 145, 168, 61, 197, 177, 20, 10, 57, 42, 250, 147, 196, 16, 41, 138, 24, 195, 208, 135, 137, 0, 145, 160, 2, 210, 146, 195, 177, 132, 136, 153, 167, 210, 146, 162, 40, 8, + 138, 148, 227, 145, 17, 137, 40, 169, 179, 130, 242, 2, 196, 9, 146, 145, 169, 167, 146, 130, 137, 136, 51, 220, 17, 163, 28, 74, 10, 76, 40, 140, 5, 137, 43, 18, 12, 107, 137, 40, 8, 201, 50, 0, 143, 3, 138, 161, 134, 138, 104, 169, 16, 162, 160, 121, 25, 28, 129, 152, 32, 56, 14, 16, 184, 146, 3, 46, 25, 176, 129, 179, + 193, 17, 130, 202, 135, 8, 57, 25, 154, 148, 184, 120, 9, 153, 211, 165, 24, 128, 26, 17, 242, 161, 18, 185, 81, 42, 11, 17, 12, 25, 181, 137, 66, 42, 47, 41, 184, 166, 129, 24, 91, 27, 136, 196, 0, 0, 74, 28, 178, 161, 149, 160, 32, 8, 225, 32, 128, 59, 8, 169, 50, 139, 47, 72, 186, 16, 132, 9, 122, 9, 160, 146, 144, 89, 153, + 10, 149, 178, 0, 121, 11, 146, 152, 162, 48, 13, 123, 177, 24, 0, 106, 27, 9, 144, 132, 12, 17, 0, 168, 0, 181, 56, 169, 129, 242, 195, 129, 17, 154, 64, 161, 244, 16, 137, 24, 144, 144, 164, 129, 75, 42, 176, 149, 9, 179, 148, 203, 4, 166, 136, 163, 128, 227, 163, 8, 57, 11, 30, 165, 0, 74, 59, 62, 9, 208, 131, 144, 40, 76, + 26, 27, 196, 129, 1, 25, 43, 49, 174, 67, 153, 136, 106, 152, 41, 25, 28, 2, 43, 44, 104, 45, 59, 8, 43, 128, 144, 120, 25, 12, 17, 152, 9, 130, 155, 151, 145, 74, 40, 13, 48, 192, 58, 90, 43, 43, 177, 146, 49, 31, 75, 24, 217, 131, 0, 76, 26, 152, 149, 161, 24, 74, 154, 193, 166, 145, 32, 27, 161, 164, 176, 135, 152, 24, 193, + 162, 146, 164, 58, 227, 193, 148, 161, 128, 18, 234, 130, 180, 145, 2, 200, 1, 163, 186, 98, 184, 129, 149, 153, 49, 42, 186, 151, 242, 129, 1, 43, 8, 177, 212, 165, 8, 40, 137, 24, 8, 144, 90, 9, 25, 48, 44, 46, 24, 138, 40, 144, 108, 58, 27, 128, 181, 128, 80, 29, 42, 152, 162, 130, 25, 106, 136, 11, 148, 8, 144, 128, 136, + 112, 139, 80, 153, 24, 136, 129, 46, 0, 60, 129, 208, 1, 3, 13, 57, 168, 144, 1, 242, 17, 9, 26, 2, 185, 27, 55, 140, 73, 137, 179, 16, 192, 3, 145, 143, 33, 9, 171, 135, 160, 17, 137, 10, 151, 168, 3, 178, 44, 17, 208, 144, 167, 0, 40, 155, 16, 167, 152, 18, 144, 26, 160, 199, 1, 136, 91, 136, 160, 178, 150, 161, 1, 10, 181, + 145, 161, 1, 145, 161, 198, 2, 9, 90, 137, 177, 160, 150, 40, 29, 129, 144, 145, 162, 57, 77, 169, 16, 148, 42, 42, 40, 141, 34, 170, 121, 154, 210, 131, 162, 107, 8, 9, 160, 195, 40, 73, 139, 18, 224, 162, 34, 139, 0, 244, 178, 163, 24, 26, 146, 194, 166, 49, 29, 42, 137, 130, 192, 16, 93, 128, 154, 19, 59, 11, 122, 11, + 146, 177, 120, 42, 26, 43, 164, 152, 17, 60, 63, 137, 128, 48, 10, 58, 92, 9, 59, 91, 75, 139, 32, 25, 25, 61, 74, 28, 177, 40, 130, 74, 29, 73, 168, 130, 128, 48, 14, 8, 77, 9, 25, 26, 179, 211, 32, 78, 26, 41, 152, 161, 180, 89, 59, 9, 153, 166, 160, 3, 26, 57, 106, 154, 88, 184, 40, 1, 27, 58, 73, 143, 131, 169, 3, 161, 184, + 122, 152, 16, 181, 145, 129, 17, 15, 129, 193, 147, 145, 192, 33, 193, 162, 183, 163, 136, 178, 129, 178, 197, 2, 41, 216, 131, 168, 163, 181, 226, 163, 178, 1, 33, 187, 166, 212, 129, 1, 27, 24, 162, 184, 151, 8, 16, 160, 144, 181, 210, 72, 168, 128, 32, 42, 25, 40, 142, 5, 185, 88, 58, 11, 58, 177, 32, 129, 63, 42, 136, + 186, 53, 29, 75, 58, 144, 144, 129, 77, 128, 11, 144, 133, 29, 40, 152, 24, 161, 129, 80, 155, 60, 3, 12, 89, 8, 60, 152, 152, 49, 136, 47, 57, 224, 129, 16, 41, 90, 139, 162, 147, 170, 51, 169, 27, 17, 95, 26, 26, 160, 5, 139, 48, 76, 10, 228, 146, 1, 136, 44, 161, 147, 209, 130, 137, 73, 224, 1, 162, 195, 32, 210, 177, 180, + 179, 148, 145, 154, 132, 242, 146, 1, 152, 32, 192, 1, 144, 155, 7, 177, 168, 5, 138, 178, 148, 152, 150, 136, 89, 152, 9, 41, 196, 145, 40, 28, 16, 8, 10, 178, 167, 24, 1, 44, 123, 137, 136, 145, 194, 48, 27, 74, 26, 192, 179, 135, 136, 88, 27, 10, 177, 163, 164, 128, 73, 24, 31, 8, 0, 192, 149, 144, 129, 9, 106, 41, 200, + 161, 151, 41, 138, 0, 24, 226, 162, 49, 42, 11, 90, 136, 136, 152, 17, 145, 10, 63, 40, 11, 56, 245, 162, 16, 26, 73, 11, 144, 135, 137, 58, 106, 10, 25, 8, 57, 137, 28, 33, 129, 156, 113, 10, 10, 161, 18, 8, 153, 77, 3, 217, 0, 1, 242, 128, 193, 18, 128, 75, 60, 178, 154, 37, 45, 58, 29, 144, 1, 184, 66, 41, 29, 8, 145, 10, + 194, 33, 148, 170, 107, 89, 139, 128, 163, 178, 16, 63, 59, 176, 144, 151, 129, 42, 74, 10, 129, 192, 2, 128, 154, 97, 192, 0, 177, 128, 178, 183, 16, 16, 155, 149, 145, 184, 84, 138, 8, 192, 161, 20, 225, 0, 130, 138, 165, 0, 28, 148, 153, 18, 209, 128, 88, 153, 89, 152, 9, 17, 9, 29, 130, 43, 122, 153, 24, 32, 202, 49, + 24, 43, 106, 154, 130, 193, 27, 51, 29, 28, 133, 138, 65, 11, 123, 25, 10, 40, 152, 44, 130, 26, 43, 148, 45, 73, 140, 33, 8, 153, 88, 128, 61, 144, 42, 59, 225, 128, 18, 155, 50, 75, 186, 20, 202, 120, 144, 42, 92, 176, 162, 165, 25, 2, 169, 152, 135, 185, 19, 152, 8, 146, 160, 123, 195, 137, 132, 209, 0, 16, 11, 2, 242, + 146, 164, 152, 73, 193, 136, 130, 178, 1, 136, 169, 23, 169, 128, 164, 242, 129, 178, 129, 32, 138, 180, 167, 153, 132, 8, 138, 2, 209, 4, 138, 1, 128, 138, 92, 136, 44, 129, 136, 162, 33, 63, 40, 141, 2, 160, 144, 106, 137, 64, 155, 17, 129, 60, 30, 146, 26, 17, 28, 48, 46, 169, 51, 154, 91, 137, 41, 26, 32, 143, 18, 138, + 1, 32, 28, 123, 177, 9, 181, 195, 56, 57, 14, 145, 161, 17, 17, 31, 41, 152, 145, 194, 194, 20, 153, 41, 9, 243, 129, 180, 0, 128, 45, 16, 43, 170, 135, 144, 16, 25, 42, 137, 242, 163, 194, 16, 0, 57, 14, 130, 194, 178, 16, 33, 30, 8, 59, 211, 163, 160, 5, 137, 44, 10, 17, 170, 3, 120, 9, 44, 146, 136, 131, 140, 91, 9, 171, + 7, 161, 32, 73, 13, 8, 161, 40, 106, 11, 25, 129, 59, 0, 49, 31, 42, 28, 40, 11, 0, 81, 176, 61, 32, 138, 25, 178, 241, 148, 136, 106, 8, 136, 128, 177, 90, 8, 155, 96, 176, 9, 18, 217, 132, 129, 10, 81, 156, 40, 178, 161, 36, 169, 76, 147, 203, 150, 0, 10, 146, 200, 147, 149, 128, 144, 148, 154, 182, 24, 0, 137, 11, 134, 211, + 24, 136, 129, 145, 209, 33, 8, 43, 163, 243, 88, 41, 13, 0, 160, 145, 33, 31, 32, 185, 145, 4, 155, 17, 32, 47, 161, 128, 73, 160, 44, 56, 176, 75, 74, 12, 35, 141, 104, 137, 9, 89, 152, 58, 56, 44, 41, 30, 41, 40, 157, 48, 128, 154, 88, 41, 42, 8, 14, 3, 184, 59, 120, 152, 9, 56, 10, 128, 41, 57, 227, 186, 52, 152, 62, 8, 56, + 242, 0, 58, 8, 156, 34, 243, 128, 24, 176, 51, 169, 58, 183, 192, 146, 164, 177, 18, 170, 7, 177, 208, 132, 161, 24, 136, 27, 147, 243, 128, 133, 10, 24, 161, 161, 178, 214, 17, 160, 25, 16, 161, 137, 165, 192, 48, 27, 72, 58, 218, 133, 162, 26, 72, 27, 10, 197, 178, 49, 138, 89, 56, 142, 1, 24, 11, 0, 44, 105, 10, 25, 0, + 194, 9, 3, 47, 8, 138, 147, 18, 28, 48, 202, 147, 199, 146, 25, 161, 0, 145, 194, 163, 57, 11, 146, 248, 130, 32, 57, 63, 154, 16, 48, 14, 128, 144, 209, 133, 26, 56, 154, 182, 162, 195, 18, 152, 44, 194, 180, 168, 5, 24, 137, 138, 35, 192, 232, 66, 176, 161, 24, 41, 26, 244, 129, 163, 160, 75, 129, 226, 147, 40, 145, 61, + 13, 130, 177, 17, 137, 112, 170, 130, 0, 136, 75, 152, 177, 241, 34, 0, 59, 156, 51, 186, 178, 91, 132, 137, 137, 122, 1, 45, 28, 50, 172, 57, 108, 8, 26, 136, 32, 152, 46, 144, 131, 171, 4, 152, 18, 141, 148, 1, 216, 32, 9, 60, 169, 66, 152, 128, 72, 90, 201, 1, 17, 201, 136, 3, 195, 26, 73, 133, 200, 176, 150, 146, 169, + 24, 33, 178, 184, 151, 73, 11, 28, 72, 44, 153, 82, 153, 17, 42, 57, 78, 153, 8, 160, 0, 1, 123, 11, 19, 171, 195, 18, 59, 31, 129, 10, 162, 2, 58, 96, 142, 130, 26, 75, 128, 176, 17, 180, 123, 9, 90, 137, 211, 145, 32, 26, 76, 43, 145, 130, 12, 90, 41, 27, 58, 160, 160, 128, 178, 7, 76, 59, 0, 203, 180, 147, 33, 62, 10, 0, 243, + 129, 146, 73, 29, 145, 144, 0, 26, 56, 153, 185, 83, 8, 76, 27, 166, 161, 193, 146, 131, 224, 145, 165, 161, 40, 168, 149, 162, 226, 2, 136, 138, 163, 131, 211, 0, 59, 146, 218, 148, 1, 192, 16, 16, 58, 248, 88, 144, 177, 136, 1, 58, 45, 9, 195, 197, 147, 48, 29, 10, 0, 162, 176, 64, 122, 9, 10, 17, 9, 153, 56, 75, 27, 31, + 72, 136, 9, 129, 129, 61, 45, 59, 10, 161, 18, 122, 43, 59, 41, 169, 34, 155, 130, 131, 219, 120, 162, 27, 49, 208, 160, 131, 156, 66, 12, 145, 50, 240, 16, 136, 12, 162, 40, 129, 130, 15, 129, 162, 146, 180, 83, 139, 58, 217, 129, 177, 4, 0, 169, 197, 163, 144, 242, 131, 168, 179, 179, 17, 197, 145, 178, 164, 128, 160, + 211, 2, 244, 163, 145, 162, 129, 212, 177, 163, 17, 208, 163, 195, 180, 57, 24, 170, 182, 164, 129, 0, 60, 60, 169, 149, 162, 177, 122, 26, 24, 136, 136, 133, 43, 27, 178, 56, 77, 24, 128, 240, 0, 2, 44, 46, 8, 128, 193, 146, 64, 27, 42, 16, 193, 25, 0, 192, 148, 11, 52, 47, 153, 147, 243, 0, 24, 73, 28, 144, 161, 150, 9, + 8, 73, 170, 2, 162, 25, 27, 147, 167, 131, 29, 1, 168, 200, 165, 16, 91, 137, 8, 162, 176, 35, 41, 31, 24, 169, 50, 168, 58, 123, 144, 48, 128, 13, 73, 169, 144, 16, 57, 123, 44, 200, 163, 56, 153, 80, 10, 176, 146, 57, 94, 8, 152, 131, 9, 168, 125, 26, 145, 177, 132, 137, 41, 60, 26, 144, 243, 32, 192, 34, 60, 43, 26, 16, + 249, 164, 16, 58, 61, 11, 130, 243, 146, 2, 42, 44, 27, 128, 165, 137, 49, 45, 28, 16, 43, 8, 211, 48, 28, 152, 105, 9, 9, 163, 161, 169, 35, 107, 42, 232, 164, 130, 168, 72, 42, 168, 210, 148, 144, 136, 129, 3, 217, 194, 50, 27, 192, 41, 210, 147, 40, 76, 226, 1, 161, 1, 155, 132, 145, 147, 171, 67, 173, 210, 132, 161, 106, + 137, 56, 169, 209, 131, 64, 13, 129, 9, 194, 17, 57, 61, 169, 17, 128, 40, 31, 16, 10, 162, 57, 61, 75, 139, 40, 242, 17, 58, 59, 138, 179, 144, 50, 105, 140, 179, 243, 57, 40, 26, 9, 243, 130, 24, 29, 57, 128, 210, 129, 25, 59, 91, 137, 162, 178, 72, 27, 181, 168, 19, 129, 8, 184, 231, 147, 178, 32, 28, 184, 198, 148, 144, + 1, 26, 128, 16, 192, 2, 26, 144, 244, 129, 0, 16, 10, 197, 177, 181, 1, 41, 9, 178, 165, 211, 129, 25, 145, 137, 210, 147, 152, 210, 163, 132, 194, 17, 91, 169, 145, 181, 130, 9, 89, 137, 152, 178, 4, 128, 9, 63, 160, 128, 106, 8, 25, 43, 10, 32, 47, 26, 123, 152, 24, 40, 25, 27, 18, 186, 35, 158, 64, 42, 216, 33, 25, 58, 58, + 45, 184, 147, 29, 72, 46, 9, 0, 178, 146, 58, 77, 26, 25, 209, 165, 128, 145, 17, 153, 128, 129, 148, 240, 129, 1, 40, 31, 0, 152, 242, 163, 16, 59, 44, 24, 243, 146, 128, 1, 26, 26, 179, 213, 145, 130, 176, 131, 40, 25, 145, 219, 179, 167, 8, 33, 59, 14, 176, 166, 16, 136, 74, 128, 176, 128, 149, 8, 8, 209, 148, 152, 0, 72, + 153, 161, 178, 35, 62, 75, 154, 163, 153, 19, 62, 170, 133, 179, 136, 89, 12, 129, 164, 144, 3, 47, 58, 193, 177, 148, 0, 61, 43, 10, 129, 17, 41, 61, 43, 25, 8, 126, 26, 25, 137, 145, 34, 44, 45, 129, 216, 179, 1, 90, 25, 137, 32, 227, 8, 16, 9, 170, 49, 31, 32, 29, 128, 145, 148, 75, 25, 75, 153, 162, 192, 35, 12, 80, 136, + 176, 8, 194, 24, 1, 176, 21, 154, 145, 80, 251, 130, 2, 30, 9, 8, 130, 145, 128, 98, 27, 26, 129, 136, 162, 15, 33, 168, 59, 65, 177, 77, 141, 1, 128, 168, 113, 10, 137, 178, 163, 146, 132, 74, 153, 224, 164, 33, 184, 19, 184, 228, 161, 17, 91, 152, 25, 146, 152, 44, 121, 9, 160, 145, 17, 25, 28, 93, 128, 152, 2, 25, 27, 161, + 210, 129, 146, 45, 179, 227, 163, 162, 9, 40, 193, 148, 179, 57, 107, 140, 196, 32, 25, 57, 47, 136, 210, 130, 24, 40, 28, 152, 210, 182, 145, 40, 8, 129, 184, 147, 147, 140, 163, 166, 160, 34, 45, 144, 194, 161, 134, 41, 46, 152, 162, 162, 3, 44, 58, 75, 209, 162, 144, 57, 129, 47, 152, 130, 59, 16, 248, 129, 17, 26, 57, + 9, 29, 167, 2, 60, 42, 138, 136, 209, 130, 90, 42, 42, 176, 146, 178, 120, 28, 8, 160, 145, 16, 33, 31, 1, 8, 160, 129, 128, 242, 164, 32, 152, 177, 146, 213, 196, 128, 40, 26, 160, 163, 180, 146, 108, 60, 144, 144, 136, 147, 137, 40, 90, 161, 3, 17, 219, 243, 33, 184, 130, 60, 136, 243, 178, 179, 132, 26, 8, 168, 212, 147, + 16, 57, 42, 31, 145, 145, 160, 32, 43, 184, 66, 45, 180, 33, 140, 226, 1, 91, 152, 16, 144, 193, 162, 48, 77, 25, 137, 153, 17, 178, 78, 0, 0, 16, 14, 90, 152, 153, 19, 129, 13, 123, 137, 129, 160, 1, 73, 44, 9, 129, 0, 153, 120, 10, 9, 162, 195, 32, 139, 28, 151, 161, 2, 128, 26, 45, 193, 146, 48, 29, 146, 153, 194, 5, 59, + 29, 128, 144, 195, 1, 64, 43, 208, 178, 149, 8, 9, 16, 240, 163, 129, 16, 42, 185, 181, 211, 24, 48, 45, 137, 149, 9, 24, 41, 75, 184, 177, 4, 43, 91, 128, 180, 16, 144, 29, 25, 184, 167, 1, 59, 60, 153, 148, 161, 146, 91, 42, 186, 4, 24, 145, 123, 11, 2, 178, 77, 136, 26, 25, 195, 40, 115, 61, 27, 168, 177, 3, 59, 79, 26, 25, + 144, 1, 48, 13, 56, 154, 248, 1, 16, 9, 129, 8, 2, 178, 31, 130, 153, 162, 20, 15, 33, 170, 56, 40, 29, 28, 128, 152, 149, 144, 56, 120, 11, 162, 212, 129, 144, 145, 59, 180, 243, 147, 145, 144, 16, 152, 48, 241, 0, 161, 176, 1, 134, 10, 129, 200, 166, 144, 128, 121, 26, 24, 177, 178, 196, 48, 75, 138, 41, 180, 195, 26, 24, + 89, 138, 24, 33, 187, 41, 84, 155, 57, 79, 136, 160, 210, 130, 0, 58, 58, 168, 243, 132, 27, 41, 75, 138, 3, 8, 61, 8, 29, 145, 179, 76, 24, 28, 146, 208, 2, 49, 140, 75, 196, 144, 0, 40, 44, 179, 208, 3, 176, 33, 15, 177, 2, 160, 106, 8, 160, 164, 164, 8, 73, 27, 226, 179, 161, 1, 57, 1, 196, 211, 128, 40, 156, 145, 166, 178, + 131, 29, 128, 145, 162, 165, 40, 27, 216, 146, 135, 144, 40, 160, 194, 177, 145, 20, 139, 200, 151, 178, 17, 136, 40, 25, 205, 130, 17, 11, 17, 129, 156, 38, 26, 25, 137, 179, 163, 11, 79, 16, 12, 146, 147, 143, 89, 25, 136, 136, 25, 48, 26, 46, 129, 40, 29, 42, 29, 8, 145, 2, 56, 27, 62, 8, 25, 212, 161, 48, 43, 144, 129, + 29, 145, 144, 41, 106, 10, 107, 43, 184, 131, 1, 36, 61, 13, 138, 2, 194, 1, 16, 27, 75, 186, 181, 151, 8, 1, 161, 138, 211, 129, 2, 59, 248, 129, 16, 0, 144, 63, 152, 150, 136, 24, 25, 128, 30, 161, 128, 17, 24, 225, 146, 10, 16, 0, 9, 227, 183, 129, 40, 60, 26, 162, 194, 181, 24, 90, 9, 24, 0, 176, 161, 193, 194, 35, 12, 63, + 8, 210, 162, 1, 32, 78, 28, 152, 164, 144, 16, 48, 45, 137, 162, 147, 168, 152, 98, 27, 43, 33, 12, 160, 165, 129, 137, 63, 41, 153, 153, 151, 16, 91, 26, 8, 8, 9, 56, 10, 46, 24, 146, 57, 168, 160, 166, 241, 129, 32, 140, 16, 145, 179, 164, 137, 113, 138, 208, 131, 26, 25, 1, 42, 178, 196, 106, 24, 171, 18, 196, 8, 18, 29, + 41, 194, 128, 3, 249, 57, 162, 152, 48, 184, 120, 160, 208, 33, 137, 74, 57, 187, 149, 129, 26, 35, 158, 72, 128, 168, 32, 26, 25, 180, 75, 2, 136, 15, 163, 161, 136, 120, 27, 41, 160, 128, 182, 56, 60, 25, 12, 178, 151, 128, 168, 72, 10, 152, 4, 177, 26, 147, 137, 113, 44, 42, 33, 220, 2, 152, 41, 82, 11, 210, 163, 184, + 133, 162, 10, 196, 128, 3, 234, 40, 149, 152, 161, 1, 44, 129, 194, 4, 225, 16, 58, 168, 24, 194, 146, 146, 154, 49, 21, 218, 33, 152, 248, 129, 194, 147, 0, 28, 1, 195, 162, 20, 140, 42, 25, 160, 198, 1, 33, 136, 142, 3, 25, 24, 141, 16, 177, 208, 112, 0, 138, 41, 160, 130, 45, 60, 32, 170, 73, 24, 75, 59, 161, 176, 49, 159, + 97, 26, 168, 149, 145, 32, 28, 25, 184, 211, 129, 179, 74, 73, 8, 153, 136, 193, 151, 160, 32, 48, 143, 9, 147, 181, 145, 32, 60, 9, 187, 133, 166, 144, 32, 152, 25, 136, 161, 150, 168, 145, 81, 10, 42, 0, 169, 182, 148, 136, 58, 41, 187, 182, 211, 131, 16, 137, 25, 243, 144, 129, 2, 9, 8, 202, 7, 25, 185, 21, 144, 136, 153, + 65, 184, 137, 56, 151, 10, 153, 49, 16, 145, 14, 56, 176, 11, 192, 19, 89, 91, 44, 168, 147, 2, 8, 147, 63, 27, 1, 136, 229, 129, 73, 26, 136, 26, 137, 81, 170, 147, 77, 72, 12, 42, 42, 192, 24, 104, 91, 26, 27, 65, 177, 27, 32, 41, 60, 14, 136, 17, 170, 150, 129, 24, 58, 11, 16, 251, 162, 19, 57, 31, 0, 152, 129, 145, 17, 61, + 14, 1, 129, 27, 129, 66, 169, 178, 74, 12, 11, 19, 198, 145, 75, 33, 138, 174, 133, 1, 184, 57, 40, 136, 169, 20, 1, 60, 174, 20, 154, 201, 67, 26, 162, 151, 42, 16, 138, 59, 130, 204, 20, 169, 59, 180, 59, 114, 184, 56, 178, 242, 128, 130, 43, 8, 194, 3, 229, 144, 33, 185, 144, 34, 181, 145, 168, 17, 149, 153, 74, 35, 220, + 129, 128, 1, 88, 59, 75, 225, 136, 130, 168, 17, 144, 12, 151, 8, 25, 179, 8, 1, 240, 16, 8, 25, 145, 211, 41, 130, 138, 115, 169, 160, 163, 168, 84, 154, 74, 0, 170, 144, 211, 149, 2, 30, 128, 137, 9, 149, 1, 144, 58, 60, 57, 153, 178, 150, 17, 29, 27, 74, 25, 195, 152, 56, 15, 1, 25, 26, 152, 149, 80, 153, 57, 73, 140, 128, + 160, 144, 113, 27, 56, 28, 25, 4, 42, 44, 137, 60, 171, 130, 50, 240, 8, 5, 139, 145, 1, 105, 137, 200, 80, 137, 145, 146, 178, 179, 160, 46, 16, 240, 195, 131, 128, 144, 24, 164, 198, 128, 0, 136, 137, 131, 194, 165, 177, 2, 161, 147, 11, 144, 188, 181, 148, 144, 23, 0, 28, 224, 128, 131, 192, 32, 1, 224, 1, 168, 132, 145, + 9, 41, 208, 58, 137, 179, 151, 145, 16, 1, 30, 8, 145, 178, 1, 47, 32, 186, 72, 169, 146, 75, 8, 41, 48, 136, 89, 13, 48, 9, 10, 124, 26, 11, 42, 32, 129, 91, 77, 16, 12, 128, 42, 57, 138, 10, 60, 2, 63, 9, 0, 93, 128, 152, 90, 8, 10, 24, 40, 44, 144, 29, 49, 188, 48, 72, 25, 30, 177, 33, 128, 186, 120, 129, 186, 133, 152, 130, + 24, 156, 51, 154, 8, 226, 2, 56, 155, 2, 179, 233, 167, 128, 24, 129, 176, 136, 151, 8, 184, 0, 33, 224, 152, 21, 177, 24, 10, 163, 16, 250, 17, 130, 171, 83, 137, 136, 37, 12, 56, 242, 154, 17, 160, 145, 82, 13, 3, 201, 128, 18, 137, 24, 162, 63, 162, 8, 107, 178, 128, 57, 158, 32, 24, 200, 18, 0, 106, 154, 73, 16, 248, 8, + 73, 137, 57, 75, 0, 128, 12, 65, 137, 59, 75, 28, 144, 129, 122, 0, 58, 140, 160, 195, 145, 105, 56, 28, 153, 145, 164, 88, 8, 28, 25, 153, 9, 162, 113, 89, 153, 136, 33, 234, 147, 128, 41, 72, 11, 138, 151, 144, 145, 16, 43, 58, 248, 130, 178, 42, 4, 40, 10, 196, 154, 147, 216, 24, 7, 136, 10, 161, 148, 210, 161, 98, 138, + 137, 128, 146, 176, 33, 105, 27, 43, 163, 49, 185, 6, 10, 136, 43, 67, 174, 161, 162, 151, 137, 1, 64, 200, 193, 24, 64, 200, 56, 145, 242, 24, 57, 137, 1, 128, 3, 162, 175, 80, 128, 162, 152, 25, 58, 175, 17, 17, 0, 200, 64, 168, 162, 91, 1, 154, 44, 211, 177, 35, 64, 160, 161, 144, 4, 241, 41, 209, 162, 25, 1, 3, 242, 176, + 134, 153, 42, 41, 136, 135, 154, 2, 130, 46, 41, 161, 153, 180, 145, 34, 26, 46, 18, 242, 137, 146, 129, 25, 128, 11, 151, 161, 40, 179, 27, 122, 168, 59, 137, 181, 50, 172, 36, 56, 15, 9, 129, 137, 128, 75, 2, 58, 12, 52, 141, 8, 24, 58, 153, 157, 122, 145, 9, 1, 80, 27, 184, 32, 74, 219, 50, 57, 168, 153, 180, 48, 28, 143, + 131, 144, 178, 65, 13, 48, 168, 162, 147, 155, 121, 9, 170, 5, 16, 153, 21, 29, 144, 161, 91, 0, 184, 57, 128, 137, 17, 159, 88, 178, 128, 105, 152, 9, 162, 33, 164, 141, 88, 178, 224, 1, 0, 16, 27, 185, 150, 161, 9, 4, 139, 16, 128, 160, 194, 144, 65, 180, 46, 40, 136, 27, 135, 160, 16, 44, 57, 145, 236, 2, 195, 40, 75, 177, + 2, 200, 179, 146, 186, 104, 50, 141, 24, 169, 165, 148, 11, 97, 10, 11, 130, 177, 49, 57, 78, 42, 154, 128, 165, 59, 33, 28, 30, 1, 136, 16, 192, 41, 128, 152, 123, 136, 24, 1, 169, 113, 10, 11, 49, 153, 14, 147, 19, 45, 43, 8, 176, 210, 148, 8, 16, 11, 96, 144, 192, 163, 150, 10, 128, 43, 26, 150, 178, 165, 24, 41, 171, 18, + 27, 215, 1, 8, 128, 136, 40, 35, 208, 11, 161, 193, 18, 73, 154, 133, 155, 165, 164, 10, 49, 154, 8, 199, 0, 2, 168, 64, 192, 0, 40, 162, 43, 202, 180, 150, 10, 106, 24, 185, 145, 131, 184, 113, 43, 24, 162, 187, 73, 146, 42, 81, 171, 121, 58, 155, 151, 16, 43, 32, 31, 9, 160, 146, 17, 136, 94, 10, 24, 145, 25, 9, 130, 59, + 65, 13, 91, 25, 169, 146, 176, 112, 42, 59, 16, 217, 130, 20, 13, 25, 9, 40, 161, 138, 68, 169, 154, 18, 62, 154, 180, 145, 135, 152, 56, 58, 155, 165, 211, 8, 40, 42, 10, 198, 1, 2, 184, 57, 184, 224, 51, 154, 27, 134, 168, 19, 202, 73, 75, 184, 35, 176, 75, 24, 25, 209, 51, 157, 19, 30, 184, 179, 3, 33, 148, 45, 232, 146, + 129, 168, 41, 32, 170, 149, 193, 35, 136, 16, 50, 191, 56, 146, 173, 149, 16, 24, 41, 30, 129, 168, 209, 3, 57, 31, 0, 16, 176, 147, 41, 152, 10, 17, 181, 14, 40, 144, 49, 170, 75, 97, 141, 25, 162, 146, 72, 177, 92, 137, 137, 19, 137, 153, 113, 154, 2, 41, 60, 129, 217, 2, 211, 152, 73, 42, 193, 197, 146, 147, 10, 59, 0, + 192, 196, 132, 41, 160, 25, 88, 169, 16, 40, 241, 1, 153, 81, 28, 10, 147, 161, 209, 88, 75, 9, 161, 162, 180, 16, 43, 57, 235, 33, 56, 156, 129, 144, 2, 135, 31, 128, 145, 136, 163, 56, 59, 154, 57, 167, 160, 105, 137, 0, 138, 163, 3, 41, 47, 185, 211, 131, 41, 41, 60, 139, 182, 146, 16, 16, 43, 242, 144, 145, 129, 16, 179, + 183, 1, 26, 9, 147, 240, 131, 160, 91, 74, 152, 184, 166, 178, 33, 140, 9, 4, 162, 233, 34, 136, 129, 144, 163, 60, 142, 144, 149, 128, 33, 73, 13, 161, 194, 131, 0, 26, 56, 142, 128, 163, 128, 1, 233, 56, 209, 41, 145, 194, 147, 179, 149, 64, 30, 8, 128, 216, 18, 24, 43, 43, 32, 153, 25, 74, 109, 137, 153, 48, 8, 137, 122, + 25, 144, 26, 43, 59, 30, 33, 41, 27, 24, 96, 153, 160, 50, 76, 27, 47, 152, 145, 163, 73, 40, 14, 152, 131, 176, 74, 90, 8, 8, 200, 67, 155, 154, 50, 49, 155, 28, 124, 177, 152, 1, 2, 17, 62, 138, 180, 176, 4, 25, 9, 177, 245, 162, 129, 40, 25, 176, 164, 130, 172, 4, 8, 181, 194, 49, 11, 168, 154, 165, 133, 152, 40, 136, 226, + 179, 19, 26, 185, 16, 167, 194, 16, 25, 57, 243, 136, 147, 1, 31, 25, 184, 132, 160, 33, 62, 138, 129, 130, 41, 121, 137, 153, 145, 26, 17, 107, 136, 179, 1, 61, 60, 26, 162, 168, 148, 64, 31, 25, 32, 168, 152, 64, 31, 137, 8, 129, 33, 62, 24, 137, 8, 16, 59, 47, 153, 33, 162, 91, 59, 41, 170, 145, 5, 43, 60, 41, 13, 178, 134, + 57, 153, 12, 194, 227, 8, 2, 128, 57, 208, 162, 19, 216, 32, 178, 25, 128, 160, 48, 194, 195, 37, 155, 10, 33, 251, 163, 146, 16, 136, 12, 166, 195, 160, 148, 129, 176, 147, 178, 150, 160, 72, 162, 162, 193, 162, 60, 200, 145, 5, 144, 25, 122, 216, 129, 161, 130, 0, 10, 73, 1, 241, 2, 9, 168, 33, 13, 161, 165, 24, 64, 203, + 50, 1, 14, 9, 9, 129, 161, 106, 33, 27, 13, 164, 128, 40, 41, 107, 169, 160, 33, 136, 60, 92, 168, 152, 2, 91, 57, 176, 129, 0, 144, 47, 136, 162, 164, 128, 80, 43, 154, 179, 213, 130, 74, 27, 0, 145, 145, 167, 58, 59, 160, 9, 26, 76, 8, 171, 5, 49, 28, 44, 169, 162, 183, 130, 72, 28, 144, 179, 228, 2, 25, 26, 129, 186, 151, + 1, 75, 128, 169, 17, 178, 15, 57, 170, 16, 166, 16, 57, 8, 139, 162, 181, 1, 8, 152, 164, 181, 41, 81, 43, 10, 242, 145, 57, 139, 89, 8, 193, 18, 154, 32, 176, 10, 165, 129, 137, 147, 177, 134, 0, 25, 25, 201, 147, 227, 129, 72, 59, 185, 167, 128, 129, 160, 91, 25, 176, 130, 147, 145, 9, 160, 5, 202, 17, 16, 186, 136, 37, + 177, 56, 76, 42, 169, 186, 48, 9, 145, 57, 24, 128, 41, 169, 134, 137, 145, 147, 28, 41, 168, 131, 228, 32, 27, 9, 60, 129, 178, 64, 60, 45, 25, 9, 24, 152, 49, 31, 136, 57, 42, 0, 25, 12, 181, 18, 153, 57, 96, 169, 177, 132, 153, 123, 9, 152, 129, 177, 17, 74, 43, 24, 169, 128, 121, 137, 25, 1, 139, 96, 42, 10, 146, 178, 18, + 44, 29, 1, 161, 164, 146, 31, 137, 146, 177, 19, 1, 10, 26, 209, 165, 146, 43, 40, 138, 240, 130, 18, 144, 25, 40, 212, 1, 58, 11, 152, 196, 147, 10, 74, 26, 152, 225, 130, 146, 58, 60, 210, 145, 16, 148, 16, 185, 192, 18, 44, 42, 57, 199, 162, 1, 9, 87, 47, 186, 215, 231, 197, 179, 180, 195, 212, 164, 32, 59, 92, 126, 62, + 41, 59, 76, 59, 60, 168, 179, 213, 197, 163, 72, 44, 25, 74, 126, 127, 127, 79, 26, 177, 148, 90, 27, 225, 247, 165, 0, 152, 147, 123, 138, 211, 164, 72, 126, 127, 46, 210, 196, 163, 228, 215, 64, 11, 210, 180, 1, 8, 58, 153, 1, 224, 149, 57, 76, 27, 24, 76, 42, 43, 136, 128, 243, 179, 130, 106, 60, 42, 42, 92, 28, 243, 231, + 147, 24, 57, 44, 58, 94, 45, 8, 57, 139, 214, 148, 40, 77, 26, 9, 16, 10, 144, 64, 62, 43, 25, 123, 59, 138, 162, 48, 63, 26, 41, 92, 60, 43, 176, 3, 59, 232, 214, 164, 16, 75, 75, 76, 60, 153, 179, 33, 62, 26, 136, 40, 75, 169, 197, 163, 129, 57, 60, 59, 75, 138, 145, 64, 63, 138, 179, 1, 42, 136, 90, 43, 176, 214, 180, 1, 25, + 152, 195, 129, 129, 106, 76, 60, 137, 145, 178, 2, 25, 10, 228, 130, 57, 59, 44, 41, 154, 165, 105, 76, 44, 144, 16, 76, 26, 41, 76, 26, 152, 1, 58, 26, 9, 193, 165, 16, 92, 26, 41, 77, 59, 76, 76, 60, 26, 136, 161, 130, 152, 195, 163, 211, 146, 0, 57, 11, 211, 130, 8, 25, 40, 62, 153, 162, 17, 109, 60, 153, 146, 40, 76, 60, + 26, 160, 179, 211, 163, 32, 60, 42, 153, 179, 194, 199, 130, 24, 58, 43, 58, 27, 128, 161, 195, 129, 226, 196, 147, 90, 59, 75, 44, 136, 128, 145, 160, 148, 123, 59, 42, 26, 41, 26, 57, 27, 192, 215, 147, 57, 59, 27, 161, 145, 213, 130, 106, 76, 43, 9, 144, 162, 129, 177, 181, 130, 136, 194, 146, 40, 10, 129, 25, 210, 146, + 178, 197, 196, 179, 196, 130, 8, 41, 9, 144, 178, 130, 209, 182, 17, 92, 43, 176, 147, 144, 212, 130, 136, 0, 177, 130, 73, 62, 10, 161, 130, 91, 75, 59, 43, 57, 46, 25, 41, 77, 10, 177, 164, 16, 26, 136, 210, 197, 179, 130, 128, 57, 77, 43, 25, 75, 10, 227, 179, 180, 179, 146, 128, 57, 185, 183, 163, 145, 0, 8, 8, 10, 119, + 114, 120, 16, 210, 244, 60, 28, 41, 25, 152, 149, 56, 161, 35, 44, 89, 27, 24, 136, 24, 164, 211, 17, 233, 176, 136, 192, 129, 179, 17, 17, 25, 0, 10, 46, 160, 132, 49, 66, 24, 132, 177, 147, 193, 56, 72, 26, 29, 232, 168, 176, 12, 137, 41, 139, 147, 9, 1, 41, 15, 91, 136, 35, 148, 21, 18, 48, 40, 1, 168, 167, 144, 0, 42, 172, + 177, 204, 193, 155, 232, 152, 152, 26, 152, 41, 146, 17, 6, 4, 65, 34, 35, 135, 4, 16, 32, 9, 24, 186, 176, 0, 250, 153, 204, 186, 173, 154, 153, 177, 3, 65, 41, 34, 145, 134, 35, 65, 98, 49, 50, 50, 2, 33, 169, 138, 155, 175, 170, 172, 204, 192, 138, 234, 136, 155, 136, 10, 32, 18, 5, 52, 48, 24, 162, 17, 67, 54, 66, 51, 34, + 131, 184, 174, 234, 153, 10, 9, 40, 0, 152, 251, 168, 142, 154, 9, 16, 33, 49, 33, 128, 154, 170, 156, 34, 54, 54, 33, 68, 0, 1, 136, 201, 137, 26, 88, 48, 35, 99, 8, 152, 189, 189, 187, 155, 171, 16, 24, 130, 145, 188, 175, 203, 144, 49, 115, 67, 67, 50, 19, 2, 1, 0, 0, 130, 131, 1, 136, 206, 216, 188, 203, 204, 187, 187, + 156, 153, 0, 0, 51, 17, 34, 24, 112, 20, 69, 67, 67, 34, 19, 0, 136, 169, 185, 137, 186, 232, 185, 219, 201, 203, 187, 173, 170, 154, 153, 129, 131, 6, 2, 19, 49, 49, 21, 65, 19, 53, 51, 83, 34, 16, 168, 201, 154, 172, 156, 138, 0, 1, 24, 201, 233, 186, 204, 186, 171, 137, 3, 37, 48, 24, 128, 201, 202, 202, 129, 17, 48, 21, + 22, 20, 19, 19, 32, 16, 2, 66, 52, 68, 4, 3, 1, 203, 235, 188, 189, 186, 171, 153, 137, 153, 170, 219, 170, 140, 9, 17, 53, 115, 50, 52, 67, 51, 51, 51, 17, 130, 0, 145, 154, 169, 188, 236, 187, 190, 203, 187, 172, 171, 138, 136, 17, 33, 18, 2, 34, 98, 98, 50, 50, 52, 66, 34, 35, 2, 19, 24, 169, 203, 203, 188, 219, 169, 154, + 9, 137, 171, 204, 188, 203, 184, 136, 34, 83, 50, 33, 153, 184, 170, 170, 152, 40, 57, 19, 36, 50, 50, 18, 35, 17, 2, 49, 49, 66, 66, 66, 34, 17, 168, 233, 202, 202, 170, 171, 170, 186, 219, 203, 188, 188, 154, 138, 25, 33, 68, 52, 68, 67, 67, 36, 51, 36, 18, 17, 17, 136, 8, 170, 176, 202, 188, 206, 202, 171, 172, 186, 169, + 153, 8, 25, 144, 128, 1, 34, 68, 52, 68, 51, 52, 34, 49, 18, 34, 2, 144, 136, 155, 140, 187, 186, 186, 154, 154, 185, 185, 153, 9, 9, 0, 24, 0, 128, 144, 168, 169, 170, 154, 154, 153, 9, 8, 16, 8, 0, 144, 19, 35, 68, 51, 52, 67, 51, 66, 34, 50, 33, 1, 144, 185, 186, 172, 204, 187, 188, 173, 172, 186, 172, 186, 154, 138, 41, + 33, 52, 53, 83, 50, 51, 52, 52, 37, 34, 34, 18, 16, 144, 152, 154, 187, 219, 203, 188, 173, 186, 186, 186, 170, 154, 153, 138, 144, 16, 17, 67, 82, 50, 51, 21, 34, 19, 33, 2, 18, 33, 1, 8, 153, 169, 153, 153, 136, 128, 0, 136, 154, 153, 153, 8, 8, 1, 16, 0, 169, 170, 187, 171, 171, 154, 153, 153, 152, 153, 153, 0, 16, 51, 83, + 66, 50, 67, 50, 51, 67, 51, 52, 35, 18, 136, 186, 219, 187, 189, 186, 171, 187, 173, 187, 188, 187, 203, 138, 9, 16, 33, 50, 52, 53, 67, 67, 147, 8, 128, 128, 128, 128, 128, 128, 128, 128, 0, 240, 255, 55, 232, 23, 220, 0, 148, 1, 9, 18, 148, 10, 189, 32, 163, 62, 160, 5, 137, 12, 149, 42, 153, 144, 34, 42, 8, 1, 138, 181, + 45, 136, 18, 144, 105, 138, 1, 160, 14, 128, 132, 145, 186, 37, 138, 41, 192, 48, 145, 46, 160, 33, 44, 24, 225, 16, 13, 132, 136, 137, 16, 148, 25, 170, 194, 82, 152, 136, 91, 24, 42, 169, 33, 233, 131, 179, 24, 185, 149, 16, 57, 172, 164, 18, 10, 211, 160, 147, 211, 33, 138, 243, 129, 16, 41, 193, 0, 43, 132, 155, 73, + 58, 145, 244, 145, 43, 35, 9, 171, 16, 110, 25, 8, 28, 74, 162, 128, 26, 27, 82, 45, 136, 153, 18, 8, 136, 8 +}; + +TownsPC98_FmSynth::ChanInternal::ChanInternal() { + memset(this, 0, sizeof(ChanInternal)); +} + +TownsPC98_FmSynth::ChanInternal::~ChanInternal() { + for (uint i = 0; i < ARRAYSIZE(opr); ++i) + delete opr[i]; +} diff --git a/sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h b/sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h new file mode 100644 index 0000000000..3072503610 --- /dev/null +++ b/sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h @@ -0,0 +1,177 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef TOWNS_PC98_FMSYNTH_H +#define TOWNS_PC98_FMSYNTH_H + +#include "sound/audiostream.h" +#include "sound/mixer.h" +#include "common/list.h" + +class TownsPC98_FmSynthOperator; +class TownsPC98_FmSynthSquareSineSource; +class TownsPC98_FmSynthPercussionSource; + +enum EnvelopeState { + kEnvReady, + kEnvAttacking, + kEnvDecaying, + kEnvSustaining, + kEnvReleasing +}; + +class TownsPC98_FmSynth : public Audio::AudioStream { +public: + enum EmuType { + kTypeTowns, + kType26, + kType86 + }; + + TownsPC98_FmSynth(Audio::Mixer *mixer, EmuType type); + virtual ~TownsPC98_FmSynth(); + + virtual bool init(); + virtual void reset(); + + void writeReg(uint8 part, uint8 regAddress, uint8 value); + + // AudioStream interface + int readBuffer(int16 *buffer, const int numSamples); + bool isStereo() const { + return true; + } + bool endOfData() const { + return false; + } + int getRate() const { + return _mixer->getOutputRate(); + } + +protected: + // Implement this in your inherited class if your driver generates + // additional output that has to be inserted into the buffer. + virtual void nextTickEx(int32 *buffer, uint32 bufferSize) {} + + void toggleRegProtection(bool prot) { + _regProtectionFlag = prot; + } + uint8 readSSGStatus(); + + virtual void timerCallbackA() = 0; + virtual void timerCallbackB() = 0; + + // The audio driver can store and apply two different audio settings + // (usually for music and sound effects). The channel mask will determine + // which channels get effected by the setting. The first bits will be + // the normal fm channels, the next bits the ssg channels and the final + // bit the rhythm channel. + void setVolumeIntern(int volA, int volB); + void setVolumeChannelMasks(int channelMaskA, int channelMaskB); + + const int _numChan; + const int _numSSG; + const bool _hasPercussion; + + Common::Mutex _mutex; +private: + void generateTables(); + void nextTick(int32 *buffer, uint32 bufferSize); + void generateOutput(int32 &leftSample, int32 &rightSample, int32 *del, int32 *feed); + + struct ChanInternal { + ChanInternal(); + ~ChanInternal(); + + void ampModSensitivity(uint32 value) { + ampModSvty = (1 << (3 - value)) - (((value >> 1) & 1) | (value & 1)); + } + void frqModSensitivity(uint32 value) { + frqModSvty = value << 5; + } + + uint16 frqTemp; + bool enableLeft; + bool enableRight; + bool updateEnvelopeParameters; + int32 feedbuf[3]; + uint8 algorithm; + + uint32 ampModSvty; + uint32 frqModSvty; + + TownsPC98_FmSynthOperator *opr[4]; + }; + + TownsPC98_FmSynthSquareSineSource *_ssg; + TownsPC98_FmSynthPercussionSource *_prc; + ChanInternal *_chanInternal; + + uint8 *_oprRates; + uint8 *_oprRateshift; + uint8 *_oprAttackDecay; + uint32 *_oprFrq; + uint32 *_oprSinTbl; + int32 *_oprLevelOut; + int32 *_oprDetune; + + bool _regProtectionFlag; + + typedef void (TownsPC98_FmSynth::*ChipTimerProc)(); + + struct ChipTimer { + bool enabled; + uint16 value; + + int32 smpTillCb; + uint32 smpTillCbRem; + int32 smpPerCb; + uint32 smpPerCbRem; + + ChipTimerProc cb; + }; + + ChipTimer _timers[2]; + + int _volMaskA, _volMaskB; + uint16 _volumeA, _volumeB; + + const float _baserate; + uint32 _timerbase; + uint32 _rtt; + + Audio::Mixer *_mixer; + Audio::SoundHandle _soundHandle; + + static const uint8 _percussionData[]; + static const uint32 _adtStat[]; + static const uint8 _detSrc[]; + static const int _ssgTables[]; + + bool _ready; +}; + +#endif + diff --git a/sound/softsynth/mt32.cpp b/sound/softsynth/mt32.cpp index 612dce06b0..54366a4300 100644 --- a/sound/softsynth/mt32.cpp +++ b/sound/softsynth/mt32.cpp @@ -39,6 +39,7 @@ #include "common/system.h" #include "common/util.h" #include "common/archive.h" +#include "common/translation.h" #include "graphics/fontman.h" #include "graphics/surface.h" @@ -148,7 +149,7 @@ static void drawProgress(float progress) { Common::Rect r(x, y, x + w, y + h); uint32 col; - + if (screenFormat.bytesPerPixel > 1) col = screenFormat.RGBToColor(0, 171, 0); else @@ -183,7 +184,7 @@ static void drawMessage(int offset, const Common::String &text) { uint16 y = g_system->getHeight() / 2 - h / 2 + offset * (h + 1); uint32 col; - + if (screenFormat.bytesPerPixel > 1) col = screenFormat.RGBToColor(0, 0, 0); else @@ -323,7 +324,7 @@ int MidiDriver_MT32::open() { } _initialising = true; - drawMessage(-1, "Initialising MT-32 Emulator"); + drawMessage(-1, _s("Initialising MT-32 Emulator")); if (!_synth->open(prop)) return MERR_DEVICE_NOT_AVAILABLE; _initialising = false; @@ -537,7 +538,7 @@ void MidiDriver_ThreadedMT32::onTimer() { class MT32EmuMusicPlugin : public MusicPluginObject { public: const char *getName() const { - return "MT-32 Emulator"; + return _s("MT-32 Emulator"); } const char *getId() const { @@ -545,7 +546,7 @@ public: } MusicDevices getDevices() const; - Common::Error createInstance(MidiDriver **mididriver) const; + Common::Error createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle = 0) const; }; MusicDevices MT32EmuMusicPlugin::getDevices() const { @@ -554,23 +555,13 @@ MusicDevices MT32EmuMusicPlugin::getDevices() const { return devices; } -Common::Error MT32EmuMusicPlugin::createInstance(MidiDriver **mididriver) const { - *mididriver = new MidiDriver_MT32(g_system->getMixer()); - - return Common::kNoError; -} - -MidiDriver *MidiDriver_MT32_create() { - // HACK: It will stay here until engine plugin loader overhaul +Common::Error MT32EmuMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const { if (ConfMan.hasKey("extrapath")) SearchMan.addDirectory("extrapath", ConfMan.get("extrapath")); - MidiDriver *mididriver; - - MT32EmuMusicPlugin p; - p.createInstance(&mididriver); + *mididriver = new MidiDriver_MT32(g_system->getMixer()); - return mididriver; + return Common::kNoError; } //#if PLUGIN_ENABLED_DYNAMIC(MT32) diff --git a/sound/softsynth/opl/dbopl.cpp b/sound/softsynth/opl/dbopl.cpp index db07eaf8cc..857ed78436 100644 --- a/sound/softsynth/opl/dbopl.cpp +++ b/sound/softsynth/opl/dbopl.cpp @@ -418,7 +418,7 @@ Bits Operator::TemplateVolume( ) { } //In sustain phase, but not sustaining, do regular release case RELEASE: - vol += RateForward( releaseAdd );; + vol += RateForward( releaseAdd ); if ( GCC_UNLIKELY(vol >= ENV_MAX) ) { volume = ENV_MAX; SetState( OFF ); diff --git a/sound/softsynth/opl/mame.cpp b/sound/softsynth/opl/mame.cpp index 9e7cbfe3dc..f6da659918 100644 --- a/sound/softsynth/opl/mame.cpp +++ b/sound/softsynth/opl/mame.cpp @@ -33,7 +33,7 @@ #include "mame.h" -#if defined (_WIN32_WCE) || defined (__SYMBIAN32__) || defined(PALMOS_MODE) || defined(__GP32__) || defined(GP2X) || defined (__MAEMO__) || defined(__DS__) || defined (__MINT__) || defined(__N64__) +#if defined (_WIN32_WCE) || defined (__SYMBIAN32__) || defined(__GP32__) || defined(GP2X) || defined (__MAEMO__) || defined(__DS__) || defined (__MINT__) || defined(__N64__) #include "common/config-manager.h" #endif @@ -1212,7 +1212,7 @@ FM_OPL *makeAdLibOPL(int rate) { // We need to emulate one YM3812 chip int env_bits = FMOPL_ENV_BITS_HQ; int eg_ent = FMOPL_EG_ENT_HQ; -#if defined (_WIN32_WCE) || defined(__SYMBIAN32__) || defined(PALMOS_MODE) || defined(__GP32__) || defined (GP2X) || defined(__MAEMO__) || defined(__DS__) || defined (__MINT__) || defined(__N64__) +#if defined (_WIN32_WCE) || defined(__SYMBIAN32__) || defined(__GP32__) || defined (GP2X) || defined(__MAEMO__) || defined(__DS__) || defined (__MINT__) || defined(__N64__) if (ConfMan.hasKey("FM_high_quality") && ConfMan.getBool("FM_high_quality")) { env_bits = FMOPL_ENV_BITS_HQ; eg_ent = FMOPL_EG_ENT_HQ; diff --git a/sound/softsynth/pcspk.cpp b/sound/softsynth/pcspk.cpp index 396d9328f1..bdf9f112e8 100644 --- a/sound/softsynth/pcspk.cpp +++ b/sound/softsynth/pcspk.cpp @@ -24,6 +24,7 @@ */ #include "sound/softsynth/pcspk.h" +#include "sound/null.h" namespace Audio { @@ -128,3 +129,59 @@ int8 PCSpeaker::generateTriangle(uint32 x, uint32 oscLength) { } } // End of namespace Audio + + +// Plugin interface +// (This can only create a null driver since pc speaker support is not part of the +// midi driver architecture. But we need the plugin for the options menu in the launcher +// and for MidiDriver::detectDevice() which is more or less used by all engines.) + +class PCSpeakerMusicPlugin : public NullMusicPlugin { +public: + const char *getName() const { + return _s("PC Speaker Emulator"); + } + + const char *getId() const { + return "pcspk"; + } + + MusicDevices getDevices() const; +}; + +MusicDevices PCSpeakerMusicPlugin::getDevices() const { + MusicDevices devices; + devices.push_back(MusicDevice(this, "", MT_PCSPK)); + return devices; +} + +class PCjrMusicPlugin : public NullMusicPlugin { +public: + const char *getName() const { + return _s("IBM PCjr Emulator"); + } + + const char *getId() const { + return "pcjr"; + } + + MusicDevices getDevices() const; +}; + +MusicDevices PCjrMusicPlugin::getDevices() const { + MusicDevices devices; + devices.push_back(MusicDevice(this, "", MT_PCJR)); + return devices; +} + +//#if PLUGIN_ENABLED_DYNAMIC(PCSPK) + //REGISTER_PLUGIN_DYNAMIC(PCSPK, PLUGIN_TYPE_MUSIC, PCSpeakerMusicPlugin); +//#else + REGISTER_PLUGIN_STATIC(PCSPK, PLUGIN_TYPE_MUSIC, PCSpeakerMusicPlugin); +//#endif + +//#if PLUGIN_ENABLED_DYNAMIC(PCJR) + //REGISTER_PLUGIN_DYNAMIC(PCJR, PLUGIN_TYPE_MUSIC, PCjrMusicPlugin); +//#else + REGISTER_PLUGIN_STATIC(PCJR, PLUGIN_TYPE_MUSIC, PCjrMusicPlugin); +//#endif diff --git a/sound/softsynth/sid.cpp b/sound/softsynth/sid.cpp index e0dfd2efc0..e925f4a447 100644 --- a/sound/softsynth/sid.cpp +++ b/sound/softsynth/sid.cpp @@ -46,11 +46,6 @@ const int SID::FIXP_MASK = 0xffff; WaveformGenerator::WaveformGenerator() { sync_source = this; - wave__ST = wave6581__ST; - wave_P_T = wave6581_P_T; - wave_PS_ = wave6581_PS_; - wave_PST = wave6581_PST; - reset(); } @@ -226,19 +221,19 @@ RESID_INLINE reg12 WaveformGenerator::outputN___() { // Combined waveforms: RESID_INLINE reg12 WaveformGenerator::output__ST() { - return wave__ST[output__S_()] << 4; + return wave6581__ST[output__S_()] << 4; } RESID_INLINE reg12 WaveformGenerator::output_P_T() { - return (wave_P_T[output___T() >> 1] << 4) & output_P__(); + return (wave6581_P_T[output___T() >> 1] << 4) & output_P__(); } RESID_INLINE reg12 WaveformGenerator::output_PS_() { - return (wave_PS_[output__S_()] << 4) & output_P__(); + return (wave6581_PS_[output__S_()] << 4) & output_P__(); } RESID_INLINE reg12 WaveformGenerator::output_PST() { - return (wave_PST[output__S_()] << 4) & output_P__(); + return (wave6581_PST[output__S_()] << 4) & output_P__(); } // Combined waveforms including noise: @@ -506,7 +501,7 @@ Filter::Filter() { + sizeof(f0_points_6581)/sizeof(*f0_points_6581) - 1, PointPlotter<sound_sample>(f0_6581), 1.0); - mixer_DC = -0xfff*0xff/18 >> 7; + mixer_DC = (-0xfff*0xff/18) >> 7; f0 = f0_6581; f0_points = f0_points_6581; diff --git a/sound/softsynth/sid.h b/sound/softsynth/sid.h index d57ec73bad..c78f538441 100644 --- a/sound/softsynth/sid.h +++ b/sound/softsynth/sid.h @@ -118,15 +118,10 @@ protected: reg12 outputNPST(); // Sample data for combinations of waveforms. - static reg8 wave6581__ST[]; - static reg8 wave6581_P_T[]; - static reg8 wave6581_PS_[]; - static reg8 wave6581_PST[]; - - reg8* wave__ST; - reg8* wave_P_T; - reg8* wave_PS_; - reg8* wave_PST; + static const reg8 wave6581__ST[]; + static const reg8 wave6581_P_T[]; + static const reg8 wave6581_PS_[]; + static const reg8 wave6581_PST[]; friend class Voice; friend class SID; diff --git a/sound/softsynth/wave6581.cpp b/sound/softsynth/wave6581.cpp index 29998dcd0a..d1ddad1623 100644 --- a/sound/softsynth/wave6581.cpp +++ b/sound/softsynth/wave6581.cpp @@ -32,11 +32,9 @@ #include "sid.h" -namespace Resid -{ +namespace Resid { -reg8 WaveformGenerator::wave6581__ST[] = -{ +const reg8 WaveformGenerator::wave6581__ST[] = { /* 0x000: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x008: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x010: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -551,8 +549,7 @@ reg8 WaveformGenerator::wave6581__ST[] = /* 0xff8: */ 0x3e, 0x3e, 0x3f, 0x3f, 0x7f, 0x7f, 0x7f, 0x7f, }; -reg8 WaveformGenerator::wave6581_P_T[] = -{ +const reg8 WaveformGenerator::wave6581_P_T[] = { /* 0x000: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x008: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x010: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1067,8 +1064,7 @@ reg8 WaveformGenerator::wave6581_P_T[] = /* 0xff8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; -reg8 WaveformGenerator::wave6581_PS_[] = -{ +const reg8 WaveformGenerator::wave6581_PS_[] = { /* 0x000: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x008: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x010: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1583,8 +1579,7 @@ reg8 WaveformGenerator::wave6581_PS_[] = /* 0xff8: */ 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, }; -reg8 WaveformGenerator::wave6581_PST[] = -{ +const reg8 WaveformGenerator::wave6581_PST[] = { /* 0x000: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x008: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x010: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/sound/softsynth/ym2612.cpp b/sound/softsynth/ym2612.cpp index e337bc4ab9..08331c6244 100644 --- a/sound/softsynth/ym2612.cpp +++ b/sound/softsynth/ym2612.cpp @@ -27,6 +27,7 @@ #include "sound/softsynth/ym2612.h" #include "common/util.h" #include "sound/musicplugin.h" +#include "common/translation.h" //////////////////////////////////////// // @@ -758,7 +759,7 @@ void MidiDriver_YM2612::removeLookupTables() { class TownsEmuMusicPlugin : public MusicPluginObject { public: const char *getName() const { - return "FM Towns Emulator"; + return _s("FM Towns Emulator"); } const char *getId() const { @@ -766,7 +767,7 @@ public: } MusicDevices getDevices() const; - Common::Error createInstance(MidiDriver **mididriver) const; + Common::Error createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle = 0) const; }; MusicDevices TownsEmuMusicPlugin::getDevices() const { @@ -775,21 +776,12 @@ MusicDevices TownsEmuMusicPlugin::getDevices() const { return devices; } -Common::Error TownsEmuMusicPlugin::createInstance(MidiDriver **mididriver) const { +Common::Error TownsEmuMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const { *mididriver = new MidiDriver_YM2612(g_system->getMixer()); return Common::kNoError; } -MidiDriver *MidiDriver_YM2612_create() { - MidiDriver *mididriver; - - TownsEmuMusicPlugin p; - p.createInstance(&mididriver); - - return mididriver; -} - //#if PLUGIN_ENABLED_DYNAMIC(TOWNS) //REGISTER_PLUGIN_DYNAMIC(TOWNS, PLUGIN_TYPE_MUSIC, TownsEmuMusicPlugin); //#else |