AUDIO/MIDI: Add dual OPL2 support to Miles AdLib driver
This adds support for the dual OPL2 chip configuration to the Miles AdLib MIDI driver.
This commit is contained in:
parent
f900cbecdd
commit
dfb8db2ae2
1 changed files with 91 additions and 20 deletions
|
@ -116,6 +116,18 @@ uint16 milesAdLibVolumeSensitivityTable[] = {
|
||||||
82, 85, 88, 91, 94, 97, 100, 103, 106, 109, 112, 115, 118, 121, 124, 127
|
82, 85, 88, 91, 94, 97, 100, 103, 106, 109, 112, 115, 118, 121, 124, 127
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// MIDI panning to register volume table for dual OPL2
|
||||||
|
// hardcoded, dumped from ADLIB.MDI
|
||||||
|
uint8 milesAdLibPanningVolumeLookUpTable[] = {
|
||||||
|
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
|
||||||
|
32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
|
||||||
|
64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94,
|
||||||
|
96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 127,
|
||||||
|
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
|
||||||
|
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
|
||||||
|
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
|
||||||
|
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127
|
||||||
|
};
|
||||||
|
|
||||||
class MidiDriver_Miles_AdLib : public MidiDriver_Multisource {
|
class MidiDriver_Miles_AdLib : public MidiDriver_Multisource {
|
||||||
public:
|
public:
|
||||||
|
@ -251,6 +263,7 @@ private:
|
||||||
void resetAdLibFMVoiceChannelRegisters(byte baseRegister, byte value);
|
void resetAdLibFMVoiceChannelRegisters(byte baseRegister, byte value);
|
||||||
|
|
||||||
void setRegister(int reg, int value);
|
void setRegister(int reg, int value);
|
||||||
|
void setRegisterStereo(uint8 reg, uint8 valueLeft, uint8 valueRight);
|
||||||
|
|
||||||
int16 searchFreeVirtualFmVoiceChannel();
|
int16 searchFreeVirtualFmVoiceChannel();
|
||||||
int16 searchFreePhysicalFmVoiceChannel();
|
int16 searchFreePhysicalFmVoiceChannel();
|
||||||
|
@ -308,14 +321,14 @@ int MidiDriver_Miles_AdLib::open() {
|
||||||
// Try to create OPL3 first
|
// Try to create OPL3 first
|
||||||
_opl = OPL::Config::create(OPL::Config::kOpl3);
|
_opl = OPL::Config::create(OPL::Config::kOpl3);
|
||||||
}
|
}
|
||||||
// TODO Add support for dual OPL2
|
if (!_opl) {
|
||||||
|
// not created yet, downgrade to dual OPL2
|
||||||
|
_oplType = OPL::Config::kDualOpl2;
|
||||||
|
_opl = OPL::Config::create(OPL::Config::kDualOpl2);
|
||||||
|
}
|
||||||
if (!_opl) {
|
if (!_opl) {
|
||||||
// not created yet, downgrade to OPL2
|
// not created yet, downgrade to OPL2
|
||||||
_oplType = OPL::Config::kOpl2;
|
_oplType = OPL::Config::kOpl2;
|
||||||
_modeVirtualFmVoicesCount = 16;
|
|
||||||
_modePhysicalFmVoicesCount = 9;
|
|
||||||
_modeStereo = false;
|
|
||||||
|
|
||||||
_opl = OPL::Config::create(OPL::Config::kOpl2);
|
_opl = OPL::Config::create(OPL::Config::kOpl2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,6 +337,12 @@ int MidiDriver_Miles_AdLib::open() {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_oplType != OPL::Config::kOpl3) {
|
||||||
|
_modeVirtualFmVoicesCount = 16;
|
||||||
|
_modePhysicalFmVoicesCount = 9;
|
||||||
|
_modeStereo = false;
|
||||||
|
}
|
||||||
|
|
||||||
_opl->init();
|
_opl->init();
|
||||||
|
|
||||||
_isOpen = true;
|
_isOpen = true;
|
||||||
|
@ -778,6 +797,8 @@ void MidiDriver_Miles_AdLib::updatePhysicalFmVoice(byte virtualFmVoice, bool key
|
||||||
uint16 channelReg = milesAdLibChannelRegister[physicalFmVoice];
|
uint16 channelReg = milesAdLibChannelRegister[physicalFmVoice];
|
||||||
|
|
||||||
uint16 compositeVolume = 0;
|
uint16 compositeVolume = 0;
|
||||||
|
uint8 leftVolume = 0;
|
||||||
|
uint8 rightVolume = 0;
|
||||||
|
|
||||||
if (registerUpdateFlags & kMilesAdLibUpdateFlags_Reg_40) {
|
if (registerUpdateFlags & kMilesAdLibUpdateFlags_Reg_40) {
|
||||||
// Calculate new volume
|
// Calculate new volume
|
||||||
|
@ -807,6 +828,19 @@ void MidiDriver_Miles_AdLib::updatePhysicalFmVoice(byte virtualFmVoice, bool key
|
||||||
}
|
}
|
||||||
// Source volume scaling might clip volume, so reduce to maximum.
|
// Source volume scaling might clip volume, so reduce to maximum.
|
||||||
compositeVolume = MIN(compositeVolume, (uint16)0x7F);
|
compositeVolume = MIN(compositeVolume, (uint16)0x7F);
|
||||||
|
|
||||||
|
if (_oplType == OPL::Config::kDualOpl2) {
|
||||||
|
// For dual OPL2, Miles pans the notes by playing the same note on
|
||||||
|
// the left and right OPL2 chips at different volume levels.
|
||||||
|
// Calculate the volume for each chip based on the panning value.
|
||||||
|
leftVolume = (milesAdLibPanningVolumeLookUpTable[_midiChannels[midiChannel].currentPanning] * compositeVolume) >> 7;
|
||||||
|
if (leftVolume)
|
||||||
|
leftVolume++; // round up in case result wasn't 0
|
||||||
|
uint8 invertedPanning = 0 - (_midiChannels[midiChannel].currentPanning - 127);
|
||||||
|
rightVolume = (milesAdLibPanningVolumeLookUpTable[invertedPanning] * compositeVolume) >> 7;
|
||||||
|
if (rightVolume)
|
||||||
|
rightVolume++; // round up in case result wasn't 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (registerUpdateFlags & kMilesAdLibUpdateFlags_Reg_20) {
|
if (registerUpdateFlags & kMilesAdLibUpdateFlags_Reg_20) {
|
||||||
|
@ -831,22 +865,52 @@ void MidiDriver_Miles_AdLib::updatePhysicalFmVoice(byte virtualFmVoice, bool key
|
||||||
uint16 volumeOp1 = (~reg40op1) & 0x3F;
|
uint16 volumeOp1 = (~reg40op1) & 0x3F;
|
||||||
uint16 volumeOp2 = (~reg40op2) & 0x3F;
|
uint16 volumeOp2 = (~reg40op2) & 0x3F;
|
||||||
|
|
||||||
if (instrumentPtr->regC0 & 1) {
|
if (_oplType != OPL::Config::kDualOpl2) {
|
||||||
// operator 2 enabled
|
if (instrumentPtr->regC0 & 1) {
|
||||||
// scale volume factor
|
// operator 2 enabled
|
||||||
volumeOp1 = (volumeOp1 * compositeVolume) / 127;
|
// scale volume factor
|
||||||
// 2nd operator always scaled
|
volumeOp1 = (volumeOp1 * compositeVolume) / 127;
|
||||||
|
// 2nd operator always scaled
|
||||||
|
}
|
||||||
|
|
||||||
|
volumeOp2 = (volumeOp2 * compositeVolume) / 127;
|
||||||
|
|
||||||
|
volumeOp1 = (~volumeOp1) & 0x3F; // negate it, so we get the proper value for the register
|
||||||
|
volumeOp2 = (~volumeOp2) & 0x3F; // ditto
|
||||||
|
reg40op1 = (reg40op1 & 0xC0) | volumeOp1; // keep "scaling level" and merge in our volume
|
||||||
|
reg40op2 = (reg40op2 & 0xC0) | volumeOp2;
|
||||||
|
|
||||||
|
setRegister(0x40 + op1Reg, reg40op1);
|
||||||
|
setRegister(0x40 + op2Reg, reg40op2);
|
||||||
|
} else {
|
||||||
|
// For dual OPL2, separate register values are calculated for the
|
||||||
|
// left and right OPL2 chip.
|
||||||
|
uint8 volumeLeftOp1 = volumeOp1;
|
||||||
|
uint8 volumeRightOp1 = volumeOp1;
|
||||||
|
|
||||||
|
if (instrumentPtr->regC0 & 1) {
|
||||||
|
// operator 2 enabled
|
||||||
|
// scale volume factor
|
||||||
|
volumeLeftOp1 = (volumeLeftOp1 * leftVolume) / 127;
|
||||||
|
volumeRightOp1 = (volumeRightOp1 * rightVolume) / 127;
|
||||||
|
// 2nd operator always scaled
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 volumeLeftOp2 = (volumeOp2 * leftVolume) / 127;
|
||||||
|
uint8 volumeRightOp2 = (volumeOp2 * rightVolume) / 127;
|
||||||
|
|
||||||
|
volumeLeftOp1 = (~volumeLeftOp1) & 0x3F; // negate it, so we get the proper value for the register
|
||||||
|
volumeRightOp1 = (~volumeRightOp1) & 0x3F;
|
||||||
|
volumeLeftOp2 = (~volumeLeftOp2) & 0x3F; // ditto
|
||||||
|
volumeRightOp2 = (~volumeRightOp2) & 0x3F;
|
||||||
|
uint8 reg40op1left = (reg40op1 & 0xC0) | volumeLeftOp1; // keep "scaling level" and merge in our volume
|
||||||
|
uint8 reg40op1right = (reg40op1 & 0xC0) | volumeRightOp1;
|
||||||
|
uint8 reg40op2left = (reg40op2 & 0xC0) | volumeLeftOp2;
|
||||||
|
uint8 reg40op2right = (reg40op2 & 0xC0) | volumeRightOp2;
|
||||||
|
|
||||||
|
setRegisterStereo(0x40 + op1Reg, reg40op1left, reg40op1right);
|
||||||
|
setRegisterStereo(0x40 + op2Reg, reg40op2left, reg40op2right);
|
||||||
}
|
}
|
||||||
|
|
||||||
volumeOp2 = (volumeOp2 * compositeVolume) / 127;
|
|
||||||
|
|
||||||
volumeOp1 = (~volumeOp1) & 0x3F; // negate it, so we get the proper value for the register
|
|
||||||
volumeOp2 = (~volumeOp2) & 0x3F; // ditto
|
|
||||||
reg40op1 = (reg40op1 & 0xC0) | volumeOp1; // keep "scaling level" and merge in our volume
|
|
||||||
reg40op2 = (reg40op2 & 0xC0) | volumeOp2;
|
|
||||||
|
|
||||||
setRegister(0x40 + op1Reg, reg40op1);
|
|
||||||
setRegister(0x40 + op2Reg, reg40op2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (registerUpdateFlags & kMilesAdLibUpdateFlags_Reg_60) {
|
if (registerUpdateFlags & kMilesAdLibUpdateFlags_Reg_60) {
|
||||||
|
@ -1157,6 +1221,13 @@ void MidiDriver_Miles_AdLib::setRegister(int reg, int value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MidiDriver_Miles_AdLib::setRegisterStereo(uint8 reg, uint8 valueLeft, uint8 valueRight) {
|
||||||
|
_opl->write(0x220, reg);
|
||||||
|
_opl->write(0x221, valueLeft);
|
||||||
|
_opl->write(0x222, reg);
|
||||||
|
_opl->write(0x223, valueRight);
|
||||||
|
}
|
||||||
|
|
||||||
MidiDriver_Multisource *MidiDriver_Miles_AdLib_create(const Common::String &filenameAdLib, const Common::String &filenameOPL3, Common::SeekableReadStream *streamAdLib, Common::SeekableReadStream *streamOPL3) {
|
MidiDriver_Multisource *MidiDriver_Miles_AdLib_create(const Common::String &filenameAdLib, const Common::String &filenameOPL3, Common::SeekableReadStream *streamAdLib, Common::SeekableReadStream *streamOPL3) {
|
||||||
// Load adlib instrument data from file SAMPLE.AD (OPL3: SAMPLE.OPL)
|
// Load adlib instrument data from file SAMPLE.AD (OPL3: SAMPLE.OPL)
|
||||||
Common::String timbreFilename;
|
Common::String timbreFilename;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue