[Scummvm-cvs-logs] SF.net SVN: scummvm: [32807] scummvm/trunk/engines/kyra

athrxx at users.sourceforge.net athrxx at users.sourceforge.net
Thu Jun 26 21:43:00 CEST 2008


Revision: 32807
          http://scummvm.svn.sourceforge.net/scummvm/?rev=32807&view=rev
Author:   athrxx
Date:     2008-06-26 12:42:59 -0700 (Thu, 26 Jun 2008)

Log Message:
-----------
- improved hof music support for fm-towns (driver for *.twn tracks) (still needs quite some work)
- some PC-98 music support since it uses a very similar driver, but this can't be considered working yet)
- Kyra 1 PC-98 music doen't work at all since I haven't figured out yet how to turn track numbers into the corresponding music file names (might require a hard coded track map)

Modified Paths:
--------------
    scummvm/trunk/engines/kyra/detection.cpp
    scummvm/trunk/engines/kyra/kyra_v1.cpp
    scummvm/trunk/engines/kyra/screen.cpp
    scummvm/trunk/engines/kyra/sound.h
    scummvm/trunk/engines/kyra/sound_lok.cpp
    scummvm/trunk/engines/kyra/sound_towns.cpp
    scummvm/trunk/engines/kyra/staticres.cpp

Modified: scummvm/trunk/engines/kyra/detection.cpp
===================================================================
--- scummvm/trunk/engines/kyra/detection.cpp	2008-06-26 18:45:46 UTC (rev 32806)
+++ scummvm/trunk/engines/kyra/detection.cpp	2008-06-26 19:42:59 UTC (rev 32807)
@@ -44,6 +44,7 @@
 #define FLAGS(x, y, z, a, b, c, id) { Common::UNK_LANG, Common::kPlatformUnknown, x, y, z, a, b, c, id }
 
 #define KYRA1_FLOPPY_FLAGS FLAGS(false, false, false, false, false, false, Kyra::GI_KYRA1)
+#define KYRA1_FLOPPY_CMP_FLAGS FLAGS(false, false, false, false, false, true, Kyra::GI_KYRA1)
 #define KYRA1_AMIGA_FLAGS FLAGS(false, false, false, false, false, false, Kyra::GI_KYRA1)
 #define KYRA1_TOWNS_FLAGS FLAGS(false, true, false, false, false, false, Kyra::GI_KYRA1)
 #define KYRA1_TOWNS_SJIS_FLAGS FLAGS(false, true, false, true, false, false, Kyra::GI_KYRA1)
@@ -66,6 +67,28 @@
 		{
 			"kyra1",
 			0,
+			AD_ENTRY1("DISK1.EXE", "c8641d0414d6c966d0a3dad79db07bf4"),
+			Common::EN_ANY,
+			Common::kPlatformPC,
+			Common::ADGF_NO_FLAGS
+		},
+		KYRA1_FLOPPY_CMP_FLAGS
+	},
+	{
+		{
+			"kyra1",
+			0,
+			AD_ENTRY1("DISK1.EXE", "5d5cee4c3d0b68d586788b74243d254a"),
+			Common::DE_DEU,
+			Common::kPlatformPC,
+			Common::ADGF_NO_FLAGS
+		},
+		KYRA1_FLOPPY_CMP_FLAGS
+	},
+	{
+		{
+			"kyra1",
+			"Extracted",
 			AD_ENTRY1("GEMCUT.EMC", "3c244298395520bb62b5edfe41688879"),
 			Common::EN_ANY,
 			Common::kPlatformPC,
@@ -76,7 +99,7 @@
 	{
 		{
 			"kyra1",
-			0,
+			"Extracted",
 			AD_ENTRY1("GEMCUT.EMC", "796e44863dd22fa635b042df1bf16673"),
 			Common::EN_ANY,
 			Common::kPlatformPC,
@@ -87,7 +110,7 @@
 	{
 		{
 			"kyra1",
-			0,
+			"Extracted",
 			AD_ENTRY1("GEMCUT.EMC", "abf8eb360e79a6c2a837751fbd4d3d24"),
 			Common::FR_FRA,
 			Common::kPlatformPC,
@@ -98,7 +121,7 @@
 	{
 		{
 			"kyra1",
-			0,
+			"Extracted",
 			AD_ENTRY1("GEMCUT.EMC", "6018e1dfeaca7fe83f8d0b00eb0dd049"),
 			Common::DE_DEU,
 			Common::kPlatformPC,
@@ -109,7 +132,7 @@
 	{ // from Arne.F
 		{
 			"kyra1",
-			0,
+			"Extracted",
 			AD_ENTRY1("GEMCUT.EMC", "f0b276781f47c130f423ec9679fe9ed9"),
 			Common::DE_DEU,
 			Common::kPlatformPC,
@@ -120,7 +143,7 @@
 	{ // from VooD
 		{
 			"kyra1",
-			0,
+			"Extracted",
 			AD_ENTRY1("GEMCUT.EMC", "8909b41596913b3f5deaf3c9f1017b01"),
 			Common::ES_ESP,
 			Common::kPlatformPC,
@@ -131,7 +154,7 @@
 	{ // floppy 1.8 from clemmy
 		{
 			"kyra1",
-			0,
+			"Extracted",
 			AD_ENTRY1("GEMCUT.EMC", "747861d2a9c643c59fdab570df5b9093"),
 			Common::ES_ESP,
 			Common::kPlatformPC,
@@ -142,7 +165,7 @@
 	{ // from gourry
 		{
 			"kyra1",
-			0,
+			"Extracted",
 			AD_ENTRY1("GEMCUT.EMC", "ef08c8c237ee1473fd52578303fc36df"),
 			Common::IT_ITA,
 			Common::kPlatformPC,
@@ -463,6 +486,28 @@
 		},
 		KYRA2_TOWNS_SJIS_FLAGS
 	},
+	{ // PC-9821
+		{
+			"kyra2",
+			0,
+			AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
+			Common::EN_ANY,
+			Common::kPlatformPC98,
+			Common::ADGF_NO_FLAGS
+		},
+		KYRA2_TOWNS_FLAGS
+	},
+	{
+		{
+			"kyra2",
+			0,
+			AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
+			Common::JA_JPN,
+			Common::kPlatformPC98,
+			Common::ADGF_NO_FLAGS
+		},
+		KYRA2_TOWNS_SJIS_FLAGS
+	},
 
 	// Kyra3
 	

Modified: scummvm/trunk/engines/kyra/kyra_v1.cpp
===================================================================
--- scummvm/trunk/engines/kyra/kyra_v1.cpp	2008-06-26 18:45:46 UTC (rev 32806)
+++ scummvm/trunk/engines/kyra/kyra_v1.cpp	2008-06-26 19:42:59 UTC (rev 32807)
@@ -107,14 +107,16 @@
 		// "KYRA1: Crash on exceeded polyphony" for more information).
 		int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB/* | MDT_PREFER_MIDI*/);
 
-		if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) {
-			// TODO: currently we don't support the PC98 sound data,
-			// but since it has the FM-Towns data files, we just use the
-			// FM-Towns driver
+		if (_flags.platform == Common::kPlatformFMTowns) {
 			if (_flags.gameID == GI_KYRA1)
 				_sound = new SoundTowns(this, _mixer);
 			else
-				_sound = new SoundTowns_v2(this, _mixer);
+				_sound = new SoundTownsPC98_v2(this, _mixer);
+		} else if (_flags.platform == Common::kPlatformPC98) {
+			if (_flags.gameID == GI_KYRA1)
+				_sound = new SoundPC98(this, _mixer);
+			else
+				_sound = new SoundTownsPC98_v2(this, _mixer);
 		} else if (midiDriver == MD_ADLIB) {
 			_sound = new SoundAdlibPC(this, _mixer);
 			assert(_sound);

Modified: scummvm/trunk/engines/kyra/screen.cpp
===================================================================
--- scummvm/trunk/engines/kyra/screen.cpp	2008-06-26 18:45:46 UTC (rev 32806)
+++ scummvm/trunk/engines/kyra/screen.cpp	2008-06-26 19:42:59 UTC (rev 32807)
@@ -92,9 +92,19 @@
 
 		if (_useSJIS) {
 			if (!_sjisFontData) {
-				_sjisFontData = _vm->resource()->fileData("FMT_FNT.ROM", 0);
-				if (!_sjisFontData)
-					error("missing font rom ('FMT_FNT.ROM') required for this version");
+				// we use the FM-Towns font rom for PC-98, too, until we feel
+				// like adding support for the PC-98 font
+				//if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
+					// FM-Towns
+					_sjisFontData = _vm->resource()->fileData("FMT_FNT.ROM", 0);
+					if (!_sjisFontData)
+						error("missing font rom ('FMT_FNT.ROM') required for this version");
+				/*} else {
+					// PC-98
+					_sjisFontData = _vm->resource()->fileData("FONT.ROM", 0);
+					if (!_sjisFontData)
+						error("missing font rom ('FONT.ROM') required for this version");
+				}*/
 			}
 
 			if (!_sjisTempPage) {

Modified: scummvm/trunk/engines/kyra/sound.h
===================================================================
--- scummvm/trunk/engines/kyra/sound.h	2008-06-26 18:45:46 UTC (rev 32806)
+++ scummvm/trunk/engines/kyra/sound.h	2008-06-26 19:42:59 UTC (rev 32807)
@@ -73,7 +73,8 @@
 		kAdlib,
 		kMidiMT32,
 		kMidiGM,
-		kTowns
+		kTowns,
+		kPC98
 	};
 
 	virtual kType getMusicType() const = 0;
@@ -382,7 +383,9 @@
 	Common::Mutex _mutex;
 };
 
-class SoundTowns_EuphonyDriver;
+class Towns_EuphonyDriver;
+class TownsPC98_OpnDriver;
+
 class SoundTowns : public MidiDriver, public Sound {
 public:
 	SoundTowns(KyraEngine_v1 *vm, Audio::Mixer *mixer);
@@ -417,6 +420,7 @@
 
 	static float semitoneAndSampleRate_to_sampleStep(int8 semiTone, int8 semiToneRootkey,
 		uint32 sampleRate, uint32 outputRate, int32 pitchWheel);
+
 private:
 	bool loadInstruments();
 	void playEuphonyTrack(uint32 offset, int loop);
@@ -430,7 +434,7 @@
 	uint _sfxFileIndex;
 	uint8 *_sfxFileData;
 
-	SoundTowns_EuphonyDriver * _driver;
+	Towns_EuphonyDriver * _driver;
 	MidiParser * _parser;
 
 	Common::Mutex _mutex;
@@ -439,15 +443,40 @@
 	const uint8 *_sfxWDTable;
 };
 
-//class SoundTowns_v2_TwnDriver;
-class SoundTowns_v2 : public Sound {
+class SoundPC98 : public Sound {
 public:
-	SoundTowns_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer);
-	~SoundTowns_v2();
+	SoundPC98(KyraEngine_v1 *vm, Audio::Mixer *mixer);
+	~SoundPC98();
 
-	kType getMusicType() const { return kTowns; }
+	virtual kType getMusicType() const { return kPC98; }
 
 	bool init();
+	
+	void process() {}
+	void loadSoundFile(uint file) {}
+
+	void playTrack(uint8 track);
+	void haltTrack();
+	void beginFadeOut();
+
+	int32 voicePlay(const char *file, bool isSfx = false) { return -1; }
+	void playSoundEffect(uint8);
+
+protected:
+	int _lastTrack;
+	uint8 *_musicTrackData;
+	uint8 *_sfxTrackData;
+	TownsPC98_OpnDriver *_driver;
+};
+
+class SoundTownsPC98_v2 : public Sound {
+public:
+	SoundTownsPC98_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer);
+	~SoundTownsPC98_v2();
+
+	kType getMusicType() const { return _vm->gameFlags().platform == Common::kPlatformFMTowns ? kTowns : kPC98; }
+
+	bool init();
 	void process();
 
 	void loadSoundFile(uint file) {}
@@ -459,13 +488,11 @@
 	int32 voicePlay(const char *file, bool isSfx = false);
 	void playSoundEffect(uint8) {}
 
-private:
+protected:
+	Audio::AudioStream *_currentSFX;
 	int _lastTrack;
-
-	Audio::AudioStream *_currentSFX;
-
-	//SoundTowns_v2_TwnDriver *_driver;
-	uint8 *_twnTrackData;
+	uint8 *_musicTrackData;
+	TownsPC98_OpnDriver *_driver;	
 };
 
 class MixedSoundDriver : public Sound {

Modified: scummvm/trunk/engines/kyra/sound_lok.cpp
===================================================================
--- scummvm/trunk/engines/kyra/sound_lok.cpp	2008-06-26 18:45:46 UTC (rev 32806)
+++ scummvm/trunk/engines/kyra/sound_lok.cpp	2008-06-26 19:42:59 UTC (rev 32807)
@@ -43,7 +43,7 @@
 	if (restart)
 		_lastMusicCommand = -1;
 
-	if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) {
+	if (_flags.platform == Common::kPlatformFMTowns) {
 		if (command == 1) {
 			_sound->beginFadeOut();
 		} else if (command >= 35 && command <= 38) {
@@ -56,6 +56,13 @@
 		} else {
 			_sound->haltTrack();
 		}
+	} else if (_flags.platform == Common::kPlatformPC98) {
+		
+		//////////////
+		//// TODO ////
+		//////////////
+		_sound->playTrack(command);
+		
 	} else {
 		KyraEngine_v1::snd_playWanderScoreViaMap(command, restart);
 	}

Modified: scummvm/trunk/engines/kyra/sound_towns.cpp
===================================================================
--- scummvm/trunk/engines/kyra/sound_towns.cpp	2008-06-26 18:45:46 UTC (rev 32806)
+++ scummvm/trunk/engines/kyra/sound_towns.cpp	2008-06-26 19:42:59 UTC (rev 32807)
@@ -34,18 +34,21 @@
 
 #include "common/util.h"
 
+#ifdef _MSC_VER
+#define	_USE_MATH_DEFINES
+#endif
 #include <math.h>
 
 #define		EUPHONY_FADEOUT_TICKS		600
 
 namespace Kyra {
 
-enum ChannelState { _s_ready, _s_attacking, _s_decaying, _s_sustaining, _s_releasing };
+enum EnvelopeState { s_ready, s_attacking, s_decaying, s_sustaining, s_releasing };
 
-class MidiChannel_EuD : public MidiChannel {
+class Towns_EuphonyChannel : public MidiChannel {
 public:
-	MidiChannel_EuD() {}
-	~MidiChannel_EuD() {}
+	Towns_EuphonyChannel() {}
+	~Towns_EuphonyChannel() {}
 
 	virtual void nextTick(int32 *outbuf, int buflen) = 0;
 	virtual void rate(uint16 r) = 0;
@@ -54,10 +57,10 @@
 	uint16 _rate;
 };
 
-class MidiChannel_EuD_FM : public MidiChannel_EuD {
+class Towns_EuphonyFmChannel : public Towns_EuphonyChannel {
 public:
-	MidiChannel_EuD_FM();
-	virtual ~MidiChannel_EuD_FM();
+	Towns_EuphonyFmChannel();
+	virtual ~Towns_EuphonyFmChannel();
 
 	void nextTick(int32 *outbuf, int buflen);
 	void rate(uint16 r);
@@ -79,13 +82,13 @@
 	Voice2612 *_voice;
 };
 
-class MidiChannel_EuD_WAVE : public MidiChannel_EuD {
+class Towns_EuphonyPcmChannel : public Towns_EuphonyChannel {
 public:
 	void nextTick(int32 *outbuf, int buflen);
 	void rate(uint16 r);
 
-	MidiChannel_EuD_WAVE();
-	virtual ~MidiChannel_EuD_WAVE();
+	Towns_EuphonyPcmChannel();
+	virtual ~Towns_EuphonyPcmChannel();
 
 	// MidiChannel interface
 	MidiDriver *device() { return 0; }
@@ -126,9 +129,9 @@
 			int32 keyOffset;
 			int32 keyNote;
 			const int8 *_samples;
-		} * _snd[8];
+		} *_snd[8];
 		struct Env {
-			ChannelState state;
+			EnvelopeState state;
 			int32 currentLevel;
 			int32 rate;
 			int32 tickCount;
@@ -141,40 +144,39 @@
 			int32 releaseRate;
 			int32 rootKeyOffset;
 			int32 size;
-		} * _env[8];
-	} * _voice;
+		} *_env[8];
+	} *_voice;
 };
 
-class SoundTowns_EuphonyTrackQueue {
+class Towns_EuphonyTrackQueue {
 public:
-	SoundTowns_EuphonyTrackQueue(SoundTowns_EuphonyDriver *driver, SoundTowns_EuphonyTrackQueue *last);
-	~SoundTowns_EuphonyTrackQueue() {}
+	Towns_EuphonyTrackQueue(Towns_EuphonyDriver *driver, Towns_EuphonyTrackQueue *last);
+	~Towns_EuphonyTrackQueue() {}
 
-	void release();
+	Towns_EuphonyTrackQueue *release();
 	void initDriver();
-	void loadDataToCurrentPosition(uint8 * trackdata, uint32 size, bool loop = 0);
-	void loadDataToEndOfQueue(uint8 * trackdata, uint32 size, bool loop = 0);
+	void loadDataToCurrentPosition(uint8 *trackdata, uint32 size, bool loop = 0);
+	void loadDataToEndOfQueue(uint8 *trackdata, uint32 size, bool loop = 0);
 	void setPlayBackStatus(bool playing);
-	SoundTowns_EuphonyTrackQueue * reset();
 	bool isPlaying() {return _playing; }
-	uint8 * trackData() {return _trackData; }
+	const uint8 * trackData() {return _trackData; }
 
 	bool _loop;
-	SoundTowns_EuphonyTrackQueue * _next;
+	Towns_EuphonyTrackQueue *_next;
 
 private:
-	uint8 * _trackData;
-	uint8 * _used;
-	uint8 * _fchan;
-	uint8 * _wchan;
+	uint8 *_trackData;
+	uint8 *_used;
+	uint8 *_fchan;
+	uint8 *_wchan;
 	bool _playing;
-	SoundTowns_EuphonyDriver * _driver;
-	SoundTowns_EuphonyTrackQueue * _last;
+	Towns_EuphonyDriver *_driver;
+	Towns_EuphonyTrackQueue *_last;
 };
 
-class MidiParser_EuD : public MidiParser {
+class Towns_EuphonyParser : public MidiParser {
 public:
-	MidiParser_EuD(SoundTowns_EuphonyTrackQueue * queue);
+	Towns_EuphonyParser(Towns_EuphonyTrackQueue * queue);
 	bool loadMusic (byte *data, uint32 size);
 	int32 calculateTempo(int16 val);
 
@@ -183,11 +185,11 @@
 	void resetTracking();
 	void setup();
 
-	byte * _enable;
-	byte * _mode;
-	byte * _channel;
-	byte * _adjVelo;
-	int8 * _adjNote;
+	byte *_enable;
+	byte *_mode;
+	byte *_channel;
+	byte *_adjVelo;
+	int8 *_adjNote;
 
 	uint8 _firstBaseTickStep;
 	uint8 _nextBaseTickStep;
@@ -195,13 +197,13 @@
 	uint32 _baseTick;
 
 	byte _tempo[3];
-	SoundTowns_EuphonyTrackQueue * _queue;
+	Towns_EuphonyTrackQueue *_queue;
 };
 
-class SoundTowns_EuphonyDriver : public MidiDriver_Emulated {
+class Towns_EuphonyDriver : public MidiDriver_Emulated {
 public:
-	SoundTowns_EuphonyDriver(Audio::Mixer *mixer);
-	virtual ~SoundTowns_EuphonyDriver();
+	Towns_EuphonyDriver(Audio::Mixer *mixer);
+	virtual ~Towns_EuphonyDriver();
 
 	int open();
 	void close();
@@ -213,7 +215,7 @@
 	void loadFmInstruments(const byte *instr);
 	void loadWaveInstruments(const byte *instr);
 
-	SoundTowns_EuphonyTrackQueue * queue() { return _queue; }
+	Towns_EuphonyTrackQueue *queue() { return _queue; }
 
 	MidiChannel *allocateChannel() { return 0; }
 	MidiChannel *getPercussionChannel() { return 0; }
@@ -237,10 +239,10 @@
 
 	void generateSamples(int16 *buf, int len);
 
-	MidiChannel_EuD_FM *_fChannel[6];
-	MidiChannel_EuD_WAVE *_wChannel[8];
-	MidiChannel_EuD * _channel[16];
-	SoundTowns_EuphonyTrackQueue * _queue;
+	Towns_EuphonyFmChannel *_fChannel[6];
+	Towns_EuphonyPcmChannel *_wChannel[8];
+	Towns_EuphonyChannel *_channel[16];
+	Towns_EuphonyTrackQueue *_queue;
 
 	int _volume;
 	bool _fading;
@@ -251,23 +253,23 @@
 	int8 * _waveSounds[10];
 };
 
-MidiChannel_EuD_FM::MidiChannel_EuD_FM() {
+Towns_EuphonyFmChannel::Towns_EuphonyFmChannel() {
 	_voice = new Voice2612;
 }
 
-MidiChannel_EuD_FM::~MidiChannel_EuD_FM() {
+Towns_EuphonyFmChannel::~Towns_EuphonyFmChannel() {
 	delete _voice;
 }
 
-void MidiChannel_EuD_FM::noteOn(byte note, byte onVelo) {
+void Towns_EuphonyFmChannel::noteOn(byte note, byte onVelo) {
 	_voice->noteOn(note, onVelo);
 }
 
-void MidiChannel_EuD_FM::noteOff(byte note) {
+void Towns_EuphonyFmChannel::noteOff(byte note) {
 	_voice->noteOff(note);
 }
 
-void MidiChannel_EuD_FM::controlChange(byte control, byte value) {
+void Towns_EuphonyFmChannel::controlChange(byte control, byte value) {
 	if (control == 121) {
 		// Reset controller
 		delete _voice;
@@ -279,25 +281,25 @@
 	}
 }
 
-void MidiChannel_EuD_FM::sysEx_customInstrument(uint32, const byte *fmInst) {
+void Towns_EuphonyFmChannel::sysEx_customInstrument(uint32, const byte *fmInst) {
 	_voice->_rate = _rate;
 	_voice->setInstrument(fmInst);
 }
 
-void MidiChannel_EuD_FM::pitchBend(int16 value) {
+void Towns_EuphonyFmChannel::pitchBend(int16 value) {
 	_voice->pitchBend(value);
 }
 
-void MidiChannel_EuD_FM::nextTick(int32 *outbuf, int buflen) {
+void Towns_EuphonyFmChannel::nextTick(int32 *outbuf, int buflen) {
 	_voice->nextTick((int*) outbuf, buflen);
 }
 
-void MidiChannel_EuD_FM::rate(uint16 r) {
+void Towns_EuphonyFmChannel::rate(uint16 r) {
 	_rate = r;
 	_voice->_rate = r;
 }
 
-MidiChannel_EuD_WAVE::MidiChannel_EuD_WAVE() {
+Towns_EuphonyPcmChannel::Towns_EuphonyPcmChannel() {
 	_voice = new Voice;
 	for (uint8 i = 0; i < 8; i++) {
 		_voice->_env[i] = new Voice::Env;
@@ -310,7 +312,7 @@
 	_current = -1;
 }
 
-MidiChannel_EuD_WAVE::~MidiChannel_EuD_WAVE() {
+Towns_EuphonyPcmChannel::~Towns_EuphonyPcmChannel() {
 	for (uint8 i = 0; i < 8; i++) {
 		if (_voice->_snd[i])
 			delete _voice->_snd[i];
@@ -319,7 +321,7 @@
 	delete _voice;
 }
 
-void MidiChannel_EuD_WAVE::noteOn(byte note, byte onVelo) {
+void Towns_EuphonyPcmChannel::noteOn(byte note, byte onVelo) {
 	_note = note;
 	velocity(onVelo);
 	_phase = 0;
@@ -329,24 +331,24 @@
 			break;
 	}
 
-	_voice->_env[_current]->state = _s_attacking;
+	_voice->_env[_current]->state = s_attacking;
 	_voice->_env[_current]->currentLevel = 0;
 	_voice->_env[_current]->rate = _rate;
 	_voice->_env[_current]->tickCount = 0;
 }
 
-void MidiChannel_EuD_WAVE::noteOff(byte note) {
+void Towns_EuphonyPcmChannel::noteOff(byte note) {
     if (_current == -1)
 		return;
-	if (_voice->_env[_current]->state == _s_ready)
+	if (_voice->_env[_current]->state == s_ready)
 		return;
 
-	_voice->_env[_current]->state = _s_releasing;
+	_voice->_env[_current]->state = s_releasing;
 	_voice->_env[_current]->releaseLevel = _voice->_env[_current]->currentLevel;
 	_voice->_env[_current]->tickCount = 0;
 }
 
-void MidiChannel_EuD_WAVE::controlChange(byte control, byte value) {
+void Towns_EuphonyPcmChannel::controlChange(byte control, byte value) {
 	switch (control) {
 		case 0x07:
 			// volume
@@ -377,7 +379,7 @@
 	}
 }
 
-void MidiChannel_EuD_WAVE::sysEx_customInstrument(uint32 type, const byte *fmInst) {
+void Towns_EuphonyPcmChannel::sysEx_customInstrument(uint32 type, const byte *fmInst) {
 	if (type == 0x80) {
 		for (uint8 i = 0; i < 8; i++) {
 			const byte * const* pos = (const byte * const*) fmInst;
@@ -406,7 +408,7 @@
 			_voice->split[i] = READ_LE_UINT16(fmInst + 16 + 2 * i);
 			_voice->id[i] = READ_LE_UINT32(fmInst + 32 + 4 * i);
 			_voice->_snd[i] = 0;
-			_voice->_env[i]->state = _s_ready;
+			_voice->_env[i]->state = s_ready;
 			_voice->_env[i]->currentLevel = 0;
 			_voice->_env[i]->totalLevel = *(fmInst + 64 + 8 * i);
 			_voice->_env[i]->attackRate = *(fmInst + 65 + 8 * i) * 10;
@@ -419,11 +421,11 @@
 	}
 }
 
-void MidiChannel_EuD_WAVE::pitchBend(int16 value) {
+void Towns_EuphonyPcmChannel::pitchBend(int16 value) {
 	_frequencyOffs = value;
 }
 
-void MidiChannel_EuD_WAVE::nextTick(int32 *outbuf, int buflen) {
+void Towns_EuphonyPcmChannel::nextTick(int32 *outbuf, int buflen) {
 	if (_current == -1 || !_voice->_snd[_current] || !_voice->_env[_current]->state || !_velocity) {
 		velocity(0);
 		_current = -1;
@@ -475,13 +477,13 @@
 	}
 }
 
-void MidiChannel_EuD_WAVE::evpNextTick() {
+void Towns_EuphonyPcmChannel::evpNextTick() {
 	switch (_voice->_env[_current]->state) {
-		case _s_ready:
+		case s_ready:
 			_voice->_env[_current]->currentLevel = 0;
 			return;
 
-		case _s_attacking:
+		case s_attacking:
 			if (_voice->_env[_current]->attackRate == 0)
 				_voice->_env[_current]->currentLevel = _voice->_env[_current]->totalLevel;
 			else if (_voice->_env[_current]->attackRate >= 1270)
@@ -493,12 +495,12 @@
 
 			if (_voice->_env[_current]->currentLevel >= _voice->_env[_current]->totalLevel) {
 				_voice->_env[_current]->currentLevel = _voice->_env[_current]->totalLevel;
-				_voice->_env[_current]->state = _s_decaying;
+				_voice->_env[_current]->state = s_decaying;
 				_voice->_env[_current]->tickCount = 0;
 			}
 			break;
 
-		case _s_decaying:
+		case s_decaying:
 			if (_voice->_env[_current]->decayRate == 0)
 				_voice->_env[_current]->currentLevel = _voice->_env[_current]->sustainLevel;
 			else if (_voice->_env[_current]->decayRate >= 1270)
@@ -512,12 +514,12 @@
 
 			if (_voice->_env[_current]->currentLevel <= _voice->_env[_current]->sustainLevel) {
 				_voice->_env[_current]->currentLevel = _voice->_env[_current]->sustainLevel;
-				_voice->_env[_current]->state = _s_sustaining;
+				_voice->_env[_current]->state = s_sustaining;
 				_voice->_env[_current]->tickCount = 0;
 			}
 			break;
 
-			case _s_sustaining:
+			case s_sustaining:
 				if (_voice->_env[_current]->sustainRate == 0)
 					_voice->_env[_current]->currentLevel = 0;
 				else if (_voice->_env[_current]->sustainRate >= 2540)
@@ -531,12 +533,12 @@
 
 				if (_voice->_env[_current]->currentLevel <= 0) {
 					_voice->_env[_current]->currentLevel = 0;
-					_voice->_env[_current]->state = _s_ready;
+					_voice->_env[_current]->state = s_ready;
 					_voice->_env[_current]->tickCount = 0;
 				}
 				break;
 
-			case _s_releasing:
+			case s_releasing:
 				if (_voice->_env[_current]->releaseRate == 0)
 					_voice->_env[_current]->currentLevel = 0;
 				else if (_voice->_env[_current]->releaseRate >= 1270)
@@ -550,7 +552,7 @@
 
 				if (_voice->_env[_current]->currentLevel <= 0) {
 					_voice->_env[_current]->currentLevel = 0;
-					_voice->_env[_current]->state = _s_ready;
+					_voice->_env[_current]->state = s_ready;
 				}
 				break;
 
@@ -559,15 +561,15 @@
 	}
 }
 
-void MidiChannel_EuD_WAVE::rate(uint16 r) {
+void Towns_EuphonyPcmChannel::rate(uint16 r) {
 	_rate = r;
 }
 
-void MidiChannel_EuD_WAVE::velocity(int velo) {
+void Towns_EuphonyPcmChannel::velocity(int velo) {
 	_velocity = velo;
 }
 
-SoundTowns_EuphonyDriver::SoundTowns_EuphonyDriver(Audio::Mixer *mixer)
+Towns_EuphonyDriver::Towns_EuphonyDriver(Audio::Mixer *mixer)
 	: MidiDriver_Emulated(mixer) {
 	_volume = 255;
 	_fadestate = EUPHONY_FADEOUT_TICKS;
@@ -576,9 +578,9 @@
 	MidiDriver_YM2612::createLookupTables();
 
 	for (uint8 i = 0; i < 6; i++)
-		_channel[i] = _fChannel[i] = new MidiChannel_EuD_FM;
+		_channel[i] = _fChannel[i] = new Towns_EuphonyFmChannel;
 	for (uint8 i = 0; i < 8; i++)
-		_channel[i + 6] = _wChannel[i] = new MidiChannel_EuD_WAVE;
+		_channel[i + 6] = _wChannel[i] = new Towns_EuphonyPcmChannel;
 	_channel[14] = _channel[15] = 0;
 
 	_fmInstruments = _waveInstruments = 0;
@@ -587,10 +589,10 @@
 	rate(getRate());
 	fading(0);
 
-	_queue = new SoundTowns_EuphonyTrackQueue(this, 0);
+	_queue = new Towns_EuphonyTrackQueue(this, 0);
 }
 
-SoundTowns_EuphonyDriver::~SoundTowns_EuphonyDriver() {
+Towns_EuphonyDriver::~Towns_EuphonyDriver() {
 	for (int i = 0; i < 6; i++)
 		delete _fChannel[i];
 	for (int i = 0; i < 8; i++)
@@ -622,7 +624,7 @@
 	}
 }
 
-int SoundTowns_EuphonyDriver::open() {
+int Towns_EuphonyDriver::open() {
 	if (_isOpen)
 		return MERR_ALREADY_OPEN;
 	MidiDriver_Emulated::open();
@@ -633,18 +635,18 @@
 	return 0;
 }
 
-void SoundTowns_EuphonyDriver::close() {
+void Towns_EuphonyDriver::close() {
 	if (!_isOpen)
 		return;
 	_isOpen = false;
 	_mixer->stopHandle(_mixerSoundHandle);
 }
 
-void SoundTowns_EuphonyDriver::send(uint32 b) {
+void Towns_EuphonyDriver::send(uint32 b) {
 	send(b & 0xF, b & 0xFFFFFFF0);
 }
 
-void SoundTowns_EuphonyDriver::send(byte chan, uint32 b) {
+void Towns_EuphonyDriver::send(byte chan, uint32 b) {
 	byte param2 = (byte) ((b >> 16) & 0xFF);
 	byte param1 = (byte) ((b >>  8) & 0xFF);
 	byte cmd    = (byte) (b & 0xF0);
@@ -703,18 +705,18 @@
 			_channel[chan]->pitchBend((param1 | (param2 << 7)) - 0x2000);
 		break;
 	default:
-		warning("SoundTowns_EuphonyDriver: Unknown send() command 0x%02X", cmd);
+		warning("Towns_EuphonyDriver: Unknown send() command 0x%02X", cmd);
 	}
 }
 
-void SoundTowns_EuphonyDriver::loadFmInstruments(const byte *instr) {
+void Towns_EuphonyDriver::loadFmInstruments(const byte *instr) {
 	if (_fmInstruments)
 		delete[] _fmInstruments;
 	_fmInstruments = new uint8[0x1800];
 	memcpy(_fmInstruments, instr, 0x1800);
 }
 
-void SoundTowns_EuphonyDriver::loadWaveInstruments(const byte *instr) {
+void Towns_EuphonyDriver::loadWaveInstruments(const byte *instr) {
 	if (_waveInstruments)
 		delete[] _waveInstruments;
 	_waveInstruments = new uint8[0x1000];
@@ -739,24 +741,24 @@
 }
 
 
-void SoundTowns_EuphonyDriver::assignFmChannel(uint8 midiChannelNumber, uint8 fmChannelNumber) {
+void Towns_EuphonyDriver::assignFmChannel(uint8 midiChannelNumber, uint8 fmChannelNumber) {
 	_channel[midiChannelNumber] = _fChannel[fmChannelNumber];
 }
 
-void SoundTowns_EuphonyDriver::assignWaveChannel(uint8 midiChannelNumber, uint8 waveChannelNumber) {
+void Towns_EuphonyDriver::assignWaveChannel(uint8 midiChannelNumber, uint8 waveChannelNumber) {
 	_channel[midiChannelNumber] = _wChannel[waveChannelNumber];
 }
 
-void SoundTowns_EuphonyDriver::removeChannel(uint8 midiChannelNumber) {
+void Towns_EuphonyDriver::removeChannel(uint8 midiChannelNumber) {
 	_channel[midiChannelNumber] = 0;
 }
 
-void SoundTowns_EuphonyDriver::generateSamples(int16 *data, int len) {
+void Towns_EuphonyDriver::generateSamples(int16 *data, int len) {
 	memset(data, 0, 2 * sizeof(int16) * len);
 	nextTick(data, len);
 }
 
-void SoundTowns_EuphonyDriver::nextTick(int16 *buf1, int buflen) {
+void Towns_EuphonyDriver::nextTick(int16 *buf1, int buflen) {
 	int32 *buf0 = (int32 *)buf1;
 
 	for (int i = 0; i < ARRAYSIZE(_channel); i++) {
@@ -779,33 +781,33 @@
 	}
 }
 
-void SoundTowns_EuphonyDriver::rate(uint16 r) {
+void Towns_EuphonyDriver::rate(uint16 r) {
 	for (uint8 i = 0; i < 16; i++) {
 		if (_channel[i])
 			_channel[i]->rate(r);
 	}
 }
 
-void SoundTowns_EuphonyDriver::fading(bool status) {
+void Towns_EuphonyDriver::fading(bool status) {
 	_fading = status;
 	if (!_fading)
 		_fadestate = EUPHONY_FADEOUT_TICKS;
 }
 
-MidiParser_EuD::MidiParser_EuD(SoundTowns_EuphonyTrackQueue * queue) : MidiParser(),
+Towns_EuphonyParser::Towns_EuphonyParser(Towns_EuphonyTrackQueue * queue) : MidiParser(),
 	_firstBaseTickStep(0x33), _nextBaseTickStep(0x33) {
 		_initialTempo = calculateTempo(0x5a);
 		_queue = queue;
 }
 
-void MidiParser_EuD::parseNextEvent(EventInfo &info) {
+void Towns_EuphonyParser::parseNextEvent(EventInfo &info) {
 	byte *pos = _position._play_pos;
 
 	if (_queue->_next) {
 		if (info.ext.type == 0x2F) {
 			unloadMusic();
 			memset(&info, 0, sizeof(EventInfo));
-			pos = _position._play_pos = _tracks[0] = _queue->trackData() + 0x806;
+			pos = _position._play_pos = _tracks[0] = (byte*) _queue->trackData() + 0x806;
 		} else if (_active_track == 255) {
 			_queue = _queue->_next;
 			setup();
@@ -920,15 +922,14 @@
 	_position._play_pos = pos;
 }
 
-bool MidiParser_EuD::loadMusic(byte *data, uint32 size) {
+bool Towns_EuphonyParser::loadMusic(byte *data, uint32 size) {
 	bool loop = _autoLoop;
 
 	if (_queue->isPlaying() && !_queue->_loop) {
 		_queue->loadDataToEndOfQueue(data, size, loop);
 	} else {
 		unloadMusic();
-		_queue = _queue->reset();
-		_queue->release();
+		_queue = _queue->release();
 		_queue->loadDataToCurrentPosition(data, size, loop);
 		setup();
 		setTrack(0);
@@ -937,7 +938,7 @@
 	return true;
 }
 
-int32 MidiParser_EuD::calculateTempo(int16 val) {
+int32 Towns_EuphonyParser::calculateTempo(int16 val) {
 	int32 tempo = val;
 
 	if (tempo < 0)
@@ -953,7 +954,7 @@
 	return tempo;
 }
 
-void MidiParser_EuD::resetTracking() {
+void Towns_EuphonyParser::resetTracking() {
 	MidiParser::resetTracking();
 
 	_nextBaseTickStep = _firstBaseTickStep;
@@ -962,8 +963,8 @@
 	_queue->setPlayBackStatus(false);
 }
 
-void MidiParser_EuD::setup() {
-	uint8 *data = _queue->trackData();
+void Towns_EuphonyParser::setup() {
+	uint8 *data = (uint8 *) _queue->trackData();
 	if (!data)
 		return;
 	_queue->initDriver();
@@ -984,7 +985,7 @@
 	_tracks[0] = data + 0x806;
 }
 
-SoundTowns_EuphonyTrackQueue::SoundTowns_EuphonyTrackQueue(SoundTowns_EuphonyDriver * driver, SoundTowns_EuphonyTrackQueue * last) {
+Towns_EuphonyTrackQueue::Towns_EuphonyTrackQueue(Towns_EuphonyDriver * driver, Towns_EuphonyTrackQueue * last) {
 	_trackData = 0;
 	_next = 0;
 	_driver = driver;
@@ -993,22 +994,15 @@
 	_playing = false;
 }
 
-void SoundTowns_EuphonyTrackQueue::setPlayBackStatus(bool playing) {
-	SoundTowns_EuphonyTrackQueue * i = this;
+void Towns_EuphonyTrackQueue::setPlayBackStatus(bool playing) {
+	Towns_EuphonyTrackQueue * i = this;
 	do {
 		i->_playing = playing;
 		i = i->_next;
 	} while (i);
 }
 
-SoundTowns_EuphonyTrackQueue * SoundTowns_EuphonyTrackQueue::reset() {
-	SoundTowns_EuphonyTrackQueue * i = this;
-	while (i->_last)
-		i = i->_last;
-	return i;
-}
-
-void SoundTowns_EuphonyTrackQueue::loadDataToCurrentPosition(uint8 * trackdata, uint32 size, bool loop) {
+void Towns_EuphonyTrackQueue::loadDataToCurrentPosition(uint8 * trackdata, uint32 size, bool loop) {
 	if (_trackData)
 		delete[] _trackData;
 	_trackData = new uint8[0xC58A];
@@ -1022,17 +1016,17 @@
 	_playing = false;
 }
 
-void SoundTowns_EuphonyTrackQueue::loadDataToEndOfQueue(uint8 * trackdata, uint32 size, bool loop) {
+void Towns_EuphonyTrackQueue::loadDataToEndOfQueue(uint8 * trackdata, uint32 size, bool loop) {
 	if (!_trackData) {
 		loadDataToCurrentPosition(trackdata, size, loop);
 		return;
 	}
 
-	SoundTowns_EuphonyTrackQueue * i = this;
+	Towns_EuphonyTrackQueue * i = this;
 	while (i->_next)
 		i = i->_next;
 
-	i = i->_next = new SoundTowns_EuphonyTrackQueue(_driver, i);
+	i = i->_next = new Towns_EuphonyTrackQueue(_driver, i);
 	i->_trackData = new uint8[0xC58A];
 	memset(i->_trackData, 0, 0xC58A);
 	Screen::decodeFrame4(trackdata, i->_trackData, size);
@@ -1044,29 +1038,39 @@
 	i->_playing = _playing;
 }
 
-void SoundTowns_EuphonyTrackQueue::release() {
-	SoundTowns_EuphonyTrackQueue * i = _next;
-	_next = 0;
-	_playing = false;
-	_used = _fchan = _wchan = 0;
+Towns_EuphonyTrackQueue *Towns_EuphonyTrackQueue::release() {
+	Towns_EuphonyTrackQueue *i = this;
+	while (i->_next)
+		i = i->_next;
 
-	if (_trackData) {
-		delete[] _trackData;
-		_trackData = 0;
-	}
+	Towns_EuphonyTrackQueue *res = i;
 
 	while (i) {
+		i->_playing = false;
+		i->_used = i->_fchan = i->_wchan = 0;
 		if (i->_trackData) {
 			delete[] i->_trackData;
 			i->_trackData = 0;
 		}
-		i = i->_next;
-		if (i)
-			delete i->_last;
+		i = i->_last;
+		if (i) {
+			res = i;
+			if (i->_next) {
+				delete i->_next;
+				i->_next = 0;
+			}
+		}
 	}
+
+	if (res->_trackData) {
+		delete[] res->_trackData;
+		res->_trackData = 0;
+	}
+
+	return res;
 }
 
-void SoundTowns_EuphonyTrackQueue::initDriver() {
+void Towns_EuphonyTrackQueue::initDriver() {
 	for (uint8 i = 0; i < 6; i++) {
 		if (_used[_fchan[i]])
 			_driver->assignFmChannel(_fchan[i], i);
@@ -1084,11 +1088,1339 @@
 	_driver->send(0x79B0);
 }
 
+class TownsPC98_OpnOperator {
+public:
+	TownsPC98_OpnOperator::TownsPC98_OpnOperator(double rate, uint8 id, const uint8 *rateTable,
+		const uint8 *shiftTable, const uint8 *attackDecayTable, const uint32 *frqTable,
+		const uint32 *sineTable, const int32 *tlevelOut, const int32 *detuneTable);
+	TownsPC98_OpnOperator::~TownsPC98_OpnOperator() {}
+
+	void keyOn();
+	void keyOff();
+	void frequency(int freq);
+	void updatePhaseIncrement();
+	void recalculateRates();
+	void generateOutput(int phasebuf, int *feedbuf, int &out);
+
+	void feedbackLevel(int32 level) {_feedbackLevel = level ? level + 6 : 0; }
+	void detune(int value) { _detn = (int32*) &_detnTbl[value << 5]; }
+	void multiple(uint32 value) { _multiple = value ? (value << 1) : 1;	}
+	void attackRate(uint32 value) { _specifiedAttackRate = value; }
+	bool scaleRate(uint8 value);
+	void decayRate(uint32 value) { _specifiedDecayRate = value;	recalculateRates();	}
+	void sustainRate(uint32 value) { _specifiedSustainRate = value;	recalculateRates();	}
+	void sustainLevel(uint32 value) { _sustainLevel = (value == 0x0f) ? 0x3e0 : value << 5; }
+	void releaseRate(uint32 value) { _specifiedReleaseRate = value;	recalculateRates();	}
+	void totalLevel(uint32 value) { _totalLevel = value << 3; }
+	void reset();
+
+protected:
+	EnvelopeState _state;
+	uint32 _feedbackLevel;
+	uint32 _multiple;
+	uint32 _totalLevel;
+	uint8 _keyScale1;
+	uint8 _keyScale2;
+	uint32 _specifiedAttackRate;
+	uint32 _specifiedDecayRate;
+	uint32 _specifiedSustainRate;
+	uint32 _specifiedReleaseRate;
+	uint32 _tickCount;
+	uint32 _sustainLevel;
+
+	uint32 _frequency;
+	uint8 _kcode;
+	uint32 _phase;
+	uint32 _phaseIncrement;
+	int32 *_detn;
+
+	const uint8 *_rateTbl;
+	const uint8 *_rshiftTbl;
+	const uint8 *_adTbl;
+	const uint32 *_fTbl;
+	const uint32 *_sinTbl;
+	const int32 *_tLvlTbl;
+	const int32 *_detnTbl;
+
+	const double _tickLength;
+	double _tick;
+	int32 _currentLevel;
+
+	struct EvpState {
+		uint8 rate;
+		uint8 shift;
+	} fs_a, fs_d, fs_s, fs_r;
+};
+
+TownsPC98_OpnOperator::TownsPC98_OpnOperator(double rate, uint8 id,
+	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),
+	_specifiedAttackRate(0), _specifiedDecayRate(0), _specifiedReleaseRate(0), _specifiedSustainRate(0),
+	_phase(0), _state(s_ready) {
+	
+	reset();
+}
+
+void TownsPC98_OpnOperator::keyOn() {
+	_state = s_attacking;
+	_phase = 0;
+}
+
+void TownsPC98_OpnOperator::keyOff() {
+	if (_state != s_ready)
+		_state = s_releasing;
+}
+
+void TownsPC98_OpnOperator::frequency(int freq) {
+	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);
+}
+
+void TownsPC98_OpnOperator::updatePhaseIncrement() {
+	_phaseIncrement = ((_frequency + _detn[_kcode]) * _multiple) >> 1;
+	uint8 keyscale = _kcode >> _keyScale1;
+	if (_keyScale2 != keyscale) {
+		_keyScale2 = keyscale;
+		recalculateRates();
+	}
+}
+
+void TownsPC98_OpnOperator::recalculateRates() {
+	int k = _keyScale2;
+	int r = _specifiedAttackRate ? (_specifiedAttackRate << 1) + 0x20 : 0;
+	fs_a.rate = ((r + k) < 94) ? _rateTbl[r + k] : 136;
+	fs_a.shift = ((r + k) < 94) ? _rshiftTbl[r + k] : 0;
+
+	r = _specifiedDecayRate ? (_specifiedDecayRate << 1) + 0x20 : 0;
+	fs_d.rate = _rateTbl[r + k];
+	fs_d.shift = _rshiftTbl[r + k];
+
+	r = _specifiedSustainRate ? (_specifiedSustainRate << 1) + 0x20 : 0;
+	fs_s.rate = _rateTbl[r + k];
+	fs_s.shift = _rshiftTbl[r + k];
+
+	r = (_specifiedReleaseRate << 2) + 0x22;
+	fs_r.rate = _rateTbl[r + k];
+	fs_r.shift = _rshiftTbl[r + k];
+}
+
+void TownsPC98_OpnOperator::generateOutput(int phasebuf, int *feedbuf, int &out) {
+	if (_state == s_ready)
+		return;
+
+	_tick += _tickLength;
+	while (_tick > 0x30000) {
+		_tick -= 0x30000;
+		++_tickCount;
+
+		int32 levelIncrement = 0;
+		uint32 targetTime = 0;
+		int32 targetLevel = 0;
+		EnvelopeState next_state = s_ready;
+
+		switch (_state) {
+			case s_ready:
+				return;
+			case s_attacking:
+				next_state = 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;
+				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;
+				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;
+				targetLevel = 1023;
+				levelIncrement = _adTbl[fs_r.rate + ((_tickCount >> fs_r.shift) & 7)];
+				break;
+		}
+
+		if (!(_tickCount & targetTime)) {
+			_currentLevel += levelIncrement;
+			if ((!targetLevel && _currentLevel <= targetLevel) || (targetLevel && _currentLevel >= targetLevel)) {
+				if (_state != s_decaying)
+					_currentLevel = targetLevel;
+				if (_state != s_sustaining)
+					_state = next_state;
+			}
+		}
+	}
+
+	uint32 lvlout = _totalLevel + (uint32) _currentLevel;
+
+	int outp = 0;
+	int *i = &outp, *o = &outp;
+	int phaseShift = 0;
+
+	if (feedbuf) {
+		o = &feedbuf[0];
+		i = &feedbuf[1];
+		phaseShift = _feedbackLevel ? ((feedbuf[0] + feedbuf[1]) << _feedbackLevel) : 0;
+		*o = *i;
+	} else {
+		phaseShift = phasebuf << 15;
+	}		
+
+	if (lvlout < 832) {
+		uint32 index = (lvlout << 3) + _sinTbl[(((int32)((_phase & 0xffff0000)
+			+ phaseShift)) >> 16) & 0x3ff];
+		*i = ((index < 6656) ? _tLvlTbl[index] : 0);
+	} else {
+		*i = 0;
+	}
+
+	_phase += _phaseIncrement;
+	out += *o;
+	if (out > 32767)
+		out = 32767;
+	if (out < -32767)
+		out = -32767;
+}
+
+void TownsPC98_OpnOperator::reset(){
+	keyOff();
+	_tick = 0;
+	_keyScale2 = -1;
+	_currentLevel = 1023;
+
+	frequency(0);
+	detune(0);
+	scaleRate(0);
+	multiple(0);
+	updatePhaseIncrement();
+	attackRate(0);
+	decayRate(0);
+	releaseRate(0);
+	sustainRate(0);
+	feedbackLevel(0);	
+	totalLevel(127);
+}
+
+bool TownsPC98_OpnOperator::scaleRate(uint8 value) {
+	value = 3 - value;
+	if (_keyScale1 != value) {
+		_keyScale1 = value;
+		return true;
+	}
+
+	int k = _keyScale2;
+	int r = _specifiedAttackRate ? (_specifiedAttackRate << 1) + 0x20 : 0;
+	fs_a.rate = ((r + k) < 94) ? _rateTbl[r + k] : 136;
+	fs_a.shift = ((r + k) < 94) ? _rshiftTbl[r + k] : 0;
+	return false;
+}
+
+class TownsPC98_OpnDriver : public Audio::AudioStream {
+public:
+	enum OpnType {
+		OD_TOWNS,
+		OD_TYPE26,
+		OD_TYPE86
+	};
+
+	TownsPC98_OpnDriver(Audio::Mixer *mixer, OpnType type);
+	~TownsPC98_OpnDriver();
+
+	bool init();
+	void loadData(uint8 *data, bool loadPaused = false);
+	void reset();
+	void fadeOut();
+	
+	void pause() { _playing = false; }
+	void cont() { _playing = true; }
+
+	void callback();
+	void nextTick(int16 *buffer, uint32 bufferSize);
+
+	bool looping() { return _looping == _updateChannelsFlag ? true : false; }
+
+	// AudioStream interface
+	int inline readBuffer(int16 *buffer, const int numSamples);
+	bool isStereo() const { return true; }
+	bool endOfData() const { return false; }
+	int getRate() const { return _mixer->getOutputRate(); }
+
+protected:
+	void generateTables();
+
+	typedef enum channelState {
+		CHS_RECALCFREQ		=	0x01,
+		CHS_KEYOFF			=	0x02,
+		CHS_PITCHWHEELOFF	=	0x08,
+		CHS_ALL_BUT_EOT		=	0x0f,
+		CHS_EOT				=	0x80
+	} ChannelState;
+
+	struct TwnChannel {
+		uint8 ticksLeft;
+		uint8 unk1, unk2, unk3;
+		uint8 algorithm;
+		uint8 instrID;
+		uint8 totalLevel;
+		uint8 frqBlockMSB;
+		int8 frqLSB;
+		uint8 keyOffTime;
+		bool protect;
+		uint8 *dataPtr;
+		uint8 unk15, unk16;
+		uint8 ptchWhlInitDelayLo;
+		uint8 ptchWhlInitDelayHi;
+		int16 ptchWhlModInitVal;
+		uint8 ptchWhlDuration;
+		uint8 ptchWhlCurDelay;
+		int16 ptchWhlModCurVal;
+		uint8 ptchWhlDurLeft;
+		uint16 frequency;
+		uint8 unk28, unk29;
+		uint8 regOffset;
+		uint8 flags;
+		uint8 chanNum;
+		uint8 keyNum;
+		uint8 part;
+		uint8 idFlag;
+
+		TownsPC98_OpnOperator *opr[4];
+		uint16 frqTemp;
+		bool enableLeft;
+		bool enableRight;
+		bool updateEnvelopes;
+		int feedbuf[3];
+	} **_channels;
+
+	void processEvents(TwnChannel *chan);
+	void processFrequency(TwnChannel *chan);
+	bool processControlEvent(TwnChannel *chan, uint8 cmd);
+
+	void setOutputLevel(TwnChannel *chan);
+	void setTempo(uint8 tempo);
+
+	void keyOn(TwnChannel *chan);
+	void keyOff(TwnChannel *chan);
+	void writeReg(TwnChannel *chan, uint8 regAdress, uint8 value);
+
+	void lock() { _mutex.lock(); }
+	void unlock() { _mutex.unlock(); }
+
+	bool control_f0_setPatch(TwnChannel *chan, uint8 para);
+	bool control_f1_presetOutputLevel(TwnChannel *chan, uint8 para);
+	bool control_f2_setKeyOffTime(TwnChannel *chan, uint8 para);
+	bool control_f3_setFreqLSB(TwnChannel *chan, uint8 para);
+	bool control_f4_setOutputLevel(TwnChannel *chan, uint8 para);
+	bool control_f5_setTempo(TwnChannel *chan, uint8 para);
+	bool control_f6_repeatSection(TwnChannel *chan, uint8 para);
+	bool control_f7_setupPitchWheel(TwnChannel *chan, uint8 para);
+	bool control_f8_togglePitchWheel(TwnChannel *chan, uint8 para);
+	bool control_f9_unk(TwnChannel *chan, uint8 para);
+	bool control_fa_writeReg(TwnChannel *chan, uint8 para);
+	bool control_fb_incOutLevel(TwnChannel *chan, uint8 para);
+	bool control_fc_decOutLevel(TwnChannel *chan, uint8 para);
+	bool control_fd_jump(TwnChannel *chan, uint8 para);
+	bool control_fe_unk(TwnChannel *chan, uint8 para);
+	bool control_ff_endOfTrack(TwnChannel *chan, uint8 para);
+
+	typedef bool (TownsPC98_OpnDriver::*ControlEventFunc)(TwnChannel * chan, uint8 para);
+
+	Audio::Mixer *_mixer;
+	TownsPC98_OpnOperator **_operators;
+	Common::Mutex _mutex;
+	Audio::SoundHandle _soundHandle;
+
+	const uint8 *_twnCarrier;
+	const uint8 *_twnFreqTable;
+	const uint8 *_twnFxCmdLen;
+	const uint8 *_twnLvlPresets;
+
+	uint8 *_oprRates;
+	uint8 *_oprRateshift;
+	uint8 *_oprAttackDecay;
+	uint32 *_oprFrq;
+	uint32 *_oprSinTbl;
+	int32 *_oprLevelOut;
+	int32 *_oprDetune;
+
+	const uint8 *_trackData;
+	const uint8 *_patches;
+	uint8 _cbCounter;
+	uint8 _updateChannelsFlag;
+	uint8 _finishedChannelsFlag;
+	uint16 _tempo;
+	bool _playing;
+	bool _fading;
+	uint8 _looping;
+
+	bool _updateEnvelopes;
+
+	int32 _samplesTillCallback;
+	int32 _samplesTillCallbackRemainder;
+	int32 _samplesPerCallback;
+	int32 _samplesPerCallbackRemainder;
+
+	const int _numChan;
+	const int _numSSG;
+	const bool _hasADPCM;
+	const bool _hasStereo;
+
+	double _baserate;
+	static const uint8 _drvTables[];
+	static const uint32 _adtStat[];
+};
+
+TownsPC98_OpnDriver::TownsPC98_OpnDriver(Audio::Mixer *mixer, OpnType type) :
+	_mixer(mixer), _trackData(0), _playing(false), _fading(false), _channels(0),
+	_operators(0), _looping(0), _twnCarrier(_drvTables + 76), _twnFreqTable(_drvTables + 84),
+	_twnFxCmdLen(_drvTables + 36), _twnLvlPresets(_drvTables + (type == OD_TOWNS ? 52 : 220)) ,
+	_oprRates(0), _oprRateshift(0), _oprAttackDecay(0), _oprFrq(0),	_oprSinTbl(0), _oprLevelOut(0),
+	_oprDetune(0), _cbCounter(4), _updateChannelsFlag(type == OD_TYPE26 ? 0x07 : 0x3F),
+	_finishedChannelsFlag(0), _samplesTillCallback(0), _samplesTillCallbackRemainder(0),
+	_numSSG(type == OD_TOWNS ? 0 : 3), _hasADPCM(type == OD_TYPE86 ? true : false),
+	_numChan(type == OD_TYPE26 ? 3 : 6), _hasStereo(type == OD_TYPE26 ? false : true) {	
+	setTempo(84);
+	_baserate = (3579545.0 / (double)getRate()) / 144.0;
+}
+
+TownsPC98_OpnDriver::~TownsPC98_OpnDriver() {
+	_mixer->stopHandle(_soundHandle);
+
+	if (_operators) {
+		for (int i = 0; i < (_numChan << 2); i++)
+			delete _operators[i];
+		delete [] _operators;
+	}
+
+	if (_channels) {
+		for (int i = 0; i < _numChan; i++)
+			delete _channels[i];
+		delete [] _channels;
+	}
+
+	delete [] _oprRates;
+	delete [] _oprRateshift;
+	delete [] _oprFrq;
+	delete [] _oprAttackDecay;
+	delete [] _oprSinTbl;
+	delete [] _oprLevelOut;
+	delete [] _oprDetune;	
+}
+
+bool TownsPC98_OpnDriver::init() {
+	generateTables();
+
+	if (_operators) {
+		for (int i = 0; i < (_numChan << 2); i++) {
+			if (_operators[i]) {
+				delete _operators[i];
+			}
+		}
+		delete [] _operators;
+	}
+
+	_operators = new TownsPC98_OpnOperator*[(_numChan << 2)];
+	for (int i = 0; i < (_numChan << 2); i++)
+		_operators[i] = new TownsPC98_OpnOperator(_baserate, i & 3, _oprRates,
+			_oprRateshift, _oprAttackDecay, _oprFrq, _oprSinTbl, _oprLevelOut, _oprDetune);
+
+	if (_channels) {
+		for (int i = 0; i < _numChan; i++) {
+			if (_channels[i])
+				delete _channels[i];
+		}
+		delete [] _channels;
+	}
+	_channels = new TwnChannel*[_numChan];
+	for (int i = 0; i < _numChan; i++) {
+		_channels[i] = new TwnChannel;
+		for (int ii = 0; ii < 4; ii++) {
+			_channels[i]->opr[ii] = _operators[(i << 2) + ii];
+			_channels[i]->updateEnvelopes = false;
+		}
+	}
+
+	for (int i = 0; i < _numChan; i++) {
+		int ix = i * 6;
+		memset(_channels[i], 0, sizeof(TwnChannel));
+		_channels[i]->regOffset = _drvTables[ix];
+		_channels[i]->flags = _drvTables[ix + 1];
+		_channels[i]->chanNum = _drvTables[ix + 2];
+		_channels[i]->keyNum = _drvTables[ix + 3];
+		_channels[i]->part = _drvTables[ix + 4];
+		_channels[i]->idFlag = _drvTables[ix + 5];
+	}
+
+	_mixer->playInputStream(Audio::Mixer::kMusicSoundType,
+		&_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, false, true);
+
+	return true;
+}
+
+int inline TownsPC98_OpnDriver::readBuffer(int16 *buffer, const int numSamples) {
+	memset(buffer, 0, sizeof(int16) * numSamples);
+	int32 samplesLeft = numSamples >> 1;
+	while (samplesLeft) {
+		if (!_samplesTillCallback) {
+			callback();
+			_samplesTillCallback = _samplesPerCallback;
+			_samplesTillCallbackRemainder += _samplesPerCallbackRemainder;
+			if (_samplesTillCallbackRemainder >= _tempo) {
+				_samplesTillCallback++;
+				_samplesTillCallbackRemainder -= _tempo;
+			}
+		}
+
+		int32 render = MIN(samplesLeft, _samplesTillCallback);
+		samplesLeft -= render;
+		_samplesTillCallback -= render;
+
+		nextTick(buffer, render);
+
+		for (int i = 0; i < render; ++i) {
+			buffer[i << 1] <<= 2;
+			buffer[(i << 1) + 1] <<= 2;
+		}
+
+		buffer += (render << 1);
+	}
+
+	return numSamples;
+}
+
+void TownsPC98_OpnDriver::loadData(uint8 * data, bool loadPaused) {
+
+	lock();
+	_trackData = data;
+
+	reset();
+
+	for (uint8 i = 0; i < _numChan; i++) {
+		uint8 tmp1 = _channels[i]->regOffset;
+		uint8 tmp2 = _channels[i]->flags;
+		uint8 tmp3 = _channels[i]->chanNum;
+		uint8 tmp4 = _channels[i]->keyNum;
+		uint8 tmp5 = _channels[i]->part;
+		uint8 tmp6 = _channels[i]->idFlag;
+		memset(_channels[i], 0, sizeof(TwnChannel));
+		_channels[i]->regOffset = tmp1;
+		_channels[i]->flags = tmp2;
+		_channels[i]->chanNum = tmp3;
+		_channels[i]->keyNum = tmp4;
+		_channels[i]->part = tmp5;
+		_channels[i]->idFlag = tmp6;
+		_channels[i]->enableLeft = _channels[i]->enableRight = true;
+		for (int ii = 0; ii < 4; ii++)
+			_channels[i]->opr[ii] = _operators[(i << 2) + ii];
+	}
+
+	const uint8 *src_a = (const uint8*) data;
+	uint8 bl = 0;
+
+	for (uint8 i = 0; i < _numChan; i++) {
+		_channels[i]->flags = (_channels[i]->flags & ~CHS_EOT) | CHS_ALL_BUT_EOT;
+		_channels[i]->ticksLeft = 1;
+		_channels[i]->dataPtr = data + READ_LE_UINT16(src_a);
+		src_a += 2;
+		_channels[i]->totalLevel = 0x7F;
+		if (bl > 2)
+			bl -= 3;
+		_channels[i]->regOffset = bl++;
+
+		uint8 * src_b = _channels[i]->dataPtr;
+		int loop = 1;
+		uint8 cmd = 0;
+		while (loop) {
+			if (loop == 1) {
+				cmd = *src_b++;
+				if (cmd < 0xf0) {
+					src_b++;
+					loop = 1;
+				} else {
+					if (cmd == 0xff) {
+						loop = *src_b ? 2 : 0;
+						if (READ_LE_UINT16(src_b))
+							_looping |= _channels[i]->idFlag;
+					} else if (cmd == 0xf6) {
+						loop = 3;
+					} else {
+						loop = 2;
+					}
+				}
+			} else if (loop == 2) {
+				src_b += _twnFxCmdLen[cmd - 240];
+				loop = 1;
+			} else if (loop == 3) {
+				src_b[0] = src_b[1];
+				src_b += 4;
+				loop = 1;
+			}
+		}
+	}
+
+	for (int i = 0; i < _numSSG; i++) {
+		// TODO
+		//_channels[i]->flags = (_channels[i]->flags & ~CHS_EOT) | CHS_ALL_BUT_EOT;
+		//_channels[i]->ticksLeft = 1;
+		//_channels[i]->dataPtr = data + READ_LE_UINT16(src_a);
+		uint8 *tmp = data + READ_LE_UINT16(src_a);
+		src_a += 2;
+	}
+
+	if (_hasADPCM) {
+		// TODO
+		src_a += 2;
+	}
+
+	_patches = src_a + 4;
+	_cbCounter = 4;
+	_finishedChannelsFlag = 0;
+
+	// AH 0x17
+	unlock();
+	_playing = (loadPaused ? false : true);
+}
+
+void TownsPC98_OpnDriver::reset() {
+	for (int i = 0; i < (_numChan << 2); i++)
+		_operators[i]->reset();
+
+	_playing = false;
+	_looping = 0;
+}
+
+void TownsPC98_OpnDriver::fadeOut() {
+	if (!_playing)
+		return;
+
+	_fading = true;
+
+	for (int i = 0; i < 20; i++) {
+
+		/// TODO ///
+		// twnFade();
+		//waitTicks(s);
+		
+	}
+
+	_fading = false;
+
+	//haltTrack();
+}
+
+void TownsPC98_OpnDriver::callback() {
+	if (!_playing || --_cbCounter)
+		return;
+
+	_cbCounter = 4;
+
+	lock();
+	for (int i = 0; i < _numChan; i++) {
+		if (_updateChannelsFlag & _channels[i]->idFlag) {
+			processEvents(_channels[i]);
+			processFrequency(_channels[i]);
+		}
+	}
+	unlock();
+
+	if (_finishedChannelsFlag == _updateChannelsFlag)
+		reset();
+}
+
+void TownsPC98_OpnDriver::nextTick(int16 *buffer, uint32 bufferSize) {
+	if (!_playing)
+		return;	
+
+	for (int i = 0; i < _numChan ; i++) {
+		if (_channels[i]->updateEnvelopes) {
+			_channels[i]->updateEnvelopes = false;
+			for (int ii = 0; ii < 4 ; ii++)
+				_channels[i]->opr[ii]->updatePhaseIncrement();
+		}
+
+		int phbuf1, phbuf2, output;
+		int *feed = _channels[i]->feedbuf;
+		int *del = &feed[2];
+		
+		for (int ii = 0; ii < bufferSize ; ii++) {	
+			phbuf1 = phbuf2 = output = 0;
+
+			switch (_channels[i]->algorithm) {
+				case 0:
+					_channels[i]->opr[0]->generateOutput(0, feed, phbuf1);
+					_channels[i]->opr[2]->generateOutput(*del, 0, phbuf2);
+					*del = 0;
+					_channels[i]->opr[1]->generateOutput(phbuf1, 0, *del);
+					_channels[i]->opr[3]->generateOutput(phbuf2, 0, output);
+					break;
+				case 1:
+					_channels[i]->opr[0]->generateOutput(0, feed, phbuf1);
+					_channels[i]->opr[2]->generateOutput(*del, 0, phbuf2);
+					_channels[i]->opr[1]->generateOutput(0, 0, phbuf1);					
+					_channels[i]->opr[3]->generateOutput(phbuf2, 0, output);
+					*del = phbuf1;
+					break;
+				case 2:
+					_channels[i]->opr[0]->generateOutput(0, feed, phbuf2);
+					_channels[i]->opr[2]->generateOutput(*del, 0, phbuf2);
+					_channels[i]->opr[1]->generateOutput(0, 0, phbuf1);
+					_channels[i]->opr[3]->generateOutput(phbuf2, 0, output);
+					*del = phbuf1;
+					break;
+				case 3:
+					_channels[i]->opr[0]->generateOutput(0, feed, phbuf2);
+					_channels[i]->opr[2]->generateOutput(0, 0, *del);
+					_channels[i]->opr[1]->generateOutput(phbuf2, 0, phbuf1);
+					_channels[i]->opr[3]->generateOutput(*del, 0, output);
+					*del = phbuf1;
+					break;
+				case 4:
+					_channels[i]->opr[0]->generateOutput(0, feed, phbuf1);
+					_channels[i]->opr[2]->generateOutput(0, 0, phbuf2);
+					_channels[i]->opr[1]->generateOutput(phbuf1, 0, output);					
+					_channels[i]->opr[3]->generateOutput(phbuf2, 0, output);
+					*del = 0;
+					break;
+				case 5:
+					*del = feed[1];
+					_channels[i]->opr[0]->generateOutput(0, feed, phbuf1);
+					_channels[i]->opr[2]->generateOutput(*del, 0, output);
+					_channels[i]->opr[1]->generateOutput(*del, 0, output);
+					_channels[i]->opr[3]->generateOutput(*del, 0, output);
+					break;
+				case 6:
+					_channels[i]->opr[0]->generateOutput(0, feed, phbuf1);
+					_channels[i]->opr[2]->generateOutput(0, 0, output);
+					_channels[i]->opr[1]->generateOutput(phbuf1, 0, output);
+					_channels[i]->opr[3]->generateOutput(0, 0, output);
+					*del = 0;
+					break;
+				case 7:
+					_channels[i]->opr[0]->generateOutput(0, feed, output);
+					_channels[i]->opr[2]->generateOutput(0, 0, output);
+					_channels[i]->opr[1]->generateOutput(0, 0, output);
+					_channels[i]->opr[3]->generateOutput(0, 0, output);
+					*del = 0;
+					break;
+			};
+
+			if (_channels[i]->enableLeft) {
+				int l = output + buffer[ii * 2];
+				if (l > 32767)
+					l = 32767;
+				if (l < -32767)
+					l = -32767;
+				buffer[ii * 2] = (int16) l;
+			}
+
+			if (_channels[i]->enableRight) {
+				int r = output + buffer[ii * 2 + 1];
+				if (r > 32767)
+					r = 32767;
+				if (r < -32767)
+					r = -32767;
+				buffer[ii * 2 + 1] = (int16) r;
+			}
+		}
+	}
+}
+
+void TownsPC98_OpnDriver::generateTables() {
+	delete [] _oprRates;
+	_oprRates = new uint8[128];
+	memset(_oprRates, 0x90, 32);
+	uint8 * dst = (uint8*) _oprRates + 32;
+	for (int i = 0; i < 48; i += 4)
+		WRITE_BE_UINT32(dst + i, 0x00081018);
+	dst += 48;
+	for (uint8 i = 0; i < 16; i ++) {
+		uint8 v = (i < 12) ? i : 12;
+		*dst++ = ((4 + v) << 3);
+	}
+	memset(dst, 0x80, 32);
+
+	delete [] _oprRateshift;
+	_oprRateshift = new uint8[128];
+	memset(_oprRateshift, 0, 128);
+	dst = (uint8*) _oprRateshift + 32;
+	for (int i = 11; i; i--) {
+		memset(dst, i, 4);
+		dst += 4;
+	}
+
+	delete [] _oprFrq;
+	_oprFrq = new uint32[0x1000];
+	for (uint32 i = 0; i < 0x1000; i++)
+		_oprFrq[i] = (uint32)(_baserate * (double)(i << 11));
+
+	delete [] _oprAttackDecay;
+	_oprAttackDecay = new uint8[152];
+	memset(_oprAttackDecay, 0, 152);
+	for (int i = 0; i < 36; i++)
+		WRITE_BE_UINT32(_oprAttackDecay + (i << 2), _adtStat[i]);
+
+	delete [] _oprSinTbl;
+	_oprSinTbl = new uint32[1024];
+	for (int i = 0; i < 1024; i++) {
+		double val = sin((double) (((i << 1) + 1) * M_PI / 1024.0));
+		double d_dcb = log(1.0 / (double)ABS(val)) / log(2.0) * 256.0;
+		int32 i_dcb = (int32)(2.0 * d_dcb);
+		i_dcb = (i_dcb & 1) ? (i_dcb >> 1) + 1 : (i_dcb >> 1);
+		_oprSinTbl[i] = (i_dcb << 1) + (val >= 0.0 ? 0 : 1);
+	}
+
+	delete [] _oprLevelOut;
+	_oprLevelOut = new int32[0x1a00];
+	for (int i = 0; i < 256; i++) {
+		double val = floor(65536.0 / pow(2.0, 0.00390625 * (double)(1 + i)));
+		int32 val_int = ((int32) val) >> 4;
+		_oprLevelOut[i << 1] = (val_int & 1) ? ((val_int >> 1) + 1) << 2 : (val_int >> 1) << 2;
+		_oprLevelOut[(i << 1) + 1] = -_oprLevelOut[i << 1];
+		for (int ii = 1; ii < 13; ii++) {
+			_oprLevelOut[(i << 1) + (ii << 9)] =  _oprLevelOut[i << 1] >> ii;
+			_oprLevelOut[(i << 1) + (ii << 9) + 1] = -_oprLevelOut[(i << 1) + (ii << 9)];
+		}
+	}
+
+	uint8 * dtt = new uint8[128];
+	memset(dtt, 0, 36);
+	memset(dtt + 36, 1, 8);
+	memcpy(dtt + 44, _drvTables + 144, 84);
+
+	delete [] _oprDetune;
+	_oprDetune = new int32[256];
+	for (int i = 0; i < 128; i++) {
+		double rate = ((double)dtt[i]) * 1024.0  * _baserate  * (1<<16) / ((double)(1<<20));
+		_oprDetune[i] = (int32)	((double)dtt[i] * _baserate * 64.0);
+		_oprDetune[i + 128] = -_oprDetune[i];
+	}
+
+	delete [] dtt;
+}
+
+void TownsPC98_OpnDriver::processEvents(TwnChannel *chan) {
+	if (chan->flags & CHS_EOT)
+		return;
+
+	if (chan->protect == false && chan->ticksLeft == chan->keyOffTime)
+		keyOff(chan);
+
+	if (--chan->ticksLeft)
+		return;
+
+	if (chan->protect == false)
+		keyOff(chan);
+
+	uint8 cmd = 0;
+	bool loop = true;
+
+	while (loop) {
+		cmd = *chan->dataPtr++;
+		if (cmd < 0xf0)
+			loop = false;
+		else if (!processControlEvent(chan, cmd))
+			return;
+	}
+
+	uint8 para = *chan->dataPtr++;
+
+	if (cmd == 0x80) {
+		keyOff(chan);
+		chan->protect = false;
+	} else {
+		keyOn(chan);
+
+		if (chan->protect == false || cmd != chan->frqBlockMSB)
+			chan->flags |= CHS_RECALCFREQ;
+	
+		chan->protect = (para & 0x80) ? true : false;
+		chan->frqBlockMSB = cmd;
+	}
+
+	chan->ticksLeft = para & 0x7f;
+}
+
+void TownsPC98_OpnDriver::processFrequency(TwnChannel *chan) {
+	if (chan->flags & CHS_RECALCFREQ) {
+		uint8 block = (chan->frqBlockMSB & 0x70) >> 1;
+		uint16 bfreq = ((uint16*)_twnFreqTable)[chan->frqBlockMSB & 0x0f];
+		chan->frequency = (bfreq + chan->frqLSB) | (block << 8);
+
+		writeReg(chan, (chan->regOffset + 0xa4), (chan->frequency >> 8));
+		writeReg(chan, (chan->regOffset + 0xa0), (chan->frequency & 0xff));
+
+		chan->ptchWhlCurDelay = chan->ptchWhlInitDelayHi;
+		if (chan->flags & CHS_KEYOFF) {
+			chan->ptchWhlModCurVal = chan->ptchWhlModInitVal;
+			chan->ptchWhlCurDelay += chan->ptchWhlInitDelayLo;
+		}
+
+		chan->ptchWhlDurLeft = (chan->ptchWhlDuration >> 1);
+		chan->flags &= ~(CHS_KEYOFF | CHS_RECALCFREQ);
+	}
+
+	if (!(chan->flags & CHS_PITCHWHEELOFF)) {
+		if (--chan->ptchWhlCurDelay)
+			return;
+		chan->ptchWhlCurDelay = chan->ptchWhlInitDelayHi;
+		chan->frequency += chan->ptchWhlModCurVal;
+
+		writeReg(chan, (chan->regOffset + 0xa4), (chan->frequency >> 8));
+		writeReg(chan, (chan->regOffset + 0xa0), (chan->frequency & 0xff));
+
+		if(!--chan->ptchWhlDurLeft) {
+			chan->ptchWhlDurLeft = chan->ptchWhlDuration;
+			chan->ptchWhlModCurVal = -chan->ptchWhlModCurVal;
+		}
+	}
+}
+
+bool TownsPC98_OpnDriver::processControlEvent(TwnChannel *chan, uint8 cmd) {
+	#define Control(x)	&TownsPC98_OpnDriver::control_##x
+	static const ControlEventFunc twnFxCommands[] = {
+		Control(f0_setPatch),
+		Control(f1_presetOutputLevel),
+		Control(f2_setKeyOffTime),
+		Control(f3_setFreqLSB),
+		Control(f4_setOutputLevel),
+		Control(f5_setTempo),
+		Control(f6_repeatSection),
+		Control(f7_setupPitchWheel),
+		Control(f8_togglePitchWheel),
+		Control(f9_unk),
+		Control(fa_writeReg),
+		Control(fb_incOutLevel),
+		Control(fc_decOutLevel),
+		Control(fd_jump),
+		Control(fe_unk),
+		Control(ff_endOfTrack)
+	};
+	#undef Control
+
+	uint8 para = *chan->dataPtr++;
+	return (this->*twnFxCommands[cmd & 0x0f])(chan, para);
+}
+
+void TownsPC98_OpnDriver::setOutputLevel(TwnChannel *chan) {
+	uint8 outopr = _twnCarrier[chan->algorithm];
+	uint8 reg = 0x40 + chan->regOffset;
+
+	for (int i = 0; i < 4; i++) {
+		if (outopr & 1)
+			writeReg(chan, reg, chan->totalLevel);
+		outopr >>= 1;
+		reg += 4;
+	}
+}
+
+void TownsPC98_OpnDriver::setTempo(uint8 tempo) {
+	_tempo = tempo;
+	_samplesPerCallback = getRate() / _tempo;
+	_samplesPerCallbackRemainder = getRate() % _tempo;
+}
+
+bool TownsPC98_OpnDriver::control_f0_setPatch(TwnChannel *chan, uint8 para) {
+	chan->instrID = para;
+	uint8 reg = chan->regOffset + 0x80;
+
+	for (int i = 0; i < 4; i++) {
+		// set release rate for each operator
+		writeReg(chan, reg, 0x0f);
+		reg += 4;
+	}
+
+	const uint8 *tptr = (uint8*) _patches + ((uint32)chan->instrID << 5);
+	reg = chan->regOffset + 0x30;
+
+	// write registers 0x30 to 0x8f
+	for (int i = 0; i < 6; i++) {
+		writeReg(chan, reg, tptr[0]);
+		reg += 4;
+		writeReg(chan, reg, tptr[2]);
+		reg += 4;
+		writeReg(chan, reg, tptr[1]);
+		reg += 4;
+		writeReg(chan, reg, tptr[3]);
+		reg += 4;
+		tptr += 4;
+	}
+
+	reg = chan->regOffset + 0xB0;
+	chan->algorithm = tptr[0] & 7;
+	// set feedback and algorithm
+	writeReg(chan, reg, tptr[0]);
+
+	setOutputLevel(chan);
+	return true;
+}
+
+bool TownsPC98_OpnDriver::control_f1_presetOutputLevel(TwnChannel *chan, uint8 para) {
+	if (_fading)
+		return true;
+
+	chan->totalLevel = _twnLvlPresets[para];
+	setOutputLevel(chan);
+	return true;
+}
+
+bool TownsPC98_OpnDriver::control_f2_setKeyOffTime(TwnChannel *chan, uint8 para) {
+	chan->keyOffTime = para;
+	return true;
+}
+
+bool TownsPC98_OpnDriver::control_f3_setFreqLSB(TwnChannel *chan, uint8 para) {
+	chan->frqLSB = (int8) para;
+	return true;
+}
+
+bool TownsPC98_OpnDriver::control_f4_setOutputLevel(TwnChannel *chan, uint8 para) {
+	if (_fading)
+		return true;
+
+	chan->totalLevel = para;
+	setOutputLevel(chan);
+	return true;
+}
+
+bool TownsPC98_OpnDriver::control_f5_setTempo(TwnChannel *chan, uint8 para) {
+	setTempo(para);
+	return true;
+}
+
+bool TownsPC98_OpnDriver::control_f6_repeatSection(TwnChannel *chan, uint8 para) {
+	chan->dataPtr--;
+	chan->dataPtr[0]--;
+
+	if (*chan->dataPtr) {
+		// repeat section until counter has reached zero
+		chan->dataPtr = (uint8*) _trackData + READ_LE_UINT16(chan->dataPtr + 2);
+	} else {
+		// reset counter, advance to next section
+		chan->dataPtr[0] = chan->dataPtr[1];
+		chan->dataPtr += 4;
+	}
+	return true;
+}
+
+bool TownsPC98_OpnDriver::control_f7_setupPitchWheel(TwnChannel *chan, uint8 para) {
+	chan->ptchWhlInitDelayLo = chan->dataPtr[0];
+	chan->ptchWhlInitDelayHi = para;
+	chan->ptchWhlModInitVal = (int16) READ_LE_UINT16(chan->dataPtr + 1);
+	chan->ptchWhlDuration = chan->dataPtr[3];
+	chan->dataPtr += 4;
+	chan->flags = (chan->flags & ~CHS_PITCHWHEELOFF) | CHS_KEYOFF | CHS_RECALCFREQ;
+	return true;
+}
+
+bool TownsPC98_OpnDriver::control_f8_togglePitchWheel(TwnChannel *chan, uint8 para) {
+	if (para == 0x10) {
+		if (*chan->dataPtr++) {
+			chan->flags = (chan->flags & ~CHS_PITCHWHEELOFF) | CHS_KEYOFF;
+		} else {
+			chan->flags |= CHS_PITCHWHEELOFF;
+		}
+	} else {
+		uint8 skipChannels = para / 36;
+		uint8 entry = para % 36;
+		TownsPC98_OpnDriver::TwnChannel *t = &chan[skipChannels];
+		////// NOT IMPLEMENTED
+		//t->unnamedEntries[entry] = *chan->dataPtr++;
+	}
+	return true;
+}
+
+bool TownsPC98_OpnDriver::control_f9_unk(TwnChannel *chan, uint8 para) {
+	//chan->dataPtr += 5;
+	return true;
+}
+
+bool TownsPC98_OpnDriver::control_fa_writeReg(TwnChannel *chan, uint8 para) {
+	writeReg(chan, para, *chan->dataPtr++);
+	return true;
+}
+
+bool TownsPC98_OpnDriver::control_fb_incOutLevel(TwnChannel *chan, uint8 para) {
+	chan->dataPtr--;
+	if (_fading)
+		return true;
+
+	uint8 val = (chan->totalLevel + 3);
+	if (val > 0x7f)
+		val = 0x7f;
+
+	chan->totalLevel = val;
+	setOutputLevel(chan);
+	return true;
+}
+
+bool TownsPC98_OpnDriver::control_fc_decOutLevel(TwnChannel *chan, uint8 para) {
+	chan->dataPtr--;
+	if (_fading)
+		return true;
+
+	int8 val = (int8) (chan->totalLevel - 3);
+	if (val < 0)
+		val = 0;
+
+	chan->totalLevel = (uint8) val;
+	setOutputLevel(chan);
+	return true;
+}
+
+bool TownsPC98_OpnDriver::control_fd_jump(TwnChannel *chan, uint8 para) {
+	uint8 *tmp = (uint8*) _trackData + READ_LE_UINT16(chan->dataPtr - 1);
+	chan->dataPtr = (tmp[1] == 1) ? tmp : ++chan->dataPtr;
+	return true;
+}
+
+bool TownsPC98_OpnDriver::control_fe_unk(TwnChannel *chan, uint8 para) {
+	chan->dataPtr--;
+	return true;
+}
+
+bool TownsPC98_OpnDriver::control_ff_endOfTrack(TwnChannel *chan, uint8 para) {
+	uint16 val = READ_LE_UINT16(--chan->dataPtr);
+	if (val) {
+		// loop
+		chan->dataPtr = (uint8 *) _trackData + val;
+		return true;
+	} else {
+		// quit parsing for active channel
+		--chan->dataPtr;
+		chan->flags |= CHS_EOT;
+		_finishedChannelsFlag |= chan->idFlag;
+		keyOff(chan);
+		return false;
+	}
+}
+
+void TownsPC98_OpnDriver::keyOff(TwnChannel *chan) {
+	// all operators off
+	uint8 value = chan->keyNum & 0x0f;
+	uint8 regAdress = 0x28;
+	writeReg(chan, regAdress, value);
+	chan->flags |= CHS_KEYOFF;
+}
+
+void TownsPC98_OpnDriver::keyOn(TwnChannel *chan) {
+	// all operators on
+	uint8 value = chan->keyNum | 0xf0;
+	uint8 regAdress = 0x28;
+	writeReg(chan, regAdress, value);
+}
+
+void TownsPC98_OpnDriver::writeReg(TwnChannel *chan, uint8 regAdress, uint8 value) {
+	uint8 h = regAdress & 0xf0;
+	uint8 l = (regAdress & 0x0f);
+	static const uint8 opr[] = { 0, 2, 1, 3 };
+	uint8 o = opr[(l - chan->regOffset) >> 2];
+	
+	switch (h) {
+		case 0x00:
+			// ssg
+			warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress);
+			break;
+		case 0x10:
+			// adpcm
+			warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress);
+			break;
+		case 0x20:
+			if (l == 8) {
+				// Key on/off
+				for (int i = 0; i < 4; i++) {
+					if ((value >> (4 + i)) & 1)
+						chan->opr[i]->keyOn();
+					else
+						chan->opr[i]->keyOff();
+				}
+			} else if (l == 2) {
+				// LFO
+				warning("TownsPC98_OpnDriver: TRYING TO USE LFO (NOT SUPPORTED)");
+			} else if (l == 7) {
+				// Timers; Ch 3/6 special mode
+				warning("TownsPC98_OpnDriver: TRYING TO USE CH 3/6 SPECIAL MODE (NOT SUPPORTED)");
+			} else if (l == 4 || l == 5) {
+				// Timer A
+				warning("TownsPC98_OpnDriver: TRYING TO USE TIMER_A (NOT SUPPORTED)");
+			} else if (l == 6) {
+				// Timer B
+				warning("TownsPC98_OpnDriver: TRYING TO USE TIMER_B (NOT SUPPORTED)");
+			} else if (l == 10 || l == 11) {
+				// DAC
+				warning("TownsPC98_OpnDriver: TRYING TO USE DAC (NOT SUPPORTED)");
+			}
+			break;
+
+		case 0x30:
+			// detune, multiple
+			chan->opr[o]->detune((value >> 4) & 7);
+			chan->opr[o]->multiple(value & 0x0f);
+			chan->updateEnvelopes = true;
+			break;
+
+		case 0x40:
+			// total level
+			chan->opr[o]->totalLevel(value & 0x7f);
+			break;
+
+		case 0x50:
+			// rate scaling, attack rate
+			chan->opr[o]->attackRate(value & 0x1f);
+			if (chan->opr[o]->scaleRate(value >> 6))
+				chan->updateEnvelopes = true;
+			break;
+
+		case 0x60:
+			// first decay rate, amplitude modulation
+			chan->opr[o]->decayRate(value & 0x1f);
+			if (value & 0x80)
+				warning("TownsPC98_OpnDriver: TRYING TO USE AMP MODULATION (NOT SUPPORTED)");
+
+			break;
+
+		case 0x70:
+			// secondary decay rate
+			chan->opr[o]->sustainRate(value & 0x1f);
+			break;
+
+		case 0x80:
+			// secondary amplitude, release rate;
+			chan->opr[o]->sustainLevel(value >> 4);
+			chan->opr[o]->releaseRate(value & 0x0f);
+			break;
+
+		case 0x90:
+			// ssg
+			warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress);
+			break;
+
+		case 0xa0:
+			// frequency
+			l -= chan->regOffset;
+			if (l == 0) {
+				chan->frqTemp = (chan->frqTemp & 0xff00) | value;
+				chan->updateEnvelopes = true;
+				for (int i = 0; i < 4; i++)
+					chan->opr[i]->frequency(chan->frqTemp);
+			} else if (l == 4) {
+				chan->frqTemp = (chan->frqTemp & 0xff) | (value << 8);
+			} else if (l == 8) {
+				// Ch 3/6 special mode frq
+				warning("TownsPC98_OpnDriver: TRYING TO USE CH 3/6 SPECIAL MODE FREQ (NOT SUPPORTED)");
+			} else if (l == 12) {
+				// Ch 3/6 special mode frq
+				warning("TownsPC98_OpnDriver: TRYING TO USE CH 3/6 SPECIAL MODE FREQ (NOT SUPPORTED)");
+			}
+			break;
+
+		case 0xb0:
+			l -= chan->regOffset;
+			if (l == 0) {
+				// feedback, algorithm
+				chan->opr[0]->feedbackLevel((value >> 3) & 7);
+				chan->opr[1]->feedbackLevel(0);
+				chan->opr[2]->feedbackLevel(0);
+				chan->opr[3]->feedbackLevel(0);
+			} else if (l == 4) {
+				// stereo, LFO sensitivity
+				chan->enableLeft = value & 0x80 ? true : false;
+				chan->enableRight = value & 0x40 ? true : false;
+				uint8 ams = (value & 0x3F) >> 3;
+				if (ams)
+					warning("TownsPC98_OpnDriver: TRYING TO USE AMP MODULATION SENSITIVITY (NOT SUPPORTED)");
+				uint8 fms = value & 3;
+				if (fms)
+					warning("TownsPC98_OpnDriver: TRYING TO USE FREQ MODULATION SENSITIVITY (NOT SUPPORTED)");
+			}
+			break;
+
+		default:
+			warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress);
+			break;
+	}
+}
+
+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) {
 
-	_driver = new SoundTowns_EuphonyDriver(_mixer);
+	_driver = new Towns_EuphonyDriver(_mixer);
 	int ret = open();
 	if (ret != MERR_ALREADY_OPEN && ret != 0)
 		error("couldn't open midi driver");
@@ -1304,7 +2636,7 @@
 	Common::StackLock lock(_mutex);
 
 	if (!_parser) {
-		_parser = new MidiParser_EuD(_driver->queue());
+		_parser = new Towns_EuphonyParser(_driver->queue());
 		_parser->setMidiDriver(this);
 		_parser->setTimerRate(getBaseTempo());
 	}
@@ -1356,22 +2688,73 @@
 	return (float) sampleRate * 10.0f * rateshift / outputRate;
 }
 
+SoundPC98::SoundPC98(KyraEngine_v1 *vm, Audio::Mixer *mixer) :
+	Sound(vm, mixer), _musicTrackData(0), _sfxTrackData(0), _lastTrack(-1), _driver(0) {
+}
+
+SoundPC98::~SoundPC98() {
+	delete[] _musicTrackData;
+	delete[] _sfxTrackData;
+	delete _driver;
+}
+
+bool SoundPC98::init() {
+	_driver = new TownsPC98_OpnDriver(_mixer, TownsPC98_OpnDriver::OD_TYPE26);
+	_sfxTrackData = _vm->resource()->fileData("se.dat", 0);
+	if (!_sfxTrackData)
+		return false;
+	return _driver->init();
+}
+
+void SoundPC98::playTrack(uint8 track) {
+	if (track == _lastTrack && _musicEnabled)
+		return;
+
+	haltTrack();
+
+	char musicfile[13];
+	sprintf(musicfile, fileListEntry(0), track);
+	delete[] _musicTrackData;
+	// This is just for testing purposes atm since we haven't found a way
+	// to determine the correct file yet
+	_musicTrackData = _vm->resource()->fileData("kyram40.dat"/*musicfile*/, 0);
+	if (_musicEnabled)
+		_driver->loadData(_musicTrackData);
+
+	_lastTrack = track;
+}
+
+void SoundPC98::haltTrack() {
+	_lastTrack = -1;
+	AudioCD.stop();
+	AudioCD.updateCD();
+	_driver->reset();
+}
+
+void SoundPC98::beginFadeOut() {
+	_driver->fadeOut();
+	haltTrack();
+}
+
+void SoundPC98::playSoundEffect(uint8) {
+	/// TODO ///
+}
+
+
 //	KYRA 2
 
-SoundTowns_v2::SoundTowns_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer)
-	: Sound(vm, mixer), _lastTrack(-1), _currentSFX(0), /*_driver(0),*/
-	 _twnTrackData(0) {
+SoundTownsPC98_v2::SoundTownsPC98_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer) :
+	Sound(vm, mixer), _currentSFX(0), _musicTrackData(0), _lastTrack(-1), _driver(0) {
 }
 
-SoundTowns_v2::~SoundTowns_v2() {
-	/*if (_driver)
-		delete _driver;*/
-	if (_twnTrackData)
-		delete[] _twnTrackData;
+SoundTownsPC98_v2::~SoundTownsPC98_v2() {
+	delete[] _musicTrackData;
+	delete _driver;
 }
 
-bool SoundTowns_v2::init() {
-	//_driver = new SoundTowns_v2_TwnDriver(_mixer);
+bool SoundTownsPC98_v2::init() {
+	_driver = new TownsPC98_OpnDriver(_mixer, _vm->gameFlags().platform == Common::kPlatformPC98 ?
+		TownsPC98_OpnDriver::OD_TYPE86 : TownsPC98_OpnDriver::OD_TOWNS);
 	_vm->checkCD();
 	// FIXME: While checking for 'track1.XXX(X)' looks like
 	// a good idea, we should definitely not be doing this
@@ -1384,55 +2767,60 @@
 		(Common::File::exists("track1.mp3") || Common::File::exists("track1.ogg") ||
 		 Common::File::exists("track1.flac") || Common::File::exists("track1.fla")))
 			_musicEnabled = 2;
-	return true;//_driver->init();
+	return _driver->init();
 }
 
-void SoundTowns_v2::process() {
+void SoundTownsPC98_v2::process() {
 	AudioCD.updateCD();
 }
 
-void SoundTowns_v2::playTrack(uint8 track) {
+void SoundTownsPC98_v2::playTrack(uint8 track) {
 	if (track == _lastTrack && _musicEnabled)
 		return;
 
 	const uint16 * const cdaTracks = (const uint16 * const) cdaData();
 
 	int trackNum = -1;
-	for (int i = 0; i < cdaTrackNum(); i++) {
-		if (track == (uint8) READ_LE_UINT16(&cdaTracks[i * 2])) {
-			trackNum = (int) READ_LE_UINT16(&cdaTracks[i * 2 + 1]) - 1;
-			break;
+	if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
+		for (int i = 0; i < cdaTrackNum(); i++) {
+			if (track == (uint8) READ_LE_UINT16(&cdaTracks[i * 2])) {
+				trackNum = (int) READ_LE_UINT16(&cdaTracks[i * 2 + 1]) - 1;
+				break;
+			}
 		}
 	}
 
 	haltTrack();
 
-	// TODO: figure out when to loop and when not for CD Audio
-	bool loop = false;
+	char musicfile[13];
+	sprintf(musicfile, fileListEntry(0), track);
+	delete[] _musicTrackData;
+	_musicTrackData = _vm->resource()->fileData(musicfile, 0);
+	_driver->loadData(_musicTrackData, true);
 
 	if (_musicEnabled == 2 && trackNum != -1) {
-		AudioCD.play(trackNum+1, loop ? -1 : 1, 0, 0);
+		AudioCD.play(trackNum+1, _driver->looping() ? -1 : 1, 0, 0);
 		AudioCD.updateCD();
 	} else if (_musicEnabled) {
-		char musicfile[13];
-		sprintf(musicfile, fileListEntry(0), track);
-		if (_twnTrackData)
-			delete[] _twnTrackData;
-		_twnTrackData = _vm->resource()->fileData(musicfile, 0);
-		//_driver->loadData(_twnTrackData);
+		_driver->cont();
 	}
 
 	_lastTrack = track;
 }
 
-void SoundTowns_v2::haltTrack() {
+void SoundTownsPC98_v2::haltTrack() {
 	_lastTrack = -1;
 	AudioCD.stop();
 	AudioCD.updateCD();
-	//_driver->reset();
+	_driver->reset();
 }
 
-int32 SoundTowns_v2::voicePlay(const char *file, bool) {
+void SoundTownsPC98_v2::beginFadeOut() {
+	_driver->fadeOut();
+	haltTrack();
+}
+
+int32 SoundTownsPC98_v2::voicePlay(const char *file, bool) {
 	static const uint16 rates[] =	{ 0x10E1, 0x0CA9, 0x0870, 0x0654, 0x0438, 0x032A, 0x021C, 0x0194 };
 
 	int h = 0;
@@ -1443,7 +2831,7 @@
 			return 0;
 	}
 
-	char filename [13];
+	char filename[13];
 	sprintf(filename, "%s.PCM", file);
 
 	uint8 * data = _vm->resource()->fileData(filename, 0);
@@ -1500,11 +2888,6 @@
 	return 1;
 }
 
-void SoundTowns_v2::beginFadeOut() {
-	//_driver->fadeOut();
-	haltTrack();
-}
-
 } // end of namespace Kyra
 
 #undef EUPHONY_FADEOUT_TICKS

Modified: scummvm/trunk/engines/kyra/staticres.cpp
===================================================================
--- scummvm/trunk/engines/kyra/staticres.cpp	2008-06-26 18:45:46 UTC (rev 32806)
+++ scummvm/trunk/engines/kyra/staticres.cpp	2008-06-26 19:42:59 UTC (rev 32807)
@@ -1034,6 +1034,9 @@
 	}
 
 	// audio data tables
+	static const char *tIntro98[] = { "intro%d.dat" };
+	static const char *tIngame98[] = { "kyram%d.dat" };
+
 	static const AudioDataStruct soundData_PC[] = {
 		{ _soundFilesIntro, _soundFilesIntroSize, 0, 0 },
 		{ _soundFiles, _soundFilesSize, 0, 0 },
@@ -1045,7 +1048,20 @@
 		{ _soundFiles, _soundFilesSize, _cdaTrackTable, _cdaTrackTableSize },
 		{ 0, 0, 0, 0}
 	};
-	_soundData = (_flags.platform == Common::kPlatformPC) ? soundData_PC : soundData_TOWNS;
+
+	static const AudioDataStruct soundData_PC98[] = {
+		{ tIntro98, 1, 0, 0 },
+		{ tIngame98, 1, 0, 0 },
+		{ 0, 0, 0, 0}
+	};
+
+	if (_flags.platform == Common::kPlatformPC)
+		_soundData = soundData_PC;
+	else if (_flags.platform == Common::kPlatformFMTowns)
+		_soundData = soundData_TOWNS;
+	else if (_flags.platform == Common::kPlatformPC98)
+		_soundData = soundData_PC98;
+
 }
 
 void KyraEngine_LoK::loadMouseShapes() {
@@ -1243,6 +1259,10 @@
 	static const char *fmtMusicFileListFinale[] = { "finale%d.twn" };
 	static const char *fmtMusicFileListIngame[] = { "km%02d.twn" };
 
+	static const char *pc98MusicFileListIntro[] = { "intro%d.86" };
+	static const char *pc98MusicFileListFinale[] = { "finale%d.86" };
+	static const char *pc98MusicFileListIngame[] = { "km%02d.86" };
+
 	static const AudioDataStruct soundData_PC[] = {
 		{ _musicFileListIntro, _musicFileListIntroSize, 0, 0 },
 		{ _musicFileListIngame, _musicFileListIngameSize, 0, 0},
@@ -1254,8 +1274,20 @@
 		{ fmtMusicFileListIngame, 1, _cdaTrackTableIngame, _cdaTrackTableIngameSize >> 1 },
 		{ fmtMusicFileListFinale, 1, _cdaTrackTableFinale, _cdaTrackTableFinaleSize >> 1 }
 	};
-	_soundData = (_flags.platform == Common::kPlatformPC) ? soundData_PC : soundData_TOWNS;
 
+	static const AudioDataStruct soundData_PC98[] = {
+		{ pc98MusicFileListIntro, 1, 0, 0 },
+		{ pc98MusicFileListIngame, 1, 0, 0 },
+		{ pc98MusicFileListFinale, 1, 0, 0 }		
+	};
+
+	if (_flags.platform == Common::kPlatformPC)
+		_soundData = soundData_PC;
+	else if (_flags.platform == Common::kPlatformFMTowns)
+		_soundData = soundData_TOWNS;
+	else if (_flags.platform == Common::kPlatformPC98)
+		_soundData = soundData_PC98;
+
 	// setup sequence data
 	_sequences = _staticres->loadHofSequenceData(k2SeqplaySeqData, tmpSize);
 


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