Remove ambiguous API implementation
[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 "pbd/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 bool
46 SessionEvent::has_per_thread_pool ()
47 {
48         return pool->has_per_thread_pool ();
49 }
50
51 void
52 SessionEvent::create_per_thread_pool (const std::string& name, uint32_t nitems)
53 {
54         /* this is a per-thread call that simply creates a thread-private ptr to
55            a CrossThreadPool for use by this thread whenever events are allocated/released
56            from SessionEvent::pool()
57         */
58         pool->create_per_thread_pool (name, sizeof (SessionEvent), nitems);
59 }
60
61 SessionEvent::SessionEvent (Type t, Action a, samplepos_t when, samplepos_t where, double spd, bool yn, bool yn2, bool yn3)
62         : type (t)
63         , action (a)
64         , action_sample (when)
65         , target_sample (where)
66         , speed (spd)
67         , yes_or_no (yn)
68         , second_yes_or_no (yn2)
69         , third_yes_or_no (yn3)
70         , event_loop (0)
71 {
72         DEBUG_TRACE (DEBUG::SessionEvents, string_compose ("NEW SESSION EVENT, type = %1 action = %2\n", enum_2_string (type), enum_2_string (action)));
73 }
74
75 void *
76 SessionEvent::operator new (size_t)
77 {
78         CrossThreadPool* p = pool->per_thread_pool ();
79         SessionEvent* ev = static_cast<SessionEvent*> (p->alloc ());
80         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,
81                                                            p->total(), p->available(), p->used()));
82
83         ev->own_pool = p;
84         return ev;
85 }
86
87 void
88 SessionEvent::operator delete (void *ptr, size_t /*size*/)
89 {
90         Pool* p = pool->per_thread_pool (false);
91         SessionEvent* ev = static_cast<SessionEvent*> (ptr);
92
93         DEBUG_TRACE (DEBUG::SessionEvents, string_compose (
94                              "%1 Deleting SessionEvent @ %2 type %3 action %4 ev thread pool = %5 ev pool = %6 size %7 free %8 used %9\n",
95                              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()
96                              ));
97
98         if (p && p == ev->own_pool) {
99                 p->release (ptr);
100         } else {
101                 assert(ev->own_pool);
102                 ev->own_pool->push (ev);
103                 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",
104                                                                    pthread_name(), ev->own_pool->name(),
105                                                                    ev->own_pool->total(), ev->own_pool->available(), ev->own_pool->used(),
106                                                                    ev->own_pool->pending_size()));
107         }
108 }
109
110 void
111 SessionEventManager::add_event (samplepos_t sample, SessionEvent::Type type, samplepos_t target_sample)
112 {
113         SessionEvent* ev = new SessionEvent (type, SessionEvent::Add, sample, target_sample, 0);
114         queue_event (ev);
115 }
116
117 void
118 SessionEventManager::remove_event (samplepos_t sample, SessionEvent::Type type)
119 {
120         SessionEvent* ev = new SessionEvent (type, SessionEvent::Remove, sample, 0, 0);
121         queue_event (ev);
122 }
123
124 void
125 SessionEventManager::replace_event (SessionEvent::Type type, samplepos_t sample, samplepos_t target)
126 {
127         SessionEvent* ev = new SessionEvent (type, SessionEvent::Replace, sample, target, 0);
128         queue_event (ev);
129 }
130
131 void
132 SessionEventManager::clear_events (SessionEvent::Type type)
133 {
134         SessionEvent* ev = new SessionEvent (type, SessionEvent::Clear, SessionEvent::Immediate, 0, 0);
135         queue_event (ev);
136 }
137
138 void
139 SessionEventManager::clear_events (SessionEvent::Type type, boost::function<void (void)> after)
140 {
141         SessionEvent* ev = new SessionEvent (type, SessionEvent::Clear, SessionEvent::Immediate, 0, 0);
142         ev->rt_slot = after;
143
144         /* in the calling thread, after the clear is complete, arrange to flush things from the event
145            pool pending list (i.e. to make sure they are really back in the free list and available
146            for future events).
147         */
148
149         ev->event_loop = PBD::EventLoop::get_event_loop_for_thread ();
150         if (ev->event_loop) {
151                 ev->rt_return = boost::bind (&CrossThreadPool::flush_pending_with_ev, ev->event_pool(), _1);
152         }
153
154         queue_event (ev);
155 }
156
157 void
158 SessionEventManager::dump_events () const
159 {
160         cerr << "EVENT DUMP" << endl;
161         for (Events::const_iterator i = events.begin(); i != events.end(); ++i) {
162
163                 cerr << "\tat " << (*i)->action_sample << ' ' << enum_2_string ((*i)->type) << " target = " << (*i)->target_sample << endl;
164         }
165         cerr << "Next event: ";
166
167         if ((Events::const_iterator) next_event == events.end()) {
168                 cerr << "none" << endl;
169         } else {
170                 cerr << "at " << (*next_event)->action_sample << ' '
171                      << enum_2_string ((*next_event)->type) << " target = "
172                      << (*next_event)->target_sample << endl;
173         }
174         cerr << "Immediate events pending:\n";
175         for (Events::const_iterator i = immediate_events.begin(); i != immediate_events.end(); ++i) {
176                 cerr << "\tat " << (*i)->action_sample << ' ' << enum_2_string((*i)->type) << " target = " << (*i)->target_sample << endl;
177         }
178         cerr << "END EVENT_DUMP" << endl;
179 }
180
181 void
182 SessionEventManager::merge_event (SessionEvent* ev)
183 {
184         switch (ev->action) {
185         case SessionEvent::Remove:
186                 _remove_event (ev);
187                 delete ev;
188                 return;
189
190         case SessionEvent::Replace:
191                 _replace_event (ev);
192                 return;
193
194         case SessionEvent::Clear:
195                 _clear_event_type (ev->type);
196                 /* run any additional realtime callback, if any */
197                 if (ev->rt_slot) {
198                         ev->rt_slot ();
199                 }
200                 if (ev->event_loop) {
201                         /* run non-realtime callback (in some other thread) */
202                         ev->event_loop->call_slot (MISSING_INVALIDATOR, boost::bind (ev->rt_return, ev));
203                 } else {
204                         delete ev;
205                 }
206                 return;
207
208         case SessionEvent::Add:
209                 break;
210         }
211
212         /* try to handle immediate events right here */
213
214         if (ev->type == SessionEvent::Locate || ev->type == SessionEvent::LocateRoll) {
215                 /* remove any existing Locates that are waiting to execute */
216                 _clear_event_type (ev->type);
217         }
218
219         if (ev->action_sample == SessionEvent::Immediate) {
220                 process_event (ev);
221                 return;
222         }
223
224         switch (ev->type) {
225         case SessionEvent::AutoLoop:
226         case SessionEvent::StopOnce:
227                 _clear_event_type (ev->type);
228                 break;
229         default:
230                 for (Events::iterator i = events.begin(); i != events.end(); ++i) {
231                         if ((*i)->type == ev->type && (*i)->action_sample == ev->action_sample) {
232                           error << string_compose(_("Session: cannot have two events of type %1 at the same sample (%2)."),
233                                                   enum_2_string (ev->type), ev->action_sample) << endmsg;
234                                 return;
235                         }
236                 }
237         }
238
239         events.insert (events.begin(), ev);
240         events.sort (SessionEvent::compare);
241         next_event = events.begin();
242         set_next_event ();
243 }
244
245 /** @return true when @a ev is deleted. */
246 bool
247 SessionEventManager::_replace_event (SessionEvent* ev)
248 {
249         bool ret = false;
250         Events::iterator i;
251
252         /* private, used only for events that can only exist once in the queue */
253
254         for (i = events.begin(); i != events.end(); ++i) {
255                 if ((*i)->type == ev->type) {
256                         (*i)->action_sample = ev->action_sample;
257                         (*i)->target_sample = ev->target_sample;
258                         if ((*i) == ev) {
259                                 ret = true;
260                         }
261                         delete ev;
262                         break;
263                 }
264         }
265
266         if (i == events.end()) {
267                 events.insert (events.begin(), ev);
268         }
269
270         events.sort (SessionEvent::compare);
271         next_event = events.end();
272         set_next_event ();
273
274         return ret;
275 }
276
277 /** @return true when @a ev is deleted. */
278 bool
279 SessionEventManager::_remove_event (SessionEvent* ev)
280 {
281         bool ret = false;
282         Events::iterator i;
283
284         for (i = events.begin(); i != events.end(); ++i) {
285                 if ((*i)->type == ev->type && (*i)->action_sample == ev->action_sample) {
286                         if ((*i) == ev) {
287                                 ret = true;
288                         }
289
290                         delete *i;
291                         if (i == next_event) {
292                                 ++next_event;
293                         }
294                         i = events.erase (i);
295                         break;
296                 }
297         }
298
299         if (i != events.end()) {
300                 set_next_event ();
301         }
302
303         return ret;
304 }
305
306 void
307 SessionEventManager::_clear_event_type (SessionEvent::Type type)
308 {
309         Events::iterator i, tmp;
310
311         for (i = events.begin(); i != events.end(); ) {
312
313                 tmp = i;
314                 ++tmp;
315
316                 if ((*i)->type == type) {
317                         delete *i;
318                         if (i == next_event) {
319                                 ++next_event;
320                         }
321                         events.erase (i);
322                 }
323
324                 i = tmp;
325         }
326
327         for (i = immediate_events.begin(); i != immediate_events.end(); ) {
328
329                 tmp = i;
330                 ++tmp;
331
332                 if ((*i)->type == type) {
333                         delete *i;
334                         immediate_events.erase (i);
335                 }
336
337                 i = tmp;
338         }
339
340         set_next_event ();
341 }