AUDIO: Small MIDI driver enhancements and fixes
This commit adds the following functionality to the MIDI drivers: - Add checking if a driver is ready to process MIDI events for a specific source (rather than any source). To facilitate this, SysExes can now be sent using a source number. This is stored with the SysEx data in the SysEx queue and can be checked when isReady is called with a source number. - Allow specifying controller default values per MIDI channel. Currently this is only implemented for program. - The OPL dynamic channel allocation algorithm will now respect statically allocated channels, in case a subclass uses combined static and dynamic channel allocation. It also fixes the following bugs: - Instrument remapping can now be specified using const arrays. - OPL instrument writing code is refactored to a separate function. - OPL note on with velocity 0 would be handled as a note off, and then continued to be processed as a note on. - OPL writeFrequency would always write key on bit, even if the note is not active. - MT-32 default channel volume was incorrect.
This commit is contained in:
parent
00a907f524
commit
6a9fc73962
10 changed files with 186 additions and 96 deletions
|
@ -78,7 +78,7 @@ void AdLibBnkInstrumentOperatorDefinition::toOplInstrumentOperatorDefinition(Opl
|
|||
operatorDef.waveformSelect = waveformSelect;
|
||||
}
|
||||
|
||||
void AdLibBnkInstrumentDefinition::toOplInstrumentDefinition(OplInstrumentDefinition& instrumentDef) {
|
||||
void AdLibBnkInstrumentDefinition::toOplInstrumentDefinition(OplInstrumentDefinition &instrumentDef) {
|
||||
instrumentDef.fourOperator = false;
|
||||
|
||||
operator0.toOplInstrumentOperatorDefinition(instrumentDef.operator0, waveformSelect0);
|
||||
|
@ -637,9 +637,11 @@ void MidiDriver_ADLIB_Multisource::noteOff(uint8 channel, uint8 note, uint8 velo
|
|||
}
|
||||
|
||||
void MidiDriver_ADLIB_Multisource::noteOn(uint8 channel, uint8 note, uint8 velocity, uint8 source) {
|
||||
if (velocity == 0)
|
||||
if (velocity == 0) {
|
||||
// Note on with velocity 0 is a note off.
|
||||
noteOff(channel, note, velocity, source);
|
||||
return;
|
||||
}
|
||||
|
||||
InstrumentInfo instrument = determineInstrument(channel, source, note);
|
||||
// If rhythm mode is on and the note is on the rhythm channel, this note
|
||||
|
@ -687,20 +689,9 @@ void MidiDriver_ADLIB_Multisource::noteOn(uint8 channel, uint8 note, uint8 veloc
|
|||
activeNote->instrumentId = instrument.instrumentId;
|
||||
activeNote->instrumentDef = instrument.instrumentDef;
|
||||
|
||||
// Calculate operator volumes and write operator definitions to
|
||||
// the OPL registers.
|
||||
for (int i = 0; i < instrument.instrumentDef->getNumberOfOperators(); i++) {
|
||||
uint16 operatorOffset = determineOperatorRegisterOffset(oplChannel, i, instrument.instrumentDef->rhythmType, instrument.instrumentDef->fourOperator);
|
||||
const OplInstrumentOperatorDefinition &operatorDef = instrument.instrumentDef->getOperatorDefinition(i);
|
||||
writeRegister(OPL_REGISTER_BASE_FREQMULT_MISC + operatorOffset, operatorDef.freqMultMisc);
|
||||
writeVolume(oplChannel, i, instrument.instrumentDef->rhythmType);
|
||||
writeRegister(OPL_REGISTER_BASE_DECAY_ATTACK + operatorOffset, operatorDef.decayAttack);
|
||||
writeRegister(OPL_REGISTER_BASE_RELEASE_SUSTAIN + operatorOffset, operatorDef.releaseSustain);
|
||||
writeRegister(OPL_REGISTER_BASE_WAVEFORMSELECT + operatorOffset, operatorDef.waveformSelect);
|
||||
}
|
||||
// Write out the instrument definition, volume and panning.
|
||||
writeInstrument(oplChannel, instrument);
|
||||
|
||||
// Determine and write panning and write feedback and connection.
|
||||
writePanning(oplChannel, instrument.instrumentDef->rhythmType);
|
||||
// Calculate and write frequency and block and write key on bit.
|
||||
writeFrequency(oplChannel, instrument.instrumentDef->rhythmType);
|
||||
|
||||
|
@ -768,10 +759,6 @@ void MidiDriver_ADLIB_Multisource::controlChange(uint8 channel, uint8 controller
|
|||
}
|
||||
|
||||
void MidiDriver_ADLIB_Multisource::programChange(uint8 channel, uint8 program, uint8 source) {
|
||||
if (_instrumentRemapping && channel != MIDI_RHYTHM_CHANNEL)
|
||||
// Apply instrument remapping (if specified) to instrument channels.
|
||||
program = _instrumentRemapping[program];
|
||||
|
||||
// Just set the MIDI program value; this event does not affect active notes.
|
||||
_controlData[source][channel].program = program;
|
||||
}
|
||||
|
@ -861,8 +848,8 @@ void MidiDriver_ADLIB_Multisource::applyControllerDefaults(uint8 source) {
|
|||
}
|
||||
} else {
|
||||
for (int i = 0; i < MIDI_CHANNEL_COUNT; i++) {
|
||||
if (_controllerDefaults.program >= 0) {
|
||||
_controlData[source][i].program = _controllerDefaults.program;
|
||||
if (_controllerDefaults.program[i] >= 0) {
|
||||
_controlData[source][i].program = _controllerDefaults.program[i];
|
||||
}
|
||||
if (_controllerDefaults.channelPressure >= 0) {
|
||||
_controlData[source][i].channelPressure = _controllerDefaults.channelPressure;
|
||||
|
@ -1093,12 +1080,15 @@ void MidiDriver_ADLIB_Multisource::stopAllNotes(uint8 source, uint8 channel) {
|
|||
}
|
||||
}
|
||||
if (_rhythmMode && !_rhythmModeIgnoreNoteOffs && (channel == 0xFF || channel == MIDI_RHYTHM_CHANNEL)) {
|
||||
bool rhythmChanged = false;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (_activeRhythmNotes[i].noteActive && (source == 0xFF || _activeRhythmNotes[i].source == source)) {
|
||||
_activeRhythmNotes[i].noteActive = false;
|
||||
rhythmChanged = true;
|
||||
}
|
||||
}
|
||||
writeRhythm();
|
||||
if (rhythmChanged)
|
||||
writeRhythm();
|
||||
}
|
||||
|
||||
_activeNotesMutex.unlock();
|
||||
|
@ -1303,7 +1293,11 @@ MidiDriver_ADLIB_Multisource::InstrumentInfo MidiDriver_ADLIB_Multisource::deter
|
|||
} else {
|
||||
// On non-rhythm channels, use the active instrument (program) on the
|
||||
// MIDI channel.
|
||||
instrument.instrumentId = _controlData[source][channel].program;
|
||||
byte program = _controlData[source][channel].program;
|
||||
if (_instrumentRemapping)
|
||||
// Apply instrument remapping (if specified).
|
||||
program = _instrumentRemapping[program];
|
||||
instrument.instrumentId = program;
|
||||
instrument.instrumentDef = &_instrumentBank[instrument.instrumentId];
|
||||
instrument.oplNote = note;
|
||||
}
|
||||
|
@ -1332,6 +1326,10 @@ uint8 MidiDriver_ADLIB_Multisource::allocateOplChannel(uint8 channel, uint8 sour
|
|||
uint32 inactiveNoteCounter = 0xFFFF, instrumentNoteCounter = 0xFFFF, lowestNoteCounter = 0xFFFF;
|
||||
for (int i = 0; i < _numMelodicChannels; i++) {
|
||||
uint8 oplChannel = _melodicChannels[i];
|
||||
if (_activeNotes[oplChannel].channelAllocated)
|
||||
// Channel has been statically allocated. Try the next channel.
|
||||
continue;
|
||||
|
||||
if (_activeNotes[oplChannel].noteCounterValue == 0) {
|
||||
// This channel is unused. No need to look any further.
|
||||
unusedChannel = oplChannel;
|
||||
|
@ -1538,7 +1536,7 @@ int32 MidiDriver_ADLIB_Multisource::calculatePitchBend(uint8 channel, uint8 sour
|
|||
return pitchBend;
|
||||
}
|
||||
|
||||
uint8 MidiDriver_ADLIB_Multisource::calculateVolume(uint8 channel, uint8 source, uint8 velocity, OplInstrumentDefinition& instrumentDef, uint8 operatorNum) {
|
||||
uint8 MidiDriver_ADLIB_Multisource::calculateVolume(uint8 channel, uint8 source, uint8 velocity, OplInstrumentDefinition &instrumentDef, uint8 operatorNum) {
|
||||
// Get the volume (level) for this operator from the instrument definition.
|
||||
uint8 operatorDefVolume = instrumentDef.getOperatorDefinition(operatorNum).level & 0x3F;
|
||||
|
||||
|
@ -1754,6 +1752,26 @@ uint16 MidiDriver_ADLIB_Multisource::determineChannelRegisterOffset(uint8 oplCha
|
|||
return offset + (oplChannel % numChannelsPerSet);
|
||||
}
|
||||
|
||||
void MidiDriver_ADLIB_Multisource::writeInstrument(uint8 oplChannel, InstrumentInfo instrument) {
|
||||
ActiveNote *activeNote = (instrument.instrumentDef->rhythmType == RHYTHM_TYPE_UNDEFINED ? &_activeNotes[oplChannel] : &_activeRhythmNotes[instrument.instrumentDef->rhythmType - 1]);
|
||||
activeNote->instrumentDef = instrument.instrumentDef;
|
||||
|
||||
// Calculate operator volumes and write operator definitions to
|
||||
// the OPL registers.
|
||||
for (int i = 0; i < instrument.instrumentDef->getNumberOfOperators(); i++) {
|
||||
uint16 operatorOffset = determineOperatorRegisterOffset(oplChannel, i, instrument.instrumentDef->rhythmType, instrument.instrumentDef->fourOperator);
|
||||
const OplInstrumentOperatorDefinition &operatorDef = instrument.instrumentDef->getOperatorDefinition(i);
|
||||
writeRegister(OPL_REGISTER_BASE_FREQMULT_MISC + operatorOffset, operatorDef.freqMultMisc);
|
||||
writeVolume(oplChannel, i, instrument.instrumentDef->rhythmType);
|
||||
writeRegister(OPL_REGISTER_BASE_DECAY_ATTACK + operatorOffset, operatorDef.decayAttack);
|
||||
writeRegister(OPL_REGISTER_BASE_RELEASE_SUSTAIN + operatorOffset, operatorDef.releaseSustain);
|
||||
writeRegister(OPL_REGISTER_BASE_WAVEFORMSELECT + operatorOffset, operatorDef.waveformSelect);
|
||||
}
|
||||
|
||||
// Determine and write panning and write feedback and connection.
|
||||
writePanning(oplChannel, instrument.instrumentDef->rhythmType);
|
||||
}
|
||||
|
||||
void MidiDriver_ADLIB_Multisource::writeKeyOff(uint8 oplChannel, OplInstrumentRhythmType rhythmType, bool forceWrite) {
|
||||
_activeNotesMutex.lock();
|
||||
|
||||
|
@ -1858,7 +1876,7 @@ void MidiDriver_ADLIB_Multisource::writeFrequency(uint8 oplChannel, OplInstrumen
|
|||
writeRegister(OPL_REGISTER_BASE_FNUMLOW + channelOffset, frequency & 0xFF);
|
||||
// Write the high 2 frequency bits and block and add the key on bit.
|
||||
writeRegister(OPL_REGISTER_BASE_FNUMHIGH_BLOCK_KEYON + channelOffset,
|
||||
(frequency >> 8) | (rhythmType == RHYTHM_TYPE_UNDEFINED ? OPL_MASK_KEYON : 0));
|
||||
(frequency >> 8) | (rhythmType == RHYTHM_TYPE_UNDEFINED && activeNote->noteActive ? OPL_MASK_KEYON : 0));
|
||||
|
||||
_activeNotesMutex.unlock();
|
||||
}
|
||||
|
@ -1866,9 +1884,11 @@ void MidiDriver_ADLIB_Multisource::writeFrequency(uint8 oplChannel, OplInstrumen
|
|||
void MidiDriver_ADLIB_Multisource::writeRegister(uint16 reg, uint8 value, bool forceWrite) {
|
||||
//debug("Writing register %X %X", reg, value);
|
||||
|
||||
// Write the value to the register if forceWrite is specified or if the
|
||||
// new register value is different from the current value.
|
||||
if (forceWrite || _shadowRegisters[reg] != value) {
|
||||
// Write the value to the register if it is a timer register, if forceWrite
|
||||
// is specified or if the new register value is different from the current
|
||||
// value.
|
||||
if ((reg >= 1 && reg <= 3) || (_oplType == OPL::Config::kDualOpl2 && reg >= 0x101 && reg <= 0x103) ||
|
||||
forceWrite || _shadowRegisters[reg] != value) {
|
||||
_shadowRegisters[reg] = value;
|
||||
_opl->writeReg(reg, value);
|
||||
}
|
||||
|
|
|
@ -606,15 +606,15 @@ public:
|
|||
uint32 property(int prop, uint32 param) override;
|
||||
uint32 getBaseTempo() override;
|
||||
/**
|
||||
* This driver does not use MidiChannel objects, so this function returns 0.
|
||||
* This driver does not use MidiChannel objects, so this function returns nullptr.
|
||||
*
|
||||
* @return 0
|
||||
* @return nullptr
|
||||
*/
|
||||
MidiChannel *allocateChannel() override;
|
||||
/**
|
||||
* This driver does not use MidiChannel objects, so this function returns 0.
|
||||
* This driver does not use MidiChannel objects, so this function returns nullptr.
|
||||
*
|
||||
* @return 0
|
||||
* @return nullptr
|
||||
*/
|
||||
MidiChannel *getPercussionChannel() override;
|
||||
|
||||
|
@ -1030,7 +1030,14 @@ protected:
|
|||
* @return The offset to the base register for this channel.
|
||||
*/
|
||||
uint16 determineChannelRegisterOffset(uint8 oplChannel, bool fourOperator = false);
|
||||
|
||||
/**
|
||||
* Writes the specified instrument definition to the specified OPL channel.
|
||||
* It will calculate volume and panning if necessary.
|
||||
*
|
||||
* @param oplChannel The OPL channel on which to write the instrument.
|
||||
* @param instrument The data of the instrument to write.
|
||||
*/
|
||||
void writeInstrument(uint8 oplChannel, InstrumentInfo instrument);
|
||||
/**
|
||||
* Sets the key on bit to false for the specified OPL channel or rhythm
|
||||
* instrument and updates _activeNotes or _activeRhythmNotes with the new
|
||||
|
@ -1076,7 +1083,7 @@ protected:
|
|||
* calculated and written. Use type undefined to calculate panning for a
|
||||
* melodic instrument.
|
||||
*/
|
||||
void writePanning(uint8 oplChannel, OplInstrumentRhythmType rhythmType = RHYTHM_TYPE_UNDEFINED);
|
||||
virtual void writePanning(uint8 oplChannel, OplInstrumentRhythmType rhythmType = RHYTHM_TYPE_UNDEFINED);
|
||||
/**
|
||||
* Calculates the frequency for the active note on the specified OPL
|
||||
* channel or of the specified rhythm type (@see calculateFrequency) and
|
||||
|
@ -1088,7 +1095,7 @@ protected:
|
|||
* be calculated and written. Use type undefined to calculate the frequency
|
||||
* for a melodic instrument.
|
||||
*/
|
||||
void writeFrequency(uint8 oplChannel, OplInstrumentRhythmType rhythmType = RHYTHM_TYPE_UNDEFINED);
|
||||
virtual void writeFrequency(uint8 oplChannel, OplInstrumentRhythmType rhythmType = RHYTHM_TYPE_UNDEFINED);
|
||||
|
||||
/**
|
||||
* Writes the specified value to the specified OPL register.
|
||||
|
|
|
@ -244,8 +244,11 @@ public:
|
|||
* A driver implementation might need time to prepare playback of
|
||||
* a track. Use this function to check if the driver is ready to
|
||||
* receive MIDI events.
|
||||
*
|
||||
* @param source Check if the driver is ready to receive events from this
|
||||
* specific source. Specify -1 to check readiness regardless of source.
|
||||
*/
|
||||
virtual bool isReady() { return true; }
|
||||
virtual bool isReady(int8 source = -1) { return true; }
|
||||
|
||||
protected:
|
||||
|
||||
|
|
|
@ -37,19 +37,20 @@ MidiDriver_Multisource::MidiSource::MidiSource() :
|
|||
fadeDuration(0) { }
|
||||
|
||||
MidiDriver_Multisource::ControllerDefaults::ControllerDefaults() :
|
||||
// The -1 value indicates no default value should be set on the controller.
|
||||
program(-1),
|
||||
instrumentBank(-1),
|
||||
drumkit(-1),
|
||||
channelPressure(-1),
|
||||
pitchBend(-1),
|
||||
modulation(-1),
|
||||
volume(-1),
|
||||
panning(-1),
|
||||
expression(-1),
|
||||
sustain(-1),
|
||||
rpn(-1),
|
||||
pitchBendSensitivity(-1) { }
|
||||
// The -1 value indicates no default value should be set on the controller.
|
||||
instrumentBank(-1),
|
||||
drumkit(-1),
|
||||
channelPressure(-1),
|
||||
pitchBend(-1),
|
||||
modulation(-1),
|
||||
volume(-1),
|
||||
panning(-1),
|
||||
expression(-1),
|
||||
sustain(-1),
|
||||
rpn(-1),
|
||||
pitchBendSensitivity(-1) {
|
||||
Common::fill(program, program + ARRAYSIZE(program), -1);
|
||||
}
|
||||
|
||||
MidiDriver_Multisource::MidiDriver_Multisource() :
|
||||
_instrumentRemapping(nullptr),
|
||||
|
@ -238,7 +239,7 @@ void MidiDriver_Multisource::setControllerDefault(ControllerDefaultType type, in
|
|||
// corresponding to the specified controller.
|
||||
switch (type) {
|
||||
case CONTROLLER_DEFAULT_PROGRAM:
|
||||
_controllerDefaults.program = value;
|
||||
Common::fill(_controllerDefaults.program, _controllerDefaults.program + ARRAYSIZE(_controllerDefaults.program), value);
|
||||
break;
|
||||
case CONTROLLER_DEFAULT_INSTRUMENT_BANK:
|
||||
_controllerDefaults.instrumentBank = value;
|
||||
|
@ -279,6 +280,19 @@ void MidiDriver_Multisource::setControllerDefault(ControllerDefaultType type, in
|
|||
}
|
||||
}
|
||||
|
||||
void MidiDriver_Multisource::setControllerDefaults(ControllerDefaultType type, int16 *value) {
|
||||
// Set the specified default values on _controllerDefaults on the field
|
||||
// corresponding to the specified controller.
|
||||
switch (type) {
|
||||
case CONTROLLER_DEFAULT_PROGRAM:
|
||||
Common::copy(value, value + ARRAYSIZE(_controllerDefaults.program), _controllerDefaults.program);
|
||||
break;
|
||||
default:
|
||||
warning("MidiDriver_Multisource::setControllerDefaults - Unsupported controller default type %i", type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_Multisource::clearControllerDefault(ControllerDefaultType type) {
|
||||
// Reset the default value for this controller to -1.
|
||||
setControllerDefault(type, -1);
|
||||
|
@ -347,7 +361,7 @@ void MidiDriver_Multisource::setSourceNeutralVolume(uint8 source, uint16 volume)
|
|||
_sources[source].neutralVolume = volume;
|
||||
}
|
||||
|
||||
void MidiDriver_Multisource::setInstrumentRemapping(byte *instrumentRemapping) {
|
||||
void MidiDriver_Multisource::setInstrumentRemapping(const byte *instrumentRemapping) {
|
||||
_instrumentRemapping = instrumentRemapping;
|
||||
}
|
||||
|
||||
|
|
|
@ -189,7 +189,7 @@ protected:
|
|||
// Stores the default values that should be set for each controller.
|
||||
// -1 means no explicit default should be set for that controller.
|
||||
struct ControllerDefaults {
|
||||
int8 program;
|
||||
int8 program[16];
|
||||
int8 instrumentBank;
|
||||
int8 drumkit;
|
||||
|
||||
|
@ -353,6 +353,20 @@ public:
|
|||
* @param value The default value which should be set.
|
||||
*/
|
||||
void setControllerDefault(ControllerDefaultType type, int16 value);
|
||||
/**
|
||||
* Specify a default value for a controller which should be set when a new
|
||||
* track is started. This expects an array of values, each of which will
|
||||
* be used as the default for the corresponding MIDI channel.
|
||||
*
|
||||
* This is currently only supported for program.
|
||||
*
|
||||
* See setControllerDefault for more details.
|
||||
*
|
||||
* @param type The controller which should be reset.
|
||||
* @param values The default values which should be set. Must be a 16 value
|
||||
* array.
|
||||
*/
|
||||
void setControllerDefaults(ControllerDefaultType type, int16 *values);
|
||||
/**
|
||||
* Clears a previously set default value for the specified controller.
|
||||
*
|
||||
|
@ -373,7 +387,7 @@ public:
|
|||
* @param instrumentRemapping The instrument map that should be used for
|
||||
* remapping, or nullptr to disable remapping.
|
||||
*/
|
||||
void setInstrumentRemapping(byte *instrumentRemapping);
|
||||
void setInstrumentRemapping(const byte *instrumentRemapping);
|
||||
|
||||
/**
|
||||
* Applies the user volume settings to the MIDI driver. MIDI channel
|
||||
|
@ -439,7 +453,7 @@ protected:
|
|||
ControllerDefaults _controllerDefaults;
|
||||
|
||||
// Map for arbitrary instrument remapping.
|
||||
byte *_instrumentRemapping;
|
||||
const byte *_instrumentRemapping;
|
||||
|
||||
// True if the driver should scale MIDI channel volume to the user
|
||||
// specified volume settings.
|
||||
|
|
|
@ -1224,8 +1224,8 @@ void MidiDriver_Miles_AdLib::applyControllerDefaults(uint8 source) {
|
|||
return;
|
||||
|
||||
for (int i = 0; i < MIDI_CHANNEL_COUNT; i++) {
|
||||
if (_controllerDefaults.program >= 0) {
|
||||
_midiChannels[i].currentProgram = _controllerDefaults.program;
|
||||
if (_controllerDefaults.program[i] >= 0) {
|
||||
_midiChannels[i].currentProgram = _controllerDefaults.program[i];
|
||||
}
|
||||
if (_controllerDefaults.pitchBend >= 0) {
|
||||
_midiChannels[i].currentPitchBender = _controllerDefaults.pitchBend;
|
||||
|
|
|
@ -378,6 +378,24 @@ void MidiDriver_MT32GM::close() {
|
|||
}
|
||||
}
|
||||
|
||||
bool MidiDriver_MT32GM::isReady(int8 source) {
|
||||
Common::StackLock lock(_sysExQueueMutex);
|
||||
|
||||
// For an unspecified source, just return if the queue is empty or not.
|
||||
if (source < 0)
|
||||
return _sysExQueue.empty();
|
||||
|
||||
// For a specific source, check if there is a SysEx for that source in the
|
||||
// queue.
|
||||
for (Common::ListInternal::Iterator<SysExData> it = _sysExQueue.begin();
|
||||
it != _sysExQueue.end(); it++) {
|
||||
if (it->source == source)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 MidiDriver_MT32GM::property(int prop, uint32 param) {
|
||||
switch (prop) {
|
||||
case PROP_MIDI_DATA_REVERSE_PANNING:
|
||||
|
@ -458,36 +476,36 @@ void MidiDriver_MT32GM::applyControllerDefaults(uint8 source, MidiChannelControl
|
|||
if (outputChannel != MIDI_RHYTHM_CHANNEL) {
|
||||
// Apply default bank and program only to melodic channels.
|
||||
if (_controllerDefaults.instrumentBank >= 0 && controlData.instrumentBank != _controllerDefaults.instrumentBank) {
|
||||
send(source, MIDI_COMMAND_CONTROL_CHANGE | outputChannel, MIDI_CONTROLLER_BANK_SELECT_MSB, _controllerDefaults.instrumentBank);
|
||||
send(source, MIDI_COMMAND_CONTROL_CHANGE | outputChannel, MIDI_CONTROLLER_BANK_SELECT_LSB, 0);
|
||||
controlChange(outputChannel, MIDI_CONTROLLER_BANK_SELECT_MSB, _controllerDefaults.instrumentBank, source, controlData);
|
||||
controlChange(outputChannel, MIDI_CONTROLLER_BANK_SELECT_LSB, 0, source, controlData);
|
||||
}
|
||||
if (_controllerDefaults.program >= 0 && controlData.program != _controllerDefaults.program) {
|
||||
send(source, MIDI_COMMAND_PROGRAM_CHANGE | outputChannel, _controllerDefaults.program, 0);
|
||||
if (_controllerDefaults.program[outputChannel] >= 0 && controlData.program != _controllerDefaults.program[outputChannel]) {
|
||||
programChange(outputChannel, _controllerDefaults.program[outputChannel], source, controlData);
|
||||
}
|
||||
} else {
|
||||
// Apply default drumkit only to the rhythm channel.
|
||||
if (_controllerDefaults.drumkit >= 0 && controlData.program != _controllerDefaults.drumkit) {
|
||||
send(source, MIDI_COMMAND_PROGRAM_CHANGE | outputChannel, _controllerDefaults.drumkit, 0);
|
||||
programChange(outputChannel, _controllerDefaults.drumkit, source, controlData);
|
||||
}
|
||||
}
|
||||
if (_controllerDefaults.channelPressure >= 0 && controlData.channelPressure != _controllerDefaults.channelPressure) {
|
||||
send(source, MIDI_COMMAND_CHANNEL_AFTERTOUCH | outputChannel, _controllerDefaults.channelPressure, 0);
|
||||
channelAftertouch(outputChannel, _controllerDefaults.channelPressure, source, controlData);
|
||||
}
|
||||
if (_controllerDefaults.pitchBend >= 0 && controlData.pitchWheel != _controllerDefaults.pitchBend) {
|
||||
send(source, MIDI_COMMAND_PITCH_BEND | outputChannel, _controllerDefaults.pitchBend & 0x7F, _controllerDefaults.pitchBend >> 7);
|
||||
pitchBend(outputChannel, _controllerDefaults.pitchBend & 0x7F, _controllerDefaults.pitchBend >> 7, source, controlData);
|
||||
}
|
||||
|
||||
if (_controllerDefaults.modulation >= 0 && controlData.modulation != _controllerDefaults.modulation) {
|
||||
send(source, MIDI_COMMAND_CONTROL_CHANGE | outputChannel, MIDI_CONTROLLER_MODULATION, _controllerDefaults.modulation);
|
||||
controlChange(outputChannel, MIDI_CONTROLLER_MODULATION, _controllerDefaults.modulation, source, controlData);
|
||||
}
|
||||
if (_controllerDefaults.volume >= 0 && controlData.volume != _controllerDefaults.volume) {
|
||||
send(source, MIDI_COMMAND_CONTROL_CHANGE | outputChannel, MIDI_CONTROLLER_VOLUME, _controllerDefaults.volume);
|
||||
controlChange(outputChannel, MIDI_CONTROLLER_VOLUME, _controllerDefaults.volume, source, controlData);
|
||||
}
|
||||
if (_controllerDefaults.panning >= 0 && controlData.panPosition != _controllerDefaults.panning) {
|
||||
send(source, MIDI_COMMAND_CONTROL_CHANGE | outputChannel, MIDI_CONTROLLER_PANNING, _controllerDefaults.panning);
|
||||
controlChange(outputChannel, MIDI_CONTROLLER_PANNING, _controllerDefaults.panning, source, controlData);
|
||||
}
|
||||
if (_controllerDefaults.expression >= 0 && controlData.expression != _controllerDefaults.expression) {
|
||||
send(source, MIDI_COMMAND_CONTROL_CHANGE | outputChannel, MIDI_CONTROLLER_EXPRESSION, _controllerDefaults.expression);
|
||||
controlChange(outputChannel, MIDI_CONTROLLER_EXPRESSION, _controllerDefaults.expression, source, controlData);
|
||||
}
|
||||
|
||||
// RPN will be changed by setting pitch bend sensitivity, so store the
|
||||
|
@ -495,10 +513,10 @@ void MidiDriver_MT32GM::applyControllerDefaults(uint8 source, MidiChannelControl
|
|||
uint16 rpn = controlData.rpn;
|
||||
bool setRpn = false;
|
||||
if (_controllerDefaults.pitchBendSensitivity >= 0 && controlData.pitchBendSensitivity != _controllerDefaults.pitchBendSensitivity) {
|
||||
send(source, MIDI_COMMAND_CONTROL_CHANGE | outputChannel, MIDI_CONTROLLER_RPN_MSB, MIDI_RPN_PITCH_BEND_SENSITIVITY >> 8);
|
||||
send(source, MIDI_COMMAND_CONTROL_CHANGE | outputChannel, MIDI_CONTROLLER_RPN_LSB, MIDI_RPN_PITCH_BEND_SENSITIVITY & 0xFF);
|
||||
send(source, MIDI_COMMAND_CONTROL_CHANGE | outputChannel, MIDI_CONTROLLER_DATA_ENTRY_MSB, _controllerDefaults.pitchBendSensitivity);
|
||||
send(source, MIDI_COMMAND_CONTROL_CHANGE | outputChannel, MIDI_CONTROLLER_DATA_ENTRY_LSB, 0);
|
||||
controlChange(outputChannel, MIDI_CONTROLLER_RPN_MSB, MIDI_RPN_PITCH_BEND_SENSITIVITY >> 8, source, controlData);
|
||||
controlChange(outputChannel, MIDI_CONTROLLER_RPN_LSB, MIDI_RPN_PITCH_BEND_SENSITIVITY & 0xFF, source, controlData);
|
||||
controlChange(outputChannel, MIDI_CONTROLLER_DATA_ENTRY_MSB, _controllerDefaults.pitchBendSensitivity, source, controlData);
|
||||
controlChange(outputChannel, MIDI_CONTROLLER_DATA_ENTRY_LSB, 0, source, controlData);
|
||||
if (rpn != controlData.rpn)
|
||||
// Active RPN was changed; reset it to previous value (or default).
|
||||
setRpn = true;
|
||||
|
@ -510,8 +528,8 @@ void MidiDriver_MT32GM::applyControllerDefaults(uint8 source, MidiChannelControl
|
|||
}
|
||||
|
||||
if (setRpn) {
|
||||
send(source, MIDI_COMMAND_CONTROL_CHANGE | outputChannel, MIDI_CONTROLLER_RPN_MSB, rpn >> 8);
|
||||
send(source, MIDI_COMMAND_CONTROL_CHANGE | outputChannel, MIDI_CONTROLLER_RPN_LSB, rpn & 0xFF);
|
||||
controlChange(outputChannel, MIDI_CONTROLLER_RPN_MSB, rpn >> 8, source, controlData);
|
||||
controlChange(outputChannel, MIDI_CONTROLLER_RPN_LSB, rpn & 0xFF, source, controlData);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -712,16 +730,16 @@ void MidiDriver_MT32GM::removeActiveNotes(uint8 outputChannel, bool sustainedNot
|
|||
}
|
||||
|
||||
void MidiDriver_MT32GM::programChange(byte outputChannel, byte patchId, int8 source, MidiChannelControlData &controlData, bool channelLockedByOtherSource) {
|
||||
if (_instrumentRemapping && outputChannel != MIDI_RHYTHM_CHANNEL)
|
||||
// Apply instrument remapping (if specified) to instrument channels.
|
||||
patchId = _instrumentRemapping[patchId];
|
||||
|
||||
// remember patch id for the current MIDI-channel
|
||||
controlData.program = patchId;
|
||||
|
||||
if (channelLockedByOtherSource)
|
||||
return;
|
||||
|
||||
if (_instrumentRemapping && outputChannel != MIDI_RHYTHM_CHANNEL)
|
||||
// Apply instrument remapping (if specified) to instrument channels.
|
||||
patchId = _instrumentRemapping[patchId];
|
||||
|
||||
if (_midiType == MT_MT32) {
|
||||
if (outputChannel == MIDI_RHYTHM_CHANNEL &&
|
||||
!(!_nativeMT32 && _enableGS && patchId == 0x7F)) {
|
||||
|
@ -881,17 +899,18 @@ uint16 MidiDriver_MT32GM::sysExNoDelay(const byte *msg, uint16 length) {
|
|||
return delay;
|
||||
}
|
||||
|
||||
void MidiDriver_MT32GM::sysExQueue(const byte *msg, uint16 length) {
|
||||
void MidiDriver_MT32GM::sysExQueue(const byte *msg, uint16 length, int8 source) {
|
||||
SysExData sysEx;
|
||||
memcpy(sysEx.data, msg, length);
|
||||
sysEx.length = length;
|
||||
sysEx.source = source;
|
||||
|
||||
_sysExQueueMutex.lock();
|
||||
_sysExQueue.push(sysEx);
|
||||
_sysExQueue.push_back(sysEx);
|
||||
_sysExQueueMutex.unlock();
|
||||
}
|
||||
|
||||
uint16 MidiDriver_MT32GM::sysExMT32(const byte *msg, uint16 length, const uint32 targetAddress, bool queue, bool delay) {
|
||||
uint16 MidiDriver_MT32GM::sysExMT32(const byte *msg, uint16 length, const uint32 targetAddress, bool queue, bool delay, int8 source) {
|
||||
if (!_nativeMT32)
|
||||
// MT-32 SysExes have no effect on GM devices.
|
||||
return 0;
|
||||
|
@ -934,7 +953,7 @@ uint16 MidiDriver_MT32GM::sysExMT32(const byte *msg, uint16 length, const uint32
|
|||
sysExMessage[sysExPos++] = sysExChecksum & 0x7F;
|
||||
|
||||
if (queue) {
|
||||
sysExQueue(sysExMessage, sysExPos);
|
||||
sysExQueue(sysExMessage, sysExPos, source);
|
||||
} else if (!delay) {
|
||||
return sysExNoDelay(sysExMessage, sysExPos);
|
||||
} else {
|
||||
|
@ -1071,6 +1090,20 @@ int8 MidiDriver_MT32GM::mapSourceChannel(uint8 source, uint8 dataChannel) {
|
|||
void MidiDriver_MT32GM::deinitSource(uint8 source) {
|
||||
assert(source < MAXIMUM_SOURCES);
|
||||
|
||||
_sysExQueueMutex.lock();
|
||||
|
||||
// Remove any pending SysExes for this source from the queue.
|
||||
Common::ListInternal::Iterator<SysExData> it = _sysExQueue.begin();
|
||||
while (it != _sysExQueue.end()) {
|
||||
if (it->source == source) {
|
||||
it = _sysExQueue.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
_sysExQueueMutex.unlock();
|
||||
|
||||
MidiDriver_Multisource::deinitSource(source);
|
||||
|
||||
// Free channels which were used by this source.
|
||||
|
@ -1082,7 +1115,7 @@ void MidiDriver_MT32GM::deinitSource(uint8 source) {
|
|||
// Set the sustain default value if it is specified (typically
|
||||
// sustain would be turned off).
|
||||
if (_controllerDefaults.sustain >= 0 && _controlData[i]->sustain != (_controllerDefaults.sustain >= 0x40)) {
|
||||
send(-1, MIDI_COMMAND_CONTROL_CHANGE | i, MIDI_CONTROLLER_SUSTAIN, _controllerDefaults.sustain);
|
||||
controlChange(i, MIDI_CONTROLLER_SUSTAIN, _controllerDefaults.sustain, _controlData[i]->source, *_controlData[i]);
|
||||
}
|
||||
|
||||
_controlData[i]->source = -1;
|
||||
|
@ -1133,8 +1166,9 @@ void MidiDriver_MT32GM::onTimer() {
|
|||
|
||||
if (!_sysExQueue.empty() && _sysExDelay == 0) {
|
||||
// Ready to send next SysEx message to the MIDI device
|
||||
SysExData sysEx = _sysExQueue.pop();
|
||||
SysExData sysEx = _sysExQueue.front();
|
||||
_sysExDelay = sysExNoDelay(sysEx.data, sysEx.length) * 1000;
|
||||
_sysExQueue.pop_front();
|
||||
}
|
||||
|
||||
_sysExQueueMutex.unlock();
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
|
||||
#include "audio/mididrv.h"
|
||||
#include "audio/mididrv_ms.h"
|
||||
#include "common/list.h"
|
||||
#include "common/mutex.h"
|
||||
#include "common/queue.h"
|
||||
|
||||
/**
|
||||
* @defgroup audio_mt32_gm MIDI driver for MT-32 and GM
|
||||
|
@ -112,7 +112,7 @@ class MidiDriver_MT32GM : public MidiDriver_Multisource {
|
|||
public:
|
||||
static const byte MT32_DEFAULT_INSTRUMENTS[8];
|
||||
static const byte MT32_DEFAULT_PANNING[8];
|
||||
static const uint8 MT32_DEFAULT_CHANNEL_VOLUME = 98;
|
||||
static const uint8 MT32_DEFAULT_CHANNEL_VOLUME = 102;
|
||||
static const uint8 GM_DEFAULT_CHANNEL_VOLUME = 100;
|
||||
// Map for correcting Roland GS drumkit numbers.
|
||||
static const uint8 GS_DRUMKIT_FALLBACK_MAP[128];
|
||||
|
@ -239,7 +239,8 @@ protected:
|
|||
struct SysExData {
|
||||
byte data[270];
|
||||
uint16 length;
|
||||
SysExData() : length(0) {
|
||||
int8 source;
|
||||
SysExData() : length(0), source(-1) {
|
||||
memset(data, 0, sizeof(data));
|
||||
}
|
||||
};
|
||||
|
@ -259,10 +260,7 @@ public:
|
|||
virtual int open(MidiDriver *driver, bool nativeMT32);
|
||||
void close() override;
|
||||
bool isOpen() const override { return _isOpen; }
|
||||
bool isReady() override {
|
||||
Common::StackLock lock(_sysExQueueMutex);
|
||||
return _sysExQueue.empty();
|
||||
}
|
||||
bool isReady(int8 source = -1) override;
|
||||
uint32 property(int prop, uint32 param) override;
|
||||
|
||||
using MidiDriver_BASE::send;
|
||||
|
@ -277,7 +275,7 @@ public:
|
|||
* MIDI messages (not using the queue) should not be sent until the queue
|
||||
* is empty.
|
||||
*/
|
||||
void sysExQueue(const byte *msg, uint16 length);
|
||||
void sysExQueue(const byte *msg, uint16 length, int8 source = -1);
|
||||
/**
|
||||
* Write data to an MT-32 memory location using a SysEx message.
|
||||
* This function will add the necessary header and checksum bytes.
|
||||
|
@ -297,7 +295,7 @@ public:
|
|||
* it is the caller's responsibility to make sure that the next SysEx is
|
||||
* not sent before this time has passed.
|
||||
*/
|
||||
uint16 sysExMT32(const byte *msg, uint16 length, const uint32 targetAddress, bool queue = false, bool delay = true);
|
||||
uint16 sysExMT32(const byte *msg, uint16 length, const uint32 targetAddress, bool queue = false, bool delay = true, int8 source = -1);
|
||||
void metaEvent(int8 source, byte type, byte *data, uint16 length) override;
|
||||
|
||||
void stopAllNotes(bool stopSustainedNotes = false) override;
|
||||
|
@ -584,7 +582,7 @@ protected:
|
|||
// SysEx message can be sent.
|
||||
uint32 _sysExDelay;
|
||||
// Queue of SysEx messages to be sent to the MIDI device.
|
||||
Common::Queue<SysExData> _sysExQueue;
|
||||
Common::List<SysExData> _sysExQueue;
|
||||
// Mutex for write access to the SysEx queue.
|
||||
Common::Mutex _sysExQueueMutex;
|
||||
};
|
||||
|
|
|
@ -512,8 +512,8 @@ void MusicPlayerXMI::stopAllNotes(bool stopSustainedNotes) {
|
|||
_driver->stopAllNotes(stopSustainedNotes);
|
||||
}
|
||||
|
||||
bool MusicPlayerXMI::isReady() {
|
||||
return _driver ? _driver->isReady() : false;
|
||||
bool MusicPlayerXMI::isReady(int8 source) {
|
||||
return _driver ? _driver->isReady(source) : false;
|
||||
}
|
||||
|
||||
void MusicPlayerXMI::updateVolume() {
|
||||
|
|
|
@ -155,7 +155,7 @@ public:
|
|||
if (_milesXmidiTimbres)
|
||||
_milesXmidiTimbres->processXMIDITimbreChunk(timbreListPtr, timbreListSize);
|
||||
};
|
||||
bool isReady() override;
|
||||
bool isReady(int8 source = -1) override;
|
||||
|
||||
void setUserVolume(uint16 volume) override;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue