diff options
| -rw-r--r-- | engines/sci/sound/music.cpp | 6 | ||||
| -rw-r--r-- | engines/sci/sound/softseq/midi.cpp | 188 | ||||
| -rw-r--r-- | engines/sci/sound/softseq/mididriver.h | 7 | 
3 files changed, 185 insertions, 16 deletions
| diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp index 55013f4db6..95e3c66221 100644 --- a/engines/sci/sound/music.cpp +++ b/engines/sci/sound/music.cpp @@ -317,6 +317,12 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) {  			// Find out what channels to filter for SCI0  			channelFilterMask = pSnd->soundRes->getChannelFilterMask(_pMidiDrv->getPlayId(_soundVersion)); + +			// Enable rhythm channel when requested +			channelFilterMask &= ~(1 << MIDI_RHYTHM_CHANNEL); +			if (_pMidiDrv->hasRhythmChannel()) +				channelFilterMask |= (1 << MIDI_RHYTHM_CHANNEL); +  			pSnd->pMidiParser->loadMusic(track, pSnd, channelFilterMask, _soundVersion);  			// Fast forward to the last position and perform associated events when loading diff --git a/engines/sci/sound/softseq/midi.cpp b/engines/sci/sound/softseq/midi.cpp index ff774786f2..84a3d62df7 100644 --- a/engines/sci/sound/softseq/midi.cpp +++ b/engines/sci/sound/softseq/midi.cpp @@ -37,24 +37,34 @@ namespace Sci {  class MidiPlayer_Midi : public MidiPlayer {  public:  	enum { -		kVoices = 32 +		kVoices = 32, +		kReverbConfigNr = 11, +		kMaxSysExSize = 264  	};  	MidiPlayer_Midi();  	virtual ~MidiPlayer_Midi();  	int open(ResourceManager *resMan); +	void close();  	void send(uint32 b); +	void sysEx(const byte *msg, uint16 length);  	bool hasRhythmChannel() const { return true; }  	byte getPlayId(SciVersion soundVersion);  	int getPolyphony() const { return kVoices; }  	void setVolume(byte volume);  	int getVolume(); +	void setReverb(byte reverb);  	void playSwitch(bool play);  private:  	bool isMt32GmPatch(const byte *data, int size);  	void readMt32GmPatch(const byte *data, int size); +	void readMt32Patch(const byte *data, int size); +	void sendMt32SysEx(const uint32 addr, Common::MemoryReadStream *str, int len, bool noDelay); +	void sendMt32SysEx(const uint32 addr, const byte *buf, int len, bool noDelay); +	void setMt32Volume(byte volume); +	void resetMt32();  	void noteOn(int channel, int note, int velocity);  	void setPatch(int channel, int patch); @@ -76,10 +86,11 @@ private:  	};  	bool _isMt32; -	bool _isHardwareMt32; +	bool _hasReverb;  	bool _playSwitch;  	int _masterVolume; +	byte _reverbConfig[kReverbConfigNr][3];  	Channel _channels[16];	  	uint8 _percussionMap[128];  	int8 _keyShift[128]; @@ -87,18 +98,21 @@ private:  	uint8 _patchMap[128];  	uint8 _velocityMapIdx[128];  	uint8 _velocityMap[4][128]; +	byte _goodbyeMsg[20]; +	byte _sysExBuf[kMaxSysExSize];  }; -MidiPlayer_Midi::MidiPlayer_Midi() : _playSwitch(true), _masterVolume(15), _isMt32(false), _isHardwareMt32(false) { +MidiPlayer_Midi::MidiPlayer_Midi() : _playSwitch(true), _masterVolume(15), _isMt32(false), _hasReverb(false) {  	MidiDriverType midiType = MidiDriver::detectMusicDriver(MDT_MIDI);  	_driver = createMidi(midiType); -	if (midiType == MD_MT32) +	if (midiType == MD_MT32 || ConfMan.getBool("native_mt32"))  		_isMt32 = true; -	else if ((midiType != MD_FLUIDSYNTH) && (midiType != MD_TIMIDITY) && ConfMan.getBool("native_mt32")) { -		_isMt32 = true; -		_isHardwareMt32 = true; -	} + +	_sysExBuf[0] = 0x41; +	_sysExBuf[1] = 0x10; +	_sysExBuf[2] = 0x16; +	_sysExBuf[3] = 0x12;  }  MidiPlayer_Midi::~MidiPlayer_Midi() { @@ -262,6 +276,12 @@ int MidiPlayer_Midi::getVolume() {  	return _masterVolume;  } +void MidiPlayer_Midi::setReverb(byte reverb) { +	_reverb = CLIP<byte>(reverb, 0, kReverbConfigNr - 1); +	if (_hasReverb) +		sendMt32SysEx(0x100001, _reverbConfig[_reverb], 3, true); +} +  void MidiPlayer_Midi::playSwitch(bool play) {  	_playSwitch = play;  	if (play) @@ -302,6 +322,100 @@ bool MidiPlayer_Midi::isMt32GmPatch(const byte *data, int size)  	return isMt32Gm;  } +void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, Common::MemoryReadStream *str, int len, bool noDelay = false) { +	if (len + 8 > kMaxSysExSize) { +		warning("SysEx message exceed maximum size; ignoring"); +		return; +	} + +	uint16 chk = 0; + +	_sysExBuf[4] = (addr >> 16) & 0xff; +	_sysExBuf[5] = (addr >> 8) & 0xff; +	_sysExBuf[6] = addr & 0xff; + +	for (int i = 0; i < len; i++) +		_sysExBuf[7 + i] = str->readByte(); + +	for (int i = 4; i < 7 + len; i++) +		chk += _sysExBuf[i]; + +	_sysExBuf[7 + len] = 128 - chk % 128; + +	if (noDelay) +		_driver->sysEx(_sysExBuf, len + 8); +	else +		sysEx(_sysExBuf, len + 8); +} + +void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, const byte *buf, int len, bool noDelay = false) { +	Common::MemoryReadStream *str = new Common::MemoryReadStream(buf, len); +	sendMt32SysEx(addr, str, len, noDelay); +	delete str; +} + +void MidiPlayer_Midi::readMt32Patch(const byte *data, int size) { +	Common::MemoryReadStream *str = new Common::MemoryReadStream(data, size); + +	// Send before-SysEx text +	str->seek(0x14); +	sendMt32SysEx(0x200000, str, 20); + +	// Save goodbye message +	str->read(_goodbyeMsg, 20); + +	// Skip volume, we leave the MT-32 volume alone +	str->seek(2, SEEK_CUR); + +	// Reverb default only used in (roughly) SCI0/SCI01 +	_reverb = str->readByte(); +	_hasReverb = true; + +	// Skip reverb SysEx message +	str->seek(11, SEEK_CUR); + +	// Read reverb data +	for (int i = 0; i < kReverbConfigNr; i++) { +		_reverbConfig[i][0] = str->readByte(); +		_reverbConfig[i][1] = str->readByte(); +		_reverbConfig[i][2] = str->readByte(); +	} + +	// Patches 1-48 +	sendMt32SysEx(0x50000, str, 256); +	sendMt32SysEx(0x50200, str, 128); + +	// Timbres +	byte timbresNr = str->readByte(); +	for (int i = 0; i < timbresNr; i++) +		sendMt32SysEx(0x80000 + (i << 9), str, 246); + +	uint16 flag = str->readUint16BE(); + +	if (!str->eos() && (flag == 0xabcd)) { +		// Patches 49-96 +		sendMt32SysEx(0x50300, str, 256); +		sendMt32SysEx(0x50500, str, 128); +		flag = str->readUint16BE(); +	} + +	if (!str->eos() && (flag == 0xdcba)) { +		// Rhythm key map +		sendMt32SysEx(0x30110, str, 256); +		// Partial reserve +		sendMt32SysEx(0x100004, str, 9); +	} + +	// Send after-SysEx text +	str->seek(0); +	sendMt32SysEx(0x200000, str, 20); + +	// Send the mystery SysEx +	sendMt32SysEx(0x52000a, (const byte *)"\x16\x16\x16\x16\x16\x16", 6); + +	delete str; +} +  void MidiPlayer_Midi::readMt32GmPatch(const byte *data, int size) {  	memcpy(_patchMap, data, 0x80);  	memcpy(_keyShift, data + 0x80, 0x80); @@ -334,9 +448,7 @@ void MidiPlayer_Midi::readMt32GmPatch(const byte *data, int size) {  					error("Failed to find end of sysEx");  				int len = sysExEnd - (midi + i); -				_driver->sysEx(midi + i, len); -				if (_isHardwareMt32) -					g_system->delayMillis(100); +				sysEx(midi + i, len);  				i += len + 1; // One more for the 0x7f  				break; @@ -368,6 +480,17 @@ void MidiPlayer_Midi::readMt32GmPatch(const byte *data, int size) {  	}  } +void MidiPlayer_Midi::setMt32Volume(byte volume) { +	sendMt32SysEx(0x100016, &volume, 1); +} + +void MidiPlayer_Midi::resetMt32() { +	sendMt32SysEx(0x7f0000, (const byte *)"\x01\x00", 2, true); + +	// This seems to require a longer delay than usual +	g_system->delayMillis(150); +} +  int MidiPlayer_Midi::open(ResourceManager *resMan) {  	assert(resMan != NULL); @@ -377,6 +500,11 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) {  		return retval;  	} +	if (_isMt32) { +		resetMt32(); +		setMt32Volume(80); +	} +  	Resource *res = NULL;  	if (!_isMt32) { @@ -393,14 +521,19 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) {  	if (isMt32GmPatch(res->data, res->size)) {  		readMt32GmPatch(res->data, res->size); +		strncpy((char *)_goodbyeMsg, "      ScummVM       ", 20);  	} else { -		// TODO -		warning("Old MT-32 patch format currently not supported"); -		for (uint i = 0; i < 127; i++) { +		if (!_isMt32) { +			warning("MT-32 to GM translation not yet supported"); +		} + +		readMt32Patch(res->data, res->size); + +		// No mapping +		for (uint i = 0; i < 128; i++) {  			_percussionMap[i] = i;  			_patchMap[i] = i; -			for (uint j = 0; j < 4; j++) -				_velocityMap[j][i] = i; +			_velocityMap[0][i] = i;  			_keyShift[i] = 0;  			_volAdjust[i] = 0;  			_velocityMapIdx[i] = 0; @@ -410,6 +543,29 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) {  	return 0;  } +void MidiPlayer_Midi::close() { +	if (_isMt32) { +		// Send goodbye message +		sendMt32SysEx(0x200000, _goodbyeMsg, 20); +	} + +	_driver->close(); +} + +void MidiPlayer_Midi::sysEx(const byte *msg, uint16 length) { +	_driver->sysEx(msg, length); + +	// Wait the time it takes to send the SysEx data +	uint32 delay = (length + 2) * 1000 / 3125; + +	// Plus an additional delay for the MT-32 rev00 +	if (_isMt32) +		delay += 40; + +	g_system->delayMillis(delay); +	g_system->updateScreen(); +} +  byte MidiPlayer_Midi::getPlayId(SciVersion soundVersion) {  	switch (soundVersion) {  	case SCI_VERSION_0_EARLY: diff --git a/engines/sci/sound/softseq/mididriver.h b/engines/sci/sound/softseq/mididriver.h index 8a8813d062..a1e6390a06 100644 --- a/engines/sci/sound/softseq/mididriver.h +++ b/engines/sci/sound/softseq/mididriver.h @@ -67,7 +67,11 @@ enum {  class MidiPlayer : public MidiDriver {  protected:  	MidiDriver *_driver; +	byte _reverb; +  public: +	MidiPlayer() : _reverb(0) { } +  	int open() {  		ResourceManager *resMan = ((SciEngine *)g_engine)->getResourceManager();	// HACK  		return open(resMan); @@ -93,6 +97,9 @@ public:  		return _driver ? _driver->property(MIDI_PROP_MASTER_VOLUME, 0xffff) : 0;   	} +	virtual byte getReverb() { return _reverb; } +	virtual void setReverb(byte reverb) { _reverb = reverb; } +  	virtual void playSwitch(bool play) {  		if (!play) {  			// Send "All Sound Off" on all channels | 
