expand SessionEvent API to allow ::clear_events() to work correctly.
[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 */
201                 ev->rt_slot ();
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->action_frame == SessionEvent::Immediate) {
217                 process_event (ev);
218                 return;
219         }
220
221         switch (ev->type) {
222         case SessionEvent::AutoLoop:
223         case SessionEvent::AutoLoopDeclick:
224         case SessionEvent::StopOnce:
225                 _clear_event_type (ev->type);
226                 break;
227
228         default:
229                 for (Events::iterator i = events.begin(); i != events.end(); ++i) {
230                         if ((*i)->type == ev->type && (*i)->action_frame == ev->action_frame) {
231                           error << string_compose(_("Session: cannot have two events of type %1 at the same frame (%2)."),
232                                                   enum_2_string (ev->type), ev->action_frame) << endmsg;
233                                 return;
234                         }
235                 }
236         }
237
238         events.insert (events.begin(), ev);
239         events.sort (SessionEvent::compare);
240         next_event = events.begin();
241         set_next_event ();
242 }
243
244 /** @return true when @a ev is deleted. */
245 bool
246 SessionEventManager::_replace_event (SessionEvent* ev)
247 {
248         bool ret = false;
249         Events::iterator i;
250
251         /* private, used only for events that can only exist once in the queue */
252
253         for (i = events.begin(); i != events.end(); ++i) {
254                 if ((*i)->type == ev->type) {
255                         (*i)->action_frame = ev->action_frame;
256                         (*i)->target_frame = ev->target_frame;
257                         if ((*i) == ev) {
258                                 ret = true;
259                         }
260                         delete ev;
261                         break;
262                 }
263         }
264
265         if (i == events.end()) {
266                 events.insert (events.begin(), ev);
267         }
268
269         events.sort (SessionEvent::compare);
270         next_event = events.end();
271         set_next_event ();
272
273         return ret;
274 }
275
276 /** @return true when @a ev is deleted. */
277 bool
278 SessionEventManager::_remove_event (SessionEvent* ev)
279 {
280         bool ret = false;
281         Events::iterator i;
282
283         for (i = events.begin(); i != events.end(); ++i) {
284                 if ((*i)->type == ev->type && (*i)->action_frame == ev->action_frame) {
285                         if ((*i) == ev) {
286                                 ret = true;
287                         }
288
289                         delete *i;
290                         if (i == next_event) {
291                                 ++next_event;
292                         }
293                         i = events.erase (i);
294                         break;
295                 }
296         }
297
298         if (i != events.end()) {
299                 set_next_event ();
300         }
301
302         return ret;
303 }
304
305 void
306 SessionEventManager::_clear_event_type (SessionEvent::Type type)
307 {
308         Events::iterator i, tmp;
309
310         for (i = events.begin(); i != events.end(); ) {
311
312                 tmp = i;
313                 ++tmp;
314
315                 if ((*i)->type == type) {
316                         delete *i;
317                         if (i == next_event) {
318                                 ++next_event;
319                         }
320                         events.erase (i);
321                 }
322
323                 i = tmp;
324         }
325
326         for (i = immediate_events.begin(); i != immediate_events.end(); ) {
327
328                 tmp = i;
329                 ++tmp;
330
331                 if ((*i)->type == type) {
332                         delete *i;
333                         immediate_events.erase (i);
334                 }
335
336                 i = tmp;
337         }
338
339         set_next_event ();
340 }
341