massive changes in automation state handling, not entirely complete; some bug fixes for automation line drawing

git-svn-id: svn://localhost/ardour2/trunk@1034 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2006-10-31 02:40:08 +00:00
parent 74df5d49c8
commit 71c94e6943
29 changed files with 543 additions and 229 deletions

View file

@ -305,7 +305,6 @@ env.Alias('install', env.Install(os.path.join(install_prefix, 'share/ardour2'),
env.Alias('install', env.Install(os.path.join(install_prefix, 'share/ardour2/pixmaps'), pixmap_files)) env.Alias('install', env.Install(os.path.join(install_prefix, 'share/ardour2/pixmaps'), pixmap_files))
env.Alias('install', env.Install(os.path.join(install_prefix, 'share/ardour2/icons'), icon_files)) env.Alias('install', env.Install(os.path.join(install_prefix, 'share/ardour2/icons'), icon_files))
env.AlwaysBuild ('version.cc')
env.Alias ('version', gtkardour.VersionBuild(['version.cc','version.h'], 'SConscript')) env.Alias ('version', gtkardour.VersionBuild(['version.cc','version.h'], 'SConscript'))
#dist #dist

View file

@ -902,12 +902,11 @@ AudioRegionView::add_gain_point_event (ArdourCanvas::Item *item, GdkEvent *ev)
trackview.session().begin_reversible_command (_("add gain control point")); trackview.session().begin_reversible_command (_("add gain control point"));
XMLNode &before = audio_region()->envelope().get_state(); XMLNode &before = audio_region()->envelope().get_state();
if (!audio_region()->envelope_active()) { if (!audio_region()->envelope_active()) {
XMLNode &before = audio_region()->get_state(); XMLNode &region_before = audio_region()->get_state();
audio_region()->set_envelope_active(true); audio_region()->set_envelope_active(true);
XMLNode &after = audio_region()->get_state(); XMLNode &region_after = audio_region()->get_state();
trackview.session().add_command (new MementoCommand<AudioRegion>(*(audio_region().get()), &before, &after)); trackview.session().add_command (new MementoCommand<AudioRegion>(*(audio_region().get()), &region_before, &region_after));
} }
audio_region()->envelope().add (fx, y); audio_region()->envelope().add (fx, y);

View file

@ -245,7 +245,7 @@ AutomationLine::AutomationLine (const string & name, TimeAxisView& tv, ArdourCan
alist.StateChanged.connect (mem_fun(*this, &AutomationLine::list_changed)); alist.StateChanged.connect (mem_fun(*this, &AutomationLine::list_changed));
trackview.session().register_with_memento_command_factory(_id, this); trackview.session().register_with_memento_command_factory(alist.id(), this);
} }
@ -670,11 +670,16 @@ AutomationLine::determine_visible_control_points (ALPoints& points)
uint32_t this_ry = 0; uint32_t this_ry = 0;
uint32_t prev_ry = 0; uint32_t prev_ry = 0;
double* slope; double* slope;
double box_size;
uint32_t cpsize;
/* hide all existing points, and the line */ /* hide all existing points, and the line */
cpsize = 0;
for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) { for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
(*i)->hide(); (*i)->hide();
++cpsize;
} }
line->hide (); line->hide ();
@ -695,6 +700,13 @@ AutomationLine::determine_visible_control_points (ALPoints& points)
slope[n] = ydelta/xdelta; slope[n] = ydelta/xdelta;
} }
if (_height > (guint32) TimeAxisView::Larger) {
box_size = 8.0;
} else if (_height > (guint32) TimeAxisView::Normal) {
box_size = 6.0;
} else {
box_size = 4.0;
}
/* read all points and decide which ones to show as control points */ /* read all points and decide which ones to show as control points */
view_index = 0; view_index = 0;
@ -738,27 +750,23 @@ AutomationLine::determine_visible_control_points (ALPoints& points)
this_rx = (uint32_t) rint (tx); this_rx = (uint32_t) rint (tx);
this_ry = (unsigned long) rint (ty); this_ry = (unsigned long) rint (ty);
if (view_index && pi != npoints && (this_rx == prev_rx) && (this_ry == prev_ry)) { if (view_index && pi != npoints && (this_rx == prev_rx) && (this_ry == prev_ry) ||
((this_rx - prev_rx) < (box_size + 2))) {
continue; continue;
} }
/* ok, we should display this point */ /* ok, we should display this point */
if (view_index >= control_points.size()) { if (view_index >= cpsize) {
/* make sure we have enough control points */ /* make sure we have enough control points */
ControlPoint* ncp = new ControlPoint (*this); ControlPoint* ncp = new ControlPoint (*this);
if (_height > (guint32) TimeAxisView::Larger) { ncp->set_size (box_size);
ncp->set_size (8.0);
} else if (_height > (guint32) TimeAxisView::Normal) {
ncp->set_size (6.0);
} else {
ncp->set_size (4.0);
}
control_points.push_back (ncp); control_points.push_back (ncp);
++cpsize;
} }
ControlPoint::ShapeType shape; ControlPoint::ShapeType shape;
@ -829,6 +837,10 @@ AutomationLine::determine_visible_control_points (ALPoints& points)
line_points.push_back (Art::Point (0,0)); line_points.push_back (Art::Point (0,0));
} }
while (line_points.size() > npoints) {
line_points.pop_back ();
}
for (view_index = 0; view_index < npoints; ++view_index) { for (view_index = 0; view_index < npoints; ++view_index) {
line_points[view_index].set_x (control_points[view_index]->get_x()); line_points[view_index].set_x (control_points[view_index]->get_x());
line_points[view_index].set_y (control_points[view_index]->get_y()); line_points[view_index].set_y (control_points[view_index]->get_y());
@ -1193,9 +1205,7 @@ AutomationLine::reset_callback (const AutomationList& events)
for (ai = events.const_begin(); ai != events.const_end(); ++ai) { for (ai = events.const_begin(); ai != events.const_end(); ++ai) {
double translated_y; double translated_y = (*ai)->value;
translated_y = (*ai)->value;
model_to_view_y (translated_y); model_to_view_y (translated_y);
tmp_points.push_back (ALPoint (trackview.editor.frame_to_unit ((*ai)->when), tmp_points.push_back (ALPoint (trackview.editor.frame_to_unit ((*ai)->when),
@ -1263,16 +1273,16 @@ AutomationLine::hide_all_but_selected_control_points ()
} }
} }
XMLNode &AutomationLine::get_state(void) XMLNode &
AutomationLine::get_state (void)
{ {
XMLNode *node = new XMLNode("AutomationLine"); /* function as a proxy for the model */
node->add_child_nocopy(alist.get_state()); return alist.get_state();
return *node;
} }
int AutomationLine::set_state(const XMLNode &node) int
AutomationLine::set_state (const XMLNode &node)
{ {
// TODO /* function as a proxy for the model */
//alist.set_state(node); return alist.set_state (node);
return 0;
} }

View file

@ -96,6 +96,10 @@ Editor::draw_metric_marks (const Metrics& metrics)
void void
Editor::tempo_map_changed (Change ignored) Editor::tempo_map_changed (Change ignored)
{ {
if (!session) {
return;
}
ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::tempo_map_changed), ignored)); ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::tempo_map_changed), ignored));
BBT_Time previous_beat, next_beat; // the beats previous to the leftmost frame and after the rightmost frame BBT_Time previous_beat, next_beat; // the beats previous to the leftmost frame and after the rightmost frame
@ -112,13 +116,13 @@ Editor::tempo_map_changed (Change ignored)
previous_beat.ticks = 0; previous_beat.ticks = 0;
if (session->tempo_map().meter_at(leftmost_frame + current_page_frames()).beats_per_bar () > next_beat.beats + 1) { if (session->tempo_map().meter_at(leftmost_frame + current_page_frames()).beats_per_bar () > next_beat.beats + 1) {
next_beat.beats += 1; next_beat.beats += 1;
} else { } else {
next_beat.bars += 1; next_beat.bars += 1;
next_beat.beats = 1; next_beat.beats = 1;
} }
next_beat.ticks = 0; next_beat.ticks = 0;
if (current_bbt_points) { if (current_bbt_points) {
delete current_bbt_points; delete current_bbt_points;
current_bbt_points = 0; current_bbt_points = 0;

View file

@ -63,12 +63,10 @@ GainAutomationTimeAxisView::add_automation_event (ArdourCanvas::Item* item, GdkE
lines.front()->view_to_model_y (y); lines.front()->view_to_model_y (y);
_session.begin_reversible_command (_("add gain automation event")); _session.begin_reversible_command (_("add gain automation event"));
XMLNode& before = curve.get_state();
XMLNode &before = curve.get_state();
curve.add (when, y); curve.add (when, y);
XMLNode &after = curve.get_state(); XMLNode& after = curve.get_state();
_session.add_command(new MementoCommand<ARDOUR::Curve>(curve, &before, &after)); _session.commit_reversible_command (new MementoCommand<ARDOUR::Curve>(curve, &before, &after));
_session.commit_reversible_command ();
_session.set_dirty (); _session.set_dirty ();
} }

View file

@ -50,7 +50,7 @@ AudioRegionGainLine::start_drag (ControlPoint* cp, float fraction)
if (!rv.audio_region()->envelope_active()) { if (!rv.audio_region()->envelope_active()) {
trackview.session().add_command(new MementoCommand<AudioRegion>(*(rv.audio_region().get()), &rv.audio_region()->get_state(), 0)); trackview.session().add_command(new MementoCommand<AudioRegion>(*(rv.audio_region().get()), &rv.audio_region()->get_state(), 0));
rv.audio_region()->set_envelope_active(false); rv.audio_region()->set_envelope_active(false);
} }
} }
// This is an extended copy from AutomationList // This is an extended copy from AutomationList
@ -65,12 +65,12 @@ AudioRegionGainLine::remove_point (ControlPoint& cp)
XMLNode &before = get_state(); XMLNode &before = get_state();
if (!rv.audio_region()->envelope_active()) { if (!rv.audio_region()->envelope_active()) {
XMLNode &before = rv.audio_region()->get_state(); XMLNode &region_before = rv.audio_region()->get_state();
rv.audio_region()->set_envelope_active(true); rv.audio_region()->set_envelope_active(true);
XMLNode &after = rv.audio_region()->get_state(); XMLNode &region_after = rv.audio_region()->get_state();
trackview.session().add_command(new MementoCommand<AudioRegion>(*(rv.audio_region().get()), &before, &after)); trackview.session().add_command(new MementoCommand<AudioRegion>(*(rv.audio_region().get()), &region_before, &region_after));
} }
alist.erase (mr.start, mr.end); alist.erase (mr.start, mr.end);
trackview.editor.current_session()->add_command (new MementoCommand<AudioRegionGainLine>(*this, &before, &get_state())); trackview.editor.current_session()->add_command (new MementoCommand<AudioRegionGainLine>(*this, &before, &get_state()));
@ -84,7 +84,8 @@ AudioRegionGainLine::end_drag (ControlPoint* cp)
if (!rv.audio_region()->envelope_active()) { if (!rv.audio_region()->envelope_active()) {
rv.audio_region()->set_envelope_active(true); rv.audio_region()->set_envelope_active(true);
trackview.session().add_command(new MementoCommand<AudioRegion>(*(rv.audio_region().get()), 0, &rv.audio_region()->get_state())); trackview.session().add_command(new MementoCommand<AudioRegion>(*(rv.audio_region().get()), 0, &rv.audio_region()->get_state()));
} }
AutomationLine::end_drag(cp); AutomationLine::end_drag(cp);
} }

View file

@ -153,8 +153,6 @@ class RouteUI : public virtual AxisView
void reversibly_apply_route_boolean (string name, void (ARDOUR::Route::*func)(bool, void*), bool, void *); void reversibly_apply_route_boolean (string name, void (ARDOUR::Route::*func)(bool, void*), bool, void *);
void reversibly_apply_audio_track_boolean (string name, void (ARDOUR::AudioTrack::*func)(bool, void*), bool, void *); void reversibly_apply_audio_track_boolean (string name, void (ARDOUR::AudioTrack::*func)(bool, void*), bool, void *);
sigc::signal<void> GoingAway;
}; };
#endif /* __ardour_route_ui__ */ #endif /* __ardour_route_ui__ */

View file

@ -69,6 +69,8 @@ class AudioTrack : public Track
bool meter); bool meter);
uint32_t n_process_buffers (); uint32_t n_process_buffers ();
int _set_state (const XMLNode&, bool call_base);
private: private:
int set_diskstream (boost::shared_ptr<AudioDiskstream>, void *); int set_diskstream (boost::shared_ptr<AudioDiskstream>, void *);

View file

@ -60,7 +60,8 @@ class AutomationList : public PBD::StatefulDestructible
typedef AutomationEventList::iterator iterator; typedef AutomationEventList::iterator iterator;
typedef AutomationEventList::const_iterator const_iterator; typedef AutomationEventList::const_iterator const_iterator;
AutomationList(double default_value, bool no_state = false); AutomationList (double default_value);
AutomationList (const XMLNode&);
~AutomationList(); ~AutomationList();
AutomationList (const AutomationList&); AutomationList (const AutomationList&);
@ -151,8 +152,10 @@ class AutomationList : public PBD::StatefulDestructible
sigc::signal<void,Change> StateChanged; sigc::signal<void,Change> StateChanged;
XMLNode &get_state(void); XMLNode& get_state(void);
int set_state (const XMLNode &s); int set_state (const XMLNode &s);
XMLNode& state (bool full);
XMLNode& serialize_events ();
void set_max_xval (double); void set_max_xval (double);
double get_max_xval() const { return max_xval; } double get_max_xval() const { return max_xval; }
@ -204,11 +207,11 @@ class AutomationList : public PBD::StatefulDestructible
double min_yval; double min_yval;
double max_yval; double max_yval;
double default_value; double default_value;
bool no_state;
iterator rt_insertion_point; iterator rt_insertion_point;
double rt_pos; double rt_pos;
void fast_simple_add (double when, double value);
void maybe_signal_changed (); void maybe_signal_changed ();
void mark_dirty (); void mark_dirty ();
void _x_scale (double factor); void _x_scale (double factor);
@ -235,6 +238,8 @@ class AutomationList : public PBD::StatefulDestructible
virtual ControlEvent* point_factory (const ControlEvent&) const; virtual ControlEvent* point_factory (const ControlEvent&) const;
AutomationList* cut_copy_clear (double, double, int op); AutomationList* cut_copy_clear (double, double, int op);
int deserialize_events (const XMLNode&);
}; };
} // namespace } // namespace

View file

@ -51,6 +51,7 @@ class Curve : public AutomationList
~Curve (); ~Curve ();
Curve (const Curve& other); Curve (const Curve& other);
Curve (const Curve& other, double start, double end); Curve (const Curve& other, double start, double end);
Curve (const XMLNode&);
bool rt_safe_get_vector (double x0, double x1, float *arg, int32_t veclen); bool rt_safe_get_vector (double x0, double x1, float *arg, int32_t veclen);
void get_vector (double x0, double x1, float *arg, int32_t veclen); void get_vector (double x0, double x1, float *arg, int32_t veclen);

View file

@ -144,6 +144,7 @@ class PluginInsert : public Insert
nframes_t latency(); nframes_t latency();
void transport_stopped (nframes_t now); void transport_stopped (nframes_t now);
void automation_snapshot (nframes_t now);
private: private:

View file

@ -67,9 +67,11 @@ class IO : public PBD::StatefulDestructible
IO (Session&, string name, IO (Session&, string name,
int input_min = -1, int input_max = -1, int input_min = -1, int input_max = -1,
int output_min = -1, int output_max = -1, int output_min = -1, int output_max = -1,
DataType default_type = DataType::AUDIO); DataType default_type = DataType::AUDIO);
virtual ~IO(); IO (Session&, const XMLNode&, DataType default_type = DataType::AUDIO);
virtual ~IO();
int input_minimum() const { return _input_minimum; } int input_minimum() const { return _input_minimum; }
int input_maximum() const { return _input_maximum; } int input_maximum() const { return _input_maximum; }
@ -205,6 +207,14 @@ public:
/* automation */ /* automation */
static void set_automation_interval (jack_nframes_t frames) {
_automation_interval = frames;
}
static jack_nframes_t automation_interval() {
return _automation_interval;
}
void clear_automation (); void clear_automation ();
bool gain_automation_recording() const { bool gain_automation_recording() const {
@ -226,6 +236,7 @@ public:
sigc::signal<void> gain_automation_style_changed; sigc::signal<void> gain_automation_style_changed;
virtual void transport_stopped (nframes_t now); virtual void transport_stopped (nframes_t now);
void automation_snapshot (nframes_t now);
ARDOUR::Curve& gain_automation_curve () { return _gain_automation_curve; } ARDOUR::Curve& gain_automation_curve () { return _gain_automation_curve; }
@ -289,6 +300,9 @@ public:
GainControllable _gain_control; GainControllable _gain_control;
nframes_t last_automation_snapshot;
static nframes_t _automation_interval;
AutoState _gain_automation_state; AutoState _gain_automation_state;
AutoStyle _gain_automation_style; AutoStyle _gain_automation_style;

View file

@ -72,8 +72,7 @@ class Route : public IO
Route (Session&, std::string name, int input_min, int input_max, int output_min, int output_max, Route (Session&, std::string name, int input_min, int input_max, int output_min, int output_max,
Flag flags = Flag(0), DataType default_type = DataType::AUDIO); Flag flags = Flag(0), DataType default_type = DataType::AUDIO);
Route (Session&, const XMLNode&, DataType default_type = DataType::AUDIO);
Route (Session&, const XMLNode&);
virtual ~Route(); virtual ~Route();
std::string comment() { return _comment; } std::string comment() { return _comment; }
@ -233,6 +232,7 @@ class Route : public IO
return _mute_control; return _mute_control;
} }
void automation_snapshot (nframes_t now);
void protect_automation (); void protect_automation ();
void set_remote_control_id (uint32_t id); void set_remote_control_id (uint32_t id);
@ -315,6 +315,8 @@ class Route : public IO
uint32_t pans_required() const; uint32_t pans_required() const;
uint32_t n_process_buffers (); uint32_t n_process_buffers ();
virtual int _set_state (const XMLNode&, bool call_base);
private: private:
void init (); void init ();

View file

@ -91,11 +91,17 @@ namespace ARDOUR {
Play = 0x4 Play = 0x4
}; };
std::string auto_state_to_string (AutoState);
AutoState string_to_auto_state (std::string);
enum AutoStyle { enum AutoStyle {
Absolute = 0x1, Absolute = 0x1,
Trim = 0x2 Trim = 0x2
}; };
std::string auto_style_to_string (AutoStyle);
AutoStyle string_to_auto_style (std::string);
enum AlignStyle { enum AlignStyle {
CaptureTime, CaptureTime,
ExistingMaterial ExistingMaterial

View file

@ -65,7 +65,7 @@ AudioTrack::AudioTrack (Session& sess, string name, Route::Flag flag, TrackMode
AudioTrack::AudioTrack (Session& sess, const XMLNode& node) AudioTrack::AudioTrack (Session& sess, const XMLNode& node)
: Track (sess, node) : Track (sess, node)
{ {
set_state (node); _set_state (node, false);
} }
AudioTrack::~AudioTrack () AudioTrack::~AudioTrack ()
@ -187,12 +187,20 @@ AudioTrack::audio_diskstream() const
int int
AudioTrack::set_state (const XMLNode& node) AudioTrack::set_state (const XMLNode& node)
{
return _set_state (node, true);
}
int
AudioTrack::_set_state (const XMLNode& node, bool call_base)
{ {
const XMLProperty *prop; const XMLProperty *prop;
XMLNodeConstIterator iter; XMLNodeConstIterator iter;
if (Route::set_state (node)) { if (call_base) {
return -1; if (Route::set_state (node)) {
return -1;
}
} }
if ((prop = node.property (X_("mode"))) != 0) { if ((prop = node.property (X_("mode"))) != 0) {
@ -507,6 +515,16 @@ AudioTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame,
nframes_t transport_frame; nframes_t transport_frame;
boost::shared_ptr<AudioDiskstream> diskstream = audio_diskstream(); boost::shared_ptr<AudioDiskstream> diskstream = audio_diskstream();
{
Glib::RWLock::ReaderLock lm (redirect_lock, Glib::TRY_LOCK);
if (lm.locked()) {
// automation snapshot can also be called from the non-rt context
// and it uses the redirect list, so we take the lock out here
automation_snapshot (start_frame);
}
}
if (n_outputs() == 0 && _redirects.empty()) { if (n_outputs() == 0 && _redirects.empty()) {
return 0; return 0;
} }

View file

@ -47,14 +47,13 @@ static void dumpit (const AutomationList& al, string prefix = "")
} }
#endif #endif
AutomationList::AutomationList (double defval, bool with_state) AutomationList::AutomationList (double defval)
{ {
_frozen = false; _frozen = false;
changed_when_thawed = false; changed_when_thawed = false;
_state = Off; _state = Off;
_style = Absolute; _style = Absolute;
_touching = false; _touching = false;
no_state = with_state;
min_yval = FLT_MIN; min_yval = FLT_MIN;
max_yval = FLT_MAX; max_yval = FLT_MAX;
max_xval = 0; // means "no limit" max_xval = 0; // means "no limit"
@ -80,7 +79,6 @@ AutomationList::AutomationList (const AutomationList& other)
_touching = other._touching; _touching = other._touching;
_dirty = false; _dirty = false;
rt_insertion_point = events.end(); rt_insertion_point = events.end();
no_state = other.no_state;
lookup_cache.left = -1; lookup_cache.left = -1;
lookup_cache.range.first = events.end(); lookup_cache.range.first = events.end();
@ -108,7 +106,6 @@ AutomationList::AutomationList (const AutomationList& other, double start, doubl
_touching = other._touching; _touching = other._touching;
_dirty = false; _dirty = false;
rt_insertion_point = events.end(); rt_insertion_point = events.end();
no_state = other.no_state;
lookup_cache.left = -1; lookup_cache.left = -1;
lookup_cache.range.first = events.end(); lookup_cache.range.first = events.end();
@ -125,13 +122,34 @@ AutomationList::AutomationList (const AutomationList& other, double start, doubl
delete section; delete section;
mark_dirty (); mark_dirty ();
AutomationListCreated(this);
}
AutomationList::AutomationList (const XMLNode& node)
{
_frozen = false;
changed_when_thawed = false;
_touching = false;
min_yval = FLT_MIN;
max_yval = FLT_MAX;
max_xval = 0; // means "no limit"
_dirty = false;
_state = Off;
_style = Absolute;
rt_insertion_point = events.end();
lookup_cache.left = -1;
lookup_cache.range.first = events.end();
set_state (node);
AutomationListCreated(this); AutomationListCreated(this);
} }
AutomationList::~AutomationList() AutomationList::~AutomationList()
{ {
GoingAway (); GoingAway ();
for (AutomationEventList::iterator x = events.begin(); x != events.end(); ++x) { for (AutomationEventList::iterator x = events.begin(); x != events.end(); ++x) {
delete (*x); delete (*x);
} }
@ -346,12 +364,19 @@ AutomationList::rt_add (double when, double value)
maybe_signal_changed (); maybe_signal_changed ();
} }
void
AutomationList::fast_simple_add (double when, double value)
{
/* to be used only for loading pre-sorted data from saved state */
events.insert (events.end(), point_factory (when, value));
}
#undef last_rt_insertion_point #undef last_rt_insertion_point
void void
AutomationList::add (double when, double value) AutomationList::add (double when, double value)
{ {
/* this is for graphical editing and loading data from storage */ /* this is for graphical editing */
{ {
Glib::Mutex::Lock lm (lock); Glib::Mutex::Lock lm (lock);
@ -395,11 +420,6 @@ AutomationList::erase (AutomationList::iterator i)
Glib::Mutex::Lock lm (lock); Glib::Mutex::Lock lm (lock);
events.erase (i); events.erase (i);
reposition_for_rt_add (0); reposition_for_rt_add (0);
if (!no_state) {
#ifdef STATE_MANAGER
save_state (_("removed event"));
#endif
}
mark_dirty (); mark_dirty ();
} }
maybe_signal_changed (); maybe_signal_changed ();
@ -1118,66 +1138,179 @@ AutomationList::point_factory (const ControlEvent& other) const
XMLNode& XMLNode&
AutomationList::get_state () AutomationList::get_state ()
{ {
stringstream str; return state (true);
XMLNode* node = new XMLNode (X_("events")); }
iterator xx;
if (events.empty()) { XMLNode&
return *node; AutomationList::state (bool full)
{
XMLNode* root = new XMLNode (X_("AutomationList"));
char buf[64];
LocaleGuard lg (X_("POSIX"));
root->add_property ("id", _id.to_s());
snprintf (buf, sizeof (buf), "%.12g", default_value);
root->add_property ("default", buf);
snprintf (buf, sizeof (buf), "%.12g", min_yval);
root->add_property ("min_yval", buf);
snprintf (buf, sizeof (buf), "%.12g", max_yval);
root->add_property ("max_yval", buf);
snprintf (buf, sizeof (buf), "%.12g", max_xval);
root->add_property ("max_xval", buf);
if (full) {
root->add_property ("state", auto_state_to_string (_state));
} else {
/* never save anything but Off for automation state to a template */
root->add_property ("state", auto_state_to_string (Off));
} }
for (xx = events.begin(); xx != events.end(); ++xx) { root->add_property ("style", auto_style_to_string (_style));
if (!events.empty()) {
root->add_child_nocopy (serialize_events());
}
return *root;
}
XMLNode&
AutomationList::serialize_events ()
{
XMLNode* node = new XMLNode (X_("events"));
stringstream str;
for (iterator xx = events.begin(); xx != events.end(); ++xx) {
str << (double) (*xx)->when; str << (double) (*xx)->when;
str << ' '; str << ' ';
str <<(double) (*xx)->value; str <<(double) (*xx)->value;
str << '\n'; str << '\n';
} }
node->add_content (str.str()); /* XML is a bit wierd */
XMLNode* content_node = new XMLNode (X_("foo")); /* it gets renamed by libxml when we set content */
content_node->set_content (str.str());
node->add_child_nocopy (*content_node);
return *node; return *node;
} }
int int
AutomationList::set_state (const XMLNode& node) AutomationList::deserialize_events (const XMLNode& node)
{ {
if (node.name() != X_("events")) { if (node.children().empty()) {
warning << _("automation list: passed XML node not called \"events\" - ignored.") << endmsg; return -1;
}
XMLNode* content_node = node.children().front();
if (content_node->content().empty()) {
return -1; return -1;
} }
freeze (); freeze ();
clear (); clear ();
if (!node.content().empty()) { stringstream str (content_node->content());
stringstream str (node.content()); double x;
double y;
double x; bool ok = true;
double y;
bool ok = true; while (str) {
str >> x;
while (str) { if (!str) {
str >> x; break;
if (!str) {
ok = false;
break;
}
str >> y;
if (!str) {
ok = false;
break;
}
add (x, y);
} }
str >> y;
if (!ok) { if (!str) {
clear (); ok = false;
error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg; break;
} }
fast_simple_add (x, y);
}
if (!ok) {
clear ();
error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg;
} else {
mark_dirty ();
reposition_for_rt_add (0);
maybe_signal_changed ();
} }
thaw (); thaw ();
return 0; return 0;
} }
int
AutomationList::set_state (const XMLNode& node)
{
XMLNodeList nlist = node.children();
XMLNodeIterator niter;
const XMLProperty* prop;
if (node.name() == X_("events")) {
/* partial state setting*/
return deserialize_events (node);
}
if (node.name() != X_("AutomationList") ) {
error << string_compose (_("AutomationList: passed XML node called %1, not \"AutomationList\" - ignored"), node.name()) << endmsg;
return -1;
}
if ((prop = node.property ("id")) != 0) {
_id = prop->value ();
/* update session AL list */
AutomationListCreated(this);
}
if ((prop = node.property (X_("default"))) != 0){
default_value = atof (prop->value());
} else {
default_value = 0.0;
}
if ((prop = node.property (X_("style"))) != 0) {
_style = string_to_auto_style (prop->value());
} else {
_style = Absolute;
}
if ((prop = node.property (X_("state"))) != 0) {
_state = string_to_auto_state (prop->value());
} else {
_state = Off;
}
if ((prop = node.property (X_("min_yval"))) != 0) {
min_yval = atof (prop->value ());
} else {
min_yval = FLT_MIN;
}
if ((prop = node.property (X_("max_yval"))) != 0) {
max_yval = atof (prop->value ());
} else {
max_yval = FLT_MAX;
}
if ((prop = node.property (X_("max_xval"))) != 0) {
max_xval = atof (prop->value ());
} else {
max_xval = 0; // means "no limit ;
}
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
if ((*niter)->name() == X_("events")) {
deserialize_events (*(*niter));
}
}
return 0;
}

View file

@ -40,14 +40,11 @@ using namespace ARDOUR;
using namespace sigc; using namespace sigc;
using namespace PBD; using namespace PBD;
sigc::signal<void, Curve*> Curve::CurveCreated;
Curve::Curve (double minv, double maxv, double canv, bool nostate) Curve::Curve (double minv, double maxv, double canv, bool nostate)
: AutomationList (canv, nostate) : AutomationList (canv)
{ {
min_yval = minv; min_yval = minv;
max_yval = maxv; max_yval = maxv;
CurveCreated(this);
} }
Curve::Curve (const Curve& other) Curve::Curve (const Curve& other)
@ -55,7 +52,6 @@ Curve::Curve (const Curve& other)
{ {
min_yval = other.min_yval; min_yval = other.min_yval;
max_yval = other.max_yval; max_yval = other.max_yval;
CurveCreated(this);
} }
Curve::Curve (const Curve& other, double start, double end) Curve::Curve (const Curve& other, double start, double end)
@ -63,7 +59,11 @@ Curve::Curve (const Curve& other, double start, double end)
{ {
min_yval = other.min_yval; min_yval = other.min_yval;
max_yval = other.max_yval; max_yval = other.max_yval;
CurveCreated(this); }
Curve::Curve (const XMLNode& node)
: AutomationList (node)
{
} }
Curve::~Curve () Curve::~Curve ()

View file

@ -310,6 +310,23 @@ PluginInsert::connect_and_run (vector<Sample*>& bufs, uint32_t nbufs, nframes_t
/* leave remaining channel buffers alone */ /* leave remaining channel buffers alone */
} }
void
PluginInsert::automation_snapshot (nframes_t now)
{
map<uint32_t,AutomationList*>::iterator li;
for (li = parameter_automation.begin(); li != parameter_automation.end(); ++li) {
AutomationList *alist = ((*li).second);
if (alist != 0 && alist->automation_write ()) {
float val = _plugins[0]->get_parameter ((*li).first);
alist->rt_add (now, val);
last_automation_snapshot = now;
}
}
}
void void
PluginInsert::transport_stopped (nframes_t now) PluginInsert::transport_stopped (nframes_t now)
{ {

View file

@ -58,6 +58,7 @@ using namespace std;
using namespace ARDOUR; using namespace ARDOUR;
using namespace PBD; using namespace PBD;
nframes_t IO::_automation_interval = 0;
const string IO::state_node_name = "IO"; const string IO::state_node_name = "IO";
bool IO::connecting_legal = false; bool IO::connecting_legal = false;
bool IO::ports_legal = false; bool IO::ports_legal = false;
@ -124,6 +125,8 @@ IO::IO (Session& s, string name,
apply_gain_automation = false; apply_gain_automation = false;
_ignore_gain_on_deliver = false; _ignore_gain_on_deliver = false;
last_automation_snapshot = 0;
_gain_automation_state = Off; _gain_automation_state = Off;
_gain_automation_style = Absolute; _gain_automation_style = Absolute;
@ -137,6 +140,38 @@ IO::IO (Session& s, string name,
_session.add_controllable (&_gain_control); _session.add_controllable (&_gain_control);
} }
IO::IO (Session& s, const XMLNode& node, DataType dt)
: _session (s),
_default_type (dt),
_gain_control (X_("gaincontrol"), *this),
_gain_automation_curve (0, 0, 0) // all reset in set_state()
{
_panner = 0;
deferred_state = 0;
no_panner_reset = false;
_desired_gain = 1.0;
_gain = 1.0;
_input_connection = 0;
_output_connection = 0;
_ninputs = 0;
_noutputs = 0;
apply_gain_automation = false;
_ignore_gain_on_deliver = false;
set_state (node);
{
// IO::Meter is emitted from another thread so the
// Meter signal must be protected.
Glib::Mutex::Lock guard (m_meter_signal_lock);
m_meter_connection = Meter.connect (mem_fun (*this, &IO::meter));
}
_session.add_controllable (&_gain_control);
}
IO::~IO () IO::~IO ()
{ {
Glib::Mutex::Lock guard (m_meter_signal_lock); Glib::Mutex::Lock guard (m_meter_signal_lock);
@ -1535,15 +1570,9 @@ IO::state (bool full_state)
snprintf (buf, sizeof (buf), "0x%x", ARDOUR::Off); snprintf (buf, sizeof (buf), "0x%x", ARDOUR::Off);
} }
node->add_property ("automation-state", buf);
snprintf (buf, sizeof (buf), "0x%x", (int) _gain_automation_curve.automation_style());
node->add_property ("automation-style", buf);
return *node; return *node;
} }
int int
IO::set_state (const XMLNode& node) IO::set_state (const XMLNode& node)
{ {
@ -1585,6 +1614,9 @@ IO::set_state (const XMLNode& node)
for (iter = node.children().begin(); iter != node.children().end(); ++iter) { for (iter = node.children().begin(); iter != node.children().end(); ++iter) {
if ((*iter)->name() == "Panner") { if ((*iter)->name() == "Panner") {
if (_panner == 0) {
_panner = new Panner (_name, _session);
}
_panner->set_state (**iter); _panner->set_state (**iter);
} }
@ -1598,20 +1630,6 @@ IO::set_state (const XMLNode& node)
} }
} }
if ((prop = node.property ("automation-state")) != 0) {
long int x;
x = strtol (prop->value().c_str(), 0, 16);
set_gain_automation_state (AutoState (x));
}
if ((prop = node.property ("automation-style")) != 0) {
long int x;
x = strtol (prop->value().c_str(), 0, 16);
set_gain_automation_style (AutoStyle (x));
}
if (ports_legal) { if (ports_legal) {
if (create_ports (node)) { if (create_ports (node)) {
@ -1644,6 +1662,8 @@ IO::set_state (const XMLNode& node)
pending_state_node = new XMLNode (node); pending_state_node = new XMLNode (node);
} }
last_automation_snapshot = 0;
return 0; return 0;
} }
@ -2398,6 +2418,7 @@ IO::set_gain_automation_state (AutoState state)
if (state != _gain_automation_curve.automation_state()) { if (state != _gain_automation_curve.automation_state()) {
changed = true; changed = true;
last_automation_snapshot = 0;
_gain_automation_curve.set_automation_state (state); _gain_automation_curve.set_automation_state (state);
if (state != Off) { if (state != Off) {
@ -2495,6 +2516,21 @@ IO::end_pan_touch (uint32_t which)
} }
void
IO::automation_snapshot (nframes_t now)
{
if (last_automation_snapshot > now || (now - last_automation_snapshot) > _automation_interval) {
if (gain_automation_recording()) {
_gain_automation_curve.rt_add (now, gain());
}
_panner->snapshot (now);
last_automation_snapshot = now;
}
}
void void
IO::transport_stopped (nframes_t frame) IO::transport_stopped (nframes_t frame)
{ {

View file

@ -473,20 +473,12 @@ EqualPowerStereoPanner::state (bool full_state)
root->add_property (X_("x"), buf); root->add_property (X_("x"), buf);
root->add_property (X_("type"), EqualPowerStereoPanner::name); root->add_property (X_("type"), EqualPowerStereoPanner::name);
if (full_state) { XMLNode* autonode = new XMLNode (X_("Automation"));
XMLNode* autonode = new XMLNode (X_("Automation")); autonode->add_child_nocopy (_automation.state (full_state));
autonode->add_child_nocopy (_automation.get_state ()); root->add_child_nocopy (*autonode);
root->add_child_nocopy (*autonode);
} else {
/* never store automation states other than off in a template */
snprintf (buf, sizeof (buf), "0x%x", ARDOUR::Off);
}
root->add_property (X_("automation-state"), buf);
snprintf (buf, sizeof (buf), "0x%x", _automation.automation_style());
root->add_property (X_("automation-style"), buf);
StreamPanner::add_state (*root); StreamPanner::add_state (*root);
root->add_child_nocopy (_control.get_state ()); root->add_child_nocopy (_control.get_state ());
return *root; return *root;
@ -496,7 +488,6 @@ int
EqualPowerStereoPanner::set_state (const XMLNode& node) EqualPowerStereoPanner::set_state (const XMLNode& node)
{ {
const XMLProperty* prop; const XMLProperty* prop;
int x;
float pos; float pos;
LocaleGuard lg (X_("POSIX")); LocaleGuard lg (X_("POSIX"));
@ -508,27 +499,21 @@ EqualPowerStereoPanner::set_state (const XMLNode& node)
StreamPanner::set_state (node); StreamPanner::set_state (node);
for (XMLNodeConstIterator iter = node.children().begin(); iter != node.children().end(); ++iter) { for (XMLNodeConstIterator iter = node.children().begin(); iter != node.children().end(); ++iter) {
if ((*iter)->name() == X_("panner")) { if ((*iter)->name() == X_("panner")) {
_control.set_state (**iter); _control.set_state (**iter);
} else if ((*iter)->name() == X_("Automation")) { } else if ((*iter)->name() == X_("Automation")) {
_automation.set_state (*((*iter)->children().front())); _automation.set_state (*((*iter)->children().front()));
}
}
if ((prop = node.property (X_("automation-state")))) {
sscanf (prop->value().c_str(), "0x%x", &x);
_automation.set_automation_state ((AutoState) x);
if (x != Off) { if (_automation.automation_state() != Off) {
set_position (_automation.eval (parent.session().transport_frame())); set_position (_automation.eval (parent.session().transport_frame()));
}
} }
} }
if ((prop = node.property (X_("automation-style")))) {
sscanf (prop->value().c_str(), "0x%x", &x);
_automation.set_automation_style ((AutoStyle) x);
}
return 0; return 0;
} }

View file

@ -108,23 +108,23 @@ Redirect::set_placement (const string& str, void *src)
} }
} }
/* NODE STRUCTURE
<Automation [optionally with visible="...." ]>
<parameter-N>
<AutomationList id=N>
<events>
X1 Y1
X2 Y2
....
</events>
</parameter-N>
<Automation>
*/
int int
Redirect::set_automation_state (const XMLNode& node) Redirect::set_automation_state (const XMLNode& node)
{ {
/* NODE STRUCTURE
<Automation [optionally with visible="...." ]>
<parameter-N>
<events>
X1 Y1
X2 Y2
....
</events>
</parameter-N>
<Automation>
*/
Glib::Mutex::Lock lm (_automation_lock); Glib::Mutex::Lock lm (_automation_lock);
parameter_automation.clear (); parameter_automation.clear ();
@ -157,20 +157,6 @@ Redirect::set_automation_state (const XMLNode& node)
XMLNode& XMLNode&
Redirect::get_automation_state () Redirect::get_automation_state ()
{ {
/* NODE STRUCTURE
<Automation [optionally with visible="...." ]>
<parameter-N>
<events>
X1 Y1
X2 Y2
....
</events>
</parameter-N>
<Automation>
*/
Glib::Mutex::Lock lm (_automation_lock); Glib::Mutex::Lock lm (_automation_lock);
XMLNode* node = new XMLNode (X_("Automation")); XMLNode* node = new XMLNode (X_("Automation"));
string fullpath; string fullpath;
@ -217,20 +203,6 @@ Redirect::state (bool full_state)
if (full_state) { if (full_state) {
/* NODE STRUCTURE
<Automation [optionally with visible="...." ]>
<parameter-N>
<events>
X1 Y1
X2 Y2
....
</events>
</parameter-N>
<Automation>
*/
XMLNode& automation = get_automation_state(); XMLNode& automation = get_automation_state();
for (set<uint32_t>::iterator x = visible_parameter_automation.begin(); x != visible_parameter_automation.end(); ++x) { for (set<uint32_t>::iterator x = visible_parameter_automation.begin(); x != visible_parameter_automation.end(); ++x) {

View file

@ -61,13 +61,13 @@ Route::Route (Session& sess, string name, int input_min, int input_max, int outp
init (); init ();
} }
Route::Route (Session& sess, const XMLNode& node) Route::Route (Session& sess, const XMLNode& node, DataType default_type)
: IO (sess, "route"), : IO (sess, *node.child ("IO"), default_type),
_solo_control (X_("solo"), *this, ToggleControllable::SoloControl), _solo_control (X_("solo"), *this, ToggleControllable::SoloControl),
_mute_control (X_("mute"), *this, ToggleControllable::MuteControl) _mute_control (X_("mute"), *this, ToggleControllable::MuteControl)
{ {
init (); init ();
set_state (node); _set_state (node, false);
} }
void void
@ -1481,6 +1481,12 @@ Route::add_redirect_from_xml (const XMLNode& node)
int int
Route::set_state (const XMLNode& node) Route::set_state (const XMLNode& node)
{
return _set_state (node, true);
}
int
Route::_set_state (const XMLNode& node, bool call_base)
{ {
XMLNodeList nlist; XMLNodeList nlist;
XMLNodeConstIterator niter; XMLNodeConstIterator niter;
@ -1604,7 +1610,7 @@ Route::set_state (const XMLNode& node)
child = *niter; child = *niter;
if (child->name() == IO::state_node_name) { if (child->name() == IO::state_node_name && call_base) {
IO::set_state (*child); IO::set_state (*child);
break; break;
@ -1925,6 +1931,10 @@ Route::handle_transport_stopped (bool abort_ignored, bool did_locate, bool can_f
{ {
Glib::RWLock::ReaderLock lm (redirect_lock); Glib::RWLock::ReaderLock lm (redirect_lock);
if (!did_locate) {
automation_snapshot (now);
}
for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) {
if (Config->get_plugins_stop_with_transport() && can_flush_redirects) { if (Config->get_plugins_stop_with_transport() && can_flush_redirects) {
@ -2024,6 +2034,15 @@ int
Route::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nframes_t offset, int declick, Route::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nframes_t offset, int declick,
bool can_record, bool rec_monitors_input) bool can_record, bool rec_monitors_input)
{ {
{
Glib::RWLock::ReaderLock lm (redirect_lock, Glib::TRY_LOCK);
if (lm.locked()) {
// automation snapshot can also be called from the non-rt context
// and it uses the redirect list, so we take the lock out here
automation_snapshot (_session.transport_frame());
}
}
if ((n_outputs() == 0 && _redirects.empty()) || n_inputs() == 0 || !_active) { if ((n_outputs() == 0 && _redirects.empty()) || n_inputs() == 0 || !_active) {
silence (nframes, offset); silence (nframes, offset);
return 0; return 0;
@ -2160,6 +2179,16 @@ Route::set_latency_delay (nframes_t longest_session_latency)
} }
} }
void
Route::automation_snapshot (nframes_t now)
{
IO::automation_snapshot (now);
for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) {
(*i)->automation_snapshot (now);
}
}
Route::ToggleControllable::ToggleControllable (std::string name, Route& s, ToggleType tp) Route::ToggleControllable::ToggleControllable (std::string name, Route& s, ToggleType tp)
: Controllable (name), route (s), type(tp) : Controllable (name), route (s), type(tp)
{ {

View file

@ -1321,6 +1321,8 @@ Session::set_frame_rate (nframes_t frames_per_second)
sync_time_vars(); sync_time_vars();
Route::set_automation_interval ((jack_nframes_t) ceil ((double) frames_per_second * 0.25));
// XXX we need some equivalent to this, somehow // XXX we need some equivalent to this, somehow
// DestructiveFileSource::setup_standard_crossfades (frames_per_second); // DestructiveFileSource::setup_standard_crossfades (frames_per_second);
@ -3760,14 +3762,8 @@ Session::nbusses () const
return n; return n;
} }
void
Session::add_curve(Curve *curve)
{
curves[curve->id()] = curve;
}
void void
Session::add_automation_list(AutomationList *al) Session::add_automation_list(AutomationList *al)
{ {
automation_lists[al->id()] = al; automation_lists[al->id()] = al;
} }

View file

@ -3,6 +3,8 @@
#include <pbd/memento_command.h> #include <pbd/memento_command.h>
#include <ardour/diskstream.h> #include <ardour/diskstream.h>
#include <ardour/playlist.h> #include <ardour/playlist.h>
#include <ardour/audioplaylist.h>
#include <ardour/audio_track.h>
#include <ardour/tempo.h> #include <ardour/tempo.h>
#include <ardour/audiosource.h> #include <ardour/audiosource.h>
#include <ardour/audioregion.h> #include <ardour/audioregion.h>
@ -53,30 +55,26 @@ Command *Session::memento_command_factory(XMLNode *n)
return 0; return 0;
} }
/* create command */ /* create command */
string obj_T = n->children().front()->name(); string obj_T = n->property ("type_name")->value();
if (obj_T == "AudioRegion" || obj_T == "Region") { if (obj_T == typeid (AudioRegion).name() || obj_T == typeid (Region).name()) {
if (audio_regions.count(id)) if (audio_regions.count(id))
return new MementoCommand<AudioRegion>(*audio_regions[id], before, after); return new MementoCommand<AudioRegion>(*audio_regions[id], before, after);
} else if (obj_T == "AudioSource") { } else if (obj_T == typeid (AudioSource).name()) {
if (audio_sources.count(id)) if (audio_sources.count(id))
return new MementoCommand<AudioSource>(*audio_sources[id], before, after); return new MementoCommand<AudioSource>(*audio_sources[id], before, after);
} else if (obj_T == "Location") { } else if (obj_T == typeid (Location).name()) {
return new MementoCommand<Location>(*_locations.get_location_by_id(id), before, after); return new MementoCommand<Location>(*_locations.get_location_by_id(id), before, after);
} else if (obj_T == "Locations") { } else if (obj_T == typeid (Locations).name()) {
return new MementoCommand<Locations>(_locations, before, after); return new MementoCommand<Locations>(_locations, before, after);
} else if (obj_T == "TempoMap") { } else if (obj_T == typeid (TempoMap).name()) {
return new MementoCommand<TempoMap>(*_tempo_map, before, after); return new MementoCommand<TempoMap>(*_tempo_map, before, after);
} else if (obj_T == "Playlist" || obj_T == "AudioPlaylist") { } else if (obj_T == typeid (Playlist).name() || obj_T == typeid (AudioPlaylist).name()) {
if (Playlist *pl = playlist_by_name(child->property("name")->value())) if (Playlist *pl = playlist_by_name(child->property("name")->value()))
return new MementoCommand<Playlist>(*pl, before, after); return new MementoCommand<Playlist>(*pl, before, after);
} else if (obj_T == "Route") { // includes AudioTrack } else if (obj_T == typeid (Route).name() || obj_T == typeid (AudioTrack).name()) {
return new MementoCommand<Route>(*route_by_id(id), before, after); return new MementoCommand<Route>(*route_by_id(id), before, after);
} else if (obj_T == "Curve") { } else if (obj_T == typeid (Curve).name() || obj_T == typeid (AutomationList).name()) {
if (curves.count(id))
return new MementoCommand<Curve>(*curves[id], before, after);
} else if (obj_T == "AutomationList") {
if (automation_lists.count(id)) if (automation_lists.count(id))
return new MementoCommand<AutomationList>(*automation_lists[id], before, after); return new MementoCommand<AutomationList>(*automation_lists[id], before, after);
} else if (registry.count(id)) { // For Editor and AutomationLine which are off-limits here } else if (registry.count(id)) { // For Editor and AutomationLine which are off-limits here
@ -84,8 +82,8 @@ Command *Session::memento_command_factory(XMLNode *n)
} }
/* we failed */ /* we failed */
error << _("could not reconstitute MementoCommand from XMLNode. id=") << id.to_s() << endmsg; error << string_compose (_("could not reconstitute MementoCommand from XMLNode. object type = %1 id = %2"), obj_T, id.to_s()) << endmsg;
return 0; return 0 ;
} }
// solo // solo

View file

@ -240,7 +240,6 @@ Session::first_stage_init (string fullpath, string snapshot_name)
Playlist::PlaylistCreated.connect (mem_fun (*this, &Session::add_playlist)); Playlist::PlaylistCreated.connect (mem_fun (*this, &Session::add_playlist));
Redirect::RedirectCreated.connect (mem_fun (*this, &Session::add_redirect)); Redirect::RedirectCreated.connect (mem_fun (*this, &Session::add_redirect));
NamedSelection::NamedSelectionCreated.connect (mem_fun (*this, &Session::add_named_selection)); NamedSelection::NamedSelectionCreated.connect (mem_fun (*this, &Session::add_named_selection));
Curve::CurveCreated.connect (mem_fun (*this, &Session::add_curve));
AutomationList::AutomationListCreated.connect (mem_fun (*this, &Session::add_automation_list)); AutomationList::AutomationListCreated.connect (mem_fun (*this, &Session::add_automation_list));
Controllable::Destroyed.connect (mem_fun (*this, &Session::remove_controllable)); Controllable::Destroyed.connect (mem_fun (*this, &Session::remove_controllable));

View file

@ -50,8 +50,8 @@ Track::Track (Session& sess, string name, Route::Flag flag, TrackMode mode, Data
} }
Track::Track (Session& sess, const XMLNode& node, DataType default_type) Track::Track (Session& sess, const XMLNode& node, DataType default_type)
: Route (sess, "to be renamed", 0, 0, -1, -1, Route::Flag(0), default_type) : Route (sess, node),
, _rec_enable_control (*this) _rec_enable_control (*this)
{ {
_freeze_record.state = NoFreeze; _freeze_record.state = NoFreeze;
_declickable = true; _declickable = true;

View file

@ -398,3 +398,68 @@ meter_hold_to_float (MeterHold hold)
return 200.0f; return 200.0f;
} }
} }
AutoState
ARDOUR::string_to_auto_state (std::string str)
{
if (str == X_("Off")) {
return Off;
} else if (str == X_("Play")) {
return Play;
} else if (str == X_("Write")) {
return Write;
} else if (str == X_("Touch")) {
return Touch;
}
fatal << string_compose (_("programming error: %1 %2"), "illegal AutoState string: ", str) << endmsg;
/*NOTREACHED*/
}
string
ARDOUR::auto_state_to_string (AutoState as)
{
/* to be used only for XML serialization, no i18n done */
switch (as) {
case Off:
return X_("Off");
break;
case Play:
return X_("Play");
break;
case Write:
return X_("Write");
break;
case Touch:
return X_("Touch");
}
}
AutoStyle
ARDOUR::string_to_auto_style (std::string str)
{
if (str == X_("Absolute")) {
return Absolute;
} else if (str == X_("Trim")) {
return Trim;
}
fatal << string_compose (_("programming error: %1 %2"), "illegal AutoStyle string: ", str) << endmsg;
/*NOTREACHED*/
}
string
ARDOUR::auto_style_to_string (AutoStyle as)
{
/* to be used only for XML serialization, no i18n done */
switch (as) {
case Absolute:
return X_("Absolute");
break;
case Trim:
return X_("Trim");
break;
}
}

View file

@ -87,9 +87,10 @@ public:
const string & set_content (const string &); const string & set_content (const string &);
XMLNode *add_content(const string & = string()); XMLNode *add_content(const string & = string());
const XMLNodeList & children (const string & = string()) const; const XMLNodeList & children (const string& str = string()) const;
XMLNode *add_child (const char *); XMLNode *add_child (const char *);
XMLNode *add_child_copy (const XMLNode&); XMLNode *add_child_copy (const XMLNode&);
XMLNode *child (const char*) const;
void add_child_nocopy (XMLNode&); void add_child_nocopy (XMLNode&);
const XMLPropertyList & properties() const { return _proplist; }; const XMLPropertyList & properties() const { return _proplist; };

View file

@ -216,13 +216,38 @@ XMLNode::set_content(const string & c)
return _content; return _content;
} }
const XMLNodeList & XMLNode*
XMLNode::children(const string & n) const XMLNode::child (const char *name) const
{ {
/* returns first child matching name */
static XMLNodeList retval; static XMLNodeList retval;
XMLNodeConstIterator cur; XMLNodeConstIterator cur;
if (n.length() == 0) { if (name == 0) {
return 0;
}
retval.erase(retval.begin(), retval.end());
for (cur = _children.begin(); cur != _children.end(); ++cur) {
if ((*cur)->name() == name) {
return *cur;
}
}
return 0;
}
const XMLNodeList &
XMLNode::children(const string& n) const
{
/* returns all children matching name */
static XMLNodeList retval;
XMLNodeConstIterator cur;
if (n.empty()) {
return _children; return _children;
} }