Reading of MIDI CC from MIDI regions (MidiModel). UI still needs work though..
authorDavid Robillard <d@drobilla.net>
Fri, 31 Aug 2007 05:02:45 +0000 (05:02 +0000)
committerDavid Robillard <d@drobilla.net>
Fri, 31 Aug 2007 05:02:45 +0000 (05:02 +0000)
Various fixes for linear/integer AutomationList interpolation (for CC).

git-svn-id: svn://localhost/ardour2/trunk@2359 d708f5d6-7413-0410-9779-e7cbd77b26cf

SConstruct
libs/ardour/ardour/automatable.h
libs/ardour/ardour/automation_event.h
libs/ardour/ardour/midi_event.h
libs/ardour/ardour/midi_model.h
libs/ardour/ardour/midi_ring_buffer.h
libs/ardour/automation_event.cc
libs/ardour/midi_buffer.cc
libs/ardour/midi_model.cc
libs/ardour/midi_track.cc
libs/ardour/session.cc

index 838ce00cd2d90afc21f0b36507b10afc414ff685..cddc7384c892be487c476a6269c473340a7ae4d7 100644 (file)
@@ -542,9 +542,9 @@ env = conf.Finish()
 
 opt_flags = []
 if env['GPROFILE'] == 1:
-    debug_flags = [ '-g', '-pg' ]
+    debug_flags = [ '-O0', '-g', '-pg' ]
 else:
-    debug_flags = [ '-g' ]
+    debug_flags = [ '-O0', '-g' ]
 
 # guess at the platform, used to define compiler flags
 
index 574d7af12990addbe680e19fc43c985bea3836aa..fe47614a1f6eecf693bfb9d473774adc08c7c334 100644 (file)
@@ -52,7 +52,7 @@ public:
        boost::shared_ptr<AutomationControl> control_factory(boost::shared_ptr<AutomationList> list);
        
        typedef std::map<Parameter,boost::shared_ptr<AutomationControl> > Controls;
-       Controls& controls() { return _controls; }
+       Controls&       controls()       { return _controls; }
        const Controls& controls() const { return _controls; }
 
        virtual void add_control(boost::shared_ptr<AutomationControl>);
index e2a98e50b086f36eac7378e950f0ff5543d26efb..1831f5ca4d4486190131fe0c0969802bda0862f5 100644 (file)
@@ -234,7 +234,8 @@ class AutomationList : public PBD::StatefulDestructible
         */
        double unlocked_eval (double x) const;
        
-       bool rt_safe_earliest_event (double start, double end, double& x, double& y) const;
+       bool rt_safe_earliest_event (double start, double end, double& x, double& y, bool start_inclusive=false) const;
+       bool rt_safe_earliest_event_unlocked (double start, double end, double& x, double& y, bool start_inclusive=false) const;
 
        Curve&       curve()       { return *_curve; }
        const Curve& curve() const { return *_curve; }
@@ -256,8 +257,8 @@ class AutomationList : public PBD::StatefulDestructible
 
        void build_search_cache_if_necessary(double start, double end) const;
        
-       bool rt_safe_earliest_event_discrete (double start, double end, double& x, double& y) const;
-       bool rt_safe_earliest_event_linear (double start, double end, double& x, double& y) const;
+       bool rt_safe_earliest_event_discrete_unlocked (double start, double end, double& x, double& y, bool inclusive) const;
+       bool rt_safe_earliest_event_linear_unlocked (double start, double end, double& x, double& y, bool inclusive) const;
 
        AutomationList* cut_copy_clear (double, double, int op);
 
index a04a19cec85258734ce20f89df157600b9db6f31..bd16440d050b11d7cd6acd9eae32470b1be9e435 100644 (file)
@@ -96,16 +96,35 @@ struct MidiEvent {
                _size = copy._size;
                return *this;
        }
+       
+       inline bool operator==(const MidiEvent& other) const {
+               if (_time != other._time)
+                       return false;
+
+               if (_size != other._size)
+                       return false;
+
+               if (_buffer == other._buffer)
+                       return true;
+
+               for (size_t i=0; i < _size; ++i)
+                       if (_buffer[i] != other._buffer[i])
+                               return false;
+
+               return true;
+       }
+       
+       inline bool operator!=(const MidiEvent& other) const { return ! operator==(other); }
 
        inline bool owns_buffer() const { return _owns_buffer; }
        
-       inline void set_buffer(Byte* buf) {
+       inline void set_buffer(Byte* buf, bool own) {
                if (_owns_buffer) {
                        free(_buffer);
                        _buffer = NULL;
                }
                _buffer = buf;
-               _owns_buffer = false;
+               _owns_buffer = own;
        }
 
 #else
index 41382b1be32237bd3667c3e7a73438abc74bf235..6337ca8e652d2337614b1cbb0effa6640655fd1f 100644 (file)
@@ -55,8 +55,8 @@ public:
        // This is crap.
        void write_lock()        { _lock.writer_lock(); _automation_lock.lock(); }
        void write_unlock()      { _lock.writer_unlock(); _automation_lock.unlock(); }
-       void read_lock()   const { _lock.reader_lock(); _automation_lock.lock(); }
-       void read_unlock() const { _lock.reader_unlock(); _automation_lock.unlock(); }
+       void read_lock()   const { _lock.reader_lock(); /*_automation_lock.lock();*/ }
+       void read_unlock() const { _lock.reader_unlock(); /*_automation_lock.unlock();*/ }
 
        void clear() { _notes.clear(); }
 
@@ -140,33 +140,41 @@ public:
        /** Read iterator */
        class const_iterator {
        public:
-               const_iterator(MidiModel& model, double t);
+               const_iterator(const MidiModel& model, double t);
                ~const_iterator();
 
-               const MidiEvent& operator*() const { return _event; }
+               const MidiEvent& operator*()  const { return _event; }
+               const MidiEvent* operator->() const { return &_event; }
 
                const const_iterator& operator++(); // prefix only
+               bool operator==(const const_iterator& other) const;
+               bool operator!=(const const_iterator& other) const { return ! operator==(other); }
 
        private:
-               const MidiModel& _model;
+               const MidiModel* _model;
                MidiEvent        _event;
 
                typedef std::priority_queue<const Note*,std::vector<const Note*>, LaterNoteEndComparator>
                                ActiveNotes;
                mutable ActiveNotes _active_notes;
 
-               Notes::iterator _note_iter;
-
-               std::vector<MidiControlIterator> _control_iters;
+               bool                                       _is_end;
+               bool                                       _locked;
+               Notes::const_iterator                      _note_iter;
+               std::vector<MidiControlIterator>           _control_iters;
+               std::vector<MidiControlIterator>::iterator _control_iter;
        };
        
+       const_iterator        begin() const { return const_iterator(*this, 0); }
+       const const_iterator& end()   const { return _end_iter; }
+       
 private:
        friend class DeltaCommand;
        void add_note_unlocked(const Note& note);
        void remove_note_unlocked(const Note& note);
 
        friend class const_iterator;
-       bool control_to_midi_event(MidiEvent& ev, const MidiControlIterator& iter);
+       bool control_to_midi_event(MidiEvent& ev, const MidiControlIterator& iter) const;
 
 #ifndef NDEBUG
        bool is_sorted() const;
@@ -185,14 +193,19 @@ private:
        WriteNotes _write_notes;
        bool       _writing;
        bool       _edited;
-       
+
+       const const_iterator _end_iter;
+
+       mutable nframes_t     _next_read;
+       mutable const_iterator _read_iter;
+
        // note state for read():
        // (TODO: Remove and replace with iterator)
        
        typedef std::priority_queue<const Note*,std::vector<const Note*>,
                        LaterNoteEndComparator> ActiveNotes;
 
-       mutable ActiveNotes _active_notes;
+       //mutable ActiveNotes _active_notes;
 };
 
 } /* namespace ARDOUR */
index 9389f962fc16e06e59e5e5c5c61583b3ff6580fb..a0078061ee20b240b374a28b20e0a0d4a3ccd55d 100644 (file)
@@ -342,7 +342,7 @@ MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t
                        }
                        
                } else {
-                       printf("MRB - SKIPPING EVENT (with time %f)\n", ev.time());
+                       printf("MRB - SKIPPING EVENT AT TIME %f\n", ev.time());
                }
        }
        
index 955acf0082f77ce5eb2d4322bfdc6889ab240f60..3a80dddc76b59d3f7c769e67d5081a9a9051c6d8 100644 (file)
@@ -1036,7 +1036,7 @@ AutomationList::build_search_cache_if_necessary(double start, double end) const
                const ControlEvent start_point (start, 0);
                const ControlEvent end_point (end, 0);
 
-               //cerr << "REBUILD: (" << _search_cache.left << ".." << _search_cache.right << ") -> ("
+               //cerr << "REBUILD: (" << _search_cache.left << ".." << _search_cache.right << ") := ("
                //      << start << ".." << end << ")" << endl;
 
                _search_cache.range.first = lower_bound (_events.begin(), _events.end(), &start_point, time_comparator);
@@ -1050,31 +1050,50 @@ AutomationList::build_search_cache_if_necessary(double start, double end) const
 /** Get the earliest event between \a start and \a end, using the current interpolation style.
  *
  * If an event is found, \a x and \a y are set to its coordinates.
+ *
+ * \param inclusive Include events with timestamp exactly equal to \a start
+ * \return true if event is found (and \a x and \a y are valid).
+ */
+bool
+AutomationList::rt_safe_earliest_event(double start, double end, double& x, double& y, bool inclusive) const
+{
+       // FIXME: It would be nice if this was unnecessary..
+       Glib::Mutex::Lock lm(_lock, Glib::TRY_LOCK);
+       if (!lm.locked()) {
+               return false;
+       }
+
+       return rt_safe_earliest_event_unlocked(start, end, x, y, inclusive);
+} 
+
+
+/** Get the earliest event between \a start and \a end, using the current interpolation style.
+ *
+ * If an event is found, \a x and \a y are set to its coordinates.
+ *
+ * \param inclusive Include events with timestamp exactly equal to \a start
  * \return true if event is found (and \a x and \a y are valid).
  */
 bool
-AutomationList::rt_safe_earliest_event(double start, double end, double& x, double& y) const
+AutomationList::rt_safe_earliest_event_unlocked(double start, double end, double& x, double& y, bool inclusive) const
 {
        if (_interpolation == Discrete)
-               return rt_safe_earliest_event_discrete(start, end, x, y);
+               return rt_safe_earliest_event_discrete_unlocked(start, end, x, y, inclusive);
        else
-               return rt_safe_earliest_event_linear(start, end, x, y);
+               return rt_safe_earliest_event_linear_unlocked(start, end, x, y, inclusive);
 } 
 
+
 /** Get the earliest event between \a start and \a end (Discrete (lack of) interpolation)
  *
  * If an event is found, \a x and \a y are set to its coordinates.
+ *
+ * \param inclusive Include events with timestamp exactly equal to \a start
  * \return true if event is found (and \a x and \a y are valid).
  */
 bool
-AutomationList::rt_safe_earliest_event_discrete (double start, double end, double& x, double& y) const
+AutomationList::rt_safe_earliest_event_discrete_unlocked (double start, double end, double& x, double& y, bool inclusive) const
 {
-       // FIXME: It would be nice if this was unnecessary..
-       Glib::Mutex::Lock lm(_lock, Glib::TRY_LOCK);
-       if (!lm.locked()) {
-               return false;
-       }
-       
        build_search_cache_if_necessary(start, end);
 
        const pair<const_iterator,const_iterator>& range = _search_cache.range;
@@ -1082,8 +1101,10 @@ AutomationList::rt_safe_earliest_event_discrete (double start, double end, doubl
        if (range.first != _events.end()) {
                const ControlEvent* const first = *range.first;
 
+               const bool past_start = (inclusive ? first->when >= start : first->when > start);
+
                /* Earliest points is in range, return it */
-               if (first->when >= start && first->when < end) {
+               if (past_start >= start && first->when < end) {
 
                        x = first->when;
                        y = first->value;
@@ -1098,7 +1119,6 @@ AutomationList::rt_safe_earliest_event_discrete (double start, double end, doubl
                        return true;
 
                } else {
-                               
                        return false;
                }
        
@@ -1111,20 +1131,19 @@ AutomationList::rt_safe_earliest_event_discrete (double start, double end, doubl
 /** Get the earliest time the line crosses an integer (Linear interpolation).
  *
  * If an event is found, \a x and \a y are set to its coordinates.
+ *
+ * \param inclusive Include events with timestamp exactly equal to \a start
  * \return true if event is found (and \a x and \a y are valid).
  */
 bool
-AutomationList::rt_safe_earliest_event_linear (double start, double end, double& x, double& y) const
+AutomationList::rt_safe_earliest_event_linear_unlocked (double start, double end, double& x, double& y, bool inclusive) const
 {
-       // FIXME: It would be nice if this was unnecessary..
-       Glib::Mutex::Lock lm(_lock, Glib::TRY_LOCK);
-       if (!lm.locked()) {
-               return false;
-       }
+       //cerr << "earliest_event(" << start << ", " << end << ", " << x << ", " << y << ", " << inclusive << endl;
 
        if (_events.size() < 2)
-               return false;
+               return rt_safe_earliest_event_discrete_unlocked(start, end, x, y, inclusive);
 
+       // Hack to avoid infinitely repeating the same event
        build_search_cache_if_necessary(start, end);
        
        pair<const_iterator,const_iterator> range = _search_cache.range;
@@ -1138,6 +1157,7 @@ AutomationList::rt_safe_earliest_event_linear (double start, double end, double&
                if (range.first == _events.begin() || (*range.first)->when == start) {
                        first = *range.first;
                        next = *(++range.first);
+                       ++_search_cache.range.first;
 
                /* Step is before first */
                } else {
@@ -1147,48 +1167,70 @@ AutomationList::rt_safe_earliest_event_linear (double start, double end, double&
                        next = *range.first;
                }
                
-               if (first->when == start) {
-                       x = start;
+               if (inclusive && first->when == start) {
+                       x = first->when;
                        y = first->value;
                        /* Move left of cache to this point
                         * (Optimize for immediate call this cycle within range) */
                        _search_cache.left = x;
-                       ++_search_cache.range.first;
+                       //++_search_cache.range.first;
                        return true;
                }
-
-               /*cerr << first->value << " @ " << first->when << " ---> "
-                               << next->value << " @ " << next->when << endl;*/
+                       
+               if (abs(first->value - next->value) <= 1) {
+                       if (next->when <= end && (!inclusive || next->when > start)) {
+                               x = next->when;
+                               y = next->value;
+                               /* Move left of cache to this point
+                                * (Optimize for immediate call this cycle within range) */
+                               _search_cache.left = x;
+                               //++_search_cache.range.first;
+                               return true;
+                       } else {
+                               return false;
+                       }
+               }
 
                const double slope = (next->value - first->value) / (double)(next->when - first->when);
-
-               const double start_y = first->value + (slope * (start - first->when));
                //cerr << "start y: " << start_y << endl;
 
-               if (start_y >= first->value) {
-                       x = first->when + ((ceil(start_y) - first->value) / (double)slope);
-                       y = ceil(start_y);
-               } else {
-                       x = first->when + ((floor(start_y) - first->value) / (double)slope);
-                       y = floor(start_y);
+               //y = first->value + (slope * fabs(start - first->when));
+               y = first->value;
+
+               if (first->value < next->value) // ramping up
+                       y = ceil(y);
+               else // ramping down
+                       y = floor(y);
+
+               x = first->when + (y - first->value) / (double)slope;
+               
+               while ((inclusive && x < start) || x <= start && y != next->value) {
+                       
+                       if (first->value < next->value) // ramping up
+                               y += 1.0;
+                       else // ramping down
+                               y -= 1.0;
+
+                       x = first->when + (y - first->value) / (double)slope;
                }
 
-               if (x >= start && x < end) {
-                       //cerr << y << " @ " << x << endl;
+               /*cerr << first->value << " @ " << first->when << " ... "
+                               << next->value << " @ " << next->when
+                               << " = " << y << " @ " << x << endl;*/
 
-                       x = floor(x);
+               assert(    (y >= first->value && y <= next->value)
+                               || (y <= first->value && y >= next->value) );
 
+               
+               const bool past_start = (inclusive ? x >= start : x > start);
+               if (past_start && x < end) {
                        /* Move left of cache to this point
                         * (Optimize for immediate call this cycle within range) */
                        _search_cache.left = x;
-                       
-                       if (x >= next->when)
-                               ++_search_cache.range.first;
 
                        return true;
 
                } else {
-                       //cerr << "\tNo: " << start_y << ", " << x << endl;
                        return false;
                }
        
index 07ae2abab08d8def729228c89d620a339aea61d0..2b476899117b28433799de866c7ec243c3caf910 100644 (file)
@@ -119,7 +119,7 @@ MidiBuffer::push_back(const MidiEvent& ev)
 
        memcpy(write_loc, ev.buffer(), ev.size());
        _events[_size] = ev;
-       _events[_size].set_buffer(write_loc);
+       _events[_size].set_buffer(write_loc, false);
        ++_size;
 
        //cerr << "MidiBuffer: pushed, size = " << _size << endl;
@@ -148,7 +148,7 @@ MidiBuffer::push_back(const jack_midi_event_t& ev)
        memcpy(write_loc, ev.buffer, ev.size);
        _events[_size].time() = (double)ev.time;
        _events[_size].size() = ev.size;
-       _events[_size].set_buffer(write_loc);
+       _events[_size].set_buffer(write_loc, false);
        ++_size;
 
        //cerr << "MidiBuffer: pushed, size = " << _size << endl;
@@ -178,7 +178,7 @@ MidiBuffer::reserve(double time, size_t size)
 
        _events[_size].time() = time;
        _events[_size].size() = size;
-       _events[_size].set_buffer(write_loc);
+       _events[_size].set_buffer(write_loc, false);
        ++_size;
 
        //cerr << "MidiBuffer: reserved, size = " << _size << endl;
index 4df4cf0b377a13b189396d7bf1066d24a955deac..765d5f7cadd0bb8d8b000731b804b420dd08dde5 100644 (file)
@@ -22,6 +22,7 @@
 
 #include <iostream>
 #include <algorithm>
+#include <stdexcept>
 #include <stdint.h>
 #include <pbd/enumwriter.h>
 #include <ardour/midi_model.h>
@@ -36,50 +37,165 @@ using namespace ARDOUR;
 
 // Read iterator (const_iterator)
 
-MidiModel::const_iterator::const_iterator(MidiModel& model, double t)
-       : _model(model)
+MidiModel::const_iterator::const_iterator(const MidiModel& model, double t)
+       : _model(&model)
+       , _is_end( (t == DBL_MAX) || model.empty())
+       , _locked( ! _is_end)
 {
-       model.read_lock();
+       //cerr << "Created MIDI iterator @ " << t << "(is end: " << _is_end << ")" << endl;
+       
+       if (_is_end)
+               return;
 
+       model.read_lock();
+       
        _note_iter = model.notes().end();
-       for (MidiModel::Notes::iterator i = model.notes().begin(); i != model.notes().end(); ++i) {
+
+       for (MidiModel::Notes::const_iterator i = model.notes().begin(); i != model.notes().end(); ++i) {
                if ((*i).time() >= t) {
                        _note_iter = i;
                        break;
                }
        }
-
-       MidiControlIterator earliest_control = make_pair(boost::shared_ptr<AutomationList>(), make_pair(DBL_MAX, 0.0));
+                       
+       MidiControlIterator earliest_control = make_pair(boost::shared_ptr<AutomationList>(),
+                       make_pair(DBL_MAX, 0.0));
 
        _control_iters.reserve(model.controls().size());
        for (Automatable::Controls::const_iterator i = model.controls().begin();
                        i != model.controls().end(); ++i) {
+
+               assert(i->first.type() == MidiCCAutomation);
+
                double x, y;
-               i->second->list()->rt_safe_earliest_event(t, DBL_MAX, x, y);
+               bool ret = i->second->list()->rt_safe_earliest_event_unlocked(t, DBL_MAX, x, y);
+               if (!ret) {
+                       cerr << "MIDI Iterator: CC " << i->first.id() << " (size " << i->second->list()->size()
+                               << ") has no events past " << t << endl;
+                       continue;
+               } 
+
+               assert(x >= 0);
+               assert(y >= 0);
+               assert(y <= UINT8_MAX);
                
                const MidiControlIterator new_iter = make_pair(i->second->list(), make_pair(x, y));
+               
+               //cerr << "MIDI Iterator: CC " << i->first.id() << " added (" << x << ", " << y << ")" << endl;
+               _control_iters.push_back(new_iter);
 
-               if (x < earliest_control.second.first)
+               if (x < earliest_control.second.first) {
                        earliest_control = new_iter;
-
-               _control_iters.push_back(new_iter);
+                       _control_iter = _control_iters.end();
+                       --_control_iter;
+               }
        }
 
-       if (_note_iter != model.notes().end())
+       if (_note_iter != model.notes().end()) {
                _event = MidiEvent(_note_iter->on_event(), false);
+               ++_note_iter;
+       }
 
-       if (earliest_control.first != 0 && earliest_control.second.first < _event.time())
+       if (earliest_control.first && earliest_control.second.first < _event.time())
                model.control_to_midi_event(_event, earliest_control);
-
+       else
+               _control_iter = _control_iters.end();
+
+       if (_event.size() == 0) {
+               //cerr << "Created MIDI iterator @ " << t << " is at end." << endl;
+               _is_end = true;
+               _model->read_unlock();
+               _locked = false;
+       //} else {
+       //      printf("MIDI Iterator = %X @ %lf\n", _event.type(), _event.time());
+       }
 }
 
 
 MidiModel::const_iterator::~const_iterator()
 {
-       _model.read_unlock();
+       if (_locked)
+               _model->read_unlock();
+}
+               
+
+const MidiModel::const_iterator&
+MidiModel::const_iterator::operator++()
+{
+       if (_is_end)
+               throw std::logic_error("Attempt to iterate past end of MidiModel");
+
+       assert(_event.is_note() || _event.is_cc());
+
+       // Increment past current control event
+       if (_control_iter->first && _event.is_cc()) {
+               double x, y;
+               const bool ret = _control_iter->first->rt_safe_earliest_event_unlocked(
+                               _control_iter->second.first, DBL_MAX, x, y, false);
+
+               if (ret) {
+                       //cerr << "Incremented " << _control_iter->first->parameter().id() << " to " << x << endl;
+                       _control_iter->second.first = x;
+                       _control_iter->second.second = y;
+               } else {
+                       //cerr << "Hit end of " << _control_iter->first->parameter().id() << endl;
+                       _control_iter->first.reset();
+                       _control_iter->second.first = DBL_MAX;
+               }
+       }
+
+       // Now find and point at the earliest event
+
+       _control_iter = _control_iters.begin();
+
+       for (std::vector<MidiControlIterator>::iterator i = _control_iters.begin();
+                       i != _control_iters.end(); ++i) {
+               if (i->second.first < _control_iter->second.first) {
+                       _control_iter = i;
+               }
+       }
+       
+       enum Type { NIL,  NOTE, CC };
+       Type type = NIL;
+
+       if (_note_iter != _model->notes().end())
+               type = NOTE;
+       
+       if (_control_iter != _control_iters.end() && _control_iter->second.first != DBL_MAX)
+               if (_note_iter == _model->notes().end() || _control_iter->second.first < _note_iter->time())
+                       type = CC;
+
+       if (type == NOTE) {
+               //cerr << "MIDI Iterator = note" << endl;
+               _event = MidiEvent(_note_iter->on_event(), false);
+               ++_note_iter;
+       } else if (type == CC) {
+               //cerr << "MIDI Iterator = CC" << endl;
+               _model->control_to_midi_event(_event, *_control_iter);
+       } else {
+               //cerr << "MIDI Iterator = NIL" << endl;
+               _is_end = true;
+               _model->read_unlock();
+               _locked = false;
+       }
+
+       return *this;
 }
+               
 
+bool
+MidiModel::const_iterator::operator==(const const_iterator& other) const
+{
+       if (_is_end)
+               if (other._is_end)
+                       return true;
+               else
+                       return false;
+       else
+               return (_event == other._event);
+}
 
+       
 // MidiModel
 
 MidiModel::MidiModel(Session& s, size_t size)
@@ -88,7 +204,10 @@ MidiModel::MidiModel(Session& s, size_t size)
        , _note_mode(Sustained)
        , _writing(false)
        , _edited(false)
-       , _active_notes(LaterNoteEndComparator())
+       //, _active_notes(LaterNoteEndComparator())
+       , _end_iter(*this, DBL_MAX)
+       , _next_read(UINT32_MAX)
+       , _read_iter(*this, DBL_MAX)
 {
 }
 
@@ -98,10 +217,26 @@ MidiModel::MidiModel(Session& s, size_t size)
  * \return number of events written to \a dst
  */
 size_t
-MidiModel::read (MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes_t stamp_offset) const
+MidiModel::read(MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes_t stamp_offset) const
 {
        size_t read_events = 0;
 
+       if (start != _next_read) {
+               _read_iter = const_iterator(*this, (double)start);
+               cerr << "Repositioning iterator from " << _next_read << " to " << start << endl;
+       //} else {
+       //      cerr << "Using cached iterator at " << _next_read << endl;
+       }
+
+       _next_read = start + nframes;
+
+       while (_read_iter != end() && _read_iter->time() < start + nframes) {
+               dst.write(_read_iter->time() + stamp_offset, _read_iter->size(), _read_iter->buffer());
+               ++_read_iter;
+               ++read_events;
+       }
+
+#if 0
        /* FIXME: cache last lookup value to avoid O(n) search every time */
 
        if (_note_mode == Sustained) {
@@ -161,17 +296,17 @@ MidiModel::read (MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframe
                        }
                }
        }
-
+#endif
        return read_events;
 }
        
 
 bool
-MidiModel::control_to_midi_event(MidiEvent& ev, const MidiControlIterator& iter)
+MidiModel::control_to_midi_event(MidiEvent& ev, const MidiControlIterator& iter) const
 {
        if (iter.first->parameter().type() == MidiCCAutomation) {
-               if (!ev.owns_buffer() || ev.size() < 3)
-                       ev = MidiEvent(iter.second.first, 3, (Byte*)malloc(3), true);
+               if (ev.size() < 3)
+                       ev.set_buffer((Byte*)malloc(3), true);
 
                assert(iter.first);
                assert(iter.first->parameter().id() <= INT8_MAX);
@@ -418,11 +553,10 @@ void
 MidiModel::append_cc_unlocked(double time, uint8_t number, uint8_t value)
 {
        Parameter param(MidiCCAutomation, number);
-
-       //cerr << "MidiModel " << this << " add CC " << (int)number << " = " << (int)value
-       //      << " @ " << time << endl;
        
        boost::shared_ptr<AutomationControl> control = Automatable::control(param, true);
+       //cerr << "MidiModel " << this << "(" << control.get() << ") add CC " << (int)number << " = " << (int)value
+       //      << " @ " << time << endl;
        control->list()->fast_simple_add(time, (double)value);
 }
 
index ac40170e72d644b9ddd624a70bc3bcd3a55bc82c..a6cd964e82cd32d7400db88da0241c8759e60b42 100644 (file)
@@ -589,7 +589,10 @@ MidiTrack::write_controller_messages(MidiBuffer& output_buf, nframes_t start_fra
        buf[0] = MIDI_CMD_CONTROL;
        MidiEvent ev(0, 3, buf, false);
 
-       // Write controller automation
+       // Write track controller automation
+#if 0
+       // This now lives in MidiModel.  Any need for track automation like this?
+       // Relative Velocity?
        if (_session.transport_rolling()) {
                for (Controls::const_iterator i = _controls.begin(); i != _controls.end(); ++i) {
                        const boost::shared_ptr<AutomationList> list = (*i).second->list();
@@ -637,6 +640,7 @@ MidiTrack::write_controller_messages(MidiBuffer& output_buf, nframes_t start_fra
                        output_buf.copy(cc_buf);
                }
        }
+#endif
 
        // Append immediate events (UI controls)
        _immediate_events.read(output_buf, 0, 0, offset + nframes-1); // all stamps = 0
index 3a7e5aeaa831f74d6691df004e6ae3b4c7f3a616..c638018ab211aecac22fcba142718a536122dbb3 100644 (file)
@@ -2151,7 +2151,7 @@ Session::update_route_solo_state ()
        
        shared_ptr<RouteList> r = routes.reader ();
 
-        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                if ((*i)->soloed()) {
                        mute = true;
                        if (dynamic_cast<Track*>((*i).get())) {