1 /*
2 * Copyright (C) 1999-2017 Paul Davis <paul@linuxaudiosystems.com>
3 * Copyright (C) 2006-2012 David Robillard <d@drobilla.net>
4 * Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
5 * Copyright (C) 2015-2018 Robin Gareus <robin@gareus.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22 #include <cmath>
23 #include <unistd.h>
24
25 #include "pbd/error.h"
26 #include "pbd/enumwriter.h"
27 #include "pbd/pthread_utils.h"
28
29 #include "ardour/debug.h"
30 #include "ardour/session_event.h"
31
32 #include "pbd/i18n.h"
33
34 using namespace std;
35 using namespace ARDOUR;
36 using namespace PBD;
37
38 PerThreadPool* SessionEvent::pool;
39
40 void
init_event_pool()41 SessionEvent::init_event_pool ()
42 {
43 pool = new PerThreadPool;
44 }
45
46 bool
has_per_thread_pool()47 SessionEvent::has_per_thread_pool ()
48 {
49 return pool->has_per_thread_pool ();
50 }
51
52 void
create_per_thread_pool(const std::string & name,uint32_t nitems)53 SessionEvent::create_per_thread_pool (const std::string& name, uint32_t nitems)
54 {
55 /* this is a per-thread call that simply creates a thread-private ptr to
56 a CrossThreadPool for use by this thread whenever events are allocated/released
57 from SessionEvent::pool()
58 */
59 pool->create_per_thread_pool (name, sizeof (SessionEvent), nitems);
60 }
61
SessionEvent(Type t,Action a,samplepos_t when,samplepos_t where,double spd,bool yn,bool yn2,bool yn3)62 SessionEvent::SessionEvent (Type t, Action a, samplepos_t when, samplepos_t where, double spd, bool yn, bool yn2, bool yn3)
63 : type (t)
64 , action (a)
65 , action_sample (when)
66 , target_sample (where)
67 , speed (spd)
68 , yes_or_no (yn)
69 , second_yes_or_no (yn2)
70 , third_yes_or_no (yn3)
71 , event_loop (0)
72 {
73 DEBUG_TRACE (DEBUG::SessionEvents, string_compose ("NEW SESSION EVENT, type = %1 action = %2\n", enum_2_string (type), enum_2_string (action)));
74 }
75
76 void *
operator new(size_t)77 SessionEvent::operator new (size_t)
78 {
79 CrossThreadPool* p = pool->per_thread_pool ();
80 SessionEvent* ev = static_cast<SessionEvent*> (p->alloc ());
81 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,
82 p->total(), p->available(), p->used()));
83
84 ev->own_pool = p;
85 return ev;
86 }
87
88 void
operator delete(void * ptr,size_t)89 SessionEvent::operator delete (void *ptr, size_t /*size*/)
90 {
91 Pool* p = pool->per_thread_pool (false);
92 SessionEvent* ev = static_cast<SessionEvent*> (ptr);
93
94 DEBUG_TRACE (DEBUG::SessionEvents, string_compose (
95 "%1 Deleting SessionEvent @ %2 type %3 action %4 ev thread pool = %5 ev pool = %6 size %7 free %8 used %9\n",
96 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()
97 ));
98
99 if (p && p == ev->own_pool) {
100 p->release (ptr);
101 } else {
102 assert(ev->own_pool);
103 ev->own_pool->push (ev);
104 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",
105 pthread_name(), ev->own_pool->name(),
106 ev->own_pool->total(), ev->own_pool->available(), ev->own_pool->used(),
107 ev->own_pool->pending_size()));
108 }
109 }
110
111 void
add_event(samplepos_t sample,SessionEvent::Type type,samplepos_t target_sample)112 SessionEventManager::add_event (samplepos_t sample, SessionEvent::Type type, samplepos_t target_sample)
113 {
114 SessionEvent* ev = new SessionEvent (type, SessionEvent::Add, sample, target_sample, 0);
115 queue_event (ev);
116 }
117
118 void
remove_event(samplepos_t sample,SessionEvent::Type type)119 SessionEventManager::remove_event (samplepos_t sample, SessionEvent::Type type)
120 {
121 SessionEvent* ev = new SessionEvent (type, SessionEvent::Remove, sample, 0, 0);
122 queue_event (ev);
123 }
124
125 void
replace_event(SessionEvent::Type type,samplepos_t sample,samplepos_t target)126 SessionEventManager::replace_event (SessionEvent::Type type, samplepos_t sample, samplepos_t target)
127 {
128 assert (sample != SessionEvent::Immediate);
129 SessionEvent* ev = new SessionEvent (type, SessionEvent::Replace, sample, target, 0);
130 queue_event (ev);
131 }
132
133 void
clear_events(SessionEvent::Type type)134 SessionEventManager::clear_events (SessionEvent::Type type)
135 {
136 SessionEvent* ev = new SessionEvent (type, SessionEvent::Clear, SessionEvent::Immediate, 0, 0);
137 queue_event (ev);
138 }
139
140 void
clear_events(SessionEvent::Type type,boost::function<void (void)> after)141 SessionEventManager::clear_events (SessionEvent::Type type, boost::function<void (void)> after)
142 {
143 SessionEvent* ev = new SessionEvent (type, SessionEvent::Clear, SessionEvent::Immediate, 0, 0);
144 ev->rt_slot = after;
145
146 /* in the calling thread, after the clear is complete, arrange to flush things from the event
147 pool pending list (i.e. to make sure they are really back in the free list and available
148 for future events).
149 */
150
151 ev->event_loop = PBD::EventLoop::get_event_loop_for_thread ();
152 if (ev->event_loop) {
153 ev->rt_return = boost::bind (&CrossThreadPool::flush_pending_with_ev, ev->event_pool(), _1);
154 }
155
156 queue_event (ev);
157 }
158
159 void
dump_events() const160 SessionEventManager::dump_events () const
161 {
162 cerr << "EVENT DUMP" << endl;
163 for (Events::const_iterator i = events.begin(); i != events.end(); ++i) {
164
165 cerr << "\tat " << (*i)->action_sample << ' ' << enum_2_string ((*i)->type) << " target = " << (*i)->target_sample << endl;
166 }
167 cerr << "Next event: ";
168
169 if ((Events::const_iterator) next_event == events.end()) {
170 cerr << "none" << endl;
171 } else {
172 cerr << "at " << (*next_event)->action_sample << ' '
173 << enum_2_string ((*next_event)->type) << " target = "
174 << (*next_event)->target_sample << endl;
175 }
176 cerr << "Immediate events pending:\n";
177 for (Events::const_iterator i = immediate_events.begin(); i != immediate_events.end(); ++i) {
178 cerr << "\tat " << (*i)->action_sample << ' ' << enum_2_string((*i)->type) << " target = " << (*i)->target_sample << endl;
179 }
180 cerr << "END EVENT_DUMP" << endl;
181 }
182
183 void
merge_event(SessionEvent * ev)184 SessionEventManager::merge_event (SessionEvent* ev)
185 {
186 switch (ev->action) {
187 case SessionEvent::Remove:
188 _remove_event (ev);
189 delete ev;
190 return;
191
192 case SessionEvent::Replace:
193 _replace_event (ev);
194 return;
195
196 case SessionEvent::Clear:
197 _clear_event_type (ev->type);
198 /* run any additional realtime callback, if any */
199 if (ev->rt_slot) {
200 ev->rt_slot ();
201 }
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->type == SessionEvent::Locate || ev->type == SessionEvent::LocateRoll) {
217 /* remove any existing Locates that are waiting to execute */
218 _clear_event_type (ev->type);
219 }
220
221 if (ev->action_sample == SessionEvent::Immediate) {
222 process_event (ev);
223 return;
224 }
225
226 switch (ev->type) {
227 case SessionEvent::AutoLoop:
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
_replace_event(SessionEvent * ev)248 SessionEventManager::_replace_event (SessionEvent* ev)
249 {
250 bool ret = false;
251 Events::iterator i;
252
253 /* use only for events that can only exist once in the respective queue */
254 Events& e (ev->action_sample == SessionEvent::Immediate ? immediate_events : events);
255
256 for (i = e.begin(); i != e.end(); ++i) {
257 if ((*i)->type == ev->type && ev->type == SessionEvent::Overwrite && (*i)->track.lock() == ev->track.lock() && (*i)->overwrite == ev->overwrite) {
258 assert (ev->action_sample == SessionEvent::Immediate);
259 ret = true;
260 delete ev;
261 break;
262 }
263 else if ((*i)->type == ev->type && ev->type != SessionEvent::Overwrite) {
264 assert (ev->action_sample != SessionEvent::Immediate);
265 assert (ev->type == SessionEvent::PunchIn || ev->type == SessionEvent::PunchOut || ev->type == SessionEvent::AutoLoop);
266 (*i)->action_sample = ev->action_sample;
267 (*i)->target_sample = ev->target_sample;
268 if ((*i) == ev) {
269 ret = true;
270 }
271 delete ev;
272 break;
273 }
274 }
275
276 if (i == e.end()) {
277 e.insert (e.begin(), ev);
278 }
279
280 if (ev->action_sample == SessionEvent::Immediate) {
281 /* no need to sort immediate events */
282 return ret;
283 }
284
285 e.sort (SessionEvent::compare);
286 next_event = e.end();
287 set_next_event ();
288
289 return ret;
290 }
291
292 /** @return true when @a ev is deleted. */
293 bool
_remove_event(SessionEvent * ev)294 SessionEventManager::_remove_event (SessionEvent* ev)
295 {
296 bool ret = false;
297 Events::iterator i;
298
299 for (i = events.begin(); i != events.end(); ++i) {
300 if ((*i)->type == ev->type && (*i)->action_sample == ev->action_sample) {
301 assert ((*i)->action_sample != SessionEvent::Immediate);
302 if ((*i) == ev) {
303 ret = true;
304 }
305
306 delete *i;
307 if (i == next_event) {
308 ++next_event;
309 }
310 i = events.erase (i);
311 break;
312 }
313 }
314
315 if (i != events.end()) {
316 set_next_event ();
317 }
318
319 return ret;
320 }
321
322 void
_clear_event_type(SessionEvent::Type type)323 SessionEventManager::_clear_event_type (SessionEvent::Type type)
324 {
325 Events::iterator i, tmp;
326
327 for (i = events.begin(); i != events.end(); ) {
328
329 tmp = i;
330 ++tmp;
331
332 if ((*i)->type == type) {
333 delete *i;
334 if (i == next_event) {
335 ++next_event;
336 }
337 events.erase (i);
338 }
339
340 i = tmp;
341 }
342
343 for (i = immediate_events.begin(); i != immediate_events.end(); ) {
344
345 tmp = i;
346 ++tmp;
347
348 if ((*i)->type == type) {
349 delete *i;
350 immediate_events.erase (i);
351 }
352
353 i = tmp;
354 }
355
356 set_next_event ();
357 }
358