make BusProfile argument to new Session constructor be const (and in associated call...
[ardour.git] / libs / ardour / session_events.cc
1 /*
2  * Copyright (C) 1999-2017 Paul Davis <paul@linuxaudiosystems.com>
3  * Copyright (C) 2006-2012 David Robillard <d@drobilla.net>
4  * Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
5  * Copyright (C) 2015-2018 Robin Gareus <robin@gareus.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include <cmath>
23 #include <unistd.h>
24
25 #include "pbd/error.h"
26 #include "pbd/enumwriter.h"
27 #include "pbd/stacktrace.h"
28 #include "pbd/pthread_utils.h"
29
30 #include "ardour/debug.h"
31 #include "ardour/session_event.h"
32
33 #include "pbd/i18n.h"
34
35 using namespace std;
36 using namespace ARDOUR;
37 using namespace PBD;
38
39 PerThreadPool* SessionEvent::pool;
40
41 void
42 SessionEvent::init_event_pool ()
43 {
44         pool = new PerThreadPool;
45 }
46
47 bool
48 SessionEvent::has_per_thread_pool ()
49 {
50         return pool->has_per_thread_pool ();
51 }
52
53 void
54 SessionEvent::create_per_thread_pool (const std::string& name, uint32_t nitems)
55 {
56         /* this is a per-thread call that simply creates a thread-private ptr to
57            a CrossThreadPool for use by this thread whenever events are allocated/released
58            from SessionEvent::pool()
59         */
60         pool->create_per_thread_pool (name, sizeof (SessionEvent), nitems);
61 }
62
63 SessionEvent::SessionEvent (Type t, Action a, samplepos_t when, samplepos_t where, double spd, bool yn, bool yn2, bool yn3)
64         : type (t)
65         , action (a)
66         , action_sample (when)
67         , target_sample (where)
68         , speed (spd)
69         , yes_or_no (yn)
70         , second_yes_or_no (yn2)
71         , third_yes_or_no (yn3)
72         , event_loop (0)
73 {
74         DEBUG_TRACE (DEBUG::SessionEvents, string_compose ("NEW SESSION EVENT, type = %1 action = %2\n", enum_2_string (type), enum_2_string (action)));
75 }
76
77 void *
78 SessionEvent::operator new (size_t)
79 {
80         CrossThreadPool* p = pool->per_thread_pool ();
81         SessionEvent* ev = static_cast<SessionEvent*> (p->alloc ());
82         DEBUG_TRACE (DEBUG::SessionEvents, string_compose ("%1 Allocating SessionEvent from %2 ev @ %3 pool size %4 free %5 used %6\n", pthread_name(), p->name(), ev,
83                                                            p->total(), p->available(), p->used()));
84
85         ev->own_pool = p;
86         return ev;
87 }
88
89 void
90 SessionEvent::operator delete (void *ptr, size_t /*size*/)
91 {
92         Pool* p = pool->per_thread_pool (false);
93         SessionEvent* ev = static_cast<SessionEvent*> (ptr);
94
95         DEBUG_TRACE (DEBUG::SessionEvents, string_compose (
96                              "%1 Deleting SessionEvent @ %2 type %3 action %4 ev thread pool = %5 ev pool = %6 size %7 free %8 used %9\n",
97                              pthread_name(), ev, enum_2_string (ev->type), enum_2_string (ev->action), p->name(), ev->own_pool->name(), ev->own_pool->total(), ev->own_pool->available(), ev->own_pool->used()
98                              ));
99
100         if (p && p == ev->own_pool) {
101                 p->release (ptr);
102         } else {
103                 assert(ev->own_pool);
104                 ev->own_pool->push (ev);
105                 DEBUG_TRACE (DEBUG::SessionEvents, string_compose ("%1 was wrong thread for this pool, pushed event onto pending list, will be deleted on next alloc from %2 pool size %3 free %4 used %5 pending %6\n",
106                                                                    pthread_name(), ev->own_pool->name(),
107                                                                    ev->own_pool->total(), ev->own_pool->available(), ev->own_pool->used(),
108                                                                    ev->own_pool->pending_size()));
109         }
110 }
111
112 void
113 SessionEventManager::add_event (samplepos_t sample, SessionEvent::Type type, samplepos_t target_sample)
114 {
115         SessionEvent* ev = new SessionEvent (type, SessionEvent::Add, sample, target_sample, 0);
116         queue_event (ev);
117 }
118
119 void
120 SessionEventManager::remove_event (samplepos_t sample, SessionEvent::Type type)
121 {
122         SessionEvent* ev = new SessionEvent (type, SessionEvent::Remove, sample, 0, 0);
123         queue_event (ev);
124 }
125
126 void
127 SessionEventManager::replace_event (SessionEvent::Type type, samplepos_t sample, samplepos_t target)
128 {
129         SessionEvent* ev = new SessionEvent (type, SessionEvent::Replace, sample, target, 0);
130         queue_event (ev);
131 }
132
133 void
134 SessionEventManager::clear_events (SessionEvent::Type type)
135 {
136         SessionEvent* ev = new SessionEvent (type, SessionEvent::Clear, SessionEvent::Immediate, 0, 0);
137         queue_event (ev);
138 }
139
140 void
141 SessionEventManager::clear_events (SessionEvent::Type type, boost::function<void (void)> after)
142 {
143         SessionEvent* ev = new SessionEvent (type, SessionEvent::Clear, SessionEvent::Immediate, 0, 0);
144         ev->rt_slot = after;
145
146         /* in the calling thread, after the clear is complete, arrange to flush things from the event
147            pool pending list (i.e. to make sure they are really back in the free list and available
148            for future events).
149         */
150
151         ev->event_loop = PBD::EventLoop::get_event_loop_for_thread ();
152         if (ev->event_loop) {
153                 ev->rt_return = boost::bind (&CrossThreadPool::flush_pending_with_ev, ev->event_pool(), _1);
154         }
155
156         queue_event (ev);
157 }
158
159 void
160 SessionEventManager::dump_events () const
161 {
162         cerr << "EVENT DUMP" << endl;
163         for (Events::const_iterator i = events.begin(); i != events.end(); ++i) {
164
165                 cerr << "\tat " << (*i)->action_sample << ' ' << enum_2_string ((*i)->type) << " target = " << (*i)->target_sample << endl;
166         }
167         cerr << "Next event: ";
168
169         if ((Events::const_iterator) next_event == events.end()) {
170                 cerr << "none" << endl;
171         } else {
172                 cerr << "at " << (*next_event)->action_sample << ' '
173                      << enum_2_string ((*next_event)->type) << " target = "
174                      << (*next_event)->target_sample << endl;
175         }
176         cerr << "Immediate events pending:\n";
177         for (Events::const_iterator i = immediate_events.begin(); i != immediate_events.end(); ++i) {
178                 cerr << "\tat " << (*i)->action_sample << ' ' << enum_2_string((*i)->type) << " target = " << (*i)->target_sample << endl;
179         }
180         cerr << "END EVENT_DUMP" << endl;
181 }
182
183 void
184 SessionEventManager::merge_event (SessionEvent* ev)
185 {
186         switch (ev->action) {
187         case SessionEvent::Remove:
188                 _remove_event (ev);
189                 delete ev;
190                 return;
191
192         case SessionEvent::Replace:
193                 _replace_event (ev);
194                 return;
195
196         case SessionEvent::Clear:
197                 _clear_event_type (ev->type);
198                 /* run any additional realtime callback, if any */
199                 if (ev->rt_slot) {
200                         ev->rt_slot ();
201                 }
202                 if (ev->event_loop) {
203                         /* run non-realtime callback (in some other thread) */
204                         ev->event_loop->call_slot (MISSING_INVALIDATOR, boost::bind (ev->rt_return, ev));
205                 } else {
206                         delete ev;
207                 }
208                 return;
209
210         case SessionEvent::Add:
211                 break;
212         }
213
214         /* try to handle immediate events right here */
215
216         if (ev->type == SessionEvent::Locate || ev->type == SessionEvent::LocateRoll) {
217                 /* remove any existing Locates that are waiting to execute */
218                 _clear_event_type (ev->type);
219         }
220
221         if (ev->action_sample == SessionEvent::Immediate) {
222                 process_event (ev);
223                 return;
224         }
225
226         switch (ev->type) {
227         case SessionEvent::AutoLoop:
228                 _clear_event_type (ev->type);
229                 break;
230         default:
231                 for (Events::iterator i = events.begin(); i != events.end(); ++i) {
232                         if ((*i)->type == ev->type && (*i)->action_sample == ev->action_sample) {
233                           error << string_compose(_("Session: cannot have two events of type %1 at the same sample (%2)."),
234                                                   enum_2_string (ev->type), ev->action_sample) << endmsg;
235                                 return;
236                         }
237                 }
238         }
239
240         events.insert (events.begin(), ev);
241         events.sort (SessionEvent::compare);
242         next_event = events.begin();
243         set_next_event ();
244 }
245
246 /** @return true when @a ev is deleted. */
247 bool
248 SessionEventManager::_replace_event (SessionEvent* ev)
249 {
250         bool ret = false;
251         Events::iterator i;
252
253         /* private, used only for events that can only exist once in the queue */
254
255         for (i = events.begin(); i != events.end(); ++i) {
256                 if ((*i)->type == ev->type) {
257                         (*i)->action_sample = ev->action_sample;
258                         (*i)->target_sample = ev->target_sample;
259                         if ((*i) == ev) {
260                                 ret = true;
261                         }
262                         delete ev;
263                         break;
264                 }
265         }
266
267         if (i == events.end()) {
268                 events.insert (events.begin(), ev);
269         }
270
271         events.sort (SessionEvent::compare);
272         next_event = events.end();
273         set_next_event ();
274
275         return ret;
276 }
277
278 /** @return true when @a ev is deleted. */
279 bool
280 SessionEventManager::_remove_event (SessionEvent* ev)
281 {
282         bool ret = false;
283         Events::iterator i;
284
285         for (i = events.begin(); i != events.end(); ++i) {
286                 if ((*i)->type == ev->type && (*i)->action_sample == ev->action_sample) {
287                         if ((*i) == ev) {
288                                 ret = true;
289                         }
290
291                         delete *i;
292                         if (i == next_event) {
293                                 ++next_event;
294                         }
295                         i = events.erase (i);
296                         break;
297                 }
298         }
299
300         if (i != events.end()) {
301                 set_next_event ();
302         }
303
304         return ret;
305 }
306
307 void
308 SessionEventManager::_clear_event_type (SessionEvent::Type type)
309 {
310         Events::iterator i, tmp;
311
312         for (i = events.begin(); i != events.end(); ) {
313
314                 tmp = i;
315                 ++tmp;
316
317                 if ((*i)->type == type) {
318                         delete *i;
319                         if (i == next_event) {
320                                 ++next_event;
321                         }
322                         events.erase (i);
323                 }
324
325                 i = tmp;
326         }
327
328         for (i = immediate_events.begin(); i != immediate_events.end(); ) {
329
330                 tmp = i;
331                 ++tmp;
332
333                 if ((*i)->type == type) {
334                         delete *i;
335                         immediate_events.erase (i);
336                 }
337
338                 i = tmp;
339         }
340
341         set_next_event ();
342 }