[Scummvm-git-logs] scummvm master -> 8d4418fa365511e8d598823e4369cdc10f87f6b8

athrxx athrxx at scummvm.org
Thu Mar 7 22:32:40 CET 2019


This automated email contains information about 6 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
20b378a41a AUDIO: (FM-TOWNS/PC-98) - cleanup
4a226aa835 AUDIO: (FM-TOWNS/PC-98) - more cleanup
b789d50a55 AUDIO: (FM-TOWNS/PC-98) - fix valgrind warning
49e85f64cf AUDIO: (FM-TOWNS/PC-98) - allow individual operator frequencies
1237eb496b AUDIO: (FM-TOWNS/PC-98) - initialize some uninitialized vars
8d4418fa36 AUDIO: (FM-TOWNS) - cleanup


Commit: 20b378a41a194bd0869bfc2bb16b9a6897bd8bd2
    https://github.com/scummvm/scummvm/commit/20b378a41a194bd0869bfc2bb16b9a6897bd8bd2
Author: athrxx (athrxx at scummvm.org)
Date: 2019-03-07T19:43:44+01:00

Commit Message:
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.

Changed paths:
  A audio/softsynth/fmtowns_pc98/pc98_audio.cpp
  A audio/softsynth/fmtowns_pc98/pc98_audio.h
    audio/module.mk
    audio/softsynth/fmtowns_pc98/towns_pc98_driver.cpp
    audio/softsynth/fmtowns_pc98/towns_pc98_driver.h
    audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp
    audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h


diff --git a/audio/module.mk b/audio/module.mk
index 0add7cf..686c70b 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 0000000..d6c6fc9
--- /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 0000000..b6269f0
--- /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 6a49278..091905b 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 918446f..0b9edcf 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 03c9679..b73030e 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 5af9282..ba484e2 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;
 


Commit: 4a226aa8358108b1a7b861bb0578ba7be84b44b1
    https://github.com/scummvm/scummvm/commit/4a226aa8358108b1a7b861bb0578ba7be84b44b1
Author: athrxx (athrxx at scummvm.org)
Date: 2019-03-07T19:43:55+01:00

Commit Message:
AUDIO: (FM-TOWNS/PC-98) - more cleanup

sort and rename some methods and vars and move as much as possible from public to private section

Changed paths:
    audio/softsynth/fmtowns_pc98/towns_pc98_driver.cpp
    audio/softsynth/fmtowns_pc98/towns_pc98_driver.h


diff --git a/audio/softsynth/fmtowns_pc98/towns_pc98_driver.cpp b/audio/softsynth/fmtowns_pc98/towns_pc98_driver.cpp
index 091905b..b5e8c84 100644
--- a/audio/softsynth/fmtowns_pc98/towns_pc98_driver.cpp
+++ b/audio/softsynth/fmtowns_pc98/towns_pc98_driver.cpp
@@ -23,35 +23,30 @@
 #include "audio/softsynth/fmtowns_pc98/towns_pc98_driver.h"
 #include "common/endian.h"
 #include "common/textconsole.h"
+#include "common/func.h"
+#include "common/array.h"
 
 class TownsPC98_MusicChannel {
 public:
-	TownsPC98_MusicChannel(TownsPC98_AudioDriver *driver, uint8 regOffs, uint8 flgs, uint8 num,
-	                       uint8 key, uint8 prt, uint8 id);
+	TownsPC98_MusicChannel(TownsPC98_AudioDriver *driver, uint8 regOffs, uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id);
 	virtual ~TownsPC98_MusicChannel();
-	virtual void init();
 
 	typedef enum channelState {
 		CHS_RECALCFREQ = 0x01,
-		CHS_KEYOFF     = 0x02,
-		CHS_SSGOFF     = 0x04,
-		CHS_VBROFF     = 0x08,
-		CHS_ALLOFF     = 0x0f,
-		CHS_PROTECT    = 0x40,
-		CHS_EOT        = 0x80
+		CHS_KEYOFF = 0x02,
+		CHS_SSGOFF = 0x04,
+		CHS_VBROFF = 0x08,
+		CHS_ALLOFF = 0x0f,
+		CHS_PROTECT = 0x40,
+		CHS_EOT = 0x80
 	} ChannelState;
 
+	virtual void reset();
 	virtual void loadData(uint8 *data);
 	virtual void processEvents();
 	virtual void processFrequency();
-	virtual bool processControlEvent(uint8 cmd);
-
-	virtual void keyOn();
-	void keyOff();
-
-	void setOutputLevel();
-	virtual void fadeStep();
-	virtual void reset();
+	
+	virtual void fadeStep();	
 
 	const uint8 _idFlag;
 
@@ -59,31 +54,52 @@ protected:
 	void setupVibrato();
 	bool processVibrato();
 
+	uint8 readReg(uint8 part, uint8 reg);
+	void writeReg(uint8 part, uint8 reg, uint8 val);
+
 	bool control_dummy(uint8 para);
-	bool control_f0_setPatch(uint8 para);
-	bool control_f1_presetOutputLevel(uint8 para);
-	bool control_f2_setKeyOffTime(uint8 para);
-	bool control_f3_setFreqLSB(uint8 para);
-	bool control_f4_setOutputLevel(uint8 para);
-	bool control_f5_setTempo(uint8 para);
+	bool control_f2_duration(uint8 para);
+	bool control_f3_pitchBend(uint8 para);
+	bool control_f5_tempo(uint8 para);
 	bool control_f6_repeatSection(uint8 para);
 	bool control_f7_setupVibrato(uint8 para);
 	bool control_f8_toggleVibrato(uint8 para);
 	bool control_fa_writeReg(uint8 para);
-	virtual bool control_fb_incOutLevel(uint8 para);
-	virtual bool control_fc_decOutLevel(uint8 para);
 	bool control_fd_jump(uint8 para);
-	virtual bool control_ff_endOfTrack(uint8 para);
 
 	uint8 _ticksLeft;
-	uint8 _algorithm;
+	uint8 _duration;
 	uint8 _instr;
 	uint8 _totalLevel;
 	uint8 _frqBlockMSB;
-	int8 _frqLSB;
-	uint8 _keyOffTime;
-	bool _hold;
+	uint16 _frequency;
+	uint8 _block;
+	int8 _pitchBend;
+	uint8 _regOffset;
+	uint8 _flags;
 	uint8 *_dataPtr;
+	bool _sustain;
+	bool _fading;
+
+	TownsPC98_AudioDriver *_driver;
+	const uint8 _chanNum;
+
+	static const uint8 _controlEventSize[16];
+
+private:
+	void keyOn();
+	void keyOff();
+
+	void setOutputLevel();
+	bool processControlEvent(uint8 cmd);
+
+	bool control_f0_setPatch(uint8 para);
+	bool control_f1_presetOutputLevel(uint8 para);
+	bool control_f4_setOutputLevel(uint8 para);
+	bool control_fb_incOutLevel(uint8 para);
+	bool control_fc_decOutLevel(uint8 para);
+	bool control_ff_endOfTrack(uint8 para);
+
 	uint8 _vbrInitDelayHi;
 	uint8 _vbrInitDelayLo;
 	int16 _vbrModInitVal;
@@ -91,49 +107,36 @@ protected:
 	uint8 _vbrCurDelay;
 	int16 _vbrModCurVal;
 	uint8 _vbrDurLeft;
-	uint16 _frequency;
-	uint8 _block;
-	uint8 _regOffset;
-	uint8 _flags;
-	uint8 _ssgTl;
-	uint8 _ssgStep;
-	uint8 _ssgTicksLeft;
-	uint8 _ssgTargetLvl;
-	uint8 _ssgStartLvl;
-
-	const uint8 _chanNum;
+	uint8 _algorithm;
+	
 	const uint8 _keyNum;
 	const uint8 _part;
 
-	TownsPC98_AudioDriver *_drv;
-
-	typedef bool (TownsPC98_MusicChannel::*ControlEventFunc)(uint8 para);
-	const ControlEventFunc *controlEvents;
+	typedef Common::Functor1Mem<uint8, bool, TownsPC98_MusicChannel> ControlEvent;
+	Common::Array<const ControlEvent*> _controlEvents;
 };
 
 class TownsPC98_MusicChannelSSG : public TownsPC98_MusicChannel {
 public:
-	TownsPC98_MusicChannelSSG(TownsPC98_AudioDriver *driver, uint8 regOffs,
-	                          uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id);
-	virtual ~TownsPC98_MusicChannelSSG() {}
-	void init();
+	TownsPC98_MusicChannelSSG(TownsPC98_AudioDriver *driver, uint8 regOffs, uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id);
+	virtual ~TownsPC98_MusicChannelSSG();
 
+	virtual void reset();
 	virtual void loadData(uint8 *data);
 	void processEvents();
 	void processFrequency();
-	bool processControlEvent(uint8 cmd);
-
-	void keyOn();
-	void nextShape();
 
 	void protect();
 	void restore();
-	virtual void reset();
 
 	void fadeStep();
 
 protected:
+	void keyOn();
+	void nextShape();
+
 	void setOutputLevel(uint8 lvl);
+	bool processControlEvent(uint8 cmd);
 
 	bool control_f0_setPatch(uint8 para);
 	bool control_f1_setTotalLevel(uint8 para);
@@ -143,101 +146,103 @@ protected:
 	bool control_fc_decOutLevel(uint8 para);
 	bool control_ff_endOfTrack(uint8 para);
 
-	typedef bool (TownsPC98_MusicChannelSSG::*ControlEventFunc)(uint8 para);
-	const ControlEventFunc *controlEvents;
+	uint8 _ssgTl;
+	uint8 _ssgStep;
+	uint8 _ssgTicksLeft;
+	uint8 _ssgTargetLvl;
+	uint8 _ssgStartLvl;
+	uint8 _algorithm;
+
+	static uint8 *_envPatchData;
+	static const uint8 _envData[256];
+
+	typedef Common::Functor1Mem<uint8, bool, TownsPC98_MusicChannelSSG> ControlEvent;
+	Common::Array<const ControlEvent*> _controlEvents;
 };
 
 class TownsPC98_SfxChannel : public TownsPC98_MusicChannelSSG {
 public:
-	TownsPC98_SfxChannel(TownsPC98_AudioDriver *driver, uint8 regOffs,
-	                     uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) :
+	TownsPC98_SfxChannel(TownsPC98_AudioDriver *driver, uint8 regOffs, uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) :
 		TownsPC98_MusicChannelSSG(driver, regOffs, flgs, num, key, prt, id) {}
-	~TownsPC98_SfxChannel() {}
+	virtual ~TownsPC98_SfxChannel() {}
 
-	void loadData(uint8 *data);
 	void reset();
+	void loadData(uint8 *data);
 };
 
 #ifndef DISABLE_PC98_RHYTHM_CHANNEL
 class TownsPC98_MusicChannelPCM : public TownsPC98_MusicChannel {
 public:
-	TownsPC98_MusicChannelPCM(TownsPC98_AudioDriver *driver, uint8 regOffs,
-	                          uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id);
-	~TownsPC98_MusicChannelPCM() {}
-	void init();
+	TownsPC98_MusicChannelPCM(TownsPC98_AudioDriver *driver, uint8 regOffs, uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id);
+	virtual ~TownsPC98_MusicChannelPCM();
 
 	void loadData(uint8 *data);
 	void processEvents();
-	bool processControlEvent(uint8 cmd);
 
 private:
+	bool processControlEvent(uint8 cmd);
 	bool control_f1_prcStart(uint8 para);
 	bool control_ff_endOfTrack(uint8 para);
 
-	typedef bool (TownsPC98_MusicChannelPCM::*ControlEventFunc)(uint8 para);
-	const ControlEventFunc *controlEvents;
+	uint8 _algorithm;
+
+	typedef Common::Functor1Mem<uint8, bool, TownsPC98_MusicChannelPCM> ControlEvent;
+	Common::Array<const ControlEvent*> _controlEvents;
 };
 #endif
 
-TownsPC98_MusicChannel::TownsPC98_MusicChannel(TownsPC98_AudioDriver *driver, uint8 regOffs, uint8 flgs, uint8 num,
-        uint8 key, uint8 prt, uint8 id) : _drv(driver), _regOffset(regOffs), _flags(flgs), _chanNum(num), _keyNum(key),
-	_part(prt), _idFlag(id), controlEvents(0) {
-
-	_ticksLeft = _algorithm = _instr = _totalLevel = _frqBlockMSB = _keyOffTime = 0;
-	_ssgStartLvl = _ssgTl = _ssgStep = _ssgTicksLeft = _ssgTargetLvl = _block = 0;
-	_vbrInitDelayHi = _vbrInitDelayLo = _vbrDuration = _vbrCurDelay = _vbrDurLeft = 0;
-	_frqLSB = 0;
-	_hold = false;
-	_dataPtr = 0;
-	_vbrModInitVal = _vbrModCurVal = 0;
-	_frequency = 0;
-}
+#define CONTROL(x) _controlEvents.push_back(new ControlEvent(this, &TownsPC98_MusicChannel::control_##x))
+TownsPC98_MusicChannel::TownsPC98_MusicChannel(TownsPC98_AudioDriver *driver, uint8 regOffs, uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) : _driver(driver),
+_regOffset(regOffs), _flags(flgs), _chanNum(num), _keyNum(key),	_part(prt), _idFlag(id), _ticksLeft(0), _algorithm(0), _instr(0), _totalLevel(0),
+_frqBlockMSB(0), _duration(0), _block(0), _vbrInitDelayHi(0), _vbrInitDelayLo(0), _vbrDuration(0), _vbrCurDelay(0), _vbrDurLeft(0), _pitchBend(0),
+_sustain(false), _fading(false), _dataPtr(0), _vbrModInitVal(0), _vbrModCurVal(0), _frequency(0) {
+	CONTROL(f0_setPatch);
+	CONTROL(f1_presetOutputLevel);
+	CONTROL(f2_duration);
+	CONTROL(f3_pitchBend);
+	CONTROL(f4_setOutputLevel);
+	CONTROL(f5_tempo);
+	CONTROL(f6_repeatSection);
+	CONTROL(f7_setupVibrato);
+	CONTROL(f8_toggleVibrato);
+	CONTROL(dummy);
+	CONTROL(fa_writeReg);
+	CONTROL(fb_incOutLevel);
+	CONTROL(fc_decOutLevel);
+	CONTROL(fd_jump);
+	CONTROL(dummy);
+	CONTROL(ff_endOfTrack);
+}
+#undef CONTROL
 
 TownsPC98_MusicChannel::~TownsPC98_MusicChannel() {
+	for (Common::Array<const ControlEvent*>::iterator i = _controlEvents.begin(); i != _controlEvents.end(); ++i)
+		delete *i;
 }
 
-void TownsPC98_MusicChannel::init() {
-#define Control(x) &TownsPC98_MusicChannel::control_##x
-	static const ControlEventFunc ctrlEvents[] = {
-		Control(f0_setPatch),
-		Control(f1_presetOutputLevel),
-		Control(f2_setKeyOffTime),
-		Control(f3_setFreqLSB),
-		Control(f4_setOutputLevel),
-		Control(f5_setTempo),
-		Control(f6_repeatSection),
-		Control(f7_setupVibrato),
-		Control(f8_toggleVibrato),
-		Control(dummy),
-		Control(fa_writeReg),
-		Control(fb_incOutLevel),
-		Control(fc_decOutLevel),
-		Control(fd_jump),
-		Control(dummy),
-		Control(ff_endOfTrack)
-	};
-#undef Control
-
-	controlEvents = ctrlEvents;
-}
+void TownsPC98_MusicChannel::reset() {
+	_sustain = false;
+	_duration = 0;
+	_fading = false;
+	_ticksLeft = 1;
 
-void TownsPC98_MusicChannel::keyOff() {
-	// all operators off
-	uint8 value = _keyNum & 0x0f;
-	if (_part)
-		value |= 4;
-	uint8 regAddress = 0x28;
-	_drv->writeReg(0, regAddress, value);
-	_flags |= CHS_KEYOFF;
-}
+	_flags = (_flags & ~CHS_EOT) | CHS_ALLOFF;
 
-void TownsPC98_MusicChannel::keyOn() {
-	// all operators on
-	uint8 value = _keyNum | 0xf0;
-	if (_part)
-		value |= 4;
-	uint8 regAddress = 0x28;
-	_drv->writeReg(0, regAddress, value);
+	_totalLevel = 0;
+	_algorithm = 0;
+
+	_block = 0;
+	_frequency = 0;
+	_frqBlockMSB = 0;
+	_pitchBend = 0;
+
+	_vbrInitDelayHi = 0;
+	_vbrInitDelayLo = 0;
+	_vbrModInitVal = 0;
+	_vbrDuration = 0;
+	_vbrCurDelay = 0;
+	_vbrModCurVal = 0;
+	_vbrDurLeft = 0;
 }
 
 void TownsPC98_MusicChannel::loadData(uint8 *data) {
@@ -253,8 +258,8 @@ void TownsPC98_MusicChannel::loadData(uint8 *data) {
 			tmp++;
 		} else if (cmd == 0xff) {
 			if (READ_LE_UINT16(tmp)) {
-				_drv->_looping |= _idFlag;
-				tmp += _drv->_opnFxCmdLen[cmd - 240];
+				_driver->_looping |= _idFlag;
+				tmp += _controlEventSize[cmd - 240];
 			} else
 				loop = false;
 		} else if (cmd == 0xf6) {
@@ -262,7 +267,7 @@ void TownsPC98_MusicChannel::loadData(uint8 *data) {
 			tmp[0] = tmp[1];
 			tmp += 4;
 		} else {
-			tmp += _drv->_opnFxCmdLen[cmd - 240];
+			tmp += _controlEventSize[cmd - 240];
 		}
 	}
 }
@@ -271,13 +276,13 @@ void TownsPC98_MusicChannel::processEvents() {
 	if (_flags & CHS_EOT)
 		return;
 
-	if (!_hold && _ticksLeft == _keyOffTime)
+	if (!_sustain && _ticksLeft == _duration)
 		keyOff();
 
 	if (--_ticksLeft)
 		return;
 
-	if (!_hold)
+	if (!_sustain)
 		keyOff();
 
 	uint8 cmd = 0;
@@ -295,14 +300,14 @@ void TownsPC98_MusicChannel::processEvents() {
 
 	if (cmd == 0x80) {
 		keyOff();
-		_hold = false;
+		_sustain = false;
 	} else {
 		keyOn();
 
-		if (_hold == false || cmd != _frqBlockMSB)
+		if (_sustain == false || cmd != _frqBlockMSB)
 			_flags |= CHS_RECALCFREQ;
 
-		_hold = (para & 0x80) ? true : false;
+		_sustain = (para & 0x80) ? true : false;
 		_frqBlockMSB = cmd;
 	}
 
@@ -310,12 +315,14 @@ void TownsPC98_MusicChannel::processEvents() {
 }
 
 void TownsPC98_MusicChannel::processFrequency() {
+	static const uint16 noteFrequencies[] = { 0x26a, 0x28f, 0x2b6, 0x2df, 0x30b, 0x339, 0x36a, 0x39e, 0x3d5, 0x410, 0x44e, 0x48f };
+
 	if (_flags & CHS_RECALCFREQ) {
 
-		_frequency = (READ_LE_UINT16(&_drv->_opnFreqTable[(_frqBlockMSB & 0x0f) << 1]) + _frqLSB) | (((_frqBlockMSB & 0x70) >> 1) << 8);
+		_frequency = (noteFrequencies[_frqBlockMSB & 0x0f] + _pitchBend) | (((_frqBlockMSB & 0x70) >> 1) << 8);
 
-		_drv->writeReg(_part, _regOffset + 0xa4, (_frequency >> 8));
-		_drv->writeReg(_part, _regOffset + 0xa0, (_frequency & 0xff));
+		writeReg(_part, _regOffset + 0xa4, (_frequency >> 8));
+		writeReg(_part, _regOffset + 0xa0, (_frequency & 0xff));
 
 		setupVibrato();
 	}
@@ -324,11 +331,19 @@ void TownsPC98_MusicChannel::processFrequency() {
 		if (!processVibrato())
 			return;
 
-		_drv->writeReg(_part, _regOffset + 0xa4, (_frequency >> 8));
-		_drv->writeReg(_part, _regOffset + 0xa0, (_frequency & 0xff));
+		writeReg(_part, _regOffset + 0xa4, (_frequency >> 8));
+		writeReg(_part, _regOffset + 0xa0, (_frequency & 0xff));
 	}
 }
 
+void TownsPC98_MusicChannel::fadeStep() {
+	_fading = true;
+	_totalLevel += 3;
+	if (_totalLevel > 0x7f)
+		_totalLevel = 0x7f;
+	setOutputLevel();
+}
+
 void TownsPC98_MusicChannel::setupVibrato() {
 	_vbrCurDelay = _vbrInitDelayHi;
 	if (_flags & CHS_KEYOFF) {
@@ -354,125 +369,31 @@ bool TownsPC98_MusicChannel::processVibrato() {
 	return true;
 }
 
-bool TownsPC98_MusicChannel::processControlEvent(uint8 cmd) {
-	uint8 para = *_dataPtr++;
-	return (this->*controlEvents[cmd & 0x0f])(para);
-}
-
-void TownsPC98_MusicChannel::setOutputLevel() {
-	uint8 outopr = _drv->_opnCarrier[_algorithm];
-	uint8 reg = 0x40 + _regOffset;
-
-	for (int i = 0; i < 4; i++) {
-		if (outopr & 1)
-			_drv->writeReg(_part, reg, _totalLevel);
-		outopr >>= 1;
-		reg += 4;
-	}
-}
-
-void TownsPC98_MusicChannel::fadeStep() {
-	_totalLevel += 3;
-	if (_totalLevel > 0x7f)
-		_totalLevel = 0x7f;
-	setOutputLevel();
+uint8 TownsPC98_MusicChannel::readReg(uint8 part, uint8 reg) {
+	return _driver->readReg(part, reg);
 }
 
-void TownsPC98_MusicChannel::reset() {
-	_hold = false;
-	_keyOffTime = 0;
-	_ticksLeft = 1;
-
-	_flags = (_flags & ~CHS_EOT) | CHS_ALLOFF;
-
-	_totalLevel = 0;
-	_algorithm = 0;
-
-	_block = 0;
-	_frequency = 0;
-	_frqBlockMSB = 0;
-	_frqLSB = 0;
-
-	_ssgTl = 0;
-	_ssgStartLvl = 0;
-	_ssgTargetLvl = 0;
-	_ssgStep = 0;
-	_ssgTicksLeft = 0;
-
-	_vbrInitDelayHi = 0;
-	_vbrInitDelayLo = 0;
-	_vbrModInitVal = 0;
-	_vbrDuration = 0;
-	_vbrCurDelay = 0;
-	_vbrModCurVal = 0;
-	_vbrDurLeft = 0;
+void TownsPC98_MusicChannel::writeReg(uint8 part, uint8 reg, uint8 val) {
+	_driver->writeReg(part, reg, val);
 }
 
-bool TownsPC98_MusicChannel::control_f0_setPatch(uint8 para) {
-	_instr = para;
-	uint8 reg = _regOffset + 0x80;
-
-	for (int i = 0; i < 4; i++) {
-		// set release rate for each operator
-		_drv->writeReg(_part, reg, 0x0f);
-		reg += 4;
-	}
-
-	const uint8 *tptr = _drv->_patches + ((uint32)_instr << 5);
-	reg = _regOffset + 0x30;
-
-	// write registers 0x30 to 0x8f
-	for (int i = 0; i < 6; i++) {
-		_drv->writeReg(_part, reg, tptr[0]);
-		reg += 4;
-		_drv->writeReg(_part, reg, tptr[2]);
-		reg += 4;
-		_drv->writeReg(_part, reg, tptr[1]);
-		reg += 4;
-		_drv->writeReg(_part, reg, tptr[3]);
-		reg += 4;
-		tptr += 4;
-	}
-
-	reg = _regOffset + 0xB0;
-	_algorithm = tptr[0] & 7;
-	// set feedback and algorithm
-	_drv->writeReg(_part, reg, tptr[0]);
-
-	setOutputLevel();
-	return true;
-}
-
-bool TownsPC98_MusicChannel::control_f1_presetOutputLevel(uint8 para) {
-	if (_drv->_fading)
-		return true;
-
-	_totalLevel = _drv->_opnLvlPresets[para];
-	setOutputLevel();
-	return true;
-}
-
-bool TownsPC98_MusicChannel::control_f2_setKeyOffTime(uint8 para) {
-	_keyOffTime = para;
+bool TownsPC98_MusicChannel::control_dummy(uint8 para) {
+	_dataPtr--;
 	return true;
 }
 
-bool TownsPC98_MusicChannel::control_f3_setFreqLSB(uint8 para) {
-	_frqLSB = (int8) para;
+bool TownsPC98_MusicChannel::control_f2_duration(uint8 para) {
+	_duration = para;
 	return true;
 }
 
-bool TownsPC98_MusicChannel::control_f4_setOutputLevel(uint8 para) {
-	if (_drv->_fading)
-		return true;
-
-	_totalLevel = para;
-	setOutputLevel();
+bool TownsPC98_MusicChannel::control_f3_pitchBend(uint8 para) {
+	_pitchBend = (int8) para;
 	return true;
 }
 
-bool TownsPC98_MusicChannel::control_f5_setTempo(uint8 para) {
-	_drv->setMusicTempo(para);
+bool TownsPC98_MusicChannel::control_f5_tempo(uint8 para) {
+	_driver->setMusicTempo(para);
 	return true;
 }
 
@@ -482,7 +403,7 @@ bool TownsPC98_MusicChannel::control_f6_repeatSection(uint8 para) {
 
 	if (*_dataPtr) {
 		// repeat section until counter has reached zero
-		_dataPtr = _drv->_trackPtr + READ_LE_UINT16(_dataPtr + 2);
+		_dataPtr = _driver->_trackPtr + READ_LE_UINT16(_dataPtr + 2);
 	} else {
 		// reset counter, advance to next section
 		_dataPtr[0] = _dataPtr[1];
@@ -520,13 +441,109 @@ bool TownsPC98_MusicChannel::control_f8_toggleVibrato(uint8 para) {
 }
 
 bool TownsPC98_MusicChannel::control_fa_writeReg(uint8 para) {
-	_drv->writeReg(_part, para, *_dataPtr++);
+	writeReg(_part, para, *_dataPtr++);
+	return true;
+}
+
+bool TownsPC98_MusicChannel::control_fd_jump(uint8 para) {
+	uint8 *tmp = _driver->_trackPtr + READ_LE_UINT16(_dataPtr - 1);
+	_dataPtr = (tmp[1] == 1) ? tmp : (_dataPtr + 1);
+	return true;
+}
+
+void TownsPC98_MusicChannel::keyOn() {
+	// all operators on
+	uint8 value = _keyNum | 0xf0;
+	if (_part)
+		value |= 4;
+	uint8 regAddress = 0x28;
+	writeReg(0, regAddress, value);
+}
+
+void TownsPC98_MusicChannel::keyOff() {
+	// all operators off
+	uint8 value = _keyNum & 0x0f;
+	if (_part)
+		value |= 4;
+	uint8 regAddress = 0x28;
+	writeReg(0, regAddress, value);
+	_flags |= CHS_KEYOFF;
+}
+
+void TownsPC98_MusicChannel::setOutputLevel() {
+	static const uint8 carrier[] = { 0x08, 0x08, 0x08, 0x08, 0x0C, 0x0E, 0x0E, 0x0F };
+	uint8 outopr = carrier[_algorithm];
+	uint8 reg = 0x40 + _regOffset;
+
+	for (int i = 0; i < 4; i++) {
+		if (outopr & 1)
+			writeReg(_part, reg, _totalLevel);
+		outopr >>= 1;
+		reg += 4;
+	}
+}
+
+bool TownsPC98_MusicChannel::processControlEvent(uint8 cmd) {
+	uint8 para = *_dataPtr++;
+	return (*_controlEvents[cmd & 0x0f])(para);
+}
+
+bool TownsPC98_MusicChannel::control_f0_setPatch(uint8 para) {
+	_instr = para;
+	uint8 reg = _regOffset + 0x80;
+
+	for (int i = 0; i < 4; i++) {
+		// set release rate for each operator
+		writeReg(_part, reg, 0x0f);
+		reg += 4;
+	}
+
+	const uint8 *tptr = _driver->_patchData + ((uint32)_instr << 5);
+	reg = _regOffset + 0x30;
+
+	// write registers 0x30 to 0x8f
+	for (int i = 0; i < 6; i++) {
+		writeReg(_part, reg, tptr[0]);
+		reg += 4;
+		writeReg(_part, reg, tptr[2]);
+		reg += 4;
+		writeReg(_part, reg, tptr[1]);
+		reg += 4;
+		writeReg(_part, reg, tptr[3]);
+		reg += 4;
+		tptr += 4;
+	}
+
+	reg = _regOffset + 0xB0;
+	_algorithm = tptr[0] & 7;
+	// set feedback and algorithm
+	writeReg(_part, reg, tptr[0]);
+
+	setOutputLevel();
+	return true;
+}
+
+bool TownsPC98_MusicChannel::control_f1_presetOutputLevel(uint8 para) {
+	if (_fading)
+		return true;
+
+	_totalLevel = _driver->_levelPresets[para];
+	setOutputLevel();
+	return true;
+}
+
+bool TownsPC98_MusicChannel::control_f4_setOutputLevel(uint8 para) {
+	if (_fading)
+		return true;
+
+	_totalLevel = para;
+	setOutputLevel();
 	return true;
 }
 
 bool TownsPC98_MusicChannel::control_fb_incOutLevel(uint8 para) {
 	_dataPtr--;
-	if (_drv->_fading)
+	if (_fading)
 		return true;
 
 	uint8 val = (_totalLevel + 3);
@@ -540,84 +557,103 @@ bool TownsPC98_MusicChannel::control_fb_incOutLevel(uint8 para) {
 
 bool TownsPC98_MusicChannel::control_fc_decOutLevel(uint8 para) {
 	_dataPtr--;
-	if (_drv->_fading)
+	if (_fading)
 		return true;
 
 	int8 val = (int8)(_totalLevel - 3);
 	if (val < 0)
 		val = 0;
 
-	_totalLevel = (uint8) val;
+	_totalLevel = (uint8)val;
 	setOutputLevel();
 	return true;
 }
 
-bool TownsPC98_MusicChannel::control_fd_jump(uint8 para) {
-	uint8 *tmp = _drv->_trackPtr + READ_LE_UINT16(_dataPtr - 1);
-	_dataPtr = (tmp[1] == 1) ? tmp : (_dataPtr + 1);
-	return true;
-}
-
-bool TownsPC98_MusicChannel::control_dummy(uint8 para) {
-	_dataPtr--;
-	return true;
-}
-
 bool TownsPC98_MusicChannel::control_ff_endOfTrack(uint8 para) {
 	uint16 val = READ_LE_UINT16(--_dataPtr);
 	if (val) {
 		// loop
-		_dataPtr = _drv->_trackPtr + val;
+		_dataPtr = _driver->_trackPtr + val;
 		return true;
 	} else {
 		// quit parsing for active channel
 		--_dataPtr;
 		_flags |= CHS_EOT;
-		_drv->_finishedChannelsFlag |= _idFlag;
+		_driver->_finishedChannelsFlag |= _idFlag;
 		keyOff();
 		return false;
 	}
 }
 
-TownsPC98_MusicChannelSSG::TownsPC98_MusicChannelSSG(TownsPC98_AudioDriver *driver, uint8 regOffs,
-        uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) :
-	TownsPC98_MusicChannel(driver, regOffs, flgs, num, key, prt, id), controlEvents(0) {
+const uint8 TownsPC98_MusicChannel::_controlEventSize[16] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x05, 0x02, 0x06, 0x02, 0x00, 0x00, 0x02, 0x00, 0x02 };
+
+#define CONTROL(x) _controlEvents.push_back(new ControlEvent(this, &TownsPC98_MusicChannelSSG::control_##x))
+TownsPC98_MusicChannelSSG::TownsPC98_MusicChannelSSG(TownsPC98_AudioDriver *driver, uint8 regOffs, uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) :
+TownsPC98_MusicChannel(driver, regOffs, flgs, num, key, prt, id), _algorithm(0x80),
+	_ssgStartLvl(0), _ssgTl(0), _ssgStep(0), _ssgTicksLeft(0), _ssgTargetLvl(0) {
+	CONTROL(f0_setPatch);
+	CONTROL(f1_setTotalLevel);
+	CONTROL(f2_duration);
+	CONTROL(f3_pitchBend);
+	CONTROL(f4_setAlgorithm);
+	CONTROL(f5_tempo);
+	CONTROL(f6_repeatSection);
+	CONTROL(f7_setupVibrato);
+	CONTROL(f8_toggleVibrato);
+	CONTROL(f9_loadCustomPatch);
+	CONTROL(fa_writeReg);
+	CONTROL(fb_incOutLevel);
+	CONTROL(fc_decOutLevel);
+	CONTROL(fd_jump);
+	CONTROL(dummy);
+	CONTROL(ff_endOfTrack);
+
+	if (!_envPatchData) {
+		_envPatchData = new uint8[256];
+		memcpy(_envPatchData, _envData, 256);
+	}
 }
+#undef CONTROL
 
-void TownsPC98_MusicChannelSSG::init() {
-	_algorithm = 0x80;
+TownsPC98_MusicChannelSSG::~TownsPC98_MusicChannelSSG() {
+	for (Common::Array<const ControlEvent*>::iterator i = _controlEvents.begin(); i != _controlEvents.end(); ++i)
+		delete *i;
+	delete[] _envPatchData;
+	_envPatchData = 0;
+}
+
+void TownsPC98_MusicChannelSSG::reset() {
+	TownsPC98_MusicChannel::reset();
+	_ssgStartLvl = _ssgTl = _ssgStep = _ssgTicksLeft = _ssgTargetLvl = 0;
+
+	// Unlike the original we restore the default patch data. This fixes a bug
+	// where certain sound effects would bring each other out of tune (e.g. the
+	// dragon's fire in Darm's house in Kyra 1 would sound different each time
+	// you triggered another sfx by dropping an item etc.)
+	uint8 i = (10 + _regOffset) << 4;
+	const uint8 *src = _envData;
+	_envPatchData[i] = src[i];
+	_envPatchData[i + 3] = src[i + 3];
+	_envPatchData[i + 4] = src[i + 4];
+	_envPatchData[i + 6] = src[i + 6];
+	_envPatchData[i + 8] = src[i + 8];
+	_envPatchData[i + 12] = src[i + 12];
+}
 
-#define Control(x) &TownsPC98_MusicChannelSSG::control_##x
-	static const ControlEventFunc ctrlEventsSSG[] = {
-		Control(f0_setPatch),
-		Control(f1_setTotalLevel),
-		Control(f2_setKeyOffTime),
-		Control(f3_setFreqLSB),
-		Control(f4_setAlgorithm),
-		Control(f5_setTempo),
-		Control(f6_repeatSection),
-		Control(f7_setupVibrato),
-		Control(f8_toggleVibrato),
-		Control(f9_loadCustomPatch),
-		Control(fa_writeReg),
-		Control(fb_incOutLevel),
-		Control(fc_decOutLevel),
-		Control(fd_jump),
-		Control(dummy),
-		Control(ff_endOfTrack)
-	};
-#undef Control
-
-	controlEvents = ctrlEventsSSG;
+void TownsPC98_MusicChannelSSG::loadData(uint8 *data) {
+	_driver->preventRegisterWrite(_flags & CHS_PROTECT ? true : false);
+	TownsPC98_MusicChannel::loadData(data);
+	setOutputLevel(0);
+	_algorithm = 0x80;
 }
 
 void TownsPC98_MusicChannelSSG::processEvents() {
 	if (_flags & CHS_EOT)
 		return;
 
-	_drv->preventRegisterWrite(_flags & CHS_PROTECT ? true : false);
+	_driver->preventRegisterWrite(_flags & CHS_PROTECT ? true : false);
 
-	if (!_hold && _ticksLeft == _keyOffTime)
+	if (!_sustain && _ticksLeft == _duration)
 		nextShape();
 
 	if (!--_ticksLeft) {
@@ -637,23 +673,23 @@ void TownsPC98_MusicChannelSSG::processEvents() {
 
 		if (cmd == 0x80) {
 			nextShape();
-			_hold = false;
+			_sustain = false;
 		} else {
-			if (!_hold) {
+			if (!_sustain) {
 				_instr &= 0xf0;
-				_ssgStep = _drv->_ssgPatches[_instr];
-				_ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f;
-				_ssgTargetLvl = _drv->_ssgPatches[_instr + 2];
-				_ssgStartLvl = _drv->_ssgPatches[_instr + 3];
+				_ssgStep = _envPatchData[_instr];
+				_ssgTicksLeft = _envPatchData[_instr + 1] & 0x7f;
+				_ssgTargetLvl = _envPatchData[_instr + 2];
+				_ssgStartLvl = _envPatchData[_instr + 3];
 				_flags = (_flags & ~CHS_SSGOFF) | CHS_KEYOFF;
 			}
 
 			keyOn();
 
-			if (_hold == false || cmd != _frqBlockMSB)
+			if (_sustain == false || cmd != _frqBlockMSB)
 				_flags |= CHS_RECALCFREQ;
 
-			_hold = (para & 0x80) ? true : false;
+			_sustain = (para & 0x80) ? true : false;
 			_frqBlockMSB = cmd;
 		}
 
@@ -662,18 +698,18 @@ void TownsPC98_MusicChannelSSG::processEvents() {
 
 	if (!(_flags & CHS_SSGOFF)) {
 		if (--_ssgTicksLeft) {
-			if (!_drv->_fading)
+			if (!_driver->_fading)
 				setOutputLevel(_ssgStartLvl);
 			return;
 		}
 
-		_ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f;
+		_ssgTicksLeft = _envPatchData[_instr + 1] & 0x7f;
 
-		if (_drv->_ssgPatches[_instr + 1] & 0x80) {
+		if (_envPatchData[_instr + 1] & 0x80) {
 			uint8 t = _ssgStartLvl - _ssgStep;
 
 			if (_ssgStep <= _ssgStartLvl && _ssgTargetLvl < t) {
-				if (!_drv->_fading)
+				if (!_driver->_fading)
 					setOutputLevel(t);
 				return;
 			}
@@ -682,7 +718,7 @@ void TownsPC98_MusicChannelSSG::processEvents() {
 			uint8 p = (uint8)(t & 0xff);
 
 			if (t < 256 && _ssgTargetLvl > p) {
-				if (!_drv->_fading)
+				if (!_driver->_fading)
 					setOutputLevel(p);
 				return;
 			}
@@ -691,9 +727,9 @@ void TownsPC98_MusicChannelSSG::processEvents() {
 		setOutputLevel(_ssgTargetLvl);
 		if (_ssgStartLvl && !(_instr & 8)) {
 			_instr += 4;
-			_ssgStep = _drv->_ssgPatches[_instr];
-			_ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f;
-			_ssgTargetLvl = _drv->_ssgPatches[_instr + 2];
+			_ssgStep = _envPatchData[_instr];
+			_ssgTicksLeft = _envPatchData[_instr + 1] & 0x7f;
+			_ssgTargetLvl = _envPatchData[_instr + 2];
 		} else {
 			_flags |= CHS_SSGOFF;
 			setOutputLevel(0);
@@ -702,16 +738,18 @@ void TownsPC98_MusicChannelSSG::processEvents() {
 }
 
 void TownsPC98_MusicChannelSSG::processFrequency() {
+	static const uint16 noteFrequencies[] = { 0xee8, 0xe12, 0xd48, 0xc89, 0xbd5, 0xb2b, 0xa8a, 0x9f3, 0x964, 0x8dd, 0x85e, 0x7e6 };
+
 	if (_algorithm & 0x40)
 		return;
 
 	if (_flags & CHS_RECALCFREQ) {
 		_block = _frqBlockMSB >> 4;
-		_frequency = READ_LE_UINT16(&_drv->_opnFreqTableSSG[(_frqBlockMSB & 0x0f) << 1]) + _frqLSB;
+		_frequency = noteFrequencies[_frqBlockMSB & 0x0f] + _pitchBend;
 
 		uint16 f = _frequency >> _block;
-		_drv->writeReg(_part, _regOffset << 1, f & 0xff);
-		_drv->writeReg(_part, (_regOffset << 1) + 1, f >> 8);
+		writeReg(0, _regOffset << 1, f & 0xff);
+		writeReg(0, (_regOffset << 1) + 1, f >> 8);
 
 		setupVibrato();
 	}
@@ -721,21 +759,30 @@ void TownsPC98_MusicChannelSSG::processFrequency() {
 			return;
 
 		uint16 f = _frequency >> _block;
-		_drv->writeReg(_part, _regOffset << 1, f & 0xff);
-		_drv->writeReg(_part, (_regOffset << 1) + 1, f >> 8);
+		writeReg(0, _regOffset << 1, f & 0xff);
+		writeReg(0, (_regOffset << 1) + 1, f >> 8);
 	}
 }
 
-bool TownsPC98_MusicChannelSSG::processControlEvent(uint8 cmd) {
-	uint8 para = *_dataPtr++;
-	return (this->*controlEvents[cmd & 0x0f])(para);
+void TownsPC98_MusicChannelSSG::protect() {
+	_flags |= CHS_PROTECT;
 }
 
-void TownsPC98_MusicChannelSSG::nextShape() {
-	_instr = (_instr & 0xf0) + 0x0c;
-	_ssgStep = _drv->_ssgPatches[_instr];
-	_ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f;
-	_ssgTargetLvl = _drv->_ssgPatches[_instr + 2];
+void TownsPC98_MusicChannelSSG::restore() {
+	_flags &= ~CHS_PROTECT;
+	keyOn();
+	writeReg(0, 8 + _regOffset, _ssgTl);
+	uint16 f = _frequency >> _block;
+	writeReg(0, _regOffset << 1, f & 0xff);
+	writeReg(0, (_regOffset << 1) + 1, f >> 8);
+}
+
+void TownsPC98_MusicChannelSSG::fadeStep() {
+	_fading = true;
+	_totalLevel--;
+	if ((int8)_totalLevel < 0)
+		_totalLevel = 0;
+	setOutputLevel(_ssgStartLvl);
 }
 
 void TownsPC98_MusicChannelSSG::keyOn() {
@@ -748,30 +795,17 @@ void TownsPC98_MusicChannelSSG::keyOn() {
 	t = (t << (_regOffset + 1)) | (t >> (7 - _regOffset));
 
 	if (!(_algorithm & 0x80))
-		_drv->writeReg(_part, 6, _algorithm & 0x7f);
+		writeReg(0, 6, _algorithm & 0x7f);
 
-	uint8 e = (_drv->_pc98a->readReg(0, 7) & c) | t;
-	_drv->writeReg(_part, 7, e);
+	uint8 e = (readReg(0, 7) & c) | t;
+	writeReg(0, 7, e);
 }
 
-void TownsPC98_MusicChannelSSG::protect() {
-	_flags |= CHS_PROTECT;
-}
-
-void TownsPC98_MusicChannelSSG::restore() {
-	_flags &= ~CHS_PROTECT;
-	keyOn();
-	_drv->writeReg(_part, 8 + _regOffset, _ssgTl);
-	uint16 f = _frequency >> _block;
-	_drv->writeReg(_part, _regOffset << 1, f & 0xff);
-	_drv->writeReg(_part, (_regOffset << 1) + 1, f >> 8);
-}
-
-void TownsPC98_MusicChannelSSG::loadData(uint8 *data) {
-	_drv->preventRegisterWrite(_flags & CHS_PROTECT ? true : false);
-	TownsPC98_MusicChannel::loadData(data);
-	setOutputLevel(0);
-	_algorithm = 0x80;
+void TownsPC98_MusicChannelSSG::nextShape() {
+	_instr = (_instr & 0xf0) + 0x0c;
+	_ssgStep = _envPatchData[_instr];
+	_ssgTicksLeft = _envPatchData[_instr + 1] & 0x7f;
+	_ssgTargetLvl = _envPatchData[_instr + 2];
 }
 
 void TownsPC98_MusicChannelSSG::setOutputLevel(uint8 lvl) {
@@ -780,31 +814,12 @@ void TownsPC98_MusicChannelSSG::setOutputLevel(uint8 lvl) {
 	if (newTl == _ssgTl)
 		return;
 	_ssgTl = newTl;
-	_drv->writeReg(_part, 8 + _regOffset, _ssgTl);
-}
-
-void TownsPC98_MusicChannelSSG::reset() {
-	TownsPC98_MusicChannel::reset();
-
-	// Unlike the original we restore the default patch data. This fixes a bug
-	// where certain sound effects would bring each other out of tune (e.g. the
-	// dragon's fire in Darm's house in Kyra 1 would sound different each time
-	// you triggered another sfx by dropping an item etc.)
-	uint8 i = (10 + _regOffset) << 4;
-	const uint8 *src = &_drv->_drvTables[156];
-	_drv->_ssgPatches[i] = src[i];
-	_drv->_ssgPatches[i + 3] = src[i + 3];
-	_drv->_ssgPatches[i + 4] = src[i + 4];
-	_drv->_ssgPatches[i + 6] = src[i + 6];
-	_drv->_ssgPatches[i + 8] = src[i + 8];
-	_drv->_ssgPatches[i + 12] = src[i + 12];
+	writeReg(0, 8 + _regOffset, _ssgTl);
 }
 
-void TownsPC98_MusicChannelSSG::fadeStep() {
-	_totalLevel--;
-	if ((int8)_totalLevel < 0)
-		_totalLevel = 0;
-	setOutputLevel(_ssgStartLvl);
+bool TownsPC98_MusicChannelSSG::processControlEvent(uint8 cmd) {
+	uint8 para = *_dataPtr++;
+	return (*_controlEvents[cmd & 0x0f])(para);
 }
 
 bool TownsPC98_MusicChannelSSG::control_f0_setPatch(uint8 para) {
@@ -816,7 +831,7 @@ bool TownsPC98_MusicChannelSSG::control_f0_setPatch(uint8 para) {
 }
 
 bool TownsPC98_MusicChannelSSG::control_f1_setTotalLevel(uint8 para) {
-	if (!_drv->_fading)
+	if (!_fading)
 		_totalLevel = para;
 	return true;
 }
@@ -827,19 +842,19 @@ bool TownsPC98_MusicChannelSSG::control_f4_setAlgorithm(uint8 para) {
 }
 
 bool TownsPC98_MusicChannelSSG::control_f9_loadCustomPatch(uint8 para) {
-	_instr = (_drv->_sfxOffs + 10 + _regOffset) << 4;
-	_drv->_ssgPatches[_instr] = *_dataPtr++;
-	_drv->_ssgPatches[_instr + 3] = para;
-	_drv->_ssgPatches[_instr + 4] = *_dataPtr++;
-	_drv->_ssgPatches[_instr + 6] = *_dataPtr++;
-	_drv->_ssgPatches[_instr + 8] = *_dataPtr++;
-	_drv->_ssgPatches[_instr + 12] = *_dataPtr++;
+	_instr = (_driver->_sfxOffs + 10 + _regOffset) << 4;
+	_envPatchData[_instr] = *_dataPtr++;
+	_envPatchData[_instr + 3] = para;
+	_envPatchData[_instr + 4] = *_dataPtr++;
+	_envPatchData[_instr + 6] = *_dataPtr++;
+	_envPatchData[_instr + 8] = *_dataPtr++;
+	_envPatchData[_instr + 12] = *_dataPtr++;
 	return true;
 }
 
 bool TownsPC98_MusicChannelSSG::control_fb_incOutLevel(uint8 para) {
 	_dataPtr--;
-	if (_drv->_fading)
+	if (_fading)
 		return true;
 
 	_totalLevel--;
@@ -851,7 +866,7 @@ bool TownsPC98_MusicChannelSSG::control_fb_incOutLevel(uint8 para) {
 
 bool TownsPC98_MusicChannelSSG::control_fc_decOutLevel(uint8 para) {
 	_dataPtr--;
-	if (_drv->_fading)
+	if (_fading)
 		return true;
 
 	if (_totalLevel + 1 < 0x10)
@@ -861,30 +876,68 @@ bool TownsPC98_MusicChannelSSG::control_fc_decOutLevel(uint8 para) {
 }
 
 bool TownsPC98_MusicChannelSSG::control_ff_endOfTrack(uint8 para) {
-	if (!_drv->_sfxOffs) {
+	if (!_driver->_sfxOffs) {
 		uint16 val = READ_LE_UINT16(--_dataPtr);
 		if (val) {
 			// loop
-			_dataPtr = _drv->_trackPtr + val;
+			_dataPtr = _driver->_trackPtr + val;
 			return true;
 		} else {
 			// stop parsing
-			if (!_drv->_fading)
+			if (!_driver->_fading)
 				setOutputLevel(0);
 			--_dataPtr;
 			_flags |= CHS_EOT;
-			_drv->_finishedSSGFlag |= _idFlag;
+			_driver->_finishedSSGFlag |= _idFlag;
 		}
 	} else {
 		// end of sfx track - restore ssg music channel
 		_flags |= CHS_EOT;
-		_drv->_finishedSfxFlag |= _idFlag;
-		_drv->_ssgChannels[_chanNum]->restore();
+		_driver->_finishedSfxFlag |= _idFlag;
+		_driver->_ssgChannels[_chanNum]->restore();
 	}
 
 	return false;
 }
 
+uint8 *TownsPC98_MusicChannelSSG::_envPatchData = 0;
+
+const uint8 TownsPC98_MusicChannelSSG::_envData[256] = {
+	0x00, 0x00, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00,
+	0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00,
+	0x00, 0x01, 0xFF, 0xFF, 0x37, 0x81, 0xC8, 0x00,
+	0x00, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
+	0x00, 0x01, 0xFF, 0xFF, 0x37, 0x81, 0xC8, 0x00,
+	0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
+	0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00,
+	0x00, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
+	0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00,
+	0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
+	0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00,
+	0x04, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
+	0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00,
+	0x0A, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
+	0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0x01, 0x00,
+	0xFF, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00,
+	0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0x00,
+	0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
+	0x64, 0x01, 0xFF, 0x64, 0xFF, 0x81, 0xFF, 0x00,
+	0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
+
+	0x02, 0x01, 0xFF, 0x28, 0xFF, 0x81, 0xF0, 0x00,
+	0x00, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
+	0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xC8, 0x00,
+	0x01, 0x81, 0x00, 0x00, 0x28, 0x81, 0x00, 0x00,
+	0x00, 0x01, 0xFF, 0x78, 0x5F, 0x81, 0xA0, 0x00,
+	0x05, 0x81, 0x00, 0x00, 0x28, 0x81, 0x00, 0x00,
+	0x00, 0x01, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00,
+	0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00,
+	0x00, 0x01, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00,
+	0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00,
+	0x00, 0x01, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00,
+	0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00
+};
+
 void TownsPC98_SfxChannel::loadData(uint8 *data) {
 	_flags = CHS_ALLOFF;
 	_ticksLeft = 1;
@@ -904,59 +957,55 @@ void TownsPC98_SfxChannel::loadData(uint8 *data) {
 			tmp[0] = tmp[1];
 			tmp += 4;
 		} else {
-			tmp += _drv->_opnFxCmdLen[cmd - 240];
+			tmp += _controlEventSize[cmd - 240];
 		}
 	}
 }
 
 void TownsPC98_SfxChannel::reset() {
 	TownsPC98_MusicChannel::reset();
+	_ssgStartLvl = _ssgTl = _ssgStep = _ssgTicksLeft = _ssgTargetLvl = 0;
 
 	// Unlike the original we restore the default patch data. This fixes a bug
 	// where certain sound effects would bring each other out of tune (e.g. the
 	// dragon's fire in Darm's house in Kyra 1 would sound different each time
 	// you triggered another sfx by dropping an item etc.)
 	uint8 i = (13 + _regOffset) << 4;
-	const uint8 *src = &_drv->_drvTables[156];
-	_drv->_ssgPatches[i] = src[i];
-	_drv->_ssgPatches[i + 3] = src[i + 3];
-	_drv->_ssgPatches[i + 4] = src[i + 4];
-	_drv->_ssgPatches[i + 6] = src[i + 6];
-	_drv->_ssgPatches[i + 8] = src[i + 8];
-	_drv->_ssgPatches[i + 12] = src[i + 12];
+	const uint8 *src = _envData;
+	_envPatchData[i] = src[i];
+	_envPatchData[i + 3] = src[i + 3];
+	_envPatchData[i + 4] = src[i + 4];
+	_envPatchData[i + 6] = src[i + 6];
+	_envPatchData[i + 8] = src[i + 8];
+	_envPatchData[i + 12] = src[i + 12];
 }
 
 #ifndef DISABLE_PC98_RHYTHM_CHANNEL
-TownsPC98_MusicChannelPCM::TownsPC98_MusicChannelPCM(TownsPC98_AudioDriver *driver, uint8 regOffs,
-        uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) :
-	TownsPC98_MusicChannel(driver, regOffs, flgs, num, key, prt, id), controlEvents(0) {
-}
-
-void TownsPC98_MusicChannelPCM::init() {
-	_algorithm = 0x80;
-
-#define Control(x) &TownsPC98_MusicChannelPCM::control_##x
-	static const ControlEventFunc ctrlEventsPCM[] = {
-		Control(dummy),
-		Control(f1_prcStart),
-		Control(dummy),
-		Control(dummy),
-		Control(dummy),
-		Control(dummy),
-		Control(f6_repeatSection),
-		Control(dummy),
-		Control(dummy),
-		Control(dummy),
-		Control(fa_writeReg),
-		Control(dummy),
-		Control(dummy),
-		Control(dummy),
-		Control(dummy),
-		Control(ff_endOfTrack)
-	};
-#undef Control
-
-	controlEvents = ctrlEventsPCM;
+#define CONTROL(x) _controlEvents.push_back(new ControlEvent(this, &TownsPC98_MusicChannelPCM::control_##x))
+TownsPC98_MusicChannelPCM::TownsPC98_MusicChannelPCM(TownsPC98_AudioDriver *driver, uint8 regOffs, uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) :
+TownsPC98_MusicChannel(driver, regOffs, flgs, num, key, prt, id), _algorithm(0x80) {
+	CONTROL(dummy);
+	CONTROL(f1_prcStart);
+	CONTROL(dummy);
+	CONTROL(dummy);
+	CONTROL(dummy);
+	CONTROL(dummy);
+	CONTROL(f6_repeatSection);
+	CONTROL(dummy);
+	CONTROL(dummy);
+	CONTROL(dummy);
+	CONTROL(fa_writeReg);
+	CONTROL(dummy);
+	CONTROL(dummy);
+	CONTROL(dummy);
+	CONTROL(dummy);
+	CONTROL(ff_endOfTrack);
+}
+#undef CONTROL
+
+TownsPC98_MusicChannelPCM::~TownsPC98_MusicChannelPCM() {
+	for (Common::Array<const ControlEvent*>::iterator i = _controlEvents.begin(); i != _controlEvents.end(); ++i)
+		delete *i;
 }
 
 void TownsPC98_MusicChannelPCM::loadData(uint8 *data) {
@@ -981,7 +1030,7 @@ void TownsPC98_MusicChannelPCM::processEvents()  {
 		if (cmd == 0x80) {
 			loop = false;
 		} else if (cmd < 0xf0) {
-			_drv->writeReg(_part, 0x10, cmd);
+			writeReg(0, 0x10, cmd);
 		} else if (!processControlEvent(cmd)) {
 			return;
 		}
@@ -992,12 +1041,12 @@ void TownsPC98_MusicChannelPCM::processEvents()  {
 
 bool TownsPC98_MusicChannelPCM::processControlEvent(uint8 cmd) {
 	uint8 para = *_dataPtr++;
-	return (this->*controlEvents[cmd & 0x0f])(para);
+	return (*_controlEvents[cmd & 0x0f])(para);
 }
 
 bool TownsPC98_MusicChannelPCM::control_f1_prcStart(uint8 para) {
 	_totalLevel = para;
-	_drv->writeReg(_part, 0x11, para);
+	writeReg(0, 0x11, para);
 	return true;
 }
 
@@ -1005,13 +1054,13 @@ bool TownsPC98_MusicChannelPCM::control_ff_endOfTrack(uint8 para) {
 	uint16 val = READ_LE_UINT16(--_dataPtr);
 	if (val) {
 		// loop
-		_dataPtr = _drv->_trackPtr + val;
+		_dataPtr = _driver->_trackPtr + val;
 		return true;
 	} else {
 		// quit parsing for active channel
 		--_dataPtr;
 		_flags |= CHS_EOT;
-		_drv->_finishedRhythmFlag |= _idFlag;
+		_driver->_finishedRhythmFlag |= _idFlag;
 		return false;
 	}
 }
@@ -1022,12 +1071,8 @@ TownsPC98_AudioDriver::TownsPC98_AudioDriver(Audio::Mixer *mixer, EmuType type)
 #ifndef DISABLE_PC98_RHYTHM_CHANNEL
 	_rhythmChannel(0),
 #endif
-	_trackPtr(0), _sfxData(0), _sfxOffs(0), _ssgPatches(0),
-	_patches(0), _sfxBuffer(0), _musicBuffer(0),
-
-	_opnCarrier(_drvTables + 76), _opnFreqTable(_drvTables + 108), _opnFreqTableSSG(_drvTables + 132),
-	_opnFxCmdLen(_drvTables + 36), _opnLvlPresets(_drvTables + (type == kTypeTowns ? 52 : 84)),
-
+	_sfxData(0), _sfxOffs(0), _patchData(0), _sfxBuffer(0), _musicBuffer(0), _trackPtr(0),
+	_levelPresets(type == kTypeTowns ? _levelPresetFMTOWNS : _levelPresetPC98),
 	_updateChannelsFlag(type == kType26 ? 0x07 : 0x3F), _finishedChannelsFlag(0),
 	_updateSSGFlag(type == kTypeTowns ? 0x00 : 0x07), _finishedSSGFlag(0),
 	_updateRhythmFlag(type == kType86 ?
@@ -1069,8 +1114,6 @@ TownsPC98_AudioDriver::~TownsPC98_AudioDriver() {
 #ifndef DISABLE_PC98_RHYTHM_CHANNEL
 	delete _rhythmChannel;
 #endif
-
-	delete[] _ssgPatches;
 }
 
 bool TownsPC98_AudioDriver::init() {
@@ -1087,37 +1130,29 @@ bool TownsPC98_AudioDriver::init() {
 	_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();
+		_channels[i] = new TownsPC98_MusicChannel(this, _channelPreset[ii], _channelPreset[ii + 1],
+		        _channelPreset[ii + 2], _channelPreset[ii + 3], _channelPreset[ii + 4], _channelPreset[ii + 5]);
 	}
 
 	if (_numChanSSG) {
-		_ssgPatches = new uint8[256];
-		memcpy(_ssgPatches, _drvTables + 156, 256);
-
 		_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]);
-			_ssgChannels[i]->init();
+			_ssgChannels[i] = new TownsPC98_MusicChannelSSG(this, _channelPreset[ii], _channelPreset[ii + 1],
+			        _channelPreset[ii + 2], _channelPreset[ii + 3], _channelPreset[ii + 4], _channelPreset[ii + 5]);
 		}
 
 		_sfxChannels = new TownsPC98_SfxChannel *[2];
 		for (int i = 0; i < 2; i++) {
 			int ii = (i + 1) * 6;
-			_sfxChannels[i] = new TownsPC98_SfxChannel(this, _drvTables[ii], _drvTables[ii + 1],
-			        _drvTables[ii + 2], _drvTables[ii + 3], _drvTables[ii + 4], _drvTables[ii + 5]);
-			_sfxChannels[i]->init();
+			_sfxChannels[i] = new TownsPC98_SfxChannel(this, _channelPreset[ii], _channelPreset[ii + 1],
+			        _channelPreset[ii + 2], _channelPreset[ii + 3], _channelPreset[ii + 4], _channelPreset[ii + 5]);
 		}
 	}
 
 #ifndef DISABLE_PC98_RHYTHM_CHANNEL
-	if (_numChanRHY) {
+	if (_numChanRHY)
 		_rhythmChannel = new TownsPC98_MusicChannelPCM(this, 0, 0, 0, 0, 0, 1);
-		_rhythmChannel->init();
-	}
 #endif
 
 	setMusicTempo(84);
@@ -1142,7 +1177,7 @@ void TownsPC98_AudioDriver::loadMusicData(uint8 *data, bool loadPaused) {
 	reset();
 
 	PC98AudioCore::MutexLock lock = _pc98a->stackLockMutex();
-	uint8 *src_a = _trackPtr = _musicBuffer = data;
+	const uint8 *src_a = _trackPtr = _musicBuffer = data;
 
 	for (uint8 i = 0; i < 3; i++) {
 		_channels[i]->loadData(data + READ_LE_UINT16(src_a));
@@ -1168,7 +1203,7 @@ void TownsPC98_AudioDriver::loadMusicData(uint8 *data, bool loadPaused) {
 
 	preventRegisterWrite(false);
 
-	_patches = src_a + 4;
+	_patchData = src_a + 4;
 	_finishedChannelsFlag = _finishedSSGFlag = _finishedRhythmFlag = 0;
 
 	_musicPlaying = !loadPaused;
@@ -1203,7 +1238,7 @@ void TownsPC98_AudioDriver::reset() {
 
 	_musicPlaying = false;
 	_sfxPlaying = false;
-	_fading = false;
+	_fading = 0;
 	_looping = 0;
 	_musicTickCounter = 0;
 	_sfxData = 0;
@@ -1218,8 +1253,6 @@ void TownsPC98_AudioDriver::reset() {
 	if (_numChanSSG) {
 		for (int i = 0; i < 2; i++)
 			_sfxChannels[i]->reset();
-
-		memcpy(_ssgPatches, _drvTables + 156, 256);
 	}
 
 #ifndef DISABLE_PC98_RHYTHM_CHANNEL
@@ -1264,11 +1297,11 @@ void TownsPC98_AudioDriver::cont() {
 	_musicPlaying = true;
 }
 
-bool TownsPC98_AudioDriver::looping() {
+bool TownsPC98_AudioDriver::looping() const {
 	return _looping == _updateChannelsFlag ? true : false;
 }
 
-bool TownsPC98_AudioDriver::musicPlaying() {
+bool TownsPC98_AudioDriver::musicPlaying() const {
 	return _musicPlaying;
 }
 
@@ -1280,6 +1313,10 @@ void TownsPC98_AudioDriver::setSoundEffectVolume(int volume) {
 	_pc98a->setSoundEffectVolume(volume);
 }
 
+uint8 TownsPC98_AudioDriver::readReg(uint8 part, uint8 reg) {
+	return _pc98a->readReg(part, reg);
+}
+
 void TownsPC98_AudioDriver::writeReg(uint8 part, uint8 reg, uint8 val) {
 	if (!_regWriteProtect)
 		_pc98a->writeReg(part, reg, val);
@@ -1377,76 +1414,24 @@ void TownsPC98_AudioDriver::setSfxTempo(uint16 tempo) {
 	writeReg(0, 0x25, tempo >> 8);
 	writeReg(0, 0x27, 0x33);
 }
-const uint8 TownsPC98_AudioDriver::_drvTables[] = {
-	//  channel presets
+
+const uint8 TownsPC98_AudioDriver::_channelPreset[36] = {
 	0x00, 0x80, 0x00, 0x00, 0x00, 0x01,
 	0x01, 0x80, 0x01, 0x01, 0x00, 0x02,
 	0x02, 0x80, 0x02, 0x02, 0x00, 0x04,
 	0x00, 0x80, 0x03, 0x04, 0x01, 0x08,
 	0x01, 0x80, 0x04, 0x05, 0x01, 0x10,
-	0x02, 0x80, 0x05, 0x06, 0x01, 0x20,
-
-	//  control event size
-	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x05,
-	0x02, 0x06, 0x02, 0x00, 0x00, 0x02, 0x00, 0x02,
+	0x02, 0x80, 0x05, 0x06, 0x01, 0x20
+};
 
-	//  fmt level presets
+const uint8 TownsPC98_AudioDriver::_levelPresetFMTOWNS[24] = {
 	0x54, 0x50, 0x4C, 0x48, 0x44, 0x40, 0x3C, 0x38,
 	0x34, 0x30, 0x2C, 0x28, 0x24, 0x20, 0x1C, 0x18,
-	0x14, 0x10, 0x0C, 0x08, 0x04, 0x90, 0x90, 0x90,
-
-	//  carriers
-	0x08, 0x08, 0x08, 0x08, 0x0C, 0x0E, 0x0E, 0x0F,
+	0x14, 0x10, 0x0C, 0x08, 0x04, 0x90, 0x90, 0x90
+};
 
-	//  pc98 level presets
+const uint8 TownsPC98_AudioDriver::_levelPresetPC98[24] = {
 	0x40, 0x3B, 0x38, 0x34, 0x30, 0x2A, 0x28, 0x25,
 	0x22, 0x20, 0x1D, 0x1A, 0x18, 0x15, 0x12, 0x10,
-	0x0D, 0x0A, 0x08, 0x05, 0x02, 0x90, 0x90, 0x90,
-
-	//  frequencies
-	0x6A, 0x02, 0x8F, 0x02, 0xB6, 0x02, 0xDF, 0x02,
-	0x0B, 0x03, 0x39, 0x03, 0x6A, 0x03, 0x9E, 0x03,
-	0xD5, 0x03, 0x10, 0x04, 0x4E, 0x04, 0x8F, 0x04,
-
-	//  ssg frequencies
-	0xE8, 0x0E, 0x12, 0x0E, 0x48, 0x0D, 0x89, 0x0C,
-	0xD5, 0x0B, 0x2B, 0x0B, 0x8A, 0x0A, 0xF3, 0x09,
-	0x64, 0x09, 0xDD, 0x08, 0x5E, 0x08, 0xE6, 0x07,
-
-	// ssg patch data
-	0x00, 0x00, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00,
-	0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00,
-	0x00, 0x01, 0xFF, 0xFF, 0x37, 0x81, 0xC8, 0x00,
-	0x00, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
-	0x00, 0x01, 0xFF, 0xFF, 0x37, 0x81, 0xC8, 0x00,
-	0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
-	0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00,
-	0x00, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
-	0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00,
-	0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
-	0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00,
-	0x04, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
-	0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00,
-	0x0A, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
-	0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0x01, 0x00,
-	0xFF, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00,
-	0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0x00,
-	0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
-	0x64, 0x01, 0xFF, 0x64, 0xFF, 0x81, 0xFF, 0x00,
-	0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
-
-	0x02, 0x01, 0xFF, 0x28, 0xFF, 0x81, 0xF0, 0x00,
-	0x00, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
-	0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xC8, 0x00,
-	0x01, 0x81, 0x00, 0x00, 0x28, 0x81, 0x00, 0x00,
-	0x00, 0x01, 0xFF, 0x78, 0x5F, 0x81, 0xA0, 0x00,
-	0x05, 0x81, 0x00, 0x00, 0x28, 0x81, 0x00, 0x00,
-	0x00, 0x01, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00,
-	0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00,
-	0x00, 0x01, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00,
-	0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00,
-	0x00, 0x01, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00,
-	0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00
+	0x0D, 0x0A, 0x08, 0x05, 0x02, 0x90, 0x90, 0x90
 };
-
-#undef EUPHONY_FADEOUT_TICKS
diff --git a/audio/softsynth/fmtowns_pc98/towns_pc98_driver.h b/audio/softsynth/fmtowns_pc98/towns_pc98_driver.h
index 0b9edcf..448bb2a 100644
--- a/audio/softsynth/fmtowns_pc98/towns_pc98_driver.h
+++ b/audio/softsynth/fmtowns_pc98/towns_pc98_driver.h
@@ -53,13 +53,14 @@ public:
 	void pause();
 	void cont();
 
-	bool looping();
-	bool musicPlaying();
+	bool looping() const;
+	bool musicPlaying() const;
 
 	void setMusicVolume(int volume);
 	void setSoundEffectVolume(int volume);
 
 private:
+	uint8 readReg(uint8 part, uint8 reg);
 	void writeReg(uint8 part, uint8 reg, uint8 val);
 	void preventRegisterWrite(bool prevent);
 
@@ -78,17 +79,9 @@ private:
 	TownsPC98_MusicChannelPCM *_rhythmChannel;
 #endif
 
-	const uint8 *_opnCarrier;
-	const uint8 *_opnFreqTable;
-	const uint8 *_opnFreqTableSSG;
-	const uint8 *_opnFxCmdLen;
-	const uint8 *_opnLvlPresets;
-
 	uint8 *_musicBuffer;
 	uint8 *_sfxBuffer;
-	uint8 *_trackPtr;
-	uint8 *_patches;
-	uint8 *_ssgPatches;
+	const uint8 *_patchData;
 
 	uint8 _updateChannelsFlag;
 	uint8 _updateSSGFlag;
@@ -109,14 +102,19 @@ private:
 	uint8 *_sfxData;
 	uint16 _sfxOffsets[2];
 
+	uint8 *_trackPtr;
 	bool _regWriteProtect;
+
 	PC98AudioCore *_pc98a;
 
 	const int _numChanFM;
 	const int _numChanSSG;
 	const int _numChanRHY;
 
-	static const uint8 _drvTables[];
+	static const uint8 _channelPreset[36];
+	static const uint8 _levelPresetFMTOWNS[24];
+	static const uint8 _levelPresetPC98[24];
+	const uint8 *_levelPresets;
 
 	bool _ready;
 };


Commit: b789d50a55cc22e481d286a1038d3ec4092b23ec
    https://github.com/scummvm/scummvm/commit/b789d50a55cc22e481d286a1038d3ec4092b23ec
Author: athrxx (athrxx at scummvm.org)
Date: 2019-03-07T21:28:35+01:00

Commit Message:
AUDIO: (FM-TOWNS/PC-98) - fix valgrind warning

(This didn't come up with the targets supported until now, but it does come up with SCI PC-98 music)

Changed paths:
    audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp


diff --git a/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp b/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp
index b73030e..e3d86b3 100644
--- a/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp
+++ b/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp
@@ -1317,17 +1317,15 @@ void TownsPC98_FmSynth::setLevelSSG(int vol) {
 
 void TownsPC98_FmSynth::generateTables() {
 	delete[] _oprRates;
-	_oprRates = new uint8[128];
+	_oprRates = new uint8[130];
 
 	WRITE_BE_UINT32(_oprRates + 32, _numChan == 6 ? 0x90900000 : 0x00081018);
 	WRITE_BE_UINT32(_oprRates + 36, _numChan == 6 ? 0x00001010 : 0x00081018);
 	memset(_oprRates, 0x90, 32);
-	memset(&_oprRates[96], 0x80, 32);
+	memset(&_oprRates[96], 0x80, 34);
 	uint8 *dst = (uint8 *)_oprRates + 40;
 	for (int i = 0; i < 40; i += 4)
 		WRITE_BE_UINT32(dst + i, 0x00081018);
-	for (int i = 0; i < 48; i += 4)
-		WRITE_BE_UINT32(dst + i, 0x00081018);
 	dst += 40;
 	for (uint8 i = 0; i < 16; i ++) {
 		uint8 v = (i < 12) ? i : 12;
@@ -1335,8 +1333,8 @@ void TownsPC98_FmSynth::generateTables() {
 	}
 
 	delete[] _oprRateshift;
-	_oprRateshift = new uint8[128];
-	memset(_oprRateshift, 0, 128);
+	_oprRateshift = new uint8[130];
+	memset(_oprRateshift, 0, 130);
 	dst = (uint8 *)_oprRateshift + 32;
 	for (int i = 11; i; i--) {
 		memset(dst, i, 4);


Commit: 49e85f64cf0470439de59ea87e342986b9779efc
    https://github.com/scummvm/scummvm/commit/49e85f64cf0470439de59ea87e342986b9779efc
Author: athrxx (athrxx at scummvm.org)
Date: 2019-03-07T21:28:41+01:00

Commit Message:
AUDIO: (FM-TOWNS/PC-98) - allow individual operator frequencies

(also add some sanity checks and make some more adjustments for SCI audio driver)

Changed paths:
    audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp
    audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h


diff --git a/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp b/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp
index e3d86b3..ce006b1 100644
--- a/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp
+++ b/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp
@@ -34,7 +34,8 @@ public:
 
 	void keyOn();
 	void keyOff();
-	void frequency(int freq);
+	void frequencyHi(uint8 frqH);
+	void frequencyLo(uint8 frqL);
 	void updatePhaseIncrement();
 	void recalculateRates();
 	void generateOutput(int32 phasebuf, int32 *feedbuf, int32 &out);
@@ -48,11 +49,14 @@ public:
 	void sustainRate(uint32 value);
 	void sustainLevel(uint32 value);
 	void releaseRate(uint32 value);
+	void envelopeShape(uint32 value);
 	void totalLevel(uint32 value);
 	void ampModulation(bool enable);
 	void reset();
 
 protected:
+	void frequency(int freq);
+
 	EnvelopeState _state;
 	bool _holdKey;
 	uint32 _feedbackLevel;
@@ -64,14 +68,18 @@ protected:
 	uint32 _specifiedDecayRate;
 	uint32 _specifiedSustainRate;
 	uint32 _specifiedReleaseRate;
+	uint32 _envelopeShapeSpecs;
 	uint32 _tickCount;
 	uint32 _sustainLevel;
 
 	bool _ampMod;
 	uint32 _frequency;
+	uint16 _freqTemp;
 	uint8 _kcode;
 	uint32 _phase;
 	uint32 _phaseIncrement;
+	uint32 _shapeState;
+	uint8 _shapeScale;
 	const int32 *_detn;
 
 	const uint8 *_rateTbl;
@@ -98,9 +106,9 @@ TownsPC98_FmSynthOperator::TownsPC98_FmSynthOperator(const uint32 timerbase, con
 		const uint32 *frqTable, const uint32 *sineTable, const int32 *tlevelOut, const int32 *detuneTable) :
 	_rtt(rtt), _rateTbl(rateTable), _rshiftTbl(shiftTable), _adTbl(attackDecayTable), _fTbl(frqTable),
 	_sinTbl(sineTable), _tLvlTbl(tlevelOut), _detnTbl(detuneTable), _tickLength(timerbase * 2),
-	_specifiedAttackRate(0), _specifiedDecayRate(0), _specifiedReleaseRate(0), _specifiedSustainRate(0),
-	_sustainLevel(0), _phase(0), _state(kEnvReady), _holdKey(false), _timer(0), _keyScale1(0),
-	_keyScale2(0), _currentLevel(1023), _ampMod(false), _tickCount(0) {
+	_specifiedAttackRate(0), _specifiedDecayRate(0), _specifiedReleaseRate(0), _envelopeShapeSpecs(0), _specifiedSustainRate(0),
+	_sustainLevel(0), _phase(0), _shapeState(0), _shapeScale(0), _state(kEnvReady), _holdKey(false), _timer(0), _keyScale1(0),
+	_keyScale2(0), _freqTemp(0), _currentLevel(1023), _ampMod(false), _tickCount(0), _phaseIncrement(0) {
 
 	fs_a.rate = fs_a.shift = fs_d.rate = fs_d.shift = fs_s.rate = fs_s.shift = fs_r.rate = fs_r.shift = 0;
 
@@ -114,6 +122,7 @@ void TownsPC98_FmSynthOperator::keyOn() {
 	_holdKey = true;
 	_state = kEnvAttacking;
 	_phase = 0;
+	_shapeState = _envelopeShapeSpecs;
 }
 
 void TownsPC98_FmSynthOperator::keyOff() {
@@ -125,6 +134,15 @@ void TownsPC98_FmSynthOperator::keyOff() {
 		_state = kEnvReleasing;
 }
 
+void TownsPC98_FmSynthOperator::frequencyHi(uint8 frqH) {
+	_freqTemp = (_freqTemp & 0xff) | ((frqH & 0x3F) << 8);
+}
+
+void TownsPC98_FmSynthOperator::frequencyLo(uint8 frqL) {
+	_freqTemp = (_freqTemp & 0xff00) | frqL;
+	frequency(_freqTemp);
+}
+
 void TownsPC98_FmSynthOperator::frequency(int freq) {
 	uint8 block = (freq >> 11);
 	uint16 pos = (freq & 0x7ff);
@@ -132,6 +150,7 @@ void TownsPC98_FmSynthOperator::frequency(int freq) {
 
 	_kcode = (block << 2) | ((c < 7) ? 0 : ((c > 8) ? 3 : c - 6));
 	_frequency = _fTbl[pos << 1] >> (7 - block);
+	_freqTemp = 0;
 }
 
 void TownsPC98_FmSynthOperator::updatePhaseIncrement() {
@@ -196,13 +215,13 @@ void TownsPC98_FmSynthOperator::generateOutput(int32 phasebuf, int32 *feed, int3
 				targetTime = (1 << fs_d.shift) - 1;
 				nextState = kEnvSustaining;
 				targetLevel = _sustainLevel;
-				levelIncrement = _adTbl[fs_d.rate + ((_tickCount >> fs_d.shift) & 7)];
+				levelIncrement = _adTbl[fs_d.rate + ((_tickCount >> fs_d.shift) & 7)] << _shapeScale;
 				break;
 			case kEnvSustaining:
 				targetTime = (1 << fs_s.shift) - 1;
 				nextState = kEnvSustaining;
-				targetLevel = 1023;
-				levelIncrement = _adTbl[fs_s.rate + ((_tickCount >> fs_s.shift) & 7)];
+				targetLevel = _shapeScale ? 832 : 1023;
+				levelIncrement = _adTbl[fs_s.rate + ((_tickCount >> fs_s.shift) & 7)] << _shapeScale;
 				break;
 			case kEnvReleasing:
 				targetTime = (1 << fs_r.shift) - 1;
@@ -219,13 +238,25 @@ void TownsPC98_FmSynthOperator::generateOutput(int32 phasebuf, int32 *feed, int3
 			if ((_state == kEnvAttacking && _currentLevel <= targetLevel) || (_state != kEnvAttacking && _currentLevel >= targetLevel)) {
 				if (_state != kEnvDecaying)
 					_currentLevel = targetLevel;
+				if (_state == kEnvSustaining && _shapeScale) {
+					_currentLevel += 191;
+					if (_shapeState & 1) {
+						if (!(_shapeState & 0x10))
+							_shapeState |= 0x40;
+					} else {
+						nextState = kEnvAttacking;
+						_phase = 0;
+						_currentLevel = 511;
+						_shapeState &= ~0x40;
+					}
+				}
 				_state = nextState;
 			}
 		}
 	}
 
-	uint32 lvlout = _totalLevel + (uint32) _currentLevel;
-
+	uint32 lvlout = _totalLevel + ((uint32) _currentLevel ^ (_state != kEnvReleasing ? ((_shapeScale * (_shapeState & 4)) >> 3) * 1023 : 0));
+	_shapeState ^= (((_shapeState & 0x40) >> 2) | ((_shapeState & 2) << 1));
 
 	int32 outp = 0;
 	int32 *i = &outp, *o = &outp;
@@ -301,6 +332,11 @@ void TownsPC98_FmSynthOperator::releaseRate(uint32 value) {
 	recalculateRates();
 }
 
+void TownsPC98_FmSynthOperator::envelopeShape(uint32 value) {
+	_envelopeShapeSpecs = value;
+	_shapeScale = (value & 8) >> 2;
+}
+
 void TownsPC98_FmSynthOperator::totalLevel(uint32 value) {
 	_totalLevel = value << 3;
 }
@@ -324,6 +360,7 @@ void TownsPC98_FmSynthOperator::reset() {
 	decayRate(0);
 	releaseRate(0);
 	sustainRate(0);
+	envelopeShape(0);
 	feedbackLevel(0);
 	totalLevel(127);
 	ampModulation(false);
@@ -369,6 +406,7 @@ private:
 	int _evpUpdateCnt;
 	uint8 _outN;
 	int _nTick;
+	uint8 _evpSwap;
 
 	int32 *_tlTable;
 	int32 *_tleTable;
@@ -389,6 +427,9 @@ private:
 
 	uint8 _noiseGenerator;
 	uint8 _chanEnable;
+	uint8 _envH;
+	uint8 _envL;
+	uint8 _flags;
 
 	uint8 **_reg;
 
@@ -473,12 +514,12 @@ 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), _volumeT(0x60),
+	_timer(0), _noiseGenerator(0), _chanEnable(0), _envH(0), _envL(0), _flags(0), _evpSwap(0), _volumeT(0x60),
 	_volMaskA(0), _volMaskB(0), _volumeA(Audio::Mixer::kMaxMixerVolume), _volumeB(Audio::Mixer::kMaxMixerVolume) {
 
 	memset(_channels, 0, sizeof(_channels));
 	memset(_updateRequestBuf, 0, sizeof(_updateRequestBuf));
-	_reg = new uint8 *[11];
+	_reg = new uint8 *[14];
 
 	_reg[0] = &_channels[0].frqL;
 	_reg[1] = &_channels[0].frqH;
@@ -491,6 +532,9 @@ TownsPC98_FmSynthSquareSineSource::TownsPC98_FmSynthSquareSineSource(const uint3
 	_reg[8] = &_channels[0].vol;
 	_reg[9] = &_channels[1].vol;
 	_reg[10] = &_channels[2].vol;
+	_reg[11] = &_envL;
+	_reg[12] = &_envH;
+	_reg[13] = &_flags;
 
 	reset();
 }
@@ -547,6 +591,7 @@ void TownsPC98_FmSynthSquareSineSource::reset() {
 	_cont = false;
 	_evpUpdate = true;
 	_timer = 0;
+	_evpSwap = 0;
 
 	for (int i = 0; i < 3; i++) {
 		_channels[i].tick = 0;
@@ -563,13 +608,23 @@ void TownsPC98_FmSynthSquareSineSource::writeReg(uint8 address, uint8 value, boo
 	if (!_ready)
 		return;
 
-	if (address > 10 || *_reg[address] == value) {
-		if ((address == 11 || address == 12 || address == 13) && value)
-			warning("TownsPC98_FmSynthSquareSineSource: unsupported reg address: %d", address);
+	if (address > 13) {
+		warning("TownsPC98_FmSynthSquareSineSource: unsupported reg address: %d", address);
 		return;
 	}
 
 	if (!force) {
+		bool alreadyBuffered = false;
+		for (int i = 0; i < _updateRequest;) {
+			uint8 b = _updateRequestBuf[i++];
+			uint8 a = _updateRequestBuf[i++];
+			if (a == address)
+				alreadyBuffered = (b == value) ? true : false;
+		}
+
+		if (alreadyBuffered)
+			return;
+
 		if (_updateRequest >= 63) {
 			warning("TownsPC98_FmSynthSquareSineSource: event buffer overflow");
 			_updateRequest = -1;
@@ -583,7 +638,7 @@ void TownsPC98_FmSynthSquareSineSource::writeReg(uint8 address, uint8 value, boo
 }
 
 uint8 TownsPC98_FmSynthSquareSineSource::readReg(uint8 address) const {
-	if (!_ready || address > 10)
+	if (!_ready || address > 13)
 		return 0;
 
 	return *_reg[address];
@@ -615,13 +670,15 @@ void TownsPC98_FmSynthSquareSineSource::nextTick(int32 *buffer, uint32 bufferSiz
 			}
 
 			if (_evpUpdate) {
-				if (++_evpUpdateCnt >= 0) {
+				if (++_evpUpdateCnt >= (_envH << 8 | _envL)) {
 					_evpUpdateCnt = 0;
 
 					if (--_evpTimer < 0) {
 						if (_cont) {
-							_evpTimer &= 0x1f;
+							_attack ^= (_evpSwap && _evpTimer & 0x20) ? 0x1F : 0;
+							_evpTimer &= 0x1F;
 						} else {
+							_attack ^= _evpSwap ? 0x1F : 0;
 							_evpUpdate = false;
 							_evpTimer = 0;
 						}
@@ -657,6 +714,15 @@ void TownsPC98_FmSynthSquareSineSource::updateRegs() {
 		uint8 b = _updateRequestBuf[i++];
 		uint8 a = _updateRequestBuf[i++];
 		writeReg(a, b, true);
+
+		if (a == 13) {
+			_attack = (_flags & 4) ? 0x1F : 0;
+			_cont = (_flags & 8) ? (_flags & 1) ^ 1: false;
+			_evpSwap = (_flags & 8) ? _flags & 2 : _attack;
+			_evpTimer = 0x1F;
+			_evpUpdate = true;
+			_pReslt = _evpTimer ^ _attack;
+		}
 	}
 	_updateRequest = -1;
 }
@@ -959,7 +1025,6 @@ void TownsPC98_FmSynth::reset() {
 			_chanInternal[i].opr[ii]->reset();
 		memset(_chanInternal[i].feedbuf, 0, 3 * sizeof(int32));
 		_chanInternal[i].algorithm = 0;
-		_chanInternal[i].frqTemp = 0;
 		_chanInternal[i].enableLeft = _chanInternal[i].enableRight = true;
 		_chanInternal[i].updateEnvelopeParameters = false;
 	}
@@ -982,9 +1047,9 @@ void TownsPC98_FmSynth::writeReg(uint8 part, uint8 regAddress, uint8 value) {
 	if (!_ready)
 		return;
 
-	if (part > 1) {
+	if (part > (_numChan >> 2)) {
 		warning("TownsPC98_FmSynth::writeReg(): invalid part argument '%d'", part);
-		part = 1;
+		return;
 	}
 
 	Common::StackLock lock(_mutex);
@@ -1000,14 +1065,24 @@ void TownsPC98_FmSynth::writeReg(uint8 part, uint8 regAddress, uint8 value) {
 	TownsPC98_FmSynthOperator **co = 0;
 	TownsPC98_FmSynthOperator *o = 0;
 
-	if (regAddress > 0x2F) {
-		c = &_chanInternal[(l & 3) + 3 * part];
-		co = c->opr;
-		o = c->opr[oprOrdr[(l - (l & 3)) >> 2]];
-	} else if (regAddress == 0x28) {
+	bool checkAddress = false;
+	c = &_chanInternal[(l & 3) + 3 * part];
+	if (regAddress == 0x28) {
 		c = &_chanInternal[(value & 3) + ((value & 4) ? 3 : 0)];
-		co = c->opr;
+		checkAddress = true;
+	} else if (h == 0xA0 && (_registers[0x27][0] & 0x40) && (l > 7 || l == 2 || l == 6)) {
+		c = &_chanInternal[3 * part + 2];
+		o = c->opr[l < 8 ? 3 : 2 - (l & 3)];
+	} else if (h != 0xA0 && h > 0x20) {
+		o = c->opr[oprOrdr[(l & ~3) >> 2]];
+		checkAddress = true;
+	}
+
+	if (checkAddress && (l & 3) == 3) {
+		warning("TownsPC98_FmSynth::writeReg(): invalid write attempt at reg address 0x%2x", regAddress);
+		return;
 	}
+	co = c->opr;
 
 	switch (h) {
 	case 0x00:
@@ -1027,9 +1102,9 @@ void TownsPC98_FmSynth::writeReg(uint8 part, uint8 regAddress, uint8 value) {
 			// Key on/off
 			for (int i = 0; i < 4; i++) {
 				if ((value >> (4 + i)) & 1)
-					co[oprOrdr[i]]->keyOn();
+					co[i]->keyOn();
 				else
-					co[oprOrdr[i]]->keyOff();
+					co[i]->keyOff();
 			}
 		} else if (l == 4) {
 			// Timer A
@@ -1082,6 +1157,7 @@ void TownsPC98_FmSynth::writeReg(uint8 part, uint8 regAddress, uint8 value) {
 				_timers[1].smpTillCb = _timers[1].smpPerCb;
 				_timers[1].smpTillCbRem = _timers[1].smpPerCbRem;
 			}
+
 		} else if (l == 2) {
 			// LFO
 			if (value & 8)
@@ -1130,26 +1206,30 @@ void TownsPC98_FmSynth::writeReg(uint8 part, uint8 regAddress, uint8 value) {
 		break;
 
 	case 0x90:
-		warning("TownsPC98_FmSynth: TRYING TO USE SSG ENVELOPE SHAPES (NOT SUPPORTED)");
+		o->envelopeShape(value & 0x0f);
 		break;
 
 	case 0xa0:
 		// frequency
 		l &= ~3;
 		if (l == 0) {
-			c->frqTemp = (c->frqTemp & 0xff00) | value;
 			c->updateEnvelopeParameters = true;
-			c->fmIndex = (c->frqTemp >> 4 & 0x7f);
-			for (int i = 0; i < 4; i++)
-				co[i]->frequency(c->frqTemp);
+			if (o)
+				o->frequencyLo(value);
+			else
+				for (int i = 0; i < 4; i++)
+					co[i]->frequencyLo(value);
 		} else if (l == 4) {
-			c->frqTemp = (c->frqTemp & 0xff) | (value << 8);
-		} else if (l == 8) {
-			// Ch 3/6 special mode frq
-			warning("TownsPC98_FmSynth: TRYING TO USE CH 3/6 SPECIAL MODE FREQ (NOT SUPPORTED)");
-		} else if (l == 12) {
-			// Ch 3/6 special mode frq
-			warning("TownsPC98_FmSynth: TRYING TO USE CH 3/6 SPECIAL MODE FREQ (NOT SUPPORTED)");
+			if (o)
+				o->frequencyHi(value);
+			else
+				for (int i = 0; i < 4; i++)
+					co[i]->frequencyHi(value);
+		} else if (l == 8 && o) {
+			c->updateEnvelopeParameters = true;
+			o->frequencyLo(value);
+		} else if (l == 12 && o) {
+			o->frequencyHi(value);
 		}
 		break;
 
diff --git a/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h b/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h
index ba484e2..58b7ccb 100644
--- a/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h
+++ b/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h
@@ -116,8 +116,6 @@ private:
 			frqModSvty = value << 5;
 		}
 
-		uint16 frqTemp;
-		uint8 fmIndex;
 		bool enableLeft;
 		bool enableRight;
 		bool updateEnvelopeParameters;


Commit: 1237eb496b64fdc8121c997dd851c17fabb648d6
    https://github.com/scummvm/scummvm/commit/1237eb496b64fdc8121c997dd851c17fabb648d6
Author: athrxx (athrxx at scummvm.org)
Date: 2019-03-07T21:28:45+01:00

Commit Message:
AUDIO: (FM-TOWNS/PC-98) - initialize some uninitialized vars

(just to remove some analysis warnings)

Changed paths:
    audio/softsynth/fmtowns_pc98/towns_euphony.cpp


diff --git a/audio/softsynth/fmtowns_pc98/towns_euphony.cpp b/audio/softsynth/fmtowns_pc98/towns_euphony.cpp
index d63f990..e80d29b 100644
--- a/audio/softsynth/fmtowns_pc98/towns_euphony.cpp
+++ b/audio/softsynth/fmtowns_pc98/towns_euphony.cpp
@@ -26,9 +26,10 @@
 #include "common/textconsole.h"
 
 EuphonyPlayer::EuphonyPlayer(Audio::Mixer *mixer) : _partConfig_enable(0), _partConfig_type(0), _partConfig_ordr(0), _partConfig_volume(0),
-	_partConfig_transpose(0), _musicPos(0), _musicStart(0), _playing(false), _savedEventsChain(0),
-	_tempoControlMode(0), _timerSetting(0), _tempoMode1PulseCounter(0), _parseToBar(0), _tempoMode1UpdateF8(0), _loop(false),
-	_endOfTrack(false), _paused(false), _musicTrackSize(0) {
+	_partConfig_transpose(0), _musicPos(0), _musicStart(0), _playing(false), _savedEventsChain(0), _tempoModifier(0), _bar(0),
+	_beat(0), _defaultBarLength(0), _barLength(0), _playerUpdatesLeft(0), _updatesPerPulseRemainder(0),	_updatesPerPulse(0),
+	_deltaTicks(0), _defaultTempo(0), _trackTempo(0), _tempoControlMode(0), _timerSetting(0), _tempoMode1PulseCounter(0),
+	_parseToBar(0), _tempoMode1UpdateF8(0), _loop(false), _endOfTrack(false), _paused(false), _musicTrackSize(0) {
 	_drivers[0] = _eupDriver = new EuphonyDriver(mixer, this);
 	_drivers[1] = new Type0Driver(this);
 	_drivers[2] = 0;


Commit: 8d4418fa365511e8d598823e4369cdc10f87f6b8
    https://github.com/scummvm/scummvm/commit/8d4418fa365511e8d598823e4369cdc10f87f6b8
Author: athrxx (athrxx at scummvm.org)
Date: 2019-03-07T21:28:49+01:00

Commit Message:
AUDIO: (FM-TOWNS) - cleanup

Changed paths:
    audio/softsynth/fmtowns_pc98/towns_euphony.cpp
    audio/softsynth/fmtowns_pc98/towns_euphony.h


diff --git a/audio/softsynth/fmtowns_pc98/towns_euphony.cpp b/audio/softsynth/fmtowns_pc98/towns_euphony.cpp
index e80d29b..e989db3 100644
--- a/audio/softsynth/fmtowns_pc98/towns_euphony.cpp
+++ b/audio/softsynth/fmtowns_pc98/towns_euphony.cpp
@@ -25,24 +25,43 @@
 #include "common/util.h"
 #include "common/textconsole.h"
 
+#define EUP_EVENT(x) _euphonyEvents.push_back(new EuphonyEvent(this, &EuphonyPlayer::event_##x))
+
 EuphonyPlayer::EuphonyPlayer(Audio::Mixer *mixer) : _partConfig_enable(0), _partConfig_type(0), _partConfig_ordr(0), _partConfig_volume(0),
-	_partConfig_transpose(0), _musicPos(0), _musicStart(0), _playing(false), _savedEventsChain(0), _tempoModifier(0), _bar(0),
+	_partConfig_transpose(0), _musicPos(0), _musicStart(0), _playing(false), _pendingEventsChain(0), _tempoModifier(0), _bar(0),
 	_beat(0), _defaultBarLength(0), _barLength(0), _playerUpdatesLeft(0), _updatesPerPulseRemainder(0),	_updatesPerPulse(0),
 	_deltaTicks(0), _defaultTempo(0), _trackTempo(0), _tempoControlMode(0), _timerSetting(0), _tempoMode1PulseCounter(0),
 	_parseToBar(0), _tempoMode1UpdateF8(0), _loop(false), _endOfTrack(false), _paused(false), _musicTrackSize(0) {
+	EUP_EVENT(notImpl);
+	EUP_EVENT(noteOn);
+	EUP_EVENT(polyphonicAftertouch);
+	EUP_EVENT(controlChange_pitchWheel);
+	EUP_EVENT(programChange_channelAftertouch);
+	EUP_EVENT(programChange_channelAftertouch);
+	EUP_EVENT(controlChange_pitchWheel);
+	EUP_EVENT(sysex);
+	EUP_EVENT(advanceBar);
+	EUP_EVENT(notImpl);
+	EUP_EVENT(notImpl);
+	EUP_EVENT(setTempo);
+	EUP_EVENT(notImpl);
+	EUP_EVENT(typeOrdrChange);
+
 	_drivers[0] = _eupDriver = new EuphonyDriver(mixer, this);
 	_drivers[1] = new Type0Driver(this);
 	_drivers[2] = 0;
 	resetTempo();
 }
 
+#undef EUP_EVENT
+
 EuphonyPlayer::~EuphonyPlayer() {
 	for (int i = 0; i < 3; i++)
 		delete _drivers[i];
 
-	while (_savedEventsChain) {
-		SavedEvent *evt = _savedEventsChain;
-		_savedEventsChain = _savedEventsChain->next;
+	while (_pendingEventsChain) {
+		PendingEvent *evt = _pendingEventsChain;
+		_pendingEventsChain = _pendingEventsChain->next;
 		delete evt;
 	}
 
@@ -51,6 +70,9 @@ EuphonyPlayer::~EuphonyPlayer() {
 	delete[] _partConfig_ordr;
 	delete[] _partConfig_volume;
 	delete[] _partConfig_transpose;
+
+	for (EuphonyEventsArray::iterator i = _euphonyEvents.begin(); i != _euphonyEvents.end(); ++i)
+		delete *i;
 }
 
 bool EuphonyPlayer::init() {
@@ -67,9 +89,9 @@ bool EuphonyPlayer::init() {
 	if (!_drivers[0] || !_drivers[1])
 		return false;
 
-	while (_savedEventsChain) {
-		SavedEvent *evt = _savedEventsChain;
-		_savedEventsChain = _savedEventsChain->next;
+	while (_pendingEventsChain) {
+		PendingEvent *evt = _pendingEventsChain;
+		_pendingEventsChain = _pendingEventsChain->next;
 		delete evt;
 	}
 
@@ -204,9 +226,9 @@ void EuphonyPlayer::reset() {
 
 	resetPartConfig();
 
-	while (_savedEventsChain) {
-		SavedEvent *evt = _savedEventsChain;
-		_savedEventsChain = _savedEventsChain->next;
+	while (_pendingEventsChain) {
+		PendingEvent *evt = _pendingEventsChain;
+		_pendingEventsChain = _pendingEventsChain->next;
 		delete evt;
 	}
 
@@ -309,35 +331,15 @@ void EuphonyPlayer::updateParser() {
 }
 
 void EuphonyPlayer::updateCheckEot() {
-	if (!_endOfTrack || _savedEventsChain)
+	if (!_endOfTrack || _pendingEventsChain)
 		return;
 	stop();
 }
 
 bool EuphonyPlayer::parseEvent() {
-#define EVENT(x) &EuphonyPlayer::event_##x
-	static const EuphonyEvent events[] = {
-		EVENT(notImpl),
-		EVENT(noteOn),
-		EVENT(polyphonicAftertouch),
-		EVENT(controlChange_pitchWheel),
-		EVENT(programChange_channelAftertouch),
-		EVENT(programChange_channelAftertouch),
-		EVENT(controlChange_pitchWheel),
-
-		EVENT(sysex),
-		EVENT(advanceBar),
-		EVENT(notImpl),
-		EVENT(notImpl),
-		EVENT(setTempo),
-		EVENT(notImpl),
-		EVENT(typeOrdrChange)
-	};
-#undef EVENT
-
 	uint cmd = _musicPos[0];
 	if (cmd != 0xfe && cmd != 0xfd) {
-		bool result = (cmd >= 0xf0) ? (this->*events[((cmd - 0xf0) >> 1) + 7])() : (this->*events[(cmd - 0x80) >> 4])();
+		bool result = (cmd >= 0xf0) ? (*_euphonyEvents[((cmd - 0xf0) >> 1) + 7])() : (*_euphonyEvents[(cmd - 0x80) >> 4])();
 		if (!result) {
 			proceedToNextEvent();
 			return false;
@@ -369,8 +371,8 @@ void EuphonyPlayer::proceedToNextEvent() {
 }
 
 void EuphonyPlayer::updateHangingNotes() {
-	SavedEvent *l = 0;
-	SavedEvent *e = _savedEventsChain;
+	PendingEvent *l = 0;
+	PendingEvent *e = _pendingEventsChain;
 
 	while (e) {
 		if (--e->len) {
@@ -379,13 +381,13 @@ void EuphonyPlayer::updateHangingNotes() {
 			continue;
 		}
 
-		SavedEvent *n = e->next;
+		PendingEvent *n = e->next;
 		if (l)
 			l->next = n;
-		if (_savedEventsChain == e)
-			_savedEventsChain = n;
+		if (_pendingEventsChain == e)
+			_pendingEventsChain = n;
 
-		sendNoteEvent(e->type, e->evt, e->note, e->velo);
+		sendPendingEvent(e->type, e->evt, e->note, e->velo);
 		delete e;
 
 		e = n;
@@ -393,10 +395,10 @@ void EuphonyPlayer::updateHangingNotes() {
 }
 
 void EuphonyPlayer::clearHangingNotes() {
-	while (_savedEventsChain) {
-		SavedEvent *e = _savedEventsChain;
-		_savedEventsChain = _savedEventsChain->next;
-		sendNoteEvent(e->type, e->evt, e->note, e->velo);
+	while (_pendingEventsChain) {
+		PendingEvent *e = _pendingEventsChain;
+		_pendingEventsChain = _pendingEventsChain->next;
+		sendPendingEvent(e->type, e->evt, e->note, e->velo);
 		delete e;
 	}
 }
@@ -445,9 +447,9 @@ bool EuphonyPlayer::event_noteOn() {
 	uint8 note = _musicPos[4];
 	uint8 velo = _musicPos[5];
 
-	sendEvent(type, evt);
-	sendEvent(type, applyTranspose(note));
-	sendEvent(type, applyVolumeAdjust(velo));
+	sendByte(type, evt);
+	sendByte(type, applyTranspose(note));
+	sendByte(type, applyVolumeAdjust(velo));
 
 	proceedToNextEvent();
 	if (_musicPos[0] == 0xfe || _musicPos[0] == 0xfd)
@@ -456,7 +458,7 @@ bool EuphonyPlayer::event_noteOn() {
 	velo = _musicPos[5];
 	uint16 len = (_musicPos[1] & 0x0f) | ((_musicPos[2] & 0x0f) << 4) | ((_musicPos[3] & 0x0f) << 8) | ((_musicPos[4] & 0x0f) << 12);
 
-	_savedEventsChain = new SavedEvent(evt, type, note, velo, len ? len : 1, _savedEventsChain);
+	_pendingEventsChain = new PendingEvent(evt, type, note, velo, len ? len : 1, _pendingEventsChain);
 
 	return false;
 }
@@ -470,9 +472,9 @@ bool EuphonyPlayer::event_polyphonicAftertouch() {
 	uint8 evt = appendEvent(_musicPos[0], _musicPos[1]);
 	uint8 type = _partConfig_type[_musicPos[1]];
 
-	sendEvent(type, evt);
-	sendEvent(type, applyTranspose(_musicPos[4]));
-	sendEvent(type, _musicPos[5]);
+	sendByte(type, evt);
+	sendByte(type, applyTranspose(_musicPos[4]));
+	sendByte(type, _musicPos[5]);
 
 	return false;
 }
@@ -486,9 +488,9 @@ bool EuphonyPlayer::event_controlChange_pitchWheel() {
 	uint8 evt = appendEvent(_musicPos[0], _musicPos[1]);
 	uint8 type = _partConfig_type[_musicPos[1]];
 
-	sendEvent(type, evt);
-	sendEvent(type, _musicPos[4]);
-	sendEvent(type, _musicPos[5]);
+	sendByte(type, evt);
+	sendByte(type, _musicPos[4]);
+	sendByte(type, _musicPos[5]);
 
 	return false;
 }
@@ -502,21 +504,21 @@ bool EuphonyPlayer::event_programChange_channelAftertouch() {
 	uint8 evt = appendEvent(_musicPos[0], _musicPos[1]);
 	uint8 type = _partConfig_type[_musicPos[1]];
 
-	sendEvent(type, evt);
-	sendEvent(type, _musicPos[4]);
+	sendByte(type, evt);
+	sendByte(type, _musicPos[4]);
 
 	return false;
 }
 
 bool EuphonyPlayer::event_sysex() {
 	uint8 type = _partConfig_type[_musicPos[1]];
-	sendEvent(type, 0xF0);
+	sendByte(type, 0xF0);
 	proceedToNextEvent();
 
 	for (bool loop = true; loop; ) {
 		for (int i = 0; i < 6; i++) {
 			if (_musicPos[i] != 0xFF) {
-				sendEvent(type, _musicPos[i]);
+				sendByte(type, _musicPos[i]);
 				if (_musicPos[i] >= 0x80) {
 					loop = false;
 					break;
@@ -579,36 +581,36 @@ uint8 EuphonyPlayer::applyVolumeAdjust(uint8 in) {
 	return out & 0xff;
 }
 
-void EuphonyPlayer::sendEvent(uint8 type, uint8 command) {
+void EuphonyPlayer::sendByte(uint8 type, uint8 command) {
 	int drv = ((type >> 4) + 1) & 3;
 	if (_drivers[drv])
 		_drivers[drv]->send(command);
 }
 
-void EuphonyPlayer::sendNoteEvent(int type, int evt, int note, int velo) {
+void EuphonyPlayer::sendPendingEvent(int type, int evt, int note, int velo) {
 	if (velo)
 		evt &= 0x8f;
-	sendEvent(type, evt);
-	sendEvent(type, note);
-	sendEvent(type, velo);
+	sendByte(type, evt);
+	sendByte(type, note);
+	sendByte(type, velo);
 }
 
 void EuphonyPlayer::sendControllerReset(int type, int part) {
-	sendEvent(type, 0xb0 | part);
-	sendEvent(type, 0x40);
-	sendEvent(type, 0);
-	sendEvent(type, 0xb0 | part);
-	sendEvent(type, 0x7b);
-	sendEvent(type, 0);
-	sendEvent(type, 0xb0 | part);
-	sendEvent(type, 0x79);
-	sendEvent(type, 0x40);
+	sendByte(type, 0xb0 | part);
+	sendByte(type, 0x40);
+	sendByte(type, 0);
+	sendByte(type, 0xb0 | part);
+	sendByte(type, 0x7b);
+	sendByte(type, 0);
+	sendByte(type, 0xb0 | part);
+	sendByte(type, 0x79);
+	sendByte(type, 0x40);
 }
 
 void EuphonyPlayer::sendAllNotesOff(int type, int part) {
-	sendEvent(type, 0xb0 | part);
-	sendEvent(type, 0x40);
-	sendEvent(type, 0);
+	sendByte(type, 0xb0 | part);
+	sendByte(type, 0x40);
+	sendByte(type, 0);
 }
 
 void EuphonyPlayer::sendTempo(int tempo) {
diff --git a/audio/softsynth/fmtowns_pc98/towns_euphony.h b/audio/softsynth/fmtowns_pc98/towns_euphony.h
index 65e55fa..6b9d94a 100644
--- a/audio/softsynth/fmtowns_pc98/towns_euphony.h
+++ b/audio/softsynth/fmtowns_pc98/towns_euphony.h
@@ -25,6 +25,7 @@
 
 #include "audio/softsynth/fmtowns_pc98/towns_audio.h"
 #include "common/array.h"
+#include "common/func.h"
 
 class EuphonyBaseDriver {
 public:
@@ -154,7 +155,6 @@ private:
 
 	uint8 appendEvent(uint8 evt, uint8 chan);
 
-	typedef bool(EuphonyPlayer::*EuphonyEvent)();
 	bool event_notImpl();
 	bool event_noteOn();
 	bool event_polyphonicAftertouch();
@@ -169,8 +169,8 @@ private:
 	uint8 applyTranspose(uint8 in);
 	uint8 applyVolumeAdjust(uint8 in);
 
-	void sendEvent(uint8 type, uint8 command);
-	void sendNoteEvent(int type, int evt, int note, int velo);
+	void sendByte(uint8 type, uint8 command);
+	void sendPendingEvent(int type, int evt, int note, int velo);
 	void sendControllerReset(int type, int part);
 	void sendAllNotesOff(int type, int part);
 	void sendTempo(int tempo);
@@ -181,17 +181,21 @@ private:
 	int8 *_partConfig_volume;
 	int8 *_partConfig_transpose;
 
-	struct SavedEvent {
-		SavedEvent(int ev, int tp, int nt, int vl, int ln, SavedEvent *chain) : evt(ev), type(tp), note(nt), velo(vl), len(ln), next(chain) {}
+	struct PendingEvent {
+		PendingEvent(int ev, int tp, int nt, int vl, int ln, PendingEvent *chain) : evt(ev), type(tp), note(nt), velo(vl), len(ln), next(chain) {}
 		uint8 evt;
 		uint8 type;
 		uint8 note;
 		uint8 velo;
 		uint16 len;
-		SavedEvent *next;
+		PendingEvent *next;
 	};
 
-	SavedEvent *_savedEventsChain;
+	PendingEvent *_pendingEventsChain;
+
+	typedef Common::Functor0Mem<bool, EuphonyPlayer> EuphonyEvent;
+	typedef Common::Array<const EuphonyEvent*> EuphonyEventsArray;
+	EuphonyEventsArray _euphonyEvents;
 
 	uint8 _defaultBarLength;
 	uint8 _barLength;





More information about the Scummvm-git-logs mailing list