diff options
Diffstat (limited to 'portmidi/porttime')
| -rwxr-xr-x | portmidi/porttime/porttime.c | 3 | ||||
| -rwxr-xr-x | portmidi/porttime/porttime.h | 103 | ||||
| -rw-r--r-- | portmidi/porttime/pthaiku.cpp | 88 | ||||
| -rwxr-xr-x | portmidi/porttime/ptlinux.c | 139 | ||||
| -rwxr-xr-x | portmidi/porttime/ptmacosx_cf.c | 140 | ||||
| -rwxr-xr-x | portmidi/porttime/ptmacosx_mach.c | 204 | ||||
| -rwxr-xr-x | portmidi/porttime/ptwinmm.c | 70 |
7 files changed, 747 insertions, 0 deletions
diff --git a/portmidi/porttime/porttime.c b/portmidi/porttime/porttime.c new file mode 100755 index 0000000..71b06f4 --- /dev/null +++ b/portmidi/porttime/porttime.c | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | /* porttime.c -- portable API for millisecond timer */ | ||
| 2 | |||
| 3 | /* There is no machine-independent implementation code to put here */ | ||
diff --git a/portmidi/porttime/porttime.h b/portmidi/porttime/porttime.h new file mode 100755 index 0000000..0a61c5c --- /dev/null +++ b/portmidi/porttime/porttime.h | |||
| @@ -0,0 +1,103 @@ | |||
| 1 | /** @file porttime.h portable interface to millisecond timer. */ | ||
| 2 | |||
| 3 | /* CHANGE LOG FOR PORTTIME | ||
| 4 | 10-Jun-03 Mark Nelson & RBD | ||
| 5 | boost priority of timer thread in ptlinux.c implementation | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef PORTMIDI_PORTTIME_H | ||
| 9 | #define PORTMIDI_PORTTIME_H | ||
| 10 | |||
| 11 | /* Should there be a way to choose the source of time here? */ | ||
| 12 | |||
| 13 | #ifdef WIN32 | ||
| 14 | #ifndef INT32_DEFINED | ||
| 15 | // rather than having users install a special .h file for windows, | ||
| 16 | // just put the required definitions inline here. portmidi.h uses | ||
| 17 | // these too, so the definitions are (unfortunately) duplicated there | ||
| 18 | typedef int int32_t; | ||
| 19 | typedef unsigned int uint32_t; | ||
| 20 | #define INT32_DEFINED | ||
| 21 | #endif | ||
| 22 | #else | ||
| 23 | #include <stdint.h> // needed for int32_t | ||
| 24 | #endif | ||
| 25 | |||
| 26 | #ifdef __cplusplus | ||
| 27 | extern "C" { | ||
| 28 | #endif | ||
| 29 | |||
| 30 | #ifndef PMEXPORT | ||
| 31 | #ifdef _WINDLL | ||
| 32 | #define PMEXPORT __declspec(dllexport) | ||
| 33 | #else | ||
| 34 | #define PMEXPORT | ||
| 35 | #endif | ||
| 36 | #endif | ||
| 37 | |||
| 38 | /** @defgroup grp_porttime PortTime: Millisecond Timer | ||
| 39 | @{ | ||
| 40 | */ | ||
| 41 | |||
| 42 | typedef enum { | ||
| 43 | ptNoError = 0, /* success */ | ||
| 44 | ptHostError = -10000, /* a system-specific error occurred */ | ||
| 45 | ptAlreadyStarted, /* cannot start timer because it is already started */ | ||
| 46 | ptAlreadyStopped, /* cannot stop timer because it is already stopped */ | ||
| 47 | ptInsufficientMemory /* memory could not be allocated */ | ||
| 48 | } PtError; /**< @brief @enum PtError PortTime error code; a common return type. | ||
| 49 | * No error is indicated by zero; errors are indicated by < 0. | ||
| 50 | */ | ||
| 51 | |||
| 52 | /** real time or time offset in milliseconds. */ | ||
| 53 | typedef int32_t PtTimestamp; | ||
| 54 | |||
| 55 | /** a function that gets a current time */ | ||
| 56 | typedef void (PtCallback)(PtTimestamp timestamp, void *userData); | ||
| 57 | |||
| 58 | /** start a real-time clock service. | ||
| 59 | |||
| 60 | @param resolution the timer resolution in ms. The time will advance every | ||
| 61 | \p resolution ms. | ||
| 62 | |||
| 63 | @param callback a function pointer to be called every resolution ms. | ||
| 64 | |||
| 65 | @param userData is passed to \p callback as a parameter. | ||
| 66 | |||
| 67 | @return #ptNoError on success. See #PtError for other values. | ||
| 68 | */ | ||
| 69 | PMEXPORT PtError Pt_Start(int resolution, PtCallback *callback, void *userData); | ||
| 70 | |||
| 71 | /** stop the timer. | ||
| 72 | |||
| 73 | @return #ptNoError on success. See #PtError for other values. | ||
| 74 | */ | ||
| 75 | PMEXPORT PtError Pt_Stop(void); | ||
| 76 | |||
| 77 | /** test if the timer is running. | ||
| 78 | |||
| 79 | @return TRUE or FALSE | ||
| 80 | */ | ||
| 81 | PMEXPORT int Pt_Started(void); | ||
| 82 | |||
| 83 | /** get the current time in ms. | ||
| 84 | |||
| 85 | @return the current time | ||
| 86 | */ | ||
| 87 | PMEXPORT PtTimestamp Pt_Time(void); | ||
| 88 | |||
| 89 | /** pauses the current thread, allowing other threads to run. | ||
| 90 | |||
| 91 | @param duration the length of the pause in ms. The true duration | ||
| 92 | of the pause may be rounded to the nearest or next clock tick | ||
| 93 | as determined by resolution in #Pt_Start(). | ||
| 94 | */ | ||
| 95 | PMEXPORT void Pt_Sleep(int32_t duration); | ||
| 96 | |||
| 97 | /** @} */ | ||
| 98 | |||
| 99 | #ifdef __cplusplus | ||
| 100 | } | ||
| 101 | #endif | ||
| 102 | |||
| 103 | #endif // PORTMIDI_PORTTIME_H | ||
diff --git a/portmidi/porttime/pthaiku.cpp b/portmidi/porttime/pthaiku.cpp new file mode 100644 index 0000000..9d8de14 --- /dev/null +++ b/portmidi/porttime/pthaiku.cpp | |||
| @@ -0,0 +1,88 @@ | |||
| 1 | // pthaiku.cpp - portable timer implementation for Haiku | ||
| 2 | |||
| 3 | #include "porttime.h" | ||
| 4 | #include <Looper.h> | ||
| 5 | #include <MessageRunner.h> | ||
| 6 | #include <OS.h> | ||
| 7 | |||
| 8 | namespace { | ||
| 9 | const uint32 timerMessage = 'PTTM'; | ||
| 10 | |||
| 11 | struct TimerLooper : BLooper { | ||
| 12 | TimerLooper() : BLooper() { | ||
| 13 | } | ||
| 14 | |||
| 15 | |||
| 16 | virtual void MessageReceived(BMessage *message) | ||
| 17 | { | ||
| 18 | PtCallback *callback; | ||
| 19 | void *userData; | ||
| 20 | if (message->what == timerMessage && message->FindPointer("callback", (void**)&callback) == B_OK && message->FindPointer("userData", &userData) == B_OK) { | ||
| 21 | (*callback)(Pt_Time(), userData); | ||
| 22 | } | ||
| 23 | BLooper::MessageReceived(message); | ||
| 24 | } | ||
| 25 | }; | ||
| 26 | |||
| 27 | bool time_started_flag = false; | ||
| 28 | bigtime_t time_offset; | ||
| 29 | TimerLooper *timerLooper; | ||
| 30 | BMessageRunner *timerRunner; | ||
| 31 | } | ||
| 32 | |||
| 33 | extern "C" { | ||
| 34 | PtError Pt_Start(int resolution, PtCallback *callback, void *userData) | ||
| 35 | { | ||
| 36 | if (time_started_flag) return ptAlreadyStarted; | ||
| 37 | time_offset = system_time(); | ||
| 38 | if (callback) { | ||
| 39 | timerLooper = new TimerLooper; | ||
| 40 | timerLooper->Run(); | ||
| 41 | BMessenger target(timerLooper); | ||
| 42 | BMessage message(timerMessage); | ||
| 43 | message.AddPointer("callback", (void*)callback); | ||
| 44 | message.AddPointer("userData", userData); | ||
| 45 | bigtime_t interval = resolution * 1000; | ||
| 46 | timerRunner = new BMessageRunner(target, &message, interval); | ||
| 47 | if(timerRunner->InitCheck() != B_OK) { | ||
| 48 | delete timerRunner; | ||
| 49 | timerRunner = NULL; | ||
| 50 | timerLooper->PostMessage(B_QUIT_REQUESTED); | ||
| 51 | timerLooper = NULL; | ||
| 52 | return ptHostError; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | time_started_flag = true; | ||
| 56 | return ptNoError; | ||
| 57 | } | ||
| 58 | |||
| 59 | |||
| 60 | PtError Pt_Stop() | ||
| 61 | { | ||
| 62 | if (!time_started_flag) return ptAlreadyStopped; | ||
| 63 | time_started_flag = false; | ||
| 64 | delete timerRunner; | ||
| 65 | timerRunner = NULL; | ||
| 66 | timerLooper->PostMessage(B_QUIT_REQUESTED); | ||
| 67 | timerLooper = NULL; | ||
| 68 | return ptNoError; | ||
| 69 | } | ||
| 70 | |||
| 71 | |||
| 72 | int Pt_Started() | ||
| 73 | { | ||
| 74 | return time_started_flag; | ||
| 75 | } | ||
| 76 | |||
| 77 | |||
| 78 | PtTimestamp Pt_Time() | ||
| 79 | { | ||
| 80 | return (system_time() - time_offset) / 1000; | ||
| 81 | } | ||
| 82 | |||
| 83 | |||
| 84 | void Pt_Sleep(int32_t duration) | ||
| 85 | { | ||
| 86 | snooze(duration * 1000); | ||
| 87 | } | ||
| 88 | } | ||
diff --git a/portmidi/porttime/ptlinux.c b/portmidi/porttime/ptlinux.c new file mode 100755 index 0000000..c4af5c1 --- /dev/null +++ b/portmidi/porttime/ptlinux.c | |||
| @@ -0,0 +1,139 @@ | |||
| 1 | /* ptlinux.c -- portable timer implementation for linux */ | ||
| 2 | |||
| 3 | |||
| 4 | /* IMPLEMENTATION NOTES (by Mark Nelson): | ||
| 5 | |||
| 6 | Unlike Windows, Linux has no system call to request a periodic callback, | ||
| 7 | so if Pt_Start() receives a callback parameter, it must create a thread | ||
| 8 | that wakes up periodically and calls the provided callback function. | ||
| 9 | If running as superuser, use setpriority() to renice thread to -20. | ||
| 10 | One could also set the timer thread to a real-time priority (SCHED_FIFO | ||
| 11 | and SCHED_RR), but this is dangerous for This is necessary because | ||
| 12 | if the callback hangs it'll never return. A more serious reason | ||
| 13 | is that the current scheduler implementation busy-waits instead | ||
| 14 | of sleeping when realtime threads request a sleep of <=2ms (as a way | ||
| 15 | to get around the 10ms granularity), which means the thread would never | ||
| 16 | let anyone else on the CPU. | ||
| 17 | |||
| 18 | CHANGE LOG | ||
| 19 | |||
| 20 | 18-Jul-03 Roger Dannenberg -- Simplified code to set priority of timer | ||
| 21 | thread. Simplified implementation notes. | ||
| 22 | |||
| 23 | */ | ||
| 24 | /* stdlib, stdio, unistd, and sys/types were added because they appeared | ||
| 25 | * in a Gentoo patch, but I'm not sure why they are needed. -RBD | ||
| 26 | */ | ||
| 27 | #include <stdlib.h> | ||
| 28 | #include <stdio.h> | ||
| 29 | #include <unistd.h> | ||
| 30 | #include <sys/types.h> | ||
| 31 | #include "porttime.h" | ||
| 32 | #include "time.h" | ||
| 33 | #include "sys/resource.h" | ||
| 34 | #include "pthread.h" | ||
| 35 | |||
| 36 | #define TRUE 1 | ||
| 37 | #define FALSE 0 | ||
| 38 | |||
| 39 | #ifndef CLOCK_MONOTONIC_RAW | ||
| 40 | #define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC | ||
| 41 | #endif | ||
| 42 | |||
| 43 | static int time_started_flag = FALSE; | ||
| 44 | static struct timespec time_offset = {0, 0}; | ||
| 45 | static pthread_t pt_thread_pid; | ||
| 46 | static int pt_thread_created = FALSE; | ||
| 47 | |||
| 48 | /* note that this is static data -- we only need one copy */ | ||
| 49 | typedef struct { | ||
| 50 | int id; | ||
| 51 | int resolution; | ||
| 52 | PtCallback *callback; | ||
| 53 | void *userData; | ||
| 54 | } pt_callback_parameters; | ||
| 55 | |||
| 56 | static int pt_callback_proc_id = 0; | ||
| 57 | |||
| 58 | static void *Pt_CallbackProc(void *p) | ||
| 59 | { | ||
| 60 | pt_callback_parameters *parameters = (pt_callback_parameters *) p; | ||
| 61 | int mytime = 1; | ||
| 62 | /* to kill a process, just increment the pt_callback_proc_id */ | ||
| 63 | /* printf("pt_callback_proc_id %d, id %d\n", pt_callback_proc_id, | ||
| 64 | parameters->id); */ | ||
| 65 | if (geteuid() == 0) setpriority(PRIO_PROCESS, 0, -20); | ||
| 66 | while (pt_callback_proc_id == parameters->id) { | ||
| 67 | /* wait for a multiple of resolution ms */ | ||
| 68 | struct timeval timeout; | ||
| 69 | int delay = mytime++ * parameters->resolution - Pt_Time(); | ||
| 70 | if (delay < 0) delay = 0; | ||
| 71 | timeout.tv_sec = 0; | ||
| 72 | timeout.tv_usec = delay * 1000; | ||
| 73 | select(0, NULL, NULL, NULL, &timeout); | ||
| 74 | (*(parameters->callback))(Pt_Time(), parameters->userData); | ||
| 75 | } | ||
| 76 | /* printf("Pt_CallbackProc exiting\n"); */ | ||
| 77 | // free(parameters); | ||
| 78 | return NULL; | ||
| 79 | } | ||
| 80 | |||
| 81 | |||
| 82 | PtError Pt_Start(int resolution, PtCallback *callback, void *userData) | ||
| 83 | { | ||
| 84 | if (time_started_flag) return ptNoError; | ||
| 85 | /* need this set before process runs: */ | ||
| 86 | clock_gettime(CLOCK_MONOTONIC_RAW, &time_offset); | ||
| 87 | if (callback) { | ||
| 88 | int res; | ||
| 89 | pt_callback_parameters *parms = (pt_callback_parameters *) | ||
| 90 | malloc(sizeof(pt_callback_parameters)); | ||
| 91 | if (!parms) return ptInsufficientMemory; | ||
| 92 | parms->id = pt_callback_proc_id; | ||
| 93 | parms->resolution = resolution; | ||
| 94 | parms->callback = callback; | ||
| 95 | parms->userData = userData; | ||
| 96 | res = pthread_create(&pt_thread_pid, NULL, | ||
| 97 | Pt_CallbackProc, parms); | ||
| 98 | if (res != 0) return ptHostError; | ||
| 99 | pt_thread_created = TRUE; | ||
| 100 | } | ||
| 101 | time_started_flag = TRUE; | ||
| 102 | return ptNoError; | ||
| 103 | } | ||
| 104 | |||
| 105 | |||
| 106 | PtError Pt_Stop() | ||
| 107 | { | ||
| 108 | /* printf("Pt_Stop called\n"); */ | ||
| 109 | pt_callback_proc_id++; | ||
| 110 | if (pt_thread_created) { | ||
| 111 | pthread_join(pt_thread_pid, NULL); | ||
| 112 | pt_thread_created = FALSE; | ||
| 113 | } | ||
| 114 | time_started_flag = FALSE; | ||
| 115 | return ptNoError; | ||
| 116 | } | ||
| 117 | |||
| 118 | |||
| 119 | int Pt_Started() | ||
| 120 | { | ||
| 121 | return time_started_flag; | ||
| 122 | } | ||
| 123 | |||
| 124 | |||
| 125 | PtTimestamp Pt_Time() | ||
| 126 | { | ||
| 127 | long seconds, ms; | ||
| 128 | struct timespec now; | ||
| 129 | clock_gettime(CLOCK_MONOTONIC_RAW, &now); | ||
| 130 | seconds = now.tv_sec - time_offset.tv_sec; | ||
| 131 | ms = (now.tv_nsec - time_offset.tv_nsec) / 1000000; /* round down */ | ||
| 132 | return seconds * 1000 + ms; | ||
| 133 | } | ||
| 134 | |||
| 135 | |||
| 136 | void Pt_Sleep(int32_t duration) | ||
| 137 | { | ||
| 138 | usleep(duration * 1000); | ||
| 139 | } | ||
diff --git a/portmidi/porttime/ptmacosx_cf.c b/portmidi/porttime/ptmacosx_cf.c new file mode 100755 index 0000000..a174c86 --- /dev/null +++ b/portmidi/porttime/ptmacosx_cf.c | |||
| @@ -0,0 +1,140 @@ | |||
| 1 | /* ptmacosx.c -- portable timer implementation for mac os x */ | ||
| 2 | |||
| 3 | #include <stdlib.h> | ||
| 4 | #include <stdio.h> | ||
| 5 | #include <pthread.h> | ||
| 6 | #include <CoreFoundation/CoreFoundation.h> | ||
| 7 | |||
| 8 | #import <mach/mach.h> | ||
| 9 | #import <mach/mach_error.h> | ||
| 10 | #import <mach/mach_time.h> | ||
| 11 | #import <mach/clock.h> | ||
| 12 | |||
| 13 | #include "porttime.h" | ||
| 14 | |||
| 15 | #define THREAD_IMPORTANCE 30 | ||
| 16 | #define LONG_TIME 1000000000.0 | ||
| 17 | |||
| 18 | static int time_started_flag = FALSE; | ||
| 19 | static CFAbsoluteTime startTime = 0.0; | ||
| 20 | static CFRunLoopRef timerRunLoop; | ||
| 21 | |||
| 22 | typedef struct { | ||
| 23 | int resolution; | ||
| 24 | PtCallback *callback; | ||
| 25 | void *userData; | ||
| 26 | } PtThreadParams; | ||
| 27 | |||
| 28 | |||
| 29 | void Pt_CFTimerCallback(CFRunLoopTimerRef timer, void *info) | ||
| 30 | { | ||
| 31 | PtThreadParams *params = (PtThreadParams*)info; | ||
| 32 | (*params->callback)(Pt_Time(), params->userData); | ||
| 33 | } | ||
| 34 | |||
| 35 | static void* Pt_Thread(void *p) | ||
| 36 | { | ||
| 37 | CFTimeInterval timerInterval; | ||
| 38 | CFRunLoopTimerContext timerContext; | ||
| 39 | CFRunLoopTimerRef timer; | ||
| 40 | PtThreadParams *params = (PtThreadParams*)p; | ||
| 41 | //CFTimeInterval timeout; | ||
| 42 | |||
| 43 | /* raise the thread's priority */ | ||
| 44 | kern_return_t error; | ||
| 45 | thread_extended_policy_data_t extendedPolicy; | ||
| 46 | thread_precedence_policy_data_t precedencePolicy; | ||
| 47 | |||
| 48 | extendedPolicy.timeshare = 0; | ||
| 49 | error = thread_policy_set(mach_thread_self(), THREAD_EXTENDED_POLICY, | ||
| 50 | (thread_policy_t)&extendedPolicy, | ||
| 51 | THREAD_EXTENDED_POLICY_COUNT); | ||
| 52 | if (error != KERN_SUCCESS) { | ||
| 53 | mach_error("Couldn't set thread timeshare policy", error); | ||
| 54 | } | ||
| 55 | |||
| 56 | precedencePolicy.importance = THREAD_IMPORTANCE; | ||
| 57 | error = thread_policy_set(mach_thread_self(), THREAD_PRECEDENCE_POLICY, | ||
| 58 | (thread_policy_t)&precedencePolicy, | ||
| 59 | THREAD_PRECEDENCE_POLICY_COUNT); | ||
| 60 | if (error != KERN_SUCCESS) { | ||
| 61 | mach_error("Couldn't set thread precedence policy", error); | ||
| 62 | } | ||
| 63 | |||
| 64 | /* set up the timer context */ | ||
| 65 | timerContext.version = 0; | ||
| 66 | timerContext.info = params; | ||
| 67 | timerContext.retain = NULL; | ||
| 68 | timerContext.release = NULL; | ||
| 69 | timerContext.copyDescription = NULL; | ||
| 70 | |||
| 71 | /* create a new timer */ | ||
| 72 | timerInterval = (double)params->resolution / 1000.0; | ||
| 73 | timer = CFRunLoopTimerCreate(NULL, startTime+timerInterval, timerInterval, | ||
| 74 | 0, 0, Pt_CFTimerCallback, &timerContext); | ||
| 75 | |||
| 76 | timerRunLoop = CFRunLoopGetCurrent(); | ||
| 77 | CFRunLoopAddTimer(timerRunLoop, timer, CFSTR("PtTimeMode")); | ||
| 78 | |||
| 79 | /* run until we're told to stop by Pt_Stop() */ | ||
| 80 | CFRunLoopRunInMode(CFSTR("PtTimeMode"), LONG_TIME, false); | ||
| 81 | |||
| 82 | CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer, CFSTR("PtTimeMode")); | ||
| 83 | CFRelease(timer); | ||
| 84 | free(params); | ||
| 85 | |||
| 86 | return NULL; | ||
| 87 | } | ||
| 88 | |||
| 89 | PtError Pt_Start(int resolution, PtCallback *callback, void *userData) | ||
| 90 | { | ||
| 91 | PtThreadParams *params = (PtThreadParams*)malloc(sizeof(PtThreadParams)); | ||
| 92 | pthread_t pthread_id; | ||
| 93 | |||
| 94 | printf("Pt_Start() called\n"); | ||
| 95 | |||
| 96 | // /* make sure we're not already playing */ | ||
| 97 | if (time_started_flag) return ptAlreadyStarted; | ||
| 98 | startTime = CFAbsoluteTimeGetCurrent(); | ||
| 99 | |||
| 100 | if (callback) { | ||
| 101 | |||
| 102 | params->resolution = resolution; | ||
| 103 | params->callback = callback; | ||
| 104 | params->userData = userData; | ||
| 105 | |||
| 106 | pthread_create(&pthread_id, NULL, Pt_Thread, params); | ||
| 107 | } | ||
| 108 | |||
| 109 | time_started_flag = TRUE; | ||
| 110 | return ptNoError; | ||
| 111 | } | ||
| 112 | |||
| 113 | |||
| 114 | PtError Pt_Stop() | ||
| 115 | { | ||
| 116 | printf("Pt_Stop called\n"); | ||
| 117 | |||
| 118 | CFRunLoopStop(timerRunLoop); | ||
| 119 | time_started_flag = FALSE; | ||
| 120 | return ptNoError; | ||
| 121 | } | ||
| 122 | |||
| 123 | |||
| 124 | int Pt_Started() | ||
| 125 | { | ||
| 126 | return time_started_flag; | ||
| 127 | } | ||
| 128 | |||
| 129 | |||
| 130 | PtTimestamp Pt_Time() | ||
| 131 | { | ||
| 132 | CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); | ||
| 133 | return (PtTimestamp) ((now - startTime) * 1000.0); | ||
| 134 | } | ||
| 135 | |||
| 136 | |||
| 137 | void Pt_Sleep(int32_t duration) | ||
| 138 | { | ||
| 139 | usleep(duration * 1000); | ||
| 140 | } | ||
diff --git a/portmidi/porttime/ptmacosx_mach.c b/portmidi/porttime/ptmacosx_mach.c new file mode 100755 index 0000000..1390af8 --- /dev/null +++ b/portmidi/porttime/ptmacosx_mach.c | |||
| @@ -0,0 +1,204 @@ | |||
| 1 | /* ptmacosx.c -- portable timer implementation for mac os x */ | ||
| 2 | |||
| 3 | #include <stdlib.h> | ||
| 4 | #include <stdio.h> | ||
| 5 | #include <CoreAudio/HostTime.h> | ||
| 6 | |||
| 7 | #import <mach/mach.h> | ||
| 8 | #import <mach/mach_error.h> | ||
| 9 | #import <mach/mach_time.h> | ||
| 10 | #import <mach/clock.h> | ||
| 11 | #include <unistd.h> | ||
| 12 | #include <AvailabilityMacros.h> | ||
| 13 | |||
| 14 | #include "porttime.h" | ||
| 15 | #include "sys/time.h" | ||
| 16 | #include "pthread.h" | ||
| 17 | |||
| 18 | #ifndef NSEC_PER_MSEC | ||
| 19 | #define NSEC_PER_MSEC 1000000 | ||
| 20 | #endif | ||
| 21 | #define THREAD_IMPORTANCE 63 | ||
| 22 | |||
| 23 | // QOS headers are available as of macOS 10.10 | ||
| 24 | #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 | ||
| 25 | #include "sys/qos.h" | ||
| 26 | #define HAVE_APPLE_QOS 1 | ||
| 27 | #else | ||
| 28 | #undef HAVE_APPLE_QOS | ||
| 29 | #endif | ||
| 30 | |||
| 31 | static int time_started_flag = FALSE; | ||
| 32 | static UInt64 start_time; | ||
| 33 | static pthread_t pt_thread_pid; | ||
| 34 | |||
| 35 | /* note that this is static data -- we only need one copy */ | ||
| 36 | typedef struct { | ||
| 37 | int id; | ||
| 38 | int resolution; | ||
| 39 | PtCallback *callback; | ||
| 40 | void *userData; | ||
| 41 | } pt_callback_parameters; | ||
| 42 | |||
| 43 | static int pt_callback_proc_id = 0; | ||
| 44 | |||
| 45 | static void *Pt_CallbackProc(void *p) | ||
| 46 | { | ||
| 47 | pt_callback_parameters *parameters = (pt_callback_parameters *) p; | ||
| 48 | int mytime = 1; | ||
| 49 | |||
| 50 | kern_return_t error; | ||
| 51 | thread_extended_policy_data_t extendedPolicy; | ||
| 52 | thread_precedence_policy_data_t precedencePolicy; | ||
| 53 | |||
| 54 | extendedPolicy.timeshare = 0; | ||
| 55 | error = thread_policy_set(mach_thread_self(), THREAD_EXTENDED_POLICY, | ||
| 56 | (thread_policy_t)&extendedPolicy, | ||
| 57 | THREAD_EXTENDED_POLICY_COUNT); | ||
| 58 | if (error != KERN_SUCCESS) { | ||
| 59 | mach_error("Couldn't set thread timeshare policy", error); | ||
| 60 | } | ||
| 61 | |||
| 62 | precedencePolicy.importance = THREAD_IMPORTANCE; | ||
| 63 | error = thread_policy_set(mach_thread_self(), THREAD_PRECEDENCE_POLICY, | ||
| 64 | (thread_policy_t)&precedencePolicy, | ||
| 65 | THREAD_PRECEDENCE_POLICY_COUNT); | ||
| 66 | if (error != KERN_SUCCESS) { | ||
| 67 | mach_error("Couldn't set thread precedence policy", error); | ||
| 68 | } | ||
| 69 | |||
| 70 | // Most important, set real-time constraints. | ||
| 71 | |||
| 72 | // Define the guaranteed and max fraction of time for the audio thread. | ||
| 73 | // These "duty cycle" values can range from 0 to 1. A value of 0.5 | ||
| 74 | // means the scheduler would give half the time to the thread. | ||
| 75 | // These values have empirically been found to yield good behavior. | ||
| 76 | // Good means that audio performance is high and other threads won't starve. | ||
| 77 | const double kGuaranteedAudioDutyCycle = 0.75; | ||
| 78 | const double kMaxAudioDutyCycle = 0.85; | ||
| 79 | |||
| 80 | // Define constants determining how much time the audio thread can | ||
| 81 | // use in a given time quantum. All times are in milliseconds. | ||
| 82 | |||
| 83 | // About 128 frames @44.1KHz | ||
| 84 | const double kTimeQuantum = 2.9; | ||
| 85 | |||
| 86 | // Time guaranteed each quantum. | ||
| 87 | const double kAudioTimeNeeded = kGuaranteedAudioDutyCycle * kTimeQuantum; | ||
| 88 | |||
| 89 | // Maximum time each quantum. | ||
| 90 | const double kMaxTimeAllowed = kMaxAudioDutyCycle * kTimeQuantum; | ||
| 91 | |||
| 92 | // Get the conversion factor from milliseconds to absolute time | ||
| 93 | // which is what the time-constraints call needs. | ||
| 94 | mach_timebase_info_data_t tb_info; | ||
| 95 | mach_timebase_info(&tb_info); | ||
| 96 | double ms_to_abs_time = | ||
| 97 | ((double)tb_info.denom / (double)tb_info.numer) * 1000000; | ||
| 98 | |||
| 99 | thread_time_constraint_policy_data_t time_constraints; | ||
| 100 | time_constraints.period = (uint32_t)(kTimeQuantum * ms_to_abs_time); | ||
| 101 | time_constraints.computation = (uint32_t)(kAudioTimeNeeded * ms_to_abs_time); | ||
| 102 | time_constraints.constraint = (uint32_t)(kMaxTimeAllowed * ms_to_abs_time); | ||
| 103 | time_constraints.preemptible = 0; | ||
| 104 | |||
| 105 | error = thread_policy_set(mach_thread_self(), | ||
| 106 | THREAD_TIME_CONSTRAINT_POLICY, | ||
| 107 | (thread_policy_t)&time_constraints, | ||
| 108 | THREAD_TIME_CONSTRAINT_POLICY_COUNT); | ||
| 109 | if (error != KERN_SUCCESS) { | ||
| 110 | mach_error("Couldn't set thread precedence policy", error); | ||
| 111 | } | ||
| 112 | |||
| 113 | /* to kill a process, just increment the pt_callback_proc_id */ | ||
| 114 | /* printf("pt_callback_proc_id %d, id %d\n", pt_callback_proc_id, | ||
| 115 | parameters->id); */ | ||
| 116 | while (pt_callback_proc_id == parameters->id) { | ||
| 117 | /* wait for a multiple of resolution ms */ | ||
| 118 | UInt64 wait_time; | ||
| 119 | int delay = mytime++ * parameters->resolution - Pt_Time(); | ||
| 120 | PtTimestamp timestamp; | ||
| 121 | if (delay < 0) delay = 0; | ||
| 122 | wait_time = AudioConvertNanosToHostTime((UInt64)delay * NSEC_PER_MSEC); | ||
| 123 | wait_time += AudioGetCurrentHostTime(); | ||
| 124 | mach_wait_until(wait_time); | ||
| 125 | timestamp = Pt_Time(); | ||
| 126 | (*(parameters->callback))(timestamp, parameters->userData); | ||
| 127 | } | ||
| 128 | free(parameters); | ||
| 129 | return NULL; | ||
| 130 | } | ||
| 131 | |||
| 132 | |||
| 133 | PtError Pt_Start(int resolution, PtCallback *callback, void *userData) | ||
| 134 | { | ||
| 135 | if (time_started_flag) return ptAlreadyStarted; | ||
| 136 | start_time = AudioGetCurrentHostTime(); | ||
| 137 | |||
| 138 | if (callback) { | ||
| 139 | int res; | ||
| 140 | pt_callback_parameters *parms; | ||
| 141 | |||
| 142 | parms = (pt_callback_parameters *) malloc(sizeof(pt_callback_parameters)); | ||
| 143 | if (!parms) return ptInsufficientMemory; | ||
| 144 | parms->id = pt_callback_proc_id; | ||
| 145 | parms->resolution = resolution; | ||
| 146 | parms->callback = callback; | ||
| 147 | parms->userData = userData; | ||
| 148 | |||
| 149 | #ifdef HAVE_APPLE_QOS | ||
| 150 | pthread_attr_t qosAttribute; | ||
| 151 | pthread_attr_init(&qosAttribute); | ||
| 152 | pthread_attr_set_qos_class_np(&qosAttribute, | ||
| 153 | QOS_CLASS_USER_INTERACTIVE, 0); | ||
| 154 | |||
| 155 | res = pthread_create(&pt_thread_pid, &qosAttribute, Pt_CallbackProc, | ||
| 156 | parms); | ||
| 157 | #else | ||
| 158 | res = pthread_create(&pt_thread_pid, NULL, Pt_CallbackProc, parms); | ||
| 159 | #endif | ||
| 160 | |||
| 161 | struct sched_param sp; | ||
| 162 | memset(&sp, 0, sizeof(struct sched_param)); | ||
| 163 | sp.sched_priority = sched_get_priority_max(SCHED_RR); | ||
| 164 | if (pthread_setschedparam(pthread_self(), SCHED_RR, &sp) == -1) { | ||
| 165 | return ptHostError; | ||
| 166 | } | ||
| 167 | |||
| 168 | if (res != 0) return ptHostError; | ||
| 169 | } | ||
| 170 | |||
| 171 | time_started_flag = TRUE; | ||
| 172 | return ptNoError; | ||
| 173 | } | ||
| 174 | |||
| 175 | |||
| 176 | PtError Pt_Stop(void) | ||
| 177 | { | ||
| 178 | /* printf("Pt_Stop called\n"); */ | ||
| 179 | pt_callback_proc_id++; | ||
| 180 | pthread_join(pt_thread_pid, NULL); | ||
| 181 | time_started_flag = FALSE; | ||
| 182 | return ptNoError; | ||
| 183 | } | ||
| 184 | |||
| 185 | |||
| 186 | int Pt_Started(void) | ||
| 187 | { | ||
| 188 | return time_started_flag; | ||
| 189 | } | ||
| 190 | |||
| 191 | |||
| 192 | PtTimestamp Pt_Time(void) | ||
| 193 | { | ||
| 194 | UInt64 clock_time, nsec_time; | ||
| 195 | clock_time = AudioGetCurrentHostTime() - start_time; | ||
| 196 | nsec_time = AudioConvertHostTimeToNanos(clock_time); | ||
| 197 | return (PtTimestamp)(nsec_time / NSEC_PER_MSEC); | ||
| 198 | } | ||
| 199 | |||
| 200 | |||
| 201 | void Pt_Sleep(int32_t duration) | ||
| 202 | { | ||
| 203 | usleep(duration * 1000); | ||
| 204 | } | ||
diff --git a/portmidi/porttime/ptwinmm.c b/portmidi/porttime/ptwinmm.c new file mode 100755 index 0000000..5204659 --- /dev/null +++ b/portmidi/porttime/ptwinmm.c | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | /* ptwinmm.c -- portable timer implementation for win32 */ | ||
| 2 | |||
| 3 | |||
| 4 | #include "porttime.h" | ||
| 5 | #include "windows.h" | ||
| 6 | #include "time.h" | ||
| 7 | |||
| 8 | |||
| 9 | TIMECAPS caps; | ||
| 10 | |||
| 11 | static long time_offset = 0; | ||
| 12 | static int time_started_flag = FALSE; | ||
| 13 | static long time_resolution; | ||
| 14 | static MMRESULT timer_id; | ||
| 15 | static PtCallback *time_callback; | ||
| 16 | |||
| 17 | void CALLBACK winmm_time_callback(UINT uID, UINT uMsg, DWORD_PTR dwUser, | ||
| 18 | DWORD_PTR dw1, DWORD_PTR dw2) | ||
| 19 | { | ||
| 20 | (*time_callback)(Pt_Time(), (void *) dwUser); | ||
| 21 | } | ||
| 22 | |||
| 23 | |||
| 24 | PMEXPORT PtError Pt_Start(int resolution, PtCallback *callback, void *userData) | ||
| 25 | { | ||
| 26 | if (time_started_flag) return ptAlreadyStarted; | ||
| 27 | timeBeginPeriod(resolution); | ||
| 28 | time_resolution = resolution; | ||
| 29 | time_offset = timeGetTime(); | ||
| 30 | time_started_flag = TRUE; | ||
| 31 | time_callback = callback; | ||
| 32 | if (callback) { | ||
| 33 | timer_id = timeSetEvent(resolution, 1, winmm_time_callback, | ||
| 34 | (DWORD_PTR) userData, TIME_PERIODIC | TIME_CALLBACK_FUNCTION); | ||
| 35 | if (!timer_id) return ptHostError; | ||
| 36 | } | ||
| 37 | return ptNoError; | ||
| 38 | } | ||
| 39 | |||
| 40 | |||
| 41 | PMEXPORT PtError Pt_Stop() | ||
| 42 | { | ||
| 43 | if (!time_started_flag) return ptAlreadyStopped; | ||
| 44 | if (time_callback && timer_id) { | ||
| 45 | timeKillEvent(timer_id); | ||
| 46 | time_callback = NULL; | ||
| 47 | timer_id = 0; | ||
| 48 | } | ||
| 49 | time_started_flag = FALSE; | ||
| 50 | timeEndPeriod(time_resolution); | ||
| 51 | return ptNoError; | ||
| 52 | } | ||
| 53 | |||
| 54 | |||
| 55 | PMEXPORT int Pt_Started() | ||
| 56 | { | ||
| 57 | return time_started_flag; | ||
| 58 | } | ||
| 59 | |||
| 60 | |||
| 61 | PMEXPORT PtTimestamp Pt_Time() | ||
| 62 | { | ||
| 63 | return timeGetTime() - time_offset; | ||
| 64 | } | ||
| 65 | |||
| 66 | |||
| 67 | PMEXPORT void Pt_Sleep(int32_t duration) | ||
| 68 | { | ||
| 69 | Sleep(duration); | ||
| 70 | } | ||
