try to fix data loss at end of a capture pass for MIDI - add a new virtual method...
authorPaul Davis <paul@linuxaudiosystems.com>
Wed, 20 Jul 2011 18:13:03 +0000 (18:13 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Wed, 20 Jul 2011 18:13:03 +0000 (18:13 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@9910 d708f5d6-7413-0410-9779-e7cbd77b26cf

libs/ardour/ardour/midi_diskstream.h
libs/ardour/ardour/midi_source.h
libs/ardour/ardour/smf_source.h
libs/ardour/midi_diskstream.cc
libs/ardour/midi_source.cc
libs/ardour/midi_stretch.cc
libs/ardour/smf_source.cc
libs/evoral/evoral/Sequence.hpp
libs/evoral/src/Sequence.cpp
libs/gtkmm2ext/stateful_button.cc

index 59f849afd7a944aa2652dc2ff2d25bd4e1d1c48d..b7f3de3887cf5e3d3b5eb95802b7695bc19385c5 100644 (file)
@@ -186,7 +186,6 @@ class MidiDiskstream : public Diskstream
        MidiRingBuffer<framepos_t>*  _capture_buf;
        MidiPort*                    _source_port;
        boost::shared_ptr<SMFSource> _write_source;
-       framepos_t                   _last_flush_frame;
        NoteMode                     _note_mode;
        volatile gint                _frames_written_to_ringbuffer;
        volatile gint                _frames_read_from_ringbuffer;
index a7b47efe21fe01b0ecf390a98a220a828e48a6bb..735a38faa558bc954925ce93f7b05b1898be9593 100644 (file)
@@ -83,6 +83,13 @@ class MidiSource : virtual public Source, public boost::enable_shared_from_this<
        virtual void mark_streaming_write_completed ();
        void mark_write_starting_now ();
 
+       /* like ::mark_streaming_write_completed() but with more arguments to
+        * allow control over MIDI-specific behaviour. Expected to be used only
+        * when recording actual MIDI input, rather then when importing files
+        * etc.
+        */
+       virtual void mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::StuckNoteOption, Evoral::MusicalTime when = 0);
+
        virtual void session_saved();
 
        std::string captured_for() const               { return _captured_for; }
@@ -91,6 +98,9 @@ class MidiSource : virtual public Source, public boost::enable_shared_from_this<
        uint32_t read_data_count()  const { return _read_data_count; }
        uint32_t write_data_count() const { return _write_data_count; }
 
+       framepos_t last_write_end() const { return _last_write_end; }
+       void set_last_write_end (framepos_t pos) { _last_write_end = pos; }
+
        static PBD::Signal1<void,MidiSource*> MidiSourceCreated;
 
        XMLNode& get_state ();
index cf97efe41463a5eec3056b021ac2c73b4aaa16cd..eb1bcaca9a73d3beff9de443cfbc91aded45fd5c 100644 (file)
@@ -56,6 +56,7 @@ public:
 
        void mark_streaming_midi_write_started (NoteMode mode);
        void mark_streaming_write_completed ();
+       void mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::StuckNoteOption, Evoral::MusicalTime when = 0);
 
        XMLNode& get_state ();
        int set_state (const XMLNode&, int version);
index 8ccdd9420e100169589f5f64b428e7cbef1ccb35..6a75537f9a41040f4c48a2b33f91febfde468675 100644 (file)
@@ -74,7 +74,6 @@ MidiDiskstream::MidiDiskstream (Session &sess, const string &name, Diskstream::F
        , _playback_buf(0)
        , _capture_buf(0)
        , _source_port(0)
-       , _last_flush_frame(0)
        , _note_mode(Sustained)
        , _frames_written_to_ringbuffer(0)
        , _frames_read_from_ringbuffer(0)
@@ -95,7 +94,6 @@ MidiDiskstream::MidiDiskstream (Session& sess, const XMLNode& node)
        , _playback_buf(0)
        , _capture_buf(0)
        , _source_port(0)
-       , _last_flush_frame(0)
        , _note_mode(Sustained)
        , _frames_written_to_ringbuffer(0)
        , _frames_read_from_ringbuffer(0)
@@ -203,7 +201,9 @@ MidiDiskstream::non_realtime_input_change ()
                seek (_session.transport_frame());
        }
 
-       _last_flush_frame = _session.transport_frame();
+       if (_write_source) {
+               _write_source->set_last_write_end (_session.transport_frame());
+       }
 }
 
 int
@@ -861,20 +861,27 @@ MidiDiskstream::do_refill ()
 int
 MidiDiskstream::do_flush (RunContext /*context*/, bool force_flush)
 {
-       uint32_t to_write;
-       int32_t ret = 0;
+       framecnt_t to_write;
        framecnt_t total;
+       int32_t ret = 0;
+
+       if (!_write_source) {
+               return 0;
+       }
+
+       assert (!destructive());
 
        cerr << name() << " flushing to disk, bufspace = " << _capture_buf->read_space() 
-            << " transport @ " << _session.transport_frame() << " last flush @ " << _last_flush_frame
+            << " transport @ " << _session.transport_frame() 
             << endl;
        
        _write_data_count = 0;
 
-       total = _session.transport_frame() - _last_flush_frame;
+       total = _session.transport_frame() - _write_source->last_write_end();
 
-       if (total == 0 || _capture_buf->read_space() == 0
-                       || (!force_flush && (total < disk_io_chunk_frames && was_recording))) {
+       if (total == 0 || 
+           _capture_buf->read_space() == 0 || 
+           (!force_flush && (total < disk_io_chunk_frames) && was_recording)) {
                cerr << "\tFlush shortcut because total = " << total
                     << " capture read space = " << _capture_buf->read_space()
                     << " force flush = " << force_flush 
@@ -898,35 +905,25 @@ MidiDiskstream::do_flush (RunContext /*context*/, bool force_flush)
                ret = 1;
        }
 
-       to_write = disk_io_chunk_frames;
-
-       assert(!destructive());
+       if (force_flush) {
+               /* push out everything we have, right now */
+               to_write = max_framecnt;
+       } else {
+               to_write = disk_io_chunk_frames;
+       }
 
-       if (record_enabled() &&
-           ((_session.transport_frame() - _last_flush_frame > disk_io_chunk_frames) ||
-            force_flush)) {
-               if ((!_write_source) || _write_source->midi_write (*_capture_buf, get_capture_start_frame (0), to_write) != to_write) {
+       if (record_enabled() && ((total > disk_io_chunk_frames) || force_flush)) {
+               if (_write_source->midi_write (*_capture_buf, get_capture_start_frame (0), to_write) != to_write) {
                        error << string_compose(_("MidiDiskstream %1: cannot write to disk"), _id) << endmsg;
                        return -1;
-               } else {
-                       cerr << "didn't write, _write_source = " << _write_source << endl;
-                       _last_flush_frame = _session.transport_frame();
-               }
+               } 
        } else {
                cerr << "\tdidn't write to disk because recenabled = " << record_enabled()
-                    << " last flush @ " << _last_flush_frame << " disk io " << disk_io_chunk_frames << " TF @ " << _session.transport_frame()
+                    << " total = " << total << " TF @ " << _session.transport_frame()
                     << " force = " << force_flush << endl;
        }
 
 out:
-
-       if (ret == 0) {
-               if (_last_flush_frame > _session.transport_frame() || _last_flush_frame < capture_start_frame) {
-                       _last_flush_frame = _session.transport_frame();
-                       cerr << name() << " set last flush frame to " << _last_flush_frame << endl;
-               }
-       }
-
        return ret;
 }
 
@@ -996,17 +993,17 @@ MidiDiskstream::transport_stopped_wallclock (struct tm& /*when*/, time_t /*twhen
                        _write_source->set_timeline_position (capture_info.front()->start);
                        _write_source->set_captured_for (_name);
 
+                       /* set length in beats to entire capture length */
+
+                       BeatsFramesConverter converter (_session.tempo_map(), capture_info.front()->start);
+                       const double total_capture_beats = converter.from (total_capture);
+                       _write_source->set_length_beats (total_capture_beats);
+
                        /* flush to disk: this step differs from the audio path,
                           where all the data is already on disk.
                        */
 
-                       _write_source->mark_streaming_write_completed ();
-
-                       /* set length in beats to entire capture length */
-
-                       BeatsFramesConverter converter (_session.tempo_map(), capture_info.front()->start);
-                       const double total_capture_beats = converter.from(total_capture);
-                       _write_source->set_length_beats(total_capture_beats);
+                       _write_source->mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::ResolveStuckNotes, total_capture_beats);
 
                        /* we will want to be able to keep (over)writing the source
                           but we don't want it to be removable. this also differs
index 404ac3a73d3cdbda4f8df5388654b53bae18c02c..42b451865588a8e4d59c94f39fbb6edba1594c92 100644 (file)
@@ -267,7 +267,13 @@ MidiSource::midi_write (MidiRingBuffer<framepos_t>& source, framepos_t source_st
        Glib::Mutex::Lock lm (_lock);
        cerr << "MidiSource calling write unlocked\n";
        const framecnt_t ret = write_unlocked (source, source_start, duration);
-       _last_write_end += duration;
+
+       if (duration == max_framecnt) {
+               _last_read_end = 0;
+       } else {
+               _last_write_end += duration;
+       }
+
        cerr << name() << " last write end now @ " << _last_write_end << endl;
        return ret;
 }
@@ -311,15 +317,21 @@ MidiSource::mark_streaming_write_started ()
 }
 
 void
-MidiSource::mark_streaming_write_completed ()
+MidiSource::mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::StuckNoteOption option, Evoral::MusicalTime end)
 {
        if (_model) {
-               _model->end_write (false);
+               _model->end_write (option, end);
        }
 
        _writing = false;
 }
 
+void
+MidiSource::mark_streaming_write_completed ()
+{
+       mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::DeleteStuckNotes);
+}
+
 boost::shared_ptr<MidiSource>
 MidiSource::clone (Evoral::MusicalTime begin, Evoral::MusicalTime end)
 {
index 4dfab65fd44bdf802790302d5d14d7c951178125..d9ac1403a9b2ff8d31736cd77bc180a51d43d3b5 100644 (file)
@@ -103,7 +103,7 @@ MidiStretch::run (boost::shared_ptr<Region> r, Progress* progress)
                new_model->append(ev, Evoral::next_event_id());
        }
 
-       new_model->end_write ();
+       new_model->end_write (Evoral::Sequence<Evoral::MusicalTime>::DeleteStuckNotes);
        new_model->set_edited (true);
 
        new_src->copy_interpolation_from (src);
index 7d90b5885992543fdd8c7017e2122335ab8e7345..9e777b6ae5fde5351d71251455c9684a06ad8282 100644 (file)
@@ -243,16 +243,21 @@ SMFSource::write_unlocked (MidiRingBuffer<framepos_t>& source, framepos_t positi
        cerr << "SMFSource::write unlocked, begins writing from src buffer with _last_write_end = " << _last_write_end << " dur = " << duration << endl;
 
        while (true) {
-               bool ret = source.peek ((uint8_t*)&time, sizeof (time));
-               if (!ret || time > _last_write_end + duration) {
-                       DEBUG_TRACE (DEBUG::MidiIO, string_compose ("SMFSource::write_unlocked: dropping event @ %1 because ret %4 or it is later than %2 + %3\n",
-                                                                   time, _last_write_end, duration, ret));
+               bool ret;
+
+               if (!(ret = source.peek ((uint8_t*)&time, sizeof (time)))) {
+                       /* no more events to consider */
                        break;
                }
 
-               ret = source.read_prefix(&time, &type, &size);
-               if (!ret) {
-                       cerr << "ERROR: Unable to read event prefix, corrupt MIDI ring buffer" << endl;
+               if ((duration != max_framecnt) && (time > (_last_write_end + duration))) {
+                       DEBUG_TRACE (DEBUG::MidiIO, string_compose ("SMFSource::write_unlocked: dropping event @ %1 because it is later than %2 + %3\n",
+                                                                   time, _last_write_end, duration));
+                       break;
+               }
+
+               if (!(ret = source.read_prefix (&time, &type, &size))) {
+                       error << _("Unable to read event prefix, corrupt MIDI ring buffer") << endmsg;
                        break;
                }
 
@@ -263,7 +268,7 @@ SMFSource::write_unlocked (MidiRingBuffer<framepos_t>& source, framepos_t positi
 
                ret = source.read_contents(size, buf);
                if (!ret) {
-                       cerr << "ERROR: Read time/size but not buffer, corrupt MIDI ring buffer" << endl;
+                       error << _("Read time/size but not buffer, corrupt MIDI ring buffer") << endmsg;
                        break;
                }
 
@@ -430,9 +435,15 @@ SMFSource::mark_streaming_midi_write_started (NoteMode mode)
 
 void
 SMFSource::mark_streaming_write_completed ()
+{
+       mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::DeleteStuckNotes);
+}
+
+void
+SMFSource::mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::StuckNoteOption stuck_notes_option, Evoral::MusicalTime when)
 {
        Glib::Mutex::Lock lm (_lock);
-       MidiSource::mark_streaming_write_completed();
+       MidiSource::mark_midi_streaming_write_completed (stuck_notes_option, when);
 
        if (!writable()) {
                return;
@@ -551,8 +562,7 @@ SMFSource::load_model (bool lock, bool force_reload)
                have_event_id = false;
        }
 
-       _model->end_write (_length_beats, false, true);
-       //_model->end_write (false);
+       _model->end_write (Evoral::Sequence<Evoral::MusicalTime>::ResolveStuckNotes, _length_beats);
        _model->set_edited (false);
 
        _model_iter = _model->begin();
index 298ef48e52ec979e75c6a035fbdbdf79d9e188ac..98d4b190b1af0b1f926cc14922eb29eddea48c1f 100644 (file)
@@ -97,7 +97,14 @@ public:
 
        void start_write();
        bool writing() const { return _writing; }
-        void end_write (Time when=0, bool delete_stuck=false, bool resolve=false);
+
+        enum StuckNoteOption {
+               Relax,
+               DeleteStuckNotes,
+               ResolveStuckNotes
+       };
+
+        void end_write (StuckNoteOption, Time when = 0);
 
        void append(const Event<Time>& ev, Evoral::event_id_t evid);
 
index 3d0fe771fb13aa3b6f6f8753caf43456f989f64b..d1fd64382bc642d436de2073013389a8bb29e429 100644 (file)
@@ -623,7 +623,7 @@ Sequence<Time>::start_write()
  */
 template<typename Time>
 void
-Sequence<Time>::end_write (Time when, bool delete_stuck, bool resolve)
+Sequence<Time>::end_write (StuckNoteOption option, Time when)
 {
        WriteLock lock(write_lock());
 
@@ -631,11 +631,7 @@ Sequence<Time>::end_write (Time when, bool delete_stuck, bool resolve)
                return;
        }
 
-       if (resolve) {
-               assert (!delete_stuck);
-       }
-
-       DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 : end_write (%2 notes)\n", this, _notes.size()));
+       DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 : end_write (%2 notes) delete stuck option %3 @ %4\n", this, _notes.size(), option, when));
 
         if (!_percussive) {
 
@@ -643,11 +639,17 @@ Sequence<Time>::end_write (Time when, bool delete_stuck, bool resolve)
                         typename Notes::iterator next = n;
                         ++next;
                        
+                       cerr << "!!!!!!! note length = " << (*n)->length() << endl;
+
                         if ((*n)->length() == 0) {
-                               if (delete_stuck) {
+                               switch (option) {
+                               case Relax:
+                                       break;
+                               case DeleteStuckNotes:
                                        cerr << "WARNING: Stuck note lost: " << (*n)->note() << endl;
                                        _notes.erase(n);
-                               } else if (resolve) {
+                                       break;
+                               case ResolveStuckNotes:
                                        if (when <= (*n)->time()) {
                                                cerr << "WARNING: Stuck note resolution - end time @ " 
                                                     << when << " is before note on: " << (**n) << endl;
@@ -656,6 +658,7 @@ Sequence<Time>::end_write (Time when, bool delete_stuck, bool resolve)
                                                (*n)->set_length (when - (*n)->time());
                                                cerr << "WARNING: resolved note-on with no note-off to generate " << (**n) << endl;
                                        }
+                                       break;
                                }
                        }
 
index 0548b8392b496c62fd7798c16e1ad0111adce573..3880a1b35c853b5f52e00e18f5c61bd02c2e2f61 100644 (file)
@@ -111,6 +111,7 @@ StateButton::avoid_prelight_on_style_changed (const Glib::RefPtr<Gtk::Style>& /*
                         gtk_widget_modify_style (GTK_WIDGET(child->gobj()), rcstyle);
                 }
 
+
                 g_object_unref (rcstyle);
                 style_changing = false;
         }