diff options
author | Vitaly Novichkov <admin@wohlnet.ru> | 2018-06-23 06:40:27 +0300 |
---|---|---|
committer | Vitaly Novichkov <admin@wohlnet.ru> | 2018-06-23 06:40:27 +0300 |
commit | f6c6b70c488ba392236e1a4f5d2a32ceda00bf24 (patch) | |
tree | 8b5fc04dbb466a2635e422c894de7257b53dffbd /src | |
parent | be42c3c566c6f8b72f3ae9b526571a2c58326379 (diff) | |
download | libADLMIDI-f6c6b70c488ba392236e1a4f5d2a32ceda00bf24.tar.gz libADLMIDI-f6c6b70c488ba392236e1a4f5d2a32ceda00bf24.tar.bz2 libADLMIDI-f6c6b70c488ba392236e1a4f5d2a32ceda00bf24.zip |
Added CC66 Sostenuto support!
Sostenuto is the pedal hold that does hold of only currently playing notes and doesn't holds notes are will begin after turning of sostenuto on, unlike to the Pedal (CC64 Sustain) event.
Diffstat (limited to 'src')
-rw-r--r-- | src/adlmidi_midiplay.cpp | 71 | ||||
-rw-r--r-- | src/adlmidi_private.hpp | 20 |
2 files changed, 65 insertions, 26 deletions
diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 8664955..d4bb4ff 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -586,8 +586,16 @@ void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) break; case 64: // Enable/disable sustain - Ch[channel].sustain = value; - if(!value) KillSustainingNotes(channel); + Ch[channel].sustain = (value >= 64); + if(!Ch[channel].sustain) + KillSustainingNotes(channel, -1, AdlChannel::LocationData::Sustain_Pedal); + break; + + case 66: // Enable/disable sostenuto + if(value >= 64) //Find notes and mark them as sostenutoed + MarkSostenutoNotes(channel); + else + KillSustainingNotes(channel, -1, AdlChannel::LocationData::Sustain_Sostenuto); break; case 11: // Change expression (another volume factor) @@ -607,7 +615,7 @@ void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) Ch[channel].resetAllControllers(); NoteUpdate_All(channel, Upd_Pan + Upd_Volume + Upd_Pitch); // Kill all sustained notes - KillSustainingNotes(channel); + KillSustainingNotes(channel, -1, AdlChannel::LocationData::Sustain_ANY); break; case 120: // All sounds off @@ -936,7 +944,7 @@ bool MIDIplay::doYamahaSysEx(unsigned dev, const uint8_t *data, size_t size) void MIDIplay::realTime_panic() { Panic(); - KillSustainingNotes(-1, -1); + KillSustainingNotes(-1, -1, AdlChannel::LocationData::Sustain_ANY); } void MIDIplay::realTime_deviceSwitch(size_t track, const char *data, size_t length) @@ -1006,7 +1014,7 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, AdlChannel::LocationData *d = ch[c].users_find_or_create(my_loc); if(d) // inserts if necessary { - d->sustained = false; + d->sustained = AdlChannel::LocationData::Sustain_None; d->vibdelay = 0; d->fixed_sustain = (ains.ms_sound_kon == static_cast<uint16_t>(adlNoteOnMaxTime)); d->kon_time_until_neglible = ains.ms_sound_kon; @@ -1025,17 +1033,17 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, if(props_mask & Upd_Off) // note off { - if(Ch[MidCh].sustain == 0) + if(!Ch[MidCh].sustain) { AdlChannel::LocationData *k = ch[c].users_find(my_loc); - - if(k) + bool do_erase_user = (k && ((k->sustained & AdlChannel::LocationData::Sustain_Sostenuto) == 0)); + if(do_erase_user) ch[c].users_erase(k); if(hooks.onNote) hooks.onNote(hooks.onNote_userData, c, noteTone, midiins, 0, 0.0); - if(ch[c].users_empty()) + if(do_erase_user && ch[c].users_empty()) { opl.NoteOff(c); if(props_mask & Upd_Mute) // Mute the note @@ -1055,7 +1063,7 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, // Also will avoid overwriting it very soon. AdlChannel::LocationData *d = ch[c].users_find_or_create(my_loc); if(d) - d->sustained = true; // note: not erased! + d->sustained |= AdlChannel::LocationData::Sustain_Pedal; // note: not erased! if(hooks.onNote) hooks.onNote(hooks.onNote_userData, c, noteTone, midiins, -1, 0.0); } @@ -1159,7 +1167,7 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, AdlChannel::LocationData *d = ch[c].users_find(my_loc); // Don't bend a sustained note - if(!d || !d->sustained) + if(!d || (d->sustained == AdlChannel::LocationData::Sustain_None)) { double midibend = Ch[MidCh].bend * Ch[MidCh].bendsense; double bend = midibend + ins.ains.finetune; @@ -1212,7 +1220,7 @@ int64_t MIDIplay::CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteI { s -= 4000; - if(!j->sustained) + if(j->sustained == AdlChannel::LocationData::Sustain_None) s -= j->kon_time_until_neglible; else s -= (j->kon_time_until_neglible / 2); @@ -1258,7 +1266,7 @@ int64_t MIDIplay::CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteI for(AdlChannel::LocationData *m = ch[c2].users_first; m; m = m->next) { - if(m->sustained) continue; + if(m->sustained != AdlChannel::LocationData::Sustain_None) continue; if(m->vibdelay >= 200) continue; if(m->ins != j->ins) continue; n_evacuation_stations += 1; @@ -1282,7 +1290,7 @@ void MIDIplay::PrepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteInfo AdlChannel::LocationData *j = jnext; jnext = jnext->next; - if(!j->sustained) + if(j->sustained == AdlChannel::LocationData::Sustain_None) { // Collision: Kill old note, // UNLESS we're going to do arpeggio @@ -1307,7 +1315,7 @@ void MIDIplay::PrepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteInfo // Kill all sustained notes on this channel // Don't keep them for arpeggio, because arpeggio requires // an intact "activenotes" record. This is a design flaw. - KillSustainingNotes(-1, static_cast<int32_t>(c)); + KillSustainingNotes(-1, static_cast<int32_t>(c), AdlChannel::LocationData::Sustain_ANY); // Keyoff the channel so that it can be retriggered, // unless the new note will be introduced as just an arpeggio. @@ -1390,7 +1398,7 @@ void MIDIplay::Panic() } } -void MIDIplay::KillSustainingNotes(int32_t MidCh, int32_t this_adlchn) +void MIDIplay::KillSustainingNotes(int32_t MidCh, int32_t this_adlchn, uint8_t sustain_type) { uint32_t first = 0, last = opl.NumChannels; @@ -1400,9 +1408,10 @@ void MIDIplay::KillSustainingNotes(int32_t MidCh, int32_t this_adlchn) last = first + 1; } - for(unsigned c = first; c < last; ++c) + for(uint32_t c = first; c < last; ++c) { - if(ch[c].users_empty()) continue; // Nothing to do + if(ch[c].users_empty()) + continue; // Nothing to do for(AdlChannel::LocationData *jnext = ch[c].users_first; jnext;) { @@ -1410,12 +1419,14 @@ void MIDIplay::KillSustainingNotes(int32_t MidCh, int32_t this_adlchn) jnext = jnext->next; if((MidCh < 0 || j->loc.MidCh == MidCh) - && j->sustained) + && ((j->sustained & sustain_type) != 0)) { int midiins = '?'; if(hooks.onNote) hooks.onNote(hooks.onNote_userData, (int)c, j->loc.note, midiins, 0, 0.0); - ch[c].users_erase(j); + j->sustained &= ~sustain_type; + if((j->sustained == AdlChannel::LocationData::Sustain_None)) + ch[c].users_erase(j);//Remove only when note is clean from any holders } } @@ -1425,6 +1436,24 @@ void MIDIplay::KillSustainingNotes(int32_t MidCh, int32_t this_adlchn) } } +void MIDIplay::MarkSostenutoNotes(int32_t MidCh) +{ + uint32_t first = 0, last = opl.NumChannels; + for(uint32_t c = first; c < last; ++c) + { + if(ch[c].users_empty()) + continue; // Nothing to do + + for(AdlChannel::LocationData *jnext = ch[c].users_first; jnext;) + { + AdlChannel::LocationData *j = jnext; + jnext = jnext->next; + if((j->loc.MidCh == MidCh) && (j->sustained == AdlChannel::LocationData::Sustain_None)) + j->sustained |= AdlChannel::LocationData::Sustain_Sostenuto; + } + } +} + void MIDIplay::SetRPN(unsigned MidCh, unsigned value, bool MSB) { bool nrpn = Ch[MidCh].nrpn; @@ -1572,7 +1601,7 @@ retry_arpeggio: n = 0; n < count; ++n) i = i->next; - if(i->sustained == false) + if(i->sustained == AdlChannel::LocationData::Sustain_None) { if(i->kon_time_until_neglible <= 0l) { diff --git a/src/adlmidi_private.hpp b/src/adlmidi_private.hpp index ec194c6..1d68bae 100644 --- a/src/adlmidi_private.hpp +++ b/src/adlmidi_private.hpp @@ -349,8 +349,9 @@ public: uint8_t bank_lsb, bank_msb; uint8_t patch; uint8_t volume, expression; - uint8_t panning, vibrato, aftertouch, sustain; + uint8_t panning, vibrato, aftertouch; uint16_t portamento; + bool sustain; bool portamentoEnable; int8_t portamentoSource; // note number or -1 double portamentoRate; @@ -564,7 +565,7 @@ public: updateBendSensitivity(); volume = 100; expression = 127; - sustain = 0; + sustain = false; vibrato = 0; aftertouch = 0; std::memset(noteAftertouch, 0, 128); @@ -612,8 +613,14 @@ public: { LocationData *prev, *next; Location loc; - bool sustained; - char ____padding[7]; + enum { + Sustain_None = 0x00, + Sustain_Pedal = 0x01, + Sustain_Sostenuto = 0x02, + Sustain_ANY = Sustain_Pedal | Sustain_Sostenuto, + }; + uint8_t sustained; + char ____padding[6]; MIDIchannel::NoteInfo::Phys ins; // a copy of that in phys[] //! Has fixed sustain, don't iterate "on" timeout bool fixed_sustain; @@ -1011,7 +1018,10 @@ private: AdlChannel::LocationData *j, MIDIchannel::activenoteiterator i); void Panic(); - void KillSustainingNotes(int32_t MidCh = -1, int32_t this_adlchn = -1); + void KillSustainingNotes(int32_t MidCh = -1, + int32_t this_adlchn = -1, + uint8_t sustain_type = AdlChannel::LocationData::Sustain_ANY); + void MarkSostenutoNotes(int32_t MidCh = -1); void SetRPN(unsigned MidCh, unsigned value, bool MSB); void UpdatePortamento(unsigned MidCh); void NoteUpdate_All(uint16_t MidCh, unsigned props_mask); |