diff options
| -rw-r--r-- | engines/scumm/detection_tables.h | 9 | ||||
| -rw-r--r-- | engines/scumm/imuse/imuse_player.cpp | 7 | ||||
| -rw-r--r-- | engines/scumm/midiparser_eup.cpp | 222 | ||||
| -rw-r--r-- | engines/scumm/module.mk | 2 | ||||
| -rw-r--r-- | engines/scumm/player_towns.cpp | 557 | ||||
| -rw-r--r-- | engines/scumm/player_towns.h | 120 | ||||
| -rw-r--r-- | engines/scumm/saveload.cpp | 9 | ||||
| -rw-r--r-- | engines/scumm/saveload.h | 2 | ||||
| -rw-r--r-- | engines/scumm/script_v5.cpp | 17 | ||||
| -rw-r--r-- | engines/scumm/scumm.cpp | 10 | ||||
| -rw-r--r-- | engines/scumm/scumm.h | 2 | ||||
| -rw-r--r-- | engines/scumm/sound.cpp | 94 | ||||
| -rw-r--r-- | sound/softsynth/fmtowns_pc98/towns_audio.cpp | 18 | ||||
| -rw-r--r-- | sound/softsynth/fmtowns_pc98/towns_audio.h | 1 | ||||
| -rw-r--r-- | sound/softsynth/fmtowns_pc98/towns_pc98_driver.cpp | 10 | ||||
| -rw-r--r-- | sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp | 12 | ||||
| -rw-r--r-- | sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h | 10 | 
17 files changed, 771 insertions, 331 deletions
diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h index f275b1c93f..98fab9468a 100644 --- a/engines/scumm/detection_tables.h +++ b/engines/scumm/detection_tables.h @@ -186,6 +186,7 @@ using Common::GUIO_NONE;  using Common::GUIO_NOLAUNCHLOAD;  using Common::GUIO_NOMIDI;  using Common::GUIO_NOSPEECH; +using Common::GUIO_MIDITOWNS;  // The following table contains information about variants of our various  // games. We index into it with help of md5table (from scumm-md5.h), to find @@ -217,19 +218,19 @@ static const GameSettings gameVariantsTable[] = {  	{"zak", "V1",       "v1", GID_ZAK, 1, 0, MDT_PCSPK | MDT_PCJR, 0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},  	{"zak", "V2",       "v2", GID_ZAK, 2, 0, MDT_PCSPK | MDT_PCJR, 0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI}, -	{"zak", "FM-TOWNS",    0, GID_ZAK, 3, 0, MDT_TOWNS, GF_OLD256 | GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI}, +	{"zak", "FM-TOWNS",    0, GID_ZAK, 3, 0, MDT_TOWNS, GF_OLD256 | GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI | GUIO_MIDITOWNS},  	{"indy3", "EGA",      "ega", GID_INDY3, 3, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS | MDT_ADLIB, 0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},  	{"indy3", "No AdLib", "ega", GID_INDY3, 3, 0, MDT_PCSPK | MDT_PCJR,             0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},  	{"indy3", "VGA",      "vga", GID_INDY3, 3, 0, MDT_PCSPK | MDT_PCJR | MDT_ADLIB, GF_OLD256 | GF_FEW_LOCALS,                  Common::kPlatformPC, GUIO_NOSPEECH | GUIO_NOMIDI}, -	{"indy3", "FM-TOWNS",     0, GID_INDY3, 3, 0, MDT_TOWNS,             GF_OLD256 | GF_FEW_LOCALS | GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI}, +	{"indy3", "FM-TOWNS",     0, GID_INDY3, 3, 0, MDT_TOWNS,             GF_OLD256 | GF_FEW_LOCALS | GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI | GUIO_MIDITOWNS},  	{"loom", "EGA",      "ega", GID_LOOM, 3, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},  	{"loom", "No AdLib", "ega", GID_LOOM, 3, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS,                        0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},  #ifdef USE_RGB_COLOR  	{"loom", "PC-Engine",    0, GID_LOOM, 3, 0, MDT_NONE,                         GF_AUDIOTRACKS | GF_OLD256 | GF_16BIT_COLOR, Common::kPlatformPCEngine, GUIO_NOSPEECH | GUIO_NOMIDI},  #endif -	{"loom", "FM-TOWNS",     0, GID_LOOM, 3, 0, MDT_TOWNS,                        GF_AUDIOTRACKS | GF_OLD256, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI}, +	{"loom", "FM-TOWNS",     0, GID_LOOM, 3, 0, MDT_TOWNS,                        GF_AUDIOTRACKS | GF_OLD256, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI | GUIO_MIDITOWNS},  	{"loom", "VGA",      "vga", GID_LOOM, 4, 0, MDT_NONE,                         GF_AUDIOTRACKS,             Common::kPlatformPC, GUIO_NOSPEECH | GUIO_NOMIDI},  	{"pass", 0, 0, GID_PASS, 4, 0, MDT_PCSPK | MDT_PCJR | MDT_ADLIB, GF_16COLOR, Common::kPlatformPC, GUIO_NOSPEECH | GUIO_NOMIDI}, @@ -239,7 +240,7 @@ static const GameSettings gameVariantsTable[] = {  	{"monkey", "No AdLib", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_PCJR,                        GF_16COLOR,     Common::kPlatformAtariST, GUIO_NOSPEECH | GUIO_NOMIDI},  	{"monkey", "Demo",     "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_PCJR | MDT_ADLIB,            GF_16COLOR,     Common::kPlatformPC, GUIO_NOSPEECH | GUIO_NOMIDI},  	{"monkey", "CD",           0, GID_MONKEY,     5, 0, MDT_ADLIB,                        GF_AUDIOTRACKS, UNK, GUIO_NOSPEECH | GUIO_NOMIDI}, -	{"monkey", "FM-TOWNS",     0, GID_MONKEY,     5, 0, MDT_ADLIB,                        GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI}, +	{"monkey", "FM-TOWNS",     0, GID_MONKEY,     5, 0, MDT_TOWNS,                        GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI | GUIO_MIDITOWNS},  	{"monkey", "SEGA",         0, GID_MONKEY,     5, 0, MDT_NONE,                         GF_AUDIOTRACKS, Common::kPlatformSegaCD, GUIO_NOSPEECH | GUIO_NOMIDI},  	{"monkey2",  0, 0, GID_MONKEY2,  5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH}, diff --git a/engines/scumm/imuse/imuse_player.cpp b/engines/scumm/imuse/imuse_player.cpp index 73aec472e4..6b38f80df1 100644 --- a/engines/scumm/imuse/imuse_player.cpp +++ b/engines/scumm/imuse/imuse_player.cpp @@ -47,7 +47,6 @@ namespace Scumm {  #define PERCUSSION_CHANNEL 9  extern MidiParser *MidiParser_createRO(); -extern MidiParser *MidiParser_createEUP();  uint16 Player::_active_notes[128]; @@ -195,7 +194,11 @@ int Player::start_seq_sound(int sound, bool reset_vars) {  		_parser = MidiParser_createRO();  	} else if (!memcmp(ptr, "SO", 2)) {  		// Euphony (FM-TOWNS) resource -		_parser = MidiParser_createEUP(); +		 +		//////////// REMOVE +		//_parser = MidiParser_createEUP(); +		/////////// +  	} else if (!memcmp(ptr, "FORM", 4)) {  		// Humongous Games XMIDI resource  		_parser = MidiParser::createParser_XMIDI(); diff --git a/engines/scumm/midiparser_eup.cpp b/engines/scumm/midiparser_eup.cpp deleted file mode 100644 index 592d43f7fe..0000000000 --- a/engines/scumm/midiparser_eup.cpp +++ /dev/null @@ -1,222 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * $URL$ - * $Id$ - * - */ - - -#include "sound/midiparser.h" -#include "sound/mididrv.h" -#include "common/util.h" - -namespace Scumm { - -/** - * The FM-TOWNS Euphony version of MidiParser. - */ -class MidiParser_EUP : public MidiParser { -protected: -	byte _instruments[6][50]; // Two extra bytes for SysEx ID and channel # -	byte *_instr_to_channel; -	struct { -		byte *enable; -		int8 *channel; -		int8 *volume; -		int8 *transpose; -	} _presets; -	bool _loop; -	byte _presend;     // Tracks which startup implied events have been sent. -	uint32 _base_tick; // Events times are relative to this base. - -protected: -	void parseNextEvent (EventInfo &info); -	void resetTracking(); - -public: -	bool loadMusic (byte *data, uint32 size); -}; - - - -////////////////////////////////////////////////// -// -// MidiParser_EUP implementation -// -////////////////////////////////////////////////// - -void MidiParser_EUP::parseNextEvent (EventInfo &info) { -	byte *pos = _position._play_pos; - -	// FIXME: The presend is for sending init events -	// that aren't actually in the stream. This would -	// be for, e.g., instrument setup. Right now, we -	// don't actually use the instruments specified -	// in the music header. We're sending fixed GM -	// program changes to get a reasonable "one-size- -	// fits-all" sound until we actually support the -	// FM synthesis capabilities of FM-TOWNS. -	for (; _presend < 12; ++_presend) { -		if (_instr_to_channel[_presend >> 1] >= 16) -			continue; -		info.start = pos; -		info.delta = 0; -		if (_presend & 1) { -			byte *data = &_instruments[_presend >> 1][0]; -			data[1] = _instr_to_channel[_presend >> 1]; -			info.event = 0xF0; -			info.ext.data = data; -			info.length = 48; -		} else { -			info.event = 0xB0 | (_presend >> 1); -			info.basic.param1 = 121; -			info.basic.param2 = 0; -		} -		++_presend; -		return; -	} - -	while (true) { -		byte cmd = *pos; -		if ((cmd & 0xF0) == 0x90) { -			byte preset = pos[1]; -			byte channel = _presets.channel[preset]; -			if (channel >= 16) -				channel = cmd & 0x0F; -			uint16 tick = (pos[2] | ((uint16) pos[3] << 7)) + _base_tick; -			int note = (int) pos[4] + _presets.transpose[preset]; -			int volume = (int) pos[5]; -			// HACK: Loom-Towns distaff tracks seem to -			// contain zero-volume note events, so change -			// those to full volume. -			if (!volume) -				volume = 127; -			volume += _presets.volume[preset]; -			if (volume > 127) -				volume = 127; -			else if (volume < 0) -				volume = 0; -			pos += 6; -			if (_presets.enable[preset]) { -				uint16 duration = pos[1] | (pos[2] << 4); -				info.start = pos; -				uint32 last = _position._last_event_tick; -				info.delta = (tick < last) ? 0 : (tick - last); -				info.event = 0x90 | channel; -				info.length = duration; -				info.basic.param1 = note; -				info.basic.param2 = volume; -				pos += 6; -				break; -			} -			pos += 6; -		} else if (cmd == 0xF2) { -			// This is a "measure marker" of sorts. -			// It advances the "base time", to which -			// all event times are relative. -			_base_tick += (pos[3] << 7) | pos[2]; -			pos += 6; -		} else if (cmd == 0xF8) { -			// TODO: Implement this. -			pos += 6; -		} else if (cmd == 0xFD || cmd == 0xFE) { -			// End of track. -			if (_loop && false) { -				// TODO: Implement this. -			} else { -				info.start = pos; -				uint32 last = _position._last_event_tick; -				info.delta = (_base_tick < last) ? 0 : (_base_tick - last); -				info.event = 0xFF; -				info.length = 0; -				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_EUP::loadMusic (byte *data, uint32 size) { -	unloadMusic(); -	byte *pos = data; -	int i; - -	if (memcmp(pos, "SO", 2)) { -		error("'SO' header expected but found '%c%c' instead.", pos[0], pos[1]); -		return false; -	} - -	byte numInstruments = pos[16]; -	pos += 16 + 2; -	for (i = 0; i < numInstruments; ++i) { -		_instruments[i][0] = 0x7C; -		memcpy (&_instruments[i][2], pos, 48); -		pos += 48; -	} - -	// Load the prest pointers -	_presets.enable = pos; -	pos += 32; -	_presets.channel = (int8 *) pos; -	pos += 32; -	_presets.volume = (int8 *) pos; -	pos += 32; -	_presets.transpose = (int8 *) pos; -	pos += 32; - -	pos += 8; // Unknown bytes -	_instr_to_channel = pos; // Instrument-to-channel mapping -	pos += 6; -	pos += 4; // Skip the music size for now. -	pos++;    // Unknown byte -	byte tempo = *pos++; -	_loop = (*pos++ != 1); -	pos++;    // Unknown byte - -	_num_tracks = 1; -	_ppqn = 120; -	_tracks[0] = pos; - -	// Note that we assume the original data passed in -	// will persist beyond this call, i.e. we do NOT -	// copy the data to our own buffer. Take warning.... -	resetTracking(); -	setTempo (1000000 * 60 / tempo); -	setTrack (0); -	return true; -} - -void MidiParser_EUP::resetTracking() { -	MidiParser::resetTracking(); -	_presend = 0; -	_base_tick = 0; -} - -MidiParser *MidiParser_createEUP() { return new MidiParser_EUP; } - -} // End of namespace Scumm diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk index e5f0745dd6..14d1f5fdd4 100644 --- a/engines/scumm/module.mk +++ b/engines/scumm/module.mk @@ -29,7 +29,6 @@ MODULE_OBJS := \  	imuse/sysex_samnmax.o \  	imuse/sysex_scumm.o \  	input.o \ -	midiparser_eup.o \  	midiparser_ro.o \  	object.o \  	palette.o \ @@ -37,6 +36,7 @@ MODULE_OBJS := \  	player_nes.o \  	player_pce.o \  	player_sid.o \ +	player_towns.o \  	player_v1.o \  	player_v2.o \  	player_v2a.o \ diff --git a/engines/scumm/player_towns.cpp b/engines/scumm/player_towns.cpp new file mode 100644 index 0000000000..ab1b1ed585 --- /dev/null +++ b/engines/scumm/player_towns.cpp @@ -0,0 +1,557 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + + +#include "scumm/sound.h" +#include "scumm/player_towns.h" + +namespace Scumm { + +Player_Towns::Player_Towns(ScummEngine *vm, Audio::Mixer *mixer) : _vm(vm) { +	_cdaCurrentSound = _eupCurrentSound = _cdaNumLoops = 0; +	_cdaForceRestart = 0; +	memset(_pcmCurrentSound, 0, sizeof(_pcmCurrentSound)); +	_cdaVolLeft = _cdaVolRight = 0; + +	_eupVolLeft = _eupVolRight = 0; +	memset(&_ovrCur, 0, sizeof(SoundOvrParameters)); +	_soundOverride = 0; + +	if (_vm->_game.version == 3) { +		_soundOverride = new SoundOvrParameters[200]; +		memset(_soundOverride, 0, 200 * sizeof(SoundOvrParameters)); +	} + +	_eupLooping = false; +	_unkFlags = 0x33; + +	_driver = new TownsEuphonyDriver(mixer); +} + +Player_Towns::~Player_Towns() { +	delete[] _soundOverride; +	delete _driver; +} + +bool Player_Towns::init() { +	if (!_driver) +		return false; +	 +	if (!_driver->init()) +		return false; + +	_driver->reserveSoundEffectChannels(8); + +	// Treat all 6 fm channels and all 8 pcm channels as sound effect channels +	// since music seems to exist as CD audio only in the games which use this +	// MusicEngine implementation. +	_driver->intf()->setSoundEffectChanMask(-1); + +	setVolumeCD(255, 255); + +	return true; +} + +void Player_Towns::setMusicVolume(int vol) { +	_driver->setMusicVolume(vol); +} + +void Player_Towns::setSfxVolume(int vol) { +	_driver->setSoundEffectVolume(vol); +} + +void Player_Towns::startSound(int sound) { +	uint8 *ptr = _vm->getResourceAddress(rtSound, sound); +	if (_vm->_game.version != 3) { +		ptr += 2; +	} else if (_soundOverride && sound > 0 && sound < 200) { +		memcpy(&_ovrCur, &_soundOverride[sound], sizeof(SoundOvrParameters)); +		memset(&_soundOverride[sound], 0, sizeof(SoundOvrParameters)); +	} + +	int type = ptr[13]; + +	if (type == 0) { +		playPcmTrack(sound, ptr + 6); +	} else if (type == 1) { +		playEuphonyTrack(sound, ptr + 6); +	} else if (type == 2) { +		playCdaTrack(sound, ptr + 6); +	} +	memset(&_ovrCur, 0, sizeof(SoundOvrParameters)); +} + +void Player_Towns::stopSound(int sound) { +	if (sound != 0 && sound == _cdaCurrentSound) { +		_cdaCurrentSound = 0; +		_vm->_sound->stopCD(); +		_vm->_sound->stopCDTimer(); +	} + +	if (sound != 0 && sound == _eupCurrentSound) { +		_eupCurrentSound = 0; +		_eupLooping = false; +		_driver->stopParser(); +	} +	 +	stopPcmTrack(sound); +} + +void Player_Towns::stopAllSounds() { +	_cdaCurrentSound = 0; +	_vm->_sound->stopCD(); +	_vm->_sound->stopCDTimer(); + +	// Loom disasm seems to stop only CD audio and PCM sounds here +	/*_eupCurrentSound = 0; +	_eupLooping = false; +	_driver->stopParser();*/ + +	stopPcmTrack(0); +} + +int Player_Towns::getSoundStatus(int sound) const { +	if (sound == _cdaCurrentSound) +		return _vm->_sound->pollCD(); +	if (sound == _eupCurrentSound) +		return _driver->parserIsPlaying() ? 1 : 0; +	for (int i = 1; i < 9; i++) { +		if (_pcmCurrentSound[i].index == sound) +			return _driver->soundEffectIsPlaying(i + 0x3f) ? 1 : 0; +	} +	return 0; +} + +int32 Player_Towns::doCommand(int numargs, int args[]) { +	int32 res = 0; +	 +	switch (args[0]) { +	case 2: +		_driver->intf()->callback(73, 0); +		break; + +	case 3: +		restartLoopingSounds(); +		break; + +	case 8: +		startSound(args[1]); +		break; + +	case 9: +		_vm->_sound->stopSound(args[1]); +		break; + +	case 11: +		stopPcmTrack(0); +		break; + +	case 14: +		startSoundEx(args[1], args[2], args[3], args[4]); +		break; + +	case 15: +		stopSoundSuspendLooping(args[1]); +		break; + +	default: +		warning("Player_Towns::doCommand: Unknown command %d", args[0]); +		break; +	} + +	return res; +} + +void Player_Towns::setVolumeCD(int left, int right) { +	_cdaVolLeft = left & 0xff; +	_cdaVolRight = right & 0xff; +	_driver->setOutputVolume(1, left >> 1, right >> 1); +} + +void Player_Towns::setSoundVolume(int sound, int left, int right) { +	if (_soundOverride && sound > 0 && sound < 200) { +		_soundOverride[sound].vLeft = left; +		_soundOverride[sound].vRight = right; +	} +} + +void Player_Towns::setSoundNote(int sound, int note) { +	if (_soundOverride && sound > 0 && sound < 200) +		_soundOverride[sound].note = note; +} + +void Player_Towns::saveLoadWithSerializer(Serializer *ser) { +	_cdaCurrentSoundTemp = (_vm->_sound->pollCD() && _cdaNumLoops > 1) ? _cdaCurrentSound & 0xff : 0; +	_cdaNumLoopsTemp = _cdaNumLoops & 0xff; + +	static const SaveLoadEntry cdEntries[] = { +		MKLINE(Player_Towns, _cdaCurrentSoundTemp, sleUint8, VER(81)), +		MKLINE(Player_Towns, _cdaNumLoopsTemp, sleUint8, VER(81)), +		MKLINE(Player_Towns, _cdaVolLeft, sleUint8, VER(81)), +		MKLINE(Player_Towns, _cdaVolRight, sleUint8, VER(81)), +		MKEND() +	}; + +	ser->saveLoadEntries(this, cdEntries); + +	if (!_eupLooping && !_driver->parserIsPlaying()) +		_eupCurrentSound = 0; + +	static const SaveLoadEntry eupEntries[] = { +		MKLINE(Player_Towns, _eupCurrentSound, sleUint8, VER(81)), +		MKLINE(Player_Towns, _eupLooping, sleUint8, VER(81)), +		MKLINE(Player_Towns, _eupVolLeft, sleUint8, VER(81)), +		MKLINE(Player_Towns, _eupVolRight, sleUint8, VER(81)), +		MKEND() +	}; + +	ser->saveLoadEntries(this, eupEntries); + +	static const SaveLoadEntry pcmEntries[] = { +		MKLINE(PcmCurrentSound, index, sleInt16, VER(81)), +		MKLINE(PcmCurrentSound, chan, sleInt16, VER(81)), +		MKLINE(PcmCurrentSound, note, sleUint8, VER(81)), +		MKLINE(PcmCurrentSound, velo, sleUint8, VER(81)), +		MKLINE(PcmCurrentSound, pan, sleUint8, VER(81)), +		MKLINE(PcmCurrentSound, paused, sleUint8, VER(81)), +		MKLINE(PcmCurrentSound, looping, sleUint8, VER(81)), +		MKLINE(PcmCurrentSound, priority, sleUint32, VER(81)), +		MKEND() +	}; + +	for (int i = 1; i < 9; i++) { +		if (!_pcmCurrentSound[i].index) +			continue; + +		if (_driver->soundEffectIsPlaying(i + 0x3f)) +			continue; + +		_driver->stopSoundEffect(i + 0x3f); + +		_pcmCurrentSound[i].index = 0; +	} + +	ser->saveLoadArrayOf(_pcmCurrentSound, 9, sizeof(PcmCurrentSound), pcmEntries); +} + +void Player_Towns::restoreAfterLoad() { +	setVolumeCD(_cdaVolLeft, _cdaVolRight); +	 +	if (_cdaCurrentSoundTemp) { +		uint8 *ptr = _vm->getResourceAddress(rtSound, _cdaCurrentSoundTemp) + 6; +		if (_vm->_game.version != 3) +			ptr += 2; +		 +		if (ptr[7] == 2) { +			playCdaTrack(_cdaCurrentSoundTemp, ptr, true); +			_cdaCurrentSound = _cdaCurrentSoundTemp; +			_cdaNumLoops = _cdaNumLoopsTemp; +		} +	} + +	if (_eupCurrentSound) { +		uint8 *ptr = _vm->getResourceAddress(rtSound, _eupCurrentSound) + 6; +		if (_vm->_game.version != 3) +			ptr += 2; +		 +		if (ptr[7] == 1) { +			setSoundVolume(_eupCurrentSound, _eupVolLeft, _eupVolRight); +			playEuphonyTrack(_eupCurrentSound, ptr); +		} +	} + +	for (int i = 1; i < 9; i++) { +		if (!_pcmCurrentSound[i].index) +			continue; + +		uint8 *ptr = _vm->getResourceAddress(rtSound, _pcmCurrentSound[i].index); +		if (!ptr) +			continue; + +		if (_vm->_game.version != 3) +			ptr += 2; + +		if (ptr[13]) +			continue; + +		playPcmTrack(_pcmCurrentSound[i].index, ptr + 6, _pcmCurrentSound[i].velo, _pcmCurrentSound[i].pan, _pcmCurrentSound[i].note); +	} +} + +int Player_Towns::getNextFreePcmChannel(int sound, int sfxChanRelIndex) { +	int chan = 0; +	for (int i = 8; i; i--) { +		if (!_pcmCurrentSound[i].index) { +			chan = i; +			continue; +		} + +		if (_driver->soundEffectIsPlaying(i + 0x3f)) +			continue; + +		chan = i; +		_vm->_sound->stopSound(_pcmCurrentSound[chan].index); +	} + +	if (!chan) { +		uint16 l = 0xffff; +		uint8 *ptr = 0; +		for (int i = 8; i; i--) { +			ptr = _vm->getResourceAddress(rtSound, _pcmCurrentSound[i].index) + 6; +			uint16 a = READ_LE_UINT16(ptr + 10); +			if (a <= l) { +				chan = i; +				l = a; +			} +		} + +		ptr = _vm->getResourceAddress(rtSound, sound) + 6; +		if (l <= READ_LE_UINT16(ptr + 10)) +			_vm->_sound->stopSound(_pcmCurrentSound[chan].index); +		else +			chan = 0; +	} + +	if (chan) { +		_pcmCurrentSound[chan].index = sound; +		_pcmCurrentSound[chan].chan = sfxChanRelIndex; +	} + +	return chan; +} + +void Player_Towns::restartLoopingSounds() { +	if (_cdaNumLoops && !_cdaForceRestart) +		_cdaForceRestart = 1; + +	for (int i = 1; i < 9; i++) { +		if (!_pcmCurrentSound[i].paused) +			continue; + +		_pcmCurrentSound[i].paused = 0; + +		uint8 *ptr = _vm->getResourceAddress(rtSound, _pcmCurrentSound[i].index); +		if (!ptr) +			continue; +		ptr += 24; + +		int c = 1; +		while (_pcmCurrentSound[i].chan != c) { +			ptr = ptr + READ_LE_UINT32(&ptr[12]) + 32; +			c++; +		} + +		_driver->playSoundEffect(i + 0x3f, _pcmCurrentSound[i].note, _pcmCurrentSound[i].velo, ptr); +	} + +	_driver->intf()->callback(73, 1); +} + +void Player_Towns::startSoundEx(int sound, int velo, int pan, int note) { +	uint8 *ptr = _vm->getResourceAddress(rtSound, sound) + 2; + +	if (pan > 99) +		pan = 99; + +	velo = velo ? (velo * ptr[14] + 50) / 100 : ptr[14]; +	velo = CLIP(velo, 1, 255); + +	if (ptr[13] == 0) { +		velo >>= 1; + +		if (!velo) +			velo = 1; + +		pan = pan ? (((pan << 7) - pan) + 50) / 100 : 64; + +		playPcmTrack(sound, ptr + 6, velo, pan, note); + +	} else if (ptr[13] == 2) { +		int volLeft = velo; +		int volRight = velo; +		 +		if (pan < 50) +			volRight = ((pan * 2 + 1) * velo + 50) / 100; +		else if (pan > 50) +			volLeft = (((99 - pan) * 2 + 1) * velo + 50) / 100; + +		setVolumeCD(volLeft, volRight); + +		if (!_cdaForceRestart && sound == _cdaCurrentSound) +			return; + +		playCdaTrack(sound, ptr + 6, true); +	} +} + +void Player_Towns::stopSoundSuspendLooping(int sound) { +	if (!sound) { +		return; +	} else if (sound == _cdaCurrentSound) { +		if (_cdaNumLoops && _cdaForceRestart) +			_cdaForceRestart = 1;		 +	} else { +		for (int i = 1; i < 9; i++) { +			if (sound == _pcmCurrentSound[i].index) { +				if (!_driver->soundEffectIsPlaying(i + 0x3f)) +					continue; +				_driver->stopSoundEffect(i + 0x3f); +				if (_pcmCurrentSound[i].looping) +					_pcmCurrentSound[i].paused = 1; +				else  +					_pcmCurrentSound[i].index = 0; +			} +		} +	} +} + +void Player_Towns::playEuphonyTrack(int sound, const uint8 *data) { +	const uint8 *pos = data + 16; +	const uint8 *src = pos + data[14] * 48; +	const uint8 *trackData = src + 150; + +	for (int i = 0; i < 32; i++) +		_driver->chanEnable(i, *src++); +	for (int i = 0; i < 32; i++) +		_driver->chanMode(i, 0xff); +	for (int i = 0; i < 32; i++) +		_driver->chanOrdr(i, *src++); +	for (int i = 0; i < 32; i++) +		_driver->chanVolumeShift(i, *src++); +	for (int i = 0; i < 32; i++) +		_driver->chanNoteShift(i, *src++); + +	src += 8; +	for (int i = 0; i < 6; i++) +		_driver->assignChannel(i, *src++); + +	for (int i = 0; i < data[14]; i++) { +		_driver->loadInstrument(i, i, pos + i * 48); +		_driver->intf()->callback(4, i, i); +	} + +	_eupVolLeft = _ovrCur.vLeft; +	_eupVolRight = _ovrCur.vRight; +	int lvl = _ovrCur.vLeft + _ovrCur.vRight; +	if (!lvl) +		lvl = data[8] + data[9]; +	lvl >>= 2; + +	for (int i = 0; i < 6; i++) +		_driver->chanVolume(i, lvl); + +	uint32 trackSize = READ_LE_UINT32(src); +	src += 4; +	uint8 startTick = *src++; +	 +	_driver->setMusicTempo(*src++); +	_driver->startMusicTrack(trackData, trackSize, startTick); + +	_eupLooping = (*src != 1) ? 1 : 0; +	_driver->setMusicLoop(_eupLooping != 0); +	_driver->continueParsing(); +	_eupCurrentSound = sound; +} + +void Player_Towns::playPcmTrack(int sound, const uint8 *data, int velo, int pan, int note) { +	const uint8 *ptr = data; +	const uint8 *sfxData = ptr + 16; +	 +	int note2, velocity; + +	if (velo) +		velocity = velo; +	else if (_ovrCur.vLeft + _ovrCur.vRight) +		velocity = (_ovrCur.vLeft + _ovrCur.vRight) >> 2; +	else +		velocity = ptr[8] >> 1; + +	int numChan = ptr[14]; +	for (int i = 0; i < numChan; i++) { +		int chan = getNextFreePcmChannel(sound, i); +		if (!chan) +			return; +		 +		_driver->intf()->callback(70, _unkFlags); +		_driver->chanPanPos(chan + 0x3f, pan); +		 +		if (note) +			note2 = note; +		else if (_ovrCur.note) +			note2 = _ovrCur.note; +		else +			note2 = sfxData[28]; +		 +		_driver->playSoundEffect(chan + 0x3f, note2, velocity, sfxData); +		 +		_pcmCurrentSound[chan].note = note2; +		_pcmCurrentSound[chan].velo = velocity; +		_pcmCurrentSound[chan].pan = pan; +		_pcmCurrentSound[chan].paused = 0; +		_pcmCurrentSound[chan].looping = READ_LE_UINT32(&sfxData[20]) ? 1 : 0; + +		sfxData += (READ_LE_UINT32(&sfxData[12]) + 32); +	} +} + +void Player_Towns::playCdaTrack(int sound, const uint8 *data, bool skipTrackVelo) { +	const uint8 *ptr = data; + +	if (!sound) +		return; + +	if (!skipTrackVelo) { +		if (_ovrCur.vLeft + _ovrCur.vRight) +			setVolumeCD(_ovrCur.vLeft, _ovrCur.vRight); +		else +			setVolumeCD(ptr[8], ptr[9]); +	} + +	if (sound == _cdaCurrentSound && _vm->_sound->pollCD() == 1)			 +		return; + +	ptr += 16; + +	int track = ptr[0]; +	_cdaNumLoops = ptr[1]; +	int start = (ptr[2] * 60 + ptr[3]) * 75 + ptr[4]; +	int end = (ptr[5] * 60 + ptr[6]) * 75 + ptr[7]; + +	_vm->_sound->playCDTrack(track, _cdaNumLoops == 0xff ? -1 : _cdaNumLoops, start, end <= start ? 0 : end - start); +	_cdaForceRestart = 0; +	_cdaCurrentSound = sound; +} + +void Player_Towns::stopPcmTrack(int sound) { +	for (int i = 1; i < 9; i++) { +		if (sound == _pcmCurrentSound[i].index || !sound) { +			_driver->stopSoundEffect(i + 0x3f); +			_pcmCurrentSound[i].index = 0; +		} +	} +} + +} // End of namespace Scumm + diff --git a/engines/scumm/player_towns.h b/engines/scumm/player_towns.h new file mode 100644 index 0000000000..6d87c93c09 --- /dev/null +++ b/engines/scumm/player_towns.h @@ -0,0 +1,120 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef SCUMM_PLAYER_TOWNS_H +#define SCUMM_PLAYER_TOWNS_H + +#include "scumm/scumm.h" +#include "scumm/music.h" +#include "sound/softsynth/fmtowns_pc98/towns_euphony.h" + +namespace Scumm { + +class Player_Towns : public MusicEngine { +public: +	Player_Towns(ScummEngine *vm, Audio::Mixer *mixer); +	virtual ~Player_Towns(); + +	bool init(); + +	void setMusicVolume(int vol); +	void setSfxVolume(int vol); +	void startSound(int sound); +	void stopSound(int sound); +	void stopAllSounds(); + +	int getSoundStatus(int sound) const; +	int getCurrentCdaSound() { return _cdaCurrentSound; }  +	int getCurrentCdaVolume() { return (_cdaVolLeft + _cdaVolRight + 1) >> 1; }  + +	virtual int32 doCommand(int numargs, int args[]); + +	void setVolumeCD(int left, int right); +	void setSoundVolume(int sound, int left, int right); +	void setSoundNote(int sound, int note); + +	void saveLoadWithSerializer(Serializer *ser); +	void restoreAfterLoad(); + +	TownsEuphonyDriver *driver() { return _driver; } + +protected: +	virtual int getNextFreePcmChannel(int sound, int sfxChanRelIndex); + +private: +	void restartLoopingSounds(); +	void startSoundEx(int sound, int velo, int pan, int note); +	void stopSoundSuspendLooping(int sound); + +	void playEuphonyTrack(int sound, const uint8 *data); +	void playPcmTrack(int sound, const uint8 *data, int velo = 0, int pan = 64, int note = 0); +	void playCdaTrack(int sound, const uint8 *data, bool skipTrackVelo = false); + +	void stopPcmTrack(int sound); + +	uint8 _cdaVolLeft; +	uint8 _cdaVolRight; + +	struct SoundOvrParameters { +		uint8 vLeft; +		uint8 vRight; +		uint8 note; +	}; + +	SoundOvrParameters *_soundOverride; +	SoundOvrParameters _ovrCur; +	 +	uint8 _unkFlags; + +	struct PcmCurrentSound { +		uint16 index; +		uint16 chan; +		uint8 note; +		uint8 velo; +		uint8 pan; +		uint8 paused; +		uint8 looping; +		uint32 priority; +	} _pcmCurrentSound[9]; + +	uint8 _eupCurrentSound; +	uint8 _eupLooping; +	uint8 _eupVolLeft; +	uint8 _eupVolRight; + +	uint8 _cdaCurrentSound; +	uint8 _cdaNumLoops; +	uint8 _cdaForceRestart; + +	uint8 _cdaCurrentSoundTemp; +	uint8 _cdaNumLoopsTemp; + +	TownsEuphonyDriver *_driver; +	ScummEngine *_vm; +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp index 3cc619f630..ca48a2b86a 100644 --- a/engines/scumm/saveload.cpp +++ b/engines/scumm/saveload.cpp @@ -32,6 +32,7 @@  #include "scumm/charset.h"  #include "scumm/imuse_digi/dimuse.h"  #include "scumm/imuse/imuse.h" +#include "player_towns.h"  #include "scumm/he/intern_he.h"  #include "scumm/object.h"  #include "scumm/resource.h" @@ -447,6 +448,9 @@ bool ScummEngine::loadState(int slot, bool compat) {  	// Update volume settings  	syncSoundSettings(); +	if (_townsPlayer && (hdr.ver >= VER(81))) +		_townsPlayer->restoreAfterLoad(); +  	// Init NES costume data  	if (_game.platform == Common::kPlatformNES) {  		if (hdr.ver < VER(47)) @@ -1394,6 +1398,11 @@ void ScummEngine::saveOrLoad(Serializer *s) {  		_imuse->save_or_load(s, this);  	} + +	// Save/load FM-Towns audio status +	if (_townsPlayer) +		_townsPlayer->saveLoadWithSerializer(s); +  	//  	// Save/load the charset renderer state  	// diff --git a/engines/scumm/saveload.h b/engines/scumm/saveload.h index fafb6b383f..44c8cc7d60 100644 --- a/engines/scumm/saveload.h +++ b/engines/scumm/saveload.h @@ -50,7 +50,7 @@ namespace Scumm {   * only saves/loads those which are valid for the version of the savegame   * which is being loaded/saved currently.   */ -#define CURRENT_VER 80 +#define CURRENT_VER 81  /**   * An auxillary macro, used to specify savegame versions. We use this instead diff --git a/engines/scumm/script_v5.cpp b/engines/scumm/script_v5.cpp index 556f5b0af1..39e691c891 100644 --- a/engines/scumm/script_v5.cpp +++ b/engines/scumm/script_v5.cpp @@ -29,6 +29,7 @@  #include "scumm/scumm_v3.h"  #include "scumm/scumm_v5.h"  #include "scumm/sound.h" +#include "scumm/player_towns.h"  #include "scumm/util.h"  #include "scumm/verbs.h" @@ -1595,21 +1596,18 @@ void ScummEngine_v5::o5_resourceRoutines() {  		debug(0, "o5_resourceRoutines %d not yet handled (script %d)", op, vm.slot[_currentScript].number);  		break;  	case 35: -		// TODO: Might be used to set CD volume in FM-TOWNS Loom -		foo = getVarOrDirectByte(PARAM_2); -		debug(0, "o5_resourceRoutines %d not yet handled (script %d)", op, vm.slot[_currentScript].number); +		if (_townsPlayer) +			_townsPlayer->setVolumeCD(getVarOrDirectByte(PARAM_2), resid);  		break;  	case 36: -		// TODO: Sets the loudness of a sound resource. Used in Indy3 and Zak.  		foo = getVarOrDirectByte(PARAM_2);  		bar = fetchScriptByte(); -		debug(0, "o5_resourceRoutines %d not yet handled (script %d)", op, vm.slot[_currentScript].number); +		if (_townsPlayer) +			_townsPlayer->setSoundVolume(resid, foo, bar);		  		break;  	case 37: -		// TODO: Sets the pitch of a sound resource (pitch = foo - center semitones. -		// "center" is at 0x32 in the sfx resource (always 0x3C in zak256, but sometimes different in Indy3). -		foo = getVarOrDirectByte(PARAM_2); -		debug(0, "o5_resourceRoutines %d not yet handled (script %d)", op, vm.slot[_currentScript].number); +		if (_townsPlayer) +			_townsPlayer->setSoundNote(resid, getVarOrDirectByte(PARAM_2));  		break;  	default: @@ -1981,6 +1979,7 @@ void ScummEngine_v5::o5_startMusic() {  			break;  		case 0xFF:  			// TODO: Might return current CD volume in FM-TOWNS Loom. See also bug #805691. +			result = _townsPlayer->getCurrentCdaVolume();  			break;  		default:  			// TODO: return track length in seconds. We'll have to extend Sound and OSystem for this. diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 6e3815d314..46708b3c4f 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -48,6 +48,7 @@  #include "scumm/imuse_digi/dimuse.h"  #include "scumm/smush/smush_mixer.h"  #include "scumm/smush/smush_player.h" +#include "scumm/player_towns.h"  #include "scumm/insane/insane.h"  #include "scumm/he/animation_he.h"  #include "scumm/he/intern_he.h" @@ -146,6 +147,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)  	_imuse = NULL;  	_imuseDigital = NULL;  	_musicEngine = NULL; +	_townsPlayer = NULL;  	_verbs = NULL;  	_objs = NULL;  	_sound = NULL; @@ -1757,6 +1759,10 @@ void ScummEngine::setupMusic(int midi) {  		_musicEngine = new Player_V2CMS(this, _mixer);  	} else if (_game.platform == Common::kPlatform3DO && _game.heversion <= 62) {  		// 3DO versions use digital music and sound samples. +	} else if (_game.platform == Common::kPlatformFMTowns && (_game.version == 3 || _game.id == GID_MONKEY)) { +		_musicEngine = _townsPlayer = new Player_Towns(this, _mixer); +		if (!_townsPlayer->init()) +			error("Failed to initialize FM-Towns audio driver.");  	} else if (_game.version >= 3 && _game.heversion <= 62) {  		MidiDriver *nativeMidiDriver = 0;  		MidiDriver *adlibMidiDriver = 0; @@ -1806,6 +1812,10 @@ void ScummEngine::syncSoundSettings() {  		_musicEngine->setMusicVolume(soundVolumeMusic);  	} +	if (_townsPlayer) { +		_townsPlayer->setSfxVolume(soundVolumeSfx); +	} +  	_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, soundVolumeSfx);  	_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);  	_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, soundVolumeSpeech); diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index 8c3df21238..1357bad8cf 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -70,6 +70,7 @@ class CharsetRenderer;  class IMuse;  class IMuseDigital;  class MusicEngine; +class Player_Towns;  class ScummEngine;  class ScummDebugger;  class Serializer; @@ -426,6 +427,7 @@ public:  	IMuse *_imuse;  	IMuseDigital *_imuseDigital;  	MusicEngine *_musicEngine; +	Player_Towns *_townsPlayer;  	Sound *_sound;  	VerbSlot *_verbs; diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp index 28f1372746..f12625c9e3 100644 --- a/engines/scumm/sound.cpp +++ b/engines/scumm/sound.cpp @@ -31,6 +31,7 @@  #include "scumm/file.h"  #include "scumm/imuse/imuse.h"  #include "scumm/imuse_digi/dimuse.h" +#include "scumm/player_towns.h"  #include "scumm/scumm.h"  #include "scumm/sound.h"  #include "scumm/util.h" @@ -150,9 +151,10 @@ void Sound::processSoundQueues() {  						data[0] >> 8, data[0] & 0xFF,  						data[1], data[2], data[3], data[4], data[5], data[6], data[7]); -			if (_vm->_imuse) { +			if (_vm->_townsPlayer) +				_vm->VAR(_vm->VAR_SOUNDRESULT) = (short)_vm->_townsPlayer->doCommand(num, data); +			else if (_vm->_imuse)  				_vm->VAR(_vm->VAR_SOUNDRESULT) = (short)_vm->_imuse->doCommand(num, data); -			}  		}  	}  	_soundQuePos = 0; @@ -312,91 +314,6 @@ void Sound::playSound(int soundID) {  		stream = Audio::makeRawStream(sound, size, rate, Audio::FLAG_UNSIGNED);  		_mixer->playStream(Audio::Mixer::kSFXSoundType, NULL, stream, soundID);  	} -	else if ((_vm->_game.platform == Common::kPlatformFMTowns && _vm->_game.version == 3) || READ_BE_UINT32(ptr) == MKID_BE('SOUN') || READ_BE_UINT32(ptr) == MKID_BE('TOWS')) { - -		bool tows = READ_BE_UINT32(ptr) == MKID_BE('TOWS'); -		if (_vm->_game.version == 3) { -			size = READ_LE_UINT32(ptr); -		} else { -			size = READ_BE_UINT32(ptr + 4) - 2; -			if (tows) -				size += 8; -			ptr += 2; -		} - -		rate = 11025; -		int type = *(ptr + 0x0D); -		int numInstruments; - -		if (tows) -			type = 0; - -		switch (type) { -		case 0:	// Sound effect -			numInstruments = *(ptr + 0x14); -			if (tows) -				numInstruments = 1; -			ptr += 0x16; -			size -= 0x16; - -			while (numInstruments--) { -				int waveSize = READ_LE_UINT32(ptr + 0x0C); -				int loopStart = READ_LE_UINT32(ptr + 0x10) * 2; -				int loopEnd = READ_LE_UINT32(ptr + 0x14) - 1; -				rate = READ_LE_UINT32(ptr + 0x18) * 1000 / 0x62; -				ptr += 0x20; -				size -= 0x20; -				if (size < waveSize) { -					warning("Wrong wave size in sound #%i: %i", soundID, waveSize); -					waveSize = size; -				} -				sound = (byte *)malloc(waveSize); -				for (int x = 0; x < waveSize; x++) { -					byte b = *ptr++; -					if (b < 0x80) -						sound[x] = 0x7F - b; -					else -						sound[x] = b; -				} -				size -= waveSize; - -				if (loopEnd > 0) { -					Audio::SeekableAudioStream *s = Audio::makeRawStream(sound, waveSize, rate, Audio::FLAG_UNSIGNED); -					stream = new Audio::SubLoopingAudioStream(s, 0, Audio::Timestamp(0, loopStart, rate), Audio::Timestamp(0, loopEnd, rate)); -				} else { -					stream = Audio::makeRawStream(sound, waveSize, rate, Audio::FLAG_UNSIGNED); -				} -				_mixer->playStream(Audio::Mixer::kSFXSoundType, NULL, stream, soundID, 255, 0); -			} -			break; -		case 1: -			// Music (Euphony format) -			if (_vm->_musicEngine) -				_vm->_musicEngine->startSound(soundID); -			break; -		case 2: // CD track resource -			ptr += 0x16; - -			if (soundID == _currentCDSound && pollCD() == 1) { -				return; -			} - -			{ -				int track = ptr[0]; -				int loops = ptr[1]; -				int start = (ptr[2] * 60 + ptr[3]) * 75 + ptr[4]; -				int end = (ptr[5] * 60 + ptr[6]) * 75 + ptr[7]; - -				playCDTrack(track, loops == 0xff ? -1 : loops, start, end <= start ? 0 : end - start); -			} - -			_currentCDSound = soundID; -			break; -		default: -			// All other sound types are ignored -			break; -		} -	}  	else if ((_vm->_game.id == GID_LOOM) && (_vm->_game.platform == Common::kPlatformMacintosh))  {  		// Mac version of Loom uses yet another sound format  		/* @@ -480,6 +397,9 @@ void Sound::playSound(int soundID) {  		if (_vm->_musicEngine) {  			_vm->_musicEngine->startSound(soundID);  		} + +		if (_vm->_townsPlayer) +			_currentCDSound = _vm->_townsPlayer->getCurrentCdaSound();  	}  } diff --git a/sound/softsynth/fmtowns_pc98/towns_audio.cpp b/sound/softsynth/fmtowns_pc98/towns_audio.cpp index 46569dd842..bef062b8e6 100644 --- a/sound/softsynth/fmtowns_pc98/towns_audio.cpp +++ b/sound/softsynth/fmtowns_pc98/towns_audio.cpp @@ -200,7 +200,7 @@ TownsAudioInterface::TownsAudioInterface(Audio::Mixer *mixer, TownsAudioInterfac  		INTCB(notImpl),  		// 72  		INTCB(notImpl), -		INTCB(notImpl), +		INTCB(cdaToggle),  		INTCB(notImpl),  		INTCB(notImpl),  		// 76 @@ -222,9 +222,19 @@ TownsAudioInterface::TownsAudioInterface(Audio::Mixer *mixer, TownsAudioInterfac  	_timerBase = (uint32)(_baserate * 1000000.0f);  	_tickLength = 2 * _timerBase; + +	setTimerCallbackA((ChipTimerProc)&TownsAudioInterface::timerCallbackA); +	setTimerCallbackB((ChipTimerProc)&TownsAudioInterface::timerCallbackB);  }  TownsAudioInterface::~TownsAudioInterface() { +	Common::StackLock lock(_mutex); +	reset(); +	_ready = false; + +	setTimerCallbackA(); +	setTimerCallbackB(); +  	delete[] _fmSaveReg[0];  	delete[] _fmSaveReg[1];  	delete[] _fmInstruments; @@ -759,6 +769,12 @@ int TownsAudioInterface::intf_updateOutputVolume(va_list &args) {  	return 0;  } +int TownsAudioInterface::intf_cdaToggle(va_list &args) { +	//int mode = va_arg(args, int); +	//_unkMask = mode ? 0x7f : 0x3f; +	return 0; +} +  int TownsAudioInterface::intf_pcmUpdateEnvelopeGenerator(va_list &args) {  	for (int i = 0; i < 8; i++)  		pcmUpdateEnvelopeGenerator(i); diff --git a/sound/softsynth/fmtowns_pc98/towns_audio.h b/sound/softsynth/fmtowns_pc98/towns_audio.h index 950c016b4e..95fb1ded59 100644 --- a/sound/softsynth/fmtowns_pc98/towns_audio.h +++ b/sound/softsynth/fmtowns_pc98/towns_audio.h @@ -95,6 +95,7 @@ private:  	int intf_setOutputVolume(va_list &args);  	int intf_resetOutputVolume(va_list &args);  	int intf_updateOutputVolume(va_list &args); +	int intf_cdaToggle(va_list &args);  	int intf_pcmUpdateEnvelopeGenerator(va_list &args);  	int intf_notImpl(va_list &args); diff --git a/sound/softsynth/fmtowns_pc98/towns_pc98_driver.cpp b/sound/softsynth/fmtowns_pc98/towns_pc98_driver.cpp index 82d0bd0438..6d52937cc6 100644 --- a/sound/softsynth/fmtowns_pc98/towns_pc98_driver.cpp +++ b/sound/softsynth/fmtowns_pc98/towns_pc98_driver.cpp @@ -1037,11 +1037,21 @@ TownsPC98_AudioDriver::TownsPC98_AudioDriver(Audio::Mixer *mixer, EmuType type)  	_musicPlaying(false), _sfxPlaying(false), _fading(false), _looping(0), _ready(false) {  	_sfxOffsets[0] = _sfxOffsets[1] = 0; + +	setTimerCallbackA((ChipTimerProc)&TownsPC98_AudioDriver::timerCallbackA); +	setTimerCallbackB((ChipTimerProc)&TownsPC98_AudioDriver::timerCallbackB);  }  TownsPC98_AudioDriver::~TownsPC98_AudioDriver() { +	Common::StackLock lock(_mutex); +  	reset(); +	_ready = false; + +	setTimerCallbackA(); +	setTimerCallbackB(); +  	if (_channels) {  		for (int i = 0; i < _numChan; i++)  			delete _channels[i]; diff --git a/sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp b/sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp index 241b9bde50..e304537c22 100644 --- a/sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp +++ b/sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp @@ -835,6 +835,7 @@ TownsPC98_FmSynth::TownsPC98_FmSynth(Audio::Mixer *mixer, EmuType type) :  	memset(&_timers[0], 0, sizeof(ChipTimer));  	memset(&_timers[1], 0, sizeof(ChipTimer)); +  	_timers[0].cb = &TownsPC98_FmSynth::timerCallbackA;  	_timers[1].cb = &TownsPC98_FmSynth::timerCallbackB;  	_timerbase = (uint32)(_baserate * 1000000.0f);	 @@ -842,6 +843,9 @@ TownsPC98_FmSynth::TownsPC98_FmSynth(Audio::Mixer *mixer, EmuType type) :  TownsPC98_FmSynth::~TownsPC98_FmSynth() {  	Common::StackLock lock(_mutex); +	 +	_ready = false; +  	_mixer->stopHandle(_soundHandle);  	delete _ssg;  	delete _prc; @@ -1154,6 +1158,14 @@ int TownsPC98_FmSynth::readBuffer(int16 *buffer, const int numSamples) {  	return numSamples;  } +void TownsPC98_FmSynth::setTimerCallbackA(ChipTimerProc proc) { +	_timers[0].cb = proc; +} + +void TownsPC98_FmSynth::setTimerCallbackB(ChipTimerProc proc) { +	_timers[1].cb = proc; +} +  uint8 TownsPC98_FmSynth::readSSGStatus() {  	return _ssg->chanEnable();  } diff --git a/sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h b/sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h index 3072503610..4c2de467d7 100644 --- a/sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h +++ b/sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h @@ -71,6 +71,10 @@ public:  	}  protected: +	typedef void (TownsPC98_FmSynth::*ChipTimerProc)(); +	void setTimerCallbackA(ChipTimerProc proc = &TownsPC98_FmSynth::timerCallbackA); +	void setTimerCallbackB(ChipTimerProc proc = &TownsPC98_FmSynth::timerCallbackB); +  	// Implement this in your inherited class if your driver generates  	// additional output that has to be inserted into the buffer.  	virtual void nextTickEx(int32 *buffer, uint32 bufferSize) {} @@ -80,8 +84,8 @@ protected:  	}  	uint8 readSSGStatus(); -	virtual void timerCallbackA() = 0; -	virtual void timerCallbackB() = 0; +	virtual void timerCallbackA() {} +	virtual void timerCallbackB() {}  	// The audio driver can store and apply two different audio settings  	// (usually for music and sound effects). The channel mask will determine @@ -139,8 +143,6 @@ private:  	bool _regProtectionFlag; -	typedef void (TownsPC98_FmSynth::*ChipTimerProc)(); -  	struct ChipTimer {  		bool enabled;  		uint16 value;  | 
