Fix pdbox makefile to actually take part in dependency generation
[kugel-rb.git] / apps / plugins / pdbox / PDa / src / m_sched.c
blob6500fc203804e4795bf5d3305656f19e6e8eaea4
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. */
5 #ifdef ROCKBOX
6 #include "plugin.h"
7 #include "../../pdbox.h"
8 #endif
10 /* scheduling stuff */
12 #include "m_pd.h"
13 #include "m_imp.h"
14 #include "s_stuff.h"
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 */
22 #ifndef ROCKBOX
23 #define THREAD_LOCKING
24 #include "pthread.h"
25 #endif
28 static int sys_quit;
29 #ifndef ROCKBOX
30 static
31 #endif
32 t_time sys_time;
33 #ifndef ROCKBOX
34 static
35 #endif
36 t_time sys_time_per_msec = TIMEUNITPERSEC / 1000.;
38 int sys_schedblocksize = DEFDACBLKSIZE;
39 int sys_usecsincelastsleep(void);
40 int sys_sleepgrain;
42 typedef void (*t_clockmethod)(void *client);
44 struct _clock
46 t_time c_settime;
47 void *c_owner;
48 t_clockmethod c_fn;
49 struct _clock *c_next;
52 t_clock *clock_setlist;
54 #ifdef UNIX
55 #include <unistd.h>
56 #endif
58 t_clock *clock_new(void *owner, t_method fn)
60 t_clock *x = (t_clock *)getbytes(sizeof *x);
61 x->c_settime = -1;
62 x->c_owner = owner;
63 x->c_fn = (t_clockmethod)fn;
64 x->c_next = 0;
65 return (x);
68 void clock_unset(t_clock *x)
70 if (x->c_settime >= 0)
72 if (x == clock_setlist) clock_setlist = x->c_next;
73 else
75 t_clock *x2 = clock_setlist;
76 while (x2->c_next != x) x2 = x2->c_next;
77 x2->c_next = x->c_next;
79 x->c_settime = -1;
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;
87 clock_unset(x);
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)
97 cbefore->c_next = x;
98 x->c_next = cafter;
99 return;
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)
118 return (sys_time);
120 /* OBSOLETE NAME */
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)
137 clock_unset(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))
147 #define NHIST 10
148 static int sys_histogram[NHIST][NBIN];
149 #ifndef ROCKBOX
150 static t_time sys_histtime;
151 #endif
152 static int sched_diddsp, sched_didpoll, sched_didnothing;
154 #ifndef ROCKBOX
155 static void sys_clearhist( void)
157 unsigned int i, j;
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;
163 #endif
165 void sys_printhist( void)
167 unsigned int i, j;
168 for (i = 0; i < NHIST; i++)
170 int doit = 0;
171 for (j = 0; j < NBIN; j++) if (sys_histogram[i][j]) doit = 1;
172 if (doit)
174 post("%2d %8d %8d %8d %8d %8d %8d %8d %8d", i,
175 sys_histogram[i][0],
176 sys_histogram[i][1],
177 sys_histogram[i][2],
178 sys_histogram[i][3],
179 sys_histogram[i][4],
180 sys_histogram[i][5],
181 sys_histogram[i][6],
182 sys_histogram[i][7]);
185 post("dsp %d, pollgui %d, nothing %d",
186 sched_diddsp, sched_didpoll, sched_didnothing);
189 #ifndef ROCKBOX
190 static int sys_histphase;
191 #endif
193 int sys_addhist(int phase)
195 #ifdef ROCKBOX
196 (void) phase;
197 #endif
198 #ifndef FIXEDPOINT
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]++;
207 break;
210 sys_histtime = newtime;
211 sys_histphase = phase;
212 return (phasewas);
213 #else
214 return 0;
215 #endif
218 #define NRESYNC 20
220 typedef struct _resync
222 int r_ntick;
223 int r_error;
224 } t_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[]) = {
232 "unknown",
233 "ADC blocked",
234 "DAC blocked",
235 "A/D/A sync",
236 "data late"
239 void glob_audiostatus(void)
241 #ifdef ROCKBOX
242 int nresync, nresyncphase, i;
243 #else
244 int dev, nresync, nresyncphase, i;
245 #endif
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++)
252 int errtype;
253 if (nresyncphase < 0)
254 nresyncphase += NRESYNC;
255 errtype = oss_resync[nresyncphase].r_error;
256 if (errtype < 0 || errtype > 4)
257 errtype = 0;
259 post("%9.2f\t%s",
260 (sched_diddsp - oss_resync[nresyncphase].r_ntick)
261 * ((double)sys_schedblocksize) / sys_dacsr,
262 oss_errornames[errtype]);
263 nresyncphase--;
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;
275 oss_nresync++;
276 if (++oss_resyncphase == NRESYNC) oss_resyncphase = 0;
277 if (type != ERR_NOTHING && !sched_diored &&
278 (sched_diddsp >= sched_dioredtime))
280 #ifndef ROCKBOX
281 sys_vgui("pdtk_pd_dio 1\n");
282 #endif
283 sched_diored = 1;
285 sched_dioredtime =
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);
294 #ifndef ROCKBOX
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. */
302 #ifdef __linux__
303 if (sys_nogui && sys_hipriority && (sched_diddsp - sched_nextpingtime > 0))
305 glob_ping(0);
306 /* ping every 2 seconds */
307 sched_nextpingtime = sched_diddsp +
308 (2* sys_dacsr) /(int)sys_schedblocksize;
310 #endif
312 if (sched_diddsp - sched_nextmeterpolltime < 0)
313 return;
314 if (sched_diored && (sched_diddsp - sched_dioredtime > 0))
316 sys_vgui("pdtk_pd_dio 0\n");
317 sched_diored = 0;
319 if (sched_meterson)
321 float inmax, outmax;
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);
328 else
330 indb = outdb = 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);
345 #endif /* ROCKBOX */
347 void glob_meters(void *dummy, float f)
349 #ifdef ROCKBOX
350 (void) dummy;
351 #endif
352 if (f == 0)
353 sys_getmeters(0, 0);
354 sched_meterson = (f != 0);
355 sched_lastinclip = sched_lastoutclip = sched_lastindb = sched_lastoutdb =
359 #if 0
360 void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv)
362 if (argc) sys_clearhist();
363 else sys_printhist();
365 #endif
367 void dsp_tick(void);
369 static int sched_usedacs = 1;
370 static t_time sched_referencerealtime, sched_referencelogicaltime;
371 #ifndef ROCKBOX
372 static
373 #endif
374 t_time sys_time_per_dsp_tick;
376 void sched_set_using_dacs(int flag)
378 sched_usedacs = flag;
379 if (!flag)
381 sched_referencerealtime = sys_getrealtime();
382 sched_referencelogicaltime = clock_getlogicaltime();
383 #ifndef ROCKBOX
384 post("schedsetuding");
385 #endif
387 sys_time_per_dsp_tick = (TIMEUNITPERSEC) *
388 ((double)sys_schedblocksize) / sys_dacsr;
390 #ifdef SIMULATOR
391 printf("%f\n%f\n%f\n%f\n", (double)sys_time_per_dsp_tick, (double)TIMEUNITPERSEC, (double) sys_schedblocksize, (double)sys_dacsr);
392 #endif
396 /* take the scheduler forward one DSP tick, also handling clock timeouts */
397 #ifndef ROCKBOX
398 static
399 #endif
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);
410 if (!countdown--)
412 countdown = 5000;
413 #ifndef ROCKBOX
414 sys_pollgui();
415 #endif
417 if (sys_quit)
418 return;
420 sys_time = next_sys_time;
421 dsp_tick();
422 sched_diddsp++;
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);
439 #ifndef ROCKBOX
440 int m_scheduler_pda( void)
442 int idlecount = 0;
444 sys_time_per_dsp_tick = (TIMEUNITPERSEC) *
445 ((double)sys_schedblocksize) / sys_dacsr;
448 sys_clearhist();
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;
456 sys_initmidiqueue();
458 while (!sys_quit)
461 int didsomething = 0;
463 int timeforward;
465 sys_addhist(0);
467 waitfortick:
468 if (sched_usedacs)
470 timeforward = sys_send_dacs();
471 sys_pollgui();
473 else {
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);
485 return 0;
489 int m_scheduler( void)
491 int idlecount = 0;
492 sys_time_per_dsp_tick = (TIMEUNITPERSEC) *
493 ((double)sys_schedblocksize) / sys_dacsr;
495 #ifdef THREAD_LOCKING
496 /* T.Grill - lock mutex */
497 sys_lock();
498 #endif
500 sys_clearhist();
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;
507 sys_initmidiqueue();
508 while (!sys_quit)
510 int didsomething = 0;
511 int timeforward;
513 sys_addhist(0);
514 waitfortick:
515 if (sched_usedacs)
517 timeforward = sys_send_dacs();
519 /* if dacs remain "idle" for 1 sec, they're hung up. */
520 if (timeforward != 0)
521 idlecount = 0;
522 else
524 idlecount++;
525 if (!(idlecount & 31))
527 static t_time idletime;
528 /* on 32nd idle, start a clock watch; every
529 32 ensuing idles, check it */
530 if (idlecount == 32)
531 idletime = sys_getrealtime();
532 else if (sys_getrealtime() - idletime > 1.)
534 post("audio I/O stuck... closing audio\n");
535 sys_close_audio();
536 sched_set_using_dacs(0);
537 goto waitfortick;
542 else
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);
550 sys_addhist(1);
551 if (timeforward != SENDDACS_NO)
552 sched_tick(sys_time + sys_time_per_dsp_tick);
553 if (timeforward == SENDDACS_YES)
554 didsomething = 1;
556 sys_addhist(2);
557 // sys_pollmidiqueue();
558 if (sys_pollgui())
560 if (!didsomething)
561 sched_didpoll++;
562 didsomething = 1;
564 sys_addhist(3);
565 /* test for idle; if so, do graphics updates. */
566 if (!didsomething)
568 sched_pollformeters();
569 sys_reportidle();
571 #ifdef THREAD_LOCKING
572 /* T.Grill - enter idle phase -> unlock thread lock */
573 sys_unlock();
574 #endif
575 if (timeforward != SENDDACS_SLEPT)
576 sys_microsleep(sys_sleepgrain);
577 #ifdef THREAD_LOCKING
578 /* T.Grill - leave idle phase -> lock thread lock */
579 sys_lock();
580 #endif
582 sys_addhist(5);
583 sched_didnothing++;
588 #ifdef THREAD_LOCKING
589 /* T.Grill - done */
590 sys_unlock();
591 #endif
593 return (0);
597 /* ------------ thread locking ------------------- */
598 /* added by Thomas Grill */
600 #ifdef THREAD_LOCKING
601 static pthread_mutex_t sys_mutex = PTHREAD_MUTEX_INITIALIZER;
603 void sys_lock(void)
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);
618 #else
620 void sys_lock(void) {}
621 void sys_unlock(void) {}
622 int sys_trylock(void) { return 0; }
624 #endif
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
633 void sys_exit(void)
635 sys_quit = 1;
638 #endif /* ROCKBOX */