diff --git a/libs/ardour/ardour/midi_track.h b/libs/ardour/ardour/midi_track.h index 2af5b4cf87..a12a0c6087 100644 --- a/libs/ardour/ardour/midi_track.h +++ b/libs/ardour/ardour/midi_track.h @@ -47,6 +47,7 @@ public: void realtime_handle_transport_stopped (); void realtime_locate (); + void non_realtime_locate (framepos_t); boost::shared_ptr create_diskstream (); void set_diskstream (boost::shared_ptr); @@ -183,6 +184,9 @@ private: void track_input_active (IOChange, void*); void map_input_active (bool); + /** Update automation controls to reflect any changes in buffers. */ + void update_controls (const BufferSet& bufs); + void filter_channels (BufferSet& bufs, ChannelMode mode, uint32_t mask); /* if mode is ForceChannel, force mask to the lowest set channel or 1 if no diff --git a/libs/ardour/ardour/parameter_types.h b/libs/ardour/ardour/parameter_types.h index 8442d1f1bf..240ad2d956 100644 --- a/libs/ardour/ardour/parameter_types.h +++ b/libs/ardour/ardour/parameter_types.h @@ -24,6 +24,7 @@ #include #include "ardour/types.h" +#include "evoral/Parameter.hpp" #include "evoral/midi_events.h" namespace ARDOUR { @@ -54,6 +55,26 @@ midi_parameter_type(uint8_t status) } } +inline Evoral::Parameter +midi_parameter(const uint8_t* buf, const uint32_t len) +{ + const uint8_t channel = buf[0] & 0x0F; + switch (midi_parameter_type(buf[0])) { + case MidiCCAutomation: + return Evoral::Parameter(MidiCCAutomation, channel, buf[1]); + case MidiPgmChangeAutomation: + return Evoral::Parameter(MidiPgmChangeAutomation, channel); + case MidiChannelPressureAutomation: + return Evoral::Parameter(MidiChannelPressureAutomation, channel); + case MidiPitchBenderAutomation: + return Evoral::Parameter(MidiPitchBenderAutomation, channel); + case MidiSystemExclusiveAutomation: + return Evoral::Parameter(MidiSystemExclusiveAutomation, channel); + default: + return Evoral::Parameter(NullAutomation); + } +} + inline bool parameter_is_midi(AutomationType type) { diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index 770e4da3fe..b8f53a87d0 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -34,6 +34,7 @@ #include "pbd/convert.h" #include "evoral/midi_util.h" +#include "ardour/beats_frames_converter.h" #include "ardour/buffer_set.h" #include "ardour/debug.h" #include "ardour/delivery.h" @@ -42,6 +43,7 @@ #include "ardour/midi_diskstream.h" #include "ardour/midi_playlist.h" #include "ardour/midi_port.h" +#include "ardour/midi_region.h" #include "ardour/midi_track.h" #include "ardour/parameter_types.h" #include "ardour/port.h" @@ -318,6 +320,20 @@ MidiTrack::set_state_part_two () return; } +void +MidiTrack::update_controls(const BufferSet& bufs) +{ + const MidiBuffer& buf = bufs.get_midi(0); + for (MidiBuffer::const_iterator e = buf.begin(); e != buf.end(); ++e) { + const Evoral::MIDIEvent& ev = *e; + const Evoral::Parameter param = midi_parameter(ev.buffer(), ev.size()); + const boost::shared_ptr control = this->control(param); + if (control) { + control->set_double(ev.value(), _session.transport_frame(), false); + } + } +} + /** @param need_butler to be set to true if this track now needs the butler, otherwise it can be left alone * or set to false. */ @@ -470,6 +486,42 @@ MidiTrack::realtime_handle_transport_stopped () } } +void +MidiTrack::non_realtime_locate (framepos_t pos) +{ + Track::non_realtime_locate(pos); + + boost::shared_ptr playlist = midi_diskstream()->midi_playlist(); + if (!playlist) { + return; + } + + /* Get the top unmuted region at this position. */ + boost::shared_ptr region = boost::dynamic_pointer_cast( + playlist->top_unmuted_region_at(pos)); + if (!region) { + return; + } + + Glib::Threads::Mutex::Lock lm (_control_lock, Glib::Threads::TRY_LOCK); + if (!lm.locked()) { + return; + } + + /* Update track controllers based on its "automation". */ + const framepos_t origin = region->position() - region->start(); + BeatsFramesConverter bfc(_session.tempo_map(), origin); + for (Controls::const_iterator c = _controls.begin(); c != _controls.end(); ++c) { + boost::shared_ptr tcontrol; + boost::shared_ptr rcontrol; + if ((tcontrol = boost::dynamic_pointer_cast(c->second)) && + (rcontrol = region->control(tcontrol->parameter()))) { + const Evoral::Beats pos_beats = bfc.from(pos - origin); + tcontrol->set_value(rcontrol->list()->eval(pos_beats.to_double())); + } + } +} + void MidiTrack::push_midi_input_to_step_edit_ringbuffer (framecnt_t nframes) { @@ -539,6 +591,8 @@ MidiTrack::write_out_of_band_data (BufferSet& bufs, framepos_t /*start*/, framep { MidiBuffer& buf (bufs.get_midi (0)); + update_controls (bufs); + // Append immediate events if (_immediate_events.read_space()) { diff --git a/libs/evoral/evoral/MIDIEvent.hpp b/libs/evoral/evoral/MIDIEvent.hpp index 9b1d72c400..f0c9c74589 100644 --- a/libs/evoral/evoral/MIDIEvent.hpp +++ b/libs/evoral/evoral/MIDIEvent.hpp @@ -104,6 +104,21 @@ public: return this->size() == 10 && this->_buf[0] == 0xf0 && this->_buf[1] == 0x7f && this->_buf[3] == 0x01 && this->_buf[4] == 0x01; } + + inline uint16_t value() const { + switch (type()) { + case MIDI_CMD_CONTROL: + return cc_value(); + case MIDI_CMD_BENDER: + return pitch_bender_value(); + case MIDI_CMD_NOTE_PRESSURE: + return aftertouch(); + case MIDI_CMD_CHANNEL_PRESSURE: + return channel_pressure(); + default: + return 0; + } + } }; } // namespace Evoral