Fix compilation with --no-lv2 (#0006169).
[ardour.git] / libs / ardour / session_events.cc
1 /*
2     Copyright (C) 1999-2004 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 <cmath>
21 #include <unistd.h>
22
23 #include "pbd/error.h"
24 #include "pbd/enumwriter.h"
25 #include "pbd/stacktrace.h"
26 #include "pbd/pthread_utils.h"
27
28 #include "ardour/debug.h"
29 #include "ardour/session_event.h"
30
31 #include "i18n.h"
32
33 using namespace std;
34 using namespace ARDOUR;
35 using namespace PBD;
36
37 PerThreadPool* SessionEvent::pool;
38
39 void
40 SessionEvent::init_event_pool ()
41 {
42         pool = new PerThreadPool;
43 }
44
45 void
46 SessionEvent::create_per_thread_pool (const std::string& name, uint32_t nitems)
47 {
48         /* this is a per-thread call that simply creates a thread-private ptr to
49            a CrossThreadPool for use by this thread whenever events are allocated/released
50            from SessionEvent::pool()
51         */
52         pool->create_per_thread_pool (name, sizeof (SessionEvent), nitems);
53 }
54
55 SessionEvent::SessionEvent (Type t, Action a, framepos_t when, framepos_t where, double spd, bool yn, bool yn2, bool yn3)
56         : type (t)
57         , action (a)
58         , action_frame (when)
59         , target_frame (where)
60         , speed (spd)
61         , yes_or_no (yn)
62         , second_yes_or_no (yn2)
63         , third_yes_or_no (yn3)
64         , event_loop (0)
65 {
66         DEBUG_TRACE (DEBUG::SessionEvents, string_compose ("NEW SESSION EVENT, type = %1 action = %2\n", enum_2_string (type), enum_2_string (action)));
67 }
68
69 void *
70 SessionEvent::operator new (size_t)
71 {
72         CrossThreadPool* p = pool->per_thread_pool ();
73         SessionEvent* ev = static_cast<SessionEvent*> (p->alloc ());
74         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,
75                                                            p->total(), p->available(), p->used()));
76                                                            
77 #ifndef NDEBUG
78         if (DEBUG::SessionEvents & PBD::debug_bits) {
79                 // stacktrace (cerr, 40);
80         }
81 #endif
82         ev->own_pool = p;
83         return ev;
84 }
85
86 void
87 SessionEvent::operator delete (void *ptr, size_t /*size*/)
88 {
89         Pool* p = pool->per_thread_pool ();
90         SessionEvent* ev = static_cast<SessionEvent*> (ptr);
91
92         DEBUG_TRACE (DEBUG::SessionEvents, string_compose (
93                              "%1 Deleting SessionEvent @ %2 type %3 action %4 ev thread pool = %5 ev pool = %6 size %7 free %8 used %9\n",
94                              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()
95                              ));
96
97 #ifndef NDEBUG
98         if (DEBUG::SessionEvents & PBD::debug_bits) {
99                 // stacktrace (cerr, 40);
100         }
101 #endif
102
103         if (p == ev->own_pool) {
104                 p->release (ptr);
105         } else {
106                 ev->own_pool->push (ev);
107                 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",
108                                                                    pthread_name(), ev->own_pool->name(),
109                                                                    ev->own_pool->total(), ev->own_pool->available(), ev->own_pool->used(),
110                                                                    ev->own_pool->pending_size()));
111         }
112 }
113
114 void
115 SessionEventManager::add_event (framepos_t frame, SessionEvent::Type type, framepos_t target_frame)
116 {
117         SessionEvent* ev = new SessionEvent (type, SessionEvent::Add, frame, target_frame, 0);
118         queue_event (ev);
119 }
120
121 void
122 SessionEventManager::remove_event (framepos_t frame, SessionEvent::Type type)
123 {
124         SessionEvent* ev = new SessionEvent (type, SessionEvent::Remove, frame, 0, 0);
125         queue_event (ev);
126 }
127
128 void
129 SessionEventManager::replace_event (SessionEvent::Type type, framepos_t frame, framepos_t target)
130 {
131         SessionEvent* ev = new SessionEvent (type, SessionEvent::Replace, frame, target, 0);
132         queue_event (ev);
133 }
134
135 void
136 SessionEventManager::clear_events (SessionEvent::Type type)
137 {
138         SessionEvent* ev = new SessionEvent (type, SessionEvent::Clear, SessionEvent::Immediate, 0, 0);
139         queue_event (ev);
140 }
141
142 void
143 SessionEventManager::clear_events (SessionEvent::Type type, boost::function<void (void)> after)
144 {
145         SessionEvent* ev = new SessionEvent (type, SessionEvent::Clear, SessionEvent::Immediate, 0, 0);
146         ev->rt_slot = after;
147
148         /* in the calling thread, after the clear is complete, arrange to flush things from the event
149            pool pending list (i.e. to make sure they are really back in the free list and available
150            for future events).
151         */
152
153         ev->event_loop = PBD::EventLoop::get_event_loop_for_thread ();
154         if (ev->event_loop) {
155                 ev->rt_return = boost::bind (&CrossThreadPool::flush_pending_with_ev, ev->event_pool(), _1);
156         }
157
158         queue_event (ev);
159 }
160
161 void
162 SessionEventManager::dump_events () const
163 {
164         cerr << "EVENT DUMP" << endl;
165         for (Events::const_iterator i = events.begin(); i != events.end(); ++i) {
166
167                 cerr << "\tat " << (*i)->action_frame << ' ' << enum_2_string ((*i)->type) << " target = " << (*i)->target_frame << endl;
168         }
169         cerr << "Next event: ";
170
171         if ((Events::const_iterator) next_event == events.end()) {
172                 cerr << "none" << endl;
173         } else {
174                 cerr << "at " << (*next_event)->action_frame << ' '
175                      << enum_2_string ((*next_event)->type) << " target = "
176                      << (*next_event)->target_frame << endl;
177         }
178         cerr << "Immediate events pending:\n";
179         for (Events::const_iterator i = immediate_events.begin(); i != immediate_events.end(); ++i) {
180                 cerr << "\tat " << (*i)->action_frame << ' ' << enum_2_string((*i)->type) << " target = " << (*i)->target_frame << endl;
181         }
182         cerr << "END EVENT_DUMP" << endl;
183 }
184
185 void
186 SessionEventManager::merge_event (SessionEvent* ev)
187 {
188         switch (ev->action) {
189         case SessionEvent::Remove:
190                 _remove_event (ev);
191                 delete ev;
192                 return;
193
194         case SessionEvent::Replace:
195                 _replace_event (ev);
196                 return;
197
198         case SessionEvent::Clear:
199                 _clear_event_type (ev->type);
200                 /* run any additional realtime callback, if any */
201                 if (ev->rt_slot) {
202                         ev->rt_slot ();
203                 }
204                 if (ev->event_loop) {
205                         /* run non-realtime callback (in some other thread) */
206                         ev->event_loop->call_slot (MISSING_INVALIDATOR, boost::bind (ev->rt_return, ev));
207                 } else {
208                         delete ev;
209                 }
210                 return;
211
212         case SessionEvent::Add:
213                 break;
214         }
215
216         /* try to handle immediate events right here */
217
218         if (ev->action_frame == SessionEvent::Immediate) {
219                 process_event (ev);
220                 return;
221         }
222
223         switch (ev->type) {
224         case SessionEvent::AutoLoop:
225         case SessionEvent::AutoLoopDeclick:
226         case SessionEvent::StopOnce:
227                 _clear_event_type (ev->type);
228                 break;
229
230         default:
231                 for (Events::iterator i = events.begin(); i != events.end(); ++i) {
232                         if ((*i)->type == ev->type && (*i)->action_frame == ev->action_frame) {
233                           error << string_compose(_("Session: cannot have two events of type %1 at the same frame (%2)."),
234                                                   enum_2_string (ev->type), ev->action_frame) << 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_frame = ev->action_frame;
258                         (*i)->target_frame = ev->target_frame;
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_frame == ev->action_frame) {
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 }
343