aboutsummaryrefslogtreecommitdiff
path: root/portmidi/porttime/ptmacosx_mach.c
diff options
context:
space:
mode:
Diffstat (limited to 'portmidi/porttime/ptmacosx_mach.c')
-rwxr-xr-xportmidi/porttime/ptmacosx_mach.c204
1 files changed, 204 insertions, 0 deletions
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
31static int time_started_flag = FALSE;
32static UInt64 start_time;
33static pthread_t pt_thread_pid;
34
35/* note that this is static data -- we only need one copy */
36typedef struct {
37 int id;
38 int resolution;
39 PtCallback *callback;
40 void *userData;
41} pt_callback_parameters;
42
43static int pt_callback_proc_id = 0;
44
45static 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
133PtError 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
176PtError 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
186int Pt_Started(void)
187{
188 return time_started_flag;
189}
190
191
192PtTimestamp 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
201void Pt_Sleep(int32_t duration)
202{
203 usleep(duration * 1000);
204}