Interpret tempo time based on read position (not source timeline position which is...
authorDavid Robillard <d@drobilla.net>
Thu, 19 Feb 2009 04:12:54 +0000 (04:12 +0000)
committerDavid Robillard <d@drobilla.net>
Thu, 19 Feb 2009 04:12:54 +0000 (04:12 +0000)
Move time conversion into the region view rather than the source.
Adapt MIDI (including controllers) regions to the destination tempo when moved (e.g. dragging a region to a location with half the tempo will make the notes twice as long).

git-svn-id: svn://localhost/ardour2/branches/3.0@4635 d708f5d6-7413-0410-9779-e7cbd77b26cf

12 files changed:
gtk2_ardour/automation_region_view.cc
gtk2_ardour/midi_region_view.cc
gtk2_ardour/region_view.cc
gtk2_ardour/region_view.h
libs/ardour/ardour/midi_source.h
libs/ardour/ardour/smf_source.h
libs/ardour/ardour/source.h
libs/ardour/import.cc
libs/ardour/midi_diskstream.cc
libs/ardour/midi_region.cc
libs/ardour/midi_source.cc
libs/ardour/smf_source.cc

index 98d402e348d41aab6424316aa779117fc851710a..3ba0794b17717861f14a44ac7d55ed5b837a42a5 100644 (file)
@@ -70,8 +70,7 @@ AutomationRegionView::create_line (boost::shared_ptr<ARDOUR::AutomationList> lis
 {
        _line = boost::shared_ptr<AutomationLine>(new AutomationLine(
                                ARDOUR::EventTypeMap::instance().to_symbol(list->parameter()),
-                               trackview, *get_canvas_group(), list,
-                               &_region->source(0)->time_converter()));
+                               trackview, *get_canvas_group(), list, &_time_converter));
        _line->set_colors();
        _line->set_interpolation(list->interpolation());
        _line->show();
index bb46a3d0986f70033699045f4717cac94cdfb696..3087d3436ee10cf950997fbb0a0f818501d2d884 100644 (file)
@@ -1405,13 +1405,13 @@ MidiRegionView::get_position_pixels()
 nframes64_t
 MidiRegionView::beats_to_frames(double beats) const
 {
-       return midi_region()->midi_source()->time_converter().to(beats);
+       return _time_converter.to(beats);
 }
 
 double
 MidiRegionView::frames_to_beats(nframes64_t frames) const
 {
-       return midi_region()->midi_source()->time_converter().from(frames);
+       return _time_converter.from(frames);
 }
 
 void
index 54e0a9ef5e86389530df45b13650eeea91b085de..1819df040172ea239450867d6a58534fd17b673c 100644 (file)
@@ -65,25 +65,25 @@ RegionView::RegionView (ArdourCanvas::Group*              parent,
                         double                            spu,
                         Gdk::Color&                       basic_color)
        : TimeAxisViewItem (r->name(), *parent, tv, spu, basic_color, r->position(), r->length(), false,
-
-                           TimeAxisViewItem::Visibility (TimeAxisViewItem::ShowNameText|
-                                                         TimeAxisViewItem::ShowNameHighlight|
-                                                         TimeAxisViewItem::ShowFrame))
-         , _region (r)
-         , sync_mark(0)
-         , sync_line(0)
-         , editor(0)
-         , current_visible_sync_position(0.0)
-         , valid(false)
-         , _enable_display(false)
-         , _pixel_width(1.0)
-         , in_destructor(false)
-         , wait_for_data(false)
+                       TimeAxisViewItem::Visibility (TimeAxisViewItem::ShowNameText|
+                               TimeAxisViewItem::ShowNameHighlight| TimeAxisViewItem::ShowFrame))
+       , _region (r)
+       , sync_mark(0)
+       , sync_line(0)
+       , editor(0)
+       , current_visible_sync_position(0.0)
+       , valid(false)
+       , _enable_display(false)
+       , _pixel_width(1.0)
+       , in_destructor(false)
+       , wait_for_data(false)
+       , _time_converter(r->session(), r->position())
 {
 }
 
 RegionView::RegionView (const RegionView& other)
        : TimeAxisViewItem (other)
+       , _time_converter(other._time_converter)
 {
        /* derived concrete type will call init () */
 
@@ -96,6 +96,7 @@ RegionView::RegionView (const RegionView& other)
 
 RegionView::RegionView (const RegionView& other, boost::shared_ptr<Region> other_region)
        : TimeAxisViewItem (other)
+       , _time_converter(other._time_converter)
 {
        /* this is a pseudo-copy constructor used when dragging regions 
           around on the canvas.
@@ -128,6 +129,7 @@ RegionView::RegionView (ArdourCanvas::Group*         parent,
        , _pixel_width(1.0)
        , in_destructor(false)
        , wait_for_data(false)
+       , _time_converter(r->session(), r->position())
 {
 }
 
@@ -246,6 +248,7 @@ RegionView::region_resized (Change what_changed)
 
        if (what_changed & ARDOUR::PositionChanged) {
                set_position (_region->position(), 0);
+               _time_converter.set_origin(_region->position());
        }
 
        if (what_changed & Change (StartChanged|LengthChanged)) {
index de9d95ccf38fe8ebf52a1cacf29e0cb5bbf31b08..0db41c24f9487b549bc7bab5f86882e621080fd0 100644 (file)
@@ -25,6 +25,7 @@
 #include <libgnomecanvasmm/polygon.h>
 #include <sigc++/signal.h>
 #include <ardour/region.h>
+#include <ardour/beats_frames_converter.h>
 
 #include "time_axis_view_item.h"
 #include "automation_line.h"
@@ -149,6 +150,8 @@ class RegionView : public TimeAxisViewItem
            that will be played at any given time.
        */
        std::list<ArdourCanvas::SimpleRect*> _coverage_frames;
+
+       ARDOUR::BeatsFramesConverter _time_converter;
 };
 
 #endif /* __gtk_ardour_region_view_h__ */
index 774b3ab37274c6cfef3fb8e91ca2da3bc5934e17..1b2188e8dde522180d9a4e5e631a94e5b9f40714 100644 (file)
@@ -47,12 +47,28 @@ class MidiSource : virtual public Source
        MidiSource (Session& session, const XMLNode&);
        virtual ~MidiSource ();
        
-       virtual nframes_t midi_read (MidiRingBuffer<nframes_t>& dst, nframes_t start, nframes_t cnt,
+       /** Read the data in a given time range from the MIDI source.
+        * All time stamps in parameters are in audio frames (even if the source has tempo time).
+        * \param dst Ring buffer where read events are written
+        * \param position Start position of the SOURCE in this read context
+        * \param start Start of range to be read
+        * \param cnt Length of range to be read (in audio frames)
+        * \param stamp_offset Offset to add to event times written to dst
+        * \param negative_stamp_offset Offset to subtract from event times written to dst
+        */
+       virtual nframes_t midi_read (MidiRingBuffer<nframes_t>& dst,
+                       nframes_t position,
+                       nframes_t start, nframes_t cnt,
                        nframes_t stamp_offset, nframes_t negative_stamp_offset) const;
-       virtual nframes_t midi_write (MidiRingBuffer<nframes_t>& src, nframes_t cnt);
+
+       virtual nframes_t midi_write (MidiRingBuffer<nframes_t>& src,
+                       nframes_t position,
+                       nframes_t cnt);
 
        virtual void append_event_unlocked_beats(const Evoral::Event<double>& ev) = 0;
-       virtual void append_event_unlocked_frames(const Evoral::Event<nframes_t>& ev) = 0;
+
+       virtual void append_event_unlocked_frames(const Evoral::Event<nframes_t>& ev,
+                       nframes_t position) = 0;
 
        virtual void mark_streaming_midi_write_started (NoteMode mode, nframes_t start_time);
        virtual void mark_streaming_write_started ();
@@ -86,29 +102,26 @@ class MidiSource : virtual public Source
 
        void set_note_mode(NoteMode mode);
        
-       void set_timeline_position (int64_t pos);
-
        boost::shared_ptr<MidiModel> model() { return _model; }
        void set_model(boost::shared_ptr<MidiModel> m) { _model = m; }
        void drop_model() { _model.reset(); }
 
-       const Evoral::TimeConverter<double, nframes_t>& time_converter() const {
-               return _converter;
-       }
-
   protected:
        virtual void flush_midi() = 0;
        
-       virtual nframes_t read_unlocked (MidiRingBuffer<nframes_t>& dst, nframes_t start, nframes_t cnt,
+       virtual nframes_t read_unlocked (MidiRingBuffer<nframes_t>& dst,
+                       nframes_t position,
+                       nframes_t start, nframes_t cnt,
                        nframes_t stamp_offset, nframes_t negative_stamp_offset) const = 0;
-       virtual nframes_t write_unlocked (MidiRingBuffer<nframes_t>& dst, nframes_t cnt) = 0;
+
+       virtual nframes_t write_unlocked (MidiRingBuffer<nframes_t>& dst,
+                       nframes_t position,
+                       nframes_t cnt) = 0;
        
        std::string         _captured_for;
        mutable uint32_t    _read_data_count;  ///< modified in read()
        mutable uint32_t    _write_data_count; ///< modified in write()
        
-       BeatsFramesConverter _converter;
-
        boost::shared_ptr<MidiModel> _model;
        bool                         _writing;
        
index 259328c5fae428c316b68c5e827f26a7d347d341..7942a118af95de1971ba9b9d60aea9f00644ee58 100644 (file)
@@ -52,7 +52,7 @@ public:
        bool set_name (const std::string& newname) { return (set_source_name(newname, false) == 0); }
        
        void append_event_unlocked_beats (const Evoral::Event<double>& ev);
-       void append_event_unlocked_frames (const Evoral::Event<nframes_t>& ev);
+       void append_event_unlocked_frames (const Evoral::Event<nframes_t>& ev, nframes_t position);
 
        void mark_streaming_midi_write_started (NoteMode mode, nframes_t start_time);
        void mark_streaming_write_completed ();
@@ -70,15 +70,15 @@ public:
        static bool safe_midi_file_extension (const Glib::ustring& path);
 
 private:
-       nframes_t read_unlocked (
-                       MidiRingBuffer<nframes_t>& dst,
+       nframes_t read_unlocked (MidiRingBuffer<nframes_t>& dst,
+                       nframes_t position,
                        nframes_t start,
-                       nframes_t cn,
+                       nframes_t cnt,
                        nframes_t stamp_offset,
                        nframes_t negative_stamp_offset) const;
 
-       nframes_t write_unlocked (
-                       MidiRingBuffer<nframes_t>& src,
+       nframes_t write_unlocked (MidiRingBuffer<nframes_t>& src,
+                       nframes_t position,
                        nframes_t cnt);
 
        void set_default_controls_interpolation ();
index a388c8db3a5d947f61446ab78572d7e0d32572a3..dd86ebe52190de2eb12a39f44e057168676945dd 100644 (file)
@@ -26,7 +26,6 @@
 #include <boost/utility.hpp>
 #include <sigc++/signal.h>
 #include <pbd/statefuldestructible.h> 
-#include <evoral/TimeConverter.hpp>
 
 #include <ardour/ardour.h>
 #include <ardour/session_object.h>
@@ -112,10 +111,6 @@ class Source : public SessionObject, public boost::noncopyable
        
        void set_allow_remove_if_empty (bool yn);
        
-       virtual const Evoral::TimeConverter<double, nframes_t>& time_converter() const {
-               return Evoral::IdentityConverter<double, nframes_t>();
-       }
-
        Glib::Mutex& mutex()       { return _lock; }
        Flag         flags() const { return _flags; }
 
index 92ecaec75305693c16571498d8e703bbda839ec5..306247f009c03ec4826f874aa7025d8230a95ba0 100644 (file)
@@ -352,8 +352,10 @@ write_midi_data_to_new_files (Evoral::SMF* source, Session::ImportStatus& status
                                status.progress += 0.01;
                }
 
+               const nframes64_t pos = 0;
                const double length_beats = ceil(t / (double)source->ppqn());
-               smfs->update_length(0, smfs->time_converter().to(length_beats));
+               BeatsFramesConverter converter(smfs->session(), pos);
+               smfs->update_length(pos, converter.to(length_beats));
                smfs->end_write();
 
                if (status.cancel) {
index ff6d05a4ac8cdc102acc6dae55280c5f099f00d6..4e124425b981a180aee6df8aad0a8c5c21935e21 100644 (file)
@@ -904,7 +904,7 @@ MidiDiskstream::do_flush (RunContext context, bool force_flush)
        assert(!destructive());
 
        if (record_enabled() && _session.transport_frame() - _last_flush_frame > disk_io_chunk_frames) {
-               if ((!_write_source) || _write_source->midi_write (*_capture_buf, to_write) != to_write) {
+               if ((!_write_source) || _write_source->midi_write (*_capture_buf, capture_start_frame, to_write) != to_write) {
                        error << string_compose(_("MidiDiskstream %1: cannot write to disk"), _id) << endmsg;
                        return -1;
                } else {
index fcbf18772110b685423d3009138b74fd8fffea15..1ce4bbdb7fc45f676673ac81edac6d5c257c9b35 100644 (file)
@@ -184,16 +184,12 @@ MidiRegion::_read_at (const SourceList& srcs, MidiRingBuffer<nframes_t>& dst, nf
        }
        
        if (src->midi_read (
-                       // the destination buffer
-                       dst,  
-                       // where to start reading in the region
-                       _start + internal_offset, 
-                       // how many bytes
-                       to_read, 
-                       // the offset in the output buffer
-                       output_buffer_position,
-                       // what to substract from note times written in the output buffer
-                       negative_output_buffer_position
+                       dst, // destination buffer
+                       _position - _start, // start position of the source in this read context
+                       _start + internal_offset, // where to start reading in the region
+                       to_read, // read duration in frames
+                       output_buffer_position, // the offset in the output buffer
+                       negative_output_buffer_position // amount to substract from note times
                ) != to_read) {
                return 0; /* "read nothing" */
        }
index 06437852c61a0f740fef37ad131783bd617e0427..b9f7776df88722f4a775440c4e61e68add24c9ab 100644 (file)
@@ -53,7 +53,6 @@ MidiSource::MidiSource (Session& s, string name, Source::Flag flags)
        : Source (s, DataType::MIDI, name, flags)
        , _read_data_count(0)
        , _write_data_count(0)
-       , _converter(s, _timeline_position)
        , _writing (false)
        , _last_read_end(0)
 {
@@ -63,7 +62,6 @@ MidiSource::MidiSource (Session& s, const XMLNode& node)
        : Source (s, node)
        , _read_data_count(0)
        , _write_data_count(0)
-       , _converter(s, _timeline_position)
        , _writing (false)
        , _last_read_end(0)
 {
@@ -110,13 +108,16 @@ MidiSource::invalidate ()
 }
 
 nframes_t
-MidiSource::midi_read (MidiRingBuffer<nframes_t>& dst, nframes_t start, nframes_t cnt,
+MidiSource::midi_read (MidiRingBuffer<nframes_t>& dst, nframes_t position,
+               nframes_t start, nframes_t cnt,
                nframes_t stamp_offset, nframes_t negative_stamp_offset) const
 {
-
        Glib::Mutex::Lock lm (_lock);
+
+       BeatsFramesConverter converter(_session, position);
+
        if (_model) {
-#define BEATS_TO_FRAMES(t) (_converter.to(t) + stamp_offset - negative_stamp_offset)
+#define BEATS_TO_FRAMES(t) (converter.to(t) + stamp_offset - negative_stamp_offset)
 
                Evoral::Sequence<double>::const_iterator& i = _model_iter;
                
@@ -141,15 +142,15 @@ MidiSource::midi_read (MidiRingBuffer<nframes_t>& dst, nframes_t start, nframes_
                }
                return cnt;
        } else {
-               return read_unlocked (dst, start, cnt, stamp_offset, negative_stamp_offset);
+               return read_unlocked (dst, position, start, cnt, stamp_offset, negative_stamp_offset);
        }
 }
 
 nframes_t
-MidiSource::midi_write (MidiRingBuffer<nframes_t>& dst, nframes_t cnt)
+MidiSource::midi_write (MidiRingBuffer<nframes_t>& dst, nframes_t position, nframes_t cnt)
 {
        Glib::Mutex::Lock lm (_lock);
-       return write_unlocked (dst, cnt);
+       return write_unlocked (dst, position, cnt);
 }
 
 bool
@@ -162,17 +163,10 @@ MidiSource::file_changed (string path)
        return !e1;
 }
 
-void
-MidiSource::set_timeline_position (int64_t pos)
-{
-       Source::set_timeline_position(pos);
-       _converter.set_origin(pos);
-}
-
 void
 MidiSource::mark_streaming_midi_write_started (NoteMode mode, nframes_t start_frame)
 {
-       set_timeline_position(start_frame); // why do I have a feeling this can break somehow...
+       set_timeline_position(start_frame);
 
        if (_model) {
                _model->set_note_mode(mode);
index be9b9dd3f125033fbb73b069a69d428b6080ed87..128acca74278943dfbe72912b708e8ccb442a4ff 100644 (file)
@@ -95,7 +95,8 @@ SMFSource::~SMFSource ()
 
 /** All stamps in audio frames */
 nframes_t
-SMFSource::read_unlocked (MidiRingBuffer<nframes_t>& dst, nframes_t start, nframes_t dur,
+SMFSource::read_unlocked (MidiRingBuffer<nframes_t>& dst, nframes_t position,
+               nframes_t start, nframes_t dur,
                nframes_t stamp_offset, nframes_t negative_stamp_offset) const
 {
        int      ret  = 0;
@@ -110,8 +111,10 @@ SMFSource::read_unlocked (MidiRingBuffer<nframes_t>& dst, nframes_t start, nfram
        uint8_t* ev_buffer  = 0;
 
        size_t scratch_size = 0; // keep track of scratch to minimize reallocs
+       
+       BeatsFramesConverter converter(_session, position);
 
-       const uint64_t start_ticks = (uint64_t)(_converter.from(start) * ppqn());
+       const uint64_t start_ticks = (uint64_t)(converter.from(start) * ppqn());
 
        if (_last_read_end == 0 || start != _last_read_end) {
                cerr << "SMFSource::read_unlocked seeking to " << start << endl;
@@ -143,7 +146,7 @@ SMFSource::read_unlocked (MidiRingBuffer<nframes_t>& dst, nframes_t start, nfram
                ev_type = EventTypeMap::instance().midi_event_type(ev_buffer[0]);
 
                assert(time >= start_ticks);
-               const nframes_t ev_frame_time = _converter.to(time / (double)ppqn()) + stamp_offset;
+               const nframes_t ev_frame_time = converter.to(time / (double)ppqn()) + stamp_offset;
 
                if (ev_frame_time < start + dur) {
                        dst.write(ev_frame_time - negative_stamp_offset, ev_type, ev_size, ev_buffer);
@@ -164,7 +167,7 @@ SMFSource::read_unlocked (MidiRingBuffer<nframes_t>& dst, nframes_t start, nfram
 
 /** All stamps in audio frames */
 nframes_t
-SMFSource::write_unlocked (MidiRingBuffer<nframes_t>& src, nframes_t dur)
+SMFSource::write_unlocked (MidiRingBuffer<nframes_t>& src, nframes_t position, nframes_t dur)
 {
        _write_data_count = 0;
                
@@ -183,7 +186,7 @@ SMFSource::write_unlocked (MidiRingBuffer<nframes_t>& src, nframes_t dur)
 
        while (true) {
                bool ret = src.peek_time(&time);
-               if (!ret || time - _timeline_position > _length + dur) {
+               if (!ret || time - position > _length + dur) {
                        break;
                }
 
@@ -203,8 +206,8 @@ SMFSource::write_unlocked (MidiRingBuffer<nframes_t>& src, nframes_t dur)
                        break;
                }
                
-               assert(time >= _timeline_position);
-               time -= _timeline_position;
+               assert(time >= position);
+               time -= position;
                
                ev.set(buf, size, time);
                ev.set_event_type(EventTypeMap::instance().midi_event_type(ev.buffer()[0]));
@@ -214,7 +217,7 @@ SMFSource::write_unlocked (MidiRingBuffer<nframes_t>& src, nframes_t dur)
                        continue;
                }
                
-               append_event_unlocked_frames(ev);
+               append_event_unlocked_frames(ev, position);
        }
 
        if (_model) {
@@ -227,7 +230,7 @@ SMFSource::write_unlocked (MidiRingBuffer<nframes_t>& src, nframes_t dur)
        const nframes_t oldlen = _length;
        update_length(oldlen, dur);
 
-       ViewDataRangeReady(_timeline_position + oldlen, dur); /* EMIT SIGNAL */
+       ViewDataRangeReady(position + oldlen, dur); /* EMIT SIGNAL */
 
        return dur;
 }
@@ -266,7 +269,7 @@ SMFSource::append_event_unlocked_beats (const Evoral::Event<double>& ev)
 
 /** Append an event with a timestamp in frames (nframes_t) */
 void
-SMFSource::append_event_unlocked_frames (const Evoral::Event<nframes_t>& ev)
+SMFSource::append_event_unlocked_frames (const Evoral::Event<nframes_t>& ev, nframes_t position)
 {
        if (ev.size() == 0)  {
                return;
@@ -282,8 +285,10 @@ SMFSource::append_event_unlocked_frames (const Evoral::Event<nframes_t>& ev)
                return;
        }
        
+       BeatsFramesConverter converter(_session, position);
+       
        const nframes_t delta_time_frames = ev.time() - _last_ev_time_frames;
-       const double    delta_time_beats  = _converter.from(delta_time_frames);
+       const double    delta_time_beats  = converter.from(delta_time_frames);
        const uint32_t  delta_time_ticks  = (uint32_t)(lrint(delta_time_beats * (double)ppqn()));
 
        Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer());
@@ -292,7 +297,7 @@ SMFSource::append_event_unlocked_frames (const Evoral::Event<nframes_t>& ev)
        _write_data_count += ev.size();
 
        if (_model) {
-               const double ev_time_beats = _converter.from(ev.time());
+               const double ev_time_beats = converter.from(ev.time());
                const Evoral::Event<double> beat_ev(
                                ev.event_type(), ev_time_beats, ev.size(), (uint8_t*)ev.buffer());
                _model->append(beat_ev);