+void
+MidiRegion::model_contents_changed ()
+{
+ send_change (PropertyChange (Properties::midi_data));
+}
+
+void
+MidiRegion::model_automation_state_changed (Evoral::Parameter const & p)
+{
+ /* Update our filtered parameters list after a change to a parameter's AutoState */
+
+ boost::shared_ptr<AutomationControl> ac = model()->automation_control (p);
+ assert (ac);
+
+ if (ac->alist()->automation_state() == Play) {
+ _filtered_parameters.erase (p);
+ } else {
+ _filtered_parameters.insert (p);
+ }
+
+ /* the source will have an iterator into the model, and that iterator will have been set up
+ for a given set of filtered_parameters, so now that we've changed that list we must invalidate
+ the iterator.
+ */
+ Glib::Threads::Mutex::Lock lm (midi_source(0)->mutex());
+ midi_source(0)->invalidate ();
+}
+
+/** This is called when a trim drag has resulted in a -ve _start time for this region.
+ * Fix it up by adding some empty space to the source.
+ */
+void
+MidiRegion::fix_negative_start ()
+{
+ BeatsFramesConverter c (_session.tempo_map(), _position);
+
+ model()->insert_silence_at_start (c.from (-_start));
+ _start = 0;
+ _start_beats = 0;
+}
+
+/** Transpose the notes in this region by a given number of semitones */
+void
+MidiRegion::transpose (int semitones)
+{
+ BeatsFramesConverter c (_session.tempo_map(), _start);
+ model()->transpose (c.from (_start), c.from (_start + _length), semitones);
+}
+
+void
+MidiRegion::set_start_internal (framecnt_t s)
+{
+ Region::set_start_internal (s);
+ set_start_beats_from_start_frames ();
+}