diff options
| -rw-r--r-- | engines/scumm/player_appleII.cpp | 364 | ||||
| -rw-r--r-- | engines/scumm/player_appleII.h | 167 | ||||
| -rw-r--r-- | engines/scumm/scumm.cpp | 3 | 
3 files changed, 533 insertions, 1 deletions
diff --git a/engines/scumm/player_appleII.cpp b/engines/scumm/player_appleII.cpp new file mode 100644 index 0000000000..c5f60cd700 --- /dev/null +++ b/engines/scumm/player_appleII.cpp @@ -0,0 +1,364 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include <assert.h> +#include "engines/engine.h" +#include "scumm/player_appleII.h" +#include "scumm/scumm.h" + +// CPU_CLOCK according to AppleWin +static const double CPU_CLOCK = 1020484.5; // ~ 1.02 MHz + +namespace Scumm { + +Player_AppleII::Player_AppleII(ScummEngine *scumm, Audio::Mixer *mixer) { +	_speakerState = 0; +	_soundNr = 0; + +	_mixer = mixer; +	_sampleRate = _mixer->getOutputRate(); +	_vm = scumm; + +	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); +} + +Player_AppleII::~Player_AppleII() { +	_mixer->stopHandle(_soundHandle); +} + +/* +void logSounds() { +	for (nr = 1; nr < 70; ++nr) { +		switch (nr) { +		// non-existing or invalid +		case 3: case 4: case 5:  case 49: case 51: case 68: +			continue; +		} + +		byte *data = _vm->getResourceAddress(rtSound, nr); +		if (data) { +			size_t size = (data[1] << 8) | data[0]; + +			std::stringstream s; +			s << "d:/msound/sound" << nr << ".snd"; +			FILE *f = fopen(s.str().c_str(), "wb"); +			fwrite(data, 1, size, f); +			fclose(f); +			printf("sound: %d\n", nr); +		} +	} +} +*/ + +void Player_AppleII::startSound(int nr) { +	Common::StackLock lock(_mutex); + +	_soundNr = nr; +	_buffer.clear(); + +	byte *data = _vm->getResourceAddress(rtSound, nr); +	assert(data); + +	byte *ptr1 = data + 4; + +	int type = ptr1[0]; +	if (type == 0) +		return; +	int loop = ptr1[1]; +	assert(loop > 0); +	ptr1 += 2; + +	debug(4, "startSound %d: type %d, loop %d", +		  nr, type, loop); +	 +	do { +		switch (type) { +			case 1: // freq up/down +				soundFunc1(ptr1); +				break; +			case 2: // symmetric wave (~) +				soundFunc2(ptr1); +				break; +			case 3: // asymmetric wave (__-) +				soundFunc3(ptr1); +				break; +			case 4: // polyphone (2 voices) +				soundFunc4(ptr1); +				break; +			case 5:	// periodic noise +				soundFunc5(ptr1); +				break; +		} +		--loop; +	} while (loop > 0); +} + +void Player_AppleII::stopAllSounds() { +	Common::StackLock lock(_mutex); +	_buffer.clear(); +} + +void Player_AppleII::stopSound(int nr) { +	Common::StackLock lock(_mutex); +	if (_soundNr == nr) { +		_buffer.clear(); +	} +} + +int Player_AppleII::getSoundStatus(int nr) const { +	Common::StackLock lock(_mutex); +	return (_buffer.availableSize() > 0 ? 1 : 0); +} + +int Player_AppleII::getMusicTimer() { +	/* Apple-II sounds are synchronous -> no music timer */ +	return 0; +} + +int Player_AppleII::readBuffer(int16 *buffer, const int numSamples) { +	Common::StackLock lock(_mutex); +	return _buffer.read((byte*)buffer, numSamples * 2) / 2; +} + +/************************************ + * Apple-II sound-resource parser + ************************************/ + +// toggle speaker on/off +void Player_AppleII::speakerToggle() { +	_speakerState ^= 0x1;  +} + +void Player_AppleII::generateSamples(int cycles) { +	// sampleDiff is used to compensate fractional samples +	static double sampleDiff = 0; +	double fSamples = (double)cycles / CPU_CLOCK * _sampleRate + sampleDiff; +	int samples = (int)(fSamples + 0.5); +	sampleDiff = fSamples - samples; + +	float vol = (float)_maxvol / 255; +	int16 value = vol * (_speakerState ? 32767 :  -32767); +	for (int i = 0; i < samples; ++i) +		_buffer.write(&value, sizeof(value)); +} + +void Player_AppleII::wait(int interval, int count /*y*/) { +	assert(count > 0); // 0 == 256?  +	assert(interval > 0); // 0 == 256?  +	generateSamples(11 + count*(8 + 5 * interval)); +} + +void Player_AppleII::_soundFunc1(int interval /*a*/, int count /*y*/) { // D076 +	assert(interval > 0); // 0 == 256?  +	assert(count > 0); // 0 == 256?  + +	for (; count >= 0; --count) { +		speakerToggle(); +		generateSamples(17 + 5 * interval); +	} +} + +void Player_AppleII::soundFunc1(const byte *params) { // D085 +	int delta = params[0]; +	int count = params[1]; +	byte interval = params[2]; // must be byte ("interval < delta" possible) +	int limit = params[3]; +	bool decInterval = (params[4] >= 0x40); + +	if (decInterval) { +		do { +			_soundFunc1(interval, count); +			interval -= delta; +		} while (interval >= limit); +	} else { +		do { +			_soundFunc1(interval, count); +			interval += delta; +		} while (interval < limit); +	} +} + +void Player_AppleII::_soundFunc2(int interval /*a*/, int count) { // D0EF +	if (interval == 0xFE) { +		wait(interval, 10); +	} else { +		assert(count > 0); // 0 == 256? +		assert(interval > 0); // 0 == 256? + +		int a = (interval >> 3) + count; +		for (int y = a; y > 0; --y) {  +			generateSamples(1292 - 5*interval); +			speakerToggle(); + +			generateSamples(1287 - 5*interval); +			speakerToggle(); +		} +	} +} + +void Player_AppleII::soundFunc2(const byte *params) { // D0D6 +	for (int pos = 1; pos < 256; ++pos) { +		byte interval = params[pos]; +		if (interval == 0xFF) +			return; +		_soundFunc2(interval, params[0] /*, LD12F=interval*/); +	} +} + +void Player_AppleII::_soundFunc3(int interval /*a*/, int count /*LD12D*/) { // D14B +	if (interval == 0xFE) { +		wait(interval, 70); +	} else { +		assert(interval > 0); // 0 == 256? +		assert(count > 0); // 0 == 256? + +		for (int y = count; y > 0; --y) { +			generateSamples(1289 - 5*interval); +			speakerToggle(); +		} +	} +} + +void Player_AppleII::soundFunc3(const byte *params) { // D132 +	for (int pos = 1; pos < 256; ++pos) { +		byte interval = params[pos]; +		if (interval == 0xFF) +			return; +		_soundFunc3(interval, params[0]); +	} +} + +void Player_AppleII::_soundFunc4(byte param0, byte param1, byte param2) { // D1A2 +	uint16 count = (-param2 << 8) | 0x3; +	byte bitmask1 = 0x3; +	byte bitmask2 = 0x3; +	 +	byte updateInterval2 = param0; +	if (updateInterval2 == 0) +		bitmask2 = 0x0; + +	byte updateInterval1 = param1; +	if (updateInterval1 == 0) { +		bitmask1 = 0x0; +		if (bitmask2 != 0) { +			bitmask1 = bitmask2; +			bitmask2 = 0; +			updateInterval1 = updateInterval2; +		} +	} + +	byte speakerShiftReg = 0; +	static byte updateRemain1 = 80; +	static byte updateRemain2 = 10; + +	while (true) { +		--updateRemain1; +		if (updateRemain1 == 0) { +			updateRemain1 = updateInterval1; +			speakerShiftReg ^= bitmask1; +		} + +		--updateRemain2; +		if (updateRemain2 == 0) { +			updateRemain2 = updateInterval2; +			if (updateRemain1 != 0) { +				speakerShiftReg ^= bitmask2;		 +			} +		} + +		if (speakerShiftReg & 0x1) +			speakerToggle(); +		speakerShiftReg >>= 1; +		generateSamples(40); + +		++count; +		if (count == 0) { +			return; +		} +	} +} + +void Player_AppleII::soundFunc4(const byte *params) { // D170 +	while (params[0] != 0x01) { +		_soundFunc4(params[0], params[1], params[2]); +		params += 3; +	} +} + +void Player_AppleII::_soundFunc5(int interval /*a*/, int count) { // D270 +	assert(count > 0); // 0 == 256? +	if (interval == 0) +		interval = 256; + +	for (int i = count; i > 0; --i) { +		generateSamples(10 + 5*interval); +		speakerToggle(); +		 +		generateSamples(5 + 5*interval); +		speakerToggle(); +	} +} + +// LD000[loc] ^ LD00A[loc] +static const byte noiseTable[256] = { +	0x65, 0x1b, 0xda, 0x11, 0x61, 0xe5, 0x77, 0x57, 0x92, 0xc8, 0x51, 0x1c, 0xd4, 0x91, 0x62, 0x63,  +	0x00, 0x38, 0x57, 0xd5, 0x18, 0xd8, 0xdc, 0x40, 0x03, 0x86, 0xd3, 0x2f, 0x10, 0x11, 0xd8, 0x3c,  +	0xbe, 0x00, 0x19, 0xc5, 0xd2, 0xc3, 0xca, 0x34, 0x00, 0x28, 0xbf, 0xb9, 0x18, 0x20, 0x01, 0xcc,  +	0xda, 0x08, 0xbc, 0x75, 0x7c, 0xb0, 0x8d, 0xe0, 0x09, 0x18, 0xbf, 0x5d, 0xe9, 0x8c, 0x75, 0x64,  +	0xe5, 0xb5, 0x5d, 0xe0, 0xb7, 0x7d, 0xe9, 0x8c, 0x55, 0x65, 0xc5, 0xb5, 0x5d, 0xd8, 0x09, 0x0d, +	0x64, 0xf0, 0xf0, 0x08, 0x63, 0x03, 0x00, 0x55, 0x35, 0xc0, 0x00, 0x20, 0x74, 0xa5, 0x1e, 0xe3,  +	0x00, 0x06, 0x3c, 0x52, 0xd1, 0x70, 0xd0, 0x57, 0x02, 0xf0, 0x00, 0xb6, 0xfc, 0x02, 0x11, 0x9a,  +	0x3b, 0xc8, 0x38, 0xdf, 0x1a, 0xb0, 0xd1, 0xb8, 0xd0, 0x18, 0x8a, 0x4a, 0xea, 0x1b, 0x12, 0x5d,  +	0x29, 0x58, 0xd8, 0x43, 0xb8, 0x2d, 0xd2, 0x61, 0x10, 0x3c, 0x0c, 0x5d, 0x1b, 0x61, 0x10, 0x3c,  +	0x0a, 0x5d, 0x1d, 0x61, 0x10, 0x3c, 0x0b, 0x19, 0x88, 0x21, 0xc0, 0x21, 0x07, 0x00, 0x65, 0x62, +	0x08, 0xe9, 0x36, 0x40, 0x20, 0x41, 0x06, 0x00, 0x20, 0x00, 0x00, 0xed, 0xa3, 0x00, 0x88, 0x06,  +	0x98, 0x01, 0x5d, 0x7f, 0x02, 0x1d, 0x78, 0x03, 0x60, 0xcb, 0x3a, 0x01, 0xbd, 0x78, 0x02, 0x5d,  +	0x7e, 0x03, 0x1d, 0xf5, 0xa6, 0x40, 0x81, 0xb4, 0xd0, 0x8d, 0xd3, 0xd0, 0x6d, 0xd5, 0x61, 0x48,  +	0x61, 0x4d, 0xd1, 0xc8, 0xb1, 0xd8, 0x69, 0xff, 0x61, 0xd9, 0xed, 0xa0, 0xfe, 0x19, 0x91, 0x37,  +	0x19, 0x37, 0x00, 0xf1, 0x00, 0x01, 0x1f, 0x00, 0xad, 0xc1, 0x01, 0x01, 0x2e, 0x00, 0x40, 0xc6, +	0x7a, 0x9b, 0x95, 0x43, 0xfc, 0x18, 0xd2, 0x9e, 0x2a, 0x5a, 0x4b, 0x2a, 0xb6, 0x87, 0x30, 0x6c +}; + +byte /*a*/ Player_AppleII::noise() { // D261 +	static int pos = 0; // initial value? +	byte result = noiseTable[pos]; +	pos = (pos + 1) % 256; +	return result; +} + +void Player_AppleII::soundFunc5(const byte *params) { // D222 +	const byte noiseMask[] = { +		0x3F, 0x3F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F +	}; + +	int param0 = params[0]; +	assert(param0 > 0); +	for (int i = 0; i < 10; ++i) { +		int count = param0; +		do { +			_soundFunc5(noise() & noiseMask[i], 1); +			--count; +		} while (count > 0); +	} +} + +} // End of namespace Scumm diff --git a/engines/scumm/player_appleII.h b/engines/scumm/player_appleII.h new file mode 100644 index 0000000000..545e590fdd --- /dev/null +++ b/engines/scumm/player_appleII.h @@ -0,0 +1,167 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef SCUMM_PLAYER_APPLEII_H +#define SCUMM_PLAYER_APPLEII_H + +#include "common/mutex.h" +#include "common/scummsys.h" +#include "common/memstream.h" +#include "scumm/music.h" +#include "audio/audiostream.h" +#include "audio/mixer.h" +#include "audio/softsynth/sid.h" + +namespace Scumm { + +class ScummEngine; + +class DynamicMemoryStream { +public: +	DynamicMemoryStream() : _data(0) { +		clear(); +	} + +	~DynamicMemoryStream() { +		free(_data); +	} + +	void clear() { +		free(_data); +		_data = 0; +		_capacity = 0; +		_size = 0; +		_ptr = 0; +		_pos = 0; +		_readPos = 0; +	} + +	void ensureCapacity(uint32 new_len) { +		if (new_len <= _capacity) +			return; + +		byte *old_data = _data; + +		_capacity *= 2; +		if (_capacity < new_len + 2048) +			_capacity = new_len + 2048; +		_data = (byte *)malloc(_capacity); +		_ptr = _data + _pos; + +		if (old_data) { +			// Copy old data +			memcpy(_data, old_data, _size); +			free(old_data); +		} + +		_size = new_len; +	} + +	uint32 availableSize() const { +		if (_readPos >= _size) +			return 0; +		return _size - _readPos; +	} + +	virtual uint32 write(const void *dataPtr, uint32 dataSize) { +		ensureCapacity(_pos + dataSize); +		memcpy(_ptr, dataPtr, dataSize); +		_ptr += dataSize; +		_pos += dataSize; +		if (_pos > _size) +			_size = _pos; +		return dataSize; +	} + +	uint32 read(byte *dataPtr, uint32 dataSize) const { +		uint32 avail = availableSize(); +		if (avail == 0) +			return 0; +		if (dataSize > avail) +			dataSize = avail; +		memcpy(dataPtr, _data + _readPos, dataSize); +		_readPos += dataSize; +		return dataSize; +	} + +private: +	mutable uint32 _readPos; +	uint32 _capacity; +	uint32 _size; +	byte *_ptr; +	byte *_data; +	uint32 _pos; +}; + +class Player_AppleII : public Audio::AudioStream, public MusicEngine { +public: +	Player_AppleII(ScummEngine *scumm, Audio::Mixer *mixer); +	virtual ~Player_AppleII(); + +	virtual void setMusicVolume(int vol) { _maxvol = vol; } +	void startMusic(int songResIndex); +	virtual void startSound(int sound); +	virtual void stopSound(int sound); +	virtual void stopAllSounds(); +	virtual int  getSoundStatus(int sound) const; +	virtual int  getMusicTimer(); + +	// AudioStream API +	int readBuffer(int16 *buffer, const int numSamples); +	bool isStereo() const { return false; } +	bool endOfData() const { return false; } +	int getRate() const { return _sampleRate; } + +private: +	ScummEngine *_vm; +	Audio::Mixer *_mixer; +	Audio::SoundHandle _soundHandle; +	int _maxvol; +	int _sampleRate; +	Common::Mutex _mutex; + +private: +	byte _speakerState; +	DynamicMemoryStream _buffer; +	int _soundNr; + +private: +	void speakerToggle(); +	void generateSamples(int cycles); +	void wait(int interval, int count); +	byte noise(); + +	void soundFunc1(const byte *params); +	void _soundFunc1(int interval, int count); +	void soundFunc2(const byte *params); +	void _soundFunc2(int interval, int count); +	void soundFunc3(const byte *params); +	void _soundFunc3(int interval, int count); +	void soundFunc4(const byte *params); +	void _soundFunc4(byte param0, byte param1, byte param2); +	void soundFunc5(const byte *params); +	void _soundFunc5(int interval, int count); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index f94496b14b..3eea68fbbe 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -55,6 +55,7 @@  #include "scumm/player_nes.h"  #include "scumm/player_sid.h"  #include "scumm/player_pce.h" +#include "scumm/player_appleII.h"  #include "scumm/player_v1.h"  #include "scumm/player_v2.h"  #include "scumm/player_v2cms.h" @@ -1797,7 +1798,7 @@ void ScummEngine::setupMusic(int midi) {  	if (_game.version >= 7) {  		// Setup for digital iMuse is performed in another place  	} else if (_game.platform == Common::kPlatformApple2GS && _game.version == 0){ -		// TODO: Add support for music format +		_musicEngine = new Player_AppleII(this, _mixer);  	} else if (_game.platform == Common::kPlatformC64 && _game.version <= 1) {  #ifndef DISABLE_SID  		_musicEngine = new Player_SID(this, _mixer);  | 
