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