Add MidiRegion _start to the list of things we leave alone on session load.
[ardour.git] / libs / ardour / midi_region.cc
index 0ea0ed6a99d01b59aec87cd263cae1301453d9c0..abaef22e4111d9cd02514a9054a783cbf40fd50f 100644 (file)
@@ -81,7 +81,6 @@ MidiRegion::MidiRegion (const SourceList& srcs)
        , _length_beats (Properties::length_beats, midi_source(0)->length_beats())
 {
        register_properties ();
-
        midi_source(0)->ModelChanged.connect_same_thread (_source_connection, boost::bind (&MidiRegion::model_changed, this));
        model_changed ();
        assert(_name.val().find("/") == string::npos);
@@ -107,7 +106,8 @@ MidiRegion::MidiRegion (boost::shared_ptr<const MidiRegion> other, frameoffset_t
        , _start_beats (Properties::start_beats, Evoral::Beats())
        , _length_beats (Properties::length_beats, other->_length_beats)
 {
-       _start_beats = Evoral::Beats (_session.tempo_map().exact_beat_at_frame ((other->_position + offset), sub_num) - other->beat()) + other->_start_beats;
+       _start_beats = Evoral::Beats (_session.tempo_map().exact_qn_at_frame (other->_position + offset, sub_num) - (other->pulse() * 4.0)) + other->_start_beats;
+
        update_length_beats (sub_num);
        register_properties ();
 
@@ -195,6 +195,7 @@ MidiRegion::clone (boost::shared_ptr<MidiSource> newsrc) const
 
        boost::shared_ptr<MidiRegion> ret (boost::dynamic_pointer_cast<MidiRegion> (RegionFactory::create (newsrc, plist, true)));
        ret->set_beat (beat());
+       ret->set_pulse (pulse());
 
        return ret;
 }
@@ -205,8 +206,15 @@ MidiRegion::post_set (const PropertyChange& pc)
        Region::post_set (pc);
 
        if (pc.contains (Properties::length) && !pc.contains (Properties::length_beats)) {
-               /* update non-musically */
-               update_length_beats (0);
+               /* we're called by Stateful::set_values() which sends a change
+                  only if the value is different from _current.
+                  session load means we can clobber length_beats here in error (not all properties differ from current),
+                  so disallow (this has been set from XML state anyway).
+               */
+               if (!_session.loading()) {
+                       /* update non-musically */
+                       update_length_beats (0);
+               }
        } else if (pc.contains (Properties::start) && !pc.contains (Properties::start_beats)) {
                set_start_beats_from_start_frames ();
        }
@@ -215,7 +223,7 @@ MidiRegion::post_set (const PropertyChange& pc)
 void
 MidiRegion::set_start_beats_from_start_frames ()
 {
-       _start_beats = Evoral::Beats (beat() - _session.tempo_map().beat_at_frame (_position - _start));
+       _start_beats = Evoral::Beats ((pulse() * 4.0) - _session.tempo_map().quarter_note_at_frame (_position - _start));
 }
 
 void
@@ -246,7 +254,7 @@ MidiRegion::update_after_tempo_map_change (bool /* send */)
                /*
                  set _start to new position in tempo map.
 
-                 The user probably expects the region contents to maintain audio position as the 
+                 The user probably expects the region contents to maintain audio position as the
                  tempo changes, but AFAICT this requires modifying the src file to use
                  SMPTE timestamps with the current disk read model (?).
 
@@ -257,10 +265,10 @@ MidiRegion::update_after_tempo_map_change (bool /* send */)
                  For now, the musical position at the region start is retained, but subsequent events
                  will maintain their beat distance according to the map.
                */
-               _start = _position - _session.tempo_map().frame_at_beat (beat() - _start_beats.val().to_double());
+               _start = _position - _session.tempo_map().frame_at_pulse (pulse() - (_start_beats.val().to_double() / 4.0));
 
                /* _length doesn't change for audio-locked regions. update length_beats to match. */
-               _length_beats = Evoral::Beats (_session.tempo_map().beat_at_frame (_position + _length) - _session.tempo_map().beat_at_frame (_position));
+               _length_beats = Evoral::Beats (_session.tempo_map().quarter_note_at_frame (_position + _length) - _session.tempo_map().quarter_note_at_frame (_position));
 
                s_and_l.add (Properties::start);
                s_and_l.add (Properties::length_beats);
@@ -272,7 +280,7 @@ MidiRegion::update_after_tempo_map_change (bool /* send */)
        Region::update_after_tempo_map_change (false);
 
        /* _start has now been updated. */
-       _length = _session.tempo_map().frame_at_beat (beat() + _length_beats.val().to_double()) - _position;
+       _length = _session.tempo_map().frame_at_pulse (pulse() + (_length_beats.val().to_double() / 4.0)) - _position;
 
        if (old_start != _start) {
                s_and_l.add (Properties::start);
@@ -290,7 +298,7 @@ MidiRegion::update_after_tempo_map_change (bool /* send */)
 void
 MidiRegion::update_length_beats (const int32_t sub_num)
 {
-       _length_beats = Evoral::Beats (_session.tempo_map().exact_beat_at_frame (_position + _length, sub_num) - beat());
+       _length_beats = Evoral::Beats (_session.tempo_map().exact_qn_at_frame (_position + _length, sub_num) - (pulse() * 4.0));
 }
 
 void
@@ -298,8 +306,13 @@ MidiRegion::set_position_internal (framepos_t pos, bool allow_bbt_recompute, con
 {
        Region::set_position_internal (pos, allow_bbt_recompute, sub_num);
 
+       /* don't clobber _start _length and _length_beats if session loading.*/
+       if (!_session.loading()) {
+               return;
+       }
+
        /* set _start to new position in tempo map */
-       _start = _position - _session.tempo_map().frame_at_beat (beat() - _start_beats.val().to_double());
+       _start = _position - _session.tempo_map().frame_at_pulse (pulse() - (_start_beats.val().to_double() / 4.0));
 
        /* in construction from src */
        if (_length_beats == Evoral::Beats()) {
@@ -307,12 +320,12 @@ MidiRegion::set_position_internal (framepos_t pos, bool allow_bbt_recompute, con
        }
 
        if (position_lock_style() == AudioTime) {
-               _length_beats = Evoral::Beats (_session.tempo_map().beat_at_frame (_position + _length) - _session.tempo_map().beat_at_frame (_position));
+               _length_beats = Evoral::Beats (_session.tempo_map().quarter_note_at_frame (_position + _length) - _session.tempo_map().quarter_note_at_frame (_position));
        } else {
                /* leave _length_beats alone, and change _length to reflect the state of things
                   at the new position (tempo map may dictate a different number of frames).
                */
-               Region::set_length_internal (_session.tempo_map().frame_at_beat (beat() + _length_beats.val().to_double()) - _position, sub_num);
+               Region::set_length_internal (_session.tempo_map().frame_at_pulse (pulse() + (_length_beats.val().to_double() / 4.0)) - _position, sub_num);
        }
 }
 
@@ -320,18 +333,24 @@ framecnt_t
 MidiRegion::read_at (Evoral::EventSink<framepos_t>& out,
                      framepos_t                     position,
                      framecnt_t                     dur,
+                     Evoral::Range<framepos_t>*     loop_range,
                      uint32_t                       chan_n,
                      NoteMode                       mode,
                      MidiStateTracker*              tracker,
                      MidiChannelFilter*             filter) const
 {
-       return _read_at (_sources, out, position, dur, chan_n, mode, tracker, filter);
+       return _read_at (_sources, out, position, dur, loop_range, chan_n, mode, tracker, filter);
 }
 
 framecnt_t
-MidiRegion::master_read_at (MidiRingBuffer<framepos_t>& out, framepos_t position, framecnt_t dur, uint32_t chan_n, NoteMode mode) const
+MidiRegion::master_read_at (MidiRingBuffer<framepos_t>& out,
+                            framepos_t                  position,
+                            framecnt_t                  dur,
+                            Evoral::Range<framepos_t>*  loop_range,
+                            uint32_t                    chan_n,
+                            NoteMode                    mode) const
 {
-       return _read_at (_master_sources, out, position, dur, chan_n, mode); /* no tracker */
+       return _read_at (_master_sources, out, position, dur, loop_range, chan_n, mode); /* no tracker */
 }
 
 framecnt_t
@@ -339,6 +358,7 @@ MidiRegion::_read_at (const SourceList&              /*srcs*/,
                       Evoral::EventSink<framepos_t>& dst,
                       framepos_t                     position,
                       framecnt_t                     dur,
+                      Evoral::Range<framepos_t>*     loop_range,
                       uint32_t                       chan_n,
                       NoteMode                       mode,
                       MidiStateTracker*              tracker,
@@ -378,27 +398,34 @@ MidiRegion::_read_at (const SourceList&              /*srcs*/,
 
        src->set_note_mode(lm, mode);
 
-       /*
-         cerr << "MR " << name () << " read @ " << position << " * " << to_read
-         << " _position = " << _position
-         << " _start = " << _start
-         << " intoffset = " << internal_offset
-         << endl;
-       */
+#if 0
+       cerr << "MR " << name () << " read @ " << position << " + " << to_read
+            << " dur was " << dur
+            << " len " << _length
+            << " l-io " << (_length - internal_offset)
+            << " _position = " << _position
+            << " _start = " << _start
+            << " intoffset = " << internal_offset
+            << " pulse = " << pulse()
+            << " start_pulse = " << start_pulse()
+            << " start_beat = " << _start_beats
+            << endl;
+#endif
 
        /* This call reads events from a source and writes them to `dst' timed in session frames */
 
        if (src->midi_read (
                    lm, // source lock
-                       dst, // destination buffer
-                       _position - _start, // start position of the source in session frames
-                       _start + internal_offset, // where to start reading in the source
-                       to_read, // read duration in frames
-                       tracker,
-                       filter,
-                       _filtered_parameters,
-                       beat(),
-                       _start_beats.val().to_double()
+                   dst, // destination buffer
+                   _position - _start, // start position of the source in session frames
+                   _start + internal_offset, // where to start reading in the source
+                   to_read, // read duration in frames
+                   loop_range,
+                   tracker,
+                   filter,
+                   _filtered_parameters,
+                   pulse(),
+                   _start_beats.val().to_double()
                    ) != to_read) {
                return 0; /* "read nothing" */
        }
@@ -485,6 +512,23 @@ MidiRegion::midi_source (uint32_t n) const
        return boost::dynamic_pointer_cast<MidiSource>(source(n));
 }
 
+/* don't use this. hopefully it will go away.
+   currently used by headless-chicken session utility.
+*/
+void
+MidiRegion::clobber_sources (boost::shared_ptr<MidiSource> s)
+{
+       drop_sources();
+
+       _sources.push_back (s);
+       s->inc_use_count ();
+       _master_sources.push_back (s);
+       s->inc_use_count ();
+
+       s->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(s)));
+
+}
+
 void
 MidiRegion::model_changed ()
 {
@@ -554,17 +598,14 @@ void
 MidiRegion::set_start_internal (framecnt_t s, const int32_t sub_num)
 {
        Region::set_start_internal (s, sub_num);
-
        if (position_lock_style() == AudioTime) {
                set_start_beats_from_start_frames ();
-               }
+       }
 }
 
 void
 MidiRegion::trim_to_internal (framepos_t position, framecnt_t length, const int32_t sub_num)
 {
-       framepos_t new_start;
-
        if (locked()) {
                return;
        }
@@ -575,8 +616,8 @@ MidiRegion::trim_to_internal (framepos_t position, framecnt_t length, const int3
           working in beats seems the correct thing to do, but reports of a missing first note
           on playback suggest otherwise. for now, we work in exact beats.
        */
-       const double pos_beat = _session.tempo_map().exact_beat_at_frame (position, sub_num);
-       const double beat_delta = pos_beat - beat();
+       const double pos_pulse = _session.tempo_map().exact_qn_at_frame (position, sub_num) / 4.0;
+       const double pulse_delta = pos_pulse - pulse();
 
        /* Set position before length, otherwise for MIDI regions this bad thing happens:
         * 1. we call set_length_internal; length in beats is computed using the region's current
@@ -587,19 +628,18 @@ MidiRegion::trim_to_internal (framepos_t position, framecnt_t length, const int3
         */
 
        if (_position != position) {
+               /* sets _beat to new position.*/
                set_position_internal (position, true, sub_num);
                what_changed.add (Properties::position);
-       }
 
-       const double new_start_beat = _start_beats.val().to_double() + beat_delta;
-       new_start = _position - _session.tempo_map().frame_at_beat (beat() - new_start_beat);
+               const double new_start_pulse = (_start_beats.val().to_double() / 4.0) + pulse_delta;
+               const framepos_t new_start = _position - _session.tempo_map().frame_at_pulse (pulse() - new_start_pulse);
 
-       if (!verify_start_and_length (new_start, length)) {
-               return;
-       }
+               if (!verify_start_and_length (new_start, length)) {
+                       return;
+               }
 
-       if (_start != new_start) {
-               _start_beats = Evoral::Beats (new_start_beat);
+               _start_beats = Evoral::Beats (new_start_pulse * 4.0);
                what_changed.add (Properties::start_beats);
 
                set_start_internal (new_start, sub_num);
@@ -607,6 +647,11 @@ MidiRegion::trim_to_internal (framepos_t position, framecnt_t length, const int3
        }
 
        if (_length != length) {
+
+               if (!verify_start_and_length (_start, length)) {
+                       return;
+               }
+
                set_length_internal (length, sub_num);
                what_changed.add (Properties::length);
                what_changed.add (Properties::length_beats);