diff options
author | N.N. <matju@users.sourceforge.net> | 2010-01-05 22:49:36 +0000 |
---|---|---|
committer | N.N. <matju@users.sourceforge.net> | 2010-01-05 22:49:36 +0000 |
commit | 8dbec761cf858ea65900c8a094599857208d8c3a (patch) | |
tree | 3228c023f87f23a354da3b57fdc2afe5b7052032 /desiredata/src/m_sched.c | |
parent | 529e59635598e2d90a7a49f6b4c676f8366109ba (diff) |
svn path=/trunk/; revision=12907
Diffstat (limited to 'desiredata/src/m_sched.c')
-rw-r--r-- | desiredata/src/m_sched.c | 655 |
1 files changed, 0 insertions, 655 deletions
diff --git a/desiredata/src/m_sched.c b/desiredata/src/m_sched.c deleted file mode 100644 index 50b71af9..00000000 --- a/desiredata/src/m_sched.c +++ /dev/null @@ -1,655 +0,0 @@ -/* 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 "desire.h" -#include <stdlib.h> - -/* for timeval */ -#ifdef UNISTD -#include <sys/time.h> -#endif -#ifdef __CYGWIN__ -#include <sys/time.h> -#endif -#ifdef MSW -#include "winsock.h" -#endif - -#include "assert.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.) - -/* T.Grill - enable PD global thread locking - sys_lock, sys_unlock, sys_trylock functions */ -#define THREAD_LOCKING -#include "pthread.h" -#include "time.h" - -static int sys_quit; -double sys_time; -static double sys_time_per_msec = TIMEUNITPERSEC / 1000.; - -/* tb: { */ -int sys_keepsched = 1; /* if 0: change scheduler mode */ -int sys_callbackscheduler = 0; /* if 1: change scheduler to callback based dsp */ -static void run_idle_callbacks(int microsec); -t_fifo * callback_fifo = NULL; -/* tb: }*/ - -int sys_usecsincelastsleep (); -int sys_sleepgrain; - -typedef void (*t_clockmethod)(void *client); - -#ifdef UNISTD -#include <unistd.h> -#endif - -struct _clock { - double settime; - void *owner; - t_clockmethod fn; - struct _clock *next; -}; - -t_clock *clock_setlist; -t_clock *clock_new(void *owner, t_method fn) { - t_clock *x = (t_clock *)malloc(sizeof(*x)); - x->settime = -1; - x->owner = owner; - x->fn = (t_clockmethod)fn; - x->next = 0; - return x; -} - -void clock_unset(t_clock *x) { - if (x->settime >= 0) { - if (x == clock_setlist) clock_setlist = x->next; - else { - t_clock *x2 = clock_setlist; - while (x2->next != x) x2 = x2->next; - x2->next = x->next; - } - x->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->settime = setticks; - if (clock_setlist && clock_setlist->settime <= setticks) { - t_clock *cbefore, *cafter; - for (cbefore = clock_setlist, cafter = clock_setlist->next; cbefore; cbefore = cafter, cafter = cbefore->next) { - if (!cafter || cafter->settime > setticks) { - cbefore->next = x; - x->next = cafter; - return; - } - } - } else x->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 () {return sys_time;} - -/* OBSOLETE NAME */ -double clock_getsystime () {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); - free(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_didpoll, sched_didnothing; - -void sys_clearhist () { - unsigned 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_didpoll = sched_didnothing = 0; -} - -void sys_printhist () { - for (int i=0; i<NHIST; i++) { - int doit = 0; - for (unsigned int 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, pollgui %d, nothing %d", sched_diddsp, sched_didpoll, sched_didnothing); -} - -static int sys_histphase; - -int sys_addhist(int phase) { - int phasewas = sys_histphase; - double newtime = sys_getrealtime(); - int msec = int((newtime-sys_histtime)*1000.); - for (int 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 - -struct t_resync { - int ntick; - int error; -}; - -static int oss_resyncphase = 0; -static int oss_nresync = 0; -static t_resync oss_resync[NRESYNC]; - -static const char *(oss_errornames[]) = { -"unknown", -"ADC blocked", -"DAC blocked", -"A/D/A sync", -"data late", -"xrun", -"sys_lock timeout" -}; - -void glob_audiostatus (void *dummy) { - int nresync, nresyncphase, i; - 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].error; - if (errtype < 0 || errtype > 4) errtype = 0; - post("%9.2f\t%s", (sched_diddsp - oss_resync[nresyncphase].ntick) - * ((double)sys_schedblocksize) / 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].ntick = sched_diddsp; - oss_resync[oss_resyncphase].error = type; - oss_nresync++; - if (++oss_resyncphase == NRESYNC) oss_resyncphase = 0; - if (type != ERR_NOTHING && !sched_diored && (sched_diddsp >= sched_dioredtime)) { - sys_vgui("pdtk_pd_dio 1\n"); - sched_diored = 1; - } - sched_dioredtime = sched_diddsp + (int)(sys_dacsr /(double)sys_schedblocksize); -} - -static int sched_lastinclip, sched_lastoutclip, sched_lastindb, sched_lastoutdb; - -static void sched_pollformeters () { - int inclip, outclip, indb, outdb; - static int sched_nextmeterpolltime; - /* if there's no GUI but we're running in "realtime", here is - where we arrange to ping the watchdog every 2 seconds. */ -#ifdef __linux__ - static int sched_nextpingtime; - if (sys_hipriority && (sched_diddsp - sched_nextpingtime > 0)) { - glob_watchdog(0); - /* ping every 2 seconds */ - sched_nextpingtime = sched_diddsp + 2*(int)(sys_dacsr /(double)sys_schedblocksize); - } -#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 = int(0.5 + rmstodb(inmax)); - outdb = int(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)sys_schedblocksize); -} - -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 0 -void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv) { - if (argc) sys_clearhist(); - else sys_printhist(); -} -#endif - -extern void dsp_tick (); - -static int sched_usedacs = 0; -static double sched_referencerealtime, sched_referencelogicaltime; -double sys_time_per_dsp_tick; - -void sched_set_using_dacs(int flag) { - sched_usedacs = flag; - if (!flag) { - sched_referencerealtime = sys_getrealtime(); - sched_referencelogicaltime = clock_getlogicaltime(); - } - sys_time_per_dsp_tick = (TIMEUNITPERSEC) * ((double)sys_schedblocksize) / sys_dacsr; -} - -static void run_clock_callbacks(double next_sys_time) { - if (clock_setlist && clock_setlist->settime <= next_sys_time) { - do { - t_clock *c = clock_setlist; - sys_time = c->settime; - clock_unset(c); /* the compiler should easily inline this */ - outlet_setstacklim(); - c->fn(c->owner); - } while (clock_setlist && clock_setlist->settime <= next_sys_time); - } -} - -/* take the scheduler forward one DSP tick, also handling clock timeouts */ -void sched_tick(double next_sys_time) { - run_clock_callbacks(next_sys_time); - sys_time = next_sys_time; - sched_diddsp++; /* rethink: how to get rid of this stupid histogram??? */ - dsp_tick(); - /* rethink: should we really do all this midi messaging in the realtime thread ? */ - sys_pollmidiqueue(); - sys_setmiditimediff(0, 1e-6 * sys_schedadvance); -} - - -/* -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 busy with previous transfers. -*/ - -void sys_pollmidiqueue (); -void sys_initmidiqueue (); - -void canvas_stop_dsp (); - -int m_scheduler () { - int idlecount = 0; - sys_time_per_dsp_tick = (TIMEUNITPERSEC) * ((double)sys_schedblocksize) / sys_dacsr; - /* T.Grill - lock mutex */ - sys_lock(); - sys_clearhist(); - /* tb: adapt sleepgrain with advance */ - sys_update_sleepgrain(); - sched_set_using_dacs(0); /* tb: dsp is switched off */ - sys_initmidiqueue(); - while (!sys_quit) { - if (!sys_callbackscheduler || !sched_usedacs) - while (sys_keepsched) { - int didsomething = 0; - int timeforward; - waitfortick: - if (sched_usedacs) { - 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"); - sys_close_audio(); - sched_set_using_dacs(0); - canvas_stop_dsp(); /* added by matju 2007.06.30 */ - goto waitfortick; - } - } - } - } else { - if (1000. * (sys_getrealtime() - sched_referencerealtime) > clock_gettimesince(sched_referencelogicaltime)) - timeforward = SENDDACS_YES; - else timeforward = SENDDACS_NO; - } - sys_setmiditimediff(0, 1e-6 * sys_schedadvance); - if (timeforward != SENDDACS_NO) sched_tick(sys_time + sys_time_per_dsp_tick); - if (timeforward == SENDDACS_YES) didsomething = 1; - sys_pollmidiqueue(); - if (sys_pollgui()) didsomething = 1; - /* test for idle; if so, do graphics updates. */ - if (!didsomething) { - sched_pollformeters(); - /* tb: call idle callbacks */ - if (timeforward != SENDDACS_SLEPT) run_idle_callbacks(sys_sleepgrain); - } - } - else /* tb: scheduler for callback-based dsp scheduling */ - while(sys_keepsched) { - /* tb: allow the audio callback to run */ - sys_unlock(); - sys_microsleep(sys_sleepgrain); - sys_lock(); - sys_pollmidiqueue(); - sys_setmiditimediff(0, 1e-6 * sys_schedadvance); - if (sys_pollgui()) continue; - /* do graphics updates and run idle callbacks */ - sched_pollformeters(); - } - sys_keepsched = 1; - } - sys_close_audio(); - sys_unlock(); - return 0; -} - -/* ------------ thread locking ------------------- */ -/* added by Thomas Grill */ - -#ifdef THREAD_LOCKING -static pthread_mutex_t sys_mutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_cond_t sys_cond = PTHREAD_COND_INITIALIZER; - -void sys_lock () { - pthread_mutex_lock(&sys_mutex); -} -void sys_unlock () { - pthread_mutex_unlock(&sys_mutex); - pthread_cond_signal(&sys_cond); -} -int sys_trylock () { - return pthread_mutex_trylock(&sys_mutex); -} - -/* tb { */ -#include <errno.h> - -#ifdef MSW -/* gettimeofday isn't available on windoze ... */ -int gettimeofday (struct timeval *tv, void* tz) { - __int64 now; /* time since 1 Jan 1601 in 100ns */ - GetSystemTimeAsFileTime ((FILETIME*) &now); - tv->tv_usec = (long) ((now / 10LL) % 1000000LL); - tv->tv_sec = (long) ((now - 116444736000000000LL) / 10000000LL); - return 0; -} -#endif - -#if 0 -/* osx doesn't define a pthread_mutex_timedlock ... maybe someday - it will ... */ -int sys_timedlock(int microsec) { - struct timespec timeout; - struct timeval now; - /* timedlock seems to have a resolution of 1ms */ - if (microsec < 1000) microsec = 1000; - gettimeofday(&now,0); - timeout.tv_sec = now.tv_sec; - timeout.tv_nsec = (now.tv_usec + microsec) * 1000; - while (timeout.tv_nsec > 1e9) { - timeout.tv_sec += 1; - timeout.tv_nsec -= 1e9; - } - int ret = pthread_mutex_timedlock(&sys_mutex, &timeout); - if (ret) post("timeout, %d", ret); - return ret; -} -#else - -int sys_timedlock(int microsec) { - struct timespec timeout; - struct timeval now; - if (sys_trylock() == 0) return 0; - if (microsec < 1000) microsec = 1000; - gettimeofday(&now,0); - timeout.tv_sec = now.tv_sec; - timeout.tv_nsec = (now.tv_usec + microsec) * 1000; - while (timeout.tv_nsec > 1000000000) { - timeout.tv_sec += 1; - timeout.tv_nsec -= 1000000000; - } - /* in case the lock has been released during the system call, try - again before waiting for the signal */ - if (sys_trylock() == 0) return 0; - return pthread_cond_timedwait(&sys_cond, &sys_mutex, &timeout); -} -#endif -/* tb } */ - -#else -void sys_lock () {} -void sys_unlock () {} -int sys_trylock () { return 0; } -int sys_timedlock (int microsec) { return 0; } -#endif - -/* ------------ soft quit ------------------- */ -/* added by Thomas Grill - - just set the quit flag for the scheduler loop - this is useful for applications using the PD shared library to signal the scheduler to terminate -*/ - -void sys_exit () { - sys_keepsched = 0; - sys_quit = 1; -} - -/* tb: place callbacks in scheduler - * { */ -/* linked list of callbacks; callback will be freed after returning 0 */ -struct t_sched_callback { - struct t_sched_callback *next; /* next callback in ringbuffer / in fifo */ - t_int (*function)(t_int *argv); - t_int *argv; - t_int argc; -}; - -void sys_callback(t_int (*callback)(t_int* argv), t_int* argv, t_int argc) { - t_sched_callback* noo = (t_sched_callback *)malloc(sizeof(t_sched_callback)); - noo->function = callback; - if (argv && argc) { - noo->argv = (t_int*) copybytes (argv, argc * sizeof (t_int)); - noo->argc = argc; - } else { - noo->argc = 0; - noo->argv = 0; - } - noo->next = 0; - if (!callback_fifo) callback_fifo = fifo_init(); - fifo_put(callback_fifo, noo); -} - -void sys_init_idle_callbacks () { - callback_fifo = fifo_init(); /* tb: initialize fifo for idle callbacks */ -} - -static t_sched_callback *ringbuffer_head = NULL; - -void run_all_idle_callbacks () { - t_sched_callback *new_callback; - /* append idle callback to ringbuffer */ - while ((new_callback = (t_sched_callback*) fifo_get(callback_fifo))) { - t_sched_callback *next; - /* set the next field to 0 ... it might be set in the fifo */ - new_callback->next = 0; - if (!ringbuffer_head) { - ringbuffer_head = new_callback; - } else { - next = ringbuffer_head; - while (next->next) next = next->next; - next->next = new_callback; - } - } - if (ringbuffer_head) { - t_sched_callback *idle_callback = ringbuffer_head; - t_sched_callback *last = 0; - t_sched_callback *next; - do { - int status; - status = (idle_callback->function)(idle_callback->argv); - switch (status) { - /* callbacks returning 0 will be deleted */ - case 0: - next = idle_callback->next; - if (idle_callback->argv) free(idle_callback->argv); - free((void*)idle_callback); - if (!last) ringbuffer_head = next; else last->next = next; - idle_callback = next; - /* callbacks returning 1 will be run again */ - case 1: - break; - /* callbacks returning 2 will be run during the next idle callback */ - case 2: - last = idle_callback; - idle_callback = idle_callback->next; - } - } while (idle_callback); - } -} - -static void run_idle_callbacks(int microsec) { - t_sched_callback *new_callback; - double stop = sys_getrealtime()*1.e6 + (double)microsec; - /* append idle callback to ringbuffer */ - while ((new_callback = (t_sched_callback*) fifo_get(callback_fifo))) { - /* set the next field to NULL ... it might be set in the fifo */ - new_callback->next = 0; - if (!ringbuffer_head) { - ringbuffer_head = new_callback; - } else { - t_sched_callback *next = ringbuffer_head; - while (next->next != 0) - next = next->next; - next->next = new_callback; - } - } - if (ringbuffer_head) { - double remain = stop - sys_getrealtime() * 1.e6; - t_sched_callback *idle_callback = ringbuffer_head; - t_sched_callback *last = 0; - t_sched_callback *next; - do { -// sys_lock(); - int status = idle_callback->function(idle_callback->argv); -// sys_unlock(); - switch (status) { - /* callbacks returning 0 will be deleted */ - case 0: - next = idle_callback->next; - if (idle_callback->argc) free(idle_callback->argv); - free((void*)idle_callback); - if (!last) ringbuffer_head = next; else last->next = next; - idle_callback = next; - /* callbacks returning 1 will be run again */ - case 1: - break; - /* callbacks returning 2 will be run during the next idle callback */ - case 2: - last = idle_callback; - idle_callback = idle_callback->next; - } - remain = stop-sys_getrealtime()*1.e6; - } while (idle_callback && remain>0); - /* sleep for the rest of the time */ - if(remain > 0) { - sys_unlock(); - sys_microsleep(int(remain)); - sys_lock(); - } - } else { - sys_unlock(); - sys_microsleep(microsec); - sys_lock(); - } -} -/* } tb */ - -void sys_setscheduler(int scheduler) { - sys_keepsched = 0; - sys_callbackscheduler = scheduler; - return; -} - -int sys_getscheduler () {return sys_callbackscheduler;} - -static t_int sys_xrun_notification_callback(t_int *dummy) { - t_symbol *pd = gensym("pd"); - t_symbol *xrun = gensym("xrun"); - typedmess(pd->s_thing, xrun, 0, 0); - return 0; -} - -void sys_xrun_notification () {sys_callback(sys_xrun_notification_callback, 0, 0);} - -static t_int sys_lock_timeout_notification_callback(t_int *dummy) { - t_symbol *pd = gensym("pd"); - t_symbol *timeout = gensym("sys_lock_timeout"); - typedmess(pd->s_thing, timeout, 0, 0); - return 0; -} - -void sys_lock_timeout_notification () {sys_callback(sys_lock_timeout_notification_callback, 0, 0);} |