Maintain correct tracker state on MIDI overwrite.
[ardour.git] / libs / ardour / smf_source.cc
index 8a865c39578abe4915f1999b5013436f77723df7..d1a82eb685cd5ed794f7af34fc92842b9b4be466 100644 (file)
@@ -51,6 +51,7 @@ using namespace ARDOUR;
 using namespace Glib;
 using namespace PBD;
 using namespace Evoral;
+using namespace std;
 
 /** Constructor used for new internal-to-session files.  File cannot exist. */
 SMFSource::SMFSource (Session& s, const string& path, Source::Flag flags)
@@ -58,6 +59,7 @@ SMFSource::SMFSource (Session& s, const string& path, Source::Flag flags)
        , MidiSource(s, path, flags)
        , FileSource(s, DataType::MIDI, path, string(), flags)
        , Evoral::SMF()
+       , _open (false)
        , _last_ev_time_beats(0.0)
        , _last_ev_time_frames(0)
        , _smf_last_read_end (0)
@@ -93,6 +95,7 @@ SMFSource::SMFSource (Session& s, const string& path)
        , MidiSource(s, path, Source::Flag (0))
        , FileSource(s, DataType::MIDI, path, string(), Source::Flag (0))
        , Evoral::SMF()
+       , _open (false)
        , _last_ev_time_beats(0.0)
        , _last_ev_time_frames(0)
        , _smf_last_read_end (0)
@@ -124,6 +127,7 @@ SMFSource::SMFSource (Session& s, const XMLNode& node, bool must_exist)
        : Source(s, node)
        , MidiSource(s, node)
        , FileSource(s, node, must_exist)
+       , _open (false)
        , _last_ev_time_beats(0.0)
        , _last_ev_time_frames(0)
        , _smf_last_read_end (0)
@@ -173,7 +177,7 @@ SMFSource::SMFSource (Session& s, const XMLNode& node, bool must_exist)
                return;
        }
 
-       if (open(_path)) {
+       if (open (_path)) {
                throw failed_constructor ();
        }
 
@@ -199,7 +203,8 @@ SMFSource::open_for_write ()
 
 /** All stamps in audio frames */
 framecnt_t
-SMFSource::read_unlocked (Evoral::EventSink<framepos_t>& destination,
+SMFSource::read_unlocked (const Lock&                    lock,
+                          Evoral::EventSink<framepos_t>& destination,
                           framepos_t const               source_start,
                           framepos_t                     start,
                           framecnt_t                     duration,
@@ -225,7 +230,7 @@ SMFSource::read_unlocked (Evoral::EventSink<framepos_t>& destination,
 
        BeatsFramesConverter converter(_session.tempo_map(), source_start);
 
-       const uint64_t start_ticks = (uint64_t)(converter.from(start) * ppqn());
+       const uint64_t start_ticks = converter.from(start).to_ticks();
        DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked: start in ticks %1\n", start_ticks));
 
        if (_smf_last_read_end == 0 || start != _smf_last_read_end) {
@@ -273,17 +278,12 @@ SMFSource::read_unlocked (Evoral::EventSink<framepos_t>& destination,
                /* Note that we add on the source start time (in session frames) here so that ev_frame_time
                   is in session frames.
                */
-               const framepos_t ev_frame_time = converter.to(time / (double)ppqn()) + source_start;
+               const framepos_t ev_frame_time = converter.to(Evoral::MusicalTime::ticks_at_rate(time, ppqn())) + source_start;
 
                if (ev_frame_time < start + duration) {
                        destination.write (ev_frame_time, ev_type, ev_size, ev_buffer);
-
                        if (tracker) {
-                               if (ev_buffer[0] & MIDI_CMD_NOTE_ON) {
-                                       tracker->add (ev_buffer[1], ev_buffer[0] & 0xf);
-                               } else if (ev_buffer[0] & MIDI_CMD_NOTE_OFF) {
-                                       tracker->remove (ev_buffer[1], ev_buffer[0] & 0xf);
-                               }
+                               tracker->track(ev_buffer);
                        }
                } else {
                        break;
@@ -299,12 +299,13 @@ SMFSource::read_unlocked (Evoral::EventSink<framepos_t>& destination,
 }
 
 framecnt_t
-SMFSource::write_unlocked (MidiRingBuffer<framepos_t>& source,
+SMFSource::write_unlocked (const Lock&                 lock,
+                           MidiRingBuffer<framepos_t>& source,
                            framepos_t                  position,
                            framecnt_t                  cnt)
 {
        if (!_writing) {
-               mark_streaming_write_started ();
+               mark_streaming_write_started (lock);
        }
 
        framepos_t        time;
@@ -368,7 +369,7 @@ SMFSource::write_unlocked (MidiRingBuffer<framepos_t>& source,
                        continue;
                }
 
-               append_event_unlocked_frames(ev, position);
+               append_event_frames(lock, ev, position);
        }
 
        Evoral::SMF::flush ();
@@ -377,22 +378,23 @@ SMFSource::write_unlocked (MidiRingBuffer<framepos_t>& source,
        return cnt;
 }
 
-/** Append an event with a timestamp in beats (double) */
+/** Append an event with a timestamp in beats */
 void
-SMFSource::append_event_unlocked_beats (const Evoral::Event<double>& ev)
+SMFSource::append_event_beats (const Glib::Threads::Mutex::Lock&         lock,
+                               const Evoral::Event<Evoral::MusicalTime>& ev)
 {
        if (!_writing || ev.size() == 0)  {
                return;
        }
 
-       /*printf("SMFSource: %s - append_event_unlocked_beats ID = %d time = %lf, size = %u, data = ",
+       /*printf("SMFSource: %s - append_event_beats ID = %d time = %lf, size = %u, data = ",
                name().c_str(), ev.id(), ev.time(), ev.size());
               for (size_t i = 0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");*/
 
-       double time = ev.time();
+       Evoral::MusicalTime time = ev.time();
        if (time < _last_ev_time_beats) {
-               const double difference = _last_ev_time_beats - time;
-               if (difference / (double)ppqn() < 1.0) {
+               const Evoral::MusicalTime difference = _last_ev_time_beats - time;
+               if (difference.to_double() / (double)ppqn() < 1.0) {
                        /* Close enough.  This problem occurs because Sequence is not
                           actually ordered due to fuzzy time comparison.  I'm pretty sure
                           this is inherently a bad idea which causes problems all over the
@@ -401,7 +403,7 @@ SMFSource::append_event_unlocked_beats (const Evoral::Event<double>& ev)
                } else {
                        /* Out of order by more than a tick. */
                        warning << string_compose(_("Skipping event with unordered beat time %1 < %2 (off by %3 beats, %4 ticks)"),
-                                                 ev.time(), _last_ev_time_beats, difference, difference / (double)ppqn())
+                                                 ev.time(), _last_ev_time_beats, difference, difference.to_double() / (double)ppqn())
                                << endmsg;
                        return;
                }
@@ -421,8 +423,8 @@ SMFSource::append_event_unlocked_beats (const Evoral::Event<double>& ev)
 
        _length_beats = max(_length_beats, time);
 
-       const double delta_time_beats   = time - _last_ev_time_beats;
-       const uint32_t delta_time_ticks = (uint32_t)lrint(delta_time_beats * (double)ppqn());
+       const Evoral::MusicalTime delta_time_beats = time - _last_ev_time_beats;
+       const uint32_t            delta_time_ticks = delta_time_beats.to_ticks(ppqn());
 
        Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer(), event_id);
        _last_ev_time_beats = time;
@@ -431,13 +433,15 @@ SMFSource::append_event_unlocked_beats (const Evoral::Event<double>& ev)
 
 /** Append an event with a timestamp in frames (framepos_t) */
 void
-SMFSource::append_event_unlocked_frames (const Evoral::Event<framepos_t>& ev, framepos_t position)
+SMFSource::append_event_frames (const Glib::Threads::Mutex::Lock& lock,
+                                const Evoral::Event<framepos_t>&  ev,
+                                framepos_t                        position)
 {
        if (!_writing || ev.size() == 0)  {
                return;
        }
 
-       // printf("SMFSource: %s - append_event_unlocked_frames ID = %d time = %u, size = %u, data = ",
+       // printf("SMFSource: %s - append_event_frames ID = %d time = %u, size = %u, data = ",
        // name().c_str(), ev.id(), ev.time(), ev.size());
        // for (size_t i=0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");
 
@@ -448,9 +452,9 @@ SMFSource::append_event_unlocked_frames (const Evoral::Event<framepos_t>& ev, fr
                return;
        }
 
-       BeatsFramesConverter converter(_session.tempo_map(), position);
-       const double ev_time_beats = converter.from(ev.time());
-       Evoral::event_id_t event_id;
+       BeatsFramesConverter      converter(_session.tempo_map(), position);
+       const Evoral::MusicalTime ev_time_beats = converter.from(ev.time());
+       Evoral::event_id_t        event_id;
 
        if (ev.id() < 0) {
                event_id  = Evoral::next_event_id();
@@ -459,10 +463,10 @@ SMFSource::append_event_unlocked_frames (const Evoral::Event<framepos_t>& ev, fr
        }
 
        if (_model) {
-               const Evoral::Event<double> beat_ev (ev.event_type(),
-                                                    ev_time_beats,
-                                                    ev.size(),
-                                                    const_cast<uint8_t*>(ev.buffer()));
+               const Evoral::Event<Evoral::MusicalTime> beat_ev (ev.event_type(),
+                                                                 ev_time_beats,
+                                                                 ev.size(),
+                                                                 const_cast<uint8_t*>(ev.buffer()));
                _model->append (beat_ev, event_id);
        }
 
@@ -470,7 +474,7 @@ SMFSource::append_event_unlocked_frames (const Evoral::Event<framepos_t>& ev, fr
 
        const Evoral::MusicalTime last_time_beats  = converter.from (_last_ev_time_frames);
        const Evoral::MusicalTime delta_time_beats = ev_time_beats - last_time_beats;
-       const uint32_t            delta_time_ticks = (uint32_t)(lrint(delta_time_beats * (double)ppqn()));
+       const uint32_t            delta_time_ticks = delta_time_beats.to_ticks(ppqn());
 
        Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer(), event_id);
        _last_ev_time_frames = ev.time();
@@ -504,33 +508,30 @@ SMFSource::set_state (const XMLNode& node, int version)
 }
 
 void
-SMFSource::mark_streaming_midi_write_started (NoteMode mode)
+SMFSource::mark_streaming_midi_write_started (const Lock& lock, NoteMode mode)
 {
-       /* CALLER MUST HOLD LOCK */
-
        if (!_open && open_for_write()) {
                error << string_compose (_("cannot open MIDI file %1 for write"), _path) << endmsg;
                /* XXX should probably throw or return something */
                return;
        }
 
-       MidiSource::mark_streaming_midi_write_started (mode);
+       MidiSource::mark_streaming_midi_write_started (lock, mode);
        Evoral::SMF::begin_write ();
-       _last_ev_time_beats = 0.0;
+       _last_ev_time_beats  = Evoral::MusicalTime();
        _last_ev_time_frames = 0;
 }
 
 void
-SMFSource::mark_streaming_write_completed ()
+SMFSource::mark_streaming_write_completed (const Lock& lock)
 {
-       mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::DeleteStuckNotes);
+       mark_midi_streaming_write_completed (lock, Evoral::Sequence<Evoral::MusicalTime>::DeleteStuckNotes);
 }
 
 void
-SMFSource::mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::StuckNoteOption stuck_notes_option, Evoral::MusicalTime when)
+SMFSource::mark_midi_streaming_write_completed (const Lock& lm, Evoral::Sequence<Evoral::MusicalTime>::StuckNoteOption stuck_notes_option, Evoral::MusicalTime when)
 {
-       Glib::Threads::Mutex::Lock lm (_lock);
-       MidiSource::mark_midi_streaming_write_completed (stuck_notes_option, when);
+       MidiSource::mark_midi_streaming_write_completed (lm, stuck_notes_option, when);
 
        if (!writable()) {
                warning << string_compose ("attempt to write to unwritable SMF file %1", _path) << endmsg;
@@ -586,22 +587,18 @@ SMFSource::safe_midi_file_extension (const string& file)
 }
 
 static bool compare_eventlist (
-               const std::pair< Evoral::Event<double>*, gint >& a,
-               const std::pair< Evoral::Event<double>*, gint >& b) {
+       const std::pair< Evoral::Event<Evoral::MusicalTime>*, gint >& a,
+       const std::pair< Evoral::Event<Evoral::MusicalTime>*, gint >& b) {
        return ( a.first->time() < b.first->time() );
 }
 
 void
-SMFSource::load_model (bool lock, bool force_reload)
+SMFSource::load_model (const Glib::Threads::Mutex::Lock& lock, bool force_reload)
 {
        if (_writing) {
                return;
        }
 
-       boost::shared_ptr<Glib::Threads::Mutex::Lock> lm;
-       if (lock)
-               lm = boost::shared_ptr<Glib::Threads::Mutex::Lock>(new Glib::Threads::Mutex::Lock(_lock));
-
        if (_model && !force_reload) {
                return;
        }
@@ -612,6 +609,8 @@ SMFSource::load_model (bool lock, bool force_reload)
                _model->clear();
        }
 
+       invalidate(lock);
+
        if (writable() && !_open) {
                return;
        }
@@ -620,7 +619,7 @@ SMFSource::load_model (bool lock, bool force_reload)
        Evoral::SMF::seek_to_start();
 
        uint64_t time = 0; /* in SMF ticks */
-       Evoral::Event<double> ev;
+       Evoral::Event<Evoral::MusicalTime> ev;
 
        uint32_t scratch_size = 0; // keep track of scratch and minimize reallocs
 
@@ -632,7 +631,7 @@ SMFSource::load_model (bool lock, bool force_reload)
        bool have_event_id;
 
        // TODO simplify event allocation
-       std::list< std::pair< Evoral::Event<double>*, gint > > eventlist;
+       std::list< std::pair< Evoral::Event<Evoral::MusicalTime>*, gint > > eventlist;
 
        for (unsigned i = 1; i <= num_tracks(); ++i) {
                if (seek_to_track(i)) continue;
@@ -658,8 +657,8 @@ SMFSource::load_model (bool lock, bool force_reload)
                                if (!have_event_id) {
                                        event_id = Evoral::next_event_id();
                                }
-                               uint32_t event_type = midi_parameter_type(buf[0]);
-                               double   event_time = time / (double) ppqn();
+                               const uint32_t            event_type = midi_parameter_type(buf[0]);
+                               const Evoral::MusicalTime event_time = Evoral::MusicalTime::ticks_at_rate(time, ppqn());
 #ifndef NDEBUG
                                std::string ss;
 
@@ -674,7 +673,7 @@ SMFSource::load_model (bool lock, bool force_reload)
 #endif
 
                                eventlist.push_back(make_pair (
-                                                       new Evoral::Event<double> (
+                                                       new Evoral::Event<Evoral::MusicalTime> (
                                                                event_type, event_time,
                                                                size, buf, true)
                                                        , event_id));
@@ -693,7 +692,7 @@ SMFSource::load_model (bool lock, bool force_reload)
 
        eventlist.sort(compare_eventlist);
 
-       std::list< std::pair< Evoral::Event<double>*, gint > >::iterator it;
+       std::list< std::pair< Evoral::Event<Evoral::MusicalTime>*, gint > >::iterator it;
        for (it=eventlist.begin(); it!=eventlist.end(); ++it) {
                _model->append (*it->first, it->second);
                delete it->first;
@@ -701,31 +700,33 @@ SMFSource::load_model (bool lock, bool force_reload)
 
        _model->end_write (Evoral::Sequence<Evoral::MusicalTime>::ResolveStuckNotes, _length_beats);
        _model->set_edited (false);
-
-       _model_iter = _model->begin();
+       invalidate(lock);
 
        free(buf);
 }
 
 void
-SMFSource::destroy_model ()
+SMFSource::destroy_model (const Glib::Threads::Mutex::Lock& lock)
 {
        //cerr << _name << " destroying model " << _model.get() << endl;
        _model.reset();
+       invalidate(lock);
 }
 
 void
-SMFSource::flush_midi ()
+SMFSource::flush_midi (const Lock& lock)
 {
        if (!writable() || _length_beats == 0.0) {
                return;
        }
 
-       ensure_disk_file ();
+       ensure_disk_file (lock);
 
        Evoral::SMF::end_write ();
        /* data in the file means its no longer removable */
        mark_nonremovable ();
+
+       invalidate(lock);
 }
 
 void
@@ -737,7 +738,7 @@ SMFSource::set_path (const string& p)
 
 /** Ensure that this source has some file on disk, even if it's just a SMF header */
 void
-SMFSource::ensure_disk_file ()
+SMFSource::ensure_disk_file (const Lock& lock)
 {
        if (!writable()) {
                return;
@@ -749,8 +750,9 @@ SMFSource::ensure_disk_file ()
                */
                boost::shared_ptr<MidiModel> mm = _model;
                _model.reset ();
-               mm->sync_to_source ();
+               mm->sync_to_source (lock);
                _model = mm;
+               invalidate(lock);
        } else {
                /* No model; if it's not already open, it's an empty source, so create
                   and open it for writing.