1 /* Copyright (c) 1997-1999 Miller Puckette.
2 * For information on usage and redistribution, and for a DISCLAIMER OF ALL
3 * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
7 #include "../../pdbox.h"
10 /* scheduling stuff */
16 /* LATER consider making this variable. It's now the LCM of all sample
17 rates we expect to see: 32000, 44100, 48000, 88200, 96000. */
18 #define TIMEUNITPERSEC (32.*441000.)
21 /* T.Grill - enable PD global thread locking - sys_lock, sys_unlock, sys_trylock functions */
23 #define THREAD_LOCKING
36 t_time sys_time_per_msec
= TIMEUNITPERSEC
/ 1000.;
38 int sys_schedblocksize
= DEFDACBLKSIZE
;
39 int sys_usecsincelastsleep(void);
42 typedef void (*t_clockmethod
)(void *client
);
49 struct _clock
*c_next
;
52 t_clock
*clock_setlist
;
58 t_clock
*clock_new(void *owner
, t_method fn
)
60 t_clock
*x
= (t_clock
*)getbytes(sizeof *x
);
63 x
->c_fn
= (t_clockmethod
)fn
;
68 void clock_unset(t_clock
*x
)
70 if (x
->c_settime
>= 0)
72 if (x
== clock_setlist
) clock_setlist
= x
->c_next
;
75 t_clock
*x2
= clock_setlist
;
76 while (x2
->c_next
!= x
) x2
= x2
->c_next
;
77 x2
->c_next
= x
->c_next
;
83 /* set the clock to call back at an absolute system time */
84 void clock_set(t_clock
*x
, t_time setticks
)
86 if (setticks
< sys_time
) setticks
= sys_time
;
88 x
->c_settime
= setticks
;
89 if (clock_setlist
&& clock_setlist
->c_settime
<= setticks
)
91 t_clock
*cbefore
, *cafter
;
92 for (cbefore
= clock_setlist
, cafter
= clock_setlist
->c_next
;
93 cbefore
; cbefore
= cafter
, cafter
= cbefore
->c_next
)
95 if (!cafter
|| cafter
->c_settime
> setticks
)
103 else x
->c_next
= clock_setlist
, clock_setlist
= x
;
106 /* set the clock to call back after a delay in msec */
107 void clock_delay(t_clock
*x
, t_time delaytime
)
109 clock_set(x
, sys_time
+ sys_time_per_msec
* delaytime
);
112 /* get current logical time. We don't specify what units this is in;
113 use clock_gettimesince() to measure intervals from time of this call.
114 This was previously, incorrectly named "clock_getsystime"; the old
115 name is aliased to the new one in m_pd.h. */
116 t_time
clock_getlogicaltime( void)
121 t_time
clock_getsystime( void) { return (sys_time
); }
123 /* elapsed time in milliseconds since the given system time */
124 t_time
clock_gettimesince(t_time prevsystime
)
126 return ((sys_time
- prevsystime
)/sys_time_per_msec
);
129 /* what value the system clock will have after a delay */
130 t_time
clock_getsystimeafter(t_time delaytime
)
132 return (sys_time
+ sys_time_per_msec
* delaytime
);
135 void clock_free(t_clock
*x
)
138 freebytes(x
, sizeof *x
);
142 /* the following routines maintain a real-execution-time histogram of the
143 various phases of real-time execution. */
145 static int sys_bin
[] = {0, 2, 5, 10, 20, 30, 50, 100, 1000};
146 #define NBIN (sizeof(sys_bin)/sizeof(*sys_bin))
148 static int sys_histogram
[NHIST
][NBIN
];
150 static t_time sys_histtime
;
152 static int sched_diddsp
, sched_didpoll
, sched_didnothing
;
155 static void sys_clearhist( void)
158 for (i
= 0; i
< NHIST
; i
++)
159 for (j
= 0; j
< NBIN
; j
++) sys_histogram
[i
][j
] = 0;
160 sys_histtime
= sys_getrealtime();
161 sched_diddsp
= sched_didpoll
= sched_didnothing
= 0;
165 void sys_printhist( void)
168 for (i
= 0; i
< NHIST
; i
++)
171 for (j
= 0; j
< NBIN
; j
++) if (sys_histogram
[i
][j
]) doit
= 1;
174 post("%2d %8d %8d %8d %8d %8d %8d %8d %8d", i
,
182 sys_histogram
[i
][7]);
185 post("dsp %d, pollgui %d, nothing %d",
186 sched_diddsp
, sched_didpoll
, sched_didnothing
);
190 static int sys_histphase
;
193 int sys_addhist(int phase
)
199 int i
, j
, phasewas
= sys_histphase
;
200 t_time newtime
= sys_getrealtime();
201 int msec
= (newtime
- sys_histtime
) * 1000.;
202 for (j
= NBIN
-1; j
>= 0; j
--)
204 if (msec
>= sys_bin
[j
])
206 sys_histogram
[phasewas
][j
]++;
210 sys_histtime
= newtime
;
211 sys_histphase
= phase
;
220 typedef struct _resync
226 static int oss_resyncphase
= 0;
227 static int oss_nresync
= 0;
228 static t_resync oss_resync
[NRESYNC
];
231 static char *(oss_errornames
[]) = {
239 void glob_audiostatus(void)
242 int nresync
, nresyncphase
, i
;
244 int dev
, nresync
, nresyncphase
, i
;
246 nresync
= (oss_nresync
>= NRESYNC
? NRESYNC
: oss_nresync
);
247 nresyncphase
= oss_resyncphase
- 1;
248 post("audio I/O error history:");
249 post("seconds ago\terror type");
250 for (i
= 0; i
< nresync
; i
++)
253 if (nresyncphase
< 0)
254 nresyncphase
+= NRESYNC
;
255 errtype
= oss_resync
[nresyncphase
].r_error
;
256 if (errtype
< 0 || errtype
> 4)
260 (sched_diddsp
- oss_resync
[nresyncphase
].r_ntick
)
261 * ((double)sys_schedblocksize
) / sys_dacsr
,
262 oss_errornames
[errtype
]);
267 static int sched_diored
;
268 static int sched_dioredtime
;
269 static int sched_meterson
;
271 void sys_log_error(int type
)
273 oss_resync
[oss_resyncphase
].r_ntick
= sched_diddsp
;
274 oss_resync
[oss_resyncphase
].r_error
= type
;
276 if (++oss_resyncphase
== NRESYNC
) oss_resyncphase
= 0;
277 if (type
!= ERR_NOTHING
&& !sched_diored
&&
278 (sched_diddsp
>= sched_dioredtime
))
281 sys_vgui("pdtk_pd_dio 1\n");
286 sched_diddsp
+ (int)(sys_dacsr
/(double)sys_schedblocksize
);
289 static int sched_lastinclip
, sched_lastoutclip
,
290 sched_lastindb
, sched_lastoutdb
;
292 void glob_ping(t_pd
*dummy
);
295 static void sched_pollformeters( void)
297 int inclip
, outclip
, indb
, outdb
;
298 static int sched_nextmeterpolltime
, sched_nextpingtime
;
300 /* if there's no GUI but we're running in "realtime", here is
301 where we arrange to ping the watchdog every 2 seconds. */
303 if (sys_nogui
&& sys_hipriority
&& (sched_diddsp
- sched_nextpingtime
> 0))
306 /* ping every 2 seconds */
307 sched_nextpingtime
= sched_diddsp
+
308 (2* sys_dacsr
) /(int)sys_schedblocksize
;
312 if (sched_diddsp
- sched_nextmeterpolltime
< 0)
314 if (sched_diored
&& (sched_diddsp
- sched_dioredtime
> 0))
316 sys_vgui("pdtk_pd_dio 0\n");
322 sys_getmeters(&inmax
, &outmax
);
323 indb
= 0.5 + rmstodb(inmax
);
324 outdb
= 0.5 + rmstodb(outmax
);
325 inclip
= (inmax
> 0.999);
326 outclip
= (outmax
>= 1.0);
331 inclip
= outclip
= 0;
333 if (inclip
!= sched_lastinclip
|| outclip
!= sched_lastoutclip
334 || indb
!= sched_lastindb
|| outdb
!= sched_lastoutdb
)
336 sys_vgui("pdtk_pd_meters %d %d %d %d\n", indb
, outdb
, inclip
, outclip
);
337 sched_lastinclip
= inclip
;
338 sched_lastoutclip
= outclip
;
339 sched_lastindb
= indb
;
340 sched_lastoutdb
= outdb
;
342 sched_nextmeterpolltime
=
343 sched_diddsp
+ (int)(sys_dacsr
/(double)sys_schedblocksize
);
347 void glob_meters(void *dummy
, float f
)
354 sched_meterson
= (f
!= 0);
355 sched_lastinclip
= sched_lastoutclip
= sched_lastindb
= sched_lastoutdb
=
360 void glob_foo(void *dummy
, t_symbol
*s
, int argc
, t_atom
*argv
)
362 if (argc
) sys_clearhist();
363 else sys_printhist();
369 static int sched_usedacs
= 1;
370 static t_time sched_referencerealtime
, sched_referencelogicaltime
;
374 t_time sys_time_per_dsp_tick
;
376 void sched_set_using_dacs(int flag
)
378 sched_usedacs
= flag
;
381 sched_referencerealtime
= sys_getrealtime();
382 sched_referencelogicaltime
= clock_getlogicaltime();
384 post("schedsetuding");
387 sys_time_per_dsp_tick
= (TIMEUNITPERSEC
) *
388 ((double)sys_schedblocksize
) / sys_dacsr
;
391 printf("%f\n%f\n%f\n%f\n", (double)sys_time_per_dsp_tick, (double)TIMEUNITPERSEC, (double) sys_schedblocksize, (double)sys_dacsr);
396 /* take the scheduler forward one DSP tick, also handling clock timeouts */
400 void sched_tick(t_time next_sys_time
)
402 int countdown
= 5000;
403 while (clock_setlist
&& clock_setlist
->c_settime
< next_sys_time
)
405 t_clock
*c
= clock_setlist
;
406 sys_time
= c
->c_settime
;
407 clock_unset(clock_setlist
);
408 outlet_setstacklim();
409 (*c
->c_fn
)(c
->c_owner
);
420 sys_time
= next_sys_time
;
426 Here is Pd's "main loop." This routine dispatches clock timeouts and DSP
427 "ticks" deterministically, and polls for input from MIDI and the GUI. If
428 we're left idle we also poll for graphics updates; but these are considered
429 lower priority than the rest.
431 The time source is normally the audio I/O subsystem via the "sys_send_dacs()"
432 call. This call returns true if samples were transferred; false means that
433 the audio I/O system is still busy with previous transfers.
436 void sys_pollmidiqueue( void);
437 void sys_initmidiqueue( void);
440 int m_scheduler_pda( void)
444 sys_time_per_dsp_tick
= (TIMEUNITPERSEC
) *
445 ((double)sys_schedblocksize
) / sys_dacsr
;
449 if (sys_sleepgrain
< 1000)
450 sys_sleepgrain
= sys_schedadvance
/4;
451 if (sys_sleepgrain
< 100)
452 sys_sleepgrain
= 100;
453 else if (sys_sleepgrain
> 5000)
454 sys_sleepgrain
= 5000;
461 int didsomething
= 0;
470 timeforward
= sys_send_dacs();
474 if ((sys_getrealtime() - sched_referencerealtime
)
475 > (t_time
)clock_gettimesince(sched_referencelogicaltime
)*1000)
476 timeforward
= SENDDACS_YES
;
477 else timeforward
= SENDDACS_NO
;
478 if (timeforward
== SENDDACS_YES
)
479 sys_microsleep(sys_sleepgrain
);
481 if (timeforward
!= SENDDACS_NO
) {
482 sched_tick(sys_time
+ sys_time_per_dsp_tick
);
489 int m_scheduler( void)
492 sys_time_per_dsp_tick
= (TIMEUNITPERSEC
) *
493 ((double)sys_schedblocksize
) / sys_dacsr
;
495 #ifdef THREAD_LOCKING
496 /* T.Grill - lock mutex */
501 if (sys_sleepgrain
< 1000)
502 sys_sleepgrain
= sys_schedadvance
/4;
503 if (sys_sleepgrain
< 100)
504 sys_sleepgrain
= 100;
505 else if (sys_sleepgrain
> 5000)
506 sys_sleepgrain
= 5000;
510 int didsomething
= 0;
517 timeforward
= sys_send_dacs();
519 /* if dacs remain "idle" for 1 sec, they're hung up. */
520 if (timeforward
!= 0)
525 if (!(idlecount
& 31))
527 static t_time idletime
;
528 /* on 32nd idle, start a clock watch; every
529 32 ensuing idles, check it */
531 idletime
= sys_getrealtime();
532 else if (sys_getrealtime() - idletime
> 1.)
534 post("audio I/O stuck... closing audio\n");
536 sched_set_using_dacs(0);
544 if (1000. * (sys_getrealtime() - sched_referencerealtime
)
545 > clock_gettimesince(sched_referencelogicaltime
))
546 timeforward
= SENDDACS_YES
;
547 else timeforward
= SENDDACS_NO
;
549 sys_setmiditimediff(0, 1e-6 * sys_schedadvance
);
551 if (timeforward
!= SENDDACS_NO
)
552 sched_tick(sys_time
+ sys_time_per_dsp_tick
);
553 if (timeforward
== SENDDACS_YES
)
557 // sys_pollmidiqueue();
565 /* test for idle; if so, do graphics updates. */
568 sched_pollformeters();
571 #ifdef THREAD_LOCKING
572 /* T.Grill - enter idle phase -> unlock thread lock */
575 if (timeforward
!= SENDDACS_SLEPT
)
576 sys_microsleep(sys_sleepgrain
);
577 #ifdef THREAD_LOCKING
578 /* T.Grill - leave idle phase -> lock thread lock */
588 #ifdef THREAD_LOCKING
597 /* ------------ thread locking ------------------- */
598 /* added by Thomas Grill */
600 #ifdef THREAD_LOCKING
601 static pthread_mutex_t sys_mutex
= PTHREAD_MUTEX_INITIALIZER
;
605 pthread_mutex_lock(&sys_mutex
);
608 void sys_unlock(void)
610 pthread_mutex_unlock(&sys_mutex
);
613 int sys_trylock(void)
615 return pthread_mutex_trylock(&sys_mutex
);
620 void sys_lock(void) {}
621 void sys_unlock(void) {}
622 int sys_trylock(void) { return 0; }
627 /* ------------ soft quit ------------------- */
628 /* added by Thomas Grill -
629 just set the quit flag for the scheduler loop
630 this is useful for applications using the PD shared library to signal the scheduler to terminate