aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/adlmidi_midiplay.cpp72
-rw-r--r--src/adlmidi_midiplay.hpp27
-rw-r--r--src/adlmidi_sequencer.cpp10
-rw-r--r--src/midi_sequencer_impl.hpp2
4 files changed, 90 insertions, 21 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;
+ }
}
diff --git a/src/adlmidi_midiplay.hpp b/src/adlmidi_midiplay.hpp
index fd58b13..1067252 100644
--- a/src/adlmidi_midiplay.hpp
+++ b/src/adlmidi_midiplay.hpp
@@ -156,6 +156,10 @@ public:
bool isPercussion;
//! Note that plays missing instrument. Doesn't using any chip channels
bool isBlank;
+ //! Whether releasing and on extended life time defined by TTL
+ bool isOnExtendedLifeTime;
+ //! Time-to-live until release (short percussion note fix)
+ double ttl;
//! Patch selected
const adlinsdata2 *ains;
enum
@@ -250,6 +254,8 @@ public:
char _padding2[5];
//! Count of gliding notes in this channel
unsigned gliding_note_count;
+ //! Count of notes having a TTL countdown in this channel
+ unsigned extended_note_count;
//! Active notes in the channel
pl_list<NoteInfo> activenotes;
@@ -271,7 +277,10 @@ public:
notes_iterator find_or_create_activenote(unsigned note)
{
notes_iterator it = find_activenote(note);
- if(it.is_end()) {
+ if(!it.is_end())
+ cleanupNote(it);
+ else
+ {
NoteInfo ni;
ni.note = note;
it = activenotes.insert(activenotes.end(), ni);
@@ -348,10 +357,23 @@ public:
bendsense = cent * (1.0 / (128 * 8192));
}
+ /**
+ * @brief Clean up the state of the active note before removal
+ */
+ void cleanupNote(notes_iterator i)
+ {
+ NoteInfo &info = i->value;
+ if(info.glideRate != HUGE_VAL)
+ --gliding_note_count;
+ if(info.ttl > 0)
+ --extended_note_count;
+ }
+
MIDIchannel()
: activenotes(128)
{
gliding_note_count = 0;
+ extended_note_count = 0;
reset();
}
};
@@ -941,8 +963,9 @@ private:
* @brief Off the note
* @param midCh MIDI channel
* @param note Note to off
+ * @param forceNow Do not delay the key-off to a later time
*/
- void noteOff(size_t midCh, uint8_t note);
+ void noteOff(size_t midCh, uint8_t note, bool forceNow = false);
/**
* @brief Update processing of vibrato to amount of seconds
diff --git a/src/adlmidi_sequencer.cpp b/src/adlmidi_sequencer.cpp
index e8c7a5d..d78815f 100644
--- a/src/adlmidi_sequencer.cpp
+++ b/src/adlmidi_sequencer.cpp
@@ -141,16 +141,8 @@ double MIDIplay::Tick(double s, double granularity)
MidiSequencer &seqr = *m_sequencer;
double ret = seqr.Tick(s, granularity);
- Synth &synth = *m_synth;
s *= seqr.getTempoMultiplier();
- for(uint16_t c = 0; c < synth.m_numChannels; ++c)
- m_chipChannels[c].addAge(static_cast<int64_t>(s * 1e6));
-
- updateVibrato(s);
- updateArpeggio(s);
-#if !defined(ADLMIDI_AUDIO_TICK_HANDLER)
- updateGlide(s);
-#endif
+ TickIterators(s);
return ret;
}
diff --git a/src/midi_sequencer_impl.hpp b/src/midi_sequencer_impl.hpp
index 446e2e7..2af80ea 100644
--- a/src/midi_sequencer_impl.hpp
+++ b/src/midi_sequencer_impl.hpp
@@ -819,7 +819,7 @@ void BW_MidiSequencer::buildTimeLine(const std::vector<MidiEvent> &tempos,
//Resolve "hell of all times" of too short drum notes:
//move too short percussion note-offs far far away as possible
/********************************************************************************/
-#if 1 //Use this to record WAVEs for comparison before/after implementing of this
+#if 0 //Use this to record WAVEs for comparison before/after implementing of this
if(m_format == Format_MIDI)//Percussion fix is needed for MIDI only, not for IMF/RSXX or CMF
{
//! Minimal real time in seconds