From 57045df5fe3ec557e57dc7434ac1a07b5521bffc Mon Sep 17 00:00:00 2001 From: Guenter Geiger Date: Mon, 29 Jul 2002 17:06:19 +0000 Subject: This commit was generated by cvs2svn to compensate for changes in r58, which included commits to RCS files with non-trunk default branches. svn path=/trunk/; revision=59 --- pd/src/m_sched.c | 462 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 462 insertions(+) create mode 100644 pd/src/m_sched.c (limited to 'pd/src/m_sched.c') diff --git a/pd/src/m_sched.c b/pd/src/m_sched.c new file mode 100644 index 00000000..514e6f8b --- /dev/null +++ b/pd/src/m_sched.c @@ -0,0 +1,462 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* scheduling stuff */ + +#include "m_imp.h" + + /* LATER consider making this variable. It's now the LCM of all sample + rates we expect to see: 32000, 44100, 48000, 88200, 96000. */ +#define TIMEUNITPERSEC (32.*441000.) + +static int sys_quit; +static double sys_time; +static double sys_time_per_dsp_tick; +static double sys_time_per_msec; + +int sys_usecsincelastsleep(void); +int sys_sleepgrain; + +typedef void (*t_clockmethod)(void *client); + +struct _clock +{ + double c_settime; + void *c_owner; + t_clockmethod c_fn; + struct _clock *c_next; +}; + +t_clock *clock_setlist; + +#ifdef UNIX +#include +#endif + +t_clock *clock_new(void *owner, t_method fn) +{ + t_clock *x = (t_clock *)getbytes(sizeof *x); + x->c_settime = -1; + x->c_owner = owner; + x->c_fn = (t_clockmethod)fn; + x->c_next = 0; + return (x); +} + +void clock_unset(t_clock *x) +{ + if (x->c_settime >= 0) + { + if (x == clock_setlist) clock_setlist = x->c_next; + else + { + t_clock *x2 = clock_setlist; + while (x2->c_next != x) x2 = x2->c_next; + x2->c_next = x->c_next; + } + x->c_settime = -1; + } +} + + /* set the clock to call back at an absolute system time */ +void clock_set(t_clock *x, double setticks) +{ + if (setticks < sys_time) setticks = sys_time; + clock_unset(x); + x->c_settime = setticks; + if (clock_setlist && clock_setlist->c_settime <= setticks) + { + t_clock *cbefore, *cafter; + for (cbefore = clock_setlist, cafter = clock_setlist->c_next; + cbefore; cbefore = cafter, cafter = cbefore->c_next) + { + if (!cafter || cafter->c_settime > setticks) + { + cbefore->c_next = x; + x->c_next = cafter; + return; + } + } + } + else x->c_next = clock_setlist, clock_setlist = x; +} + + /* set the clock to call back after a delay in msec */ +void clock_delay(t_clock *x, double delaytime) +{ + clock_set(x, sys_time + sys_time_per_msec * delaytime); +} + + /* get current logical time. We don't specify what units this is in; + use clock_gettimesince() to measure intervals from time of this call. + This was previously, incorrectly named "clock_getsystime"; the old + name is aliased to the new one in m_pd.h. */ +double clock_getlogicaltime( void) +{ + return (sys_time); +} + /* OBSOLETE NAME */ +double clock_getsystime( void) { return (sys_time); } + + /* elapsed time in milliseconds since the given system time */ +double clock_gettimesince(double prevsystime) +{ + return ((sys_time - prevsystime)/sys_time_per_msec); +} + + /* what value the system clock will have after a delay */ +double clock_getsystimeafter(double delaytime) +{ + return (sys_time + sys_time_per_msec * delaytime); +} + +void clock_free(t_clock *x) +{ + clock_unset(x); + freebytes(x, sizeof *x); +} + +/* the following routines maintain a real-execution-time histogram of the +various phases of real-time execution. */ + +static int sys_bin[] = {0, 2, 5, 10, 20, 30, 50, 100, 1000}; +#define NBIN (sizeof(sys_bin)/sizeof(*sys_bin)) +#define NHIST 10 +static int sys_histogram[NHIST][NBIN]; +static double sys_histtime; +static int sched_diddsp, sched_didmidi, sched_didpoll, sched_didnothing; + +static void sys_clearhist( void) +{ + unsigned int i, j; + for (i = 0; i < NHIST; i++) + for (j = 0; j < NBIN; j++) sys_histogram[i][j] = 0; + sys_histtime = sys_getrealtime(); + sched_diddsp = sched_didmidi = sched_didpoll = sched_didnothing = 0; +} + +void sys_printhist( void) +{ + unsigned int i, j; + for (i = 0; i < NHIST; i++) + { + int doit = 0; + for (j = 0; j < NBIN; j++) if (sys_histogram[i][j]) doit = 1; + if (doit) + { + post("%2d %8d %8d %8d %8d %8d %8d %8d %8d", i, + sys_histogram[i][0], + sys_histogram[i][1], + sys_histogram[i][2], + sys_histogram[i][3], + sys_histogram[i][4], + sys_histogram[i][5], + sys_histogram[i][6], + sys_histogram[i][7]); + } + } + post("dsp %d, midi %d, poll %d, nothing %d", + sched_diddsp, sched_didmidi, sched_didpoll, sched_didnothing); +} + +static int sys_histphase; + +int sys_addhist(int phase) +{ + int i, j, phasewas = sys_histphase; + double newtime = sys_getrealtime(); + int msec = (newtime - sys_histtime) * 1000.; + for (j = NBIN-1; j >= 0; j--) + { + if (msec >= sys_bin[j]) + { + sys_histogram[phasewas][j]++; + break; + } + } + sys_histtime = newtime; + sys_histphase = phase; + return (phasewas); +} + +#define NRESYNC 20 + +typedef struct _resync +{ + int r_ntick; + int r_error; +} t_resync; + +static int oss_resyncphase = 0; +static int oss_nresync = 0; +static t_resync oss_resync[NRESYNC]; + +#ifdef __linux__ +void linux_audiostatus(void); +#endif + +static char *(oss_errornames[]) = { +"unknown", +"ADC blocked", +"DAC blocked", +"A/D/A sync", +"data late" +}; + +void glob_audiostatus(void) +{ + int dev, nresync, nresyncphase, i; +#ifdef __linux__ + linux_audiostatus(); +#endif + nresync = (oss_nresync >= NRESYNC ? NRESYNC : oss_nresync); + nresyncphase = oss_resyncphase - 1; + post("audio I/O error history:"); + post("seconds ago\terror type"); + for (i = 0; i < nresync; i++) + { + int errtype; + if (nresyncphase < 0) + nresyncphase += NRESYNC; + errtype = oss_resync[nresyncphase].r_error; + if (errtype < 0 || errtype > 4) + errtype = 0; + + post("%9.2f\t%s", + (sched_diddsp - oss_resync[nresyncphase].r_ntick) + * ((double)DACBLKSIZE) / sys_dacsr, + oss_errornames[errtype]); + nresyncphase--; + } +} + +static int sched_diored; +static int sched_dioredtime; +static int sched_meterson; + +void sys_log_error(int type) +{ + oss_resync[oss_resyncphase].r_ntick = sched_diddsp; + oss_resync[oss_resyncphase].r_error = type; + oss_nresync++; + if (++oss_resyncphase == NRESYNC) oss_resyncphase = 0; + if (type != ERR_NOTHING && !sched_diored) + { + sys_vgui("pdtk_pd_dio 1\n"); + sched_diored = 1; + } + sched_dioredtime = + sched_diddsp + (int)(sys_dacsr /(double)DACBLKSIZE); +} + +static int sched_lastinclip, sched_lastoutclip, + sched_lastindb, sched_lastoutdb; + +void glob_ping(t_pd *dummy); + +static void sched_pollformeters( void) +{ + int inclip, outclip, indb, outdb; + static int sched_nextmeterpolltime, sched_nextpingtime; + + /* if there's no GUI but we're running in "realtime", here is + where we arrange to ping the watchdog every 2 seconds. */ +#ifdef UNIX + if (sys_nogui && sys_hipriority && (sched_diddsp - sched_nextpingtime > 0)) + { + glob_ping(0); + /* ping every 2 seconds */ + sched_nextpingtime = sched_diddsp + + 2 * (int)(sys_dacsr /(double)DACBLKSIZE); + } +#endif + + if (sched_diddsp - sched_nextmeterpolltime < 0) + return; + if (sched_diored && (sched_diddsp - sched_dioredtime > 0)) + { + sys_vgui("pdtk_pd_dio 0\n"); + sched_diored = 0; + } + if (sched_meterson) + { + float inmax, outmax; + sys_getmeters(&inmax, &outmax); + indb = 0.5 + rmstodb(inmax); + outdb = 0.5 + rmstodb(outmax); + inclip = (inmax > 0.999); + outclip = (outmax >= 1.0); + } + else + { + indb = outdb = 0; + inclip = outclip = 0; + } + if (inclip != sched_lastinclip || outclip != sched_lastoutclip + || indb != sched_lastindb || outdb != sched_lastoutdb) + { + sys_vgui("pdtk_pd_meters %d %d %d %d\n", indb, outdb, inclip, outclip); + sched_lastinclip = inclip; + sched_lastoutclip = outclip; + sched_lastindb = indb; + sched_lastoutdb = outdb; + } + sched_nextmeterpolltime = + sched_diddsp + (int)(sys_dacsr /(double)DACBLKSIZE); +} + +void glob_meters(void *dummy, float f) +{ + if (f == 0) + sys_getmeters(0, 0); + sched_meterson = (f != 0); + sched_lastinclip = sched_lastoutclip = sched_lastindb = sched_lastoutdb = + -1; +} + +#if 1 +void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv) +{ + if (argc) sys_clearhist(); + else sys_printhist(); +} +#endif + +void dsp_tick(void); + +static int m_nodacs = 0; + + /* this must be called earlier than any patches are loaded */ +void m_schedsetsr( void) +{ + sys_time_per_dsp_tick = + (TIMEUNITPERSEC) * ((double)DACBLKSIZE) / sys_dacsr; + sys_time_per_msec = + TIMEUNITPERSEC / 1000.; +} + +/* +Here is Pd's "main loop." This routine dispatches clock timeouts and DSP +"ticks" deterministically, and polls for input from MIDI and the GUI. If +we're left idle we also poll for graphics updates; but these are considered +lower priority than the rest. + +The time source is normally the audio I/O subsystem via the "sys_send_dacs()" +call. This call returns true if samples were transferred; false means that +the audio I/O system is still bussy with previous transfers. +The sys_send_dacs call is OS dependent and is variously implemented in +s_linux.c, s_nt.c, and s_sgi.c. +*/ + +void sys_pollmidiqueue( void); +void sys_initmidiqueue( void); + +int m_scheduler(int nodacs) +{ + int lasttimeforward = SENDDACS_YES; + int idlecount = 0; + double lastdactime = 0; + sys_clearhist(); + m_nodacs = nodacs; + if (sys_sleepgrain < 1000) + sys_sleepgrain = (sys_schedadvance >= 4000? + (sys_schedadvance >> 2) : 1000); + sys_initmidiqueue(); + while (1) + { + int didsomething = 0; + int timeforward; + + sys_addhist(0); + if (m_nodacs) + { + double elapsed = sys_getrealtime() - lastdactime; + static double next = 0; + if (elapsed > next) + { + timeforward = SENDDACS_YES; + next += (double)DACBLKSIZE / sys_dacsr; + } + else timeforward = SENDDACS_NO; + } + else + { + timeforward = sys_send_dacs(); + + /* if dacs remain "idle" for 1 sec, they're hung up. */ + if (timeforward != 0) idlecount = 0; + else + { + idlecount++; + if (!(idlecount & 31)) + { + static double idletime; + /* on 32nd idle, start a clock watch; every + 32 ensuing idles, check it */ + if (idlecount == 32) + idletime = sys_getrealtime(); + else if (sys_getrealtime() - idletime > 1.) + { + post("audio I/O stuck... closing audio\n"); + m_nodacs = 1; + sys_close_audio(); + lastdactime = sys_getrealtime(); + } + } + } + } + sys_setmiditimediff(0, 1e-6 * sys_schedadvance); + lasttimeforward = timeforward; + sys_addhist(1); + if (timeforward != SENDDACS_NO) + { + /* time has moved forward. Check MIDI and clocks */ + + double next_sys_time = sys_time + sys_time_per_dsp_tick; + int countdown = 5000; + while (clock_setlist && clock_setlist->c_settime < next_sys_time) + { + t_clock *c = clock_setlist; + sys_time = c->c_settime; + clock_unset(clock_setlist); + outlet_setstacklim(); + (*c->c_fn)(c->c_owner); + if (!countdown--) + { + countdown = 5000; + sys_pollgui(); + } + } + sys_time = next_sys_time; + if (sys_quit) break; + dsp_tick(); + if (timeforward != SENDDACS_SLEPT) + didsomething = 1; + sched_diddsp++; + } + + sys_addhist(2); + sys_pollmidiqueue(); + if (sys_pollgui()) + { + if (!didsomething) + sched_didpoll++; + didsomething = 1; + } + sys_addhist(3); + /* test for idle; if so, do graphics updates. */ + if (!didsomething) + { + sched_pollformeters(); + sys_reportidle(); + if (timeforward != SENDDACS_SLEPT) + sys_microsleep(sys_sleepgrain); + sys_addhist(5); + sched_didnothing++; + } + } + return (0); +} + + -- cgit v1.2.1