mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-24 13:13:58 +00:00
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
@ -116,6 +116,18 @@ uint16 milesAdLibVolumeSensitivityTable[] = {
|
||||
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 {
|
||||
public:
|
||||
@ -251,6 +263,7 @@ private:
|
||||
void resetAdLibFMVoiceChannelRegisters(byte baseRegister, byte value);
|
||||
|
||||
void setRegister(int reg, int value);
|
||||
void setRegisterStereo(uint8 reg, uint8 valueLeft, uint8 valueRight);
|
||||
|
||||
int16 searchFreeVirtualFmVoiceChannel();
|
||||
int16 searchFreePhysicalFmVoiceChannel();
|
||||
@ -308,14 +321,14 @@ int MidiDriver_Miles_AdLib::open() {
|
||||
// Try to create OPL3 first
|
||||
_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) {
|
||||
// not created yet, downgrade to OPL2
|
||||
_oplType = OPL::Config::kOpl2;
|
||||
_modeVirtualFmVoicesCount = 16;
|
||||
_modePhysicalFmVoicesCount = 9;
|
||||
_modeStereo = false;
|
||||
|
||||
_opl = OPL::Config::create(OPL::Config::kOpl2);
|
||||
}
|
||||
|
||||
@ -324,6 +337,12 @@ int MidiDriver_Miles_AdLib::open() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_oplType != OPL::Config::kOpl3) {
|
||||
_modeVirtualFmVoicesCount = 16;
|
||||
_modePhysicalFmVoicesCount = 9;
|
||||
_modeStereo = false;
|
||||
}
|
||||
|
||||
_opl->init();
|
||||
|
||||
_isOpen = true;
|
||||
@ -778,6 +797,8 @@ void MidiDriver_Miles_AdLib::updatePhysicalFmVoice(byte virtualFmVoice, bool key
|
||||
uint16 channelReg = milesAdLibChannelRegister[physicalFmVoice];
|
||||
|
||||
uint16 compositeVolume = 0;
|
||||
uint8 leftVolume = 0;
|
||||
uint8 rightVolume = 0;
|
||||
|
||||
if (registerUpdateFlags & kMilesAdLibUpdateFlags_Reg_40) {
|
||||
// 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.
|
||||
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) {
|
||||
@ -831,22 +865,52 @@ void MidiDriver_Miles_AdLib::updatePhysicalFmVoice(byte virtualFmVoice, bool key
|
||||
uint16 volumeOp1 = (~reg40op1) & 0x3F;
|
||||
uint16 volumeOp2 = (~reg40op2) & 0x3F;
|
||||
|
||||
if (instrumentPtr->regC0 & 1) {
|
||||
// operator 2 enabled
|
||||
// scale volume factor
|
||||
volumeOp1 = (volumeOp1 * compositeVolume) / 127;
|
||||
// 2nd operator always scaled
|
||||
if (_oplType != OPL::Config::kDualOpl2) {
|
||||
if (instrumentPtr->regC0 & 1) {
|
||||
// operator 2 enabled
|
||||
// scale volume factor
|
||||
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) {
|
||||
@ -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) {
|
||||
// Load adlib instrument data from file SAMPLE.AD (OPL3: SAMPLE.OPL)
|
||||
Common::String timbreFilename;
|
||||
|
Loading…
x
Reference in New Issue
Block a user