[Scummvm-cvs-logs] SF.net SVN: scummvm: [26285] scummvm/trunk

lordhoto at users.sourceforge.net lordhoto at users.sourceforge.net
Sat Mar 24 01:04:09 CET 2007


Revision: 26285
          http://scummvm.svn.sourceforge.net/scummvm/?rev=26285&view=rev
Author:   lordhoto
Date:     2007-03-23 17:04:08 -0700 (Fri, 23 Mar 2007)

Log Message:
-----------
Commit of patch #1686414 ("Kyra: FM-Towns music").

Modified Paths:
--------------
    scummvm/trunk/engines/kyra/gui.cpp
    scummvm/trunk/engines/kyra/kyra.h
    scummvm/trunk/engines/kyra/resource.cpp
    scummvm/trunk/engines/kyra/sound.cpp
    scummvm/trunk/engines/kyra/sound.h
    scummvm/trunk/engines/kyra/sound_towns.cpp
    scummvm/trunk/sound/softsynth/ym2612.cpp

Added Paths:
-----------
    scummvm/trunk/sound/softsynth/ym2612.h

Modified: scummvm/trunk/engines/kyra/gui.cpp
===================================================================
--- scummvm/trunk/engines/kyra/gui.cpp	2007-03-23 23:38:45 UTC (rev 26284)
+++ scummvm/trunk/engines/kyra/gui.cpp	2007-03-24 00:04:08 UTC (rev 26285)
@@ -38,6 +38,7 @@
 	// Most settings already have sensible defaults. This one, however, is
 	// specific to the Kyra engine.
 	ConfMan.registerDefault("walkspeed", 2);
+	ConfMan.registerDefault("cdaudio", _flags.platform == Common::kPlatformFMTowns);
 }
 
 void KyraEngine::readSettings() {
@@ -55,7 +56,7 @@
 		_configTextspeed = 2;	// Fast
 
 	_configWalkspeed = ConfMan.getInt("walkspeed");
-	_configMusic = ConfMan.getBool("music_mute") ? 0 : 1;
+	_configMusic = ConfMan.getBool("music_mute") ? 0 : ((ConfMan.getBool("cdaudio") && _flags.platform == Common::kPlatformFMTowns) ? 2 : 1);	
 	_configSounds = ConfMan.getBool("sfx_mute") ? 0 : 1;
 
 	_sound->enableMusic(_configMusic);
@@ -96,6 +97,7 @@
 	ConfMan.setInt("talkspeed", talkspeed);
 	ConfMan.setInt("walkspeed", _configWalkspeed);
 	ConfMan.setBool("music_mute", _configMusic == 0);
+	ConfMan.setBool("cdaudio", _configMusic == 2);
 	ConfMan.setBool("sfx_mute", _configSounds == 0);
 
 	switch (_configVoice) {
@@ -563,6 +565,7 @@
 	_textSpeedString = _guiStrings[25 + offsetOptions];
 	_onString =  _guiStrings[20 + offsetOn];
 	_offString =  _guiStrings[21 + offset];
+	_onCDString = _guiStrings[21];
 }
 
 int KyraEngine::buttonMenuCallback(Button *caller) {
@@ -1214,10 +1217,17 @@
 void KyraEngine::gui_setupControls(Menu &menu) {
 	debugC(9, kDebugLevelGUI, "KyraEngine::gui_setupControls()");
 
-	if (_configMusic)
-		menu.item[0].itemString = _onString; //"On"
-	else
-		menu.item[0].itemString = _offString; //"Off"
+	switch (_configMusic) {
+		case 0:
+			menu.item[0].itemString = _offString; //"Off"
+			break;
+		case 1:
+			menu.item[0].itemString = _onString; //"On"
+			break;
+		case 2:
+			menu.item[0].itemString = _onCDString; //"On + CD"
+			break;
+	}
 
 	if (_configSounds)
 		menu.item[1].itemString = _onString; //"On"
@@ -1299,7 +1309,7 @@
 	debugC(9, kDebugLevelGUI, "KyraEngine::gui_controlsChangeMusic()");
 	processMenuButton(button);
 
-	_configMusic = !_configMusic;
+	_configMusic = ++_configMusic % (_flags.platform == Common::kPlatformFMTowns ? 3 : 2);
 	gui_setupControls(_menu[5]);
 	return 0;
 }

Modified: scummvm/trunk/engines/kyra/kyra.h
===================================================================
--- scummvm/trunk/engines/kyra/kyra.h	2007-03-23 23:38:45 UTC (rev 26284)
+++ scummvm/trunk/engines/kyra/kyra.h	2007-03-24 00:04:08 UTC (rev 26285)
@@ -790,7 +790,7 @@
 
 	uint8 _configTextspeed;
 	uint8 _configWalkspeed;
-	bool _configMusic;
+	int _configMusic;
 	bool _configSounds;
 	uint8 _configVoice;
 
@@ -891,6 +891,7 @@
 	const char *_textSpeedString;
 	const char *_onString;
 	const char *_offString;
+	const char *_onCDString;
 		
 	int _itemList_Size;
 	int _takenList_Size;

Modified: scummvm/trunk/engines/kyra/resource.cpp
===================================================================
--- scummvm/trunk/engines/kyra/resource.cpp	2007-03-23 23:38:45 UTC (rev 26284)
+++ scummvm/trunk/engines/kyra/resource.cpp	2007-03-24 00:04:08 UTC (rev 26285)
@@ -88,6 +88,11 @@
 		for (FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
 			Common::String filename = file->name();
 			filename.toUppercase();
+
+			// No real PAK file!
+			if (filename == "TWMUSIC.PAK")
+				continue;
+
 			if (filename.hasSuffix("PAK") || filename.hasSuffix("APK")) {
 				if (!loadPakFile(file->name())) {
 					error("couldn't open pakfile '%s'", file->name().c_str());

Modified: scummvm/trunk/engines/kyra/sound.cpp
===================================================================
--- scummvm/trunk/engines/kyra/sound.cpp	2007-03-23 23:38:45 UTC (rev 26284)
+++ scummvm/trunk/engines/kyra/sound.cpp	2007-03-24 00:04:08 UTC (rev 26285)
@@ -37,7 +37,7 @@
 
 Sound::Sound(KyraEngine *engine, Audio::Mixer *mixer)
 	: _engine(engine), _mixer(mixer), _currentVocFile(0), _vocHandle(), _compressHandle(),
-	_musicEnabled(true), _sfxEnabled(true), _soundFileList(0), _soundFileListSize(0) {
+	_musicEnabled(1), _sfxEnabled(true), _soundFileList(0), _soundFileListSize(0) {
 }
 
 Sound::~Sound() {
@@ -450,14 +450,9 @@
 
 void KyraEngine::snd_playSoundEffect(int track) {
 	debugC(9, kDebugLevelMain | kDebugLevelSound, "KyraEngine::snd_playSoundEffect(%d)", track);
-	if (_flags.platform == Common::kPlatformFMTowns) {
-		if (track == 49) {
-			snd_playWanderScoreViaMap(56, 1);
-			return;
-		} else if (track == 0 || track == 1 || track == 10) {
-			// I don't know what's supposed to happen here, but calling playSoundEffect will lead to crash
-			return;
-		}
+	if (_flags.platform == Common::kPlatformFMTowns && track == 49) {
+		snd_playWanderScoreViaMap(56, 1);
+		return;
 	}
 	_sound->playSoundEffect(track);
 }

Modified: scummvm/trunk/engines/kyra/sound.h
===================================================================
--- scummvm/trunk/engines/kyra/sound.h	2007-03-23 23:38:45 UTC (rev 26284)
+++ scummvm/trunk/engines/kyra/sound.h	2007-03-24 00:04:08 UTC (rev 26285)
@@ -49,9 +49,9 @@
 #include "common/file.h"
 #include "common/mutex.h"
 
-#include "sound/mididrv.h"
 #include "sound/midiparser.h"
 #include "sound/mixer.h"
+#include "sound/softsynth/ym2612.h"
 
 #include "kyra/kyra.h"
 
@@ -84,8 +84,8 @@
 
 	virtual void beginFadeOut() = 0;
 
-	void enableMusic(bool enable) { _musicEnabled = enable; }
-	bool musicEnabled() const { return _musicEnabled; }
+	void enableMusic(int enable) { _musicEnabled = enable; }
+	int musicEnabled() const { return _musicEnabled; }
 	void enableSFX(bool enable) { _sfxEnabled = enable; }
 	bool sfxEnabled() const { return _sfxEnabled; }
 
@@ -93,10 +93,10 @@
 	void voiceUnload() {}
 	bool voiceIsPlaying();
 	void voiceStop();
-	
+
 protected:
 	const char *soundFilename(uint file) { return (file < _soundFileListSize) ? _soundFileList[file] : ""; }
-	bool _musicEnabled;
+	int _musicEnabled;
 	bool _sfxEnabled;
 
 	KyraEngine *_engine;
@@ -129,14 +129,14 @@
 
 	void setVolume(int volume);
 	int getVolume();
-	
+
 	void loadSoundFile(uint file);
-	
+
 	void playTrack(uint8 track);
 	void haltTrack();
-	
+
 	void playSoundEffect(uint8 track);
-	
+
 	void beginFadeOut();
 private:
 	void play(uint8 track);
@@ -231,7 +231,8 @@
 	Common::Mutex _mutex;
 };
 
-class SoundTowns : public Sound {
+class FMT_EuphonyDriver;
+class SoundTowns : public MidiDriver, public Sound {
 public:
 	SoundTowns(KyraEngine *engine, Audio::Mixer *mixer);
 	~SoundTowns();
@@ -249,23 +250,47 @@
 
 	void playSoundEffect(uint8);
 
-	void beginFadeOut() { /* TODO */ }
+	void beginFadeOut();
+
+	//MidiDriver interface implementation
+	int open();
+	void close();
+	void send(uint32 b);
+	void metaEvent(byte type, byte *data, uint16 length) {}
+
+	void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { }
+	uint32 getBaseTempo(void);
+
+	//Channel allocation functions
+	MidiChannel *allocateChannel()		{ return 0; }
+	MidiChannel *getPercussionChannel()	{ return 0; }
+
+	static float semitoneAndSampleRate_to_sampleStep(int8 semiTone, int8 semiToneRootkey,
+		uint32 sampleRate, uint32 outputRate, int32 pitchWheel);
 private:
-	void stopSoundEffect();
-	void setPitch(uint8 *&data, uint32 &size, int8 sourcePitch, int8 targetPitch);
+	bool loadInstruments();
+	void playEuphonyTrack(uint32 offset, int loop);
 
+	static void onTimer(void *data);
+
 	int _lastTrack;
 	Audio::AudioStream *_currentSFX;
 	Audio::SoundHandle _sfxHandle;
+
 	int _currentTrackTable;
-	bool _sfxIsPlaying;
 	uint _sfxFileIndex;
 	uint8 *_sfxFileData;
 
+	FMT_EuphonyDriver * _driver;
+	MidiParser * _parser;
+	uint8 *_musicTrackData;
+
+	Common::Mutex _mutex;
+
 	static const char *_sfxFiles[];
 	static const int _sfxFilenum;
 	static const uint8 _sfxBTTable[256];
-	const uint8 *_sfxWDTable;	
+	const uint8 *_sfxWDTable;
 };
 
 class MixedSoundDriver : public Sound {
@@ -300,7 +325,7 @@
 	~SoundDigital();
 
 	bool init();
-	
+
 	int playSound(Common::File *fileHandle, bool loop = false, bool fadeIn = false, int channel = -1);
 	bool isPlaying(int channel);
 	void stopSound(int channel);

Modified: scummvm/trunk/engines/kyra/sound_towns.cpp
===================================================================
--- scummvm/trunk/engines/kyra/sound_towns.cpp	2007-03-23 23:38:45 UTC (rev 26284)
+++ scummvm/trunk/engines/kyra/sound_towns.cpp	2007-03-24 00:04:08 UTC (rev 26285)
@@ -29,23 +29,859 @@
 #include "sound/audiocd.h"
 #include "sound/audiostream.h"
 
+#include "common/util.h"
+#include <math.h>
+
 namespace Kyra {
 
+enum EuD_ChannelState { _s_ready, _s_attacking, _s_decaying, _s_sustaining, _s_releasing };
+
+class MidiChannel_EuD : public MidiChannel {
+public:
+	MidiChannel_EuD() {}
+	~MidiChannel_EuD() {}
+
+	virtual void nextTick(int32 *outbuf, int buflen) = 0;
+	virtual void rate(uint16 r) = 0;
+
+protected:
+	uint16 _rate;
+};
+
+class MidiChannel_EuD_FM : public MidiChannel_EuD {
+public:
+	MidiChannel_EuD_FM();
+	virtual ~MidiChannel_EuD_FM();
+
+	void nextTick(int32 *outbuf, int buflen);
+	void rate(uint16 r);
+
+	// MidiChannel interface
+	MidiDriver *device() { return 0; }
+	byte getNumber() { return 0; }
+	void release() { }
+	void send(uint32 b) { }
+	void noteOff(byte note);
+	void noteOn(byte note, byte onVelo);
+	void programChange(byte program) {}
+	void pitchBend(int16 value);
+	void controlChange(byte control, byte value);
+	void pitchBendFactor(byte value) { }
+	void sysEx_customInstrument(uint32 unused, const byte *instr);
+
+protected:
+	Voice2612 *_voice;
+};
+
+class MidiChannel_EuD_WAVE : public MidiChannel_EuD {
+public:
+	void nextTick(int32 *outbuf, int buflen);
+	void rate(uint16 r);
+
+	MidiChannel_EuD_WAVE();
+	virtual ~MidiChannel_EuD_WAVE();
+
+	// MidiChannel interface
+	MidiDriver *device() { return 0; }
+	byte getNumber() { return 0; }
+	void release() { }
+	void send(uint32 b) { }
+	void noteOff(byte note);
+	void noteOn(byte note, byte onVelo);
+	void programChange(byte program) {}
+	void pitchBend(int16 value);
+	void controlChange(byte control, byte value);
+	void pitchBendFactor(byte value) { }
+	void sysEx_customInstrument(uint32 type, const byte *instr);
+
+protected:
+	void velocity(int velo);
+	void panPosition(int8 pan);
+	void evpNextTick();
+
+	int _ctrl7_volume;
+	int16 _velocity;
+	int16 _note;
+	int32 _frequencyOffs;
+	float _phase;
+	int8 _current;
+
+	struct Voice {
+		char name[9];
+		uint16 split[8];
+		uint32 id[8];
+		struct Snd {
+			char name[9];
+			int32 id;
+			int32 numSamples;
+			int32 loopStart;
+			int32 loopLength;
+			int32 samplingRate;
+			int32 keyOffset;
+			int32 keyNote;
+			int8 *_samples;
+		} * _snd[8];
+		struct Env {
+			EuD_ChannelState state;
+			int32 currentLevel;
+			int32 rate;
+			int32 tickCount;
+			int32 totalLevel;
+			int32 attackRate;
+			int32 decayRate;
+			int32 sustainLevel;
+			int32 sustainRate;
+			int32 releaseLevel;
+			int32 releaseRate;
+			int32 rootKeyOffset;
+			int32 size;
+		} * _env[8];
+	} * _voice;
+};
+
+class MidiParser_EuD : public MidiParser {
+public:
+	MidiParser_EuD();
+
+	bool loadMusic (byte *data, uint32 unused = 0);
+	void setTempo(uint32 tempo);
+protected:
+	void parseNextEvent (EventInfo &info);
+	void resetTracking();
+
+	byte * _enable;
+	byte * _mode;
+	byte * _channel;
+	byte * _adjVelo;
+	int8 * _adjNote;
+
+	uint8 _firstBaseTickStep;
+	uint8 _nextBaseTickStep;
+	uint8 _initialTempo;
+	uint32 _baseTick;
+};
+
+class FMT_EuphonyDriver : public MidiDriver_Emulated {
+public:
+	FMT_EuphonyDriver(Audio::Mixer *mixer);
+	virtual ~FMT_EuphonyDriver();
+
+	int open();
+	void close();
+	void send(uint32 b);
+	void send(byte channel, uint32 b);
+	uint32 property(int prop, uint32 param) { return 0; }
+
+	void setPitchBendRange(byte channel, uint range) { }
+	//void sysEx(const byte *msg, uint16 length);
+	void loadFmInstruments(const byte *instr);
+	void loadWaveInstruments(const byte *instr);
+
+	MidiChannel *allocateChannel() { return 0; }
+	MidiChannel *getPercussionChannel() { return 0; }
+
+	void assignFmChannel(uint8 midiChannelNumber, uint8 fmChannelNumber);
+	void assignWaveChannel(uint8 midiChannelNumber, uint8 waveChannelNumber);
+	void removeChannel(uint8 midiChannelNumber);
+
+	// AudioStream API
+	bool isStereo() const { return true; }
+	int getRate() const { return _mixer->getOutputRate(); }
+
+protected:
+	void nextTick(int16 *buf1, int buflen);
+	int volume(int val = -1) { if (val >= 0) _volume = val; return _volume; }
+	void rate(uint16 r);
+
+	void generateSamples(int16 *buf, int len);
+
+	MidiChannel_EuD_FM *_fChannel[6];
+	MidiChannel_EuD_WAVE *_wChannel[8];
+	MidiChannel_EuD * _channel[16];
+
+	int _volume;
+
+	uint8 *_fmInstruments;
+	uint8 *_waveInstruments;
+	int8 * _waveSounds[10];
+};
+
+MidiChannel_EuD_FM::MidiChannel_EuD_FM() {
+	_voice = new Voice2612;
+}
+
+MidiChannel_EuD_FM::~MidiChannel_EuD_FM() {
+	delete _voice;
+}
+
+void MidiChannel_EuD_FM::noteOn(byte note, byte onVelo) {
+	_voice->noteOn(note, onVelo);
+}
+
+void MidiChannel_EuD_FM::noteOff(byte note) {
+	_voice->noteOff(note);
+}
+
+void MidiChannel_EuD_FM::controlChange(byte control, byte value) {
+	if (control == 121) {
+		// Reset controller
+		delete _voice;
+		_voice = new Voice2612;
+	} else if (control == 10) {
+		// pan position
+	} else {
+		_voice->setControlParameter(control, value);
+	}
+}
+
+void MidiChannel_EuD_FM::sysEx_customInstrument(uint32, const byte *fmInst) {
+	_voice->_rate = _rate;
+	_voice->setInstrument(fmInst);
+}
+
+void MidiChannel_EuD_FM::pitchBend(int16 value) {
+	_voice->pitchBend(value);
+}
+
+void MidiChannel_EuD_FM::nextTick(int32 *outbuf, int buflen) {
+	_voice->nextTick((int*) outbuf, buflen);
+}
+
+void MidiChannel_EuD_FM::rate(uint16 r) {
+	_rate = r;
+	_voice->_rate = r;
+}
+
+MidiChannel_EuD_WAVE::MidiChannel_EuD_WAVE() {
+	_voice = new Voice;
+	for (uint8 i = 0; i < 8; i++) {
+		_voice->_env[i] = new Voice::Env;
+		_voice->_snd[i] = 0;
+	}
+
+	_ctrl7_volume = 127;
+	velocity(0);
+	_frequencyOffs = 0x2000;
+	_current = -1;
+}
+
+MidiChannel_EuD_WAVE::~MidiChannel_EuD_WAVE() {
+	for (uint8 i = 0; i < 8; i++) {
+		if (_voice->_snd[i])
+			delete _voice->_snd[i];
+		delete _voice->_env[i];
+	}
+	delete _voice;
+}
+
+void MidiChannel_EuD_WAVE::noteOn(byte note, byte onVelo) {
+	_note = note;
+	velocity(onVelo);
+	_phase = 0;
+
+	for (_current = 0; _current < 7; _current++) {
+		if (note <=	_voice->split[_current])
+			break;
+	}
+
+	_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) {
+    if (_current == -1)
+		return;
+	if (_voice->_env[_current]->state == _s_ready)
+		return;
+
+	_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) {
+	switch (control) {
+		case 0x07:
+			// volume
+			_ctrl7_volume = value;
+			break;
+		case 0x0A:
+			// pan position
+			break;
+		case 0x79:
+		// Reset controller
+			for (uint8 i = 0; i < 8; i++) {
+				if (_voice->_snd[i])
+					delete _voice->_snd[i];
+				delete _voice->_env[i];
+			}
+			delete _voice;
+			_voice = new Voice;
+			for (uint8 i = 0; i < 8; i++) {
+				_voice->_env[i] = new Voice::Env;
+				_voice->_snd[i] = 0;
+			}
+			break;
+		case 0x7B:
+			noteOff(_note);
+			break;
+		default:
+			break;
+	}
+}
+
+void MidiChannel_EuD_WAVE::sysEx_customInstrument(uint32 type, const byte *fmInst) {
+	if (type == 0x80) {
+		for (uint8 i = 0; i < 8; i++) {
+			byte ** pos = (byte **) fmInst;
+			for (uint8 ii = 0; ii < 10; ii++) {
+				if (_voice->id[i] == *(pos[ii] + 8)) {
+					if (!_voice->_snd[i])
+						_voice->_snd[i] = new Voice::Snd;
+					memset (_voice->_snd[i]->name, 0, 9);
+					memcpy (_voice->_snd[i]->name, (const char*) pos[ii], 8);
+					_voice->_snd[i]->id = READ_LE_UINT32(pos[ii] + 8);
+					_voice->_snd[i]->numSamples = READ_LE_UINT32(pos[ii] + 12);
+					_voice->_snd[i]->loopStart = READ_LE_UINT32(pos[ii] + 16);
+					_voice->_snd[i]->loopLength = READ_LE_UINT32(pos[ii] + 20);
+					_voice->_snd[i]->samplingRate = READ_LE_UINT16(pos[ii] + 24);
+					_voice->_snd[i]->keyOffset = READ_LE_UINT16(pos[ii] + 26);
+					_voice->_snd[i]->keyNote = *(uint8*)(pos[ii] + 28);
+					_voice->_snd[i]->_samples = (int8*)(pos[ii] + 32);
+				}
+			}
+		}
+	} else {
+		memset (_voice->name, 0, 9);
+		memcpy (_voice->name, (const char*) fmInst, 8);
+
+		for (uint8 i = 0; i < 8; i++) {
+			_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]->currentLevel = 0;
+			_voice->_env[i]->totalLevel = *(fmInst + 64 + 8 * i);
+			_voice->_env[i]->attackRate = *(fmInst + 65 + 8 * i) * 10;
+			_voice->_env[i]->decayRate = *(fmInst + 66 + 8 * i) * 10;
+			_voice->_env[i]->sustainLevel = *(fmInst + 67 + 8 * i);
+			_voice->_env[i]->sustainRate = *(fmInst + 68 + 8 * i) * 20;
+			_voice->_env[i]->releaseRate = *(fmInst + 69 + 8 * i) * 10;
+			_voice->_env[i]->rootKeyOffset = *(fmInst + 70 + 8 * i);
+		}
+	}
+}
+
+void MidiChannel_EuD_WAVE::pitchBend(int16 value) {
+	_frequencyOffs = value;
+}
+
+void MidiChannel_EuD_WAVE::nextTick(int32 *outbuf, int buflen) {
+	if (_current == -1 || !_voice->_snd[_current] || !_voice->_env[_current]->state || !_velocity) {
+		velocity(0);
+		_current = -1;
+		return;
+	}
+
+	float phaseStep = SoundTowns::semitoneAndSampleRate_to_sampleStep(_note, _voice->_snd[_current]->keyNote -
+		_voice->_env[_current]->rootKeyOffset, _voice->_snd[_current]->samplingRate, _rate, _frequencyOffs);
+
+	int32 looplength = _voice->_snd[_current]->loopLength;
+	int32 numsamples = _voice->_snd[_current]->numSamples;
+	int8 * samples = _voice->_snd[_current]->_samples;
+
+	for (int i = 0; i < buflen; i++) {
+		if (looplength > 0) {
+			while (_phase >= numsamples)
+				_phase -= looplength;
+		} else {
+			if (_phase >= numsamples) {
+				velocity(0);
+				_current = -1;
+				break;
+			}
+		}
+
+		int32 output;
+
+		int32 phase0 = int32(_phase);
+		int32 phase1 = int32(_phase + 1);
+		if (phase1 >= numsamples)
+			phase1 -= looplength;
+		float weight0 = _phase - phase0;
+		float weight1 = phase1 - _phase;
+		output = int32(samples[phase0] * weight0 + samples[phase1] * weight1);
+
+		output *= _velocity;
+		output <<= 1;
+
+		evpNextTick();
+		output *= _voice->_env[_current]->currentLevel;
+		output >>= 7;
+		output *= _ctrl7_volume;
+		output >>= 7;
+
+		output *= 185;
+		output >>= 8;
+		outbuf[i] += output;
+		_phase += phaseStep;
+	}
+}
+
+void MidiChannel_EuD_WAVE::evpNextTick() {
+	switch (_voice->_env[_current]->state) {
+		case _s_ready:
+			_voice->_env[_current]->currentLevel = 0;
+			return;
+
+		case _s_attacking:
+			if (_voice->_env[_current]->attackRate == 0)
+				_voice->_env[_current]->currentLevel = _voice->_env[_current]->totalLevel;
+			else if (_voice->_env[_current]->attackRate >= 1270)
+				_voice->_env[_current]->currentLevel = 0;
+			else
+				_voice->_env[_current]->currentLevel = (_voice->_env[_current]->totalLevel *
+					_voice->_env[_current]->tickCount++ * 1000) /
+						(_voice->_env[_current]->attackRate * _voice->_env[_current]->rate);
+
+			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]->tickCount = 0;
+			}
+			break;
+
+		case _s_decaying:
+			if (_voice->_env[_current]->decayRate == 0)
+				_voice->_env[_current]->currentLevel = _voice->_env[_current]->sustainLevel;
+			else if (_voice->_env[_current]->decayRate >= 1270)
+				_voice->_env[_current]->currentLevel = _voice->_env[_current]->totalLevel;
+			else {
+				_voice->_env[_current]->currentLevel = _voice->_env[_current]->totalLevel;
+				_voice->_env[_current]->currentLevel -= ((_voice->_env[_current]->totalLevel -
+					_voice->_env[_current]->sustainLevel) * _voice->_env[_current]->tickCount++ * 1000) /
+						(_voice->_env[_current]->decayRate * _voice->_env[_current]->rate);
+			}
+
+			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]->tickCount = 0;
+			}
+			break;
+
+			case _s_sustaining:
+				if (_voice->_env[_current]->sustainRate == 0)
+					_voice->_env[_current]->currentLevel = 0;
+				else if (_voice->_env[_current]->sustainRate >= 2540)
+					_voice->_env[_current]->currentLevel = _voice->_env[_current]->sustainLevel;
+				else {
+					_voice->_env[_current]->currentLevel = _voice->_env[_current]->sustainLevel;
+					_voice->_env[_current]->currentLevel -= (_voice->_env[_current]->sustainLevel *
+						_voice->_env[_current]->tickCount++ * 1000) / (_voice->_env[_current]->sustainRate *
+							_voice->_env[_current]->rate);
+				}
+
+				if (_voice->_env[_current]->currentLevel <= 0) {
+					_voice->_env[_current]->currentLevel = 0;
+					_voice->_env[_current]->state = _s_ready;
+					_voice->_env[_current]->tickCount = 0;
+				}
+				break;
+
+			case _s_releasing:
+				if (_voice->_env[_current]->releaseRate == 0)
+					_voice->_env[_current]->currentLevel = 0;
+				else if (_voice->_env[_current]->releaseRate >= 1270)
+					_voice->_env[_current]->currentLevel = _voice->_env[_current]->releaseLevel;
+				else {
+					_voice->_env[_current]->currentLevel = _voice->_env[_current]->releaseLevel;
+					_voice->_env[_current]->currentLevel -= (_voice->_env[_current]->releaseLevel *
+						_voice->_env[_current]->tickCount++ * 1000) / (_voice->_env[_current]->releaseRate *
+							_voice->_env[_current]->rate);
+				}
+
+				if (_voice->_env[_current]->currentLevel <= 0) {
+					_voice->_env[_current]->currentLevel = 0;
+					_voice->_env[_current]->state = _s_ready;
+				}
+				break;
+
+			default:
+			break;
+	}
+}
+
+void MidiChannel_EuD_WAVE::rate(uint16 r) {
+	_rate = r;
+}
+
+void MidiChannel_EuD_WAVE::velocity(int velo) {
+	_velocity = velo;
+}
+
+FMT_EuphonyDriver::FMT_EuphonyDriver(Audio::Mixer *mixer)
+	: MidiDriver_Emulated(mixer) {
+
+	MidiDriver_YM2612::createLookupTables();
+
+	_volume = 255;
+	for (uint8 i = 0; i < 6; i++)
+		_channel[i] = _fChannel[i] = new MidiChannel_EuD_FM;
+	for (uint8 i = 0; i < 8; i++)
+		_channel[i + 6] = _wChannel[i] = new MidiChannel_EuD_WAVE;
+	_channel[14] = _channel[15] = 0;
+
+	_fmInstruments = _waveInstruments = 0;
+	memset(_waveSounds, 0, sizeof(uint8*) * 10);
+
+	rate(getRate());
+}
+
+FMT_EuphonyDriver::~FMT_EuphonyDriver() {
+	for (int i = 0; i < 6; i++)
+		delete _fChannel[i];
+	for (int i = 0; i < 8; i++)
+		delete _wChannel[i];
+
+	MidiDriver_YM2612::removeLookupTables();
+
+	if (_fmInstruments) {
+		delete [] _fmInstruments;
+		_fmInstruments = 0;
+	}
+
+	if (_waveInstruments) {
+		delete [] _waveInstruments;
+		_waveInstruments = 0;
+	}
+
+	for (int i = 0; i < 10; i++) {
+		if (_waveSounds[i]) {
+			delete [] _waveSounds[i];
+			_waveSounds[i] = 0;
+		}
+	}
+}
+
+int FMT_EuphonyDriver::open() {
+	if (_isOpen)
+		return MERR_ALREADY_OPEN;
+
+	MidiDriver_Emulated::open();
+
+	_mixer->playInputStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle,
+		this, -1, Audio::Mixer::kMaxChannelVolume, 0, false, true);
+	return 0;
+}
+
+void FMT_EuphonyDriver::close() {
+	if (!_isOpen)
+		return;
+	_isOpen = false;
+	_mixer->stopHandle(_mixerSoundHandle);
+}
+
+void FMT_EuphonyDriver::send(uint32 b) {
+	send(b & 0xF, b & 0xFFFFFFF0);
+}
+
+void FMT_EuphonyDriver::send(byte chan, uint32 b) {
+	//byte param3 = (byte) ((b >> 24) & 0xFF);
+
+	byte param2 = (byte) ((b >> 16) & 0xFF);
+	byte param1 = (byte) ((b >>  8) & 0xFF);
+	byte cmd    = (byte) (b & 0xF0);
+	if (chan > ARRAYSIZE(_channel))
+		return;
+
+	switch (cmd) {
+	case 0x80:// Note Off
+		if (_channel[chan])
+			_channel[chan]->noteOff(param1);
+		break;
+	case 0x90: // Note On
+		if (_channel[chan])
+			_channel[chan]->noteOn(param1, param2);
+		break;
+	case 0xA0: // Aftertouch
+		break; // Not supported.
+	case 0xB0: // Control Change
+		if (param1 == 0x79) {
+			for (int i = 0; i < 15; i++) {
+				if (_channel[i]) {
+					_channel[i]->controlChange(param1, param2);
+					_channel[i]->programChange(0);
+				}
+			}
+		} else if (param1 == 0x7B) {
+			for (int i = 0; i < 15; i++) {
+				if (_channel[i])
+					_channel[i]->controlChange(param1, param2);
+			}
+		} else {
+			if (_channel[chan])
+				_channel[chan]->controlChange(param1, param2);
+		}
+		break;
+	case 0xC0: // Program Change
+        for (int i = 0; i < 6; i++) {
+			if (_channel[chan] == _fChannel[i]) {
+				_channel[chan]->sysEx_customInstrument(0, _fmInstruments + param1 * 0x30);
+				break;
+			}
+		}
+		for (int i = 0; i < 8; i++) {
+			if (_channel[chan] == _wChannel[i]) {
+				_channel[chan]->sysEx_customInstrument(0, _waveInstruments + param1 * 0x80);
+				_channel[chan]->sysEx_customInstrument(0x80, (const byte*) _waveSounds);
+				break;
+			}
+		}
+		break;
+	case 0xD0: // Channel Pressure
+		break; // Not supported.
+	case 0xE0: // Pitch Bend
+		if (_channel[chan])
+			_channel[chan]->pitchBend((param1 | (param2 << 7)) - 0x2000);
+		break;
+	default:
+		warning("FMT_EuphonyDriver: Unknown send() command 0x%02X", cmd);
+	}
+}
+
+void FMT_EuphonyDriver::loadFmInstruments(const byte *instr) {
+	if (_fmInstruments)
+		delete [] _fmInstruments;
+	_fmInstruments = new uint8[0x1800];
+	memcpy(_fmInstruments, instr, 0x1800);
+}
+
+void FMT_EuphonyDriver::loadWaveInstruments(const byte *instr) {
+	if (_waveInstruments)
+		delete [] _waveInstruments;
+	_waveInstruments = new uint8[0x1000];
+	memcpy(_waveInstruments, instr, 0x1000);
+
+	const uint8 *pos = (const uint8 *)(instr + 0x1000);
+
+	for (uint8 i = 0; i < 10; i++) {
+		if (_waveSounds[i])
+			delete [] _waveSounds[i];
+		uint32 numsamples = READ_LE_UINT32(pos + 0x0C);
+		_waveSounds[i] = new int8[numsamples + 0x20];
+        memcpy(_waveSounds[i], pos, 0x20);
+		pos += 0x20;
+		for (uint32 ii = 0; ii < numsamples; ii++) {
+			uint8 s = *(pos + ii);
+			s = (s < 0x80) ? 0x80 - s : s;
+			_waveSounds[i][ii + 0x20] = s ^ 0x80;
+		}
+		pos += numsamples;
+	}
+}
+
+
+void FMT_EuphonyDriver::assignFmChannel(uint8 midiChannelNumber, uint8 fmChannelNumber) {
+	_channel[midiChannelNumber] = _fChannel[fmChannelNumber];
+}
+
+void FMT_EuphonyDriver::assignWaveChannel(uint8 midiChannelNumber, uint8 waveChannelNumber) {
+	_channel[midiChannelNumber] = _wChannel[waveChannelNumber];
+}
+
+void FMT_EuphonyDriver::removeChannel(uint8 midiChannelNumber) {
+	_channel[midiChannelNumber] = 0;
+}
+
+void FMT_EuphonyDriver::generateSamples(int16 *data, int len) {
+	memset(data, 0, 2 * sizeof(int16) * len);
+	nextTick(data, len);
+}
+
+void FMT_EuphonyDriver::nextTick(int16 *buf1, int buflen) {
+	int32 *buf0 = (int32 *)buf1;
+
+	for (int i = 0; i < ARRAYSIZE(_channel); i++) {
+		if (_channel[i])
+			_channel[i]->nextTick(buf0, buflen);
+	}
+
+	for (int i = 0; i < buflen; ++i) {
+		buf1[i*2] = buf1[i*2+1] =((buf0[i] * volume()) >> 9) & 0xffff;
+	}
+}
+
+void FMT_EuphonyDriver::rate(uint16 r)
+{
+	for (uint8 i = 0; i < 16; i++) {
+		if (_channel[i])
+			_channel[i]->rate(r);
+	}
+}
+
+MidiParser_EuD::MidiParser_EuD() : MidiParser() {
+}
+
+void MidiParser_EuD::parseNextEvent(EventInfo &info) {
+	byte *pos = _position._play_pos;
+
+	while (true) {
+		byte cmd = *pos;
+		byte evt = (cmd & 0xF0);
+
+		if (evt == 0x90) {
+			byte chan = pos[1];
+
+			if (_enable[chan]) {
+				uint16 tick = (pos[2] | ((uint16) pos[3] << 7)) + _baseTick;
+				info.start = pos + 6;
+				uint32 last = _position._last_event_tick;
+				info.delta = (tick < last) ? 0 : (tick - last);
+
+				info.event = 0x90 | _channel[chan];
+				info.length = pos[7] | (pos[8] << 4);
+
+				int8 note = (int8) pos[4];
+				if (_adjNote[chan]) {
+					note = (note & 0x7f) & _adjNote[chan];
+					if (note > 0x7c)
+						note -= 0x0c;
+					else if (note < 0)
+						note += 0x0c;
+				}
+				info.basic.param1 = (byte) note;
+
+				uint8 onVelo = (pos[5] & 0x7f) + _adjVelo[chan];
+				if (onVelo > 0x7f)
+					onVelo = 0x7f;
+				if (onVelo < 1)
+					onVelo = 1;
+				info.basic.param2 = onVelo;
+
+				pos += 12;
+				break;
+			} else {
+				pos += 6;
+			}
+		} else if (evt == 0xB0 || evt == 0xC0 || evt == 0xe0) {
+			byte chan = pos[1];
+
+			if (_enable[chan]) {
+				info.start = pos;
+				uint16 tick = (pos[2] | ((uint16) pos[3] << 7)) + _baseTick;
+				uint32 last = _position._last_event_tick;
+				info.delta = (tick < last) ? 0 : (tick - last);
+				info.event = evt | _channel[chan];
+				info.length = 0;
+				info.basic.param1 = pos[4];
+				info.basic.param2 = pos[5];
+				pos += 6;
+				break;
+			} else {
+				pos += 6;
+			}
+		} else if (cmd == 0xF2) {
+			static uint16 tickTable [] = { 0x180, 0xC0, 0x80, 0x60, 0x40, 0x30, 0x20, 0x18 };
+			_baseTick += tickTable[_nextBaseTickStep >> 4] * ((_nextBaseTickStep & 0x0f) + 1);
+			_nextBaseTickStep = pos[1];
+			pos += 6;
+		} else if (cmd == 0xF8) {
+			uint16 tempo = pos[4] | (pos[5] << 7);
+			setTempo(tempo);
+			pos += 6;
+		} else if (cmd == 0xFD || cmd == 0xFE) {
+			// End of track.
+			if (_autoLoop)
+				pos = info.start = _tracks[0];
+			else
+				info.start = pos;
+
+			uint32 last = _position._last_event_tick;
+			uint16 tick = (pos[2] | ((uint16) pos[3] << 7)) + _baseTick;
+			info.delta = (tick < last) ? 0 : (tick - last);
+			info.event = 0xFF;
+			info.ext.type = 0x2F;
+			info.ext.data = pos;
+			break;
+		} else {
+			error("Unknown Euphony music event 0x%02X", (int) cmd);
+			memset(&info, 0, sizeof(info));
+			pos = 0;
+			break;
+		}
+	}
+	_position._play_pos = pos;
+}
+
+bool MidiParser_EuD::loadMusic(byte *data, uint32) {
+	unloadMusic();
+
+	_enable = data + 0x354;
+	_mode = data + 0x374;
+	_channel = data + 0x394;
+	_adjVelo = data + 0x3B4;
+	_adjNote = (int8*) data + 0x3D4;
+
+	_firstBaseTickStep = data[0x804];
+	_initialTempo = (data[0x805] > 0xfc) ? 0x5a : data[0x805];
+
+	_num_tracks = 1;
+	_ppqn = 120;
+	_tracks[0] = data + 0x806;
+
+	resetTracking();
+	setTrack (0);
+
+	return true;
+}
+
+void MidiParser_EuD::setTempo(uint32 tempo) {
+	if (tempo)
+		MidiParser::setTempo(60000000 / tempo);
+}
+
+void MidiParser_EuD::resetTracking() {
+	MidiParser::resetTracking();
+
+	_nextBaseTickStep = _firstBaseTickStep;
+	_baseTick = 0;
+	setTempo(_initialTempo);
+}
+
 SoundTowns::SoundTowns(KyraEngine *engine, Audio::Mixer *mixer) : Sound(engine, mixer), _lastTrack(-1),
-	_currentSFX(0),	_sfxFileData(0), _sfxFileIndex((uint)-1), _sfxWDTable(0) {
+	 _currentSFX(0), _sfxFileData(0), _sfxFileIndex((uint)-1), _sfxWDTable(0), _parser(0), _musicTrackData(0) {
+
+	_driver = new FMT_EuphonyDriver(_mixer);
+	int ret = open();
+	if (ret != MERR_ALREADY_OPEN && ret != 0) {
+		error("couldn't open midi driver");
+	}
 }
 
 SoundTowns::~SoundTowns() {
 	AudioCD.stop();
+	haltTrack();
 	delete [] _sfxFileData;
-	stopSoundEffect();
+
+	Common::StackLock lock(_mutex);
+	_driver->setTimerCallback(0, 0);
+	close();
+
+	if (_musicTrackData)
+		delete [] _musicTrackData;
+
+	_driver = 0;
 }
 
 bool SoundTowns::init() {
 	_engine->checkCD();
 	int unused = 0;
 	_sfxWDTable = _engine->staticres()->loadRawData(kKyra1TownsSFXTable, unused);
-	return true;
+
+	return loadInstruments();
 }
 
 void SoundTowns::process() {
@@ -131,22 +967,38 @@
 
 	int trackNum = tTable[track].track;
 	bool loop = tTable[track].loop;
-	// could be that if the trackNum is -1, the music should be stopped
-	// instead of letting the old music play on
-	if (trackNum == -1 || trackNum == _lastTrack)
+
+	if (track == _lastTrack && _musicEnabled)
 		return;
 
 	haltTrack();
-	AudioCD.play(trackNum+1, loop ? -1 : 1, 0, 0);
-	AudioCD.updateCD();
 
-	_lastTrack = trackNum;
+	if (_musicEnabled == 2 && trackNum != -1) {
+		AudioCD.play(trackNum+1, loop ? -1 : 1, 0, 0);
+		AudioCD.updateCD();
+	} else if (_musicEnabled) {
+		playEuphonyTrack(tTable[track].fileOffset, loop);
+	}
+
+	_lastTrack = track;
 }
 
 void SoundTowns::haltTrack() {
 	_lastTrack = -1;
 	AudioCD.stop();
 	AudioCD.updateCD();
+	if (_parser) {
+		Common::StackLock lock(_mutex);
+
+		_parser->setTrack(0);
+		_parser->jumpToTick(0);
+
+		_parser->unloadMusic();
+		delete _parser;
+		_parser = 0;
+
+		setVolume(255);
+	}
 }
 
 void SoundTowns::loadSoundFile(uint file) {
@@ -157,27 +1009,29 @@
 	_sfxFileData = _engine->resource()->fileData(soundFilename(file), 0);
 }
 
-void SoundTowns::stopSoundEffect() {
-	_sfxIsPlaying = false;	
-	_mixer->stopHandle(_sfxHandle);
-}
-
 void SoundTowns::playSoundEffect(uint8 track) {
 	if (!_sfxEnabled || !_sfxFileData)
 		return;
 
-	_sfxIsPlaying = true;
-	
-	uint8 pitch = 0x3c;
-	if (_sfxFileIndex == 5) {		
+	if (track == 0 || track == 10) {
+		_mixer->stopHandle(_sfxHandle);
+		return;
+	} else if (track == 1) {
+		// sfx fadeout
+		_mixer->stopHandle(_sfxHandle);
+		return;
+	}
+
+	uint8 note = 0x3c;
+	if (_sfxFileIndex == 5) {
 		if (track == 0x10) {
-			pitch = 0x3e;
+			note = 0x3e;
 			track = 0x0f;
 		} else if (track == 0x11) {
-			pitch = 0x40;
+			note = 0x40;
 			track = 0x0f;
 		} else if (track == 0x12) {
-			pitch = 0x41;
+			note = 0x41;
 			track = 0x0f;
 		}
 	}
@@ -194,18 +1048,18 @@
 		uint32 outBufferSize;
 		uint32 unused2;
 		uint32 unused3;
-		uint32 unknown1;
-		uint32 pitch;
+		uint32 rate;
+		uint32 rootNoteOffs;
 	} *sfxHeader = (SfxHeader*)(fileBody + offset);
 
 	uint32 sfxHeaderID = TO_LE_32(sfxHeader->id);
 	uint32 sfxHeaderInBufferSize = TO_LE_32(sfxHeader->inBufferSize);
 	uint32 sfxHeaderOutBufferSize = TO_LE_32(sfxHeader->outBufferSize);
-	sfxHeader->pitch = TO_LE_32(sfxHeader->pitch);
+	uint32 sfxRootNoteOffs = TO_LE_32(sfxHeader->rootNoteOffs);
+	uint32 sfxRate = TO_LE_32(sfxHeader->rate);
 
 	uint32 playbackBufferSize = (sfxHeaderID == 1) ? sfxHeaderInBufferSize : sfxHeaderOutBufferSize;
 
-	stopSoundEffect();
 	uint8 *sfxPlaybackBuffer = (uint8 *)malloc(playbackBufferSize);
 	memset(sfxPlaybackBuffer, 0x80, playbackBufferSize);
 
@@ -220,7 +1074,7 @@
 		uint32 sfx_BtTable_Offset = 0;
 		uint32 sfx_WdTable_Offset = 0;
 		uint32 sfx_WdTable_Number = 5;
-		
+
 		for (uint32 i = 0; i < sfxHeaderInBufferSize; i++) {
 			sfx_WdTable_Offset = (sfx_WdTable_Number * 3 << 9) + sfxBody[i] * 6;
 			sfx_WdTable_Number = READ_LE_UINT16(_sfxWDTable + sfx_WdTable_Offset);
@@ -233,34 +1087,129 @@
 		}
 	}
 
-	for (uint32 i = 0; i < playbackBufferSize; i++) {		
+	for (uint32 i = 0; i < playbackBufferSize; i++) {
 		if (sfxPlaybackBuffer[i] < 0x80)
 			sfxPlaybackBuffer[i] = 0x80 - sfxPlaybackBuffer[i];
 	}
 
 	playbackBufferSize -= 0x20;
-	setPitch(sfxPlaybackBuffer, playbackBufferSize, sfxHeader->pitch, pitch);
-	
-	_currentSFX = Audio::makeLinearInputStream(sfxPlaybackBuffer, playbackBufferSize, 
-		0x2b11,
-		Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_LITTLE_ENDIAN | Audio::Mixer::FLAG_AUTOFREE,
-		0, 0);
+	uint32 outputRate = uint32(11025 * semitoneAndSampleRate_to_sampleStep(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);
 }
 
-void SoundTowns::setPitch(uint8 *&data, uint32 &size, int8 sourcePitch, int8 targetPitch) {
-	if (sourcePitch == targetPitch)
-		return;
+void SoundTowns::beginFadeOut() {
+	//int vol = 255;
+	haltTrack();
+}
 
-	if (sourcePitch < 0)
-		sourcePitch = 0;
-	if (sourcePitch > 119)
-		sourcePitch = 119;
-	if (targetPitch < 0)
-		targetPitch = 0;
-	if (targetPitch > 119)
-		targetPitch = 119;
-	
+int SoundTowns::open() {
+	if (!_driver)
+		return 255;
+
+	int ret = _driver->open();
+	if (ret)
+		return ret;
+
+	_driver->setTimerCallback(this, &onTimer);
+	return 0;
+}
+
+void SoundTowns::close() {
+	if (_driver)
+		_driver->close();
+}
+
+void SoundTowns::send(uint32 b) {
+	_driver->send(b);
+}
+
+uint32 SoundTowns::getBaseTempo(void) {
+	return _driver ? _driver->getBaseTempo() : 0;
+}
+
+bool SoundTowns::loadInstruments() {
+	if (!_musicTrackData)
+		_musicTrackData = new uint8[0xC58A];
+
+	memset(_musicTrackData, 0, 0xC58A);
+	uint8 * twm = _engine->resource()->fileData("twmusic.pak", 0);
+	if (!twm)
+		return false;
+	Screen::decodeFrame4(twm, _musicTrackData, 0x8BF0);
+	_driver->loadFmInstruments(_musicTrackData + 8);
+
+	memset (_musicTrackData, 0, 0xC58A);
+	Screen::decodeFrame4(twm + 0x0CA0, _musicTrackData, 0xC58A);
+	delete [] twm;
+	_driver->loadWaveInstruments(_musicTrackData + 8);
+
+	return true;
+}
+
+void SoundTowns::playEuphonyTrack(uint32 offset, int loop) {
+	if (!_musicTrackData)
+		_musicTrackData = new uint8[0xC58A];
+
+	memset(_musicTrackData, 0, 0xC58A);
+	uint8 * twm = _engine->resource()->fileData("twmusic.pak", 0);
+	Screen::decodeFrame4(twm + 0x4b70 + offset, _musicTrackData, 0xC58A);
+	delete [] twm;
+
+	Common::StackLock lock(_mutex);
+
+	uint8 * used = _musicTrackData + 0x374;
+	uint8 * fchan = _musicTrackData + 0x6d4;
+	uint8 * wchan = _musicTrackData + 0x6dA;
+
+	for (uint8 i = 0; i < 6; i++) {
+		if (used[fchan[i]])
+			_driver->assignFmChannel(fchan[i], i);
+	}
+
+	for (uint8 i = 0; i < 8; i++) {
+		if (used[wchan[i]])
+			_driver->assignWaveChannel(wchan[i], i);
+	}
+
+	for (uint8 i = 0; i < 16; i++) {
+		if (!used[i])
+			_driver->removeChannel(i);
+	}
+	_driver->send(0x79B0);
+
+	if (_parser)
+		delete _parser;
+
+	_parser = new MidiParser_EuD;
+	_parser->property(MidiParser::mpAutoLoop, loop);
+	_parser->loadMusic(_musicTrackData, 0);
+	_parser->jumpToTick(0);
+
+	_parser->setMidiDriver(this);
+	_parser->setTimerRate(getBaseTempo());
+}
+
+void SoundTowns::onTimer(void * data) {
+	SoundTowns *music = (SoundTowns *)data;
+	Common::StackLock lock(music->_mutex);
+	if (music->_parser)
+		music->_parser->onTimer();
+}
+
+float SoundTowns::semitoneAndSampleRate_to_sampleStep(int8 semiTone, int8 semiToneRootkey,
+	uint32 sampleRate, uint32 outputRate, int32 pitchWheel) {
+	if (semiTone < 0)
+		semiTone = 0;
+	if (semiTone > 119)
+		semiTone = 119;
+	if (semiTone < 0)
+		semiTone = 0;
+	if (semiTone > 119)
+		semiTone = 119;
+
 	static const float noteFrq[] = {
 		0004.13f, 0004.40f, 0004.64f, 0004.95f, 0005.16f, 0005.50f, 0005.80f, 0006.19f, 0006.60f, 0006.86f,
 		0007.43f, 0007.73f, 0008.25f, 0008.80f, 0009.28f, 0009.90f, 0010.31f, 0011.00f, 0011.60f, 0012.38f,
@@ -276,28 +1225,12 @@
 		2376.00f, 2534.40f, 2640.00f, 2816.00f, 2970.40f, 3168.00f, 3379.20f, 3520.00f, 3801.60f, 3960.00f
 	};
 
-	const float inc = noteFrq[targetPitch] / noteFrq[sourcePitch];
+	float pwModifier = (pitchWheel - 0x2000) / 0x2000;
+	int8 d = pwModifier ? (pwModifier < 0 ? -1 : 1) : 0;
+	float rateshift = (noteFrq[semiTone] - ((noteFrq[semiTone] -
+		noteFrq[semiTone + d]) * pwModifier * d)) / noteFrq[semiToneRootkey];
 
-	uint32 estimatedSize = (uint32)(((float) size / inc) + 1);
-	uint32 exactSize = 0;
-	uint8 * tmp = new uint8[estimatedSize];
-	memset(tmp, 0x80, estimatedSize);
-
-	int last = 0;
-	for (float i = 0; i < size; i += inc) {
-		int cur = (int) i;
-		if (cur == last + 2)
-			tmp[exactSize++] = (data[last] + data[cur - 1] + data[cur]) / 3;
-		else if (cur == last)
-			tmp[exactSize++] = (data[cur] + data[cur + 1]) / 2;
-		else
-			tmp[exactSize++] = data[cur];
-		last = (int) i;
-	}
-
-    size = MIN(exactSize, estimatedSize);
-	delete[] data;
-	data = tmp;
+	return (float) sampleRate * 10.0f * rateshift / outputRate;
 }
 
 const uint8 SoundTowns::_sfxBTTable[256] = {

Modified: scummvm/trunk/sound/softsynth/ym2612.cpp
===================================================================
--- scummvm/trunk/sound/softsynth/ym2612.cpp	2007-03-23 23:38:45 UTC (rev 26284)
+++ scummvm/trunk/sound/softsynth/ym2612.cpp	2007-03-24 00:04:08 UTC (rev 26285)
@@ -23,10 +23,9 @@
  * $Id$
  */
 
-#include "sound/softsynth/emumidi.h"
-
 #include <math.h>
 
+#include "sound/softsynth/ym2612.h"
 #include "common/util.h"
 
 ////////////////////////////////////////
@@ -42,154 +41,7 @@
 static int *keyscaleTable = 0;
 static int *attackOut = 0;
 
-////////////////////////////////////////
-//
-// Class declarations
-//
-////////////////////////////////////////
 
-class Operator2612;
-class Voice2612;
-class MidiChannel_YM2612;
-class MidiDriver_YM2612;
-
-class Operator2612 {
-protected:
-	Voice2612 *_owner;
-	enum State { _s_ready, _s_attacking, _s_decaying, _s_sustaining, _s_releasing };
-	State _state;
-	int32 _currentLevel;
-	int _frequency;
-	uint32 _phase;
-	int _lastOutput;
-	int _feedbackLevel;
-	int _detune;
-	int _multiple;
-	int32 _totalLevel;
-	int _keyScale;
-	int _velocity;
-	int _specifiedTotalLevel;
-	int _specifiedAttackRate;
-	int _specifiedDecayRate;
-	int _specifiedSustainLevel;
-	int _specifiedSustainRate;
-	int _specifiedReleaseRate;
-	int _tickCount;
-	int _attackTime;
-	int32 _decayRate;
-	int32 _sustainLevel;
-	int32 _sustainRate;
-	int32 _releaseRate;
-
-public:
-	Operator2612 (Voice2612 *owner);
-	~Operator2612();
-	void feedbackLevel(int level);
-	void setInstrument(byte const *instrument);
-	void velocity(int velo);
-	void keyOn();
-	void keyOff();
-	void frequency(int freq);
-	void nextTick(const int *phaseShift, int *outbuf, int buflen);
-	bool inUse() { return (_state != _s_ready); }
-};
-
-class Voice2612 {
-public:
-	Voice2612 *next;
-	uint16 _rate;
-
-protected:
-	Operator2612 *_opr[4];
-	int _velocity;
-	int _control7;
-	int _note;
-	int _frequencyOffs;
-	int _frequency;
-	int _algorithm;
-
-	int *_buffer;
-	int _buflen;
-
-public:
-	Voice2612();
-	~Voice2612();
-	void setControlParameter(int control, int value);
-	void setInstrument(byte const *instrument);
-	void velocity(int velo);
-	void nextTick(int *outbuf, int buflen);
-	void noteOn(int n, int onVelo);
-	bool noteOff(int note);
-	void pitchBend(int value);
-	void recalculateFrequency();
-};
-
-class MidiChannel_YM2612 : public MidiChannel {
-protected:
-	uint16 _rate;
-	Voice2612 *_voices;
-	Voice2612 *_next_voice;
-
-public:
-	void removeAllVoices();
-	void nextTick(int *outbuf, int buflen);
-	void rate(uint16 r);
-
-public:
-	MidiChannel_YM2612();
-	virtual ~MidiChannel_YM2612();
-
-	// MidiChannel interface
-	MidiDriver *device() { return 0; }
-	byte getNumber() { return 0; }
-	void release() { }
-	void send(uint32 b) { }
-	void noteOff(byte note);
-	void noteOn(byte note, byte onVelo);
-	void programChange(byte program) { }
-	void pitchBend(int16 value);
-	void controlChange(byte control, byte value);
-	void pitchBendFactor(byte value) { }
-	void sysEx_customInstrument(uint32 type, const byte *instr);
-};
-
-class MidiDriver_YM2612 : public MidiDriver_Emulated {
-protected:
-	MidiChannel_YM2612 *_channel[16];
-
-	int _next_voice;
-	int _volume;
-
-protected:
-	static void createLookupTables();
-	void nextTick(int16 *buf1, int buflen);
-	int volume(int val = -1) { if (val >= 0) _volume = val; return _volume; }
-	void rate(uint16 r);
-
-	void generateSamples(int16 *buf, int len);
-
-public:
-	MidiDriver_YM2612(Audio::Mixer *mixer);
-	virtual ~MidiDriver_YM2612();
-
-	int open();
-	void close();
-	void send(uint32 b);
-	void send(byte channel, uint32 b); // Supports higher than channel 15
-	uint32 property(int prop, uint32 param) { return 0; }
-
-	void setPitchBendRange(byte channel, uint range) { }
-	void sysEx(const byte *msg, uint16 length);
-
-	MidiChannel *allocateChannel() { return 0; }
-	MidiChannel *getPercussionChannel() { return 0; }
-
-
-	// AudioStream API
-	bool isStereo() const { return true; }
-	int getRate() const { return _mixer->getOutputRate(); }
-};
-
 ////////////////////////////////////////
 //
 // Operator2612 implementation
@@ -726,13 +578,7 @@
 	int i;
 	for (i = 0; i < ARRAYSIZE(_channel); i++)
 		delete _channel[i];
-	delete sintbl;
-	delete powtbl;
-	delete frequencyTable;
-	delete keycodeTable;
-	delete keyscaleTable;
-	delete attackOut;
-	sintbl = powtbl = frequencyTable = keycodeTable = keyscaleTable = attackOut = 0;
+	removeLookupTables();
 }
 
 int MidiDriver_YM2612::open() {
@@ -896,6 +742,16 @@
 	}
 }
 
+void MidiDriver_YM2612::removeLookupTables() {
+	delete [] sintbl;
+	delete [] powtbl;
+	delete [] frequencyTable;
+	delete [] keycodeTable;
+	delete [] keyscaleTable;
+	delete [] attackOut;
+	sintbl = powtbl = frequencyTable = keycodeTable = keyscaleTable = attackOut = 0;
+}
+
 ////////////////////////////////////////
 //
 // MidiDriver_YM2612 factory
@@ -905,3 +761,4 @@
 MidiDriver *MidiDriver_YM2612_create(Audio::Mixer *mixer) {
 	return new MidiDriver_YM2612(mixer);
 }
+

Added: scummvm/trunk/sound/softsynth/ym2612.h
===================================================================
--- scummvm/trunk/sound/softsynth/ym2612.h	                        (rev 0)
+++ scummvm/trunk/sound/softsynth/ym2612.h	2007-03-24 00:04:08 UTC (rev 26285)
@@ -0,0 +1,156 @@
+#ifndef SOUND_SOFTSYNTH_Y2612_H
+#define SOUND_SOFTSYNTH_Y2612_H
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+
+#include "sound/softsynth/emumidi.h"
+
+////////////////////////////////////////
+//
+// Class declarations
+//
+////////////////////////////////////////
+
+class Voice2612;
+class Operator2612 {
+protected:
+	Voice2612 *_owner;
+	enum State { _s_ready, _s_attacking, _s_decaying, _s_sustaining, _s_releasing };
+	State _state;
+	int32 _currentLevel;
+	int _frequency;
+	uint32 _phase;
+	int _lastOutput;
+	int _feedbackLevel;
+	int _detune;
+	int _multiple;
+	int32 _totalLevel;
+	int _keyScale;
+	int _velocity;
+	int _specifiedTotalLevel;
+	int _specifiedAttackRate;
+	int _specifiedDecayRate;
+	int _specifiedSustainLevel;
+	int _specifiedSustainRate;
+	int _specifiedReleaseRate;
+	int _tickCount;
+	int _attackTime;
+	int32 _decayRate;
+	int32 _sustainLevel;
+	int32 _sustainRate;
+	int32 _releaseRate;
+
+public:
+	Operator2612 (Voice2612 *owner);
+	~Operator2612();
+	void feedbackLevel(int level);
+	void setInstrument(byte const *instrument);
+	void velocity(int velo);
+	void keyOn();
+	void keyOff();
+	void frequency(int freq);
+	void nextTick(const int *phaseShift, int *outbuf, int buflen);
+	bool inUse() { return (_state != _s_ready); }
+};
+
+class Voice2612 {
+public:
+	Voice2612 *next;
+	uint16 _rate;
+
+protected:
+	Operator2612 *_opr[4];
+	int _velocity;
+	int _control7;
+	int _note;
+	int _frequencyOffs;
+	int _frequency;
+	int _algorithm;
+
+	int *_buffer;
+	int _buflen;
+
+public:
+	Voice2612();
+	~Voice2612();
+	void setControlParameter(int control, int value);
+	void setInstrument(byte const *instrument);
+	void velocity(int velo);
+	void nextTick(int *outbuf, int buflen);
+	void noteOn(int n, int onVelo);
+	bool noteOff(int note);
+	void pitchBend(int value);
+	void recalculateFrequency();
+};
+
+class MidiChannel_YM2612 : public MidiChannel {
+protected:
+	uint16 _rate;
+	Voice2612 *_voices;
+	Voice2612 *_next_voice;
+
+public:
+	void removeAllVoices();
+	void nextTick(int *outbuf, int buflen);
+	void rate(uint16 r);
+
+public:
+	MidiChannel_YM2612();
+	virtual ~MidiChannel_YM2612();
+
+	// MidiChannel interface
+	MidiDriver *device() { return 0; }
+	byte getNumber() { return 0; }
+	void release() { }
+	void send(uint32 b) { }
+	void noteOff(byte note);
+	void noteOn(byte note, byte onVelo);
+	void programChange(byte program) { }
+	void pitchBend(int16 value);
+	void controlChange(byte control, byte value);
+	void pitchBendFactor(byte value) { }
+	void sysEx_customInstrument(uint32 type, const byte *instr);
+};
+
+class MidiDriver_YM2612 : public MidiDriver_Emulated {
+protected:
+	MidiChannel_YM2612 *_channel[16];
+
+	int _next_voice;
+	int _volume;
+
+protected:
+	void nextTick(int16 *buf1, int buflen);
+	int volume(int val = -1) { if (val >= 0) _volume = val; return _volume; }
+	void rate(uint16 r);
+
+	void generateSamples(int16 *buf, int len);
+
+public:
+	MidiDriver_YM2612(Audio::Mixer *mixer);
+	virtual ~MidiDriver_YM2612();
+
+	static void createLookupTables();
+	static void removeLookupTables();
+
+	int open();
+	void close();
+	void send(uint32 b);
+	void send(byte channel, uint32 b); // Supports higher than channel 15
+	uint32 property(int prop, uint32 param) { return 0; }
+
+	void setPitchBendRange(byte channel, uint range) { }
+	void sysEx(const byte *msg, uint16 length);
+
+	MidiChannel *allocateChannel() { return 0; }
+	MidiChannel *getPercussionChannel() { return 0; }
+
+
+	// AudioStream API
+	bool isStereo() const { return true; }
+	int getRate() const { return _mixer->getOutputRate(); }
+};
+
+#endif
+


Property changes on: scummvm/trunk/sound/softsynth/ym2612.h
___________________________________________________________________
Name: svn:mime-type
   + text/plain
Name: svn:eol-style
   + native


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