Wrap MusicalTime in a class.
[ardour.git] / libs / ardour / midi_diskstream.cc
index 39932db8bf28b7ce87badf5c7983962dfafde680..40d333d584a701542881de734288e723f65fe858 100644 (file)
@@ -75,6 +75,7 @@ MidiDiskstream::MidiDiskstream (Session &sess, const string &name, Diskstream::F
        , _frames_read_from_ringbuffer(0)
        , _frames_pending_write(0)
        , _num_captured_loops(0)
+       , _accumulated_capture_offset(0)
        , _gui_feed_buffer(AudioEngine::instance()->raw_buffer_size (DataType::MIDI))
 {
        in_set_state = true;
@@ -99,6 +100,7 @@ MidiDiskstream::MidiDiskstream (Session& sess, const XMLNode& node)
        , _frames_read_from_ringbuffer(0)
        , _frames_pending_write(0)
        , _num_captured_loops(0)
+       , _accumulated_capture_offset(0)
        , _gui_feed_buffer(AudioEngine::instance()->raw_buffer_size (DataType::MIDI))
 {
        in_set_state = true;
@@ -136,6 +138,8 @@ MidiDiskstream::init ()
 MidiDiskstream::~MidiDiskstream ()
 {
        Glib::Threads::Mutex::Lock lm (state_lock);
+       delete _playback_buf;
+       delete _capture_buf;
 }
 
 
@@ -362,6 +366,15 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
                Evoral::OverlapType ot = Evoral::coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes);
 
                calculate_record_range(ot, transport_frame, nframes, rec_nframes, rec_offset);
+               /* For audio: not writing frames to the capture ringbuffer offsets
+                * the recording. For midi: we need to keep track of the record range
+                * and subtract the accumulated difference from the event time.
+                */
+               if (rec_nframes) {
+                       _accumulated_capture_offset += rec_offset;
+               } else {
+                       _accumulated_capture_offset += nframes;
+               }
 
                if (rec_nframes && !was_recording) {
                        if (loop_loc) {
@@ -394,6 +407,9 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
 
                for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) {
                        Evoral::MIDIEvent<MidiBuffer::TimeType> ev(*i, false);
+                       if (ev.time() + rec_offset > rec_nframes) {
+                               break;
+                       }
 #ifndef NDEBUG
                        if (DEBUG::MidiIO & PBD::debug_bits) {
                                const uint8_t* __data = ev.buffer();
@@ -416,23 +432,26 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
                           any desirable behaviour.  We don't want to send event with
                           transport time here since that way the source can not
                           reconstruct their actual time; future clever MIDI looping should
-                          probabl be implemented in the source instead of here.
+                          probably be implemented in the source instead of here.
                        */
                        const framecnt_t loop_offset = _num_captured_loops * loop_length;
-                       
+                       const framepos_t event_time = transport_frame + loop_offset - _accumulated_capture_offset + ev.time();
+                       if (event_time < 0 || event_time < first_recordable_frame) {
+                               continue;
+                       }
                        switch (mode) {
                        case AllChannels:
-                               _capture_buf->write(transport_frame + loop_offset + ev.time(),
+                               _capture_buf->write(event_time,
                                                    ev.type(), ev.size(), ev.buffer());
                                break;
                        case FilterChannels:
                                if (ev.is_channel_event()) {
                                        if ((1<<ev.channel()) & mask) {
-                                               _capture_buf->write(transport_frame + loop_offset + ev.time(),
+                                               _capture_buf->write(event_time,
                                                                    ev.type(), ev.size(), ev.buffer());
                                        }
                                } else {
-                                       _capture_buf->write(transport_frame + loop_offset + ev.time(),
+                                       _capture_buf->write(event_time,
                                                            ev.type(), ev.size(), ev.buffer());
                                }
                                break;
@@ -440,12 +459,12 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
                                if (ev.is_channel_event()) {
                                        ev.set_channel (PBD::ffs(mask) - 1);
                                }
-                               _capture_buf->write(transport_frame + loop_offset + ev.time(),
+                               _capture_buf->write(event_time,
                                                    ev.type(), ev.size(), ev.buffer());
                                break;
                        }
                }
-               g_atomic_int_add(const_cast<gint*> (&_frames_pending_write), nframes);
+               g_atomic_int_add(const_cast<gint*>(&_frames_pending_write), nframes);
 
                if (buf.size() != 0) {
                        Glib::Threads::Mutex::Lock lm (_gui_feed_buffer_mutex, Glib::Threads::TRY_LOCK);
@@ -472,6 +491,7 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
                if (was_recording) {
                        finish_capture ();
                }
+               _accumulated_capture_offset = 0;
 
        }
 
@@ -807,7 +827,7 @@ MidiDiskstream::do_flush (RunContext /*context*/, bool force_flush)
        }
 
        /* if there are 2+ chunks of disk i/o possible for
-          this track, let the caller know so that it can arrange
+          this track), let the caller know so that it can arrange
           for us to be called again, ASAP.
 
           if we are forcing a flush, then if there is* any* extra
@@ -907,7 +927,7 @@ MidiDiskstream::transport_stopped_wallclock (struct tm& /*when*/, time_t /*twhen
                        /* 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);
+                       const Evoral::MusicalTime 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,
@@ -972,6 +992,10 @@ MidiDiskstream::transport_stopped_wallclock (struct tm& /*when*/, time_t /*twhen
 
                                RegionFactory::region_name (region_name, _write_source->name(), false);
 
+                               DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 capture start @ %2 length %3 add new region %4\n",
+                                                       _name, (*ci)->start, (*ci)->frames, region_name));
+
+
                                // cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add a region\n";
 
                                try {
@@ -1194,11 +1218,12 @@ MidiDiskstream::use_new_write_source (uint32_t n)
                return 1;
        }
 
+       _accumulated_capture_offset = 0;
        _write_source.reset();
 
        try {
                _write_source = boost::dynamic_pointer_cast<SMFSource>(
-                       _session.create_midi_source_for_session (0, name ()));
+                       _session.create_midi_source_for_session (write_source_name ()));
 
                if (!_write_source) {
                        throw failed_constructor();
@@ -1213,25 +1238,36 @@ MidiDiskstream::use_new_write_source (uint32_t n)
 
        return 0;
 }
-
-list<boost::shared_ptr<Source> >
-MidiDiskstream::steal_write_sources()
+/**
+ * We want to use the name of the existing write source (the one that will be
+ * used by the next capture) for another purpose. So change the name of the
+ * current source, and return its current name.
+ *
+ * Return an empty string if the change cannot be accomplished.
+ */
+std::string
+MidiDiskstream::steal_write_source_name ()
 {
-       list<boost::shared_ptr<Source> > ret;
-
-       /* put some data on the disk, even if its just a header for an empty file */
-       boost::dynamic_pointer_cast<SMFSource> (_write_source)->ensure_disk_file ();
-
-       /* never let it go away */
-       _write_source->mark_nonremovable ();
+       string our_old_name = _write_source->name();
 
-       ret.push_back (_write_source);
-
-       /* get a new one */
-
-       use_new_write_source (0);
+       /* this will bump the name of the current write source to the next one
+        * (e.g. "MIDI 1-1" gets renamed to "MIDI 1-2"), thus leaving the
+        * current write source name (e.g. "MIDI 1-1" available). See the
+        * comments in Session::create_midi_source_by_stealing_name() about why
+        * we do this.
+        */
 
-       return ret;
+       try {
+               string new_path = _session.new_midi_source_path (name());
+               
+               if (_write_source->rename (new_path)) {
+                       return string();
+               }
+       } catch (...) {
+               return string ();
+       }
+       
+       return our_old_name;
 }
 
 void
@@ -1405,6 +1441,9 @@ MidiDiskstream::get_playback (MidiBuffer& dst, framecnt_t nframes)
 bool
 MidiDiskstream::set_name (string const & name)
 {
+       if (_name == name) {
+               return true;
+       }
        Diskstream::set_name (name);
 
        /* get a new write source so that its name reflects the new diskstream name */
@@ -1413,6 +1452,19 @@ MidiDiskstream::set_name (string const & name)
        return true;
 }
 
+bool
+MidiDiskstream::set_write_source_name (const std::string& str) {
+       if (_write_source_name == str) {
+               return true;
+       }
+       Diskstream::set_write_source_name (str);
+       if (_write_source_name == name()) {
+               return true;
+       }
+       use_new_write_source (0);
+       return true;
+}
+
 boost::shared_ptr<MidiBuffer>
 MidiDiskstream::get_gui_feed_buffer () const
 {