aboutsummaryrefslogtreecommitdiff
path: root/src/adlmidi_midiplay.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/adlmidi_midiplay.cpp')
-rw-r--r--src/adlmidi_midiplay.cpp72
1 files changed, 63 insertions, 9 deletions
diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp
index 8b2f185..011f74a 100644
--- a/src/adlmidi_midiplay.cpp
+++ b/src/adlmidi_midiplay.cpp
@@ -26,6 +26,10 @@
#include "adlmidi_private.hpp"
#include "midi_sequencer.hpp"
+// Minimum life time of percussion notes
+static const double drum_note_min_time = 0.03;
+
+
// Mapping from MIDI volume level to OPL level value.
static const uint_fast32_t DMX_volume_mapping_table[128] =
@@ -255,8 +259,41 @@ void MIDIplay::resetMIDI()
void MIDIplay::TickIterators(double s)
{
Synth &synth = *m_synth;
- for(uint16_t c = 0; c < synth.m_numChannels; ++c)
- m_chipChannels[c].addAge(static_cast<int64_t>(s * 1e6));
+ for(uint32_t c = 0, n = synth.m_numChannels; c < n; ++c)
+ {
+ AdlChannel &ch = m_chipChannels[c];
+ ch.addAge(static_cast<int64_t>(s * 1e6));
+ }
+
+ // Resolve "hell of all times" of too short drum notes
+ for(size_t c = 0, n = m_midiChannels.size(); c < n; ++c)
+ {
+ MIDIchannel &ch = m_midiChannels[c];
+ if(ch.extended_note_count == 0)
+ continue;
+
+ for(MIDIchannel::notes_iterator inext = ch.activenotes.begin(); !inext.is_end();)
+ {
+ MIDIchannel::notes_iterator i(inext++);
+ MIDIchannel::NoteInfo &ni = i->value;
+
+ double ttl = ni.ttl;
+ if(ttl <= 0)
+ continue;
+
+ ni.ttl = ttl = ttl - s;
+ if(ttl <= 0)
+ {
+ --ch.extended_note_count;
+ if(ni.isOnExtendedLifeTime)
+ {
+ noteUpdate(c, i, Upd_Off);
+ ni.isOnExtendedLifeTime = false;
+ }
+ }
+ }
+ }
+
updateVibrato(s);
updateArpeggio(s);
#if !defined(ADLMIDI_AUDIO_TICK_HANDLER)
@@ -308,7 +345,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity)
if(static_cast<size_t>(channel) > m_midiChannels.size())
channel = channel % 16;
- noteOff(channel, note);
+ noteOff(channel, note, velocity != 0);
// On Note on, Keyoff the note first, just in case keyoff
// was omitted; this fixes Dance of sugar-plum fairy
// by Microsoft. Now that we've done a Keyoff,
@@ -472,6 +509,8 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity)
MIDIchannel::notes_iterator i = midiChan.ensure_find_or_create_activenote(note);
MIDIchannel::NoteInfo &dummy = i->value;
dummy.isBlank = true;
+ dummy.isOnExtendedLifeTime = false;
+ dummy.ttl = 0;
dummy.ains = NULL;
dummy.chip_channels_count = 0;
// Record the last note on MIDI channel as source of portamento
@@ -576,6 +615,8 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity)
ni.midiins = midiins;
ni.isPercussion = isPercussion;
ni.isBlank = isBlankNote;
+ ni.isOnExtendedLifeTime = false;
+ ni.ttl = 0;
ni.ains = ains;
ni.chip_channels_count = 0;
@@ -595,6 +636,13 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity)
++midiChan.gliding_note_count;
}
+ // Enable life time extension on percussion note
+ if (isPercussion)
+ {
+ ni.ttl = drum_note_min_time;
+ ++midiChan.extended_note_count;
+ }
+
for(unsigned ccount = 0; ccount < MIDIchannel::NoteInfo::MaxNumPhysChans; ++ccount)
{
int32_t c = adlchannel[ccount];
@@ -1336,8 +1384,7 @@ void MIDIplay::noteUpdate(size_t midCh,
if(info.chip_channels_count == 0)
{
- if(info.glideRate != HUGE_VAL)
- --m_midiChannels[midCh].gliding_note_count;
+ m_midiChannels[midCh].cleanupNote(i);
m_midiChannels[midCh].activenotes.erase(i);
}
}
@@ -1686,12 +1733,19 @@ void MIDIplay::updatePortamento(size_t midCh)
}
-void MIDIplay::noteOff(size_t midCh, uint8_t note)
+void MIDIplay::noteOff(size_t midCh, uint8_t note, bool forceNow)
{
- MIDIchannel::notes_iterator
- i = m_midiChannels[midCh].find_activenote(note);
+ MIDIchannel &ch = m_midiChannels[midCh];
+ MIDIchannel::notes_iterator i = ch.find_activenote(note);
+
if(!i.is_end())
- noteUpdate(midCh, i, Upd_Off);
+ {
+ MIDIchannel::NoteInfo &ni = i->value;
+ if(forceNow || ni.ttl <= 0)
+ noteUpdate(midCh, i, Upd_Off);
+ else
+ ni.isOnExtendedLifeTime = true;
+ }
}