From a6256daf01e9ffe1fcb32557f9f9712432f855fd Mon Sep 17 00:00:00 2001 From: JP Cimalando Date: Thu, 24 Jan 2019 06:47:36 +0100 Subject: drum note length extension --- src/adlmidi_midiplay.cpp | 50 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 8b2f185..6261181 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,33 @@ 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(s * 1e6)); + for(uint32_t c = 0, n = synth.m_numChannels; c < n; ++c) + { + AdlChannel &ch = m_chipChannels[c]; + ch.addAge(static_cast(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]; + 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) + { + ni.ttl = ttl = ttl - s; + if(ni.isOnExtendedLifeTime && ttl <= 0) + { + noteUpdate(c, i, Upd_Off); + ni.isOnExtendedLifeTime = false; + } + } + } + } + updateVibrato(s); updateArpeggio(s); #if !defined(ADLMIDI_AUDIO_TICK_HANDLER) @@ -472,6 +501,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 +607,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 = isPercussion ? drum_note_min_time : 0; ni.ains = ains; ni.chip_channels_count = 0; @@ -1688,10 +1721,17 @@ void MIDIplay::updatePortamento(size_t midCh) void MIDIplay::noteOff(size_t midCh, uint8_t note) { - 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(ni.ttl <= 0) + noteUpdate(midCh, i, Upd_Off); + else + ni.isOnExtendedLifeTime = true; + } } -- cgit v1.2.3 From 60bc095f2f431b4e6421307fc7ab62c6cc9ce0c2 Mon Sep 17 00:00:00 2001 From: JP Cimalando Date: Thu, 24 Jan 2019 11:29:29 +0100 Subject: cache the extended note count per channel --- src/adlmidi_midiplay.cpp | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 6261181..f8bd0a2 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -269,15 +269,23 @@ void MIDIplay::TickIterators(double s) 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) + if(ttl <= 0) + continue; + + ni.ttl = ttl = ttl - s; + if(ttl <= 0) { - ni.ttl = ttl = ttl - s; - if(ni.isOnExtendedLifeTime && ttl <= 0) + --ch.extended_note_count; + if(ni.isOnExtendedLifeTime) { noteUpdate(c, i, Upd_Off); ni.isOnExtendedLifeTime = false; @@ -608,7 +616,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) ni.isPercussion = isPercussion; ni.isBlank = isBlankNote; ni.isOnExtendedLifeTime = false; - ni.ttl = isPercussion ? drum_note_min_time : 0; + ni.ttl = 0; ni.ains = ains; ni.chip_channels_count = 0; @@ -628,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]; @@ -1369,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); } } -- cgit v1.2.3 From 3bdda83b7d5417785f5df0120d4f2d73af4253d4 Mon Sep 17 00:00:00 2001 From: JP Cimalando Date: Fri, 25 Jan 2019 08:16:31 +0100 Subject: force note-on to do immediate note killing --- src/adlmidi_midiplay.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index f8bd0a2..011f74a 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -345,7 +345,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) if(static_cast(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, @@ -1733,7 +1733,7 @@ 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 &ch = m_midiChannels[midCh]; MIDIchannel::notes_iterator i = ch.find_activenote(note); @@ -1741,7 +1741,7 @@ void MIDIplay::noteOff(size_t midCh, uint8_t note) if(!i.is_end()) { MIDIchannel::NoteInfo &ni = i->value; - if(ni.ttl <= 0) + if(forceNow || ni.ttl <= 0) noteUpdate(midCh, i, Upd_Off); else ni.isOnExtendedLifeTime = true; -- cgit v1.2.3