From 20b378a41a194bd0869bfc2bb16b9a6897bd8bd2 Mon Sep 17 00:00:00 2001 From: athrxx Date: Tue, 4 Dec 2018 17:49:15 +0100 Subject: AUDIO: (FM-TOWNS/PC-98) - cleanup Apart from some basic cleanup this commit reverts a somewhat unfortunate design decision I made. The Kyra/Hof/Lol PC-98 sound drivers shouldn't inherit from the emulator. This commit separates the driver from the emulator putting some common interface in between. This should allow easier implementation of other PC-98 sound drivers. --- audio/module.mk | 1 + audio/softsynth/fmtowns_pc98/pc98_audio.cpp | 290 +++++++++++++++++++++ audio/softsynth/fmtowns_pc98/pc98_audio.h | 84 ++++++ audio/softsynth/fmtowns_pc98/towns_pc98_driver.cpp | 103 ++++---- audio/softsynth/fmtowns_pc98/towns_pc98_driver.h | 15 +- .../softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp | 79 ++++-- audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h | 9 +- 7 files changed, 504 insertions(+), 77 deletions(-) create mode 100644 audio/softsynth/fmtowns_pc98/pc98_audio.cpp create mode 100644 audio/softsynth/fmtowns_pc98/pc98_audio.h (limited to 'audio') diff --git a/audio/module.mk b/audio/module.mk index 0add7cf291..686c70be63 100644 --- a/audio/module.mk +++ b/audio/module.mk @@ -46,6 +46,7 @@ MODULE_OBJS := \ softsynth/opl/dbopl.o \ softsynth/opl/dosbox.o \ softsynth/opl/mame.o \ + softsynth/fmtowns_pc98/pc98_audio.o \ softsynth/fmtowns_pc98/towns_audio.o \ softsynth/fmtowns_pc98/towns_euphony.o \ softsynth/fmtowns_pc98/towns_midi.o \ diff --git a/audio/softsynth/fmtowns_pc98/pc98_audio.cpp b/audio/softsynth/fmtowns_pc98/pc98_audio.cpp new file mode 100644 index 0000000000..d6c6fc94e3 --- /dev/null +++ b/audio/softsynth/fmtowns_pc98/pc98_audio.cpp @@ -0,0 +1,290 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "audio/softsynth/fmtowns_pc98/pc98_audio.h" +#include "audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h" +#include "common/mutex.h" + +class PC98AudioCoreInternal : public TownsPC98_FmSynth { +private: + PC98AudioCoreInternal(Audio::Mixer *mixer, PC98AudioCore *owner, PC98AudioPluginDriver *driver, PC98AudioPluginDriver::EmuType type, bool externalMutexHandling = false); +public: + ~PC98AudioCoreInternal(); + + static PC98AudioCoreInternal *addNewRef(Audio::Mixer *mixer, PC98AudioCore *owner, PC98AudioPluginDriver *driver, PC98AudioPluginDriver::EmuType type, bool externalMutexHandling = false); + static void releaseRef(PC98AudioCore *owner); + + bool init(); + + void writePort(uint16 port, uint8 value); + uint8 readPort(uint16 port); + + 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 6 fm channels. The next 3 bits are ssg channels. The next bit is the rhythm channel. + void setSoundEffectChanMask(int mask); + + void ssgSetVolume(int volume); + + Common::Mutex &mutex(); + +private: + bool assignPluginDriver(PC98AudioCore *owner, PC98AudioPluginDriver *driver, bool externalMutexHandling = false); + void removePluginDriver(PC98AudioCore *owner); + + void timerCallbackA(); + void timerCallbackB(); + + uint16 _musicVolume; + uint16 _sfxVolume; + + const uint16 _port1, _port2, _port3, _port4; + uint8 _address[2]; + + uint16 _frequency; + + PC98AudioPluginDriver *_drv; + void *_drvOwner; + bool _ready; + + static PC98AudioCoreInternal *_refInstance; + static int _refCount; +}; + +PC98AudioCoreInternal::PC98AudioCoreInternal(Audio::Mixer *mixer, PC98AudioCore *owner, PC98AudioPluginDriver *driver, PC98AudioPluginDriver::EmuType type, bool externalMutexHandling) : + TownsPC98_FmSynth(mixer, (TownsPC98_FmSynth::EmuType)type, externalMutexHandling), + _drv(driver), _drvOwner(owner), + _musicVolume(Audio::Mixer::kMaxMixerVolume), _sfxVolume(Audio::Mixer::kMaxMixerVolume), + _port1(type == PC98AudioPluginDriver::kTypeTowns ? 0x4D8 : 0x188), _port2(type == PC98AudioPluginDriver::kTypeTowns ? 0x4DA : 0x18A), + _port3(type == PC98AudioPluginDriver::kTypeTowns ? 0x4DC : 0x18C), _port4(type == PC98AudioPluginDriver::kTypeTowns ? 0x4DE : 0x18E), + _frequency(0), _ready(false) { + _address[0] = _address[1] = 0xFF; +} + +PC98AudioCoreInternal::~PC98AudioCoreInternal() { + _ready = false; + deinit(); + + Common::StackLock lock(_mutex); + /* + + */ +} + +PC98AudioCoreInternal *PC98AudioCoreInternal::addNewRef(Audio::Mixer *mixer, PC98AudioCore *owner, PC98AudioPluginDriver *driver, PC98AudioPluginDriver::EmuType type, bool externalMutexHandling) { + _refCount++; + if (_refCount == 1 && _refInstance == 0) + _refInstance = new PC98AudioCoreInternal(mixer, owner, driver, type, externalMutexHandling); + else if (_refCount < 2 || _refInstance == 0) + error("PC98AudioCoreInternal::addNewRef(): Internal reference management failure"); + else if (!_refInstance->assignPluginDriver(owner, driver, externalMutexHandling)) + error("PC98AudioCoreInternal::addNewRef(): Plugin driver conflict"); + + return _refInstance; +} + +void PC98AudioCoreInternal::releaseRef(PC98AudioCore *owner) { + if (!_refCount) + return; + + _refCount--; + + if (_refCount) { + if (_refInstance) + _refInstance->removePluginDriver(owner); + } else { + delete _refInstance; + _refInstance = 0; + } +} + +bool PC98AudioCoreInternal::init() { + if (_ready) + return true; + + if (!TownsPC98_FmSynth::init()) + return false; + + reset(); + + writeReg(0, 0x26, 0xDD); + writeReg(0, 0x25, 0x01); + writeReg(0, 0x24, 0x00); + writeReg(0, 0x27, 0x30); + + setVolumeChannelMasks(-1, 0); + ssgSetVolume(0x60); + + _ready = true; + + return true; +} + +void PC98AudioCoreInternal::writePort(uint16 port, uint8 value) { + if (port == _port1) + _address[0] = value; + else if (port == _port2 && _address[0] < 0xc0) { + writeReg(0, _address[0], value); + _address[0] = 0xFF; + } else if (port == _port3) + _address[1] = value; + else if (port == _port4 && _address[1] < 0xc0) { + writeReg(1, _address[1], value); + _address[1] = 0xFF; + } +} + +uint8 PC98AudioCoreInternal::readPort(uint16 port) { + uint8 val = 0; + if (port == _port2 && _address[0] < 0xc0) { + val = readReg(0, _address[0]); + _address[0] = 0xFF; + } else if (port == _port4 && _address[1] < 0xc0) { + val = readReg(1, _address[1]); + _address[1] = 0xFF; + } + return val; +} + +void PC98AudioCoreInternal::setMusicVolume(int volume) { + _musicVolume = CLIP(volume, 0, Audio::Mixer::kMaxMixerVolume); + setVolumeIntern(_musicVolume, _sfxVolume); +} + +void PC98AudioCoreInternal::setSoundEffectVolume(int volume) { + _sfxVolume = CLIP(volume, 0, Audio::Mixer::kMaxMixerVolume); + setVolumeIntern(_musicVolume, _sfxVolume); +} + +void PC98AudioCoreInternal::setSoundEffectChanMask(int mask) { + setVolumeChannelMasks(~mask, mask); +} + +void PC98AudioCoreInternal::ssgSetVolume(int volume) { + setLevelSSG(volume); +} + +Common::Mutex &PC98AudioCoreInternal::mutex() { + return _mutex; +} + +bool PC98AudioCoreInternal::assignPluginDriver(PC98AudioCore *owner, PC98AudioPluginDriver *driver, bool externalMutexHandling) { + if (_refCount <= 1) + return true; + + if (_drv) { + if (driver && driver != _drv) + return false; + } else { + Common::StackLock lock(_mutex); + _drv = driver; + _drvOwner = owner; + _externalMutex = externalMutexHandling; + } + + return true; +} + +void PC98AudioCoreInternal::removePluginDriver(PC98AudioCore *owner) { + if (_drvOwner == owner) { + Common::StackLock lock(_mutex); + _drv = 0; + } +} + +void PC98AudioCoreInternal::timerCallbackA() { + if (_drv && _ready) + _drv->timerCallbackA(); +} + +void PC98AudioCoreInternal::timerCallbackB() { + if (_drv && _ready) + _drv->timerCallbackB(); +} + +PC98AudioCoreInternal *PC98AudioCoreInternal::_refInstance = 0; + +int PC98AudioCoreInternal::_refCount = 0; + +PC98AudioCore::PC98AudioCore(Audio::Mixer *mixer, PC98AudioPluginDriver *driver, PC98AudioPluginDriver::EmuType type, bool externalMutexHandling) { + _internal = PC98AudioCoreInternal::addNewRef(mixer, this, driver, type, externalMutexHandling); +} + +PC98AudioCore::~PC98AudioCore() { + PC98AudioCoreInternal::releaseRef(this); + _internal = 0; +} + +bool PC98AudioCore::init() { + return _internal->init(); +} + +void PC98AudioCore::reset() { + _internal->reset(); +} + +void PC98AudioCore::writeReg(uint8 part, uint8 regAddress, uint8 value) { + _internal->writeReg(part, regAddress, value); +} + +uint8 PC98AudioCore::readReg(uint8 part, uint8 regAddress) { + return _internal->readReg(part, regAddress); +} + +void PC98AudioCore::writePort(uint16 port, uint8 value) { + _internal->writePort(port, value); +} + +uint8 PC98AudioCore::readPort(uint16 port) { + return _internal->readPort(port); +} + +void PC98AudioCore::setMusicVolume(int volume) { + _internal->setMusicVolume(volume); +} + +void PC98AudioCore::setSoundEffectVolume(int volume) { + _internal->setSoundEffectVolume(volume); +} + +void PC98AudioCore::setSoundEffectChanMask(int mask) { + _internal->setSoundEffectChanMask(mask); +} + +void PC98AudioCore::ssgSetVolume(int volume) { + _internal->ssgSetVolume(volume); +} + +PC98AudioCore::MutexLock PC98AudioCore::stackLockMutex() { + return MutexLock(_internal); +} + +PC98AudioCore::MutexLock::MutexLock(PC98AudioCoreInternal *pc98int) : _pc98int(pc98int) { + if (_pc98int) + _pc98int->mutex().lock(); +} + +PC98AudioCore::MutexLock::~MutexLock() { + if (_pc98int) + _pc98int->mutex().unlock(); +} \ No newline at end of file diff --git a/audio/softsynth/fmtowns_pc98/pc98_audio.h b/audio/softsynth/fmtowns_pc98/pc98_audio.h new file mode 100644 index 0000000000..b6269f0f67 --- /dev/null +++ b/audio/softsynth/fmtowns_pc98/pc98_audio.h @@ -0,0 +1,84 @@ +/* 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. + * + */ + +#ifndef PC98_AUDIO_H +#define PC98_AUDIO_H + +#include "common/scummsys.h" + +namespace Audio { +class Mixer; +} + +class PC98AudioCoreInternal; +class PC98AudioPluginDriver { +public: + enum EmuType { + kTypeTowns, + kType26, + kType86 + }; + + virtual ~PC98AudioPluginDriver() {} + virtual void timerCallbackA() {} + virtual void timerCallbackB() {} +}; + +class PC98AudioCore { +public: + PC98AudioCore(Audio::Mixer *mixer, PC98AudioPluginDriver *driver, PC98AudioPluginDriver::EmuType type, bool externalMutexHandling = false); + ~PC98AudioCore(); + + bool init(); + void reset(); + + void writeReg(uint8 part, uint8 regAddress, uint8 value); + uint8 readReg(uint8 part, uint8 regAddress); + + void writePort(uint16 port, uint8 value); + uint8 readPort(uint16 port); + + 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 3 bits are ssg channels. The next bit is the rhythm channel. + void setSoundEffectChanMask(int mask); + + void ssgSetVolume(int volume); + + class MutexLock { + friend class PC98AudioCore; + public: + ~MutexLock(); + private: + MutexLock(PC98AudioCoreInternal *pc98int); + PC98AudioCoreInternal *_pc98int; + }; + + MutexLock stackLockMutex(); + +private: + PC98AudioCoreInternal *_internal; +}; + +#endif diff --git a/audio/softsynth/fmtowns_pc98/towns_pc98_driver.cpp b/audio/softsynth/fmtowns_pc98/towns_pc98_driver.cpp index 6a492787b2..091905b18e 100644 --- a/audio/softsynth/fmtowns_pc98/towns_pc98_driver.cpp +++ b/audio/softsynth/fmtowns_pc98/towns_pc98_driver.cpp @@ -615,7 +615,7 @@ void TownsPC98_MusicChannelSSG::processEvents() { if (_flags & CHS_EOT) return; - _drv->toggleRegProtection(_flags & CHS_PROTECT ? true : false); + _drv->preventRegisterWrite(_flags & CHS_PROTECT ? true : false); if (!_hold && _ticksLeft == _keyOffTime) nextShape(); @@ -750,7 +750,7 @@ void TownsPC98_MusicChannelSSG::keyOn() { if (!(_algorithm & 0x80)) _drv->writeReg(_part, 6, _algorithm & 0x7f); - uint8 e = (_drv->readSSGStatus() & c) | t; + uint8 e = (_drv->_pc98a->readReg(0, 7) & c) | t; _drv->writeReg(_part, 7, e); } @@ -768,7 +768,7 @@ void TownsPC98_MusicChannelSSG::restore() { } void TownsPC98_MusicChannelSSG::loadData(uint8 *data) { - _drv->toggleRegProtection(_flags & CHS_PROTECT ? true : false); + _drv->preventRegisterWrite(_flags & CHS_PROTECT ? true : false); TownsPC98_MusicChannel::loadData(data); setOutputLevel(0); _algorithm = 0x80; @@ -1017,7 +1017,7 @@ bool TownsPC98_MusicChannelPCM::control_ff_endOfTrack(uint8 para) { } #endif // DISABLE_PC98_RHYTHM_CHANNEL -TownsPC98_AudioDriver::TownsPC98_AudioDriver(Audio::Mixer *mixer, EmuType type) : TownsPC98_FmSynth(mixer, type), +TownsPC98_AudioDriver::TownsPC98_AudioDriver(Audio::Mixer *mixer, EmuType type) : _channels(0), _ssgChannels(0), _sfxChannels(0), #ifndef DISABLE_PC98_RHYTHM_CHANNEL _rhythmChannel(0), @@ -1036,32 +1036,27 @@ TownsPC98_AudioDriver::TownsPC98_AudioDriver(Audio::Mixer *mixer, EmuType type) #else 0x00 #endif - : 0x00), _finishedRhythmFlag(0), - _updateSfxFlag(0), _finishedSfxFlag(0), - - _musicTickCounter(0), - - _musicVolume(255), _sfxVolume(255), - + : 0x00), + _numChanFM(type == kType26 ? 3 : 6), _numChanSSG(type == kTypeTowns ? 0 : 3), _numChanRHY(type == kType86 ? 1 : 0), + _finishedRhythmFlag(0), _updateSfxFlag(0), _finishedSfxFlag(0), + _musicTickCounter(0), _regWriteProtect(false), _musicPlaying(false), _sfxPlaying(false), _fading(false), _looping(0), _ready(false) { - _sfxOffsets[0] = _sfxOffsets[1] = 0; + _pc98a = new PC98AudioCore(mixer, this, type); } TownsPC98_AudioDriver::~TownsPC98_AudioDriver() { _ready = false; - deinit(); - - Common::StackLock lock(_mutex); + delete _pc98a; if (_channels) { - for (int i = 0; i < _numChan; i++) + for (int i = 0; i < _numChanFM; i++) delete _channels[i]; delete[] _channels; } if (_ssgChannels) { - for (int i = 0; i < _numSSG; i++) + for (int i = 0; i < _numChanSSG; i++) delete _ssgChannels[i]; delete[] _ssgChannels; } @@ -1084,24 +1079,25 @@ bool TownsPC98_AudioDriver::init() { return true; } - TownsPC98_FmSynth::init(); + if (!_pc98a->init()) + return false; - setVolumeChannelMasks(-1, 0); + _pc98a->setSoundEffectChanMask(0); - _channels = new TownsPC98_MusicChannel *[_numChan]; - for (int i = 0; i < _numChan; i++) { + _channels = new TownsPC98_MusicChannel *[_numChanFM]; + for (int i = 0; i < _numChanFM; 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) { + if (_numChanSSG) { _ssgPatches = new uint8[256]; memcpy(_ssgPatches, _drvTables + 156, 256); - _ssgChannels = new TownsPC98_MusicChannelSSG *[_numSSG]; - for (int i = 0; i < _numSSG; i++) { + _ssgChannels = new TownsPC98_MusicChannelSSG *[_numChanSSG]; + for (int i = 0; i < _numChanSSG; 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]); @@ -1118,7 +1114,7 @@ bool TownsPC98_AudioDriver::init() { } #ifndef DISABLE_PC98_RHYTHM_CHANNEL - if (_hasPercussion) { + if (_numChanRHY) { _rhythmChannel = new TownsPC98_MusicChannelPCM(this, 0, 0, 0, 0, 0, 1); _rhythmChannel->init(); } @@ -1145,7 +1141,7 @@ void TownsPC98_AudioDriver::loadMusicData(uint8 *data, bool loadPaused) { reset(); - Common::StackLock lock(_mutex); + PC98AudioCore::MutexLock lock = _pc98a->stackLockMutex(); uint8 *src_a = _trackPtr = _musicBuffer = data; for (uint8 i = 0; i < 3; i++) { @@ -1153,24 +1149,24 @@ void TownsPC98_AudioDriver::loadMusicData(uint8 *data, bool loadPaused) { src_a += 2; } - for (int i = 0; i < _numSSG; i++) { + for (int i = 0; i < _numChanSSG; i++) { _ssgChannels[i]->loadData(data + READ_LE_UINT16(src_a)); src_a += 2; } - for (uint8 i = 3; i < _numChan; i++) { + for (uint8 i = 3; i < _numChanFM; i++) { _channels[i]->loadData(data + READ_LE_UINT16(src_a)); src_a += 2; } - if (_hasPercussion) { + if (_numChanRHY) { #ifndef DISABLE_PC98_RHYTHM_CHANNEL _rhythmChannel->loadData(data + READ_LE_UINT16(src_a)); #endif src_a += 2; } - toggleRegProtection(false); + preventRegisterWrite(false); _patches = src_a + 4; _finishedChannelsFlag = _finishedSSGFlag = _finishedRhythmFlag = 0; @@ -1194,7 +1190,7 @@ void TownsPC98_AudioDriver::loadSoundEffectData(uint8 *data, uint8 trackNum) { return; } - Common::StackLock lock(_mutex); + PC98AudioCore::MutexLock lock = _pc98a->stackLockMutex(); _sfxData = _sfxBuffer = data; _sfxOffsets[0] = READ_LE_UINT16(&_sfxData[(trackNum << 2)]); _sfxOffsets[1] = READ_LE_UINT16(&_sfxData[(trackNum << 2) + 2]); @@ -1203,7 +1199,7 @@ void TownsPC98_AudioDriver::loadSoundEffectData(uint8 *data, uint8 trackNum) { } void TownsPC98_AudioDriver::reset() { - Common::StackLock lock(_mutex); + PC98AudioCore::MutexLock lock = _pc98a->stackLockMutex(); _musicPlaying = false; _sfxPlaying = false; @@ -1212,14 +1208,14 @@ void TownsPC98_AudioDriver::reset() { _musicTickCounter = 0; _sfxData = 0; - TownsPC98_FmSynth::reset(); + _pc98a->reset(); - for (int i = 0; i < _numChan; i++) + for (int i = 0; i < _numChanFM; i++) _channels[i]->reset(); - for (int i = 0; i < _numSSG; i++) + for (int i = 0; i < _numChanSSG; i++) _ssgChannels[i]->reset(); - if (_numSSG) { + if (_numChanSSG) { for (int i = 0; i < 2; i++) _sfxChannels[i]->reset(); @@ -1236,12 +1232,12 @@ void TownsPC98_AudioDriver::fadeStep() { if (!_musicPlaying) return; - for (int j = 0; j < _numChan; j++) { + for (int j = 0; j < _numChanFM; j++) { if (_updateChannelsFlag & _channels[j]->_idFlag) _channels[j]->fadeStep(); } - for (int j = 0; j < _numSSG; j++) { + for (int j = 0; j < _numChanSSG; j++) { if (_updateSSGFlag & _ssgChannels[j]->_idFlag) _ssgChannels[j]->fadeStep(); } @@ -1249,7 +1245,7 @@ void TownsPC98_AudioDriver::fadeStep() { if (!_fading) { _fading = 19; #ifndef DISABLE_PC98_RHYTHM_CHANNEL - if (_hasPercussion) { + if (_numChanRHY) { if (_updateRhythmFlag & _rhythmChannel->_idFlag) _rhythmChannel->reset(); } @@ -1277,13 +1273,20 @@ bool TownsPC98_AudioDriver::musicPlaying() { } void TownsPC98_AudioDriver::setMusicVolume(int volume) { - _musicVolume = volume; - setVolumeIntern(_musicVolume, _sfxVolume); + _pc98a->setMusicVolume(volume); } void TownsPC98_AudioDriver::setSoundEffectVolume(int volume) { - _sfxVolume = volume; - setVolumeIntern(_musicVolume, _sfxVolume); + _pc98a->setSoundEffectVolume(volume); +} + +void TownsPC98_AudioDriver::writeReg(uint8 part, uint8 reg, uint8 val) { + if (!_regWriteProtect) + _pc98a->writeReg(part, reg, val); +} + +void TownsPC98_AudioDriver::preventRegisterWrite(bool prevent) { + _regWriteProtect = prevent; } void TownsPC98_AudioDriver::timerCallbackA() { @@ -1307,7 +1310,7 @@ void TownsPC98_AudioDriver::timerCallbackA() { if (_updateSfxFlag && _finishedSfxFlag == _updateSfxFlag) { _sfxPlaying = false; _updateSfxFlag = 0; - setVolumeChannelMasks(-1, 0); + _pc98a->setSoundEffectChanMask(0); } } @@ -1317,14 +1320,14 @@ void TownsPC98_AudioDriver::timerCallbackB() { if (_musicPlaying) { _musicTickCounter++; - for (int i = 0; i < _numChan; i++) { + for (int i = 0; i < _numChanFM; i++) { if (_updateChannelsFlag & _channels[i]->_idFlag) { _channels[i]->processEvents(); _channels[i]->processFrequency(); } } - for (int i = 0; i < _numSSG; i++) { + for (int i = 0; i < _numChanSSG; i++) { if (_updateSSGFlag & _ssgChannels[i]->_idFlag) { _ssgChannels[i]->processEvents(); _ssgChannels[i]->processFrequency(); @@ -1332,13 +1335,13 @@ void TownsPC98_AudioDriver::timerCallbackB() { } #ifndef DISABLE_PC98_RHYTHM_CHANNEL - if (_hasPercussion) + if (_numChanRHY) if (_updateRhythmFlag & _rhythmChannel->_idFlag) _rhythmChannel->processEvents(); #endif } - toggleRegProtection(false); + preventRegisterWrite(false); if (_finishedChannelsFlag == _updateChannelsFlag && _finishedSSGFlag == _updateSSGFlag && _finishedRhythmFlag == _updateRhythmFlag) _musicPlaying = false; @@ -1353,14 +1356,14 @@ void TownsPC98_AudioDriver::startSoundEffect() { _sfxChannels[i]->reset(); _sfxChannels[i]->loadData(_sfxData + _sfxOffsets[i]); _updateSfxFlag |= _sfxChannels[i]->_idFlag; - volFlags |= (_sfxChannels[i]->_idFlag << _numChan); + volFlags |= (_sfxChannels[i]->_idFlag << _numChanFM); } else { _ssgChannels[i + 1]->restore(); _updateSfxFlag &= ~_sfxChannels[i]->_idFlag; } } - setVolumeChannelMasks(~volFlags, volFlags); + _pc98a->setSoundEffectChanMask(volFlags); _sfxData = 0; } diff --git a/audio/softsynth/fmtowns_pc98/towns_pc98_driver.h b/audio/softsynth/fmtowns_pc98/towns_pc98_driver.h index 918446f6c5..0b9edcfd58 100644 --- a/audio/softsynth/fmtowns_pc98/towns_pc98_driver.h +++ b/audio/softsynth/fmtowns_pc98/towns_pc98_driver.h @@ -23,7 +23,7 @@ #ifndef TOWNS_PC98_AUDIODRIVER_H #define TOWNS_PC98_AUDIODRIVER_H -#include "audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h" +#include "audio/softsynth/fmtowns_pc98/pc98_audio.h" class TownsPC98_MusicChannel; class TownsPC98_MusicChannelSSG; @@ -32,7 +32,7 @@ class TownsPC98_SfxChannel; class TownsPC98_MusicChannelPCM; #endif -class TownsPC98_AudioDriver : public TownsPC98_FmSynth { +class TownsPC98_AudioDriver : public PC98AudioPluginDriver { friend class TownsPC98_MusicChannel; friend class TownsPC98_MusicChannelSSG; friend class TownsPC98_SfxChannel; @@ -60,6 +60,9 @@ public: void setSoundEffectVolume(int volume); private: + void writeReg(uint8 part, uint8 reg, uint8 val); + void preventRegisterWrite(bool prevent); + void timerCallbackA(); void timerCallbackB(); @@ -106,8 +109,12 @@ private: uint8 *_sfxData; uint16 _sfxOffsets[2]; - uint16 _musicVolume; - uint16 _sfxVolume; + bool _regWriteProtect; + PC98AudioCore *_pc98a; + + const int _numChanFM; + const int _numChanSSG; + const int _numChanRHY; static const uint8 _drvTables[]; diff --git a/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp b/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp index 03c96792f6..b73030e416 100644 --- a/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp +++ b/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp @@ -288,9 +288,9 @@ void TownsPC98_FmSynthOperator::decayRate(uint32 value) { } void TownsPC98_FmSynthOperator::sustainRate(uint32 value) { - _specifiedSustainRate = value; - recalculateRates(); - } + _specifiedSustainRate = value; + recalculateRates(); +} void TownsPC98_FmSynthOperator::sustainLevel(uint32 value) { _sustainLevel = (value == 0x0f) ? 0x3e0 : value << 5; @@ -337,6 +337,7 @@ public: void init(const int *rsTable, const int *rseTable); void reset(); void writeReg(uint8 address, uint8 value, bool force = false); + uint8 readReg(uint8 address) const; void nextTick(int32 *buffer, uint32 bufferSize); @@ -348,10 +349,10 @@ public: _volMaskA = channelMaskA; _volMaskB = channelMaskB; } - - uint8 chanEnable() const { - return _chanEnable; + void setOutputLevel(int vol) { + _volumeT = vol; } + private: void updateRegs(); @@ -393,6 +394,7 @@ private: uint16 _volumeA; uint16 _volumeB; + uint16 _volumeT; int _volMaskA; int _volMaskB; @@ -410,6 +412,7 @@ public: void init(const uint8 *instrData = 0); void reset(); void writeReg(uint8 address, uint8 value); + uint8 readReg(uint8 address) const; void nextTick(int32 *buffer, uint32 bufferSize); @@ -470,7 +473,7 @@ private: 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), + _timer(0), _noiseGenerator(0), _chanEnable(0), _volumeT(0x60), _volMaskA(0), _volMaskB(0), _volumeA(Audio::Mixer::kMaxMixerVolume), _volumeB(Audio::Mixer::kMaxMixerVolume) { memset(_channels, 0, sizeof(_channels)); @@ -579,6 +582,13 @@ void TownsPC98_FmSynthSquareSineSource::writeReg(uint8 address, uint8 value, boo *_reg[address] = value; } +uint8 TownsPC98_FmSynthSquareSineSource::readReg(uint8 address) const { + if (!_ready || address > 10) + return 0; + + return *_reg[address]; +} + void TownsPC98_FmSynthSquareSineSource::nextTick(int32 *buffer, uint32 bufferSize) { if (!_ready) return; @@ -635,7 +645,7 @@ void TownsPC98_FmSynthSquareSineSource::nextTick(int32 *buffer, uint32 bufferSiz finOut += finOutTemp; } - finOut /= 3; + finOut = (finOut * _volumeT) / Audio::Mixer::kMaxMixerVolume; buffer[i << 1] += finOut; buffer[(i << 1) + 1] += finOut; @@ -781,6 +791,13 @@ void TownsPC98_FmSynthPercussionSource::writeReg(uint8 address, uint8 value) { } } +uint8 TownsPC98_FmSynthPercussionSource::readReg(uint8 address) const { + if (!_ready || address > 0x0F) + return 0; + + return *_reg[address]; +} + void TownsPC98_FmSynthPercussionSource::nextTick(int32 *buffer, uint32 bufferSize) { if (!_ready) return; @@ -861,12 +878,14 @@ TownsPC98_FmSynth::TownsPC98_FmSynth(Audio::Mixer *mixer, EmuType type, bool ext _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), _externalMutex(externalMutexHandling), _ready(false) { + _volMaskA(0), _volMaskB(0), _volumeA(255), _volumeB(255), _externalMutex(externalMutexHandling), _ready(false) { memset(&_timers[0], 0, sizeof(ChipTimer)); memset(&_timers[1], 0, sizeof(ChipTimer)); + memset(_registers[0], 0, 255); + memset(_registers[1], 0, 255); + _timers[0].cb = _timers[1].cb = &TownsPC98_FmSynth::idleTimerCallback; _timerbase = (uint32)(_baserate * 1000000.0f); } @@ -931,6 +950,9 @@ bool TownsPC98_FmSynth::init() { } void TownsPC98_FmSynth::reset() { + if (!_ready) + return; + Common::StackLock lock(_mutex); for (int i = 0; i < _numChan; i++) { for (int ii = 0; ii < 4; ii++) @@ -942,6 +964,9 @@ void TownsPC98_FmSynth::reset() { _chanInternal[i].updateEnvelopeParameters = false; } + memset(_registers[0], 0, 255); + memset(_registers[1], 0, 255); + writeReg(0, 0x27, 0x33); if (_ssg) @@ -954,13 +979,20 @@ void TownsPC98_FmSynth::reset() { } void TownsPC98_FmSynth::writeReg(uint8 part, uint8 regAddress, uint8 value) { - if (_regProtectionFlag || !_ready) + if (!_ready) return; + if (part > 1) { + warning("TownsPC98_FmSynth::writeReg(): invalid part argument '%d'", part); + part = 1; + } + Common::StackLock lock(_mutex); static const uint8 oprOrdr[] = { 0, 2, 1, 3 }; + _registers[regAddress][part] = value; + uint8 h = regAddress & 0xf0; uint8 l = (regAddress & 0x0f); @@ -1141,6 +1173,18 @@ void TownsPC98_FmSynth::writeReg(uint8 part, uint8 regAddress, uint8 value) { } } +uint8 TownsPC98_FmSynth::readReg(uint8 part, uint8 regAddress) { + if (!_ready || part > 1) + return 0; + + if (!(regAddress & 0xF0) && _ssg) + return _ssg->readReg(regAddress & 0x0F); + else if ((regAddress & 0xF0) == 0x10 && _prc) + return _prc->readReg(regAddress & 0x0F); + + return _registers[regAddress][part]; +} + int TownsPC98_FmSynth::readBuffer(int16 *buffer, const int numSamples) { memset(buffer, 0, sizeof(int16) * numSamples); int32 *tmp = new int32[numSamples]; @@ -1242,14 +1286,6 @@ void TownsPC98_FmSynth::deinit() { _timers[0].cb = _timers[1].cb = &TownsPC98_FmSynth::idleTimerCallback; } -void TownsPC98_FmSynth::toggleRegProtection(bool prot) { - _regProtectionFlag = prot; -} - -uint8 TownsPC98_FmSynth::readSSGStatus() { - return _ssg->chanEnable(); -} - void TownsPC98_FmSynth::setVolumeIntern(int volA, int volB) { Common::StackLock lock(_mutex); _volumeA = CLIP(volA, 0, Audio::Mixer::kMaxMixerVolume); @@ -1274,6 +1310,11 @@ void TownsPC98_FmSynth::setVolumeChannelMasks(int channelMaskA, int channelMaskB #endif } +void TownsPC98_FmSynth::setLevelSSG(int vol) { + if (_ssg) + _ssg->setOutputLevel(vol); +} + void TownsPC98_FmSynth::generateTables() { delete[] _oprRates; _oprRates = new uint8[128]; diff --git a/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h b/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h index 5af92820ca..ba484e2413 100644 --- a/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h +++ b/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h @@ -66,6 +66,7 @@ public: virtual void reset(); void writeReg(uint8 part, uint8 regAddress, uint8 value); + uint8 readReg(uint8 part, uint8 regAddress); // AudioStream interface int readBuffer(int16 *buffer, const int numSamples); @@ -80,9 +81,6 @@ protected: // additional output that has to be inserted into the buffer. virtual void nextTickEx(int32 *buffer, uint32 bufferSize) {} - void toggleRegProtection(bool prot); - uint8 readSSGStatus(); - virtual void timerCallbackA() = 0; virtual void timerCallbackB() = 0; @@ -94,6 +92,8 @@ protected: void setVolumeIntern(int volA, int volB); void setVolumeChannelMasks(int channelMaskA, int channelMaskB); + void setLevelSSG(int vol); + const int _numChan; const int _numSSG; const bool _hasPercussion; @@ -104,7 +104,6 @@ protected: private: void generateTables(); void nextTick(int32 *buffer, uint32 bufferSize); - void generateOutput(int32 &leftSample, int32 &rightSample, int32 *del, int32 *feed); struct ChanInternal { ChanInternal(); @@ -171,6 +170,8 @@ private: uint32 _timerbase; uint32 _rtt; + uint8 _registers[255][2]; + Audio::Mixer *_mixer; Audio::SoundHandle _soundHandle; -- cgit v1.2.3