1 /* gAlan - Graphical Audio Language
2 * Copyright (C) 1999 Tony Garnock-Jones
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include "generator.h"
26 typedef struct event_callback
{
31 typedef struct EventQ EventQ
;
38 PRIVATE EventQ
*event_q
= NULL
; /* list of AEvents */
39 PRIVATE GList
*rtfuncs
= NULL
; /* list of event_callbacks */
41 PRIVATE GAsyncQueue
*event_queue
;
42 PRIVATE GAsyncQueue
*addrt_queue
;
44 PUBLIC SAMPLETIME gen_current_sampletime
= 0; /* current time */
47 PRIVATE
void aevent_copy( AEvent
*src
, AEvent
*dst
) {
54 dst
->d
.string
= safe_string_dup( src
->d
.string
);
57 dst
->d
.array
.numbers
= safe_malloc( sizeof(SAMPLE
) * dst
->d
.array
.len
);
58 memcpy( dst
->d
.array
.numbers
, src
->d
.array
.numbers
, src
->d
.array
.len
* sizeof(SAMPLE
) );
61 dst
->d
.darray
.numbers
= safe_malloc( sizeof(gdouble
) * dst
->d
.darray
.len
);
62 memcpy( dst
->d
.darray
.numbers
, src
->d
.darray
.numbers
, src
->d
.darray
.len
* sizeof(gdouble
) );
71 PRIVATE
void eventq_free( EventQ
*evq
) {
74 switch( evq
->e
.kind
) {
76 if( evq
->e
.d
.string
!= NULL
)
77 safe_free( evq
->e
.d
.string
);
80 if( evq
->e
.d
.array
.numbers
!= NULL
)
81 safe_free( evq
->e
.d
.array
.numbers
);
84 if( evq
->e
.d
.darray
.numbers
!= NULL
)
85 safe_free( evq
->e
.d
.darray
.numbers
);
90 g_slice_free1( sizeof(EventQ
), evq
);
95 * \brief Put an AEvent into the main event queue.
97 * \param e The Event which shall be copied into the EventQueue
99 * this would use an async queue to make this operation thread safe.
100 * the main event processor will pop all events from the queue and
101 * sort them into the main list.
103 * with this function threadsafe control_emit() gets threadsafe as well.
105 * i believe this is the problem XXX XXX
108 PUBLIC
void gen_post_aevent(AEvent
*e
) {
109 EventQ
*q
= g_slice_alloc(sizeof(EventQ
));
111 aevent_copy( e
, &(q
->e
) );
113 g_async_queue_push( event_queue
, q
);
117 * \brief sort in all posted aevents
119 * XXX: ok... this has to be modified, to support for
120 * local event queues on the generators...
122 * if( g->has_local_queue )
123 * deliver_directly( e );
125 * deliver normally( e );
127 * this seems thread safe... lets see if that
128 * brings us some advantage...
131 PRIVATE
void gen_sortin_aevents(void) {
134 while( (q
=g_async_queue_try_pop( event_queue
)) != NULL
)
136 EventQ
*prev
= NULL
, *curr
= event_q
;
138 while (curr
!= NULL
) {
139 if (q
->e
.time
< curr
->e
.time
)
155 * \brief Remove all events for Generator \a g from the global queue
157 * \param g The Generator
159 * To make this function threadsafe i need to add a command queue.
160 * The mainloop will execute all commands in the queue when its time.
162 * the event insertion queue should be the same as the command queue.
163 * i also need commands for inserting and removing Eventlinks.
165 * also a command for removing generators is needed because
166 * i dont know when the Removal of an EventLink occurs.
168 * At the Moment this function may only be called from the audiothread.
169 * This is ok for all current plugins.
173 PUBLIC
void gen_purge_event_queue_refs(Generator
*g
) {
174 EventQ
*prev
= NULL
, *curr
= event_q
;
176 while (curr
!= NULL
) {
177 EventQ
*next
= curr
->next
;
179 if (curr
->e
.src
== g
|| curr
->e
.dst
== g
) {
196 PUBLIC
void gen_purge_inputevent_queue_refs(Generator
*g
) {
197 EventQ
*prev
= NULL
, *curr
= event_q
;
199 while (curr
!= NULL
) {
200 EventQ
*next
= curr
->next
;
202 if (curr
->e
.dst
== g
) {
220 * \brief insert a function into \a list
222 * \param list pointer to a GList *
223 * \param g The Generator which will be a paramter of \a func
225 * If this was threadsafe gen_register_realtime_fn() would be threadsafe
228 * i need a Mutex for the rtfuncs struct.
231 //PRIVATE void insert_fn(GList **lst, Generator *g, AEvent_handler_t func) {
232 // event_callback *ec = safe_malloc(sizeof(event_callback));
237 // *lst = g_list_prepend(*lst, ec);
241 * \brief insert an event_callback into \a list
243 * \param list pointer to a GList *
244 * \param ec pointer to the eventcallback.
246 * If this was threadsafe gen_register_realtime_fn() would be threadsafe
249 * i need a Mutex for the rtfuncs struct.
251 * no ... make it a GSList.
254 PRIVATE
void insert_fn_ec(GList
**lst
, event_callback
*ec
) {
256 *lst
= g_list_prepend(*lst
, ec
);
258 PRIVATE gint
event_callback_cmp(event_callback
*a
, event_callback
*b
) {
259 return !(a
->g
== b
->g
);
262 PRIVATE
void remove_all_fns(GList
**lst
, Generator
*g
) {
267 link
= g_list_find_custom(*lst
, &ec
, (GCompareFunc
) event_callback_cmp
);
272 *lst
= g_list_remove_link(*lst
, link
);
277 * \brief Register a realtime function
279 * \param g The Genrator wishing to receive Realtime Events
280 * \param func The function which should be called on receive of a Realtime Event
282 * The Realtime functions are the entry points into the graph.
284 * this function is sometimes (scope, outputs) called by the init_instance()
285 * of a Generator to have it realtime_handler installed.
287 * This function must be threadsafe.
288 * an async queue is a good way to accomplish this.
290 * The deregister_realtime_fn function is changed to a noop.
291 * it gets removed during generator destruction.
295 PUBLIC
void gen_register_realtime_fn(Generator
*g
, AEvent_handler_t func
) {
296 event_callback
*ec
= safe_malloc( sizeof(event_callback
) );
300 g_async_queue_push( addrt_queue
, ec
);
304 * \brief deregister a realtime function
306 * \param g The Generator
307 * \param func The callback that should be removed
309 * i will make this a no-op.
310 * its only called in destructors and
311 * i will remove all functions for a generator in the RT stage of removal.
314 PUBLIC
void gen_deregister_realtime_fn(Generator
*g
, AEvent_handler_t func
) {
315 //remove_fn(&rtfuncs, g, func);
318 PUBLIC
void gen_purge_realtime_fns( Generator
*g
)
320 remove_all_fns( &rtfuncs
, g
);
323 PRIVATE
void sortin_rtevents(void) {
326 while( (ec
=g_async_queue_try_pop( addrt_queue
)) != NULL
)
327 insert_fn_ec( &rtfuncs
, ec
);
330 PRIVATE
void send_rt_event(event_callback
*ec
, AEvent
*rtevent
) {
331 // why a local copy ? because the generator can modify an event passed.
332 // warning this only works for REALTIME events.
333 AEvent local_copy
= *rtevent
;
334 ec
->fn(ec
->g
, &local_copy
);
338 * \brief Call all Realtime functions which have registered themselves
339 * with gen_register_realtime_fn()
341 * \param e an AEvent of type AE_REALTIME
343 * this is called when the eventprocessing is done
344 * and the data must be computed.
346 * needs to insert realtime functions from the queue
347 * deletion of functions will be done from the audio thread so
348 * it does not need to be safe.
351 PUBLIC
void gen_send_realtime_fns(AEvent
*e
) {
354 g_list_foreach(rtfuncs
, (GFunc
) send_rt_event
, e
);
358 * \brief process AEvents pending
360 * \return time until next event has to be processed
361 * (At Maximum MAXIMUM_REALTIME_STEP)
363 * This Functions inserts all new aevents and then processes all
364 * pending events from the queue.
365 * it also calls do_checks to make the changes to the connection graph.
368 PUBLIC gint
gen_mainloop_once(void) {
369 int loopcheck_counter
=0;
371 gen_sortin_aevents();
372 gen_mainloop_do_checks();
377 if (e
== NULL
|| e
->e
.time
> gen_get_sampletime()) {
378 SAMPLETIME delta
= (e
== NULL
) ? MAXIMUM_REALTIME_STEP
: e
->e
.time
- gen_get_sampletime();
379 delta
= MIN(delta
, MAXIMUM_REALTIME_STEP
);
381 /* Notice that the clock is *not* advanced here. That's up to the master-clock
382 routine - it should call gen_advance_clock() with the result of this routine. */
387 e
->e
.dst
->klass
->in_handlers
[e
->e
.dst_q
](e
->e
.dst
, &e
->e
);
390 gen_sortin_aevents();
391 loopcheck_counter
+= 1;
392 if( loopcheck_counter
> 1000 ) {
393 // We processed 1000 events now.
394 // this seems to be an event loop.
395 // let break out and, let events processing
397 return MAXIMUM_REALTIME_STEP
;
402 PUBLIC
void gen_advance_clock(gint delta
) {
403 gen_current_sampletime
+= delta
;
406 PUBLIC
void init_event( void ) {
407 if (!g_thread_supported ()) g_thread_init (NULL
);
408 event_queue
= g_async_queue_new();
409 addrt_queue
= g_async_queue_new();