2 SuperCollider real time audio synthesis system
3 Copyright (c) 2002 James McCartney. All rights reserved.
4 http://www.audiosynth.com
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "PyrKernel.h"
25 #include "PyrPrimitive.h"
26 #include "PyrSymbol.h"
28 # include <CoreAudio/HostTime.h>
39 #include "SC_Win32Utils.h"
42 static const double dInfinity
= std::numeric_limits
<double>::infinity();
44 void runAwakeMessage(VMGlobals
*g
);
46 // heaps use an integer timestamp to ensure stable heap order
50 PyrSlot count
; // stability count
51 PyrSlot slots
[0]; // slots
56 bool addheap(VMGlobals
*g
, PyrObject
*heapArg
, double schedtime
, PyrSlot
*task
)
58 PyrHeap
* heap
= (PyrHeap
*)heapArg
;
62 if (heap
->size
>= ARRAYMAXINDEXSIZE(heap
))
66 // post("->addheap\n");
69 /* parent and sibling in the heap, not in the task hierarchy */
70 int mom
= heap
->size
- 1;
71 PyrSlot
* pme
= heap
->slots
+ mom
;
72 int stabilityCount
= slotRawInt(&heap
->count
);
73 SetRaw(&heap
->count
, stabilityCount
+ 1);
75 for (; mom
>0;) { /* percolate up heap */
76 int newMom
= ((mom
- 3) / 2);
77 mom
= newMom
- newMom
% 3; /// LATER: we could avoid the division by using 4 slots per element
78 PyrSlot
* pmom
= heap
->slots
+ mom
;
79 if (schedtime
< slotRawFloat(pmom
)) {
80 assert(slotRawInt(pmom
+ 2) < stabilityCount
);
81 slotCopy(&pme
[0], &pmom
[0]);
82 slotCopy(&pme
[1], &pmom
[1]);
83 slotCopy(&pme
[2], &pmom
[2]);
87 SetFloat(&pme
[0], schedtime
);
88 slotCopy(&pme
[1], task
);
89 SetInt(&pme
[2], stabilityCount
);
90 g
->gc
->GCWrite(heap
, task
);
97 // post("<-addheap %g\n", schedtime);
102 bool lookheap(PyrObject
*heap
, double *schedtime
, PyrSlot
*task
)
104 if (heap
->size
> 1) {
105 *schedtime
= slotRawFloat(&heap
->slots
[0]);
106 slotCopy(task
, &heap
->slots
[1]);
111 bool getheap(VMGlobals
*g
, PyrObject
*heapArg
, double *schedtime
, PyrSlot
*task
)
113 PyrHeap
* heap
= (PyrHeap
*)heapArg
;
115 bool isPartialScanObj
= gc
->IsPartialScanObject(heapArg
);
118 // post("->getheap\n");
119 // dumpheap(heapArg);
121 *schedtime
= slotRawFloat(&heap
->slots
[0]);
122 slotCopy(task
, &heap
->slots
[1]);
124 int size
= heap
->size
- 1;
125 slotCopy(&heap
->slots
[0], &heap
->slots
[size
]);
126 slotCopy(&heap
->slots
[1], &heap
->slots
[size
+1]);
127 slotCopy(&heap
->slots
[2], &heap
->slots
[size
+2]);
129 /* parent and sibling in the heap, not in the task hierarchy */
132 PyrSlot
* pmom
= heap
->slots
+ mom
;
133 PyrSlot
* pme
= heap
->slots
+ me
;
134 PyrSlot
* pend
= heap
->slots
+ size
;
135 double timetemp
= slotRawFloat(&pmom
[0]);
136 int stabilityCountTemp
= slotRawInt(&pmom
[2]);
138 slotCopy(&tasktemp
, &pmom
[1]);
141 if (pme
+3 < pend
&& ((slotRawFloat(&pme
[0]) > slotRawFloat(&pme
[3])) ||
142 ((slotRawFloat(&pme
[0]) == slotRawFloat(&pme
[3])) && (slotRawInt(&pme
[2]) > slotRawInt(&pme
[5]))
146 if (timetemp
> slotRawFloat(&pme
[0]) ||
147 (timetemp
== slotRawFloat(&pme
[0]) && stabilityCountTemp
> slotRawInt(&pme
[2]))) {
148 slotCopy(&pmom
[0], &pme
[0]);
149 slotCopy(&pmom
[1], &pme
[1]);
150 slotCopy(&pmom
[2], &pme
[2]);
151 if (isPartialScanObj
) {
152 gc
->GCWriteBlack(pmom
+1);
155 me
= ((mom
= me
) * 2) + 3;
156 pme
= heap
->slots
+ me
;
159 SetRaw(&pmom
[0], timetemp
);
160 slotCopy(&pmom
[1], &tasktemp
);
161 SetRaw(&pmom
[2], stabilityCountTemp
);
162 if (isPartialScanObj
)
163 gc
->GCWriteBlack(pmom
+1);
166 SetInt(&heap
->count
, 0);
168 // dumpheap(heapArg);
169 // post("<-getheap true\n");
172 // post("<-getheap false\n");
177 void offsetheap(VMGlobals
*g
, PyrObject
*heap
, double offset
)
180 for (i
=0; i
<heap
->size
; i
+=2) {
181 SetRaw(&heap
->slots
[i
], slotRawFloat(&heap
->slots
[i
]) + offset
);
182 //post("%3d %9.2f %9.2f\n", i>>1, heap->slots[i].uf, offset);
186 void dumpheap(PyrObject
*heapArg
)
188 PyrHeap
* heap
= (PyrHeap
*)heapArg
;
189 double mintime
= slotRawFloat(&heap
->slots
[0]);
190 int count
= slotRawFloat(&heap
->slots
[2]);
191 int heapSize
= heap
->size
- 1;
192 post("SCHED QUEUE (%d)\n", heapSize
);
193 for (int i
=0; i
<heapSize
; i
+=3) {
194 post("%3d(%3d) %9.2f %p %d\n", i
/3, i
, slotRawFloat(&heap
->slots
[i
]), slotRawObject(&heap
->slots
[i
+1]), slotRawInt(&heap
->slots
[i
+2]));
195 if ((slotRawFloat(&heap
->slots
[i
]) < mintime
)
196 || (slotRawFloat(&heap
->slots
[i
]) == mintime
&& slotRawInt(&heap
->slots
[i
+2]) < count
)
198 post("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
204 bool gRunSched
= false;
205 pthread_t gSchedThread
;
206 pthread_t gResyncThread
;
207 pthread_cond_t gSchedCond
;
208 pthread_mutex_t gLangMutex
;
211 int64 gHostOSCoffset
= 0;
212 int64 gHostStartNanos
= 0;
215 int64 gElapsedOSCoffset
= 0;
217 const int32 kSECONDS_FROM_1900_to_1970
= (int32
)2208988800UL; /* 17 leap years */
218 const double fSECONDS_FROM_1900_to_1970
= 2208988800.; /* 17 leap years */
221 static void syncOSCOffsetWithTimeOfDay();
222 void* resyncThread(void* arg
);
228 # include <sys/time.h>
231 inline double GetTimeOfDay();
232 double GetTimeOfDay()
235 gettimeofday(&tv
, 0);
236 return (double)tv
.tv_sec
+ 1.0e-6 * (double)tv
.tv_usec
;
240 SC_DLLEXPORT_C
void schedInit()
242 pthread_cond_init (&gSchedCond
, NULL
);
243 pthread_mutex_init (&gLangMutex
, NULL
);
246 syncOSCOffsetWithTimeOfDay();
247 pthread_create (&gResyncThread
, NULL
, resyncThread
, (void*)0);
249 gHostStartNanos
= AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
250 gElapsedOSCoffset
= (int64
)(gHostStartNanos
* kNanosToOSC
) + gHostOSCoffset
;
252 gElapsedOSCoffset
= (int64
)kSECONDS_FROM_1900_to_1970
<< 32;
256 SC_DLLEXPORT_C
void schedCleanup()
258 pthread_mutex_destroy (&gLangMutex
);
259 pthread_cond_destroy (&gSchedCond
);
262 double bootSeconds();
266 return 1e-9 * (double)AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
268 return GetTimeOfDay();
272 double elapsedTime();
276 return 1e-9 * (double)(AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) - gHostStartNanos
);
278 return GetTimeOfDay();
282 int64
ElapsedTimeToOSC(double elapsed
)
284 return (int64
)(elapsed
* kSecondsToOSC
) + gElapsedOSCoffset
;
287 double OSCToElapsedTime(int64 oscTime
)
289 return (double)(oscTime
- gElapsedOSCoffset
) * kOSCtoSecs
;
292 void ElapsedTimeToTimespec(double elapsed
, struct timespec
*spec
);
293 void ElapsedTimeToTimespec(double elapsed
, struct timespec
*spec
)
295 int64 oscTime
= ElapsedTimeToOSC(elapsed
);
297 spec
->tv_sec
= (time_t)((oscTime
>> 32) - kSECONDS_FROM_1900_to_1970
);
298 spec
->tv_nsec
= (int32
)((oscTime
& 0xFFFFFFFF) * kOSCtoNanos
);
303 return ElapsedTimeToOSC(elapsedTime());
307 void syncOSCOffsetWithTimeOfDay()
309 // generate a value gHostOSCoffset such that
310 // (gHostOSCoffset + systemTimeInOSCunits)
311 // is equal to gettimeofday time in OSCunits.
312 // Then if this machine is synced via NTP, we are synced with the world.
313 // more accurate way to do this??
317 int64 systemTimeBefore
, systemTimeAfter
, diff
;
318 int64 minDiff
= 0x7fffFFFFffffFFFFLL
;
320 // take best of several tries
321 const int numberOfTries
= 8;
322 int64 newOffset
= gHostOSCoffset
;
323 for (int i
=0; i
<numberOfTries
; ++i
) {
324 systemTimeBefore
= AudioGetCurrentHostTime();
325 gettimeofday(&tv
, 0);
326 systemTimeAfter
= AudioGetCurrentHostTime();
328 diff
= systemTimeAfter
- systemTimeBefore
;
329 if (diff
< minDiff
) {
331 // assume that gettimeofday happens halfway between AudioGetCurrentHostTime calls
332 int64 systemTimeBetween
= systemTimeBefore
+ diff
/2;
333 int64 systemTimeInOSCunits
= (int64
)((double)AudioConvertHostTimeToNanos(systemTimeBetween
) * kNanosToOSC
);
334 int64 timeOfDayInOSCunits
= ((int64
)(tv
.tv_sec
+ kSECONDS_FROM_1900_to_1970
) << 32)
335 + (int64
)(tv
.tv_usec
* kMicrosToOSC
);
336 newOffset
= timeOfDayInOSCunits
- systemTimeInOSCunits
;
340 gHostOSCoffset
= newOffset
;
341 //postfl("gHostOSCoffset %016llX\n", gHostOSCoffset);
345 void schedAdd(VMGlobals
*g
, PyrObject
* inQueue
, double inSeconds
, PyrSlot
* inTask
);
346 void schedAdd(VMGlobals
*g
, PyrObject
* inQueue
, double inSeconds
, PyrSlot
* inTask
)
348 // gLangMutex must be locked
349 double prevTime
= inQueue
->size
> 1 ? slotRawFloat(inQueue
->slots
+ 1) : -1e10
;
350 bool added
= addheap(g
, inQueue
, inSeconds
, inTask
);
351 if (!added
) post("scheduler queue is full.\n");
353 if (isKindOfSlot(inTask
, class_thread
)) {
354 SetFloat(&slotRawThread(inTask
)->nextBeat
, inSeconds
);
356 if (slotRawFloat(inQueue
->slots
+ 1) != prevTime
) {
357 //post("pthread_cond_signal\n");
358 pthread_cond_signal (&gSchedCond
);
364 void doubleToTimespec(double secs
, struct timespec
*spec
);
365 void doubleToTimespec(double secs
, struct timespec
*spec
)
367 double isecs
= floor(secs
);
368 spec
->tv_sec
= (long)isecs
;
369 spec
->tv_nsec
= (long)floor(1000000000. * (secs
- isecs
));
372 SC_DLLEXPORT_C
void schedStop()
374 //printf("->schedStop\n");
375 pthread_mutex_lock (&gLangMutex
);
378 pthread_cond_signal (&gSchedCond
);
379 pthread_mutex_unlock (&gLangMutex
);
380 pthread_join(gSchedThread
, 0);
382 pthread_mutex_unlock (&gLangMutex
);
384 //printf("<-schedStop\n");
387 void schedClearUnsafe();
389 SC_DLLEXPORT_C
void schedClear()
391 pthread_mutex_lock (&gLangMutex
);
393 pthread_mutex_unlock (&gLangMutex
);
396 void schedClearUnsafe()
398 //postfl("->schedClear %d\n", gRunSched);
400 VMGlobals
*g
= gMainVMGlobals
;
401 PyrObject
* inQueue
= slotRawObject(&g
->process
->sysSchedulerQueue
);
403 pthread_cond_signal (&gSchedCond
);
404 //pthread_mutex_unlock (&gLangMutex);
406 //postfl("<-schedClear %d\n", gRunSched);
409 void post(const char *fmt
, ...);
412 void* resyncThread(void* arg
)
416 syncOSCOffsetWithTimeOfDay();
417 gElapsedOSCoffset
= (int64
)(gHostStartNanos
* kNanosToOSC
) + gHostOSCoffset
;
423 extern bool gTraceInterpreter
;
425 void* schedRunFunc(void* arg
);
426 void* schedRunFunc(void* arg
)
428 pthread_mutex_lock (&gLangMutex
);
431 VMGlobals
*g
= gMainVMGlobals
;
432 PyrObject
* inQueue
= slotRawObject(&g
->process
->sysSchedulerQueue
);
433 //dumpObject(inQueue);
437 assert(inQueue
->size
);
439 //postfl("wait until there is something in scheduler\n");
440 // wait until there is something in scheduler
441 while (inQueue
->size
== 1) {
442 //postfl("wait until there is something in scheduler\n");
443 pthread_cond_wait (&gSchedCond
, &gLangMutex
);
444 if (!gRunSched
) goto leave
;
446 //postfl("wait until an event is ready\n");
448 // wait until an event is ready
451 elapsed
= elapsedTime();
452 if (elapsed
>= slotRawFloat(inQueue
->slots
+ 1)) break;
453 struct timespec abstime
;
454 //doubleToTimespec(inQueue->slots->uf, &abstime);
455 ElapsedTimeToTimespec(slotRawFloat(inQueue
->slots
+ 1), &abstime
);
456 //postfl("wait until an event is ready\n");
457 pthread_cond_timedwait (&gSchedCond
, &gLangMutex
, &abstime
);
458 if (!gRunSched
) goto leave
;
459 //postfl("time diff %g\n", elapsedTime() - inQueue->slots->uf);
460 } while (inQueue
->size
> 1);
462 //postfl("perform all events that are ready %d %.9f\n", inQueue->size, elapsed);
464 // perform all events that are ready
465 //postfl("perform all events that are ready\n");
466 while ((inQueue
->size
> 1) && elapsed
>= slotRawFloat(inQueue
->slots
+ 1)) {
467 double schedtime
, delta
;
470 //postfl("while %.6f >= %.6f\n", elapsed, inQueue->slots->uf);
472 getheap(g
, inQueue
, &schedtime
, &task
);
474 if (isKindOfSlot(&task
, class_thread
)) {
475 SetNil(&slotRawThread(&task
)->nextBeat
);
478 slotCopy((++g
->sp
), &task
);
479 SetFloat(++g
->sp
, schedtime
);
480 SetFloat(++g
->sp
, schedtime
);
481 ++g
->sp
; SetObject(g
->sp
, s_systemclock
->u
.classobj
);
484 long err
= slotDoubleVal(&g
->result
, &delta
);
486 // add delta time and reschedule
487 double time
= schedtime
+ delta
;
489 schedAdd(g
, inQueue
, time
, &task
);
494 //postfl("exitloop\n");
496 pthread_mutex_unlock (&gLangMutex
);
501 #include <mach/mach.h>
502 #include <mach/thread_policy.h>
504 //Polls a (non-realtime) thread to find out how it is scheduled
505 //Results are undefined of an error is returned. Otherwise, the
506 //priority is returned in the address pointed to by the priority
507 //parameter, and whether or not the thread uses timeshare scheduling
508 //is returned at the address pointed to by the isTimeShare parameter
509 kern_return_t
GetStdThreadSchedule( mach_port_t machThread
,
511 boolean_t
*isTimeshare
)
513 kern_return_t result
= 0;
514 thread_extended_policy_data_t timeShareData
;
515 thread_precedence_policy_data_t precedenceData
;
516 mach_msg_type_number_t structItemCount
;
517 boolean_t fetchDefaults
= false;
519 memset( &timeShareData
, 0, sizeof( thread_extended_policy_data_t
));
520 memset( &precedenceData
, 0, sizeof( thread_precedence_policy_data_t
));
522 if( 0 == machThread
)
523 machThread
= mach_thread_self();
525 if( NULL
!= isTimeshare
)
527 structItemCount
= THREAD_EXTENDED_POLICY_COUNT
;
528 result
= thread_policy_get( machThread
, THREAD_EXTENDED_POLICY
,
529 (integer_t
*)&timeShareData
, &structItemCount
, &fetchDefaults
);
530 *isTimeshare
= timeShareData
.timeshare
;
535 if( NULL
!= priority
)
537 fetchDefaults
= false;
538 structItemCount
= THREAD_PRECEDENCE_POLICY_COUNT
;
539 result
= thread_policy_get( machThread
, THREAD_PRECEDENCE_POLICY
,
540 (integer_t
*)&precedenceData
, &structItemCount
, &fetchDefaults
);
541 *priority
= precedenceData
.importance
;
547 // Reschedules the indicated thread according to new parameters:
549 // machThread The mach thread id. Pass 0 for the current thread.
550 // newPriority The desired priority.
551 // isTimeShare false for round robin (fixed) priority,
552 // true for timeshare (normal) priority
554 // A standard new thread usually has a priority of 0 and uses the
555 // timeshare scheduling scheme. Use pthread_mach_thread_np() to
556 // to convert a pthread id to a mach thread id
557 kern_return_t
RescheduleStdThread( mach_port_t machThread
,
559 boolean_t isTimeshare
)
561 kern_return_t result
= 0;
562 thread_extended_policy_data_t timeShareData
;
563 thread_precedence_policy_data_t precedenceData
;
565 //Set up some variables that we need for the task
566 precedenceData
.importance
= newPriority
;
567 timeShareData
.timeshare
= isTimeshare
;
568 if( 0 == machThread
)
569 machThread
= mach_thread_self();
571 //Set the scheduling flavor. We want to do this first, since doing so
572 //can alter the priority
573 result
= thread_policy_set( machThread
,
574 THREAD_EXTENDED_POLICY
,
575 (integer_t
*)&timeShareData
,
576 THREAD_EXTENDED_POLICY_COUNT
);
581 //Now set the priority
582 return thread_policy_set( machThread
,
583 THREAD_PRECEDENCE_POLICY
,
584 (integer_t
*)&precedenceData
,
585 THREAD_PRECEDENCE_POLICY_COUNT
);
593 static void SC_LinuxSetRealtimePriority(pthread_t thread
, int priority
)
596 struct sched_param param
;
598 pthread_getschedparam(thread
, &policy
, ¶m
);
601 const int minprio
= sched_get_priority_min(policy
);
602 const int maxprio
= sched_get_priority_max(policy
);
603 param
.sched_priority
= sc_clip(priority
, minprio
, maxprio
);
605 int err
= pthread_setschedparam(thread
, policy
, ¶m
);
607 post("Couldn't set realtime scheduling priority %d: %s\n",
608 param
.sched_priority
, strerror(err
));
614 SC_DLLEXPORT_C
void schedRun()
616 pthread_create (&gSchedThread
, NULL
, schedRunFunc
, (void*)0);
620 struct sched_param param
;
622 //pthread_t thread = pthread_self ();
623 pthread_getschedparam (gSchedThread
, &policy
, ¶m
);
624 //post("param.sched_priority %d\n", param.sched_priority);
626 policy
= SCHED_RR
; // round-robin, AKA real-time scheduling
630 GetStdThreadSchedule(pthread_mach_thread_np(gSchedThread
), &machprio
, ×hare
);
631 //post("mach priority %d timeshare %d\n", machprio, timeshare);
633 // what priority should gSchedThread use?
635 RescheduleStdThread(pthread_mach_thread_np(gSchedThread
), 62, false);
637 GetStdThreadSchedule(pthread_mach_thread_np(gSchedThread
), &machprio
, ×hare
);
638 //post("mach priority %d timeshare %d\n", machprio, timeshare);
640 //param.sched_priority = 70; // you'll have to play with this to see what it does
641 //pthread_setschedparam (gSchedThread, policy, ¶m);
643 pthread_getschedparam (gSchedThread
, &policy
, ¶m
);
645 //post("param.sched_priority %d\n", param.sched_priority);
649 SC_LinuxSetRealtimePriority(gSchedThread
, 1);
660 mouse, keyboard, MIDI
662 all these happen in the main thread.
680 TempoClock(VMGlobals
*inVMGlobals
, PyrObject
* inTempoClockObj
,
681 double inTempo
, double inBaseBeats
, double inBaseSeconds
);
688 void Add(double inBeats
, PyrSlot
* inTask
);
689 void SetTempoAtBeat(double inTempo
, double inBeats
);
690 void SetTempoAtTime(double inTempo
, double inSeconds
);
691 void SetAll(double inTempo
, double inBeats
, double inSeconds
);
692 double ElapsedBeats();
695 double GetTempo() const { return mTempo
; }
696 double GetBeatDur() const { return mBeatDur
; }
697 double BeatsToSecs(double beats
) const
698 { return (beats
- mBaseBeats
) * mBeatDur
+ mBaseSeconds
; }
699 double SecsToBeats(double secs
) const
700 { return (secs
- mBaseSeconds
) * mTempo
+ mBaseBeats
; }
705 PyrObject
* mTempoClockObj
;
708 double mTempo
; // beats per second
709 double mBeatDur
; // 1/tempo
710 double mBeats
; // beats
715 pthread_cond_t mCondition
;
716 TempoClock
*mPrev
, *mNext
;
718 static TempoClock
*sAll
;
722 TempoClock
*TempoClock::sAll
= 0;
725 void* TempoClock_run_func(void* p
)
727 TempoClock
* clock
= (TempoClock
*)p
;
731 void* TempoClock_stop_func(void* p
)
733 //printf("->TempoClock_stop_func\n");
734 TempoClock
* clock
= (TempoClock
*)p
;
736 //printf("delete\n");
738 //printf("<-TempoClock_stop_func\n");
742 void TempoClock_stopAll(void)
744 //printf("->TempoClock_stopAll %p\n", TempoClock::sAll);
745 TempoClock
*clock
= TempoClock::sAll
;
747 TempoClock
* next
= clock
->mNext
;
749 //printf("delete\n");
753 //printf("<-TempoClock_stopAll %p\n", TempoClock::sAll);
754 TempoClock::sAll
= 0;
757 TempoClock::TempoClock(VMGlobals
*inVMGlobals
, PyrObject
* inTempoClockObj
,
758 double inTempo
, double inBaseBeats
, double inBaseSeconds
)
759 : g(inVMGlobals
), mTempoClockObj(inTempoClockObj
), mTempo(inTempo
), mBeatDur(1./inTempo
),
760 mBaseSeconds(inBaseSeconds
), mBaseBeats(inBaseBeats
), mRun(true), mPrev(0), mNext(sAll
)
762 if (sAll
) sAll
->mPrev
= this;
765 mQueue
= (PyrHeap
*)slotRawObject(&mTempoClockObj
->slots
[0]);
767 SetInt(&mQueue
->count
, 0);
768 pthread_cond_init (&mCondition
, NULL
);
770 int err
= pthread_create (&mThread
, NULL
, TempoClock_run_func
, (void*)this);
773 post("Couldn't start thread for TempoClock: %s\n", strerror(err
));
779 GetStdThreadSchedule(pthread_mach_thread_np(mThread
), &machprio
, ×hare
);
780 //post("mach priority %d timeshare %d\n", machprio, timeshare);
782 // what priority should gSchedThread use?
784 RescheduleStdThread(pthread_mach_thread_np(mThread
), 10, false);
786 GetStdThreadSchedule(pthread_mach_thread_np(mThread
), &machprio
, ×hare
);
787 //post("mach priority %d timeshare %d\n", machprio, timeshare);
789 //param.sched_priority = 70; // you'll have to play with this to see what it does
790 //pthread_setschedparam (mThread, policy, ¶m);
794 SC_LinuxSetRealtimePriority(mThread
, 1);
798 void TempoClock::StopReq()
800 //printf("->TempoClock::StopReq\n");
801 pthread_t stopThread
;
802 pthread_create (&stopThread
, NULL
, TempoClock_stop_func
, (void*)this);
803 pthread_detach(stopThread
);
804 //printf("<-TempoClock::StopReq\n");
807 void TempoClock::Stop()
809 //printf("->TempoClock::Stop\n");
810 pthread_mutex_lock (&gLangMutex
);
811 //printf("Stop mRun %d\n", mRun);
816 if (mPrev
) mPrev
->mNext
= mNext
;
818 if (mNext
) mNext
->mPrev
= mPrev
;
820 //printf("Stop pthread_cond_signal\n");
821 pthread_cond_signal (&mCondition
);
822 //printf("Stop pthread_mutex_unlock\n");
823 pthread_mutex_unlock (&gLangMutex
);
824 //printf("Stop pthread_join\n");
825 pthread_join(mThread
, 0);
827 pthread_mutex_unlock (&gLangMutex
);
829 //printf("Stop pthread_cond_destroy\n");
830 pthread_cond_destroy (&mCondition
);
831 //printf("<-TempoClock::Stop\n");
834 void TempoClock::SetAll(double inTempo
, double inBeats
, double inSeconds
)
836 mBaseSeconds
= inSeconds
;
837 mBaseBeats
= inBeats
;
839 mBeatDur
= 1. / mTempo
;
840 pthread_cond_signal (&mCondition
);
843 void TempoClock::SetTempoAtBeat(double inTempo
, double inBeats
)
845 mBaseSeconds
= BeatsToSecs(inBeats
);
846 mBaseBeats
= inBeats
;
848 mBeatDur
= 1. / mTempo
;
849 pthread_cond_signal (&mCondition
);
852 void TempoClock::SetTempoAtTime(double inTempo
, double inSeconds
)
854 mBaseBeats
= SecsToBeats(inSeconds
);
855 mBaseSeconds
= inSeconds
;
857 mBeatDur
= 1. / mTempo
;
858 pthread_cond_signal (&mCondition
);
861 double TempoClock::ElapsedBeats()
863 return SecsToBeats(elapsedTime());
866 void* TempoClock::Run()
868 //printf("->TempoClock::Run\n");
869 pthread_mutex_lock (&gLangMutex
);
871 assert(mQueue
->size
);
872 //printf("tempo %g dur %g beats %g\n", mTempo, mBeatDur, mBeats);
873 //printf("wait until there is something in scheduler\n");
874 // wait until there is something in scheduler
875 while (mQueue
->size
== 1) {
876 //printf("wait until there is something in scheduler\n");
877 pthread_cond_wait (&mCondition
, &gLangMutex
);
878 //printf("mRun a %d\n", mRun);
879 if (!mRun
) goto leave
;
881 //printf("wait until an event is ready\n");
883 // wait until an event is ready
886 elapsedBeats
= ElapsedBeats();
887 if (elapsedBeats
>= slotRawFloat(mQueue
->slots
)) break;
888 struct timespec abstime
;
889 //doubleToTimespec(mQueue->slots->uf, &abstime);
890 //printf("event ready at %g . elapsed beats %g\n", mQueue->slots->uf, elapsedBeats);
891 double wakeTime
= BeatsToSecs(slotRawFloat(mQueue
->slots
));
892 ElapsedTimeToTimespec(wakeTime
, &abstime
);
893 //printf("wait until an event is ready. wake %g now %g\n", wakeTime, elapsedTime());
894 pthread_cond_timedwait (&mCondition
, &gLangMutex
, &abstime
);
895 //printf("mRun b %d\n", mRun);
896 if (!mRun
) goto leave
;
897 //printf("time diff %g\n", elapsedTime() - mQueue->slots->uf);
898 } while (mQueue
->size
> 1);
899 //printf("perform all events that are ready %d %.9f\n", mQueue->size, elapsedBeats);
901 // perform all events that are ready
902 //printf("perform all events that are ready\n");
903 while (mQueue
->size
> 1 && elapsedBeats
>= slotRawFloat(mQueue
->slots
)) {
907 //printf("while %.6f >= %.6f\n", elapsedBeats, mQueue->slots->uf);
909 getheap(g
, (PyrObject
*)mQueue
, &mBeats
, &task
);
911 if (isKindOfSlot(&task
, class_thread
)) {
912 SetNil(&slotRawThread(&task
)->nextBeat
);
915 slotCopy((++g
->sp
), &task
);
916 SetFloat(++g
->sp
, mBeats
);
917 SetFloat(++g
->sp
, BeatsToSecs(mBeats
));
918 ++g
->sp
; SetObject(g
->sp
, mTempoClockObj
);
921 long err
= slotDoubleVal(&g
->result
, &delta
);
923 // add delta time and reschedule
924 double beats
= mBeats
+ delta
;
930 //printf("<-TempoClock::Run\n");
931 pthread_mutex_unlock (&gLangMutex
);
936 void TempoClock::Flush()
938 while (mQueue->size && elapsedBeats >= mQueue->slots->uf) {
942 //printf("while %.6f >= %.6f\n", elapsedBeats, mQueue->slots->uf);
944 getheap(g, mQueue, &mBeats, &task);
946 slotCopy((++g->sp), &task);
947 (++g->sp)->uf = mBeats;
948 (++g->sp)->uf = BeatsToSecs(mBeats);
949 ++g->sp; SetObject(g->sp, mTempoClockObj);
952 long err = slotDoubleVal(&g->result, &delta);
954 // add delta time and reschedule
955 double beats = mBeats + delta;
963 void TempoClock::Add(double inBeats
, PyrSlot
* inTask
)
965 double prevBeats
= mQueue
->size
> 1 ? slotRawFloat(mQueue
->slots
) : -1e10
;
966 bool added
= addheap(g
, (PyrObject
*)mQueue
, inBeats
, inTask
);
967 if (!added
) post("scheduler queue is full.\n");
969 if (isKindOfSlot(inTask
, class_thread
)) {
970 SetFloat(&slotRawThread(inTask
)->nextBeat
, inBeats
);
972 if (slotRawFloat(mQueue
->slots
) != prevBeats
) {
973 pthread_cond_signal (&mCondition
);
978 void TempoClock::Clear()
982 pthread_cond_signal (&mCondition
);
986 void TempoClock::Dump()
988 post("mTempo %g\n", mTempo
);
989 post("mBeatDur %g\n", mBeatDur
);
990 post("mBeats %g\n", mBeats
);
991 post("seconds %g\n", BeatsToSecs(mBeats
));
992 post("mBaseSeconds %g\n", mBaseSeconds
);
993 post("mBaseBeats %g\n", mBaseBeats
);
996 int prTempoClock_New(struct VMGlobals
*g
, int numArgsPushed
);
997 int prTempoClock_New(struct VMGlobals
*g
, int numArgsPushed
)
999 PyrSlot
*a
= g
->sp
- 3;
1000 PyrSlot
*b
= g
->sp
- 2;
1001 PyrSlot
*c
= g
->sp
- 1;
1005 int err
= slotDoubleVal(b
, &tempo
);
1006 if (err
) tempo
= 1.;
1008 error("invalid tempo %g\n", tempo
);
1009 SetPtr(slotRawObject(a
)->slots
+1, NULL
);
1014 err
= slotDoubleVal(c
, &beats
);
1015 if (err
) beats
= 0.;
1018 err
= slotDoubleVal(d
, &seconds
);
1019 if (err
) seconds
= elapsedTime();
1021 TempoClock
* clock
= new TempoClock(g
, slotRawObject(a
), tempo
, beats
, seconds
);
1022 SetPtr(slotRawObject(a
)->slots
+1, clock
);
1026 int prTempoClock_Free(struct VMGlobals
*g
, int numArgsPushed
);
1027 int prTempoClock_Free(struct VMGlobals
*g
, int numArgsPushed
)
1030 TempoClock
*clock
= (TempoClock
*)slotRawPtr(&slotRawObject(a
)->slots
[1]);
1031 if (!clock
) return errNone
; // not running
1033 SetNil(slotRawObject(a
)->slots
+ 1);
1039 int prTempoClock_Clear(struct VMGlobals
*g
, int numArgsPushed
);
1040 int prTempoClock_Clear(struct VMGlobals
*g
, int numArgsPushed
)
1043 TempoClock
*clock
= (TempoClock
*)slotRawPtr(&slotRawObject(a
)->slots
[1]);
1044 if (clock
) clock
->Clear();
1049 int prTempoClock_Dump(struct VMGlobals
*g
, int numArgsPushed
);
1050 int prTempoClock_Dump(struct VMGlobals
*g
, int numArgsPushed
)
1053 TempoClock
*clock
= (TempoClock
*)slotRawPtr(&slotRawObject(a
)->slots
[1]);
1054 if (clock
) clock
->Dump();
1060 int prTempoClock_Tempo(struct VMGlobals
*g
, int numArgsPushed
);
1061 int prTempoClock_Tempo(struct VMGlobals
*g
, int numArgsPushed
)
1064 TempoClock
*clock
= (TempoClock
*)slotRawPtr(&slotRawObject(a
)->slots
[1]);
1066 error("clock is not running.\n");
1070 SetFloat(a
, clock
->mTempo
);
1075 int prTempoClock_BeatDur(struct VMGlobals
*g
, int numArgsPushed
);
1076 int prTempoClock_BeatDur(struct VMGlobals
*g
, int numArgsPushed
)
1079 TempoClock
*clock
= (TempoClock
*)slotRawPtr(&slotRawObject(a
)->slots
[1]);
1081 error("clock is not running.\n");
1085 SetFloat(a
, clock
->mBeatDur
);
1090 int prTempoClock_ElapsedBeats(struct VMGlobals
*g
, int numArgsPushed
);
1091 int prTempoClock_ElapsedBeats(struct VMGlobals
*g
, int numArgsPushed
)
1094 TempoClock
*clock
= (TempoClock
*)slotRawPtr(&slotRawObject(a
)->slots
[1]);
1096 error("clock is not running.\n");
1100 SetFloat(a
, clock
->ElapsedBeats());
1105 int prTempoClock_Beats(struct VMGlobals
*g
, int numArgsPushed
);
1106 int prTempoClock_Beats(struct VMGlobals
*g
, int numArgsPushed
)
1109 double beats
, seconds
;
1111 if (SlotEq(&g
->thread
->clock
, a
)) {
1112 int err
= slotDoubleVal(&g
->thread
->beats
, &beats
);
1113 if (err
) return errWrongType
;
1115 TempoClock
*clock
= (TempoClock
*)slotRawPtr(&slotRawObject(a
)->slots
[1]);
1117 error("clock is not running.\n");
1121 int err
= slotDoubleVal(&g
->thread
->seconds
, &seconds
);
1122 if (err
) return errWrongType
;
1124 beats
= clock
->SecsToBeats(seconds
);
1130 int prTempoClock_Sched(struct VMGlobals
*g
, int numArgsPushed
);
1131 int prTempoClock_Sched(struct VMGlobals
*g
, int numArgsPushed
)
1133 PyrSlot
*a
= g
->sp
- 2;
1134 PyrSlot
*b
= g
->sp
- 1;
1136 double delta
, beats
;
1139 TempoClock
*clock
= (TempoClock
*)slotRawPtr(&slotRawObject(a
)->slots
[1]);
1141 error("clock is not running.\n");
1145 if (!SlotEq(&g
->thread
->clock
, a
)) {
1146 beats
= clock
->ElapsedBeats();
1147 //post("shouldn't call TempoClock-sched from a different clock. Use schedAbs.\n");
1150 err
= slotDoubleVal(&g
->thread
->beats
, &beats
);
1151 if (err
) return errNone
; // return nil OK, just don't schedule
1154 err
= slotDoubleVal(b
, &delta
);
1155 if (err
) return errNone
; // return nil OK, just don't schedule
1157 if (beats
== dInfinity
)
1158 return errNone
; // return nil OK, just don't schedule
1160 clock
->Add(beats
, c
);
1165 int prTempoClock_SchedAbs(struct VMGlobals
*g
, int numArgsPushed
);
1166 int prTempoClock_SchedAbs(struct VMGlobals
*g
, int numArgsPushed
)
1168 PyrSlot
*a
= g
->sp
- 2;
1169 PyrSlot
*b
= g
->sp
- 1;
1172 TempoClock
*clock
= (TempoClock
*)slotRawPtr(&slotRawObject(a
)->slots
[1]);
1174 error("clock is not running.\n");
1179 int err
= slotDoubleVal(b
, &beats
) || (beats
== dInfinity
);
1180 if (err
) return errNone
; // return nil OK, just don't schedule
1182 clock
->Add(beats
, c
);
1187 int prTempoClock_SetTempoAtBeat(struct VMGlobals
*g
, int numArgsPushed
);
1188 int prTempoClock_SetTempoAtBeat(struct VMGlobals
*g
, int numArgsPushed
)
1190 PyrSlot
*a
= g
->sp
- 2;
1191 PyrSlot
*b
= g
->sp
- 1;
1194 TempoClock
*clock
= (TempoClock
*)slotRawPtr(&slotRawObject(a
)->slots
[1]);
1196 error("clock is not running.\n");
1201 int err
= slotDoubleVal(b
, &tempo
);
1202 if (err
) return errFailed
;
1204 error("invalid tempo %g\n", tempo
);
1208 err
= slotDoubleVal(c
, &beat
);
1209 if (err
) return errFailed
;
1211 clock
->SetTempoAtBeat(tempo
, beat
);
1216 int prTempoClock_SetAll(struct VMGlobals
*g
, int numArgsPushed
);
1217 int prTempoClock_SetAll(struct VMGlobals
*g
, int numArgsPushed
)
1219 PyrSlot
*a
= g
->sp
- 3;
1220 PyrSlot
*b
= g
->sp
- 2;
1221 PyrSlot
*c
= g
->sp
- 1;
1224 TempoClock
*clock
= (TempoClock
*)slotRawPtr(&slotRawObject(a
)->slots
[1]);
1226 error("clock is not running.\n");
1230 double tempo
, beat
, secs
;
1231 int err
= slotDoubleVal(b
, &tempo
);
1232 if (err
) return errFailed
;
1234 err
= slotDoubleVal(c
, &beat
);
1235 if (err
) return errFailed
;
1237 err
= slotDoubleVal(d
, &secs
);
1238 if (err
) return errFailed
;
1240 clock
->SetAll(tempo
, beat
, secs
);
1245 int prTempoClock_SetTempoAtTime(struct VMGlobals
*g
, int numArgsPushed
);
1246 int prTempoClock_SetTempoAtTime(struct VMGlobals
*g
, int numArgsPushed
)
1248 PyrSlot
*a
= g
->sp
- 2;
1249 PyrSlot
*b
= g
->sp
- 1;
1252 TempoClock
*clock
= (TempoClock
*)slotRawPtr(&slotRawObject(a
)->slots
[1]);
1254 error("clock is not running.\n");
1259 int err
= slotDoubleVal(b
, &tempo
);
1260 if (err
) return errFailed
;
1262 err
= slotDoubleVal(c
, &sec
);
1263 if (err
) return errFailed
;
1265 clock
->SetTempoAtTime(tempo
, sec
);
1272 int prTempoClock_BeatsToSecs(struct VMGlobals
*g
, int numArgsPushed
);
1273 int prTempoClock_BeatsToSecs(struct VMGlobals
*g
, int numArgsPushed
)
1275 PyrSlot
*a
= g
->sp
- 1;
1278 TempoClock
*clock
= (TempoClock
*)slotRawPtr(&slotRawObject(a
)->slots
[1]);
1280 error("clock is not running.\n");
1285 int err
= slotDoubleVal(b
, &beats
);
1286 if (err
) return errFailed
;
1288 SetFloat(a
, clock
->BeatsToSecs(beats
));
1293 int prTempoClock_SecsToBeats(struct VMGlobals
*g
, int numArgsPushed
);
1294 int prTempoClock_SecsToBeats(struct VMGlobals
*g
, int numArgsPushed
)
1296 PyrSlot
*a
= g
->sp
- 1;
1299 TempoClock
*clock
= (TempoClock
*)slotRawPtr(&slotRawObject(a
)->slots
[1]);
1301 error("clock is not running.\n");
1306 int err
= slotDoubleVal(b
, &secs
);
1307 if (err
) return errFailed
;
1309 SetFloat(a
, clock
->SecsToBeats(secs
));
1315 int prSystemClock_Clear(struct VMGlobals
*g
, int numArgsPushed
);
1316 int prSystemClock_Clear(struct VMGlobals
*g
, int numArgsPushed
)
1318 //PyrSlot *a = g->sp;
1325 int prSystemClock_Sched(struct VMGlobals
*g
, int numArgsPushed
);
1326 int prSystemClock_Sched(struct VMGlobals
*g
, int numArgsPushed
)
1328 //PyrSlot *a = g->sp - 2;
1329 PyrSlot
*b
= g
->sp
- 1;
1332 double delta
, seconds
;
1333 int err
= slotDoubleVal(b
, &delta
);
1334 if (err
) return errNone
; // return nil OK, just don't schedule
1335 err
= slotDoubleVal(&g
->thread
->seconds
, &seconds
);
1336 if (err
) return errNone
; // return nil OK, just don't schedule
1338 if (seconds
== dInfinity
) return errNone
; // return nil OK, just don't schedule
1339 PyrObject
* inQueue
= slotRawObject(&g
->process
->sysSchedulerQueue
);
1341 schedAdd(g
, inQueue
, seconds
, c
);
1346 int prSystemClock_SchedAbs(struct VMGlobals
*g
, int numArgsPushed
);
1347 int prSystemClock_SchedAbs(struct VMGlobals
*g
, int numArgsPushed
)
1349 //PyrSlot *a = g->sp - 2;
1350 PyrSlot
*b
= g
->sp
- 1;
1354 int err
= slotDoubleVal(b
, &time
) || (time
== dInfinity
);
1355 if (err
) return errNone
; // return nil OK, just don't schedule
1356 PyrObject
* inQueue
= slotRawObject(&g
->process
->sysSchedulerQueue
);
1358 schedAdd(g
, inQueue
, time
, c
);
1363 int prElapsedTime(struct VMGlobals
*g
, int numArgsPushed
);
1364 int prElapsedTime(struct VMGlobals
*g
, int numArgsPushed
)
1366 SetFloat(g
->sp
, elapsedTime());
1370 void initSchedPrimitives()
1374 base
= nextPrimitiveIndex();
1376 definePrimitive(base
, index
++, "_TempoClock_New", prTempoClock_New
, 4, 0);
1377 definePrimitive(base
, index
++, "_TempoClock_Free", prTempoClock_Free
, 1, 0);
1378 definePrimitive(base
, index
++, "_TempoClock_Clear", prTempoClock_Clear
, 1, 0);
1379 definePrimitive(base
, index
++, "_TempoClock_Dump", prTempoClock_Dump
, 1, 0);
1380 definePrimitive(base
, index
++, "_TempoClock_Sched", prTempoClock_Sched
, 3, 0);
1381 definePrimitive(base
, index
++, "_TempoClock_SchedAbs", prTempoClock_SchedAbs
, 3, 0);
1382 definePrimitive(base
, index
++, "_TempoClock_Tempo", prTempoClock_Tempo
, 1, 0);
1383 definePrimitive(base
, index
++, "_TempoClock_BeatDur", prTempoClock_BeatDur
, 1, 0);
1384 definePrimitive(base
, index
++, "_TempoClock_ElapsedBeats", prTempoClock_ElapsedBeats
, 1, 0);
1385 definePrimitive(base
, index
++, "_TempoClock_Beats", prTempoClock_Beats
, 1, 0);
1386 definePrimitive(base
, index
++, "_TempoClock_SetTempoAtBeat", prTempoClock_SetTempoAtBeat
, 3, 0);
1387 definePrimitive(base
, index
++, "_TempoClock_SetTempoAtTime", prTempoClock_SetTempoAtTime
, 3, 0);
1388 definePrimitive(base
, index
++, "_TempoClock_SetAll", prTempoClock_SetAll
, 4, 0);
1389 definePrimitive(base
, index
++, "_TempoClock_BeatsToSecs", prTempoClock_BeatsToSecs
, 2, 0);
1390 definePrimitive(base
, index
++, "_TempoClock_SecsToBeats", prTempoClock_SecsToBeats
, 2, 0);
1392 definePrimitive(base
, index
++, "_SystemClock_Clear", prSystemClock_Clear
, 1, 0);
1393 definePrimitive(base
, index
++, "_SystemClock_Sched", prSystemClock_Sched
, 3, 0);
1394 definePrimitive(base
, index
++, "_SystemClock_SchedAbs", prSystemClock_SchedAbs
, 3, 0);
1396 definePrimitive(base
, index
++, "_ElapsedTime", prElapsedTime
, 1, 0);