aboutsummaryrefslogtreecommitdiff
path: root/pd/portmidi/porttime/ptmacosx_cf.c
blob: c0c22a32e5966cb7b1917705a706fe373747a1b6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/* ptmacosx.c -- portable timer implementation for mac os x */

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <CoreFoundation/CoreFoundation.h>

#import <mach/mach.h>
#import <mach/mach_error.h>
#import <mach/mach_time.h>
#import <mach/clock.h>

#include "porttime.h"

#define THREAD_IMPORTANCE 30
#define LONG_TIME 1000000000.0

static int time_started_flag = FALSE;
static CFAbsoluteTime startTime = 0.0;
static CFRunLoopRef timerRunLoop;

typedef struct {
    int resolution;
    PtCallback *callback;
    void *userData;
} PtThreadParams;


void Pt_CFTimerCallback(CFRunLoopTimerRef timer, void *info)
{
    PtThreadParams *params = (PtThreadParams*)info;
    (*params->callback)(Pt_Time(), params->userData);
}

static void* Pt_Thread(void *p)
{
    CFTimeInterval timerInterval;
    CFRunLoopTimerContext timerContext;
    CFRunLoopTimerRef timer;
    PtThreadParams *params = (PtThreadParams*)p;
    //CFTimeInterval timeout;

    /* raise the thread's priority */
    kern_return_t error;
    thread_extended_policy_data_t extendedPolicy;
    thread_precedence_policy_data_t precedencePolicy;

    extendedPolicy.timeshare = 0;
    error = thread_policy_set(mach_thread_self(), THREAD_EXTENDED_POLICY,
                              (thread_policy_t)&extendedPolicy,
                              THREAD_EXTENDED_POLICY_COUNT);
    if (error != KERN_SUCCESS) {
        mach_error("Couldn't set thread timeshare policy", error);
    }

    precedencePolicy.importance = THREAD_IMPORTANCE;
    error = thread_policy_set(mach_thread_self(), THREAD_PRECEDENCE_POLICY,
                              (thread_policy_t)&precedencePolicy,
                              THREAD_PRECEDENCE_POLICY_COUNT);
    if (error != KERN_SUCCESS) {
        mach_error("Couldn't set thread precedence policy", error);
    }

    /* set up the timer context */
    timerContext.version = 0;
    timerContext.info = params;
    timerContext.retain = NULL;
    timerContext.release = NULL;
    timerContext.copyDescription = NULL;

    /* create a new timer */
    timerInterval = (double)params->resolution / 1000.0;
    timer = CFRunLoopTimerCreate(NULL, startTime+timerInterval, timerInterval,
                                 0, 0, Pt_CFTimerCallback, &timerContext);

    timerRunLoop = CFRunLoopGetCurrent();
    CFRunLoopAddTimer(timerRunLoop, timer, CFSTR("PtTimeMode"));

    /* run until we're told to stop by Pt_Stop() */
    CFRunLoopRunInMode(CFSTR("PtTimeMode"), LONG_TIME, false);
    
    CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer, CFSTR("PtTimeMode"));
    CFRelease(timer);
    free(params);

    return NULL;
}

PtError Pt_Start(int resolution, PtCallback *callback, void *userData)
{
    PtThreadParams *params = (PtThreadParams*)malloc(sizeof(PtThreadParams));
    pthread_t pthread_id;

    printf("Pt_Start() called\n");

    // /* make sure we're not already playing */
    if (time_started_flag) return ptAlreadyStarted;
    startTime = CFAbsoluteTimeGetCurrent();

    if (callback) {
    
        params->resolution = resolution;
        params->callback = callback;
        params->userData = userData;
    
        pthread_create(&pthread_id, NULL, Pt_Thread, params);
    }

    time_started_flag = TRUE;
    return ptNoError;
}


PtError Pt_Stop()
{
    printf("Pt_Stop called\n");

    CFRunLoopStop(timerRunLoop);
    time_started_flag = FALSE;
    return ptNoError;
}


int Pt_Started()
{
    return time_started_flag;
}


PtTimestamp Pt_Time()
{
    CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
    return (PtTimestamp) ((now - startTime) * 1000.0);
}