SCUMM: (IMS) - add dedicated MT-32 sound driver
(fixes bug no. 1248 - "SAM: some MIDI channels missing on MT-32") This is a very obvious bug if the speech is turned off (otherwise the speech will kind of cover it up). In the SAMNMAX intro it is hardly playing anything. The reason is that the newer sound system has an extra driver layer between the imuse parts and the actual hardware which mananges the channels (similar to what we already do for AdLib, Amiga, FM-Towns...). Whenever the songs are attempting to use a Midi part > 9 our current system will fail. I have written the MT-32 player to also handle the older games music and sound effects. This will allow cleaning up various more hacks in the common sound code. I have done only little cleanup here, since it is not the main purpose of the commit. I will also write a GM driver. This is going to be easier. Currently, there is a only some init and skeleton code for it, the rest is just pass-through...
This commit is contained in:
parent
99dd6cb248
commit
40b459164b
11 changed files with 788 additions and 125 deletions
|
@ -39,6 +39,8 @@ int IMuseDriver_GMidi::open() {
|
|||
return MERR_CANNOT_CONNECT;
|
||||
|
||||
int res = _drv->open();
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
if (_gsMode)
|
||||
initDeviceAsRolandGS();
|
||||
|
@ -48,15 +50,30 @@ int IMuseDriver_GMidi::open() {
|
|||
return res;
|
||||
}
|
||||
|
||||
void IMuseDriver_GMidi::close() {
|
||||
if (isOpen() && _drv)
|
||||
_drv->close();
|
||||
|
||||
//releaseChannels();
|
||||
}
|
||||
|
||||
MidiChannel *IMuseDriver_GMidi::allocateChannel() {
|
||||
if (!_newSystem)
|
||||
if (!isOpen())
|
||||
return nullptr;
|
||||
|
||||
// Pass through everything for now.
|
||||
//if (!_newSystem)
|
||||
return _drv->allocateChannel();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MidiChannel *IMuseDriver_GMidi::getPercussionChannel() {
|
||||
if (!_newSystem)
|
||||
if (!isOpen())
|
||||
return nullptr;
|
||||
|
||||
// Pass through everything for now.
|
||||
//if (!_newSystem)
|
||||
return _drv->getPercussionChannel();
|
||||
|
||||
return nullptr;
|
||||
|
@ -93,7 +110,7 @@ void IMuseDriver_GMidi::initDeviceAsRolandGS() {
|
|||
// General MIDI System On message
|
||||
// Resets all GM devices to default settings
|
||||
memcpy(&buffer[0], "\x7E\x7F\x09\x01", 4);
|
||||
_drv->sysEx(buffer, 4);
|
||||
sysEx(buffer, 4);
|
||||
debug(2, "GM SysEx: GM System On");
|
||||
g_system->delayMillis(200);
|
||||
|
||||
|
@ -109,13 +126,13 @@ void IMuseDriver_GMidi::initDeviceAsRolandGS() {
|
|||
|
||||
// GS Reset
|
||||
memcpy(&buffer[4], "\x40\x00\x7F\x00\x41", 5);
|
||||
_drv->sysEx(buffer, 9);
|
||||
sysEx(buffer, 9);
|
||||
debug(2, "GS SysEx: GS Reset");
|
||||
g_system->delayMillis(200);
|
||||
|
||||
// Set global Master Tune to 442.0kHz, as on the MT-32
|
||||
memcpy(&buffer[4], "\x40\x00\x00\x00\x04\x04\x0F\x29", 8);
|
||||
_drv->sysEx(buffer, 12);
|
||||
sysEx(buffer, 12);
|
||||
debug(2, "GS SysEx: Master Tune set to 442.0kHz");
|
||||
|
||||
// Note: All Roland GS devices support CM-64/32L maps
|
||||
|
@ -132,24 +149,24 @@ void IMuseDriver_GMidi::initDeviceAsRolandGS() {
|
|||
// Switch Drum Map to CM-64/32L (MT-32 Compatible Drums)
|
||||
getPercussionChannel()->controlChange(0, 0);
|
||||
getPercussionChannel()->controlChange(32, 1);
|
||||
_drv->send(127 << 8 | 0xC0 | 9);
|
||||
send(127 << 8 | 0xC0 | 9);
|
||||
debug(2, "GS Program Change: Drum Map is CM-64/32L");
|
||||
|
||||
// Set Master Chorus to 0. The MT-32 has no chorus capability.
|
||||
memcpy(&buffer[4], "\x40\x01\x3A\x00\x05", 5);
|
||||
_drv->sysEx(buffer, 9);
|
||||
sysEx(buffer, 9);
|
||||
debug(2, "GS SysEx: Master Chorus Level is 0");
|
||||
|
||||
// Set Channels 1-16 Reverb to 64, which is the
|
||||
// equivalent of MT-32 default Reverb Level 5
|
||||
for (i = 0; i < 16; ++i)
|
||||
_drv->send((64 << 16) | (91 << 8) | (0xB0 | i));
|
||||
send((64 << 16) | (91 << 8) | (0xB0 | i));
|
||||
debug(2, "GM Controller 91 Change: Channels 1-16 Reverb Level is 64");
|
||||
|
||||
// Set Channels 1-16 Pitch Bend Sensitivity to
|
||||
// 12 semitones; then lock the RPN by setting null.
|
||||
for (i = 0; i < 16; ++i)
|
||||
_drv->setPitchBendRange(i, 12);
|
||||
setPitchBendRange(i, 12);
|
||||
debug(2, "GM Controller 6 Change: Channels 1-16 Pitch Bend Sensitivity is 12 semitones");
|
||||
|
||||
// Set channels 1-16 Mod. LFO1 Pitch Depth to 4
|
||||
|
@ -157,7 +174,7 @@ void IMuseDriver_GMidi::initDeviceAsRolandGS() {
|
|||
for (i = 0; i < 16; ++i) {
|
||||
buffer[5] = 0x20 + i;
|
||||
buffer[8] = 0x18 - i;
|
||||
_drv->sysEx(buffer, 9);
|
||||
sysEx(buffer, 9);
|
||||
}
|
||||
|
||||
debug(2, "GS SysEx: Channels 1-16 Mod. LFO1 Pitch Depth Level is 4");
|
||||
|
@ -170,25 +187,25 @@ void IMuseDriver_GMidi::initDeviceAsRolandGS() {
|
|||
// Expression cannot be modified. I don't know why, but
|
||||
// Roland does it this way.
|
||||
memcpy(&buffer[4], "\x40\x10\x0E\x00\x22", 5);
|
||||
_drv->sysEx(buffer, 9);
|
||||
sysEx(buffer, 9);
|
||||
debug(2, "GS SysEx: Percussion Channel Rx. Expression is OFF");
|
||||
|
||||
// Change Reverb Character to 0. I don't think this
|
||||
// sounds most like MT-32, but apparently Roland does.
|
||||
memcpy(&buffer[4], "\x40\x01\x31\x00\x0E", 5);
|
||||
_drv->sysEx(buffer, 9);
|
||||
sysEx(buffer, 9);
|
||||
debug(2, "GS SysEx: Reverb Character is 0");
|
||||
|
||||
// Change Reverb Pre-LF to 4, which is similar to
|
||||
// what MT-32 reverb does.
|
||||
memcpy(&buffer[4], "\x40\x01\x32\x04\x09", 5);
|
||||
_drv->sysEx(buffer, 9);
|
||||
sysEx(buffer, 9);
|
||||
debug(2, "GS SysEx: Reverb Pre-LF is 4");
|
||||
|
||||
// Change Reverb Time to 106; the decay on Hall 2
|
||||
// Reverb is too fast compared to the MT-32's
|
||||
memcpy(&buffer[4], "\x40\x01\x34\x6A\x21", 5);
|
||||
_drv->sysEx(buffer, 9);
|
||||
sysEx(buffer, 9);
|
||||
debug(2, "GS SysEx: Reverb Time is 106");
|
||||
}
|
||||
|
||||
|
|
|
@ -32,10 +32,10 @@ public:
|
|||
~IMuseDriver_GMidi() override;
|
||||
|
||||
int open() override;
|
||||
void close() override;
|
||||
|
||||
// Just pass these through...
|
||||
bool isOpen() const override { return _drv ? _drv->isOpen() : false; }
|
||||
void close() override { if (_drv) _drv->close(); }
|
||||
uint32 property(int prop, uint32 param) override { return _drv ? _drv->property(prop, param) : 0; }
|
||||
void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) override { if (_drv) _drv->setTimerCallback(timerParam, timerProc); }
|
||||
uint32 getBaseTempo() override { return _drv ? _drv->getBaseTempo() : 0; }
|
||||
|
|
|
@ -24,17 +24,501 @@
|
|||
#include "base/version.h"
|
||||
#include "scumm/imuse/drivers/mt32.h"
|
||||
|
||||
|
||||
// This makes older titles play new system style, with 16 virtual channels and
|
||||
// dynamic allocation (instead of playing on fixed channels).
|
||||
//#define FORCE_NEWSTYLE_CHANNEL_ALLOCATION
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
IMuseDriver_MT32::IMuseDriver_MT32(MidiDriver::DeviceHandle dev, bool newSystem) : MidiDriver(), _newSystem(newSystem), _drv(nullptr) {
|
||||
class IMuseChannel_MT32 : public MidiChannel {
|
||||
public:
|
||||
IMuseChannel_MT32(IMuseDriver_MT32 *drv, int number);
|
||||
~IMuseChannel_MT32() override {}
|
||||
|
||||
MidiDriver *device() override { return _drv; }
|
||||
byte getNumber() override { return _number; }
|
||||
|
||||
bool allocate();
|
||||
void release() override { _allocated = false; }
|
||||
|
||||
void send(uint32 b) override { if (_drv) _drv->send((b & ~0x0F) | _number); }
|
||||
|
||||
// Regular messages
|
||||
void noteOff(byte note) override;
|
||||
void noteOn(byte note, byte velocity) override;
|
||||
void controlChange(byte control, byte value) override;
|
||||
void programChange(byte program) override;
|
||||
void pitchBend(int16 bend) override;
|
||||
|
||||
// Control Change and SCUMM specific functions
|
||||
void volume(byte value) override;
|
||||
void panPosition(byte value) override;
|
||||
void pitchBendFactor(byte value) override { _pitchBendSensitivity = value; }
|
||||
void transpose(int8 value) override { _transpose = value; }
|
||||
void detune(byte value) override { _detune = value; }
|
||||
void priority(byte value) override { _prio = value; }
|
||||
void modulationWheel(byte value) override;
|
||||
void sustain(bool value) override;
|
||||
void effectLevel(byte value) override;
|
||||
void chorusLevel(byte value) override {}
|
||||
void allNotesOff() override;
|
||||
void sysEx_customInstrument(uint32 type, const byte *instr) override;
|
||||
|
||||
void setOutput(MT32RealChan *out) { _out = out; }
|
||||
|
||||
private:
|
||||
void noteOffIntern(byte note);
|
||||
void noteOnIntern(byte note, byte velocity);
|
||||
|
||||
void sendSysexPatchData(byte offset, const byte *data, uint32 dataSize) const;
|
||||
void sendSysexTimbreData(const byte *data, uint32 dataSize) const;
|
||||
|
||||
void setNotePlaying(byte note) { _drv->setNoteFlag(_number, note); }
|
||||
void clearNotePlaying(byte note) { _drv->clearNoteFlag(_number, note); }
|
||||
bool isNotePlaying(byte note) const { return _drv->queryNoteFlag(_number, note); }
|
||||
void setNoteSustained(byte note) { _drv->setSustainFlag(_number, note); }
|
||||
void clearNoteSustained(byte note) { _drv->clearSustainFlag(_number, note); }
|
||||
bool isNoteSustained(byte note) const { return _drv->querySustainFlag(_number, note); }
|
||||
|
||||
IMuseDriver_MT32 *_drv;
|
||||
const byte _number;
|
||||
const bool _newSystem;
|
||||
bool _allocated;
|
||||
|
||||
MT32RealChan *_out;
|
||||
byte _program;
|
||||
byte _timbre;
|
||||
byte _volume;
|
||||
byte _panPos;
|
||||
byte _polyphony;
|
||||
byte _channelUsage;
|
||||
bool _exhaust;
|
||||
byte _prio;
|
||||
int8 _detune;
|
||||
int8 _transpose;
|
||||
byte _reverbSwitch;
|
||||
byte _pitchBendSensitivity;
|
||||
int16 _pitchBend;
|
||||
bool _sustain;
|
||||
|
||||
MT32ControlChan *&_idleChain;
|
||||
MT32RealChan *&_availHwChain;
|
||||
MT32ControlChan *&_activeChain;
|
||||
|
||||
const byte *_programsMapping;
|
||||
const uint32 _sysexPatchAddrBase;
|
||||
const uint32 _sysexTimbreAddrBase;
|
||||
};
|
||||
|
||||
class MT32Chan {
|
||||
public:
|
||||
MT32Chan(int number, IMuseChannel_MT32 *in) : _prev(nullptr), _next(nullptr), _in(in), _number(number) {}
|
||||
MT32Chan *_prev;
|
||||
MT32Chan *_next;
|
||||
IMuseChannel_MT32 *_in;
|
||||
byte _number;
|
||||
};
|
||||
|
||||
class MT32RealChan : public MT32Chan {
|
||||
public:
|
||||
MT32RealChan(int number, IMuseChannel_MT32 *in, MidiChannel *out) : MT32Chan(number, in), _out(out), _sysexTempAddrBase(0xC000 + (number << 4)) {
|
||||
assert(_out && _number == out->getNumber());
|
||||
if (in)
|
||||
in->setOutput(this);
|
||||
}
|
||||
MidiChannel *_out;
|
||||
const uint32 _sysexTempAddrBase;
|
||||
};
|
||||
|
||||
class MT32ControlChan : public MT32Chan{
|
||||
public:
|
||||
MT32ControlChan() : MT32Chan(0, nullptr), _note(0) {}
|
||||
byte _note;
|
||||
};
|
||||
|
||||
template <typename MT32ChanTmpl>
|
||||
void connect(MT32ChanTmpl *&chain, MT32Chan *node) {
|
||||
if (!node || node->_prev || node->_next)
|
||||
return;
|
||||
if ((node->_next = chain))
|
||||
chain->_prev = node;
|
||||
chain = static_cast<MT32ChanTmpl*>(node);
|
||||
}
|
||||
|
||||
template <typename MT32ChanTmpl>
|
||||
void disconnect(MT32ChanTmpl *&chain, MT32Chan *node) {
|
||||
if (!node || !chain)
|
||||
return;
|
||||
|
||||
const MT32Chan *ch = static_cast<MT32Chan*>(chain);
|
||||
while (ch && ch != node)
|
||||
ch = ch->_next;
|
||||
if (!ch)
|
||||
return;
|
||||
|
||||
if (node->_next)
|
||||
node->_next->_prev = node->_prev;
|
||||
|
||||
if (node->_prev)
|
||||
node->_prev->_next = node->_next;
|
||||
else
|
||||
chain = static_cast<MT32ChanTmpl*>(node->_next);
|
||||
|
||||
node->_next = node->_prev = nullptr;
|
||||
}
|
||||
|
||||
#define sendMidi(stat, par1, par2) _drv->send(((par2) << 16) | ((par1) << 8) | (stat))
|
||||
|
||||
IMuseChannel_MT32::IMuseChannel_MT32(IMuseDriver_MT32 *drv, int number) :MidiChannel(), _drv(drv), _number(number), _allocated(false), _out(nullptr), _sustain(false),
|
||||
_program(0), _timbre(0xFF), _volume(0x7F), _panPos(0x40), _pitchBend(0x2000), _polyphony(1), _channelUsage(0), _exhaust(false), _prio(0), _detune(0), _transpose(0),
|
||||
_pitchBendSensitivity(2), _reverbSwitch(1), _idleChain(_drv->_idleChain), _availHwChain(_drv->_hwChain), _activeChain(_drv->_activeChain),
|
||||
_sysexPatchAddrBase(0x14000 + (number << 3)), _sysexTimbreAddrBase(0x20000 + (number << 8)), _programsMapping(_drv->_programsMapping), _newSystem(_drv->_newSystem) {
|
||||
assert(_drv);
|
||||
}
|
||||
|
||||
bool IMuseChannel_MT32::allocate() {
|
||||
if (_allocated)
|
||||
return false;
|
||||
|
||||
if (!_newSystem) {
|
||||
byte msg[] = { (byte)(_timbre >> 6), (byte)(_timbre & 0x3F), 0x18, 0x32, 0x10, 0x00, _reverbSwitch};
|
||||
sendSysexPatchData(0, msg, sizeof(msg));
|
||||
_program = _number;
|
||||
_prio = 0x80;
|
||||
}
|
||||
|
||||
return (_allocated = true);
|
||||
}
|
||||
|
||||
void IMuseChannel_MT32::noteOff(byte note) {
|
||||
if (_newSystem) {
|
||||
if (!isNotePlaying(note))
|
||||
return;
|
||||
|
||||
clearNotePlaying(note);
|
||||
if (_sustain) {
|
||||
setNoteSustained(note);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef FORCE_NEWSTYLE_CHANNEL_ALLOCATION
|
||||
noteOffIntern(note);
|
||||
#else
|
||||
if (_newSystem)
|
||||
noteOffIntern(note);
|
||||
else if (_out || _number == 9)
|
||||
sendMidi(0x80 | _number, note, 0x40);
|
||||
#endif
|
||||
}
|
||||
|
||||
void IMuseChannel_MT32::noteOn(byte note, byte velocity) {
|
||||
if (_newSystem) {
|
||||
if (isNotePlaying(note)) {
|
||||
noteOffIntern(note);
|
||||
} else if (isNoteSustained(note)) {
|
||||
setNotePlaying(note);
|
||||
clearNoteSustained(note);
|
||||
noteOffIntern(note);
|
||||
} else {
|
||||
setNotePlaying(note);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef FORCE_NEWSTYLE_CHANNEL_ALLOCATION
|
||||
noteOnIntern(note, velocity);
|
||||
#else
|
||||
if (_newSystem) {
|
||||
noteOnIntern(note, velocity);
|
||||
} else if (_out) {
|
||||
sendMidi(0x90 | _out->_number, note, velocity);
|
||||
} else if (_number == 9) {
|
||||
sendMidi(0xB9, 0x07, _volume);
|
||||
sendMidi(0x99, note, velocity);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void IMuseChannel_MT32::controlChange(byte control, byte value) {
|
||||
switch (control) {
|
||||
case 7:
|
||||
volume(value);
|
||||
break;
|
||||
case 10:
|
||||
panPosition(value);
|
||||
break;
|
||||
case 17:
|
||||
if (_newSystem)
|
||||
_polyphony = value;
|
||||
else
|
||||
detune(value);
|
||||
break;
|
||||
case 18:
|
||||
priority(value);
|
||||
break;
|
||||
case 123:
|
||||
allNotesOff();
|
||||
break;
|
||||
default:
|
||||
warning("Unhandled Control: %d", control);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void IMuseChannel_MT32::programChange(byte program) {
|
||||
if (program > 127)
|
||||
return;
|
||||
|
||||
if (_newSystem) {
|
||||
if (_programsMapping)
|
||||
program = _programsMapping[program];
|
||||
_program = program;
|
||||
} else if (_timbre != program) {
|
||||
_timbre = program;
|
||||
byte msg[2] = { (byte)(program >> 6), (byte)(program & 0x3F) };
|
||||
sendSysexPatchData(0, msg, sizeof(msg));
|
||||
}
|
||||
|
||||
if (_out && _program < 128)
|
||||
sendMidi(0xC0 | _out->_number, _program, 0);
|
||||
}
|
||||
|
||||
void IMuseChannel_MT32::pitchBend(int16 bend) {
|
||||
_pitchBend = bend + 0x2000;
|
||||
if (_out)
|
||||
sendMidi(0xE0 | _out->_number, _pitchBend & 0x7F, (_pitchBend >> 7) & 0x7F);
|
||||
}
|
||||
|
||||
void IMuseChannel_MT32::volume(byte value) {
|
||||
_volume = value;
|
||||
if (_out)
|
||||
sendMidi(0xB0 | _out->_number, 0x07, value);
|
||||
}
|
||||
|
||||
void IMuseChannel_MT32::panPosition(byte value) {
|
||||
_panPos = value;
|
||||
if (_out)
|
||||
sendMidi(0xB0 | _out->_number, 0x0A, value);
|
||||
}
|
||||
|
||||
void IMuseChannel_MT32::modulationWheel(byte value) {
|
||||
if (_out && !_newSystem)
|
||||
sendMidi(0xB0 | _out->_number, 0x01, value);
|
||||
}
|
||||
|
||||
void IMuseChannel_MT32::sustain(bool value) {
|
||||
_sustain = value;
|
||||
|
||||
if (_newSystem) {
|
||||
// For SAMNMAX, this is fully software controlled. No control change message gets sent.
|
||||
if (_sustain)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < 128; ++i) {
|
||||
if (isNoteSustained(i))
|
||||
noteOffIntern(i);
|
||||
}
|
||||
|
||||
} else if (_out) {
|
||||
sendMidi(0xB0 | _out->_number, 0x40, value);
|
||||
}
|
||||
}
|
||||
|
||||
void IMuseChannel_MT32::effectLevel(byte value) {
|
||||
// The SAMNMAX Roland MT-32 driver ignores this (same with most of the other
|
||||
// sysex magic that the older drivers did in several places).
|
||||
if (_newSystem)
|
||||
return;
|
||||
|
||||
value = value ? 1 : 0;
|
||||
if (_reverbSwitch == value)
|
||||
return;
|
||||
|
||||
_reverbSwitch = value;
|
||||
|
||||
sendSysexPatchData(6, &_reverbSwitch, 1);
|
||||
if (_out)
|
||||
_drv->sendMT32Sysex(_out->_sysexTempAddrBase + 6, &_reverbSwitch, 1);
|
||||
}
|
||||
|
||||
void IMuseChannel_MT32::allNotesOff() {
|
||||
if (_newSystem) {
|
||||
// For SAMNMAX, this is fully software controlled. No control change message gets sent.
|
||||
if (_sustain)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < 128; ++i) {
|
||||
if (isNotePlaying(i)) {
|
||||
noteOffIntern(i);
|
||||
clearNotePlaying(i);
|
||||
} else if (isNoteSustained(i)) {
|
||||
noteOffIntern(i);
|
||||
clearNoteSustained(i);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (_out) {
|
||||
sendMidi(0xB0 | _out->_number, 0x7B, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void IMuseChannel_MT32::sysEx_customInstrument(uint32 type, const byte *instr) {
|
||||
if (*instr++ != 0x41) {
|
||||
warning("IMuseChannel_MT32::sysEx_customInstrument(): Invalid (non-Roland) sysex message received");
|
||||
return;
|
||||
}
|
||||
|
||||
byte partNo = *instr;
|
||||
uint32 addr = (instr[3] << 14) | (instr[4] << 7) | instr[5];
|
||||
|
||||
if (!(addr & 0xFFFF) || partNo < 16) {
|
||||
sendSysexTimbreData(instr + 6, 246);
|
||||
_timbre = 0xFF;
|
||||
byte msg[2] = { 0x02, _program };
|
||||
sendSysexPatchData(0, msg, sizeof(msg));
|
||||
if (_out)
|
||||
sendMidi(0xC0 | _out->_number, _program, 0);
|
||||
} else {
|
||||
_drv->sendMT32Sysex(0x22000 + (partNo << 8), instr + 6, 246);
|
||||
}
|
||||
}
|
||||
|
||||
void IMuseChannel_MT32::noteOffIntern(byte note) {
|
||||
if (_program > 127 || _activeChain == nullptr)
|
||||
return;
|
||||
|
||||
MT32Chan *node = nullptr;
|
||||
for (MT32ControlChan *i = _activeChain; i; i = static_cast<MT32ControlChan*>(i->_next)) {
|
||||
if (i->_number == _number && i->_note == note) {
|
||||
node = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
if (_out)
|
||||
sendMidi(0x80 | _out->_number, note, 0x40);
|
||||
else
|
||||
sendMidi(0x89, note, 0x40);
|
||||
|
||||
if (_newSystem)
|
||||
_exhaust = (--_channelUsage > _polyphony);
|
||||
|
||||
disconnect(_activeChain, node);
|
||||
connect(_idleChain, node);
|
||||
}
|
||||
|
||||
void IMuseChannel_MT32::noteOnIntern(byte note, byte velocity) {
|
||||
if (_program > 127 || (_newSystem && _number == 9 && note > 75))
|
||||
return;
|
||||
|
||||
MT32ControlChan *node = nullptr;
|
||||
|
||||
if (_idleChain) {
|
||||
node = _idleChain;
|
||||
disconnect(_idleChain, node);
|
||||
} else {
|
||||
MT32Chan *foundChan = nullptr;
|
||||
IMuseChannel_MT32 *best = this;
|
||||
for (MT32Chan *i = _activeChain; i; i = i->_next) {
|
||||
assert (i->_in);
|
||||
if ((best->_exhaust == i->_in->_exhaust && best->_prio >= i->_in->_prio) || (!best->_exhaust && i->_in->_exhaust)) {
|
||||
best = i->_in;
|
||||
foundChan = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundChan)
|
||||
return;
|
||||
|
||||
node = static_cast<MT32ControlChan*>(foundChan);
|
||||
|
||||
IMuseChannel_MT32 *prt = _drv->getPart(node->_number);
|
||||
if (prt && prt->_out)
|
||||
sendMidi(0x80 | prt->_out->_number, node->_note, 0x40);
|
||||
else if (node->_number == 9)
|
||||
sendMidi(0x89, node->_note, 0x40);
|
||||
|
||||
if (_newSystem && prt)
|
||||
prt->_exhaust = (--prt->_channelUsage > prt->_polyphony);
|
||||
|
||||
disconnect(_activeChain, node);
|
||||
}
|
||||
|
||||
assert(node);
|
||||
node->_in = this;
|
||||
node->_number = _number;
|
||||
node->_note = note;
|
||||
|
||||
connect(_activeChain, node);
|
||||
|
||||
if (_newSystem)
|
||||
_exhaust = (++_channelUsage > _polyphony);
|
||||
|
||||
if (_number == 9) {
|
||||
sendMidi(0xB9, 0x07, _volume);
|
||||
sendMidi(0x99, note, velocity);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_out) {
|
||||
MT32RealChan *nodeReal = _availHwChain;
|
||||
while (nodeReal && nodeReal->_next)
|
||||
nodeReal = static_cast<MT32RealChan*>(nodeReal->_next);
|
||||
|
||||
assert(nodeReal);
|
||||
assert(nodeReal->_in);
|
||||
|
||||
nodeReal->_in->_out = nullptr;
|
||||
nodeReal->_in = this;
|
||||
_out = nodeReal;
|
||||
|
||||
sendMidi(0xB0 | _out->_number, 0x7B, 0);
|
||||
sendMidi(0xB0 | _out->_number, 0x07, _volume);
|
||||
sendMidi(0xB0 | _out->_number, 0x0A, _panPos);
|
||||
sendMidi(0xC0 | _out->_number, _program, 0);
|
||||
sendMidi(0xE0 | _out->_number, _pitchBend & 0x7F, (_pitchBend >> 7) & 0x7F);
|
||||
}
|
||||
|
||||
disconnect(_availHwChain, _out);
|
||||
connect(_availHwChain, _out);
|
||||
|
||||
sendMidi(0x90 | _out->_number, note, velocity);
|
||||
}
|
||||
|
||||
void IMuseChannel_MT32::sendSysexPatchData(byte offset, const byte *data, uint32 dataSize) const {
|
||||
assert(!_newSystem);
|
||||
_drv->sendMT32Sysex(_sysexPatchAddrBase + offset, data, dataSize);
|
||||
}
|
||||
|
||||
void IMuseChannel_MT32::sendSysexTimbreData(const byte *data, uint32 dataSize) const {
|
||||
assert(!_newSystem);
|
||||
_drv->sendMT32Sysex(_sysexTimbreAddrBase, data, dataSize);
|
||||
}
|
||||
|
||||
#undef sendMidi
|
||||
|
||||
IMuseDriver_MT32::IMuseDriver_MT32(MidiDriver::DeviceHandle dev, bool newSystem) : MidiDriver(), _newSystem(newSystem), _programsMapping(nullptr), _notesPlaying(nullptr),
|
||||
_notesSustained(nullptr), _drv(nullptr), _imsParts(nullptr), _hwOutputChan(nullptr), _controlChan(nullptr), _idleChain(nullptr), _hwChain(nullptr), _activeChain(nullptr),
|
||||
#ifdef FORCE_NEWSTYLE_CHANNEL_ALLOCATION
|
||||
_numChannels(16) {
|
||||
#else
|
||||
_numChannels(newSystem ? 16 : 9) {
|
||||
#endif
|
||||
_drv = MidiDriver::createMidi(dev);
|
||||
assert(_drv);
|
||||
|
||||
if (!_newSystem)
|
||||
_drv->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
|
||||
_drv->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
|
||||
|
||||
if (newSystem)
|
||||
_programsMapping = MidiDriver::_gmToMt32;
|
||||
}
|
||||
|
||||
IMuseDriver_MT32::~IMuseDriver_MT32() {
|
||||
close();
|
||||
delete _drv;
|
||||
}
|
||||
|
||||
|
@ -43,44 +527,84 @@ int IMuseDriver_MT32::open() {
|
|||
return MERR_CANNOT_CONNECT;
|
||||
|
||||
int res = _drv->open();
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
initDevice();
|
||||
createChannels();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void IMuseDriver_MT32::close() {
|
||||
if (isOpen()) {
|
||||
// Reset the MT-32
|
||||
sendMT32Sysex(0x1FC000, 0, 0);
|
||||
g_system->delayMillis(250);
|
||||
|
||||
if (_drv)
|
||||
_drv->close();
|
||||
}
|
||||
|
||||
releaseChannels();
|
||||
}
|
||||
|
||||
MidiChannel *IMuseDriver_MT32::allocateChannel() {
|
||||
if (!_newSystem)
|
||||
return _drv->allocateChannel();
|
||||
if (!isOpen())
|
||||
return nullptr;
|
||||
|
||||
for (int i = 0; i < _numChannels; ++i) {
|
||||
IMuseChannel_MT32 *ch = _imsParts[i];
|
||||
if (ch && ch->getNumber() != 9 && ch->allocate())
|
||||
return ch;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MidiChannel *IMuseDriver_MT32::getPercussionChannel() {
|
||||
if (!_newSystem)
|
||||
return _drv->getPercussionChannel();
|
||||
if (!isOpen())
|
||||
return nullptr;
|
||||
|
||||
return nullptr;
|
||||
IMuseChannel_MT32 *ch = getPart(9);
|
||||
if (ch) {
|
||||
ch->release();
|
||||
ch->allocate();
|
||||
}
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
void IMuseDriver_MT32::initDevice() {
|
||||
byte buffer[52];
|
||||
|
||||
// Reset the MT-32
|
||||
_drv->sysEx((const byte *) "\x41\x10\x16\x12\x7f\x00\x00\x01\x00", 9);
|
||||
sendMT32Sysex(0x1FC000, 0, 0);
|
||||
g_system->delayMillis(250);
|
||||
|
||||
// Setup master tune, reverb mode, reverb time, reverb level,
|
||||
// channel mapping, partial reserve and master volume
|
||||
_drv->sysEx((const byte *) "\x41\x10\x16\x12\x10\x00\x00\x40\x00\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x64\x77", 31);
|
||||
static const char initSysex1[] = "\x40\x00\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x64";
|
||||
sendMT32Sysex(0x40000, (const byte*)initSysex1, sizeof(initSysex1) - 1);
|
||||
g_system->delayMillis(250);
|
||||
|
||||
// Map percussion to notes 24 - 34 without reverb
|
||||
if (!_newSystem) {
|
||||
_drv->sysEx((const byte *) "\x41\x10\x16\x12\x03\x01\x10\x40\x64\x07\x00\x4a\x64\x06\x00\x41\x64\x07\x00\x4b\x64\x08\x00\x45\x64\x06\x00\x44\x64\x0b\x00\x51\x64\x05\x00\x43\x64\x08\x00\x50\x64\x07\x00\x42\x64\x03\x00\x4c\x64\x07\x00\x44", 52);
|
||||
// Map percussion to notes 24 - 34 without reverb. It still happens in the DOTT driver, but not in the SAMNMAX one.
|
||||
static const char initSysex2[] = "\x40\x64\x07\x00\x4a\x64\x06\x00\x41\x64\x07\x00\x4b\x64\x08\x00\x45\x64\x06\x00\x44\x64\x0b\x00\x51\x64\x05\x00\x43\x64\x08\x00\x50\x64\x07\x00\x42\x64\x03\x00\x4c\x64\x07\x00";
|
||||
sendMT32Sysex(0xC090, (const byte*)initSysex2, sizeof(initSysex2) - 1);
|
||||
g_system->delayMillis(250);
|
||||
}
|
||||
|
||||
const byte pbRange = 0x10;
|
||||
for (int i = 0; i < 128; ++i) {
|
||||
sendMT32Sysex(0x014004 + (i << 3), &pbRange, 1);
|
||||
g_system->delayMillis(5);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
send(0x0000C0 | i);
|
||||
send(0x0040B0 | i);
|
||||
send(0x007BB0 | i);
|
||||
}
|
||||
|
||||
// Compute version string (truncated to 20 chars max.)
|
||||
Common::String infoStr = "ScummVM ";
|
||||
infoStr += gScummVMVersion;
|
||||
|
@ -89,6 +613,7 @@ void IMuseDriver_MT32::initDevice() {
|
|||
len = 20;
|
||||
|
||||
// Display a welcome message on MT-32 displays.
|
||||
byte buffer[28];
|
||||
memcpy(&buffer[0], "\x41\x10\x16\x12\x20\x00\x00", 7);
|
||||
memcpy(&buffer[7], " ", 20);
|
||||
memcpy(buffer + 7 + (20 - len) / 2, infoStr.c_str(), len);
|
||||
|
@ -96,8 +621,120 @@ void IMuseDriver_MT32::initDevice() {
|
|||
for (int i = 4; i < 27; ++i)
|
||||
checksum -= buffer[i];
|
||||
buffer[27] = checksum & 0x7F;
|
||||
_drv->sysEx(buffer, 28);
|
||||
sysEx(buffer, 28);
|
||||
g_system->delayMillis(1000);
|
||||
}
|
||||
|
||||
void IMuseDriver_MT32::createChannels() {
|
||||
releaseChannels();
|
||||
|
||||
_imsParts = new IMuseChannel_MT32*[_numChannels];
|
||||
_hwOutputChan = new MT32RealChan*[8];
|
||||
_controlChan = new MT32ControlChan*[9];
|
||||
|
||||
assert(_imsParts);
|
||||
assert(_hwOutputChan);
|
||||
assert(_controlChan);
|
||||
|
||||
for (int i = 0; i < _numChannels; ++i)
|
||||
_imsParts[i] = new IMuseChannel_MT32(this, (i + 1) & 0x0F);
|
||||
|
||||
MidiChannel *driverChannels[16];
|
||||
memset(driverChannels, 0, sizeof(driverChannels));
|
||||
for (int i = 0; i < ARRAYSIZE(driverChannels); ++i)
|
||||
driverChannels[i] = _drv->allocateChannel();
|
||||
|
||||
for (int i = 1; i < 9; ++i) {
|
||||
MidiChannel *m = nullptr;
|
||||
for (int ii = 0; m == nullptr && ii < ARRAYSIZE(driverChannels); ++ii) {
|
||||
if (driverChannels[ii] && driverChannels[ii]->getNumber() == i)
|
||||
SWAP(m, driverChannels[ii]);
|
||||
}
|
||||
if (!m)
|
||||
error("IMuseDriver_MT32::createChannels(): Failed to create channels.");
|
||||
_hwOutputChan[i - 1] = new MT32RealChan(i, getPart(i), m);
|
||||
connect(_hwChain, _hwOutputChan[i - 1]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ARRAYSIZE(driverChannels); ++i) {
|
||||
if (driverChannels[i])
|
||||
driverChannels[i]->release();
|
||||
}
|
||||
|
||||
for (int i = 0; i < 9; ++i) {
|
||||
_controlChan[i] = new MT32ControlChan();
|
||||
connect(_idleChain, _controlChan[i]);
|
||||
}
|
||||
|
||||
if (_newSystem) {
|
||||
_notesPlaying = new uint16[128]();
|
||||
_notesSustained = new uint16[128]();
|
||||
}
|
||||
}
|
||||
|
||||
void IMuseDriver_MT32::releaseChannels() {
|
||||
if (_imsParts) {
|
||||
for (int i = 0; i < _numChannels; ++i)
|
||||
delete _imsParts[i];
|
||||
delete[] _imsParts;
|
||||
_imsParts = nullptr;
|
||||
}
|
||||
|
||||
if (_hwOutputChan) {
|
||||
for (int i = 0; i < 8; ++i)
|
||||
delete _hwOutputChan[i];
|
||||
delete[] _hwOutputChan;
|
||||
_hwOutputChan = nullptr;
|
||||
}
|
||||
|
||||
if (_controlChan) {
|
||||
for (int i = 0; i < 9; ++i)
|
||||
delete _controlChan[i];
|
||||
delete[] _controlChan;
|
||||
_controlChan = nullptr;
|
||||
}
|
||||
|
||||
delete[] _notesPlaying;
|
||||
_notesPlaying = nullptr;
|
||||
delete[] _notesSustained;
|
||||
_notesSustained = nullptr;
|
||||
}
|
||||
|
||||
IMuseChannel_MT32 *IMuseDriver_MT32::getPart(int number) const {
|
||||
for (int i = 0; i < _numChannels; ++i)
|
||||
if (_imsParts[i]->getNumber() == number)
|
||||
return _imsParts[i];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void IMuseDriver_MT32::sendMT32Sysex(uint32 addr, const byte *data, uint32 dataSize) {
|
||||
static const byte header[] = { 0x41, 0x10, 0x16, 0x12 };
|
||||
|
||||
byte *msg = new byte[sizeof(header) + 4 + dataSize];
|
||||
memcpy(msg, header, sizeof(header));
|
||||
byte *dst = msg + sizeof(header);
|
||||
const byte *src = dst;
|
||||
|
||||
*dst++ = (addr >> 14) & 0x7F;
|
||||
*dst++ = (addr >> 7) & 0x7F;
|
||||
*dst++ = addr & 0x7F;
|
||||
|
||||
while (dataSize) {
|
||||
*dst++ = *data++;
|
||||
--dataSize;
|
||||
}
|
||||
|
||||
uint8 checkSum = 0;
|
||||
while (src < dst)
|
||||
checkSum -= *src++;
|
||||
|
||||
*dst++ = checkSum & 0x7F;
|
||||
|
||||
sysEx(msg, dst - msg);
|
||||
|
||||
delete[] msg;
|
||||
}
|
||||
|
||||
#undef FORCE_NEWSTYLE_CHANNEL_ALLOCATION
|
||||
|
||||
} // End of namespace Scumm
|
||||
|
|
|
@ -26,22 +26,26 @@
|
|||
|
||||
namespace Scumm {
|
||||
|
||||
class IMuseChannel_MT32;
|
||||
class MT32RealChan;
|
||||
class MT32ControlChan;
|
||||
|
||||
class IMuseDriver_MT32 : public MidiDriver {
|
||||
friend class IMuseChannel_MT32;
|
||||
public:
|
||||
IMuseDriver_MT32(MidiDriver::DeviceHandle dev, bool newSystem);
|
||||
~IMuseDriver_MT32() override;
|
||||
|
||||
int open() override;
|
||||
void close() override;
|
||||
|
||||
// Just pass these through...
|
||||
bool isOpen() const override { return _drv ? _drv->isOpen() : false; }
|
||||
void close() override { if (_drv) _drv->close(); }
|
||||
uint32 property(int prop, uint32 param) override { return _drv ? _drv->property(prop, param) : 0; }
|
||||
void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) override { if (_drv) _drv->setTimerCallback(timerParam, timerProc); }
|
||||
uint32 getBaseTempo() override { return _drv ? _drv->getBaseTempo() : 0; }
|
||||
void send(uint32 b) override { if (_drv) _drv->send(b); };
|
||||
void sysEx(const byte *msg, uint16 length) override { if (_drv) _drv->sysEx(msg, length); }
|
||||
void setPitchBendRange(byte channel, uint range) override { if (_drv) _drv->setPitchBendRange(channel, range); }
|
||||
|
||||
// Channel allocation functions
|
||||
MidiChannel *allocateChannel() override;
|
||||
|
@ -49,9 +53,37 @@ public:
|
|||
|
||||
private:
|
||||
void initDevice();
|
||||
void createChannels();
|
||||
void releaseChannels();
|
||||
|
||||
IMuseChannel_MT32 *getPart(int number) const;
|
||||
|
||||
// Convenience function that allows to send the sysex message with the exact same arguments as they are used in the original drivers.
|
||||
void sendMT32Sysex(uint32 addr, const byte *data, uint32 dataSize);
|
||||
|
||||
void setNoteFlag(byte chan, byte note) { if (_notesPlaying && chan < 16 && note < 128) _notesPlaying[note] |= (1 << chan); }
|
||||
void clearNoteFlag(byte chan, byte note) { if (_notesPlaying && chan < 16 && note < 128) _notesPlaying[note] &= ~(1 << chan); }
|
||||
bool queryNoteFlag(byte chan, byte note) const { return (_notesPlaying && chan < 16 && note < 128) ? _notesPlaying[note] & (1 << chan) : false; }
|
||||
void setSustainFlag(byte chan, byte note) { if (_notesSustained && chan < 16 && note < 128) _notesSustained[note] |= (1 << chan); }
|
||||
void clearSustainFlag(byte chan, byte note) { if (_notesSustained && chan < 16 && note < 128) _notesSustained[note] &= ~(1 << chan); }
|
||||
bool querySustainFlag(byte chan, byte note) const { return (_notesSustained && chan < 16 && note < 128) ? _notesSustained[note] & (1 << chan) : false; }
|
||||
|
||||
MidiDriver *_drv;
|
||||
const bool _newSystem;
|
||||
const byte _numChannels;
|
||||
|
||||
IMuseChannel_MT32 **_imsParts;
|
||||
MT32RealChan **_hwOutputChan;
|
||||
MT32ControlChan **_controlChan;
|
||||
|
||||
MT32ControlChan *_idleChain;
|
||||
MT32RealChan *_hwChain;
|
||||
MT32ControlChan *_activeChain;
|
||||
|
||||
const byte *_programsMapping;
|
||||
|
||||
uint16 *_notesPlaying;
|
||||
uint16 *_notesSustained;
|
||||
};
|
||||
|
||||
} // End of namespace Scumm
|
||||
|
|
|
@ -46,6 +46,7 @@ namespace Scumm {
|
|||
IMuseInternal::IMuseInternal(ScummEngine *vm, MidiDriverFlags sndType, uint32 initFlags) :
|
||||
_native_mt32((initFlags & kFlagNativeMT32) || (initFlags & kFlagRolandGS)), // GS Mode emulates MT-32 on a GS device, so _native_mt32 should always be true
|
||||
_enable_gs(initFlags & kFlagRolandGS),
|
||||
_newSystem(initFlags & kFlagNewSystem),
|
||||
_midi_adlib(nullptr),
|
||||
_midi_native(nullptr),
|
||||
_sysex(nullptr),
|
||||
|
@ -90,12 +91,6 @@ IMuseInternal::~IMuseInternal() {
|
|||
}
|
||||
|
||||
if (_midi_native) {
|
||||
if (_native_mt32) {
|
||||
// Reset the MT-32
|
||||
_midi_native->sysEx((const byte *) "\x41\x10\x16\x12\x7f\x00\x00\x01\x00", 9);
|
||||
_system->delayMillis(250);
|
||||
}
|
||||
|
||||
_midi_native->close();
|
||||
delete _midi_native;
|
||||
_midi_native = nullptr;
|
||||
|
|
|
@ -56,8 +56,9 @@ public:
|
|||
};
|
||||
|
||||
enum {
|
||||
kFlagNativeMT32 = 1 << 0,
|
||||
kFlagRolandGS = 1 << 1
|
||||
kFlagNewSystem = 1 << 0,
|
||||
kFlagNativeMT32 = 1 << 1,
|
||||
kFlagRolandGS = 1 << 2
|
||||
};
|
||||
|
||||
public:
|
||||
|
|
|
@ -323,6 +323,7 @@ struct Part : public Common::Serializable {
|
|||
byte _vol, _vol_eff;
|
||||
int8 _detune, _detune_eff;
|
||||
int8 _pan, _pan_eff;
|
||||
byte _polyphony;
|
||||
bool _on;
|
||||
byte _modwheel;
|
||||
bool _pedal;
|
||||
|
@ -367,7 +368,7 @@ struct Part : public Common::Serializable {
|
|||
void set_pri(int8 pri);
|
||||
void set_pan(int8 pan);
|
||||
|
||||
void set_sm17(int8 val);
|
||||
void set_polyphony(byte val);
|
||||
void set_onoff(bool on);
|
||||
void fix_after_load();
|
||||
|
||||
|
@ -384,6 +385,7 @@ private:
|
|||
void sendDetune();
|
||||
void sendPanPosition(uint8 value);
|
||||
void sendEffectLevel(uint8 value);
|
||||
void sendPolyphony();
|
||||
};
|
||||
|
||||
|
||||
|
@ -409,6 +411,7 @@ class IMuseInternal : public IMuse {
|
|||
protected:
|
||||
const bool _native_mt32;
|
||||
const bool _enable_gs;
|
||||
const bool _newSystem;
|
||||
const MidiDriverFlags _soundType;
|
||||
MidiDriver *_midi_adlib;
|
||||
MidiDriver *_midi_native;
|
||||
|
@ -458,6 +461,15 @@ protected:
|
|||
CommandQueue _cmd_queue[64];
|
||||
DeferredCommand _deferredCommands[4];
|
||||
|
||||
// These are basically static vars in the original drivers
|
||||
struct RhyState {
|
||||
RhyState() : RhyState(127, 1, 0) {}
|
||||
RhyState(byte volume, byte polyphony, byte priority) : vol(volume), poly(polyphony), prio(priority) {}
|
||||
byte vol;
|
||||
byte poly;
|
||||
byte prio;
|
||||
} _rhyState;
|
||||
|
||||
protected:
|
||||
IMuseInternal(ScummEngine *vm, MidiDriverFlags sndType, uint32 initFlags);
|
||||
~IMuseInternal() override;
|
||||
|
|
|
@ -51,6 +51,7 @@ Part::Part() {
|
|||
_detune_eff = 0;
|
||||
_pan = 0;
|
||||
_pan_eff = 0;
|
||||
_polyphony = 0;
|
||||
_on = false;
|
||||
_modwheel = 0;
|
||||
_pedal = false;
|
||||
|
@ -108,20 +109,15 @@ void Part::set_detune(int8 detune) {
|
|||
// Sam&Max does not have detune, so we just ignore this here. We still get
|
||||
// this called, since Sam&Max uses the same controller for a different
|
||||
// purpose.
|
||||
if (_se->_game_id == GID_SAMNMAX) {
|
||||
#if 0
|
||||
if (_mc) {
|
||||
_mc->controlChange(17, detune + 0x40);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
_detune_eff = clamp((_detune = detune) + _player->getDetune(), -128, 127);
|
||||
// Some drivers handle the transpose and the detune in pitchBend()...
|
||||
if (_player->isAdLibOrFMTowns())
|
||||
sendDetune();
|
||||
else
|
||||
sendPitchBend();
|
||||
}
|
||||
if (_se->_newSystem)
|
||||
return;
|
||||
|
||||
_detune_eff = clamp((_detune = detune) + _player->getDetune(), -128, 127);
|
||||
// Some drivers handle the transpose and the detune in pitchBend()...
|
||||
if (_player->isAdLibOrFMTowns())
|
||||
sendDetune();
|
||||
else
|
||||
sendPitchBend();
|
||||
}
|
||||
|
||||
void Part::pitchBend(int16 value) {
|
||||
|
@ -146,8 +142,12 @@ void Part::set_pan(int8 pan) {
|
|||
sendPanPosition(_pan_eff + 0x40);
|
||||
}
|
||||
|
||||
void Part::set_sm17(int8 val) {
|
||||
|
||||
void Part::set_polyphony(byte val) {
|
||||
if (!_se->_newSystem)
|
||||
return;
|
||||
_polyphony = val;
|
||||
if (_mc)
|
||||
_mc->controlChange(17, val);
|
||||
}
|
||||
|
||||
void Part::set_transpose(int8 transpose, int8 clipRangeLow, int8 clipRangeHi) {
|
||||
|
@ -255,19 +255,20 @@ void Part::noteOn(byte note, byte velocity) {
|
|||
if (!mc)
|
||||
return;
|
||||
|
||||
// FIXME: The following is evil, EVIL!!! Either prev_vol_eff is
|
||||
// actually meant to be a member of the Part class (i.e. each
|
||||
// instance of Part keeps a separate copy of it); or it really
|
||||
// is supposed to be shared by all Part instances -- but then it
|
||||
// should be implemented as a class static var. As it is, using
|
||||
// a function level static var in most cases is arcane and evil.
|
||||
static byte prev_vol_eff = 128;
|
||||
if (_vol_eff != prev_vol_eff) {
|
||||
if (_vol_eff != _se->_rhyState.vol)
|
||||
mc->volume(_vol_eff);
|
||||
prev_vol_eff = _vol_eff;
|
||||
}
|
||||
if ((note < 35) && (!_player->_se->isNativeMT32()))
|
||||
|
||||
if (_se->_newSystem) {
|
||||
if (_pri_eff != _se->_rhyState.prio)
|
||||
mc->priority(_pri_eff);
|
||||
if (_polyphony != _se->_rhyState.poly)
|
||||
mc->controlChange(17, _polyphony);
|
||||
|
||||
} else if ((note < 35) && (!_player->_se->isNativeMT32())) {
|
||||
note = Instrument::_gmRhythmMap[note];
|
||||
}
|
||||
|
||||
_se->_rhyState = IMuseInternal::RhyState(_vol_eff, _polyphony, _pri_eff);
|
||||
|
||||
mc->noteOn(note, velocity);
|
||||
}
|
||||
|
@ -358,6 +359,7 @@ void Part::sendAll() {
|
|||
_mc->sustain(_pedal);
|
||||
_mc->modulationWheel(_modwheel);
|
||||
sendPanPosition(_pan_eff + 0x40);
|
||||
sendPolyphony();
|
||||
|
||||
if (_instrument.isValid())
|
||||
_instrument.send(_mc);
|
||||
|
@ -483,29 +485,13 @@ void Part::sendPanPosition(uint8 value) {
|
|||
void Part::sendEffectLevel(uint8 value) {
|
||||
if (!_mc)
|
||||
return;
|
||||
_mc->effectLevel(value);
|
||||
}
|
||||
|
||||
// As described in bug report #1849 "MI2: Minor problems in native MT-32 mode"
|
||||
// for the MT-32 one has to use a sysEx event to change the effect level (rather
|
||||
// the reverb setting).
|
||||
if (_player->_se->isNativeMT32()) {
|
||||
if (value != 127 && value != 0) {
|
||||
warning("Trying to use unsupported effect level value %d in native MT-32 mode.", value);
|
||||
|
||||
if (value >= 64)
|
||||
value = 127;
|
||||
else
|
||||
value = 0;
|
||||
}
|
||||
|
||||
byte message[9];
|
||||
memcpy(message, "\x41\x00\x16\x12\x00\x00\x06\x00\x00", 9);
|
||||
message[1] = _mc->getNumber();
|
||||
message[7] = (value == 127) ? 1 : 0;
|
||||
message[8] = 128 - (6 + message[7]);
|
||||
_player->getMidiDriver()->sysEx(message, 9);
|
||||
} else {
|
||||
_mc->effectLevel(value);
|
||||
}
|
||||
void Part::sendPolyphony() {
|
||||
if (!_mc || !_se->_newSystem)
|
||||
return;
|
||||
_mc->controlChange(17, _polyphony);
|
||||
}
|
||||
|
||||
} // End of namespace Scumm
|
||||
|
|
|
@ -223,7 +223,7 @@ int Player::start_seq_sound(int sound, bool reset_vars) {
|
|||
}
|
||||
|
||||
void Player::loadStartParameters(int sound) {
|
||||
_priority = (_se->_game_id != GID_SAMNMAX) ? 0x80 : 0;
|
||||
_priority = _se->_newSystem ? 0 : 0x80;
|
||||
_volume = 0x7F;
|
||||
_vol_chan = 0xFFFF;
|
||||
_vol_eff = (_se->get_channel_volume(0xFFFF) << 7) >> 7;
|
||||
|
@ -319,13 +319,13 @@ void Player::send(uint32 b) {
|
|||
part->pitchBendFactor(param2);
|
||||
break;
|
||||
case 17: // GP Slider 2
|
||||
if (_se->_game_id == GID_SAMNMAX)
|
||||
part->set_sm17(param2);
|
||||
if (_se->_newSystem)
|
||||
part->set_polyphony(param2);
|
||||
else
|
||||
part->set_detune(param2 - 0x40);
|
||||
break;
|
||||
case 18: // GP Slider 3
|
||||
if (_se->_game_id != GID_SAMNMAX)
|
||||
if (!_se->_newSystem)
|
||||
param2 -= 0x40;
|
||||
part->set_pri(param2);
|
||||
_se->reallocateMidiChannels(_midi);
|
||||
|
|
|
@ -414,8 +414,8 @@ void Instrument_Program::send(MidiChannel *mc) {
|
|||
return;
|
||||
|
||||
byte program = _program;
|
||||
if (_nativeMT32Device != _soundTypeMT32)
|
||||
program = _nativeMT32Device ? MidiDriver::_gmToMt32[program] : MidiDriver::_mt32ToGm[program];
|
||||
if (!_nativeMT32Device && _soundTypeMT32)
|
||||
program = MidiDriver::_mt32ToGm[program];
|
||||
if (program < 128)
|
||||
mc->programChange(program);
|
||||
}
|
||||
|
@ -483,25 +483,7 @@ void Instrument_Roland::saveLoadWithSerializer(Common::Serializer &s) {
|
|||
|
||||
void Instrument_Roland::send(MidiChannel *mc) {
|
||||
if (_nativeMT32Device) {
|
||||
if (mc->getNumber() > 8)
|
||||
return;
|
||||
_instrument.device_id = mc->getNumber();
|
||||
|
||||
// Remap instrument to appropriate address space.
|
||||
int address = 0x008000;
|
||||
_instrument.address[0] = (address >> 14) & 0x7F;
|
||||
_instrument.address[1] = (address >> 7) & 0x7F;
|
||||
_instrument.address[2] = (address ) & 0x7F;
|
||||
|
||||
// Recompute the checksum.
|
||||
byte checksum = 0;
|
||||
byte *ptr = (byte *)&_instrument + 4;
|
||||
int i;
|
||||
for (i = 4; i < (int)sizeof(_instrument) - 1; ++i)
|
||||
checksum -= *ptr++;
|
||||
_instrument.checksum = checksum & 0x7F;
|
||||
|
||||
mc->device()->sysEx((byte *)&_instrument, sizeof(_instrument));
|
||||
mc->sysEx_customInstrument(0, (byte *)&_instrument);
|
||||
} else {
|
||||
// Convert to a GM program change.
|
||||
byte program = getEquivalentGM();
|
||||
|
|
|
@ -1970,9 +1970,6 @@ void ScummEngine::setupMusic(int midi, const Common::String &macInstrumentFile)
|
|||
_sound->_musicType = MDT_MIDI;
|
||||
}
|
||||
|
||||
// DOTT + SAM use General MIDI, so they shouldn't use GS settings
|
||||
bool enable_gs = (_game.id == GID_TENTACLE || _game.id == GID_SAMNMAX) ? false : ConfMan.getBool("enable_gs");
|
||||
|
||||
/* Bind the mixer to the system => mixer will be invoked
|
||||
* automatically when samples need to be generated */
|
||||
if (!_mixer->isReady()) {
|
||||
|
@ -2046,11 +2043,9 @@ void ScummEngine::setupMusic(int midi, const Common::String &macInstrumentFile)
|
|||
bool multi_midi = ConfMan.getBool("multi_midi") && _sound->_musicType != MDT_NONE && _sound->_musicType != MDT_PCSPK && (midi & MDT_ADLIB);
|
||||
bool useOnlyNative = false;
|
||||
|
||||
uint32 imsFlags = 0;
|
||||
if (_native_mt32)
|
||||
imsFlags |= IMuse::kFlagNativeMT32;
|
||||
if (enable_gs && MidiDriver::getMusicType(dev) != MT_MT32)
|
||||
imsFlags |= IMuse::kFlagRolandGS;
|
||||
// DOTT + SAM use General MIDI, so they shouldn't use GS settings
|
||||
bool enable_gs = (_game.id == GID_TENTACLE || _game.id == GID_SAMNMAX) ? false : (ConfMan.getBool("enable_gs") && MidiDriver::getMusicType(dev) != MT_MT32);
|
||||
bool newSystem = (_game.id == GID_SAMNMAX);
|
||||
|
||||
if (isMacM68kIMuse()) {
|
||||
// We setup this driver as native MIDI driver to avoid playback
|
||||
|
@ -2066,9 +2061,9 @@ void ScummEngine::setupMusic(int midi, const Common::String &macInstrumentFile)
|
|||
useOnlyNative = true;
|
||||
} else if (_sound->_musicType != MDT_ADLIB && _sound->_musicType != MDT_TOWNS && _sound->_musicType != MDT_PCSPK) {
|
||||
if (_native_mt32)
|
||||
nativeMidiDriver = new IMuseDriver_MT32(dev, _game.id == GID_SAMNMAX);
|
||||
nativeMidiDriver = new IMuseDriver_MT32(dev, newSystem);
|
||||
else
|
||||
nativeMidiDriver = new IMuseDriver_GMidi(dev, imsFlags & IMuse::kFlagRolandGS, _game.id == GID_SAMNMAX);
|
||||
nativeMidiDriver = new IMuseDriver_GMidi(dev, enable_gs, newSystem);
|
||||
}
|
||||
|
||||
if (!useOnlyNative) {
|
||||
|
@ -2084,6 +2079,12 @@ void ScummEngine::setupMusic(int midi, const Common::String &macInstrumentFile)
|
|||
}
|
||||
}
|
||||
|
||||
uint32 imsFlags = newSystem ? IMuse::kFlagNewSystem : 0;
|
||||
if (_native_mt32)
|
||||
imsFlags |= IMuse::kFlagNativeMT32;
|
||||
if (enable_gs)
|
||||
imsFlags |= IMuse::kFlagRolandGS;
|
||||
|
||||
_imuse = IMuse::create(this, nativeMidiDriver, adlibMidiDriver, isMacM68kIMuse() ? MDT_MACINTOSH : _sound->_musicType, imsFlags);
|
||||
|
||||
if (_game.platform == Common::kPlatformFMTowns) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue