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.
This commit is contained in:
athrxx 2018-12-04 17:49:15 +01:00
parent 82a821bf7f
commit 20b378a41a
7 changed files with 504 additions and 77 deletions

View file

@ -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 \

View file

@ -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();
}

View file

@ -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

View file

@ -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;
}

View file

@ -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[];

View file

@ -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];

View file

@ -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;