aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorathrxx2019-07-16 23:18:42 +0200
committerathrxx2019-07-17 17:38:26 +0200
commit59f1e7aa48960d0c6f1a75067a7975ce01bc947a (patch)
treeebc008633aef166a6bfd2a968916c3ebdaa30c13
parent77dbefb907125481ffc83f29da4627564612ebf1 (diff)
downloadscummvm-rg350-59f1e7aa48960d0c6f1a75067a7975ce01bc947a.tar.gz
scummvm-rg350-59f1e7aa48960d0c6f1a75067a7975ce01bc947a.tar.bz2
scummvm-rg350-59f1e7aa48960d0c6f1a75067a7975ce01bc947a.zip
SCI: (FB01 sound driver) - SCI0_LATE fix
Fix up SCI0_LATE variant of the driver so that it actually plays anything (and even correctly). SCI1 hasn't seen any testing from me so far. I don't know whether that version works. Same for SCI0_EARLY...
-rw-r--r--engines/sci/sound/drivers/fb01.cpp140
1 files changed, 131 insertions, 9 deletions
diff --git a/engines/sci/sound/drivers/fb01.cpp b/engines/sci/sound/drivers/fb01.cpp
index 3f3f581ee2..4713b2346b 100644
--- a/engines/sci/sound/drivers/fb01.cpp
+++ b/engines/sci/sound/drivers/fb01.cpp
@@ -55,6 +55,7 @@ public:
int open(ResourceManager *resMan);
void close();
+ void initTrack(SciSpan<const byte>& header);
void send(uint32 b);
void sysEx(const byte *msg, uint16 length);
bool hasRhythmChannel() const { return false; }
@@ -74,6 +75,7 @@ private:
void setSystemParam(byte sysChan, byte param, byte value);
void sendVoiceData(byte instrument, const SciSpan<const byte> &data);
void sendBanks(const SciSpan<const byte> &data);
+ void sendDisplayString(int bank);
void storeVoiceData(byte instrument, byte bank, byte index);
void initVoices();
@@ -102,6 +104,7 @@ private:
struct Voice {
int8 channel; // MIDI channel that this voice is assigned to or -1
+ uint8 poly; // Number of hardware voices
int8 note; // Currently playing MIDI note or -1
int bank; // Current bank setting or -1
int patch; // Currently playing patch or -1
@@ -109,11 +112,13 @@ private:
bool isSustained; // Flag indicating a note that is being sustained by the hold pedal
uint16 age; // Age of the current note
- Voice() : channel(-1), note(-1), bank(-1), patch(-1), velocity(0), isSustained(false), age(0) { }
+ Voice() : channel(-1), note(-1), bank(-1), patch(-1), velocity(0), isSustained(false), age(0), poly(0) { }
};
bool _playSwitch;
int _masterVolume;
+ int _numParts;
+ bool _isOpen;
Channel _channels[16];
Voice _voices[kVoices];
@@ -126,7 +131,8 @@ private:
byte _sysExBuf[kMaxSysExSize];
};
-MidiPlayer_Fb01::MidiPlayer_Fb01(SciVersion version) : MidiPlayer(version), _playSwitch(true), _masterVolume(15), _timerParam(NULL), _timerProc(NULL) {
+MidiPlayer_Fb01::MidiPlayer_Fb01(SciVersion version) : MidiPlayer(version), _playSwitch(true), _masterVolume(15), _timerParam(NULL), _timerProc(NULL),
+ _numParts(version == SCI_VERSION_0_LATE ? 0 : kVoices), _isOpen(false) {
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI);
_driver = MidiDriver::createMidi(dev);
@@ -264,16 +270,22 @@ int MidiPlayer_Fb01::findVoice(int channel) {
}
void MidiPlayer_Fb01::sendToChannel(byte channel, byte command, byte op1, byte op2) {
- for (int i = 0; i < kVoices; i++) {
+ for (int i = 0; i < _numParts; i++) {
// Send command to all voices assigned to this channel
if (_voices[i].channel == channel)
- _driver->send(command | i, op1, op2);
+ _driver->send(command | (_version == SCI_VERSION_0_LATE ? channel : i), op1, op2);
}
}
void MidiPlayer_Fb01::setPatch(int channel, int patch) {
int bank = 0;
+ if (_version == SCI_VERSION_0_LATE && channel == 15) {
+ // The original driver has some parsing related handling for program 127.
+ // We can't handle that here.
+ return;
+ }
+
_channels[channel].patch = patch;
if (patch >= 48) {
@@ -281,13 +293,13 @@ void MidiPlayer_Fb01::setPatch(int channel, int patch) {
bank = 1;
}
- for (int voice = 0; voice < kVoices; voice++) {
+ for (int voice = 0; voice < _numParts; voice++) {
if (_voices[voice].channel == channel) {
if (_voices[voice].bank != bank) {
_voices[voice].bank = bank;
setVoiceParam(voice, 4, bank);
}
- _driver->send(0xc0 | voice, patch, 0);
+ _driver->send(0xc0 | (_version == SCI_VERSION_0_LATE ? channel : voice), patch, 0);
}
}
}
@@ -342,6 +354,22 @@ void MidiPlayer_Fb01::noteOn(int channel, int note, int velocity) {
}
void MidiPlayer_Fb01::controlChange(int channel, int control, int value) {
+ if (_version == SCI_VERSION_0_LATE) {
+ // The SCI specific 0x4B control is the only one that gets filtered here.
+ // We also ignore the control channel 15.
+ if (control == 0x4B) {
+ for (int i = 0; i < _numParts; ++i) {
+ if (_voices[i].channel == channel && _voices[i].poly != value) {
+ _voices[i].poly = value;
+ setVoiceParam(i, 0, value);
+ }
+ }
+ } else if (channel != 15) {
+ sendToChannel(channel, 0xb0, control, value);
+ }
+ return;
+ }
+
switch (control) {
case 0x07: {
_channels[channel].volume = value;
@@ -385,6 +413,16 @@ void MidiPlayer_Fb01::send(uint32 b) {
byte op1 = (b >> 8) & 0x7f;
byte op2 = (b >> 16) & 0x7f;
+ if (_version == SCI_VERSION_0_LATE && command != 0xB0 && command != 0xC0) {
+ // Since the voice mapping takes place inside the hardware, most messages
+ // are simply passed through. Channel 15 is never assigned to a part but is
+ // used for certain parsing related events which we cannot handle here.
+ // Just making sure that no nonsense is sent to the device...
+ if (channel != 15)
+ sendToChannel(channel, command, op1, op2);
+ return;
+ }
+
switch (command) {
case 0x80:
noteOff(channel, op1);
@@ -447,23 +485,40 @@ void MidiPlayer_Fb01::sendBanks(const SciSpan<const byte> &data) {
if (data.size() < 3072)
error("Failed to read FB-01 patch");
+ int first = (_version == SCI_VERSION_0_LATE) ? 1 : 0;
+ sendDisplayString(0);
+
// SSCI sends bank dumps containing 48 instruments at once. We cannot do that
// due to the limited maximum SysEx length. Instead we send the instruments
// one by one and store them in the banks.
- for (int i = 0; i < 48; i++) {
+ for (int i = first; i < 48; i++) {
sendVoiceData(0, data.subspan(i * 64));
storeVoiceData(0, 0, i);
}
// Send second bank if available
if (data.size() >= 6146 && data.getUint16BEAt(3072) == 0xabcd) {
- for (int i = 0; i < 48; i++) {
+ sendDisplayString(1);
+ for (int i = first; i < 48; i++) {
sendVoiceData(0, data.subspan(3074 + i * 64));
storeVoiceData(0, 1, i);
}
}
}
+void MidiPlayer_Fb01::sendDisplayString(int bank) {
+ if (_version != SCI_VERSION_0_LATE)
+ return;
+
+ Common::String sierraStr = Common::String::format("SIERRA %d", bank + 1);
+ byte buf[64];
+ memset(buf, 0, 64);
+ Common::strlcpy((char*)buf, sierraStr.c_str(), sierraStr.size());
+ SciSpan<const byte> displayStr(buf, 64);
+
+ sendVoiceData(0, displayStr);
+}
+
int MidiPlayer_Fb01::open(ResourceManager *resMan) {
assert(resMan != NULL);
@@ -474,7 +529,8 @@ int MidiPlayer_Fb01::open(ResourceManager *resMan) {
}
// Set system channel to 0. We send this command over all 16 system channels
- for (int i = 0; i < 16; i++)
+ int n = (_version == SCI_VERSION_0_LATE) ? 1 : 16;
+ for (int i = 0; i < n; i++)
setSystemParam(i, 0x20, 0);
// Turn off memory protection
@@ -525,6 +581,8 @@ int MidiPlayer_Fb01::open(ResourceManager *resMan) {
// Set master volume
setSystemParam(0, 0x24, 0x7f);
+ _isOpen = true;
+
return 0;
}
@@ -532,6 +590,65 @@ void MidiPlayer_Fb01::close() {
_driver->close();
}
+void MidiPlayer_Fb01::initTrack(SciSpan<const byte>& header) {
+ // I haven't seen any SCI0_EARLY variant of this driver. Skip this for now...
+ if (!_isOpen || _version != SCI_VERSION_0_LATE)
+ return;
+
+ uint8 readPos = 0;
+ uint8 caps = header.getInt8At(readPos++);
+ if (caps != 0 && caps != 2)
+ return;
+
+ for (int i = 0; i < 8; ++i)
+ _voices[i] = Voice();
+
+ _numParts = 0;
+ for (int i = 0; i < 16; ++i) {
+ uint8 num = header.getInt8At(readPos++) & 0x7F;
+ uint8 flags = header.getInt8At(readPos++);
+
+ if (flags & 0x02) {
+ _voices[_numParts].channel = i;
+ _voices[_numParts].poly = num;
+ _numParts++;
+ }
+ }
+
+ for (int i = 0; i < _numParts; ++i)
+ setVoiceParam(i, 1, _voices[i].channel);
+
+ // From here this is more or less a copy of initVoices() with some modifications which are relevant for the
+ // correct assignment of hardware channels. TODO: Maybe merge this somehow. I'd have to see a SCI1 driver for
+ // that, though. Right now, this is only about fixing SCI_0_LATE...
+ int i = 2;
+ _sysExBuf[i++] = 0x70;
+
+ for (int j = 0; j < 16; j++) {
+ _sysExBuf[i++] = 0x70 | j;
+ _sysExBuf[i++] = 0x00;
+ _sysExBuf[i++] = 0x00;
+ }
+
+ for (int j = 0; j < _numParts; ++j) {
+ _sysExBuf[i] = _sysExBuf[i + 3] = _sysExBuf[i + 6] = _sysExBuf[i + 9] = _sysExBuf[i + 12] = _voices[j].channel | 0x70;
+ _sysExBuf[i + 1] = 0;
+ _sysExBuf[i + 2] = _voices[j].poly;
+ _sysExBuf[i + 4] = 2;
+ _sysExBuf[i + 5] = 127;
+ _sysExBuf[i + 7] = 3;
+ _sysExBuf[i + 8] = 0;
+ _sysExBuf[i + 10] = 4;
+ _sysExBuf[i + 11] = 0;
+ _sysExBuf[i + 13] = 5;
+ _sysExBuf[i + 14] = 10;
+ i += 15;
+ }
+
+ sysEx(_sysExBuf, i);
+ setSystemParam(0, 0x24, (_masterVolume << 3) + 7);
+}
+
void MidiPlayer_Fb01::setVoiceParam(byte voice, byte param, byte value) {
_sysExBuf[2] = 0x00;
_sysExBuf[3] = 0x18 | voice;
@@ -582,6 +699,11 @@ void MidiPlayer_Fb01::storeVoiceData(byte instrument, byte bank, byte index) {
}
void MidiPlayer_Fb01::initVoices() {
+ // There is no need for this in SCI0, since the voice assignment is done in initTrack().
+ // Maybe this can be merged (would require that I actually look at the SCI1 version of the driver).
+ if (_version == SCI_VERSION_0_LATE)
+ return;
+
int i = 2;
_sysExBuf[i++] = 0x70;