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