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:
parent
82a821bf7f
commit
20b378a41a
7 changed files with 504 additions and 77 deletions
|
@ -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 \
|
||||
|
|
290
audio/softsynth/fmtowns_pc98/pc98_audio.cpp
Normal file
290
audio/softsynth/fmtowns_pc98/pc98_audio.cpp
Normal 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();
|
||||
}
|
84
audio/softsynth/fmtowns_pc98/pc98_audio.h
Normal file
84
audio/softsynth/fmtowns_pc98/pc98_audio.h
Normal 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
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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[];
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue