diff options
author | Vitaly Novichkov <Wohlstand@users.noreply.github.com> | 2019-01-28 18:25:49 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-01-28 18:25:49 +0300 |
commit | 307678d1831434b9565c82960aae3b97d37f71df (patch) | |
tree | fb1ce7d24f72ac4ea98977b3009fe4e7586be342 | |
parent | de2aa6999c38046472bf557b654f296df0903629 (diff) | |
parent | 3bdda83b7d5417785f5df0120d4f2d73af4253d4 (diff) | |
download | libADLMIDI-307678d1831434b9565c82960aae3b97d37f71df.tar.gz libADLMIDI-307678d1831434b9565c82960aae3b97d37f71df.tar.bz2 libADLMIDI-307678d1831434b9565c82960aae3b97d37f71df.zip |
Merge pull request #200 from jpcima/drum-ttl
drum note length extension
-rw-r--r-- | src/adlmidi_midiplay.cpp | 72 | ||||
-rw-r--r-- | src/adlmidi_midiplay.hpp | 27 | ||||
-rw-r--r-- | src/adlmidi_sequencer.cpp | 10 | ||||
-rw-r--r-- | src/midi_sequencer_impl.hpp | 2 |
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 |