aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--audio/module.mk1
-rw-r--r--audio/softsynth/fmtowns_pc98/pc98_audio.cpp290
-rw-r--r--audio/softsynth/fmtowns_pc98/pc98_audio.h84
-rw-r--r--audio/softsynth/fmtowns_pc98/towns_pc98_driver.cpp103
-rw-r--r--audio/softsynth/fmtowns_pc98/towns_pc98_driver.h15
-rw-r--r--audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp79
-rw-r--r--audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h9
7 files changed, 504 insertions, 77 deletions
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<uint16>(volume, 0, Audio::Mixer::kMaxMixerVolume);
+ setVolumeIntern(_musicVolume, _sfxVolume);
+}
+
+void PC98AudioCoreInternal::setSoundEffectVolume(int volume) {
+ _sfxVolume = CLIP<uint16>(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<uint16>(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;