├── debian ├── dirs ├── manpages ├── rules ├── copyright ├── control └── changelog ├── include └── Makefile.am ├── .github ├── FUNDING.yml └── workflows │ └── ubuntu.yml ├── alsa-midi-latency-test.gif ├── src ├── Makefile.am ├── alsa-midi-latency-test.1 └── alsa-midi-latency-test.c ├── m4 ├── Makefile.am ├── xsize.m4 ├── signed.m4 ├── wchar_t.m4 ├── codeset.m4 ├── inttypes-h.m4 ├── glibc2.m4 ├── ulonglong.m4 ├── glibc21.m4 ├── stdint_h.m4 ├── inttypes_h.m4 ├── wint_t.m4 ├── uintmax_t.m4 ├── intmax.m4 ├── longdouble.m4 ├── inttypes-pri.m4 ├── lcmessage.m4 ├── lock.m4 ├── printf-posix.m4 ├── intdiv0.m4 ├── size_max.m4 ├── visibility.m4 └── longlong.m4 ├── Makefile.am ├── autogen.sh ├── .gitignore ├── configure.ac ├── README.md ├── COPYING └── config.rpath /debian/dirs: -------------------------------------------------------------------------------- 1 | usr/bin 2 | -------------------------------------------------------------------------------- /debian/manpages: -------------------------------------------------------------------------------- 1 | src/alsa-midi-latency-test.1 2 | -------------------------------------------------------------------------------- /include/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CPPFLAGS=-I$(top_srcdir)/include 2 | 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: koppi 2 | custom: 'https://koppi.github.io' -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | %: 3 | dh $@ --with autoreconf 4 | -------------------------------------------------------------------------------- /alsa-midi-latency-test.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koppi/alsa-midi-latency-test/HEAD/alsa-midi-latency-test.gif -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | SOURCES = alsa-midi-latency-test.c 2 | 3 | LDADD = -lasound @CLOCK_LIB@ 4 | 5 | EXTRA_DIST = alsa-midi-latency-test.1 6 | 7 | bin_PROGRAMS = alsa-midi-latency-test 8 | man_MANS = alsa-midi-latency-test.1 9 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | The source code was downloaded from the alsa-midi-latency-test homepage: 2 | 3 | http://github.com/koppi/alsa-midi-latency-test/ 4 | 5 | alsa-midi-latency-test 6 | ---------------------- 7 | © 2009,2019 Jakob Flierl 8 | -------------------------------------------------------------------------------- /m4/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = codeset.m4 gettext.m4 glibc2.m4 glibc21.m4 iconv.m4 intdiv0.m4 intmax.m4 inttypes_h.m4 inttypes-h.m4 inttypes-pri.m4 lcmessage.m4 lib-ld.m4 lib-link.m4 lib-prefix.m4 lock.m4 longdouble.m4 longlong.m4 nls.m4 po.m4 printf-posix.m4 progtest.m4 signed.m4 size_max.m4 stdint_h.m4 uintmax_t.m4 ulonglong.m4 visibility.m4 wchar_t.m4 wint_t.m4 xsize.m4 gettext.m4 2 | -------------------------------------------------------------------------------- /m4/xsize.m4: -------------------------------------------------------------------------------- 1 | # xsize.m4 serial 5 2 | dnl Copyright (C) 2003-2004, 2008-2016 Free Software Foundation, Inc. 3 | dnl This file is free software; the Free Software Foundation 4 | dnl gives unlimited permission to copy and/or distribute it, 5 | dnl with or without modifications, as long as this notice is preserved. 6 | 7 | AC_DEFUN([gl_XSIZE], 8 | [ 9 | dnl Prerequisites of lib/xsize.h. 10 | AC_REQUIRE([gl_SIZE_MAX]) 11 | AC_CHECK_HEADERS([stdint.h]) 12 | ]) 13 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CPPFLAGS=-I$(top_srcdir)/include 2 | 3 | SUBDIRS= include src 4 | EXTRA_DIST= config.rpath config.rpath mkinstalldirs INSTALL TODO README configure 5 | AUTOMAKE_OPTIONS=foreign 6 | ACLOCAL_AMFLAGS = -I m4 7 | 8 | dist-hook: 9 | -chmod -R a+r $(distdir) 10 | @if ! test -z "$(AMTAR)"; then \ 11 | $(AMTAR) --create --verbose --file=- $(distdir) | bzip2 -c -9 > $(distdir).tar.bz2 ; \ 12 | else \ 13 | $(TAR) --create --verbose --file=- $(distdir) | bzip2 -c -9 > $(distdir).tar.bz2 ; \ 14 | fi 15 | 16 | upload: 17 | debuild -S -sa && dput ppa-alsa-midi-latency-test alsa-midi-latency-test_*_source.changes 18 | -------------------------------------------------------------------------------- /m4/signed.m4: -------------------------------------------------------------------------------- 1 | # signed.m4 serial 1 (gettext-0.10.40) 2 | dnl Copyright (C) 2001-2002 Free Software Foundation, Inc. 3 | dnl This file is free software; the Free Software Foundation 4 | dnl gives unlimited permission to copy and/or distribute it, 5 | dnl with or without modifications, as long as this notice is preserved. 6 | 7 | dnl From Bruno Haible. 8 | 9 | AC_DEFUN([bh_C_SIGNED], 10 | [ 11 | AC_CACHE_CHECK([for signed], bh_cv_c_signed, 12 | [AC_TRY_COMPILE(, [signed char x;], bh_cv_c_signed=yes, bh_cv_c_signed=no)]) 13 | if test $bh_cv_c_signed = no; then 14 | AC_DEFINE(signed, , 15 | [Define to empty if the C compiler doesn't support this keyword.]) 16 | fi 17 | ]) 18 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: alsa-midi-latency-test 2 | Section: sound 3 | Priority: optional 4 | Maintainer: Jakob Flierl 5 | Build-Depends: debhelper-compat (= 13), libtool, autotools-dev, automake, libasound2-dev (>= 1.2.11), dh-autoreconf, autopoint 6 | Standards-Version: 4.7.0 7 | Homepage: http://github.com/koppi/alsa-midi-latency-test 8 | Vcs-Git: https://github.com/koppi/alsa-midi-latency-test.git 9 | Vcs-Browser: https://github.com/koppi/alsa-midi-latency-test 10 | Rules-Requires-Root: no 11 | 12 | Package: alsa-midi-latency-test 13 | Architecture: linux-any 14 | Depends: ${misc:Depends}, ${shlibs:Depends} 15 | Description: Utility for measuring ALSA midi latency 16 | Included tools 17 | - alsa-midi-latency: utility for measuring ALSA midi latency 18 | . 19 | ALSA is the Advanced Linux Sound Architecture. 20 | -------------------------------------------------------------------------------- /m4/wchar_t.m4: -------------------------------------------------------------------------------- 1 | # wchar_t.m4 serial 4 (gettext-0.18.2) 2 | dnl Copyright (C) 2002-2003, 2008-2016 Free Software Foundation, Inc. 3 | dnl This file is free software; the Free Software Foundation 4 | dnl gives unlimited permission to copy and/or distribute it, 5 | dnl with or without modifications, as long as this notice is preserved. 6 | 7 | dnl From Bruno Haible. 8 | dnl Test whether has the 'wchar_t' type. 9 | dnl Prerequisite: AC_PROG_CC 10 | 11 | AC_DEFUN([gt_TYPE_WCHAR_T], 12 | [ 13 | AC_CACHE_CHECK([for wchar_t], [gt_cv_c_wchar_t], 14 | [AC_COMPILE_IFELSE( 15 | [AC_LANG_PROGRAM( 16 | [[#include 17 | wchar_t foo = (wchar_t)'\0';]], 18 | [[]])], 19 | [gt_cv_c_wchar_t=yes], 20 | [gt_cv_c_wchar_t=no])]) 21 | if test $gt_cv_c_wchar_t = yes; then 22 | AC_DEFINE([HAVE_WCHAR_T], [1], [Define if you have the 'wchar_t' type.]) 23 | fi 24 | ]) 25 | -------------------------------------------------------------------------------- /m4/codeset.m4: -------------------------------------------------------------------------------- 1 | # codeset.m4 serial 5 (gettext-0.18.2) 2 | dnl Copyright (C) 2000-2002, 2006, 2008-2014, 2016 Free Software Foundation, 3 | dnl Inc. 4 | dnl This file is free software; the Free Software Foundation 5 | dnl gives unlimited permission to copy and/or distribute it, 6 | dnl with or without modifications, as long as this notice is preserved. 7 | 8 | dnl From Bruno Haible. 9 | 10 | AC_DEFUN([AM_LANGINFO_CODESET], 11 | [ 12 | AC_CACHE_CHECK([for nl_langinfo and CODESET], [am_cv_langinfo_codeset], 13 | [AC_LINK_IFELSE( 14 | [AC_LANG_PROGRAM( 15 | [[#include ]], 16 | [[char* cs = nl_langinfo(CODESET); return !cs;]])], 17 | [am_cv_langinfo_codeset=yes], 18 | [am_cv_langinfo_codeset=no]) 19 | ]) 20 | if test $am_cv_langinfo_codeset = yes; then 21 | AC_DEFINE([HAVE_LANGINFO_CODESET], [1], 22 | [Define if you have and nl_langinfo(CODESET).]) 23 | fi 24 | ]) 25 | -------------------------------------------------------------------------------- /m4/inttypes-h.m4: -------------------------------------------------------------------------------- 1 | # inttypes-h.m4 serial 1 (gettext-0.15) 2 | dnl Copyright (C) 1997-2002, 2006 Free Software Foundation, Inc. 3 | dnl This file is free software; the Free Software Foundation 4 | dnl gives unlimited permission to copy and/or distribute it, 5 | dnl with or without modifications, as long as this notice is preserved. 6 | 7 | dnl From Paul Eggert. 8 | 9 | # Define HAVE_INTTYPES_H if exists and doesn't clash with 10 | # . 11 | 12 | AC_DEFUN([gl_HEADER_INTTYPES_H], 13 | [ 14 | AC_CACHE_CHECK([for inttypes.h], gl_cv_header_inttypes_h, 15 | [ 16 | AC_TRY_COMPILE( 17 | [#include 18 | #include ], 19 | [], gl_cv_header_inttypes_h=yes, gl_cv_header_inttypes_h=no) 20 | ]) 21 | if test $gl_cv_header_inttypes_h = yes; then 22 | AC_DEFINE_UNQUOTED(HAVE_INTTYPES_H, 1, 23 | [Define if exists and doesn't clash with .]) 24 | fi 25 | ]) 26 | -------------------------------------------------------------------------------- /m4/glibc2.m4: -------------------------------------------------------------------------------- 1 | # glibc2.m4 serial 3 2 | dnl Copyright (C) 2000-2002, 2004, 2008, 2010-2016 Free Software Foundation, 3 | dnl Inc. 4 | dnl This file is free software; the Free Software Foundation 5 | dnl gives unlimited permission to copy and/or distribute it, 6 | dnl with or without modifications, as long as this notice is preserved. 7 | 8 | # Test for the GNU C Library, version 2.0 or newer. 9 | # From Bruno Haible. 10 | 11 | AC_DEFUN([gt_GLIBC2], 12 | [ 13 | AC_CACHE_CHECK([whether we are using the GNU C Library 2 or newer], 14 | [ac_cv_gnu_library_2], 15 | [AC_EGREP_CPP([Lucky GNU user], 16 | [ 17 | #include 18 | #ifdef __GNU_LIBRARY__ 19 | #if (__GLIBC__ >= 2) && !defined __UCLIBC__ 20 | Lucky GNU user 21 | #endif 22 | #endif 23 | ], 24 | [ac_cv_gnu_library_2=yes], 25 | [ac_cv_gnu_library_2=no]) 26 | ] 27 | ) 28 | AC_SUBST([GLIBC2]) 29 | GLIBC2="$ac_cv_gnu_library_2" 30 | ] 31 | ) 32 | -------------------------------------------------------------------------------- /.github/workflows/ubuntu.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ master ] 4 | pull_request: 5 | branches: [ master ] 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: install devscripts 15 | run: | 16 | sudo apt -qq update 17 | sudo DEBIAN_FRONTEND=noninteractive apt -qq -y install devscripts equivs lintian 18 | mk-build-deps -i -s sudo -t "apt --yes --no-install-recommends" 19 | 20 | - name: build the package 21 | run: dpkg-buildpackage -b -rfakeroot -us -uc 22 | 23 | - name: install runtime dependencies 24 | run: sudo apt -y install linux-sound-base 25 | 26 | - name: install the package 27 | run: sudo dpkg -i ../alsa-midi-latency-test*deb 28 | 29 | - name: install package dependencies 30 | run: sudo apt -f install 31 | 32 | - name: run lintian 33 | run: lintian ../alsa-midi-latency-test*deb | lintian-info 34 | -------------------------------------------------------------------------------- /m4/ulonglong.m4: -------------------------------------------------------------------------------- 1 | # ulonglong.m4 serial 4 2 | dnl Copyright (C) 1999-2004 Free Software Foundation, Inc. 3 | dnl This file is free software; the Free Software Foundation 4 | dnl gives unlimited permission to copy and/or distribute it, 5 | dnl with or without modifications, as long as this notice is preserved. 6 | 7 | dnl From Paul Eggert. 8 | 9 | # Define HAVE_UNSIGNED_LONG_LONG if 'unsigned long long' works. 10 | 11 | AC_DEFUN([gl_AC_TYPE_UNSIGNED_LONG_LONG], 12 | [ 13 | AC_CACHE_CHECK([for unsigned long long], ac_cv_type_unsigned_long_long, 14 | [AC_TRY_LINK([unsigned long long ull = 1ULL; int i = 63;], 15 | [unsigned long long ullmax = (unsigned long long) -1; 16 | return ull << i | ull >> i | ullmax / ull | ullmax % ull;], 17 | ac_cv_type_unsigned_long_long=yes, 18 | ac_cv_type_unsigned_long_long=no)]) 19 | if test $ac_cv_type_unsigned_long_long = yes; then 20 | AC_DEFINE(HAVE_UNSIGNED_LONG_LONG, 1, 21 | [Define if you have the 'unsigned long long' type.]) 22 | fi 23 | ]) 24 | -------------------------------------------------------------------------------- /m4/glibc21.m4: -------------------------------------------------------------------------------- 1 | # glibc21.m4 serial 5 2 | dnl Copyright (C) 2000-2002, 2004, 2008, 2010-2016 Free Software Foundation, 3 | dnl Inc. 4 | dnl This file is free software; the Free Software Foundation 5 | dnl gives unlimited permission to copy and/or distribute it, 6 | dnl with or without modifications, as long as this notice is preserved. 7 | 8 | # Test for the GNU C Library, version 2.1 or newer, or uClibc. 9 | # From Bruno Haible. 10 | 11 | AC_DEFUN([gl_GLIBC21], 12 | [ 13 | AC_CACHE_CHECK([whether we are using the GNU C Library >= 2.1 or uClibc], 14 | [ac_cv_gnu_library_2_1], 15 | [AC_EGREP_CPP([Lucky], 16 | [ 17 | #include 18 | #ifdef __GNU_LIBRARY__ 19 | #if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) || (__GLIBC__ > 2) 20 | Lucky GNU user 21 | #endif 22 | #endif 23 | #ifdef __UCLIBC__ 24 | Lucky user 25 | #endif 26 | ], 27 | [ac_cv_gnu_library_2_1=yes], 28 | [ac_cv_gnu_library_2_1=no]) 29 | ] 30 | ) 31 | AC_SUBST([GLIBC21]) 32 | GLIBC21="$ac_cv_gnu_library_2_1" 33 | ] 34 | ) 35 | -------------------------------------------------------------------------------- /m4/stdint_h.m4: -------------------------------------------------------------------------------- 1 | # stdint_h.m4 serial 9 2 | dnl Copyright (C) 1997-2004, 2006, 2008-2016 Free Software Foundation, Inc. 3 | dnl This file is free software; the Free Software Foundation 4 | dnl gives unlimited permission to copy and/or distribute it, 5 | dnl with or without modifications, as long as this notice is preserved. 6 | 7 | dnl From Paul Eggert. 8 | 9 | # Define HAVE_STDINT_H_WITH_UINTMAX if exists, 10 | # doesn't clash with , and declares uintmax_t. 11 | 12 | AC_DEFUN([gl_AC_HEADER_STDINT_H], 13 | [ 14 | AC_CACHE_CHECK([for stdint.h], [gl_cv_header_stdint_h], 15 | [AC_COMPILE_IFELSE( 16 | [AC_LANG_PROGRAM( 17 | [[#include 18 | #include ]], 19 | [[uintmax_t i = (uintmax_t) -1; return !i;]])], 20 | [gl_cv_header_stdint_h=yes], 21 | [gl_cv_header_stdint_h=no])]) 22 | if test $gl_cv_header_stdint_h = yes; then 23 | AC_DEFINE_UNQUOTED([HAVE_STDINT_H_WITH_UINTMAX], [1], 24 | [Define if exists, doesn't clash with , 25 | and declares uintmax_t. ]) 26 | fi 27 | ]) 28 | -------------------------------------------------------------------------------- /m4/inttypes_h.m4: -------------------------------------------------------------------------------- 1 | # inttypes_h.m4 serial 10 2 | dnl Copyright (C) 1997-2004, 2006, 2008-2016 Free Software Foundation, Inc. 3 | dnl This file is free software; the Free Software Foundation 4 | dnl gives unlimited permission to copy and/or distribute it, 5 | dnl with or without modifications, as long as this notice is preserved. 6 | 7 | dnl From Paul Eggert. 8 | 9 | # Define HAVE_INTTYPES_H_WITH_UINTMAX if exists, 10 | # doesn't clash with , and declares uintmax_t. 11 | 12 | AC_DEFUN([gl_AC_HEADER_INTTYPES_H], 13 | [ 14 | AC_CACHE_CHECK([for inttypes.h], [gl_cv_header_inttypes_h], 15 | [AC_COMPILE_IFELSE( 16 | [AC_LANG_PROGRAM( 17 | [[ 18 | #include 19 | #include 20 | ]], 21 | [[uintmax_t i = (uintmax_t) -1; return !i;]])], 22 | [gl_cv_header_inttypes_h=yes], 23 | [gl_cv_header_inttypes_h=no])]) 24 | if test $gl_cv_header_inttypes_h = yes; then 25 | AC_DEFINE_UNQUOTED([HAVE_INTTYPES_H_WITH_UINTMAX], [1], 26 | [Define if exists, doesn't clash with , 27 | and declares uintmax_t. ]) 28 | fi 29 | ]) 30 | -------------------------------------------------------------------------------- /m4/wint_t.m4: -------------------------------------------------------------------------------- 1 | # wint_t.m4 serial 5 (gettext-0.18.2) 2 | dnl Copyright (C) 2003, 2007-2016 Free Software Foundation, Inc. 3 | dnl This file is free software; the Free Software Foundation 4 | dnl gives unlimited permission to copy and/or distribute it, 5 | dnl with or without modifications, as long as this notice is preserved. 6 | 7 | dnl From Bruno Haible. 8 | dnl Test whether has the 'wint_t' type. 9 | dnl Prerequisite: AC_PROG_CC 10 | 11 | AC_DEFUN([gt_TYPE_WINT_T], 12 | [ 13 | AC_CACHE_CHECK([for wint_t], [gt_cv_c_wint_t], 14 | [AC_COMPILE_IFELSE( 15 | [AC_LANG_PROGRAM( 16 | [[ 17 | /* Tru64 with Desktop Toolkit C has a bug: must be included before 18 | . 19 | BSD/OS 4.0.1 has a bug: , and must be included 20 | before . */ 21 | #include 22 | #include 23 | #include 24 | #include 25 | wint_t foo = (wchar_t)'\0';]], 26 | [[]])], 27 | [gt_cv_c_wint_t=yes], 28 | [gt_cv_c_wint_t=no])]) 29 | if test $gt_cv_c_wint_t = yes; then 30 | AC_DEFINE([HAVE_WINT_T], [1], [Define if you have the 'wint_t' type.]) 31 | fi 32 | ]) 33 | -------------------------------------------------------------------------------- /m4/uintmax_t.m4: -------------------------------------------------------------------------------- 1 | # uintmax_t.m4 serial 12 2 | dnl Copyright (C) 1997-2004, 2007-2016 Free Software Foundation, Inc. 3 | dnl This file is free software; the Free Software Foundation 4 | dnl gives unlimited permission to copy and/or distribute it, 5 | dnl with or without modifications, as long as this notice is preserved. 6 | 7 | dnl From Paul Eggert. 8 | 9 | AC_PREREQ([2.13]) 10 | 11 | # Define uintmax_t to 'unsigned long' or 'unsigned long long' 12 | # if it is not already defined in or . 13 | 14 | AC_DEFUN([gl_AC_TYPE_UINTMAX_T], 15 | [ 16 | AC_REQUIRE([gl_AC_HEADER_INTTYPES_H]) 17 | AC_REQUIRE([gl_AC_HEADER_STDINT_H]) 18 | if test $gl_cv_header_inttypes_h = no && test $gl_cv_header_stdint_h = no; then 19 | AC_REQUIRE([AC_TYPE_UNSIGNED_LONG_LONG_INT]) 20 | test $ac_cv_type_unsigned_long_long_int = yes \ 21 | && ac_type='unsigned long long' \ 22 | || ac_type='unsigned long' 23 | AC_DEFINE_UNQUOTED([uintmax_t], [$ac_type], 24 | [Define to unsigned long or unsigned long long 25 | if and don't define.]) 26 | else 27 | AC_DEFINE([HAVE_UINTMAX_T], [1], 28 | [Define if you have the 'uintmax_t' type in or .]) 29 | fi 30 | ]) 31 | -------------------------------------------------------------------------------- /m4/intmax.m4: -------------------------------------------------------------------------------- 1 | # intmax.m4 serial 6 (gettext-0.18.2) 2 | dnl Copyright (C) 2002-2005, 2008-2016 Free Software Foundation, Inc. 3 | dnl This file is free software; the Free Software Foundation 4 | dnl gives unlimited permission to copy and/or distribute it, 5 | dnl with or without modifications, as long as this notice is preserved. 6 | 7 | dnl From Bruno Haible. 8 | dnl Test whether the system has the 'intmax_t' type, but don't attempt to 9 | dnl find a replacement if it is lacking. 10 | 11 | AC_DEFUN([gt_TYPE_INTMAX_T], 12 | [ 13 | AC_REQUIRE([gl_AC_HEADER_INTTYPES_H]) 14 | AC_REQUIRE([gl_AC_HEADER_STDINT_H]) 15 | AC_CACHE_CHECK([for intmax_t], [gt_cv_c_intmax_t], 16 | [AC_COMPILE_IFELSE( 17 | [AC_LANG_PROGRAM( 18 | [[ 19 | #include 20 | #include 21 | #if HAVE_STDINT_H_WITH_UINTMAX 22 | #include 23 | #endif 24 | #if HAVE_INTTYPES_H_WITH_UINTMAX 25 | #include 26 | #endif 27 | ]], 28 | [[intmax_t x = -1; 29 | return !x;]])], 30 | [gt_cv_c_intmax_t=yes], 31 | [gt_cv_c_intmax_t=no])]) 32 | if test $gt_cv_c_intmax_t = yes; then 33 | AC_DEFINE([HAVE_INTMAX_T], [1], 34 | [Define if you have the 'intmax_t' type in or .]) 35 | fi 36 | ]) 37 | -------------------------------------------------------------------------------- /m4/longdouble.m4: -------------------------------------------------------------------------------- 1 | # longdouble.m4 serial 2 (gettext-0.15) 2 | dnl Copyright (C) 2002-2003, 2006 Free Software Foundation, Inc. 3 | dnl This file is free software; the Free Software Foundation 4 | dnl gives unlimited permission to copy and/or distribute it, 5 | dnl with or without modifications, as long as this notice is preserved. 6 | 7 | dnl From Bruno Haible. 8 | dnl Test whether the compiler supports the 'long double' type. 9 | dnl Prerequisite: AC_PROG_CC 10 | 11 | dnl This file is only needed in autoconf <= 2.59. Newer versions of autoconf 12 | dnl have a macro AC_TYPE_LONG_DOUBLE with identical semantics. 13 | 14 | AC_DEFUN([gt_TYPE_LONGDOUBLE], 15 | [ 16 | AC_CACHE_CHECK([for long double], gt_cv_c_long_double, 17 | [if test "$GCC" = yes; then 18 | gt_cv_c_long_double=yes 19 | else 20 | AC_TRY_COMPILE([ 21 | /* The Stardent Vistra knows sizeof(long double), but does not support it. */ 22 | long double foo = 0.0; 23 | /* On Ultrix 4.3 cc, long double is 4 and double is 8. */ 24 | int array [2*(sizeof(long double) >= sizeof(double)) - 1]; 25 | ], , 26 | gt_cv_c_long_double=yes, gt_cv_c_long_double=no) 27 | fi]) 28 | if test $gt_cv_c_long_double = yes; then 29 | AC_DEFINE(HAVE_LONG_DOUBLE, 1, [Define if you have the 'long double' type.]) 30 | fi 31 | ]) 32 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | if [ "$USER" = "root" ]; then 4 | echo "*** Warning. You cannot do this as "$USER" please use a normal user account." 5 | fi 6 | if test ! -f configure.ac ; then 7 | echo "*** Please invoke this script from directory containing configure.ac." 8 | exit 1 9 | fi 10 | 11 | echo "running aclocal" 12 | aclocal 13 | rc=$? 14 | 15 | if test $rc -eq 0; then 16 | echo "running libtool" 17 | libtoolize --force --automake --copy 18 | rc=$? 19 | else 20 | echo "An error occured, autogen.sh stopping." 21 | exit $rc 22 | fi 23 | 24 | if test $rc -eq 0; then 25 | echo "libtool worked." 26 | else 27 | echo "libtool not found. trying glibtool." 28 | glibtoolize --force --automake --copy 29 | rc=$? 30 | fi 31 | 32 | if test $rc -eq 0; then 33 | echo "running autoheader" 34 | autoheader 35 | rc=$? 36 | else 37 | echo "An error occured, autogen.sh stopping." 38 | exit $rc 39 | fi 40 | 41 | if test $rc -eq 0; then 42 | echo "running automake" 43 | automake -f -i --add-missing --copy 44 | rc=$? 45 | else 46 | echo "An error occured, autogen.sh stopping." 47 | exit $rc 48 | fi 49 | 50 | if test $rc -eq 0; then 51 | echo "running autoconf" 52 | autoconf 53 | rc=$? 54 | else 55 | echo "An error occured, autogen.sh stopping." 56 | exit $rc 57 | fi 58 | 59 | echo "autogen.sh complete" 60 | exit $rc 61 | -------------------------------------------------------------------------------- /m4/inttypes-pri.m4: -------------------------------------------------------------------------------- 1 | # inttypes-pri.m4 serial 7 (gettext-0.18.2) 2 | dnl Copyright (C) 1997-2002, 2006, 2008-2016 Free Software Foundation, Inc. 3 | dnl This file is free software; the Free Software Foundation 4 | dnl gives unlimited permission to copy and/or distribute it, 5 | dnl with or without modifications, as long as this notice is preserved. 6 | 7 | dnl From Bruno Haible. 8 | 9 | AC_PREREQ([2.53]) 10 | 11 | # Define PRI_MACROS_BROKEN if exists and defines the PRI* 12 | # macros to non-string values. This is the case on AIX 4.3.3. 13 | 14 | AC_DEFUN([gt_INTTYPES_PRI], 15 | [ 16 | AC_CHECK_HEADERS([inttypes.h]) 17 | if test $ac_cv_header_inttypes_h = yes; then 18 | AC_CACHE_CHECK([whether the inttypes.h PRIxNN macros are broken], 19 | [gt_cv_inttypes_pri_broken], 20 | [ 21 | AC_COMPILE_IFELSE( 22 | [AC_LANG_PROGRAM( 23 | [[ 24 | #include 25 | #ifdef PRId32 26 | char *p = PRId32; 27 | #endif 28 | ]], 29 | [[]])], 30 | [gt_cv_inttypes_pri_broken=no], 31 | [gt_cv_inttypes_pri_broken=yes]) 32 | ]) 33 | fi 34 | if test "$gt_cv_inttypes_pri_broken" = yes; then 35 | AC_DEFINE_UNQUOTED([PRI_MACROS_BROKEN], [1], 36 | [Define if exists and defines unusable PRI* macros.]) 37 | PRI_MACROS_BROKEN=1 38 | else 39 | PRI_MACROS_BROKEN=0 40 | fi 41 | AC_SUBST([PRI_MACROS_BROKEN]) 42 | ]) 43 | -------------------------------------------------------------------------------- /m4/lcmessage.m4: -------------------------------------------------------------------------------- 1 | # lcmessage.m4 serial 7 (gettext-0.18.2) 2 | dnl Copyright (C) 1995-2002, 2004-2005, 2008-2014, 2016 Free Software 3 | dnl Foundation, Inc. 4 | dnl This file is free software; the Free Software Foundation 5 | dnl gives unlimited permission to copy and/or distribute it, 6 | dnl with or without modifications, as long as this notice is preserved. 7 | dnl 8 | dnl This file can be used in projects which are not available under 9 | dnl the GNU General Public License or the GNU Library General Public 10 | dnl License but which still want to provide support for the GNU gettext 11 | dnl functionality. 12 | dnl Please note that the actual code of the GNU gettext library is covered 13 | dnl by the GNU Library General Public License, and the rest of the GNU 14 | dnl gettext package is covered by the GNU General Public License. 15 | dnl They are *not* in the public domain. 16 | 17 | dnl Authors: 18 | dnl Ulrich Drepper , 1995. 19 | 20 | # Check whether LC_MESSAGES is available in . 21 | 22 | AC_DEFUN([gt_LC_MESSAGES], 23 | [ 24 | AC_CACHE_CHECK([for LC_MESSAGES], [gt_cv_val_LC_MESSAGES], 25 | [AC_LINK_IFELSE( 26 | [AC_LANG_PROGRAM( 27 | [[#include ]], 28 | [[return LC_MESSAGES]])], 29 | [gt_cv_val_LC_MESSAGES=yes], 30 | [gt_cv_val_LC_MESSAGES=no])]) 31 | if test $gt_cv_val_LC_MESSAGES = yes; then 32 | AC_DEFINE([HAVE_LC_MESSAGES], [1], 33 | [Define if your file defines LC_MESSAGES.]) 34 | fi 35 | ]) 36 | -------------------------------------------------------------------------------- /m4/lock.m4: -------------------------------------------------------------------------------- 1 | # lock.m4 serial 13 (gettext-0.18.2) 2 | dnl Copyright (C) 2005-2016 Free Software Foundation, Inc. 3 | dnl This file is free software; the Free Software Foundation 4 | dnl gives unlimited permission to copy and/or distribute it, 5 | dnl with or without modifications, as long as this notice is preserved. 6 | 7 | dnl From Bruno Haible. 8 | 9 | AC_DEFUN([gl_LOCK], 10 | [ 11 | AC_REQUIRE([gl_THREADLIB]) 12 | if test "$gl_threads_api" = posix; then 13 | # OSF/1 4.0 and Mac OS X 10.1 lack the pthread_rwlock_t type and the 14 | # pthread_rwlock_* functions. 15 | AC_CHECK_TYPE([pthread_rwlock_t], 16 | [AC_DEFINE([HAVE_PTHREAD_RWLOCK], [1], 17 | [Define if the POSIX multithreading library has read/write locks.])], 18 | [], 19 | [#include ]) 20 | # glibc defines PTHREAD_MUTEX_RECURSIVE as enum, not as a macro. 21 | AC_COMPILE_IFELSE([ 22 | AC_LANG_PROGRAM( 23 | [[#include ]], 24 | [[ 25 | #if __FreeBSD__ == 4 26 | error "No, in FreeBSD 4.0 recursive mutexes actually don't work." 27 | #elif (defined __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ \ 28 | && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070) 29 | error "No, in Mac OS X < 10.7 recursive mutexes actually don't work." 30 | #else 31 | int x = (int)PTHREAD_MUTEX_RECURSIVE; 32 | return !x; 33 | #endif 34 | ]])], 35 | [AC_DEFINE([HAVE_PTHREAD_MUTEX_RECURSIVE], [1], 36 | [Define if the defines PTHREAD_MUTEX_RECURSIVE.])]) 37 | fi 38 | gl_PREREQ_LOCK 39 | ]) 40 | 41 | # Prerequisites of lib/glthread/lock.c. 42 | AC_DEFUN([gl_PREREQ_LOCK], [:]) 43 | -------------------------------------------------------------------------------- /m4/printf-posix.m4: -------------------------------------------------------------------------------- 1 | # printf-posix.m4 serial 6 (gettext-0.18.2) 2 | dnl Copyright (C) 2003, 2007, 2009-2016 Free Software Foundation, Inc. 3 | dnl This file is free software; the Free Software Foundation 4 | dnl gives unlimited permission to copy and/or distribute it, 5 | dnl with or without modifications, as long as this notice is preserved. 6 | 7 | dnl From Bruno Haible. 8 | dnl Test whether the printf() function supports POSIX/XSI format strings with 9 | dnl positions. 10 | 11 | AC_DEFUN([gt_PRINTF_POSIX], 12 | [ 13 | AC_REQUIRE([AC_PROG_CC]) 14 | AC_CACHE_CHECK([whether printf() supports POSIX/XSI format strings], 15 | gt_cv_func_printf_posix, 16 | [ 17 | AC_RUN_IFELSE( 18 | [AC_LANG_SOURCE([[ 19 | #include 20 | #include 21 | /* The string "%2$d %1$d", with dollar characters protected from the shell's 22 | dollar expansion (possibly an autoconf bug). */ 23 | static char format[] = { '%', '2', '$', 'd', ' ', '%', '1', '$', 'd', '\0' }; 24 | static char buf[100]; 25 | int main () 26 | { 27 | sprintf (buf, format, 33, 55); 28 | return (strcmp (buf, "55 33") != 0); 29 | }]])], 30 | [gt_cv_func_printf_posix=yes], 31 | [gt_cv_func_printf_posix=no], 32 | [ 33 | AC_EGREP_CPP([notposix], [ 34 | #if defined __NetBSD__ || defined __BEOS__ || defined _MSC_VER || defined __MINGW32__ || defined __CYGWIN__ 35 | notposix 36 | #endif 37 | ], 38 | [gt_cv_func_printf_posix="guessing no"], 39 | [gt_cv_func_printf_posix="guessing yes"]) 40 | ]) 41 | ]) 42 | case $gt_cv_func_printf_posix in 43 | *yes) 44 | AC_DEFINE([HAVE_POSIX_PRINTF], [1], 45 | [Define if your printf() function supports format strings with positions.]) 46 | ;; 47 | esac 48 | ]) 49 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | alsa-midi-latency-test (0.0.6) UNRELEASED; urgency=medium 2 | 3 | * Push version 0.0.5.1. 4 | 5 | -- Jakob Flierl Tue, 13 May 2025 17:25:38 +0200 6 | 7 | alsa-midi-latency-test (0.0.4-3ubuntu1) unstable; urgency=low 8 | 9 | * Push version 0.0.4-3. 10 | 11 | -- Jakob Flierl Thu, 30 Jan 2014 00:01:02 +0100 12 | 13 | alsa-midi-latency-test (0.0.4-2ubuntu1) unstable; urgency=low 14 | 15 | * Push version 0.0.4-2. 16 | 17 | -- Jakob Flierl Wed, 29 Jan 2014 22:26:58 +0100 18 | 19 | alsa-midi-latency-test (0.0.4-1ubuntu1) unstable; urgency=low 20 | 21 | * Jakob Flierl: change CLOCK_MONOTONIC to CLOCK_MONOTONIC_RAW. 22 | 23 | -- Jakob Flierl Tue, 28 Jan 2014 21:33:54 +0100 24 | 25 | alsa-midi-latency-test (0.0.3-1) unstable; urgency=low 26 | 27 | * Clemens Ladisch: added -w and -r options. 28 | 29 | -- Jakob Flierl Tue, 13 Oct 2009 19:22:27 +0100 30 | 31 | alsa-midi-latency-test (0.0.2-1) unstable; urgency=low 32 | 33 | * Clemens Ladisch: High precision timer changes, lots of fixes. 34 | * Arnout Engelen: Fix sending notes: the source port is always '0', instead 35 | of equal to the destination port. 36 | * Arnout Engelen: Improve control flow: collect data inside the while loop, 37 | show results outside it. 38 | * Arnout Engelen: Always show results, even when collection of data was 39 | interrupted. 40 | * Improved interruption support: allow interruption when no data is coming 41 | in. 42 | 43 | -- Jakob Flierl Mon, 12 Oct 2009 11:22:27 +0100 44 | 45 | alsa-midi-latency-test (0.0.1-1) unstable; urgency=low 46 | 47 | * Initial release. 48 | 49 | -- Jakob Flierl Thu, 12 Mar 2009 12:01:50 +0100 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # debuild stuff 2 | 3 | m4/Makefile 4 | 5 | config.guess.cdbs-orig 6 | config.sub.cdbs-orig 7 | debian/files 8 | debian/stamp-autotools 9 | debian/stamp-autotools-files 10 | debian/stamp-makefile-build 11 | debian/stamp-makefile-install 12 | 13 | 14 | ## generic files to ignore 15 | *.lock 16 | *.DS_Store 17 | *.swp 18 | *.out 19 | *~ 20 | *.o 21 | 22 | # ctags etags stuff 23 | TAGS 24 | 25 | # autoconf stuff 26 | autom4te.cache 27 | Makefile 28 | Makefile.in 29 | GNUmakefile 30 | GNUmakefile.in 31 | aclocal.m4 32 | configure 33 | config.log 34 | config.status 35 | libtool 36 | aclocal.m4 37 | include/Makefile.in 38 | include/aconfig.h.in 39 | m4/Makefile 40 | m4/Makefile.in 41 | src/Makefile.in 42 | build-stamp 43 | compile 44 | config.guess 45 | config.rpath 46 | config.status 47 | config.sub 48 | configure 49 | configure-stamp 50 | 51 | # debian related stuff 52 | debian/alsa-midi-latency-test 53 | debian/alsa-midi-latency-test.substvars 54 | debian/alsa-midi-latency-test.debhelper.log 55 | 56 | # generated makefiles 57 | include/Makefile 58 | include/aconfig.h 59 | include/stamp-h1 60 | 61 | # more autoconf generated stuff 62 | install-sh 63 | ltmain.sh 64 | m4/libtool.m4 65 | m4/ltoptions.m4 66 | m4/ltsugar.m4 67 | m4/ltversion.m4 68 | m4/lt~obsolete.m4 69 | missing 70 | 71 | ABOUT-NLS 72 | debian/autoreconf.after 73 | debian/autoreconf.before 74 | debian/tmp/ 75 | depcomp 76 | m4/intl.m4 77 | po/ 78 | 79 | # more generated files 80 | src/Makefile 81 | src/alsa-midi-latency-test 82 | 83 | # java specific 84 | *.class 85 | 86 | # python specific 87 | *.pyc 88 | 89 | # xcode iphone project stuff 90 | build/* 91 | *.pbxuser 92 | *.mode2v3 93 | *.mode1v3 94 | *.perspective 95 | *.perspectivev3 96 | *~.nib 97 | 98 | # also ignore private workspace stuff added by Xcode4 99 | xcuserdata 100 | project.xcworkspace 101 | 102 | -------------------------------------------------------------------------------- /src/alsa-midi-latency-test.1: -------------------------------------------------------------------------------- 1 | .TH "ALSA-MIDI-LATENCY-TEST" "1" "0.0.4" "01/2014" "" 2 | .SH NAME 3 | alsa-midi-latency-test \- measures latency of a MIDI loop back chain. 4 | 5 | .SH SYNOPSIS 6 | .B alsa-midi-latency-test 7 | \-o client:port -i client:port [options] 8 | .br 9 | .B alsa-midi-latency-test 10 | \-\-help 11 | 12 | .SH DESCRIPTION 13 | .B alsa-midi-latency-test 14 | measures latency of a MIDI device or a MIDI loop back chain. 15 | 16 | To interrupt a running latency measurement test, press . 17 | 18 | .SH OPTIONS 19 | 20 | .TP 21 | .I \-o,\-\-output=client:port 22 | Sets the sequencer output port where test events are sent to. 23 | 24 | A client can be specified by its number, its name, or a prefix of its 25 | name. A port is specified by its number; for port 0 of a client, the 26 | ":0" part of the port specification can be omitted. 27 | 28 | .TP 29 | .I \-i,\-\-input=client:port 30 | Sets the sequencer input port where test events are received from. 31 | 32 | A client can be specified by its number, its name, or a prefix of its 33 | name. A port is specified by its number; for port 0 of a client, the 34 | ":0" part of the port specification can be omitted. 35 | 36 | .TP 37 | .I \-l,\-\-list 38 | Lists MIDI input and output ports. 39 | 40 | .TP 41 | .I \-R,\-\-realtime 42 | Enables realtime scheduling priorization. (default: no rt). 43 | 44 | .TP 45 | .I \-P,\-\-priority=int 46 | When running with --realtime, sets the scheduler priority to int. 47 | 48 | .TP 49 | .I \-S,\-\-samples=int 50 | Sets the given number of samples (default: 10000) to take. 51 | 52 | .TP 53 | .I \-s,\-\-skip=int 54 | Skip measuring latency for the given number of samples (default: 0/no sample skip). 55 | 56 | .TP 57 | .I \-w,\-\-wait=ms 58 | Wait ms milliseconds between measurements (default: 0ms). 59 | 60 | .TP 61 | .I \-r,\-\-random-wait 62 | Wait between wait and 2*wait milliseconds between measurements (default: off/no wait). 63 | 64 | .TP 65 | .I \-h,\-\-help 66 | Prints a list of options. 67 | 68 | .TP 69 | .I \-V,\-\-version 70 | Prints the current version. 71 | 72 | .SH SEE ALSO 73 | .PP 74 | .I https://www.linuxaudio.org/resources.html 75 | .br 76 | The linux-audio tuning (LAT) mailing list is to help GNU/Linux distribution 77 | maintainers and other interested users to share information on system 78 | performance tuning matters, especially with regard to real-time Linux 79 | kernels. 80 | .PP 81 | .I http://www.alsa\-project.org 82 | .br 83 | The Advanced Linux Sound Architecture. 84 | 85 | .SH BUGS 86 | Please report bugs to the authors. 87 | 88 | .SH AUTHORS 89 | Jakob Flierl 90 | -------------------------------------------------------------------------------- /m4/intdiv0.m4: -------------------------------------------------------------------------------- 1 | # intdiv0.m4 serial 6 (gettext-0.18.2) 2 | dnl Copyright (C) 2002, 2007-2008, 2010-2016 Free Software Foundation, Inc. 3 | dnl This file is free software; the Free Software Foundation 4 | dnl gives unlimited permission to copy and/or distribute it, 5 | dnl with or without modifications, as long as this notice is preserved. 6 | 7 | dnl From Bruno Haible. 8 | 9 | AC_DEFUN([gt_INTDIV0], 10 | [ 11 | AC_REQUIRE([AC_PROG_CC])dnl 12 | AC_REQUIRE([AC_CANONICAL_HOST])dnl 13 | 14 | AC_CACHE_CHECK([whether integer division by zero raises SIGFPE], 15 | gt_cv_int_divbyzero_sigfpe, 16 | [ 17 | gt_cv_int_divbyzero_sigfpe= 18 | changequote(,)dnl 19 | case "$host_os" in 20 | macos* | darwin[6-9]* | darwin[1-9][0-9]*) 21 | # On Mac OS X 10.2 or newer, just assume the same as when cross- 22 | # compiling. If we were to perform the real test, 1 Crash Report 23 | # dialog window would pop up. 24 | case "$host_cpu" in 25 | i[34567]86 | x86_64) 26 | gt_cv_int_divbyzero_sigfpe="guessing yes" ;; 27 | esac 28 | ;; 29 | esac 30 | changequote([,])dnl 31 | if test -z "$gt_cv_int_divbyzero_sigfpe"; then 32 | AC_RUN_IFELSE( 33 | [AC_LANG_SOURCE([[ 34 | #include 35 | #include 36 | 37 | static void 38 | sigfpe_handler (int sig) 39 | { 40 | /* Exit with code 0 if SIGFPE, with code 1 if any other signal. */ 41 | _exit (sig != SIGFPE); 42 | } 43 | 44 | int x = 1; 45 | int y = 0; 46 | int z; 47 | int nan; 48 | 49 | int main () 50 | { 51 | signal (SIGFPE, sigfpe_handler); 52 | /* IRIX and AIX (when "xlc -qcheck" is used) yield signal SIGTRAP. */ 53 | #if (defined (__sgi) || defined (_AIX)) && defined (SIGTRAP) 54 | signal (SIGTRAP, sigfpe_handler); 55 | #endif 56 | /* Linux/SPARC yields signal SIGILL. */ 57 | #if defined (__sparc__) && defined (__linux__) 58 | signal (SIGILL, sigfpe_handler); 59 | #endif 60 | 61 | z = x / y; 62 | nan = y / y; 63 | exit (2); 64 | } 65 | ]])], 66 | [gt_cv_int_divbyzero_sigfpe=yes], 67 | [gt_cv_int_divbyzero_sigfpe=no], 68 | [ 69 | # Guess based on the CPU. 70 | changequote(,)dnl 71 | case "$host_cpu" in 72 | alpha* | i[34567]86 | x86_64 | m68k | s390*) 73 | gt_cv_int_divbyzero_sigfpe="guessing yes";; 74 | *) 75 | gt_cv_int_divbyzero_sigfpe="guessing no";; 76 | esac 77 | changequote([,])dnl 78 | ]) 79 | fi 80 | ]) 81 | case "$gt_cv_int_divbyzero_sigfpe" in 82 | *yes) value=1;; 83 | *) value=0;; 84 | esac 85 | AC_DEFINE_UNQUOTED([INTDIV0_RAISES_SIGFPE], [$value], 86 | [Define if integer division by zero raises signal SIGFPE.]) 87 | ]) 88 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | dnl Process this file with autoconf to produce a configure script. 2 | AC_PREREQ([2.71]) 3 | AC_INIT([alsa-midi-latency-test],[0.0.6]) 4 | AC_CONFIG_SRCDIR([src/alsa-midi-latency-test.c]) 5 | AC_PREFIX_DEFAULT(/usr) 6 | AM_INIT_AUTOMAKE([subdir-objects]) 7 | 8 | AM_MAINTAINER_MODE([enable]) 9 | 10 | AM_GNU_GETTEXT([external]) 11 | AM_GNU_GETTEXT_VERSION([0.19.8]) 12 | 13 | dnl Checks for programs. 14 | 15 | AC_PROG_CC 16 | dnl AC_PROG_CXX 17 | AC_PROG_INSTALL 18 | AC_PROG_MKDIR_P 19 | AC_PROG_LN_S 20 | AC_PROG_SED 21 | AC_DISABLE_STATIC 22 | LT_INIT 23 | PKG_PROG_PKG_CONFIG 24 | 25 | dnl try to gues cross-compiler if not set 26 | if test "x$target" != "x$host" -a -z "`echo $CC | grep -e '-gcc'`"; 27 | then 28 | AC_MSG_CHECKING(for cross-compiler) 29 | 30 | which ${program_prefix}gcc >/dev/null 2>&1 && CC=${program_prefix}gcc 31 | which ${target_cpu}-${target_os}-gcc >/dev/null 2>&1 \ 32 | && CC=${target_cpu}-${target-os}-gcc 33 | which ${target_cpu}-${target_vendor}-${target_os}-gcc >/dev/null 2>&1 \ 34 | && CC=${target_cpu}-${target_vendor}-${target_os}-gcc 35 | 36 | AC_MSG_RESULT($CC) 37 | fi 38 | 39 | dnl Checks for header files. 40 | AC_CHECK_HEADERS([alsa/rawmidi.h], [have_rawmidi="yes"], [have_rawmidi="no"], 41 | [#include ]) 42 | AC_CHECK_HEADERS([alsa/seq.h], [have_seq="yes"], [have_seq="no"], 43 | [#include ]) 44 | 45 | AM_CONDITIONAL(HAVE_RAWMIDI, test "$have_rawmidi" = "yes") 46 | AM_CONDITIONAL(HAVE_SEQ, test "$have_seq" = "yes") 47 | 48 | 49 | 50 | test "x$prefix" = xNONE && prefix=$ac_default_prefix 51 | 52 | eval dir="$datadir" 53 | dnl Check for librt 54 | LIBRT="" 55 | AC_MSG_CHECKING(for librt) 56 | AC_ARG_WITH(librt, 57 | AS_HELP_STRING([--with-librt], [Use librt for monotonic clock (default = yes)]), 58 | [ have_librt="$withval" ], [ have_librt="yes" ]) 59 | if test "$have_librt" = "yes"; then 60 | AC_CHECK_LIB([rt], [clock_gettime], [HAVE_LIBRT="yes"]) 61 | if test "$HAVE_LIBRT" = "yes" ; then 62 | LIBRT="-lrt" 63 | AC_DEFINE([HAVE_LIBRT], 1, [Have librt]) 64 | AC_DEFINE([HAVE_CLOCK_GETTIME], 1, [Have clock gettime]) 65 | fi 66 | else 67 | AC_MSG_RESULT(no) 68 | fi 69 | 70 | AC_CHECK_FUNCS([clock_gettime], [CLOCK_LIB=], [AC_CHECK_LIB([rt], 71 | [clock_gettime], [CLOCK_LIB=-lrt], 72 | [AC_MSG_ERROR([Couldn't find clock_gettime])])]) 73 | AC_SUBST([CLOCK_LIB]) 74 | 75 | case "$dir" in 76 | /*) ;; 77 | *) dir="$prefix/share" 78 | esac 79 | 80 | AC_CONFIG_HEADERS(include/aconfig.h) 81 | 82 | dnl Checks for typedefs, structures, and compiler characteristics. 83 | AC_C_CONST 84 | AC_C_INLINE 85 | AC_CHECK_HEADERS_ONCE([sys/time.h]) 86 | 87 | dnl Checks for library functions. 88 | 89 | dnl Enable largefile support 90 | AC_SYS_LARGEFILE 91 | 92 | AC_CONFIG_FILES([Makefile m4/Makefile src/Makefile include/Makefile]) 93 | AC_OUTPUT 94 | -------------------------------------------------------------------------------- /m4/size_max.m4: -------------------------------------------------------------------------------- 1 | # size_max.m4 serial 10 2 | dnl Copyright (C) 2003, 2005-2006, 2008-2016 Free Software Foundation, Inc. 3 | dnl This file is free software; the Free Software Foundation 4 | dnl gives unlimited permission to copy and/or distribute it, 5 | dnl with or without modifications, as long as this notice is preserved. 6 | 7 | dnl From Bruno Haible. 8 | 9 | AC_DEFUN([gl_SIZE_MAX], 10 | [ 11 | AC_CHECK_HEADERS([stdint.h]) 12 | dnl First test whether the system already has SIZE_MAX. 13 | AC_CACHE_CHECK([for SIZE_MAX], [gl_cv_size_max], [ 14 | gl_cv_size_max= 15 | AC_EGREP_CPP([Found it], [ 16 | #include 17 | #if HAVE_STDINT_H 18 | #include 19 | #endif 20 | #ifdef SIZE_MAX 21 | Found it 22 | #endif 23 | ], [gl_cv_size_max=yes]) 24 | if test -z "$gl_cv_size_max"; then 25 | dnl Define it ourselves. Here we assume that the type 'size_t' is not wider 26 | dnl than the type 'unsigned long'. Try hard to find a definition that can 27 | dnl be used in a preprocessor #if, i.e. doesn't contain a cast. 28 | AC_COMPUTE_INT([size_t_bits_minus_1], [sizeof (size_t) * CHAR_BIT - 1], 29 | [#include 30 | #include ], [size_t_bits_minus_1=]) 31 | AC_COMPUTE_INT([fits_in_uint], [sizeof (size_t) <= sizeof (unsigned int)], 32 | [#include ], [fits_in_uint=]) 33 | if test -n "$size_t_bits_minus_1" && test -n "$fits_in_uint"; then 34 | if test $fits_in_uint = 1; then 35 | dnl Even though SIZE_MAX fits in an unsigned int, it must be of type 36 | dnl 'unsigned long' if the type 'size_t' is the same as 'unsigned long'. 37 | AC_COMPILE_IFELSE( 38 | [AC_LANG_PROGRAM( 39 | [[#include 40 | extern size_t foo; 41 | extern unsigned long foo; 42 | ]], 43 | [[]])], 44 | [fits_in_uint=0]) 45 | fi 46 | dnl We cannot use 'expr' to simplify this expression, because 'expr' 47 | dnl works only with 'long' integers in the host environment, while we 48 | dnl might be cross-compiling from a 32-bit platform to a 64-bit platform. 49 | if test $fits_in_uint = 1; then 50 | gl_cv_size_max="(((1U << $size_t_bits_minus_1) - 1) * 2 + 1)" 51 | else 52 | gl_cv_size_max="(((1UL << $size_t_bits_minus_1) - 1) * 2 + 1)" 53 | fi 54 | else 55 | dnl Shouldn't happen, but who knows... 56 | gl_cv_size_max='((size_t)~(size_t)0)' 57 | fi 58 | fi 59 | ]) 60 | if test "$gl_cv_size_max" != yes; then 61 | AC_DEFINE_UNQUOTED([SIZE_MAX], [$gl_cv_size_max], 62 | [Define as the maximum value of type 'size_t', if the system doesn't define it.]) 63 | fi 64 | dnl Don't redefine SIZE_MAX in config.h if config.h is re-included after 65 | dnl . Remember that the #undef in AH_VERBATIM gets replaced with 66 | dnl #define by AC_DEFINE_UNQUOTED. 67 | AH_VERBATIM([SIZE_MAX], 68 | [/* Define as the maximum value of type 'size_t', if the system doesn't define 69 | it. */ 70 | #ifndef SIZE_MAX 71 | # undef SIZE_MAX 72 | #endif]) 73 | ]) 74 | 75 | dnl Autoconf >= 2.61 has AC_COMPUTE_INT built-in. 76 | dnl Remove this when we can assume autoconf >= 2.61. 77 | m4_ifdef([AC_COMPUTE_INT], [], [ 78 | AC_DEFUN([AC_COMPUTE_INT], [_AC_COMPUTE_INT([$2],[$1],[$3],[$4])]) 79 | ]) 80 | -------------------------------------------------------------------------------- /m4/visibility.m4: -------------------------------------------------------------------------------- 1 | # visibility.m4 serial 5 (gettext-0.18.2) 2 | dnl Copyright (C) 2005, 2008, 2010-2016 Free Software Foundation, Inc. 3 | dnl This file is free software; the Free Software Foundation 4 | dnl gives unlimited permission to copy and/or distribute it, 5 | dnl with or without modifications, as long as this notice is preserved. 6 | 7 | dnl From Bruno Haible. 8 | 9 | dnl Tests whether the compiler supports the command-line option 10 | dnl -fvisibility=hidden and the function and variable attributes 11 | dnl __attribute__((__visibility__("hidden"))) and 12 | dnl __attribute__((__visibility__("default"))). 13 | dnl Does *not* test for __visibility__("protected") - which has tricky 14 | dnl semantics (see the 'vismain' test in glibc) and does not exist e.g. on 15 | dnl Mac OS X. 16 | dnl Does *not* test for __visibility__("internal") - which has processor 17 | dnl dependent semantics. 18 | dnl Does *not* test for #pragma GCC visibility push(hidden) - which is 19 | dnl "really only recommended for legacy code". 20 | dnl Set the variable CFLAG_VISIBILITY. 21 | dnl Defines and sets the variable HAVE_VISIBILITY. 22 | 23 | AC_DEFUN([gl_VISIBILITY], 24 | [ 25 | AC_REQUIRE([AC_PROG_CC]) 26 | CFLAG_VISIBILITY= 27 | HAVE_VISIBILITY=0 28 | if test -n "$GCC"; then 29 | dnl First, check whether -Werror can be added to the command line, or 30 | dnl whether it leads to an error because of some other option that the 31 | dnl user has put into $CC $CFLAGS $CPPFLAGS. 32 | AC_MSG_CHECKING([whether the -Werror option is usable]) 33 | AC_CACHE_VAL([gl_cv_cc_vis_werror], [ 34 | gl_save_CFLAGS="$CFLAGS" 35 | CFLAGS="$CFLAGS -Werror" 36 | AC_COMPILE_IFELSE( 37 | [AC_LANG_PROGRAM([[]], [[]])], 38 | [gl_cv_cc_vis_werror=yes], 39 | [gl_cv_cc_vis_werror=no]) 40 | CFLAGS="$gl_save_CFLAGS"]) 41 | AC_MSG_RESULT([$gl_cv_cc_vis_werror]) 42 | dnl Now check whether visibility declarations are supported. 43 | AC_MSG_CHECKING([for simple visibility declarations]) 44 | AC_CACHE_VAL([gl_cv_cc_visibility], [ 45 | gl_save_CFLAGS="$CFLAGS" 46 | CFLAGS="$CFLAGS -fvisibility=hidden" 47 | dnl We use the option -Werror and a function dummyfunc, because on some 48 | dnl platforms (Cygwin 1.7) the use of -fvisibility triggers a warning 49 | dnl "visibility attribute not supported in this configuration; ignored" 50 | dnl at the first function definition in every compilation unit, and we 51 | dnl don't want to use the option in this case. 52 | if test $gl_cv_cc_vis_werror = yes; then 53 | CFLAGS="$CFLAGS -Werror" 54 | fi 55 | AC_COMPILE_IFELSE( 56 | [AC_LANG_PROGRAM( 57 | [[extern __attribute__((__visibility__("hidden"))) int hiddenvar; 58 | extern __attribute__((__visibility__("default"))) int exportedvar; 59 | extern __attribute__((__visibility__("hidden"))) int hiddenfunc (void); 60 | extern __attribute__((__visibility__("default"))) int exportedfunc (void); 61 | void dummyfunc (void) {} 62 | ]], 63 | [[]])], 64 | [gl_cv_cc_visibility=yes], 65 | [gl_cv_cc_visibility=no]) 66 | CFLAGS="$gl_save_CFLAGS"]) 67 | AC_MSG_RESULT([$gl_cv_cc_visibility]) 68 | if test $gl_cv_cc_visibility = yes; then 69 | CFLAG_VISIBILITY="-fvisibility=hidden" 70 | HAVE_VISIBILITY=1 71 | fi 72 | fi 73 | AC_SUBST([CFLAG_VISIBILITY]) 74 | AC_SUBST([HAVE_VISIBILITY]) 75 | AC_DEFINE_UNQUOTED([HAVE_VISIBILITY], [$HAVE_VISIBILITY], 76 | [Define to 1 or 0, depending whether the compiler supports simple visibility declarations.]) 77 | ]) 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ``alsa-midi-latency-test`` is a Linux©-based tool used to measure the roundtrip delay of MIDI messages within the ALSA (Advanced Linux© Sound Architecture) subsystem. 2 | 3 | The tool utilizes a high-precision timer to calculate the time it takes for a MIDI message to travel from the ALSA source to the ALSA sink, and back. It's a helpful way to understand and troubleshoot MIDI latency issues, often in situations where noticeable delay is encountered between playing a MIDI keyboard and hearing the sound. 4 | 5 | ![alsa-midi-latency-test](https://raw.github.com/koppi/alsa-midi-latency-test/master/alsa-midi-latency-test.gif "alsa-midi-latency-test") 6 | 7 | * **Roundtrip Measurement** 8 | 9 | The tool measures the total time it takes for a MIDI message to complete a round trip, meaning from the output of one device to the input of another. 10 | 11 | * **High-Precision Timer** 12 | 13 | It uses a high-precision timer to accurately measure the delay, which is crucial for precise latency measurements. 14 | 15 | * **ALSA Subsystem Focus** 16 | 17 | The tool specifically focuses on the ALSA subsystem, allowing users to diagnose latency issues related to Linux's© audio/MIDI infrastructure. 18 | 19 | How alsa-midi-latency-test can help: 20 | 21 | * **Diagnosing MIDI Latency** 22 | 23 | The tool can help users pinpoint the source of latency, whether it's related to the MIDI hardware, software drivers, or system configuration. 24 | 25 | * **Optimizing ALSA Settings** 26 | 27 | The measurements can guide users in adjusting ALSA settings, such as buffer sizes, to minimize latency. 28 | 29 | * **Identifying System Bottlenecks** 30 | 31 | In some cases, latency can be a result of system limitations, and ``alsa-midi-latency-test`` can help identify those bottlenecks. 32 | 33 | In summary, ``alsa-midi-latency-test`` is a valuable tool for anyone working with MIDI and ALSA on Linux©, allowing them to measure and troubleshoot latency issues effectively. 34 | 35 | ## Install from source code 36 | ```shell 37 | git clone https://github.com/koppi/alsa-midi-latency-test.git 38 | cd alsa-midi-latency-test/ 39 | ``` 40 | The following packages are required to build alsa-midi-latency-test: 41 | ```shell 42 | sudo apt -y install debhelper autotools-dev automake libasound2-dev 43 | ``` 44 | Compile alsa-midi-latency-test as follows: 45 | ```shell 46 | sh autogen.sh 47 | ./configure 48 | make 49 | ``` 50 | Install alsa-midi-latency-test as follows: 51 | ```shell 52 | sudo make install 53 | ``` 54 | or build and install a Debian / Ubuntu package: 55 | ```shell 56 | sudo apt -y install devscripts 57 | debuild -uc -us 58 | sudo dpkg -i ../alsa-midi-latency-test*.deb 59 | ``` 60 | 61 | ## Run alsa-midi-latency-test 62 | * ``` alsa-midi-latency-test -l ``` 63 | 64 | Lists available MIDI input and output ports. 65 | 66 | * ``` alsa-midi-latency-test -i [input port] -o [output port] ``` 67 | 68 | This runs the benchmark with the given input and output port. Note, that the 69 | input and output ports have to be connected using a MIDI cable in the real 70 | hardware to loop the MIDI message back. 71 | 72 | * ``` man alsa-midi-latency-test ``` 73 | 74 | The man page contains documentation for all available command line switches. 75 | 76 | ### Benchmarking Results 77 | 78 | Please share your results in the [Wiki](../../wiki/). 79 | 80 | ### Definitions 81 | 82 | * [Wikipedia EN - Latency in engineering](http://tinyurl.com/wikipedia-latency-engineering) 83 | * [Wikipedia EN - Latency with audio](http://tinyurl.com/wikipedia-latency-audio) 84 | 85 | ## See also 86 | 87 | * http://lists.linuxaudio.org/listinfo/linux-audio-tuning 88 | 89 | The linux-audio tuning (LAT) mailing list is to help GNU/Linux© distribution 90 | maintainers and other interested users to share information on system 91 | performance tuning matters, especially with regard to real-time Linux© 92 | kernels. 93 | 94 | * http://www.alsa-project.org 95 | 96 | The Advanced Linux© Sound Architecture. 97 | 98 | * [http://www.evc-soft.nl/evc/products/miditest](http://web.archive.org/web/20061128213256/http://www.evc-soft.nl/evc/products/miditest) 99 | 100 | The MidiTest software for Microsoft Windows. 101 | 102 | * https://codeberg.org/rtcqs/rtcqs 103 | 104 | rtcqs is a Python utility to analyze your system and detect possible bottlenecks that could have a negative impact on the performance of your system when working with Linux© audio. 105 | 106 | ## Thanks to 107 | 108 | * [Arnout Engelen](https://github.com/raboof) for initial testing, 109 | * [Clemens Ladisch](https://github.com/cladisch) for a number of fixes with the high precision timer and ALSA midi event handling, 110 | * [Giulio Moro](https://github.com/giuliomoro) for code cleanup, various fixes and support for UART I/O. 111 | 112 | ## BUGS and AUTHORS 113 | 114 | Please [report bugs](https://github.com/koppi/alsa-midi-latency-test/issues) to the authors: 115 | 116 | * [Jakob Flierl](https://github.com/koppi) 117 | 118 | Last updated Jun 2025. 119 | -------------------------------------------------------------------------------- /m4/longlong.m4: -------------------------------------------------------------------------------- 1 | # longlong.m4 serial 17 2 | dnl Copyright (C) 1999-2007, 2009-2016 Free Software Foundation, Inc. 3 | dnl This file is free software; the Free Software Foundation 4 | dnl gives unlimited permission to copy and/or distribute it, 5 | dnl with or without modifications, as long as this notice is preserved. 6 | 7 | dnl From Paul Eggert. 8 | 9 | # Define HAVE_LONG_LONG_INT if 'long long int' works. 10 | # This fixes a bug in Autoconf 2.61, and can be faster 11 | # than what's in Autoconf 2.62 through 2.68. 12 | 13 | # Note: If the type 'long long int' exists but is only 32 bits large 14 | # (as on some very old compilers), HAVE_LONG_LONG_INT will not be 15 | # defined. In this case you can treat 'long long int' like 'long int'. 16 | 17 | AC_DEFUN([AC_TYPE_LONG_LONG_INT], 18 | [ 19 | AC_REQUIRE([AC_TYPE_UNSIGNED_LONG_LONG_INT]) 20 | AC_CACHE_CHECK([for long long int], [ac_cv_type_long_long_int], 21 | [ac_cv_type_long_long_int=yes 22 | if test "x${ac_cv_prog_cc_c99-no}" = xno; then 23 | ac_cv_type_long_long_int=$ac_cv_type_unsigned_long_long_int 24 | if test $ac_cv_type_long_long_int = yes; then 25 | dnl Catch a bug in Tandem NonStop Kernel (OSS) cc -O circa 2004. 26 | dnl If cross compiling, assume the bug is not important, since 27 | dnl nobody cross compiles for this platform as far as we know. 28 | AC_RUN_IFELSE( 29 | [AC_LANG_PROGRAM( 30 | [[@%:@include 31 | @%:@ifndef LLONG_MAX 32 | @%:@ define HALF \ 33 | (1LL << (sizeof (long long int) * CHAR_BIT - 2)) 34 | @%:@ define LLONG_MAX (HALF - 1 + HALF) 35 | @%:@endif]], 36 | [[long long int n = 1; 37 | int i; 38 | for (i = 0; ; i++) 39 | { 40 | long long int m = n << i; 41 | if (m >> i != n) 42 | return 1; 43 | if (LLONG_MAX / 2 < m) 44 | break; 45 | } 46 | return 0;]])], 47 | [], 48 | [ac_cv_type_long_long_int=no], 49 | [:]) 50 | fi 51 | fi]) 52 | if test $ac_cv_type_long_long_int = yes; then 53 | AC_DEFINE([HAVE_LONG_LONG_INT], [1], 54 | [Define to 1 if the system has the type 'long long int'.]) 55 | fi 56 | ]) 57 | 58 | # Define HAVE_UNSIGNED_LONG_LONG_INT if 'unsigned long long int' works. 59 | # This fixes a bug in Autoconf 2.61, and can be faster 60 | # than what's in Autoconf 2.62 through 2.68. 61 | 62 | # Note: If the type 'unsigned long long int' exists but is only 32 bits 63 | # large (as on some very old compilers), AC_TYPE_UNSIGNED_LONG_LONG_INT 64 | # will not be defined. In this case you can treat 'unsigned long long int' 65 | # like 'unsigned long int'. 66 | 67 | AC_DEFUN([AC_TYPE_UNSIGNED_LONG_LONG_INT], 68 | [ 69 | AC_CACHE_CHECK([for unsigned long long int], 70 | [ac_cv_type_unsigned_long_long_int], 71 | [ac_cv_type_unsigned_long_long_int=yes 72 | if test "x${ac_cv_prog_cc_c99-no}" = xno; then 73 | AC_LINK_IFELSE( 74 | [_AC_TYPE_LONG_LONG_SNIPPET], 75 | [], 76 | [ac_cv_type_unsigned_long_long_int=no]) 77 | fi]) 78 | if test $ac_cv_type_unsigned_long_long_int = yes; then 79 | AC_DEFINE([HAVE_UNSIGNED_LONG_LONG_INT], [1], 80 | [Define to 1 if the system has the type 'unsigned long long int'.]) 81 | fi 82 | ]) 83 | 84 | # Expands to a C program that can be used to test for simultaneous support 85 | # of 'long long' and 'unsigned long long'. We don't want to say that 86 | # 'long long' is available if 'unsigned long long' is not, or vice versa, 87 | # because too many programs rely on the symmetry between signed and unsigned 88 | # integer types (excluding 'bool'). 89 | AC_DEFUN([_AC_TYPE_LONG_LONG_SNIPPET], 90 | [ 91 | AC_LANG_PROGRAM( 92 | [[/* For now, do not test the preprocessor; as of 2007 there are too many 93 | implementations with broken preprocessors. Perhaps this can 94 | be revisited in 2012. In the meantime, code should not expect 95 | #if to work with literals wider than 32 bits. */ 96 | /* Test literals. */ 97 | long long int ll = 9223372036854775807ll; 98 | long long int nll = -9223372036854775807LL; 99 | unsigned long long int ull = 18446744073709551615ULL; 100 | /* Test constant expressions. */ 101 | typedef int a[((-9223372036854775807LL < 0 && 0 < 9223372036854775807ll) 102 | ? 1 : -1)]; 103 | typedef int b[(18446744073709551615ULL <= (unsigned long long int) -1 104 | ? 1 : -1)]; 105 | int i = 63;]], 106 | [[/* Test availability of runtime routines for shift and division. */ 107 | long long int llmax = 9223372036854775807ll; 108 | unsigned long long int ullmax = 18446744073709551615ull; 109 | return ((ll << 63) | (ll >> 63) | (ll < i) | (ll > i) 110 | | (llmax / ll) | (llmax % ll) 111 | | (ull << 63) | (ull >> 63) | (ull << i) | (ull >> i) 112 | | (ullmax / ull) | (ullmax % ull));]]) 113 | ]) 114 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /config.rpath: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Output a system dependent set of variables, describing how to set the 3 | # run time search path of shared libraries in an executable. 4 | # 5 | # Copyright 1996-2016 Free Software Foundation, Inc. 6 | # Taken from GNU libtool, 2001 7 | # Originally by Gordon Matzigkeit , 1996 8 | # 9 | # This file is free software; the Free Software Foundation gives 10 | # unlimited permission to copy and/or distribute it, with or without 11 | # modifications, as long as this notice is preserved. 12 | # 13 | # The first argument passed to this file is the canonical host specification, 14 | # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM 15 | # or 16 | # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM 17 | # The environment variables CC, GCC, LDFLAGS, LD, with_gnu_ld 18 | # should be set by the caller. 19 | # 20 | # The set of defined variables is at the end of this script. 21 | 22 | # Known limitations: 23 | # - On IRIX 6.5 with CC="cc", the run time search patch must not be longer 24 | # than 256 bytes, otherwise the compiler driver will dump core. The only 25 | # known workaround is to choose shorter directory names for the build 26 | # directory and/or the installation directory. 27 | 28 | # All known linkers require a '.a' archive for static linking (except MSVC, 29 | # which needs '.lib'). 30 | libext=a 31 | shrext=.so 32 | 33 | host="$1" 34 | host_cpu=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` 35 | host_vendor=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` 36 | host_os=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` 37 | 38 | # Code taken from libtool.m4's _LT_CC_BASENAME. 39 | 40 | for cc_temp in $CC""; do 41 | case $cc_temp in 42 | compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; 43 | distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; 44 | \-*) ;; 45 | *) break;; 46 | esac 47 | done 48 | cc_basename=`echo "$cc_temp" | sed -e 's%^.*/%%'` 49 | 50 | # Code taken from libtool.m4's _LT_COMPILER_PIC. 51 | 52 | wl= 53 | if test "$GCC" = yes; then 54 | wl='-Wl,' 55 | else 56 | case "$host_os" in 57 | aix*) 58 | wl='-Wl,' 59 | ;; 60 | mingw* | cygwin* | pw32* | os2* | cegcc*) 61 | ;; 62 | hpux9* | hpux10* | hpux11*) 63 | wl='-Wl,' 64 | ;; 65 | irix5* | irix6* | nonstopux*) 66 | wl='-Wl,' 67 | ;; 68 | linux* | k*bsd*-gnu | kopensolaris*-gnu) 69 | case $cc_basename in 70 | ecc*) 71 | wl='-Wl,' 72 | ;; 73 | icc* | ifort*) 74 | wl='-Wl,' 75 | ;; 76 | lf95*) 77 | wl='-Wl,' 78 | ;; 79 | nagfor*) 80 | wl='-Wl,-Wl,,' 81 | ;; 82 | pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) 83 | wl='-Wl,' 84 | ;; 85 | ccc*) 86 | wl='-Wl,' 87 | ;; 88 | xl* | bgxl* | bgf* | mpixl*) 89 | wl='-Wl,' 90 | ;; 91 | como) 92 | wl='-lopt=' 93 | ;; 94 | *) 95 | case `$CC -V 2>&1 | sed 5q` in 96 | *Sun\ F* | *Sun*Fortran*) 97 | wl= 98 | ;; 99 | *Sun\ C*) 100 | wl='-Wl,' 101 | ;; 102 | esac 103 | ;; 104 | esac 105 | ;; 106 | newsos6) 107 | ;; 108 | *nto* | *qnx*) 109 | ;; 110 | osf3* | osf4* | osf5*) 111 | wl='-Wl,' 112 | ;; 113 | rdos*) 114 | ;; 115 | solaris*) 116 | case $cc_basename in 117 | f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) 118 | wl='-Qoption ld ' 119 | ;; 120 | *) 121 | wl='-Wl,' 122 | ;; 123 | esac 124 | ;; 125 | sunos4*) 126 | wl='-Qoption ld ' 127 | ;; 128 | sysv4 | sysv4.2uw2* | sysv4.3*) 129 | wl='-Wl,' 130 | ;; 131 | sysv4*MP*) 132 | ;; 133 | sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) 134 | wl='-Wl,' 135 | ;; 136 | unicos*) 137 | wl='-Wl,' 138 | ;; 139 | uts4*) 140 | ;; 141 | esac 142 | fi 143 | 144 | # Code taken from libtool.m4's _LT_LINKER_SHLIBS. 145 | 146 | hardcode_libdir_flag_spec= 147 | hardcode_libdir_separator= 148 | hardcode_direct=no 149 | hardcode_minus_L=no 150 | 151 | case "$host_os" in 152 | cygwin* | mingw* | pw32* | cegcc*) 153 | # FIXME: the MSVC++ port hasn't been tested in a loooong time 154 | # When not using gcc, we currently assume that we are using 155 | # Microsoft Visual C++. 156 | if test "$GCC" != yes; then 157 | with_gnu_ld=no 158 | fi 159 | ;; 160 | interix*) 161 | # we just hope/assume this is gcc and not c89 (= MSVC++) 162 | with_gnu_ld=yes 163 | ;; 164 | openbsd*) 165 | with_gnu_ld=no 166 | ;; 167 | esac 168 | 169 | ld_shlibs=yes 170 | if test "$with_gnu_ld" = yes; then 171 | # Set some defaults for GNU ld with shared library support. These 172 | # are reset later if shared libraries are not supported. Putting them 173 | # here allows them to be overridden if necessary. 174 | # Unlike libtool, we use -rpath here, not --rpath, since the documented 175 | # option of GNU ld is called -rpath, not --rpath. 176 | hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' 177 | case "$host_os" in 178 | aix[3-9]*) 179 | # On AIX/PPC, the GNU linker is very broken 180 | if test "$host_cpu" != ia64; then 181 | ld_shlibs=no 182 | fi 183 | ;; 184 | amigaos*) 185 | case "$host_cpu" in 186 | powerpc) 187 | ;; 188 | m68k) 189 | hardcode_libdir_flag_spec='-L$libdir' 190 | hardcode_minus_L=yes 191 | ;; 192 | esac 193 | ;; 194 | beos*) 195 | if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then 196 | : 197 | else 198 | ld_shlibs=no 199 | fi 200 | ;; 201 | cygwin* | mingw* | pw32* | cegcc*) 202 | # hardcode_libdir_flag_spec is actually meaningless, as there is 203 | # no search path for DLLs. 204 | hardcode_libdir_flag_spec='-L$libdir' 205 | if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then 206 | : 207 | else 208 | ld_shlibs=no 209 | fi 210 | ;; 211 | haiku*) 212 | ;; 213 | interix[3-9]*) 214 | hardcode_direct=no 215 | hardcode_libdir_flag_spec='${wl}-rpath,$libdir' 216 | ;; 217 | gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) 218 | if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then 219 | : 220 | else 221 | ld_shlibs=no 222 | fi 223 | ;; 224 | netbsd*) 225 | ;; 226 | solaris*) 227 | if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then 228 | ld_shlibs=no 229 | elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then 230 | : 231 | else 232 | ld_shlibs=no 233 | fi 234 | ;; 235 | sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) 236 | case `$LD -v 2>&1` in 237 | *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) 238 | ld_shlibs=no 239 | ;; 240 | *) 241 | if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then 242 | hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`' 243 | else 244 | ld_shlibs=no 245 | fi 246 | ;; 247 | esac 248 | ;; 249 | sunos4*) 250 | hardcode_direct=yes 251 | ;; 252 | *) 253 | if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then 254 | : 255 | else 256 | ld_shlibs=no 257 | fi 258 | ;; 259 | esac 260 | if test "$ld_shlibs" = no; then 261 | hardcode_libdir_flag_spec= 262 | fi 263 | else 264 | case "$host_os" in 265 | aix3*) 266 | # Note: this linker hardcodes the directories in LIBPATH if there 267 | # are no directories specified by -L. 268 | hardcode_minus_L=yes 269 | if test "$GCC" = yes; then 270 | # Neither direct hardcoding nor static linking is supported with a 271 | # broken collect2. 272 | hardcode_direct=unsupported 273 | fi 274 | ;; 275 | aix[4-9]*) 276 | if test "$host_cpu" = ia64; then 277 | # On IA64, the linker does run time linking by default, so we don't 278 | # have to do anything special. 279 | aix_use_runtimelinking=no 280 | else 281 | aix_use_runtimelinking=no 282 | # Test if we are trying to use run time linking or normal 283 | # AIX style linking. If -brtl is somewhere in LDFLAGS, we 284 | # need to do runtime linking. 285 | case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) 286 | for ld_flag in $LDFLAGS; do 287 | if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then 288 | aix_use_runtimelinking=yes 289 | break 290 | fi 291 | done 292 | ;; 293 | esac 294 | fi 295 | hardcode_direct=yes 296 | hardcode_libdir_separator=':' 297 | if test "$GCC" = yes; then 298 | case $host_os in aix4.[012]|aix4.[012].*) 299 | collect2name=`${CC} -print-prog-name=collect2` 300 | if test -f "$collect2name" && \ 301 | strings "$collect2name" | grep resolve_lib_name >/dev/null 302 | then 303 | # We have reworked collect2 304 | : 305 | else 306 | # We have old collect2 307 | hardcode_direct=unsupported 308 | hardcode_minus_L=yes 309 | hardcode_libdir_flag_spec='-L$libdir' 310 | hardcode_libdir_separator= 311 | fi 312 | ;; 313 | esac 314 | fi 315 | # Begin _LT_AC_SYS_LIBPATH_AIX. 316 | echo 'int main () { return 0; }' > conftest.c 317 | ${CC} ${LDFLAGS} conftest.c -o conftest 318 | aix_libpath=`dump -H conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } 319 | }'` 320 | if test -z "$aix_libpath"; then 321 | aix_libpath=`dump -HX64 conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } 322 | }'` 323 | fi 324 | if test -z "$aix_libpath"; then 325 | aix_libpath="/usr/lib:/lib" 326 | fi 327 | rm -f conftest.c conftest 328 | # End _LT_AC_SYS_LIBPATH_AIX. 329 | if test "$aix_use_runtimelinking" = yes; then 330 | hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" 331 | else 332 | if test "$host_cpu" = ia64; then 333 | hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' 334 | else 335 | hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" 336 | fi 337 | fi 338 | ;; 339 | amigaos*) 340 | case "$host_cpu" in 341 | powerpc) 342 | ;; 343 | m68k) 344 | hardcode_libdir_flag_spec='-L$libdir' 345 | hardcode_minus_L=yes 346 | ;; 347 | esac 348 | ;; 349 | bsdi[45]*) 350 | ;; 351 | cygwin* | mingw* | pw32* | cegcc*) 352 | # When not using gcc, we currently assume that we are using 353 | # Microsoft Visual C++. 354 | # hardcode_libdir_flag_spec is actually meaningless, as there is 355 | # no search path for DLLs. 356 | hardcode_libdir_flag_spec=' ' 357 | libext=lib 358 | ;; 359 | darwin* | rhapsody*) 360 | hardcode_direct=no 361 | if { case $cc_basename in ifort*) true;; *) test "$GCC" = yes;; esac; }; then 362 | : 363 | else 364 | ld_shlibs=no 365 | fi 366 | ;; 367 | dgux*) 368 | hardcode_libdir_flag_spec='-L$libdir' 369 | ;; 370 | freebsd2.[01]*) 371 | hardcode_direct=yes 372 | hardcode_minus_L=yes 373 | ;; 374 | freebsd* | dragonfly*) 375 | hardcode_libdir_flag_spec='-R$libdir' 376 | hardcode_direct=yes 377 | ;; 378 | hpux9*) 379 | hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' 380 | hardcode_libdir_separator=: 381 | hardcode_direct=yes 382 | # hardcode_minus_L: Not really in the search PATH, 383 | # but as the default location of the library. 384 | hardcode_minus_L=yes 385 | ;; 386 | hpux10*) 387 | if test "$with_gnu_ld" = no; then 388 | hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' 389 | hardcode_libdir_separator=: 390 | hardcode_direct=yes 391 | # hardcode_minus_L: Not really in the search PATH, 392 | # but as the default location of the library. 393 | hardcode_minus_L=yes 394 | fi 395 | ;; 396 | hpux11*) 397 | if test "$with_gnu_ld" = no; then 398 | hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' 399 | hardcode_libdir_separator=: 400 | case $host_cpu in 401 | hppa*64*|ia64*) 402 | hardcode_direct=no 403 | ;; 404 | *) 405 | hardcode_direct=yes 406 | # hardcode_minus_L: Not really in the search PATH, 407 | # but as the default location of the library. 408 | hardcode_minus_L=yes 409 | ;; 410 | esac 411 | fi 412 | ;; 413 | irix5* | irix6* | nonstopux*) 414 | hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' 415 | hardcode_libdir_separator=: 416 | ;; 417 | netbsd*) 418 | hardcode_libdir_flag_spec='-R$libdir' 419 | hardcode_direct=yes 420 | ;; 421 | newsos6) 422 | hardcode_direct=yes 423 | hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' 424 | hardcode_libdir_separator=: 425 | ;; 426 | *nto* | *qnx*) 427 | ;; 428 | openbsd*) 429 | if test -f /usr/libexec/ld.so; then 430 | hardcode_direct=yes 431 | if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then 432 | hardcode_libdir_flag_spec='${wl}-rpath,$libdir' 433 | else 434 | case "$host_os" in 435 | openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) 436 | hardcode_libdir_flag_spec='-R$libdir' 437 | ;; 438 | *) 439 | hardcode_libdir_flag_spec='${wl}-rpath,$libdir' 440 | ;; 441 | esac 442 | fi 443 | else 444 | ld_shlibs=no 445 | fi 446 | ;; 447 | os2*) 448 | hardcode_libdir_flag_spec='-L$libdir' 449 | hardcode_minus_L=yes 450 | ;; 451 | osf3*) 452 | hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' 453 | hardcode_libdir_separator=: 454 | ;; 455 | osf4* | osf5*) 456 | if test "$GCC" = yes; then 457 | hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' 458 | else 459 | # Both cc and cxx compiler support -rpath directly 460 | hardcode_libdir_flag_spec='-rpath $libdir' 461 | fi 462 | hardcode_libdir_separator=: 463 | ;; 464 | solaris*) 465 | hardcode_libdir_flag_spec='-R$libdir' 466 | ;; 467 | sunos4*) 468 | hardcode_libdir_flag_spec='-L$libdir' 469 | hardcode_direct=yes 470 | hardcode_minus_L=yes 471 | ;; 472 | sysv4) 473 | case $host_vendor in 474 | sni) 475 | hardcode_direct=yes # is this really true??? 476 | ;; 477 | siemens) 478 | hardcode_direct=no 479 | ;; 480 | motorola) 481 | hardcode_direct=no #Motorola manual says yes, but my tests say they lie 482 | ;; 483 | esac 484 | ;; 485 | sysv4.3*) 486 | ;; 487 | sysv4*MP*) 488 | if test -d /usr/nec; then 489 | ld_shlibs=yes 490 | fi 491 | ;; 492 | sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) 493 | ;; 494 | sysv5* | sco3.2v5* | sco5v6*) 495 | hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' 496 | hardcode_libdir_separator=':' 497 | ;; 498 | uts4*) 499 | hardcode_libdir_flag_spec='-L$libdir' 500 | ;; 501 | *) 502 | ld_shlibs=no 503 | ;; 504 | esac 505 | fi 506 | 507 | # Check dynamic linker characteristics 508 | # Code taken from libtool.m4's _LT_SYS_DYNAMIC_LINKER. 509 | # Unlike libtool.m4, here we don't care about _all_ names of the library, but 510 | # only about the one the linker finds when passed -lNAME. This is the last 511 | # element of library_names_spec in libtool.m4, or possibly two of them if the 512 | # linker has special search rules. 513 | library_names_spec= # the last element of library_names_spec in libtool.m4 514 | libname_spec='lib$name' 515 | case "$host_os" in 516 | aix3*) 517 | library_names_spec='$libname.a' 518 | ;; 519 | aix[4-9]*) 520 | library_names_spec='$libname$shrext' 521 | ;; 522 | amigaos*) 523 | case "$host_cpu" in 524 | powerpc*) 525 | library_names_spec='$libname$shrext' ;; 526 | m68k) 527 | library_names_spec='$libname.a' ;; 528 | esac 529 | ;; 530 | beos*) 531 | library_names_spec='$libname$shrext' 532 | ;; 533 | bsdi[45]*) 534 | library_names_spec='$libname$shrext' 535 | ;; 536 | cygwin* | mingw* | pw32* | cegcc*) 537 | shrext=.dll 538 | library_names_spec='$libname.dll.a $libname.lib' 539 | ;; 540 | darwin* | rhapsody*) 541 | shrext=.dylib 542 | library_names_spec='$libname$shrext' 543 | ;; 544 | dgux*) 545 | library_names_spec='$libname$shrext' 546 | ;; 547 | freebsd[23].*) 548 | library_names_spec='$libname$shrext$versuffix' 549 | ;; 550 | freebsd* | dragonfly*) 551 | library_names_spec='$libname$shrext' 552 | ;; 553 | gnu*) 554 | library_names_spec='$libname$shrext' 555 | ;; 556 | haiku*) 557 | library_names_spec='$libname$shrext' 558 | ;; 559 | hpux9* | hpux10* | hpux11*) 560 | case $host_cpu in 561 | ia64*) 562 | shrext=.so 563 | ;; 564 | hppa*64*) 565 | shrext=.sl 566 | ;; 567 | *) 568 | shrext=.sl 569 | ;; 570 | esac 571 | library_names_spec='$libname$shrext' 572 | ;; 573 | interix[3-9]*) 574 | library_names_spec='$libname$shrext' 575 | ;; 576 | irix5* | irix6* | nonstopux*) 577 | library_names_spec='$libname$shrext' 578 | case "$host_os" in 579 | irix5* | nonstopux*) 580 | libsuff= shlibsuff= 581 | ;; 582 | *) 583 | case $LD in 584 | *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= ;; 585 | *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 ;; 586 | *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 ;; 587 | *) libsuff= shlibsuff= ;; 588 | esac 589 | ;; 590 | esac 591 | ;; 592 | linux*oldld* | linux*aout* | linux*coff*) 593 | ;; 594 | linux* | k*bsd*-gnu | kopensolaris*-gnu) 595 | library_names_spec='$libname$shrext' 596 | ;; 597 | knetbsd*-gnu) 598 | library_names_spec='$libname$shrext' 599 | ;; 600 | netbsd*) 601 | library_names_spec='$libname$shrext' 602 | ;; 603 | newsos6) 604 | library_names_spec='$libname$shrext' 605 | ;; 606 | *nto* | *qnx*) 607 | library_names_spec='$libname$shrext' 608 | ;; 609 | openbsd*) 610 | library_names_spec='$libname$shrext$versuffix' 611 | ;; 612 | os2*) 613 | libname_spec='$name' 614 | shrext=.dll 615 | library_names_spec='$libname.a' 616 | ;; 617 | osf3* | osf4* | osf5*) 618 | library_names_spec='$libname$shrext' 619 | ;; 620 | rdos*) 621 | ;; 622 | solaris*) 623 | library_names_spec='$libname$shrext' 624 | ;; 625 | sunos4*) 626 | library_names_spec='$libname$shrext$versuffix' 627 | ;; 628 | sysv4 | sysv4.3*) 629 | library_names_spec='$libname$shrext' 630 | ;; 631 | sysv4*MP*) 632 | library_names_spec='$libname$shrext' 633 | ;; 634 | sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) 635 | library_names_spec='$libname$shrext' 636 | ;; 637 | tpf*) 638 | library_names_spec='$libname$shrext' 639 | ;; 640 | uts4*) 641 | library_names_spec='$libname$shrext' 642 | ;; 643 | esac 644 | 645 | sed_quote_subst='s/\(["`$\\]\)/\\\1/g' 646 | escaped_wl=`echo "X$wl" | sed -e 's/^X//' -e "$sed_quote_subst"` 647 | shlibext=`echo "$shrext" | sed -e 's,^\.,,'` 648 | escaped_libname_spec=`echo "X$libname_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` 649 | escaped_library_names_spec=`echo "X$library_names_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` 650 | escaped_hardcode_libdir_flag_spec=`echo "X$hardcode_libdir_flag_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` 651 | 652 | LC_ALL=C sed -e 's/^\([a-zA-Z0-9_]*\)=/acl_cv_\1=/' < 5 | * 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 | */ 21 | #include "aconfig.h" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof *(a)) 36 | #define ENABLE_UART 37 | 38 | static snd_seq_t *seq; 39 | static snd_rawmidi_t *raw_in; 40 | static snd_rawmidi_t *raw_out; 41 | #ifdef ENABLE_UART 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #endif // ENABLE_UART 48 | 49 | static volatile sig_atomic_t signal_received = 0; 50 | 51 | void print_uname() 52 | { 53 | struct utsname u; 54 | uname (&u); 55 | printf ("> running on %s release %s (version %s) on %s\n", 56 | u.sysname, u.release, u.version, u.machine); 57 | } 58 | 59 | /* prints an error message to stderr, and dies */ 60 | static void fatal(const char *msg, ...) 61 | { 62 | va_list ap; 63 | 64 | va_start(ap, msg); 65 | vfprintf(stderr, msg, ap); 66 | va_end(ap); 67 | fputc('\n', stderr); 68 | exit(EXIT_FAILURE); 69 | } 70 | 71 | /* memory allocation error handling */ 72 | static void check_mem(void *p) 73 | { 74 | if (!p) 75 | fatal("out of memory"); 76 | } 77 | 78 | /* error handling for ALSA functions */ 79 | static void check_snd(const char *operation, int err) 80 | { 81 | if (err < 0) 82 | fatal("cannot %s - %s", operation, snd_strerror(err)); 83 | } 84 | 85 | /* sets the process to "policy" policy at given priority */ 86 | static int set_realtime_priority(int policy, int prio) 87 | { 88 | struct sched_param schp; 89 | memset(&schp, 0, sizeof(schp)); 90 | 91 | schp.sched_priority = prio; 92 | if (sched_setscheduler(0, policy, &schp) != 0) { 93 | perror("sched_setscheduler"); 94 | return -1; 95 | } 96 | return 0; 97 | } 98 | 99 | static void quiet_error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...) 100 | { 101 | return; 102 | } 103 | // code below borrowed rom: Craig Stuart Sapp 104 | // https://ccrma.stanford.edu/~craig/articles/linuxmidi/alsa-1.0/alsarawportlist.c 105 | // 106 | ////////////////////////////// 107 | // 108 | // print_midi_ports -- go through the list of available "soundcards", 109 | // checking them to see if there are devices/subdevices on them which 110 | // can read/write MIDI data. 111 | // 112 | static void list_midi_devices_on_card(int card); 113 | static void list_subdevice_info(snd_ctl_t *ctl, int card, int device); 114 | 115 | ////////////////////////////// 116 | // 117 | // is_input -- returns true if specified card/device/sub can output MIDI data. 118 | // 119 | 120 | static int is_input(snd_ctl_t *ctl, int card, int device, int sub) { 121 | snd_rawmidi_info_t *info; 122 | int status; 123 | 124 | snd_rawmidi_info_alloca(&info); 125 | snd_rawmidi_info_set_device(info, device); 126 | snd_rawmidi_info_set_subdevice(info, sub); 127 | snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_INPUT); 128 | 129 | if ((status = snd_ctl_rawmidi_info(ctl, info)) < 0 && status != -ENXIO) { 130 | return status; 131 | } else if (status == 0) { 132 | return 1; 133 | } 134 | 135 | return 0; 136 | } 137 | 138 | ////////////////////////////// 139 | // 140 | // is_output -- returns true if specified card/device/sub can output MIDI data. 141 | // 142 | 143 | static int is_output(snd_ctl_t *ctl, int card, int device, int sub) { 144 | snd_rawmidi_info_t *info; 145 | int status; 146 | 147 | snd_rawmidi_info_alloca(&info); 148 | snd_rawmidi_info_set_device(info, device); 149 | snd_rawmidi_info_set_subdevice(info, sub); 150 | snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_OUTPUT); 151 | 152 | if ((status = snd_ctl_rawmidi_info(ctl, info)) < 0 && status != -ENXIO) { 153 | return status; 154 | } else if (status == 0) { 155 | return 1; 156 | } 157 | 158 | return 0; 159 | } 160 | 161 | ////////////////////////////// 162 | // 163 | // list_midi_devices_on_card -- For a particular "card" look at all 164 | // of the "devices/subdevices" on it and print information about it 165 | // if it can handle MIDI input and/or output. 166 | // 167 | 168 | static void list_midi_devices_on_card(int card) { 169 | snd_ctl_t *ctl; 170 | char name[32]; 171 | int device = -1; 172 | int status; 173 | sprintf(name, "hw:%d", card); 174 | if ((status = snd_ctl_open(&ctl, name, 0)) < 0) { 175 | fatal("cannot open control for card %d: %s", card, snd_strerror(status)); 176 | } 177 | do { 178 | status = snd_ctl_rawmidi_next_device(ctl, &device); 179 | if (status < 0) { 180 | fatal("cannot determine device number: %s", snd_strerror(status)); 181 | } 182 | if (device >= 0) { 183 | list_subdevice_info(ctl, card, device); 184 | } 185 | } while (device >= 0); 186 | snd_ctl_close(ctl); 187 | } 188 | 189 | ////////////////////////////// 190 | // 191 | // list_subdevice_info -- Print information about a subdevice 192 | // of a device of a card if it can handle MIDI input and/or output. 193 | // 194 | 195 | static void list_subdevice_info(snd_ctl_t *ctl, int card, int device) { 196 | snd_rawmidi_info_t *info; 197 | const char *name; 198 | const char *sub_name; 199 | int subs, subs_in, subs_out; 200 | int sub, in, out; 201 | int status; 202 | 203 | snd_rawmidi_info_alloca(&info); 204 | snd_rawmidi_info_set_device(info, device); 205 | 206 | snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_INPUT); 207 | snd_ctl_rawmidi_info(ctl, info); 208 | subs_in = snd_rawmidi_info_get_subdevices_count(info); 209 | snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_OUTPUT); 210 | snd_ctl_rawmidi_info(ctl, info); 211 | subs_out = snd_rawmidi_info_get_subdevices_count(info); 212 | subs = subs_in > subs_out ? subs_in : subs_out; 213 | 214 | sub = 0; 215 | in = out = 0; 216 | if ((status = is_output(ctl, card, device, sub)) < 0) { 217 | fatal("cannot get rawmidi information %d:%d: %s", 218 | card, device, snd_strerror(status)); 219 | } else if (status) 220 | out = 1; 221 | 222 | if (status == 0) { 223 | if ((status = is_input(ctl, card, device, sub)) < 0) { 224 | fatal("cannot get rawmidi information %d:%d: %s", 225 | card, device, snd_strerror(status)); 226 | } 227 | } else if (status) 228 | in = 1; 229 | 230 | if (status == 0) 231 | return; 232 | 233 | name = snd_rawmidi_info_get_name(info); 234 | sub_name = snd_rawmidi_info_get_subdevice_name(info); 235 | if (sub_name[0] == '\0') { 236 | if (subs == 1) { 237 | printf("%c%c hw:%d,%d %s\n", 238 | in ? 'I' : ' ', 239 | out ? 'O' : ' ', 240 | card, device, name); 241 | } else 242 | printf("%c%c hw:%d,%d %s (%d subdevices)\n", 243 | in ? 'I' : ' ', 244 | out ? 'O' : ' ', 245 | card, device, name, subs); 246 | } else { 247 | sub = 0; 248 | for (;;) { 249 | printf("%c%c hw:%d,%d,%d %s\n", 250 | in ? 'I' : ' ', out ? 'O' : ' ', 251 | card, device, sub, sub_name); 252 | if (++sub >= subs) 253 | break; 254 | 255 | in = is_input(ctl, card, device, sub); 256 | out = is_output(ctl, card, device, sub); 257 | snd_rawmidi_info_set_subdevice(info, sub); 258 | if (out) { 259 | snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_OUTPUT); 260 | if ((status = snd_ctl_rawmidi_info(ctl, info)) < 0) { 261 | fatal("cannot get rawmidi information %d:%d:%d: %s", 262 | card, device, sub, snd_strerror(status)); 263 | } 264 | } else { 265 | snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_INPUT); 266 | if ((status = snd_ctl_rawmidi_info(ctl, info)) < 0) { 267 | fatal("cannot get rawmidi information %d:%d:%d: %s", 268 | card, device, sub, snd_strerror(status)); 269 | } 270 | } 271 | sub_name = snd_rawmidi_info_get_subdevice_name(info); 272 | } 273 | } 274 | } 275 | 276 | static void list_ports_raw(void) 277 | { 278 | int status; 279 | int card = -1; // use -1 to prime the pump of iterating through card list 280 | char* longname = NULL; 281 | char* shortname = NULL; 282 | puts("Rawmidi ports:"); 283 | 284 | int found = 0; 285 | while (1) { 286 | if ((status = snd_card_next(&card)) < 0) { 287 | fatal("cannot determine card number: %s", snd_strerror(status)); 288 | } 289 | if (card < 0) 290 | break; 291 | list_midi_devices_on_card(card); 292 | } 293 | } 294 | 295 | static void list_ports(void) 296 | { 297 | list_ports_raw(); 298 | if (seq) 299 | puts("Sequencer ports:"); 300 | else { 301 | fprintf(stderr, "ALSA sequencer disabled (load module and/or rebuild kernel to enable)\n"); 302 | return; 303 | } 304 | snd_seq_client_info_t *cinfo; 305 | snd_seq_port_info_t *pinfo; 306 | 307 | snd_seq_client_info_alloca(&cinfo); 308 | snd_seq_port_info_alloca(&pinfo); 309 | 310 | puts(" Port Client name Port name"); 311 | 312 | snd_seq_client_info_set_client(cinfo, -1); 313 | while (snd_seq_query_next_client(seq, cinfo) >= 0) { 314 | unsigned int caps; 315 | int client = snd_seq_client_info_get_client(cinfo); 316 | 317 | snd_seq_port_info_set_client(pinfo, client); 318 | snd_seq_port_info_set_port(pinfo, -1); 319 | while (snd_seq_query_next_port(seq, pinfo) >= 0) { 320 | /* port must understand MIDI messages */ 321 | if (!(snd_seq_port_info_get_type(pinfo) 322 | & SND_SEQ_PORT_TYPE_MIDI_GENERIC)) 323 | continue; 324 | /* we need both READ/WRITE and SUBS_READ/WRITE */ 325 | caps = snd_seq_port_info_get_capability(pinfo); 326 | if ((caps & (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE)) 327 | != (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE) && 328 | (caps & (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ)) 329 | != (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ)) 330 | continue; 331 | printf("%3d:%-3d %-32.32s %s\n", 332 | snd_seq_port_info_get_client(pinfo), 333 | snd_seq_port_info_get_port(pinfo), 334 | snd_seq_client_info_get_name(cinfo), 335 | snd_seq_port_info_get_name(pinfo)); 336 | } 337 | } 338 | } 339 | 340 | static void usage(const char *argv0) 341 | { 342 | printf("Usage: %s -o client:port -i client:port ...\n\n" 343 | " -o, --output=client:port port to send events to\n" 344 | " -i, --input=client:port port to receive events from\n" 345 | " -l, --list list available midi input/output ports\n\n" 346 | " -a, --raw interpret ports as snd_rawmidi names\n" 347 | #ifdef ENABLE_UART 348 | " -u, --uart baudrate interpret ports as UART devices (any valid device in /dev.\n" 349 | " UART devices will not be listed with -l). `baudrate' should\n" 350 | " be one of the ones supported by the system\n" 351 | " -y , --system= execute (via system(3)) after opening file descriptors for I/O\n" 352 | #endif // ENABLE_UART 353 | " -T, --timeout=# of ms how long to wait before considering a message lost (default is 1000)\n" 354 | " -g, --grace # of fail gracefully fail (i.e.: print results) after # of failures (i.e.: timeout/2 exceeded)\n" 355 | " -t, --terse only send to stdout the test specs and test results:\n" 356 | " '<#samples>, , , , \n" 357 | " , , , '\n" 358 | " -R, --realtime use realtime scheduling (default: no)\n" 359 | " -P, --priority=int scheduling priority, use with -R\n" 360 | " (default: maximum)\n\n" 361 | " -S, --samples=# of samples to take for the measurement (default: 10000)\n" 362 | " -s, --skip=# of samples to skip at the beginning (default: 0)\n" 363 | " -w, --wait=ms time interval between measurements\n" 364 | " -r, --random-wait use random interval between wait and 2*wait\n" 365 | " -x disable debug output of measurements,\n" 366 | " this improves timing accuracy with very low latencies\n" 367 | " use this with -w to avoid CPU saturation.\n" 368 | " group bins in histogram:\n" 369 | " -1 -2 -3 -4 -5 -6 0.1ms, 0.01ms, 0.001ms.. 0.000001ms (default: 0.1ms)\n\n" 370 | " -h, --help this help\n" 371 | " -V, --version print current version\n" 372 | "\n", argv0); 373 | } 374 | 375 | static void print_version(void) 376 | { 377 | printf("> %s %s\n", PACKAGE, VERSION); 378 | } 379 | 380 | static unsigned int timespec_sub(const struct timespec *a, 381 | const struct timespec *b) 382 | { 383 | unsigned int diff; 384 | 385 | if (a->tv_sec - b->tv_sec > 2) 386 | return UINT_MAX; 387 | diff = (a->tv_sec - b->tv_sec) * 1000000000; 388 | diff += a->tv_nsec - b->tv_nsec; 389 | return diff; 390 | } 391 | 392 | static void wait_ms(double t) 393 | { 394 | struct timespec ts; 395 | 396 | ts.tv_sec = t / 1000; 397 | ts.tv_nsec = (t - ts.tv_sec * 1000) * 1000000; 398 | nanosleep(&ts, NULL); 399 | } 400 | 401 | /* many thanks to Randall Munroe (http://xkcd.com/221/) */ 402 | static int getRandomNumber(void) 403 | { 404 | return 4; // chosen by fair dice roll. 405 | // guaranteed to be random. 406 | } 407 | 408 | static void sighandler(int sig) 409 | { 410 | signal_received = 1; 411 | } 412 | 413 | #ifdef ENABLE_UART 414 | /* error handling for POSIX functions */ 415 | static void check_posix(const char *operation, int err) 416 | { 417 | if (err) 418 | fatal("cannot %s - %s", operation, strerror(err)); 419 | } 420 | 421 | static unsigned int speedToBaudRate(unsigned int speed) { 422 | switch(speed) { 423 | case 50: speed = B50; 424 | break; 425 | case 75: speed = B75; 426 | break; 427 | case 110: speed = B110; 428 | break; 429 | case 134: speed = B134; 430 | break; 431 | case 150: speed = B150; 432 | break; 433 | case 200: speed = B200; 434 | break; 435 | case 300: speed = B300; 436 | break; 437 | case 600: speed = B600; 438 | break; 439 | case 1200: speed = B1200; 440 | break; 441 | case 1800: speed = B1800; 442 | break; 443 | case 2400: speed = B2400; 444 | break; 445 | case 4800: speed = B4800; 446 | break; 447 | case 9600: speed = B9600; 448 | break; 449 | case 19200: speed = B19200; 450 | break; 451 | case 38400: speed = B38400; 452 | break; 453 | case 57600: speed = B57600; 454 | break; 455 | case 115200: speed = B115200; 456 | break; 457 | case 230400: speed = B230400; 458 | break; 459 | case 0: 460 | //nobreak 461 | default: 462 | speed = B0; 463 | break; 464 | } 465 | return speed; 466 | } 467 | 468 | static int setInterfaceAttribs(int fd, unsigned int speed) { 469 | struct termios tty; 470 | 471 | if (tcgetattr(fd, &tty) < 0) { 472 | printf("Error from tcgetattr: %s\n", strerror(errno)); 473 | return -1; 474 | } 475 | 476 | cfsetospeed(&tty, (speed_t)speed); 477 | cfsetispeed(&tty, (speed_t)speed); 478 | 479 | tty.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */ 480 | tty.c_cflag &= ~CSIZE; 481 | tty.c_cflag |= CS8; /* 8-bit characters */ 482 | tty.c_cflag &= ~PARENB; /* no parity bit */ 483 | tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */ 484 | tty.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */ 485 | 486 | /* setup for non-canonical mode */ 487 | tty.c_iflag &= 488 | ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); 489 | tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); 490 | tty.c_oflag &= ~OPOST; 491 | 492 | /* fetch bytes as they become available */ 493 | tty.c_cc[VMIN] = 1; 494 | tty.c_cc[VTIME] = 1; 495 | 496 | if (tcsetattr(fd, TCSANOW, &tty) != 0) { 497 | printf("Error from tcsetattr: %s\n", strerror(errno)); 498 | return -1; 499 | } 500 | return 0; 501 | } 502 | 503 | static void setMinCount(int fd, int mcount) { 504 | struct termios tty; 505 | 506 | if (tcgetattr(fd, &tty) < 0) { 507 | printf("Error tcgetattr: %s\n", strerror(errno)); 508 | return; 509 | } 510 | 511 | tty.c_cc[VMIN] = mcount ? 1 : 0; 512 | tty.c_cc[VTIME] = 5; /* half second timer */ 513 | 514 | if (tcsetattr(fd, TCSANOW, &tty) < 0) { 515 | printf("Error tcsetattr: %s\n", strerror(errno)); 516 | } 517 | } 518 | #endif // ENABLE_UART 519 | 520 | int compare_unsigned_int(const void *p1, const void *p2) 521 | { 522 | return *(unsigned int *)p1 - *(unsigned int *)p2; 523 | } 524 | 525 | int main(int argc, char *argv[]) 526 | { 527 | static char short_options[] = "hVlau:y:T:g:to:i:RP:s:S:w:r123456x"; 528 | static struct option long_options[] = { 529 | {"help", 0, NULL, 'h'}, 530 | {"version", 0, NULL, 'V'}, 531 | {"list", 0, NULL, 'l'}, 532 | {"raw", 0, NULL, 'a'}, 533 | {"uart", 1, NULL, 'u'}, 534 | {"system", 1, NULL, 'y'}, 535 | {"timeout", 1, NULL, 'T'}, 536 | {"grace", 1, NULL, 'g'}, 537 | {"terse", 0, NULL, 't'}, 538 | {"output", 1, NULL, 'o'}, 539 | {"input", 1, NULL, 'i'}, 540 | {"realtime", 0, NULL, 'R'}, 541 | {"priority", 1, NULL, 'P'}, 542 | {"skip", 1, NULL, 's'}, 543 | {"samples", 1, NULL, 'S'}, 544 | {"wait", 1, NULL, 'w'}, 545 | {"random-wait", 0, NULL, 'r'}, 546 | {} 547 | }; 548 | int do_list = 0; 549 | int do_realtime = 0; 550 | int rt_prio = sched_get_priority_max(SCHED_FIFO); 551 | int skip_samples = 0; 552 | int nr_samples = 10000; 553 | int random_wait = 0; 554 | int precision = 1; 555 | int high_precision_display = 1; 556 | int debug = 1; 557 | double wait = 0.0; 558 | const char *output_name = NULL; 559 | const char *input_name = NULL; 560 | snd_seq_addr_t output_addr; 561 | snd_seq_addr_t input_addr; 562 | int c, err; 563 | int use_rawmidi = 0; 564 | #ifdef ENABLE_UART 565 | int use_uart = 0; 566 | int uart_speed = 0; 567 | const char* system_exec = NULL; 568 | #endif // ENABLE_UART 569 | unsigned int timeout = 1000; 570 | unsigned int grace = 0; 571 | int verbose = 1; 572 | 573 | while ((c = getopt_long(argc, argv, short_options, 574 | long_options, NULL)) != -1) { 575 | switch (c) { 576 | case 'a': 577 | use_rawmidi = 1; 578 | break; 579 | #ifdef ENABLE_UART 580 | case 'u': 581 | use_uart = 1; 582 | uart_speed = atoi(optarg); 583 | break; 584 | case 'y': 585 | system_exec = optarg; 586 | break; 587 | #endif //ENABLE_UART 588 | case 'h': 589 | usage(argv[0]); 590 | return EXIT_SUCCESS; 591 | case 'V': 592 | print_version(); 593 | return EXIT_SUCCESS; 594 | case 'l': 595 | do_list = 1; 596 | break; 597 | case 'o': 598 | output_name = optarg; 599 | break; 600 | case 'i': 601 | input_name = optarg; 602 | break; 603 | case 'R': 604 | do_realtime = 1; 605 | break; 606 | case 'P': 607 | rt_prio = atoi(optarg); 608 | if (rt_prio > sched_get_priority_max(SCHED_FIFO)) { 609 | printf("> Warning: Given priority: %d > sched_get_priority_max(SCHED_FIFO)! ", rt_prio); 610 | printf("Setting priority to %d.\n", sched_get_priority_max(SCHED_FIFO)); 611 | rt_prio = sched_get_priority_max(SCHED_FIFO); 612 | } else if (rt_prio < sched_get_priority_min(SCHED_FIFO)) { 613 | printf("> Warning: Given priority: %d < sched_get_priority_min(SCHED_FIFO)! ", rt_prio); 614 | printf("Setting priority to %d.\n", sched_get_priority_min(SCHED_FIFO)); 615 | rt_prio = sched_get_priority_min(SCHED_FIFO); 616 | } 617 | break; 618 | case 's': 619 | skip_samples = atoi(optarg); 620 | if (skip_samples < 0) { 621 | printf("> Warning: Given number of events to skip cannot be smaller than zero! "); 622 | printf("Setting nr of skip events to zero.\n"); 623 | skip_samples = 0; 624 | } 625 | break; 626 | case 'S': 627 | nr_samples = atoi(optarg); 628 | if (nr_samples <= 0) { 629 | printf("> Warning: Given number of samples to take is less or equal zero! "); 630 | printf("Setting nr of samples to take to 1.\n"); 631 | nr_samples = 1; 632 | } 633 | break; 634 | case 'T': 635 | timeout = atoi(optarg); 636 | break; 637 | case 'g': 638 | grace = atoi(optarg); 639 | break; 640 | case 't': 641 | verbose = 0; 642 | debug = 0; 643 | break; 644 | case 'w': 645 | wait = atof(optarg); 646 | if (wait < 0) { 647 | printf("> Warning: Wait time is negative; using zero.\n"); 648 | wait = 0; 649 | } 650 | break; 651 | case 'r': 652 | random_wait = 1; 653 | break; 654 | case '1': 655 | precision = 1; 656 | high_precision_display = 1; 657 | break; 658 | case '2': 659 | precision = 2; 660 | high_precision_display = 10; 661 | break; 662 | case '3': 663 | precision = 3; 664 | high_precision_display = 100; 665 | break; 666 | case '4': 667 | precision = 4; 668 | high_precision_display = 1000; 669 | break; 670 | case '5': 671 | precision = 5; 672 | high_precision_display = 10000; 673 | break; 674 | case '6': 675 | precision = 6; 676 | high_precision_display = 100000; 677 | break; 678 | case 'x': 679 | debug = 0; 680 | break; 681 | default: 682 | usage(argv[0]); 683 | return EXIT_FAILURE; 684 | } 685 | } 686 | if (argc == 1 || argv[optind]) { 687 | usage(argv[0]); 688 | return EXIT_FAILURE; 689 | } 690 | 691 | int use_seq = 1; 692 | // temporarily change the ALSA error handler to silence warning in case 693 | // /dev/snd/seq doesn't exist (e.g.: lacks kernel support or module not 694 | // loaded) 695 | snd_lib_error_set_handler(quiet_error_handler); 696 | err = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0); 697 | snd_lib_error_set_handler(NULL); // restore default handler 698 | if (err) 699 | use_seq = 0; 700 | 701 | if (do_list) { 702 | list_ports(); 703 | return 0; 704 | } 705 | 706 | if (!output_name) 707 | fatal("Please specify an output port with --output. Use -l to get a list."); 708 | if (!input_name) 709 | fatal("Please specify an input port with --input. Use -l to get a list."); 710 | // ensure that exactly one of rawmidi or seq is enabled 711 | if (use_rawmidi) 712 | use_seq = 0; 713 | else 714 | use_rawmidi = !use_seq; 715 | #ifdef ENABLE_UART 716 | if (use_uart) 717 | use_rawmidi = use_seq = 0; 718 | #endif // ENABLE_UART 719 | if (use_rawmidi) { 720 | err = snd_rawmidi_open(&raw_in, NULL, input_name, SND_RAWMIDI_NONBLOCK); 721 | check_snd("open input", err); 722 | err = snd_rawmidi_open(NULL, &raw_out, output_name, SND_RAWMIDI_SYNC); 723 | check_snd("open output", err); 724 | } 725 | #ifdef ENABLE_UART 726 | int uart_fd_in = -1; 727 | int uart_fd_out= -1; 728 | if (use_uart) { 729 | uart_fd_in = open(input_name, O_RDWR | O_NOCTTY | O_SYNC 730 | ); 731 | if (uart_fd_in < 0) 732 | check_posix("open input", errno); 733 | uart_fd_out = open(output_name, O_RDWR | O_NOCTTY | O_SYNC 734 | ); 735 | if (uart_fd_out < 0) 736 | check_posix("open output", errno); 737 | unsigned int baudRate = speedToBaudRate(uart_speed); 738 | if(B0 == baudRate) { 739 | fprintf(stderr, "Error setting BAUD rate: %d speed not supported\n", uart_speed); 740 | return -1; 741 | } 742 | setInterfaceAttribs(uart_fd_in, baudRate); 743 | setInterfaceAttribs(uart_fd_out, baudRate); 744 | setMinCount(uart_fd_in, 0); /* set to pure timed read */ 745 | setMinCount(uart_fd_out, 0); /* set to pure timed read */ 746 | } 747 | if (system_exec) { 748 | err = system(system_exec); 749 | if (err) { 750 | fprintf(stderr, "executing '%s' returned '%d'\n", system_exec, err); 751 | return 1; 752 | } 753 | } 754 | #endif // ENABLE_UART 755 | int port; 756 | int client; 757 | if (use_seq) { 758 | err = snd_seq_parse_address(seq, &output_addr, output_name); 759 | check_snd("parse output port", err); 760 | err = snd_seq_parse_address(seq, &input_addr, input_name); 761 | check_snd("parse input port", err); 762 | 763 | err = snd_seq_set_client_name(seq, "alsa-midi-latency-test"); 764 | check_snd("set client name", err); 765 | client = snd_seq_client_id(seq); 766 | check_snd("get client id", client); 767 | port = snd_seq_create_simple_port(seq, "alsa-midi-latency-test", 768 | SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SYNC_WRITE, 769 | SND_SEQ_PORT_TYPE_APPLICATION); 770 | check_snd("create port", port); 771 | err = snd_seq_connect_to(seq, port, output_addr.client, output_addr.port); 772 | check_snd("connect output port", err); 773 | err = snd_seq_connect_from(seq, port, input_addr.client, input_addr.port); 774 | check_snd("connect input port", err); 775 | } 776 | 777 | if (verbose) { 778 | print_version(); 779 | print_uname(); 780 | } 781 | 782 | if (random_wait) 783 | srand(getRandomNumber()); 784 | 785 | if (do_realtime) { 786 | if(verbose) 787 | printf("> set_realtime_priority(SCHED_FIFO, %d).. ", rt_prio); 788 | set_realtime_priority(SCHED_FIFO, rt_prio); 789 | if (verbose) 790 | printf("done.\n"); 791 | } 792 | 793 | #if defined(CLOCK_MONOTONIC_RAW) 794 | #define HR_CLOCK CLOCK_MONOTONIC_RAW 795 | #else 796 | #define HR_CLOCK CLOCK_MONOTONIC 797 | #endif 798 | 799 | struct timespec begin, end; 800 | if (clock_gettime(HR_CLOCK, &begin) < 0) 801 | fatal("monotonic raw clock not supported"); 802 | if (clock_getres(HR_CLOCK, &begin) < 0) 803 | fatal("monotonic raw clock not supported"); 804 | if (verbose) 805 | printf("> clock resolution: %d.%09ld s\n", (int)begin.tv_sec, begin.tv_nsec); 806 | if ((begin.tv_sec || begin.tv_nsec > 1000000) && verbose) 807 | puts("WARNING: You do not have a high-resolution clock!"); 808 | if (wait && verbose) { 809 | if (random_wait) 810 | printf("> interval between measurements: %.3f .. %.3f ms\n", wait, wait * 2); 811 | else 812 | printf("> interval between measurements: %.3f ms\n", wait); 813 | } 814 | 815 | if (verbose) { 816 | printf("\n> sampling %d midi latency values - please wait …\n", nr_samples); 817 | printf("> press Ctrl+C to abort test\n"); 818 | } 819 | 820 | signal(SIGINT, sighandler); 821 | signal(SIGTERM, sighandler); 822 | 823 | unsigned int *delays = calloc(nr_samples, sizeof *delays); 824 | check_mem(delays); 825 | 826 | if (skip_samples) { 827 | if (skip_samples == 1) 828 | if(verbose) 829 | printf("> skipping first latency sample\n"); 830 | else 831 | if(verbose) 832 | printf("> skipping first %d latency samples\n", skip_samples); 833 | } 834 | 835 | if (debug == 1) 836 | printf("\nsample; latency_ms; latency_ms_worst\n"); 837 | 838 | snd_seq_event_t ev; 839 | int pollfds_count; 840 | struct pollfd *pollfds; 841 | err = 0; 842 | if (use_seq) 843 | { 844 | snd_seq_ev_clear(&ev); 845 | snd_seq_ev_set_dest(&ev, output_addr.client, output_addr.port); 846 | snd_seq_ev_set_source(&ev, port); 847 | snd_seq_ev_set_direct(&ev); 848 | snd_seq_ev_set_noteon(&ev, 0, 60, 127); 849 | 850 | pollfds_count = snd_seq_poll_descriptors_count(seq, POLLIN); 851 | pollfds = alloca(pollfds_count * sizeof *pollfds); 852 | err = snd_seq_poll_descriptors(seq, pollfds, pollfds_count, POLLIN); 853 | } 854 | const char test_status_byte = 0x90; 855 | char msg[3] = { test_status_byte, 60, 127 }; 856 | if (use_rawmidi) 857 | { 858 | pollfds_count = snd_rawmidi_poll_descriptors_count(raw_in); 859 | pollfds = alloca(pollfds_count * sizeof *pollfds); 860 | err = snd_rawmidi_poll_descriptors(raw_in, pollfds, pollfds_count); 861 | snd_rawmidi_drain(raw_in); 862 | snd_rawmidi_drain(raw_out); 863 | // not sure if this is documented anwhere, but in practical 864 | // applications we find that one needs to poll() at least once 865 | // before incoming messages start being queued. 866 | // skipping this dummy poll() here would result in the first 867 | // response message not being received if the roundtrip is so 868 | // fast that the first call to poll() happens after the 869 | // device has sent back its response 870 | poll(pollfds, pollfds_count, 0); 871 | } 872 | check_snd("get poll descriptors", err); 873 | pollfds_count = err; 874 | #ifdef ENABLE_UART 875 | if (use_uart) 876 | { 877 | pollfds_count = 1; 878 | pollfds = alloca(pollfds_count * sizeof(*pollfds)); 879 | pollfds[0].fd = uart_fd_in; 880 | pollfds[0].events = POLLIN; 881 | poll(pollfds, pollfds_count, 0); 882 | } 883 | #endif // ENABLE_UART 884 | 885 | unsigned int sample_nr = 0; 886 | unsigned int min_delay = UINT_MAX, max_delay = 0; 887 | long unsigned int total_delay = 0; 888 | unsigned int graceTimeouts = 0; 889 | for (c = 0; c < nr_samples; ++c) { 890 | if (wait) { 891 | if (random_wait) 892 | wait_ms(wait + rand() * wait / RAND_MAX); 893 | else 894 | wait_ms(wait); 895 | if (signal_received) 896 | break; 897 | } 898 | 899 | clock_gettime(HR_CLOCK, &begin); 900 | 901 | if (use_seq) 902 | err = snd_seq_event_output_direct(seq, &ev); 903 | char rec_msg[3]; 904 | if (use_rawmidi) 905 | err = snd_rawmidi_write(raw_out, msg, sizeof(msg)); 906 | check_snd("output MIDI event", err); 907 | #ifdef ENABLE_UART 908 | if (use_uart) 909 | { 910 | err = write(uart_fd_out, msg, sizeof(msg)); 911 | if (err != sizeof(msg)) 912 | check_posix("output UART event", errno); 913 | } 914 | #endif // ENABLE_UART 915 | 916 | snd_seq_event_t *rec_ev; 917 | int received_something = 0; 918 | for (;;) { 919 | if (use_seq) 920 | rec_ev = NULL; 921 | if ( 922 | use_rawmidi 923 | #ifdef ENABLE_UART 924 | || use_uart 925 | #endif // ENABLE_UART 926 | ) 927 | memset(rec_msg, 0, sizeof(rec_msg)); 928 | err = poll(pollfds, pollfds_count, timeout); 929 | if (signal_received) 930 | break; 931 | if (err == 0) 932 | fatal("timeout: there seems to be no connection between ports %s and %s", output_name, input_name); 933 | if (err < 0) 934 | fatal("poll error: %s", strerror(errno)); 935 | unsigned short revents; 936 | if (use_seq) 937 | err = snd_seq_poll_descriptors_revents(seq, pollfds, pollfds_count, &revents); 938 | if (use_rawmidi) 939 | err = snd_rawmidi_poll_descriptors_revents(raw_in, pollfds, pollfds_count, &revents); 940 | check_snd("get poll events", err); 941 | #ifdef ENABLE_UART 942 | if (use_uart) 943 | revents = pollfds[0].revents; 944 | #endif // ENABLE_UART 945 | if (revents & (POLLERR | POLLNVAL)) 946 | break; 947 | if (!(revents & POLLIN)) 948 | continue; 949 | if (use_seq) { 950 | err = snd_seq_event_input(seq, &rec_ev); 951 | check_snd("input MIDI event", err); 952 | received_something = 1; 953 | if (rec_ev->type == SND_SEQ_EVENT_NOTEON) 954 | break; 955 | } 956 | if (use_rawmidi) { 957 | err = snd_rawmidi_read(raw_in, rec_msg, sizeof(rec_msg)); 958 | check_snd("input MIDI event", err); 959 | received_something = 1; 960 | if (test_status_byte == rec_msg[0]) 961 | break; 962 | } 963 | #ifdef ENABLE_UART 964 | if (use_uart) { 965 | err = read(uart_fd_in, rec_msg, sizeof(rec_msg)); 966 | // TODO: handle the case where these are not received all at once 967 | if (err != sizeof(rec_msg)) 968 | check_snd("input UART event", errno); 969 | received_something = 1; 970 | if (test_status_byte == rec_msg[0]) 971 | break; 972 | } 973 | #endif // ENABLE_UART 974 | } 975 | if (!received_something) 976 | break; 977 | 978 | clock_gettime(HR_CLOCK, &end); 979 | 980 | unsigned int delay_ns = timespec_sub(&end, &begin); 981 | if (sample_nr < skip_samples) { 982 | //if (debug == 1) printf("skipping sample %d\n", sample_nr); 983 | } else if (delay_ns > max_delay) { 984 | max_delay = delay_ns; 985 | if (debug == 1) 986 | printf("%6u; %10.*f; %10.*f \n", 987 | sample_nr, 2 + precision, delay_ns / 1000000.0, 2 + precision, max_delay / 1000000.0); 988 | } else { 989 | if (debug == 1) 990 | printf("%6u; %10.*f; %10.*f \r", 991 | sample_nr, 2 + precision, delay_ns / 1000000.0, 2 + precision, max_delay / 1000000.0); 992 | } 993 | if (delay_ns < min_delay) 994 | min_delay = delay_ns; 995 | delays[sample_nr++] = delay_ns; 996 | total_delay += delay_ns; 997 | 998 | ev.data.note.channel ^= 1; // prevent running status 999 | 1000 | if (delay_ns >= (timeout * 1000000 / 2) && sample_nr >= skip_samples) { 1001 | ++graceTimeouts; 1002 | } 1003 | if (grace && graceTimeouts >= grace) { 1004 | fprintf(stderr, "Exiting earlier because of %d timeouts / 2\n", graceTimeouts); 1005 | break; 1006 | } 1007 | } 1008 | unsigned int mean_delay = total_delay / sample_nr; 1009 | 1010 | if (verbose) 1011 | printf("\n> done.\n\n> latency distribution:\n"); 1012 | 1013 | if (!max_delay) { 1014 | puts("no delay was measured; clock has too low resolution"); 1015 | return EXIT_FAILURE; 1016 | } 1017 | 1018 | unsigned int delay_hist[1000000]; 1019 | unsigned int i, j; 1020 | for (i = 0; i < ARRAY_SIZE(delay_hist); ++i) 1021 | delay_hist[i] = 0; 1022 | for (i = skip_samples; i < sample_nr; ++i) { 1023 | unsigned int index = (delays[i] + 50000/high_precision_display) / (100000/high_precision_display); 1024 | if (index >= ARRAY_SIZE(delay_hist)) 1025 | index = ARRAY_SIZE(delay_hist) - 1; 1026 | delay_hist[index]++; 1027 | } 1028 | 1029 | unsigned int max_samples = 0; 1030 | for (i = 0; i < ARRAY_SIZE(delay_hist); ++i) { 1031 | if (delay_hist[i] > max_samples) 1032 | max_samples = delay_hist[i]; 1033 | } 1034 | if (!max_samples) { 1035 | puts("(no measurements)"); 1036 | return EXIT_FAILURE; 1037 | } 1038 | 1039 | // plot ascii bars 1040 | int skipped = 0; 1041 | if (verbose) { 1042 | for (i = 0; i < ARRAY_SIZE(delay_hist); ++i) { 1043 | if (delay_hist[i] > 0) { 1044 | if (skipped) { 1045 | puts("..."); 1046 | skipped = 0; 1047 | } 1048 | printf("%*.*f -%*.*f ms: %8u ", 4 + precision, precision, i/(10.0*high_precision_display), 4 + precision, precision, i/(10.0*high_precision_display) + (0.09999999/high_precision_display), delay_hist[i]); 1049 | unsigned int bar_width = (delay_hist[i] * 50 + max_samples / 2) / max_samples; 1050 | if (!bar_width && delay_hist[i]) 1051 | bar_width = 1; 1052 | for (j = 0; j < bar_width; ++j) 1053 | printf("#"); 1054 | puts(""); 1055 | } else { 1056 | skipped = 1; 1057 | } 1058 | } 1059 | } 1060 | 1061 | if (use_seq) 1062 | snd_seq_close(seq); 1063 | if (use_rawmidi) { 1064 | snd_rawmidi_close(raw_in); 1065 | snd_rawmidi_close(raw_out); 1066 | } 1067 | 1068 | if (verbose) { 1069 | if (max_delay / 1000000.0 > 6.0) { // latencies <= 6ms are o.k. imho 1070 | printf("\n> FAIL\n"); 1071 | printf("\n best latency was %.2f ms\n", min_delay / 1000000.0); 1072 | printf(" worst latency was %.2f ms, which is too much. Please check:\n\n", max_delay/1000000.0); 1073 | printf(" - if your hardware uses shared IRQs - `watch -n 1 cat /proc/interrupts`\n"); 1074 | printf(" while running this test to see, which IRQs the OS is using for your midi hardware,\n\n"); 1075 | printf(" - if you're running this test on a realtime OS - `uname -a` should contain '-rt',\n\n"); 1076 | printf(" - your OS' scheduling priorities - `chrt -p [pidof process name|IRQ-?]`.\n\n"); 1077 | printf(" Have a look at\n"); 1078 | printf(" https://www.linuxaudio.org/resources.html\n"); 1079 | printf(" to find out, howto fix issues with high midi latencies.\n\n"); 1080 | 1081 | return EXIT_FAILURE; 1082 | 1083 | } else { 1084 | qsort(delays, sample_nr, sizeof(delays[0]), compare_unsigned_int); 1085 | double median = delays[sample_nr / 2]; 1086 | if ((sample_nr & 1) == 0) 1087 | median = (median + delays[sample_nr / 2 + 1]) / 2.0; 1088 | 1089 | printf("\n> SUCCESS\n"); 1090 | printf("\n best latency was %.*f ms\n", precision, min_delay / 1000000.0); 1091 | printf(" mean latency was %.*f ms\n", precision, mean_delay /1000000.0); 1092 | printf(" median latency was %.*f ms\n", precision, median /1000000.0); 1093 | printf(" worst latency was %.*f ms, which is great.\n", precision, max_delay/1000000.0); 1094 | 1095 | printf("\n> Share your benchmarking results in the wiki at:\n\n https://github.com/koppi/alsa-midi-latency-test/wiki\n\n"); 1096 | 1097 | return EXIT_SUCCESS; 1098 | } 1099 | } else { 1100 | printf("%6d, %1d, %3d, %3d, %.3f, %1d, %.3f, %.3f, %.3f\n", 1101 | sample_nr, 1102 | do_realtime, 1103 | rt_prio, 1104 | skip_samples, 1105 | wait, 1106 | random_wait, 1107 | min_delay / 1000000.0, 1108 | mean_delay / 1000000.0, 1109 | max_delay / 1000000.0 1110 | ); 1111 | 1112 | return EXIT_SUCCESS; 1113 | } 1114 | } 1115 | --------------------------------------------------------------------------------