[Scummvm-cvs-logs] SF.net SVN: scummvm:[33994] scummvm/branches/branch-0-12-0/engines/kyra/ sound_towns.cpp

athrxx at users.sourceforge.net athrxx at users.sourceforge.net
Mon Aug 18 19:48:05 CEST 2008


Revision: 33994
          http://scummvm.svn.sourceforge.net/scummvm/?rev=33994&view=rev
Author:   athrxx
Date:     2008-08-18 17:48:02 +0000 (Mon, 18 Aug 2008)

Log Message:
-----------
KYRA: FM-Towns/PC-98 Audio: commit latest fixes and improvements to branch

Modified Paths:
--------------
    scummvm/branches/branch-0-12-0/engines/kyra/sound_towns.cpp

Modified: scummvm/branches/branch-0-12-0/engines/kyra/sound_towns.cpp
===================================================================
--- scummvm/branches/branch-0-12-0/engines/kyra/sound_towns.cpp	2008-08-18 17:42:32 UTC (rev 33993)
+++ scummvm/branches/branch-0-12-0/engines/kyra/sound_towns.cpp	2008-08-18 17:48:02 UTC (rev 33994)
@@ -23,7 +23,6 @@
  *
  */
 
-
 #include "common/system.h"
 #include "kyra/resource.h"
 #include "kyra/sound.h"
@@ -427,7 +426,7 @@
 		return;
 	}
 
-	float phaseStep = SoundTowns::semitoneAndSampleRate_to_sampleStep(_note, _voice->_snd[_current]->keyNote -
+	float phaseStep = SoundTowns::calculatePhaseStep(_note, _voice->_snd[_current]->keyNote -
 		_voice->_env[_current]->rootKeyOffset, _voice->_snd[_current]->samplingRate, _rate, _frequencyOffs);
 
 	int32 looplength = _voice->_snd[_current]->loopLength;
@@ -1085,7 +1084,7 @@
 
 class TownsPC98_OpnOperator {
 public:
-	TownsPC98_OpnOperator(double rate, const uint8 *rateTable,
+	TownsPC98_OpnOperator(const float rate, const uint8 *rateTable,
 		const uint8 *shiftTable, const uint8 *attackDecayTable, const uint32 *frqTable,
 		const uint32 *sineTable, const int32 *tlevelOut, const int32 *detuneTable);
 	~TownsPC98_OpnOperator() {}
@@ -1095,7 +1094,7 @@
 	void frequency(int freq);
 	void updatePhaseIncrement();
 	void recalculateRates();
-	void generateOutput(int phasebuf, int *_feedbuf, int &out);
+	void generateOutput(int32 phasebuf, int32 *feedbuf, int32 &out);
 
 	void feedbackLevel(int32 level) {_feedbackLevel = level ? level + 6 : 0; }
 	void detune(int value) { _detn = &_detnTbl[value << 5]; }
@@ -1137,8 +1136,8 @@
 	const int32 *_tLvlTbl;
 	const int32 *_detnTbl;
 
-	const double _tickLength;
-	double _tick;
+	const int _tickLength;
+	int _tick;
 	int32 _currentLevel;
 
 	struct EvpState {
@@ -1147,14 +1146,14 @@
 	} fs_a, fs_d, fs_s, fs_r;
 };
 
-TownsPC98_OpnOperator::TownsPC98_OpnOperator(double rate, const uint8 *rateTable, 
+TownsPC98_OpnOperator::TownsPC98_OpnOperator(const float rate, const uint8 *rateTable,
 	const uint8 *shiftTable, const uint8 *attackDecayTable,	const uint32 *frqTable,
 	const uint32 *sineTable, const int32 *tlevelOut, const int32 *detuneTable) :
 	_rateTbl(rateTable), _rshiftTbl(shiftTable), _adTbl(attackDecayTable), _fTbl(frqTable),
-	_sinTbl(sineTable), _tLvlTbl(tlevelOut), _detnTbl(detuneTable), _tickLength(rate * 65536.0),
+	_sinTbl(sineTable), _tLvlTbl(tlevelOut), _detnTbl(detuneTable), _tickLength((int)(rate * 65536.0f)),
 	_specifiedAttackRate(0), _specifiedDecayRate(0), _specifiedReleaseRate(0), _specifiedSustainRate(0),
 	_phase(0), _state(s_ready) {
-	
+
 	reset();
 }
 
@@ -1172,6 +1171,7 @@
 	uint8 block = (freq >> 11);
 	uint16 pos = (freq & 0x7ff);
 	uint8 c = pos >> 7;
+
 	_kcode = (block << 2) | ((c < 7) ? 0 : ((c > 8) ? 3 : c - 6 ));
 	_frequency = _fTbl[pos << 1] >> (7 - block);
 }
@@ -1204,7 +1204,7 @@
 	fs_r.shift = _rshiftTbl[r + k];
 }
 
-void TownsPC98_OpnOperator::generateOutput(int phasebuf, int *_feedbuf, int &out) {
+void TownsPC98_OpnOperator::generateOutput(int32 phasebuf, int32 *feed, int32 &out) {
 	if (_state == s_ready)
 		return;
 
@@ -1216,32 +1216,32 @@
 		int32 levelIncrement = 0;
 		uint32 targetTime = 0;
 		int32 targetLevel = 0;
-		EnvelopeState next_state = s_ready;
+		EnvelopeState nextState = s_ready;
 
 		switch (_state) {
 			case s_ready:
 				return;
 			case s_attacking:
-				next_state = s_decaying;
+				nextState = s_decaying;
 				targetTime = (1 << fs_a.shift) - 1;
 				targetLevel = 0;
 				levelIncrement = (~_currentLevel * _adTbl[fs_a.rate + ((_tickCount >> fs_a.shift) & 7)]) >> 4;
 				break;
 			case s_decaying:
 				targetTime = (1 << fs_d.shift) - 1;
-				next_state = s_sustaining;
+				nextState = s_sustaining;
 				targetLevel = _sustainLevel;
 				levelIncrement = _adTbl[fs_d.rate + ((_tickCount >> fs_d.shift) & 7)];
 				break;
 			case s_sustaining:
 				targetTime = (1 << fs_s.shift) - 1;
-				next_state = s_ready;
+				nextState = s_sustaining;
 				targetLevel = 1023;
 				levelIncrement = _adTbl[fs_s.rate + ((_tickCount >> fs_s.shift) & 7)];
 				break;
 			case s_releasing:
 				targetTime = (1 << fs_r.shift) - 1;
-				next_state = s_ready;
+				nextState = s_ready;
 				targetLevel = 1023;
 				levelIncrement = _adTbl[fs_r.rate + ((_tickCount >> fs_r.shift) & 7)];
 				break;
@@ -1249,11 +1249,10 @@
 
 		if (!(_tickCount & targetTime)) {
 			_currentLevel += levelIncrement;
-			if ((!targetLevel && _currentLevel <= targetLevel) || (targetLevel && _currentLevel >= targetLevel)) {
+			if ((_state == s_attacking && _currentLevel <= targetLevel) || (_state != s_attacking && _currentLevel >= targetLevel)) {
 				if (_state != s_decaying)
 					_currentLevel = targetLevel;
-				if (_state != s_sustaining)
-					_state = next_state;
+				_state = nextState;
 			}
 		}
 	}
@@ -1264,16 +1263,16 @@
 	int *i = &outp, *o = &outp;
 	int phaseShift = 0;
 
-	if (_feedbuf) {
-		o = &_feedbuf[0];
-		i = &_feedbuf[1];
-		phaseShift = _feedbackLevel ? ((_feedbuf[0] + _feedbuf[1]) << _feedbackLevel) : 0;
+	if (feed) {
+		o = &feed[0];
+		i = &feed[1];
+		phaseShift = _feedbackLevel ? ((*o + *i) << _feedbackLevel) : 0;
 		if (phasebuf == -1)
 			*i = 0;
 		*o = *i;
 	} else {
 		phaseShift = phasebuf << 15;
-	}		
+	}
 
 	if (lvlout < 832) {
 		uint32 index = (lvlout << 3) + _sinTbl[(((int32)((_phase & 0xffff0000)
@@ -1285,10 +1284,6 @@
 
 	_phase += _phaseIncrement;
 	out += *o;
-	if (out > 32767)
-		out = 32767;
-	if (out < -32767)
-		out = -32767;
 }
 
 void TownsPC98_OpnOperator::reset(){
@@ -1306,7 +1301,7 @@
 	decayRate(0);
 	releaseRate(0);
 	sustainRate(0);
-	feedbackLevel(0);	
+	feedbackLevel(0);
 	totalLevel(127);
 }
 
@@ -1332,40 +1327,42 @@
 	virtual ~TownsPC98_OpnChannel();
 	virtual void init();
 
-	typedef bool (TownsPC98_OpnChannel::*ControlEventFunc)(uint8 para);
-
 	typedef enum channelState {
 		CHS_RECALCFREQ		=	0x01,
 		CHS_KEYOFF			=	0x02,
-		CHS_SSG				=	0x04,
+		CHS_SSGOFF			=	0x04,
 		CHS_PITCHWHEELOFF	=	0x08,
 		CHS_ALL_BUT_EOT		=	0x0f,
+		CHS_PROTECT			=	0x40,
 		CHS_EOT				=	0x80
 	} ChannelState;
 
 	virtual void loadData(uint8 *data);
 	virtual void processEvents();
 	virtual void processFrequency();
-	bool processControlEvent(uint8 cmd);
-	void writeReg(uint8 regAdress, uint8 value);
+	virtual bool processControlEvent(uint8 cmd);
+	void writeReg(uint8 regAddress, uint8 value);
 
 	virtual void keyOn();
-	virtual void keyOff();	
-	
+	void keyOff();
+
 	void setOutputLevel();
-	void fadeStep();
-	void reset();
+	virtual void fadeStep();
+	virtual void reset();
 
 	void updateEnv();
-	void generateOutput(int16 &leftSample, int16 &rightSample, int *del, int *feed);
+	void generateOutput(int32 &leftSample, int32 &rightSample, int32 *del, int32 *feed);
 
 	bool _enableLeft;
 	bool _enableRight;
-	bool _updateEnvelopes;
+	bool _updateEnvelopeParameters;
 	const uint8 _idFlag;
 	int _feedbuf[3];
 
 protected:
+	void setupPitchWheel();
+	bool processPitchWheel();
+
 	bool control_dummy(uint8 para);
 	bool control_f0_setPatch(uint8 para);
 	bool control_f1_presetOutputLevel(uint8 para);
@@ -1377,40 +1374,36 @@
 	bool control_f7_setupPitchWheel(uint8 para);
 	bool control_f8_togglePitchWheel(uint8 para);
 	bool control_fa_writeReg(uint8 para);
-	bool control_fb_incOutLevel(uint8 para);
-	bool control_fc_decOutLevel(uint8 para);
+	virtual bool control_fb_incOutLevel(uint8 para);
+	virtual bool control_fc_decOutLevel(uint8 para);
 	bool control_fd_jump(uint8 para);
-	bool control_ff_endOfTrack(uint8 para);
+	virtual bool control_ff_endOfTrack(uint8 para);
 
-	bool control_f0_setPatchSSG(uint8 para);
-	bool control_f1_setTotalLevel(uint8 para);
-	bool control_f4_setAlgorithm(uint8 para);
-	bool control_f9_unkSSG(uint8 para);
-	bool control_fb_incOutLevelSSG(uint8 para);
-	bool control_fc_decOutLevelSSG(uint8 para);
-	bool control_ff_endOfTrackSSG(uint8 para);
-
 	uint8 _ticksLeft;
 	uint8 _algorithm;
-	uint8 _instrID;
+	uint8 _instr;
 	uint8 _totalLevel;
 	uint8 _frqBlockMSB;
 	int8 _frqLSB;
 	uint8 _keyOffTime;
-	bool _protect;
+	bool _hold;
 	uint8 *_dataPtr;
+	uint8 _ptchWhlInitDelayHi;
 	uint8 _ptchWhlInitDelayLo;
-	uint8 _ptchWhlInitDelayHi;
 	int16 _ptchWhlModInitVal;
 	uint8 _ptchWhlDuration;
 	uint8 _ptchWhlCurDelay;
 	int16 _ptchWhlModCurVal;
 	uint8 _ptchWhlDurLeft;
-	uint16 frequency;
+	uint16 _frequency;
+	uint8 _block;
 	uint8 _regOffset;
 	uint8 _flags;
-	uint8 _ssg1;
-	uint8 _ssg2;
+	uint8 _ssgTl;
+	uint8 _ssgStep;
+	uint8 _ssgTicksLeft;
+	uint8 _ssgTargetLvl;
+	uint8 _ssgStartLvl;
 
 	const uint8 _chanNum;
 	const uint8 _keyNum;
@@ -1420,6 +1413,7 @@
 	TownsPC98_OpnOperator **_opr;
 	uint16 _frqTemp;
 
+	typedef bool (TownsPC98_OpnChannel::*ControlEventFunc)(uint8 para);
 	const ControlEventFunc *controlEvents;
 };
 
@@ -1427,24 +1421,162 @@
 public:
 	TownsPC98_OpnChannelSSG(TownsPC98_OpnDriver *driver, uint8 regOffs,
 		uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id);
-	~TownsPC98_OpnChannelSSG() {}
+	virtual ~TownsPC98_OpnChannelSSG() {}
 	void init();
 
+	virtual void loadData(uint8 *data);
 	void processEvents();
 	void processFrequency();
+	bool processControlEvent(uint8 cmd);
 
 	void keyOn();
-	void keyOff();
+	void nextShape();
+
+	void protect();
+	void restore();
+
+	void fadeStep();
+
+protected:
+	void setOutputLevel(uint8 lvl);
+
+	bool control_f0_setInstr(uint8 para);
+	bool control_f1_setTotalLevel(uint8 para);
+	bool control_f4_setAlgorithm(uint8 para);
+	bool control_f9_loadCustomPatch(uint8 para);
+	bool control_fb_incOutLevel(uint8 para);
+	bool control_fc_decOutLevel(uint8 para);
+	bool control_ff_endOfTrack(uint8 para);
+
+	typedef bool (TownsPC98_OpnChannelSSG::*ControlEventFunc)(uint8 para);
+	const ControlEventFunc *controlEvents;
+};
+
+class TownsPC98_OpnSfxChannel : public TownsPC98_OpnChannelSSG {
+public:
+	TownsPC98_OpnSfxChannel(TownsPC98_OpnDriver *driver, uint8 regOffs,
+		uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) :
+		TownsPC98_OpnChannelSSG(driver, regOffs, flgs, num, key, prt, id) {}
+	~TownsPC98_OpnSfxChannel() {}
+
 	void loadData(uint8 *data);
+};
 
+class TownsPC98_OpnChannelPCM : public TownsPC98_OpnChannel {
+public:
+	TownsPC98_OpnChannelPCM(TownsPC98_OpnDriver *driver, uint8 regOffs,
+		uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id);
+	~TownsPC98_OpnChannelPCM() {}
+	void init();
+
+	void loadData(uint8 *data);
+	void processEvents();
+	bool processControlEvent(uint8 cmd);
+
+	void reset();
+
 private:
-	void opn_SSG_UNK(uint8 a);
+	bool control_f1_pcmStart(uint8 para);
+	bool control_ff_endOfTrack(uint8 para);
+
+	typedef bool (TownsPC98_OpnChannelPCM::*ControlEventFunc)(uint8 para);
+	const ControlEventFunc *controlEvents;
 };
 
+class TownsPC98_OpnSquareSineSource {
+public:
+	TownsPC98_OpnSquareSineSource(const float rate);
+	~TownsPC98_OpnSquareSineSource();
 
+	void init(const int *rsTable, const int *rseTable);
+	void reset();
+	uint8 readReg(uint8 address);
+	void writeReg(uint8 address, uint8 value, bool force = false);
+
+	void nextTick(int32 *buffer, uint32 bufferSize);
+
+private:
+	void updatesRegs();
+
+	uint8 _reg[16];
+	uint8 _updateRequestBuf[32];
+	int _updateRequest;
+	uint8 *_regIndex;
+	int _rand;
+
+	int8 _evpTimer;
+	uint32 _pReslt;
+	uint8 _attack;
+
+	bool _evpUpdate, _cont;
+
+	int _evpUpdateCnt;
+	uint8 _outN;
+	int _nTick;
+
+	int32 *_tlTable;
+	int32 *_tleTable;
+
+	const float _rate;
+	const int _tickLength;
+	int _timer;
+
+	struct Channel {
+		int tick;
+		uint8 smp;
+		uint8 out;
+	} _channels[3];
+
+	bool _ready;
+};
+
+class TownsPC98_OpnPercussionSource {
+public:
+	TownsPC98_OpnPercussionSource(const float rate);
+	~TownsPC98_OpnPercussionSource();
+
+	void init(const void *pcmData = 0);
+	void reset();
+	void writeReg(uint8 address, uint8 value);
+
+	void nextTick(int32 *buffer, uint32 bufferSize);
+
+private:
+	struct PcmInstrument {
+		const uint8 *data;
+
+		const uint8 *start;
+		const uint8 *end;
+		const uint8 *pos;
+		uint32 size;
+		bool active;
+		uint8 level;
+
+		int8 decState;
+		uint8 decStep;
+
+		int16 samples[2];
+		int out;
+	};
+
+	void recalcOuput(PcmInstrument *ins);
+	void advanceInput(PcmInstrument *ins);
+
+	PcmInstrument _pcmInstr[6];
+	uint8 _regs[48];
+
+	uint8 _totalLevel;
+
+	const int _tickLength;
+	int _timer;
+	bool _ready;
+};
+
 class TownsPC98_OpnDriver : public Audio::AudioStream {
 friend class TownsPC98_OpnChannel;
 friend class TownsPC98_OpnChannelSSG;
+friend class TownsPC98_OpnSfxChannel;
+friend class TownsPC98_OpnChannelPCM;
 public:
 	enum OpnType {
 		OD_TOWNS,
@@ -1456,15 +1588,17 @@
 	~TownsPC98_OpnDriver();
 
 	bool init();
-	void loadData(uint8 *data, bool loadPaused = false);
+
+	void loadMusicData(uint8 *data, bool loadPaused = false);
+	void loadSoundEffectData(uint8 *data, uint8 trackNum);
 	void reset();
 	void fadeOut();
-	
-	void pause() { _playing = false; }
-	void cont() { _playing = true; }
 
+	void pause() { _musicPlaying = false; }
+	void cont() { _musicPlaying = true; }
+
 	void callback();
-	void nextTick(int16 *buffer, uint32 bufferSize);
+	void nextTick(int32 *buffer, uint32 bufferSize);
 
 	bool looping() { return _looping == _updateChannelsFlag ? true : false; }
 
@@ -1479,8 +1613,13 @@
 
 	TownsPC98_OpnChannel **_channels;
 	TownsPC98_OpnChannelSSG **_ssgChannels;
-	//TownsPC98_OpnChannel *_adpcmChannel;
+	TownsPC98_OpnSfxChannel **_sfxChannels;
+	TownsPC98_OpnChannelPCM *_pcmChannel;
 
+	TownsPC98_OpnSquareSineSource *_ssg;
+	TownsPC98_OpnPercussionSource *_pcm;
+
+	void startSoundEffect();
 	void setTempo(uint8 tempo);
 
 	void lock() { _mutex.lock(); }
@@ -1492,6 +1631,7 @@
 
 	const uint8 *_opnCarrier;
 	const uint8 *_opnFreqTable;
+	const uint8 *_opnFreqTableSSG;
 	const uint8 *_opnFxCmdLen;
 	const uint8 *_opnLvlPresets;
 
@@ -1503,21 +1643,34 @@
 	int32 *_oprLevelOut;
 	int32 *_oprDetune;
 
-	uint8 *_trackData;
+	uint8 *_musicBuffer;
+	uint8 *_sfxBuffer;
+	uint8 *_trackPtr;
 	uint8 *_patches;
+	uint8 *_ssgPatches;
 
 	uint8 _cbCounter;
 	uint8 _updateChannelsFlag;
+	uint8 _updateSSGFlag;
+	uint8 _updatePCMFlag;
 	uint8 _finishedChannelsFlag;
+	uint8 _finishedSSGFlag;
+	uint8 _finishedPCMFlag;
+
 	uint16 _tempo;
-	bool _playing;
+	bool _musicPlaying;
+	bool _sfxPlaying;
 	bool _fading;
 	uint8 _looping;
-	uint32 _tickCounter;
+	uint32 _musicTickCounter;
 
-	bool _updateEnvelopes;
-	int _ssgFlag;
+	bool _updateEnvelopeParameters;
 
+	bool _regProtectionFlag;
+	int _sfxOffs;
+	uint8 *_sfxData;
+	uint16 _sfxOffsets[2];
+
 	int32 _samplesTillCallback;
 	int32 _samplesTillCallbackRemainder;
 	int32 _samplesPerCallback;
@@ -1525,12 +1678,14 @@
 
 	const int _numChan;
 	const int _numSSG;
-	const bool _hasADPCM;
+	const bool _hasPCM;
 	const bool _hasStereo;
 
-	double _baserate;
 	static const uint8 _drvTables[];
 	static const uint32 _adtStat[];
+	static const int _ssgTables[];
+
+	const float _baserate;
 	bool _ready;
 };
 
@@ -1538,14 +1693,15 @@
 	uint8 key, uint8 prt, uint8 id) : _drv(driver), _regOffset(regOffs), _flags(flgs), _chanNum(num), _keyNum(key),
 	_part(prt), _idFlag(id) {
 
-	_ticksLeft = _algorithm = _instrID = _totalLevel = _frqBlockMSB = _keyOffTime = _ssg1 = _ssg2 = 0;
-	_ptchWhlInitDelayLo = _ptchWhlInitDelayHi = _ptchWhlDuration = _ptchWhlCurDelay = _ptchWhlDurLeft = 0;
+	_ticksLeft = _algorithm = _instr = _totalLevel = _frqBlockMSB = _keyOffTime = 0;
+	_ssgStartLvl = _ssgTl = _ssgStep = _ssgTicksLeft = _ssgTargetLvl = _block = 0;
+	_ptchWhlInitDelayHi = _ptchWhlInitDelayLo = _ptchWhlDuration = _ptchWhlCurDelay = _ptchWhlDurLeft = 0;
 	_frqLSB = 0;
-	_protect = _updateEnvelopes = false;
+	_hold = _updateEnvelopeParameters = false;
 	_enableLeft = _enableRight = true;
-	_dataPtr = 0;		
+	_dataPtr = 0;
 	_ptchWhlModInitVal = _ptchWhlModCurVal = 0;
-	frequency = _frqTemp = 0;
+	_frequency = _frqTemp = 0;
 	memset(&_feedbuf, 0, sizeof(int) * 3);
 	_opr = 0;
 }
@@ -1559,7 +1715,6 @@
 }
 
 void TownsPC98_OpnChannel::init() {
-	
 	_opr = new TownsPC98_OpnOperator*[4];
 	for (int i = 0; i < 4; i++)
 		_opr[i] = new TownsPC98_OpnOperator(_drv->_baserate, _drv->_oprRates, _drv->_oprRateshift,
@@ -1592,16 +1747,16 @@
 void TownsPC98_OpnChannel::keyOff() {
 	// all operators off
 	uint8 value = _keyNum & 0x0f;
-	uint8 regAdress = 0x28;
-	writeReg(regAdress, value);
+	uint8 regAddress = 0x28;
+	writeReg(regAddress, value);
 	_flags |= CHS_KEYOFF;
 }
 
 void TownsPC98_OpnChannel::keyOn() {
 	// all operators on
 	uint8 value = _keyNum | 0xf0;
-	uint8 regAdress = 0x28;
-	writeReg(regAdress, value);
+	uint8 regAddress = 0x28;
+	writeReg(regAddress, value);
 }
 
 void TownsPC98_OpnChannel::loadData(uint8 *data) {
@@ -1645,13 +1800,13 @@
 	if (_flags & CHS_EOT)
 		return;
 
-	if (_protect == false && _ticksLeft == _keyOffTime)
+	if (_hold == false && _ticksLeft == _keyOffTime)
 		keyOff();
 
 	if (--_ticksLeft)
 		return;
 
-	if (_protect == false)
+	if (_hold == false)
 		keyOff();
 
 	uint8 cmd = 0;
@@ -1669,14 +1824,14 @@
 
 	if (cmd == 0x80) {
 		keyOff();
-		_protect = false;
+		_hold = false;
 	} else {
 		keyOn();
 
-		if (_protect == false || cmd != _frqBlockMSB)
+		if (_hold == false || cmd != _frqBlockMSB)
 			_flags |= CHS_RECALCFREQ;
-	
-		_protect = (para & 0x80) ? true : false;
+
+		_hold = (para & 0x80) ? true : false;
 		_frqBlockMSB = cmd;
 	}
 
@@ -1687,37 +1842,48 @@
 	if (_flags & CHS_RECALCFREQ) {
 		uint8 block = (_frqBlockMSB & 0x70) >> 1;
 		uint16 bfreq = ((const uint16*)_drv->_opnFreqTable)[_frqBlockMSB & 0x0f];
-		frequency = (bfreq + _frqLSB) | (block << 8);
 
-		writeReg(_regOffset + 0xa4, (frequency >> 8));
-		writeReg(_regOffset + 0xa0, (frequency & 0xff));
+		_frequency = (bfreq + _frqLSB) | (block << 8);
+		writeReg(_regOffset + 0xa4, (_frequency >> 8));
+		writeReg(_regOffset + 0xa0, (_frequency & 0xff));
 
-		_ptchWhlCurDelay = _ptchWhlInitDelayHi;
-		if (_flags & CHS_KEYOFF) {
-			_ptchWhlModCurVal = _ptchWhlModInitVal;
-			_ptchWhlCurDelay += _ptchWhlInitDelayLo;
-		}
-
-		_ptchWhlDurLeft = (_ptchWhlDuration >> 1);
-		_flags &= ~(CHS_KEYOFF | CHS_RECALCFREQ);
+		setupPitchWheel();
 	}
 
 	if (!(_flags & CHS_PITCHWHEELOFF)) {
-		if (--_ptchWhlCurDelay)
+		if (!processPitchWheel())
 			return;
-		_ptchWhlCurDelay = _ptchWhlInitDelayHi;
-		frequency += _ptchWhlModCurVal;
 
-		writeReg(_regOffset + 0xa4, (frequency >> 8));
-		writeReg(_regOffset + 0xa0, (frequency & 0xff));
+		writeReg(_regOffset + 0xa4, (_frequency >> 8));
+		writeReg(_regOffset + 0xa0, (_frequency & 0xff));
+	}
+}
 
-		if(!--_ptchWhlDurLeft) {
-			_ptchWhlDurLeft = _ptchWhlDuration;
-			_ptchWhlModCurVal = -_ptchWhlModCurVal;
-		}
+void TownsPC98_OpnChannel::setupPitchWheel() {
+	_ptchWhlCurDelay = _ptchWhlInitDelayHi;
+	if (_flags & CHS_KEYOFF) {
+		_ptchWhlModCurVal = _ptchWhlModInitVal;
+		_ptchWhlCurDelay += _ptchWhlInitDelayLo;
 	}
+	_ptchWhlDurLeft = (_ptchWhlDuration >> 1);
+	_flags &= ~(CHS_KEYOFF | CHS_RECALCFREQ);
 }
 
+bool TownsPC98_OpnChannel::processPitchWheel() {
+	if (--_ptchWhlCurDelay)
+		return false;
+
+	_ptchWhlCurDelay = _ptchWhlInitDelayHi;
+	_frequency += _ptchWhlModCurVal;
+
+	if(!--_ptchWhlDurLeft) {
+		_ptchWhlDurLeft = _ptchWhlDuration;
+		_ptchWhlModCurVal = -_ptchWhlModCurVal;
+	}
+
+	return true;
+}
+
 bool TownsPC98_OpnChannel::processControlEvent(uint8 cmd) {
 	uint8 para = *_dataPtr++;
 	return (this->*controlEvents[cmd & 0x0f])(para);
@@ -1743,10 +1909,24 @@
 }
 
 void TownsPC98_OpnChannel::reset() {
-	for (int i = 0; i < 4; i++)
-		_opr[i]->reset();
+	if (_opr) {
+		for (int i = 0; i < 4; i++)
+			_opr[i]->reset();
+	}
 
-	_updateEnvelopes = false;
+	_block = 0;
+	_frequency = 0;
+	_hold = false;
+	_frqTemp = 0;
+	_ssgTl = 0;
+	_ssgStartLvl = 0;
+	_ssgTargetLvl = 0;
+	_ssgStep = 0;
+	_ssgTicksLeft = 0;
+	_totalLevel = 0;
+	_flags |= CHS_EOT;
+
+	_updateEnvelopeParameters = false;
 	_enableLeft = _enableRight = true;
 	memset(&_feedbuf, 0, sizeof(int) * 3);
 }
@@ -1756,10 +1936,10 @@
 		_opr[i]->updatePhaseIncrement();
 }
 
-void TownsPC98_OpnChannel::generateOutput(int16 &leftSample, int16 &rightSample, int *del, int *feed) {
+void TownsPC98_OpnChannel::generateOutput(int32 &leftSample, int32 &rightSample, int *del, int *feed) {
 	int phbuf1, phbuf2, output;
 	phbuf1 = phbuf2 = output = 0;
-	
+
 	switch (_algorithm) {
 		case 0:
 			_opr[0]->generateOutput(0, feed, phbuf1);
@@ -1771,7 +1951,7 @@
 		case 1:
 			_opr[0]->generateOutput(0, feed, phbuf1);
 			_opr[2]->generateOutput(*del, 0, phbuf2);
-			_opr[1]->generateOutput(0, 0, phbuf1);					
+			_opr[1]->generateOutput(0, 0, phbuf1);
 			_opr[3]->generateOutput(phbuf2, 0, output);
 			*del = phbuf1;
 			break;
@@ -1792,7 +1972,7 @@
 		case 4:
 			_opr[0]->generateOutput(0, feed, phbuf1);
 			_opr[2]->generateOutput(0, 0, phbuf2);
-			_opr[1]->generateOutput(phbuf1, 0, output);					
+			_opr[1]->generateOutput(phbuf1, 0, output);
 			_opr[3]->generateOutput(phbuf2, 0, output);
 			*del = 0;
 			break;
@@ -1819,39 +1999,34 @@
 			break;
 		};
 
-	if (_enableLeft) {
-		int l = output + leftSample;
-		if (l > 32767)
-			l = 32767;
-		if (l < -32767)
-			l = -32767;
-		leftSample = (int16) l;
-	}
+	int32 finOut = ((output * 7) / 2);
 
-	if (_enableRight) {
-		int r = output + rightSample;
-		if (r > 32767)
-			r = 32767;
-		if (r < -32767)
-			r = -32767;
-		rightSample = (int16) r;
-	}
+	if (_enableLeft)
+		leftSample += finOut;
+
+	if (_enableRight)
+		rightSample += finOut;
 }
 
-void TownsPC98_OpnChannel::writeReg(uint8 regAdress, uint8 value) {
-	uint8 h = regAdress & 0xf0;
-	uint8 l = (regAdress & 0x0f);
+void TownsPC98_OpnChannel::writeReg(uint8 regAddress, uint8 value) {
+	if (_drv->_regProtectionFlag)
+		return;
+
+	uint8 h = regAddress & 0xf0;
+	uint8 l = (regAddress & 0x0f);
 	static const uint8 oprOrdr[] = { 0, 2, 1, 3 };
 	uint8 o = oprOrdr[(l - _regOffset) >> 2];
-	
+
 	switch (h) {
 		case 0x00:
 			// ssg
-			warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress);
+			if (_drv->_ssg)
+				_drv->_ssg->writeReg(regAddress, value);
 			break;
 		case 0x10:
-			// adpcm
-			warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress);
+			// pcm rhythm channel
+			if (_drv->_pcm)
+				_drv->_pcm->writeReg(regAddress - 0x10, value);
 			break;
 		case 0x20:
 			if (l == 8) {
@@ -1884,7 +2059,7 @@
 			// detune, multiple
 			_opr[o]->detune((value >> 4) & 7);
 			_opr[o]->multiple(value & 0x0f);
-			_updateEnvelopes = true;
+			_updateEnvelopeParameters = true;
 			break;
 
 		case 0x40:
@@ -1896,7 +2071,7 @@
 			// rate scaling, attack rate
 			_opr[o]->attackRate(value & 0x1f);
 			if (_opr[o]->scaleRate(value >> 6))
-				_updateEnvelopes = true;
+				_updateEnvelopeParameters = true;
 			break;
 
 		case 0x60:
@@ -1904,7 +2079,6 @@
 			_opr[o]->decayRate(value & 0x1f);
 			if (value & 0x80)
 				warning("TownsPC98_OpnDriver: TRYING TO USE AMP MODULATION (NOT SUPPORTED)");
-
 			break;
 
 		case 0x70:
@@ -1919,8 +2093,7 @@
 			break;
 
 		case 0x90:
-			// ssg
-			warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress);
+			warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAddress);
 			break;
 
 		case 0xa0:
@@ -1928,7 +2101,7 @@
 			l -= _regOffset;
 			if (l == 0) {
 				_frqTemp = (_frqTemp & 0xff00) | value;
-				_updateEnvelopes = true;
+				_updateEnvelopeParameters = true;
 				for (int i = 0; i < 4; i++)
 					_opr[i]->frequency(_frqTemp);
 			} else if (l == 4) {
@@ -1964,13 +2137,13 @@
 			break;
 
 		default:
-			warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress);
+			warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAddress);
 			break;
 	}
 }
 
 bool TownsPC98_OpnChannel::control_f0_setPatch(uint8 para) {
-	_instrID = para;
+	_instr = para;
 	uint8 reg = _regOffset + 0x80;
 
 	for (int i = 0; i < 4; i++) {
@@ -1979,7 +2152,7 @@
 		reg += 4;
 	}
 
-	const uint8 *tptr = _drv->_patches + ((uint32)_instrID << 5);
+	const uint8 *tptr = _drv->_patches + ((uint32)_instr << 5);
 	reg = _regOffset + 0x30;
 
 	// write registers 0x30 to 0x8f
@@ -2043,7 +2216,7 @@
 
 	if (*_dataPtr) {
 		// repeat section until counter has reached zero
-		_dataPtr = _drv->_trackData + READ_LE_UINT16(_dataPtr + 2);
+		_dataPtr = _drv->_trackPtr + READ_LE_UINT16(_dataPtr + 2);
 	} else {
 		// reset counter, advance to next section
 		_dataPtr[0] = _dataPtr[1];
@@ -2053,8 +2226,8 @@
 }
 
 bool TownsPC98_OpnChannel::control_f7_setupPitchWheel(uint8 para) {
-	_ptchWhlInitDelayLo = _dataPtr[0];
-	_ptchWhlInitDelayHi = para;
+	_ptchWhlInitDelayHi = _dataPtr[0];
+	_ptchWhlInitDelayLo = para;
 	_ptchWhlModInitVal = (int16) READ_LE_UINT16(_dataPtr + 1);
 	_ptchWhlDuration = _dataPtr[3];
 	_dataPtr += 4;
@@ -2070,11 +2243,12 @@
 			_flags |= CHS_PITCHWHEELOFF;
 		}
 	} else {
-		//uint8 skipChannels = para / 36;
-		//uint8 entry = para % 36;
-		//TownsPC98_OpnDriver::TownsPC98_OpnChannel *t = &chan[skipChannels];
-		////// NOT IMPLEMENTED
-		//t->unnamedEntries[entry] = *_dataPtr++;
+		/* NOT IMPLEMENTED
+		uint8 skipChannels = para / 36;
+		uint8 entry = para % 36;
+		TownsPC98_OpnDriver::TownsPC98_OpnChannel *t = &chan[skipChannels];
+		
+		t->unnamedEntries[entry] = *_dataPtr++;*/
 	}
 	return true;
 }
@@ -2113,7 +2287,7 @@
 }
 
 bool TownsPC98_OpnChannel::control_fd_jump(uint8 para) {
-	uint8 *tmp = _drv->_trackData + READ_LE_UINT16(_dataPtr - 1);
+	uint8 *tmp = _drv->_trackPtr + READ_LE_UINT16(_dataPtr - 1);
 	_dataPtr = (tmp[1] == 1) ? tmp : ++_dataPtr;
 	return true;
 }
@@ -2127,7 +2301,7 @@
 	uint16 val = READ_LE_UINT16(--_dataPtr);
 	if (val) {
 		// loop
-		_dataPtr = _drv->_trackData + val;
+		_dataPtr = _drv->_trackPtr + val;
 		return true;
 	} else {
 		// quit parsing for active channel
@@ -2139,31 +2313,250 @@
 	}
 }
 
-bool TownsPC98_OpnChannel::control_f0_setPatchSSG(uint8 para) {
-	_instrID = para << 4;
+TownsPC98_OpnChannelSSG::TownsPC98_OpnChannelSSG(TownsPC98_OpnDriver *driver, uint8 regOffs,
+		uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) :
+		TownsPC98_OpnChannel(driver, regOffs, flgs, num, key, prt, id) {
+}
+
+void TownsPC98_OpnChannelSSG::init() {
+	_algorithm = 0x80;
+
+	#define Control(x)	&TownsPC98_OpnChannelSSG::control_##x
+	static const ControlEventFunc ctrlEventsSSG[] = {
+		Control(f0_setInstr),
+		Control(f1_setTotalLevel),
+		Control(f2_setKeyOffTime),
+		Control(f3_setFreqLSB),
+		Control(f4_setAlgorithm),
+		Control(f5_setTempo),
+		Control(f6_repeatSection),
+		Control(f7_setupPitchWheel),
+		Control(f8_togglePitchWheel),
+		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_OpnChannelSSG::processEvents() {
+	if (_flags & CHS_EOT)
+		return;
+
+	_drv->_regProtectionFlag = (_flags & CHS_PROTECT) ? true : false;
+
+	if (!_hold && _ticksLeft == _keyOffTime)
+		nextShape();
+
+	if (!--_ticksLeft) {
+
+		uint8 cmd = 0;
+		bool loop = true;
+
+		while (loop) {
+			cmd = *_dataPtr++;
+			if (cmd < 0xf0)
+				loop = false;
+			else if (!processControlEvent(cmd))
+				return;
+		}
+
+		uint8 para = *_dataPtr++;
+
+		if (cmd == 0x80) {
+			nextShape();
+			_hold = false;
+		} else {
+			if (!_hold) {
+				_instr &= 0xf0;
+				_ssgStep = _drv->_ssgPatches[_instr];
+				_ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f;
+				_ssgTargetLvl = _drv->_ssgPatches[_instr + 2];
+				_ssgStartLvl = _drv->_ssgPatches[_instr + 3];
+				_flags = (_flags & ~CHS_SSGOFF) | CHS_KEYOFF;
+			}
+
+			keyOn();
+
+			if (_hold == false || cmd != _frqBlockMSB)
+				_flags |= CHS_RECALCFREQ;
+
+			_hold = (para & 0x80) ? true : false;
+			_frqBlockMSB = cmd;
+		}
+
+		_ticksLeft = para & 0x7f;
+	}
+
+	if (!(_flags & CHS_SSGOFF)) {
+		if (--_ssgTicksLeft) {
+			if (!_drv->_fading)
+				setOutputLevel(_ssgStartLvl);
+			return;
+		}
+
+		_ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f;
+
+		if (_drv->_ssgPatches[_instr + 1] & 0x80) {
+			uint8 t = _ssgStartLvl - _ssgStep;
+
+			if (_ssgStep <= _ssgStartLvl && _ssgTargetLvl < t) {
+				if (!_drv->_fading)
+					setOutputLevel(t);
+				return;
+			}
+		} else {
+			int t = _ssgStartLvl + _ssgStep;
+			uint8 p = (uint8) (t & 0xff);
+
+			if (t < 256 && _ssgTargetLvl > p) {
+				if (!_drv->_fading)
+					setOutputLevel(p);
+				return;
+			}
+		}
+
+		setOutputLevel(_ssgTargetLvl);
+		if (_ssgStartLvl && !(_instr & 8)){
+			_instr += 4;
+			_ssgStep = _drv->_ssgPatches[_instr];
+			_ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f;
+			_ssgTargetLvl = _drv->_ssgPatches[_instr + 2];
+		} else {
+			_flags |= CHS_SSGOFF;
+			setOutputLevel(0);
+		}
+	}
+}
+
+void TownsPC98_OpnChannelSSG::processFrequency() {
+	if (_algorithm & 0x40)
+		return;
+
+	if (_flags & CHS_RECALCFREQ) {
+		_block = _frqBlockMSB >> 4;
+		_frequency = ((const uint16*)_drv->_opnFreqTableSSG)[_frqBlockMSB & 0x0f] + _frqLSB;
+
+		uint16 f = _frequency >> _block;
+		writeReg(_regOffset << 1, f & 0xff);
+		writeReg((_regOffset << 1) + 1, f >> 8);
+
+		setupPitchWheel();
+	}
+
+	if (!(_flags & (CHS_EOT | CHS_PITCHWHEELOFF | CHS_SSGOFF))) {
+		if (!processPitchWheel())
+			return;
+
+		processPitchWheel();
+
+		uint16 f = _frequency >> _block;
+		writeReg(_regOffset << 1, f & 0xff);
+		writeReg((_regOffset << 1) + 1, f >> 8);
+	}
+}
+
+bool TownsPC98_OpnChannelSSG::processControlEvent(uint8 cmd) {
+	uint8 para = *_dataPtr++;
+	return (this->*controlEvents[cmd & 0x0f])(para);
+}
+
+void TownsPC98_OpnChannelSSG::nextShape() {
+	_instr = (_instr & 0xf0) + 0x0c;
+	_ssgStep = _drv->_ssgPatches[_instr];
+	_ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f;
+	_ssgTargetLvl = _drv->_ssgPatches[_instr + 2];
+}
+
+void TownsPC98_OpnChannelSSG::keyOn() {
+	uint8 c = 0x7b;
+	uint8 t = (_algorithm & 0xC0) << 1;
+	if (_algorithm & 0x80)
+		t |= 4;
+
+	c = (c << (_regOffset + 1)) | (c >> (7 - _regOffset));
+	t = (t << (_regOffset + 1)) | (t >> (7 - _regOffset));
+
+	if (!(_algorithm & 0x80))
+		writeReg(6, _algorithm & 0x7f);
+
+	uint8 e = (_drv->_ssg->readReg(7) & c) | t;
+	writeReg(7, e);
+}
+
+void TownsPC98_OpnChannelSSG::protect() {
+	_flags |= CHS_PROTECT;
+}
+
+void TownsPC98_OpnChannelSSG::restore() {
+	_flags &= ~CHS_PROTECT;
+	keyOn();
+	writeReg(8 + _regOffset, _ssgTl);
+	uint16 f = _frequency >> _block;
+	writeReg(_regOffset << 1, f & 0xff);
+	writeReg((_regOffset << 1) + 1, f >> 8);
+}
+
+void TownsPC98_OpnChannelSSG::loadData(uint8 *data) {
+	_drv->_regProtectionFlag = (_flags & CHS_PROTECT) ? true : false;
+	TownsPC98_OpnChannel::loadData(data);
+	setOutputLevel(0);
+	_algorithm = 0x80;
+}
+
+void TownsPC98_OpnChannelSSG::setOutputLevel(uint8 lvl) {
+	_ssgStartLvl = lvl;
+	uint16 newTl = (((uint16)_totalLevel + 1) * (uint16)lvl) >> 8;
+	if (newTl == _ssgTl)
+		return;
+	_ssgTl = newTl;
+	writeReg(8 + _regOffset, _ssgTl);
+}
+
+void TownsPC98_OpnChannelSSG::fadeStep() {
+	_totalLevel--;
+	if ((int8)_totalLevel < 0)
+		_totalLevel = 0;
+	setOutputLevel(_ssgStartLvl);
+}
+
+bool TownsPC98_OpnChannelSSG::control_f0_setInstr(uint8 para) {
+	_instr = para << 4;
 	para = (para >> 3) & 0x1e;
 	if (para)
 		return control_f4_setAlgorithm(para | 0x40);
 	return true;
 }
 
-bool TownsPC98_OpnChannel::control_f1_setTotalLevel(uint8 para) {
+bool TownsPC98_OpnChannelSSG::control_f1_setTotalLevel(uint8 para) {
 	if (!_drv->_fading)
 		_totalLevel = para;
 	return true;
 }
 
-bool TownsPC98_OpnChannel::control_f4_setAlgorithm(uint8 para) {
+bool TownsPC98_OpnChannelSSG::control_f4_setAlgorithm(uint8 para) {
 	_algorithm = para;
 	return true;
 }
 
-bool TownsPC98_OpnChannel::control_f9_unkSSG(uint8 para) {
-	_dataPtr += 5;
+bool TownsPC98_OpnChannelSSG::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++;
 	return true;
 }
 
-bool TownsPC98_OpnChannel::control_fb_incOutLevelSSG(uint8 para) {
+bool TownsPC98_OpnChannelSSG::control_fb_incOutLevel(uint8 para) {
 	_dataPtr--;
 	if (_drv->_fading)
 		return true;
@@ -2175,7 +2568,7 @@
 	return true;
 }
 
-bool TownsPC98_OpnChannel::control_fc_decOutLevelSSG(uint8 para) {
+bool TownsPC98_OpnChannelSSG::control_fc_decOutLevel(uint8 para) {
 	_dataPtr--;
 	if (_drv->_fading)
 		return true;
@@ -2186,184 +2579,474 @@
 	return true;
 }
 
-bool TownsPC98_OpnChannel::control_ff_endOfTrackSSG(uint8 para) {
-	uint16 val = READ_LE_UINT16(--_dataPtr);
-	if (val) {
-		// loop
-		_dataPtr = _drv->_trackData + val;
-		return true;
+bool TownsPC98_OpnChannelSSG::control_ff_endOfTrack(uint8 para) {
+	if (!_drv->_sfxOffs) {
+		uint16 val = READ_LE_UINT16(--_dataPtr);
+		if (val) {
+			// loop
+			_dataPtr = _drv->_trackPtr + val;
+			return true;
+		} else {
+			// stop parsing
+			if (!_drv->_fading)
+				setOutputLevel(0);
+			--_dataPtr;
+			_flags |= CHS_EOT;
+			_drv->_finishedSSGFlag |= _idFlag;
+		}
 	} else {
-		// quit parsing for active channel
-		--_dataPtr;
+		// end of sfx track - restore ssg music channel
 		_flags |= CHS_EOT;
-		//_finishedChannelsFlag |= _idFlag;
-		keyOff();
-		return false;
+		_drv->_ssgChannels[_chanNum]->restore();
 	}
+
+	return false;
 }
 
-TownsPC98_OpnChannelSSG::TownsPC98_OpnChannelSSG(TownsPC98_OpnDriver *driver, uint8 regOffs, 
+void TownsPC98_OpnSfxChannel::loadData(uint8 *data) {
+	_flags = CHS_ALL_BUT_EOT;
+	_ticksLeft = 1;
+	_dataPtr = data;
+	_ssgTl = 0xff;
+	_algorithm = 0x80;
+}
+
+TownsPC98_OpnChannelPCM::TownsPC98_OpnChannelPCM(TownsPC98_OpnDriver *driver, uint8 regOffs,
 		uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) :
 		TownsPC98_OpnChannel(driver, regOffs, flgs, num, key, prt, id) {
 }
 
-void TownsPC98_OpnChannelSSG::init() {
+void TownsPC98_OpnChannelPCM::init() {
 	_algorithm = 0x80;
-	
-	_opr = new TownsPC98_OpnOperator*[4];
-	for (int i = 0; i < 4; i++)
-		_opr[i] = new TownsPC98_OpnOperator(_drv->_baserate, _drv->_oprRates, _drv->_oprRateshift,
-			_drv->_oprAttackDecay, _drv->_oprFrq, _drv->_oprSinTbl, _drv->_oprLevelOut, _drv->_oprDetune);
 
-	#define Control(x)	&TownsPC98_OpnChannelSSG::control_##x
-	static const ControlEventFunc ctrlEventsSSG[] = {
-		Control(f0_setPatchSSG),
-		Control(f1_setTotalLevel),
-		Control(f2_setKeyOffTime),
-		Control(f3_setFreqLSB),
-		Control(f4_setOutputLevel),
-		Control(f5_setTempo),
+	#define Control(x)	&TownsPC98_OpnChannelPCM::control_##x
+	static const ControlEventFunc ctrlEventsPCM[] = {
+		Control(dummy),
+		Control(f1_pcmStart),
+		Control(dummy),
+		Control(dummy),
+		Control(dummy),
+		Control(dummy),
 		Control(f6_repeatSection),
-		Control(f7_setupPitchWheel),
-		Control(f8_togglePitchWheel),
-		Control(f9_unkSSG),
+		Control(dummy),
+		Control(dummy),
+		Control(dummy),
 		Control(fa_writeReg),
-		Control(fb_incOutLevelSSG),
-		Control(fc_decOutLevelSSG),
-		Control(fd_jump),
 		Control(dummy),
-		Control(ff_endOfTrackSSG)
+		Control(dummy),
+		Control(dummy),
+		Control(dummy),
+		Control(ff_endOfTrack)
 	};
 	#undef Control
 
-	controlEvents = ctrlEventsSSG;
+	controlEvents = ctrlEventsPCM;
 }
 
-void TownsPC98_OpnChannelSSG::processEvents() {
+void TownsPC98_OpnChannelPCM::loadData(uint8 *data) {
+	_flags = (_flags & ~CHS_EOT) | CHS_ALL_BUT_EOT;
+	_ticksLeft = 1;
+	_dataPtr = data;
+	_totalLevel = 0x7F;
+}
+
+void TownsPC98_OpnChannelPCM::processEvents()  {
 	if (_flags & CHS_EOT)
 		return;
 
-	_drv->_ssgFlag = (_flags & CHS_SSG) ? -1 : 0;
-
-	if (_protect == false && _ticksLeft == _keyOffTime)
-		keyOff();
-
 	if (--_ticksLeft)
 		return;
 
-	if (_protect == false)
-		keyOff();
-
 	uint8 cmd = 0;
 	bool loop = true;
 
 	while (loop) {
 		cmd = *_dataPtr++;
-		if (cmd < 0xf0)
+		if (cmd == 0x80) {
 			loop = false;
-		else if (!processControlEvent(cmd))
+		} else if (cmd < 0xf0) {
+			writeReg(0x10, cmd);
+		} else if (!processControlEvent(cmd)) {
 			return;
+		}
 	}
 
+	_ticksLeft = *_dataPtr++;
+}
+
+bool TownsPC98_OpnChannelPCM::processControlEvent(uint8 cmd) {
 	uint8 para = *_dataPtr++;
+	return (this->*controlEvents[cmd & 0x0f])(para);
+}
 
-	if (cmd == 0x80) {
-		keyOff();
-		_protect = false;
+void TownsPC98_OpnChannelPCM::reset() {
+	TownsPC98_OpnChannel::reset();
+
+	if (_drv->_pcm)
+		_drv->_pcm->reset();
+}
+
+bool TownsPC98_OpnChannelPCM::control_f1_pcmStart(uint8 para) {
+	_totalLevel = para;
+	writeReg(0x11, para);
+	return true;
+}
+
+bool TownsPC98_OpnChannelPCM::control_ff_endOfTrack(uint8 para) {
+	uint16 val = READ_LE_UINT16(--_dataPtr);
+	if (val) {
+		// loop
+		_dataPtr = _drv->_trackPtr + val;
+		return true;
 	} else {
-		keyOn();
+		// quit parsing for active channel
+		--_dataPtr;
+		_flags |= CHS_EOT;
+		_drv->_finishedPCMFlag |= _idFlag;
+		return false;
+	}
+}
 
-		if (_protect == false || cmd != _frqBlockMSB)
-			_flags |= CHS_RECALCFREQ;
-	
-		_protect = (para & 0x80) ? true : false;
-		_frqBlockMSB = cmd;
+TownsPC98_OpnSquareSineSource::TownsPC98_OpnSquareSineSource(const float rate) : _rate(rate),	_tlTable(0),
+	_tleTable(0), _regIndex(_reg), _updateRequest(-1), _tickLength((int)(rate * 32768.0f * 27.0f)), _ready(0) {
+	memset(_reg, 0, 16);
+	memset(_channels, 0, sizeof(Channel) * 3);
+	reset();
+}
+
+TownsPC98_OpnSquareSineSource::~TownsPC98_OpnSquareSineSource() {
+	delete [] _tlTable;
+	delete [] _tleTable;
+}
+
+void TownsPC98_OpnSquareSineSource::init(const int *rsTable, const int *rseTable) {
+	if (_ready) {
+		reset();
+		return;
 	}
 
-	_ticksLeft = para & 0x7f;
+	delete [] _tlTable;
+	delete [] _tleTable;
+	_tlTable = new int32[16];
+	_tleTable = new int32[32];
+	float a, b, d;
+	d = 801.0f;
 
-	if (!(_flags & CHS_SSG)) {
+	for (int i = 0; i < 16; i++) {
+		b = 1.0f / rsTable[i];
+		a = 1.0f / d + b + 1.0f / 1000.0f;
+		float v = (b / a) * 32767.0f;
+		_tlTable[i] = (int32) v;
 
+		b = 1.0f / rseTable[i];
+		a = 1.0f / d + b + 1.0f / 1000.0f;
+		v = (b / a) * 32767.0f;
+		_tleTable[i] = (int32) v;
 	}
+
+	for (int i = 16; i < 32; i++) {
+		b = 1.0f / rseTable[i];
+		a = 1.0f / d + b + 1.0f / 1000.0f;
+		float v = (b / a) * 32767.0f;
+		_tleTable[i] = (int32) v;
+	}
+
+	_ready = true;
 }
 
-void TownsPC98_OpnChannelSSG::processFrequency() {
-	if (_flags & CHS_RECALCFREQ) {
-		uint8 block = (_frqBlockMSB & 0x70) >> 1;
-		uint16 bfreq = ((const uint16*)_drv->_opnFreqTable)[_frqBlockMSB & 0x0f];
-		frequency = (bfreq + _frqLSB) | (block << 8);
+void TownsPC98_OpnSquareSineSource::reset() {
+	_rand = 1;
+	_outN = 1;
+	_updateRequest = -1;
+	_nTick = _evpUpdateCnt = 0;
+	_regIndex = _reg;
+	_evpTimer = 0x1f;
+	_pReslt = 0x1f;
+	_attack = 0;
+	_cont = false;
+	_evpUpdate = true;
+	_timer = 0;
 
-		writeReg(_regOffset + 0xa4, (frequency >> 8));
-		writeReg(_regOffset + 0xa0, (frequency & 0xff));
+	for (int i = 0; i < 3; i++) {
+		_channels[i].tick = 0;
+		_channels[i].smp = _channels[i].out = 0;
+	}
 
-		_ptchWhlCurDelay = _ptchWhlInitDelayHi;
-		if (_flags & CHS_KEYOFF) {
-			_ptchWhlModCurVal = _ptchWhlModInitVal;
-			_ptchWhlCurDelay += _ptchWhlInitDelayLo;
+	for (int i = 0; i < 14; i++)
+		writeReg(i, 0, true);
+
+	writeReg(7, 0xbf, true);
+}
+
+uint8 TownsPC98_OpnSquareSineSource::readReg(uint8 address) {
+	return _reg[address];
+}
+
+void TownsPC98_OpnSquareSineSource::writeReg(uint8 address, uint8 value, bool force) {
+	_regIndex = &_reg[address];
+	int o = _regIndex - _reg;
+	if (!force && (o == 13 || *_regIndex != value)) {
+		if (_updateRequest == 31) {
+			warning("TownsPC98_OpnSquareSineSource: event buffer overflow");
+			_updateRequest = -1;
 		}
+		_updateRequestBuf[++_updateRequest] = value;
+		_updateRequestBuf[++_updateRequest] = o;
+		return;
+	}
 
-		_ptchWhlDurLeft = (_ptchWhlDuration >> 1);
-		_flags &= ~(CHS_KEYOFF | CHS_RECALCFREQ);
+	*_regIndex = value;
+}
+
+void TownsPC98_OpnSquareSineSource::updatesRegs() {
+	for (int i = 0; i < _updateRequest;) {
+		uint8 b = _updateRequestBuf[i++];
+		uint8 a = _updateRequestBuf[i++];
+		writeReg(a, b, true);
 	}
+	_updateRequest = -1;
+}
 
-	if (!(_flags & CHS_PITCHWHEELOFF)) {
-		if (--_ptchWhlCurDelay)
-			return;
-		_ptchWhlCurDelay = _ptchWhlInitDelayHi;
-		frequency += _ptchWhlModCurVal;
+TownsPC98_OpnPercussionSource::TownsPC98_OpnPercussionSource(const float rate) :
+	_tickLength((int)(rate * 65536.0)), _timer(0), _ready(false) {
+		memset(_pcmInstr, 0, sizeof(PcmInstrument) * 6);
+}
 
-		writeReg(_regOffset + 0xa4, (frequency >> 8));
-		writeReg(_regOffset + 0xa0, (frequency & 0xff));
+TownsPC98_OpnPercussionSource::~TownsPC98_OpnPercussionSource() {
+}
 
-		if(!--_ptchWhlDurLeft) {
-			_ptchWhlDurLeft = _ptchWhlDuration;
-			_ptchWhlModCurVal = -_ptchWhlModCurVal;
+void TownsPC98_OpnPercussionSource::init(const void *pcmData) {
+	if (_ready) {
+		reset();
+		return;
+	}
+
+	const uint8 *start = (const uint8*) pcmData;
+	const uint8 *pos = start;
+
+	if (pcmData) {
+		for (int i = 0; i < 6; i++) {
+			_pcmInstr[i].data = start + READ_BE_UINT16(pos);
+			pos += 2;
+			_pcmInstr[i].size = READ_BE_UINT16(pos);
+			pos += 2;
 		}
+		reset();
+		_ready = true;
+	} else {
+		memset(_pcmInstr, 0, sizeof(PcmInstrument) * 6);
+		_ready = false;
 	}
 }
 
-void TownsPC98_OpnChannelSSG::keyOff() {
-	// all operators off
-	uint8 value = _keyNum & 0x0f;
-	uint8 regAdress = 0x28;
-	writeReg(regAdress, value);
-	_flags |= CHS_KEYOFF;
+void TownsPC98_OpnPercussionSource::reset() {
+	_timer = 0;
+	_totalLevel = 63;
+
+	memset(_regs, 0, 48);
+
+	for (int i = 0; i < 6; i++) {
+		PcmInstrument *s = &_pcmInstr[i];
+		s->pos = s->start = s->data;
+		s->end = s->data + s->size;
+		s->active = false;
+		s->level = 0;
+		s->out = 0;
+		s->decStep = 1;
+		s->decState = 0;
+		s->samples[0] = s->samples[1] = 0;
+	}
 }
 
-void TownsPC98_OpnChannelSSG::keyOn() {
-	// all operators on
-	uint8 value = _keyNum | 0xf0;
-	uint8 regAdress = 0x28;
-	writeReg(regAdress, value);
+void TownsPC98_OpnPercussionSource::writeReg(uint8 address, uint8 value) {
+	if (!_ready)
+		return;
+
+	 uint8 h = address >> 4;
+	 uint8 l = address & 15;
+
+	_regs[address] = value;
+
+	if (address == 0) {
+		if (value & 0x80) {
+			//key off
+			for (int i = 0; i < 6; i++) {
+				if ((value >> i) & 1)
+					_pcmInstr[i].active = false;
+			}
+		} else {
+			//key on
+			for (int i = 0; i < 6; i++) {
+				if ((value >> i) & 1) {
+					PcmInstrument *s = &_pcmInstr[i];
+					s->pos = s->start;
+					s->active = true;
+					s->out = 0;
+					s->samples[0] = s->samples[1] = 0;
+					s->decStep = 1;
+					s->decState = 0;
+				}
+			}
+		}
+	} else if (address == 1) {
+		// total level
+		_totalLevel = (value & 63) ^ 63;
+		for (int i = 0; i < 6; i++)
+			recalcOuput(&_pcmInstr[i]);
+	} else if (!h && l & 8) {
+		// instrument level
+		l &= 7;
+		_pcmInstr[l].level = (value & 0x1f) ^ 0x1f;
+		recalcOuput(&_pcmInstr[l]);
+	} else if (h & 3) {
+		l &= 7;
+		if (h == 1) {
+			// set start offset
+			_pcmInstr[l].start  = _pcmInstr[l].data + ((_regs[24 + l] * 256 + _regs[16 + l]) << 8);
+		} else if (h == 2) {
+			// set end offset
+			_pcmInstr[l].end = _pcmInstr[l].data + ((_regs[40 + l] * 256 + _regs[32 + l]) << 8) + 255;
+		}
+	}
 }
 
-void TownsPC98_OpnChannelSSG::loadData(uint8 *data) {
-	_drv->_ssgFlag = (_flags & CHS_SSG) ? -1 : 0;
-	opn_SSG_UNK(0);
-	TownsPC98_OpnChannel::loadData(data);
-	_algorithm = 0x80;
+void TownsPC98_OpnPercussionSource::nextTick(int32 *buffer, uint32 bufferSize) {
+	if (!_ready)
+		return;
+
+	for (uint32 i = 0; i < bufferSize; i++) {
+		_timer += _tickLength;
+		while (_timer > 0x30000) {
+			_timer -= 0x30000;
+
+			for (int ii = 0; ii < 6; ii++) {
+				PcmInstrument *s = &_pcmInstr[ii];
+				if (s->active) {
+					recalcOuput(s);
+					if (s->decStep) {
+						advanceInput(s);
+						if (s->pos == s->end)
+							s->active = false;
+					}
+					s->decStep ^= 1;
+				}
+			}
+		}
+
+		int32 finOut = 0;
+
+		for (int ii = 0; ii < 6; ii++) {
+			if (_pcmInstr[ii].active)
+				finOut += _pcmInstr[ii].out;
+		}
+
+		finOut = (finOut * 7);
+
+		buffer[i << 1] += finOut;
+		buffer[(i << 1) + 1] += finOut;
+	}
 }
 
-void TownsPC98_OpnChannelSSG::opn_SSG_UNK(uint8 a) {
-	_ssg1 = a;
-	uint16 h = (_totalLevel + 1) * a;
-	if ((h >> 8) == _ssg2)
+void TownsPC98_OpnPercussionSource::recalcOuput(PcmInstrument *ins) {
+	uint32 s = _totalLevel + ins->level;
+	uint32 x = s > 62 ? 0 : (1 + (s >> 3));
+	int32 y = s > 62 ? 0 : (15 - (s & 7));
+	ins->out = ((ins->samples[ins->decStep] * y) >> x) & ~3;
+}
+
+void TownsPC98_OpnPercussionSource::advanceInput(PcmInstrument *ins) {
+	static const int8 adjustIndex[] = {-1, -1, -1, -1, 2, 5, 7, 9 };
+
+	static const int16 stepTable[] = { 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55,
+		60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337,
+		371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552
+	};
+	
+	int d = ins->samples[1];
+	uint8 cur = (int8) *ins->pos++;
+
+	for (int i = 0; i < 2; i++) {
+		int b = (2 * (cur & 7) + 1) * stepTable[ins->decState] / 8;
+		if (cur & 8)
+			b = -b;
+		d += b;
+		d = CLIP<int16>(d, -2048, 2047);
+		ins->samples[i] = d;
+		ins->decState = CLIP<int8>(ins->decState + adjustIndex[cur & 7], 0, 48);
+		cur >>= 4;
+	}
+}
+
+void TownsPC98_OpnSquareSineSource::nextTick(int32 *buffer, uint32 bufferSize) {
+	if (!_ready)
 		return;
-	_ssg2 = (h >> 8);
-	writeReg(8 + _regOffset, _ssg2);
+
+	for (uint32 i = 0; i < bufferSize; i++) {
+		_timer += _tickLength;
+		while (_timer > 0x30000) {
+			_timer -= 0x30000;
+
+			if (++_nTick >= (_reg[6] & 0x1f)) {
+				if ((_rand + 1) & 2)
+					_outN ^= 1;
+
+				_rand = (((_rand & 1) ^ ((_rand >> 3) & 1)) << 16) | (_rand >> 1);
+				_nTick = 0;
+			}
+
+			for (int ii = 0; ii < 3; ii++) {
+				if (++_channels[ii].tick >= (((_reg[ii * 2 + 1] & 0x0f) << 8) | _reg[ii * 2])) {
+					_channels[ii].tick = 0;
+					_channels[ii].smp ^= 1;
+				}
+				_channels[ii].out = (_channels[ii].smp | ((_reg[7] >> ii) & 1)) & (_outN | ((_reg[7] >> (ii + 3)) & 1));
+			}
+
+			if (_evpUpdate) {
+				if (++_evpUpdateCnt >= ((_reg[12] << 8) | _reg[11])) {
+					_evpUpdateCnt = 0;
+
+					if (--_evpTimer < 0) {
+						if (_cont) {
+							_evpTimer &= 0x1f;
+						} else {
+							_evpUpdate = false;
+							_evpTimer = 0;
+						}
+					}
+				}
+			}
+			_pReslt = _evpTimer ^ _attack;
+			updatesRegs();
+		}
+
+		int32 finOut = 0;
+		for (int ii = 0; ii < 3; ii++) {
+			if ((_reg[ii + 8] >> 4) & 1)
+				finOut += _tleTable[_channels[ii].out ? _pReslt : 0];
+			else
+				finOut += _tlTable[_channels[ii].out ? (_reg[ii + 8] & 0x0f) : 0];
+		}
+
+		finOut /= 2;
+		buffer[i << 1] += finOut;
+		buffer[(i << 1) + 1] += finOut;
+	}
 }
 
 TownsPC98_OpnDriver::TownsPC98_OpnDriver(Audio::Mixer *mixer, OpnType type) :
-	_mixer(mixer), _trackData(0), _playing(false), _fading(false), _channels(0), _ssgChannels(0),
-	_looping(0), _opnCarrier(_drvTables + 76), _opnFreqTable(_drvTables + 84),
-	_opnFxCmdLen(_drvTables + 36), _opnLvlPresets(_drvTables + (type == OD_TOWNS ? 52 : 220)) ,
-	_oprRates(0), _oprRateshift(0), _oprAttackDecay(0), _oprFrq(0),	_oprSinTbl(0), _oprLevelOut(0),
-	_oprDetune(0), _cbCounter(4), _tickCounter(0), _updateChannelsFlag(type == OD_TYPE26 ? 0x07 : 0x3F),
-	_finishedChannelsFlag(0), _samplesTillCallback(0), _samplesTillCallbackRemainder(0), _ready(false),
-	_numSSG(type == OD_TOWNS ? 0 : 3), _hasADPCM(type == OD_TYPE86 ? true : false),
-	_numChan(type == OD_TYPE26 ? 3 : 6), _hasStereo(type == OD_TYPE26 ? false : true) {	
+	_mixer(mixer), _trackPtr(0), _musicPlaying(false), _sfxPlaying(false), _fading(false), _channels(0),
+	_ssgChannels(0), _sfxChannels(0), _pcmChannel(0),	_looping(0), _opnCarrier(_drvTables + 76),
+	_opnFreqTable(_drvTables + 84), _opnFreqTableSSG(_drvTables + 252),	_opnFxCmdLen(_drvTables + 36),
+	_opnLvlPresets(_drvTables + (type == OD_TOWNS ? 52 : 228)), _oprRates(0), _oprRateshift(0), _oprAttackDecay(0),
+	_oprFrq(0), _oprSinTbl(0), _oprLevelOut(0), _oprDetune(0), _cbCounter(4), _musicTickCounter(0),
+	_updateChannelsFlag(type == OD_TYPE26 ? 0x07 : 0x3F), _updateSSGFlag(type == OD_TOWNS ? 0 : 7),
+	_updatePCMFlag(type == OD_TYPE86 ? 1 : 0), _finishedChannelsFlag(0), _finishedSSGFlag(0),
+	_finishedPCMFlag(0), _samplesTillCallback(0), _samplesTillCallbackRemainder(0),
+	_sfxData(0), _ready(false), _numSSG(type == OD_TOWNS ? 0 : 3), _hasPCM(type == OD_TYPE86 ? true : false),
+	_sfxOffs(0), _numChan(type == OD_TYPE26 ? 3 : 6), _hasStereo(type == OD_TYPE26 ? false : true),
+	_ssgPatches(0), _ssg(0), _pcm(0), _baserate(55125.0f / (float)getRate()) {
 	setTempo(84);
-	_baserate = (486202500.0 / (double)getRate()) / 10368.0;
 }
 
 TownsPC98_OpnDriver::~TownsPC98_OpnDriver() {
@@ -2381,13 +3064,26 @@
 		delete [] _ssgChannels;
 	}
 
+	if (_sfxChannels) {
+		for (int i = 0; i < 2; i++)
+			delete _sfxChannels[i];
+		delete [] _sfxChannels;
+	}
+
+	if (_pcmChannel)
+		delete _pcmChannel;
+
+	delete _ssg;
+	delete _pcm;
+
 	delete [] _oprRates;
 	delete [] _oprRateshift;
 	delete [] _oprFrq;
 	delete [] _oprAttackDecay;
 	delete [] _oprSinTbl;
 	delete [] _oprLevelOut;
-	delete [] _oprDetune;	
+	delete [] _oprDetune;
+	delete [] _ssgPatches;
 }
 
 bool TownsPC98_OpnDriver::init() {
@@ -2420,7 +3116,21 @@
 		}
 		delete [] _ssgChannels;
 	}
+
+	if (_sfxChannels) {
+		for (int i = 0; i < 2; i++) {
+			if (_sfxChannels[i])
+				delete _sfxChannels[i];
+		}
+		delete [] _sfxChannels;
+	}
+
 	if (_numSSG) {
+		_ssg = new TownsPC98_OpnSquareSineSource(_baserate);
+		_ssg->init(&_ssgTables[0], &_ssgTables[16]);
+		_ssgPatches = new uint8[256];
+		memcpy(_ssgPatches, _drvTables + 244, 256);
+
 		_ssgChannels = new TownsPC98_OpnChannelSSG*[_numSSG];
 		for (int i = 0; i < _numSSG; i++) {
 			int ii = i * 6;
@@ -2428,8 +3138,25 @@
 				_drvTables[ii + 2],	_drvTables[ii + 3],	_drvTables[ii + 4], _drvTables[ii + 5]);
 			_ssgChannels[i]->init();
 		}
+
+		_sfxChannels = new TownsPC98_OpnSfxChannel*[2];
+		for (int i = 0; i < 2; i++) {
+			int ii = (i + 1) * 6;
+			_sfxChannels[i] = new TownsPC98_OpnSfxChannel(this, _drvTables[ii], _drvTables[ii + 1],
+				_drvTables[ii + 2],	_drvTables[ii + 3],	_drvTables[ii + 4], _drvTables[ii + 5]);
+			_sfxChannels[i]->init();
+		}
 	}
 
+	if (_hasPCM) {
+		_pcm = new TownsPC98_OpnPercussionSource(_baserate);
+		_pcm->init();
+
+		delete _pcmChannel;
+		_pcmChannel = new TownsPC98_OpnChannelPCM(this, 0, 0, 0, 0, 0, 1);
+		_pcmChannel->init();
+	}
+
 	_mixer->playInputStream(Audio::Mixer::kMusicSoundType,
 		&_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, false, true);
 
@@ -2439,10 +3166,15 @@
 
 int inline TownsPC98_OpnDriver::readBuffer(int16 *buffer, const int numSamples) {
 	memset(buffer, 0, sizeof(int16) * numSamples);
+	int32 *tmp = new int32[numSamples];
+	int32 *tmpStart = tmp;
+	memset(tmp, 0, sizeof(int32) * numSamples);
 	int32 samplesLeft = numSamples >> 1;
+
 	while (samplesLeft) {
 		if (!_samplesTillCallback) {
 			callback();
+
 			_samplesTillCallback = _samplesPerCallback;
 			_samplesTillCallbackRemainder += _samplesPerCallbackRemainder;
 			if (_samplesTillCallbackRemainder >= _tempo) {
@@ -2455,20 +3187,38 @@
 		samplesLeft -= render;
 		_samplesTillCallback -= render;
 
-		nextTick(buffer, render);
+		nextTick(tmp, render);
 
+		if (_ssg)
+			_ssg->nextTick(tmp, render);
+		if (_pcm)
+			_pcm->nextTick(tmp, render);
+
 		for (int i = 0; i < render; ++i) {
-			buffer[i << 1] <<= 2;
-			buffer[(i << 1) + 1] <<= 2;
+			int32 l = tmp[i << 1];
+			if (l > 32767)
+				l = 32767;
+			if (l < -32767)
+				l = -32767;
+			buffer[i << 1] = (int16) l;
+
+			int32 r = tmp[(i << 1) + 1];
+			if (r > 32767)
+				r = 32767;
+			if (r < -32767)
+				r = -32767;
+			buffer[(i << 1) + 1] = (int16) r;
 		}
 
 		buffer += (render << 1);
+		tmp += (render << 1);
 	}
 
+	delete [] tmpStart;
 	return numSamples;
 }
 
-void TownsPC98_OpnDriver::loadData(uint8 *data, bool loadPaused) {
+void TownsPC98_OpnDriver::loadMusicData(uint8 *data, bool loadPaused) {
 	if (!_ready) {
 		warning("TownsPC98_OpnDriver: Driver must be initialized before loading data");
 		return;
@@ -2479,12 +3229,11 @@
 		return;
 	}
 
+	reset();
+
 	lock();
-	_trackData = data;
 
-	reset();
-	
-	uint8 *src_a = data;
+	uint8 *src_a = _trackPtr = _musicBuffer = data;
 
 	for (uint8 i = 0; i < 3; i++) {
 		_channels[i]->loadData(data + READ_LE_UINT16(src_a));
@@ -2501,118 +3250,177 @@
 		src_a += 2;
 	}
 
-	if (_hasADPCM) {
-		//_adpcmChannel->loadData(data + READ_LE_UINT16(src_a));
+	if (_hasPCM) {
+		_pcmChannel->loadData(data + READ_LE_UINT16(src_a));
 		src_a += 2;
 	}
 
-	_ssgFlag = 0;
+	_regProtectionFlag = false;
 
 	_patches = src_a + 4;
 	_cbCounter = 4;
-	_finishedChannelsFlag = 0;
+	_finishedChannelsFlag = _finishedSSGFlag = 0;
 
-	// AH 0x17
+	_musicPlaying = (loadPaused ? false : true);
+
 	unlock();
-	_playing = (loadPaused ? false : true);
 }
 
+void TownsPC98_OpnDriver::loadSoundEffectData(uint8 *data, uint8 trackNum) {
+	if (!_ready) {
+		warning("TownsPC98_OpnDriver: Driver must be initialized before loading data");
+		return;
+	}
+
+	if (!_sfxChannels) {
+		warning("TownsPC98_OpnDriver: sound effects not supported by this configuration");
+		return;
+	}
+
+	if (!data) {
+		warning("TownsPC98_OpnDriver: Invalid sound effect file data");
+		return;
+	}
+
+	lock();
+	_sfxData = _sfxBuffer = data;
+	_sfxOffsets[0] = READ_LE_UINT16(&_sfxData[(trackNum << 2)]);
+	_sfxOffsets[1] = READ_LE_UINT16(&_sfxData[(trackNum << 2) + 2]);
+	_sfxPlaying = true;
+	unlock();
+}
+
 void TownsPC98_OpnDriver::reset() {
-	for (int i = 0; i < (_numChan); i++)
+	lock();
+
+	for (int i = 0; i < _numChan; i++)
 		_channels[i]->reset();
-	for (int i = 0; i < (_numSSG); i++)
+	for (int i = 0; i < _numSSG; i++)
 		_ssgChannels[i]->reset();
 
-	_playing = _fading = false;
+	if (_ssg) {
+		for (int i = 0; i < 2; i++)
+			_sfxChannels[i]->reset();
+
+		memcpy(_ssgPatches, _drvTables + 276, 256);
+		_ssg->reset();
+	}
+
+	if (_pcmChannel)
+		_pcmChannel->reset();
+
+	_musicPlaying = _sfxPlaying = _fading = false;
 	_looping = 0;
-	_tickCounter = 0;
+	_musicTickCounter = 0;
+	_sfxData = 0;
+
+	unlock();
 }
 
 void TownsPC98_OpnDriver::fadeOut() {
-	if (!_playing)
+	if (!_musicPlaying)
 		return;
 
-	_fading = true;
+	if (_hasPCM) {
+		lock();
+		if (_updatePCMFlag & _pcmChannel->_idFlag)
+			_pcmChannel->reset();
+		unlock();
+	}
 
-	for (int i = 0; i < 20; i++) {		
+	for (int i = 0; i < 20; i++) {
 		lock();
-		uint32 dTime = _tickCounter + 2;
+
+		_fading = true;
+
+		uint32 dTime = _musicTickCounter + 2;
 		for (int j = 0; j < _numChan; j++) {
 			if (_updateChannelsFlag & _channels[j]->_idFlag)
 				_channels[j]->fadeStep();
 		}
-		for (int j = 0; j < _numSSG; j++)
-			_ssgChannels[j]->fadeStep();
+		for (int j = 0; j < _numSSG; j++) {
+			if (_updateSSGFlag & _ssgChannels[j]->_idFlag)
+				_ssgChannels[j]->fadeStep();
+		}
 
 		unlock();
 
-		while (_playing) {
-			if (_tickCounter >= dTime)
+		while (_musicPlaying) {
+			if (_musicTickCounter >= dTime)
 				break;
 		}
 	}
 
-	_fading = false;
-
 	reset();
 }
 
 void TownsPC98_OpnDriver::callback() {
-	if (!_playing || --_cbCounter)
-		return;
+	lock();
 
-	_cbCounter = 4;
-	_tickCounter++;
+	if (_sfxChannels && _sfxPlaying) {
+		if (_sfxData)
+			startSoundEffect();
 
-	lock();
+		_sfxOffs = 3;
+		_trackPtr = _sfxBuffer;
 
-	for (int i = 0; i < _numChan; i++) {
-		if (_updateChannelsFlag & _channels[i]->_idFlag) {
-			_channels[i]->processEvents();
-			_channels[i]->processFrequency();
+		for (int i = 0; i < 2; i++) {
+			_sfxChannels[i]->processEvents();
+			_sfxChannels[i]->processFrequency();
 		}
+
+		_trackPtr = _musicBuffer;
 	}
 
-	if (_numSSG) {
-		for (int i = 0; i < _numSSG; i++) {
-			_ssgChannels[i]->processEvents();
-			_ssgChannels[i]->processFrequency();
+	_sfxOffs = 0;
+
+	if (!--_cbCounter && _musicPlaying) {
+		_cbCounter = 4;
+		_musicTickCounter++;
+
+		for (int i = 0; i < _numChan; i++) {
+			if (_updateChannelsFlag & _channels[i]->_idFlag) {
+				_channels[i]->processEvents();
+				_channels[i]->processFrequency();
+			}
 		}
+
+		if (_numSSG) {
+			for (int i = 0; i < _numSSG; i++) {
+				if (_updateSSGFlag & _ssgChannels[i]->_idFlag) {
+					_ssgChannels[i]->processEvents();
+					_ssgChannels[i]->processFrequency();
+				}
+			}
+		}
+
+		if (_hasPCM)
+			if (_updatePCMFlag & _pcmChannel->_idFlag)
+				_pcmChannel->processEvents();
 	}
-	
-	_ssgFlag = 0;
 
+	_regProtectionFlag = false;
+
+	if (_finishedChannelsFlag == _updateChannelsFlag && _finishedSSGFlag == _updateSSGFlag && _finishedPCMFlag == _updatePCMFlag)
+		_musicPlaying = false;
+
 	unlock();
-
-	if (_finishedChannelsFlag == _updateChannelsFlag)
-		reset();
 }
 
-void TownsPC98_OpnDriver::nextTick(int16 *buffer, uint32 bufferSize) {
-	if (!_playing)
-		return;	
+void TownsPC98_OpnDriver::nextTick(int32 *buffer, uint32 bufferSize) {
+	if (!_ready)
+		return;
 
-	for (int i = 0; i < _numChan ; i++) {
-		if (_channels[i]->_updateEnvelopes) {
-			_channels[i]->_updateEnvelopes = false;
+	for (int i = 0; i < _numChan; i++) {
+		if (_channels[i]->_updateEnvelopeParameters) {
+			_channels[i]->_updateEnvelopeParameters = false;
 			_channels[i]->updateEnv();
 		}
-		
+
 		for (uint32 ii = 0; ii < bufferSize ; ii++)
 			_channels[i]->generateOutput(buffer[ii * 2],
-			buffer[ii * 2 + 1],	&_channels[i]->_feedbuf[2], _channels[i]->_feedbuf);
+				buffer[ii * 2 + 1],	&_channels[i]->_feedbuf[2], _channels[i]->_feedbuf);
 	}
-
-	for (int i = 0; i < _numSSG ; i++) {
-		if (_ssgChannels[i]->_updateEnvelopes) {
-			_ssgChannels[i]->_updateEnvelopes = false;
-			_ssgChannels[i]->updateEnv();
-		}
-		
-		for (uint32 ii = 0; ii < bufferSize ; ii++)
-			_ssgChannels[i]->generateOutput(buffer[ii * 2],
-			buffer[ii * 2 + 1],	&_ssgChannels[i]->_feedbuf[2], _ssgChannels[i]->_feedbuf);
-	}
 }
 
 void TownsPC98_OpnDriver::generateTables() {
@@ -2641,7 +3449,7 @@
 	delete [] _oprFrq;
 	_oprFrq = new uint32[0x1000];
 	for (uint32 i = 0; i < 0x1000; i++)
-		_oprFrq[i] = (uint32)(_baserate * (double)(i << 11));
+		_oprFrq[i] = (uint32)(_baserate * (float)(i << 11));
 
 	delete [] _oprAttackDecay;
 	_oprAttackDecay = new uint8[152];
@@ -2680,83 +3488,31 @@
 	delete [] _oprDetune;
 	_oprDetune = new int32[256];
 	for (int i = 0; i < 128; i++) {
-		_oprDetune[i] = (int32)	((double)dtt[i] * _baserate * 64.0);
+		_oprDetune[i] = (int32)	((float)dtt[i] * _baserate * 64.0);
 		_oprDetune[i + 128] = -_oprDetune[i];
 	}
 
 	delete [] dtt;
 }
 
+void TownsPC98_OpnDriver::startSoundEffect() {
+	for (int i = 0; i < 2; i++) {
+		if (_sfxOffsets[i]) {
+			_ssgChannels[i + 1]->protect();
+			_sfxChannels[i]->reset();
+			_sfxChannels[i]->loadData(_sfxData + _sfxOffsets[i]);
+		}
+	}
+
+	_sfxData = 0;
+}
+
 void TownsPC98_OpnDriver::setTempo(uint8 tempo) {
-	_tempo = tempo;
-	_samplesPerCallback = getRate() / _tempo;
-	_samplesPerCallbackRemainder = getRate() % _tempo;
+	_tempo = tempo - 0x10;
+	_samplesPerCallback = _tempo ? (getRate() / _tempo) : 0;
+	_samplesPerCallbackRemainder = _tempo ? (getRate() % _tempo) : 0;
 }
 
-const uint8 TownsPC98_OpnDriver::_drvTables[] = {
-	//	channel presets
-	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,
-
-	//	fmt level presets 
-	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,
-
-	//	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,
-	0x00, 0x00, 0x00, 0x00,
-
-	//	unused
-	0x01, 0x00,	0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
-	0x02, 0x00,	0x00, 0x00,	0x05, 0x00, 0x00, 0x00,
-	0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
-	0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
-
-	//	detune
-	0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03,
-	0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07,
-	0x08, 0x08, 0x08, 0x08, 0x01, 0x01,	0x01, 0x01,
-	0x02, 0x02, 0x02, 0x02, 0x02, 0x03,	0x03, 0x03,
-	0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07,
-	0x08, 0x08, 0x09, 0x0a,	0x0b, 0x0c, 0x0d, 0x0e,
-	0x10, 0x10, 0x10, 0x10,	0x02, 0x02, 0x02, 0x02,
-	0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05,
-	0x05, 0x06,	0x06, 0x07, 0x08, 0x08, 0x09, 0x0a,
-	0x0b, 0x0c,	0x0d, 0x0e, 0x10, 0x11, 0x13, 0x14,
-	0x16, 0x16, 0x16, 0x16,
-
-	//	pc98 level presets 
-	0x40, 0x3B, 0x38, 0x34, 0x30, 0x2A, 0x28, 0x25,
-	0x22, 0x20, 0x1D, 0x1A, 0x18, 0x15, 0x12, 0x10,
-	0x0D, 0x0A, 0x08, 0x05, 0x02, 0x90, 0x90, 0x90
-};
-
-const uint32 TownsPC98_OpnDriver::_adtStat[] = {
-	0x00010001, 0x00010001,	0x00010001, 0x01010001,
-	0x00010101, 0x00010101, 0x00010101, 0x01010101,
-	0x01010101, 0x01010101, 0x01010102, 0x01010102,
-	0x01020102, 0x01020102, 0x01020202, 0x01020202,
-	0x02020202, 0x02020202, 0x02020204, 0x02020204,
-	0x02040204, 0x02040204, 0x02040404, 0x02040404,
-	0x04040404, 0x04040404, 0x04040408, 0x04040408,
-	0x04080408, 0x04080408, 0x04080808, 0x04080808,
-	0x08080808, 0x08080808, 0x10101010, 0x10101010
-};
-
 SoundTowns::SoundTowns(KyraEngine_v1 *vm, Audio::Mixer *mixer)
 	: Sound(vm, mixer), _lastTrack(-1), _currentSFX(0), _sfxFileData(0),
 	_sfxFileIndex((uint)-1), _sfxWDTable(0), _sfxBTTable(0), _parser(0) {
@@ -2916,8 +3672,9 @@
 	}
 
 	playbackBufferSize -= 0x20;
-	uint32 outputRate = uint32(11025 * semitoneAndSampleRate_to_sampleStep(note, sfxRootNoteOffs, sfxRate, 11025, 0x2000));
 
+	uint32 outputRate = uint32(11025 * calculatePhaseStep(note, sfxRootNoteOffs, sfxRate, 11025, 0x2000));
+
 	_currentSFX = Audio::makeLinearInputStream(sfxPlaybackBuffer, playbackBufferSize,
 		outputRate, Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_LITTLE_ENDIAN | Audio::Mixer::FLAG_AUTOFREE, 0, 0);
 	_mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_sfxHandle, _currentSFX);
@@ -2995,7 +3752,7 @@
 		music->_parser->onTimer();
 }
 
-float SoundTowns::semitoneAndSampleRate_to_sampleStep(int8 semiTone, int8 semiToneRootkey,
+float SoundTowns::calculatePhaseStep(int8 semiTone, int8 semiToneRootkey,
 	uint32 sampleRate, uint32 outputRate, int32 pitchWheel) {
 	if (semiTone < 0)
 		semiTone = 0;
@@ -3050,18 +3807,18 @@
 void SoundPC98::playTrack(uint8 track) {
 	if (--track >= 56)
 		track -= 55;
- 
+
 	if (track == _lastTrack && _musicEnabled)
 		return;
 
-	haltTrack();
+	beginFadeOut();
 
 	char musicfile[13];
 	sprintf(musicfile, fileListEntry(0), track);
 	delete[] _musicTrackData;
 	_musicTrackData = _vm->resource()->fileData(musicfile, 0);
 	if (_musicEnabled)
-		_driver->loadData(_musicTrackData);
+		_driver->loadMusicData(_musicTrackData);
 
 	_lastTrack = track;
 }
@@ -3078,25 +3835,32 @@
 	haltTrack();
 }
 
-void SoundPC98::playSoundEffect(uint8) {
-	/// TODO ///
+void SoundPC98::playSoundEffect(uint8 track) {
+	if (!_sfxTrackData)
+		return;
+
+	//	This has been disabled for now since I don't know
+	//	how to make up the correct track number. It probably
+	//	needs a map.
+	// _driver->loadSoundEffectData(_sfxTrackData, track);
 }
 
 
 //	KYRA 2
 
 SoundTownsPC98_v2::SoundTownsPC98_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer) :
-	Sound(vm, mixer), _currentSFX(0), _musicTrackData(0), _lastTrack(-1), _driver(0), _useFmSfx(false) {
+	Sound(vm, mixer), _currentSFX(0), _musicTrackData(0), _sfxTrackData(0), _lastTrack(-1), _driver(0), _useFmSfx(false) {
 }
 
 SoundTownsPC98_v2::~SoundTownsPC98_v2() {
 	delete[] _musicTrackData;
+	delete[] _sfxTrackData;
 	delete _driver;
 }
 
 bool SoundTownsPC98_v2::init() {
-	_driver = new TownsPC98_OpnDriver(_mixer, /*_vm->gameFlags().platform == Common::kPlatformPC98 ?
-		TownsPC98_OpnDriver::OD_TYPE86 :*/ TownsPC98_OpnDriver::OD_TOWNS);
+	_driver = new TownsPC98_OpnDriver(_mixer, _vm->gameFlags().platform == Common::kPlatformPC98 ?
+		TownsPC98_OpnDriver::OD_TYPE86 : TownsPC98_OpnDriver::OD_TOWNS);
 	_useFmSfx = _vm->gameFlags().platform == Common::kPlatformPC98 ? true : false;
 	_vm->checkCD();
 	// FIXME: While checking for 'track1.XXX(X)' looks like
@@ -3110,9 +3874,15 @@
 		(Common::File::exists("track1.mp3") || Common::File::exists("track1.ogg") ||
 		 Common::File::exists("track1.flac") || Common::File::exists("track1.fla")))
 			_musicEnabled = 2;
+	
 	return _driver->init();
 }
 
+void SoundTownsPC98_v2::loadSoundFile(Common::String file) {
+	delete [] _sfxTrackData;
+	_sfxTrackData = _vm->resource()->fileData(file.c_str(), 0);
+}
+
 void SoundTownsPC98_v2::process() {
 	AudioCD.updateCD();
 }
@@ -3138,9 +3908,9 @@
 	char musicfile[13];
 	sprintf(musicfile, fileListEntry(0), track);
 	delete[] _musicTrackData;
-	
+
 	_musicTrackData = _vm->resource()->fileData(musicfile, 0);
-	_driver->loadData(_musicTrackData, true);
+	_driver->loadMusicData(_musicTrackData, true);
 
 	if (_musicEnabled == 2 && trackNum != -1) {
 		AudioCD.play(trackNum+1, _driver->looping() ? -1 : 1, 0, 0);
@@ -3221,7 +3991,7 @@
 		sfx[i] = cmd;
 	}
 
-	uint32 outputRate = uint32(11025 * SoundTowns::semitoneAndSampleRate_to_sampleStep(0x3c, 0x3c, sfxRate, 11025, 0x2000));
+	uint32 outputRate = uint32(11025 * SoundTowns::calculatePhaseStep(0x3c, 0x3c, sfxRate, 11025, 0x2000));
 
 	_currentSFX = Audio::makeLinearInputStream(sfx, outsize, outputRate,
 		Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_LITTLE_ENDIAN | Audio::Mixer::FLAG_AUTOFREE, 0, 0);
@@ -3233,17 +4003,127 @@
 }
 
 void SoundTownsPC98_v2::playSoundEffect(uint8 track) {
-	if (!_useFmSfx)
+	if (!_useFmSfx || !_sfxTrackData)
 		return;
 
-	uint8 *sd = _vm->resource()->fileData("sound.dat", 0);
+	_driver->loadSoundEffectData(_sfxTrackData, track);
+}
 
+// static resources
 
-	//TODO
+const uint8 TownsPC98_OpnDriver::_drvTables[] = {
+	//	channel presets
+	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,
 
-	delete [] sd;
-}
+	//	control event size
+	0x01, 0x01, 0x01, 0x01,	0x01, 0x01, 0x04, 0x05,
+	0x02, 0x06, 0x02, 0x00, 0x00, 0x02, 0x00, 0x02,
 
+	//	fmt level presets
+	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,
+
+	//	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,
+	0x00, 0x00, 0x00, 0x00,
+
+	//	unused
+	0x01, 0x00,	0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+	0x02, 0x00,	0x00, 0x00,	0x05, 0x00, 0x00, 0x00,
+	0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+	0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+
+	//	detune
+	0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03,
+	0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07,
+	0x08, 0x08, 0x08, 0x08, 0x01, 0x01,	0x01, 0x01,
+	0x02, 0x02, 0x02, 0x02, 0x02, 0x03,	0x03, 0x03,
+	0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07,
+	0x08, 0x08, 0x09, 0x0a,	0x0b, 0x0c, 0x0d, 0x0e,
+	0x10, 0x10, 0x10, 0x10,	0x02, 0x02, 0x02, 0x02,
+	0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05,
+	0x05, 0x06,	0x06, 0x07, 0x08, 0x08, 0x09, 0x0a,
+	0x0b, 0x0c,	0x0d, 0x0e, 0x10, 0x11, 0x13, 0x14,
+	0x16, 0x16, 0x16, 0x16,
+
+	//	pc98 level presets
+	0x40, 0x3B, 0x38, 0x34, 0x30, 0x2A, 0x28, 0x25,
+	0x22, 0x20, 0x1D, 0x1A, 0x18, 0x15, 0x12, 0x10,
+	0x0D, 0x0A, 0x08, 0x05, 0x02, 0x90, 0x90, 0x90,
+
+	//	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
+};
+
+const uint32 TownsPC98_OpnDriver::_adtStat[] = {
+	0x00010001, 0x00010001,	0x00010001, 0x01010001,
+	0x00010101, 0x00010101, 0x00010101, 0x01010101,
+	0x01010101, 0x01010101, 0x01010102, 0x01010102,
+	0x01020102, 0x01020102, 0x01020202, 0x01020202,
+	0x02020202, 0x02020202, 0x02020204, 0x02020204,
+	0x02040204, 0x02040204, 0x02040404, 0x02040404,
+	0x04040404, 0x04040404, 0x04040408, 0x04040408,
+	0x04080408, 0x04080408, 0x04080808, 0x04080808,
+	0x08080808, 0x08080808, 0x10101010, 0x10101010
+};
+
+const int TownsPC98_OpnDriver::_ssgTables[] = {
+	0x01202A, 0x0092D2, 0x006B42, 0x0053CB, 0x003DF8, 0x003053, 0x0022DA, 0x001A8C,
+	0x00129B, 0x000DC1, 0x000963, 0x0006C9, 0x000463, 0x0002FA, 0x0001B6, 0x0000FB,
+	0x0193B6, 0x01202A, 0x00CDB1, 0x0092D2, 0x007D7D, 0x006B42, 0x005ECD, 0x0053CB,
+	0x00480F, 0x003DF8, 0x0036B9, 0x003053, 0x00290A, 0x0022DA, 0x001E6B, 0x001A8C,
+	0x001639, 0x00129B, 0x000FFF, 0x000DC1, 0x000B5D, 0x000963, 0x0007FB, 0x0006C9,
+	0x000575, 0x000463, 0x00039D, 0x0002FA, 0x000242, 0x0001B6, 0x00014C, 0x0000FB
+};
+
 } // end of namespace Kyra
 
 #undef EUPHONY_FADEOUT_TICKS


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list