diff options
| author | Eugene Sandulenko | 2009-06-06 18:21:49 +0000 | 
|---|---|---|
| committer | Eugene Sandulenko | 2009-06-06 18:21:49 +0000 | 
| commit | cda17beff56b0c503f03b5f75be0a5f589dbeedc (patch) | |
| tree | a30bbfa93593ad4f07f9c5a29718eec85239e08c | |
| parent | 986a0b83b61146094cb748b7b291ed5e074a832b (diff) | |
| download | scummvm-rg350-cda17beff56b0c503f03b5f75be0a5f589dbeedc.tar.gz scummvm-rg350-cda17beff56b0c503f03b5f75be0a5f589dbeedc.tar.bz2 scummvm-rg350-cda17beff56b0c503f03b5f75be0a5f589dbeedc.zip | |
Patch #1365914: "SCUMM: CMS support." Disabled by default. Still plenty to do.
svn-id: r41282
| -rw-r--r-- | engines/scumm/detection_tables.h | 8 | ||||
| -rw-r--r-- | engines/scumm/module.mk | 1 | ||||
| -rw-r--r-- | engines/scumm/player_v2.h | 173 | ||||
| -rw-r--r-- | engines/scumm/player_v2cms.cpp | 1851 | ||||
| -rw-r--r-- | engines/scumm/scumm.cpp | 11 | ||||
| -rw-r--r-- | engines/scumm/sound.cpp | 15 | ||||
| -rw-r--r-- | engines/scumm/vars.cpp | 3 | ||||
| -rw-r--r-- | sound/mididrv.cpp | 2 | ||||
| -rw-r--r-- | sound/mididrv.h | 10 | 
9 files changed, 2064 insertions, 10 deletions
| diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h index b88bddb751..2ea593c571 100644 --- a/engines/scumm/detection_tables.h +++ b/engines/scumm/detection_tables.h @@ -207,13 +207,13 @@ static const GameSettings gameVariantsTable[] = {  	{"zak", "V2",       "v2", GID_ZAK, 2, 0, MDT_PCSPK, 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}, -	{"indy3", "EGA",      "ega", GID_INDY3, 3, 0, MDT_PCSPK | MDT_ADLIB, 0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI}, +	{"indy3", "EGA",      "ega", GID_INDY3, 3, 0, MDT_PCSPK | MDT_CMS | MDT_ADLIB, 0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},  	{"indy3", "No Adlib", "ega", GID_INDY3, 3, 0, MDT_PCSPK,             0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},  	{"indy3", "VGA",      "vga", GID_INDY3, 3, 0, MDT_PCSPK | 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}, -	{"loom", "EGA",      "ega", GID_LOOM, 3, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI, 0, UNK, GUIO_NOSPEECH}, -	{"loom", "No Adlib", "ega", GID_LOOM, 3, 0, MDT_PCSPK,                        0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI}, +	{"loom", "EGA",      "ega", GID_LOOM, 3, 0, MDT_PCSPK | MDT_CMS | MDT_ADLIB | MDT_MIDI, 0, UNK, GUIO_NOSPEECH}, +	{"loom", "No Adlib", "ega", GID_LOOM, 3, 0, MDT_PCSPK | MDT_CMS,                        0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},  	{"loom", "PC-Engine",    0, GID_LOOM, 3, 0, MDT_NONE,                         GF_AUDIOTRACKS, Common::kPlatformPCEngine, GUIO_NOSPEECH | GUIO_NOMIDI},  	{"loom", "FM-TOWNS",     0, GID_LOOM, 3, 0, MDT_TOWNS,                        GF_AUDIOTRACKS | GF_OLD256, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI},  	{"loom", "VGA",      "vga", GID_LOOM, 4, 0, MDT_NONE,                         GF_AUDIOTRACKS,             Common::kPlatformPC, GUIO_NOSPEECH | GUIO_NOMIDI}, @@ -221,7 +221,7 @@ static const GameSettings gameVariantsTable[] = {  	{"pass", 0, 0, GID_PASS, 4, 0, MDT_PCSPK | MDT_ADLIB, GF_16COLOR, Common::kPlatformPC, GUIO_NOSPEECH | GUIO_NOMIDI},  	{"monkey", "VGA",      "vga", GID_MONKEY_VGA, 4, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI, 0, UNK, GUIO_NOSPEECH}, -	{"monkey", "EGA",      "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI, GF_16COLOR,     Common::kPlatformPC, GUIO_NOSPEECH}, +	{"monkey", "EGA",      "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_CMS | MDT_ADLIB | MDT_MIDI, GF_16COLOR,     Common::kPlatformPC, GUIO_NOSPEECH},  	{"monkey", "No Adlib", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK,                        GF_16COLOR,     Common::kPlatformAtariST, GUIO_NOSPEECH | GUIO_NOMIDI},  	{"monkey", "Demo",     "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | 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}, diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk index 240d3c290d..58d8db91fc 100644 --- a/engines/scumm/module.mk +++ b/engines/scumm/module.mk @@ -38,6 +38,7 @@ MODULE_OBJS := \  	player_v1.o \  	player_v2.o \  	player_v2a.o \ +	player_v2cms.o \  	player_v3a.o \  	resource_v2.o \  	resource_v3.o \ diff --git a/engines/scumm/player_v2.h b/engines/scumm/player_v2.h index cd88d57602..25041f42a9 100644 --- a/engines/scumm/player_v2.h +++ b/engines/scumm/player_v2.h @@ -158,6 +158,179 @@ private:  	void next_freqs(ChannelInfo *channel);  }; +/** + * Scumm V2 CMS/Gameblaster MIDI driver. + */ +class Player_V2CMS : public Audio::AudioStream, public MusicEngine { +public: +	Player_V2CMS(ScummEngine *scumm, Audio::Mixer *mixer); +	virtual ~Player_V2CMS(); + +	virtual void setMusicVolume(int vol); +	virtual void startSound(int sound); +	virtual void stopSound(int sound); +	virtual void stopAllSounds(); +	virtual int  getMusicTimer() const; +	virtual int  getSoundStatus(int sound) const; + +	// AudioStream API +	int readBuffer(int16 *buffer, const int numSamples); +	bool isStereo() const { return true; } +	bool endOfData() const { return false; } +	int getRate() const { return _sample_rate; } +	 +protected: + +#include "common/pack-start.h"	// START STRUCT PACKING +	struct Voice { +		byte attack; +		byte decay; +		byte sustain; +		byte release; +		byte octadd; +		int16 vibrato; +		int16 vibrato2; +		int16 noise; +	} PACKED_STRUCT; +	 +	struct Voice2 { +		byte *amplitudeOutput; +		byte *freqOutput; +		byte *octaveOutput; +		 +		int8 channel; +		int8 sustainLevel; +		int8 attackRate; +		int8 maxAmpl; +		int8 decayRate; +		int8 sustainRate; +		int8 releaseRate; +		int8 releaseTime; +		int8 vibratoRate; +		int8 vibratoDepth; +		 +		int8 curVibratoRate; +		int8 curVibratoUnk; + +		int8 unkVibratoRate; +		int8 unkVibratoDepth; + +		int8 unkRate; +		int8 unkCount; + +		int nextProcessState; +		int8 curVolume; +		int8 curOctave; +		int8 curFreq; + +		int8 octaveAdd; + +		int8 playingNote; +		Voice2 *nextVoice; + +		byte chanNumber; +	} PACKED_STRUCT; +	 +	struct MusicChip { +		byte ampl[4]; +		byte freq[4]; +		byte octave[2]; +	} PACKED_STRUCT; +#include "common/pack-end.h"	// END STRUCT PACKING + +	Voice _cmsVoicesBase[16]; +	Voice2 _cmsVoices[8]; +	MusicChip _cmsChips[2]; +	 +	char _tempo; +	char _tempoSum; +	byte _looping; +	byte _octaveMask; +	int16 _midiDelay; +	Voice2 *_midiChannel[16]; +	byte _midiChannelUse[16]; +	byte *_midiData; +	byte *_midiSongBegin; +	 +	int _loadedMidiSong; +	 +	byte _lastMidiCommand; +	uint _outputTableReady; +	byte _clkFrequenz; +	byte _restart; +	byte _curSno; +	 +	void loadMidiData(byte *data, int sound); +	void play(); +	 +	void processChannel(Voice2 *channel); +	void processRelease(Voice2 *channel); +	void processAttack(Voice2 *channel); +	void processDecay(Voice2 *channel); +	void processSustain(Voice2 *channel); +	void processVibrato(Voice2 *channel); +	 +	void playMusicChips(const MusicChip *table); +	void playNote(byte *&data); +	void clearNote(byte *&data); +	void offAllChannels(); +	void playVoice(); +	void processMidiData(uint ticks); +	 +	Voice2 *getFreeVoice(); +	Voice2 *getPlayVoice(byte param); +	 +	// from Player_V2 +protected: +	bool _isV3Game; +	Audio::Mixer *_mixer; +	Audio::SoundHandle _soundHandle; +	ScummEngine *_vm; + +	int _header_len; + +	uint32 _sample_rate; +	uint32 _next_tick; +	uint32 _tick_len; + +	int _timer_count[4]; +	int _timer_output; + +	int   _current_nr; +	byte *_current_data; +	int   _next_nr; +	byte *_next_data; +	byte *_retaddr; + +private: +	union ChannelInfo { +		channel_data d; +		uint16 array[sizeof(channel_data)/2]; +	}; + +	int _music_timer; +	int _music_timer_ctr; +	int _ticks_per_music_timer; + +	Common::Mutex _mutex; +	ChannelInfo _channels[5]; + +protected: +	void mutex_up(); +	void mutex_down(); + +	virtual void nextTick(); +	virtual void clear_channel(int i); +	virtual void chainSound(int nr, byte *data); +	virtual void chainNextSound(); + +private: +	void do_mix(int16 *buf, uint len); + +	void execute_cmd(ChannelInfo *channel); +	void next_freqs(ChannelInfo *channel); +}; +  } // End of namespace Scumm  #endif diff --git a/engines/scumm/player_v2cms.cpp b/engines/scumm/player_v2cms.cpp index e69de29bb2..25bd756307 100644 --- a/engines/scumm/player_v2cms.cpp +++ b/engines/scumm/player_v2cms.cpp @@ -0,0 +1,1851 @@ +/* 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 "engines/engine.h" +#include "scumm/player_v2.h" +#include "scumm/scumm.h" +#include "sound/mididrv.h" +#include "sound/mixer.h" + +namespace Scumm { +#define FREQ_HZ 236 // Don't change! + +#define FIXP_SHIFT  16 +#define MAX_OUTPUT 0x7fff + +#define NG_PRESET 0x0f35        /* noise generator preset */ +#define FB_WNOISE 0x12000       /* feedback for white noise */ +#define FB_PNOISE 0x08000       /* feedback for periodic noise */ + +// CMS/Gameblaster Emulation taken from DosBox + +#define LEFT	0x00 +#define RIGHT	0x01 +#define MAX_OUTPUT 0x7fff +#define MIN_OUTPUT -0x8000 +//#define CMS_BUFFER_SIZE 128 +#define CMS_RATE 22050 + +#define PROCESS_ATTACK 1 +#define PROCESS_RELEASE 2 +#define PROCESS_SUSTAIN 3 +#define PROCESS_DECAY 4 +#define PROCESS_VIBRATO 5 + +/* this structure defines a channel */ +struct saa1099_channel +{ +	int frequency;				/* frequency (0x00..0xff) */ +	int freq_enable;			/* frequency enable */ +	int noise_enable;			/* noise enable */ +	int octave;				/* octave (0x00..0x07) */ +	int amplitude[2];			/* amplitude (0x00..0x0f) */ +	int envelope[2];			/* envelope (0x00..0x0f or 0x10 == off) */ + +	/* vars to simulate the square wave */ +	double counter; +	double freq; +	int level; +}; + +/* this structure defines a noise channel */ +struct saa1099_noise +{ +	/* vars to simulate the noise generator output */ +	double counter; +	double freq; +	int level;				/* noise polynomal shifter */ +}; + +/* this structure defines a SAA1099 chip */ +struct SAA1099 +{ +	int stream;				/* our stream */ +	int noise_params[2];			/* noise generators parameters */ +	int env_enable[2];			/* envelope generators enable */ +	int env_reverse_right[2];		/* envelope reversed for right channel */ +	int env_mode[2];			/* envelope generators mode */ +	int env_bits[2];			/* non zero = 3 bits resolution */ +	int env_clock[2];			/* envelope clock mode (non-zero external) */ +	int env_step[2];			/* current envelope step */ +	int all_ch_enable;			/* all channels enable */ +	int sync_state;				/* sync all channels */ +	int selected_reg;			/* selected register */ +	struct saa1099_channel channels[6];	/* channels */ +	struct saa1099_noise noise[2];		/* noise generators */ +}; + +static byte envelope[8][64] = { +	/* zero amplitude */ +	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +	/* maximum amplitude */ +	{15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, +	 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, +	 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, +	 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, }, +	/* single decay */ +	{15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, +	  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +	/* repetitive decay */ +	{15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, +	 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, +	 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, +	 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, +	/* single triangular */ +	{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, +	 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, +	  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +	/* repetitive triangular */ +	{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, +	 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, +	  0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, +	 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, +	/* single attack */ +	{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, +	  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +	/* repetitive attack */ +	{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, +	  0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, +	  0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, +	  0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 } +}; + +static int amplitude_lookup[16] = { +	 0*32767/16,  1*32767/16,  2*32767/16,	3*32767/16, +	 4*32767/16,  5*32767/16,  6*32767/16,	7*32767/16, +	 8*32767/16,  9*32767/16, 10*32767/16, 11*32767/16, +	12*32767/16, 13*32767/16, 14*32767/16, 15*32767/16 +}; + +class CMSEmulator { +public: +	CMSEmulator(uint32 sampleRate) { +		_sampleRate = sampleRate; +		memset(_saa1099, 0, sizeof(SAA1099)*2); +	} +	 +	~CMSEmulator() { } +	 +	void portWrite(int port, int val); +	void readBuffer(int16 *buffer, const int numSamples); +private: +	uint32 _sampleRate; +	 +	SAA1099 _saa1099[2]; +	 +	void envelope(int chip, int ch); +	void update(int chip, int16 *buffer, int length); +	void portWriteIntern(int chip, int offset, int data); +}; + +void CMSEmulator::portWrite(int port, int val) { +	switch (port) { +		case 0x220: +			portWriteIntern(0, 1, val); +			break; +			 +		case 0x221: +			_saa1099[0].selected_reg = val & 0x1f; +			if (_saa1099[0].selected_reg == 0x18 || _saa1099[0].selected_reg == 0x19) { +				/* clock the envelope channels */ +				if (_saa1099[0].env_clock[0]) envelope(0, 0); +				if (_saa1099[0].env_clock[1]) envelope(0, 1); +			} +			break; +			 +		case 0x222: +			portWriteIntern(1, 1, val); +			break; +			 +		case 0x223: +			_saa1099[1].selected_reg = val & 0x1f; +			if (_saa1099[1].selected_reg == 0x18 || _saa1099[1].selected_reg == 0x19) { +				/* clock the envelope channels */ +				if (_saa1099[1].env_clock[0]) envelope(1, 0); +				if (_saa1099[1].env_clock[1]) envelope(1, 1); +			} +			break; +			 +		default: +			warning("CMSEmulator got port: 0x%X", port); +			break; +	} +} + +void CMSEmulator::readBuffer(int16 *buffer, const int numSamples) { +	update(0, &buffer[0], numSamples); +	update(1, &buffer[0], numSamples); +} + +void CMSEmulator::envelope(int chip, int ch) { +	SAA1099 *saa = &_saa1099[chip]; +	if (saa->env_enable[ch]) { +		int step, mode, mask; +		mode = saa->env_mode[ch]; +		/* step from 0..63 and then loop in steps 32..63 */ +		step = saa->env_step[ch] = ((saa->env_step[ch] + 1) & 0x3f) | (saa->env_step[ch] & 0x20); + +		mask = 15; +		if (saa->env_bits[ch]) +			mask &= ~1; 	/* 3 bit resolution, mask LSB */ + +		saa->channels[ch*3+0].envelope[ LEFT] = +		saa->channels[ch*3+1].envelope[ LEFT] = +		saa->channels[ch*3+2].envelope[ LEFT] = Scumm::envelope[mode][step] & mask; +		if (saa->env_reverse_right[ch] & 0x01) { +			saa->channels[ch*3+0].envelope[RIGHT] = +			saa->channels[ch*3+1].envelope[RIGHT] = +			saa->channels[ch*3+2].envelope[RIGHT] = (15 - Scumm::envelope[mode][step]) & mask; +		} else { +			saa->channels[ch*3+0].envelope[RIGHT] = +			saa->channels[ch*3+1].envelope[RIGHT] = +			saa->channels[ch*3+2].envelope[RIGHT] = Scumm::envelope[mode][step] & mask; +		} +	} else { +		/* envelope mode off, set all envelope factors to 16 */ +		saa->channels[ch*3+0].envelope[ LEFT] = +		saa->channels[ch*3+1].envelope[ LEFT] = +		saa->channels[ch*3+2].envelope[ LEFT] = +		saa->channels[ch*3+0].envelope[RIGHT] = +		saa->channels[ch*3+1].envelope[RIGHT] = +		saa->channels[ch*3+2].envelope[RIGHT] = 16; +	} +} + +void CMSEmulator::update(int chip, int16 *buffer, int length) { +	struct SAA1099 *saa = &_saa1099[chip]; +	int j, ch; + +	/* if the channels are disabled we're done */ +	if (!saa->all_ch_enable) { +		/* init output data */ +		if (chip == 0) { +			memset(buffer, 0, sizeof(int16)*length*2); +		} +		return; +	} +	 +	if (chip == 0) { +		memset(buffer, 0, sizeof(int16)*length*2); +	} + +	for (ch = 0; ch < 2; ch++) { +		switch (saa->noise_params[ch]) { +			case 0: saa->noise[ch].freq = 31250.0 * 2; break; +			case 1: saa->noise[ch].freq = 15625.0 * 2; break; +			case 2: saa->noise[ch].freq =  7812.5 * 2; break; +			case 3: saa->noise[ch].freq = saa->channels[ch * 3].freq; break; +		} +	} + +	/* fill all data needed */ +	for (j = 0; j < length; ++j) { +		int output_l = 0, output_r = 0; + +		/* for each channel */ +		for (ch = 0; ch < 6; ch++) { +			if (saa->channels[ch].freq == 0.0) +				saa->channels[ch].freq = (double)((2 * 15625) << saa->channels[ch].octave) / +				(511.0 - (double)saa->channels[ch].frequency); + +			/* check the actual position in the square wave */ +			saa->channels[ch].counter -= saa->channels[ch].freq; +			while (saa->channels[ch].counter < 0) { +				/* calculate new frequency now after the half wave is updated */ +				saa->channels[ch].freq = (double)((2 * 15625) << saa->channels[ch].octave) / +					(511.0 - (double)saa->channels[ch].frequency); + +				saa->channels[ch].counter += _sampleRate; +				saa->channels[ch].level ^= 1; + +				/* eventually clock the envelope counters */ +				if (ch == 1 && saa->env_clock[0] == 0) +					envelope(chip, 0); +				if (ch == 4 && saa->env_clock[1] == 0) +					envelope(chip, 1); +			} + +			/* if the noise is enabled */ +			if (saa->channels[ch].noise_enable) { +				/* if the noise level is high (noise 0: chan 0-2, noise 1: chan 3-5) */ +				if (saa->noise[ch/3].level & 1) { +					/* subtract to avoid overflows, also use only half amplitude */ +					output_l -= saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 16 / 2; +					output_r -= saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 16 / 2; +				} +			} + +			/* if the square wave is enabled */ +			if (saa->channels[ch].freq_enable) { +				/* if the channel level is high */ +				if (saa->channels[ch].level & 1) { +					output_l += saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 16; +					output_r += saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 16; +				} +			} +		} + +		for (ch = 0; ch < 2; ch++) { +			/* check the actual position in noise generator */ +			saa->noise[ch].counter -= saa->noise[ch].freq; +			while (saa->noise[ch].counter < 0) { +				saa->noise[ch].counter += _sampleRate; +				if( ((saa->noise[ch].level & 0x4000) == 0) == ((saa->noise[ch].level & 0x0040) == 0) ) +					saa->noise[ch].level = (saa->noise[ch].level << 1) | 1; +				else +					saa->noise[ch].level <<= 1; +			} +		} +		/* write sound data to the buffer */ +		buffer[j*2] += output_l / 6; +		buffer[j*2+1] += output_r / 6; +	} +} + +void CMSEmulator::portWriteIntern(int chip, int offset, int data) { +	SAA1099 *saa = &_saa1099[chip]; +	int reg = saa->selected_reg; +	int ch; + +	switch (reg) { +		/* channel i amplitude */ +		case 0x00: +		case 0x01: +		case 0x02: +		case 0x03: +		case 0x04: +		case 0x05: +			ch = reg & 7; +			saa->channels[ch].amplitude[LEFT] = amplitude_lookup[data & 0x0f]; +			saa->channels[ch].amplitude[RIGHT] = amplitude_lookup[(data >> 4) & 0x0f]; +			break; +			 +		/* channel i frequency */ +		case 0x08: +		case 0x09: +		case 0x0a: +		case 0x0b: +		case 0x0c: +		case 0x0d: +			ch = reg & 7; +			saa->channels[ch].frequency = data & 0xff; +			break; +			 +		/* channel i octave */ +		case 0x10: +		case 0x11: +		case 0x12: +			ch = (reg - 0x10) << 1; +			saa->channels[ch + 0].octave = data & 0x07; +			saa->channels[ch + 1].octave = (data >> 4) & 0x07; +			break; +			 +		/* channel i frequency enable */ +		case 0x14: +			saa->channels[0].freq_enable = data & 0x01; +			saa->channels[1].freq_enable = data & 0x02; +			saa->channels[2].freq_enable = data & 0x04; +			saa->channels[3].freq_enable = data & 0x08; +			saa->channels[4].freq_enable = data & 0x10; +			saa->channels[5].freq_enable = data & 0x20; +			break; +			 +		/* channel i noise enable */ +		case 0x15: +			saa->channels[0].noise_enable = data & 0x01; +			saa->channels[1].noise_enable = data & 0x02; +			saa->channels[2].noise_enable = data & 0x04; +			saa->channels[3].noise_enable = data & 0x08; +			saa->channels[4].noise_enable = data & 0x10; +			saa->channels[5].noise_enable = data & 0x20; +			break; +			 +		/* noise generators parameters */ +		case 0x16: +			saa->noise_params[0] = data & 0x03; +			saa->noise_params[1] = (data >> 4) & 0x03; +			break; +			 +		/* envelope generators parameters */ +		case 0x18: +		case 0x19: +			ch = reg - 0x18; +			saa->env_reverse_right[ch] = data & 0x01; +			saa->env_mode[ch] = (data >> 1) & 0x07; +			saa->env_bits[ch] = data & 0x10; +			saa->env_clock[ch] = data & 0x20; +			saa->env_enable[ch] = data & 0x80; +			/* reset the envelope */ +			saa->env_step[ch] = 0; +			break; +			 +		/* channels enable & reset generators */ +		case 0x1c: +			saa->all_ch_enable = data & 0x01; +			saa->sync_state = data & 0x02; +			if (data & 0x02) { +				int i; +				/* Synch & Reset generators */ +				for (i = 0; i < 6; i++) { +					saa->channels[i].level = 0; +					saa->channels[i].counter = 0.0; +				} +			} +			break; +			 +		default:	/* Error! */ +			error("CMS Unkown write to reg %x with %x",reg, data); +	} +} + +#pragma mark - +#pragma mark - Player_V2CMS +#pragma mark - + +const uint8 note_lengths[] = { +	0, +	0,  0,  2, +	0,  3,  4, +	5,  6,  8, +	9, 12, 16, +	18, 24, 32, +	36, 48, 64, +	72, 96 +}; + +static const uint16 hull_offsets[] = { +	0, 12, 24, 36, 48, 60, +	72, 88, 104, 120, 136, 256, +	152, 164, 180 +}; + +static const int16 hulls[] = { +	// hull 0 +	3, -1, 0, 0, 0, 0, 0, 0, +	0, -1, 0, 0, +	// hull 1 (staccato) +	3, -1, 0, 32, 0, -1, 0, 0, +	0, -1, 0, 0, +	// hull 2 (legato) +	3, -1, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, +	// hull 3 (staccatissimo) +	3, -1, 0, 2, 0, -1, 0, 0, +	0, -1, 0, 0, +	// hull 4 +	3, -1, 0, 6, 0, -1, 0, 0, +	0, -1, 0, 0, +	// hull 5 +	3, -1, 0, 16, 0, -1, 0, 0, +	0, -1, 0, 0, +	// hull 6 +	(int16) 60000, -1, -1000, 20, 0, 0, 0, 0, +	(int16) 40000, -1, -5000,  5, 0, -1, 0, 0, +	// hull 7 +	(int16) 50000, -1, 0, 8, 30000, -1, 0, 0, +	28000, -1, -5000,  5, 0, -1, 0, 0, +	// hull 8 +	(int16) 60000, -1, -2000, 16, 0, 0, 0, 0, +	28000, -1, -6000,  5, 0, -1, 0, 0, +	// hull 9 +	(int16) 55000, -1,     0,  8, (int16) 35000, -1, 0, 0, +	(int16) 40000, -1, -2000, 10, 0, -1, 0, 0, +	// hull 10 +	(int16) 60000, -1,     0,  4, -2000, 8, 0, 0, +	(int16) 40000, -1, -6000,  5, 0, -1, 0, 0, +	// hull 12 +	0, -1,   150, 340, -150, 340, 0, -1, +	0, -1, 0, 0, +	// hull 13  == 164 +	20000, -1,  4000,  7, 1000, 15, 0, 0, +	(int16) 35000, -1, -2000, 15, 0, -1, 0, 0, + +	// hull 14  == 180 +	(int16) 35000, -1,   500, 20, 0,  0, 0, 0, +	(int16) 45000, -1,  -500, 60, 0, -1, 0, 0, + +	// hull misc = 196 +	(int16) 44000, -1, -4400, 10, 0, -1, 0, 0, +	0, -1, 0, 0, + +	(int16) 53000, -1, -5300, 10, 0, -1, 0, 0, +	0, -1, 0, 0, + +	(int16) 63000, -1, -6300, 10, 0, -1, 0, 0, +	0, -1, 0, 0, + +	(int16) 44000, -1, -1375, 32, 0, -1, 0, 0, +	0, -1, 0, 0, + +	(int16) 53000, -1, -1656, 32, 0, -1, 0, 0, +	0, -1, 0, 0, + +	// hull 11 == 256 +	(int16) 63000, -1, -1968, 32, 0, -1, 0, 0, +	0, -1, 0, 0, + +	(int16) 44000, -1, - 733, 60, 0, -1, 0, 0, +	0, -1, 0, 0, + +	(int16) 53000, -1, - 883, 60, 0, -1, 0, 0, +	0, -1, 0, 0, + +	(int16) 63000, -1, -1050, 60, 0, -1, 0, 0, +	0, -1, 0, 0, + +	(int16) 44000, -1, - 488, 90, 0, -1, 0, 0, +	0, -1, 0, 0, + +	(int16) 53000, -1, - 588, 90, 0, -1, 0, 0, +	0, -1, 0, 0, + +	(int16) 63000, -1, - 700, 90, 0, -1, 0, 0, +	0, -1, 0, 0 +}; + +static const uint16 freqmod_lengths[] = { +	0x1000, 0x1000, 0x20, 0x2000, 0x1000 +}; + +static const uint16 freqmod_offsets[] = { +	0, 0x100, 0x200, 0x302, 0x202 +}; + +static const int8 freqmod_table[0x502] = { +     0,   3,   6,   9,  12,  15,  18,  21, +    24,  27,  30,  33,  36,  39,  42,  45, +    48,  51,  54,  57,  59,  62,  65,  67, +    70,  73,  75,  78,  80,  82,  85,  87, +    89,  91,  94,  96,  98, 100, 102, 103, +   105, 107, 108, 110, 112, 113, 114, 116, +   117, 118, 119, 120, 121, 122, 123, 123, +   124, 125, 125, 126, 126, 126, 126, 126, +   126, 126, 126, 126, 126, 126, 125, 125, +   124, 123, 123, 122, 121, 120, 119, 118, +   117, 116, 114, 113, 112, 110, 108, 107, +   105, 103, 102, 100,  98,  96,  94,  91, +    89,  87,  85,  82,  80,  78,  75,  73, +    70,  67,  65,  62,  59,  57,  54,  51, +    48,  45,  42,  39,  36,  33,  30,  27, +    24,  21,  18,  15,  12,   9,   6,   3, +     0,  -3,  -6,  -9, -12, -15, -18, -21, +   -24, -27, -30, -33, -36, -39, -42, -45, +   -48, -51, -54, -57, -59, -62, -65, -67, +   -70, -73, -75, -78, -80, -82, -85, -87, +   -89, -91, -94, -96, -98,-100,-102,-103, +  -105,-107,-108,-110,-112,-113,-114,-116, +  -117,-118,-119,-120,-121,-122,-123,-123, +  -124,-125,-125,-126,-126,-126,-126,-126, +  -126,-126,-126,-126,-126,-126,-125,-125, +  -124,-123,-123,-122,-121,-120,-119,-118, +  -117,-116,-114,-113,-112,-110,-108,-107, +  -105,-103,-102,-100, -98, -96, -94, -91, +   -89, -87, -85, -82, -80, -78, -75, -73, +   -70, -67, -65, -62, -59, -57, -54, -51, +   -48, -45, -42, -39, -36, -33, -30, -27, +   -24, -21, -18, -15, -12,  -9,  -6,  -3, + +     0,   1,   2,   3,   4,   5,   6,   7, +     8,   9,  10,  11,  12,  13,  14,  15, +    16,  17,  18,  19,  20,  21,  22,  23, +    24,  25,  26,  27,  28,  29,  30,  31, +    32,  33,  34,  35,  36,  37,  38,  39, +    40,  41,  42,  43,  44,  45,  46,  47, +    48,  49,  50,  51,  52,  53,  54,  55, +    56,  57,  58,  59,  60,  61,  62,  63, +    64,  65,  66,  67,  68,  69,  70,  71, +    72,  73,  74,  75,  76,  77,  78,  79, +    80,  81,  82,  83,  84,  85,  86,  87, +    88,  89,  90,  91,  92,  93,  94,  95, +    96,  97,  98,  99, 100, 101, 102, 103, +   104, 105, 106, 107, 108, 109, 110, 111, +   112, 113, 114, 115, 116, 117, 118, 119, +   120, 121, 122, 123, 124, 125, 126, 127, +  -128,-127,-126,-125,-124,-123,-122,-121, +  -120,-119,-118,-117,-116,-115,-114,-113, +  -112,-111,-110,-109,-108,-107,-106,-105, +  -104,-103,-102,-101,-100, -99, -98, -97, +   -96, -95, -94, -93, -92, -91, -90, -89, +   -88, -87, -86, -85, -84, -83, -82, -81, +   -80, -79, -78, -77, -76, -75, -74, -73, +   -72, -71, -70, -69, -68, -67, -66, -65, +   -64, -63, -62, -61, -60, -59, -58, -57, +   -56, -55, -54, -53, -52, -51, -50, -49, +   -48, -47, -46, -45, -44, -43, -42, -41, +   -40, -39, -38, -37, -36, -35, -34, -33, +   -32, -31, -30, -29, -28, -27, -26, -25, +   -24, -23, -22, -21, -20, -19, -18, -17, +   -16, -15, -14, -13, -12, -11, -10,  -9, +    -8,  -7,  -6,  -5,  -4,  -3,  -2,  -1, + +  -120, 120, + +  -120,-120,-120,-120,-120,-120,-120,-120, +  -120,-120,-120,-120,-120,-120,-120,-120, +  -120,-120,-120,-120,-120,-120,-120,-120, +  -120,-120,-120,-120,-120,-120,-120,-120, +  -120,-120,-120,-120,-120,-120,-120,-120, +  -120,-120,-120,-120,-120,-120,-120,-120, +  -120,-120,-120,-120,-120,-120,-120,-120, +  -120,-120,-120,-120,-120,-120,-120,-120, +  -120,-120,-120,-120,-120,-120,-120,-120, +  -120,-120,-120,-120,-120,-120,-120,-120, +  -120,-120,-120,-120,-120,-120,-120,-120, +  -120,-120,-120,-120,-120,-120,-120,-120, +  -120,-120,-120,-120,-120,-120,-120,-120, +  -120,-120,-120,-120,-120,-120,-120,-120, +  -120,-120,-120,-120,-120,-120,-120,-120, +  -120,-120,-120,-120,-120,-120,-120,-120, +   120, 120, 120, 120, 120, 120, 120, 120, +   120, 120, 120, 120, 120, 120, 120, 120, +   120, 120, 120, 120, 120, 120, 120, 120, +   120, 120, 120, 120, 120, 120, 120, 120, +   120, 120, 120, 120, 120, 120, 120, 120, +   120, 120, 120, 120, 120, 120, 120, 120, +   120, 120, 120, 120, 120, 120, 120, 120, +   120, 120, 120, 120, 120, 120, 120, 120, +   120, 120, 120, 120, 120, 120, 120, 120, +   120, 120, 120, 120, 120, 120, 120, 120, +   120, 120, 120, 120, 120, 120, 120, 120, +   120, 120, 120, 120, 120, 120, 120, 120, +   120, 120, 120, 120, 120, 120, 120, 120, +   120, 120, 120, 120, 120, 120, 120, 120, +   120, 120, 120, 120, 120, 120, 120, 120, +   120, 120, 120, 120, 120, 120, 120, 120, + +    41,  35, -66,-124, -31, 108, -42, -82, +    82,-112,  73, -15, -15, -69, -23, -21, +   -77, -90, -37,  60,-121,  12,  62,-103, +    36,  94,  13,  28,   6, -73,  71, -34, +   -77,  18,  77, -56,  67, -69,-117, -90, +    31,   3,  90, 125,   9,  56,  37,  31, +    93, -44, -53,  -4,-106, -11,  69,  59, +    19,  13,-119,  10,  28, -37, -82,  50, +    32,-102,  80, -18,  64, 120,  54,  -3, +    18,  73,  50, -10, -98, 125,  73, -36, +   -83,  79,  20, -14,  68,  64, 102, -48, +   107, -60,  48, -73,  50,  59, -95,  34, +   -10,  34,-111, -99, -31,-117,  31, -38, +   -80, -54,-103,   2, -71, 114, -99,  73, +    44,-128, 126, -59,-103, -43, -23,-128, +   -78, -22, -55, -52,  83, -65, 103, -42, +   -65,  20, -42, 126,  45, -36,-114, 102, +  -125, -17,  87,  73,  97,  -1, 105,-113, +    97, -51, -47,  30, -99,-100,  22, 114, +   114, -26,  29, -16,-124,  79,  74, 119, +     2, -41, -24,  57,  44,  83, -53, -55, +    18,  30,  51, 116, -98,  12, -12, -43, +   -44, -97, -44, -92,  89, 126,  53, -49, +    50,  34, -12, -52, -49, -45,-112,  45, +    72, -45,-113, 117, -26, -39,  29,  42, +   -27, -64,  -9,  43, 120,-127,-121,  68, +    14,  95,  80,   0, -44,  97,-115, -66, +   123,   5,  21,   7,  59,  51,-126,  31, +    24, 112,-110, -38, 100,  84, -50, -79, +  -123,  62, 105,  21,  -8,  70, 106,   4, +  -106, 115,  14, -39,  22,  47, 103, 104, +   -44,  -9,  74,  74, -48,  87, 104, 118, +    -6,  22, -69,  17, -83, -82,  36,-120, +   121,  -2,  82, -37,  37,  67, -27,  60, +   -12,  69, -45, -40,  40, -50,  11, -11, +   -59,  96,  89,  61,-105,  39,-118,  89, +   118,  45, -48, -62, -55, -51, 104, -44, +    73, 106, 121,  37,   8,  97,  64,  20, +   -79,  59, 106, -91,  17,  40, -63,-116, +   -42, -87,  11,-121,-105,-116,  47, -15, +    21,  29,-102,-107, -63,-101, -31, -64, +   126, -23, -88,-102, -89,-122, -62, -75, +    84, -65,-102, -25, -39,  35, -47,  85, +  -112,  56,  40, -47, -39, 108, -95, 102, +    94,  78, -31,  48,-100,  -2, -39, 113, +   -97, -30, -91, -30,  12,-101, -76,  71, +   101,  56,  42,  70,-119, -87,-126, 121, +   122, 118, 120, -62,  99, -79,  38, -33, +   -38,  41, 109,  62,  98, -32,-106,  18, +    52, -65,  57, -90,  63,-119,  94, -15, +   109,  14, -29, 108,  40, -95,  30,  32, +    29, -53, -62,   3,  63,  65,   7,-124, +    15,  20,   5, 101,  27,  40,  97, -55, +   -59, -25,  44,-114,  70,  54,   8, -36, +   -13, -88,-115,  -2, -66, -14, -21, 113, +    -1, -96, -48,  59, 117,   6,-116, 126, +  -121, 120, 115,  77, -48, -66,-126, -66, +   -37, -62,  70,  65,  43,-116,  -6,  48, +   127, 112, -16, -89,  84,-122,  50,-107, +   -86,  91, 104,  19,  11, -26,  -4, -11, +   -54, -66, 125, -97,-119,-118,  65,  27, +    -3, -72,  79, 104, -10, 114, 123,  20, +  -103, -51, -45,  13, -16,  68,  58, -76, +   -90, 102,  83,  51,  11, -53, -95,  16 +}; + +static const byte freqTable[] = { +	  3,  10,  17,  24,  31,  38,  45,  51, +	 58,  65,  71,  77,  83,  90,  96, 102, +	107, 113, 119, 125, 130, 136, 141, 146, +	151, 157, 162, 167, 172, 177, 181, 186, +	191, 195, 200, 204, 209, 213, 217, 221, +	226, 230, 234, 238, 242, 246, 249, 253 +}; + +/*static const byte amplTable[] = { +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	// 0 % +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	 +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	// 10 % +	0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, +	0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10,	// 20 % +	0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, +	0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x20,	// 30% +	0x20, 0x20, 0x30, 0x30, 0x30, 0x30, 0x40, 0x40, +	0x00, 0x00, 0x00, 0x10, 0x10, 0x20, 0x20, 0x20,	// 40 % +	0x30, 0x30, 0x40, 0x40, 0x40, 0x50, 0x50, 0x60, +	0x00, 0x00, 0x10, 0x10, 0x20, 0x20, 0x30, 0x30,	// 50% +	0x40, 0x40, 0x50, 0x50, 0x60, 0x60, 0x70, 0x70, +	0x00, 0x00, 0x10, 0x10, 0x20, 0x30, 0x30, 0x40,	// 60 % +	0x40, 0x50, 0x60, 0x60, 0x70, 0x70, 0x80, 0x90, +	0x00, 0x00, 0x10, 0x20, 0x20, 0x30, 0x40, 0x40,	// 70 % +	0x50, 0x60, 0x70, 0x70, 0x80, 0x90, 0x90, 0xA0, +	0x00, 0x00, 0x10, 0x20, 0x30, 0x40, 0x40, 0x50,	// 80 % +	0x60, 0x70, 0x80, 0x80, 0x90, 0xA0, 0xB0, 0xC0, +	0x00, 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60,	// 90 % +	0x70, 0x80, 0x90, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, +	0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,	// 100 % +	0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0	 +};*/ + +static const byte octaveTable[] = { +	0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, +	0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, +	0x00, 0x08, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x0B,	 +	0x01, 0x00, 0x01, 0x01, 0x01, 0x02, 0x01, 0x03, +	0x01, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x07, +	0x01, 0x08, 0x01, 0x09, 0x01, 0x0A, 0x01, 0x0B, +	0x02, 0x00, 0x02, 0x01, 0x02, 0x02, 0x02, 0x03, +	0x02, 0x04, 0x02, 0x05, 0x02, 0x06, 0x02, 0x07, +	0x02, 0x08, 0x02, 0x09, 0x02, 0x0A, 0x02, 0x0B, +	0x03, 0x00, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, +	0x03, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x07, +	0x03, 0x08, 0x03, 0x09, 0x03, 0x0A, 0x03, 0x0B, +	0x04, 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, +	0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, +	0x04, 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, +	0x05, 0x00, 0x05, 0x01, 0x05, 0x02, 0x05, 0x03, +	0x05, 0x04, 0x05, 0x05, 0x05, 0x06, 0x05, 0x07, +	0x05, 0x08, 0x05, 0x09, 0x05, 0x0A, 0x05, 0x0B, +	0x06, 0x00, 0x06, 0x01, 0x06, 0x02, 0x06, 0x03, +	0x06, 0x04, 0x06, 0x05, 0x06, 0x06, 0x06, 0x07, +	0x06, 0x08, 0x06, 0x09, 0x06, 0x0A, 0x06, 0x0B, +	0x07, 0x00, 0x07, 0x01, 0x07, 0x02, 0x07, 0x03, +	0x07, 0x04, 0x07, 0x05, 0x07, 0x06, 0x07, 0x07, +	0x07, 0x08, 0x07, 0x09, 0x07, 0x0A, 0x07, 0x0B, +	0x08, 0x00, 0x08, 0x01, 0x08, 0x02, 0x08, 0x03, +	0x08, 0x04, 0x08, 0x05, 0x08, 0x06, 0x08, 0x07, +	0x08, 0x08, 0x08, 0x09, 0x08, 0x0A, 0x08, 0x0B, +	0x09, 0x00, 0x09, 0x01, 0x09, 0x02, 0x09, 0x03, +	0x09, 0x04, 0x09, 0x05, 0x09, 0x06, 0x09, 0x07, +	0x09, 0x08, 0x09, 0x09, 0x09, 0x0A, 0x09, 0x0B, +	0x0A, 0x00, 0x0A, 0x01, 0x0A, 0x02, 0x0A, 0x03, +	0x0A, 0x04, 0x0A, 0x05, 0x0A, 0x06, 0x0A, 0x07, +	0x0A, 0x08, 0x0A, 0x09, 0x0A, 0x0A, 0x0A, 0x0B +}; + +static const byte attackRate[] = { +	  0,   2,   4,   7,  14,  26,  48,  82, +	128, 144, 160, 176, 192, 208, 224, 255 +}; + +static const byte decayRate[] = { +	  0,   1,   2,   3,   4,   6,  12,  24, +	 48,  96, 192, 215, 255, 255, 255, 255 +}; + +static const byte sustainRate[] = { +	255, 180, 128,  96,  80,  64,  56,  48, +	 42,  36,  32,  28,  24,  20,  16,   0 +}; + +static const byte releaseRate[] = { +	  0,   1,   2,   4,   6,   9,  14,  22, +	 36,  56,  80, 100, 120, 140, 160, 255 +}; + +static const uint16 pcjr_freq_table[12] = { +	65472, 61760, 58304, 55040, 52032, 49024, +	46272, 43648, 41216, 38912, 36736, 34624 +}; + +static const byte volumeTable[] = { +	0x00, 0x10, 0x10, 0x11, 0x11, 0x21, 0x22, 0x22, +	0x33, 0x44, 0x55, 0x66, 0x88, 0xAA, 0xCC, 0xFF +}; + +static CMSEmulator *g_cmsEmu = 0; + +Player_V2CMS::Player_V2CMS(ScummEngine *scumm, Audio::Mixer *mixer) { +	int i; + +	_isV3Game = (scumm->_game.version >= 3); +	_vm = scumm; +	_mixer = mixer; +//	debug("mixer rate: %d", _mixer->getOutputRate()); +	_sample_rate = CMS_RATE; + +	_header_len = (scumm->_game.features & GF_OLD_BUNDLE) ? 4 : 6; + +	// Initialize sound queue +	_current_nr = _next_nr = 0; +	_current_data = _next_data = 0; + +	// Initialize channel code +	for (i = 0; i < 4; ++i) +		clear_channel(i); + +	_next_tick = 0; +	_tick_len = (_sample_rate << FIXP_SHIFT) / FREQ_HZ; + +	// Initialize V3 music timer +	_music_timer_ctr = _music_timer = 0; +	_ticks_per_music_timer = 65535; + +	setMusicVolume(255); + +	_timer_output = 0; +	for (i = 0; i < 4; i++) +		_timer_count[i] = 0; +	 +	memset(_cmsVoicesBase, 0, sizeof(Voice)*16); +	memset(_cmsVoices, 0, sizeof(Voice2)*8); +	memset(_cmsChips, 0, sizeof(MusicChip)*2); +	_midiDelay = _octaveMask = _looping = _tempo = 0; +	_midiData = _midiSongBegin = 0; +	_loadedMidiSong = 0; +	memset(_midiChannel, 0, sizeof(Voice2*)*16);	 +	memset(_midiChannelUse, 0, sizeof(byte)*16); + +	_cmsVoices[0].amplitudeOutput = &(_cmsChips[0].ampl[0]); +	_cmsVoices[0].freqOutput = &(_cmsChips[0].freq[0]); +	_cmsVoices[0].octaveOutput = &(_cmsChips[0].octave[0]); +	_cmsVoices[1].amplitudeOutput = &(_cmsChips[0].ampl[1]); +	_cmsVoices[1].freqOutput = &(_cmsChips[0].freq[1]); +	_cmsVoices[1].octaveOutput = &(_cmsChips[0].octave[0]); +	_cmsVoices[2].amplitudeOutput = &(_cmsChips[0].ampl[2]); +	_cmsVoices[2].freqOutput = &(_cmsChips[0].freq[2]); +	_cmsVoices[2].octaveOutput = &(_cmsChips[0].octave[1]); +	_cmsVoices[3].amplitudeOutput = &(_cmsChips[0].ampl[3]); +	_cmsVoices[3].freqOutput = &(_cmsChips[0].freq[3]); +	_cmsVoices[3].octaveOutput = &(_cmsChips[0].octave[1]); +	_cmsVoices[4].amplitudeOutput = &(_cmsChips[1].ampl[0]); +	_cmsVoices[4].freqOutput = &(_cmsChips[1].freq[0]); +	_cmsVoices[4].octaveOutput = &(_cmsChips[1].octave[0]); +	_cmsVoices[5].amplitudeOutput = &(_cmsChips[1].ampl[1]); +	_cmsVoices[5].freqOutput = &(_cmsChips[1].freq[1]); +	_cmsVoices[5].octaveOutput = &(_cmsChips[1].octave[0]); +	_cmsVoices[6].amplitudeOutput = &(_cmsChips[1].ampl[2]); +	_cmsVoices[6].freqOutput = &(_cmsChips[1].freq[2]); +	_cmsVoices[6].octaveOutput = &(_cmsChips[1].octave[1]); +	_cmsVoices[7].amplitudeOutput = &(_cmsChips[1].ampl[3]); +	_cmsVoices[7].freqOutput = &(_cmsChips[1].freq[3]); +	_cmsVoices[7].octaveOutput = &(_cmsChips[1].octave[1]); + +	// inits the CMS Emulator like in the original +	g_cmsEmu = new CMSEmulator(_sample_rate); +	static const byte cmsInitData[13*2] = { +		0x1C, 0x02, +		0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, +		0x14, 0x3F, 0x15, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1C, 0x01 +	}; +	 +	i = 0; +	for (int cmsPort = 0x220; i < 2; cmsPort += 2, ++i) { +		for (int off = 0; off < 13; ++off) { +			g_cmsEmu->portWrite(cmsPort+1, cmsInitData[off*2]); +			g_cmsEmu->portWrite(cmsPort, cmsInitData[off*2+1]); +		} +	} +	 +	_mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, false, true); +} + +Player_V2CMS::~Player_V2CMS() { +	mutex_up(); +	_mixer->stopHandle(_soundHandle); +	delete g_cmsEmu; +	mutex_down(); +} + +void Player_V2CMS::setMusicVolume(int vol) { +} + +void Player_V2CMS::chainSound(int nr, byte *data) { +	int offset = _header_len + 10; + +	_current_nr = nr; +	_current_data = data; + +	for (int i = 0; i < 4; i++) { +		clear_channel(i); + +		_channels[i].d.music_script_nr = nr; +		if (data) { +			_channels[i].d.next_cmd = READ_LE_UINT16(data + offset + 2 * i); +			if (_channels[i].d.next_cmd) { +				_channels[i].d.time_left = 1; +			} +		} +	} +	_music_timer = 0; +} + +void Player_V2CMS::chainNextSound() { +	if (_next_nr) { +		chainSound(_next_nr, _next_data); +		_next_nr = 0; +		_next_data = 0; +	} +} + +void Player_V2CMS::stopAllSounds() { +	mutex_up(); +	for (int i = 0; i < 4; i++) { +		clear_channel(i); +	} +	_next_nr = _current_nr = 0; +	_next_data = _current_data = 0; +	_midiData = 0; +	_midiSongBegin = 0; +	_midiDelay = 0; +	offAllChannels(); +	mutex_down(); +} + +void Player_V2CMS::stopSound(int nr) { +	mutex_up(); +	if (_next_nr == nr) { +		_next_nr = 0; +		_next_data = 0; +	} +	if (_current_nr == nr) { +		for (int i = 0; i < 4; i++) { +			clear_channel(i); +		} +		_current_nr = 0; +		_current_data = 0; +		chainNextSound(); +	} +	if (_loadedMidiSong == nr) { +		_midiData = 0; +		_midiSongBegin = 0; +		_midiDelay = 0; +		offAllChannels(); +	} +	mutex_down(); +} + +void Player_V2CMS::startSound(int nr) { +	byte *data = _vm->getResourceAddress(rtSound, nr); +	assert(data); +	 +	if (data[6] == 0x80) { +		mutex_up(); +		loadMidiData(data, nr); +		mutex_down(); +	} else { +		mutex_up(); + +		int cprio = _current_data ? *(_current_data + _header_len) : 0; +		int prio  = *(data + _header_len); +		int nprio = _next_data ? *(_next_data + _header_len) : 0; + +		int restartable = *(data + _header_len + 1); + +		if (!_current_nr || cprio <= prio) { +			int tnr = _current_nr; +			int tprio = cprio; +			byte *tdata  = _current_data; + +			chainSound(nr, data); +			nr   = tnr; +			prio = tprio; +			data = tdata; +			restartable = data ? *(data + _header_len + 1) : 0; +		} + +		if (!_current_nr) { +			nr = 0; +			_next_nr = 0; +			_next_data = 0; +		} + +		if (nr != _current_nr +			&& restartable +			&& (!_next_nr +			|| nprio <= prio)) { + +			_next_nr = nr; +			_next_data = data; +		} + +		mutex_down(); +	} +} + +void Player_V2CMS::loadMidiData(byte *data, int sound) { +	memset(_midiChannelUse, 0, sizeof(byte)*16); +	 +	_tempo = data[7]; +	_looping = data[8]; +	 +	byte channels = data[14]; +	byte curChannel = 0; +	byte *voice2 = data + 23; +	 +	for (; channels != 0; ++curChannel, --channels, voice2 += 16) { +		if (*(data + 15 + curChannel)) { +			byte channel = *(data + 15 + curChannel) - 1; +			_midiChannelUse[channel] = 1; +			 +			Voice *voiceDef = &_cmsVoicesBase[channel]; +			 +			byte attackDecay = voice2[10]; +			voiceDef->attack = attackRate[attackDecay >> 4]; +			voiceDef->decay = decayRate[attackDecay & 0x0F]; +			byte sustainRelease = voice2[11]; +			voiceDef->sustain = sustainRate[sustainRelease >> 4]; +			voiceDef->release = releaseRate[sustainRelease & 0x0F]; +			 +			if (voice2[3] & 0x40) { +				voiceDef->vibrato = 0x0301; +				if (voice2[13] & 0x40) { +					voiceDef->vibrato = 0x0601; +				} +			} else { +				voiceDef->vibrato = 0; +			} +			 +			if (voice2[8] & 0x80) { +				voiceDef->vibrato2 = 0x0506; +				if (voice2[13] & 0x80) { +					voiceDef->vibrato2 = 0x050C; +				} +			} else { +				voiceDef->vibrato2 = 0; +			} +			 +			if ((voice2[8] & 0x0F) > 1) { +				voiceDef->octadd = 0x01; +			} else { +				voiceDef->octadd = 0x00; +			} +		} +	} +	 +	for (int i = 0, channel = 0; i < 8; ++i, channel += 2) { +		_cmsVoices[i].chanNumber = 0xFF; +		_cmsVoices[i].curVolume = 0; +		_cmsVoices[i].nextVoice = 0; + +		_midiChannel[channel] = 0; +	} +	 +	_midiDelay = 0; +	memset(_cmsChips, 0, sizeof(MusicChip)*2); +	_midiData = data + 151; +	_midiSongBegin = _midiData + data[9]; +	 +	_loadedMidiSong = sound; +} + +int Player_V2CMS::getSoundStatus(int nr) const { +	return _current_nr == nr || _next_nr == nr || _loadedMidiSong == nr; +} + + +void Player_V2CMS::clear_channel(int i) { +	ChannelInfo *channel = &_channels[i]; +	memset(channel, 0, sizeof(ChannelInfo)); +} + +int Player_V2CMS::getMusicTimer() const { +	if (_isV3Game) +		return _music_timer; +	else +		return _channels[0].d.music_timer; +} + +void Player_V2CMS::execute_cmd(ChannelInfo *channel) { +	uint16 value; +	int16 offset; +	uint8 *script_ptr; +	ChannelInfo * current_channel; +	ChannelInfo * dest_channel; + +	current_channel = channel; + +	if (channel->d.next_cmd == 0) +		goto check_stopped; +	script_ptr = &_current_data[channel->d.next_cmd]; + +	for (;;) { +		uint8 opcode = *script_ptr++; +		if (opcode >= 0xf8) { +			switch (opcode) { +			case 0xf8: // set hull curve +				debug(7, "channels[%d]: hull curve %2d", +				channel - _channels, *script_ptr); +				channel->d.hull_curve = hull_offsets[*script_ptr / 2]; +				script_ptr++; +				break; + +			case 0xf9: // set freqmod curve +				debug(7, "channels[%d]: freqmod curve %2d", +				channel - _channels, *script_ptr); +				channel->d.freqmod_table = freqmod_offsets[*script_ptr / 4]; +				channel->d.freqmod_modulo = freqmod_lengths[*script_ptr / 4]; +				script_ptr++; +				break; + +			case 0xfd: // clear other channel +				value = READ_LE_UINT16 (script_ptr) / sizeof (ChannelInfo); +				debug(7, "clear channel %d", value); +				script_ptr += 2; +				// In Indy3, when traveling to Venice a command is +				// issued to clear channel 4. So we introduce a 4th +				// channel, which is never used.  All OOB accesses are +				// mapped to this channel. +				// +				// The original game had room for 8 channels, but only +				// channels 0-3 are read, changes to other channels +				// had no effect. +				if (value >= ARRAYSIZE (_channels)) +					value = 4; +				channel = &_channels[value]; +				// fall through + +			case 0xfa: // clear current channel +				if (opcode == 0xfa) +					debug(7, "clear channel"); +				channel->d.next_cmd   = 0; +				channel->d.base_freq  = 0; +				channel->d.freq_delta = 0; +				channel->d.freq = 0; +				channel->d.volume = 0; +				channel->d.volume_delta = 0; +				channel->d.inter_note_pause = 0; +				channel->d.transpose = 0; +				channel->d.hull_curve = 0; +				channel->d.hull_offset = 0; +				channel->d.hull_counter = 0; +				channel->d.freqmod_table = 0; +				channel->d.freqmod_offset = 0; +				channel->d.freqmod_incr = 0; +				channel->d.freqmod_multiplier = 0; +				channel->d.freqmod_modulo = 0; +				break; + +			case 0xfb: // ret from subroutine +				debug(7, "ret from sub"); +				script_ptr = _retaddr; +				break; + +			case 0xfc: // call subroutine +				offset = READ_LE_UINT16 (script_ptr); +				debug(7, "subroutine %d", offset); +				script_ptr += 2; +				_retaddr = script_ptr; +				script_ptr = _current_data + offset; +				break; + +			case 0xfe: // loop music +				opcode = *script_ptr++; +				offset = READ_LE_UINT16 (script_ptr); +				script_ptr += 2; +				debug(7, "loop if %d to %d", opcode, offset); +				if (!channel->array[opcode / 2] || --channel->array[opcode/2]) +					script_ptr += offset; +				break; + +			case 0xff: // set parameter +				opcode = *script_ptr++; +				value = READ_LE_UINT16 (script_ptr); +				channel->array[opcode / 2] = value; +				debug(7, "channels[%d]: set param %2d = %5d", +						channel - &_channels[0], opcode, value); +				script_ptr += 2; +				if (opcode == 14) { +				    /* tempo var */ +				    _ticks_per_music_timer = 125; +				} +				if (opcode == 0) +					goto end; +				break; +			} +		} else { // opcode < 0xf8 +			for (;;) { +				int16 note, octave; +				int is_last_note; +				dest_channel = &_channels[(opcode >> 5) & 3]; + +				if (!(opcode & 0x80)) { + +					int tempo = channel->d.tempo; +					if (!tempo) +						tempo = 1; +					channel->d.time_left = tempo * note_lengths[opcode & 0x1f]; + +					note = *script_ptr++; +					is_last_note = note & 0x80; +					note &= 0x7f; +					if (note == 0x7f) { +						debug(8, "channels[%d]: pause %d", +							  channel - _channels, channel->d.time_left); +						goto end; +					} +				} else { + +					channel->d.time_left = ((opcode & 7) << 8) | *script_ptr++; + +					if ((opcode & 0x10)) { +						debug(8, "channels[%d]: pause %d", +							  channel - _channels, channel->d.time_left); +						goto end; +					} + +					is_last_note = 0; +					note = (*script_ptr++) & 0x7f; +				} + +				debug(8, "channels[%d]: @%04x note: %3d+%d len: %2d hull: %d mod: %d/%d/%d %s", +						dest_channel - channel, script_ptr ? script_ptr - _current_data - 2 : 0, +						note, (signed short) dest_channel->d.transpose, channel->d.time_left, +						dest_channel->d.hull_curve, dest_channel->d.freqmod_table, +						dest_channel->d.freqmod_incr,dest_channel->d.freqmod_multiplier, +						is_last_note ? "last":""); + +				uint16 myfreq; +				dest_channel->d.time_left = channel->d.time_left; +				dest_channel->d.note_length = +					channel->d.time_left - dest_channel->d.inter_note_pause; +				note += dest_channel->d.transpose; +				while (note < 0) +					note += 12; +				octave = note / 12; +				note = note % 12; +				dest_channel->d.hull_offset = 0; +				dest_channel->d.hull_counter = 1; +				if (dest_channel == &_channels[3]) { +					dest_channel->d.hull_curve = 196 + note * 12; +					myfreq = 384 - 64 * octave; +				} else { +					myfreq = pcjr_freq_table[note] >> octave; +				} +				dest_channel->d.freq = dest_channel->d.base_freq = myfreq; +				if (is_last_note) +					goto end; +				opcode = *script_ptr++; +			} +		} +	} + +end: +	channel = current_channel; +	if (channel->d.time_left) { +		channel->d.next_cmd = script_ptr - _current_data; +		return; +	} + +	channel->d.next_cmd = 0; + +check_stopped: +	int i; +	for (i = 0; i < 4; i++) { +		if (_channels[i].d.time_left) +			return; +	} + +	_current_nr = 0; +	_current_data = 0; +	chainNextSound(); +	return; +} + +void Player_V2CMS::next_freqs(ChannelInfo *channel) { +	channel->d.volume    += channel->d.volume_delta; +	channel->d.base_freq += channel->d.freq_delta; + +	channel->d.freqmod_offset += channel->d.freqmod_incr; +	if (channel->d.freqmod_offset != 0) +		if (channel->d.freqmod_offset > channel->d.freqmod_modulo) +			channel->d.freqmod_offset -= channel->d.freqmod_modulo; +		 +	channel->d.freq = +		(int) (freqmod_table[channel->d.freqmod_table + (channel->d.freqmod_offset >> 4)]) +		* (int) channel->d.freqmod_multiplier / 256 +		+ channel->d.base_freq; + +	debug(9, "Freq: %d/%d, %d/%d/%d*%d %d", +			channel->d.base_freq, (int16)channel->d.freq_delta, +			channel->d.freqmod_table, channel->d.freqmod_offset, +			channel->d.freqmod_incr, channel->d.freqmod_multiplier, +			channel->d.freq); + +	if (channel->d.note_length && !--channel->d.note_length) { +		channel->d.hull_offset  = 16; +		channel->d.hull_counter = 1; +	} + +	if (!--channel->d.time_left) { +		execute_cmd(channel); +	} + +	if (channel->d.hull_counter && !--channel->d.hull_counter) { +		for (;;) { +			const int16 *hull_ptr = hulls +			+ channel->d.hull_curve + channel->d.hull_offset / 2; +			if (hull_ptr[1] == -1) { +				channel->d.volume = hull_ptr[0]; +				if (hull_ptr[0] == 0) +					channel->d.volume_delta = 0; +				channel->d.hull_offset += 4; +			} else { +				channel->d.volume_delta = hull_ptr[0]; +				channel->d.hull_counter = hull_ptr[1]; +				channel->d.hull_offset += 4; +				break; +			} +		} +	} +} + +void Player_V2CMS::nextTick() { +	for (int i = 0; i < 4; i++) { +		if (!_channels[i].d.time_left) +			continue; +		next_freqs(&_channels[i]); +	} +	if (_music_timer_ctr++ >= _ticks_per_music_timer) { +		_music_timer_ctr = 0; +		_music_timer++; +	} +} + +void Player_V2CMS::processMidiData(uint ticks) { +	byte *currentData = _midiData; +	byte command = 0x00; +	int16 temp = 0; + +	if (!_midiDelay) { +		while (true) { +			if ((command = *currentData++) == 0xFF) { +				if ((command = *currentData++) == 0x2F) { +					if (_looping == 0) { +						currentData = _midiData = _midiSongBegin; +						continue; +					} +					_midiData = _midiSongBegin = 0; +					offAllChannels(); +					return; +				} else { +					if (command == 0x58) { +						currentData += 6; +					} +				} +			} else { +				_lastMidiCommand = command; +				if (command < 0x90) { +					clearNote(currentData);					 +				} else { +					playNote(currentData); +				} +			} +			 +			temp = command = *currentData++; +			if (command & 0x80) { +				temp = (command & 0x7F) << 8; +				command = *currentData++; +				temp |= (command << 1); +				temp >>= 1; +			} +			temp >>= 1; +			int lastBit = temp & 1; +			temp >>= 1; +			temp += lastBit; +			 +			if (temp) +				break; +		} +		_midiData = currentData; +		_midiDelay = temp; +	} +	 +	_midiDelay -= ticks; +	if (_midiDelay < 0) +		_midiDelay = 0; + +	return; +} + +int Player_V2CMS::readBuffer(int16 *buffer, const int numSamples) { +	mutex_up(); +	uint step = 1; +	int len = numSamples/2; + +	// maybe this needs a complete rewrite +	do { +		if (_midiData) { +			--_clkFrequenz; +			if (!(_clkFrequenz & 0x01)) { +				playVoice(); +			} +		 +			_tempoSum += _tempo; +			if (_tempoSum < 0) { +				// this have to be called in the same rate as in the original (I think) +				processMidiData(1); +			} +		} +		 +		if (!(_next_tick >> FIXP_SHIFT) && !_midiData) { +			_next_tick += _tick_len; +			nextTick(); +			play(); +		} + +		step = len; +		if (step > (_next_tick >> FIXP_SHIFT)) +			step = (_next_tick >> FIXP_SHIFT); +		g_cmsEmu->readBuffer(buffer, step); +		buffer += 2 * step; +		_next_tick -= step << FIXP_SHIFT; +	} while (len -= step); + +	mutex_down(); +	return numSamples; +} + +void Player_V2CMS::playVoice() { +	if (_outputTableReady) { +		playMusicChips(_cmsChips); +		_outputTableReady = 0; +	} +	 +	_octaveMask = 0xF0; +	Voice2 *voice =0; +	for (int i = 0; i < 8; ++i) { +		voice = &_cmsVoices[i]; +		_octaveMask = ~_octaveMask; +		 +		if (voice->chanNumber != 0xFF) { +			processChannel(voice); +			continue; +		} +		 +		if (!voice->curVolume) { +			*(voice->amplitudeOutput) = 0; +		} +		 +		int volume = voice->curVolume - voice->releaseRate; +		voice->curVolume = volume; +		 +		if (volume < 0) { +			volume = voice->curVolume = 0; +		} +		 +		*(voice->amplitudeOutput) = ((volume >> 4) | (volume & 0xF0)) & voice->channel; +		++_outputTableReady; +	} +} + +void Player_V2CMS::processChannel(Voice2 *channel) { +	++_outputTableReady; +	switch (channel->nextProcessState) { +		case PROCESS_RELEASE: +			processRelease(channel); +		break; +		 +		case PROCESS_ATTACK: +			processAttack(channel); +		break; +		 +		case PROCESS_DECAY: +			processDecay(channel); +		break; +		 +		case PROCESS_SUSTAIN: +			processSustain(channel); +		break; +		 +		case PROCESS_VIBRATO: +			processVibrato(channel); +		break; +		 +		default: +		break; +	} +} + +void Player_V2CMS::processRelease(Voice2 *channel) { +	channel->curVolume -= channel->releaseRate; +	if (channel->curVolume < 0) +		channel->curVolume = 0; +	processVibrato(channel); +} + +void Player_V2CMS::processAttack(Voice2 *channel) { +	channel->curVolume += channel->attackRate; +	if (channel->curVolume >= 0) { +		if (channel->curVolume <= channel->maxAmpl) +			return processVibrato(channel); +	} +	channel->curVolume = channel->maxAmpl; +	channel->nextProcessState = PROCESS_DECAY; +	processVibrato(channel); +} + +void Player_V2CMS::processDecay(Voice2 *channel) { +	channel->curVolume -= channel->decayRate; +	if (channel->curVolume >= 0) { +		if (channel->curVolume > channel->sustainRate) +			return processVibrato(channel); +	} +	channel->curVolume = channel->sustainRate; +	channel->nextProcessState = PROCESS_SUSTAIN; +	processVibrato(channel); +} + +void Player_V2CMS::processSustain(Voice2 *channel) { +	if (channel->unkVibratoRate) { +		int volume = (int)channel->curVolume + (int)channel->unkRate; +		if (volume & 0xFF00) { +			volume = ((~volume) >> 8) & 0xFF; +		} +		channel->curVolume = volume; +		--(channel->unkCount); +		if (!channel->unkCount) { +			channel->unkRate = ~(channel->unkRate); +			channel->unkCount = (channel->unkVibratoDepth & 0x0F) << 1; +		} +	} +	processVibrato(channel); +} + +void Player_V2CMS::processVibrato(Voice2 *channel) { +	if (channel->vibratoRate) { +		uint16 temp = channel->curFreq + channel->curVibratoRate; +		channel->curOctave += (temp & 0xFF00) >> 8; +		channel->curFreq = temp & 0xFF; +		--(channel->curVibratoUnk); +		if (!channel->curVibratoUnk) { +			channel->curVibratoRate = ~(channel->curVibratoRate); +			channel->curVibratoUnk = (channel->vibratoDepth & 0x0F) << 1; +		} +	} +	 +	byte *output = channel->amplitudeOutput; +	*output = ((channel->curVolume >> 4) | (channel->curVolume & 0xF0)) & channel->channel; +	output = channel->freqOutput; +	*output = channel->curFreq; +	output = channel->octaveOutput; +	*output = ((((channel->curOctave >> 4) | (channel->curOctave & 0x0F)) & _octaveMask) | ((~_octaveMask) & *output)); +} + +void Player_V2CMS::offAllChannels() { +	warning("offAllChannels STUB"); +/*	 +	// after using this sound can not be played anymore (since it would deinit the emulator) +	static const byte cmsOffData[10*2] = { +		0x1C, 0x02, +		0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, +		0x14, 0x3F, 0x15, 0x00, 0x16, 0x00 +	}; +	 +	for (int cmsPort = 0x220, i = 0; i < 2; cmsPort += 2, ++i) { +		for (int off = 0; off < 10; ++off) { +			g_cmsEmu->portWrite(cmsPort+1, cmsOffData[off*2]); +			g_cmsEmu->portWrite(cmsPort, cmsOffData[off*2+1]); +		} +	}*/ +} + +Player_V2CMS::Voice2 *Player_V2CMS::getFreeVoice() { +	Voice2 *curVoice = 0; +	Voice2 *selected = 0; +	uint8 volume = 0xFF; +	 +	for (int i = 0; i < 8; ++i) { +		curVoice = &_cmsVoices[i]; + +		if (curVoice->chanNumber == 0xFF) { +			if (!curVoice->curVolume) { +				selected = curVoice; +				break; +			} +			 +			if (curVoice->curVolume < volume) { +				selected = curVoice; +				volume = selected->curVolume; +			} +		} +	} +	 +	if (selected) { +		selected->chanNumber = _lastMidiCommand & 0x0F; +		 +		uint8 channel = selected->chanNumber; +		Voice2 *oldChannel = _midiChannel[channel]; +		_midiChannel[channel] = selected; +		selected->nextVoice = oldChannel; +	} +	 +	return selected; +} + +void Player_V2CMS::playNote(byte *&data) { +	byte channel = _lastMidiCommand & 0x0F; +	if (_midiChannelUse[channel]) { +		Voice2 *freeVoice = getFreeVoice(); +		if (freeVoice) { +			Voice *voice = &_cmsVoicesBase[freeVoice->chanNumber]; +			freeVoice->attackRate = voice->attack; +			freeVoice->decayRate = voice->decay; +			freeVoice->sustainRate = voice->sustain; +			freeVoice->releaseRate = voice->release; +			freeVoice->octaveAdd = voice->octadd; +			freeVoice->vibratoRate = freeVoice->curVibratoRate = voice->vibrato; +			freeVoice->unkVibratoRate = freeVoice->unkRate = voice->vibrato2; +			freeVoice->maxAmpl = 0xFF; +			 +			uint8 rate = freeVoice->attackRate; +			uint8 volume = freeVoice->curVolume >> 1; +			 +			if (rate < volume) +				rate = volume; +				 +			rate -= freeVoice->attackRate; +			freeVoice->curVolume = rate; +			freeVoice->playingNote = *data; +			int octave = octaveTable[(*data + 3) << 1] + freeVoice->octaveAdd - 3; +			if (octave < 0) +				octave = 0; +			if (octave > 7) +				octave = 7; +			if (!octave) +				++octave; +			freeVoice->curOctave = octave; +			freeVoice->curFreq = freqTable[volume << 2]; +			freeVoice->curVolume = 0; +			freeVoice->nextProcessState = PROCESS_ATTACK; +			if (_lastMidiCommand & 1) +				freeVoice->channel = 0xF0; +			else +				freeVoice->channel = 0x0F; +		} +	} +	data += 2; +} + +Player_V2CMS::Voice2 *Player_V2CMS::getPlayVoice(byte param) { +	byte channelNum = _lastMidiCommand & 0x0F; +	Voice2 *channel = _midiChannel[channelNum]; +	 +	if (channel) { +		Voice2 *backUp = 0; +		while (true) { +			if (channel->playingNote == param) +				break; + +			backUp = channel; +			channel = channel->nextVoice; +			if (!channel) +				return 0; +		} +		 +		Voice2 *backUp2 = channel->nextVoice; +		{ +			Voice2 *temp = backUp; +			backUp = channel; +			channel = temp; +		} +		if (channel) { +			channel->nextVoice = backUp2; +		} else { +			_midiChannel[channelNum] = backUp2; +		} +		channel = backUp; +	} +	 +	return channel; +} + +void Player_V2CMS::clearNote(byte *&data) { +	Voice2 *voice = getPlayVoice(*data); +	if (voice) { +		voice->chanNumber = 0xFF; +		voice->nextVoice = 0; +		voice->nextProcessState = PROCESS_RELEASE; +	} +	data += 2; +} + +void Player_V2CMS::play() { +	_octaveMask = 0xF0; +	channel_data *chan = &(_channels[0].d); +	 +	static byte volumeReg[4] = { 0x00, 0x00, 0x00, 0x00 }; +	static byte octaveReg[4] = { 0x66, 0x66, 0x66, 0x66 }; +	static byte freqReg[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; +	 +	static byte freqEnable = 0x3E; +	static byte noiseEnable = 0x01; +	static byte noiseGen = 0x02; +	for (int i = 1; i <= 4; ++i) { +		if (chan->time_left) { +			uint16 freq = chan->freq; +		 +			if (i == 4) { +				if ((freq >> 8) & 0x40) { +					noiseGen = freq & 0xFF; +				} else { +					noiseGen = 3; +					freqReg[0] = freqReg[3]; +					octaveReg[0] = (octaveReg[0] & 0xF0) | ((octaveReg[1] & 0xF0) >> 4); +				} +			} else { +				if (freq == 0) { +					freq = 0xFFC0; +				} +				 +				int cmsOct = 2; +				int freqOct = 0x8000; +				 +				while (true) { +					if (freq >= freqOct) { +						break; +					} +					freqOct >>= 1; +					++cmsOct; +					if (cmsOct == 8) { +						--cmsOct; +						freq = 1024; +						break; +					} +				} +				byte oct = cmsOct << 4; +				oct |= cmsOct; +				 +				oct &= _octaveMask; +				oct |= ((~_octaveMask) & octaveReg[((i & 3) >> 1)]); +				octaveReg[((i & 3) >> 1)] = oct; +				 +				freq >>= -(cmsOct-9); +				freqReg[(i&3)] = (-(freq-511)) & 0xFF; +			} +			volumeReg[i & 3] = volumeTable[chan->volume >> 12]; +		} else { +			volumeReg[i & 3] = 0; +		} +		chan = &(_channels[i].d); +		_octaveMask ^= 0xFF; +	} + +	// with the high nibble of the volumeReg value +	// the right channels amplitude is set +	// with the low value the left channels amplitude +	g_cmsEmu->portWrite(0x221, 0); +	g_cmsEmu->portWrite(0x220, volumeReg[0]); +	g_cmsEmu->portWrite(0x221, 1); +	g_cmsEmu->portWrite(0x220, volumeReg[1]); +	g_cmsEmu->portWrite(0x221, 2); +	g_cmsEmu->portWrite(0x220, volumeReg[2]); +	g_cmsEmu->portWrite(0x221, 3); +	g_cmsEmu->portWrite(0x220, volumeReg[3]); +	g_cmsEmu->portWrite(0x221, 8); +	g_cmsEmu->portWrite(0x220, freqReg[0]); +	g_cmsEmu->portWrite(0x221, 9); +	g_cmsEmu->portWrite(0x220, freqReg[1]); +	g_cmsEmu->portWrite(0x221, 10); +	g_cmsEmu->portWrite(0x220, freqReg[2]); +	g_cmsEmu->portWrite(0x221, 11); +	g_cmsEmu->portWrite(0x220, freqReg[3]); +	g_cmsEmu->portWrite(0x221, 0x10); +	g_cmsEmu->portWrite(0x220, octaveReg[0]); +	g_cmsEmu->portWrite(0x221, 0x11); +	g_cmsEmu->portWrite(0x220, octaveReg[1]); +	g_cmsEmu->portWrite(0x221, 0x14); +	g_cmsEmu->portWrite(0x220, freqEnable);	 +	g_cmsEmu->portWrite(0x221, 0x15); +	g_cmsEmu->portWrite(0x220, noiseEnable);	 +	g_cmsEmu->portWrite(0x221, 0x16); +	g_cmsEmu->portWrite(0x220, noiseGen); +} + +void Player_V2CMS::playMusicChips(const MusicChip *table) { +	int cmsPort = 0x21E; + +	do { +		cmsPort += 2; +		g_cmsEmu->portWrite(cmsPort+1, 0); +		g_cmsEmu->portWrite(cmsPort, table->ampl[0]); +		g_cmsEmu->portWrite(cmsPort+1, 1); +		g_cmsEmu->portWrite(cmsPort, table->ampl[1]); +		g_cmsEmu->portWrite(cmsPort+1, 2); +		g_cmsEmu->portWrite(cmsPort, table->ampl[2]); +		g_cmsEmu->portWrite(cmsPort+1, 3); +		g_cmsEmu->portWrite(cmsPort, table->ampl[3]); +		g_cmsEmu->portWrite(cmsPort+1, 8); +		g_cmsEmu->portWrite(cmsPort, table->freq[0]); +		g_cmsEmu->portWrite(cmsPort+1, 9); +		g_cmsEmu->portWrite(cmsPort, table->freq[1]); +		g_cmsEmu->portWrite(cmsPort+1, 10); +		g_cmsEmu->portWrite(cmsPort, table->freq[2]); +		g_cmsEmu->portWrite(cmsPort+1, 11); +		g_cmsEmu->portWrite(cmsPort, table->freq[3]); +		g_cmsEmu->portWrite(cmsPort+1, 0x10); +		g_cmsEmu->portWrite(cmsPort, table->octave[0]); +		g_cmsEmu->portWrite(cmsPort+1, 0x11); +		g_cmsEmu->portWrite(cmsPort, table->octave[1]); +		g_cmsEmu->portWrite(cmsPort+1, 0x14); +		g_cmsEmu->portWrite(cmsPort, 0x3F); +		g_cmsEmu->portWrite(cmsPort+1, 0x15); +		g_cmsEmu->portWrite(cmsPort, 0x00); +		++table; +	} while ((cmsPort & 2) == 0); +} + +void Player_V2CMS::mutex_up() { +	_mutex.lock(); +} + +void Player_V2CMS::mutex_down() { +	_mutex.unlock(); +} +} // end of namespace Scumm diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 07660ab5bf..dcbf95ef4b 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -1585,6 +1585,13 @@ void ScummEngine::setupMusic(int midi) {  	case MD_PCJR:  		_musicType = MDT_PCSPK;  		break; +	case MD_CMS: +#if 1 +		_musicType = MDT_ADLIB; +#else +		_musicType = MDT_CMS; // Still has number of bugs, disable by default +#endif +		break;  	case MD_TOWNS:  		_musicType = MDT_TOWNS;  		break; @@ -1639,7 +1646,7 @@ void ScummEngine::setupMusic(int midi) {  	 * automatically when samples need to be generated */  	if (!_mixer->isReady()) {  		warning("Sound mixer initialization failed"); -		if (_musicType == MDT_ADLIB || _musicType == MDT_PCSPK)	{ +		if (_musicType == MDT_ADLIB || _musicType == MDT_PCSPK || _musicType == MDT_CMS)	{  			midiDriver = MD_NULL;  			_musicType = MDT_NONE;  			warning("MIDI driver depends on sound mixer, switching to null MIDI driver"); @@ -1669,6 +1676,8 @@ void ScummEngine::setupMusic(int midi) {  		_musicEngine = new Player_V2(this, _mixer, midiDriver != MD_PCSPK);  	} else if ((_musicType == MDT_PCSPK) && (_game.version > 2 && _game.version <= 4)) {  		_musicEngine = new Player_V2(this, _mixer, midiDriver != MD_PCSPK); +	} else if (_musicType == MDT_CMS) { +		_musicEngine = new Player_V2CMS(this, _mixer);  	} else if (_game.platform == Common::kPlatform3DO && _game.heversion == 61) {  		// 3DO versions use digital music and sound samples.  	} else if (_game.version >= 3 && _game.heversion <= 61) { diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp index f1be5e0b17..bc3ca00571 100644 --- a/engines/scumm/sound.cpp +++ b/engines/scumm/sound.cpp @@ -1181,7 +1181,7 @@ int ScummEngine::readSoundResource(int idx) {  				break;  			} -			if ((_musicType == MDT_PCSPK) && pri != 11) +			if ((_musicType == MDT_PCSPK || _musicType == MDT_CMS) && pri != 11)  				pri = -1;  			debugC(DEBUG_RESOURCE, "    tag: %s, total_size=%d, pri=%d", tag2str(tag), size, pri); @@ -2121,6 +2121,19 @@ int ScummEngine::readSoundResourceSmallHeader(int idx) {  			_fileHandle->read(_res->createResource(rtSound, idx, wa_size + 6), wa_size + 6);  		}  		return 1; +	} else if (_musicType == MDT_CMS && ad_offs != 0) { +		if (_game.features & GF_OLD_BUNDLE) { +			_fileHandle->seek(wa_offs + wa_size + 6, SEEK_SET); +			byte musType = _fileHandle->readByte(); +		 +			if (musType == 0x80) { +				_fileHandle->seek(ad_offs, SEEK_SET); +				_fileHandle->read(_res->createResource(rtSound, idx, ad_size), ad_size); +			} else { +				_fileHandle->seek(wa_offs, SEEK_SET); +				_fileHandle->read(_res->createResource(rtSound, idx, wa_size), wa_size); +			} +		}  	} else if (ad_offs != 0) {  		// AD resources have a header, instrument definitions and one MIDI track.  		// We build an 'ADL ' resource from that: diff --git a/engines/scumm/vars.cpp b/engines/scumm/vars.cpp index 631c88ffa6..caae659766 100644 --- a/engines/scumm/vars.cpp +++ b/engines/scumm/vars.cpp @@ -706,6 +706,9 @@ void ScummEngine::resetScummVars() {  		case MDT_PCSPK:  			VAR(VAR_SOUNDCARD) = 0;  			break; +		case MDT_CMS: +			VAR(VAR_SOUNDCARD) = 2; +			break;  		case MDT_ADLIB:  			VAR(VAR_SOUNDCARD) = 3;  			break; diff --git a/sound/mididrv.cpp b/sound/mididrv.cpp index 85d6994992..11c11c35d4 100644 --- a/sound/mididrv.cpp +++ b/sound/mididrv.cpp @@ -88,6 +88,7 @@ static const MidiDriverDescription s_musicDrivers[] = {  	{"adlib", "AdLib", MD_ADLIB, MDT_ADLIB},  	{"pcspk", "PC Speaker", MD_PCSPK, MDT_PCSPK},  	{"pcjr", "IBM PCjr", MD_PCJR, MDT_PCSPK}, +	{"cms", "Creative Music System", MD_CMS, MDT_CMS},  	{"towns", "FM Towns", MD_TOWNS, MDT_TOWNS},  #if defined(UNIX)  	{"timidity", "TiMidity", MD_TIMIDITY, MDT_MIDI}, @@ -236,6 +237,7 @@ MidiDriver *MidiDriver::createMidi(int midiDriver) {  	// outside the MidiDriver architecture, so  	// don't create anything for now.  	case MD_PCSPK: +	case MD_CMS:  	case MD_PCJR:      return NULL;  #ifdef USE_FLUIDSYNTH diff --git a/sound/mididrv.h b/sound/mididrv.h index 700fd01f6b..bf2b804e16 100644 --- a/sound/mididrv.h +++ b/sound/mididrv.h @@ -80,6 +80,7 @@ enum MidiDriverType {  	// "Fake" MIDI devices  	MD_ADLIB,  	MD_PCSPK, +	MD_CMS,  	MD_PCJR,  	MD_TOWNS,  	MD_TIMIDITY @@ -98,10 +99,11 @@ enum MidiDriverType {  enum MidiDriverFlags {  	MDT_NONE   = 0,  	MDT_PCSPK  = 1 << 0,      // PC Speaker: Maps to MD_PCSPK and MD_PCJR -	MDT_ADLIB  = 1 << 1,      // Adlib: Maps to MD_ADLIB -	MDT_TOWNS  = 1 << 2,      // FM-TOWNS: Maps to MD_TOWNS -	MDT_MIDI   = 1 << 3,      // Real MIDI -	MDT_PREFER_MIDI = 1 << 4  // Real MIDI output is preferred +	MDT_CMS    = 1 << 1,      // Creative Music System / Gameblaster: Maps to MD_CMS +	MDT_ADLIB  = 1 << 2,      // Adlib: Maps to MD_ADLIB +	MDT_TOWNS  = 1 << 3,      // FM-TOWNS: Maps to MD_TOWNS +	MDT_MIDI   = 1 << 4,      // Real MIDI +	MDT_PREFER_MIDI = 1 << 5  // Real MIDI output is preferred  };  /** | 
