diff --git a/libs/ardour/ardour/midi_state_tracker.h b/libs/ardour/ardour/midi_state_tracker.h index 91d081649d..984baf8aeb 100644 --- a/libs/ardour/ardour/midi_state_tracker.h +++ b/libs/ardour/ardour/midi_state_tracker.h @@ -29,6 +29,7 @@ namespace Evoral { template class EventSink; +template class EventList; } namespace ARDOUR { @@ -87,6 +88,7 @@ class LIBARDOUR_API MidiStateTracker : public MidiNoteTracker void reset (); void flush (MidiBuffer&, samplepos_t, bool reset); + void resolve_state (Evoral::EventSink&, Evoral::EventList const&, samplepos_t time, bool reset = true); private: uint8_t program[16]; diff --git a/libs/ardour/midi_playlist.cc b/libs/ardour/midi_playlist.cc index f556767dde..93b21f79db 100644 --- a/libs/ardour/midi_playlist.cc +++ b/libs/ardour/midi_playlist.cc @@ -352,7 +352,9 @@ MidiPlaylist::render (MidiChannelFilter* filter) Evoral::EventList evlist; if (all_transparent) { + DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1 regions to read\n", regs.size())); + for (auto i = regs.rbegin(); i != regs.rend(); ++i) { boost::shared_ptr mr = *i; DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("render from %1\n", mr->name())); @@ -388,14 +390,16 @@ MidiPlaylist::render (MidiChannelFilter* filter) } tmp.sort (cmp); - MidiNoteTracker mtr; + MidiStateTracker mtr; + Evoral::EventList const slist (evlist); + for (Evoral::EventList::iterator e = tmp.begin(); e != tmp.end(); ++e) { Evoral::Event* ev (*e); timepos_t t (ev->time()); if (ev->event_type () == Evoral::NO_EVENT) { /* reached region bound of an opaque region above this region. */ - mtr.resolve_notes (evlist, ev->time()); + mtr.resolve_state (evlist, slist, ev->time()); } else if (region_is_audible_at (mr, t)) { /* no opaque region above this event */ uint8_t* evbuf = ev->buffer(); @@ -403,15 +407,10 @@ MidiPlaylist::render (MidiChannelFilter* filter) ; /* skip note off */ } else { evlist.write (ev->time(), ev->event_type(), ev->size(), evbuf); - if (3 == ev->size()) { - mtr.track (evbuf); - } + mtr.track (evbuf); } } else { - /* there is an opaque region above this event, skip this event, - * and for good measure resolve notes. - */ - mtr.resolve_notes (evlist, ev->time()); + /* there is an opaque region above this event, skip this event. */ } delete ev; } diff --git a/libs/ardour/midi_state_tracker.cc b/libs/ardour/midi_state_tracker.cc index 2b713d8c9e..80fd5a5f87 100644 --- a/libs/ardour/midi_state_tracker.cc +++ b/libs/ardour/midi_state_tracker.cc @@ -22,7 +22,7 @@ #include "pbd/compose.h" -#include "evoral/EventSink.h" +#include "evoral/EventList.h" #include "ardour/debug.h" #include "ardour/midi_source.h" @@ -244,6 +244,7 @@ MidiStateTracker::reset () for (size_t n = 0; n < n_channels; ++n) { program[n] = 0x80; + bender[n] = 0x8000; } for (size_t chn = 0; chn < n_channels; ++chn) { @@ -324,6 +325,7 @@ MidiStateTracker::track (const uint8_t* evbuf) break; case MIDI_CMD_BENDER: + bender[chan] = ((evbuf[2]<<7) | evbuf[1]) & 0x3fff; break; case MIDI_CMD_COMMON_RESET: @@ -369,3 +371,137 @@ MidiStateTracker::flush (MidiBuffer& dst, samplepos_t time, bool reset) } } } + +/* return 0 if event is not found + * return 1 if event is found before time t + * return -1 if event is found at time t + */ +static int +find_event (Evoral::EventList const& evlist, samplepos_t time, uint8_t* buf) +{ + for (auto const& e : evlist) { + Evoral::Event* ev (e); + timepos_t t (ev->time ()); + if (t > time) { + break; + } + uint8_t const* evbuf = ev->buffer (); + if (evbuf[0] == buf[0]) { + if (buf[1] != 0x80 && evbuf[1] != buf[1]) { + continue; + } + for (uint32_t i = 1; i < ev->size (); ++i) { + buf[i] = evbuf[i]; + } + return t == time ? -1 : 1; + } + } + return 0; +} + +void +MidiStateTracker::resolve_state (Evoral::EventSink& dst, Evoral::EventList const& evlist, samplepos_t time, bool reset) +{ + /* XXX implement me */ + + uint8_t buf[3]; + const size_t n_channels = 16; + const size_t n_controls = 127; + + resolve_notes (dst, time); + + for (size_t chn = 0; chn < n_channels; ++chn) { + + /* restore CC */ + for (size_t ctl = 0; ctl < n_controls; ++ctl) { + if ((control[chn][ctl] & 0x80) == 0) { + if (reset) { + control[chn][ctl] = 0x80; + } + buf[0] = MIDI_CMD_CONTROL | chn; + buf[1] = ctl; + switch (find_event (evlist, time, buf)) { + case 1: + /* (event found before tme) + * restore prior CC (notably bank select) + * + * Layer 1: [CX....] [.......] + * Layer 2: [.....CY.......] + * restore CX: ^ + */ + dst.write (time, Evoral::MIDI_EVENT, 3, buf); + break; + case 0: + /* (no event was found before, or at tme) + * The goal is to reset a conroller, unless there already + * is an CC event at the start of above region (case -1:). + * + * Layer 1: [......] [CZ......] + * Layer 2: [.....CY.......] + * reset, unless CZ exist: ^ + */ + switch (ctl) { + /* clang-format off */ + case 0x01: buf[2] = 0x00; break; /* mod wheel MSB */ + case 0x21: buf[2] = 0x00; break; /* mod wheel LSB */ + case 0x02: buf[2] = 0x00; break; /* breath MSB */ + case 0x22: buf[2] = 0x00; break; /* breath LSB */ + case 0x07: buf[2] = 0x7f; break; /* volume MSB */ + case 0x27: buf[2] = 0x7f; break; /* volume LSB */ + case 0x08: buf[2] = 0x40; break; /* balance MSB */ + case 0x28: buf[2] = 0x00; break; /* balance LSB */ + case 0x0a: buf[2] = 0x40; break; /* pan MSB */ + case 0x2a: buf[2] = 0x00; break; /* pan LSB */ + case 0x40: buf[2] = 0x00; break; /* sustain */ + case 0x41: buf[2] = 0x00; break; /* portamento */ + case 0x42: buf[2] = 0x00; break; /* sostenuto */ + case 0x43: buf[2] = 0x00; break; /* soft pedal */ + case 0x44: buf[2] = 0x00; break; /* legato switch */ + /* clang-format on */ + default: + /* do not reset other controls */ + continue; + } + dst.write (time, Evoral::MIDI_EVENT, 3, buf); + break; + + default: + /* do nothing */ + break; + } + } + } + + /* If the program was modified, replay the most recent event found in evlist before *time*. + * + * Layer 1: [P1....] [.......] + * Layer 2: [.....P2.......] + * restore P1: ^ + */ + if ((program[chn] & 0x80) == 0) { + buf[0] = MIDI_CMD_PGM_CHANGE | chn; + buf[1] = 0x80; + if (find_event (evlist, time, buf) > 0) { + dst.write (time, Evoral::MIDI_EVENT, 2, buf); + } + if (reset) { + program[chn] = 0x80; + } + } + + /* reset pitch-bend */ + if ((bender[chn] & 0x8000) == 0) { + buf[0] = MIDI_CMD_BENDER | chn; + buf[1] = 0x80; + /* .. unless there is a PB event at the start */ + if (find_event (evlist, time, buf) >= 0) { + buf[1] = 0x00; + buf[2] = 0x40; + dst.write (time, Evoral::MIDI_EVENT, 3, buf); + } + if (reset) { + bender[chn] = 0x8000; + } + } + } +}