ardour
automation_watch.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2012 Paul Davis
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or
7  (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, write to the Free Software
16  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 
18 */
19 
20 #include <iostream>
21 
22 #include <glibmm/timer.h>
23 
24 #include "pbd/compose.h"
25 
28 #include "ardour/debug.h"
29 #include "ardour/session.h"
30 
31 using namespace ARDOUR;
32 using namespace PBD;
33 
35 
38 {
39  if (_instance == 0) {
40  _instance = new AutomationWatch;
41  }
42  return *_instance;
43 }
44 
46  : _thread (0)
47  , _last_time (0)
48  , _run_thread (false)
49 {
50 
51 }
52 
54 {
55  if (_thread) {
56  _run_thread = false;
57  _thread->join ();
58  _thread = 0;
59  }
60 
62  automation_watches.clear ();
63 }
64 
65 void
67 {
69  DEBUG_TRACE (DEBUG::Automation, string_compose ("now watching control %1 for automation, astate = %2\n", ac->name(), enum_2_string (ac->automation_state())));
70  automation_watches.insert (ac);
71 
72  /* if an automation control is added here while the transport is
73  * rolling, make sure that it knows that there is a write pass going
74  * on, rather than waiting for the transport to start.
75  */
76 
78  DEBUG_TRACE (DEBUG::Automation, string_compose ("\ttransport is rolling @ %1, audible = %2so enter write pass\n",
80  /* add a guard point since we are already moving */
81  ac->list()->set_in_write_pass (true, true, _session->audible_frame());
82  }
83 
84  /* we can't store shared_ptr<Destructible> in connections because it
85  * creates reference cycles. we don't need to make the weak_ptr<>
86  * explicit here, but it helps to remind us what is going on.
87  */
88 
90  ac->DropReferences.connect_same_thread (*this, boost::bind (&AutomationWatch::remove_weak_automation_watch, this, wac));
91 }
92 
93 void
95 {
97 
98  if (!ac) {
99  return;
100  }
101 
103 }
104 
105 void
107 {
109  DEBUG_TRACE (DEBUG::Automation, string_compose ("remove control %1 from automation watch\n", ac->name()));
110  automation_watches.erase (ac);
111  ac->list()->set_in_write_pass (false);
112 }
113 
114 gint
116 {
117  if (!_session || !_session->transport_rolling()) {
118  return TRUE;
119  }
120 
121  {
123 
124  framepos_t time = _session->audible_frame ();
125  if (time > _last_time) { //we only write automation in the forward direction; this fixes automation-recording in a loop
126  for (AutomationWatches::iterator aw = automation_watches.begin(); aw != automation_watches.end(); ++aw) {
127  if ((*aw)->alist()->automation_write()) {
128  (*aw)->list()->add (time, (*aw)->user_double(), true);
129  }
130  }
131  } else { //transport stopped or reversed. stop the automation pass and start a new one (for bonus points, someday store the previous pass in an undo record)
132  for (AutomationWatches::iterator aw = automation_watches.begin(); aw != automation_watches.end(); ++aw) {
133  DEBUG_TRACE (DEBUG::Automation, string_compose ("%1: transport in rewind, speed %2, in write pass ? %3 writing ? %4\n",
134  (*aw)->name(), _session->transport_speed(), _session->transport_rolling(),
135  (*aw)->alist()->automation_write()));
136  (*aw)->list()->set_in_write_pass (false);
137  if ( (*aw)->alist()->automation_write() ) {
138  (*aw)->list()->set_in_write_pass (true);
139  }
140  }
141  }
142 
143  _last_time = time;
144  }
145 
146  return TRUE;
147 }
148 
149 void
151 {
152  while (_run_thread) {
153  Glib::usleep ((gulong) floor (Config->get_automation_interval_msecs() * 1000));
154  timer ();
155  }
156 }
157 
158 void
160 {
162 
163  if (_thread) {
164  _run_thread = false;
165  _thread->join ();
166  _thread = 0;
167  }
168 
170 
171  if (_session) {
172  _run_thread = true;
173  _thread = Glib::Threads::Thread::create (boost::bind (&AutomationWatch::thread, this));
174 
176  }
177 }
178 
179 void
181 {
182  if (!_session) {
183  return;
184  }
185 
186  bool rolling = _session->transport_rolling();
187 
189 
190  {
192 
193  for (AutomationWatches::iterator aw = automation_watches.begin(); aw != automation_watches.end(); ++aw) {
194  DEBUG_TRACE (DEBUG::Automation, string_compose ("%1: transport state changed, speed %2, in write pass ? %3 writing ? %4\n",
195  (*aw)->name(), _session->transport_speed(), rolling,
196  (*aw)->alist()->automation_write()));
197  if (rolling && (*aw)->alist()->automation_write()) {
198  (*aw)->list()->set_in_write_pass (true);
199  } else {
200  (*aw)->list()->set_in_write_pass (false);
201  }
202  }
203  }
204 }
framepos_t audible_frame() const
Definition: session.cc:1760
bool transport_rolling() const
Definition: session.h:592
AutoState automation_state() const
PBD::Signal0< void > TransportStateChange
Definition: session.h:308
PBD::Signal0< void > DropReferences
Definition: destructible.h:34
double transport_speed() const
Definition: session.h:590
#define enum_2_string(e)
Definition: enumwriter.h:97
static AutomationWatch & instance()
void add_automation_watch(boost::shared_ptr< ARDOUR::AutomationControl >)
Glib::Threads::Mutex automation_watch_lock
static AutomationWatch * _instance
LIBARDOUR_API uint64_t Automation
Definition: debug.cc:62
LIBARDOUR_API RCConfiguration * Config
Definition: globals.cc:119
void set_session(ARDOUR::Session *)
Definition: amp.h:29
#define DEBUG_TRACE(bits, str)
Definition: debug.h:55
void remove_weak_automation_watch(boost::weak_ptr< ARDOUR::AutomationControl >)
AutomationWatches automation_watches
int64_t framepos_t
Definition: types.h:66
bool automation_write() const
void remove_automation_watch(boost::shared_ptr< ARDOUR::AutomationControl >)
Glib::Threads::Thread * _thread
boost::shared_ptr< ControlList > list()
Definition: Control.hpp:66
Definition: debug.h:30
boost::shared_ptr< AutomationList > alist() const
virtual void set_session(ARDOUR::Session *)
PBD::ScopedConnection transport_connection
std::string name() const
Definition: controllable.h:99
ARDOUR::Session * _session
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208