aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorWalter van Niftrik2010-01-16 16:06:41 +0000
committerWalter van Niftrik2010-01-16 16:06:41 +0000
commit220c118a8fd4459e8947b3896bef386dc8fe51c7 (patch)
tree92bfd8d7675b759153aa5d62d164d7dd544ac8ea /engines
parent77ea73e9a5ce6d38e5f15dae9792909487fd3cb7 (diff)
downloadscummvm-rg350-220c118a8fd4459e8947b3896bef386dc8fe51c7.tar.gz
scummvm-rg350-220c118a8fd4459e8947b3896bef386dc8fe51c7.tar.bz2
scummvm-rg350-220c118a8fd4459e8947b3896bef386dc8fe51c7.zip
SCI: Add support for earlier MT-32 patch format
svn-id: r47321
Diffstat (limited to 'engines')
-rw-r--r--engines/sci/sound/music.cpp6
-rw-r--r--engines/sci/sound/softseq/midi.cpp188
-rw-r--r--engines/sci/sound/softseq/mididriver.h7
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