diff options
Diffstat (limited to '3rdparty/portmidi/porttime/ptmacosx_mach.c')
-rw-r--r-- | 3rdparty/portmidi/porttime/ptmacosx_mach.c | 103 |
1 files changed, 87 insertions, 16 deletions
diff --git a/3rdparty/portmidi/porttime/ptmacosx_mach.c b/3rdparty/portmidi/porttime/ptmacosx_mach.c index f4fec09d4c4..bc6a1a01084 100644 --- a/3rdparty/portmidi/porttime/ptmacosx_mach.c +++ b/3rdparty/portmidi/porttime/ptmacosx_mach.c @@ -4,20 +4,29 @@ #include <stdio.h> #include <CoreAudio/HostTime.h> -#include <mach/mach.h> -#include <mach/mach_error.h> -#include <mach/mach_time.h> -#include <mach/clock.h> +#import <mach/mach.h> +#import <mach/mach_error.h> +#import <mach/mach_time.h> +#import <mach/clock.h> #include <unistd.h> +#include <AvailabilityMacros.h> #include "porttime.h" #include "sys/time.h" #include "pthread.h" -#if !defined NSEC_PER_MSEC -#define NSEC_PER_MSEC 1000000ull +#ifndef NSEC_PER_MSEC +#define NSEC_PER_MSEC 1000000 +#endif +#define THREAD_IMPORTANCE 63 + +// QOS headers are available as of macOS 10.10 +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 +#include "sys/qos.h" +#define HAVE_APPLE_QOS 1 +#else +#undef HAVE_APPLE_QOS #endif -#define THREAD_IMPORTANCE 30 static int time_started_flag = FALSE; static UInt64 start_time; @@ -47,7 +56,7 @@ static void *Pt_CallbackProc(void *p) (thread_policy_t)&extendedPolicy, THREAD_EXTENDED_POLICY_COUNT); if (error != KERN_SUCCESS) { - mach_error((char *)"Couldn't set thread timeshare policy", error); + mach_error("Couldn't set thread timeshare policy", error); } precedencePolicy.importance = THREAD_IMPORTANCE; @@ -55,22 +64,65 @@ static void *Pt_CallbackProc(void *p) (thread_policy_t)&precedencePolicy, THREAD_PRECEDENCE_POLICY_COUNT); if (error != KERN_SUCCESS) { - mach_error((char *)"Couldn't set thread precedence policy", error); + mach_error("Couldn't set thread precedence policy", error); } + // Most important, set real-time constraints. + + // Define the guaranteed and max fraction of time for the audio thread. + // These "duty cycle" values can range from 0 to 1. A value of 0.5 + // means the scheduler would give half the time to the thread. + // These values have empirically been found to yield good behavior. + // Good means that audio performance is high and other threads won't starve. + const double kGuaranteedAudioDutyCycle = 0.75; + const double kMaxAudioDutyCycle = 0.85; + + // Define constants determining how much time the audio thread can + // use in a given time quantum. All times are in milliseconds. + + // About 128 frames @44.1KHz + const double kTimeQuantum = 2.9; + + // Time guaranteed each quantum. + const double kAudioTimeNeeded = kGuaranteedAudioDutyCycle * kTimeQuantum; + + // Maximum time each quantum. + const double kMaxTimeAllowed = kMaxAudioDutyCycle * kTimeQuantum; + // Get the conversion factor from milliseconds to absolute time + // which is what the time-constraints call needs. + mach_timebase_info_data_t tb_info; + mach_timebase_info(&tb_info); + double ms_to_abs_time = + ((double)tb_info.denom / (double)tb_info.numer) * 1000000; + + thread_time_constraint_policy_data_t time_constraints; + time_constraints.period = (uint32_t)(kTimeQuantum * ms_to_abs_time); + time_constraints.computation = (uint32_t)(kAudioTimeNeeded * ms_to_abs_time); + time_constraints.constraint = (uint32_t)(kMaxTimeAllowed * ms_to_abs_time); + time_constraints.preemptible = 0; + + error = thread_policy_set(mach_thread_self(), + THREAD_TIME_CONSTRAINT_POLICY, + (thread_policy_t)&time_constraints, + THREAD_TIME_CONSTRAINT_POLICY_COUNT); + if (error != KERN_SUCCESS) { + mach_error("Couldn't set thread precedence policy", error); + } + /* to kill a process, just increment the pt_callback_proc_id */ - /* printf("pt_callback_proc_id %d, id %d\n", pt_callback_proc_id, parameters->id); */ + /* printf("pt_callback_proc_id %d, id %d\n", pt_callback_proc_id, + parameters->id); */ while (pt_callback_proc_id == parameters->id) { /* wait for a multiple of resolution ms */ UInt64 wait_time; int delay = mytime++ * parameters->resolution - Pt_Time(); - PtTimestamp timestamp; + PtTimestamp timestamp; if (delay < 0) delay = 0; wait_time = AudioConvertNanosToHostTime((UInt64)delay * NSEC_PER_MSEC); wait_time += AudioGetCurrentHostTime(); - error = mach_wait_until(wait_time); - timestamp = Pt_Time(); + mach_wait_until(wait_time); + timestamp = Pt_Time(); (*(parameters->callback))(timestamp, parameters->userData); } free(parameters); @@ -93,7 +145,26 @@ PtError Pt_Start(int resolution, PtCallback *callback, void *userData) parms->resolution = resolution; parms->callback = callback; parms->userData = userData; + +#ifdef HAVE_APPLE_QOS + pthread_attr_t qosAttribute; + pthread_attr_init(&qosAttribute); + pthread_attr_set_qos_class_np(&qosAttribute, + QOS_CLASS_USER_INTERACTIVE, 0); + + res = pthread_create(&pt_thread_pid, &qosAttribute, Pt_CallbackProc, + parms); +#else res = pthread_create(&pt_thread_pid, NULL, Pt_CallbackProc, parms); +#endif + + struct sched_param sp; + memset(&sp, 0, sizeof(struct sched_param)); + sp.sched_priority = sched_get_priority_max(SCHED_RR); + if (pthread_setschedparam(pthread_self(), SCHED_RR, &sp) == -1) { + return ptHostError; + } + if (res != 0) return ptHostError; } @@ -102,7 +173,7 @@ PtError Pt_Start(int resolution, PtCallback *callback, void *userData) } -PtError Pt_Stop() +PtError Pt_Stop(void) { /* printf("Pt_Stop called\n"); */ pt_callback_proc_id++; @@ -112,13 +183,13 @@ PtError Pt_Stop() } -int Pt_Started() +int Pt_Started(void) { return time_started_flag; } -PtTimestamp Pt_Time() +PtTimestamp Pt_Time(void) { UInt64 clock_time, nsec_time; clock_time = AudioGetCurrentHostTime() - start_time; |