From 14e67c85f9cac496dad3d4e77dfb5e40f4cd94b0 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Sun, 31 Dec 2006 22:49:16 +0000 Subject: added latency measuring code on Darwin (to be removed once testing is done), and removed threading svn path=/trunk/externals/io/hidio/; revision=7115 --- Makefile | 8 +- TODO | 10 +- hidio-help.pd | 17 ++- hidio.c | 347 +++++++++------------------------------------- hidio.h | 24 ++-- hidio_darwin.c | 146 +++++++++++++------ hidio_linux.c | 7 +- test/latency_test.pd | 22 +++ test/test_instance.pd | 93 +++++++++++++ test/test_instance_GUI.pd | 26 ++++ 10 files changed, 346 insertions(+), 354 deletions(-) create mode 100644 test/latency_test.pd create mode 100644 test/test_instance.pd create mode 100644 test/test_instance_GUI.pd diff --git a/Makefile b/Makefile index 78d1860..98be310 100644 --- a/Makefile +++ b/Makefile @@ -15,14 +15,18 @@ test_locations: # for emacs etags: - etags ../../../pd/src/*.h *.[ch] linux/input.h + etags ../../../pd/src/*.h *.[ch] linux/input.h /usr/include/stdlib.h \ + /usr/include/time.h /usr/include/stdio.h make etags_`uname -s` etags_Darwin: etags -a HID\ Utilities\ Source/*.[ch] \ /System/Library/Frameworks/ForceFeedback.framework/Headers/*.h \ /System/Library/Frameworks/Carbon.framework/Headers/*.h \ - /System/Library/Frameworks/IOKit.framework/Headers/hid*/*.[ch] + /System/Library/Frameworks/CoreServices.framework/Headers/*.h \ + /System/Library/Frameworks/IOKit.framework/Headers/*.[ch] \ + /System/Library/Frameworks/IOKit.framework/Headers/hid*/*.[ch] \ + /usr/include/mach/*.h etags_Linux: etags -a /usr/include/*.h linux/input.h /usr/include/sys/*.h diff --git a/TODO b/TODO index f3e32e2..c883ae2 100644 --- a/TODO +++ b/TODO @@ -52,10 +52,12 @@ compare against ______________________________________________________________________________ = make only the first executed instance fetch the data - the function is ugen_getsortno() -- returns -an integer which increases eachtime DSP is restarted. You can add the -function call (to the ugen chain for instance) each time you see -ugen_getsortno() return an integer greater than the previous one you've +- double right_now = clock_getlogicaltime(); + this only works when instances are in the same patch, not when instances are + in different patches + +- implement event output data structure + ______________________________________________________________________________ = output one value per poll diff --git a/hidio-help.pd b/hidio-help.pd index 0cace51..592fc72 100644 --- a/hidio-help.pd +++ b/hidio-help.pd @@ -1,4 +1,4 @@ -#N canvas 157 38 862 587 10; +#N canvas 157 38 878 603 10; #X floatatom 27 445 5 0 0 0 - - -; #X floatatom 83 445 5 0 0 0 - - -; #X obj 191 164 tgl 35 0 empty empty empty 0 -6 0 8 -24198 -1 -1 25 @@ -14,7 +14,7 @@ #X text 435 117 refresh device list; #X text 537 487 For more info:; #X text 266 557 released under the GNU GPL; -#X text 472 544 $Revision: 1.3 $$Date: 2006-12-08 06:33:26 $; +#X text 472 544 $Revision: 1.4 $$Date: 2006-12-31 22:49:16 $; #X text 473 557 $Author: eighthave $; #X msg 436 201 poll 20; #X msg 374 201 poll 2; @@ -270,7 +270,7 @@ of it could change without notice !!!; 1; #X obj 459 508 tgl 25 0 empty empty empty 0 -6 0 8 -195568 -1 -1 0 1; -#X msg 25 155 debug 0; +#X msg 25 155 debug 9; #X msg 298 145 info; #N canvas 743 25 411 235 see 0; #N canvas 108 318 543 264 route 0; @@ -398,10 +398,10 @@ IDs (it is not case sensitive):; #X text 49 543 (C) Copyright 2004 Hans-Christoph Steiner ; #N canvas 162 133 570 420 serin 0; -#X obj 209 61 cnv 15 15 15 empty \$0-debug-canvas 0 4 8 0 14 -233017 +#X obj 209 61 cnv 15 15 15 empty \$0-debug-canvas 9 4 8 0 14 -233017 -1 0; #X obj 60 61 hradio 15 1 1 10 empty empty empty 0 -6 0 8 -261689 -1 --1 0; +-1 9; #X obj 60 13 inlet; #X msg 200 202 label \$1; #X obj 200 180 makefilename %d; @@ -478,7 +478,7 @@ IDs (it is not case sensitive):; #X restore 10 293 pd remove instance numbers; #X obj 9 466 route a_key b_key c_key d_key e_key f_key g_key h_key i_key j_key k_key l_key m_key n_key o_key p_key; -#N canvas 114 93 471 350 raw 0; +#N canvas 114 93 475 354 raw 0; #X obj 144 45 inlet; #X obj 88 104 route DESKTOP; #X obj 87 158 route DESKTOP57; @@ -493,6 +493,7 @@ i_key j_key k_key l_key m_key n_key o_key p_key; #X obj 290 163 print test; #X obj 333 99 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1 ; +#X obj 354 45 inlet; #X connect 0 0 1 0; #X connect 0 0 8 0; #X connect 1 0 2 0; @@ -503,8 +504,11 @@ i_key j_key k_key l_key m_key n_key o_key p_key; #X connect 7 0 5 1; #X connect 8 0 9 0; #X connect 10 0 8 1; +#X connect 11 0 10 0; #X restore 408 294 pd raw; #X floatatom 63 401 6 0 0 0 - - -; +#X obj 455 295 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 +1; #X connect 2 0 65 0; #X connect 8 0 65 0; #X connect 9 0 65 0; @@ -560,3 +564,4 @@ i_key j_key k_key l_key m_key n_key o_key p_key; #X connect 74 13 50 0; #X connect 74 14 51 0; #X connect 74 15 52 0; +#X connect 77 0 75 1; diff --git a/hidio.c b/hidio.c index 0ff2cce..d2c114c 100644 --- a/hidio.c +++ b/hidio.c @@ -87,7 +87,6 @@ t_symbol *relative_symbols[RELATIVE_ARRAY_MAX]; //static void hidio_poll(t_hidio *x, t_float f); static void hidio_open(t_hidio *x, t_symbol *s, int argc, t_atom *argv); //static t_int hidio_close(t_hidio *x); -//static t_int hidio_child_read(t_hidio *x); //static void hidio_float(t_hidio* x, t_floatarg f); @@ -297,40 +296,40 @@ void hidio_output_event(t_hidio *x, t_hid_element *output_data) SETSYMBOL(event_data, output_data->name); SETFLOAT(event_data + 1, output_data->instance); SETFLOAT(event_data + 2, output_data->value); -#else +#else /* Max */ atom_setsym(event_data, output_data->name); atom_setsym(event_data, output_data->name); atom_setlong(event_data + 1, (long)output_data->instance); atom_setlong(event_data + 2, (long)output_data->value); -#endif +#endif /* PD */ outlet_anything(x->x_data_outlet, output_data->type, 3, event_data); } } /* stop polling the device */ -static void stop_poll(t_hidio* x) +static void hidio_stop_poll(t_hidio* x) { - debug_print(LOG_DEBUG,"stop_poll"); + debug_print(LOG_DEBUG,"hidio_stop_poll"); - pthread_mutex_lock(&x->x_mutex); if (x->x_started) { clock_unset(x->x_clock); debug_print(LOG_INFO,"[hidio] polling stopped"); x->x_started = 0; } - pthread_mutex_unlock(&x->x_mutex); } /*------------------------------------------------------------------------------ * METHODS FOR [hidio]'s MESSAGES */ - +/* TODO: poll time should be set based on how fast the OS is actually polling + * the device, whether that is IOUSBEndpointDescriptor.bInterval, or something + * else. + */ void hidio_poll(t_hidio* x, t_float f) { - pthread_mutex_lock(&x->x_mutex); debug_print(LOG_DEBUG,"hidio_poll"); /* if the user sets the delay less than 2, set to block size */ @@ -342,9 +341,7 @@ void hidio_poll(t_hidio* x, t_float f) { if(!x->x_device_open) { - pthread_mutex_unlock(&x->x_mutex); hidio_open(x,ps_open,0,NULL); - pthread_mutex_lock(&x->x_mutex); } if(!x->x_started) { @@ -353,37 +350,28 @@ void hidio_poll(t_hidio* x, t_float f) x->x_started = 1; } } - pthread_mutex_unlock(&x->x_mutex); } static void hidio_set_from_float(t_hidio *x, t_floatarg f) { - pthread_mutex_lock(&x->x_mutex); /* values greater than 1 set the polling delay time */ /* 1 and 0 for start/stop so you can use a [tgl] */ if(f > 1) { x->x_delay = (t_int)f; - pthread_mutex_unlock(&x->x_mutex); hidio_poll(x,f); - pthread_mutex_lock(&x->x_mutex); } else if(f == 1) { if(! x->x_started) { - pthread_mutex_unlock(&x->x_mutex); hidio_poll(x,f); - pthread_mutex_lock(&x->x_mutex); } } else if(f == 0) { - pthread_mutex_unlock(&x->x_mutex); - stop_poll(x); - pthread_mutex_lock(&x->x_mutex); + hidio_stop_poll(x); } - pthread_mutex_unlock(&x->x_mutex); } /* close the device */ @@ -391,10 +379,16 @@ t_int hidio_close(t_hidio *x) { debug_print(LOG_DEBUG,"hidio_close"); - pthread_mutex_lock(&x->x_mutex); - x->x_requestcode = REQUEST_CLOSE; - pthread_cond_signal(&x->x_requestcondition); - pthread_mutex_unlock(&x->x_mutex); + /* just to be safe, stop it first */ + hidio_stop_poll(x); + + if(! hidio_close_device(x)) + { + debug_print(LOG_INFO,"[hidio] closed device %d",x->x_device_number); + x->x_device_open = 0; + return (0); + } + return (1); } @@ -409,26 +403,48 @@ t_int hidio_close(t_hidio *x) */ static void hidio_open(t_hidio *x, t_symbol *s, int argc, t_atom *argv) { - short device_number; debug_print(LOG_DEBUG,"hid_%s",s->s_name); + short new_device_number = get_device_number_from_arguments(argc, argv); + t_int started = x->x_started; // store state to restore after device is opened - pthread_mutex_lock(&x->x_mutex); - device_number = get_device_number_from_arguments(argc, argv); - if (device_number > -1) + if (new_device_number > -1) { - x->x_device_number = device_number; - x->x_requestcode = REQUEST_OPEN; - pthread_cond_signal(&x->x_requestcondition); + /* check whether we have to close previous device */ + if (x->x_device_open && new_device_number != x->x_device_number) + { + hidio_close(x); + } + /* no device open, so open one now */ + if (!x->x_device_open) + { + if(hidio_open_device(x, new_device_number)) + { + x->x_device_number = -1; + error("[hidio] can not open device %d",new_device_number); + } + else + { + x->x_device_open = 1; + x->x_device_number = new_device_number; + /* restore the polling state so that when I [tgl] is used to + * start/stop [hidio], the [tgl]'s state will continue to + * accurately reflect [hidio]'s state */ + if (started) + hidio_set_from_float(x,x->x_delay); // TODO is this useful? + debug_print(LOG_DEBUG,"[hidio] set device# to %d",new_device_number); + output_device_number(x); + } + } } else debug_print(LOG_WARNING,"[hidio] device does not exist"); - pthread_mutex_unlock(&x->x_mutex); + /* always output open result so you can test for success in Pd space */ + output_open_status(x); } -/* read from event queue, called from child thread, no mutex needed */ -t_int hidio_child_read(t_hidio *x) +static void hidio_tick(t_hidio *x) { -// debug_print(LOG_DEBUG,"hidio_child_read"); +// debug_print(LOG_DEBUG,"hidio_tick"); t_hid_element *current_element; unsigned int i; #ifdef PD @@ -437,7 +453,9 @@ t_int hidio_child_read(t_hidio *x) double right_now; clock_getftime(&right_now); #endif /* PD */ - + + debug_print(LOG_DEBUG,"# %u\tnow: %u\tlast: %u", x->x_device_number, + right_now, last_execute_time[x->x_device_number]); if(right_now > last_execute_time[x->x_device_number]) { hidio_get_events(x); @@ -455,41 +473,20 @@ t_int hidio_child_read(t_hidio *x) current_element->previous_value = current_element->value; } } - - // TODO: why is this 1? - return 1; -} - -static void *hidio_tick(t_hidio *x) -{ - pthread_mutex_lock(&x->x_mutex); - if (x->x_requestcode == REQUEST_NOTHING) - { - x->x_requestcode = REQUEST_READ; - pthread_cond_signal(&x->x_requestcondition); - } if (x->x_started) { clock_delay(x->x_clock, x->x_delay); } - pthread_mutex_unlock(&x->x_mutex); - return NULL; -} - -static void hidio_print(t_hidio *x) -{ - pthread_mutex_lock(&x->x_mutex); - x->x_requestcode = REQUEST_PRINT; - pthread_cond_signal(&x->x_requestcondition); - pthread_mutex_unlock(&x->x_mutex); } static void hidio_info(t_hidio *x) { - pthread_mutex_lock(&x->x_mutex); - x->x_requestcode = REQUEST_INFO; - pthread_cond_signal(&x->x_requestcondition); - pthread_mutex_unlock(&x->x_mutex); + output_open_status(x); + output_device_number(x); + output_device_count(x); + output_poll_time(x); + output_element_ranges(x); + hidio_platform_specific_info(x); } static void hidio_float(t_hidio* x, t_floatarg f) @@ -499,7 +496,7 @@ static void hidio_float(t_hidio* x, t_floatarg f) hidio_set_from_float(x,f); } -#ifndef PD +#ifndef PD /* Max */ static void hidio_int(t_hidio* x, long l) { debug_print(LOG_DEBUG,"hid_int"); @@ -510,185 +507,7 @@ static void hidio_int(t_hidio* x, long l) static void hidio_debug(t_hidio *x, t_float f) { - pthread_mutex_lock(&x->x_mutex); global_debug_level = f; - pthread_mutex_unlock(&x->x_mutex); -} - - -/*------------------------------------------------------------------------------ - * child thread - */ - -static void *hidio_child(void *zz) -{ - t_hidio *x = zz; - short device_number = -1; - - pthread_mutex_lock(&x->x_mutex); - while (1) - { - if (x->x_requestcode == REQUEST_NOTHING) - { - pthread_cond_signal(&x->x_answercondition); - pthread_cond_wait(&x->x_requestcondition, &x->x_mutex); - } - else if (x->x_requestcode == REQUEST_OPEN) - { - short new_device_number = x->x_device_number; - /* store running state to be restored after the device has been opened */ - t_int started = x->x_started; - int ret; - /* check whether we have to close previous device */ - if (x->x_device_open && device_number != x->x_device_number) - { - pthread_mutex_unlock(&x->x_mutex); - stop_poll(x); - ret = hidio_close_device(x); - pthread_mutex_lock(&x->x_mutex); - x->x_device_open = 0; - device_number = -1; - } - /* no device open, so open one now */ - if (!x->x_device_open) - { - pthread_mutex_unlock(&x->x_mutex); - ret = hidio_open_device(x, new_device_number); - pthread_mutex_lock(&x->x_mutex); - if (ret) - { - x->x_device_number = -1; - error("[hidio] can not open device %d",device_number); - } - else - { - x->x_device_open = 1; - device_number = x->x_device_number; /* keep local copy */ - pthread_mutex_unlock(&x->x_mutex); - /* restore the polling state so that when I [tgl] is used to start/stop [hidio], - * the [tgl]'s state will continue to accurately reflect [hidio]'s state */ - if (started) - hidio_set_from_float(x,x->x_delay); - debug_print(LOG_DEBUG,"[hidio] set device# to %d",device_number); - output_open_status(x); - output_device_number(x); - pthread_mutex_lock(&x->x_mutex); - } - } - if (x->x_requestcode == REQUEST_OPEN) - x->x_requestcode = REQUEST_NOTHING; - pthread_cond_signal(&x->x_answercondition); - } - else if (x->x_requestcode == REQUEST_READ) - { - pthread_mutex_unlock(&x->x_mutex); - hidio_child_read(x); - pthread_mutex_lock(&x->x_mutex); - if (x->x_requestcode == REQUEST_READ) - x->x_requestcode = REQUEST_NOTHING; - pthread_cond_signal(&x->x_answercondition); - } - else if (x->x_requestcode == REQUEST_SEND) - { - if (x->x_requestcode == REQUEST_SEND) - x->x_requestcode = REQUEST_NOTHING; - pthread_cond_signal(&x->x_answercondition); - } - else if (x->x_requestcode == REQUEST_PRINT) - { - pthread_mutex_unlock(&x->x_mutex); - hidio_doprint(x); - pthread_mutex_lock(&x->x_mutex); - if (x->x_requestcode == REQUEST_PRINT) - x->x_requestcode = REQUEST_NOTHING; - pthread_cond_signal(&x->x_answercondition); - } - else if (x->x_requestcode == REQUEST_INFO) - { - pthread_mutex_unlock(&x->x_mutex); - output_open_status(x); - output_device_number(x); - output_device_count(x); - output_poll_time(x); - output_element_ranges(x); - hidio_platform_specific_info(x); - pthread_mutex_lock(&x->x_mutex); - if (x->x_requestcode == REQUEST_INFO) - x->x_requestcode = REQUEST_NOTHING; - pthread_cond_signal(&x->x_answercondition); - } - else if (x->x_requestcode == REQUEST_CLOSE) - { - t_int ret; - pthread_mutex_unlock(&x->x_mutex); - stop_poll(x); - ret = hidio_close_device(x); - pthread_mutex_lock(&x->x_mutex); - if (!ret) - { - debug_print(LOG_INFO,"[hidio] closed device %d",x->x_device_number); - x->x_device_open = 0; - } - if (x->x_requestcode == REQUEST_CLOSE) - x->x_requestcode = REQUEST_NOTHING; - pthread_cond_signal(&x->x_answercondition); - } - else if (x->x_requestcode == REQUEST_QUIT) - { - pthread_mutex_unlock(&x->x_mutex); - stop_poll(x); - hidio_close_device(x); - pthread_mutex_lock(&x->x_mutex); - x->x_requestcode = REQUEST_NOTHING; - pthread_cond_signal(&x->x_answercondition); - break; /* leave the while loop */ - } - else - { - ; /* nothing: shouldn't get here anyway */ - } - } - pthread_mutex_unlock(&x->x_mutex); - return (0); -} - -/* change priority of child thread */ -#ifdef PD -static void hidio_priority(t_hidio *x, t_floatarg p) -#else -static void hidio_priority(t_hidio *x, long p) -#endif -{ - pthread_mutex_lock(&x->x_mutex); - p = 2 * (CLIP(p, 0, 10) - 5); - if (x->x_thread) - { - struct sched_param parm; - int policy; - if (pthread_getschedparam(x->x_thread, &policy, &parm) < 0) - { - post("hidio: warning: failed to get thread priority"); - } - else - { - parm.sched_priority = x->x_priority + (int)p; /* adjust priority */ - - if (parm.sched_priority < sched_get_priority_min(policy)) - { - parm.sched_priority = sched_get_priority_min(policy); - } - else if (parm.sched_priority > sched_get_priority_max(policy)) - { - parm.sched_priority = sched_get_priority_max(policy); - } - - if (pthread_setschedparam(x->x_thread, policy, &parm) < 0) - { - post("hidio: warning: failed to change thread priority to %d", parm.sched_priority); - } - } - } - pthread_mutex_unlock(&x->x_mutex); } @@ -697,42 +516,13 @@ static void hidio_priority(t_hidio *x, long p) */ static void hidio_free(t_hidio* x) { - void *threadrtn; - debug_print(LOG_DEBUG,"hidio_free"); - - /* stop polling for input */ - if (x->x_clock) - clock_unset(x->x_clock); - - pthread_mutex_lock(&x->x_mutex); - /* request QUIT and wait for acknowledge */ - x->x_requestcode = REQUEST_QUIT; - if (x->x_thread) - { - post("hidio: stopping worker thread. . ."); - pthread_cond_signal(&x->x_requestcondition); - while (x->x_requestcode != REQUEST_NOTHING) - { - post("hidio: ...signalling..."); - pthread_cond_signal(&x->x_requestcondition); - pthread_cond_wait(&x->x_answercondition, &x->x_mutex); - } - pthread_mutex_unlock(&x->x_mutex); - if (pthread_join(x->x_thread, &threadrtn)) - error("hidio_free: join failed"); - post("hidio: ...done"); - } - else pthread_mutex_unlock(&x->x_mutex); + hidio_close(x); clock_free(x->x_clock); hidio_instance_count--; hidio_platform_specific_free(x); - - pthread_cond_destroy(&x->x_requestcondition); - pthread_cond_destroy(&x->x_answercondition); - pthread_mutex_destroy(&x->x_mutex); } /* create a new instance of this class */ @@ -768,18 +558,11 @@ static void *hidio_new(t_symbol *s, int argc, t_atom *argv) x->x_fd = INVALID_HANDLE_VALUE; #endif /* _WIN32 */ - pthread_mutex_init(&x->x_mutex, 0); - pthread_cond_init(&x->x_requestcondition, 0); - pthread_cond_init(&x->x_answercondition, 0); - x->x_device_number = get_device_number_from_arguments(argc, argv); x->x_instance = hidio_instance_count; hidio_instance_count++; - x->x_requestcode = REQUEST_NOTHING; - pthread_create(&x->x_thread, 0, hidio_child, x); - return (x); } @@ -820,9 +603,6 @@ void hidio_setup(void) class_addmethod(hidio_class,(t_method) hidio_ff_fftest,gensym("fftest"),A_DEFFLOAT,0); class_addmethod(hidio_class,(t_method) hidio_ff_print,gensym("ff_print"),0); - class_addmethod(hidio_class,(t_method) hidio_priority, gensym("priority"), A_FLOAT, A_NULL); - - post("[hidio] %d.%d, written by Hans-Christoph Steiner ", HIDIO_MAJOR_VERSION, HIDIO_MINOR_VERSION); post("\tcompiled on "__DATE__" at "__TIME__ " "); @@ -914,7 +694,6 @@ int main() class_addmethod(c, (method)hidio_ff_fftest, "fftest",A_DEFFLOAT,0); class_addmethod(c, (method)hidio_ff_print, "ff_print",0); /* perfomrance / system stuff */ - class_addmethod(c, (method)hidio_priority, "priority", A_LONG,0); class_addmethod(c, (method)hidio_assist, "assist", A_CANT, 0); diff --git a/hidio.h b/hidio.h index 2e1ce08..e7d08c5 100644 --- a/hidio.h +++ b/hidio.h @@ -3,7 +3,6 @@ #include #ifdef _WIN32 -#include "pthread.h" /* needs pthread library */ #define LOG_DEBUG 7 #define LOG_INFO 6 #define LOG_WARNING 4 @@ -11,7 +10,6 @@ #define vsnprintf _vsnprintf #else #include -#include #endif /* _WIN32 */ #ifdef _MSC_VER /* this only applies to Microsoft compilers */ @@ -44,7 +42,7 @@ typedef void t_clock; #define HIDIO_MAJOR_VERSION 0 #define HIDIO_MINOR_VERSION 0 -/* static char *version = "$Revision: 1.11 $"; */ +/* static char *version = "$Revision: 1.12 $"; */ /*------------------------------------------------------------------------------ * MACRO DEFINES @@ -67,8 +65,10 @@ typedef void t_clock; #define MAX_ELEMENTS 64 /* this is limited so that the object doesn't cause a click getting too many - * events from the OS's event queue */ -#define MAX_EVENTS_PER_POLL 64 + * events from the OS's event queue. On Mac OS X, this is set in on the + * kernel level in HID_Utilities_External.h with the constant + * kDeviceQueueSize */ +#define MAX_EVENTS_PER_POLL 50 /*------------------------------------------------------------------------------ * THREADING RELATED DEFINES @@ -93,13 +93,13 @@ typedef struct _hidio t_object x_obj; #ifndef PD void *x_obex; -#endif /* PD */ +#endif #ifdef _WIN32 HANDLE x_fd; -#endif /* _WIN32 */ +#endif #ifdef __linux__ t_int x_fd; -#endif /* __linux */ +#endif void *x_ff_device; short x_device_number; short x_instance; @@ -110,12 +110,6 @@ typedef struct _hidio t_clock *x_clock; t_outlet *x_data_outlet; t_outlet *x_status_outlet; - t_int x_requestcode; - pthread_mutex_t x_mutex; - pthread_cond_t x_requestcondition; - pthread_cond_t x_answercondition; - pthread_t x_thread; - t_int x_priority; } t_hidio; @@ -186,7 +180,7 @@ extern t_int hidio_open_device(t_hidio *x, short device_number); extern t_int hidio_close_device(t_hidio *x); extern void hidio_build_device_list(void); extern void hidio_get_events(t_hidio *x); -extern void hidio_doprint(t_hidio* x); /* print info to the console */ +extern void hidio_print(t_hidio* x); /* print info to the console */ extern void hidio_platform_specific_info(t_hidio* x); /* device info on the status outlet */ extern void hidio_platform_specific_free(t_hidio *x); extern short get_device_number_by_id(unsigned short vendor_id, unsigned short product_id); diff --git a/hidio_darwin.c b/hidio_darwin.c index 0905d5f..1e77731 100644 --- a/hidio_darwin.c +++ b/hidio_darwin.c @@ -2,8 +2,6 @@ /* * Apple Darwin HID Manager support for Pd [hidio] object * - * some code from SuperCollider3's SC_HID.cpp by Jan Truetzschler Falkenstein - * * Copyright (c) 2004 Hans-Christoph All rights reserved. * * This program is free software; you can redistribute it and/or modify @@ -47,24 +45,29 @@ #include #include +#include #include +#include #include #include "hidio.h" -#define DEBUG(x) -//#define DEBUG(x) x +//#define DEBUG(x) +#define DEBUG(x) x /*============================================================================== * GLOBAL VARS *======================================================================== */ -extern t_int hidio_instance_count; // in hidio.h - /* store device pointers so I don't have to query them all the time */ pRecDevice device_pointer[MAX_DEVICES]; +// temp hack for measuring latency +#define LATENCY_MAX 8192 +int latency[LATENCY_MAX]; +int latency_i; +int latency_average; // this stuff is moving to the t_hid_element struct @@ -136,7 +139,7 @@ static void get_usage_symbols(pRecElement pCurrentHIDElement, t_hid_element *new case kHIDUsage_GD_Dial: convert_axis_to_symbols(pCurrentHIDElement, new_element, 7); break; case kHIDUsage_GD_Wheel: convert_axis_to_symbols(pCurrentHIDElement, new_element, 8); break; case kHIDUsage_GD_Hatswitch: - // this is still a mystery how to handle + // TODO: this is still a mystery how to handle, due to USB HID vs. Linux input.h new_element->type = ps_absolute; new_element->name = absolute_symbols[9]; /* hatswitch */ break; @@ -278,6 +281,25 @@ static t_float get_type_name_instance(t_symbol *type, t_symbol *name, /* DARWIN-SPECIFIC SUPPORT FUNCTIONS */ /* ============================================================================== */ +double calculate_event_latency( uint64_t endTime, uint64_t startTime ) +{ + uint64_t difference = endTime - startTime; + static double conversion = 0.0; + + if( 0.0 == conversion ) + { + mach_timebase_info_data_t info; + kern_return_t err = mach_timebase_info( &info ); + if( 0 == err ) + { + //convert to seconds (multiply by 1e-6 to get miliseconds) + conversion = 1e-6 * (double) info.numer / (double) info.denom; + } + } + return conversion * (double) difference; +} + + short get_device_number_by_id(unsigned short vendor_id, unsigned short product_id) { debug_print(LOG_DEBUG,"get_device_number_from_usage"); @@ -380,6 +402,11 @@ static void hidio_build_element_list(t_hidio *x) HIDQueueDevice(pCurrentHIDDevice); pCurrentHIDElement = HIDGetFirstDeviceElement( pCurrentHIDDevice, kHIDElementTypeInput ); + + /* TODO: axes should be the first elements in the array since they + * produce many more events than buttons. This will save loop cycles + * in the read statement, since it had to cycle thru the elements to + * find a match. */ while( pCurrentHIDElement != NULL) { /* these two functions just get the pretty names for display */ @@ -396,33 +423,40 @@ static void hidio_build_element_list(t_hidio *x) element_count[x->x_device_number], element[x->x_device_number]); - if( (pCurrentHIDElement->usagePage == kHIDPage_GenericDesktop) && - (!pCurrentHIDElement->relative) ) + if(!pCurrentHIDElement->relative) /* relative elements should remain queued */ { - switch(pCurrentHIDElement->usage) + switch( pCurrentHIDElement->usagePage) { - case kHIDUsage_GD_X: - case kHIDUsage_GD_Y: - case kHIDUsage_GD_Z: - case kHIDUsage_GD_Rx: - case kHIDUsage_GD_Ry: - case kHIDUsage_GD_Rz: - case kHIDUsage_GD_Slider: - case kHIDUsage_GD_Dial: - case kHIDUsage_GD_Wheel: - //case kHIDUsage_GD_Hatswitch: // hatswitches are more like buttons, so queue them - debug_print(LOG_INFO,"[hidio] storing absolute axis to poll %s, %s (0x%04x 0x%04x)", - type_name, usage_name, + case kHIDPage_GenericDesktop: + switch(pCurrentHIDElement->usage) + { + case kHIDUsage_GD_X: + case kHIDUsage_GD_Y: + case kHIDUsage_GD_Z: + case kHIDUsage_GD_Rx: + case kHIDUsage_GD_Ry: + case kHIDUsage_GD_Rz: + case kHIDUsage_GD_Slider: + case kHIDUsage_GD_Dial: + case kHIDUsage_GD_Wheel: + //case kHIDUsage_GD_Hatswitch: // hatswitches are more like buttons, so queue them + debug_print(LOG_INFO,"[hidio] storing absolute axis to poll %s, %s (0x%04x 0x%04x)", + type_name, usage_name, + pCurrentHIDElement->usagePage, pCurrentHIDElement->usage); + if(HIDDequeueElement(pCurrentHIDDevice,pCurrentHIDElement) != kIOReturnSuccess) + debug_print(LOG_ERR,"[hidio] could not dequeue element"); + new_element->polled = 1; + break; + } + default: + debug_print(LOG_INFO,"\tqueuing element %s, %s (0x%04x 0x%04x)", + type_name, usage_name, pCurrentHIDElement->usagePage, pCurrentHIDElement->usage); - if(HIDDequeueElement(pCurrentHIDDevice,pCurrentHIDElement) != kIOReturnSuccess) - debug_print(LOG_ERR,"[hidio] could not dequeue element"); - new_element->polled = 1; - break; } } - else + else { - debug_print(LOG_INFO,"[hidio] queuing element %s, %s (0x%04x 0x%04x)", + debug_print(LOG_INFO,"\tqueuing element %s, %s (0x%04x 0x%04x)", type_name, usage_name, pCurrentHIDElement->usagePage, pCurrentHIDElement->usage); } @@ -608,15 +642,16 @@ void hidio_platform_specific_info(t_hidio *x) void hidio_get_events(t_hidio *x) { - unsigned int i; - pRecDevice pCurrentHIDDevice; - t_hid_element *current_element; - IOHIDEventStruct event; + unsigned int i,j; + pRecDevice pCurrentHIDDevice; + t_hid_element *current_element; + IOHIDEventStruct event; + uint64_t timestamp, now, difference; pCurrentHIDDevice = device_pointer[x->x_device_number]; /* get the queued events first and store them */ -// while( (HIDGetEvent(pCurrentHIDDevice, (void*) &event)) && (event_counter < MAX_EVENTS_PER_POLL) ) +// TODO: while( (HIDGetEvent(pCurrentHIDDevice, (void*) &event)) && (event_counter < MAX_EVENTS_PER_POLL) ) while(HIDGetEvent(pCurrentHIDDevice, (void*) &event)) { i=0; @@ -628,9 +663,35 @@ void hidio_get_events(t_hidio *x) (IOHIDElementCookie) event.elementCookie) ); current_element->value = event.value; - debug_print(LOG_DEBUG,"output this: %s %s %d prev %d",current_element->type->s_name, - current_element->name->s_name, current_element->value, - current_element->previous_value); +// debug_print(LOG_DEBUG,"output this: %s %s %d prev %d",current_element->type->s_name, +// current_element->name->s_name, current_element->value, +// current_element->previous_value); +// debug_print(LOG_DEBUG,"timestamp: %u %u", event.timestamp.hi, event.timestamp.lo); + timestamp = * (uint64_t *) &(event.timestamp); + now = mach_absolute_time(); + difference = calculate_event_latency(now, timestamp); + // temp hack to measure latency + if( latency_i < LATENCY_MAX) + { + latency[latency_i] = (int) difference; + if( latency[latency_i] < 100 ) + latency_average += latency[latency_i]; + ++latency_i; + } + else + { +/* for(j=0;jx_device_number]; ++i) @@ -645,7 +706,7 @@ void hidio_get_events(t_hidio *x) } } - +// TODO: return the same as POSIX open()/close() - 0=success, -1=fail t_int hidio_open_device(t_hidio *x, short device_number) { debug_print(LOG_DEBUG,"hidio_open_device"); @@ -656,6 +717,8 @@ t_int hidio_open_device(t_hidio *x, short device_number) io_service_t hidDevice = 0; FFDeviceObjectReference ffDeviceReference = NULL; + latency_i = 0;latency_average = 0; // temp hack, to be removed + /* rebuild device list to make sure the list is current */ if( !HIDHaveDeviceList() ) hidio_build_device_list(); @@ -694,7 +757,7 @@ t_int hidio_open_device(t_hidio *x, short device_number) return(result); } - +// TODO: return the same as POSIX open()/close() - 0=success, -1=fail t_int hidio_close_device(t_hidio *x) { debug_print(LOG_DEBUG,"hidio_close_device"); @@ -738,7 +801,7 @@ void hidio_build_device_list(void) } /* TODO: this should be dumped for [devices( and [elements( messages */ -void hidio_doprint(t_hidio *x) +void hidio_print(t_hidio *x) { if( !HIDHaveDeviceList() ) hidio_build_device_list(); hidio_print_device_list(x); @@ -752,6 +815,7 @@ void hidio_doprint(t_hidio *x) void hidio_platform_specific_free(t_hidio *x) { + int j; debug_print(LOG_DEBUG,"hidio_platform_specific_free"); /* only call this if the last instance is being freed */ if (hidio_instance_count < 1) @@ -760,6 +824,10 @@ void hidio_platform_specific_free(t_hidio *x) HIDReleaseAllDeviceQueues(); HIDReleaseDeviceList(); } + for(j=0;j