globally change all use of "frame" to refer to audio into "sample".
[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::AutoLoopDeclick:
227         case SessionEvent::StopOnce:
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 }