diff options
Diffstat (limited to 'engines/kyra/sound_towns.cpp')
| -rw-r--r-- | engines/kyra/sound_towns.cpp | 1890 | 
1 files changed, 1410 insertions, 480 deletions
diff --git a/engines/kyra/sound_towns.cpp b/engines/kyra/sound_towns.cpp index ec1962a58f..abda839ab4 100644 --- a/engines/kyra/sound_towns.cpp +++ b/engines/kyra/sound_towns.cpp @@ -23,7 +23,6 @@   *   */ -  #include "common/system.h"  #include "kyra/resource.h"  #include "kyra/sound.h" @@ -64,13 +63,13 @@ public:  	MidiDriver *device() { return 0; }  	byte getNumber() { return 0; }  	void release() { } -	void send(uint32 b) { } +	void send(uint32) { }  	void noteOff(byte note);  	void noteOn(byte note, byte onVelo); -	void programChange(byte program) {} +	void programChange(byte) {}  	void pitchBend(int16 value);  	void controlChange(byte control, byte value); -	void pitchBendFactor(byte value) { } +	void pitchBendFactor(byte) { }  	void sysEx_customInstrument(uint32 unused, const byte *instr);  protected: @@ -427,7 +426,7 @@ void Towns_EuphonyPcmChannel::nextTick(int32 *outbuf, int buflen) {  		return;  	} -	float phaseStep = SoundTowns::semitoneAndSampleRate_to_sampleStep(_note, _voice->_snd[_current]->keyNote - +	float phaseStep = SoundTowns::calculatePhaseStep(_note, _voice->_snd[_current]->keyNote -  		_voice->_env[_current]->rootKeyOffset, _voice->_snd[_current]->samplingRate, _rate, _frequencyOffs);  	int32 looplength = _voice->_snd[_current]->loopLength; @@ -819,7 +818,8 @@ void Towns_EuphonyParser::parseNextEvent(EventInfo &info) {  		}  	} -	while (true) { +	bool loop = true; +	while (loop) {  		byte cmd = *pos;  		byte evt = (cmd & 0xF0); @@ -853,7 +853,7 @@ void Towns_EuphonyParser::parseNextEvent(EventInfo &info) {  				info.basic.param2 = onVelo;  				pos += 12; -				break; +				loop = false;  			} else {  				pos += 6;  			} @@ -870,7 +870,7 @@ void Towns_EuphonyParser::parseNextEvent(EventInfo &info) {  				info.basic.param1 = pos[4];  				info.basic.param2 = pos[5];  				pos += 6; -				break; +				loop = false;  			} else {  				pos += 6;  			} @@ -889,7 +889,7 @@ void Towns_EuphonyParser::parseNextEvent(EventInfo &info) {  			_tempo[2] = tempo & 0xff;  			info.ext.data = (byte*) _tempo;  			pos += 6; -			break; +			loop = false;  		} else if (cmd == 0xFD || cmd == 0xFE) {  			// End of track.  			if (_autoLoop) { @@ -906,12 +906,12 @@ void Towns_EuphonyParser::parseNextEvent(EventInfo &info) {  			info.event = 0xFF;  			info.ext.type = 0x2F;  			info.ext.data = pos; -			break; +			loop = false;  		} else {  			error("Unknown Euphony music event 0x%02X", (int)cmd);  			memset(&info, 0, sizeof(info));  			pos = 0; -			break; +			loop = false;  		}  	}  	_position._play_pos = pos; @@ -1085,7 +1085,7 @@ void Towns_EuphonyTrackQueue::initDriver() {  class TownsPC98_OpnOperator {  public: -	TownsPC98_OpnOperator(double rate, const uint8 *rateTable, +	TownsPC98_OpnOperator(const uint32 timerbase, const uint8 *rateTable,  		const uint8 *shiftTable, const uint8 *attackDecayTable, const uint32 *frqTable,  		const uint32 *sineTable, const int32 *tlevelOut, const int32 *detuneTable);  	~TownsPC98_OpnOperator() {} @@ -1095,7 +1095,7 @@ public:  	void frequency(int freq);  	void updatePhaseIncrement();  	void recalculateRates(); -	void generateOutput(int phasebuf, int *_feedbuf, int &out); +	void generateOutput(int32 phasebuf, int32 *feedbuf, int32 &out);  	void feedbackLevel(int32 level) {_feedbackLevel = level ? level + 6 : 0; }  	void detune(int value) { _detn = &_detnTbl[value << 5]; } @@ -1111,6 +1111,7 @@ public:  protected:  	EnvelopeState _state; +	bool _playing;  	uint32 _feedbackLevel;  	uint32 _multiple;  	uint32 _totalLevel; @@ -1137,8 +1138,8 @@ protected:  	const int32 *_tLvlTbl;  	const int32 *_detnTbl; -	const double _tickLength; -	double _tick; +	const uint32 _tickLength; +	uint32 _timer;  	int32 _currentLevel;  	struct EvpState { @@ -1147,23 +1148,31 @@ protected:  	} fs_a, fs_d, fs_s, fs_r;  }; -TownsPC98_OpnOperator::TownsPC98_OpnOperator(double rate, const uint8 *rateTable,  +TownsPC98_OpnOperator::TownsPC98_OpnOperator(const uint32 timerbase, const uint8 *rateTable,  	const uint8 *shiftTable, const uint8 *attackDecayTable,	const uint32 *frqTable,  	const uint32 *sineTable, const int32 *tlevelOut, const int32 *detuneTable) :  	_rateTbl(rateTable), _rshiftTbl(shiftTable), _adTbl(attackDecayTable), _fTbl(frqTable), -	_sinTbl(sineTable), _tLvlTbl(tlevelOut), _detnTbl(detuneTable), _tickLength(rate * 65536.0), +	_sinTbl(sineTable), _tLvlTbl(tlevelOut), _detnTbl(detuneTable), _tickLength(timerbase * 2),  	_specifiedAttackRate(0), _specifiedDecayRate(0), _specifiedReleaseRate(0), _specifiedSustainRate(0),  	_phase(0), _state(s_ready) { -	 +  	reset();  }  void TownsPC98_OpnOperator::keyOn() { +	if (_playing) +		return; + +	_playing = true;  	_state = s_attacking;  	_phase = 0;  }  void TownsPC98_OpnOperator::keyOff() { +	if (!_playing) +		return; +	 +	_playing = false;  	if (_state != s_ready)  		_state = s_releasing;  } @@ -1172,6 +1181,7 @@ void TownsPC98_OpnOperator::frequency(int freq) {  	uint8 block = (freq >> 11);  	uint16 pos = (freq & 0x7ff);  	uint8 c = pos >> 7; +  	_kcode = (block << 2) | ((c < 7) ? 0 : ((c > 8) ? 3 : c - 6 ));  	_frequency = _fTbl[pos << 1] >> (7 - block);  } @@ -1204,44 +1214,44 @@ void TownsPC98_OpnOperator::recalculateRates() {  	fs_r.shift = _rshiftTbl[r + k];  } -void TownsPC98_OpnOperator::generateOutput(int phasebuf, int *_feedbuf, int &out) { +void TownsPC98_OpnOperator::generateOutput(int32 phasebuf, int32 *feed, int32 &out) {  	if (_state == s_ready)  		return; -	_tick += _tickLength; -	while (_tick > 0x30000) { -		_tick -= 0x30000; +	_timer += _tickLength; +	while (_timer > 0x5B8D80) { +		_timer -= 0x5B8D80;  		++_tickCount;  		int32 levelIncrement = 0;  		uint32 targetTime = 0;  		int32 targetLevel = 0; -		EnvelopeState next_state = s_ready; +		EnvelopeState nextState = s_ready;  		switch (_state) {  			case s_ready:  				return;  			case s_attacking: -				next_state = s_decaying; +				nextState = s_decaying;  				targetTime = (1 << fs_a.shift) - 1;  				targetLevel = 0;  				levelIncrement = (~_currentLevel * _adTbl[fs_a.rate + ((_tickCount >> fs_a.shift) & 7)]) >> 4;  				break;  			case s_decaying:  				targetTime = (1 << fs_d.shift) - 1; -				next_state = s_sustaining; +				nextState = s_sustaining;  				targetLevel = _sustainLevel;  				levelIncrement = _adTbl[fs_d.rate + ((_tickCount >> fs_d.shift) & 7)];  				break;  			case s_sustaining:  				targetTime = (1 << fs_s.shift) - 1; -				next_state = s_ready; +				nextState = s_sustaining;  				targetLevel = 1023;  				levelIncrement = _adTbl[fs_s.rate + ((_tickCount >> fs_s.shift) & 7)];  				break;  			case s_releasing:  				targetTime = (1 << fs_r.shift) - 1; -				next_state = s_ready; +				nextState = s_ready;  				targetLevel = 1023;  				levelIncrement = _adTbl[fs_r.rate + ((_tickCount >> fs_r.shift) & 7)];  				break; @@ -1249,31 +1259,31 @@ void TownsPC98_OpnOperator::generateOutput(int phasebuf, int *_feedbuf, int &out  		if (!(_tickCount & targetTime)) {  			_currentLevel += levelIncrement; -			if ((!targetLevel && _currentLevel <= targetLevel) || (targetLevel && _currentLevel >= targetLevel)) { +			if ((_state == s_attacking && _currentLevel <= targetLevel) || (_state != s_attacking && _currentLevel >= targetLevel)) {  				if (_state != s_decaying)  					_currentLevel = targetLevel; -				if (_state != s_sustaining) -					_state = next_state; +				_state = nextState;  			}  		}  	}  	uint32 lvlout = _totalLevel + (uint32) _currentLevel; -	int outp = 0; -	int *i = &outp, *o = &outp; + +	int32 outp = 0; +	int32 *i = &outp, *o = &outp;  	int phaseShift = 0; -	if (_feedbuf) { -		o = &_feedbuf[0]; -		i = &_feedbuf[1]; -		phaseShift = _feedbackLevel ? ((_feedbuf[0] + _feedbuf[1]) << _feedbackLevel) : 0; +	if (feed) { +		o = &feed[0]; +		i = &feed[1]; +		phaseShift = _feedbackLevel ? ((*o + *i) << _feedbackLevel) : 0;  		if (phasebuf == -1)  			*i = 0;  		*o = *i;  	} else {  		phaseShift = phasebuf << 15; -	}		 +	}  	if (lvlout < 832) {  		uint32 index = (lvlout << 3) + _sinTbl[(((int32)((_phase & 0xffff0000) @@ -1285,15 +1295,11 @@ void TownsPC98_OpnOperator::generateOutput(int phasebuf, int *_feedbuf, int &out  	_phase += _phaseIncrement;  	out += *o; -	if (out > 32767) -		out = 32767; -	if (out < -32767) -		out = -32767;  }  void TownsPC98_OpnOperator::reset(){  	keyOff(); -	_tick = 0; +	_timer = 0;  	_keyScale2 = 0;  	_currentLevel = 1023; @@ -1306,7 +1312,7 @@ void TownsPC98_OpnOperator::reset(){  	decayRate(0);  	releaseRate(0);  	sustainRate(0); -	feedbackLevel(0);	 +	feedbackLevel(0);  	totalLevel(127);  } @@ -1332,40 +1338,42 @@ public:  	virtual ~TownsPC98_OpnChannel();  	virtual void init(); -	typedef bool (TownsPC98_OpnChannel::*ControlEventFunc)(uint8 para); -  	typedef enum channelState {  		CHS_RECALCFREQ		=	0x01,  		CHS_KEYOFF			=	0x02, -		CHS_SSG				=	0x04, +		CHS_SSGOFF			=	0x04,  		CHS_PITCHWHEELOFF	=	0x08,  		CHS_ALL_BUT_EOT		=	0x0f, +		CHS_PROTECT			=	0x40,  		CHS_EOT				=	0x80  	} ChannelState;  	virtual void loadData(uint8 *data);  	virtual void processEvents();  	virtual void processFrequency(); -	bool processControlEvent(uint8 cmd); -	void writeReg(uint8 regAdress, uint8 value); +	virtual bool processControlEvent(uint8 cmd); +	void writeReg(uint8 regAddress, uint8 value);  	virtual void keyOn(); -	virtual void keyOff();	 -	 +	void keyOff(); +  	void setOutputLevel(); -	void fadeStep(); -	void reset(); +	virtual void fadeStep(); +	virtual void reset();  	void updateEnv(); -	void generateOutput(int16 &leftSample, int16 &rightSample, int *del, int *feed); +	void generateOutput(int32 &leftSample, int32 &rightSample, int32 *del, int32 *feed);  	bool _enableLeft;  	bool _enableRight; -	bool _updateEnvelopes; +	bool _updateEnvelopeParameters;  	const uint8 _idFlag; -	int _feedbuf[3]; +	int32 _feedbuf[3];  protected: +	void setupPitchWheel(); +	bool processPitchWheel(); +  	bool control_dummy(uint8 para);  	bool control_f0_setPatch(uint8 para);  	bool control_f1_presetOutputLevel(uint8 para); @@ -1377,40 +1385,36 @@ protected:  	bool control_f7_setupPitchWheel(uint8 para);  	bool control_f8_togglePitchWheel(uint8 para);  	bool control_fa_writeReg(uint8 para); -	bool control_fb_incOutLevel(uint8 para); -	bool control_fc_decOutLevel(uint8 para); +	virtual bool control_fb_incOutLevel(uint8 para); +	virtual bool control_fc_decOutLevel(uint8 para);  	bool control_fd_jump(uint8 para); -	bool control_ff_endOfTrack(uint8 para); - -	bool control_f0_setPatchSSG(uint8 para); -	bool control_f1_setTotalLevel(uint8 para); -	bool control_f4_setAlgorithm(uint8 para); -	bool control_f9_unkSSG(uint8 para); -	bool control_fb_incOutLevelSSG(uint8 para); -	bool control_fc_decOutLevelSSG(uint8 para); -	bool control_ff_endOfTrackSSG(uint8 para); +	virtual bool control_ff_endOfTrack(uint8 para);  	uint8 _ticksLeft;  	uint8 _algorithm; -	uint8 _instrID; +	uint8 _instr;  	uint8 _totalLevel;  	uint8 _frqBlockMSB;  	int8 _frqLSB;  	uint8 _keyOffTime; -	bool _protect; +	bool _hold;  	uint8 *_dataPtr; -	uint8 _ptchWhlInitDelayLo;  	uint8 _ptchWhlInitDelayHi; +	uint8 _ptchWhlInitDelayLo;  	int16 _ptchWhlModInitVal;  	uint8 _ptchWhlDuration;  	uint8 _ptchWhlCurDelay;  	int16 _ptchWhlModCurVal;  	uint8 _ptchWhlDurLeft; -	uint16 frequency; +	uint16 _frequency; +	uint8 _block;  	uint8 _regOffset;  	uint8 _flags; -	uint8 _ssg1; -	uint8 _ssg2; +	uint8 _ssgTl; +	uint8 _ssgStep; +	uint8 _ssgTicksLeft; +	uint8 _ssgTargetLvl; +	uint8 _ssgStartLvl;  	const uint8 _chanNum;  	const uint8 _keyNum; @@ -1420,6 +1424,7 @@ protected:  	TownsPC98_OpnOperator **_opr;  	uint16 _frqTemp; +	typedef bool (TownsPC98_OpnChannel::*ControlEventFunc)(uint8 para);  	const ControlEventFunc *controlEvents;  }; @@ -1427,24 +1432,161 @@ class TownsPC98_OpnChannelSSG : public TownsPC98_OpnChannel {  public:  	TownsPC98_OpnChannelSSG(TownsPC98_OpnDriver *driver, uint8 regOffs,  		uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id); -	~TownsPC98_OpnChannelSSG() {} +	virtual ~TownsPC98_OpnChannelSSG() {}  	void init(); +	virtual void loadData(uint8 *data);  	void processEvents();  	void processFrequency(); +	bool processControlEvent(uint8 cmd);  	void keyOn(); -	void keyOff(); +	void nextShape(); + +	void protect(); +	void restore(); + +	void fadeStep(); + +protected: +	void setOutputLevel(uint8 lvl); + +	bool control_f0_setInstr(uint8 para); +	bool control_f1_setTotalLevel(uint8 para); +	bool control_f4_setAlgorithm(uint8 para); +	bool control_f9_loadCustomPatch(uint8 para); +	bool control_fb_incOutLevel(uint8 para); +	bool control_fc_decOutLevel(uint8 para); +	bool control_ff_endOfTrack(uint8 para); + +	typedef bool (TownsPC98_OpnChannelSSG::*ControlEventFunc)(uint8 para); +	const ControlEventFunc *controlEvents; +}; + +class TownsPC98_OpnSfxChannel : public TownsPC98_OpnChannelSSG { +public: +	TownsPC98_OpnSfxChannel(TownsPC98_OpnDriver *driver, uint8 regOffs, +		uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) : +		TownsPC98_OpnChannelSSG(driver, regOffs, flgs, num, key, prt, id) {} +	~TownsPC98_OpnSfxChannel() {} + +	void loadData(uint8 *data); +}; + +class TownsPC98_OpnChannelPCM : public TownsPC98_OpnChannel { +public: +	TownsPC98_OpnChannelPCM(TownsPC98_OpnDriver *driver, uint8 regOffs, +		uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id); +	~TownsPC98_OpnChannelPCM() {} +	void init(); +  	void loadData(uint8 *data); +	void processEvents(); +	bool processControlEvent(uint8 cmd); + +	void reset(); + +private: +	bool control_f1_pcmStart(uint8 para); +	bool control_ff_endOfTrack(uint8 para); + +	typedef bool (TownsPC98_OpnChannelPCM::*ControlEventFunc)(uint8 para); +	const ControlEventFunc *controlEvents; +}; + +class TownsPC98_OpnSquareSineSource { +public: +	TownsPC98_OpnSquareSineSource(const uint32 timerbase); +	~TownsPC98_OpnSquareSineSource(); + +	void init(const int *rsTable, const int *rseTable); +	void reset(); +	uint8 readReg(uint8 address); +	void writeReg(uint8 address, uint8 value, bool force = false); + +	void nextTick(int32 *buffer, uint32 bufferSize);  private: -	void opn_SSG_UNK(uint8 a); +	void updatesRegs(); + +	uint8 _reg[16]; +	uint8 _updateRequestBuf[32]; +	int _updateRequest; +	uint8 *_regIndex; +	int _rand; + +	int8 _evpTimer; +	uint32 _pReslt; +	uint8 _attack; + +	bool _evpUpdate, _cont; + +	int _evpUpdateCnt; +	uint8 _outN; +	int _nTick; + +	int32 *_tlTable; +	int32 *_tleTable; + +	const uint32 _tickLength; +	uint32 _timer; + +	struct Channel { +		int tick; +		uint8 smp; +		uint8 out; +	} _channels[3]; + +	bool _ready;  }; +class TownsPC98_OpnPercussionSource { +public: +	TownsPC98_OpnPercussionSource(const uint32 timerbase); +	~TownsPC98_OpnPercussionSource() {} + +	void init(const uint8 *pcmData = 0); +	void reset(); +	void writeReg(uint8 address, uint8 value); + +	void nextTick(int32 *buffer, uint32 bufferSize); + +private: +	struct PcmInstrument { +		const uint8 *data; + +		const uint8 *start; +		const uint8 *end; +		const uint8 *pos; +		uint32 size; +		bool active; +		uint8 level; + +		int8 decState; +		uint8 decStep; + +		int16 samples[2]; +		int out; +	}; + +	void recalcOuput(PcmInstrument *ins); +	void advanceInput(PcmInstrument *ins); + +	PcmInstrument _pcmInstr[6]; +	uint8 _regs[48]; + +	uint8 _totalLevel; + +	const uint32 _tickLength; +	uint32 _timer; +	bool _ready; +};  class TownsPC98_OpnDriver : public Audio::AudioStream {  friend class TownsPC98_OpnChannel;  friend class TownsPC98_OpnChannelSSG; +friend class TownsPC98_OpnSfxChannel; +friend class TownsPC98_OpnChannelPCM;  public:  	enum OpnType {  		OD_TOWNS, @@ -1456,17 +1598,21 @@ public:  	~TownsPC98_OpnDriver();  	bool init(); -	void loadData(uint8 *data, bool loadPaused = false); + +	void loadMusicData(uint8 *data, bool loadPaused = false); +	void loadSoundEffectData(uint8 *data, uint8 trackNum);  	void reset(); -	void fadeOut(); -	 -	void pause() { _playing = false; } -	void cont() { _playing = true; } +	void fadeStep(); + +	void pause() { _musicPlaying = false; } +	void cont() { _musicPlaying = true; } -	void callback(); -	void nextTick(int16 *buffer, uint32 bufferSize); +	void musicCallback(); +	void sfxCallback(); +	void nextTick(int32 *buffer, uint32 bufferSize);  	bool looping() { return _looping == _updateChannelsFlag ? true : false; } +	bool musicPlaying() { return _musicPlaying; }  	// AudioStream interface  	int inline readBuffer(int16 *buffer, const int numSamples); @@ -1479,9 +1625,16 @@ protected:  	TownsPC98_OpnChannel **_channels;  	TownsPC98_OpnChannelSSG **_ssgChannels; -	//TownsPC98_OpnChannel *_adpcmChannel; +	TownsPC98_OpnSfxChannel **_sfxChannels; +	TownsPC98_OpnChannelPCM *_pcmChannel; -	void setTempo(uint8 tempo); +	TownsPC98_OpnSquareSineSource *_ssg; +	TownsPC98_OpnPercussionSource *_pcm; + +	void startSoundEffect(); +	 +	void setMusicTempo(uint8 tempo); +	void setSfxTempo(uint16 tempo);  	void lock() { _mutex.lock(); }  	void unlock() { _mutex.unlock(); } @@ -1492,6 +1645,7 @@ protected:  	const uint8 *_opnCarrier;  	const uint8 *_opnFreqTable; +	const uint8 *_opnFreqTableSSG;  	const uint8 *_opnFxCmdLen;  	const uint8 *_opnLvlPresets; @@ -1503,34 +1657,54 @@ protected:  	int32 *_oprLevelOut;  	int32 *_oprDetune; -	uint8 *_trackData; +	uint8 *_musicBuffer; +	uint8 *_sfxBuffer; +	uint8 *_trackPtr;  	uint8 *_patches; +	uint8 *_ssgPatches; -	uint8 _cbCounter;  	uint8 _updateChannelsFlag; +	uint8 _updateSSGFlag; +	uint8 _updatePCMFlag; +	uint8 _updateSfxFlag;  	uint8 _finishedChannelsFlag; -	uint16 _tempo; -	bool _playing; -	bool _fading; +	uint8 _finishedSSGFlag; +	uint8 _finishedPCMFlag; +	uint8 _finishedSfxFlag; + +	bool _musicPlaying; +	bool _sfxPlaying; +	uint8 _fading;  	uint8 _looping; -	uint32 _tickCounter; +	uint32 _musicTickCounter; -	bool _updateEnvelopes; -	int _ssgFlag; +	bool _updateEnvelopeParameters; -	int32 _samplesTillCallback; -	int32 _samplesTillCallbackRemainder; -	int32 _samplesPerCallback; -	int32 _samplesPerCallbackRemainder; +	bool _regProtectionFlag; +	int _sfxOffs; +	uint8 *_sfxData; +	uint16 _sfxOffsets[2]; + +	int32 _samplesTillMusicCallback; +	uint32 _samplesTillMusicCallbackRemainder; +	int32 _samplesPerMusicCallback; +	uint32 _samplesPerMusicCallbackRemainder; +	int32 _samplesTillSfxCallback; +	uint32 _samplesTillSfxCallbackRemainder; +	int32 _samplesPerSfxCallback; +	uint32 _samplesPerSfxCallbackRemainder;  	const int _numChan;  	const int _numSSG; -	const bool _hasADPCM; -	const bool _hasStereo; +	const bool _hasPCM; -	double _baserate;  	static const uint8 _drvTables[];  	static const uint32 _adtStat[]; +	static const int _ssgTables[]; + +	const float _baserate; +	uint32 _timerbase; +  	bool _ready;  }; @@ -1538,14 +1712,15 @@ TownsPC98_OpnChannel::TownsPC98_OpnChannel(TownsPC98_OpnDriver *driver, uint8 re  	uint8 key, uint8 prt, uint8 id) : _drv(driver), _regOffset(regOffs), _flags(flgs), _chanNum(num), _keyNum(key),  	_part(prt), _idFlag(id) { -	_ticksLeft = _algorithm = _instrID = _totalLevel = _frqBlockMSB = _keyOffTime = _ssg1 = _ssg2 = 0; -	_ptchWhlInitDelayLo = _ptchWhlInitDelayHi = _ptchWhlDuration = _ptchWhlCurDelay = _ptchWhlDurLeft = 0; +	_ticksLeft = _algorithm = _instr = _totalLevel = _frqBlockMSB = _keyOffTime = 0; +	_ssgStartLvl = _ssgTl = _ssgStep = _ssgTicksLeft = _ssgTargetLvl = _block = 0; +	_ptchWhlInitDelayHi = _ptchWhlInitDelayLo = _ptchWhlDuration = _ptchWhlCurDelay = _ptchWhlDurLeft = 0;  	_frqLSB = 0; -	_protect = _updateEnvelopes = false; +	_hold = _updateEnvelopeParameters = false;  	_enableLeft = _enableRight = true; -	_dataPtr = 0;		 +	_dataPtr = 0;  	_ptchWhlModInitVal = _ptchWhlModCurVal = 0; -	frequency = _frqTemp = 0; +	_frequency = _frqTemp = 0;  	memset(&_feedbuf, 0, sizeof(int) * 3);  	_opr = 0;  } @@ -1559,10 +1734,9 @@ TownsPC98_OpnChannel::~TownsPC98_OpnChannel() {  }  void TownsPC98_OpnChannel::init() { -	  	_opr = new TownsPC98_OpnOperator*[4];  	for (int i = 0; i < 4; i++) -		_opr[i] = new TownsPC98_OpnOperator(_drv->_baserate, _drv->_oprRates, _drv->_oprRateshift, +		_opr[i] = new TownsPC98_OpnOperator(_drv->_timerbase, _drv->_oprRates, _drv->_oprRateshift,  			_drv->_oprAttackDecay, _drv->_oprFrq, _drv->_oprSinTbl, _drv->_oprLevelOut, _drv->_oprDetune);  	#define Control(x)	&TownsPC98_OpnChannel::control_##x @@ -1592,16 +1766,16 @@ void TownsPC98_OpnChannel::init() {  void TownsPC98_OpnChannel::keyOff() {  	// all operators off  	uint8 value = _keyNum & 0x0f; -	uint8 regAdress = 0x28; -	writeReg(regAdress, value); +	uint8 regAddress = 0x28; +	writeReg(regAddress, value);  	_flags |= CHS_KEYOFF;  }  void TownsPC98_OpnChannel::keyOn() {  	// all operators on  	uint8 value = _keyNum | 0xf0; -	uint8 regAdress = 0x28; -	writeReg(regAdress, value); +	uint8 regAddress = 0x28; +	writeReg(regAddress, value);  }  void TownsPC98_OpnChannel::loadData(uint8 *data) { @@ -1645,13 +1819,13 @@ void TownsPC98_OpnChannel::processEvents() {  	if (_flags & CHS_EOT)  		return; -	if (_protect == false && _ticksLeft == _keyOffTime) +	if (!_hold && _ticksLeft == _keyOffTime)  		keyOff();  	if (--_ticksLeft)  		return; -	if (_protect == false) +	if (!_hold)  		keyOff();  	uint8 cmd = 0; @@ -1669,14 +1843,14 @@ void TownsPC98_OpnChannel::processEvents() {  	if (cmd == 0x80) {  		keyOff(); -		_protect = false; +		_hold = false;  	} else {  		keyOn(); -		if (_protect == false || cmd != _frqBlockMSB) +		if (_hold == false || cmd != _frqBlockMSB)  			_flags |= CHS_RECALCFREQ; -	 -		_protect = (para & 0x80) ? true : false; + +		_hold = (para & 0x80) ? true : false;  		_frqBlockMSB = cmd;  	} @@ -1687,35 +1861,46 @@ void TownsPC98_OpnChannel::processFrequency() {  	if (_flags & CHS_RECALCFREQ) {  		uint8 block = (_frqBlockMSB & 0x70) >> 1;  		uint16 bfreq = ((const uint16*)_drv->_opnFreqTable)[_frqBlockMSB & 0x0f]; -		frequency = (bfreq + _frqLSB) | (block << 8); -		writeReg(_regOffset + 0xa4, (frequency >> 8)); -		writeReg(_regOffset + 0xa0, (frequency & 0xff)); +		_frequency = (bfreq + _frqLSB) | (block << 8); +		writeReg(_regOffset + 0xa4, (_frequency >> 8)); +		writeReg(_regOffset + 0xa0, (_frequency & 0xff)); -		_ptchWhlCurDelay = _ptchWhlInitDelayHi; -		if (_flags & CHS_KEYOFF) { -			_ptchWhlModCurVal = _ptchWhlModInitVal; -			_ptchWhlCurDelay += _ptchWhlInitDelayLo; -		} - -		_ptchWhlDurLeft = (_ptchWhlDuration >> 1); -		_flags &= ~(CHS_KEYOFF | CHS_RECALCFREQ); +		setupPitchWheel();  	}  	if (!(_flags & CHS_PITCHWHEELOFF)) { -		if (--_ptchWhlCurDelay) +		if (!processPitchWheel())  			return; -		_ptchWhlCurDelay = _ptchWhlInitDelayHi; -		frequency += _ptchWhlModCurVal; -		writeReg(_regOffset + 0xa4, (frequency >> 8)); -		writeReg(_regOffset + 0xa0, (frequency & 0xff)); +		writeReg(_regOffset + 0xa4, (_frequency >> 8)); +		writeReg(_regOffset + 0xa0, (_frequency & 0xff)); +	} +} -		if(!--_ptchWhlDurLeft) { -			_ptchWhlDurLeft = _ptchWhlDuration; -			_ptchWhlModCurVal = -_ptchWhlModCurVal; -		} +void TownsPC98_OpnChannel::setupPitchWheel() { +	_ptchWhlCurDelay = _ptchWhlInitDelayHi; +	if (_flags & CHS_KEYOFF) { +		_ptchWhlModCurVal = _ptchWhlModInitVal; +		_ptchWhlCurDelay += _ptchWhlInitDelayLo;  	} +	_ptchWhlDurLeft = (_ptchWhlDuration >> 1); +	_flags &= ~(CHS_KEYOFF | CHS_RECALCFREQ); +} + +bool TownsPC98_OpnChannel::processPitchWheel() { +	if (--_ptchWhlCurDelay) +		return false; + +	_ptchWhlCurDelay = _ptchWhlInitDelayHi; +	_frequency += _ptchWhlModCurVal; + +	if(!--_ptchWhlDurLeft) { +		_ptchWhlDurLeft = _ptchWhlDuration; +		_ptchWhlModCurVal = -_ptchWhlModCurVal; +	} + +	return true;  }  bool TownsPC98_OpnChannel::processControlEvent(uint8 cmd) { @@ -1743,10 +1928,24 @@ void TownsPC98_OpnChannel::fadeStep() {  }  void TownsPC98_OpnChannel::reset() { -	for (int i = 0; i < 4; i++) -		_opr[i]->reset(); +	if (_opr) { +		for (int i = 0; i < 4; i++) +			_opr[i]->reset(); +	} -	_updateEnvelopes = false; +	_block = 0; +	_frequency = 0; +	_hold = false; +	_frqTemp = 0; +	_ssgTl = 0; +	_ssgStartLvl = 0; +	_ssgTargetLvl = 0; +	_ssgStep = 0; +	_ssgTicksLeft = 0; +	_totalLevel = 0; +	_flags |= CHS_EOT; + +	_updateEnvelopeParameters = false;  	_enableLeft = _enableRight = true;  	memset(&_feedbuf, 0, sizeof(int) * 3);  } @@ -1756,10 +1955,10 @@ void TownsPC98_OpnChannel::updateEnv() {  		_opr[i]->updatePhaseIncrement();  } -void TownsPC98_OpnChannel::generateOutput(int16 &leftSample, int16 &rightSample, int *del, int *feed) { -	int phbuf1, phbuf2, output; +void TownsPC98_OpnChannel::generateOutput(int32 &leftSample, int32 &rightSample, int32 *del, int32 *feed) { +	int32 phbuf1, phbuf2, output;  	phbuf1 = phbuf2 = output = 0; -	 +  	switch (_algorithm) {  		case 0:  			_opr[0]->generateOutput(0, feed, phbuf1); @@ -1771,7 +1970,7 @@ void TownsPC98_OpnChannel::generateOutput(int16 &leftSample, int16 &rightSample,  		case 1:  			_opr[0]->generateOutput(0, feed, phbuf1);  			_opr[2]->generateOutput(*del, 0, phbuf2); -			_opr[1]->generateOutput(0, 0, phbuf1);					 +			_opr[1]->generateOutput(0, 0, phbuf1);  			_opr[3]->generateOutput(phbuf2, 0, output);  			*del = phbuf1;  			break; @@ -1792,7 +1991,7 @@ void TownsPC98_OpnChannel::generateOutput(int16 &leftSample, int16 &rightSample,  		case 4:  			_opr[0]->generateOutput(0, feed, phbuf1);  			_opr[2]->generateOutput(0, 0, phbuf2); -			_opr[1]->generateOutput(phbuf1, 0, output);					 +			_opr[1]->generateOutput(phbuf1, 0, output);  			_opr[3]->generateOutput(phbuf2, 0, output);  			*del = 0;  			break; @@ -1819,39 +2018,34 @@ void TownsPC98_OpnChannel::generateOutput(int16 &leftSample, int16 &rightSample,  			break;  		}; -	if (_enableLeft) { -		int l = output + leftSample; -		if (l > 32767) -			l = 32767; -		if (l < -32767) -			l = -32767; -		leftSample = (int16) l; -	} +	int32 finOut = ((output * 7) / 2); -	if (_enableRight) { -		int r = output + rightSample; -		if (r > 32767) -			r = 32767; -		if (r < -32767) -			r = -32767; -		rightSample = (int16) r; -	} +	if (_enableLeft) +		leftSample += finOut; + +	if (_enableRight) +		rightSample += finOut;  } -void TownsPC98_OpnChannel::writeReg(uint8 regAdress, uint8 value) { -	uint8 h = regAdress & 0xf0; -	uint8 l = (regAdress & 0x0f); +void TownsPC98_OpnChannel::writeReg(uint8 regAddress, uint8 value) { +	if (_drv->_regProtectionFlag) +		return; + +	uint8 h = regAddress & 0xf0; +	uint8 l = (regAddress & 0x0f);  	static const uint8 oprOrdr[] = { 0, 2, 1, 3 };  	uint8 o = oprOrdr[(l - _regOffset) >> 2]; -	 +  	switch (h) {  		case 0x00:  			// ssg -			warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress); +			if (_drv->_ssg) +				_drv->_ssg->writeReg(regAddress, value);  			break;  		case 0x10: -			// adpcm -			warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress); +			// pcm rhythm channel +			if (_drv->_pcm) +				_drv->_pcm->writeReg(regAddress - 0x10, value);  			break;  		case 0x20:  			if (l == 8) { @@ -1884,7 +2078,7 @@ void TownsPC98_OpnChannel::writeReg(uint8 regAdress, uint8 value) {  			// detune, multiple  			_opr[o]->detune((value >> 4) & 7);  			_opr[o]->multiple(value & 0x0f); -			_updateEnvelopes = true; +			_updateEnvelopeParameters = true;  			break;  		case 0x40: @@ -1896,7 +2090,7 @@ void TownsPC98_OpnChannel::writeReg(uint8 regAdress, uint8 value) {  			// rate scaling, attack rate  			_opr[o]->attackRate(value & 0x1f);  			if (_opr[o]->scaleRate(value >> 6)) -				_updateEnvelopes = true; +				_updateEnvelopeParameters = true;  			break;  		case 0x60: @@ -1904,7 +2098,6 @@ void TownsPC98_OpnChannel::writeReg(uint8 regAdress, uint8 value) {  			_opr[o]->decayRate(value & 0x1f);  			if (value & 0x80)  				warning("TownsPC98_OpnDriver: TRYING TO USE AMP MODULATION (NOT SUPPORTED)"); -  			break;  		case 0x70: @@ -1919,8 +2112,7 @@ void TownsPC98_OpnChannel::writeReg(uint8 regAdress, uint8 value) {  			break;  		case 0x90: -			// ssg -			warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress); +			warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAddress);  			break;  		case 0xa0: @@ -1928,7 +2120,7 @@ void TownsPC98_OpnChannel::writeReg(uint8 regAdress, uint8 value) {  			l -= _regOffset;  			if (l == 0) {  				_frqTemp = (_frqTemp & 0xff00) | value; -				_updateEnvelopes = true; +				_updateEnvelopeParameters = true;  				for (int i = 0; i < 4; i++)  					_opr[i]->frequency(_frqTemp);  			} else if (l == 4) { @@ -1964,13 +2156,13 @@ void TownsPC98_OpnChannel::writeReg(uint8 regAdress, uint8 value) {  			break;  		default: -			warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress); +			warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAddress);  			break;  	}  }  bool TownsPC98_OpnChannel::control_f0_setPatch(uint8 para) { -	_instrID = para; +	_instr = para;  	uint8 reg = _regOffset + 0x80;  	for (int i = 0; i < 4; i++) { @@ -1979,7 +2171,7 @@ bool TownsPC98_OpnChannel::control_f0_setPatch(uint8 para) {  		reg += 4;  	} -	const uint8 *tptr = _drv->_patches + ((uint32)_instrID << 5); +	const uint8 *tptr = _drv->_patches + ((uint32)_instr << 5);  	reg = _regOffset + 0x30;  	// write registers 0x30 to 0x8f @@ -2033,7 +2225,7 @@ bool TownsPC98_OpnChannel::control_f4_setOutputLevel(uint8 para) {  }  bool TownsPC98_OpnChannel::control_f5_setTempo(uint8 para) { -	_drv->setTempo(para); +	_drv->setMusicTempo(para);  	return true;  } @@ -2043,7 +2235,7 @@ bool TownsPC98_OpnChannel::control_f6_repeatSection(uint8 para) {  	if (*_dataPtr) {  		// repeat section until counter has reached zero -		_dataPtr = _drv->_trackData + READ_LE_UINT16(_dataPtr + 2); +		_dataPtr = _drv->_trackPtr + READ_LE_UINT16(_dataPtr + 2);  	} else {  		// reset counter, advance to next section  		_dataPtr[0] = _dataPtr[1]; @@ -2053,8 +2245,8 @@ bool TownsPC98_OpnChannel::control_f6_repeatSection(uint8 para) {  }  bool TownsPC98_OpnChannel::control_f7_setupPitchWheel(uint8 para) { -	_ptchWhlInitDelayLo = _dataPtr[0]; -	_ptchWhlInitDelayHi = para; +	_ptchWhlInitDelayHi = _dataPtr[0]; +	_ptchWhlInitDelayLo = para;  	_ptchWhlModInitVal = (int16) READ_LE_UINT16(_dataPtr + 1);  	_ptchWhlDuration = _dataPtr[3];  	_dataPtr += 4; @@ -2070,11 +2262,12 @@ bool TownsPC98_OpnChannel::control_f8_togglePitchWheel(uint8 para) {  			_flags |= CHS_PITCHWHEELOFF;  		}  	} else { -		//uint8 skipChannels = para / 36; -		//uint8 entry = para % 36; -		//TownsPC98_OpnDriver::TownsPC98_OpnChannel *t = &chan[skipChannels]; -		////// NOT IMPLEMENTED -		//t->unnamedEntries[entry] = *_dataPtr++; +		/* NOT IMPLEMENTED +		uint8 skipChannels = para / 36; +		uint8 entry = para % 36; +		TownsPC98_OpnDriver::TownsPC98_OpnChannel *t = &chan[skipChannels]; +		 +		t->unnamedEntries[entry] = *_dataPtr++;*/  	}  	return true;  } @@ -2113,7 +2306,7 @@ bool TownsPC98_OpnChannel::control_fc_decOutLevel(uint8 para) {  }  bool TownsPC98_OpnChannel::control_fd_jump(uint8 para) { -	uint8 *tmp = _drv->_trackData + READ_LE_UINT16(_dataPtr - 1); +	uint8 *tmp = _drv->_trackPtr + READ_LE_UINT16(_dataPtr - 1);  	_dataPtr = (tmp[1] == 1) ? tmp : ++_dataPtr;  	return true;  } @@ -2127,7 +2320,7 @@ bool TownsPC98_OpnChannel::control_ff_endOfTrack(uint8 para) {  	uint16 val = READ_LE_UINT16(--_dataPtr);  	if (val) {  		// loop -		_dataPtr = _drv->_trackData + val; +		_dataPtr = _drv->_trackPtr + val;  		return true;  	} else {  		// quit parsing for active channel @@ -2139,31 +2332,248 @@ bool TownsPC98_OpnChannel::control_ff_endOfTrack(uint8 para) {  	}  } -bool TownsPC98_OpnChannel::control_f0_setPatchSSG(uint8 para) { -	_instrID = para << 4; +TownsPC98_OpnChannelSSG::TownsPC98_OpnChannelSSG(TownsPC98_OpnDriver *driver, uint8 regOffs, +		uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) : +		TownsPC98_OpnChannel(driver, regOffs, flgs, num, key, prt, id) { +} + +void TownsPC98_OpnChannelSSG::init() { +	_algorithm = 0x80; + +	#define Control(x)	&TownsPC98_OpnChannelSSG::control_##x +	static const ControlEventFunc ctrlEventsSSG[] = { +		Control(f0_setInstr), +		Control(f1_setTotalLevel), +		Control(f2_setKeyOffTime), +		Control(f3_setFreqLSB), +		Control(f4_setAlgorithm), +		Control(f5_setTempo), +		Control(f6_repeatSection), +		Control(f7_setupPitchWheel), +		Control(f8_togglePitchWheel), +		Control(f9_loadCustomPatch), +		Control(fa_writeReg), +		Control(fb_incOutLevel), +		Control(fc_decOutLevel), +		Control(fd_jump), +		Control(dummy), +		Control(ff_endOfTrack) +	}; +	#undef Control + +	controlEvents = ctrlEventsSSG; +} + +void TownsPC98_OpnChannelSSG::processEvents() { +	if (_flags & CHS_EOT) +		return; + +	_drv->_regProtectionFlag = (_flags & CHS_PROTECT) ? true : false; + +	if (!_hold && _ticksLeft == _keyOffTime) +		nextShape(); + +	if (!--_ticksLeft) { + +		uint8 cmd = 0; +		bool loop = true; + +		while (loop) { +			cmd = *_dataPtr++; +			if (cmd < 0xf0) +				loop = false; +			else if (!processControlEvent(cmd)) +				return; +		} + +		uint8 para = *_dataPtr++; + +		if (cmd == 0x80) { +			nextShape(); +			_hold = false; +		} else { +			if (!_hold) { +				_instr &= 0xf0; +				_ssgStep = _drv->_ssgPatches[_instr]; +				_ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f; +				_ssgTargetLvl = _drv->_ssgPatches[_instr + 2]; +				_ssgStartLvl = _drv->_ssgPatches[_instr + 3]; +				_flags = (_flags & ~CHS_SSGOFF) | CHS_KEYOFF; +			} + +			keyOn(); + +			if (_hold == false || cmd != _frqBlockMSB) +				_flags |= CHS_RECALCFREQ; + +			_hold = (para & 0x80) ? true : false; +			_frqBlockMSB = cmd; +		} + +		_ticksLeft = para & 0x7f; +	} + +	if (!(_flags & CHS_SSGOFF)) { +		if (--_ssgTicksLeft) { +			if (!_drv->_fading) +				setOutputLevel(_ssgStartLvl); +			return; +		} + +		_ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f; + +		if (_drv->_ssgPatches[_instr + 1] & 0x80) { +			uint8 t = _ssgStartLvl - _ssgStep; + +			if (_ssgStep <= _ssgStartLvl && _ssgTargetLvl < t) { +				if (!_drv->_fading) +					setOutputLevel(t); +				return; +			} +		} else { +			int t = _ssgStartLvl + _ssgStep; +			uint8 p = (uint8) (t & 0xff); + +			if (t < 256 && _ssgTargetLvl > p) { +				if (!_drv->_fading) +					setOutputLevel(p); +				return; +			} +		} + +		setOutputLevel(_ssgTargetLvl); +		if (_ssgStartLvl && !(_instr & 8)){ +			_instr += 4; +			_ssgStep = _drv->_ssgPatches[_instr]; +			_ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f; +			_ssgTargetLvl = _drv->_ssgPatches[_instr + 2]; +		} else { +			_flags |= CHS_SSGOFF; +			setOutputLevel(0); +		} +	} +} + +void TownsPC98_OpnChannelSSG::processFrequency() { +	if (_algorithm & 0x40) +		return; + +	if (_flags & CHS_RECALCFREQ) { +		_block = _frqBlockMSB >> 4; +		_frequency = ((const uint16*)_drv->_opnFreqTableSSG)[_frqBlockMSB & 0x0f] + _frqLSB; + +		uint16 f = _frequency >> _block; +		writeReg(_regOffset << 1, f & 0xff); +		writeReg((_regOffset << 1) + 1, f >> 8); + +		setupPitchWheel(); +	} + +	if (!(_flags & (CHS_EOT | CHS_PITCHWHEELOFF | CHS_SSGOFF))) { +		if (!processPitchWheel()) +			return; + +		uint16 f = _frequency >> _block; +		writeReg(_regOffset << 1, f & 0xff); +		writeReg((_regOffset << 1) + 1, f >> 8); +	} +} + +bool TownsPC98_OpnChannelSSG::processControlEvent(uint8 cmd) { +	uint8 para = *_dataPtr++; +	return (this->*controlEvents[cmd & 0x0f])(para); +} + +void TownsPC98_OpnChannelSSG::nextShape() { +	_instr = (_instr & 0xf0) + 0x0c; +	_ssgStep = _drv->_ssgPatches[_instr]; +	_ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f; +	_ssgTargetLvl = _drv->_ssgPatches[_instr + 2]; +} + +void TownsPC98_OpnChannelSSG::keyOn() { +	uint8 c = 0x7b; +	uint8 t = (_algorithm & 0xC0) << 1; +	if (_algorithm & 0x80) +		t |= 4; + +	c = (c << (_regOffset + 1)) | (c >> (7 - _regOffset)); +	t = (t << (_regOffset + 1)) | (t >> (7 - _regOffset)); + +	if (!(_algorithm & 0x80)) +		writeReg(6, _algorithm & 0x7f); + +	uint8 e = (_drv->_ssg->readReg(7) & c) | t; +	writeReg(7, e); +} + +void TownsPC98_OpnChannelSSG::protect() { +	_flags |= CHS_PROTECT; +} + +void TownsPC98_OpnChannelSSG::restore() { +	_flags &= ~CHS_PROTECT; +	keyOn(); +	writeReg(8 + _regOffset, _ssgTl); +	uint16 f = _frequency >> _block; +	writeReg(_regOffset << 1, f & 0xff); +	writeReg((_regOffset << 1) + 1, f >> 8); +} + +void TownsPC98_OpnChannelSSG::loadData(uint8 *data) { +	_drv->_regProtectionFlag = (_flags & CHS_PROTECT) ? true : false; +	TownsPC98_OpnChannel::loadData(data); +	setOutputLevel(0); +	_algorithm = 0x80; +} + +void TownsPC98_OpnChannelSSG::setOutputLevel(uint8 lvl) { +	_ssgStartLvl = lvl; +	uint16 newTl = (((uint16)_totalLevel + 1) * (uint16)lvl) >> 8; +	if (newTl == _ssgTl) +		return; +	_ssgTl = newTl; +	writeReg(8 + _regOffset, _ssgTl); +} + +void TownsPC98_OpnChannelSSG::fadeStep() { +	_totalLevel--; +	if ((int8)_totalLevel < 0) +		_totalLevel = 0; +	setOutputLevel(_ssgStartLvl); +} + +bool TownsPC98_OpnChannelSSG::control_f0_setInstr(uint8 para) { +	_instr = para << 4;  	para = (para >> 3) & 0x1e;  	if (para)  		return control_f4_setAlgorithm(para | 0x40);  	return true;  } -bool TownsPC98_OpnChannel::control_f1_setTotalLevel(uint8 para) { +bool TownsPC98_OpnChannelSSG::control_f1_setTotalLevel(uint8 para) {  	if (!_drv->_fading)  		_totalLevel = para;  	return true;  } -bool TownsPC98_OpnChannel::control_f4_setAlgorithm(uint8 para) { +bool TownsPC98_OpnChannelSSG::control_f4_setAlgorithm(uint8 para) {  	_algorithm = para;  	return true;  } -bool TownsPC98_OpnChannel::control_f9_unkSSG(uint8 para) { -	_dataPtr += 5; +bool TownsPC98_OpnChannelSSG::control_f9_loadCustomPatch(uint8 para) { +	_instr = (_drv->_sfxOffs + 10 + _regOffset) << 4; +	_drv->_ssgPatches[_instr] = *_dataPtr++; +	_drv->_ssgPatches[_instr + 3] = para; +	_drv->_ssgPatches[_instr + 4] = *_dataPtr++; +	_drv->_ssgPatches[_instr + 6] = *_dataPtr++; +	_drv->_ssgPatches[_instr + 8] = *_dataPtr++; +	_drv->_ssgPatches[_instr + 12] = *_dataPtr++;  	return true;  } -bool TownsPC98_OpnChannel::control_fb_incOutLevelSSG(uint8 para) { +bool TownsPC98_OpnChannelSSG::control_fb_incOutLevel(uint8 para) {  	_dataPtr--;  	if (_drv->_fading)  		return true; @@ -2175,7 +2585,7 @@ bool TownsPC98_OpnChannel::control_fb_incOutLevelSSG(uint8 para) {  	return true;  } -bool TownsPC98_OpnChannel::control_fc_decOutLevelSSG(uint8 para) { +bool TownsPC98_OpnChannelSSG::control_fc_decOutLevel(uint8 para) {  	_dataPtr--;  	if (_drv->_fading)  		return true; @@ -2186,184 +2596,481 @@ bool TownsPC98_OpnChannel::control_fc_decOutLevelSSG(uint8 para) {  	return true;  } -bool TownsPC98_OpnChannel::control_ff_endOfTrackSSG(uint8 para) { -	uint16 val = READ_LE_UINT16(--_dataPtr); -	if (val) { -		// loop -		_dataPtr = _drv->_trackData + val; -		return true; +bool TownsPC98_OpnChannelSSG::control_ff_endOfTrack(uint8 para) { +	if (!_drv->_sfxOffs) { +		uint16 val = READ_LE_UINT16(--_dataPtr); +		if (val) { +			// loop +			_dataPtr = _drv->_trackPtr + val; +			return true; +		} else { +			// stop parsing +			if (!_drv->_fading) +				setOutputLevel(0); +			--_dataPtr; +			_flags |= CHS_EOT; +			_drv->_finishedSSGFlag |= _idFlag; +		}  	} else { -		// quit parsing for active channel -		--_dataPtr; +		// end of sfx track - restore ssg music channel  		_flags |= CHS_EOT; -		//_finishedChannelsFlag |= _idFlag; -		keyOff(); -		return false; +		_drv->_finishedSfxFlag |= _idFlag; +		_drv->_ssgChannels[_chanNum]->restore();  	} + +	return false;  } -TownsPC98_OpnChannelSSG::TownsPC98_OpnChannelSSG(TownsPC98_OpnDriver *driver, uint8 regOffs,  +void TownsPC98_OpnSfxChannel::loadData(uint8 *data) { +	_flags = CHS_ALL_BUT_EOT; +	_ticksLeft = 1; +	_dataPtr = data; +	_ssgTl = 0xff; +	_algorithm = 0x80; +} + +TownsPC98_OpnChannelPCM::TownsPC98_OpnChannelPCM(TownsPC98_OpnDriver *driver, uint8 regOffs,  		uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) :  		TownsPC98_OpnChannel(driver, regOffs, flgs, num, key, prt, id) {  } -void TownsPC98_OpnChannelSSG::init() { +void TownsPC98_OpnChannelPCM::init() {  	_algorithm = 0x80; -	 -	_opr = new TownsPC98_OpnOperator*[4]; -	for (int i = 0; i < 4; i++) -		_opr[i] = new TownsPC98_OpnOperator(_drv->_baserate, _drv->_oprRates, _drv->_oprRateshift, -			_drv->_oprAttackDecay, _drv->_oprFrq, _drv->_oprSinTbl, _drv->_oprLevelOut, _drv->_oprDetune); -	#define Control(x)	&TownsPC98_OpnChannelSSG::control_##x -	static const ControlEventFunc ctrlEventsSSG[] = { -		Control(f0_setPatchSSG), -		Control(f1_setTotalLevel), -		Control(f2_setKeyOffTime), -		Control(f3_setFreqLSB), -		Control(f4_setOutputLevel), -		Control(f5_setTempo), +	#define Control(x)	&TownsPC98_OpnChannelPCM::control_##x +	static const ControlEventFunc ctrlEventsPCM[] = { +		Control(dummy), +		Control(f1_pcmStart), +		Control(dummy), +		Control(dummy), +		Control(dummy), +		Control(dummy),  		Control(f6_repeatSection), -		Control(f7_setupPitchWheel), -		Control(f8_togglePitchWheel), -		Control(f9_unkSSG), +		Control(dummy), +		Control(dummy), +		Control(dummy),  		Control(fa_writeReg), -		Control(fb_incOutLevelSSG), -		Control(fc_decOutLevelSSG), -		Control(fd_jump),  		Control(dummy), -		Control(ff_endOfTrackSSG) +		Control(dummy), +		Control(dummy), +		Control(dummy), +		Control(ff_endOfTrack)  	};  	#undef Control -	controlEvents = ctrlEventsSSG; +	controlEvents = ctrlEventsPCM;  } -void TownsPC98_OpnChannelSSG::processEvents() { +void TownsPC98_OpnChannelPCM::loadData(uint8 *data) { +	_flags = (_flags & ~CHS_EOT) | CHS_ALL_BUT_EOT; +	_ticksLeft = 1; +	_dataPtr = data; +	_totalLevel = 0x7F; +} + +void TownsPC98_OpnChannelPCM::processEvents()  {  	if (_flags & CHS_EOT)  		return; -	_drv->_ssgFlag = (_flags & CHS_SSG) ? -1 : 0; - -	if (_protect == false && _ticksLeft == _keyOffTime) -		keyOff(); -  	if (--_ticksLeft)  		return; -	if (_protect == false) -		keyOff(); -  	uint8 cmd = 0;  	bool loop = true;  	while (loop) {  		cmd = *_dataPtr++; -		if (cmd < 0xf0) +		if (cmd == 0x80) {  			loop = false; -		else if (!processControlEvent(cmd)) +		} else if (cmd < 0xf0) { +			writeReg(0x10, cmd); +		} else if (!processControlEvent(cmd)) {  			return; +		}  	} +	_ticksLeft = *_dataPtr++; +} + +bool TownsPC98_OpnChannelPCM::processControlEvent(uint8 cmd) {  	uint8 para = *_dataPtr++; +	return (this->*controlEvents[cmd & 0x0f])(para); +} -	if (cmd == 0x80) { -		keyOff(); -		_protect = false; +void TownsPC98_OpnChannelPCM::reset() { +	TownsPC98_OpnChannel::reset(); + +	if (_drv->_pcm) +		_drv->_pcm->reset(); +} + +bool TownsPC98_OpnChannelPCM::control_f1_pcmStart(uint8 para) { +	_totalLevel = para; +	writeReg(0x11, para); +	return true; +} + +bool TownsPC98_OpnChannelPCM::control_ff_endOfTrack(uint8 para) { +	uint16 val = READ_LE_UINT16(--_dataPtr); +	if (val) { +		// loop +		_dataPtr = _drv->_trackPtr + val; +		return true;  	} else { -		keyOn(); +		// quit parsing for active channel +		--_dataPtr; +		_flags |= CHS_EOT; +		_drv->_finishedPCMFlag |= _idFlag; +		return false; +	} +} -		if (_protect == false || cmd != _frqBlockMSB) -			_flags |= CHS_RECALCFREQ; -	 -		_protect = (para & 0x80) ? true : false; -		_frqBlockMSB = cmd; +TownsPC98_OpnSquareSineSource::TownsPC98_OpnSquareSineSource(const uint32 timerbase) : 	_tlTable(0), +	_tleTable(0), _regIndex(_reg), _updateRequest(-1), _tickLength(timerbase * 27), _ready(0) { +	memset(_reg, 0, 16); +	memset(_channels, 0, sizeof(Channel) * 3); +	reset(); +} + +TownsPC98_OpnSquareSineSource::~TownsPC98_OpnSquareSineSource() { +	delete [] _tlTable; +	delete [] _tleTable; +} + +void TownsPC98_OpnSquareSineSource::init(const int *rsTable, const int *rseTable) { +	if (_ready) { +		reset(); +		return;  	} -	_ticksLeft = para & 0x7f; +	delete [] _tlTable; +	delete [] _tleTable; +	_tlTable = new int32[16]; +	_tleTable = new int32[32]; +	float a, b, d; +	d = 801.0f; + +	for (int i = 0; i < 16; i++) { +		b = 1.0f / rsTable[i]; +		a = 1.0f / d + b + 1.0f / 1000.0f; +		float v = (b / a) * 32767.0f; +		_tlTable[i] = (int32) v; + +		b = 1.0f / rseTable[i]; +		a = 1.0f / d + b + 1.0f / 1000.0f; +		v = (b / a) * 32767.0f; +		_tleTable[i] = (int32) v; +	} -	if (!(_flags & CHS_SSG)) { +	for (int i = 16; i < 32; i++) { +		b = 1.0f / rseTable[i]; +		a = 1.0f / d + b + 1.0f / 1000.0f; +		float v = (b / a) * 32767.0f; +		_tleTable[i] = (int32) v; +	} +	_ready = true; +} + +void TownsPC98_OpnSquareSineSource::reset() { +	_rand = 1; +	_outN = 1; +	_updateRequest = -1; +	_nTick = _evpUpdateCnt = 0; +	_regIndex = _reg; +	_evpTimer = 0x1f; +	_pReslt = 0x1f; +	_attack = 0; +	_cont = false; +	_evpUpdate = true; +	_timer = 0; + +	for (int i = 0; i < 3; i++) { +		_channels[i].tick = 0; +		_channels[i].smp = _channels[i].out = 0;  	} + +	for (int i = 0; i < 14; i++) +		writeReg(i, 0, true); + +	writeReg(7, 0xbf, true);  } -void TownsPC98_OpnChannelSSG::processFrequency() { -	if (_flags & CHS_RECALCFREQ) { -		uint8 block = (_frqBlockMSB & 0x70) >> 1; -		uint16 bfreq = ((const uint16*)_drv->_opnFreqTable)[_frqBlockMSB & 0x0f]; -		frequency = (bfreq + _frqLSB) | (block << 8); +uint8 TownsPC98_OpnSquareSineSource::readReg(uint8 address) { +	return _reg[address]; +} -		writeReg(_regOffset + 0xa4, (frequency >> 8)); -		writeReg(_regOffset + 0xa0, (frequency & 0xff)); +void TownsPC98_OpnSquareSineSource::writeReg(uint8 address, uint8 value, bool force) { +	_regIndex = &_reg[address]; +	int o = _regIndex - _reg; +	if (!force && (o == 13 || *_regIndex != value)) { +		if (_updateRequest == 31) { +			warning("TownsPC98_OpnSquareSineSource: event buffer overflow"); +			_updateRequest = -1; +		} +		_updateRequestBuf[++_updateRequest] = value; +		_updateRequestBuf[++_updateRequest] = o; +		return; +	} + +	*_regIndex = value; +} + +void TownsPC98_OpnSquareSineSource::nextTick(int32 *buffer, uint32 bufferSize) { +	if (!_ready) +		return; + +	for (uint32 i = 0; i < bufferSize; i++) { +		_timer += _tickLength; +		while (_timer > 0x5B8D80) { +			_timer -= 0x5B8D80; + +			if (++_nTick >= (_reg[6] & 0x1f)) { +				if ((_rand + 1) & 2) +					_outN ^= 1; + +				_rand = (((_rand & 1) ^ ((_rand >> 3) & 1)) << 16) | (_rand >> 1); +				_nTick = 0; +			} + +			for (int ii = 0; ii < 3; ii++) { +				if (++_channels[ii].tick >= (((_reg[ii * 2 + 1] & 0x0f) << 8) | _reg[ii * 2])) { +					_channels[ii].tick = 0; +					_channels[ii].smp ^= 1; +				} +				_channels[ii].out = (_channels[ii].smp | ((_reg[7] >> ii) & 1)) & (_outN | ((_reg[7] >> (ii + 3)) & 1)); +			} -		_ptchWhlCurDelay = _ptchWhlInitDelayHi; -		if (_flags & CHS_KEYOFF) { -			_ptchWhlModCurVal = _ptchWhlModInitVal; -			_ptchWhlCurDelay += _ptchWhlInitDelayLo; +			if (_evpUpdate) { +				if (++_evpUpdateCnt >= ((_reg[12] << 8) | _reg[11])) { +					_evpUpdateCnt = 0; + +					if (--_evpTimer < 0) { +						if (_cont) { +							_evpTimer &= 0x1f; +						} else { +							_evpUpdate = false; +							_evpTimer = 0; +						} +					} +				} +			} +			_pReslt = _evpTimer ^ _attack; +			updatesRegs();  		} -		_ptchWhlDurLeft = (_ptchWhlDuration >> 1); -		_flags &= ~(CHS_KEYOFF | CHS_RECALCFREQ); +		int32 finOut = 0; +		for (int ii = 0; ii < 3; ii++) { +			if ((_reg[ii + 8] >> 4) & 1) +				finOut += _tleTable[_channels[ii].out ? _pReslt : 0]; +			else +				finOut += _tlTable[_channels[ii].out ? (_reg[ii + 8] & 0x0f) : 0]; +		} + +		finOut /= 2; +		buffer[i << 1] += finOut; +		buffer[(i << 1) + 1] += finOut;  	} +} -	if (!(_flags & CHS_PITCHWHEELOFF)) { -		if (--_ptchWhlCurDelay) -			return; -		_ptchWhlCurDelay = _ptchWhlInitDelayHi; -		frequency += _ptchWhlModCurVal; +void TownsPC98_OpnSquareSineSource::updatesRegs() { +	for (int i = 0; i < _updateRequest;) { +		uint8 b = _updateRequestBuf[i++]; +		uint8 a = _updateRequestBuf[i++]; +		writeReg(a, b, true); +	} +	_updateRequest = -1; +} -		writeReg(_regOffset + 0xa4, (frequency >> 8)); -		writeReg(_regOffset + 0xa0, (frequency & 0xff)); +TownsPC98_OpnPercussionSource::TownsPC98_OpnPercussionSource(const uint32 timerbase) : +	_tickLength(timerbase * 2), _timer(0), _ready(false) { +		memset(_pcmInstr, 0, sizeof(PcmInstrument) * 6); +} -		if(!--_ptchWhlDurLeft) { -			_ptchWhlDurLeft = _ptchWhlDuration; -			_ptchWhlModCurVal = -_ptchWhlModCurVal; +void TownsPC98_OpnPercussionSource::init(const uint8 *pcmData) { +	if (_ready) { +		reset(); +		return; +	} + +	const uint8 *start = pcmData; +	const uint8 *pos = start; + +	if (pcmData) { +		for (int i = 0; i < 6; i++) { +			_pcmInstr[i].data = start + READ_BE_UINT16(pos); +			pos += 2; +			_pcmInstr[i].size = READ_BE_UINT16(pos); +			pos += 2;  		} +		reset(); +		_ready = true; +	} else { +		memset(_pcmInstr, 0, sizeof(PcmInstrument) * 6); +		_ready = false;  	}  } -void TownsPC98_OpnChannelSSG::keyOff() { -	// all operators off -	uint8 value = _keyNum & 0x0f; -	uint8 regAdress = 0x28; -	writeReg(regAdress, value); -	_flags |= CHS_KEYOFF; -} +void TownsPC98_OpnPercussionSource::reset() { +	_timer = 0; +	_totalLevel = 63; -void TownsPC98_OpnChannelSSG::keyOn() { -	// all operators on -	uint8 value = _keyNum | 0xf0; -	uint8 regAdress = 0x28; -	writeReg(regAdress, value); +	memset(_regs, 0, 48); + +	for (int i = 0; i < 6; i++) { +		PcmInstrument *s = &_pcmInstr[i]; +		s->pos = s->start = s->data; +		s->end = s->data + s->size; +		s->active = false; +		s->level = 0; +		s->out = 0; +		s->decStep = 1; +		s->decState = 0; +		s->samples[0] = s->samples[1] = 0; +	}  } -void TownsPC98_OpnChannelSSG::loadData(uint8 *data) { -	_drv->_ssgFlag = (_flags & CHS_SSG) ? -1 : 0; -	opn_SSG_UNK(0); -	TownsPC98_OpnChannel::loadData(data); -	_algorithm = 0x80; +void TownsPC98_OpnPercussionSource::writeReg(uint8 address, uint8 value) { +	if (!_ready) +		return; + +	 uint8 h = address >> 4; +	 uint8 l = address & 15; + +	_regs[address] = value; + +	if (address == 0) { +		if (value & 0x80) { +			//key off +			for (int i = 0; i < 6; i++) { +				if ((value >> i) & 1) +					_pcmInstr[i].active = false; +			} +		} else { +			//key on +			for (int i = 0; i < 6; i++) { +				if ((value >> i) & 1) { +					PcmInstrument *s = &_pcmInstr[i]; +					s->pos = s->start; +					s->active = true; +					s->out = 0; +					s->samples[0] = s->samples[1] = 0; +					s->decStep = 1; +					s->decState = 0; +				} +			} +		} +	} else if (address == 1) { +		// total level +		_totalLevel = (value & 63) ^ 63; +		for (int i = 0; i < 6; i++) +			recalcOuput(&_pcmInstr[i]); +	} else if (!h && l & 8) { +		// instrument level +		l &= 7; +		_pcmInstr[l].level = (value & 0x1f) ^ 0x1f; +		recalcOuput(&_pcmInstr[l]); +	} else if (h & 3) { +		l &= 7; +		if (h == 1) { +			// set start offset +			_pcmInstr[l].start  = _pcmInstr[l].data + ((_regs[24 + l] * 256 + _regs[16 + l]) << 8); +		} else if (h == 2) { +			// set end offset +			_pcmInstr[l].end = _pcmInstr[l].data + ((_regs[40 + l] * 256 + _regs[32 + l]) << 8) + 255; +		} +	}  } -void TownsPC98_OpnChannelSSG::opn_SSG_UNK(uint8 a) { -	_ssg1 = a; -	uint16 h = (_totalLevel + 1) * a; -	if ((h >> 8) == _ssg2) +void TownsPC98_OpnPercussionSource::nextTick(int32 *buffer, uint32 bufferSize) { +	if (!_ready)  		return; -	_ssg2 = (h >> 8); -	writeReg(8 + _regOffset, _ssg2); + +	for (uint32 i = 0; i < bufferSize; i++) { +		_timer += _tickLength; +		while (_timer > 0x5B8D80) { +			_timer -= 0x5B8D80; + +			for (int ii = 0; ii < 6; ii++) { +				PcmInstrument *s = &_pcmInstr[ii]; +				if (s->active) { +					recalcOuput(s); +					if (s->decStep) { +						advanceInput(s); +						if (s->pos == s->end) +							s->active = false; +					} +					s->decStep ^= 1; +				} +			} +		} + +		int32 finOut = 0; + +		for (int ii = 0; ii < 6; ii++) { +			if (_pcmInstr[ii].active) +				finOut += _pcmInstr[ii].out; +		} + +		finOut = (finOut * 7); + +		buffer[i << 1] += finOut; +		buffer[(i << 1) + 1] += finOut; +	} +} + +void TownsPC98_OpnPercussionSource::recalcOuput(PcmInstrument *ins) { +	uint32 s = _totalLevel + ins->level; +	uint32 x = s > 62 ? 0 : (1 + (s >> 3)); +	int32 y = s > 62 ? 0 : (15 - (s & 7)); +	ins->out = ((ins->samples[ins->decStep] * y) >> x) & ~3; +} + +void TownsPC98_OpnPercussionSource::advanceInput(PcmInstrument *ins) { +	static const int8 adjustIndex[] = {-1, -1, -1, -1, 2, 5, 7, 9 }; + +	static const int16 stepTable[] = { 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, +		60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, +		371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552 +	}; +	 +	uint8 cur = (int8) *ins->pos++; + +	for (int i = 0; i < 2; i++) { +		int b = (2 * (cur & 7) + 1) * stepTable[ins->decState] / 8; +		ins->samples[i] = CLIP<int16>(ins->samples[i ^ 1] + (cur & 8 ? b : -b), -2048, 2047); +		ins->decState = CLIP<int8>(ins->decState + adjustIndex[cur & 7], 0, 48); +		cur >>= 4; +	}  }  TownsPC98_OpnDriver::TownsPC98_OpnDriver(Audio::Mixer *mixer, OpnType type) : -	_mixer(mixer), _trackData(0), _playing(false), _fading(false), _channels(0), _ssgChannels(0), -	_looping(0), _opnCarrier(_drvTables + 76), _opnFreqTable(_drvTables + 84), -	_opnFxCmdLen(_drvTables + 36), _opnLvlPresets(_drvTables + (type == OD_TOWNS ? 52 : 220)) , -	_oprRates(0), _oprRateshift(0), _oprAttackDecay(0), _oprFrq(0),	_oprSinTbl(0), _oprLevelOut(0), -	_oprDetune(0), _cbCounter(4), _tickCounter(0), _updateChannelsFlag(type == OD_TYPE26 ? 0x07 : 0x3F), -	_finishedChannelsFlag(0), _samplesTillCallback(0), _samplesTillCallbackRemainder(0), _ready(false), -	_numSSG(type == OD_TOWNS ? 0 : 3), _hasADPCM(type == OD_TYPE86 ? true : false), -	_numChan(type == OD_TYPE26 ? 3 : 6), _hasStereo(type == OD_TYPE26 ? false : true) {	 -	setTempo(84); -	_baserate = (486202500.0 / (double)getRate()) / 10368.0; +	_mixer(mixer), + +	_channels(0), _ssgChannels(0), _sfxChannels(0), _pcmChannel(0), _ssg(0), _pcm(0), +	 +	_trackPtr(0), _sfxData(0), _sfxOffs(0), _ssgPatches(0),	 + +	_opnCarrier(_drvTables + 76), _opnFreqTable(_drvTables + 84), _opnFreqTableSSG(_drvTables + 252), +	_opnFxCmdLen(_drvTables + 36), _opnLvlPresets(_drvTables + (type == OD_TOWNS ? 52 : 228)), +	_oprRates(0), _oprRateshift(0), _oprAttackDecay(0),	_oprFrq(0), _oprSinTbl(0), _oprLevelOut(0), _oprDetune(0),	 +	 +	_numChan(type == OD_TYPE26 ? 3 : 6), _numSSG(type == OD_TOWNS ? 0 : 3), _hasPCM(type == OD_TYPE86 ? true : false), +	_updateChannelsFlag(type == OD_TYPE26 ? 0x07 : 0x3F), _finishedChannelsFlag(0), +	_updateSSGFlag(type == OD_TOWNS ? 0x00 : 0x07), _finishedSSGFlag(0), +	_updatePCMFlag(type == OD_TYPE86 ? 0x01 : 0x00), _finishedPCMFlag(0), +	_updateSfxFlag(type == OD_TOWNS ? 0x00 : 0x06), _finishedSfxFlag(0), +	 +	_baserate(55125.0f / (float)getRate()), +	_samplesTillMusicCallback(0), _samplesTillSfxCallback(0), +	_samplesTillMusicCallbackRemainder(0), _samplesTillSfxCallbackRemainder(0), +	_musicTickCounter(0), +	 +	_musicPlaying(false), _sfxPlaying(false), _fading(false), _looping(0), _ready(false) { + +	_timerbase = (uint32)(_baserate * 1000000.0f); +	setMusicTempo(84); +	setSfxTempo(654);  }  TownsPC98_OpnDriver::~TownsPC98_OpnDriver() { @@ -2381,13 +3088,26 @@ TownsPC98_OpnDriver::~TownsPC98_OpnDriver() {  		delete [] _ssgChannels;  	} +	if (_sfxChannels) { +		for (int i = 0; i < 2; i++) +			delete _sfxChannels[i]; +		delete [] _sfxChannels; +	} + +	if (_pcmChannel) +		delete _pcmChannel; + +	delete _ssg; +	delete _pcm; +  	delete [] _oprRates;  	delete [] _oprRateshift;  	delete [] _oprFrq;  	delete [] _oprAttackDecay;  	delete [] _oprSinTbl;  	delete [] _oprLevelOut; -	delete [] _oprDetune;	 +	delete [] _oprDetune; +	delete [] _ssgPatches;  }  bool TownsPC98_OpnDriver::init() { @@ -2420,7 +3140,21 @@ bool TownsPC98_OpnDriver::init() {  		}  		delete [] _ssgChannels;  	} + +	if (_sfxChannels) { +		for (int i = 0; i < 2; i++) { +			if (_sfxChannels[i]) +				delete _sfxChannels[i]; +		} +		delete [] _sfxChannels; +	} +  	if (_numSSG) { +		_ssg = new TownsPC98_OpnSquareSineSource(_timerbase); +		_ssg->init(&_ssgTables[0], &_ssgTables[16]); +		_ssgPatches = new uint8[256]; +		memcpy(_ssgPatches, _drvTables + 244, 256); +  		_ssgChannels = new TownsPC98_OpnChannelSSG*[_numSSG];  		for (int i = 0; i < _numSSG; i++) {  			int ii = i * 6; @@ -2428,6 +3162,23 @@ bool TownsPC98_OpnDriver::init() {  				_drvTables[ii + 2],	_drvTables[ii + 3],	_drvTables[ii + 4], _drvTables[ii + 5]);  			_ssgChannels[i]->init();  		} + +		_sfxChannels = new TownsPC98_OpnSfxChannel*[2]; +		for (int i = 0; i < 2; i++) { +			int ii = (i + 1) * 6; +			_sfxChannels[i] = new TownsPC98_OpnSfxChannel(this, _drvTables[ii], _drvTables[ii + 1], +				_drvTables[ii + 2],	_drvTables[ii + 3],	_drvTables[ii + 4], _drvTables[ii + 5]); +			_sfxChannels[i]->init(); +		} +	} + +	if (_hasPCM) { +		_pcm = new TownsPC98_OpnPercussionSource(_timerbase); +		_pcm->init(); + +		delete _pcmChannel; +		_pcmChannel = new TownsPC98_OpnChannelPCM(this, 0, 0, 0, 0, 0, 1); +		_pcmChannel->init();  	}  	_mixer->playInputStream(Audio::Mixer::kMusicSoundType, @@ -2439,36 +3190,63 @@ bool TownsPC98_OpnDriver::init() {  int inline TownsPC98_OpnDriver::readBuffer(int16 *buffer, const int numSamples) {  	memset(buffer, 0, sizeof(int16) * numSamples); +	int32 *tmp = new int32[numSamples]; +	int32 *tmpStart = tmp; +	memset(tmp, 0, sizeof(int32) * numSamples);  	int32 samplesLeft = numSamples >> 1; +  	while (samplesLeft) { -		if (!_samplesTillCallback) { -			callback(); -			_samplesTillCallback = _samplesPerCallback; -			_samplesTillCallbackRemainder += _samplesPerCallbackRemainder; -			if (_samplesTillCallbackRemainder >= _tempo) { -				_samplesTillCallback++; -				_samplesTillCallbackRemainder -= _tempo; +		if (!_samplesTillMusicCallback) { +			musicCallback(); +			_samplesTillMusicCallback = _samplesPerMusicCallback; + +			_samplesTillMusicCallbackRemainder += _samplesPerMusicCallbackRemainder; +			if (_samplesTillMusicCallbackRemainder >= _timerbase) { +				_samplesTillMusicCallback++; +				_samplesTillMusicCallbackRemainder -= _timerbase;  			}  		} -		int32 render = MIN(samplesLeft, _samplesTillCallback); +		if (!_samplesTillSfxCallback) { +			sfxCallback(); +			_samplesTillSfxCallback = _samplesPerSfxCallback; + +			_samplesTillSfxCallbackRemainder += _samplesPerSfxCallbackRemainder; +			if (_samplesTillSfxCallbackRemainder >= _timerbase) { +				_samplesTillSfxCallback++; +				_samplesTillSfxCallbackRemainder -= _timerbase; +			} +		} + +		int32 render = MIN(samplesLeft, MIN(_samplesTillSfxCallback, _samplesTillMusicCallback));  		samplesLeft -= render; -		_samplesTillCallback -= render; -		nextTick(buffer, render); +		_samplesTillMusicCallback -= render;		 +		_samplesTillSfxCallback -= render; + +		nextTick(tmp, render); + +		if (_ssg) +			_ssg->nextTick(tmp, render); +		if (_pcm) +			_pcm->nextTick(tmp, render);  		for (int i = 0; i < render; ++i) { -			buffer[i << 1] <<= 2; -			buffer[(i << 1) + 1] <<= 2; +			int32 l = CLIP<int32>(tmp[i << 1], -32767, 32767); +			buffer[i << 1] = (int16) l; +			int32 r = CLIP<int32>(tmp[(i << 1) + 1], -32767, 32767); +			buffer[(i << 1) + 1] = (int16) r;  		}  		buffer += (render << 1); +		tmp += (render << 1);  	} +	delete [] tmpStart;  	return numSamples;  } -void TownsPC98_OpnDriver::loadData(uint8 *data, bool loadPaused) { +void TownsPC98_OpnDriver::loadMusicData(uint8 *data, bool loadPaused) {  	if (!_ready) {  		warning("TownsPC98_OpnDriver: Driver must be initialized before loading data");  		return; @@ -2479,12 +3257,11 @@ void TownsPC98_OpnDriver::loadData(uint8 *data, bool loadPaused) {  		return;  	} +	reset(); +  	lock(); -	_trackData = data; -	reset(); -	 -	uint8 *src_a = data; +	uint8 *src_a = _trackPtr = _musicBuffer = data;  	for (uint8 i = 0; i < 3; i++) {  		_channels[i]->loadData(data + READ_LE_UINT16(src_a)); @@ -2501,117 +3278,179 @@ void TownsPC98_OpnDriver::loadData(uint8 *data, bool loadPaused) {  		src_a += 2;  	} -	if (_hasADPCM) { -		//_adpcmChannel->loadData(data + READ_LE_UINT16(src_a)); +	if (_hasPCM) { +		_pcmChannel->loadData(data + READ_LE_UINT16(src_a));  		src_a += 2;  	} -	_ssgFlag = 0; +	_regProtectionFlag = false;  	_patches = src_a + 4; -	_cbCounter = 4; -	_finishedChannelsFlag = 0; +	_finishedChannelsFlag = _finishedSSGFlag = _finishedPCMFlag = 0; + +	_musicPlaying = (loadPaused ? false : true); -	// AH 0x17  	unlock(); -	_playing = (loadPaused ? false : true); +} + +void TownsPC98_OpnDriver::loadSoundEffectData(uint8 *data, uint8 trackNum) { +	if (!_ready) { +		warning("TownsPC98_OpnDriver: Driver must be initialized before loading data"); +		return; +	} + +	if (!_sfxChannels) { +		warning("TownsPC98_OpnDriver: Sound effects not supported by this configuration"); +		return; +	} + +	if (!data) { +		warning("TownsPC98_OpnDriver: Invalid sound effects file data"); +		return; +	} + +	lock(); +	_sfxData = _sfxBuffer = data; +	_sfxOffsets[0] = READ_LE_UINT16(&_sfxData[(trackNum << 2)]); +	_sfxOffsets[1] = READ_LE_UINT16(&_sfxData[(trackNum << 2) + 2]); +	_sfxPlaying = true; +	_finishedSfxFlag = 0; +	unlock();  }  void TownsPC98_OpnDriver::reset() { -	for (int i = 0; i < (_numChan); i++) +	lock(); + +	for (int i = 0; i < _numChan; i++)  		_channels[i]->reset(); -	for (int i = 0; i < (_numSSG); i++) +	for (int i = 0; i < _numSSG; i++)  		_ssgChannels[i]->reset(); -	_playing = _fading = false; +	if (_ssg) { +		for (int i = 0; i < 2; i++) +			_sfxChannels[i]->reset(); + +		memcpy(_ssgPatches, _drvTables + 276, 256); +		_ssg->reset(); +	} + +	if (_pcmChannel) +		_pcmChannel->reset(); + +	_musicPlaying = false; +	_sfxPlaying = false; +	_fading = false;  	_looping = 0; -	_tickCounter = 0; +	_musicTickCounter = 0; +	_sfxData = 0; + +	unlock();  } -void TownsPC98_OpnDriver::fadeOut() { -	if (!_playing) +void TownsPC98_OpnDriver::fadeStep() { +	if (!_musicPlaying)  		return; -	_fading = true; +	lock(); -	for (int i = 0; i < 20; i++) {		 -		lock(); -		uint32 dTime = _tickCounter + 2; -		for (int j = 0; j < _numChan; j++) { -			if (_updateChannelsFlag & _channels[j]->_idFlag) -				_channels[j]->fadeStep(); -		} -		for (int j = 0; j < _numSSG; j++) +	for (int j = 0; j < _numChan; j++) { +		if (_updateChannelsFlag & _channels[j]->_idFlag) +			_channels[j]->fadeStep(); +	} +		 +	for (int j = 0; j < _numSSG; j++) { +		if (_updateSSGFlag & _ssgChannels[j]->_idFlag)  			_ssgChannels[j]->fadeStep(); +	} -		unlock(); - -		while (_playing) { -			if (_tickCounter >= dTime) -				break; +	if (!_fading) { +		_fading = 19; +		if (_hasPCM) { +			if (_updatePCMFlag & _pcmChannel->_idFlag) +				_pcmChannel->reset();  		} +	} else { +		if (!--_fading) +			reset();  	} -	_fading = false; - -	reset(); +	unlock();  } -void TownsPC98_OpnDriver::callback() { -	if (!_playing || --_cbCounter) -		return; +void TownsPC98_OpnDriver::musicCallback() { +	lock(); -	_cbCounter = 4; -	_tickCounter++; +	_sfxOffs = 0; -	lock(); +	if (_musicPlaying) { +		_musicTickCounter++; -	for (int i = 0; i < _numChan; i++) { -		if (_updateChannelsFlag & _channels[i]->_idFlag) { -			_channels[i]->processEvents(); -			_channels[i]->processFrequency(); +		for (int i = 0; i < _numChan; i++) { +			if (_updateChannelsFlag & _channels[i]->_idFlag) { +				_channels[i]->processEvents(); +				_channels[i]->processFrequency(); +			}  		} -	} -	if (_numSSG) {  		for (int i = 0; i < _numSSG; i++) { -			_ssgChannels[i]->processEvents(); -			_ssgChannels[i]->processFrequency(); +			if (_updateSSGFlag & _ssgChannels[i]->_idFlag) { +				_ssgChannels[i]->processEvents(); +				_ssgChannels[i]->processFrequency(); +			}  		} +		 +		if (_hasPCM) +			if (_updatePCMFlag & _pcmChannel->_idFlag) +				_pcmChannel->processEvents();  	} -	 -	_ssgFlag = 0; -	unlock(); +	_regProtectionFlag = false; -	if (_finishedChannelsFlag == _updateChannelsFlag) -		reset(); +	if (_finishedChannelsFlag == _updateChannelsFlag && _finishedSSGFlag == _updateSSGFlag && _finishedPCMFlag == _updatePCMFlag) +		_musicPlaying = false; + +	unlock();  } -void TownsPC98_OpnDriver::nextTick(int16 *buffer, uint32 bufferSize) { -	if (!_playing) -		return;	 +void TownsPC98_OpnDriver::sfxCallback() { +	lock(); -	for (int i = 0; i < _numChan ; i++) { -		if (_channels[i]->_updateEnvelopes) { -			_channels[i]->_updateEnvelopes = false; -			_channels[i]->updateEnv(); +	if (_sfxChannels && _sfxPlaying) { +		if (_sfxData) +			startSoundEffect(); + +		_sfxOffs = 3; +		_trackPtr = _sfxBuffer; + +		for (int i = 0; i < 2; i++) { +			if (_updateSfxFlag & _sfxChannels[i]->_idFlag) { +				_sfxChannels[i]->processEvents(); +				_sfxChannels[i]->processFrequency(); +			}  		} -		 -		for (uint32 ii = 0; ii < bufferSize ; ii++) -			_channels[i]->generateOutput(buffer[ii * 2], -			buffer[ii * 2 + 1],	&_channels[i]->_feedbuf[2], _channels[i]->_feedbuf); + +		_trackPtr = _musicBuffer;  	} -	for (int i = 0; i < _numSSG ; i++) { -		if (_ssgChannels[i]->_updateEnvelopes) { -			_ssgChannels[i]->_updateEnvelopes = false; -			_ssgChannels[i]->updateEnv(); +	if (_finishedSfxFlag == _updateSfxFlag) +		_sfxPlaying = false; + +	unlock(); +} + +void TownsPC98_OpnDriver::nextTick(int32 *buffer, uint32 bufferSize) { +	if (!_ready) +		return; + +	for (int i = 0; i < _numChan; i++) { +		if (_channels[i]->_updateEnvelopeParameters) { +			_channels[i]->_updateEnvelopeParameters = false; +			_channels[i]->updateEnv();  		} -		 +  		for (uint32 ii = 0; ii < bufferSize ; ii++) -			_ssgChannels[i]->generateOutput(buffer[ii * 2], -			buffer[ii * 2 + 1],	&_ssgChannels[i]->_feedbuf[2], _ssgChannels[i]->_feedbuf); +			_channels[i]->generateOutput(buffer[ii * 2], +				buffer[ii * 2 + 1],	&_channels[i]->_feedbuf[2], _channels[i]->_feedbuf);  	}  } @@ -2641,7 +3480,7 @@ void TownsPC98_OpnDriver::generateTables() {  	delete [] _oprFrq;  	_oprFrq = new uint32[0x1000];  	for (uint32 i = 0; i < 0x1000; i++) -		_oprFrq[i] = (uint32)(_baserate * (double)(i << 11)); +		_oprFrq[i] = (uint32)(_baserate * (float)(i << 11));  	delete [] _oprAttackDecay;  	_oprAttackDecay = new uint8[152]; @@ -2680,82 +3519,36 @@ void TownsPC98_OpnDriver::generateTables() {  	delete [] _oprDetune;  	_oprDetune = new int32[256];  	for (int i = 0; i < 128; i++) { -		_oprDetune[i] = (int32)	((double)dtt[i] * _baserate * 64.0); +		_oprDetune[i] = (int32)	((float)dtt[i] * _baserate * 64.0);  		_oprDetune[i + 128] = -_oprDetune[i];  	}  	delete [] dtt;  } -void TownsPC98_OpnDriver::setTempo(uint8 tempo) { -	_tempo = tempo; -	_samplesPerCallback = getRate() / _tempo; -	_samplesPerCallbackRemainder = getRate() % _tempo; -} - -const uint8 TownsPC98_OpnDriver::_drvTables[] = { -	//	channel presets -	0x00, 0x80, 0x00, 0x00, 0x00, 0x01, -	0x01, 0x80, 0x01, 0x01, 0x00, 0x02, -	0x02, 0x80, 0x02, 0x02, 0x00, 0x04, -	0x00, 0x80, 0x03, 0x04, 0x01, 0x08, -	0x01, 0x80, 0x04, 0x05, 0x01, 0x10, -	0x02, 0x80, 0x05, 0x06, 0x01, 0x20, - -	//	control event size -	0x01, 0x01, 0x01, 0x01,	0x01, 0x01, 0x04, 0x05, -	0x02, 0x06, 0x02, 0x00, 0x00, 0x02, 0x00, 0x02, - -	//	fmt level presets  -	0x54, 0x50,	0x4C, 0x48,	0x44, 0x40, 0x3C, 0x38, -	0x34, 0x30, 0x2C, 0x28, 0x24, 0x20, 0x1C, 0x18, -	0x14, 0x10, 0x0C, 0x08,	0x04, 0x90, 0x90, 0x90, -	 -	//	carriers -	0x08, 0x08, 0x08, 0x08,	0x0C, 0x0E, 0x0E, 0x0F, - -	//	frequencies -	0x6A, 0x02, 0x8F, 0x02, 0xB6, 0x02,	0xDF, 0x02, -	0x0B, 0x03, 0x39, 0x03, 0x6A, 0x03, 0x9E, 0x03, -	0xD5, 0x03,	0x10, 0x04, 0x4E, 0x04, 0x8F, 0x04, -	0x00, 0x00, 0x00, 0x00, - -	//	unused -	0x01, 0x00,	0x00, 0x00, 0x03, 0x00, 0x00, 0x00, -	0x02, 0x00,	0x00, 0x00,	0x05, 0x00, 0x00, 0x00, -	0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, -	0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, +void TownsPC98_OpnDriver::startSoundEffect() { +	for (int i = 0; i < 2; i++) { +		if (_sfxOffsets[i]) { +			_ssgChannels[i + 1]->protect(); +			_sfxChannels[i]->reset(); +			_sfxChannels[i]->loadData(_sfxData + _sfxOffsets[i]); +		} +	} -	//	detune -	0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, -	0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, -	0x08, 0x08, 0x08, 0x08, 0x01, 0x01,	0x01, 0x01, -	0x02, 0x02, 0x02, 0x02, 0x02, 0x03,	0x03, 0x03, -	0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, -	0x08, 0x08, 0x09, 0x0a,	0x0b, 0x0c, 0x0d, 0x0e, -	0x10, 0x10, 0x10, 0x10,	0x02, 0x02, 0x02, 0x02, -	0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, -	0x05, 0x06,	0x06, 0x07, 0x08, 0x08, 0x09, 0x0a, -	0x0b, 0x0c,	0x0d, 0x0e, 0x10, 0x11, 0x13, 0x14, -	0x16, 0x16, 0x16, 0x16, +	_sfxData = 0; +} -	//	pc98 level presets  -	0x40, 0x3B, 0x38, 0x34, 0x30, 0x2A, 0x28, 0x25, -	0x22, 0x20, 0x1D, 0x1A, 0x18, 0x15, 0x12, 0x10, -	0x0D, 0x0A, 0x08, 0x05, 0x02, 0x90, 0x90, 0x90 -}; +void TownsPC98_OpnDriver::setMusicTempo(uint8 tempo) { +	float spc = (float)(0x100 - tempo) * 16.0f / _baserate; +	_samplesPerMusicCallback = (int32) spc; +	_samplesPerMusicCallbackRemainder = (uint32) ((spc - (float)_samplesPerMusicCallback) * 1000000.0f); +} -const uint32 TownsPC98_OpnDriver::_adtStat[] = { -	0x00010001, 0x00010001,	0x00010001, 0x01010001, -	0x00010101, 0x00010101, 0x00010101, 0x01010101, -	0x01010101, 0x01010101, 0x01010102, 0x01010102, -	0x01020102, 0x01020102, 0x01020202, 0x01020202, -	0x02020202, 0x02020202, 0x02020204, 0x02020204, -	0x02040204, 0x02040204, 0x02040404, 0x02040404, -	0x04040404, 0x04040404, 0x04040408, 0x04040408, -	0x04080408, 0x04080408, 0x04080808, 0x04080808, -	0x08080808, 0x08080808, 0x10101010, 0x10101010 -}; +void TownsPC98_OpnDriver::setSfxTempo(uint16 tempo) { +	float spc = (float)(0x400 - tempo) / _baserate; +	_samplesPerSfxCallback = (int32) spc; +	_samplesPerSfxCallbackRemainder = (uint32) ((spc - (float)_samplesPerSfxCallback) * 1000000.0f); +}  SoundTowns::SoundTowns(KyraEngine_v1 *vm, Audio::Mixer *mixer)  	: Sound(vm, mixer), _lastTrack(-1), _currentSFX(0), _sfxFileData(0), @@ -2916,7 +3709,8 @@ void SoundTowns::playSoundEffect(uint8 track) {  	}  	playbackBufferSize -= 0x20; -	uint32 outputRate = uint32(11025 * semitoneAndSampleRate_to_sampleStep(note, sfxRootNoteOffs, sfxRate, 11025, 0x2000)); + +	uint32 outputRate = uint32(11025 * calculatePhaseStep(note, sfxRootNoteOffs, sfxRate, 11025, 0x2000));  	_currentSFX = Audio::makeLinearInputStream(sfxPlaybackBuffer, playbackBufferSize,  		outputRate, Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_LITTLE_ENDIAN | Audio::Mixer::FLAG_AUTOFREE, 0, 0); @@ -2995,7 +3789,7 @@ void SoundTowns::onTimer(void *data) {  		music->_parser->onTimer();  } -float SoundTowns::semitoneAndSampleRate_to_sampleStep(int8 semiTone, int8 semiToneRootkey, +float SoundTowns::calculatePhaseStep(int8 semiTone, int8 semiToneRootkey,  	uint32 sampleRate, uint32 outputRate, int32 pitchWheel) {  	if (semiTone < 0)  		semiTone = 0; @@ -3050,18 +3844,18 @@ bool SoundPC98::init() {  void SoundPC98::playTrack(uint8 track) {  	if (--track >= 56)  		track -= 55; -  +  	if (track == _lastTrack && _musicEnabled)  		return; -	haltTrack(); +	beginFadeOut();  	char musicfile[13];  	sprintf(musicfile, fileListEntry(0), track);  	delete[] _musicTrackData;  	_musicTrackData = _vm->resource()->fileData(musicfile, 0);  	if (_musicEnabled) -		_driver->loadData(_musicTrackData); +		_driver->loadMusicData(_musicTrackData);  	_lastTrack = track;  } @@ -3074,29 +3868,42 @@ void SoundPC98::haltTrack() {  }  void SoundPC98::beginFadeOut() { -	_driver->fadeOut(); +	if (!_driver->musicPlaying()) +		return; + +	for (int i = 0; i < 20; i++) { +		_driver->fadeStep(); +		_vm->delay(32); +	}  	haltTrack();  } -void SoundPC98::playSoundEffect(uint8) { -	/// TODO /// +void SoundPC98::playSoundEffect(uint8 track) { +	if (!_sfxTrackData) +		return; + +	//	This has been disabled for now since I don't know +	//	how to make up the correct track number. It probably +	//	needs a map. +	//_driver->loadSoundEffectData(_sfxTrackData, track);  }  //	KYRA 2  SoundTownsPC98_v2::SoundTownsPC98_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer) : -	Sound(vm, mixer), _currentSFX(0), _musicTrackData(0), _lastTrack(-1), _driver(0), _useFmSfx(false) { +	Sound(vm, mixer), _currentSFX(0), _musicTrackData(0), _sfxTrackData(0), _lastTrack(-1), _driver(0), _useFmSfx(false) {  }  SoundTownsPC98_v2::~SoundTownsPC98_v2() {  	delete[] _musicTrackData; +	delete[] _sfxTrackData;  	delete _driver;  }  bool SoundTownsPC98_v2::init() { -	_driver = new TownsPC98_OpnDriver(_mixer, /*_vm->gameFlags().platform == Common::kPlatformPC98 ? -		TownsPC98_OpnDriver::OD_TYPE86 :*/ TownsPC98_OpnDriver::OD_TOWNS); +	_driver = new TownsPC98_OpnDriver(_mixer, _vm->gameFlags().platform == Common::kPlatformPC98 ? +		TownsPC98_OpnDriver::OD_TYPE86 : TownsPC98_OpnDriver::OD_TOWNS);  	_useFmSfx = _vm->gameFlags().platform == Common::kPlatformPC98 ? true : false;  	_vm->checkCD();  	// FIXME: While checking for 'track1.XXX(X)' looks like @@ -3110,9 +3917,15 @@ bool SoundTownsPC98_v2::init() {  		(Common::File::exists("track1.mp3") || Common::File::exists("track1.ogg") ||  		 Common::File::exists("track1.flac") || Common::File::exists("track1.fla")))  			_musicEnabled = 2; +	  	return _driver->init();  } +void SoundTownsPC98_v2::loadSoundFile(Common::String file) { +	delete [] _sfxTrackData; +	_sfxTrackData = _vm->resource()->fileData(file.c_str(), 0); +} +  void SoundTownsPC98_v2::process() {  	AudioCD.updateCD();  } @@ -3138,9 +3951,9 @@ void SoundTownsPC98_v2::playTrack(uint8 track) {  	char musicfile[13];  	sprintf(musicfile, fileListEntry(0), track);  	delete[] _musicTrackData; -	 +  	_musicTrackData = _vm->resource()->fileData(musicfile, 0); -	_driver->loadData(_musicTrackData, true); +	_driver->loadMusicData(_musicTrackData, true);  	if (_musicEnabled == 2 && trackNum != -1) {  		AudioCD.play(trackNum+1, _driver->looping() ? -1 : 1, 0, 0); @@ -3160,7 +3973,14 @@ void SoundTownsPC98_v2::haltTrack() {  }  void SoundTownsPC98_v2::beginFadeOut() { -	_driver->fadeOut(); +	if (!_driver->musicPlaying()) +		return; + +	for (int i = 0; i < 20; i++) { +		_driver->fadeStep(); +		_vm->delay(32); +	} +  	haltTrack();  } @@ -3221,7 +4041,7 @@ int32 SoundTownsPC98_v2::voicePlay(const char *file, bool) {  		sfx[i] = cmd;  	} -	uint32 outputRate = uint32(11025 * SoundTowns::semitoneAndSampleRate_to_sampleStep(0x3c, 0x3c, sfxRate, 11025, 0x2000)); +	uint32 outputRate = uint32(11025 * SoundTowns::calculatePhaseStep(0x3c, 0x3c, sfxRate, 11025, 0x2000));  	_currentSFX = Audio::makeLinearInputStream(sfx, outsize, outputRate,  		Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_LITTLE_ENDIAN | Audio::Mixer::FLAG_AUTOFREE, 0, 0); @@ -3233,16 +4053,126 @@ int32 SoundTownsPC98_v2::voicePlay(const char *file, bool) {  }  void SoundTownsPC98_v2::playSoundEffect(uint8 track) { -	if (!_useFmSfx) +	if (!_useFmSfx || !_sfxTrackData)  		return; -	uint8 *sd = _vm->resource()->fileData("sound.dat", 0); +	_driver->loadSoundEffectData(_sfxTrackData, track); +} + +// static resources + +const uint8 TownsPC98_OpnDriver::_drvTables[] = { +	//	channel presets +	0x00, 0x80, 0x00, 0x00, 0x00, 0x01, +	0x01, 0x80, 0x01, 0x01, 0x00, 0x02, +	0x02, 0x80, 0x02, 0x02, 0x00, 0x04, +	0x00, 0x80, 0x03, 0x04, 0x01, 0x08, +	0x01, 0x80, 0x04, 0x05, 0x01, 0x10, +	0x02, 0x80, 0x05, 0x06, 0x01, 0x20, +	//	control event size +	0x01, 0x01, 0x01, 0x01,	0x01, 0x01, 0x04, 0x05, +	0x02, 0x06, 0x02, 0x00, 0x00, 0x02, 0x00, 0x02, -	//TODO +	//	fmt level presets +	0x54, 0x50,	0x4C, 0x48,	0x44, 0x40, 0x3C, 0x38, +	0x34, 0x30, 0x2C, 0x28, 0x24, 0x20, 0x1C, 0x18, +	0x14, 0x10, 0x0C, 0x08,	0x04, 0x90, 0x90, 0x90, -	delete [] sd; -} +	//	carriers +	0x08, 0x08, 0x08, 0x08,	0x0C, 0x0E, 0x0E, 0x0F, + +	//	frequencies +	0x6A, 0x02, 0x8F, 0x02, 0xB6, 0x02,	0xDF, 0x02, +	0x0B, 0x03, 0x39, 0x03, 0x6A, 0x03, 0x9E, 0x03, +	0xD5, 0x03,	0x10, 0x04, 0x4E, 0x04, 0x8F, 0x04, +	0x00, 0x00, 0x00, 0x00, + +	//	unused +	0x01, 0x00,	0x00, 0x00, 0x03, 0x00, 0x00, 0x00, +	0x02, 0x00,	0x00, 0x00,	0x05, 0x00, 0x00, 0x00, +	0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, +	0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + +	//	detune +	0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, +	0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, +	0x08, 0x08, 0x08, 0x08, 0x01, 0x01,	0x01, 0x01, +	0x02, 0x02, 0x02, 0x02, 0x02, 0x03,	0x03, 0x03, +	0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, +	0x08, 0x08, 0x09, 0x0a,	0x0b, 0x0c, 0x0d, 0x0e, +	0x10, 0x10, 0x10, 0x10,	0x02, 0x02, 0x02, 0x02, +	0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, +	0x05, 0x06,	0x06, 0x07, 0x08, 0x08, 0x09, 0x0a, +	0x0b, 0x0c,	0x0d, 0x0e, 0x10, 0x11, 0x13, 0x14, +	0x16, 0x16, 0x16, 0x16, + +	//	pc98 level presets +	0x40, 0x3B, 0x38, 0x34, 0x30, 0x2A, 0x28, 0x25, +	0x22, 0x20, 0x1D, 0x1A, 0x18, 0x15, 0x12, 0x10, +	0x0D, 0x0A, 0x08, 0x05, 0x02, 0x90, 0x90, 0x90, + +	//	ssg frequencies +	0xE8, 0x0E, 0x12, 0x0E, 0x48, 0x0D, 0x89, 0x0C, +	0xD5, 0x0B, 0x2B, 0x0B, 0x8A, 0x0A, 0xF3, 0x09, +	0x64, 0x09, 0xDD, 0x08, 0x5E, 0x08, 0xE6, 0x07, + +	// ssg patch data +	0x00, 0x00, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00, +	0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00, +	0x00, 0x01, 0xFF, 0xFF, 0x37, 0x81, 0xC8, 0x00, +	0x00, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, +	0x00, 0x01, 0xFF, 0xFF, 0x37, 0x81, 0xC8, 0x00, +	0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, +	0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00, +	0x00, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, +	0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00, +	0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, +	0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00, +	0x04, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, +	0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00, +	0x0A, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, +	0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0x01, 0x00, +	0xFF, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00, +	0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0x00, +	0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, +	0x64, 0x01, 0xFF, 0x64, 0xFF, 0x81, 0xFF, 0x00, +	0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, + +	0x02, 0x01, 0xFF, 0x28, 0xFF, 0x81, 0xF0, 0x00, +	0x00, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, +	0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xC8, 0x00, +	0x01, 0x81, 0x00, 0x00, 0x28, 0x81, 0x00, 0x00, +	0x00, 0x01, 0xFF, 0x78, 0x5F, 0x81, 0xA0, 0x00, +	0x05, 0x81, 0x00, 0x00, 0x28, 0x81, 0x00, 0x00, +	0x00, 0x01, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00, +	0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00, +	0x00, 0x01, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00, +	0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00, +	0x00, 0x01, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00, +	0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00 +}; + +const uint32 TownsPC98_OpnDriver::_adtStat[] = { +	0x00010001, 0x00010001,	0x00010001, 0x01010001, +	0x00010101, 0x00010101, 0x00010101, 0x01010101, +	0x01010101, 0x01010101, 0x01010102, 0x01010102, +	0x01020102, 0x01020102, 0x01020202, 0x01020202, +	0x02020202, 0x02020202, 0x02020204, 0x02020204, +	0x02040204, 0x02040204, 0x02040404, 0x02040404, +	0x04040404, 0x04040404, 0x04040408, 0x04040408, +	0x04080408, 0x04080408, 0x04080808, 0x04080808, +	0x08080808, 0x08080808, 0x10101010, 0x10101010 +}; + +const int TownsPC98_OpnDriver::_ssgTables[] = { +	0x01202A, 0x0092D2, 0x006B42, 0x0053CB, 0x003DF8, 0x003053, 0x0022DA, 0x001A8C, +	0x00129B, 0x000DC1, 0x000963, 0x0006C9, 0x000463, 0x0002FA, 0x0001B6, 0x0000FB, +	0x0193B6, 0x01202A, 0x00CDB1, 0x0092D2, 0x007D7D, 0x006B42, 0x005ECD, 0x0053CB, +	0x00480F, 0x003DF8, 0x0036B9, 0x003053, 0x00290A, 0x0022DA, 0x001E6B, 0x001A8C, +	0x001639, 0x00129B, 0x000FFF, 0x000DC1, 0x000B5D, 0x000963, 0x0007FB, 0x0006C9, +	0x000575, 0x000463, 0x00039D, 0x0002FA, 0x000242, 0x0001B6, 0x00014C, 0x0000FB +};  } // end of namespace Kyra  | 
