MIDI: Use driver-specific impl. for stopping all notes
When the MIDI parser wanted to stop all notes on a MIDI device, it would send an All Notes Off message and, optionally, a Sustain off message on all 16 MIDI channels. This is inefficient for devices like the MT-32, which uses only 9 channels. The MT-32 hardware is also quite slow, so it is more responsive if it has to process less messages. I've added a generic stopAllNotes message to the MIDI driver using the old method, which can be overriden by more specific implementations in subclasses. The Miles MIDI driver implementation sends All Notes Off only on the channels used by the MT-32. There are probably also more efficient implementations possible for f.e. AdLib.
This commit is contained in:
parent
0856c04bfa
commit
315bbc7b5b
8 changed files with 42 additions and 13 deletions
|
@ -818,6 +818,14 @@ void MidiDriver_BASE::send(int8 source, byte status, byte firstOp, byte secondOp
|
|||
send(source, status | ((uint32)firstOp << 8) | ((uint32)secondOp << 16));
|
||||
}
|
||||
|
||||
void MidiDriver_BASE::stopAllNotes(bool stopSustainedNotes) {
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
send(0xB0 | i, 0x7B, 0); // All notes off
|
||||
if (stopSustainedNotes)
|
||||
send(0xB0 | i, 0x40, 0); // Also send a sustain off event (bug #3116608)
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver::midiDriverCommonSend(uint32 b) {
|
||||
if (_midiDumpEnable) {
|
||||
midiDumpDo(b);
|
||||
|
|
|
@ -160,6 +160,24 @@ public:
|
|||
*/
|
||||
virtual void metaEvent(int8 source, byte type, byte *data, uint16 length) { metaEvent(type, data, length); }
|
||||
|
||||
/**
|
||||
* Stops all currently active notes. Specify stopSustainedNotes if
|
||||
* the MIDI data makes use of the sustain controller to also stop
|
||||
* sustained notes.
|
||||
*
|
||||
* Usually, the MIDI parser tracks active notes and terminates them
|
||||
* when playback is stopped. This method should be used as a backup
|
||||
* to silence the MIDI output in case the MIDI parser makes a
|
||||
* mistake when tracking acive notes. It can also be used when
|
||||
* quitting or pausing a game.
|
||||
*
|
||||
* By default, this method sends an All Notes Off message and, if
|
||||
* stopSustainedNotes is true, a Sustain off message on all MIDI
|
||||
* channels. Driver implementations can override this if they want
|
||||
* to implement this functionality in a different way.
|
||||
*/
|
||||
virtual void stopAllNotes(bool stopSustainedNotes = false);
|
||||
|
||||
/**
|
||||
* A driver implementation might need time to prepare playback of
|
||||
* a track. Use this function to check if the driver is ready to
|
||||
|
|
|
@ -330,12 +330,7 @@ void MidiParser::allNotesOff() {
|
|||
if (!_disableAllNotesOffMidiEvents) {
|
||||
// To be sure, send an "All Note Off" event (but not all MIDI devices
|
||||
// support this...).
|
||||
|
||||
for (i = 0; i < 16; ++i) {
|
||||
sendToDriver(0xB0 | i, 0x7b, 0); // All notes off
|
||||
if (_sendSustainOffOnNotesOff)
|
||||
sendToDriver(0xB0 | i, 0x40, 0); // Also send a sustain off event (bug #3116608)
|
||||
}
|
||||
_driver->stopAllNotes(_sendSustainOffOnNotesOff);
|
||||
}
|
||||
|
||||
memset(_activeNotes, 0, sizeof(_activeNotes));
|
||||
|
|
|
@ -169,8 +169,7 @@ public:
|
|||
*/
|
||||
void setSourceVolume(uint8 source, uint16 volume);
|
||||
|
||||
/** Stops all notes currently playing on the MIDI device. */
|
||||
void allNotesOff();
|
||||
void stopAllNotes(bool stopSustainedNotes = false) override;
|
||||
|
||||
MidiChannel *allocateChannel() override {
|
||||
if (_driver)
|
||||
|
|
|
@ -784,12 +784,15 @@ void MidiDriver_Miles_Midi::stopNotesOnChannel(uint8 outputChannelNumber) {
|
|||
}
|
||||
}
|
||||
|
||||
void MidiDriver_Miles_Midi::allNotesOff() {
|
||||
void MidiDriver_Miles_Midi::stopAllNotes(bool stopSustainedNotes) {
|
||||
for (int i = 0; i < MILES_MIDI_CHANNEL_COUNT; ++i) {
|
||||
if (!isOutputChannelUsed(i))
|
||||
continue;
|
||||
_driver->send(0xB0 | i, MILES_CONTROLLER_SUSTAIN, 0);
|
||||
_midiChannels[i].currentData.sustain = false;
|
||||
|
||||
if (stopSustainedNotes) {
|
||||
_driver->send(0xB0 | i, MILES_CONTROLLER_SUSTAIN, 0);
|
||||
_midiChannels[i].currentData.sustain = false;
|
||||
}
|
||||
_driver->send(0xB0 | i, MILES_CONTROLLER_ALL_NOTES_OFF, 0);
|
||||
_midiChannels[i].activeNotes = 0;
|
||||
}
|
||||
|
|
|
@ -458,6 +458,11 @@ void MusicPlayerXMI::metaEvent(int8 source, byte type, byte *data, uint16 length
|
|||
}
|
||||
}
|
||||
|
||||
void MusicPlayerXMI::stopAllNotes(bool stopSustainedNotes) {
|
||||
if (_driver)
|
||||
_driver->stopAllNotes(stopSustainedNotes);
|
||||
}
|
||||
|
||||
bool MusicPlayerXMI::isReady() {
|
||||
return _driver ? _driver->isReady() : false;
|
||||
}
|
||||
|
|
|
@ -128,6 +128,7 @@ public:
|
|||
|
||||
void send(int8 source, uint32 b) override;
|
||||
void metaEvent(int8 source, byte type, byte *data, uint16 length) override;
|
||||
void stopAllNotes(bool stopSustainedNotes) override;
|
||||
void processXMIDITimbreChunk(const byte *timbreListPtr, uint32 timbreListSize) override {
|
||||
if (_milesMidiDriver)
|
||||
_milesMidiDriver->processXMIDITimbreChunk(timbreListPtr, timbreListSize);
|
||||
|
|
|
@ -89,7 +89,7 @@ SoundMidiPC::~SoundMidiPC() {
|
|||
delete _music;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
delete _sfx[i];
|
||||
_output->allNotesOff();
|
||||
_output->stopAllNotes();
|
||||
|
||||
delete _output; // This automatically frees _driver (!)
|
||||
|
||||
|
@ -356,7 +356,7 @@ void SoundMidiPC::pause(bool paused) {
|
|||
for (int i = 0; i < 3; i++)
|
||||
_sfx[i]->pausePlaying();
|
||||
if (_output)
|
||||
_output->allNotesOff();
|
||||
_output->stopAllNotes();
|
||||
} else {
|
||||
_music->resumePlaying();
|
||||
for (int i = 0; i < 3; ++i)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue