summaryrefslogtreecommitdiff
path: root/apps/plugins/pdbox/PDa/src/m_sched.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/pdbox/PDa/src/m_sched.c')
-rw-r--r--apps/plugins/pdbox/PDa/src/m_sched.c1162
1 files changed, 1162 insertions, 0 deletions
diff --git a/apps/plugins/pdbox/PDa/src/m_sched.c b/apps/plugins/pdbox/PDa/src/m_sched.c
new file mode 100644
index 0000000000..f40f0ff270
--- /dev/null
+++ b/apps/plugins/pdbox/PDa/src/m_sched.c
@@ -0,0 +1,1162 @@
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. */
4
5/* scheduling stuff */
6
7#include "m_pd.h"
8#include "m_imp.h"
9#include "s_stuff.h"
10
11 /* LATER consider making this variable. It's now the LCM of all sample
12 rates we expect to see: 32000, 44100, 48000, 88200, 96000. */
13#define TIMEUNITPERSEC (32.*441000.)
14
15
16/* T.Grill - enable PD global thread locking - sys_lock, sys_unlock, sys_trylock functions */
17#define THREAD_LOCKING
18#include "pthread.h"
19
20
21static int sys_quit;
22static t_time sys_time;
23static t_time sys_time_per_msec = TIMEUNITPERSEC / 1000.;
24
25int sys_schedblocksize = DEFDACBLKSIZE;
26int sys_usecsincelastsleep(void);
27int sys_sleepgrain;
28
29typedef void (*t_clockmethod)(void *client);
30
31struct _clock
32{
33 t_time c_settime;
34 void *c_owner;
35 t_clockmethod c_fn;
36 struct _clock *c_next;
37};
38
39t_clock *clock_setlist;
40
41#ifdef UNIX
42#include <unistd.h>
43#endif
44
45t_clock *clock_new(void *owner, t_method fn)
46{
47 t_clock *x = (t_clock *)getbytes(sizeof *x);
48 x->c_settime = -1;
49 x->c_owner = owner;
50 x->c_fn = (t_clockmethod)fn;
51 x->c_next = 0;
52 return (x);
53}
54
55void clock_unset(t_clock *x)
56{
57 if (x->c_settime >= 0)
58 {
59 if (x == clock_setlist) clock_setlist = x->c_next;
60 else
61 {
62 t_clock *x2 = clock_setlist;
63 while (x2->c_next != x) x2 = x2->c_next;
64 x2->c_next = x->c_next;
65 }
66 x->c_settime = -1;
67 }
68}
69
70 /* set the clock to call back at an absolute system time */
71void clock_set(t_clock *x, t_time setticks)
72{
73 if (setticks < sys_time) setticks = sys_time;
74 clock_unset(x);
75 x->c_settime = setticks;
76 if (clock_setlist && clock_setlist->c_settime <= setticks)
77 {
78 t_clock *cbefore, *cafter;
79 for (cbefore = clock_setlist, cafter = clock_setlist->c_next;
80 cbefore; cbefore = cafter, cafter = cbefore->c_next)
81 {
82 if (!cafter || cafter->c_settime > setticks)
83 {
84 cbefore->c_next = x;
85 x->c_next = cafter;
86 return;
87 }
88 }
89 }
90 else x->c_next = clock_setlist, clock_setlist = x;
91}
92
93 /* set the clock to call back after a delay in msec */
94void clock_delay(t_clock *x, t_time delaytime)
95{
96 clock_set(x, sys_time + sys_time_per_msec * delaytime);
97}
98
99 /* get current logical time. We don't specify what units this is in;
100 use clock_gettimesince() to measure intervals from time of this call.
101 This was previously, incorrectly named "clock_getsystime"; the old
102 name is aliased to the new one in m_pd.h. */
103t_time clock_getlogicaltime( void)
104{
105 return (sys_time);
106}
107 /* OBSOLETE NAME */
108t_time clock_getsystime( void) { return (sys_time); }
109
110 /* elapsed time in milliseconds since the given system time */
111t_time clock_gettimesince(t_time prevsystime)
112{
113 return ((sys_time - prevsystime)/sys_time_per_msec);
114}
115
116 /* what value the system clock will have after a delay */
117t_time clock_getsystimeafter(t_time delaytime)
118{
119 return (sys_time + sys_time_per_msec * delaytime);
120}
121
122void clock_free(t_clock *x)
123{
124 clock_unset(x);
125 freebytes(x, sizeof *x);
126}
127
128
129/* the following routines maintain a real-execution-time histogram of the
130various phases of real-time execution. */
131
132static int sys_bin[] = {0, 2, 5, 10, 20, 30, 50, 100, 1000};
133#define NBIN (sizeof(sys_bin)/sizeof(*sys_bin))
134#define NHIST 10
135static int sys_histogram[NHIST][NBIN];
136static t_time sys_histtime;
137static int sched_diddsp, sched_didpoll, sched_didnothing;
138
139static void sys_clearhist( void)
140{
141 unsigned int i, j;
142 for (i = 0; i < NHIST; i++)
143 for (j = 0; j < NBIN; j++) sys_histogram[i][j] = 0;
144 sys_histtime = sys_getrealtime();
145 sched_diddsp = sched_didpoll = sched_didnothing = 0;
146}
147
148void sys_printhist( void)
149{
150 unsigned int i, j;
151 for (i = 0; i < NHIST; i++)
152 {
153 int doit = 0;
154 for (j = 0; j < NBIN; j++) if (sys_histogram[i][j]) doit = 1;
155 if (doit)
156 {
157 post("%2d %8d %8d %8d %8d %8d %8d %8d %8d", i,
158 sys_histogram[i][0],
159 sys_histogram[i][1],
160 sys_histogram[i][2],
161 sys_histogram[i][3],
162 sys_histogram[i][4],
163 sys_histogram[i][5],
164 sys_histogram[i][6],
165 sys_histogram[i][7]);
166 }
167 }
168 post("dsp %d, pollgui %d, nothing %d",
169 sched_diddsp, sched_didpoll, sched_didnothing);
170}
171
172static int sys_histphase;
173
174int sys_addhist(int phase)
175{
176#ifndef FIXEDPOINT
177 int i, j, phasewas = sys_histphase;
178 t_time newtime = sys_getrealtime();
179 int msec = (newtime - sys_histtime) * 1000.;
180 for (j = NBIN-1; j >= 0; j--)
181 {
182 if (msec >= sys_bin[j])
183 {
184 sys_histogram[phasewas][j]++;
185 break;
186 }
187 }
188 sys_histtime = newtime;
189 sys_histphase = phase;
190 return (phasewas);
191#else
192 return 0;
193#endif
194}
195
196#define NRESYNC 20
197
198typedef struct _resync
199{
200 int r_ntick;
201 int r_error;
202} t_resync;
203
204static int oss_resyncphase = 0;
205static int oss_nresync = 0;
206static t_resync oss_resync[NRESYNC];
207
208
209static char *(oss_errornames[]) = {
210"unknown",
211"ADC blocked",
212"DAC blocked",
213"A/D/A sync",
214"data late"
215};
216
217void glob_audiostatus(void)
218{
219 int dev, nresync, nresyncphase, i;
220 nresync = (oss_nresync >= NRESYNC ? NRESYNC : oss_nresync);
221 nresyncphase = oss_resyncphase - 1;
222 post("audio I/O error history:");
223 post("seconds ago\terror type");
224 for (i = 0; i < nresync; i++)
225 {
226 int errtype;
227 if (nresyncphase < 0)
228 nresyncphase += NRESYNC;
229 errtype = oss_resync[nresyncphase].r_error;
230 if (errtype < 0 || errtype > 4)
231 errtype = 0;
232
233 post("%9.2f\t%s",
234 (sched_diddsp - oss_resync[nresyncphase].r_ntick)
235 * ((double)sys_schedblocksize) / sys_dacsr,
236 oss_errornames[errtype]);
237 nresyncphase--;
238 }
239}
240
241static int sched_diored;
242static int sched_dioredtime;
243static int sched_meterson;
244
245void sys_log_error(int type)
246{
247 oss_resync[oss_resyncphase].r_ntick = sched_diddsp;
248 oss_resync[oss_resyncphase].r_error = type;
249 oss_nresync++;
250 if (++oss_resyncphase == NRESYNC) oss_resyncphase = 0;
251 if (type != ERR_NOTHING && !sched_diored &&
252 (sched_diddsp >= sched_dioredtime))
253 {
254 sys_vgui("pdtk_pd_dio 1\n");
255 sched_diored = 1;
256 }
257 sched_dioredtime =
258 sched_diddsp + (int)(sys_dacsr /(double)sys_schedblocksize);
259}
260
261static int sched_lastinclip, sched_lastoutclip,
262 sched_lastindb, sched_lastoutdb;
263
264void glob_ping(t_pd *dummy);
265
266static void sched_pollformeters( void)
267{
268 int inclip, outclip, indb, outdb;
269 static int sched_nextmeterpolltime, sched_nextpingtime;
270
271 /* if there's no GUI but we're running in "realtime", here is
272 where we arrange to ping the watchdog every 2 seconds. */
273#ifdef __linux__
274 if (sys_nogui && sys_hipriority && (sched_diddsp - sched_nextpingtime > 0))
275 {
276 glob_ping(0);
277 /* ping every 2 seconds */
278 sched_nextpingtime = sched_diddsp +
279 (2* sys_dacsr) /(int)sys_schedblocksize;
280 }
281#endif
282
283 if (sched_diddsp - sched_nextmeterpolltime < 0)
284 return;
285 if (sched_diored && (sched_diddsp - sched_dioredtime > 0))
286 {
287 sys_vgui("pdtk_pd_dio 0\n");
288 sched_diored = 0;
289 }
290 if (sched_meterson)
291 {
292 float inmax, outmax;
293 sys_getmeters(&inmax, &outmax);
294 indb = 0.5 + rmstodb(inmax);
295 outdb = 0.5 + rmstodb(outmax);
296 inclip = (inmax > 0.999);
297 outclip = (outmax >= 1.0);
298 }
299 else
300 {
301 indb = outdb = 0;
302 inclip = outclip = 0;
303 }
304 if (inclip != sched_lastinclip || outclip != sched_lastoutclip
305 || indb != sched_lastindb || outdb != sched_lastoutdb)
306 {
307 sys_vgui("pdtk_pd_meters %d %d %d %d\n", indb, outdb, inclip, outclip);
308 sched_lastinclip = inclip;
309 sched_lastoutclip = outclip;
310 sched_lastindb = indb;
311 sched_lastoutdb = outdb;
312 }
313 sched_nextmeterpolltime =
314 sched_diddsp + (int)(sys_dacsr /(double)sys_schedblocksize);
315}
316
317void glob_meters(void *dummy, float f)
318{
319 if (f == 0)
320 sys_getmeters(0, 0);
321 sched_meterson = (f != 0);
322 sched_lastinclip = sched_lastoutclip = sched_lastindb = sched_lastoutdb =
323 -1;
324}
325
326#if 0
327void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv)
328{
329 if (argc) sys_clearhist();
330 else sys_printhist();
331}
332#endif
333
334void dsp_tick(void);
335
336static int sched_usedacs = 1;
337static t_time sched_referencerealtime, sched_referencelogicaltime;
338static t_time sys_time_per_dsp_tick;
339
340void sched_set_using_dacs(int flag)
341{
342 sched_usedacs = flag;
343 if (!flag)
344 {
345 sched_referencerealtime = sys_getrealtime();
346 sched_referencelogicaltime = clock_getlogicaltime();
347 post("schedsetuding");
348 }
349 sys_time_per_dsp_tick = (TIMEUNITPERSEC) *
350 ((double)sys_schedblocksize) / sys_dacsr;
351}
352
353 /* take the scheduler forward one DSP tick, also handling clock timeouts */
354static void sched_tick(t_time next_sys_time)
355{
356 int countdown = 5000;
357 while (clock_setlist && clock_setlist->c_settime < next_sys_time)
358 {
359 t_clock *c = clock_setlist;
360 sys_time = c->c_settime;
361 clock_unset(clock_setlist);
362 outlet_setstacklim();
363 (*c->c_fn)(c->c_owner);
364 if (!countdown--)
365 {
366 countdown = 5000;
367 sys_pollgui();
368 }
369 if (sys_quit)
370 return;
371 }
372 sys_time = next_sys_time;
373 dsp_tick();
374 sched_diddsp++;
375}
376
377/*
378Here is Pd's "main loop." This routine dispatches clock timeouts and DSP
379"ticks" deterministically, and polls for input from MIDI and the GUI. If
380we're left idle we also poll for graphics updates; but these are considered
381lower priority than the rest.
382
383The time source is normally the audio I/O subsystem via the "sys_send_dacs()"
384call. This call returns true if samples were transferred; false means that
385the audio I/O system is still busy with previous transfers.
386*/
387
388void sys_pollmidiqueue( void);
389void sys_initmidiqueue( void);
390
391int m_scheduler_pda( void)
392{
393 int idlecount = 0;
394 sys_time_per_dsp_tick = (TIMEUNITPERSEC) *
395 ((double)sys_schedblocksize) / sys_dacsr;
396
397
398 sys_clearhist();
399 if (sys_sleepgrain < 1000)
400 sys_sleepgrain = sys_schedadvance/4;
401 if (sys_sleepgrain < 100)
402 sys_sleepgrain = 100;
403 else if (sys_sleepgrain > 5000)
404 sys_sleepgrain = 5000;
405 sys_initmidiqueue();
406 while (!sys_quit)
407 {
408 int didsomething = 0;
409 int timeforward;
410
411 sys_addhist(0);
412 waitfortick:
413 if (sched_usedacs)
414 {
415 timeforward = sys_send_dacs();
416 sys_pollgui();
417 }
418 else {
419 if ((sys_getrealtime() - sched_referencerealtime)
420 > (t_time)clock_gettimesince(sched_referencelogicaltime)*1000)
421 timeforward = SENDDACS_YES;
422 else timeforward = SENDDACS_NO;
423 if (timeforward == SENDDACS_YES)
424 sys_microsleep(sys_sleepgrain);
425 }
426 if (timeforward != SENDDACS_NO) {
427 sched_tick(sys_time + sys_time_per_dsp_tick);
428 }
429 }
430 return 0;
431}
432
433
434int m_scheduler( void)
435{
436 int idlecount = 0;
437 sys_time_per_dsp_tick = (TIMEUNITPERSEC) *
438 ((double)sys_schedblocksize) / sys_dacsr;
439
440#ifdef THREAD_LOCKING
441 /* T.Grill - lock mutex */
442 sys_lock();
443#endif
444
445 sys_clearhist();
446 if (sys_sleepgrain < 1000)
447 sys_sleepgrain = sys_schedadvance/4;
448 if (sys_sleepgrain < 100)
449 sys_sleepgrain = 100;
450 else if (sys_sleepgrain > 5000)
451 sys_sleepgrain = 5000;
452 sys_initmidiqueue();
453 while (!sys_quit)
454 {
455 int didsomething = 0;
456 int timeforward;
457
458 sys_addhist(0);
459 waitfortick:
460 if (sched_usedacs)
461 {
462 timeforward = sys_send_dacs();
463
464 /* if dacs remain "idle" for 1 sec, they're hung up. */
465 if (timeforward != 0)
466 idlecount = 0;
467 else
468 {
469 idlecount++;
470 if (!(idlecount & 31))
471 {
472 static t_time idletime;
473 /* on 32nd idle, start a clock watch; every
474 32 ensuing idles, check it */
475 if (idlecount == 32)
476 idletime = sys_getrealtime();
477 else if (sys_getrealtime() - idletime > 1.)
478 {
479 post("audio I/O stuck... closing audio\n");
480 sys_close_audio();
481 sched_set_using_dacs(0);
482 goto waitfortick;
483 }
484 }
485 }
486 }
487 else
488 {
489 if (1000. * (sys_getrealtime() - sched_referencerealtime)
490 > clock_gettimesince(sched_referencelogicaltime))
491 timeforward = SENDDACS_YES;
492 else timeforward = SENDDACS_NO;
493 }
494 sys_setmiditimediff(0, 1e-6 * sys_schedadvance);
495 sys_addhist(1);
496 if (timeforward != SENDDACS_NO)
497 sched_tick(sys_time + sys_time_per_dsp_tick);
498 if (timeforward == SENDDACS_YES)
499 didsomething = 1;
500
501 sys_addhist(2);
502 // sys_pollmidiqueue();
503 if (sys_pollgui())
504 {
505 if (!didsomething)
506 sched_didpoll++;
507 didsomething = 1;
508 }
509 sys_addhist(3);
510 /* test for idle; if so, do graphics updates. */
511 if (!didsomething)
512 {
513 sched_pollformeters();
514 sys_reportidle();
515
516#ifdef THREAD_LOCKING
517 /* T.Grill - enter idle phase -> unlock thread lock */
518 sys_unlock();
519#endif
520 if (timeforward != SENDDACS_SLEPT)
521 sys_microsleep(sys_sleepgrain);
522#ifdef THREAD_LOCKING
523 /* T.Grill - leave idle phase -> lock thread lock */
524 sys_lock();
525#endif
526
527 sys_addhist(5);
528 sched_didnothing++;
529
530 }
531 }
532
533#ifdef THREAD_LOCKING
534 /* T.Grill - done */
535 sys_unlock();
536#endif
537
538 return (0);
539}
540
541
542/* ------------ thread locking ------------------- */
543/* added by Thomas Grill */
544
545#ifdef THREAD_LOCKING
546static pthread_mutex_t sys_mutex = PTHREAD_MUTEX_INITIALIZER;
547
548void sys_lock(void)
549{
550 pthread_mutex_lock(&sys_mutex);
551}
552
553void sys_unlock(void)
554{
555 pthread_mutex_unlock(&sys_mutex);
556}
557
558int sys_trylock(void)
559{
560 return pthread_mutex_trylock(&sys_mutex);
561}
562
563#else
564
565void sys_lock(void) {}
566void sys_unlock(void) {}
567int sys_trylock(void) {}
568
569#endif
570
571
572/* ------------ soft quit ------------------- */
573/* added by Thomas Grill -
574 just set the quit flag for the scheduler loop
575 this is useful for applications using the PD shared library to signal the scheduler to terminate
576*/
577
578void sys_exit(void)
579{
580 sys_quit = 1;
581}
582/* Copyright (c) 1997-1999 Miller Puckette.
583* For information on usage and redistribution, and for a DISCLAIMER OF ALL
584* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
585
586/* scheduling stuff */
587
588#include "m_pd.h"
589#include "m_imp.h"
590#include "s_stuff.h"
591
592 /* LATER consider making this variable. It's now the LCM of all sample
593 rates we expect to see: 32000, 44100, 48000, 88200, 96000. */
594#define TIMEUNITPERSEC (32.*441000.)
595
596
597/* T.Grill - enable PD global thread locking - sys_lock, sys_unlock, sys_trylock functions */
598#define THREAD_LOCKING
599#include "pthread.h"
600
601
602static int sys_quit;
603static t_time sys_time;
604static t_time sys_time_per_msec = TIMEUNITPERSEC / 1000.;
605
606int sys_schedblocksize = DEFDACBLKSIZE;
607int sys_usecsincelastsleep(void);
608int sys_sleepgrain;
609
610typedef void (*t_clockmethod)(void *client);
611
612struct _clock
613{
614 t_time c_settime;
615 void *c_owner;
616 t_clockmethod c_fn;
617 struct _clock *c_next;
618};
619
620t_clock *clock_setlist;
621
622#ifdef UNIX
623#include <unistd.h>
624#endif
625
626t_clock *clock_new(void *owner, t_method fn)
627{
628 t_clock *x = (t_clock *)getbytes(sizeof *x);
629 x->c_settime = -1;
630 x->c_owner = owner;
631 x->c_fn = (t_clockmethod)fn;
632 x->c_next = 0;
633 return (x);
634}
635
636void clock_unset(t_clock *x)
637{
638 if (x->c_settime >= 0)
639 {
640 if (x == clock_setlist) clock_setlist = x->c_next;
641 else
642 {
643 t_clock *x2 = clock_setlist;
644 while (x2->c_next != x) x2 = x2->c_next;
645 x2->c_next = x->c_next;
646 }
647 x->c_settime = -1;
648 }
649}
650
651 /* set the clock to call back at an absolute system time */
652void clock_set(t_clock *x, t_time setticks)
653{
654 if (setticks < sys_time) setticks = sys_time;
655 clock_unset(x);
656 x->c_settime = setticks;
657 if (clock_setlist && clock_setlist->c_settime <= setticks)
658 {
659 t_clock *cbefore, *cafter;
660 for (cbefore = clock_setlist, cafter = clock_setlist->c_next;
661 cbefore; cbefore = cafter, cafter = cbefore->c_next)
662 {
663 if (!cafter || cafter->c_settime > setticks)
664 {
665 cbefore->c_next = x;
666 x->c_next = cafter;
667 return;
668 }
669 }
670 }
671 else x->c_next = clock_setlist, clock_setlist = x;
672}
673
674 /* set the clock to call back after a delay in msec */
675void clock_delay(t_clock *x, t_time delaytime)
676{
677 clock_set(x, sys_time + sys_time_per_msec * delaytime);
678}
679
680 /* get current logical time. We don't specify what units this is in;
681 use clock_gettimesince() to measure intervals from time of this call.
682 This was previously, incorrectly named "clock_getsystime"; the old
683 name is aliased to the new one in m_pd.h. */
684t_time clock_getlogicaltime( void)
685{
686 return (sys_time);
687}
688 /* OBSOLETE NAME */
689t_time clock_getsystime( void) { return (sys_time); }
690
691 /* elapsed time in milliseconds since the given system time */
692t_time clock_gettimesince(t_time prevsystime)
693{
694 return ((sys_time - prevsystime)/sys_time_per_msec);
695}
696
697 /* what value the system clock will have after a delay */
698t_time clock_getsystimeafter(t_time delaytime)
699{
700 return (sys_time + sys_time_per_msec * delaytime);
701}
702
703void clock_free(t_clock *x)
704{
705 clock_unset(x);
706 freebytes(x, sizeof *x);
707}
708
709
710/* the following routines maintain a real-execution-time histogram of the
711various phases of real-time execution. */
712
713static int sys_bin[] = {0, 2, 5, 10, 20, 30, 50, 100, 1000};
714#define NBIN (sizeof(sys_bin)/sizeof(*sys_bin))
715#define NHIST 10
716static int sys_histogram[NHIST][NBIN];
717static t_time sys_histtime;
718static int sched_diddsp, sched_didpoll, sched_didnothing;
719
720static void sys_clearhist( void)
721{
722 unsigned int i, j;
723 for (i = 0; i < NHIST; i++)
724 for (j = 0; j < NBIN; j++) sys_histogram[i][j] = 0;
725 sys_histtime = sys_getrealtime();
726 sched_diddsp = sched_didpoll = sched_didnothing = 0;
727}
728
729void sys_printhist( void)
730{
731 unsigned int i, j;
732 for (i = 0; i < NHIST; i++)
733 {
734 int doit = 0;
735 for (j = 0; j < NBIN; j++) if (sys_histogram[i][j]) doit = 1;
736 if (doit)
737 {
738 post("%2d %8d %8d %8d %8d %8d %8d %8d %8d", i,
739 sys_histogram[i][0],
740 sys_histogram[i][1],
741 sys_histogram[i][2],
742 sys_histogram[i][3],
743 sys_histogram[i][4],
744 sys_histogram[i][5],
745 sys_histogram[i][6],
746 sys_histogram[i][7]);
747 }
748 }
749 post("dsp %d, pollgui %d, nothing %d",
750 sched_diddsp, sched_didpoll, sched_didnothing);
751}
752
753static int sys_histphase;
754
755int sys_addhist(int phase)
756{
757#ifndef FIXEDPOINT
758 int i, j, phasewas = sys_histphase;
759 t_time newtime = sys_getrealtime();
760 int msec = (newtime - sys_histtime) * 1000.;
761 for (j = NBIN-1; j >= 0; j--)
762 {
763 if (msec >= sys_bin[j])
764 {
765 sys_histogram[phasewas][j]++;
766 break;
767 }
768 }
769 sys_histtime = newtime;
770 sys_histphase = phase;
771 return (phasewas);
772#else
773 return 0;
774#endif
775}
776
777#define NRESYNC 20
778
779typedef struct _resync
780{
781 int r_ntick;
782 int r_error;
783} t_resync;
784
785static int oss_resyncphase = 0;
786static int oss_nresync = 0;
787static t_resync oss_resync[NRESYNC];
788
789
790static char *(oss_errornames[]) = {
791"unknown",
792"ADC blocked",
793"DAC blocked",
794"A/D/A sync",
795"data late"
796};
797
798void glob_audiostatus(void)
799{
800 int dev, nresync, nresyncphase, i;
801 nresync = (oss_nresync >= NRESYNC ? NRESYNC : oss_nresync);
802 nresyncphase = oss_resyncphase - 1;
803 post("audio I/O error history:");
804 post("seconds ago\terror type");
805 for (i = 0; i < nresync; i++)
806 {
807 int errtype;
808 if (nresyncphase < 0)
809 nresyncphase += NRESYNC;
810 errtype = oss_resync[nresyncphase].r_error;
811 if (errtype < 0 || errtype > 4)
812 errtype = 0;
813
814 post("%9.2f\t%s",
815 (sched_diddsp - oss_resync[nresyncphase].r_ntick)
816 * ((double)sys_schedblocksize) / sys_dacsr,
817 oss_errornames[errtype]);
818 nresyncphase--;
819 }
820}
821
822static int sched_diored;
823static int sched_dioredtime;
824static int sched_meterson;
825
826void sys_log_error(int type)
827{
828 oss_resync[oss_resyncphase].r_ntick = sched_diddsp;
829 oss_resync[oss_resyncphase].r_error = type;
830 oss_nresync++;
831 if (++oss_resyncphase == NRESYNC) oss_resyncphase = 0;
832 if (type != ERR_NOTHING && !sched_diored &&
833 (sched_diddsp >= sched_dioredtime))
834 {
835 sys_vgui("pdtk_pd_dio 1\n");
836 sched_diored = 1;
837 }
838 sched_dioredtime =
839 sched_diddsp + (int)(sys_dacsr /(double)sys_schedblocksize);
840}
841
842static int sched_lastinclip, sched_lastoutclip,
843 sched_lastindb, sched_lastoutdb;
844
845void glob_ping(t_pd *dummy);
846
847static void sched_pollformeters( void)
848{
849 int inclip, outclip, indb, outdb;
850 static int sched_nextmeterpolltime, sched_nextpingtime;
851
852 /* if there's no GUI but we're running in "realtime", here is
853 where we arrange to ping the watchdog every 2 seconds. */
854#ifdef __linux__
855 if (sys_nogui && sys_hipriority && (sched_diddsp - sched_nextpingtime > 0))
856 {
857 glob_ping(0);
858 /* ping every 2 seconds */
859 sched_nextpingtime = sched_diddsp +
860 (2* sys_dacsr) /(int)sys_schedblocksize;
861 }
862#endif
863
864 if (sched_diddsp - sched_nextmeterpolltime < 0)
865 return;
866 if (sched_diored && (sched_diddsp - sched_dioredtime > 0))
867 {
868 sys_vgui("pdtk_pd_dio 0\n");
869 sched_diored = 0;
870 }
871 if (sched_meterson)
872 {
873 float inmax, outmax;
874 sys_getmeters(&inmax, &outmax);
875 indb = 0.5 + rmstodb(inmax);
876 outdb = 0.5 + rmstodb(outmax);
877 inclip = (inmax > 0.999);
878 outclip = (outmax >= 1.0);
879 }
880 else
881 {
882 indb = outdb = 0;
883 inclip = outclip = 0;
884 }
885 if (inclip != sched_lastinclip || outclip != sched_lastoutclip
886 || indb != sched_lastindb || outdb != sched_lastoutdb)
887 {
888 sys_vgui("pdtk_pd_meters %d %d %d %d\n", indb, outdb, inclip, outclip);
889 sched_lastinclip = inclip;
890 sched_lastoutclip = outclip;
891 sched_lastindb = indb;
892 sched_lastoutdb = outdb;
893 }
894 sched_nextmeterpolltime =
895 sched_diddsp + (int)(sys_dacsr /(double)sys_schedblocksize);
896}
897
898void glob_meters(void *dummy, float f)
899{
900 if (f == 0)
901 sys_getmeters(0, 0);
902 sched_meterson = (f != 0);
903 sched_lastinclip = sched_lastoutclip = sched_lastindb = sched_lastoutdb =
904 -1;
905}
906
907#if 0
908void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv)
909{
910 if (argc) sys_clearhist();
911 else sys_printhist();
912}
913#endif
914
915void dsp_tick(void);
916
917static int sched_usedacs = 1;
918static t_time sched_referencerealtime, sched_referencelogicaltime;
919static t_time sys_time_per_dsp_tick;
920
921void sched_set_using_dacs(int flag)
922{
923 sched_usedacs = flag;
924 if (!flag)
925 {
926 sched_referencerealtime = sys_getrealtime();
927 sched_referencelogicaltime = clock_getlogicaltime();
928 post("schedsetuding");
929 }
930 sys_time_per_dsp_tick = (TIMEUNITPERSEC) *
931 ((double)sys_schedblocksize) / sys_dacsr;
932}
933
934 /* take the scheduler forward one DSP tick, also handling clock timeouts */
935static void sched_tick(t_time next_sys_time)
936{
937 int countdown = 5000;
938 while (clock_setlist && clock_setlist->c_settime < next_sys_time)
939 {
940 t_clock *c = clock_setlist;
941 sys_time = c->c_settime;
942 clock_unset(clock_setlist);
943 outlet_setstacklim();
944 (*c->c_fn)(c->c_owner);
945 if (!countdown--)
946 {
947 countdown = 5000;
948 sys_pollgui();
949 }
950 if (sys_quit)
951 return;
952 }
953 sys_time = next_sys_time;
954 dsp_tick();
955 sched_diddsp++;
956}
957
958/*
959Here is Pd's "main loop." This routine dispatches clock timeouts and DSP
960"ticks" deterministically, and polls for input from MIDI and the GUI. If
961we're left idle we also poll for graphics updates; but these are considered
962lower priority than the rest.
963
964The time source is normally the audio I/O subsystem via the "sys_send_dacs()"
965call. This call returns true if samples were transferred; false means that
966the audio I/O system is still busy with previous transfers.
967*/
968
969void sys_pollmidiqueue( void);
970void sys_initmidiqueue( void);
971
972int m_scheduler_pda( void)
973{
974 int idlecount = 0;
975 sys_time_per_dsp_tick = (TIMEUNITPERSEC) *
976 ((double)sys_schedblocksize) / sys_dacsr;
977
978
979 sys_clearhist();
980 if (sys_sleepgrain < 1000)
981 sys_sleepgrain = sys_schedadvance/4;
982 if (sys_sleepgrain < 100)
983 sys_sleepgrain = 100;
984 else if (sys_sleepgrain > 5000)
985 sys_sleepgrain = 5000;
986 sys_initmidiqueue();
987 while (!sys_quit)
988 {
989 int didsomething = 0;
990 int timeforward;
991
992 sys_addhist(0);
993 waitfortick:
994 if (sched_usedacs)
995 {
996 timeforward = sys_send_dacs();
997 sys_pollgui();
998 }
999 else {
1000 if ((sys_getrealtime() - sched_referencerealtime)
1001 > (t_time)clock_gettimesince(sched_referencelogicaltime)*1000)
1002 timeforward = SENDDACS_YES;
1003 else timeforward = SENDDACS_NO;
1004 if (timeforward == SENDDACS_YES)
1005 sys_microsleep(sys_sleepgrain);
1006 }
1007 if (timeforward != SENDDACS_NO) {
1008 sched_tick(sys_time + sys_time_per_dsp_tick);
1009 }
1010 }
1011 return 0;
1012}
1013
1014
1015int m_scheduler( void)
1016{
1017 int idlecount = 0;
1018 sys_time_per_dsp_tick = (TIMEUNITPERSEC) *
1019 ((double)sys_schedblocksize) / sys_dacsr;
1020
1021#ifdef THREAD_LOCKING
1022 /* T.Grill - lock mutex */
1023 sys_lock();
1024#endif
1025
1026 sys_clearhist();
1027 if (sys_sleepgrain < 1000)
1028 sys_sleepgrain = sys_schedadvance/4;
1029 if (sys_sleepgrain < 100)
1030 sys_sleepgrain = 100;
1031 else if (sys_sleepgrain > 5000)
1032 sys_sleepgrain = 5000;
1033 sys_initmidiqueue();
1034 while (!sys_quit)
1035 {
1036 int didsomething = 0;
1037 int timeforward;
1038
1039 sys_addhist(0);
1040 waitfortick:
1041 if (sched_usedacs)
1042 {
1043 timeforward = sys_send_dacs();
1044
1045 /* if dacs remain "idle" for 1 sec, they're hung up. */
1046 if (timeforward != 0)
1047 idlecount = 0;
1048 else
1049 {
1050 idlecount++;
1051 if (!(idlecount & 31))
1052 {
1053 static t_time idletime;
1054 /* on 32nd idle, start a clock watch; every
1055 32 ensuing idles, check it */
1056 if (idlecount == 32)
1057 idletime = sys_getrealtime();
1058 else if (sys_getrealtime() - idletime > 1.)
1059 {
1060 post("audio I/O stuck... closing audio\n");
1061 sys_close_audio();
1062 sched_set_using_dacs(0);
1063 goto waitfortick;
1064 }
1065 }
1066 }
1067 }
1068 else
1069 {
1070 if (1000. * (sys_getrealtime() - sched_referencerealtime)
1071 > clock_gettimesince(sched_referencelogicaltime))
1072 timeforward = SENDDACS_YES;
1073 else timeforward = SENDDACS_NO;
1074 }
1075 sys_setmiditimediff(0, 1e-6 * sys_schedadvance);
1076 sys_addhist(1);
1077 if (timeforward != SENDDACS_NO)
1078 sched_tick(sys_time + sys_time_per_dsp_tick);
1079 if (timeforward == SENDDACS_YES)
1080 didsomething = 1;
1081
1082 sys_addhist(2);
1083 // sys_pollmidiqueue();
1084 if (sys_pollgui())
1085 {
1086 if (!didsomething)
1087 sched_didpoll++;
1088 didsomething = 1;
1089 }
1090 sys_addhist(3);
1091 /* test for idle; if so, do graphics updates. */
1092 if (!didsomething)
1093 {
1094 sched_pollformeters();
1095 sys_reportidle();
1096
1097#ifdef THREAD_LOCKING
1098 /* T.Grill - enter idle phase -> unlock thread lock */
1099 sys_unlock();
1100#endif
1101 if (timeforward != SENDDACS_SLEPT)
1102 sys_microsleep(sys_sleepgrain);
1103#ifdef THREAD_LOCKING
1104 /* T.Grill - leave idle phase -> lock thread lock */
1105 sys_lock();
1106#endif
1107
1108 sys_addhist(5);
1109 sched_didnothing++;
1110
1111 }
1112 }
1113
1114#ifdef THREAD_LOCKING
1115 /* T.Grill - done */
1116 sys_unlock();
1117#endif
1118
1119 return (0);
1120}
1121
1122
1123/* ------------ thread locking ------------------- */
1124/* added by Thomas Grill */
1125
1126#ifdef THREAD_LOCKING
1127static pthread_mutex_t sys_mutex = PTHREAD_MUTEX_INITIALIZER;
1128
1129void sys_lock(void)
1130{
1131 pthread_mutex_lock(&sys_mutex);
1132}
1133
1134void sys_unlock(void)
1135{
1136 pthread_mutex_unlock(&sys_mutex);
1137}
1138
1139int sys_trylock(void)
1140{
1141 return pthread_mutex_trylock(&sys_mutex);
1142}
1143
1144#else
1145
1146void sys_lock(void) {}
1147void sys_unlock(void) {}
1148int sys_trylock(void) {}
1149
1150#endif
1151
1152
1153/* ------------ soft quit ------------------- */
1154/* added by Thomas Grill -
1155 just set the quit flag for the scheduler loop
1156 this is useful for applications using the PD shared library to signal the scheduler to terminate
1157*/
1158
1159void sys_exit(void)
1160{
1161 sys_quit = 1;
1162}