Logo Search packages:      
Sourcecode: ardour version File versions  Download package

automation_event.cc

/*
    Copyright (C) 2002 Paul Davis 

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    $Id: automation_event.cc,v 1.53 2005/05/05 16:17:12 pauld Exp $
*/

#include <set>
#include <climits>
#include <float.h>
#include <cmath>
#include <algorithm>
#include <sigc++/bind.h>
#include <ardour/automation_event.h>

#include "i18n.h"

using namespace std;
using namespace ARDOUR;
using namespace SigC;

#if 0
static void dumpit (const AutomationList& al, string prefix = "")
{
      cerr << prefix << &al << endl;
      for (AutomationList::const_iterator i = al.const_begin(); i != al.const_end(); ++i) {
            cerr << prefix << '\t' << (*i)->when << ',' << (*i)->value << endl;
      }
      cerr << "\n";
}
#endif

AutomationList::AutomationList (double defval, bool with_state)
{
      _frozen = false;
      changed_when_thawed = false;
      _state = Off;
      _style = Absolute;
      _touching = false;
      no_state = with_state;
      min_yval = FLT_MIN;
      max_yval = FLT_MAX;
      max_xval = 0; // means "no limit" 
      default_value = defval;
      _dirty = false;
      rt_insertion_point = events.end();
      lookup_cache.left = -1;
      lookup_cache.range.first = events.end();

      if (!no_state) {
            save_state (_("initial"));
      }
}

AutomationList::AutomationList (const AutomationList& other)
{
      _frozen = false;
      changed_when_thawed = false;
      _style = other._style;
      min_yval = other.min_yval;
      max_yval = other.max_yval;
      max_xval = other.max_xval;
      default_value = other.default_value;
      _state = other._state;
      _touching = other._touching;
      _dirty = false;
      rt_insertion_point = events.end();
      no_state = other.no_state;
      lookup_cache.left = -1;
      lookup_cache.range.first = events.end();

      for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) {
            /* we have to use other point_factory() because
               its virtual and we're in a constructor.
            */
            events.push_back (other.point_factory (**i));
      }

      mark_dirty ();
}

AutomationList::AutomationList (const AutomationList& other, double start, double end)
{
      _frozen = false;
      changed_when_thawed = false;
      _style = other._style;
      min_yval = other.min_yval;
      max_yval = other.max_yval;
      max_xval = other.max_xval;
      default_value = other.default_value;
      _state = other._state;
      _touching = other._touching;
      _dirty = false;
      rt_insertion_point = events.end();
      no_state = other.no_state;
      lookup_cache.left = -1;
      lookup_cache.range.first = events.end();

      /* now grab the relevant points, and shift them back if necessary */

      AutomationList* section = const_cast<AutomationList*>(&other)->copy (start, end);

      if (!section->empty()) {
            for (AutomationList::iterator i = section->begin(); i != section->end(); ++i) {
                  events.push_back (other.point_factory ((*i)->when, (*i)->value));
            }
      }

      delete section;

      mark_dirty ();
}

AutomationList::~AutomationList()
{
      std::set<ControlEvent*> all_events;
      AutomationList::State* asp;

      for (AutomationEventList::iterator x = events.begin(); x != events.end(); ++x) {
            all_events.insert (*x);
      }

      for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {

            if ((asp = dynamic_cast<AutomationList::State*> (*i)) != 0) {
                  
                  for (AutomationEventList::iterator x = asp->events.begin(); x != asp->events.end(); ++x) {
                        all_events.insert (*x);
                  }
            }
      }

      for (std::set<ControlEvent*>::iterator i = all_events.begin(); i != all_events.end(); ++i) {
            delete (*i);
      }
}

bool
AutomationList::operator== (const AutomationList& other)
{
      return events == other.events;
}

AutomationList&
AutomationList::operator= (const AutomationList& other)
{
      if (this != &other) {
            
            events.clear ();
            
            for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) {
                  events.push_back (point_factory (**i));
            }
            
            min_yval = other.min_yval;
            max_yval = other.max_yval;
            max_xval = other.max_xval;
            default_value = other.default_value;
            
            mark_dirty ();
            maybe_signal_changed ();
      }

      return *this;
}

void
AutomationList::maybe_signal_changed ()
{
      mark_dirty ();

      if (_frozen) {
            changed_when_thawed = true;
      } else {
            StateChanged (Change (0));
      }
}

void
AutomationList::set_automation_state (AutoState s)
{
      if (s != _state) {
            _state = s;
            automation_state_changed (); /* EMIT SIGNAL */
      }
}

void
AutomationList::set_automation_style (AutoStyle s)
{
      if (s != _style) {
            _style = s;
            automation_style_changed (); /* EMIT SIGNAL */
      }
}

void
AutomationList::start_touch ()
{
      _touching = true;
      _new_touch = true;
}

void
AutomationList::stop_touch ()
{
      _touching = false;
      _new_touch = false;
}

void
AutomationList::clear ()
{
      {
            LockMonitor lm (lock, __LINE__, __FILE__);
            events.clear ();
            if (!no_state) {
                  save_state (_("cleared"));
            }
            mark_dirty ();
      }

      maybe_signal_changed ();
}

void
AutomationList::x_scale (double factor)
{
      LockMonitor lm (lock, __LINE__, __FILE__);
      _x_scale (factor);
}

bool
AutomationList::extend_to (double when)
{
      LockMonitor lm (lock, __LINE__, __FILE__);
      if (events.empty() || events.back()->when == when) {
            return false;
      }
      double factor = when / events.back()->when;
      _x_scale (factor);
      return true;
}

void AutomationList::_x_scale (double factor)
{
      for (AutomationList::iterator i = events.begin(); i != events.end(); ++i) {
            (*i)->when = floor ((*i)->when * factor);
      }

      save_state ("x-scaled");
      mark_dirty ();
}

void
AutomationList::reposition_for_rt_add (double when)
{
      rt_insertion_point = events.end();
}

#define last_rt_insertion_point rt_insertion_point

void
AutomationList::rt_add (double when, double value)
{
      /* this is for automation recording */

      if ((_state & Touch) && !_touching) {
            return;
      }

      // cerr << "RT: alist @ " << this << " add " << value << " @ " << when << endl;

      {
            LockMonitor lm (lock, __LINE__, __FILE__);

            iterator where;
            TimeComparator cmp;
            ControlEvent cp (when, 0.0);
            bool done = false;

            if ((last_rt_insertion_point != events.end()) && ((*last_rt_insertion_point)->when < when) ) {

                  /* we have a previous insertion point, so we should delete
                     everything between it and the position where we are going
                     to insert this point.
                  */

                  iterator after = last_rt_insertion_point;

                  if (++after != events.end()) {
                        iterator far = after;

                        while (far != events.end()) {
                              if ((*far)->when > when) {
                                    break;
                              }
                              ++far;
                        }

                                if(_new_touch) {
                                        where = far;
                                        last_rt_insertion_point = where;
                                                                                             
                                        if((*where)->when == when) {
                                                (*where)->value = value;
                                                done = true;
                                        }
                                } else {
                                        where = events.erase (after, far);
                                }

                  } else {

                        where = after;

                  }
                  
                  iterator previous = last_rt_insertion_point;
                        --previous;
                  
                  if (last_rt_insertion_point != events.begin() && (*last_rt_insertion_point)->value == value && (*previous)->value == value) {
                        (*last_rt_insertion_point)->when = when;
                        done = true;
                        
                  }
                  
            } else {

                  where = lower_bound (events.begin(), events.end(), &cp, cmp);

                  if (where != events.end()) {
                        if ((*where)->when == when) {
                              (*where)->value = value;
                              done = true;
                        }
                  }
            }
            
            if (!done) {
                  last_rt_insertion_point = events.insert (where, point_factory (when, value));
            }
            
            _new_touch = false;
            mark_dirty ();
      }

      maybe_signal_changed ();
}

#undef last_rt_insertion_point

void
AutomationList::add (double when, double value, bool for_loading)
{
      /* this is for graphical editing and loading data from storage */

      {
            LockMonitor lm (lock, __LINE__, __FILE__);
            TimeComparator cmp;
            ControlEvent cp (when, 0.0f);
            bool insert = true;
            iterator insertion_point;

            for (insertion_point = lower_bound (events.begin(), events.end(), &cp, cmp); insertion_point != events.end(); ++insertion_point) {

                  /* only one point allowed per time point */

                  if ((*insertion_point)->when == when) {
                        (*insertion_point)->value = value;
                        insert = false;
                        break;
                  } 

                  if ((*insertion_point)->when >= when) {
                        break;
                  }
            }

            if (insert) {

                  events.insert (insertion_point, point_factory (when, value));
                  reposition_for_rt_add (0);

            } 

            mark_dirty ();

            if (!no_state && !for_loading) {
                  save_state (_("added event"));
            }
      }

      if (!for_loading) {
            maybe_signal_changed ();
      }
}

void
AutomationList::erase (AutomationList::iterator i)
{
      {
            LockMonitor lm (lock, __LINE__, __FILE__);
            events.erase (i);
            reposition_for_rt_add (0);
            if (!no_state) {
                  save_state (_("removed event"));
            }
            mark_dirty ();
      }
      maybe_signal_changed ();
}

void
AutomationList::erase (AutomationList::iterator start, AutomationList::iterator end)
{
      {
            LockMonitor lm (lock, __LINE__, __FILE__);
            events.erase (start, end);
            reposition_for_rt_add (0);
            if (!no_state) {
                  save_state (_("removed multiple events"));
            }
            mark_dirty ();
      }
      maybe_signal_changed ();
}     

void
AutomationList::erase_range (double start, double endt)
{
      bool erased = false;

      {
            LockMonitor lm (lock, __LINE__, __FILE__);
            TimeComparator cmp;
            ControlEvent cp (start, 0.0f);
            iterator s;
            iterator e;

            if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) {
                  cp.when = endt;
                  e = upper_bound (events.begin(), events.end(), &cp, cmp);
                  events.erase (s, e);
                  reposition_for_rt_add (0);
                  erased = true;
                  if (!no_state) {
                        save_state (_("removed range"));
                  }
                  mark_dirty ();
            }
            
      }

      if (erased) {
            maybe_signal_changed ();
      }
}

void
AutomationList::move_range (iterator start, iterator end, double xdelta, double ydelta)
{
      /* note: we assume higher level logic is in place to avoid this
         reordering the time-order of control events in the list. ie. all
         points after end are later than (end)->when.
      */

      {
            LockMonitor lm (lock, __LINE__, __FILE__);

            while (start != end) {
                  (*start)->when += xdelta;
                  (*start)->value += ydelta;
                  ++start;
            }

            if (!no_state) {
                  save_state (_("event range adjusted"));
            }

            mark_dirty ();
      }

      maybe_signal_changed ();
}

void
AutomationList::modify (iterator iter, double when, double val)
{
      /* note: we assume higher level logic is in place to avoid this
         reordering the time-order of control events in the list. ie. all
         points after *iter are later than when.
      */

      {
            LockMonitor lm (lock, __LINE__, __FILE__);
            (*iter)->when = when;
            (*iter)->value = val;
            if (!no_state) {
                  save_state (_("event adjusted"));
            }

            mark_dirty ();
      }
      
      maybe_signal_changed ();
}

std::pair<AutomationList::iterator,AutomationList::iterator>
AutomationList::control_points_adjacent (double xval)
{
      LockMonitor lm (lock, __LINE__, __FILE__);
      iterator i;
      TimeComparator cmp;
      ControlEvent cp (xval, 0.0f);
      std::pair<iterator,iterator> ret;

      ret.first = events.end();
      ret.second = events.end();

      for (i = lower_bound (events.begin(), events.end(), &cp, cmp); i != events.end(); ++i) {
            
            if (ret.first == events.end()) {
                  if ((*i)->when >= xval) {
                        if (i != events.begin()) {
                              ret.first = i;
                              --ret.first;
                        } else {
                              return ret;
                        }
                  }
            } 
            
            if ((*i)->when > xval) {
                  ret.second = i;
                  break;
            }
      }

      return ret;
}

void
AutomationList::freeze ()
{
      _frozen = true;
}

void
AutomationList::thaw ()
{
      _frozen = false;
      if (changed_when_thawed) {
             StateChanged(Change(0)); /* EMIT SIGNAL */
      }
}

StateManager::State*
AutomationList::state_factory (std::string why) const
{
      State* state = new State (why);

      for (AutomationEventList::const_iterator x = events.begin(); x != events.end(); ++x) {
            state->events.push_back (point_factory (**x));
      }

      return state;
}

Change
AutomationList::restore_state (StateManager::State& state) 
{
      {
            LockMonitor lm (lock, __LINE__, __FILE__);
            State* lstate = dynamic_cast<State*> (&state);

            events.clear ();
            for (AutomationEventList::const_iterator x = lstate->events.begin(); x != lstate->events.end(); ++x) {
                  events.push_back (point_factory (**x));
            }
      }

      return Change (0);
}

UndoAction
AutomationList::get_memento () const
{
      return bind (slot (*(const_cast<AutomationList*> (this)), &StateManager::use_state), _current_state_id);
}

void
AutomationList::set_max_xval (double x)
{
      max_xval = x;
}

void 
AutomationList::mark_dirty ()
{
      lookup_cache.left = -1;
      _dirty = true;
}

void
AutomationList::truncate_end (double last_coordinate)
{
      {
            LockMonitor lm (lock, __LINE__, __FILE__);
            ControlEvent cp (last_coordinate, 0);
            list<ControlEvent*>::reverse_iterator i;
            double last_val;

            if (events.empty()) {
                  fatal << _("programming error:")
                        << "AutomationList::truncate_end() called on an empty list"
                        << endmsg;
                  /*NOTREACHED*/
                  return;
            }

            if (last_coordinate == events.back()->when) {
                  return;
            }

            if (last_coordinate > events.back()->when) {
                  
                  /* extending end:
                  */

                  iterator foo = events.begin();
                  bool lessthantwo;

                  if (foo == events.end()) {
                        lessthantwo = true;
                  } else if (++foo == events.end()) {
                        lessthantwo = true;
                  } else {
                        lessthantwo = false;
                  }

                  if (lessthantwo) {
                        /* less than 2 points: add a new point */
                        events.push_back (point_factory (last_coordinate, events.back()->value));
                  } else {

                        /* more than 2 points: check to see if the last 2 values
                           are equal. if so, just move the position of the
                           last point. otherwise, add a new point.
                        */

                        iterator penultimate = events.end();
                        --penultimate; /* points at last point */
                        --penultimate; /* points at the penultimate point */
                        
                        if (events.back()->value == (*penultimate)->value) {
                              events.back()->when = last_coordinate;
                        } else {
                              events.push_back (point_factory (last_coordinate, events.back()->value));
                        }
                  }

            } else {

                  /* shortening end */

                  last_val = unlocked_eval (last_coordinate);
                  last_val = max ((double) min_yval, last_val);
                  last_val = min ((double) max_yval, last_val);
                  
                  i = events.rbegin();
                  
                  /* make i point to the last control point */
                  
                  ++i;
                  
                  /* now go backwards, removing control points that are
                     beyond the new last coordinate.
                  */

                  uint32_t sz = events.size();
                  
                  while (i != events.rend() && sz > 2) {
                        list<ControlEvent*>::reverse_iterator tmp;
                        
                        tmp = i;
                        ++tmp;
                        
                        if ((*i)->when < last_coordinate) {
                              break;
                        }
                        
                        events.erase (i.base());
                        --sz;

                        i = tmp;
                  }
                  
                  events.back()->when = last_coordinate;
                  events.back()->value = last_val;
            }

            reposition_for_rt_add (0);
            mark_dirty();
      }

      maybe_signal_changed ();
}

void
AutomationList::truncate_start (double overall_length)
{
      {
            LockMonitor lm (lock, __LINE__, __FILE__);
            AutomationList::iterator i;
            double first_legal_value;
            double first_legal_coordinate;

            if (events.empty()) {
                  fatal << _("programming error:")
                        << "AutomationList::truncate_start() called on an empty list"
                        << endmsg;
                  /*NOTREACHED*/
                  return;
            }
            
            if (overall_length == events.back()->when) {
                  /* no change in overall length */
                  return;
            }
            
            if (overall_length > events.back()->when) {
                  
                  /* growing at front: duplicate first point. shift all others */

                  double shift = overall_length - events.back()->when;
                  uint32_t np;

                  for (np = 0, i = events.begin(); i != events.end(); ++i, ++np) {
                        (*i)->when += shift;
                  }

                  if (np < 2) {

                        /* less than 2 points: add a new point */
                        events.push_front (point_factory (0, events.front()->value));

                  } else {

                        /* more than 2 points: check to see if the first 2 values
                           are equal. if so, just move the position of the
                           first point. otherwise, add a new point.
                        */

                        iterator second = events.begin();
                        ++second; /* points at the second point */
                        
                        if (events.front()->value == (*second)->value) {
                              /* first segment is flat, just move start point back to zero */
                              events.front()->when = 0;
                        } else {
                              /* leave non-flat segment in place, add a new leading point. */
                              events.push_front (point_factory (0, events.front()->value));
                        }
                  }

            } else {

                  /* shrinking at front */
                  
                  first_legal_coordinate = events.back()->when - overall_length;
                  first_legal_value = unlocked_eval (first_legal_coordinate);
                  first_legal_value = max (min_yval, first_legal_value);
                  first_legal_value = min (max_yval, first_legal_value);

                  /* remove all events earlier than the new "front" */

                  i = events.begin();
                  
                  while (i != events.end() && !events.empty()) {
                        list<ControlEvent*>::iterator tmp;
                        
                        tmp = i;
                        ++tmp;
                        
                        if ((*i)->when > first_legal_coordinate) {
                              break;
                        }
                        
                        events.erase (i);
                        
                        i = tmp;
                  }
                  

                  /* shift all remaining points left to keep their same
                     relative position
                  */
                  
                  for (i = events.begin(); i != events.end(); ++i) {
                        (*i)->when -= first_legal_coordinate;
                  }

                  /* add a new point for the interpolated new value */
                  
                  events.push_front (point_factory (0, first_legal_value));
            }         

            reposition_for_rt_add (0);

            mark_dirty();
      }

      maybe_signal_changed ();
}

double
AutomationList::unlocked_eval (double x)
{
      return shared_eval (x);
}

double
AutomationList::shared_eval (double x) 
{
      pair<AutomationEventList::iterator,AutomationEventList::iterator> range;
      int32_t npoints;
      double lpos, upos;
      double lval, uval;
      double fraction;

      npoints = events.size();

      switch (npoints) {
      case 0:
            return default_value;

      case 1:
            if (x >= events.front()->when) {
                  return events.front()->value;
            } else {
                  // return default_value;
                  return events.front()->value;
            } 
            
      case 2:
            if (x >= events.back()->when) {
                  return events.back()->value;
            } else if (x == events.front()->when) {
                  return events.front()->value;
            } else if (x < events.front()->when) {
                  // return default_value;
                  return events.front()->value;
            }

            lpos = events.front()->when;
            lval = events.front()->value;
            upos = events.back()->when;
            uval = events.back()->value;
            
            /* linear interpolation betweeen the two points
            */

            fraction = (double) (x - lpos) / (double) (upos - lpos);
            return lval + (fraction * (uval - lval));

      default:

            if (x >= events.back()->when) {
                  return events.back()->value;
            } else if (x == events.front()->when) {
                  return events.front()->value;
            } else if (x < events.front()->when) {
                  // return default_value;
                  return events.front()->value;
            }

            return multipoint_eval (x);
            break;
      }
}

double
AutomationList::multipoint_eval (double x) 
{
      pair<AutomationList::iterator,AutomationList::iterator> range;
      double upos, lpos;
      double uval, lval;
      double fraction;

      /* only do the range lookup if x is in a different range than last time
         this was called (or if the lookup cache has been marked "dirty" (left<0)
      */

      if ((lookup_cache.left < 0) ||
          ((lookup_cache.left > x) || 
           (lookup_cache.range.first == events.end()) || 
           ((*lookup_cache.range.second)->when < x))) {

            ControlEvent cp (x, 0);
            TimeComparator cmp;
            
            lookup_cache.range = equal_range (events.begin(), events.end(), &cp, cmp);
      }
      
      range = lookup_cache.range;

      if (range.first == range.second) {

            /* x does not exist within the list as a control point */

            lookup_cache.left = x;

            if (range.first != events.begin()) {
                  --range.first;
                  lpos = (*range.first)->when;
                  lval = (*range.first)->value;
            }  else {
                  /* we're before the first point */
                  // return default_value;
                  return events.front()->value;
            }
            
            if (range.second == events.end()) {
                  /* we're after the last point */
                  return events.back()->value;
            }

            upos = (*range.second)->when;
            uval = (*range.second)->value;
            
            /* linear interpolation betweeen the two points
               on either side of x
            */

            fraction = (double) (x - lpos) / (double) (upos - lpos);
            return lval + (fraction * (uval - lval));

      } 

      /* x is a control point in the data */
      lookup_cache.left = -1;
      return (*range.first)->value;
}

AutomationList*
AutomationList::cut (iterator start, iterator end)
{
      AutomationList* nal = new AutomationList (default_value);

      {
            LockMonitor lm (lock, __LINE__, __FILE__);

            for (iterator x = start; x != end; ) {
                  iterator tmp;
                  
                  tmp = x;
                  ++tmp;
                  
                  nal->events.push_back (point_factory (**x));
                  events.erase (x);
                  
                  reposition_for_rt_add (0);

                  x = tmp;
            }

            mark_dirty ();
      }

      maybe_signal_changed ();

      return nal;
}

AutomationList*
AutomationList::cut_copy_clear (double start, double end, int op)
{
      AutomationList* nal = new AutomationList (default_value);
      iterator s, e;
      ControlEvent cp (start, 0.0);
      TimeComparator cmp;
      bool changed = false;
      
      {
            LockMonitor lm (lock, __LINE__, __FILE__);

            if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) == events.end()) {
                  return nal;
            }

            cp.when = end;
            e = upper_bound (events.begin(), events.end(), &cp, cmp);

            if (op != 2 && (*s)->when != start) {
                  nal->events.push_back (point_factory (0, unlocked_eval (start)));
            }

            for (iterator x = s; x != e; ) {
                  iterator tmp;
                  
                  tmp = x;
                  ++tmp;

                  changed = true;
                  
                  /* adjust new points to be relative to start, which
                     has been set to zero.
                  */
                  
                  if (op != 2) {
                        nal->events.push_back (point_factory ((*x)->when - start, (*x)->value));
                  }

                  if (op != 1) {
                        events.erase (x);
                  }
                  
                  x = tmp;
            }

            if (op != 2 && nal->events.back()->when != end - start) {
                  nal->events.push_back (point_factory (end - start, unlocked_eval (end)));
            }

            if (changed) {
                  reposition_for_rt_add (0);
                  if (!no_state) {
                        save_state (_("cut/copy/clear"));
                  }
            }

            mark_dirty ();
      }

      maybe_signal_changed ();

      return nal;

}

AutomationList*
AutomationList::copy (iterator start, iterator end)
{
      AutomationList* nal = new AutomationList (default_value);

      {
            LockMonitor lm (lock, __LINE__, __FILE__);
            
            for (iterator x = start; x != end; ) {
                  iterator tmp;
                  
                  tmp = x;
                  ++tmp;
                  
                  nal->events.push_back (point_factory (**x));
                  
                  x = tmp;
            }

            if (!no_state) {
                  save_state (_("copy"));
            }
      }

      return nal;
}

AutomationList*
AutomationList::cut (double start, double end)
{
      return cut_copy_clear (start, end, 0);
}

AutomationList*
AutomationList::copy (double start, double end)
{
      return cut_copy_clear (start, end, 1);
}

void
AutomationList::clear (double start, double end)
{
      (void) cut_copy_clear (start, end, 2);
}

bool
AutomationList::paste (AutomationList& alist, double pos, float times)
{
      if (alist.events.empty()) {
            return false;
      }

      {
            LockMonitor lm (lock, __LINE__, __FILE__);
            iterator where;
            iterator prev;
            double end = 0;
            ControlEvent cp (pos, 0.0);
            TimeComparator cmp;

            where = upper_bound (events.begin(), events.end(), &cp, cmp);

            for (iterator i = alist.begin();i != alist.end(); ++i) {
                  events.insert (where, point_factory( (*i)->when+pos,( *i)->value));
                  end = (*i)->when + pos;
            }
      
      
            /* move all  points after the insertion along the timeline by 
               the correct amount.
            */

            while (where != events.end()) {
                  iterator tmp;
                  if ((*where)->when <= end) {
                        tmp = where;
                        ++tmp;
                        events.erase(where);
                        where = tmp;

                  } else {
                        break;
                  }
            }

            reposition_for_rt_add (0);

            if (!no_state) {
                  save_state (_("paste"));
            }

            mark_dirty ();
      }

      maybe_signal_changed ();
      return true;
}

ControlEvent*
AutomationList::point_factory (double when, double val) const
{
      return new ControlEvent (when, val);
}

ControlEvent*
AutomationList::point_factory (const ControlEvent& other) const
{
      return new ControlEvent (other);
}

void
AutomationList::store_state (XMLNode& node) const
{
      LocaleGuard lg (X_("POSIX"));

      for (const_iterator i = const_begin(); i != const_end(); ++i) {
            char buf[64];
            
            XMLNode *pointnode = new XMLNode ("point");
            
            snprintf (buf, sizeof (buf), "%" PRIu32, (jack_nframes_t) floor ((*i)->when));
            pointnode->add_property ("x", buf);
            snprintf (buf, sizeof (buf), "%f", (*i)->value);
            pointnode->add_property ("y", buf);

            node.add_child_nocopy (*pointnode);
      }
}

void
AutomationList::load_state (const XMLNode& node)
{
      const XMLNodeList& elist = node.children();
      XMLNodeConstIterator i;
      XMLProperty* prop;
      jack_nframes_t x;
      double y;

      clear ();
      
      for (i = elist.begin(); i != elist.end(); ++i) {
            
            if ((prop = (*i)->property ("x")) == 0) {
                  error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg;
                  continue;
            }
            x = atoi (prop->value().c_str());
            
            if ((prop = (*i)->property ("y")) == 0) {
                  error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg;
                  continue;
            }
            y = atof (prop->value().c_str());
            
            add (x, y);
      }
}

Generated by  Doxygen 1.6.0   Back to index