diff --git a/.dir-locals.el b/.dir-locals.el new file mode 100644 index 00000000..3c3abd62 --- /dev/null +++ b/.dir-locals.el @@ -0,0 +1,8 @@ +;;; Directory Local Variables +;;; For more information see (info "(emacs) Directory Variables") + +((c++-mode + (c-basic-offset . 2)) + (c-mode + (c-basic-offset . 2))) + diff --git a/.gitignore b/.gitignore index 44b3d6ab..1f8c32bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,15 @@ /config /configure -/Makefile.in +/doc/html +Makefile +Makefile.in /aclocal.m4 /autom4te.cache /configure -/m4 -/doc/Makefile.in -/tests/Makefile.in -/build*/ +/m4/libtool.m4 +/m4/ltoptions.m4 +/m4/ltsugar.m4 +/m4/ltversion.m4 +/m4/lt~obsolete.m4 +*.orig +*~ diff --git a/.travis.yml b/.travis.yml index ccc9d688..c3318992 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,8 @@ -language: cpp +git: + depth: 1 +branches: + except: + - /(?i:appveyor)/ sudo: false # docker VM matrix: include: @@ -15,11 +19,17 @@ matrix: env: HOST="" API="jack" compiler: clang - os: linux - env: HOST="--host=i686-w64-mingw32" API="winmm" + env: HOST="--with-jack" API="alsa" compiler: gcc - os: linux - env: HOST="--host=x86_64-w64-mingw32" API="winmm" - compiler: gcc + env: HOST="--with-jack" API="alsa" + compiler: clang + # - os: linux + # env: HOST="--host=i686-w64-mingw32" API="winmm" + # compiler: gcc + # - os: linux + # env: HOST="--host=x86_64-w64-mingw32" API="winmm" + # compiler: gcc # jack and asound not found on ARM gnueabihf # - os: linux # env: HOST="--host=arm-linux-gnueabihf" API="alsa" @@ -28,20 +38,56 @@ matrix: # env: HOST="--host=arm-linux-gnueabihf" API="jack" # compiler: gcc - os: osx - env: HOST="" API="core" + env: HOST="" API="coremidi" + compiler: gcc + - os: osx + env: HOST="" API="coremidi" + compiler: clang + - os: osx + env: HOST="" API="jack" + compiler: gcc + - os: osx + env: HOST="" API="jack" + compiler: clang + - os: osx + env: HOST="--with-jack" API="coremidi" compiler: gcc - os: osx - env: HOST="" API="core" + env: HOST="--with-jack" API="coremidi" compiler: clang -install: -- if [ $TRAVIS_OS_NAME = linux ]; then sudo apt-get install libasound2-dev libjack-dev doxygen g++-mingw-w64-i686 g++-mingw-w64-x86-64 g++-arm-linux-gnueabihf; fi -- if [ $TRAVIS_OS_NAME = osx ]; then brew install doxygen || (brew update && brew install doxygen); fi -- if [ -n "$HOST" ]; then unset CXX; unset CC; fi -script: ./autogen.sh --enable-debug --with-$API $HOST && make -after_script: -- make check -- make distcheck -- make install -# ALSA: no access to /dev/snd/seq -# JACK: Jack server not running -# - tests/midiprobe +language: c++ +group: travis_latest + + +addons: + apt: + packages: + - autoconf + - libtool + - autopoint + - automake + - libasound-dev + - libjack-dev + - libcppunit-dev + - doxygen + - g++-mingw-w64-i686 + - g++-mingw-w64-x86-64 + - g++-arm-linux-gnueabihf + + +before_install: + - eval "${MATRIX_EVAL}" + - if test "$TRAVIS_OS_NAME" = osx ; then brew update ; fi + - if test "$TRAVIS_OS_NAME" = osx ; then if brew upgrade boost ; then echo "upgraded" ; else echo "nothing to be done"; fi ; fi + - if test "$TRAVIS_OS_NAME" = osx ; then rm -rf /usr/local/include/c++ ; fi # fix broken gcc installation + - if test "$TRAVIS_OS_NAME" = osx ; then brew install $EXTRA_BREW gettext flex bison libtool autoconf jack doxygen ; fi + - if test "$TRAVIS_OS_NAME" = osx ; then brew link --force $EXTRA_BREW gettext flex bison libtool autoconf jack ; fi + - mkdir -p config + - touch config/config.rpath + +script: + - mkdir -p config + - touch config/config.rpath + - ./autogen.sh --disable-apisearch --with-$API $HOST && make && make -j4 && $TEST_RUNNER make check || (cat test-suite.log ; false) + - make distcheck + - cat tests/errors.log diff --git a/ABOUT-NLS b/ABOUT-NLS new file mode 100644 index 00000000..3cc82865 --- /dev/null +++ b/ABOUT-NLS @@ -0,0 +1,1379 @@ +1 Notes on the Free Translation Project +*************************************** + +Free software is going international! The Free Translation Project is a +way to get maintainers of free software, translators, and users all +together, so that free software will gradually become able to speak many +languages. A few packages already provide translations for their +messages. + + If you found this 'ABOUT-NLS' file inside a distribution, you may +assume that the distributed package does use GNU 'gettext' internally, +itself available at your nearest GNU archive site. But you do _not_ +need to install GNU 'gettext' prior to configuring, installing or using +this package with messages translated. + + Installers will find here some useful hints. These notes also +explain how users should proceed for getting the programs to use the +available translations. They tell how people wanting to contribute and +work on translations can contact the appropriate team. + +1.1 INSTALL Matters +=================== + +Some packages are "localizable" when properly installed; the programs +they contain can be made to speak your own native language. Most such +packages use GNU 'gettext'. Other packages have their own ways to +internationalization, predating GNU 'gettext'. + + By default, this package will be installed to allow translation of +messages. It will automatically detect whether the system already +provides the GNU 'gettext' functions. Installers may use special +options at configuration time for changing the default behaviour. The +command: + + ./configure --disable-nls + +will _totally_ disable translation of messages. + + When you already have GNU 'gettext' installed on your system and run +configure without an option for your new package, 'configure' will +probably detect the previously built and installed 'libintl' library and +will decide to use it. If not, you may have to to use the +'--with-libintl-prefix' option to tell 'configure' where to look for it. + + Internationalized packages usually have many 'po/LL.po' files, where +LL gives an ISO 639 two-letter code identifying the language. Unless +translations have been forbidden at 'configure' time by using the +'--disable-nls' switch, all available translations are installed +together with the package. However, the environment variable 'LINGUAS' +may be set, prior to configuration, to limit the installed set. +'LINGUAS' should then contain a space separated list of two-letter +codes, stating which languages are allowed. + +1.2 Using This Package +====================== + +As a user, if your language has been installed for this package, you +only have to set the 'LANG' environment variable to the appropriate +'LL_CC' combination. If you happen to have the 'LC_ALL' or some other +'LC_xxx' environment variables set, you should unset them before setting +'LANG', otherwise the setting of 'LANG' will not have the desired +effect. Here 'LL' is an ISO 639 two-letter language code, and 'CC' is +an ISO 3166 two-letter country code. For example, let's suppose that +you speak German and live in Germany. At the shell prompt, merely +execute 'setenv LANG de_DE' (in 'csh'), 'export LANG; LANG=de_DE' (in +'sh') or 'export LANG=de_DE' (in 'bash'). This can be done from your +'.login' or '.profile' file, once and for all. + + You might think that the country code specification is redundant. +But in fact, some languages have dialects in different countries. For +example, 'de_AT' is used for Austria, and 'pt_BR' for Brazil. The +country code serves to distinguish the dialects. + + The locale naming convention of 'LL_CC', with 'LL' denoting the +language and 'CC' denoting the country, is the one use on systems based +on GNU libc. On other systems, some variations of this scheme are used, +such as 'LL' or 'LL_CC.ENCODING'. You can get the list of locales +supported by your system for your language by running the command +'locale -a | grep '^LL''. + + Not all programs have translations for all languages. By default, an +English message is shown in place of a nonexistent translation. If you +understand other languages, you can set up a priority list of languages. +This is done through a different environment variable, called +'LANGUAGE'. GNU 'gettext' gives preference to 'LANGUAGE' over 'LANG' +for the purpose of message handling, but you still need to have 'LANG' +set to the primary language; this is required by other parts of the +system libraries. For example, some Swedish users who would rather read +translations in German than English for when Swedish is not available, +set 'LANGUAGE' to 'sv:de' while leaving 'LANG' to 'sv_SE'. + + Special advice for Norwegian users: The language code for Norwegian +bokma*l changed from 'no' to 'nb' recently (in 2003). During the +transition period, while some message catalogs for this language are +installed under 'nb' and some older ones under 'no', it's recommended +for Norwegian users to set 'LANGUAGE' to 'nb:no' so that both newer and +older translations are used. + + In the 'LANGUAGE' environment variable, but not in the 'LANG' +environment variable, 'LL_CC' combinations can be abbreviated as 'LL' to +denote the language's main dialect. For example, 'de' is equivalent to +'de_DE' (German as spoken in Germany), and 'pt' to 'pt_PT' (Portuguese +as spoken in Portugal) in this context. + +1.3 Translating Teams +===================== + +For the Free Translation Project to be a success, we need interested +people who like their own language and write it well, and who are also +able to synergize with other translators speaking the same language. +Each translation team has its own mailing list. The up-to-date list of +teams can be found at the Free Translation Project's homepage, +'http://translationproject.org/', in the "Teams" area. + + If you'd like to volunteer to _work_ at translating messages, you +should become a member of the translating team for your own language. +The subscribing address is _not_ the same as the list itself, it has +'-request' appended. For example, speakers of Swedish can send a +message to 'sv-request@li.org', having this message body: + + subscribe + + Keep in mind that team members are expected to participate _actively_ +in translations, or at solving translational difficulties, rather than +merely lurking around. If your team does not exist yet and you want to +start one, or if you are unsure about what to do or how to get started, +please write to 'coordinator@translationproject.org' to reach the +coordinator for all translator teams. + + The English team is special. It works at improving and uniformizing +the terminology in use. Proven linguistic skills are praised more than +programming skills, here. + +1.4 Available Packages +====================== + +Languages are not equally supported in all packages. The following +matrix shows the current state of internationalization, as of Jun 2014. +The matrix shows, in regard of each package, for which languages PO +files have been submitted to translation coordination, with a +translation percentage of at least 50%. + + Ready PO files af am an ar as ast az be bg bn bn_IN bs ca crh cs + +---------------------------------------------------+ + a2ps | [] [] [] | + aegis | | + anubis | | + aspell | [] [] [] | + bash | [] [] [] | + bfd | | + binutils | [] | + bison | | + bison-runtime | [] | + buzztrax | [] | + ccd2cue | | + ccide | | + cflow | | + clisp | | + coreutils | [] [] | + cpio | | + cppi | | + cpplib | [] | + cryptsetup | [] | + datamash | | + denemo | [] [] | + dfarc | [] | + dialog | [] [] [] | + dico | | + diffutils | [] | + dink | [] | + direvent | | + doodle | [] | + dos2unix | | + dos2unix-man | | + e2fsprogs | [] [] | + enscript | [] | + exif | [] | + fetchmail | [] [] | + findutils | [] | + flex | [] | + freedink | [] [] | + fusionforge | | + gas | | + gawk | [] | + gcal | [] | + gcc | | + gdbm | | + gettext-examples | [] [] [] [] [] | + gettext-runtime | [] [] [] | + gettext-tools | [] [] | + gjay | | + glunarclock | [] [] [] | + gnubiff | [] | + gnubik | [] | + gnucash | () () [] | + gnuchess | | + gnulib | [] | + gnunet | | + gnunet-gtk | | + gold | | + gphoto2 | [] | + gprof | [] | + gramadoir | | + grep | [] [] [] | + grub | [] | + gsasl | | + gss | | + gst-plugins-bad | [] [] | + gst-plugins-base | [] [] [] | + gst-plugins-good | [] [] [] | + gst-plugins-ugly | [] [] [] | + gstreamer | [] [] [] [] | + gtick | [] | + gtkam | [] [] | + gtkspell | [] [] [] [] [] | + guix | | + guix-packages | | + gutenprint | [] | + hello | [] | + help2man | | + help2man-texi | | + hylafax | | + idutils | | + iso_15924 | [] | + iso_3166 | [] [] [] [] [] [] [] [] [] [] | + iso_3166_2 | | + iso_4217 | [] | + iso_639 | [] [] [] [] [] [] [] [] [] | + iso_639_3 | [] [] | + iso_639_5 | | + jwhois | | + kbd | [] | + klavaro | [] [] [] [] [] | + ld | [] | + leafpad | [] [] [] [] | + libc | [] [] [] | + libexif | () | + libextractor | | + libgnutls | [] | + libgphoto2 | [] | + libgphoto2_port | [] | + libgsasl | | + libiconv | [] [] | + libidn | [] | + liferea | [] [] [] [] | + lilypond | [] [] | + lordsawar | [] | + lprng | | + lynx | [] [] | + m4 | [] | + mailfromd | | + mailutils | | + make | [] | + man-db | [] [] | + man-db-manpages | | + midi-instruments | [] [] [] | + minicom | [] | + mkisofs | [] | + myserver | [] | + nano | [] [] [] | + opcodes | | + parted | [] | + pies | | + pnmixer | | + popt | [] | + procps-ng | | + procps-ng-man | | + psmisc | [] | + pspp | [] | + pushover | [] | + pwdutils | | + pyspread | | + radius | [] | + recode | [] [] [] | + recutils | | + rpm | | + rush | | + sarg | | + sed | [] [] [] [] | + sharutils | [] | + shishi | | + skribilo | | + solfege | [] [] | + solfege-manual | | + spotmachine | | + sudo | [] [] | + sudoers | [] [] | + sysstat | [] | + tar | [] [] [] | + texinfo | [] [] | + texinfo_document | [] [] | + tigervnc | [] | + tin | | + tin-man | | + tracgoogleappsa... | | + trader | | + util-linux | [] | + ve | | + vice | | + vmm | | + vorbis-tools | [] | + wastesedge | | + wcd | | + wcd-man | | + wdiff | [] [] | + wget | [] | + wyslij-po | | + xboard | | + xdg-user-dirs | [] [] [] [] [] [] [] [] [] [] | + xkeyboard-config | [] [] [] | + +---------------------------------------------------+ + af am an ar as ast az be bg bn bn_IN bs ca crh cs + 4 0 2 5 3 11 0 8 25 3 3 1 55 4 74 + + da de el en en_GB en_ZA eo es et eu fa fi fr + +--------------------------------------------------+ + a2ps | [] [] [] [] [] [] [] [] [] | + aegis | [] [] [] [] | + anubis | [] [] [] [] [] | + aspell | [] [] [] [] [] [] [] | + bash | [] [] [] | + bfd | [] [] [] [] | + binutils | [] [] [] | + bison | [] [] [] [] [] [] [] [] | + bison-runtime | [] [] [] [] [] [] [] [] | + buzztrax | [] [] [] [] | + ccd2cue | [] [] [] [] | + ccide | [] [] [] [] [] [] | + cflow | [] [] [] [] [] | + clisp | [] [] [] [] [] | + coreutils | [] [] [] [] [] | + cpio | [] [] [] [] [] | + cppi | [] [] [] [] [] | + cpplib | [] [] [] [] [] [] | + cryptsetup | [] [] [] [] [] | + datamash | [] [] [] [] | + denemo | [] | + dfarc | [] [] [] [] [] [] | + dialog | [] [] [] [] [] [] [] [] [] | + dico | [] [] [] [] | + diffutils | [] [] [] [] [] [] | + dink | [] [] [] [] [] [] | + direvent | [] [] [] [] | + doodle | [] [] [] [] | + dos2unix | [] [] [] [] [] | + dos2unix-man | [] [] [] | + e2fsprogs | [] [] [] [] [] | + enscript | [] [] [] [] [] [] | + exif | [] [] [] [] [] [] | + fetchmail | [] () [] [] [] [] [] | + findutils | [] [] [] [] [] [] [] [] | + flex | [] [] [] [] [] [] | + freedink | [] [] [] [] [] [] [] [] | + fusionforge | [] [] [] | + gas | [] [] [] | + gawk | [] [] [] [] [] | + gcal | [] [] [] [] | + gcc | [] | + gdbm | [] [] [] [] [] | + gettext-examples | [] [] [] [] [] [] [] | + gettext-runtime | [] [] [] [] [] [] | + gettext-tools | [] [] [] [] [] | + gjay | [] [] [] [] | + glunarclock | [] [] [] [] [] | + gnubiff | () [] [] () | + gnubik | [] [] [] [] [] | + gnucash | [] () () () () () () | + gnuchess | [] [] [] [] | + gnulib | [] [] [] [] [] [] [] | + gnunet | [] | + gnunet-gtk | [] | + gold | [] [] [] | + gphoto2 | [] () [] [] | + gprof | [] [] [] [] [] [] | + gramadoir | [] [] [] [] [] | + grep | [] [] [] [] [] [] [] | + grub | [] [] [] [] [] | + gsasl | [] [] [] [] [] | + gss | [] [] [] [] [] | + gst-plugins-bad | [] [] [] | + gst-plugins-base | [] [] [] [] [] [] | + gst-plugins-good | [] [] [] [] [] [] [] | + gst-plugins-ugly | [] [] [] [] [] [] [] [] | + gstreamer | [] [] [] [] [] [] [] | + gtick | [] () [] [] [] | + gtkam | [] () [] [] [] [] | + gtkspell | [] [] [] [] [] [] [] [] | + guix | [] [] | + guix-packages | | + gutenprint | [] [] [] [] | + hello | [] [] [] [] [] [] [] [] | + help2man | [] [] [] [] [] [] [] | + help2man-texi | [] [] [] | + hylafax | [] [] | + idutils | [] [] [] [] [] | + iso_15924 | [] () [] [] () [] () | + iso_3166 | [] () [] [] [] [] () [] () | + iso_3166_2 | [] () () () | + iso_4217 | [] () [] [] [] () [] () | + iso_639 | [] () [] [] () [] () | + iso_639_3 | () () () | + iso_639_5 | () () () | + jwhois | [] [] [] [] [] | + kbd | [] [] [] [] [] [] | + klavaro | [] [] [] [] [] [] [] | + ld | [] [] [] [] | + leafpad | [] [] [] [] [] [] [] [] | + libc | [] [] [] [] [] | + libexif | [] [] () [] [] | + libextractor | [] | + libgnutls | [] [] [] [] | + libgphoto2 | [] () [] | + libgphoto2_port | [] () [] [] [] [] | + libgsasl | [] [] [] [] [] | + libiconv | [] [] [] [] [] [] [] | + libidn | [] [] [] [] [] | + liferea | [] () [] [] [] [] [] | + lilypond | [] [] [] [] [] [] | + lordsawar | [] [] | + lprng | | + lynx | [] [] [] [] [] [] | + m4 | [] [] [] [] [] [] | + mailfromd | [] | + mailutils | [] [] [] [] | + make | [] [] [] [] [] | + man-db | [] [] [] [] | + man-db-manpages | [] [] | + midi-instruments | [] [] [] [] [] [] [] [] [] | + minicom | [] [] [] [] [] | + mkisofs | [] [] [] | + myserver | [] [] [] [] | + nano | [] [] [] [] [] [] [] | + opcodes | [] [] [] [] [] | + parted | [] [] [] | + pies | [] | + pnmixer | [] [] | + popt | [] [] [] [] [] [] | + procps-ng | [] [] | + procps-ng-man | [] [] | + psmisc | [] [] [] [] [] [] [] | + pspp | [] [] [] | + pushover | () [] [] [] | + pwdutils | [] [] [] | + pyspread | [] [] [] | + radius | [] [] | + recode | [] [] [] [] [] [] [] | + recutils | [] [] [] [] | + rpm | [] [] [] [] [] | + rush | [] [] [] | + sarg | [] [] | + sed | [] [] [] [] [] [] [] [] | + sharutils | [] [] [] [] | + shishi | [] [] [] | + skribilo | [] [] [] | + solfege | [] [] [] [] [] [] [] [] | + solfege-manual | [] [] [] [] [] | + spotmachine | [] [] [] [] [] | + sudo | [] [] [] [] [] [] | + sudoers | [] [] [] [] [] [] | + sysstat | [] [] [] [] [] [] | + tar | [] [] [] [] [] [] [] | + texinfo | [] [] [] [] [] | + texinfo_document | [] [] [] [] | + tigervnc | [] [] [] [] [] [] | + tin | [] [] [] [] | + tin-man | [] | + tracgoogleappsa... | [] [] [] [] [] | + trader | [] [] [] [] [] [] | + util-linux | [] [] [] [] | + ve | [] [] [] [] [] | + vice | () () () | + vmm | [] [] | + vorbis-tools | [] [] [] [] | + wastesedge | [] | + wcd | [] [] [] [] | + wcd-man | [] | + wdiff | [] [] [] [] [] [] [] | + wget | [] [] [] [] [] [] | + wyslij-po | [] [] [] [] | + xboard | [] [] [] [] | + xdg-user-dirs | [] [] [] [] [] [] [] [] [] [] | + xkeyboard-config | [] [] [] [] [] [] [] | + +--------------------------------------------------+ + da de el en en_GB en_ZA eo es et eu fa fi fr + 119 131 32 1 6 0 94 95 22 13 4 102 139 + + ga gd gl gu he hi hr hu hy ia id is it ja ka kk + +-------------------------------------------------+ + a2ps | [] [] [] [] | + aegis | [] | + anubis | [] [] [] [] | + aspell | [] [] [] [] [] | + bash | [] [] [] [] | + bfd | [] [] | + binutils | [] [] [] | + bison | [] | + bison-runtime | [] [] [] [] [] [] [] [] | + buzztrax | | + ccd2cue | [] | + ccide | [] [] | + cflow | [] [] [] | + clisp | | + coreutils | [] [] | + cpio | [] [] [] [] [] [] | + cppi | [] [] [] [] [] | + cpplib | [] [] | + cryptsetup | [] | + datamash | | + denemo | [] | + dfarc | [] [] [] | + dialog | [] [] [] [] [] [] [] [] [] [] | + dico | | + diffutils | [] [] [] [] | + dink | [] | + direvent | [] | + doodle | [] [] | + dos2unix | [] [] | + dos2unix-man | | + e2fsprogs | [] [] | + enscript | [] [] [] | + exif | [] [] [] [] [] [] | + fetchmail | [] [] [] | + findutils | [] [] [] [] [] [] [] | + flex | [] | + freedink | [] [] [] [] | + fusionforge | | + gas | [] | + gawk | [] () [] | + gcal | | + gcc | | + gdbm | | + gettext-examples | [] [] [] [] [] [] [] | + gettext-runtime | [] [] [] [] [] [] [] | + gettext-tools | [] [] [] | + gjay | [] | + glunarclock | [] [] [] [] [] [] | + gnubiff | [] [] () | + gnubik | [] [] [] | + gnucash | () () () () () | + gnuchess | | + gnulib | [] [] [] [] [] | + gnunet | | + gnunet-gtk | | + gold | [] [] | + gphoto2 | [] [] [] [] | + gprof | [] [] [] [] | + gramadoir | [] [] [] | + grep | [] [] [] [] [] [] [] | + grub | [] [] [] | + gsasl | [] [] [] [] [] | + gss | [] [] [] [] [] | + gst-plugins-bad | [] [] [] | + gst-plugins-base | [] [] [] [] | + gst-plugins-good | [] [] [] [] [] [] | + gst-plugins-ugly | [] [] [] [] [] [] | + gstreamer | [] [] [] [] [] | + gtick | [] [] [] [] [] | + gtkam | [] [] [] [] [] | + gtkspell | [] [] [] [] [] [] [] [] [] [] | + guix | | + guix-packages | | + gutenprint | [] [] [] | + hello | [] [] [] [] [] | + help2man | [] [] [] | + help2man-texi | | + hylafax | [] | + idutils | [] [] | + iso_15924 | [] [] [] [] [] [] | + iso_3166 | [] [] [] [] [] [] [] [] [] [] [] [] [] | + iso_3166_2 | [] [] | + iso_4217 | [] [] [] [] [] [] | + iso_639 | [] [] [] [] [] [] [] [] [] | + iso_639_3 | [] [] | + iso_639_5 | | + jwhois | [] [] [] [] | + kbd | [] [] [] | + klavaro | [] [] [] [] [] | + ld | [] [] [] [] | + leafpad | [] [] [] [] [] [] [] () | + libc | [] [] [] [] [] | + libexif | [] | + libextractor | | + libgnutls | [] | + libgphoto2 | [] [] | + libgphoto2_port | [] [] | + libgsasl | [] [] [] [] | + libiconv | [] [] [] [] [] [] [] | + libidn | [] [] [] [] | + liferea | [] [] [] [] [] | + lilypond | [] | + lordsawar | | + lprng | [] | + lynx | [] [] [] [] | + m4 | [] [] [] [] [] | + mailfromd | | + mailutils | | + make | [] [] [] [] | + man-db | [] [] | + man-db-manpages | [] [] | + midi-instruments | [] [] [] [] [] [] [] [] [] | + minicom | [] [] [] | + mkisofs | [] [] | + myserver | [] | + nano | [] [] [] [] [] [] | + opcodes | [] [] [] | + parted | [] [] [] [] [] | + pies | | + pnmixer | [] [] | + popt | [] [] [] [] [] [] [] [] [] [] | + procps-ng | | + procps-ng-man | | + psmisc | [] [] [] [] | + pspp | [] [] | + pushover | [] | + pwdutils | [] | + pyspread | | + radius | [] | + recode | [] [] [] [] [] [] [] | + recutils | | + rpm | [] | + rush | [] | + sarg | | + sed | [] [] [] [] [] [] [] | + sharutils | | + shishi | | + skribilo | [] | + solfege | [] [] | + solfege-manual | | + spotmachine | | + sudo | [] [] [] [] | + sudoers | [] [] [] | + sysstat | [] [] [] [] | + tar | [] [] [] [] [] [] | + texinfo | [] [] [] | + texinfo_document | [] [] [] | + tigervnc | | + tin | | + tin-man | | + tracgoogleappsa... | [] [] [] [] | + trader | [] [] | + util-linux | [] | + ve | [] | + vice | () () | + vmm | | + vorbis-tools | [] [] | + wastesedge | [] | + wcd | | + wcd-man | | + wdiff | [] [] [] | + wget | [] [] [] [] | + wyslij-po | [] [] [] | + xboard | | + xdg-user-dirs | [] [] [] [] [] [] [] [] [] [] [] [] [] [] | + xkeyboard-config | [] [] [] [] [] [] | + +-------------------------------------------------+ + ga gd gl gu he hi hr hu hy ia id is it ja ka kk + 35 2 47 4 8 2 60 71 2 6 81 11 87 57 0 3 + + kn ko ku ky lg lt lv mk ml mn mr ms mt nb ne nl + +--------------------------------------------------+ + a2ps | [] [] | + aegis | [] | + anubis | [] [] [] | + aspell | [] [] | + bash | [] [] | + bfd | | + binutils | | + bison | [] | + bison-runtime | [] [] [] [] [] [] | + buzztrax | | + ccd2cue | | + ccide | [] [] | + cflow | [] | + clisp | [] | + coreutils | [] [] | + cpio | [] | + cppi | | + cpplib | [] | + cryptsetup | [] | + datamash | [] [] | + denemo | | + dfarc | [] [] | + dialog | [] [] [] [] [] [] | + dico | | + diffutils | [] [] [] | + dink | [] | + direvent | [] | + doodle | [] | + dos2unix | [] [] | + dos2unix-man | [] | + e2fsprogs | [] | + enscript | [] | + exif | [] [] [] | + fetchmail | [] | + findutils | [] [] | + flex | [] | + freedink | [] [] | + fusionforge | | + gas | | + gawk | [] | + gcal | | + gcc | | + gdbm | | + gettext-examples | [] [] [] [] [] [] | + gettext-runtime | [] [] [] | + gettext-tools | [] | + gjay | | + glunarclock | [] [] | + gnubiff | [] | + gnubik | [] [] | + gnucash | () () () () () () () [] | + gnuchess | [] [] | + gnulib | [] | + gnunet | | + gnunet-gtk | | + gold | | + gphoto2 | [] | + gprof | [] [] | + gramadoir | [] | + grep | [] [] | + grub | [] [] [] | + gsasl | [] | + gss | | + gst-plugins-bad | [] [] [] | + gst-plugins-base | [] [] [] | + gst-plugins-good | [] [] [] [] | + gst-plugins-ugly | [] [] [] [] [] | + gstreamer | [] [] [] | + gtick | [] | + gtkam | [] [] | + gtkspell | [] [] [] [] [] [] [] | + guix | | + guix-packages | | + gutenprint | [] | + hello | [] [] [] | + help2man | [] | + help2man-texi | | + hylafax | [] | + idutils | [] | + iso_15924 | () [] [] | + iso_3166 | [] [] [] () [] [] [] [] [] [] | + iso_3166_2 | () [] | + iso_4217 | () [] [] [] | + iso_639 | [] [] () [] [] [] [] | + iso_639_3 | [] () [] | + iso_639_5 | () | + jwhois | [] [] | + kbd | [] | + klavaro | [] [] | + ld | | + leafpad | [] [] [] [] [] | + libc | [] [] | + libexif | [] | + libextractor | [] | + libgnutls | [] [] | + libgphoto2 | [] | + libgphoto2_port | [] | + libgsasl | [] | + libiconv | [] [] | + libidn | [] | + liferea | [] [] [] | + lilypond | [] | + lordsawar | | + lprng | | + lynx | [] | + m4 | [] | + mailfromd | | + mailutils | | + make | [] [] | + man-db | [] | + man-db-manpages | [] | + midi-instruments | [] [] [] [] [] [] [] | + minicom | [] | + mkisofs | [] | + myserver | | + nano | [] [] [] | + opcodes | [] | + parted | [] [] | + pies | | + pnmixer | [] | + popt | [] [] [] [] [] | + procps-ng | | + procps-ng-man | | + psmisc | [] | + pspp | [] [] | + pushover | | + pwdutils | [] | + pyspread | | + radius | [] | + recode | [] [] | + recutils | [] | + rpm | [] | + rush | [] | + sarg | | + sed | [] [] | + sharutils | [] | + shishi | | + skribilo | | + solfege | [] [] | + solfege-manual | [] | + spotmachine | [] | + sudo | [] [] [] | + sudoers | [] [] [] | + sysstat | [] [] | + tar | [] [] [] | + texinfo | [] | + texinfo_document | [] | + tigervnc | [] | + tin | | + tin-man | | + tracgoogleappsa... | [] [] [] | + trader | [] | + util-linux | [] | + ve | [] | + vice | [] | + vmm | [] | + vorbis-tools | [] | + wastesedge | [] | + wcd | [] | + wcd-man | [] | + wdiff | [] | + wget | [] [] | + wyslij-po | [] | + xboard | [] | + xdg-user-dirs | [] [] [] [] [] [] [] [] [] [] [] | + xkeyboard-config | [] [] [] | + +--------------------------------------------------+ + kn ko ku ky lg lt lv mk ml mn mr ms mt nb ne nl + 5 15 4 6 0 13 23 3 3 3 4 11 2 42 1 125 + + nn or pa pl ps pt pt_BR ro ru rw sk sl sq sr + +------------------------------------------------+ + a2ps | [] [] [] [] [] [] [] | + aegis | [] [] | + anubis | [] [] [] | + aspell | [] [] [] [] [] [] [] | + bash | [] [] [] [] [] [] | + bfd | [] [] | + binutils | [] [] | + bison | [] [] [] | + bison-runtime | [] [] [] [] [] [] [] [] | + buzztrax | [] | + ccd2cue | [] [] | + ccide | [] [] [] | + cflow | [] [] [] | + clisp | [] | + coreutils | [] [] [] [] | + cpio | [] [] [] | + cppi | [] [] [] | + cpplib | [] [] [] | + cryptsetup | [] [] [] | + datamash | [] [] | + denemo | | + dfarc | [] [] [] | + dialog | [] [] [] [] [] [] [] | + dico | [] | + diffutils | [] [] [] | + dink | | + direvent | [] [] [] | + doodle | [] [] | + dos2unix | [] [] [] [] | + dos2unix-man | [] [] | + e2fsprogs | [] | + enscript | [] [] [] [] [] [] | + exif | [] [] [] [] [] [] | + fetchmail | [] [] [] | + findutils | [] [] [] [] [] [] | + flex | [] [] [] [] [] | + freedink | [] [] [] [] [] | + fusionforge | | + gas | | + gawk | [] | + gcal | | + gcc | | + gdbm | [] [] [] | + gettext-examples | [] [] [] [] [] [] [] [] | + gettext-runtime | [] [] [] [] [] [] [] [] [] | + gettext-tools | [] [] [] [] [] [] [] | + gjay | [] | + glunarclock | [] [] [] [] [] [] | + gnubiff | [] | + gnubik | [] [] [] [] | + gnucash | () () () () () [] | + gnuchess | [] [] | + gnulib | [] [] [] [] [] | + gnunet | | + gnunet-gtk | | + gold | | + gphoto2 | [] [] [] [] [] | + gprof | [] [] [] [] | + gramadoir | [] [] | + grep | [] [] [] [] [] [] | + grub | [] [] [] [] [] | + gsasl | [] [] [] | + gss | [] [] [] [] | + gst-plugins-bad | [] [] [] [] [] | + gst-plugins-base | [] [] [] [] [] [] | + gst-plugins-good | [] [] [] [] [] [] [] | + gst-plugins-ugly | [] [] [] [] [] [] [] | + gstreamer | [] [] [] [] [] [] [] | + gtick | [] [] [] [] [] | + gtkam | [] [] [] [] [] [] | + gtkspell | [] [] [] [] [] [] [] [] [] | + guix | | + guix-packages | | + gutenprint | [] [] | + hello | [] [] [] [] [] [] | + help2man | [] [] [] [] | + help2man-texi | [] | + hylafax | | + idutils | [] [] [] | + iso_15924 | [] () [] [] [] [] | + iso_3166 | [] [] [] [] () [] [] [] [] [] [] [] [] | + iso_3166_2 | [] () [] | + iso_4217 | [] [] () [] [] [] [] [] | + iso_639 | [] [] [] () [] [] [] [] [] [] | + iso_639_3 | [] () | + iso_639_5 | () [] | + jwhois | [] [] [] [] | + kbd | [] [] | + klavaro | [] [] [] [] [] | + ld | | + leafpad | [] [] [] [] [] [] [] [] | + libc | [] [] [] | + libexif | [] () [] | + libextractor | [] | + libgnutls | [] | + libgphoto2 | [] | + libgphoto2_port | [] [] [] [] [] | + libgsasl | [] [] [] [] | + libiconv | [] [] [] [] [] | + libidn | [] [] [] | + liferea | [] [] [] [] () [] [] | + lilypond | | + lordsawar | | + lprng | [] | + lynx | [] [] | + m4 | [] [] [] [] [] | + mailfromd | [] | + mailutils | [] | + make | [] [] [] | + man-db | [] [] [] | + man-db-manpages | [] [] [] | + midi-instruments | [] [] [] [] [] [] [] [] | + minicom | [] [] [] [] | + mkisofs | [] [] [] | + myserver | [] [] | + nano | [] [] [] [] [] [] | + opcodes | | + parted | [] [] [] [] [] [] | + pies | [] | + pnmixer | [] | + popt | [] [] [] [] [] [] | + procps-ng | [] | + procps-ng-man | [] | + psmisc | [] [] [] [] | + pspp | [] [] | + pushover | | + pwdutils | [] | + pyspread | [] [] | + radius | [] [] | + recode | [] [] [] [] [] [] [] [] | + recutils | [] [] | + rpm | [] | + rush | [] [] [] | + sarg | [] [] | + sed | [] [] [] [] [] [] [] [] | + sharutils | [] [] [] | + shishi | [] [] | + skribilo | [] | + solfege | [] [] [] | + solfege-manual | [] [] | + spotmachine | [] [] | + sudo | [] [] [] [] [] [] | + sudoers | [] [] [] [] | + sysstat | [] [] [] [] [] | + tar | [] [] [] [] [] | + texinfo | [] [] [] | + texinfo_document | [] [] | + tigervnc | [] [] [] | + tin | [] | + tin-man | | + tracgoogleappsa... | [] [] [] [] | + trader | [] [] | + util-linux | [] [] | + ve | [] [] [] | + vice | | + vmm | | + vorbis-tools | [] [] [] | + wastesedge | | + wcd | | + wcd-man | | + wdiff | [] [] [] [] [] | + wget | [] [] [] [] [] | + wyslij-po | [] [] [] [] | + xboard | [] [] [] | + xdg-user-dirs | [] [] [] [] [] [] [] [] [] [] [] [] [] | + xkeyboard-config | [] [] [] [] | + +------------------------------------------------+ + nn or pa pl ps pt pt_BR ro ru rw sk sl sq sr + 7 3 6 114 1 12 88 32 82 3 40 45 7 101 + + sv sw ta te tg th tr uk ur vi wa wo zh_CN + +----------------------------------------------+ + a2ps | [] [] [] [] [] | + aegis | [] | + anubis | [] [] [] [] | + aspell | [] [] [] [] [] | + bash | [] [] [] [] | + bfd | [] [] [] | + binutils | [] [] [] | + bison | [] [] [] [] | + bison-runtime | [] [] [] [] [] [] | + buzztrax | [] [] [] | + ccd2cue | [] [] [] | + ccide | [] [] [] [] | + cflow | [] [] [] [] | + clisp | | + coreutils | [] [] [] | + cpio | [] [] [] [] [] | + cppi | [] [] [] [] | + cpplib | [] [] [] [] [] | + cryptsetup | [] [] [] | + datamash | [] [] [] | + denemo | [] | + dfarc | [] [] | + dialog | [] [] [] [] [] [] | + dico | [] | + diffutils | [] [] [] [] [] | + dink | [] | + direvent | [] [] | + doodle | [] [] | + dos2unix | [] [] [] [] | + dos2unix-man | [] [] [] | + e2fsprogs | [] [] [] [] | + enscript | [] [] [] [] | + exif | [] [] [] [] [] | + fetchmail | [] [] [] [] | + findutils | [] [] [] [] [] | + flex | [] [] [] [] | + freedink | [] [] [] | + fusionforge | | + gas | [] | + gawk | [] [] [] | + gcal | [] [] [] | + gcc | [] | + gdbm | [] [] | + gettext-examples | [] [] [] [] [] | + gettext-runtime | [] [] [] [] [] | + gettext-tools | [] [] [] [] [] | + gjay | [] [] [] | + glunarclock | [] [] [] [] | + gnubiff | [] [] | + gnubik | [] [] [] [] | + gnucash | () () () () [] | + gnuchess | [] [] [] | + gnulib | [] [] [] [] | + gnunet | | + gnunet-gtk | | + gold | [] [] | + gphoto2 | [] [] [] [] | + gprof | [] [] [] [] | + gramadoir | [] [] [] | + grep | [] [] [] [] [] | + grub | [] [] [] [] | + gsasl | [] [] [] [] | + gss | [] [] [] | + gst-plugins-bad | [] [] [] [] [] | + gst-plugins-base | [] [] [] [] [] | + gst-plugins-good | [] [] [] [] [] | + gst-plugins-ugly | [] [] [] [] [] | + gstreamer | [] [] [] [] [] | + gtick | [] [] [] | + gtkam | [] [] [] [] | + gtkspell | [] [] [] [] [] [] [] | + guix | | + guix-packages | | + gutenprint | [] [] [] [] | + hello | [] [] [] [] [] [] | + help2man | [] [] [] | + help2man-texi | [] | + hylafax | [] | + idutils | [] [] [] | + iso_15924 | [] () [] [] () [] | + iso_3166 | [] [] () [] [] () [] [] | + iso_3166_2 | () [] [] () [] | + iso_4217 | [] () [] [] () [] | + iso_639 | [] [] [] () [] [] () [] [] | + iso_639_3 | [] () [] [] () | + iso_639_5 | () [] () | + jwhois | [] [] [] [] | + kbd | [] [] [] [] | + klavaro | [] [] [] [] [] [] | + ld | [] [] [] [] [] | + leafpad | [] [] [] [] [] [] | + libc | [] [] [] [] [] | + libexif | [] [] () | + libextractor | [] [] | + libgnutls | [] [] [] [] | + libgphoto2 | [] [] [] | + libgphoto2_port | [] [] [] [] | + libgsasl | [] [] [] [] | + libiconv | [] [] [] [] [] | + libidn | () [] [] [] | + liferea | [] [] [] [] [] | + lilypond | [] | + lordsawar | | + lprng | [] | + lynx | [] [] [] [] | + m4 | [] [] [] | + mailfromd | [] [] | + mailutils | [] | + make | [] [] [] [] | + man-db | [] [] [] | + man-db-manpages | [] [] | + midi-instruments | [] [] [] [] [] [] | + minicom | [] [] | + mkisofs | [] [] [] | + myserver | [] | + nano | [] [] [] [] | + opcodes | [] [] [] | + parted | [] [] [] [] [] | + pies | [] [] | + pnmixer | [] [] [] | + popt | [] [] [] [] [] [] [] | + procps-ng | [] [] | + procps-ng-man | [] | + psmisc | [] [] [] [] | + pspp | [] [] [] | + pushover | [] | + pwdutils | [] [] | + pyspread | [] | + radius | [] [] | + recode | [] [] [] [] | + recutils | [] [] [] | + rpm | [] [] [] [] | + rush | [] [] | + sarg | | + sed | [] [] [] [] [] | + sharutils | [] [] [] [] | + shishi | [] [] | + skribilo | [] [] | + solfege | [] [] [] [] | + solfege-manual | [] | + spotmachine | [] [] [] | + sudo | [] [] [] [] [] | + sudoers | [] [] [] [] | + sysstat | [] [] [] [] [] | + tar | [] [] [] [] [] | + texinfo | [] [] [] | + texinfo_document | [] | + tigervnc | [] [] [] | + tin | [] | + tin-man | | + tracgoogleappsa... | [] [] [] [] [] | + trader | [] | + util-linux | [] [] [] [] | + ve | [] [] [] [] | + vice | () () | + vmm | | + vorbis-tools | [] [] | + wastesedge | | + wcd | [] [] [] | + wcd-man | [] | + wdiff | [] [] [] [] | + wget | [] [] [] | + wyslij-po | [] [] | + xboard | [] [] | + xdg-user-dirs | [] [] [] [] [] [] [] [] | + xkeyboard-config | [] [] [] [] | + +----------------------------------------------+ + sv sw ta te tg th tr uk ur vi wa wo zh_CN + 106 1 4 3 0 13 51 115 1 125 7 1 100 + + zh_HK zh_TW + +-------------+ + a2ps | | 30 + aegis | | 9 + anubis | | 19 + aspell | | 29 + bash | [] | 23 + bfd | | 11 + binutils | | 12 + bison | [] | 18 + bison-runtime | [] | 38 + buzztrax | | 9 + ccd2cue | | 10 + ccide | | 17 + cflow | | 16 + clisp | | 10 + coreutils | | 18 + cpio | | 20 + cppi | | 17 + cpplib | [] | 19 + cryptsetup | | 14 + datamash | | 11 + denemo | | 5 + dfarc | | 17 + dialog | [] | 42 + dico | | 6 + diffutils | | 22 + dink | | 10 + direvent | | 11 + doodle | | 12 + dos2unix | [] | 18 + dos2unix-man | | 9 + e2fsprogs | | 15 + enscript | | 21 + exif | | 27 + fetchmail | | 19 + findutils | | 29 + flex | [] | 19 + freedink | | 24 + fusionforge | | 3 + gas | | 5 + gawk | | 13 + gcal | | 8 + gcc | | 2 + gdbm | | 10 + gettext-examples | [] [] | 40 + gettext-runtime | [] [] | 35 + gettext-tools | [] | 24 + gjay | | 9 + glunarclock | [] | 27 + gnubiff | | 9 + gnubik | | 19 + gnucash | () | 6 + gnuchess | | 11 + gnulib | | 23 + gnunet | | 1 + gnunet-gtk | | 1 + gold | | 7 + gphoto2 | [] | 19 + gprof | | 21 + gramadoir | | 14 + grep | [] | 31 + grub | | 21 + gsasl | [] | 19 + gss | | 17 + gst-plugins-bad | | 21 + gst-plugins-base | | 27 + gst-plugins-good | | 32 + gst-plugins-ugly | | 34 + gstreamer | [] | 32 + gtick | | 19 + gtkam | | 24 + gtkspell | [] [] | 48 + guix | | 2 + guix-packages | | 0 + gutenprint | | 15 + hello | [] | 30 + help2man | | 18 + help2man-texi | | 5 + hylafax | | 5 + idutils | | 14 + iso_15924 | [] | 23 + iso_3166 | [] [] | 58 + iso_3166_2 | | 9 + iso_4217 | [] [] | 28 + iso_639 | [] [] | 46 + iso_639_3 | | 10 + iso_639_5 | | 2 + jwhois | [] | 20 + kbd | | 17 + klavaro | | 30 + ld | [] | 15 + leafpad | [] | 39 + libc | [] | 24 + libexif | | 10 + libextractor | | 5 + libgnutls | | 13 + libgphoto2 | | 10 + libgphoto2_port | [] | 19 + libgsasl | | 18 + libiconv | [] | 29 + libidn | | 17 + liferea | | 29 + lilypond | | 11 + lordsawar | | 3 + lprng | | 3 + lynx | | 19 + m4 | [] | 22 + mailfromd | | 4 + mailutils | | 6 + make | | 19 + man-db | | 15 + man-db-manpages | | 10 + midi-instruments | [] | 43 + minicom | [] | 17 + mkisofs | | 13 + myserver | | 9 + nano | [] | 30 + opcodes | | 12 + parted | [] | 23 + pies | | 4 + pnmixer | | 9 + popt | [] | 36 + procps-ng | | 5 + procps-ng-man | | 4 + psmisc | [] | 22 + pspp | | 13 + pushover | | 6 + pwdutils | | 8 + pyspread | | 6 + radius | | 9 + recode | | 31 + recutils | | 10 + rpm | [] | 13 + rush | | 10 + sarg | | 4 + sed | [] | 35 + sharutils | | 13 + shishi | | 7 + skribilo | | 7 + solfege | | 21 + solfege-manual | | 9 + spotmachine | | 11 + sudo | | 26 + sudoers | | 22 + sysstat | | 23 + tar | [] | 30 + texinfo | | 17 + texinfo_document | | 13 + tigervnc | | 14 + tin | [] | 7 + tin-man | | 1 + tracgoogleappsa... | [] | 22 + trader | | 12 + util-linux | | 13 + ve | | 14 + vice | | 1 + vmm | | 3 + vorbis-tools | | 13 + wastesedge | | 3 + wcd | | 8 + wcd-man | | 3 + wdiff | [] | 23 + wget | | 21 + wyslij-po | | 14 + xboard | | 10 + xdg-user-dirs | [] [] | 68 + xkeyboard-config | [] | 28 + +-------------+ + 89 teams zh_HK zh_TW + 166 domains 7 42 2809 + + Some counters in the preceding matrix are higher than the number of +visible blocks let us expect. This is because a few extra PO files are +used for implementing regional variants of languages, or language +dialects. + + For a PO file in the matrix above to be effective, the package to +which it applies should also have been internationalized and distributed +as such by its maintainer. There might be an observable lag between the +mere existence a PO file and its wide availability in a distribution. + + If Jun 2014 seems to be old, you may fetch a more recent copy of this +'ABOUT-NLS' file on most GNU archive sites. The most up-to-date matrix +with full percentage details can be found at +'http://translationproject.org/extra/matrix.html'. + +1.5 Using 'gettext' in new packages +=================================== + +If you are writing a freely available program and want to +internationalize it you are welcome to use GNU 'gettext' in your +package. Of course you have to respect the GNU Lesser General Public +License which covers the use of the GNU 'gettext' library. This means +in particular that even non-free programs can use 'libintl' as a shared +library, whereas only free software can use 'libintl' as a static +library or use modified versions of 'libintl'. + + Once the sources are changed appropriately and the setup can handle +the use of 'gettext' the only thing missing are the translations. The +Free Translation Project is also available for packages which are not +developed inside the GNU project. Therefore the information given above +applies also for every other Free Software Project. Contact +'coordinator@translationproject.org' to make the '.pot' files available +to the translation teams. diff --git a/Makefile.am b/Makefile.am index 1606cc77..e08af61f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,21 +1,13 @@ -SUBDIRS = . tests +ACLOCAL_AMFLAGS = -I m4 -I rtmidi-m4 --install +SUBDIRS = po if MAKE_DOC SUBDIRS += doc endif - -lib_LTLIBRARIES = %D%/librtmidi.la -%C%_librtmidi_la_LDFLAGS = -no-undefined -export-dynamic -version-info @SO_VERSION@ -%C%_librtmidi_la_SOURCES = \ - %D%/RtMidi.cpp \ - %D%/rtmidi_c.cpp - -rtmidi_incdir = $(includedir)/rtmidi -rtmidi_inc_HEADERS = \ - %D%/RtMidi.h \ - %D%/rtmidi_c.h - -pkgconfigdatadir = $(libdir)/pkgconfig -pkgconfigdata_DATA = rtmidi.pc - -EXTRA_DIST = autogen.sh README.md msw - +RTMIDICONFIGRPATH = config/config.rpath +DLLPROGRAMS = $(check_PROGRAMS) +check_PROGRAMS = +TESTS = +CLEANFILES = +DISTCLEANFILES = + +include %D%/Makefile.library diff --git a/Makefile.library b/Makefile.library new file mode 100644 index 00000000..cb1b983e --- /dev/null +++ b/Makefile.library @@ -0,0 +1,59 @@ +# -*- makefile-automake -*- +RTMIDITESTCXXFLAGS = $(EXTRA_RTMIDITESTCXXFLAGS) -I$(top_srcdir)/%D% +RTMIDILIBRARYNAME = %D%/librtmidi@rtmidi_suffix@.la +RTMIDITESTLDFLAGS = $(EXTRA_RTMIDITESTLDFLAGS) $(RTMIDILIBRARYNAME) +lib_LTLIBRARIES += %D%/librtmidi@rtmidi_suffix@.la +DLLPROGRAMS += %D%/librtmidi@rtmidi_suffix@.* +DLLPROGRAMS += %D%/.libs/librtmidi@rtmidi_suffix@.* + +DISTCLEANFILES += \ + %D%/rtmidi@rtmidi_suffix@.pc \ + %D%/rtmidi@rtmidi_suffix@-config + +%C%_librtmidi@rtmidi_suffix@_la_LDFLAGS = \ + $(RTMIDI_LIBS) \ + $(LDADD) \ + -export-dynamic \ + -version-info @RTMIDI_SO_VERSION@ \ + -no-undefined \ + -v +%C%_librtmidi@rtmidi_suffix@_la_CXXFLAGS = \ + $(rtmidi_visibility) \ + $(CXXFLAGS) \ + $(CPPFLAGS) \ + $(RTMIDI_API) \ + $(RTMIDI_LIB_CFLAGS) \ + -DRTMIDI_NO_WARN_DEPRECATED +%C%_librtmidi@rtmidi_suffix@_la_SOURCES = \ + %D%/RtMidi.cpp \ + %D%/RtMidi.h \ + %D%/rtmidi_c.cpp \ + %D%/rtmidi_c.h + +%C%_librtmidi@rtmidi_suffix@_ladir = $(includedir)/rtmidi@rtmidi_suffix@ +%C%_librtmidi@rtmidi_suffix@_la_HEADERS = \ + %D%/RtMidi.h \ + %D%/rtmidi_c.h + + +EXTRA_DIST += \ + $(RTMIDICONFIGRPATH) \ + %D%/m4/ChangeLog \ + %D%/rtmidi-config.in \ + %D%/rtmidi.pc.in \ + %D%/autogen.sh \ + %D%/README.md \ + %D%/msw + +pkgconfigdatadir = $(libdir)/pkgconfig +pkgconfigdata_DATA = %D%/rtmidi@rtmidi_suffix@.pc + +%D%/rtmidi@rtmidi_suffix@.pc: %D%/rtmidi.pc + if cmp "$<" "$@" ; then : ; else cp "$<" "$@" ; fi +%D%/rtmidi@rtmidi_suffix@-config: %D%/rtmidi-config + if cmp "$<" "$@" ; then : ; else cp -a "$<" "$@" ; fi + + + + +include %D%/tests/Makefile.am diff --git a/README.md b/README.md index 2ec68cb0..1d9a603a 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,82 @@ -# RtMidi +[![Build Status](https://travis-ci.org/keinstein/rtmidi.svg?branch=master-ts)](https://travis-ci.org/keinstein/rtmidi)[![Build status](https://ci.appveyor.com/api/projects/status/ac98q210qscfjayk/branch/master-ts?svg=true)](https://ci.appveyor.com/project/keinstein/rtmidi/branch/travis-tests) -[![Build Status](https://travis-ci.org/thestk/rtmidi.svg?branch=master)](https://travis-ci.org/thestk/rtmidi) +Extended RtMidi fork for Mutabor, GUI based Software, and saving MIDI connections +================================================================================= A set of C++ classes that provide a common API for realtime MIDI input/output across Linux (ALSA & JACK), Macintosh OS X (CoreMIDI & JACK) and Windows (Multimedia). By Gary P. Scavone, 2003-2017. +Forked by Tobias Schlemmer, 2014-2018. + +This fork has been started because the original RtMidi did not work +for Mutabor and ALSA. Mutabor has a two-step activation scheme. The +MIDI configuration is set up while no connections are active. After +compiling the tuning logic all MIDI interfaces are activated at +once. Each MIDI connection that RtMidi makes is reported as MIDI +device again by RtMidi. This leads to a renumbering of all MIDI +endpoints which have a higher ALSA device ID than the current RtMidi +instance. The result is unexpected behaviour. + +As a side effect this library uses a different namespace and is more +C++-ish than the original RtMidi. + +Incompatible changes against upstream +------------------------------------- + +- The old API has been deprecated as there is no way to rely on + consecutive port numbers. It is always the responsibility of the + end user not to change the MIDI configuration between certain points + in the execution path of the library. Obviously they usually lack the + necessary information for that Currently, it is still available, but a + compiler warning is generated if applicable + +- `__MACOSX_CORE__` has been renamed to `__MACOSX_COREMIDI__` + +- The classes of RtMidi now reside in the namespace rtmidi. +- The beginning letters “Rt” are dropped from the names +- For easy adoption of the new interface wrappers for the old API are provided. +- The library uses backend provided port descriptors, now. This provides a more reliable port handling for changing environments (See below). + +- The way MIDI devices are enumerated has changed. The old way, using the ordinal number of MIDI devices works only in cases where MIDI devices are not added or removed during the program session. When a virtual MIDI port or USB MIDI device is added or removed the ordinal number of each of the other devices may change. + + Suppose your computer has the following list of MIDI devices. + 1. MIDI loopback device + 2. Removable USB MIDI device + 3. Another MIDI device + 4. Software MIDI Synth + 5. A virtual MIDI port + + After the software obtained this list, your friend remembers that he + must catch the next bus and unplugs his removable USB MIDI device. + The software does not recognize this removal and keeps the above list, + while the system has a new one: + 1. MIDI loopback device + 2. Another MIDI device + 3. Software MIDI Synth + 4. A virtual MIDI port + + Somehow you told the software to use the Software MIDI Synth. The + program stores the number 4 as it obtained during enumeration of the + MIDI devices. Instead of playing the music using your sound card it + sends the music to a different port. + + While this behaviour is only annoying in interactive environments it + results in unpredictable behaviour if several ports are opened at + once. E.g. in the ALSA backend every opened port results in an + aditional newly created virtual port. + + In order to avoid such problems, most backends identify ports (except + WinMM) by different data structures. + + The current version introduces a new class \ref rtmidi::PortDescriptor + in order to hide this implementation detail from the user code. In + order to avoid the above problems these are retrieved at once using \ref rtmidi::Midi::getPortList. + This new feature also allows to retreive the port descriptor of an open device using + \ref rtmidi::Midi::getDescriptor. The latter can be used to obtain + + +Compilation +----------- This distribution of RtMidi contains the following: @@ -15,9 +87,10 @@ On Unix systems, type `./configure` in the top level directory, then `make` in t If you checked out the code from git, please run `./autogen.sh` before `./configure`. -## Overview +Overview +-------- -RtMidi is a set of C++ classes (RtMidiIn, RtMidiOut, and API specific classes) that provide a common API (Application Programming Interface) for realtime MIDI input/output across Linux (ALSA, JACK), Macintosh OS X (CoreMIDI, JACK), and Windows (Multimedia Library) operating systems. RtMidi significantly simplifies the process of interacting with computer MIDI hardware and software. It was designed with the following goals: +RtMidi is a set of C++ classes (rtmidi::MidiIn, rtmidi::MidiOut, and API specific classes) that provide a common API (Application Programming Interface) for realtime MIDI input/output across Linux (ALSA, JACK), Macintosh OS X (CoreMIDI, JACK), and Windows (Multimedia Library) operating systems. RtMidi significantly simplifies the process of interacting with computer MIDI hardware and software. It was designed with the following goals: - object oriented C++ design - simple, common API across all supported platforms @@ -26,21 +99,25 @@ RtMidi is a set of C++ classes (RtMidiIn, RtMidiOut, and API specific classes) t MIDI input and output functionality are separated into two classes, RtMidiIn and RtMidiOut. Each class instance supports only a single MIDI connection. RtMidi does not provide timing functionality (i.e., output messages are sent immediately). Input messages are timestamped with delta times in seconds (via a double floating point type). MIDI data is passed to the user as raw bytes using an std::vector. -## Windows +Windows +------- In some cases, for example to use RtMidi with GS Synth, it may be necessary for your program to call CoInitializeEx and CoUninitialize on entry to and exit from the thread that uses RtMidi. -## Further reading +Further reading +--------------- -For complete documentation on RtMidi, see the doc directory of the distribution or surf to http://music.mcgill.ca/~gary/rtmidi/. +For complete documentation on the original RtMidi, see the doc directory of the distribution or surf to http://music.mcgill.ca/~gary/rtmidi/. -## Legal and ethical +Legal and ethical +----------------- The RtMidi license is similar to the MIT License, with the added *feature* that modifications be sent to the developer. RtMidi: realtime MIDI i/o C++ classes Copyright (c) 2003-2017 Gary P. Scavone + Forked by Tobias Schlemmer, 2014-2018. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files diff --git a/RtMidi.cpp b/RtMidi.cpp index 61dd55fd..b7113ad7 100644 --- a/RtMidi.cpp +++ b/RtMidi.cpp @@ -1,49 +1,57 @@ /**********************************************************************/ -/*! \class RtMidi - \brief An abstract base class for realtime MIDI input/output. +/*! \class Midi + \brief An abstract base class for realtime MIDI input/output. - This class implements some common functionality for the realtime - MIDI input/output subclasses RtMidiIn and RtMidiOut. + This class implements some common functionality for the realtime + MIDI input/output subclasses MidiIn and MidiOut. RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/ - RtMidi: realtime MIDI i/o C++ classes - Copyright (c) 2003-2017 Gary P. Scavone - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - Any person wishing to distribute modifications to the Software is - asked to send the modifications to the original developer so that - they can be incorporated into the canonical version. This is, - however, not a binding provision of this license. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + Midi: realtime MIDI i/o C++ classes + Copyright (c) 2003-2017 Gary P. Scavone + Forked by Tobias Schlemmer, 2014-2018. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + asked to send the modifications to the original developer so that + they can be incorporated into the canonical version. This is, + however, not a binding provision of this license. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**********************************************************************/ #include "RtMidi.h" #include +#include +#include +#include +#include +#ifndef RTMIDI_FALLTHROUGH +#define RTMIDI_FALLTHROUGH +#endif -#if defined(__MACOSX_CORE__) - #if TARGET_OS_IPHONE - #define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime - #define AudioConvertHostTimeToNanos CAHostTimeBase::ConvertToNanos - #endif +#if defined(__MACOSX_COREMIDI__) +#if TARGET_OS_IPHONE +#define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime +#define AudioConvertHostTimeToNanos CAHostTimeBase::ConvertToNanos +#endif #endif // Default for Windows is to add an identifier to the port names; this @@ -56,20 +64,28 @@ // // **************************************************************** // -#if !defined(__LINUX_ALSA__) && !defined(__UNIX_JACK__) && !defined(__MACOSX_CORE__) && !defined(__WINDOWS_MM__) +#if !defined(__LINUX_ALSA__) && !defined(__UNIX_JACK__) && !defined(__MACOSX_COREMIDI__) && !defined(__WINDOWS_MM__) #define __RTMIDI_DUMMY__ #endif -#if defined(__MACOSX_CORE__) +RTMIDI_NAMESPACE_START +#if defined(__MACOSX_COREMIDI__) +RTMIDI_NAMESPACE_END +struct MIDIPacketList; +RTMIDI_NAMESPACE_START class MidiInCore: public MidiInApi { public: MidiInCore( const std::string &clientName, unsigned int queueSizeLimit ); ~MidiInCore( void ); - RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; }; + ApiType getCurrentApi( void ) throw() { return rtmidi::MACOSX_CORE; }; + bool hasVirtualPorts() const { return true; } void openPort( unsigned int portNumber, const std::string &portName ); void openVirtualPort( const std::string &portName ); + void openPort( const PortDescriptor & port, const std::string &portName); + Pointer getDescriptor(bool local=false); + PortList getPortList(int capabilities); void closePort( void ); void setClientName( const std::string &clientName ); void setPortName( const std::string &portName ); @@ -77,7 +93,12 @@ class MidiInCore: public MidiInApi std::string getPortName( unsigned int portNumber ); protected: + static void midiInputCallback( const MIDIPacketList *list, + void *procRef, + void */*srcRef*/) throw(); void initialize( const std::string& clientName ); + template + friend class CoreSequencer; }; class MidiOutCore: public MidiOutApi @@ -85,9 +106,13 @@ class MidiOutCore: public MidiOutApi public: MidiOutCore( const std::string &clientName ); ~MidiOutCore( void ); - RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; }; + ApiType getCurrentApi( void ) throw() { return rtmidi::MACOSX_CORE; }; + bool hasVirtualPorts() const { return true; } void openPort( unsigned int portNumber, const std::string &portName ); void openVirtualPort( const std::string &portName ); + void openPort( const PortDescriptor & port, const std::string &portName); + Pointer getDescriptor(bool local=false); + PortList getPortList(int capabilities); void closePort( void ); void setClientName( const std::string &clientName ); void setPortName( const std::string &portName ); @@ -108,9 +133,13 @@ class MidiInJack: public MidiInApi public: MidiInJack( const std::string &clientName, unsigned int queueSizeLimit ); ~MidiInJack( void ); - RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; }; + ApiType getCurrentApi( void ) throw() { return rtmidi::UNIX_JACK; }; + bool hasVirtualPorts() const { return true; } void openPort( unsigned int portNumber, const std::string &portName ); void openVirtualPort( const std::string &portName ); + void openPort( const PortDescriptor & port, const std::string &portName); + Pointer getDescriptor(bool local=false); + PortList getPortList(int capabilities); void closePort( void ); void setClientName( const std::string &clientName ); void setPortName( const std::string &portName); @@ -129,9 +158,13 @@ class MidiOutJack: public MidiOutApi public: MidiOutJack( const std::string &clientName ); ~MidiOutJack( void ); - RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; }; + ApiType getCurrentApi( void ) throw() { return rtmidi::UNIX_JACK; }; + bool hasVirtualPorts() const { return true; } void openPort( unsigned int portNumber, const std::string &portName ); void openVirtualPort( const std::string &portName ); + void openPort( const PortDescriptor & port, const std::string &portName); + Pointer getDescriptor(bool local=false); + PortList getPortList(int capabilities); void closePort( void ); void setClientName( const std::string &clientName ); void setPortName( const std::string &portName); @@ -155,9 +188,13 @@ class MidiInAlsa: public MidiInApi public: MidiInAlsa( const std::string &clientName, unsigned int queueSizeLimit ); ~MidiInAlsa( void ); - RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; }; + ApiType getCurrentApi( void ) throw() { return rtmidi::LINUX_ALSA; }; + bool hasVirtualPorts() const { return true; } void openPort( unsigned int portNumber, const std::string &portName ); void openVirtualPort( const std::string &portName ); + void openPort( const PortDescriptor & port, const std::string &portName); + Pointer getDescriptor(bool local=false); + PortList getPortList(int capabilities); void closePort( void ); void setClientName( const std::string &clientName ); void setPortName( const std::string &portName); @@ -165,7 +202,9 @@ class MidiInAlsa: public MidiInApi std::string getPortName( unsigned int portNumber ); protected: - void initialize( const std::string& clientName ); + static void * alsaMidiHandler( void *ptr ) throw(); + void initialize( const std::string &clientName ); + friend struct AlsaMidiData; }; class MidiOutAlsa: public MidiOutApi @@ -173,9 +212,13 @@ class MidiOutAlsa: public MidiOutApi public: MidiOutAlsa( const std::string &clientName ); ~MidiOutAlsa( void ); - RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; }; + ApiType getCurrentApi( void ) throw() { return rtmidi::LINUX_ALSA; }; + bool hasVirtualPorts() const { return true; } void openPort( unsigned int portNumber, const std::string &portName ); void openVirtualPort( const std::string &portName ); + void openPort( const PortDescriptor & port, const std::string &portName); + Pointer getDescriptor(bool local=false); + PortList getPortList(int capabilities); void closePort( void ); void setClientName( const std::string &clientName ); void setPortName( const std::string &portName); @@ -196,9 +239,13 @@ class MidiInWinMM: public MidiInApi public: MidiInWinMM( const std::string &clientName, unsigned int queueSizeLimit ); ~MidiInWinMM( void ); - RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; }; + ApiType getCurrentApi( void ) throw() { return rtmidi::WINDOWS_MM; }; + bool hasVirtualPorts() const { return false; } void openPort( unsigned int portNumber, const std::string &portName ); void openVirtualPort( const std::string &portName ); + void openPort( const PortDescriptor & port, const std::string &portName); + Pointer getDescriptor(bool local=false); + PortList getPortList(int capabilities); void closePort( void ); void setClientName( const std::string &clientName ); void setPortName( const std::string &portName ); @@ -207,6 +254,7 @@ class MidiInWinMM: public MidiInApi protected: void initialize( const std::string& clientName ); + friend struct WinMMCallbacks; }; class MidiOutWinMM: public MidiOutApi @@ -214,9 +262,13 @@ class MidiOutWinMM: public MidiOutApi public: MidiOutWinMM( const std::string &clientName ); ~MidiOutWinMM( void ); - RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; }; + ApiType getCurrentApi( void ) throw() { return rtmidi::WINDOWS_MM; }; + bool hasVirtualPorts() const { return false; } void openPort( unsigned int portNumber, const std::string &portName ); void openVirtualPort( const std::string &portName ); + void openPort( const PortDescriptor & port, const std::string &portName); + Pointer getDescriptor(bool local=false); + PortList getPortList(int capabilities); void closePort( void ); void setClientName( const std::string &clientName ); void setPortName( const std::string &portName ); @@ -232,13 +284,27 @@ class MidiOutWinMM: public MidiOutApi #if defined(__RTMIDI_DUMMY__) +#define RTMIDI_CLASSNAME "MidiInDummy" class MidiInDummy: public MidiInApi { public: - MidiInDummy( const std::string &/*clientName*/, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { errorString_ = "MidiInDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); } - RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; } + MidiInDummy( const std::string &/*clientName*/, unsigned int queueSizeLimit ) + : MidiInApi( queueSizeLimit ) { + error( RTMIDI_ERROR(rtmidi_gettext("No valid MIDI interfaces. I'm using a dummy input interface that never receives anything."), + Error::WARNING) ); + } + ApiType getCurrentApi( void ) throw() { return rtmidi::DUMMY; } + bool hasVirtualPorts() const { return false; } void openPort( unsigned int /*portNumber*/, const std::string &/*portName*/ ) {} void openVirtualPort( const std::string &/*portName*/ ) {} + void openPort( const PortDescriptor & /* port */, + const std::string &/* portName */) {} + Pointer getDescriptor(bool /* local=false */) { + return 0; + } + PortList getPortList(int /* capabilities */) { + return PortList(); + } void closePort( void ) {} void setClientName( const std::string &/*clientName*/ ) {}; void setPortName( const std::string &/*portName*/ ) {}; @@ -252,10 +318,22 @@ class MidiInDummy: public MidiInApi class MidiOutDummy: public MidiOutApi { public: - MidiOutDummy( const std::string &/*clientName*/ ) { errorString_ = "MidiOutDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); } - RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; } + MidiOutDummy( const std::string &/*clientName*/ ) { + error( RTMIDI_ERROR(rtmidi_gettext("No valid MIDI interfaces. I'm using a dummy output interface that does nothing."), + Error::WARNING) ); + } + ApiType getCurrentApi( void ) throw() { return rtmidi::DUMMY; } + bool hasVirtualPorts() const { return false; } void openPort( unsigned int /*portNumber*/, const std::string &/*portName*/ ) {} void openVirtualPort( const std::string &/*portName*/ ) {} + void openPort( const PortDescriptor & /* port */, + const std::string &/* portName */) {} + Pointer getDescriptor(bool /* local=false */) { + return 0; + } + PortList getPortList(int /* capabilities */) { + return PortList(); + } void closePort( void ) {} void setClientName( const std::string &/*clientName*/ ) {}; void setPortName( const std::string &/*portName*/ ) {}; @@ -266,203 +344,383 @@ class MidiOutDummy: public MidiOutApi protected: void initialize( const std::string& /*clientName*/ ) {} }; +#undef RTMIDI_CLASSNAME #endif + //*********************************************************************// // RtMidi Definitions //*********************************************************************// -RtMidi :: RtMidi() - : rtapi_(0) -{ +#ifdef RTMIDI_GETTEXT +const char * rtmidi_gettext (const char * s) { + init_rtmidi_gettext(); + return dgettext("rtmidi",s); } -RtMidi :: ~RtMidi() -{ - delete rtapi_; - rtapi_ = 0; +void init_rtmidi_gettext() { + static bool initialized = false; + if (initialized) + return; + bindtextdomain("rtmidi",LOCALEDIR); + initialized = true; +} +#endif + +//! The constructor. +Error::Error( const char * message, + Type type, + const char * class_name, + const char * function_name, + const char * file_name, + int line_number, ...) throw():exception(), + classname(class_name), + function(function_name), + file(file_name), + line(line_number), + type_(type) +{ +#ifdef RTMIDI_GETTEXT + message = rtmidi_gettext(message); +#endif + std::va_list args; + va_start(args,line_number); + size_t length; + length = vsnprintf(NULL,0,message,args); + if (length > 0) { + message_.resize(length+1); + std::vsnprintf(&(message_[0]),length+1,message,args); + message_.resize(length); + } else { + const char * fmt = gettext_noopt("Error formatting the error string:\n'%s'\nFound in %s::%s at \n%s:%d"); +#ifdef RTMIDI_GETTEXT + fmt = rtmidi_gettext(fmt); +#endif + + length = snprintf(NULL,0,fmt,message,class_name,function_name,file_name,line); + if (length > 0) { + message_.resize(length+1); + snprintf(&(message_[0]),length+1,fmt,message,class_name,function_name,file_name,line); + message_.resize(length); + } else { + const char * msg + = gettext_noopt("Error: could not format the error message"); +#ifdef RTMIDI_GETTEXT + msg = rtmidi_gettext(msg); +#endif + message_ = msg; + } + } + va_end(args); + } -std::string RtMidi :: getVersion( void ) throw() +//*********************************************************************// +// Midi Definitions +//*********************************************************************// +std::string Midi :: getVersion( void ) throw() { return std::string( RTMIDI_VERSION ); } -void RtMidi :: getCompiledApi( std::vector &apis ) throw() +void Midi :: getCompiledApi( std::vector &apis, bool preferSystem ) throw() { apis.clear(); // The order here will control the order of RtMidi's API search in // the constructor. -#if defined(__MACOSX_CORE__) - apis.push_back( MACOSX_CORE ); + + if (!preferSystem) { + // first check software and network backends +#if defined(__UNIX_JACK__) + apis.push_back( rtmidi::UNIX_JACK ); #endif -#if defined(__LINUX_ALSA__) - apis.push_back( LINUX_ALSA ); + } + + // check OS provided backends +#if defined(__MACOSX_COREMIDI__) + apis.push_back( rtmidi::MACOSX_CORE ); #endif -#if defined(__UNIX_JACK__) - apis.push_back( UNIX_JACK ); +#if defined(__LINUX_ALSA__) + apis.push_back( rtmidi::LINUX_ALSA ); #endif #if defined(__WINDOWS_MM__) - apis.push_back( WINDOWS_MM ); + apis.push_back( rtmidi::WINDOWS_MM ); #endif + + if (preferSystem) { + // if we prefer OS provided backends, + // we should add the software backends, here. +#if defined(__UNIX_JACK__) + apis.push_back( rtmidi::UNIX_JACK ); +#endif + } + + // DUMMY is a no-backend class so we add it at + // the very end. #if defined(__RTMIDI_DUMMY__) - apis.push_back( RTMIDI_DUMMY ); + apis.push_back( rtmidi::DUMMY ); #endif } -void RtMidi :: setClientName( const std::string &clientName ) -{ - rtapi_->setClientName(clientName); -} -void RtMidi :: setPortName( const std::string &portName ) + +void Midi :: error(Error e) { - rtapi_->setPortName(portName); + + // use the callback if present. + if (rtapi_) { + rtapi_->error(e); + return; + } + + if ( e.getType() == Error::WARNING ) { + e.printMessage(); + } + else if ( e.getType() == Error::DEBUG_WARNING ) { +#if defined(__RTMIDI_DEBUG__) + e.printMessage(); +#endif + } + else { + e.printMessage(); + throw e; + } } //*********************************************************************// -// RtMidiIn Definitions +// MidiIn Definitions //*********************************************************************// - -void RtMidiIn :: openMidiApi( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit ) +#define RTMIDI_CLASSNAME "MidiIn" +void MidiIn :: openMidiApi( ApiType api ) { delete rtapi_; rtapi_ = 0; + try { + switch (api) { + case rtmidi::UNIX_JACK: #if defined(__UNIX_JACK__) - if ( api == UNIX_JACK ) - rtapi_ = new MidiInJack( clientName, queueSizeLimit ); + rtapi_ = new MidiInJack( clientName, queueSizeLimit ); #endif + break; + case rtmidi::LINUX_ALSA: #if defined(__LINUX_ALSA__) - if ( api == LINUX_ALSA ) - rtapi_ = new MidiInAlsa( clientName, queueSizeLimit ); + rtapi_ = new MidiInAlsa( clientName, queueSizeLimit ); #endif + break; + case rtmidi::WINDOWS_MM: #if defined(__WINDOWS_MM__) - if ( api == WINDOWS_MM ) - rtapi_ = new MidiInWinMM( clientName, queueSizeLimit ); + rtapi_ = new MidiInWinMM( clientName, queueSizeLimit ); #endif -#if defined(__MACOSX_CORE__) - if ( api == MACOSX_CORE ) - rtapi_ = new MidiInCore( clientName, queueSizeLimit ); + break; + case rtmidi::MACOSX_CORE: +#if defined(__MACOSX_COREMIDI__) + rtapi_ = new MidiInCore( clientName, queueSizeLimit ); #endif + break; + case rtmidi::DUMMY: #if defined(__RTMIDI_DUMMY__) - if ( api == RTMIDI_DUMMY ) - rtapi_ = new MidiInDummy( clientName, queueSizeLimit ); + rtapi_ = new MidiInDummy( clientName, queueSizeLimit ); #endif + break; + case rtmidi::ALL_API: + case rtmidi::UNSPECIFIED: + default: + break; + } + } catch (const Error & e) { + rtapi_ = 0; + throw; + } } -RTMIDI_DLL_PUBLIC RtMidiIn :: RtMidiIn( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit ) - : RtMidi() +MidiApiList MidiIn::queryApis; + +RTMIDI_DLL_PUBLIC MidiIn :: MidiIn( ApiType api, + const std::string &clientName, + unsigned int queueSize, + bool pfsystem ) + : Midi(&queryApis,pfsystem,clientName), + queueSizeLimit(queueSize) { - if ( api != UNSPECIFIED ) { + if ( api == rtmidi::ALL_API) { + if (!queryApis.empty()) { + rtapi_ = NULL; + return; + } + + std::vector< ApiType > apis; + getCompiledApi( apis ); + for ( unsigned int i=0; i apis; + std::vector< ApiType > apis; getCompiledApi( apis ); for ( unsigned int i=0; igetPortCount() ) break; } if ( rtapi_ ) return; - // It should not be possible to get here because the preprocessor - // definition __RTMIDI_DUMMY__ is automatically defined if no - // API-specific definitions are passed to the compiler. But just in - // case something weird happens, we'll throw an error. - std::string errorText = "RtMidiIn: no compiled API support found ... critical error!!"; - throw( RtMidiError( errorText, RtMidiError::UNSPECIFIED ) ); + // We may reach this point if the only API is JACK, + // but no JACK devices are found. + throw( RTMIDI_ERROR( gettext_noopt("No supported MIDI system has been found."), + Error::NO_DEVICES_FOUND ) ); } -RtMidiIn :: ~RtMidiIn() throw() +MidiIn :: ~MidiIn() throw() { } +#undef RTMIDI_CLASSNAME //*********************************************************************// -// RtMidiOut Definitions +// MidiOut Definitions //*********************************************************************// -void RtMidiOut :: openMidiApi( RtMidi::Api api, const std::string &clientName ) +#define RTMIDI_CLASSNAME "MidiOut" +void MidiOut :: openMidiApi( ApiType api ) { delete rtapi_; rtapi_ = 0; + try { + switch (api) { + case rtmidi::UNIX_JACK: #if defined(__UNIX_JACK__) - if ( api == UNIX_JACK ) - rtapi_ = new MidiOutJack( clientName ); + rtapi_ = new MidiOutJack( clientName ); #endif + break; + case rtmidi::LINUX_ALSA: #if defined(__LINUX_ALSA__) - if ( api == LINUX_ALSA ) - rtapi_ = new MidiOutAlsa( clientName ); + rtapi_ = new MidiOutAlsa( clientName ); #endif + break; + case rtmidi::WINDOWS_MM: #if defined(__WINDOWS_MM__) - if ( api == WINDOWS_MM ) - rtapi_ = new MidiOutWinMM( clientName ); + rtapi_ = new MidiOutWinMM( clientName ); #endif -#if defined(__MACOSX_CORE__) - if ( api == MACOSX_CORE ) - rtapi_ = new MidiOutCore( clientName ); + break; + case rtmidi::MACOSX_CORE: +#if defined(__MACOSX_COREMIDI__) + rtapi_ = new MidiOutCore( clientName ); #endif + break; + case rtmidi::DUMMY: #if defined(__RTMIDI_DUMMY__) - if ( api == RTMIDI_DUMMY ) - rtapi_ = new MidiOutDummy( clientName ); + rtapi_ = new MidiOutDummy( clientName ); #endif + break; + case rtmidi::UNSPECIFIED: + case rtmidi::ALL_API: + default: + break; + } + } catch (const Error & e) { + rtapi_ = 0; + throw; + } + } -RTMIDI_DLL_PUBLIC RtMidiOut :: RtMidiOut( RtMidi::Api api, const std::string &clientName) + +MidiApiList MidiOut::queryApis; + +RTMIDI_DLL_PUBLIC MidiOut :: MidiOut( ApiType api, const std::string &clientName, bool pfsystem ) + : Midi(&queryApis, pfsystem, clientName) { - if ( api != UNSPECIFIED ) { + if ( api == rtmidi::ALL_API) { + if (!queryApis.empty()) { + rtapi_ = NULL; + return; + } + + std::vector< ApiType > apis; + getCompiledApi( apis ); + for ( unsigned int i=0; i apis; + std::vector< ApiType > apis; getCompiledApi( apis ); for ( unsigned int i=0; igetPortCount() ) break; } if ( rtapi_ ) return; - // It should not be possible to get here because the preprocessor - // definition __RTMIDI_DUMMY__ is automatically defined if no - // API-specific definitions are passed to the compiler. But just in - // case something weird happens, we'll thrown an error. - std::string errorText = "RtMidiOut: no compiled API support found ... critical error!!"; - throw( RtMidiError( errorText, RtMidiError::UNSPECIFIED ) ); + // We may reach this point, e.g. if JACK is the only + // compiled API, but no JACK devices are found. + throw( RTMIDI_ERROR(gettext_noopt("No supported MIDI system has been found."), + Error::NO_DEVICES_FOUND ) ); } -RtMidiOut :: ~RtMidiOut() throw() +MidiOut :: ~MidiOut() throw() { } +#undef RTMIDI_CLASSNAME -//*********************************************************************// -// Common MidiApi Definitions -//*********************************************************************// MidiApi :: MidiApi( void ) - : apiData_( 0 ), connected_( false ), errorCallback_(0), firstErrorOccurred_(false), errorCallbackUserData_(0) + : apiData_( 0 ), + connected_( false ), + firstErrorOccurred_ (false), + errorCallback_(0) { } @@ -470,13 +728,17 @@ MidiApi :: ~MidiApi( void ) { } -void MidiApi :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData = 0 ) +void MidiApi :: setErrorCallback( ErrorCallback errorCallback, void *userData ) { - errorCallback_ = errorCallback; - errorCallbackUserData_ = userData; + errorCallback_ = new CompatibilityErrorInterface(errorCallback,userData); +} + +void MidiApi :: setErrorCallback(ErrorInterface * callback) { + errorCallback_ = callback; } -void MidiApi :: error( RtMidiError::Type type, std::string errorString ) + +void MidiApi :: error(Error e) { if ( errorCallback_ ) { @@ -484,24 +746,25 @@ void MidiApi :: error( RtMidiError::Type type, std::string errorString ) return; firstErrorOccurred_ = true; - const std::string errorMessage = errorString; + std::ostringstream s; + e.printMessage(s); - errorCallback_( type, errorMessage, errorCallbackUserData_); + errorCallback_->rtmidi_error(e); firstErrorOccurred_ = false; return; } - if ( type == RtMidiError::WARNING ) { - std::cerr << '\n' << errorString << "\n\n"; + if ( e.getType() == Error::WARNING ) { + e.printMessage(); } - else if ( type == RtMidiError::DEBUG_WARNING ) { + else if ( e.getType() == Error::DEBUG_WARNING ) { #if defined(__RTMIDI_DEBUG__) - std::cerr << '\n' << errorString << "\n\n"; + e.printMessage(); #endif } else { - std::cerr << '\n' << errorString << "\n\n"; - throw RtMidiError( errorString, type ); + e.printMessage(); + throw e; } } @@ -509,73 +772,92 @@ void MidiApi :: error( RtMidiError::Type type, std::string errorString ) // Common MidiInApi Definitions //*********************************************************************// +#define RTMIDI_CLASSNAME "MidiInApi" MidiInApi :: MidiInApi( unsigned int queueSizeLimit ) - : MidiApi() + : MidiApi(), ignoreFlags(7), doInput(false), firstMessage(true), + userCallback(0), + continueSysex(false) { // Allocate the MIDI queue. - inputData_.queue.ringSize = queueSizeLimit; - if ( inputData_.queue.ringSize > 0 ) - inputData_.queue.ring = new MidiMessage[ inputData_.queue.ringSize ]; + queue.ringSize = queueSizeLimit; + if ( queue.ringSize > 0 ) + queue.ring = new MidiMessage[ queue.ringSize ]; } MidiInApi :: ~MidiInApi( void ) { + if (userCallback) + userCallback->delete_me(); // Delete the MIDI queue. - if ( inputData_.queue.ringSize > 0 ) delete [] inputData_.queue.ring; + if ( queue.ringSize > 0 ) delete [] queue.ring; +} + +void MidiInApi :: setCallback( MidiCallback callback, void *userData ) +{ + if ( userCallback ) { + error(RTMIDI_ERROR(gettext_noopt("A callback function is already set."), + Error::WARNING)); + return; + } + + if ( !callback ) { + error(RTMIDI_ERROR(gettext_noopt("The callback function value is invalid."), + Error::WARNING)); + return; + } + + userCallback = new CompatibilityMidiInterface(callback,userData); } -void MidiInApi :: setCallback( RtMidiIn::RtMidiCallback callback, void *userData ) +void MidiInApi :: setCallback( MidiInterface * callback ) { - if ( inputData_.usingCallback ) { - errorString_ = "MidiInApi::setCallback: a callback function is already set!"; - error( RtMidiError::WARNING, errorString_ ); + if ( userCallback ) { + error(RTMIDI_ERROR(gettext_noopt("A callback function is already set."), + Error::WARNING)); return; } if ( !callback ) { - errorString_ = "RtMidiIn::setCallback: callback function value is invalid!"; - error( RtMidiError::WARNING, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("The callback function value is invalid."), + Error::WARNING)); return; } - inputData_.userCallback = callback; - inputData_.userData = userData; - inputData_.usingCallback = true; + userCallback = callback; } void MidiInApi :: cancelCallback() { - if ( !inputData_.usingCallback ) { - errorString_ = "RtMidiIn::cancelCallback: no callback function was set!"; - error( RtMidiError::WARNING, errorString_ ); + if ( !userCallback ) { + error(RTMIDI_ERROR(gettext_noopt("No callback function was set."), + Error::WARNING)); return; } - inputData_.userCallback = 0; - inputData_.userData = 0; - inputData_.usingCallback = false; + userCallback->delete_me(); + userCallback = 0; } void MidiInApi :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) { - inputData_.ignoreFlags = 0; - if ( midiSysex ) inputData_.ignoreFlags = 0x01; - if ( midiTime ) inputData_.ignoreFlags |= 0x02; - if ( midiSense ) inputData_.ignoreFlags |= 0x04; + ignoreFlags = 0; + if ( midiSysex ) ignoreFlags = 0x01; + if ( midiTime ) ignoreFlags |= 0x02; + if ( midiSense ) ignoreFlags |= 0x04; } -double MidiInApi :: getMessage( std::vector *message ) +double MidiInApi :: getMessage( std::vector &message ) { - message->clear(); + message.clear(); - if ( inputData_.usingCallback ) { - errorString_ = "RtMidiIn::getNextMessage: a user callback is currently set for this port."; - error( RtMidiError::WARNING, errorString_ ); + if ( userCallback ) { + error(RTMIDI_ERROR(gettext_noopt("Returning an empty MIDI message as all input is handled by a callback function."), + Error::WARNING)); return 0.0; } double timeStamp; - if (!inputData_.queue.pop(message, &timeStamp)) + if (!queue.pop(message, timeStamp)) return 0.0; return timeStamp; @@ -618,7 +900,7 @@ bool MidiInApi::MidiQueue::push(const MidiInApi::MidiMessage& msg) return false; } -bool MidiInApi::MidiQueue::pop(std::vector *msg, double* timeStamp) +bool MidiInApi::MidiQueue::pop(std::vector &msg, double& timeStamp) { // Local stack copies of front/back unsigned int _back, _front, _size; @@ -630,18 +912,20 @@ bool MidiInApi::MidiQueue::pop(std::vector *msg, double* timeStam return false; // Copy queued message to the vector pointer argument and then "pop" it. - msg->assign( ring[_front].bytes.begin(), ring[_front].bytes.end() ); - *timeStamp = ring[_front].timeStamp; + msg.assign( ring[_front].bytes.begin(), ring[_front].bytes.end() ); + timeStamp = ring[_front].timeStamp; // Update front front = (front+1)%ringSize; return true; } +#undef RTMIDI_CLASSNAME //*********************************************************************// // Common MidiOutApi Definitions //*********************************************************************// +#define RTMIDI_CLASSNAME "MidiOutApi" MidiOutApi :: MidiOutApi( void ) : MidiApi() { @@ -651,13 +935,38 @@ MidiOutApi :: ~MidiOutApi( void ) { } + +// trim from start +static inline std::string <rim(std::string &s) { + s.erase(s.begin(), + std::find_if(s.begin(), s.end(), [](int ch) { + return ! std::isspace(ch); + })); + return s; +} + +// trim from end +static inline std::string &rtrim(std::string &s) { + s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { + return ! std::isspace(ch); + }).base(), s.end()); + return s; +} + +// trim from both ends +static inline std::string &trim(std::string &s) { + return ltrim(rtrim(s)); +} +RTMIDI_NAMESPACE_END +#undef RTMIDI_CLASSNAME + // *************************************************** // // // OS/API-specific methods. // // *************************************************** // -#if defined(__MACOSX_CORE__) +#if defined(__MACOSX_COREMIDI__) // The CoreMIDI API is based on the use of a callback function for // MIDI input. We convert the system specific time stamps to delta @@ -668,737 +977,1679 @@ MidiOutApi :: ~MidiOutApi( void ) #include #include -// A structure to hold variables related to the CoreMIDI API -// implementation. -struct CoreMidiData { - MIDIClientRef client; - MIDIPortRef port; - MIDIEndpointRef endpoint; - MIDIEndpointRef destinationId; - unsigned long long lastTime; - MIDISysexSendRequest sysexreq; -}; - -//*********************************************************************// -// API: OS-X -// Class Definitions: MidiInCore -//*********************************************************************// +RTMIDI_NAMESPACE_START +/*! An abstraction layer for the CORE sequencer layer. It provides + the following functionality: + - dynamic allocation of the sequencer + - optionallay avoid concurrent access to the CORE sequencer, + which is not thread proof. This feature is controlled by + the parameter \ref locking. +*/ -static void midiInputCallback( const MIDIPacketList *list, void *procRef, void */*srcRef*/ ) -{ - MidiInApi::RtMidiInData *data = static_cast (procRef); - CoreMidiData *apiData = static_cast (data->apiData); +/* A wrapper for temporary CFString objects */ +class CFStringWrapper { +public: + CFStringWrapper(const std::string &s): + cfname(CFStringCreateWithCStringNoCopy( NULL, + s.c_str(), + kCFStringEncodingUTF8, + kCFAllocatorNull)) + {} + CFStringWrapper(const char * s): + cfname(CFStringCreateWithCStringNoCopy( NULL, + s, + kCFStringEncodingUTF8, + kCFAllocatorNull)) + {} + ~CFStringWrapper() { + CFRelease(cfname); + } - unsigned char status; - unsigned short nBytes, iByte, size; - unsigned long long time; + operator CFStringRef&() { return cfname; } +protected: + CFStringRef cfname; +}; - bool& continueSysex = data->continueSysex; - MidiInApi::MidiMessage& message = data->message; +// This function was submitted by Douglas Casey Tucker and apparently +// derived largely from PortMidi. +// or copied from the Apple developer Q&A website +// https://developer.apple.com/library/mac/qa/qa1374/_index.html - const MIDIPacket *packet = &list->packet[0]; - for ( unsigned int i=0; inumPackets; ++i ) { +CFStringRef EndpointName( MIDIEndpointRef endpoint, bool isExternal ) +{ + CFMutableStringRef result = CFStringCreateMutable( NULL, 0 ); + CFStringRef str; - // My interpretation of the CoreMIDI documentation: all message - // types, except sysex, are complete within a packet and there may - // be several of them in a single packet. Sysex messages can be - // broken across multiple packets and PacketLists but are bundled - // alone within each packet (these packets do not contain other - // message types). If sysex messages are split across multiple - // MIDIPacketLists, they must be handled by multiple calls to this - // function. + // Begin with the endpoint's name. + str = NULL; + MIDIObjectGetStringProperty( endpoint, kMIDIPropertyName, &str ); + if ( str != NULL ) { + CFStringAppend( result, str ); + CFRelease( str ); + } - nBytes = packet->length; - if ( nBytes == 0 ) { - packet = MIDIPacketNext(packet); - continue; - } + MIDIEntityRef entity = 0; + MIDIEndpointGetEntity( endpoint, &entity ); + if ( entity == 0 ) + // probably virtual + return result; - // Calculate time stamp. - if ( data->firstMessage ) { - message.timeStamp = 0.0; - data->firstMessage = false; - } - else { - time = packet->timeStamp; - if ( time == 0 ) { // this happens when receiving asynchronous sysex messages - time = AudioGetCurrentHostTime(); - } - time -= apiData->lastTime; - time = AudioConvertHostTimeToNanos( time ); - if ( !continueSysex ) - message.timeStamp = time * 0.000000001; + if ( CFStringGetLength( result ) == 0 ) { + // endpoint name has zero length -- try the entity + str = NULL; + MIDIObjectGetStringProperty( entity, kMIDIPropertyName, &str ); + if ( str != NULL ) { + CFStringAppend( result, str ); + CFRelease( str ); } + } + // now consider the device's name + MIDIDeviceRef device = 0; + MIDIEntityGetDevice( entity, &device ); + if ( device == 0 ) + return result; - // Track whether any non-filtered messages were found in this - // packet for timestamp calculation - bool foundNonFiltered = false; - - iByte = 0; - if ( continueSysex ) { - // We have a continuing, segmented sysex message. - if ( !( data->ignoreFlags & 0x01 ) ) { - // If we're not ignoring sysex messages, copy the entire packet. - for ( unsigned int j=0; jdata[j] ); - } - continueSysex = packet->data[nBytes-1] != 0xF7; - - if ( !( data->ignoreFlags & 0x01 ) && !continueSysex ) { - // If not a continuing sysex message, invoke the user callback function or queue the message. - if ( data->usingCallback ) { - RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; - callback( message.timeStamp, &message.bytes, data->userData ); - } - else { - // As long as we haven't reached our queue size limit, push the message. - if (!data->queue.push(message)) - std::cerr << "\nMidiInCore: message queue limit reached!!\n\n"; - } - message.bytes.clear(); - } - } - else { - while ( iByte < nBytes ) { - size = 0; - // We are expecting that the next byte in the packet is a status byte. - status = packet->data[iByte]; - if ( !(status & 0x80) ) break; - // Determine the number of bytes in the MIDI message. - if ( status < 0xC0 ) size = 3; - else if ( status < 0xE0 ) size = 2; - else if ( status < 0xF0 ) size = 3; - else if ( status == 0xF0 ) { - // A MIDI sysex - if ( data->ignoreFlags & 0x01 ) { - size = 0; - iByte = nBytes; - } - else size = nBytes - iByte; - continueSysex = packet->data[nBytes-1] != 0xF7; - } - else if ( status == 0xF1 ) { - // A MIDI time code message - if ( data->ignoreFlags & 0x02 ) { - size = 0; - iByte += 2; - } - else size = 2; - } - else if ( status == 0xF2 ) size = 3; - else if ( status == 0xF3 ) size = 2; - else if ( status == 0xF8 && ( data->ignoreFlags & 0x02 ) ) { - // A MIDI timing tick message and we're ignoring it. - size = 0; - iByte += 1; - } - else if ( status == 0xFE && ( data->ignoreFlags & 0x04 ) ) { - // A MIDI active sensing message and we're ignoring it. - size = 0; - iByte += 1; - } - else size = 1; - - // Copy the MIDI data to our vector. - if ( size ) { - foundNonFiltered = true; - message.bytes.assign( &packet->data[iByte], &packet->data[iByte+size] ); - if ( !continueSysex ) { - // If not a continuing sysex message, invoke the user callback function or queue the message. - if ( data->usingCallback ) { - RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; - callback( message.timeStamp, &message.bytes, data->userData ); - } - else { - // As long as we haven't reached our queue size limit, push the message. - if (!data->queue.push(message)) - std::cerr << "\nMidiInCore: message queue limit reached!!\n\n"; - } - message.bytes.clear(); - } - iByte += size; - } + str = NULL; + MIDIObjectGetStringProperty( device, kMIDIPropertyName, &str ); + if ( CFStringGetLength( result ) == 0 ) { + CFRelease( result ); + return str; + } + if ( str != NULL ) { + // if an external device has only one entity, throw away + // the endpoint name and just use the device name + if ( isExternal && MIDIDeviceGetNumberOfEntities( device ) < 2 ) { + CFRelease( result ); + return str; + } else { + if ( CFStringGetLength( str ) == 0 ) { + CFRelease( str ); + return result; } - } - - // Save the time of the last non-filtered message - if (foundNonFiltered) - { - apiData->lastTime = packet->timeStamp; - if ( apiData->lastTime == 0 ) { // this happens when receiving asynchronous sysex messages - apiData->lastTime = AudioGetCurrentHostTime(); + // does the entity name already start with the device name? + // (some drivers do this though they shouldn't) + // if so, do not prepend + if ( CFStringCompareWithOptions( result, /* endpoint name */ + str /* device name */, + CFRangeMake(0, CFStringGetLength( str ) ), 0 ) != kCFCompareEqualTo ) { + // prepend the device name to the entity name + if ( CFStringGetLength( result ) > 0 ) + CFStringInsert( result, 0, CFSTR(" ") ); + CFStringInsert( result, 0, str ); } + CFRelease( str ); } - - packet = MIDIPacketNext(packet); } + return result; } -MidiInCore :: MidiInCore( const std::string &clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) -{ - MidiInCore::initialize( clientName ); -} - -MidiInCore :: ~MidiInCore( void ) +// This function was submitted by Douglas Casey Tucker and apparently +// derived largely from PortMidi. +// Nearly the same text can be found in the Apple Q&A qa1374: +// https://developer.apple.com/library/mac/qa/qa1374/_index.html +static CFStringRef ConnectedEndpointName( MIDIEndpointRef endpoint ) { - // Close a connection if it exists. - MidiInCore::closePort(); + CFMutableStringRef result = CFStringCreateMutable( NULL, 0 ); + CFStringRef str; + OSStatus err; + int i; - // Cleanup. - CoreMidiData *data = static_cast (apiData_); - MIDIClientDispose( data->client ); - if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); - delete data; -} - -void MidiInCore :: initialize( const std::string& clientName ) -{ - // Set up our client. - MIDIClientRef client; - CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ); - OSStatus result = MIDIClientCreate(name, NULL, NULL, &client ); - if ( result != noErr ) { - std::ostringstream ost; - ost << "MidiInCore::initialize: error creating OS-X MIDI client object (" << result << ")."; - errorString_ = ost.str(); - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; + // Does the endpoint have connections? + CFDataRef connections = NULL; + int nConnected = 0; + bool anyStrings = false; + err = MIDIObjectGetDataProperty( endpoint, kMIDIPropertyConnectionUniqueID, &connections ); + if ( connections != NULL ) { + // It has connections, follow them + // Concatenate the names of all connected devices + nConnected = CFDataGetLength( connections ) / sizeof(MIDIUniqueID); + if ( nConnected ) { + const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections)); + for ( i=0; iclient = client; - data->endpoint = 0; - apiData_ = (void *) data; - inputData_.apiData = (void *) data; - CFRelease(name); + CFRelease( result ); + + // Here, either the endpoint had no connections, or we failed to obtain names + return EndpointName( endpoint, false ); } -void MidiInCore :: openPort( unsigned int portNumber, const std::string &portName ) -{ - if ( connected_ ) { - errorString_ = "MidiInCore::openPort: a valid connection already exists!"; - error( RtMidiError::WARNING, errorString_ ); - return; - } - CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); - unsigned int nSrc = MIDIGetNumberOfSources(); - if (nSrc < 1) { - errorString_ = "MidiInCore::openPort: no MIDI input sources found!"; - error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); - return; +#define RTMIDI_CLASSNAME "CoreSequencer" +template +class CoreSequencer { +public: + CoreSequencer():seq(0) + { + if (locking) { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); + pthread_mutex_init(&mutex, &attr); + } } - if ( portNumber >= nSrc ) { - std::ostringstream ost; - ost << "MidiInCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtMidiError::INVALID_PARAMETER, errorString_ ); - return; + CoreSequencer(const std::string &n):seq(0),name(n) + { + if (locking) { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); + pthread_mutex_init(&mutex, &attr); + } + init(); } - MIDIPortRef port; - CoreMidiData *data = static_cast (apiData_); - CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); - OSStatus result = MIDIInputPortCreate( data->client, - portNameRef, - midiInputCallback, (void *)&inputData_, &port ); - CFRelease( portNameRef ); - - if ( result != noErr ) { - MIDIClientDispose( data->client ); - errorString_ = "MidiInCore::openPort: error creating OS-X MIDI input port."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; + ~CoreSequencer() + { + if (seq) { + scoped_lock lock(mutex); + MIDIClientDispose(seq); + seq = 0; + } + if (locking) { + pthread_mutex_destroy(&mutex); + } } - // Get the desired input source identifier. - MIDIEndpointRef endpoint = MIDIGetSource( portNumber ); - if ( endpoint == 0 ) { - MIDIPortDispose( port ); - MIDIClientDispose( data->client ); - errorString_ = "MidiInCore::openPort: error getting MIDI input source reference."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; + bool setName(const std::string &n) { + /* we don't want to rename the client after opening it. */ + if (seq) return false; + name = n; + return true; } - // Make the connection. - result = MIDIPortConnectSource( port, endpoint, NULL ); - if ( result != noErr ) { - MIDIPortDispose( port ); - MIDIClientDispose( data->client ); - errorString_ = "MidiInCore::openPort: error connecting OS-X MIDI input port."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; + static std::string str(CFStringRef s) { + const char * cstr = + CFStringGetCStringPtr(s,kCFStringEncodingUTF8); + if (cstr) return cstr; + + CFIndex len = CFStringGetLength(s); + std::string retval; + retval.resize(CFStringGetMaximumSizeForEncoding(len, + kCFStringEncodingUTF8)+1); + CFStringGetBytes(s, + CFRangeMake(0, len), + kCFStringEncodingUTF8, + 0, + false, + reinterpret_cast(&retval[0]), + retval.size()-1, + &len); + retval.resize(len); + return trim(retval); } - // Save our api-specific port information. - data->port = port; - connected_ = true; -} +#if 0 + // Obtain the name of an endpoint, following connections. -void MidiInCore :: openVirtualPort( const std::string &portName ) -{ - CoreMidiData *data = static_cast (apiData_); + // The result should be released by the caller. - // Create a virtual MIDI input destination. - MIDIEndpointRef endpoint; - CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); - OSStatus result = MIDIDestinationCreate( data->client, - portNameRef, - midiInputCallback, (void *)&inputData_, &endpoint ); - CFRelease( portNameRef ); - - if ( result != noErr ) { - errorString_ = "MidiInCore::openVirtualPort: error creating virtual OS-X MIDI destination."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } + static CFStringRef CreateConnectedEndpointName(MIDIEndpointRef endpoint) + { + CFMutableStringRef result = CFStringCreateMutable(NULL, 0); + CFStringRef str; + OSStatus err; + + + // Does the endpoint have connections? + CFDataRef connections = NULL; + int nConnected = 0; + bool anyStrings = false; + err = MIDIObjectGetDataProperty(endpoint, kMIDIPropertyConnectionUniqueID, &connections); + if (connections != NULL) { + // It has connections, follow them + // Concatenate the names of all connected devices + nConnected = CFDataGetLength(connections) / sizeof(MIDIUniqueID); + + if (nConnected) { + const SInt32 *pid = reinterpret_cast(CFDataGetBytePtr(connections)); + for (int i = 0; i < nConnected; ++i, ++pid) { + MIDIUniqueID id = EndianS32_BtoN(*pid); + MIDIObjectRef connObject; + MIDIObjectType connObjectType; + err = MIDIObjectFindByUniqueID(id, &connObject, &connObjectType); + if (err == noErr) { + if (connObjectType == kMIDIObjectType_ExternalSource || + connObjectType == kMIDIObjectType_ExternalDestination) { + // Connected to an external device's endpoint (10.3 and later). + str = EndpointName(static_cast(connObject), true); + } else { + // Connected to an external device (10.2) (or something else, catch-all) + str = NULL; + MIDIObjectGetStringProperty(connObject, kMIDIPropertyName, &str); + } + + if (str != NULL) { + if (anyStrings) + CFStringAppend(result, CFSTR(", ")); + else anyStrings = true; + CFStringAppend(result, str); + CFRelease(str); + } + } + } + } + CFRelease(connections); + } - // Save our api-specific connection information. - data->endpoint = endpoint; -} -void MidiInCore :: closePort( void ) -{ - CoreMidiData *data = static_cast (apiData_); + if (anyStrings) + return result; + else + CFRelease(result); - if ( data->endpoint ) { - MIDIEndpointDispose( data->endpoint ); - data->endpoint = 0; + // Here, either the endpoint had no connections, or we failed to obtain names for any of them. + return CreateEndpointName(endpoint, false); } - if ( data->port ) { - MIDIPortDispose( data->port ); - data->port = 0; - } - connected_ = false; -} -void MidiInCore :: setClientName ( const std::string& ) -{ + ////////////////////////////////////// + // Obtain the name of an endpoint without regard for whether it has connections. + // The result should be released by the caller. - errorString_ = "MidiInCore::setClientName: this function is not implemented for the MACOSX_CORE API!"; - error( RtMidiError::WARNING, errorString_ ); + static CFStringRef CreateEndpointName(MIDIEndpointRef endpoint, bool isExternal) + { + CFMutableStringRef result = CFStringCreateMutable(NULL, 0); + CFStringRef str; -} + // begin with the endpoint's name + str = NULL; + MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &str); + if (str != NULL) { + CFStringAppend(result, str); + CFRelease(str); + } -void MidiInCore :: setPortName ( const std::string& ) -{ + MIDIEntityRef entity = NULL; + MIDIEndpointGetEntity(endpoint, &entity); + if (entity == NULL) + // probably virtual + return result; + + if (CFStringGetLength(result) == 0) { + // endpoint name has zero length -- try the entity + str = NULL; + MIDIObjectGetStringProperty(entity, kMIDIPropertyName, &str); + if (str != NULL) { + CFStringAppend(result, str); + CFRelease(str); + } + } - errorString_ = "MidiInCore::setPortName: this function is not implemented for the MACOSX_CORE API!"; - error( RtMidiError::WARNING, errorString_ ); -} -unsigned int MidiInCore :: getPortCount() -{ - CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); - return MIDIGetNumberOfSources(); -} + // now consider the device's name + MIDIDeviceRef device = NULL; + MIDIEntityGetDevice(entity, &device); + if (device == NULL) return result; -// This function was submitted by Douglas Casey Tucker and apparently -// derived largely from PortMidi. -CFStringRef EndpointName( MIDIEndpointRef endpoint, bool isExternal ) -{ - CFMutableStringRef result = CFStringCreateMutable( NULL, 0 ); - CFStringRef str; + str = NULL; + MIDIObjectGetStringProperty(device, kMIDIPropertyName, &str); + if (str != NULL) { + // if an external device has only one entity, throw away + // the endpoint name and just use the device name + if (isExternal && MIDIDeviceGetNumberOfEntities(device) < 2) { + CFRelease(result); + return str; + } else { + // does the entity name already start with the device name? + // (some drivers do this though they shouldn't) + // if so, do not prepend + + if (CFStringCompareWithOptions(str /* device name */, + result /* endpoint name */, + CFRangeMake(0, + CFStringGetLength(str)), + 0) + != kCFCompareEqualTo) { + // prepend the device name to the entity name + if (CFStringGetLength(result) > 0) + CFStringInsert(result, 0, CFSTR(" ")); + CFStringInsert(result, 0, str); + } + CFRelease(str); + } + } - // Begin with the endpoint's name. - str = NULL; - MIDIObjectGetStringProperty( endpoint, kMIDIPropertyName, &str ); - if ( str != NULL ) { - CFStringAppend( result, str ); - CFRelease( str ); + return result; } +#endif - MIDIEntityRef entity = 0; - MIDIEndpointGetEntity( endpoint, &entity ); - if ( entity == 0 ) - // probably virtual - return result; + static std::string getConnectionsString(MIDIEndpointRef port) + { + /* This function is derived from + CreateConnectedEndpointName at Apple Q&A */ + std::ostringstream result; + CFDataRef connections = NULL; + OSStatus err = MIDIObjectGetDataProperty(port, + kMIDIPropertyConnectionUniqueID, + &connections); + if (err != noErr) + return result.str(); + + if (!connections) + return result.str(); + CFIndex size = CFDataGetLength( connections ) / sizeof(MIDIUniqueID); + if (!size) { + CFRelease(connections); + return result.str(); + } - if ( CFStringGetLength( result ) == 0 ) { - // endpoint name has zero length -- try the entity - str = NULL; - MIDIObjectGetStringProperty( entity, kMIDIPropertyName, &str ); - if ( str != NULL ) { - CFStringAppend( result, str ); - CFRelease( str ); + CFStringRef strRef; + const SInt32 *pid + = reinterpret_cast(CFDataGetBytePtr(connections)); + bool anyStrings = false; + for (int i = 0; i < size; ++i, ++pid) { + MIDIUniqueID id = EndianS32_BtoN(*pid); + MIDIObjectRef connObject; + MIDIObjectType connObjectType; + err = MIDIObjectFindByUniqueID(id, &connObject, &connObjectType); + if (err != noErr) + continue; + + if (connObjectType == kMIDIObjectType_ExternalSource || + connObjectType == kMIDIObjectType_ExternalDestination) { + // Connected to an external + // device's endpoint + // (10.3 and later). + strRef = EndpointName(static_cast(connObject), + true); + } else { + // Connected to an external device + // (10.2) (or something else, catch-all) + strRef = NULL; + MIDIObjectGetStringProperty(connObject, + kMIDIPropertyName, &strRef); + } + + if (strRef != NULL) { + if (anyStrings) + result << ", "; + else anyStrings = true; + result << str(strRef); + CFRelease(strRef); + } } + CFRelease(connections); + return result.str(); } - // now consider the device's name - MIDIDeviceRef device = 0; - MIDIEntityGetDevice( entity, &device ); - if ( device == 0 ) - return result; - str = NULL; - MIDIObjectGetStringProperty( device, kMIDIPropertyName, &str ); - if ( CFStringGetLength( result ) == 0 ) { - CFRelease( result ); - return str; - } - if ( str != NULL ) { - // if an external device has only one entity, throw away - // the endpoint name and just use the device name - if ( isExternal && MIDIDeviceGetNumberOfEntities( device ) < 2 ) { - CFRelease( result ); - return str; - } else { - if ( CFStringGetLength( str ) == 0 ) { - CFRelease( str ); - return result; + static std::string getPortName(MIDIEndpointRef port, int flags) { + // std::string clientname; + std::string devicename; + std::string portname; + std::string entityname; + // std::string externaldevicename; + std::string connections; + std::string recommendedname; + // bool isVirtual; + bool hasManyEntities = false; + bool hasManyEndpoints = false; + CFStringRef nameRef; + MIDIObjectGetStringProperty(port, + kMIDIPropertyDisplayName, + &nameRef); + recommendedname = str(nameRef); + connections = getConnectionsString(port); + + MIDIObjectGetStringProperty(port, + kMIDIPropertyName, + &nameRef); + portname = str(nameRef); + CFRelease( nameRef ); + + MIDIEntityRef entity = 0; + MIDIEndpointGetEntity(port, &entity); + // entity == NULL: probably virtual + if (entity != 0) { + nameRef = NULL; + MIDIObjectGetStringProperty(entity, kMIDIPropertyName, &nameRef); + if (nameRef != NULL) { + entityname = str(nameRef); + CFRelease(nameRef); + } + hasManyEndpoints = + MIDIEntityGetNumberOfSources(entity) >= 2 || + MIDIEntityGetNumberOfDestinations(entity) + >= 2; + + // now consider the device's name + MIDIDeviceRef device = 0; + MIDIEntityGetDevice(entity, &device); + if (device != 0) { + hasManyEntities = MIDIDeviceGetNumberOfEntities(device) >= 2; + MIDIObjectGetStringProperty(device, + kMIDIPropertyName, + &nameRef); + devicename = str(nameRef); + CFRelease(nameRef); } // does the entity name already start with the device name? // (some drivers do this though they shouldn't) - // if so, do not prepend - if ( CFStringCompareWithOptions( result, /* endpoint name */ - str /* device name */, - CFRangeMake(0, CFStringGetLength( str ) ), 0 ) != kCFCompareEqualTo ) { - // prepend the device name to the entity name - if ( CFStringGetLength( result ) > 0 ) - CFStringInsert( result, 0, CFSTR(" ") ); - CFStringInsert( result, 0, str ); + if (entityname.substr(0,devicename.length()) + == devicename) { + int start = devicename.length(); + while (isspace(entityname[start])) + start++; + entityname = entityname.substr(start); } - CFRelease( str ); } - } - return result; -} -// This function was submitted by Douglas Casey Tucker and apparently -// derived largely from PortMidi. -static CFStringRef ConnectedEndpointName( MIDIEndpointRef endpoint ) -{ - CFMutableStringRef result = CFStringCreateMutable( NULL, 0 ); - CFStringRef str; - OSStatus err; - int i; + int naming = flags & PortDescriptor::NAMING_MASK; - // Does the endpoint have connections? - CFDataRef connections = NULL; - int nConnected = 0; - bool anyStrings = false; - err = MIDIObjectGetDataProperty( endpoint, kMIDIPropertyConnectionUniqueID, &connections ); - if ( connections != NULL ) { - // It has connections, follow them - // Concatenate the names of all connected devices - nConnected = CFDataGetLength( connections ) / sizeof(MIDIUniqueID); - if ( nConnected ) { - const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections)); - for ( i=0; i= MIDIGetNumberOfSources() ) { - std::ostringstream ost; - ost << "MidiInCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtMidiError::WARNING, errorString_ ); - return stringName; + MIDIPortRef createPort (const std::string &portName, + int flags, + MidiInCore * data = NULL) + { + init(); + scoped_lock lock (mutex); + MIDIPortRef port = 0; + OSStatus result; + switch (flags) { + case PortDescriptor::INPUT: { + result = MIDIInputPortCreate(seq, + CFStringWrapper(portName), + MidiInCore::midiInputCallback, + (void *)data, + &port); + } + break; + case PortDescriptor::OUTPUT: { + result + = MIDIOutputPortCreate(seq, + CFStringWrapper(portName), + &port); + } + break; + default: + throw RTMIDI_ERROR(gettext_noopt("Error creating OS X MIDI port because of invalid port flags."), + Error::INVALID_PARAMETER); + } + if ( result != noErr ) { + throw RTMIDI_ERROR(gettext_noopt("Error creating OS-X MIDI port."), + Error::DRIVER_ERROR); + } + return port; } - portRef = MIDIGetSource( portNumber ); - nameRef = ConnectedEndpointName(portRef); - CFStringGetCString( nameRef, name, sizeof(name), kCFStringEncodingUTF8); - CFRelease( nameRef ); + MIDIEndpointRef createVirtualPort (const std::string &portName, + int flags, + MidiInCore * data = NULL) + { + init(); + scoped_lock lock (mutex); + MIDIEndpointRef port = 0; + OSStatus result; + switch (flags) { + case PortDescriptor::INPUT: { + result + = MIDIDestinationCreate(seq, + CFStringWrapper(portName), + MidiInCore::midiInputCallback, + (void *)data, + &port); + } + break; + case PortDescriptor::OUTPUT: { + result + = MIDISourceCreate(seq, + CFStringWrapper(portName), + &port); + } + break; + default: + throw RTMIDI_ERROR(gettext_noopt("Error creating OS X MIDI port because of invalid port flags."), + Error::INVALID_PARAMETER); + } + if ( result != noErr ) { + throw RTMIDI_ERROR(gettext_noopt("Error creating OS-X MIDI port."), + Error::DRIVER_ERROR); + } + return port; + } + + + /*! Use CoreSequencer like a C pointer. + \note This function breaks the design to control thread safety + by the selection of the \ref locking parameter to the class. + It should be removed as soon as possible in order ensure the + thread policy that has been intended by creating this class. + */ + operator MIDIClientRef () + { + return seq; + } +protected: + struct scoped_lock { + pthread_mutex_t * mutex; + scoped_lock(pthread_mutex_t & m): mutex(&m) + { + if (locking) + pthread_mutex_lock(mutex); + } + ~scoped_lock() + { + if (locking) + pthread_mutex_unlock(mutex); + } + }; + pthread_mutex_t mutex; + MIDIClientRef seq; + std::string name; + + + void init() + { + init (seq); + } + + void init(MIDIClientRef &client) + { + if (client) return; + { + scoped_lock lock(mutex); + + OSStatus result = MIDIClientCreate(CFStringWrapper(name), NULL, NULL, &client ); + if ( result != noErr ) { + throw RTMIDI_ERROR1(gettext_noopt("Error creating OS-X MIDI client object (Error no: %d)."), + Error::DRIVER_ERROR, + result); + return; + } + } + } +}; +#undef RTMIDI_CLASSNAME + +typedef CoreSequencer<1> LockingCoreSequencer; +typedef CoreSequencer<0> NonLockingCoreSequencer; + +#define RTMIDI_CLASSNAME "CorePortDescriptor" +struct CorePortDescriptor:public PortDescriptor { + CorePortDescriptor(const std::string &name):api(0), + clientName(name), + endpoint(0) + { + } + CorePortDescriptor(MIDIEndpointRef p, + const std::string &name):api(0), + clientName(name), + endpoint(p) + { + seq.setName(name); + } + CorePortDescriptor(CorePortDescriptor & + other):PortDescriptor(other), + api(other.api), + clientName(other.clientName), + endpoint(other.endpoint) + { + seq.setName(clientName); + } + ~CorePortDescriptor() {} + + MidiInApi * getInputApi(unsigned int queueSizeLimit = 100) const { + if (getCapabilities() & INPUT) + return new MidiInCore(clientName,queueSizeLimit); + else + return 0; + } + + MidiOutApi * getOutputApi() const { + if (getCapabilities() & OUTPUT) + return new MidiOutCore(clientName); + else + return 0; + } + + void setEndpoint(MIDIEndpointRef e) + { + endpoint = e; + } + MIDIEndpointRef getEndpoint() const + { + return endpoint; + } + + std::string getName(int flags = SHORT_NAME | UNIQUE_PORT_NAME) { + return seq.getPortName(endpoint,flags); + } + + const std::string &getClientName() { + return clientName; + } + int getCapabilities() const { + if (!endpoint) return 0; + return seq.getPortCapabilities(endpoint); + } + static PortList getPortList(int capabilities, const std::string &clientName); +protected: + MidiApi * api; + static LockingCoreSequencer seq; + + std::string clientName; + MIDIEndpointRef endpoint; +}; + +LockingCoreSequencer CorePortDescriptor::seq; + + + +PortList CorePortDescriptor :: getPortList(int capabilities, const std::string &clientName) +{ + PortList list; + + CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); + int caps = capabilities & PortDescriptor::INOUTPUT; + // bool unlimited = capabilities & PortDescriptor::UNLIMITED; + bool forceInput = PortDescriptor::INPUT & caps; + bool forceOutput = PortDescriptor::OUTPUT & caps; + bool allowOutput = forceOutput || !forceInput; + bool allowInput = forceInput || !forceOutput; + if (allowOutput) { + ItemCount count = + MIDIGetNumberOfDestinations(); + for (ItemCount i = 0 ; i < count; i++) { + MIDIEndpointRef destination = + MIDIGetDestination(i); + try { + if ((seq.getPortCapabilities(destination) + & caps) == caps) + list.push_back(Pointer( + new CorePortDescriptor(destination, clientName))); + } catch (Error & e) { + if (e.getType() == Error::WARNING || + e.getType() == Error::DEBUG_WARNING) + e.printMessage(); + else throw; + } + } + // Combined sources and destinations + // should be both occur as destinations and as + // sources. So we have finished the search, here. + } else if (allowInput) { + ItemCount count = + MIDIGetNumberOfSources(); + for (ItemCount i = 0 ; i < count; i++) { + MIDIEndpointRef src = + MIDIGetSource(i); + try { + if ((seq.getPortCapabilities(src) + & caps) == caps) + list.push_back(Pointer( + new CorePortDescriptor(src, clientName))); + } catch (Error & e) { + if (e.getType() == Error::WARNING || + e.getType() == Error::DEBUG_WARNING) + e.printMessage(); + else throw; + } + } + } + return list; +} +#undef RTMIDI_CLASSNAME + + +#define RTMIDI_CLASSNAME "CoreMidiData" +// A structure to hold variables related to the CoreMIDI API +// implementation. +struct CoreMidiData:public CorePortDescriptor { + CoreMidiData(const std::string &clientname):CorePortDescriptor(clientname), + client(clientname), + localEndpoint(0), + localPort(0) {} + ~CoreMidiData() { + if (localEndpoint) + MIDIEndpointDispose(localEndpoint); + localEndpoint = 0; + } + + void openPort(const std::string &name, + int flags, + MidiInCore * data = NULL) { + localPort = client.createPort(name, flags, data); + } + + void setRemote(const CorePortDescriptor & remote) + { + setEndpoint(remote.getEndpoint()); + } + + NonLockingCoreSequencer client; + MIDIEndpointRef localEndpoint; + MIDIPortRef localPort; + unsigned long long lastTime; + MIDISysexSendRequest sysexreq; +}; +#undef RTMIDI_CLASSNAME - return stringName = name; -} //*********************************************************************// // API: OS-X -// Class Definitions: MidiOutCore +// Class Definitions: MidiInCore //*********************************************************************// -MidiOutCore :: MidiOutCore( const std::string &clientName ) : MidiOutApi() +#define RTMIDI_CLASSNAME "MidiInCore" +void MidiInCore::midiInputCallback( const MIDIPacketList *list, + void *procRef, + void */*srcRef*/ ) throw() { - MidiOutCore::initialize( clientName ); -} + MidiInCore *data = static_cast (procRef); + CoreMidiData *apiData = static_cast (data->apiData_); -MidiOutCore :: ~MidiOutCore( void ) -{ - // Close a connection if it exists. - MidiOutCore::closePort(); + unsigned char status; + unsigned short nBytes, iByte, size; + unsigned long long time; - // Cleanup. - CoreMidiData *data = static_cast (apiData_); - MIDIClientDispose( data->client ); - if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); - delete data; -} + bool& continueSysex = data->continueSysex; + MidiInApi::MidiMessage& message = data->message; -void MidiOutCore :: initialize( const std::string& clientName ) -{ - // Set up our client. - MIDIClientRef client; - CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ); - OSStatus result = MIDIClientCreate(name, NULL, NULL, &client ); - if ( result != noErr ) { - std::ostringstream ost; - ost << "MidiInCore::initialize: error creating OS-X MIDI client object (" << result << ")."; - errorString_ = ost.str(); - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } + const MIDIPacket *packet = &list->packet[0]; + for ( unsigned int i=0; inumPackets; ++i ) { - // Save our api-specific connection information. - CoreMidiData *data = (CoreMidiData *) new CoreMidiData; - data->client = client; - data->endpoint = 0; - apiData_ = (void *) data; - CFRelease( name ); + // My interpretation of the CoreMIDI documentation: all message + // types, except sysex, are complete within a packet and there may + // be several of them in a single packet. Sysex messages can be + // broken across multiple packets and PacketLists but are bundled + // alone within each packet (these packets do not contain other + // message types). If sysex messages are split across multiple + // MIDIPacketLists, they must be handled by multiple calls to this + // function. + + nBytes = packet->length; + if ( nBytes == 0 ) { + packet = MIDIPacketNext(packet); + continue; + } + + // Calculate time stamp. + + if ( data->firstMessage ) { + message.timeStamp = 0.0; + data->firstMessage = false; + } + else { + time = packet->timeStamp; + if ( time == 0 ) { // this happens when receiving asynchronous sysex messages + time = AudioGetCurrentHostTime(); + } + time -= apiData->lastTime; + time = AudioConvertHostTimeToNanos( time ); + if ( !continueSysex ) + message.timeStamp = time * 0.000000001; + } + // Track whether any non-filtered messages were found in this + // packet for timestamp calculation + bool foundNonFiltered = false; + //std::cout << "TimeStamp = " << packet->timeStamp << std::endl; + + iByte = 0; + if ( continueSysex ) { + // We have a continuing, segmented sysex message. + if ( !( data->ignoreFlags & 0x01 ) ) { + // If we're not ignoring sysex messages, copy the entire packet. + for ( unsigned int j=0; jdata[j] ); + } + continueSysex = packet->data[nBytes-1] != 0xF7; + + if ( !( data->ignoreFlags & 0x01 ) ) { + if ( !continueSysex ) { + // If not a continuing sysex message, invoke the user callback function or queue the message. + if ( data->userCallback ) { + data->userCallback->rtmidi_midi_in( message.timeStamp, + message.bytes); + } + else { + // As long as we haven't reached our queue size limit, push the message. + if (!data->queue.push(message)) { + try { + data->error(RTMIDI_ERROR(rtmidi_gettext("Error: Message queue limit reached."), + Error::WARNING)); + } catch (Error & e) { + // don't bother ALSA with an unhandled exception + } + } + } + message.bytes.clear(); + } + } + } + else { + while ( iByte < nBytes ) { + size = 0; + // We are expecting that the next byte in the packet is a status byte. + status = packet->data[iByte]; + if ( !(status & 0x80) ) break; + // Determine the number of bytes in the MIDI message. + if ( status < 0xC0 ) size = 3; + else if ( status < 0xE0 ) size = 2; + else if ( status < 0xF0 ) size = 3; + else if ( status == 0xF0 ) { + // A MIDI sysex + if ( data->ignoreFlags & 0x01 ) { + size = 0; + iByte = nBytes; + } + else size = nBytes - iByte; + continueSysex = packet->data[nBytes-1] != 0xF7; + } + else if ( status == 0xF1 ) { + // A MIDI time code message + if ( data->ignoreFlags & 0x02 ) { + size = 0; + iByte += 2; + } + else size = 2; + } + else if ( status == 0xF2 ) size = 3; + else if ( status == 0xF3 ) size = 2; + else if ( status == 0xF8 && ( data->ignoreFlags & 0x02 ) ) { + // A MIDI timing tick message and we're ignoring it. + size = 0; + iByte += 1; + } + else if ( status == 0xFE && ( data->ignoreFlags & 0x04 ) ) { + // A MIDI active sensing message and we're ignoring it. + size = 0; + iByte += 1; + } + else size = 1; + + // Copy the MIDI data to our vector. + if ( size ) { + foundNonFiltered = true; + message.bytes.assign( &packet->data[iByte], &packet->data[iByte+size] ); + if ( !continueSysex ) { + // If not a continuing sysex message, invoke the user callback function or queue the message. + if ( data->userCallback ) { + data->userCallback->rtmidi_midi_in( message.timeStamp, + message.bytes); + } + else { + // As long as we haven't reached our queue size limit, push the message. + if (!data->queue.push(message)) { + try { + data->error(RTMIDI_ERROR(rtmidi_gettext("Error: Message queue limit reached."), + Error::WARNING)); + } catch (Error & e) { + // don't bother WinMM with an unhandled exception + } + } + } + message.bytes.clear(); + } + iByte += size; + } + } + } + packet = MIDIPacketNext(packet); + + // Save the time of the last non-filtered message + if (foundNonFiltered) + { + apiData->lastTime = packet->timeStamp; + if ( apiData->lastTime == 0 ) { // this happens when receiving asynchronous sysex messages + apiData->lastTime = AudioGetCurrentHostTime(); + } + } + + } } -unsigned int MidiOutCore :: getPortCount() +MidiInCore :: MidiInCore( const std::string &clientName, + unsigned int queueSizeLimit ) : + MidiInApi( queueSizeLimit ) { - CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); - return MIDIGetNumberOfDestinations(); + MidiInCore::initialize( clientName ); } -std::string MidiOutCore :: getPortName( unsigned int portNumber ) +MidiInCore :: ~MidiInCore( void ) { - CFStringRef nameRef; - MIDIEndpointRef portRef; - char name[128]; + // Cleanup. + CoreMidiData *data = static_cast (apiData_); + try { + // Close a connection if it exists. + MidiInCore::closePort(); - std::string stringName; - CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); - if ( portNumber >= MIDIGetNumberOfDestinations() ) { - std::ostringstream ost; - ost << "MidiOutCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtMidiError::WARNING, errorString_ ); - return stringName; + } catch (Error & e) { + delete data; + throw; } + delete data; - portRef = MIDIGetDestination( portNumber ); - nameRef = ConnectedEndpointName(portRef); - CFStringGetCString( nameRef, name, sizeof(name), kCFStringEncodingUTF8 ); - CFRelease( nameRef ); - - return stringName = name; } -void MidiOutCore :: openPort( unsigned int portNumber, const std::string &portName ) +void MidiInCore :: initialize( const std::string &clientName ) +{ + // Save our api-specific connection information. + CoreMidiData *data = (CoreMidiData *) new CoreMidiData(clientName); + apiData_ = (void *) data; +} + +void MidiInCore :: openPort( unsigned int portNumber, + const std::string &portName ) { if ( connected_ ) { - errorString_ = "MidiOutCore::openPort: a valid connection already exists!"; - error( RtMidiError::WARNING, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("A valid connection already exists."), + Error::WARNING)); return; } CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); - unsigned int nDest = MIDIGetNumberOfDestinations(); - if (nDest < 1) { - errorString_ = "MidiOutCore::openPort: no MIDI output destinations found!"; - error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); + unsigned int nSrc = MIDIGetNumberOfSources(); + if (nSrc < 1) { + error(RTMIDI_ERROR(gettext_noopt("No MIDI input sources found."), + Error::NO_DEVICES_FOUND)); return; } - if ( portNumber >= nDest ) { + if ( portNumber >= nSrc ) { std::ostringstream ost; - ost << "MidiOutCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + ost << ""; errorString_ = ost.str(); - error( RtMidiError::INVALID_PARAMETER, errorString_ ); + error(RTMIDI_ERROR1(gettext_noopt("The 'portNumber' argument (%d) is invalid."), + Error::INVALID_PARAMETER, portNumber) ); return; } MIDIPortRef port; CoreMidiData *data = static_cast (apiData_); - CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); - OSStatus result = MIDIOutputPortCreate( data->client, - portNameRef, - &port ); - CFRelease( portNameRef ); + OSStatus result = MIDIInputPortCreate( data->client, + CFStringWrapper(portName), + midiInputCallback, (void *)this, &port ); if ( result != noErr ) { MIDIClientDispose( data->client ); - errorString_ = "MidiOutCore::openPort: error creating OS-X MIDI output port."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("Error creating OS-X MIDI input port."), + Error::DRIVER_ERROR)); return; } - // Get the desired output port identifier. - MIDIEndpointRef destination = MIDIGetDestination( portNumber ); - if ( destination == 0 ) { + // Get the desired input source identifier. + MIDIEndpointRef endpoint = MIDIGetSource( portNumber ); + if ( endpoint == 0 ) { MIDIPortDispose( port ); + port = 0; MIDIClientDispose( data->client ); - errorString_ = "MidiOutCore::openPort: error getting MIDI output destination reference."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("Error getting MIDI input source reference."), + Error::DRIVER_ERROR) ); return; } - // Save our api-specific connection information. - data->port = port; - data->destinationId = destination; + // Make the connection. + result = MIDIPortConnectSource( port, endpoint, NULL ); + if ( result != noErr ) { + MIDIPortDispose( port ); + port = 0; + MIDIClientDispose( data->client ); + error(RTMIDI_ERROR(gettext_noopt("Error connecting OS-X MIDI input port."), + Error::DRIVER_ERROR) ); + return; + } + + // Save our api-specific port information. + data->localPort = port; + data->setEndpoint(endpoint); + connected_ = true; } -void MidiOutCore :: closePort( void ) +void MidiInCore :: openVirtualPort( const std::string &portName ) { CoreMidiData *data = static_cast (apiData_); - if ( data->endpoint ) { - MIDIEndpointDispose( data->endpoint ); - data->endpoint = 0; - } - - if ( data->port ) { - MIDIPortDispose( data->port ); - data->port = 0; + // Create a virtual MIDI input destination. + MIDIEndpointRef endpoint; + OSStatus result = MIDIDestinationCreate( data->client, + CFStringWrapper(portName), + midiInputCallback, (void *)this, &endpoint ); + if ( result != noErr ) { + error(RTMIDI_ERROR(gettext_noopt("Error creating virtual OS-X MIDI destination."), + Error::DRIVER_ERROR) ); + return; } - connected_ = false; + // Save our api-specific connection information. + data->localEndpoint = endpoint; } -void MidiOutCore :: setClientName ( const std::string& ) +void MidiInCore :: openPort( const PortDescriptor & port, + const std::string &portName) { + CoreMidiData *data = static_cast (apiData_); + const CorePortDescriptor * remote = dynamic_cast(&port); + + if ( !data ) { + error(RTMIDI_ERROR(gettext_noopt("Data has not been allocated."), + Error::SYSTEM_ERROR) ); + return; + } + if ( connected_ || data -> localEndpoint) { + error(RTMIDI_ERROR(gettext_noopt("A valid connection already exists."), + Error::WARNING) ); + return; + } + if (!remote) { + error(RTMIDI_ERROR(gettext_noopt("Core MIDI has been instructed to open a non-Core MIDI port. This doesn't work."), + Error::INVALID_DEVICE) ); + return; + } - errorString_ = "MidiOutCore::setClientName: this function is not implemented for the MACOSX_CORE API!"; - error( RtMidiError::WARNING, errorString_ ); + data->openPort (portName, + PortDescriptor::INPUT, + this); + data->setRemote(*remote); + OSStatus result = + MIDIPortConnectSource(data->localPort, + data->getEndpoint(), + NULL); + if ( result != noErr ) { + error(RTMIDI_ERROR(gettext_noopt("Error creating OS-X MIDI port."), + Error::DRIVER_ERROR)); + } + connected_ = true; } -void MidiOutCore :: setPortName ( const std::string& ) +Pointer MidiInCore :: getDescriptor(bool local) { - - errorString_ = "MidiOutCore::setPortName: this function is not implemented for the MACOSX_CORE API!"; - error( RtMidiError::WARNING, errorString_ ); - + CoreMidiData *data = static_cast + (apiData_); + if (!data) { + return NULL; + } + if (local) { + if (data && data->localEndpoint) { + return Pointer(new + CorePortDescriptor(data->localEndpoint, data->getClientName())); + } + } else { + if (data->getEndpoint()) { + return Pointer(new CorePortDescriptor(*data)); + } + } + return NULL; } -void MidiOutCore :: openVirtualPort( const std::string &portName ) +PortList MidiInCore :: getPortList(int capabilities) { CoreMidiData *data = static_cast (apiData_); - - if ( data->endpoint ) { - errorString_ = "MidiOutCore::openVirtualPort: a virtual output port already exists!"; - error( RtMidiError::WARNING, errorString_ ); - return; + try { + return CorePortDescriptor::getPortList(capabilities | PortDescriptor::INPUT, + data->getClientName()); + } catch (Error & e) { + error(e); + return PortList(); } +} - // Create a virtual MIDI output source. +void MidiInCore :: closePort( void ) +{ + CoreMidiData *data = static_cast (apiData_); + + if ( data->localPort ) { + MIDIPortDispose( data->localPort ); + data->localPort = 0; + } + + if (data->localEndpoint) { + MIDIEndpointDispose( data->localEndpoint ); + data->localEndpoint = 0; + } + + connected_ = false; +} + +void MidiInCore :: setClientName ( const std::string& ) +{ + error(RTMIDI_ERROR(gettext_noopt("Setting client names is not implemented for Mac OS X CoreMIDI."), + Error::WARNING)); +} + +void MidiInCore :: setPortName ( const std::string& ) +{ + error(RTMIDI_ERROR(gettext_noopt("Setting port names is not implemented for Mac OS X CoreMIDI."), + Error::WARNING)); +} + +unsigned int MidiInCore :: getPortCount() +{ + CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); + return MIDIGetNumberOfSources(); +} + +std::string MidiInCore :: getPortName( unsigned int portNumber ) +{ + CFStringRef nameRef; + MIDIEndpointRef portRef; + char name[128]; + + std::string stringName; + CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); + if ( portNumber >= MIDIGetNumberOfSources() ) { + error(RTMIDI_ERROR1(gettext_noopt("The 'portNumber' argument (%d) is invalid."), + Error::WARNING, portNumber)); + return stringName; + } + + portRef = MIDIGetSource( portNumber ); + nameRef = ConnectedEndpointName(portRef); + CFStringGetCString( nameRef, name, sizeof(name), kCFStringEncodingUTF8); + CFRelease( nameRef ); + + return stringName = name; +} +#undef RTMIDI_CLASSNAME + + +//*********************************************************************// +// API: OS-X +// Class Definitions: MidiOutCore +//*********************************************************************// + +#define RTMIDI_CLASSNAME "MidiOutCore" +MidiOutCore :: MidiOutCore( const std::string &clientName ) : MidiOutApi() +{ + MidiOutCore::initialize( clientName ); +} + +MidiOutCore :: ~MidiOutCore( void ) +{ + // Close a connection if it exists. + MidiOutCore::closePort(); + + // Cleanup. + CoreMidiData *data = static_cast (apiData_); + delete data; +} + +void MidiOutCore :: initialize( const std::string &clientName ) +{ + // Save our api-specific connection information. + CoreMidiData *data = (CoreMidiData *) new CoreMidiData(clientName); + apiData_ = (void *) data; +} + +unsigned int MidiOutCore :: getPortCount() +{ + CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); + return MIDIGetNumberOfDestinations(); +} + +std::string MidiOutCore :: getPortName( unsigned int portNumber ) +{ + CFStringRef nameRef; + MIDIEndpointRef portRef; + char name[128]; + + std::string stringName; + CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); + if ( portNumber >= MIDIGetNumberOfDestinations() ) { + error(RTMIDI_ERROR1(gettext_noopt("The 'portNumber' argument (%d) is invalid."), + Error::WARNING, portNumber) ); + return stringName; + } + + portRef = MIDIGetDestination( portNumber ); + nameRef = ConnectedEndpointName(portRef); + CFStringGetCString( nameRef, name, sizeof(name), kCFStringEncodingUTF8 ); + CFRelease( nameRef ); + + return stringName = name; +} + +void MidiOutCore :: openPort( unsigned int portNumber, + const std::string &portName ) +{ + if ( connected_ ) { + error(RTMIDI_ERROR(gettext_noopt("A valid connection already exists."), + Error::WARNING) ); + return; + } + + CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); + unsigned int nDest = MIDIGetNumberOfDestinations(); + if (nDest < 1) { + error(RTMIDI_ERROR(gettext_noopt("No MIDI output destinations found."), + Error::NO_DEVICES_FOUND) ); + return; + } + + if ( portNumber >= nDest ) { + error(RTMIDI_ERROR1(gettext_noopt("The 'portNumber' argument (%d) is invalid."), + Error::INVALID_PARAMETER, portNumber) ); + return; + } + + MIDIPortRef port; + CoreMidiData *data = static_cast (apiData_); + OSStatus result = MIDIOutputPortCreate( data->client, + CFStringWrapper( portName ), + &port ); + if ( result != noErr ) { + MIDIClientDispose( data->client ); + error(RTMIDI_ERROR(gettext_noopt("Error creating OS-X MIDI output port."), + Error::DRIVER_ERROR) ); + return; + } + + // Get the desired output port identifier. + MIDIEndpointRef destination = MIDIGetDestination( portNumber ); + if ( destination == 0 ) { + MIDIPortDispose( port ); + port = 0; + MIDIClientDispose( data->client ); + error(RTMIDI_ERROR(gettext_noopt("Error getting MIDI output destination reference."), + Error::DRIVER_ERROR) ); + return; + } + + // Save our api-specific connection information. + data->localPort = port; + data->setEndpoint(destination); + connected_ = true; +} + +void MidiOutCore :: closePort( void ) +{ + CoreMidiData *data = static_cast (apiData_); + + if ( data->localPort ) { + MIDIPortDispose( data->localPort ); + data->localPort = 0; + } + + if (data->localEndpoint) { + MIDIEndpointDispose( data->localEndpoint ); + data->localEndpoint = 0; + } + + connected_ = false; +} + +void MidiOutCore :: setClientName ( const std::string& ) +{ + error(RTMIDI_ERROR(gettext_noopt("Setting client names is not implemented for Mac OS X CoreMIDI."), + Error::WARNING)); +} + +void MidiOutCore :: setPortName ( const std::string& ) +{ + error(RTMIDI_ERROR(gettext_noopt("Setting port names is not implemented for Mac OS X CoreMIDI."), + Error::WARNING)); +} + +void MidiOutCore :: openVirtualPort( const std::string &portName ) +{ + CoreMidiData *data = static_cast (apiData_); + + if ( data->localEndpoint ) { + error(RTMIDI_ERROR(gettext_noopt("A virtual output port already exists."), + Error::WARNING) ); + return; + } + + // Create a virtual MIDI output source. MIDIEndpointRef endpoint; - CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); OSStatus result = MIDISourceCreate( data->client, - portNameRef, - &endpoint ); - CFRelease( portNameRef ); - + CFStringWrapper( portName ), + &endpoint ); if ( result != noErr ) { - errorString_ = "MidiOutCore::initialize: error creating OS-X virtual MIDI source."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("Error creating OS-X virtual MIDI source."), + Error::DRIVER_ERROR) ); return; } // Save our api-specific connection information. - data->endpoint = endpoint; + data->localEndpoint = endpoint; +} + +void MidiOutCore :: openPort( const PortDescriptor & port, + const std::string &portName) +{ + CoreMidiData *data = static_cast (apiData_); + const CorePortDescriptor * remote = dynamic_cast(&port); + + if ( !data ) { + error(RTMIDI_ERROR(gettext_noopt("Data has not been allocated."), + Error::SYSTEM_ERROR) ); + return; + } + if ( connected_ || data -> localEndpoint) { + error(RTMIDI_ERROR(gettext_noopt("A valid connection already exists."), + Error::WARNING) ); + return; + } + if (!remote) { + error(RTMIDI_ERROR(gettext_noopt("Core MIDI has been instructed to open a non-Core MIDI port. This doesn't work."), + Error::INVALID_DEVICE) ); + return; + } + + try { + data->openPort (portName, + PortDescriptor::OUTPUT); + data->setRemote(*remote); + connected_ = true; + } catch (Error & e) { + error(e); + } +} + +Pointer MidiOutCore :: getDescriptor(bool local) +{ + CoreMidiData *data = static_cast + (apiData_); + if (!data) { + return NULL; + } + try { + if (local) { + if (data && data->localEndpoint) { + return Pointer( + new CorePortDescriptor(data->localEndpoint, data->getClientName())); + } + } else { + if (data->getEndpoint()) { + return Pointer( + new CorePortDescriptor(*data)); + } + } + } catch (Error & e) { + error(e); + } + return NULL; +} + +PortList MidiOutCore :: getPortList(int capabilities) +{ + CoreMidiData *data = static_cast (apiData_); + try { + return CorePortDescriptor::getPortList(capabilities | PortDescriptor::OUTPUT, + data->getClientName()); + } catch (Error & e) { + error(e); + return PortList(); + } } + +// Not necessary if we don't treat sysex messages any differently than +// normal messages ... see below. +//static void sysexCompletionProc( MIDISysexSendRequest *sreq ) +//{ +// free( sreq ); +//} + void MidiOutCore :: sendMessage( const unsigned char *message, size_t size ) { // We use the MIDISendSysex() function to asynchronously send sysex - // messages. Otherwise, we use a single CoreMidi MIDIPacket. - unsigned int nBytes = static_cast (size); - if ( nBytes == 0 ) { - errorString_ = "MidiOutCore::sendMessage: no data in message argument!"; - error( RtMidiError::WARNING, errorString_ ); + // messages. Otherwise, we use a single CoreMIDI MIDIPacket. + if ( size == 0 ) { + error(RTMIDI_ERROR(gettext_noopt("No data in message argument."), + Error::WARNING)); return; } + // unsigned int packetBytes, bytesLeft = size; + // unsigned int messageIndex = 0; MIDITimeStamp timeStamp = AudioGetCurrentHostTime(); CoreMidiData *data = static_cast (apiData_); OSStatus result; - if ( message[0] != 0xF0 && nBytes > 3 ) { - errorString_ = "MidiOutCore::sendMessage: message format problem ... not sysex but > 3 bytes?"; - error( RtMidiError::WARNING, errorString_ ); + if ( message[0] != 0xF0 && size > 3 ) { + error(RTMIDI_ERROR(gettext_noopt("message format problem ... not sysex but > 3 bytes?"), + Error::WARNING )); return; } - Byte buffer[nBytes+(sizeof(MIDIPacketList))]; + Byte buffer[size+(sizeof(MIDIPacketList))]; ByteCount listSize = sizeof(buffer); MIDIPacketList *packetList = (MIDIPacketList*)buffer; MIDIPacket *packet = MIDIPacketListInit( packetList ); - ByteCount remainingBytes = nBytes; + ByteCount remainingBytes = size; while (remainingBytes && packet) { ByteCount bytesForPacket = remainingBytes > 65535 ? 65535 : remainingBytes; // 65535 = maximum size of a MIDIPacket - const Byte* dataStartPtr = (const Byte *) &message[nBytes - remainingBytes]; + const Byte* dataStartPtr = (const Byte *) &message[size - remainingBytes]; packet = MIDIPacketListAdd( packetList, listSize, packet, timeStamp, bytesForPacket, dataStartPtr); - remainingBytes -= bytesForPacket; + remainingBytes -= bytesForPacket; } if ( !packet ) { - errorString_ = "MidiOutCore::sendMessage: could not allocate packet list"; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("Could not allocate packet list."), + Error::DRIVER_ERROR) ); return; } // Send to any destinations that may have connected to us. - if ( data->endpoint ) { - result = MIDIReceived( data->endpoint, packetList ); + if ( data->localEndpoint ) { + result = MIDIReceived( data->localEndpoint, packetList ); if ( result != noErr ) { - errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations."; - error( RtMidiError::WARNING, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("Error sending MIDI to virtual destinations."), + Error::WARNING) ); } } // And send to an explicit destination port if we're connected. if ( connected_ ) { - result = MIDISend( data->port, data->destinationId, packetList ); + result = MIDISend( data->localPort, data->getEndpoint(), packetList ); if ( result != noErr ) { - errorString_ = "MidiOutCore::sendMessage: error sending MIDI message to port."; - error( RtMidiError::WARNING, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("Error sending MIDI message to port."), + Error::WARNING) ); } } } - -#endif // __MACOSX_CORE__ +#undef RTMIDI_CLASSNAME +RTMIDI_NAMESPACE_END +#endif // __MACOSX_COREMIDI__ //*********************************************************************// @@ -1426,79 +2677,622 @@ void MidiOutCore :: sendMessage( const unsigned char *message, size_t size ) // ALSA header file. #include -// A structure to hold variables related to the ALSA API -// implementation. -struct AlsaMidiData { - snd_seq_t *seq; - unsigned int portNum; - int vport; - snd_seq_port_subscribe_t *subscription; - snd_midi_event_t *coder; - unsigned int bufferSize; - unsigned char *buffer; - pthread_t thread; - pthread_t dummy_thread_id; - snd_seq_real_time_t lastTime; - int queue_id; // an input queue is needed to get timestamped events - int trigger_fds[2]; -}; - -#define PORT_TYPE( pinfo, bits ) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits)) +RTMIDI_NAMESPACE_START +struct AlsaMidiData; -//*********************************************************************// -// API: LINUX ALSA -// Class Definitions: MidiInAlsa -//*********************************************************************// +/*! An abstraction layer for the ALSA sequencer layer. It provides + the following functionality: + - dynamic allocation of the sequencer + - optionallay avoid concurrent access to the ALSA sequencer, + which is not thread proof. This feature is controlled by + the parameter \ref locking. +*/ -static void *alsaMidiHandler( void *ptr ) -{ - MidiInApi::RtMidiInData *data = static_cast (ptr); - AlsaMidiData *apiData = static_cast (data->apiData); +#define RTMIDI_CLASSNAME "AlsaSequencer" +template +class AlsaSequencer { +public: + AlsaSequencer():seq(0) + { + if (locking) { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); + pthread_mutex_init(&mutex, &attr); + } + } - long nBytes; - double time; - bool continueSysex = false; - bool doDecode = false; - MidiInApi::MidiMessage message; - int poll_fd_count; - struct pollfd *poll_fds; + AlsaSequencer(const std::string &n):seq(0),name(n) + { + if (locking) { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); + pthread_mutex_init(&mutex, &attr); + } + init(); + { + scoped_lock lock(mutex); + snd_seq_set_client_name( seq, name.c_str() ); + } + } - snd_seq_event_t *ev; - int result; - apiData->bufferSize = 32; - result = snd_midi_event_new( 0, &apiData->coder ); - if ( result < 0 ) { - data->doInput = false; - std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing MIDI event parser!\n\n"; - return 0; + ~AlsaSequencer() + { + if (seq) { + scoped_lock lock(mutex); + snd_seq_close(seq); + seq = 0; + } + if (locking) { + pthread_mutex_destroy(&mutex); + } } - unsigned char *buffer = (unsigned char *) malloc( apiData->bufferSize ); - if ( buffer == NULL ) { - data->doInput = false; - snd_midi_event_free( apiData->coder ); - apiData->coder = 0; - std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing buffer memory!\n\n"; + + int setName(const std::string &n) { + /* we don't want to rename the client after opening it. */ + name = n; + if (seq) { + return snd_seq_set_client_name( seq, name.c_str() ); + } return 0; } - snd_midi_event_init( apiData->coder ); - snd_midi_event_no_status( apiData->coder, 1 ); // suppress running status messages - poll_fd_count = snd_seq_poll_descriptors_count( apiData->seq, POLLIN ) + 1; - poll_fds = (struct pollfd*)alloca( poll_fd_count * sizeof( struct pollfd )); - snd_seq_poll_descriptors( apiData->seq, poll_fds + 1, poll_fd_count - 1, POLLIN ); - poll_fds[0].fd = apiData->trigger_fds[0]; - poll_fds[0].events = POLLIN; + std::string GetPortName(int client, int port, int flags) { + init(); + snd_seq_client_info_t *cinfo; + snd_seq_client_info_alloca( &cinfo ); + { + scoped_lock lock (mutex); + snd_seq_get_any_client_info(seq,client,cinfo); + } - while ( data->doInput ) { + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + { + scoped_lock lock (mutex); + snd_seq_get_any_port_info(seq,client,port,pinfo); + } - if ( snd_seq_event_input_pending( apiData->seq, 1 ) == 0 ) { + int naming = flags & PortDescriptor::NAMING_MASK; + + std::ostringstream os; + switch (naming) { + case PortDescriptor::SESSION_PATH: + if (flags & PortDescriptor::INCLUDE_API) + os << "ALSA:"; + os << client << ":" << port; + break; + case PortDescriptor::STORAGE_PATH: + if (flags & PortDescriptor::INCLUDE_API) + os << "ALSA:"; + os << snd_seq_client_info_get_name(cinfo); + os << ":"; + os << snd_seq_port_info_get_name(pinfo); + if (flags & PortDescriptor::UNIQUE_PORT_NAME) + os << ";" << client << ":" << port; + break; + case PortDescriptor::LONG_NAME: + os << snd_seq_client_info_get_name( cinfo ); + if (flags & PortDescriptor::UNIQUE_PORT_NAME) { + os << " " << client; + } + os << ":"; + if (flags & PortDescriptor::UNIQUE_PORT_NAME) { + os << port; + } + + os << " " << snd_seq_port_info_get_name(pinfo); + if (flags & PortDescriptor::INCLUDE_API) + os << " (ALSA)"; + break; + case PortDescriptor::SHORT_NAME: + default: + os << snd_seq_client_info_get_name( cinfo ); + if (flags & PortDescriptor::UNIQUE_PORT_NAME) { + os << " "; + os << client; + } + os << ":" << port; + if (flags & PortDescriptor::INCLUDE_API) + os << " (ALSA)"; + + break; + } + return os.str(); + } + + void setPortName(const std::string &name, int port) { + snd_seq_port_info_t *pinfo = NULL; + int error; + snd_seq_port_info_alloca( &pinfo ); + if (pinfo == NULL) { + throw RTMIDI_ERROR(gettext_noopt("Could not allocate ALSA port info structure."), + Error::MEMORY_ERROR); + } + if ((error = snd_seq_get_port_info( seq, port, pinfo ))<0) { + throw RTMIDI_ERROR1(gettext_noopt("Could not get ALSA port information: %s"), + Error::DRIVER_ERROR, + snd_strerror(error)); + } + snd_seq_port_info_set_name( pinfo, name.c_str() ); + if ((error = snd_seq_set_port_info( seq, port, pinfo ) )) { + throw RTMIDI_ERROR1(gettext_noopt("Could not set ALSA port information: %s"), + Error::DRIVER_ERROR, + snd_strerror(error)); + } + } + + int getPortCapabilities(int client, int port) { + init(); + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + { + scoped_lock lock (mutex); + snd_seq_get_any_port_info(seq,client,port,pinfo); + } + unsigned int caps = snd_seq_port_info_get_capability(pinfo); + int retval = (caps & (SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ))? + PortDescriptor::INPUT:0; + if (caps & (SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE)) + retval |= PortDescriptor::OUTPUT; + return retval; + } + + int getNextClient(snd_seq_client_info_t * cinfo ) { + init(); + scoped_lock lock (mutex); + return snd_seq_query_next_client (seq, cinfo); + } + + int getNextPort(snd_seq_port_info_t * pinfo ) { + init(); + scoped_lock lock (mutex); + return snd_seq_query_next_port (seq, pinfo); + } + + int createPort (snd_seq_port_info_t *pinfo) { + init(); + scoped_lock lock (mutex); + return snd_seq_create_port(seq, pinfo); + } + + void deletePort(int port) { + init(); + scoped_lock lock (mutex); + snd_seq_delete_port( seq, port ); + } + + snd_seq_port_subscribe_t * connectPorts(const snd_seq_addr_t & from, + const snd_seq_addr_t & to, + bool real_time) { + init(); + snd_seq_port_subscribe_t *subscription; + + if (snd_seq_port_subscribe_malloc( &subscription ) < 0) { + throw RTMIDI_ERROR(gettext_noopt("Could not allocate ALSA port subscription."), + Error::DRIVER_ERROR ); + return 0; + } + snd_seq_port_subscribe_set_sender(subscription, &from); + snd_seq_port_subscribe_set_dest(subscription, &to); + if (real_time) { + snd_seq_port_subscribe_set_time_update(subscription, 1); + snd_seq_port_subscribe_set_time_real(subscription, 1); + } + { + scoped_lock lock (mutex); + if ( snd_seq_subscribe_port(seq, subscription) ) { + snd_seq_port_subscribe_free( subscription ); + subscription = 0; + throw RTMIDI_ERROR(gettext_noopt("Error making ALSA port connection."), + Error::DRIVER_ERROR); + return 0; + } + } + return subscription; + } + + void closePort(snd_seq_port_subscribe_t * subscription ) { + init(); + scoped_lock lock(mutex); + snd_seq_unsubscribe_port( seq, subscription ); + } + + void startQueue(int queue_id) { + init(); + scoped_lock lock(mutex); + snd_seq_start_queue( seq, queue_id, NULL ); + snd_seq_drain_output( seq ); + } + + /*! Use AlsaSequencer like a C pointer. + \note This function breaks the design to control thread safety + by the selection of the \ref locking parameter to the class. + It should be removed as soon as possible in order ensure the + thread policy that has been intended by creating this class. + */ + operator snd_seq_t * () + { + return seq; + } +protected: + struct scoped_lock { + pthread_mutex_t * mutex; + scoped_lock(pthread_mutex_t & m): mutex(&m) + { + if (locking) + pthread_mutex_lock(mutex); + } + ~scoped_lock() + { + if (locking) + pthread_mutex_unlock(mutex); + } + }; + pthread_mutex_t mutex; + snd_seq_t * seq; + std::string name; + + + snd_seq_client_info_t * GetClient(int id) { + init(); + snd_seq_client_info_t * cinfo; + scoped_lock lock(mutex); + snd_seq_get_any_client_info(seq,id,cinfo); + return cinfo; + } + + void init() + { + init (seq); + } + + void init(snd_seq_t * &s) + { + if (s) return; + { + scoped_lock lock(mutex); + int result = snd_seq_open(&s, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK); + if ( result < 0 ) { + switch (result) { + case -ENOENT: // /dev/snd/seq does not exist + // Error numbers are defined to be positive + case ENOENT: // just in case ... + throw RTMIDI_ERROR(snd_strerror(result), + Error::NO_DEVICES_FOUND); + return; + default: + std::cerr << __FILE__ << ":" << __LINE__ + << "Got unhandled error number " << result << std::endl; + throw RTMIDI_ERROR(snd_strerror(result), + Error::DRIVER_ERROR ); + return; + } + } + snd_seq_set_client_name( seq, name.c_str() ); + } + } +}; +#undef RTMIDI_CLASSNAME +typedef AlsaSequencer<1> LockingAlsaSequencer; +typedef AlsaSequencer<0> NonLockingAlsaSequencer; + +#define RTMIDI_CLASSNAME "AlsaPortDescriptor" +struct AlsaPortDescriptor:public PortDescriptor, + public snd_seq_addr_t +{ + MidiApi * api; + static LockingAlsaSequencer seq; + AlsaPortDescriptor(const std::string &name):api(0),clientName(name) + { + client = 0; + port = 0; + } + AlsaPortDescriptor(int c, int p, const std::string &name):api(0),clientName(name) + { + client = c; + port = p; + seq.setName(name); + } + AlsaPortDescriptor(snd_seq_addr_t & other, + const std::string &name):snd_seq_addr_t(other), + clientName(name) { + seq.setName(name); + } + ~AlsaPortDescriptor() {} + MidiInApi * getInputApi(unsigned int queueSizeLimit = 100) const { + if (getCapabilities() & INPUT) + return new MidiInAlsa(clientName,queueSizeLimit); + else + return 0; + } + MidiOutApi * getOutputApi() const { + if (getCapabilities() & OUTPUT) + return new MidiOutAlsa(clientName); + else + return 0; + } + std::string getName(int flags = SHORT_NAME | UNIQUE_PORT_NAME) { + return seq.GetPortName(client,port,flags); + } + + const std::string &getClientName() { + return clientName; + } + + int getCapabilities() const { + if (!client) return 0; + return seq.getPortCapabilities(client,port); + } + static PortList getPortList(int capabilities, const std::string &clientName); +protected: + std::string clientName; +}; + +LockingAlsaSequencer AlsaPortDescriptor::seq; + + + +PortList AlsaPortDescriptor :: getPortList(int capabilities, const std::string &clientName) +{ + PortList list; + snd_seq_client_info_t *cinfo; + snd_seq_port_info_t *pinfo; + int client; + snd_seq_client_info_alloca( &cinfo ); + snd_seq_port_info_alloca( &pinfo ); + + snd_seq_client_info_set_client( cinfo, -1 ); + while ( seq.getNextClient(cinfo ) >= 0 ) { + client = snd_seq_client_info_get_client( cinfo ); + // ignore default device (it is included in the following results again) + if ( client == 0 ) continue; + // Reset query info + snd_seq_port_info_set_client( pinfo, client ); + snd_seq_port_info_set_port( pinfo, -1 ); + while ( seq.getNextPort( pinfo ) >= 0 ) { + unsigned int atyp = snd_seq_port_info_get_type( pinfo ); + // otherwise we get ports without any + if ( !(capabilities & UNLIMITED) && + !( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) && + !( atyp & SND_SEQ_PORT_TYPE_SYNTH ) + ) continue; + unsigned int caps = snd_seq_port_info_get_capability( pinfo ); + if (capabilities & INPUT) { + /* we need both READ and SUBS_READ */ + if ((caps & (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ)) + != (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ)) + continue; + } + if (capabilities & OUTPUT) { + /* we need both WRITE and SUBS_WRITE */ + if ((caps & (SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE)) + != (SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE)) + continue; + } + list.push_back(Pointer( + new AlsaPortDescriptor(client,snd_seq_port_info_get_port(pinfo),clientName))); + } + } + return list; +} +#undef RTMIDI_CLASSNAME + + +/*! A structure to hold variables related to the ALSA API + implementation. + + \note After all sequencer handling is covered by the \ref + AlsaSequencer class, we should make seq to be a pointer in order + to allow a common client implementation. +*/ +#define RTMIDI_CLASSNAME "AlsaMidiData" + +struct AlsaMidiData:public AlsaPortDescriptor { + /* + AlsaMidiData():seq() + { + init(); + } + */ + AlsaMidiData(const std::string &clientName):AlsaPortDescriptor(clientName), + seq(clientName) + { + init(); + } + ~AlsaMidiData() + { + if (local.client) + deletePort(); + } + void init () { + local.port = 0; + local.client = 0; + port = -1; + subscription = 0; + coder = 0; + bufferSize = 32; + buffer = 0; + dummy_thread_id = pthread_self(); + thread = dummy_thread_id; + queue_id = -1; + trigger_fds[0] = -1; + trigger_fds[1] = -1; + } + snd_seq_addr_t local; /*!< Our port and client id. If client = 0 (default) this means we didn't aquire a port so far. */ + NonLockingAlsaSequencer seq; + // unsigned int portNum; + snd_seq_port_subscribe_t *subscription; + snd_midi_event_t *coder; + unsigned int bufferSize; + unsigned char *buffer; + pthread_t thread; + pthread_t dummy_thread_id; + snd_seq_real_time_t lastTime; + int queue_id; // an input queue is needed to get timestamped events + int trigger_fds[2]; + + void setRemote(const AlsaPortDescriptor * remote) { + port = remote->port; + client = remote->client; + } + void connectPorts(const snd_seq_addr_t &from, + const snd_seq_addr_t &to, + bool real_time) { + subscription = seq.connectPorts(from, to, real_time); + } + + int openPort(int alsaCapabilities, + const std::string &portName) { + if (subscription) { + api->error( RTMIDI_ERROR(gettext_noopt("Could not allocate ALSA port subscription."), + Error::DRIVER_ERROR )); + return -99; + } + + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + + snd_seq_port_info_set_client( pinfo, 0 ); + snd_seq_port_info_set_port( pinfo, 0 ); + snd_seq_port_info_set_capability( pinfo, + alsaCapabilities); + snd_seq_port_info_set_type( pinfo, + SND_SEQ_PORT_TYPE_MIDI_GENERIC | + SND_SEQ_PORT_TYPE_APPLICATION ); + snd_seq_port_info_set_midi_channels(pinfo, 16); +#ifndef AVOID_TIMESTAMPING + snd_seq_port_info_set_timestamping(pinfo, 1); + snd_seq_port_info_set_timestamp_real(pinfo, 1); + snd_seq_port_info_set_timestamp_queue(pinfo, queue_id); +#endif + snd_seq_port_info_set_name(pinfo, portName.c_str() ); + int createok = seq.createPort(pinfo); + + if ( createok < 0 ) { + api->error(RTMIDI_ERROR("ALSA error while creating input port.", + Error::DRIVER_ERROR)); + return createok; + } + + local.client = snd_seq_port_info_get_client( pinfo ); + local.port = snd_seq_port_info_get_port(pinfo); + return 0; + } + + void deletePort() { + seq.deletePort(local.port); + local.client = 0; + local.port = 0; + } + + void closePort() { + seq.closePort(subscription ); + snd_seq_port_subscribe_free( subscription ); + subscription = 0; + } + + void setName(const std::string &name) { + seq.setPortName(name, local.port); + } + + void setClientName(const std::string &name) { + seq.setName(name); + } + + bool startQueue(void * userdata) { + // Start the input queue +#ifndef AVOID_TIMESTAMPING + seq.startQueue(queue_id); +#endif + // Start our MIDI input thread. + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + pthread_attr_setschedpolicy(&attr, SCHED_OTHER); + + int err = pthread_create(&thread, &attr, MidiInAlsa::alsaMidiHandler, userdata); + pthread_attr_destroy(&attr); + if ( err ) { + closePort(); + api->error(RTMIDI_ERROR(gettext_noopt("Error starting MIDI input thread!"), + Error::THREAD_ERROR)); + return false; + } + return true; + } +}; +#undef RTMIDI_CLASSNAME + +#define PORT_TYPE( pinfo, bits ) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits)) + +//*********************************************************************// +// API: LINUX ALSA +// Class Definitions: MidiInAlsa +//*********************************************************************// + +#define RTMIDI_CLASSNAME "MidiInAlsa" +// static function: +void * MidiInAlsa::alsaMidiHandler( void *ptr ) throw() +{ + MidiInAlsa *data = static_cast (ptr); + AlsaMidiData *apiData = static_cast (data->apiData_); + + long nBytes; + double time; + bool continueSysex = false; + bool doDecode = false; + MidiInApi::MidiMessage message; + int poll_fd_count; + struct pollfd *poll_fds; + + snd_seq_event_t *ev; + int result; + apiData->bufferSize = 32; + result = snd_midi_event_new( 0, &apiData->coder ); + if ( result < 0 ) { + data->doInput = false; + try { + data->error(RTMIDI_ERROR(rtmidi_gettext("Error initializing MIDI event parser."), + Error::WARNING)); + } catch (Error & e) { + // don't bother ALSA with an unhandled exception + } + return 0; + } + unsigned char *buffer = (unsigned char *) malloc( apiData->bufferSize ); + if ( buffer == NULL ) { + data->doInput = false; + snd_midi_event_free( apiData->coder ); + apiData->coder = 0; + try { + data->error(RTMIDI_ERROR(rtmidi_gettext("Error initializing buffer memory."), + Error::WARNING)); + } catch (Error & e) { + // don't bother ALSA with an unhandled exception + } + return 0; + } + snd_midi_event_init( apiData->coder ); + snd_midi_event_no_status( apiData->coder, 1 ); // suppress running status messages + + poll_fd_count = snd_seq_poll_descriptors_count( apiData->seq, POLLIN ) + 1; + poll_fds = (struct pollfd*)alloca( poll_fd_count * sizeof( struct pollfd )); + snd_seq_poll_descriptors( apiData->seq, poll_fds + 1, poll_fd_count - 1, POLLIN ); + poll_fds[0].fd = apiData->trigger_fds[0]; + poll_fds[0].events = POLLIN; + + while ( data->doInput ) { + + if ( snd_seq_event_input_pending( apiData->seq, 1 ) == 0 ) { // No data pending if ( poll( poll_fds, poll_fd_count, -1) >= 0 ) { - if ( poll_fds[0].revents & POLLIN ) { - bool dummy; - int res = read( poll_fds[0].fd, &dummy, sizeof(dummy) ); - (void) res; - } + if ( poll_fds[0].revents & POLLIN ) { + bool dummy; + int res = read( poll_fds[0].fd, &dummy, sizeof(dummy) ); + (void) res; + } } continue; } @@ -1506,12 +3300,33 @@ static void *alsaMidiHandler( void *ptr ) // If here, there should be data. result = snd_seq_event_input( apiData->seq, &ev ); if ( result == -ENOSPC ) { - std::cerr << "\nMidiInAlsa::alsaMidiHandler: MIDI input buffer overrun!\n\n"; + try { + data->error(RTMIDI_ERROR(rtmidi_gettext("MIDI input buffer overrun."), + Error::WARNING)); + } catch (Error & e) { + // don't bother ALSA with an unhandled exception + } + + continue; + } + else if ( result == -EAGAIN ) { + try { + data->error(RTMIDI_ERROR(rtmidi_gettext("ALSA returned without providing a MIDI event."), + Error::WARNING)); + } catch (Error & e) { + // don't bother ALSA with an unhandled exception + } + continue; } else if ( result <= 0 ) { - std::cerr << "\nMidiInAlsa::alsaMidiHandler: unknown MIDI input error!\n"; - perror("System reports"); + try { + data->error(RTMIDI_ERROR1(rtmidi_gettext("Unknown MIDI input error.\nThe system reports:\n%s"), + Error::WARNING, + strerror(-result))); + } catch (Error & e) { + // don't bother ALSA with an unhandled exception + } continue; } @@ -1532,10 +3347,10 @@ static void *alsaMidiHandler( void *ptr ) #if defined(__RTMIDI_DEBUG__) std::cerr << "MidiInAlsa::alsaMidiHandler: port connection has closed!\n"; std::cout << "sender = " << (int) ev->data.connect.sender.client << ":" - << (int) ev->data.connect.sender.port - << ", dest = " << (int) ev->data.connect.dest.client << ":" - << (int) ev->data.connect.dest.port - << std::endl; + << (int) ev->data.connect.sender.port + << ", dest = " << (int) ev->data.connect.dest.client << ":" + << (int) ev->data.connect.dest.port + << std::endl; #endif break; @@ -1555,20 +3370,24 @@ static void *alsaMidiHandler( void *ptr ) if ( !( data->ignoreFlags & 0x04 ) ) doDecode = true; break; - case SND_SEQ_EVENT_SYSEX: + case SND_SEQ_EVENT_SYSEX: if ( (data->ignoreFlags & 0x01) ) break; if ( ev->data.ext.len > apiData->bufferSize ) { - apiData->bufferSize = ev->data.ext.len; - free( buffer ); - buffer = (unsigned char *) malloc( apiData->bufferSize ); - if ( buffer == NULL ) { - data->doInput = false; - std::cerr << "\nMidiInAlsa::alsaMidiHandler: error resizing buffer memory!\n\n"; - break; - } + apiData->bufferSize = ev->data.ext.len; + free( buffer ); + buffer = (unsigned char *) malloc( apiData->bufferSize ); + if ( buffer == NULL ) { + data->doInput = false; + try { + data->error(RTMIDI_ERROR(rtmidi_gettext("Error resizing buffer memory."), + Error::WARNING)); + } catch (Error & e) { + // don't bother ALSA with an unhandled exception + } + break; + } } - break; - + RTMIDI_FALLTHROUGH; default: doDecode = true; } @@ -1577,75 +3396,89 @@ static void *alsaMidiHandler( void *ptr ) nBytes = snd_midi_event_decode( apiData->coder, buffer, apiData->bufferSize, ev ); if ( nBytes > 0 ) { - // The ALSA sequencer has a maximum buffer size for MIDI sysex - // events of 256 bytes. If a device sends sysex messages larger - // than this, they are segmented into 256 byte chunks. So, - // we'll watch for this and concatenate sysex chunks into a - // single sysex message if necessary. - if ( !continueSysex ) - message.bytes.assign( buffer, &buffer[nBytes] ); - else - message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] ); - - continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) ); - if ( !continueSysex ) { - - // Calculate the time stamp: - message.timeStamp = 0.0; - - // Method 1: Use the system time. - //(void)gettimeofday(&tv, (struct timezone *)NULL); - //time = (tv.tv_sec * 1000000) + tv.tv_usec; - - // Method 2: Use the ALSA sequencer event time data. - // (thanks to Pedro Lopez-Cabanillas!). - - // Using method from: - // https://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html - - // Perform the carry for the later subtraction by updating y. - snd_seq_real_time_t &x(ev->time.time); - snd_seq_real_time_t &y(apiData->lastTime); - if (x.tv_nsec < y.tv_nsec) { - int nsec = (y.tv_nsec - x.tv_nsec) / 1000000000 + 1; - y.tv_nsec -= 1000000000 * nsec; - y.tv_sec += nsec; - } - if (x.tv_nsec - y.tv_nsec > 1000000000) { - int nsec = (x.tv_nsec - y.tv_nsec) / 1000000000; - y.tv_nsec += 1000000000 * nsec; - y.tv_sec -= nsec; - } - - // Compute the time difference. - time = x.tv_sec - y.tv_sec + (x.tv_nsec - y.tv_nsec)*1e-9; - - apiData->lastTime = ev->time.time; - - if ( data->firstMessage == true ) - data->firstMessage = false; - else - message.timeStamp = time; - } - else { + // The ALSA sequencer has a maximum buffer size for MIDI sysex + // events of 256 bytes. If a device sends sysex messages larger + // than this, they are segmented into 256 byte chunks. So, + // we'll watch for this and concatenate sysex chunks into a + // single sysex message if necessary. + if ( !continueSysex ) + message.bytes.assign( buffer, &buffer[nBytes] ); + else + message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] ); + + continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) ); + if ( !continueSysex ) { + + // Calculate the time stamp: + message.timeStamp = 0.0; + + // Method 1: Use the system time. + //(void)gettimeofday(&tv, (struct timezone *)NULL); + //time = (tv.tv_sec * 1000000) + tv.tv_usec; + + // Method 2: Use the ALSA sequencer event time data. + // (thanks to Pedro Lopez-Cabanillas!). + // time = ( ev->time.time.tv_sec * 1000000 ) + ( ev->time.time.tv_nsec/1000 ); + // lastTime = time; + // time -= apiData->lastTime; + // apiData->lastTime = lastTime; + // Using method from: + // https://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html + + // Perform the carry for the later subtraction by updating y. + snd_seq_real_time_t &x(ev->time.time); + snd_seq_real_time_t &y(apiData->lastTime); + if (x.tv_nsec < y.tv_nsec) { + int nsec = (y.tv_nsec - x.tv_nsec) / 1000000000 + 1; + y.tv_nsec -= 1000000000 * nsec; + y.tv_sec += nsec; + } + if (x.tv_nsec - y.tv_nsec > 1000000000) { + int nsec = (x.tv_nsec - y.tv_nsec) / 1000000000; + y.tv_nsec += 1000000000 * nsec; + y.tv_sec -= nsec; + } + + // Compute the time difference. + time = x.tv_sec - y.tv_sec + (x.tv_nsec - y.tv_nsec)*1e-9; + + apiData->lastTime = ev->time.time; + + if ( data->firstMessage == true ) + data->firstMessage = false; + else + message.timeStamp = time * 0.000001; + } + else { #if defined(__RTMIDI_DEBUG__) - std::cerr << "\nMidiInAlsa::alsaMidiHandler: event parsing error or not a MIDI event!\n\n"; + try { + data->error(RTMIDI_ERROR(rtmidi_gettext("Event parsing error or not a MIDI event."), + Error::WARNING)); + } catch (Error & e) { + // don't bother ALSA with an unhandled exception + } #endif - } + } } } snd_seq_free_event( ev ); + ev = 0; if ( message.bytes.size() == 0 || continueSysex ) continue; - if ( data->usingCallback ) { - RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; - callback( message.timeStamp, &message.bytes, data->userData ); + if ( data->userCallback ) { + data->userCallback->rtmidi_midi_in( message.timeStamp, message.bytes); } else { // As long as we haven't reached our queue size limit, push the message. - if (!data->queue.push(message)) - std::cerr << "\nMidiInAlsa: message queue limit reached!!\n\n"; + if (!data->queue.push(message)) { + try { + data->error(RTMIDI_ERROR(rtmidi_gettext("Error: Message queue limit reached."), + Error::WARNING)); + } catch (Error & e) { + // don't bother ALSA with an unhandled exception + } + } } } @@ -1656,7 +3489,8 @@ static void *alsaMidiHandler( void *ptr ) return 0; } -MidiInAlsa :: MidiInAlsa( const std::string &clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) +MidiInAlsa :: MidiInAlsa( const std::string &clientName, + unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { MidiInAlsa::initialize( clientName ); } @@ -1668,9 +3502,9 @@ MidiInAlsa :: ~MidiInAlsa() // Shutdown the input thread. AlsaMidiData *data = static_cast (apiData_); - if ( inputData_.doInput ) { - inputData_.doInput = false; - int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof(inputData_.doInput) ); + if ( doInput ) { + doInput = false; + int res = write( data->trigger_fds[1], &doInput, sizeof(doInput) ); (void) res; if ( !pthread_equal(data->thread, data->dummy_thread_id) ) pthread_join( data->thread, NULL ); @@ -1679,50 +3513,30 @@ MidiInAlsa :: ~MidiInAlsa() // Cleanup. close ( data->trigger_fds[0] ); close ( data->trigger_fds[1] ); - if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); + if ( data->local.client ) data->deletePort(); #ifndef AVOID_TIMESTAMPING snd_seq_free_queue( data->seq, data->queue_id ); + data->queue_id = -1; #endif - snd_seq_close( data->seq ); delete data; } -void MidiInAlsa :: initialize( const std::string& clientName ) +void MidiInAlsa :: initialize( const std::string &clientName ) { - // Set up the ALSA sequencer client. - snd_seq_t *seq; - int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK); - if ( result < 0 ) { - errorString_ = "MidiInAlsa::initialize: error creating ALSA sequencer client object."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - - // Set client name. - snd_seq_set_client_name( seq, clientName.c_str() ); // Save our api-specific connection information. - AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; - data->seq = seq; - data->portNum = -1; - data->vport = -1; - data->subscription = 0; - data->dummy_thread_id = pthread_self(); - data->thread = data->dummy_thread_id; - data->trigger_fds[0] = -1; - data->trigger_fds[1] = -1; + AlsaMidiData *data = new AlsaMidiData (clientName); apiData_ = (void *) data; - inputData_.apiData = (void *) data; - if ( pipe(data->trigger_fds) == -1 ) { - errorString_ = "MidiInAlsa::initialize: error creating pipe objects."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + if ( pipe(data->trigger_fds) == -1 ) { + error(RTMIDI_ERROR(gettext_noopt("Error creating pipe objects."), + Error::DRIVER_ERROR) ); return; } // Create the input queue #ifndef AVOID_TIMESTAMPING - data->queue_id = snd_seq_alloc_named_queue(seq, "RtMidi Queue"); + data->queue_id = snd_seq_alloc_named_queue(data->seq, "Midi Queue"); // Set arbitrary tempo (mm=100) and resolution (240) snd_seq_queue_tempo_t *qtempo; snd_seq_queue_tempo_alloca(&qtempo); @@ -1751,9 +3565,7 @@ unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) { unsigned int atyp = snd_seq_port_info_get_type( pinfo ); if ( ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) && - ( ( atyp & SND_SEQ_PORT_TYPE_SYNTH ) == 0 ) && - ( ( atyp & SND_SEQ_PORT_TYPE_APPLICATION ) == 0 ) ) continue; - + ( ( atyp & SND_SEQ_PORT_TYPE_SYNTH ) == 0 ) ) continue; unsigned int caps = snd_seq_port_info_get_capability( pinfo ); if ( ( caps & type ) != type ) continue; if ( count == portNumber ) return 1; @@ -1800,23 +3612,23 @@ std::string MidiInAlsa :: getPortName( unsigned int portNumber ) } // If we get here, we didn't find a match. - errorString_ = "MidiInAlsa::getPortName: error looking for port name!"; - error( RtMidiError::WARNING, errorString_ ); + error( RTMIDI_ERROR(gettext_noopt("Error looking for port name."), + Error::WARNING) ); return stringName; } void MidiInAlsa :: openPort( unsigned int portNumber, const std::string &portName ) { if ( connected_ ) { - errorString_ = "MidiInAlsa::openPort: a valid connection already exists!"; - error( RtMidiError::WARNING, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("A valid connection already exists."), + Error::WARNING) ); return; } unsigned int nSrc = this->getPortCount(); if ( nSrc < 1 ) { - errorString_ = "MidiInAlsa::openPort: no MIDI input sources found!"; - error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); + error( RTMIDI_ERROR(gettext_noopt("No MIDI input sources found."), + Error::NO_DEVICES_FOUND )); return; } @@ -1825,52 +3637,52 @@ void MidiInAlsa :: openPort( unsigned int portNumber, const std::string &portNam AlsaMidiData *data = static_cast (apiData_); if ( portInfo( data->seq, src_pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) { std::ostringstream ost; - ost << "MidiInAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtMidiError::INVALID_PARAMETER, errorString_ ); + error( RTMIDI_ERROR1(gettext_noopt("The 'portNumber' argument (%d) is invalid."), + Error::INVALID_PARAMETER, + portNumber) ); return; } snd_seq_addr_t sender, receiver; sender.client = snd_seq_port_info_get_client( src_pinfo ); sender.port = snd_seq_port_info_get_port( src_pinfo ); - receiver.client = snd_seq_client_id( data->seq ); snd_seq_port_info_t *pinfo; snd_seq_port_info_alloca( &pinfo ); - if ( data->vport < 0 ) { + if ( !data->local.client ) { snd_seq_port_info_set_client( pinfo, 0 ); snd_seq_port_info_set_port( pinfo, 0 ); snd_seq_port_info_set_capability( pinfo, - SND_SEQ_PORT_CAP_WRITE | - SND_SEQ_PORT_CAP_SUBS_WRITE ); + SND_SEQ_PORT_CAP_WRITE | + SND_SEQ_PORT_CAP_SUBS_WRITE ); snd_seq_port_info_set_type( pinfo, - SND_SEQ_PORT_TYPE_MIDI_GENERIC | - SND_SEQ_PORT_TYPE_APPLICATION ); + SND_SEQ_PORT_TYPE_MIDI_GENERIC | + SND_SEQ_PORT_TYPE_APPLICATION ); snd_seq_port_info_set_midi_channels(pinfo, 16); #ifndef AVOID_TIMESTAMPING snd_seq_port_info_set_timestamping(pinfo, 1); - snd_seq_port_info_set_timestamp_real(pinfo, 1); + snd_seq_port_info_set_timestamp_real(pinfo, 1); snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id); #endif snd_seq_port_info_set_name(pinfo, portName.c_str() ); - data->vport = snd_seq_create_port(data->seq, pinfo); - - if ( data->vport < 0 ) { - errorString_ = "MidiInAlsa::openPort: ALSA error creating input port."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + int createok = snd_seq_create_port(data->seq, pinfo); + + if ( createok < 0 ) { + error( RTMIDI_ERROR(gettext_noopt("Error creating ALSA input port."), + Error::DRIVER_ERROR)); return; } - data->vport = snd_seq_port_info_get_port(pinfo); + data->local.port = snd_seq_port_info_get_port(pinfo); + data->local.client = snd_seq_port_info_get_client(pinfo); } - receiver.port = data->vport; + receiver = data->local; if ( !data->subscription ) { // Make subscription if (snd_seq_port_subscribe_malloc( &data->subscription ) < 0) { - errorString_ = "MidiInAlsa::openPort: ALSA error allocation port subscription."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + error( RTMIDI_ERROR(gettext_noopt("Could not allocate ALSA port subscription."), + Error::DRIVER_ERROR) ); return; } snd_seq_port_subscribe_set_sender(data->subscription, &sender); @@ -1878,13 +3690,13 @@ void MidiInAlsa :: openPort( unsigned int portNumber, const std::string &portNam if ( snd_seq_subscribe_port(data->seq, data->subscription) ) { snd_seq_port_subscribe_free( data->subscription ); data->subscription = 0; - errorString_ = "MidiInAlsa::openPort: ALSA error making port connection."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + error( RTMIDI_ERROR(gettext_noopt("Error making ALSA port connection."), + Error::DRIVER_ERROR) ); return; } } - if ( inputData_.doInput == false ) { + if ( doInput == false ) { // Start the input queue #ifndef AVOID_TIMESTAMPING snd_seq_start_queue( data->seq, data->queue_id, NULL ); @@ -1896,16 +3708,16 @@ void MidiInAlsa :: openPort( unsigned int portNumber, const std::string &portNam pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_attr_setschedpolicy(&attr, SCHED_OTHER); - inputData_.doInput = true; - int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_); + doInput = true; + int err = pthread_create(&data->thread, &attr, alsaMidiHandler, this); pthread_attr_destroy(&attr); if ( err ) { snd_seq_unsubscribe_port( data->seq, data->subscription ); snd_seq_port_subscribe_free( data->subscription ); data->subscription = 0; - inputData_.doInput = false; - errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!"; - error( RtMidiError::THREAD_ERROR, errorString_ ); + doInput = false; + error( RTMIDI_ERROR(gettext_noopt("Error starting MIDI input thread!"), + Error::THREAD_ERROR) ); return; } } @@ -1913,10 +3725,93 @@ void MidiInAlsa :: openPort( unsigned int portNumber, const std::string &portNam connected_ = true; } -void MidiInAlsa :: openVirtualPort( const std::string &portName ) +void MidiInAlsa :: openPort( const PortDescriptor & port, + const std::string &portName) +{ + AlsaMidiData *data = static_cast (apiData_); + const AlsaPortDescriptor * remote = dynamic_cast(&port); + + if ( !data ) { + error( RTMIDI_ERROR(gettext_noopt("Data has not been allocated."), + Error::SYSTEM_ERROR) ); + return; + } + if ( connected_ ) { + error( RTMIDI_ERROR(gettext_noopt("A valid connection already exists."), + Error::WARNING) ); + return; + } + if (data->subscription) { + error( RTMIDI_ERROR(gettext_noopt("Could not allocate ALSA port subscription."), + Error::DRIVER_ERROR)); + return; + } + if (!remote) { + error( RTMIDI_ERROR(gettext_noopt("ALSA has been instructed to open a non-ALSA MIDI port. This doesn't work."), + Error::INVALID_DEVICE) ); + return; + } + + try { + if (!data->local.client) + data->openPort (SND_SEQ_PORT_CAP_WRITE + | SND_SEQ_PORT_CAP_SUBS_WRITE, + portName); + data->setRemote(remote); + data->connectPorts(*remote, + data->local, + false); + + + if ( doInput == false ) { + doInput + = data->startQueue(this); + } + + connected_ = true; + } catch (Error & e) { + error(e); + } +} + +Pointer MidiInAlsa :: getDescriptor(bool local) +{ + AlsaMidiData *data = static_cast (apiData_); + try { + if (local) { + if (data && data->local.client) { + return Pointer( + new AlsaPortDescriptor(data->local,data->getClientName())); + } + } else { + if (data && data->client) { + return Pointer( + new AlsaPortDescriptor(*data,data->getClientName())); + } + } + } catch (Error & e) { + error (e); + } + return NULL; +} +PortList MidiInAlsa :: getPortList(int capabilities) +{ + AlsaMidiData *data = static_cast (apiData_); + try { + return AlsaPortDescriptor::getPortList(capabilities | PortDescriptor::INPUT, + data->getClientName()); + } catch (Error & e) { + error (e); + return PortList(); + } +} + + + +void MidiInAlsa :: openVirtualPort(const std::string &portName ) { AlsaMidiData *data = static_cast (apiData_); - if ( data->vport < 0 ) { + if ( !data->local.client ) { snd_seq_port_info_t *pinfo; snd_seq_port_info_alloca( &pinfo ); snd_seq_port_info_set_capability( pinfo, @@ -1928,21 +3823,23 @@ void MidiInAlsa :: openVirtualPort( const std::string &portName ) snd_seq_port_info_set_midi_channels(pinfo, 16); #ifndef AVOID_TIMESTAMPING snd_seq_port_info_set_timestamping(pinfo, 1); - snd_seq_port_info_set_timestamp_real(pinfo, 1); + snd_seq_port_info_set_timestamp_real(pinfo, 1); snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id); #endif snd_seq_port_info_set_name(pinfo, portName.c_str()); - data->vport = snd_seq_create_port(data->seq, pinfo); + int createok = snd_seq_create_port(data->seq, pinfo); - if ( data->vport < 0 ) { - errorString_ = "MidiInAlsa::openVirtualPort: ALSA error creating virtual port."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + if ( createok < 0 ) { + errorString_ = "MidiInAlsa::openVirtualPort: "; + error( RTMIDI_ERROR(gettext_noopt("Error creating ALSA virtual port."), + Error::DRIVER_ERROR) ); return; } - data->vport = snd_seq_port_info_get_port(pinfo); + data->local.port = snd_seq_port_info_get_port(pinfo); + data->local.client = snd_seq_port_info_get_client(pinfo); } - if ( inputData_.doInput == false ) { + if ( doInput == false ) { // Wait for old thread to stop, if still running if ( !pthread_equal(data->thread, data->dummy_thread_id) ) pthread_join( data->thread, NULL ); @@ -1958,18 +3855,18 @@ void MidiInAlsa :: openVirtualPort( const std::string &portName ) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_attr_setschedpolicy(&attr, SCHED_OTHER); - inputData_.doInput = true; - int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_); + doInput = true; + int err = pthread_create(&data->thread, &attr, alsaMidiHandler, this); pthread_attr_destroy(&attr); if ( err ) { if ( data->subscription ) { - snd_seq_unsubscribe_port( data->seq, data->subscription ); - snd_seq_port_subscribe_free( data->subscription ); - data->subscription = 0; + snd_seq_unsubscribe_port( data->seq, data->subscription ); + snd_seq_port_subscribe_free( data->subscription ); + data->subscription = 0; } - inputData_.doInput = false; - errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!"; - error( RtMidiError::THREAD_ERROR, errorString_ ); + doInput = false; + error( RTMIDI_ERROR(gettext_noopt("Error starting MIDI input thread!"), + Error::THREAD_ERROR) ); return; } } @@ -1994,9 +3891,9 @@ void MidiInAlsa :: closePort( void ) } // Stop thread to avoid triggering the callback, while the port is intended to be closed - if ( inputData_.doInput ) { - inputData_.doInput = false; - int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof(inputData_.doInput) ); + if ( doInput ) { + doInput = false; + int res = write( data->trigger_fds[1], &doInput, sizeof(doInput) ); (void) res; if ( !pthread_equal(data->thread, data->dummy_thread_id) ) pthread_join( data->thread, NULL ); @@ -2005,27 +3902,24 @@ void MidiInAlsa :: closePort( void ) void MidiInAlsa :: setClientName( const std::string &clientName ) { - AlsaMidiData *data = static_cast ( apiData_ ); - snd_seq_set_client_name( data->seq, clientName.c_str() ); - + data->setClientName(clientName); } void MidiInAlsa :: setPortName( const std::string &portName ) { AlsaMidiData *data = static_cast (apiData_); - snd_seq_port_info_t *pinfo; - snd_seq_port_info_alloca( &pinfo ); - snd_seq_get_port_info( data->seq, data->vport, pinfo ); - snd_seq_port_info_set_name( pinfo, portName.c_str() ); - snd_seq_set_port_info( data->seq, data->vport, pinfo ); + data->setName(portName); } +#undef RTMIDI_CLASSNAME + //*********************************************************************// // API: LINUX ALSA // Class Definitions: MidiOutAlsa //*********************************************************************// +#define RTMIDI_CLASSNAME "MidiOutAlsa" MidiOutAlsa :: MidiOutAlsa( const std::string &clientName ) : MidiOutApi() { MidiOutAlsa::initialize( clientName ); @@ -2038,47 +3932,54 @@ MidiOutAlsa :: ~MidiOutAlsa() // Cleanup. AlsaMidiData *data = static_cast (apiData_); - if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); - if ( data->coder ) snd_midi_event_free( data->coder ); - if ( data->buffer ) free( data->buffer ); - snd_seq_close( data->seq ); + if ( data->local.client > 0 ) { + snd_seq_delete_port( data->seq, data->local.port ); + data->local.port = -1; + } + if ( data->coder ) { + snd_midi_event_free( data->coder ); + data->coder = 0; + } + if ( data->buffer ) { + free( data->buffer ); + data->buffer = 0; + } delete data; } -void MidiOutAlsa :: initialize( const std::string& clientName ) +void MidiOutAlsa :: initialize( const std::string &clientName ) { +#if 0 // Set up the ALSA sequencer client. snd_seq_t *seq; int result1 = snd_seq_open( &seq, "default", SND_SEQ_OPEN_OUTPUT, SND_SEQ_NONBLOCK ); if ( result1 < 0 ) { - errorString_ = "MidiOutAlsa::initialize: error creating ALSA sequencer client object."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("Error creating ALSA sequencer client object."), + Error::DRIVER_ERROR, errorString_ )); return; - } + } // Set client name. snd_seq_set_client_name( seq, clientName.c_str() ); +#endif // Save our api-specific connection information. - AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; - data->seq = seq; - data->portNum = -1; - data->vport = -1; - data->bufferSize = 32; - data->coder = 0; - data->buffer = 0; + AlsaMidiData *data = new AlsaMidiData(clientName); + // data->seq = seq; + // data->portNum = -1; + int result = snd_midi_event_new( data->bufferSize, &data->coder ); if ( result < 0 ) { delete data; - errorString_ = "MidiOutAlsa::initialize: error initializing MIDI event parser!\n\n"; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + error( RTMIDI_ERROR(gettext_noopt("Error initializing MIDI event parser."), + Error::DRIVER_ERROR) ); return; } data->buffer = (unsigned char *) malloc( data->bufferSize ); if ( data->buffer == NULL ) { delete data; - errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n"; - error( RtMidiError::MEMORY_ERROR, errorString_ ); + error( RTMIDI_ERROR(gettext_noopt("Error while allocating buffer memory."), + Error::MEMORY_ERROR) ); return; } snd_midi_event_init( data->coder ); @@ -2087,8 +3988,8 @@ void MidiOutAlsa :: initialize( const std::string& clientName ) unsigned int MidiOutAlsa :: getPortCount() { - snd_seq_port_info_t *pinfo; - snd_seq_port_info_alloca( &pinfo ); + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); AlsaMidiData *data = static_cast (apiData_); return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, -1 ); @@ -2119,70 +4020,75 @@ std::string MidiOutAlsa :: getPortName( unsigned int portNumber ) } // If we get here, we didn't find a match. - errorString_ = "MidiOutAlsa::getPortName: error looking for port name!"; - error( RtMidiError::WARNING, errorString_ ); + errorString_ = "MidiOutAlsa::getPortName: "; + error( RTMIDI_ERROR(gettext_noopt("Error looking for port name."), + Error::WARNING) ); return stringName; } void MidiOutAlsa :: openPort( unsigned int portNumber, const std::string &portName ) { if ( connected_ ) { - errorString_ = "MidiOutAlsa::openPort: a valid connection already exists!"; - error( RtMidiError::WARNING, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("A valid connection already exists."), + Error::WARNING) ); return; } unsigned int nSrc = this->getPortCount(); if (nSrc < 1) { - errorString_ = "MidiOutAlsa::openPort: no MIDI output sources found!"; - error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); + errorString_ = "MidiOutAlsa::openPort: !"; + error(RTMIDI_ERROR(gettext_noopt("No MIDI output sinks found."), + Error::NO_DEVICES_FOUND) ); return; } - snd_seq_port_info_t *pinfo; - snd_seq_port_info_alloca( &pinfo ); + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); AlsaMidiData *data = static_cast (apiData_); if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) == 0 ) { std::ostringstream ost; - ost << "MidiOutAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + ost << "MidiOutAlsa::openPort: "; errorString_ = ost.str(); - error( RtMidiError::INVALID_PARAMETER, errorString_ ); + error(RTMIDI_ERROR1(gettext_noopt("The 'portNumber' argument (%d) is invalid."), + Error::INVALID_PARAMETER, portNumber) ); return; } - snd_seq_addr_t sender, receiver; - receiver.client = snd_seq_port_info_get_client( pinfo ); - receiver.port = snd_seq_port_info_get_port( pinfo ); - sender.client = snd_seq_client_id( data->seq ); - - if ( data->vport < 0 ) { - data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(), - SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, - SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION ); - if ( data->vport < 0 ) { - errorString_ = "MidiOutAlsa::openPort: ALSA error creating output port."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + data->client = snd_seq_port_info_get_client( pinfo ); + data->port = snd_seq_port_info_get_port( pinfo ); + data->local.client = snd_seq_client_id( data->seq ); + + if ( !data->local.client ) { + int port = snd_seq_create_simple_port( data->seq, portName.c_str(), + SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, + SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION ); + if ( port < 0 ) { + errorString_ = "MidiOutAlsa::openPort: "; + error(RTMIDI_ERROR(gettext_noopt("Error creating ALSA output port."), + Error::DRIVER_ERROR)); return; } - } - sender.port = data->vport; + data->local.port = port; + } // Make subscription if (snd_seq_port_subscribe_malloc( &data->subscription ) < 0) { snd_seq_port_subscribe_free( data->subscription ); - errorString_ = "MidiOutAlsa::openPort: error allocating port subscription."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + data->subscription = 0; + error(RTMIDI_ERROR(gettext_noopt("Could not allocate ALSA port subscription."), + Error::DRIVER_ERROR) ); return; } - snd_seq_port_subscribe_set_sender(data->subscription, &sender); - snd_seq_port_subscribe_set_dest(data->subscription, &receiver); + snd_seq_port_subscribe_set_sender(data->subscription, data); + snd_seq_port_subscribe_set_dest(data->subscription, &data->local); snd_seq_port_subscribe_set_time_update(data->subscription, 1); snd_seq_port_subscribe_set_time_real(data->subscription, 1); if ( snd_seq_subscribe_port(data->seq, data->subscription) ) { snd_seq_port_subscribe_free( data->subscription ); - errorString_ = "MidiOutAlsa::openPort: ALSA error making port connection."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + data->subscription = 0; + error(RTMIDI_ERROR(gettext_noopt("Error making ALSA port connection."), + Error::DRIVER_ERROR) ); return; } @@ -2202,34 +4108,30 @@ void MidiOutAlsa :: closePort( void ) void MidiOutAlsa :: setClientName( const std::string &clientName ) { - AlsaMidiData *data = static_cast ( apiData_ ); - snd_seq_set_client_name( data->seq, clientName.c_str() ); - + data->setClientName(clientName); } void MidiOutAlsa :: setPortName( const std::string &portName ) { AlsaMidiData *data = static_cast (apiData_); - snd_seq_port_info_t *pinfo; - snd_seq_port_info_alloca( &pinfo ); - snd_seq_get_port_info( data->seq, data->vport, pinfo ); - snd_seq_port_info_set_name( pinfo, portName.c_str() ); - snd_seq_set_port_info( data->seq, data->vport, pinfo ); + data->setName(portName); } -void MidiOutAlsa :: openVirtualPort( const std::string &portName ) +void MidiOutAlsa :: openVirtualPort(const std::string &portName ) { AlsaMidiData *data = static_cast (apiData_); - if ( data->vport < 0 ) { - data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(), - SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, - SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION ); - - if ( data->vport < 0 ) { - errorString_ = "MidiOutAlsa::openVirtualPort: ALSA error creating virtual port."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + if ( !data->local.client ) { + int port = snd_seq_create_simple_port( data->seq, portName.c_str(), + SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, + SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION ); + + if ( port < 0 ) { + error(RTMIDI_ERROR(gettext_noopt("Error creating ALSA virtual port."), + Error::DRIVER_ERROR) ); } + data->local.port = port; + data->local.client = snd_seq_client_id(data->seq); } } @@ -2237,47 +4139,117 @@ void MidiOutAlsa :: sendMessage( const unsigned char *message, size_t size ) { int result; AlsaMidiData *data = static_cast (apiData_); - unsigned int nBytes = static_cast (size); - if ( nBytes > data->bufferSize ) { - data->bufferSize = nBytes; - result = snd_midi_event_resize_buffer ( data->coder, nBytes); + if ( size > data->bufferSize ) { + data->bufferSize = size; + result = snd_midi_event_resize_buffer ( data->coder, size); if ( result != 0 ) { - errorString_ = "MidiOutAlsa::sendMessage: ALSA error resizing MIDI event buffer."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("ALSA error resizing MIDI event buffer."), + Error::DRIVER_ERROR) ); return; } free (data->buffer); data->buffer = (unsigned char *) malloc( data->bufferSize ); if ( data->buffer == NULL ) { - errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n"; - error( RtMidiError::MEMORY_ERROR, errorString_ ); - return; + error(RTMIDI_ERROR(gettext_noopt("Error while allocating buffer memory."), + Error::MEMORY_ERROR) ); + return; } } snd_seq_event_t ev; snd_seq_ev_clear(&ev); - snd_seq_ev_set_source(&ev, data->vport); + snd_seq_ev_set_source(&ev, data->local.port); snd_seq_ev_set_subs(&ev); snd_seq_ev_set_direct(&ev); - for ( unsigned int i=0; ibuffer[i] = message[i]; - result = snd_midi_event_encode( data->coder, data->buffer, (long)nBytes, &ev ); - if ( result < (int)nBytes ) { - errorString_ = "MidiOutAlsa::sendMessage: event parsing error!"; - error( RtMidiError::WARNING, errorString_ ); + for ( unsigned int i=0; ibuffer[i] = message[i]; + result = snd_midi_event_encode( data->coder, data->buffer, (long)size, &ev ); + if ( result < (int)size ) { + error(RTMIDI_ERROR(gettext_noopt("Event parsing error."), + Error::WARNING) ); return; } // Send the event. result = snd_seq_event_output(data->seq, &ev); if ( result < 0 ) { - errorString_ = "MidiOutAlsa::sendMessage: error sending MIDI message to port."; - error( RtMidiError::WARNING, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("Error sending MIDI message to port."), + Error::WARNING) ); return; } snd_seq_drain_output(data->seq); } +void MidiOutAlsa :: openPort( const PortDescriptor & port, + const std::string &portName) +{ + AlsaMidiData *data = static_cast (apiData_); + const AlsaPortDescriptor * remote = dynamic_cast(&port); + + if ( !data ) { + error(RTMIDI_ERROR(gettext_noopt("Data has not been allocated."), + Error::SYSTEM_ERROR) ); + return; + } + if ( connected_ ) { + error(RTMIDI_ERROR(gettext_noopt("A valid connection already exists."), + Error::WARNING) ); + return; + } + if (data->subscription) { + error(RTMIDI_ERROR(gettext_noopt("Error allocating ALSA port subscription."), + Error::DRIVER_ERROR) ); + return; + } + if (!remote) { + error(RTMIDI_ERROR(gettext_noopt("ALSA has been instructed to open a non-ALSA MIDI port. This doesn't work."), + Error::INVALID_DEVICE) ); + return; + } + + try { + if (!data->local.client) + data->openPort (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ, + portName); + data->setRemote(remote); + data->connectPorts(data->local,*remote,true); + + connected_ = true; + } catch (Error & e) { + error (e); + } +} +Pointer MidiOutAlsa :: getDescriptor(bool local) +{ + AlsaMidiData *data = static_cast (apiData_); + try { + if (local) { + if (data && data->local.client) { + return Pointer( + new AlsaPortDescriptor(data->local, data->getClientName())); + } + } else { + if (data && data->client) { + return Pointer( + new AlsaPortDescriptor(*data, data->getClientName())); + } + } + } catch (Error & e) { + error(e); + } + return NULL; +} +PortList MidiOutAlsa :: getPortList(int capabilities) +{ + AlsaMidiData *data = static_cast (apiData_); + try { + return AlsaPortDescriptor::getPortList(capabilities | PortDescriptor::OUTPUT, + data->getClientName()); + } catch (Error & e) { + return PortList(); + } +} +RTMIDI_NAMESPACE_END +#undef RTMIDI_CLASSNAME #endif // __LINUX_ALSA__ @@ -2331,9 +4303,307 @@ static std::string ConvertToUTF8(const TCHAR *str) #define RT_SYSEX_BUFFER_SIZE 1024 #define RT_SYSEX_BUFFER_COUNT 4 -// A structure to hold variables related to the CoreMIDI API -// implementation. -struct WinMidiData { +/* some header defines UNIQUE_PORT_NAME as a macro */ +#ifdef UNIQUE_PORT_NAME +#undef UNIQUE_PORT_NAME +#endif +RTMIDI_NAMESPACE_START +/*! An abstraction layer for the ALSA sequencer layer. It provides + the following functionality: + - dynamic allocation of the sequencer + - optionallay avoid concurrent access to the ALSA sequencer, + which is not thread proof. This feature is controlled by + the parameter \ref locking. +*/ + +#define RTMIDI_CLASSNAME "WinMMSequencer" +template +class WinMMSequencer { +public: + WinMMSequencer():mutex(0),name() + { + if (locking) { +#if 0 + // use mthreads instead + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); + pthread_mutex_init(&mutex, &attr); +#endif + } + } + + WinMMSequencer(const std::string &n):name(n) + { + if (locking) { +#if 0 + // use mthreads instead + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); + pthread_mutex_init(&mutex, &attr); +#endif + } + init(); + { + scoped_lock lock(mutex); + } + } + + ~WinMMSequencer() + { + if (locking) { +#if 0 + // use mthreads instead + pthread_mutex_destroy(&mutex); +#endif + } + } + + bool setName(const std::string &n) { + /* we don't want to rename the client after opening it. */ + name = n; + return true; + } + + std::string getPortName(int port, bool is_input, int flags) { + init(); + int naming = flags & PortDescriptor::NAMING_MASK; + std::string name; + + unsigned int nDevices = is_input?midiInGetNumDevs() + : midiOutGetNumDevs(); + if ( port < 0 || (unsigned int)port >= nDevices ) { + throw Error(RTMIDI_ERROR1(gettext_noopt("The port argument %d is invalid."), + Error::INVALID_PARAMETER,port)); + } + + if (is_input) { + MIDIINCAPS deviceCaps; + midiInGetDevCaps( port, &deviceCaps, sizeof(MIDIINCAPS)); + +#if defined( UNICODE ) || defined( _UNICODE ) + int length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, -1, NULL, 0, NULL, NULL) - 1; + name.assign( length, 0 ); + length = WideCharToMultiByte(CP_UTF8, + 0, + deviceCaps.szPname, + static_cast(wcslen(deviceCaps.szPname)), + &name[0], + length, + NULL, + NULL); +#else + name = deviceCaps.szPname; +#endif + } else { + MIDIOUTCAPS deviceCaps; + midiOutGetDevCaps( port, &deviceCaps, sizeof(MIDIOUTCAPS)); + +#if defined( UNICODE ) || defined( _UNICODE ) + int length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, -1, NULL, 0, NULL, NULL) - 1; + name.assign( length, 0 ); + length = WideCharToMultiByte(CP_UTF8, + 0, + deviceCaps.szPname, + static_cast(wcslen(deviceCaps.szPname)), + &name[0], + length, + NULL, + NULL); +#else + name = deviceCaps.szPname; +#endif + + } + + + std::ostringstream os; + switch (naming) { + case PortDescriptor::SESSION_PATH: + if (flags & PortDescriptor::INCLUDE_API) + os << "WinMM:"; + os << port << ":" << name.c_str(); + break; + case PortDescriptor::STORAGE_PATH: + if (flags & PortDescriptor::INCLUDE_API) + os << "WinMM:"; + os << name.c_str(); + if (flags & PortDescriptor::UNIQUE_PORT_NAME) + os << ";" << port; + break; + case PortDescriptor::LONG_NAME: + case PortDescriptor::SHORT_NAME: + default: + os << name.c_str(); + if (flags & PortDescriptor::UNIQUE_PORT_NAME) { + os << " "; + os << port; + } + if (flags & PortDescriptor::INCLUDE_API) + os << " (WinMM)"; + + break; + } + return os.str(); + } + +protected: + struct scoped_lock { + // pthread_mutex_t * mutex; + scoped_lock(unsigned int &) + { +#if 0 + if (locking) + pthread_mutex_lock(mutex); +#endif + } + ~scoped_lock() + { +#if 0 + if (locking) + pthread_mutex_unlock(mutex); +#endif + } + }; + // to keep the API simple + int mutex; + std::string name; + + + void init() + { + // init (seq); + } + +}; +// typedef WinMMSequencer<1> LockingWinMMSequencer; +typedef WinMMSequencer<0> NonLockingWinMMSequencer; +#undef RTMIDI_CLASSNAME + +struct WinMMPortDescriptor:public PortDescriptor +{ + static NonLockingWinMMSequencer seq; + WinMMPortDescriptor(const std::string &/*cname*/):name(),port(0),clientName(name) + { + } + WinMMPortDescriptor(unsigned int p, const std::string &pn, bool i_o, const std::string &n): + name(pn), + port(p), + is_input(i_o), + clientName(n) + { + } + ~WinMMPortDescriptor() {} + MidiInApi * getInputApi(unsigned int queueSizeLimit = 100) const { + if (is_input) + return new MidiInWinMM(clientName,queueSizeLimit); + else + return 0; + } + MidiOutApi * getOutputApi() const { + if (!is_input) + return new MidiOutWinMM(clientName); + else + return 0; + } + std::string getName(int flags = SHORT_NAME | UNIQUE_PORT_NAME) { + return seq.getPortName(port,is_input,flags); + } + + const std::string &getClientName() const { + return clientName; + } + int getCapabilities() const { + return is_input ? INPUT : OUTPUT; + } + + int getCapabilities() { + const WinMMPortDescriptor * self = this; + return self->getCapabilities(); + } + + bool is_valid() const { + if (is_input) { + if (midiInGetNumDevs() <= port) { + return false; + } + } else { + if (midiOutGetNumDevs() <= port) { + return false; + } + } + return seq.getPortName(port,is_input,PortDescriptor::STORAGE_PATH) + == name; + } + + void setRemote(const WinMMPortDescriptor * remote) { + port = remote->port; + name = remote->name; + is_input = remote->is_input; + } + + + unsigned int getPortNumber() const { return port; } + + static PortList getPortList(int capabilities, const std::string &clientName); +protected: + /* There is no perfect port descriptor available in this API. + We use the port number and issue an error if the port name has changed + between the creation of the port descriptor and opening the port. */ + std::string name; + unsigned int port; + bool is_input; + std::string clientName; +}; + +NonLockingWinMMSequencer WinMMPortDescriptor::seq; + + + +PortList WinMMPortDescriptor :: getPortList(int capabilities, const std::string &clientName) +{ + PortList list; + + if (capabilities & INPUT && capabilities & OUTPUT) return list; + + if (capabilities & INPUT) { + size_t n = midiInGetNumDevs(); + for (size_t i = 0 ; i < n ; i++) { + std::string name = seq.getPortName(i,true,PortDescriptor::STORAGE_PATH); + list.push_back(Pointer( + new WinMMPortDescriptor(i,name,true,clientName))); + } + } else { + size_t n = midiOutGetNumDevs(); + for (size_t i = 0 ; i < n ; i++) { + std::string name = seq.getPortName(i,false,PortDescriptor::STORAGE_PATH); + list.push_back(Pointer( + new WinMMPortDescriptor(i,name,false,clientName))); + } + } + return list; +} + + +/*! A structure to hold variables related to the WINMM API + implementation. + + \note After all sequencer handling is covered by the \ref + WinMMSequencer class, we should make seq to be a pointer in order + to allow a common client implementation. +*/ + +struct WinMidiData:public WinMMPortDescriptor { + /* + WinMMMidiData():seq() + { + init(); + } + */ + WinMidiData(const std::string &clientName):WinMMPortDescriptor(clientName) {} + ~WinMidiData() {} + HMIDIIN inHandle; // Handle to Midi Input Device HMIDIOUT outHandle; // Handle to Midi Output Device DWORD lastTime; @@ -2342,107 +4612,128 @@ struct WinMidiData { CRITICAL_SECTION _mutex; // [Patrice] see https://groups.google.com/forum/#!topic/mididev/6OUjHutMpEo }; + //*********************************************************************// // API: Windows MM // Class Definitions: MidiInWinMM //*********************************************************************// +#define RTMIDI_CLASSNAME "WinMMCallbacks" +//! Windows callbacks +/*! In order to avoid including too many header files in RtMidi.h, we use this + * class to callect all friend functions of Midi*WinMM. + */ +struct WinMMCallbacks { + static void CALLBACK midiInputCallback( HMIDIIN /*hmin*/, + UINT inputStatus, + DWORD_PTR instancePtr, + DWORD_PTR midiMessage, + DWORD timestamp ) + { + if ( inputStatus != MIM_DATA && inputStatus != MIM_LONGDATA && inputStatus != MIM_LONGERROR ) return; -static void CALLBACK midiInputCallback( HMIDIIN /*hmin*/, - UINT inputStatus, - DWORD_PTR instancePtr, - DWORD_PTR midiMessage, - DWORD timestamp ) -{ - if ( inputStatus != MIM_DATA && inputStatus != MIM_LONGDATA && inputStatus != MIM_LONGERROR ) return; - - //MidiInApi::RtMidiInData *data = static_cast (instancePtr); - MidiInApi::RtMidiInData *data = (MidiInApi::RtMidiInData *)instancePtr; - WinMidiData *apiData = static_cast (data->apiData); - - // Calculate time stamp. - if ( data->firstMessage == true ) { - apiData->message.timeStamp = 0.0; - data->firstMessage = false; - } - else apiData->message.timeStamp = (double) ( timestamp - apiData->lastTime ) * 0.001; - - if ( inputStatus == MIM_DATA ) { // Channel or system message - - // Make sure the first byte is a status byte. - unsigned char status = (unsigned char) (midiMessage & 0x000000FF); - if ( !(status & 0x80) ) return; + //MidiInApi::MidiInData *data = static_cast (instancePtr); + MidiInWinMM *data = (MidiInWinMM *)instancePtr; + WinMidiData *apiData = static_cast (data->apiData_); - // Determine the number of bytes in the MIDI message. - unsigned short nBytes = 1; - if ( status < 0xC0 ) nBytes = 3; - else if ( status < 0xE0 ) nBytes = 2; - else if ( status < 0xF0 ) nBytes = 3; - else if ( status == 0xF1 ) { - if ( data->ignoreFlags & 0x02 ) return; - else nBytes = 2; - } - else if ( status == 0xF2 ) nBytes = 3; - else if ( status == 0xF3 ) nBytes = 2; - else if ( status == 0xF8 && (data->ignoreFlags & 0x02) ) { - // A MIDI timing tick message and we're ignoring it. - return; - } - else if ( status == 0xFE && (data->ignoreFlags & 0x04) ) { - // A MIDI active sensing message and we're ignoring it. - return; + // Calculate time stamp. + if ( data->firstMessage == true ) { + apiData->message.timeStamp = 0.0; + data->firstMessage = false; } + else apiData->message.timeStamp = (double) ( timestamp - apiData->lastTime ) * 0.001; + + if ( inputStatus == MIM_DATA ) { // Channel or system message + + // Make sure the first byte is a status byte. + unsigned char status = (unsigned char) (midiMessage & 0x000000FF); + if ( !(status & 0x80) ) return; + + // Determine the number of bytes in the MIDI message. + unsigned short nBytes = 1; + if ( status < 0xC0 ) nBytes = 3; + else if ( status < 0xE0 ) nBytes = 2; + else if ( status < 0xF0 ) nBytes = 3; + else if ( status == 0xF1 ) { + if ( data->ignoreFlags & 0x02 ) return; + else nBytes = 2; + } + else if ( status == 0xF2 ) nBytes = 3; + else if ( status == 0xF3 ) nBytes = 2; + else if ( status == 0xF8 && (data->ignoreFlags & 0x02) ) { + // A MIDI timing tick message and we're ignoring it. + return; + } + else if ( status == 0xFE && (data->ignoreFlags & 0x04) ) { + // A MIDI active sensing message and we're ignoring it. + return; + } - // Copy bytes to our MIDI message. - unsigned char *ptr = (unsigned char *) &midiMessage; - for ( int i=0; imessage.bytes.push_back( *ptr++ ); - } - else { // Sysex message ( MIM_LONGDATA or MIM_LONGERROR ) - MIDIHDR *sysex = ( MIDIHDR *) midiMessage; - if ( !( data->ignoreFlags & 0x01 ) && inputStatus != MIM_LONGERROR ) { - // Sysex message and we're not ignoring it - for ( int i=0; i<(int)sysex->dwBytesRecorded; ++i ) - apiData->message.bytes.push_back( sysex->lpData[i] ); + // Copy bytes to our MIDI message. + unsigned char *ptr = (unsigned char *) &midiMessage; + for ( int i=0; imessage.bytes.push_back( *ptr++ ); } + else { // Sysex message ( MIM_LONGDATA or MIM_LONGERROR ) + MIDIHDR *sysex = ( MIDIHDR *) midiMessage; + if ( !( data->ignoreFlags & 0x01 ) && inputStatus != MIM_LONGERROR ) { + // Sysex message and we're not ignoring it + for ( int i=0; i<(int)sysex->dwBytesRecorded; ++i ) + apiData->message.bytes.push_back( sysex->lpData[i] ); + } - // The WinMM API requires that the sysex buffer be requeued after - // input of each sysex message. Even if we are ignoring sysex - // messages, we still need to requeue the buffer in case the user - // decides to not ignore sysex messages in the future. However, - // it seems that WinMM calls this function with an empty sysex - // buffer when an application closes and in this case, we should - // avoid requeueing it, else the computer suddenly reboots after - // one or two minutes. - if ( apiData->sysexBuffer[sysex->dwUser]->dwBytesRecorded > 0 ) { - //if ( sysex->dwBytesRecorded > 0 ) { - EnterCriticalSection( &(apiData->_mutex) ); - MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer[sysex->dwUser], sizeof(MIDIHDR) ); - LeaveCriticalSection( &(apiData->_mutex) ); - if ( result != MMSYSERR_NOERROR ) - std::cerr << "\nRtMidiIn::midiInputCallback: error sending sysex to Midi device!!\n\n"; + // The WinMM API requires that the sysex buffer be requeued after + // input of each sysex message. Even if we are ignoring sysex + // messages, we still need to requeue the buffer in case the user + // decides to not ignore sysex messages in the future. However, + // it seems that WinMM calls this function with an empty sysex + // buffer when an application closes and in this case, we should + // avoid requeueing it, else the computer suddenly reboots after + // one or two minutes. + if ( apiData->sysexBuffer[sysex->dwUser]->dwBytesRecorded > 0 ) { + //if ( sysex->dwBytesRecorded > 0 ) { + EnterCriticalSection( &(apiData->_mutex) ); + MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer[sysex->dwUser], sizeof(MIDIHDR) ); + LeaveCriticalSection( &(apiData->_mutex) ); + if ( result != MMSYSERR_NOERROR ){ + try { + data->error(RTMIDI_ERROR(rtmidi_gettext("Error sending sysex to Midi device."), + Error::WARNING)); + } catch (Error & e) { + // don't bother WinMM with an unhandled exception + } + } - if ( data->ignoreFlags & 0x01 ) return; + if ( data->ignoreFlags & 0x01 ) return; + } + else return; } - else return; - } - // Save the time of the last non-filtered message - apiData->lastTime = timestamp; + // Save the time of the last non-filtered message + apiData->lastTime = timestamp; - if ( data->usingCallback ) { - RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; - callback( apiData->message.timeStamp, &apiData->message.bytes, data->userData ); - } - else { - // As long as we haven't reached our queue size limit, push the message. - if (!data->queue.push(apiData->message)) - std::cerr << "\nMidiInWinMM: message queue limit reached!!\n\n"; - } + if ( data->userCallback ) { + data->userCallback->rtmidi_midi_in( apiData->message.timeStamp, apiData->message.bytes ); + } + else { + // As long as we haven't reached our queue size limit, push the message. + if (!data->queue.push(apiData->message)) { + try { + data->error(RTMIDI_ERROR(rtmidi_gettext("Error: Message queue limit reached."), + Error::WARNING)); + } catch (Error & e) { + // don't bother WinMM with an unhandled exception + } + } + } - // Clear the vector for the next input message. - apiData->message.bytes.clear(); -} + // Clear the vector for the next input message. + apiData->message.bytes.clear(); + } +}; +#undef RTMIDI_CLASSNAME -MidiInWinMM :: MidiInWinMM( const std::string &clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) +#define RTMIDI_CLASSNAME "MidiInWinMM" +MidiInWinMM :: MidiInWinMM( const std::string &clientName, + unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { MidiInWinMM::initialize( clientName ); } @@ -2459,60 +4750,60 @@ MidiInWinMM :: ~MidiInWinMM() delete data; } -void MidiInWinMM :: initialize( const std::string& /*clientName*/ ) +void MidiInWinMM :: initialize( const std::string &clientName ) { // We'll issue a warning here if no devices are available but not // throw an error since the user can plugin something later. unsigned int nDevices = midiInGetNumDevs(); if ( nDevices == 0 ) { - errorString_ = "MidiInWinMM::initialize: no MIDI input devices currently available."; - error( RtMidiError::WARNING, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("No MIDI input devices currently available."), + Error::WARNING) ); } // Save our api-specific connection information. - WinMidiData *data = (WinMidiData *) new WinMidiData; + WinMidiData *data = (WinMidiData *) new WinMidiData(clientName); apiData_ = (void *) data; - inputData_.apiData = (void *) data; data->message.bytes.clear(); // needs to be empty for first input message if ( !InitializeCriticalSectionAndSpinCount(&(data->_mutex), 0x00000400) ) { - errorString_ = "MidiInWinMM::initialize: InitializeCriticalSectionAndSpinCount failed."; - error( RtMidiError::WARNING, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("Failed to initialize a critical section."), + Error::WARNING) ); } } void MidiInWinMM :: openPort( unsigned int portNumber, const std::string &/*portName*/ ) { if ( connected_ ) { - errorString_ = "MidiInWinMM::openPort: a valid connection already exists!"; - error( RtMidiError::WARNING, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("A valid connection already exists."), + Error::WARNING) ); return; } unsigned int nDevices = midiInGetNumDevs(); if (nDevices == 0) { - errorString_ = "MidiInWinMM::openPort: no MIDI input sources found!"; - error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("No MIDI input sources found."), + Error::NO_DEVICES_FOUND) ); return; } if ( portNumber >= nDevices ) { std::ostringstream ost; - ost << "MidiInWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + ost << "MidiInWinMM::openPort: "; errorString_ = ost.str(); - error( RtMidiError::INVALID_PARAMETER, errorString_ ); + error(RTMIDI_ERROR1(gettext_noopt("the 'portNumber' argument (%d) is invalid."), + Error::INVALID_PARAMETER, portNumber) ); return; } WinMidiData *data = static_cast (apiData_); MMRESULT result = midiInOpen( &data->inHandle, - portNumber, - (DWORD_PTR)&midiInputCallback, - (DWORD_PTR)&inputData_, - CALLBACK_FUNCTION ); + portNumber, + (DWORD_PTR)&WinMMCallbacks::midiInputCallback, + (DWORD_PTR)this, + CALLBACK_FUNCTION ); if ( result != MMSYSERR_NOERROR ) { - errorString_ = "MidiInWinMM::openPort: error creating Windows MM MIDI input port."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("Error creating Windows MM MIDI input port."), + Error::DRIVER_ERROR) ); return; } @@ -2528,8 +4819,8 @@ void MidiInWinMM :: openPort( unsigned int portNumber, const std::string &/*port if ( result != MMSYSERR_NOERROR ) { midiInClose( data->inHandle ); data->inHandle = 0; - errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (PrepareHeader)."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("Error initializing data for Windows MM MIDI input port."), + Error::DRIVER_ERROR )); return; } @@ -2538,8 +4829,8 @@ void MidiInWinMM :: openPort( unsigned int portNumber, const std::string &/*port if ( result != MMSYSERR_NOERROR ) { midiInClose( data->inHandle ); data->inHandle = 0; - errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (AddBuffer)."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("Could not register the input buffer for Windows MM MIDI input port."), + Error::DRIVER_ERROR) ); return; } } @@ -2548,39 +4839,121 @@ void MidiInWinMM :: openPort( unsigned int portNumber, const std::string &/*port if ( result != MMSYSERR_NOERROR ) { midiInClose( data->inHandle ); data->inHandle = 0; - errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("Error starting Windows MM MIDI input port."), + Error::DRIVER_ERROR) ); return; } connected_ = true; } -void MidiInWinMM :: openVirtualPort( const std::string &/*portName*/ ) +void MidiInWinMM :: openVirtualPort(const std::string &/*portName*/ ) { // This function cannot be implemented for the Windows MM MIDI API. - errorString_ = "MidiInWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; - error( RtMidiError::WARNING, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("Virtual ports are not available Windows Multimedia MIDI API."), + Error::WARNING )); } -void MidiInWinMM :: closePort( void ) -{ +void MidiInWinMM :: openPort(const PortDescriptor & p, const std::string &portName) { + const WinMMPortDescriptor * port = dynamic_cast (&p); + if ( !port) { + error( RTMIDI_ERROR(gettext_noopt("Windows Multimedia (WinMM) has been instructed to open a non-WinMM MIDI port. This doesn't work."), + Error::INVALID_DEVICE)); + return; + } if ( connected_ ) { - WinMidiData *data = static_cast (apiData_); - EnterCriticalSection( &(data->_mutex) ); - midiInReset( data->inHandle ); - midiInStop( data->inHandle ); + error( RTMIDI_ERROR(gettext_noopt("We are overwriting an existing connection. This is probably a programming error."), + Error::WARNING) ); + return; + } + if (port->getCapabilities() != PortDescriptor::INPUT) { + error(RTMIDI_ERROR(gettext_noopt("Trying to open a non-input port as input MIDI port. This doesn't work."), + Error::INVALID_DEVICE)); + return; + } - for ( int i=0; iinHandle, data->sysexBuffer[i], sizeof(MIDIHDR)); - delete [] data->sysexBuffer[i]->lpData; - delete [] data->sysexBuffer[i]; - if ( result != MMSYSERR_NOERROR ) { + // there is a possible race condition between opening the port and + // reordering of ports so we must check whether we opened the right port. + try { + openPort(port->getPortNumber(),portName); + } catch (Error & e) { + error(e); + } + if (!port->is_valid()) { + closePort(); + error (RTMIDI_ERROR(gettext_noopt("Some change in the arrangement of MIDI input ports invalidated the port descriptor."), + Error::DRIVER_ERROR)); + return; + } + connected_ = true; +} + +Pointer MidiInWinMM :: getDescriptor(bool local) +{ + if (local || !connected_) return 0; + WinMidiData *data = static_cast (apiData_); + if (!data) return 0; + UINT devid; + switch (midiInGetID(data->inHandle,&devid)) { + case MMSYSERR_INVALHANDLE: + error (RTMIDI_ERROR(gettext_noopt("The handle is invalid. Did you disconnect the device?"), + Error::DRIVER_ERROR)); + return 0; + case MMSYSERR_NODRIVER: + error (RTMIDI_ERROR(gettext_noopt("The system has no driver for our handle :-(. Did you disconnect the device?"), + Error::DRIVER_ERROR)); + return 0; + case MMSYSERR_NOMEM: + error (RTMIDI_ERROR(gettext_noopt("Out of memory."), + Error::DRIVER_ERROR)); + return 0; + } + WinMMPortDescriptor * retval = NULL; + try { + retval = new WinMMPortDescriptor(devid, getPortName(devid), true, data->getClientName()); + } catch (Error & e) { + try { + error(e); + } catch (...) { + if (retval) delete retval; + throw; + } + } + return Pointer(retval); + +} + +PortList MidiInWinMM :: getPortList(int capabilities) +{ + WinMidiData *data = static_cast (apiData_); + if (!data || capabilities != PortDescriptor::INPUT) return PortList(); + try { + return WinMMPortDescriptor::getPortList(PortDescriptor::INPUT,data->getClientName()); + } catch (Error & e) { + error(e); + return PortList(); + } +} + + +void MidiInWinMM :: closePort( void ) +{ + if ( connected_ ) { + WinMidiData *data = static_cast (apiData_); + EnterCriticalSection( &(data->_mutex) ); + midiInReset( data->inHandle ); + midiInStop( data->inHandle ); + + for ( int i=0; iinHandle, data->sysexBuffer[i], sizeof(MIDIHDR)); + delete [] data->sysexBuffer[i]->lpData; + delete [] data->sysexBuffer[i]; + if ( result != MMSYSERR_NOERROR ) { midiInClose( data->inHandle ); data->inHandle = 0; - errorString_ = "MidiInWinMM::openPort: error closing Windows MM MIDI input port (midiInUnprepareHeader)."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; + error(RTMIDI_ERROR(gettext_noopt("Error closing Windows MM MIDI input port."), + Error::DRIVER_ERROR) ); + return; } } @@ -2593,18 +4966,14 @@ void MidiInWinMM :: closePort( void ) void MidiInWinMM :: setClientName ( const std::string& ) { - - errorString_ = "MidiInWinMM::setClientName: this function is not implemented for the WINDOWS_MM API!"; - error( RtMidiError::WARNING, errorString_ ); - + error(RTMIDI_ERROR(gettext_noopt("Setting the client name is not supported by Windows MM."), + Error::WARNING )); } void MidiInWinMM :: setPortName ( const std::string& ) { - - errorString_ = "MidiInWinMM::setPortName: this function is not implemented for the WINDOWS_MM API!"; - error( RtMidiError::WARNING, errorString_ ); - + error(RTMIDI_ERROR(gettext_noopt("Setting the port name is not supported by Windows MM."), + Error::WARNING )); } unsigned int MidiInWinMM :: getPortCount() @@ -2617,10 +4986,8 @@ std::string MidiInWinMM :: getPortName( unsigned int portNumber ) std::string stringName; unsigned int nDevices = midiInGetNumDevs(); if ( portNumber >= nDevices ) { - std::ostringstream ost; - ost << "MidiInWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtMidiError::WARNING, errorString_ ); + error(RTMIDI_ERROR1(gettext_noopt("The 'portNumber' argument (%d) is invalid."), + Error::WARNING,portNumber)); return stringName; } @@ -2628,7 +4995,7 @@ std::string MidiInWinMM :: getPortName( unsigned int portNumber ) midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS)); stringName = ConvertToUTF8( deviceCaps.szPname ); - // Next lines added to add the portNumber to the name so that + // Next lines added to add the portNumber to the name so that // the device's names are sure to be listed with individual names // even when they have the same brand name #ifndef RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES @@ -2640,12 +5007,16 @@ std::string MidiInWinMM :: getPortName( unsigned int portNumber ) return stringName; } +#undef RTMIDI_CLASSNAME + + //*********************************************************************// // API: Windows MM // Class Definitions: MidiOutWinMM //*********************************************************************// +#define RTMIDI_CLASSNAME "MidiOutWinMM" MidiOutWinMM :: MidiOutWinMM( const std::string &clientName ) : MidiOutApi() { MidiOutWinMM::initialize( clientName ); @@ -2661,18 +5032,18 @@ MidiOutWinMM :: ~MidiOutWinMM() delete data; } -void MidiOutWinMM :: initialize( const std::string& /*clientName*/ ) +void MidiOutWinMM :: initialize( const std::string &clientName ) { // We'll issue a warning here if no devices are available but not // throw an error since the user can plug something in later. unsigned int nDevices = midiOutGetNumDevs(); if ( nDevices == 0 ) { - errorString_ = "MidiOutWinMM::initialize: no MIDI output devices currently available."; - error( RtMidiError::WARNING, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("No MIDI output devices currently available."), + Error::WARNING)); } // Save our api-specific connection information. - WinMidiData *data = (WinMidiData *) new WinMidiData; + WinMidiData *data = (WinMidiData *) new WinMidiData(clientName); apiData_ = (void *) data; } @@ -2687,9 +5058,10 @@ std::string MidiOutWinMM :: getPortName( unsigned int portNumber ) unsigned int nDevices = midiOutGetNumDevs(); if ( portNumber >= nDevices ) { std::ostringstream ost; - ost << "MidiOutWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + ost << "MidiOutWinMM::getPortName: "; errorString_ = ost.str(); - error( RtMidiError::WARNING, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("The 'portNumber' argument (%d) is invalid."), + Error::WARNING)); return stringName; } @@ -2697,7 +5069,7 @@ std::string MidiOutWinMM :: getPortName( unsigned int portNumber ) midiOutGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIOUTCAPS)); stringName = ConvertToUTF8( deviceCaps.szPname ); - // Next lines added to add the portNumber to the name so that + // Next lines added to add the portNumber to the name so that // the device's names are sure to be listed with individual names // even when they have the same brand name std::ostringstream os; @@ -2710,38 +5082,37 @@ std::string MidiOutWinMM :: getPortName( unsigned int portNumber ) return stringName; } + void MidiOutWinMM :: openPort( unsigned int portNumber, const std::string &/*portName*/ ) { if ( connected_ ) { - errorString_ = "MidiOutWinMM::openPort: a valid connection already exists!"; - error( RtMidiError::WARNING, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("A valid connection already exists."), + Error::WARNING) ); return; } unsigned int nDevices = midiOutGetNumDevs(); if (nDevices < 1) { - errorString_ = "MidiOutWinMM::openPort: no MIDI output destinations found!"; - error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("No MIDI output destinations found!"), + Error::NO_DEVICES_FOUND) ); return; } if ( portNumber >= nDevices ) { - std::ostringstream ost; - ost << "MidiOutWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtMidiError::INVALID_PARAMETER, errorString_ ); + error(RTMIDI_ERROR1(gettext_noopt("The 'portNumber' argument (%d) is invalid."), + Error::INVALID_PARAMETER, portNumber) ); return; } WinMidiData *data = static_cast (apiData_); MMRESULT result = midiOutOpen( &data->outHandle, - portNumber, - (DWORD)NULL, - (DWORD)NULL, - CALLBACK_NULL ); + portNumber, + (DWORD)NULL, + (DWORD)NULL, + CALLBACK_NULL ); if ( result != MMSYSERR_NOERROR ) { - errorString_ = "MidiOutWinMM::openPort: error creating Windows MM MIDI output port."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("Error creating Windows MM MIDI output port."), + Error::DRIVER_ERROR) ); return; } @@ -2759,158 +5130,775 @@ void MidiOutWinMM :: closePort( void ) } } -void MidiOutWinMM :: setClientName ( const std::string& ) -{ +void MidiOutWinMM :: setClientName ( const std::string& ) +{ + error(RTMIDI_ERROR(gettext_noopt("Setting the client name is not supported by Windows MM."), + Error::WARNING )); +} + +void MidiOutWinMM :: setPortName ( const std::string& ) +{ + error(RTMIDI_ERROR(gettext_noopt("Setting the port name is not supported by Windows MM."), + Error::WARNING )); +} + +void MidiOutWinMM :: openVirtualPort(const std::string &/*portName*/ ) +{ + // This function cannot be implemented for the Windows MM MIDI API. + error(RTMIDI_ERROR(gettext_noopt("Virtual ports are not available Windows Multimedia MIDI API."), + Error::WARNING) ); +} + + +void MidiOutWinMM :: openPort(const PortDescriptor & p, const std::string &portName) { + const WinMMPortDescriptor * port = dynamic_cast (&p); + if ( !port) { + error( RTMIDI_ERROR(gettext_noopt("Windows Multimedia (WinMM) has been instructed to open a non-WinMM MIDI port. This doesn't work."), + Error::INVALID_DEVICE)); + return; + } + if ( connected_ ) { + error( RTMIDI_ERROR(gettext_noopt("A valid connection already exists." ), + Error::WARNING) ); + return; + } + if (port->getCapabilities() != PortDescriptor::OUTPUT) { + error( RTMIDI_ERROR(gettext_noopt("The port descriptor cannot be used to open an output port."), + Error::DRIVER_ERROR)); + return; + } + + // there is a possible race condition between opening the port and + // reordering of ports so we must check whether we opened the right port. + try { + openPort(port->getPortNumber(),portName); + } catch (Error & e) { + error(e); + } + if (!port->is_valid()) { + closePort(); + error (RTMIDI_ERROR(gettext_noopt("Some change in the arrangement of MIDI input ports invalidated the port descriptor."), + Error::DRIVER_ERROR)); + return; + } + connected_ = true; +} + +Pointer MidiOutWinMM :: getDescriptor(bool local) +{ + if (local || !connected_) return 0; + WinMidiData *data = static_cast (apiData_); + if (!data) return 0; + UINT devid; + switch (midiOutGetID(data->outHandle,&devid)) { + case MMSYSERR_INVALHANDLE: + error (RTMIDI_ERROR(gettext_noopt("The internal handle is invalid. Did you disconnect the device?"), + Error::DRIVER_ERROR)); + return 0; + case MMSYSERR_NODRIVER: + error (RTMIDI_ERROR(gettext_noopt("The system has no driver for our handle :-(. Did you disconnect the device?"), + Error::DRIVER_ERROR)); + return 0; + case MMSYSERR_NOMEM: + error (RTMIDI_ERROR(gettext_noopt("Out of memory."), + Error::DRIVER_ERROR)); + return 0; + } + return Pointer( + new WinMMPortDescriptor(devid, getPortName(devid), true, data->getClientName())); + +} + +PortList MidiOutWinMM :: getPortList(int capabilities) +{ + WinMidiData *data = static_cast (apiData_); + if (!data || capabilities != PortDescriptor::OUTPUT) return PortList(); + try { + return WinMMPortDescriptor::getPortList(PortDescriptor::OUTPUT,data->getClientName()); + } catch (Error & e) { + error(e); + return PortList(); + } +} + + +void MidiOutWinMM :: sendMessage( const unsigned char *message, size_t size ) +{ + if ( !connected_ ) return; + + if ( size == 0 ) { + error(RTMIDI_ERROR(gettext_noopt("Message argument is empty."), + Error::WARNING)); + return; + } + + MMRESULT result; + WinMidiData *data = static_cast (apiData_); + if ( message[0] == 0xF0 ) { // Sysex message + + // Allocate buffer for sysex data. + char *buffer = (char *) malloc( size ); + if ( buffer == NULL ) { + error(RTMIDI_ERROR(gettext_noopt("Error while allocating sysex message memory."), + Error::MEMORY_ERROR) ); + return; + } + + // Copy data to buffer. + for ( unsigned int i=0; ioutHandle, &sysex, sizeof(MIDIHDR) ); + if ( result != MMSYSERR_NOERROR ) { + free( buffer ); + error(RTMIDI_ERROR(gettext_noopt("Error preparing sysex header."), + Error::DRIVER_ERROR)); + return; + } + + // Send the message. + result = midiOutLongMsg( data->outHandle, &sysex, sizeof(MIDIHDR) ); + if ( result != MMSYSERR_NOERROR ) { + free( buffer ); + error(RTMIDI_ERROR(gettext_noopt("Error sending sysex message."), + Error::DRIVER_ERROR) ); + return; + } + + // Unprepare the buffer and MIDIHDR. + while ( MIDIERR_STILLPLAYING == midiOutUnprepareHeader( data->outHandle, &sysex, sizeof (MIDIHDR) ) ) Sleep( 1 ); + free( buffer ); + } + else { // Channel or system message. + + // Make sure the message size isn't too big. + if ( size > 3 ) { + error(RTMIDI_ERROR(gettext_noopt("Message size is greater than 3 bytes (and not sysex)."), + Error::WARNING) ); + return; + } + + // Pack MIDI bytes into double word. + DWORD packet; + unsigned char *ptr = (unsigned char *) &packet; + for ( unsigned int i=0; ioutHandle, packet ); + if ( result != MMSYSERR_NOERROR ) { + error(RTMIDI_ERROR(gettext_noopt("Error sending MIDI message."), + Error::DRIVER_ERROR )); + } + } +} +#undef RTMIDI_CLASSNAME +RTMIDI_NAMESPACE_END +#endif // __WINDOWS_MM__ + + +//*********************************************************************// +// API: UNIX JACK +// +// Written primarily by Alexander Svetalkin, with updates for delta +// time by Gary Scavone, April 2011. +// +// *********************************************************************// + +#if defined(__UNIX_JACK__) + +// JACK header files +#include +#include +#include +#ifdef HAVE_SEMAPHORE + #include +#endif + +#define JACK_RINGBUFFER_SIZE 16384 // Default size for ringbuffer + +RTMIDI_NAMESPACE_START + +struct JackMidiData; +struct JackBackendCallbacks { + static int jackProcessIn( jack_nframes_t nframes, void *arg ); + static int jackProcessOut( jack_nframes_t nframes, void *arg ); +}; + + +#define RTMIDI_CLASSNAME "JackSequencer" +template +class JackSequencer { +public: + JackSequencer():client(0),name(),data(0) + { + if (locking) { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); + pthread_mutex_init(&mutex, &attr); + } + } + + JackSequencer(const std::string &n, JackMidiData * d):client(0),name(n),data(d) + { + if (locking) { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); + pthread_mutex_init(&mutex, &attr); + } + } + + ~JackSequencer() + { + { + scoped_lock lock (mutex); + if (client) { + jack_deactivate (client); + // the latter doesn't flush the queue + jack_client_close (client); + client = 0; + } + } + if (locking) { + pthread_mutex_destroy(&mutex); + } + } + + void init(bool startqueue) { + init(client,startqueue); + } + + bool setName(const std::string &n) { + /* we don't want to rename the client after opening it. */ + if (client) return false; + name = n; + return true; + } + + const char ** getPortList(unsigned long flags) { + init(); + return jack_get_ports(client, + NULL, + "midi", + flags); + } + + jack_port_t * getPort(const char * name) { + init(); + return jack_port_by_name(client,name); + } + + std::string getPortName(jack_port_t * port, int flags) { + init(); + int naming = flags & PortDescriptor::NAMING_MASK; + + std::ostringstream os; + switch (naming) { + case PortDescriptor::SESSION_PATH: + if (flags & PortDescriptor::INCLUDE_API) + os << "JACK:"; +#if __UNIX_JACK_HAS_UUID__ + os << "UUID:" << std::hex << jack_port_uuid(port); +#else + os << jack_port_name(port); +#endif + break; + case PortDescriptor::STORAGE_PATH: + if (flags & PortDescriptor::INCLUDE_API) + os << "JACK:"; + os << jack_port_name(port); + break; + case PortDescriptor::LONG_NAME: + os << jack_port_name(port); + if (flags & PortDescriptor::INCLUDE_API) + os << " (JACK)"; + break; + case PortDescriptor::SHORT_NAME: + default: + os << jack_port_short_name(port); + if (flags & PortDescriptor::INCLUDE_API) + os << " (JACK)"; + break; + } + return os.str(); + } + + int getPortCapabilities(jack_port_t * port) { + if (!port) return 0; + const char * type = jack_port_type(port); + if (strcmp(type,JACK_DEFAULT_MIDI_TYPE)) return 0; + int flags = jack_port_flags(port); + int retval = 0; + /* a JACK input port is capable of handling output to it and vice versa */ + if (flags & JackPortIsInput) + retval |= PortDescriptor::OUTPUT; + if (flags & JackPortIsOutput) + retval |= PortDescriptor::INPUT; + + return retval; + } + + + jack_port_t * createPort (const std::string &portName, unsigned long portOptions) { + init(); + scoped_lock lock (mutex); + return jack_port_register(client, + portName.c_str(), + JACK_DEFAULT_MIDI_TYPE, + portOptions, + 0); + } + + int renamePort(jack_port_t * port, const std::string &portName) { +#ifdef JACK_HAS_PORT_RENAME + return jack_port_rename( client, port, portName.c_str() ); +#else + return jack_port_set_name( port, portName.c_str() ); +#endif + } + + void deletePort(jack_port_t * port) { + init(); + scoped_lock lock (mutex); + jack_port_unregister( client, port ); + } + + void connectPorts(jack_port_t * from, + jack_port_t * to) + { + init(); + jack_connect( client, + jack_port_name( from ), + jack_port_name( to ) ); + } + + void closePort(jack_port_t * port) + { + init(); + jack_port_disconnect( client, port); + } + + + /*! Use JackSequencer like a C pointer. + \note This function breaks the design to control thread safety + by the selection of the \ref locking parameter to the class. + It should be removed as soon as possible in order ensure the + thread policy that has been intended by creating this class. + */ + operator jack_client_t * () + { + return client; + } + +protected: + struct scoped_lock { + pthread_mutex_t * mutex; + scoped_lock(pthread_mutex_t & m): mutex(&m) + { + if (locking) + pthread_mutex_lock(mutex); + } + ~scoped_lock() + { + if (locking) + pthread_mutex_unlock(mutex); + } + }; + pthread_mutex_t mutex; + jack_client_t * client; + std::string name; + JackMidiData * data; + + + void init() + { + init (client,false); + } + + void init(jack_client_t * &c, bool isoutput) + { + if (c) return; + { + scoped_lock lock(mutex); + if (( c = jack_client_open( name.c_str(), + JackNoStartServer, + NULL )) == 0) { + c = NULL; + throw RTMIDI_ERROR(gettext_noopt("Could not connect to JACK server. Is it runnig?"), + Error::NO_DEVICES_FOUND); + return; + } + + if (isoutput && data) { + jack_set_process_callback( c, JackBackendCallbacks::jackProcessOut, data ); + } else if (data) + jack_set_process_callback( c, JackBackendCallbacks::jackProcessIn, data ); + jack_activate( c ); + } + } +}; +typedef JackSequencer<1> LockingJackSequencer; +typedef JackSequencer<0> NonLockingJackSequencer; +#undef RTMIDI_CLASSNAME + +#define RTMIDI_CLASSNAME "JackPortDescriptor" +struct JackPortDescriptor:public PortDescriptor +{ + MidiApi * api; + static LockingJackSequencer seq; + JackPortDescriptor(const std::string &name):api(0),clientName(name) + { + port = 0; + } + JackPortDescriptor(const char * portname, const std::string &name):api(0),clientName(name) + { + seq.setName(name); + port = seq.getPort(portname); + } + JackPortDescriptor(jack_port_t * other, + const std::string &name):api(0), + clientName(name) + { + port = other; + seq.setName(name); + } + JackPortDescriptor(JackPortDescriptor & other, + const std::string &name):api(0), + clientName(name) + { + port = other.port; + seq.setName(name); + } + ~JackPortDescriptor() + { + } + MidiInApi * getInputApi(unsigned int queueSizeLimit = 100) const { + if (getCapabilities() & INPUT) + return new MidiInJack(clientName,queueSizeLimit); + else + return 0; + } + MidiOutApi * getOutputApi() const { + if (getCapabilities() & OUTPUT) + return new MidiOutJack(clientName); + else + return 0; + } + + + std::string getName(int flags = SHORT_NAME | UNIQUE_PORT_NAME) { + return seq.getPortName(port,flags); + } + + const std::string &getClientName() { + return clientName; + } + int getCapabilities() const { + return seq.getPortCapabilities(port); + } + static PortList getPortList(int capabilities, const std::string &clientName); + + operator jack_port_t * () const { return port; } + +protected: + std::string clientName; + jack_port_t * port; + + friend struct JackMidiData; +}; + +LockingJackSequencer JackPortDescriptor::seq; + + + +PortList JackPortDescriptor :: getPortList(int capabilities, const std::string &clientName) +{ + PortList list; + unsigned long flags = 0; + + if (capabilities & INPUT) { + flags |= JackPortIsOutput; + } + if (capabilities & OUTPUT) { + flags |= JackPortIsInput; + } + const char ** ports = seq.getPortList(flags); + if (!ports) return list; + for (const char ** port = ports; *port; port++) { + list.push_back(Pointer( + new JackPortDescriptor(*port, clientName))); + } + jack_free(ports); + return list; +} +#undef RTMIDI_CLASSNAME + +/*! A structure to hold variables related to the JACK API + implementation. + + \note After all sequencer handling is covered by the \ref + JackSequencer class, we should make seq to be a pointer in order + to allow a common client implementation. +*/ + +#define RTMIDI_CLASSNAME "JackMidiData" +struct JackMidiData:public JackPortDescriptor { + /* signal the JACK process what to do next */ + volatile enum { + RUNNING, /*!< keep the client open, flag is owned by the controlling process */ + CLOSING, /*!< close the current port */ + DELETING /*!< Delete the client after delivering the contents of the ring buffer */ + } stateflags; + /*! response/state from the JACK thread. See \ref jackProcessOut for details */ + volatile enum { + OPEN, + CLOSING2, + CLOSED, + DELETING2, + DELETING3 + /* DELETED is useless as this doesn't exist anymore */ + } state_response; + + jack_port_t * local; + jack_ringbuffer_t *buffSize; + jack_ringbuffer_t *buffMessage; + jack_time_t lastTime; +#ifdef HAVE_SEMAPHORE + sem_t sem_cleanup; + sem_t sem_needpost; +#endif + MidiInJack *rtMidiIn; + /*! Sequencer object: This must be deleted _before_ the MIDI data to avoid + segmentation faults while queued data is still in the ring buffer. */ + NonLockingJackSequencer * seq; + + /* + JackMidiData():seq() + { + init(); + } + */ + JackMidiData(const std::string &clientName, + MidiInJack * inputData_):JackPortDescriptor(clientName), + stateflags(RUNNING), + local(0), + buffSize(0), + buffMessage(0), + lastTime(0), + rtMidiIn(inputData_), + seq(new NonLockingJackSequencer(clientName,this)) + { +#ifdef HAVE_SEMAPHORE + sem_init(&sem_cleanup, 0, 0); + sem_init(&sem_needpost, 0, 0); +#endif + } + + /** + * Create output midi data. + * + * \param clientName + * + * \return + */ + JackMidiData(const std::string &clientName):JackPortDescriptor(clientName), + stateflags(RUNNING), + local(0), + buffSize(jack_ringbuffer_create( JACK_RINGBUFFER_SIZE )), + buffMessage(jack_ringbuffer_create( JACK_RINGBUFFER_SIZE )), + lastTime(0), + rtMidiIn(), + seq(new NonLockingJackSequencer(clientName,this)) + { +#ifdef HAVE_SEMAPHORE + sem_init(&sem_cleanup, 0, 0); + sem_init(&sem_needpost, 0, 0); +#endif + } + + + ~JackMidiData() + { + if (local) + deletePort(); + if (seq) + delete seq; +#ifdef HAVE_SEMAPHORE + sem_destroy(&sem_cleanup); + sem_destroy(&sem_needpost); +#endif + + if (buffSize) { + jack_ringbuffer_free( buffSize ); + buffSize = 0; + } + if (buffMessage) { + jack_ringbuffer_free( buffMessage ); + buffMessage = 0; + } + } + + void init(bool isinput) { + seq->init(!isinput); + } - errorString_ = "MidiOutWinMM::setClientName: this function is not implemented for the WINDOWS_MM API!"; - error( RtMidiError::WARNING, errorString_ ); -} + void setRemote(jack_port_t * remote) { + port = remote; + } -void MidiOutWinMM :: setPortName ( const std::string& ) -{ + void connectPorts(jack_port_t * from, + jack_port_t * to) { + seq->connectPorts(from, to); + } - errorString_ = "MidiOutWinMM::setPortName: this function is not implemented for the WINDOWS_MM API!"; - error( RtMidiError::WARNING, errorString_ ); + int openPort(unsigned long jackCapabilities, + const std::string &portName) { + local = seq->createPort(portName, jackCapabilities); + if (!local) { + api->error(RTMIDI_ERROR(gettext_noopt("Error opening JACK port subscription."), + Error::DRIVER_ERROR) ); + return -99; + } + return 0; + } -} + int rename(const std::string &portName) { + return seq->renamePort(local,portName); + } -void MidiOutWinMM :: openVirtualPort( const std::string &/*portName*/ ) -{ - // This function cannot be implemented for the Windows MM MIDI API. - errorString_ = "MidiOutWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; - error( RtMidiError::WARNING, errorString_ ); -} + void delayedDeletePort() { + /* Closing the port can be twofold to ensure all data is sent: + - Use a semaphore to wait for this state + - Close the port from within jackProcessOut + */ + if (local == NULL) return; -void MidiOutWinMM :: sendMessage( const unsigned char *message, size_t size ) -{ - if ( !connected_ ) return; +#ifdef HAVE_SEMAPHORE + struct timespec ts; + if (clock_gettime(CLOCK_REALTIME, &ts) != -1) + { + ts.tv_sec += 1; // wait max one second + sem_post(&sem_needpost); + sem_timedwait(&sem_cleanup, &ts); + } - unsigned int nBytes = static_cast(size); - if ( nBytes == 0 ) { - errorString_ = "MidiOutWinMM::sendMessage: message argument is empty!"; - error( RtMidiError::WARNING, errorString_ ); - return; + deletePort(); +#else + if ( local == NULL || state_response == JackMidiData::CLOSED ) return; + stateflags = JackMidiData::CLOSING; +#endif +#if defined(__RTMIDI_DEBUG__) + std::cerr << "Closed Port" << std::endl; +#endif } - MMRESULT result; - WinMidiData *data = static_cast (apiData_); - if ( message[0] == 0xF0 ) { // Sysex message + void request_delete() { + // signal the output callback to delete the data + // after finishing its job. + // this can be done twofold: + // - via signal in jackProcessOut + // - using a semaphore +#ifdef HAVE_SEMAPHORE + closePort(); - // Allocate buffer for sysex data. - char *buffer = (char *) malloc( nBytes ); - if ( buffer == NULL ) { - errorString_ = "MidiOutWinMM::sendMessage: error allocating sysex message memory!"; - error( RtMidiError::MEMORY_ERROR, errorString_ ); - return; - } + // Cleanup + delete this; + return; +#else + stateflags = JackMidiData::DELETING; +#endif + } - // Copy data to buffer. - for ( unsigned int i=0; ioutHandle, &sysex, sizeof(MIDIHDR) ); - if ( result != MMSYSERR_NOERROR ) { - free( buffer ); - errorString_ = "MidiOutWinMM::sendMessage: error preparing sysex header."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } + case JackMidiData::DELETING: +#if defined(__RTMIDI_DEBUG__) + std::cerr << "deleting port" << std::endl; +#endif + if (state_response != JackMidiData::DELETING2) { + state_response = JackMidiData::DELETING2; + /* output the transferred data */ + return; + } - // Send the message. - result = midiOutLongMsg( data->outHandle, &sysex, sizeof(MIDIHDR) ); - if ( result != MMSYSERR_NOERROR ) { - free( buffer ); - errorString_ = "MidiOutWinMM::sendMessage: error sending sysex message."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + delete this; return; +#if defined(__RTMIDI_DEBUG__) + std::cerr << "deleted port" << std::endl; +#endif + break; } - - // Unprepare the buffer and MIDIHDR. - while ( MIDIERR_STILLPLAYING == midiOutUnprepareHeader( data->outHandle, &sysex, sizeof (MIDIHDR) ) ) Sleep( 1 ); - free( buffer ); +#endif } - else { // Channel or system message. - // Make sure the message size isn't too big. - if ( nBytes > 3 ) { - errorString_ = "MidiOutWinMM::sendMessage: message size is greater than 3 bytes (and not sysex)!"; - error( RtMidiError::WARNING, errorString_ ); + void deletePort() { + if (local == NULL) return; - } - // Pack MIDI bytes into double word. - DWORD packet; - unsigned char *ptr = (unsigned char *) &packet; - for ( unsigned int i=0; ioutHandle, packet ); - if ( result != MMSYSERR_NOERROR ) { - errorString_ = "MidiOutWinMM::sendMessage: error sending MIDI message."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - } + seq->deletePort(local); + local = NULL; } -} - -#endif // __WINDOWS_MM__ - - -//*********************************************************************// -// API: UNIX JACK -// -// Written primarily by Alexander Svetalkin, with updates for delta -// time by Gary Scavone, April 2011. -// -// *********************************************************************// -#if defined(__UNIX_JACK__) + void closePort() { + seq->closePort( local ); + local = NULL; + } -// JACK header files -#include -#include -#include -#ifdef HAVE_SEMAPHORE - #include -#endif + operator jack_port_t * () const { return port; } +}; +#undef RTMIDI_CLASSNAME -#define JACK_RINGBUFFER_SIZE 16384 // Default size for ringbuffer -struct JackMidiData { - jack_client_t *client; - jack_port_t *port; - jack_ringbuffer_t *buffSize; - jack_ringbuffer_t *buffMessage; - jack_time_t lastTime; -#ifdef HAVE_SEMAPHORE - sem_t sem_cleanup; - sem_t sem_needpost; -#endif - MidiInApi :: RtMidiInData *rtMidiIn; - }; //*********************************************************************// // API: JACK // Class Definitions: MidiInJack //*********************************************************************// -static int jackProcessIn( jack_nframes_t nframes, void *arg ) +#define RTMIDI_CLASSNAME "JackBackendCallbacks" +int JackBackendCallbacks::jackProcessIn( jack_nframes_t nframes, void *arg ) { JackMidiData *jData = (JackMidiData *) arg; - MidiInApi :: RtMidiInData *rtData = jData->rtMidiIn; + MidiInJack *rtData = jData->rtMidiIn; jack_midi_event_t event; jack_time_t time; // Is port created? - if ( jData->port == NULL ) return 0; - void *buff = jack_port_get_buffer( jData->port, nframes ); + if ( jData->local == NULL ) return 0; + void *buff = jack_port_get_buffer( jData->local, nframes ); // We have midi events in buffer int evCount = jack_midi_get_event_count( buff ); @@ -2933,14 +5921,19 @@ static int jackProcessIn( jack_nframes_t nframes, void *arg ) jData->lastTime = time; if ( !rtData->continueSysex ) { - if ( rtData->usingCallback ) { - RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) rtData->userCallback; - callback( message.timeStamp, &message.bytes, rtData->userData ); + if ( rtData->userCallback ) { + rtData->userCallback->rtmidi_midi_in( message.timeStamp, message.bytes); } else { - // As long as we haven't reached our queue size limit, push the message. - if (!rtData->queue.push(message)) - std::cerr << "\nMidiInJack: message queue limit reached!!\n\n"; + // As long as we haven't reached our queue size limit, push the message. + if (!rtData->queue.push(message)) { + try { + rtData->error(RTMIDI_ERROR(rtmidi_gettext("Error: Message queue limit reached."), + Error::WARNING)); + } catch (Error & e) { + // don't bother JACK with an unhandled exception + } + } } } } @@ -2948,48 +5941,93 @@ static int jackProcessIn( jack_nframes_t nframes, void *arg ) return 0; } +// Jack process callback +int JackBackendCallbacks::jackProcessOut( jack_nframes_t nframes, void *arg ) +{ + JackMidiData *data = (JackMidiData *) arg; + jack_midi_data_t *midiData; + int space; + + // Is port created? + if ( data->local == NULL ) return 0; + + void *buff = jack_port_get_buffer( data->local, nframes ); + if (buff != NULL) { + jack_midi_clear_buffer( buff ); + + while ( jack_ringbuffer_read_space( data->buffSize ) > 0 ) { + jack_ringbuffer_read( data->buffSize, (char *) &space, (size_t) sizeof(space) ); + midiData = jack_midi_event_reserve( buff, 0, space ); + + jack_ringbuffer_read( data->buffMessage, (char *) midiData, (size_t) space ); + } + } + + data->deletePortIfRequested(); + return 0; +} +#undef RTMIDI_CLASSNAME + +#define RTMIDI_CLASSNAME "MidiInJack" MidiInJack :: MidiInJack( const std::string &clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { MidiInJack::initialize( clientName ); } -void MidiInJack :: initialize( const std::string& clientName ) +void MidiInJack :: initialize( const std::string &clientName ) { - JackMidiData *data = new JackMidiData; + JackMidiData *data = new JackMidiData(clientName,this); apiData_ = (void *) data; - - data->rtMidiIn = &inputData_; - data->port = NULL; - data->client = NULL; this->clientName = clientName; - - connect(); + try { + data->init(true); + } catch (const Error & e) { + delete data; + apiData_ = 0; + throw; + } } +#if 0 void MidiInJack :: connect() { + abort(); + // this should be unnecessary JackMidiData *data = static_cast (apiData_); - if ( data->client ) + if ( data->local ) return; // Initialize JACK client - if (( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) { - errorString_ = "MidiInJack::initialize: JACK server not running?"; - error( RtMidiError::WARNING, errorString_ ); + if (( data->local = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) { + error(RTMIDI_ERROR(gettext_noopt("JACK server not running?"), + Error::WARNING) ); return; } jack_set_process_callback( data->client, jackProcessIn, data ); jack_activate( data->client ); } +#endif MidiInJack :: ~MidiInJack() { JackMidiData *data = static_cast (apiData_); - MidiInJack::closePort(); + try { + MidiInJack::closePort(); + } catch (Error & e) { + try { + delete data; + } catch (...) { + } + error(e); + return; + } +#if 0 if ( data->client ) jack_client_close( data->client ); +#endif + /* immediately shut down the JACK client */ delete data; } @@ -2997,49 +6035,118 @@ void MidiInJack :: openPort( unsigned int portNumber, const std::string &portNam { JackMidiData *data = static_cast (apiData_); - connect(); + // connect(); // Creating new port - if ( data->port == NULL) - data->port = jack_port_register( data->client, portName.c_str(), - JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 ); + if ( data->local == NULL) + data->local = jack_port_register( *(data->seq), portName.c_str(), + JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 ); - if ( data->port == NULL) { - errorString_ = "MidiInJack::openPort: JACK error creating port"; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + if ( data->local == NULL) { + error(RTMIDI_ERROR(gettext_noopt("Error creating JACK port."), + Error::DRIVER_ERROR) ); return; } // Connecting to the output std::string name = getPortName( portNumber ); - jack_connect( data->client, name.c_str(), jack_port_name( data->port ) ); + jack_connect( *(data->seq), name.c_str(), jack_port_name( data->local ) ); } void MidiInJack :: openVirtualPort( const std::string &portName ) { JackMidiData *data = static_cast (apiData_); - connect(); - if ( data->port == NULL ) - data->port = jack_port_register( data->client, portName.c_str(), - JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 ); + // connect(); + if ( data->local == NULL ) + data->local = jack_port_register( *(data->seq), portName.c_str(), + JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 ); + + if ( data->local == NULL ) { + error(RTMIDI_ERROR(gettext_noopt("Error creating JACK virtual port."), + Error::DRIVER_ERROR) ); + } +} + +void MidiInJack :: openPort( const PortDescriptor & p, + const std::string &portName ) +{ + JackMidiData *data = static_cast (apiData_); + const JackPortDescriptor * port = dynamic_cast(&p); + + if ( !data ) { + error(RTMIDI_ERROR(gettext_noopt("Data has not been allocated."), + Error::SYSTEM_ERROR) ); + return; + } +#if 0 + if ( connected_ ) { + error(RTMIDI_ERROR(gettext_noopt("A valid connection already exists."), + Error::WARNING) ); + return; + } +#endif + if (!port) { + error(RTMIDI_ERROR(gettext_noopt("JACK has been instructed to open a non-JACK MIDI port. This doesn't work."), + Error::INVALID_DEVICE) ); + return; + } + + try { + if (!data->local) + data->openPort (JackPortIsInput, + portName); + data->setRemote(*port); + data->connectPorts(*port,data->local); + } catch (Error & e) { + error (e); + } + +} + +Pointer MidiInJack :: getDescriptor(bool local) +{ + JackMidiData *data = static_cast (apiData_); + try { + if (local) { + if (data && data->local) { + return Pointer( + new JackPortDescriptor(data->local,data->getClientName())); + } + } else { + if (data && *data) { + return Pointer( + new JackPortDescriptor(*data,data->getClientName())); + } + } + } catch (Error & e) { + error(e); + } + return NULL; +} - if ( data->port == NULL ) { - errorString_ = "MidiInJack::openVirtualPort: JACK error creating virtual port"; - error( RtMidiError::DRIVER_ERROR, errorString_ ); +PortList MidiInJack :: getPortList(int capabilities) +{ + JackMidiData *data = static_cast (apiData_); + try { + return JackPortDescriptor::getPortList(capabilities | PortDescriptor::INPUT, + data->getClientName()); + } catch (Error & e) { + error(e); } + return PortList(); } unsigned int MidiInJack :: getPortCount() { int count = 0; + // connect(); JackMidiData *data = static_cast (apiData_); - connect(); - if ( !data->client ) + if ( !(*(data->seq)) ) return 0; // List of available ports - const char **ports = jack_get_ports( data->client, NULL, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput ); + const char **ports = jack_get_ports( *(data->seq), NULL, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput ); if ( ports == NULL ) return 0; while ( ports[count] != NULL ) @@ -3055,16 +6162,16 @@ std::string MidiInJack :: getPortName( unsigned int portNumber ) JackMidiData *data = static_cast (apiData_); std::string retStr(""); - connect(); + // connect(); // List of available ports - const char **ports = jack_get_ports( data->client, NULL, - JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput ); + const char **ports = jack_get_ports(* (data->seq), NULL, + JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput ); // Check port validity if ( ports == NULL ) { - errorString_ = "MidiInJack::getPortName: no ports available!"; - error( RtMidiError::WARNING, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("No ports available."), + Error::WARNING) ); return retStr; } @@ -3072,9 +6179,8 @@ std::string MidiInJack :: getPortName( unsigned int portNumber ) for (i=0; i (apiData_); + if (!data) return; - if ( data->port == NULL ) return; - jack_port_unregister( data->client, data->port ); - data->port = NULL; + data->deletePort(); } -void MidiInJack:: setClientName( const std::string& ) +void MidiInJack :: setClientName( const std::string& ) { - - errorString_ = "MidiInJack::setClientName: this function is not implemented for the UNIX_JACK API!"; - error( RtMidiError::WARNING, errorString_ ); - + error(RTMIDI_ERROR(gettext_noopt("Setting the client name is not supported by JACK."), + Error::WARNING )); } void MidiInJack :: setPortName( const std::string &portName ) { JackMidiData *data = static_cast (apiData_); -#ifdef JACK_HAS_PORT_RENAME - jack_port_rename( data->client, data->port, portName.c_str() ); -#else - jack_port_set_name( data->port, portName.c_str() ); -#endif + data->rename(portName); } +#undef RTMIDI_CLASSNAME //*********************************************************************// // API: JACK // Class Definitions: MidiOutJack //*********************************************************************// -// Jack process callback -static int jackProcessOut( jack_nframes_t nframes, void *arg ) -{ - JackMidiData *data = (JackMidiData *) arg; - jack_midi_data_t *midiData; - int space; - - // Is port created? - if ( data->port == NULL ) return 0; - - void *buff = jack_port_get_buffer( data->port, nframes ); - jack_midi_clear_buffer( buff ); - - while ( jack_ringbuffer_read_space( data->buffSize ) > 0 ) { - jack_ringbuffer_read( data->buffSize, (char *) &space, (size_t) sizeof(space) ); - midiData = jack_midi_event_reserve( buff, 0, space ); - - jack_ringbuffer_read( data->buffMessage, (char *) midiData, (size_t) space ); - } - -#ifdef HAVE_SEMAPHORE - if (!sem_trywait(&data->sem_needpost)) - sem_post(&data->sem_cleanup); -#endif - - return 0; -} +#define RTMIDI_CLASSNAME "MidiOutJack" MidiOutJack :: MidiOutJack( const std::string &clientName ) : MidiOutApi() { MidiOutJack::initialize( clientName ); } -void MidiOutJack :: initialize( const std::string& clientName ) +void MidiOutJack :: initialize( const std::string &clientName ) { - JackMidiData *data = new JackMidiData; + JackMidiData *data = new JackMidiData(clientName); apiData_ = (void *) data; - - data->port = NULL; - data->client = NULL; -#ifdef HAVE_SEMAPHORE - sem_init(&data->sem_cleanup, 0, 0); - sem_init(&data->sem_needpost, 0, 0); -#endif this->clientName = clientName; - - connect(); + // init is the last as it may throw an exception + try { + data->init(false); + } catch (const Error & e) { + delete data; + apiData_ = 0; + throw; + } } void MidiOutJack :: connect() { +#if 0 JackMidiData *data = static_cast (apiData_); - if ( data->client ) + if ( *(data->seq) ) return; - - // Initialize output ringbuffers - data->buffSize = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE ); - data->buffMessage = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE ); // Initialize JACK client - if (( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) { - errorString_ = "MidiOutJack::initialize: JACK server not running?"; - error( RtMidiError::WARNING, errorString_ ); + if (( *(data->seq) = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) { + error(RTMIDI_ERROR(gettext_noopt("JACK server not running?"), + Error::WARNING) ); return; } jack_set_process_callback( data->client, jackProcessOut, data ); + data->buffSize = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE ); + data->buffMessage = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE ); jack_activate( data->client ); +#endif } MidiOutJack :: ~MidiOutJack() { JackMidiData *data = static_cast (apiData_); - MidiOutJack::closePort(); - - // Cleanup - jack_ringbuffer_free( data->buffSize ); - jack_ringbuffer_free( data->buffMessage ); - if ( data->client ) { - jack_client_close( data->client ); - } - -#ifdef HAVE_SEMAPHORE - sem_destroy(&data->sem_cleanup); - sem_destroy(&data->sem_needpost); -#endif - - delete data; + // closePort(); + data -> request_delete(); } void MidiOutJack :: openPort( unsigned int portNumber, const std::string &portName ) { JackMidiData *data = static_cast (apiData_); - connect(); + // connect(); // Creating new port - if ( data->port == NULL ) - data->port = jack_port_register( data->client, portName.c_str(), - JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 ); + if ( data->local == NULL ) + data->local = jack_port_register( *(data->seq), portName.c_str(), + JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 ); - if ( data->port == NULL ) { - errorString_ = "MidiOutJack::openPort: JACK error creating port"; - error( RtMidiError::DRIVER_ERROR, errorString_ ); + if ( data->local == NULL ) { + error(RTMIDI_ERROR(gettext_noopt("Error creating JACK port."), + Error::DRIVER_ERROR) ); return; } // Connecting to the output std::string name = getPortName( portNumber ); - jack_connect( data->client, jack_port_name( data->port ), name.c_str() ); + jack_connect( *(data->seq), jack_port_name( data->local ), name.c_str() ); } void MidiOutJack :: openVirtualPort( const std::string &portName ) { JackMidiData *data = static_cast (apiData_); - connect(); - if ( data->port == NULL ) - data->port = jack_port_register( data->client, portName.c_str(), - JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 ); + // connect(); + if ( data->local == NULL ) + data->local = jack_port_register( *(data->seq), portName.c_str(), + JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 ); + + if ( data->local == NULL ) { + error(RTMIDI_ERROR(gettext_noopt("Error creating JACK virtual port."), + Error::DRIVER_ERROR) ); + } +} + +void MidiOutJack :: openPort( const PortDescriptor & p, + const std::string &portName ) +{ + JackMidiData *data = static_cast (apiData_); + const JackPortDescriptor * port = dynamic_cast(&p); + + if ( !data ) { + error(RTMIDI_ERROR(gettext_noopt("Data has not been allocated."), + Error::SYSTEM_ERROR) ); + return; + } +#if 0 + if ( connected_ ) { + error(RTMIDI_ERROR(gettext_noopt("A valid connection already exists."), + Error::WARNING) ); + return; + } +#endif + if (!port) { + error(RTMIDI_ERROR(gettext_noopt("JACK has been instructed to open a non-JACK MIDI port. This doesn't work."), + Error::INVALID_DEVICE) ); + return; + } + + try { + if (!data->local) + data->openPort (JackPortIsOutput, + portName); + data->setRemote(*port); + data->connectPorts(data->local,*port); + } catch (Error & e) { + error(e); + } +} - if ( data->port == NULL ) { - errorString_ = "MidiOutJack::openVirtualPort: JACK error creating virtual port"; - error( RtMidiError::DRIVER_ERROR, errorString_ ); +Pointer MidiOutJack :: getDescriptor(bool local) +{ + JackMidiData *data = static_cast (apiData_); + try { + if (local) { + if (data && data->local) { + return Pointer( + new JackPortDescriptor(data->local,data->getClientName())); + } + } else { + if (data && *data) { + return Pointer( + new JackPortDescriptor(*data,data->getClientName())); + } + } + } catch (Error & e) { + error(e); } + return NULL; +} + +PortList MidiOutJack :: getPortList(int capabilities) +{ + JackMidiData *data = static_cast (apiData_); + return JackPortDescriptor::getPortList(capabilities | PortDescriptor::OUTPUT, + data->getClientName()); } unsigned int MidiOutJack :: getPortCount() { int count = 0; JackMidiData *data = static_cast (apiData_); - connect(); - if ( !data->client ) + // connect(); + if ( !*(data->seq) ) return 0; // List of available ports - const char **ports = jack_get_ports( data->client, NULL, - JACK_DEFAULT_MIDI_TYPE, JackPortIsInput ); + const char **ports = jack_get_ports(* (data->seq), NULL, + JACK_DEFAULT_MIDI_TYPE, JackPortIsInput ); if ( ports == NULL ) return 0; while ( ports[count] != NULL ) @@ -3267,24 +6390,23 @@ std::string MidiOutJack :: getPortName( unsigned int portNumber ) JackMidiData *data = static_cast (apiData_); std::string retStr(""); - connect(); + // connect(); // List of available ports - const char **ports = jack_get_ports( data->client, NULL, - JACK_DEFAULT_MIDI_TYPE, JackPortIsInput ); + const char **ports = jack_get_ports(*(data->seq), NULL, + JACK_DEFAULT_MIDI_TYPE, JackPortIsInput ); // Check port validity if ( ports == NULL) { - errorString_ = "MidiOutJack::getPortName: no ports available!"; - error( RtMidiError::WARNING, errorString_ ); + error(RTMIDI_ERROR(gettext_noopt("No ports available."), + Error::WARNING) ); return retStr; } if ( ports[portNumber] == NULL) { std::ostringstream ost; - ost << "MidiOutJack::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtMidiError::WARNING, errorString_ ); + error(RTMIDI_ERROR1(gettext_noopt("The 'portNumber' argument (%d) is invalid."), + Error::WARNING, portNumber) ); } else retStr.assign( ports[portNumber] ); @@ -3294,45 +6416,29 @@ std::string MidiOutJack :: getPortName( unsigned int portNumber ) void MidiOutJack :: closePort() { - JackMidiData *data = static_cast (apiData_); - - if ( data->port == NULL ) return; - -#ifdef HAVE_SEMAPHORE - struct timespec ts; - if (clock_gettime(CLOCK_REALTIME, &ts) != -1) - { - ts.tv_sec += 1; // wait max one second - sem_post(&data->sem_needpost); - sem_timedwait(&data->sem_cleanup, &ts); - } +#if defined(__RTMIDI_DEBUG__) + std::cerr << "Closing Port" << std::endl; #endif + JackMidiData *data = static_cast (apiData_); - jack_port_unregister( data->client, data->port ); - data->port = NULL; + data->delayedDeletePort(); } void MidiOutJack:: setClientName( const std::string& ) { - - errorString_ = "MidiOutJack::setClientName: this function is not implemented for the UNIX_JACK API!"; - error( RtMidiError::WARNING, errorString_ ); - + error(RTMIDI_ERROR(gettext_noopt("Setting the client name is not supported by JACK."), + Error::WARNING )); } void MidiOutJack :: setPortName( const std::string &portName ) { JackMidiData *data = static_cast (apiData_); -#ifdef JACK_HAS_PORT_RENAME - jack_port_rename( data->client, data->port, portName.c_str() ); -#else - jack_port_set_name( data->port, portName.c_str() ); -#endif + data->rename(portName); } void MidiOutJack :: sendMessage( const unsigned char *message, size_t size ) { - int nBytes = static_cast(size); + int nBytes = size; JackMidiData *data = static_cast (apiData_); // Write full message to buffer @@ -3340,5 +6446,7 @@ void MidiOutJack :: sendMessage( const unsigned char *message, size_t size ) nBytes ); jack_ringbuffer_write( data->buffSize, ( char * ) &nBytes, sizeof( nBytes ) ); } - +#undef RTMIDI_CLASSNAME +RTMIDI_NAMESPACE_END #endif // __UNIX_JACK__ + diff --git a/RtMidi.h b/RtMidi.h index 8c47f4c7..51f9a3d8 100644 --- a/RtMidi.h +++ b/RtMidi.h @@ -1,48 +1,76 @@ -/**********************************************************************/ +/********************* -*- C++ -*- ****************************************/ /*! \class RtMidi - \brief An abstract base class for realtime MIDI input/output. - - This class implements some common functionality for the realtime - MIDI input/output subclasses RtMidiIn and RtMidiOut. - - RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/ - - RtMidi: realtime MIDI i/o C++ classes - Copyright (c) 2003-2017 Gary P. Scavone - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - Any person wishing to distribute modifications to the Software is - asked to send the modifications to the original developer so that - they can be incorporated into the canonical version. This is, - however, not a binding provision of this license. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + \brief An abstract base class for realtime MIDI input/output. + + This class implements some common functionality for the realtime + MIDI input/output subclasses RtMidiIn and RtMidiOut. + + RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/ + + RtMidi: realtime MIDI i/o C++ classes + Copyright (c) 2003-2017 Gary P. Scavone + Forked by Tobias Schlemmer, 2014-2018. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + asked to send the modifications to the original developer so that + they can be incorporated into the canonical version. This is, + however, not a binding provision of this license. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**********************************************************************/ /*! \file RtMidi.h - */ +*/ #ifndef RTMIDI_H #define RTMIDI_H +#define RTMIDI_VERSION "4.0.0" + +#ifdef RTMIDI_NO_WARN_DEPRECATED +#define RTMIDI_DEPRECATED(func,message) func +#else +#ifdef __GNUC__ +#define RTMIDI_DEPRECATED(func,message) func __attribute__ ((deprecated(message))) +#elif defined(_MSC_VER) +#define RTMIDI_DEPRECATED(func,message) __declspec(deprecated(message)) func +#else +#pragma message("WARNING: You need to implement the macro RTMIDI_DEPRECATED for this compiler if this code doesn't compile") +#define RTMIDI_DEPRECATED(func,message) func [[deprecated(message)]] +#endif +#endif + +#define rtmidiUnused(x) do { (void)x; } while (0) + +// Check for C++11 support +#if defined(_MSC_VER) && _MSC_VER >= 1800 +// At least Visual Studio 2013 +#define RTMIDI_SUPPORTS_CPP11 1 +#elif __cplusplus >= 201103L +#define RTMIDI_SUPPORTS_CPP11 1 +#else +#define RTMIDI_SUPPORTS_CPP11 0 +#endif + #if defined _WIN32 || defined __CYGWIN__ #define RTMIDI_DLL_PUBLIC #else @@ -53,27 +81,114 @@ #endif #endif -#define RTMIDI_VERSION "3.0.0" #include #include #include #include +#include +#include +#include +// the following are used in the error constructor +#include +#include +#include + +#ifdef RTMIDI_GETTEXT +#include "libintl.h" +#endif +#define gettext_noopt(str) (str) + +#define RTMIDI_NAMESPACE_START namespace rtmidi { +#define RTMIDI_NAMESPACE_END } + +RTMIDI_NAMESPACE_START + +#ifdef RTMIDI_GETTEXT +const char * rtmidi_gettext(const char * s); +void init_rtmidi_gettext(); +#else +#define rtmidi_gettext(arg) (arg) +#endif + +//! MIDI API specifier arguments. +enum ApiType { + UNSPECIFIED, /*!< Search for a working compiled API. */ + MACOSX_CORE, /*!< Macintosh OS-X Core Midi API. */ + LINUX_ALSA, /*!< The Advanced Linux Sound Architecture API. */ + UNIX_JACK, /*!< The JACK Low-Latency MIDI Server API. */ + WINDOWS_MM, /*!< The Microsoft Multimedia MIDI API. */ + WINDOWS_KS, /*!< The Microsoft Kernel Streaming MIDI API. */ + DUMMY, /*!< A compilable but non-functional API. */ + ALL_API /*!< Use all available APIs for port selection. */ +}; + +//! Return the name on a MIDI API +inline std::string getApiName(ApiType type) { + switch (type) { + case UNSPECIFIED: return rtmidi_gettext("Automatic selection"); + case MACOSX_CORE: return rtmidi_gettext("Core MIDI"); + case LINUX_ALSA: return rtmidi_gettext("ALSA"); + case UNIX_JACK: return rtmidi_gettext("JACK"); + case WINDOWS_MM: return rtmidi_gettext("Windows Multimedia"); + case WINDOWS_KS: return rtmidi_gettext("DirectX/Kernel Streaming"); + case DUMMY: return rtmidi_gettext("NULL device"); + case ALL_API: return rtmidi_gettext("All available MIDI systems"); + } + return ""; +} + +//! C++ style user callback interface. +/*! + This interface class can be used to implement type safe MIDI callbacks. + Every time a MIDI message is received the function \ref MidiInterface::rtmidi_midi_in of the + currently set callback object is called. +*/ +struct MidiInterface { + //! Virtual destructor to avoid unexpected behaviour. + virtual ~MidiInterface() {} + + //! The MIDI callback function. + /*! This function is called whenever a MIDI packet is received by a + MIDI backend that uses the corresponding callback object. + \param timestamp the timestamp when the MIDI message has been received + \param message the message itself. + */ + virtual void rtmidi_midi_in(double timestamp, std::vector &message) = 0; + + //! Delete the object if necessary. + /*! This function allows the user to delete the Midi callback object, + when MIDI backend drops its reference to it. By default it does nothing. + But, callback objects are owned by the MIDI backend. These must be deleted + after the reference to them has been dropped. + + \sa CompatibilityMidiInterface + */ + virtual void delete_me() {} +}; /************************************************************************/ -/*! \class RtMidiError - \brief Exception handling class for RtMidi. +/*! \class Error + \brief Exception handling class for RtMidi. - The RtMidiError class is quite simple but it does allow errors to be - "caught" by RtMidiError::Type. See the RtMidi documentation to know - which methods can throw an RtMidiError. + The Error class is quite simple but it does allow errors to be + "caught" by Error::Type. See the RtMidi documentation to know + which methods can throw an Error. */ /************************************************************************/ -class RTMIDI_DLL_PUBLIC RtMidiError : public std::exception +#define RTMIDI_ERROR(message, type) \ + rtmidi::Error(message, type, \ + RTMIDI_CLASSNAME, __FUNCTION__, \ + __FILE__, __LINE__) +#define RTMIDI_ERROR1(message, type, arg1) \ + rtmidi::Error(message, type, \ + RTMIDI_CLASSNAME, __FUNCTION__, \ + __FILE__, __LINE__, arg1) +class RTMIDI_DLL_PUBLIC Error : public std::exception { - public: - //! Defined RtMidiError types. +public: + //! Defined Error types. enum Type { WARNING, /*!< A non-critical error. */ DEBUG_WARNING, /*!< A non-critical error which might be useful for debugging. */ @@ -89,54 +204,305 @@ class RTMIDI_DLL_PUBLIC RtMidiError : public std::exception }; //! The constructor. - RtMidiError( const std::string& message, Type type = RtMidiError::UNSPECIFIED ) throw() : message_(message), type_(type) {} - + Error( const char * message, + Type type, + const char * class_name, + const char * function_name, + const char * file_name, + int line_number, ...) throw(); + //! The destructor. - virtual ~RtMidiError( void ) throw() {} + virtual ~Error( void ) throw() {} //! Prints thrown error message to stderr. - virtual void printMessage( void ) const throw() { std::cerr << '\n' << message_ << "\n\n"; } + virtual void printMessage( std::ostream &s = std::cerr ) const throw() { + s << std::endl + << file << ":" << line << ": in function " + << classname << "::" << function << std::endl + << message_ << std::endl << std::endl; + } //! Returns the thrown error message type. virtual const Type& getType(void) const throw() { return type_; } //! Returns the thrown error message string. - virtual const std::string& getMessage(void) const throw() { return message_; } + virtual const std::string &getMessage(void) const throw() { return message_; } //! Returns the thrown error message as a c-style string. virtual const char* what( void ) const throw() { return message_.c_str(); } - protected: +protected: + const char * classname; + const char * function; + const char * file; + int line; std::string message_; Type type_; }; -//! RtMidi error callback function prototype. -/*! - \param type Type of error. - \param errorText Error description. - Note that class behaviour is undefined after a critical error (not - a warning) is reported. - */ -typedef void (*RtMidiErrorCallback)( RtMidiError::Type type, const std::string &errorText, void *userData ); +struct ErrorInterface { + virtual ~ErrorInterface() {} + virtual void rtmidi_error (Error e) = 0; + virtual void delete_me() {}; +}; + +#if !RTMIDI_SUPPORTS_CPP11 +class PortDescriptor; + +template +class Pointer { +public: + typedef T datatype; +protected: + struct countPointer { + int count; + datatype * descriptor; + }; +public: + Pointer():ptr(0) {} + Pointer(datatype * p):ptr(new countPointer) { + ptr->count = 1; + ptr->descriptor = p; + } + Pointer(const Pointer & other): + ptr(other.ptr) { + if (ptr) + ptr->count++; + } + Pointer(const Pointer && other): + ptr(other.ptr) { + } + + ~Pointer() { + if (!ptr) return; + if (!(--(ptr->count))) { + delete ptr->descriptor; + delete ptr; + } + } + + datatype * operator -> () { + if (!ptr) return 0; + // this should throw an exception + + return ptr->descriptor; + } + + datatype & operator * () { + if (!ptr || !ptr->descriptor) { + throw std::invalid_argument("rtmidi::Pointer: trying to dereference a NULL pointer."); + } + else return (*ptr->descriptor); + } + + const datatype & operator * () const { + if (!ptr || !ptr->descriptor) { + throw std::invalid_argument("rtmidi::Pointer: trying to dereference a NULL pointer."); + } + else return (*ptr->descriptor); + } + + bool operator ! () { + return (!ptr || !ptr->descriptor); + } + + operator bool () { + return (ptr && ptr->descriptor); + } + + Pointer & operator = (const Pointer & other) { + if (ptr) { + if (!(--ptr->count)) { + delete ptr->descriptor; + delete ptr; + } + } + if ((ptr = other.ptr)) + ptr->count++; + return *this; + } +protected: + countPointer * ptr; +}; + +template +bool operator==(const Pointer& lhs, const Pointer& rhs) { + return (&(*lhs)) == (&(*rhs)); +} + +template +bool operator!=(const Pointer& lhs, const Pointer& rhs) { + return (&(*lhs)) != (&(*rhs)); +} + +#else +template +using Pointer = std::shared_ptr; +#endif class MidiApi; +class MidiInApi; +class MidiOutApi; +typedef Pointer MidiApiPtr; +typedef std::list MidiApiList; + +class PortDescriptor { +public: + //! Flags for formatting a string description of the port. + /*! These flags just mark the requirements that the string + should fulfil. An API may return the same string for + different requirements e.g. the same short and long + name. */ + enum NamingType { + SHORT_NAME =0, /*!< A short human readable name + depending on the API + e.g. “Ensoniq AudioPCI” */ + LONG_NAME, /*!< A complete human readable + name depending on the API + e.g. "Ensoniq AudioPCI: ES1371" */ + SESSION_PATH, /*!< A unique description that can + be used to identify the port + during runtime. It may be a + cryptic string. */ + STORAGE_PATH, /*!< A unique description that is + optimised for storage in + configuration files. This is a + more textual representation that + is more robust to small changes in + the surrounding environment than + \ref SESSION_PATH */ + NAMING_MASK = 0x0F, /*!< part of the flags + that is concerned with + naming. + */ + UNIQUE_PORT_NAME = 0x10, /*!< Make all names uniqe. This + is usually done by adding + numbers to the end of the + string \note: use #undef UNIQUE_PORT_NAME + on windows in case of any errors */ + INCLUDE_API = 0x20 /*!< Add a string describing the + API at the beginning of the + string. */ + }; -class RTMIDI_DLL_PUBLIC RtMidi -{ - public: - - //! MIDI API specifier arguments. - enum Api { - UNSPECIFIED, /*!< Search for a working compiled API. */ - MACOSX_CORE, /*!< Macintosh OS-X CoreMIDI API. */ - LINUX_ALSA, /*!< The Advanced Linux Sound Architecture API. */ - UNIX_JACK, /*!< The JACK Low-Latency MIDI Server API. */ - WINDOWS_MM, /*!< The Microsoft Multimedia MIDI API. */ - RTMIDI_DUMMY /*!< A compilable but non-functional API. */ + //! Flags describing the capabilities of a given port. + enum PortCapabilities { + INPUT = 1, /*!< Ports that can be read from. */ + OUTPUT = 2, /*!< Ports that can be written to. */ + INOUTPUT = 3, /*!< Ports that allow reading and writing (INPUT | OUTPUT) */ + UNLIMITED = 0x10 /*!< Some APIs can filter out certain ports which they consider + not to be useful. This flags supresses this behaviour and + selects all ports that are useable. */ }; + //! Default constructor. + /*! + * Derived classes should have a constructor. + */ + PortDescriptor() {}; + + //! A virtual destructor + /*! As we might have to destruct the object from the application code + * each port id must have a virtual destructor. + */ + virtual ~PortDescriptor() {}; + + //! Get the MIDI input api for the current port. + /*! This is the only information RtMidi needs to know: Which + * API should handle this object. This can be used to get + * an API which can send data to the given port. + * + * \param queueSizeLimit The limit of the midi queue. This parameter is handled by + * the constructor of the backend API. + * + * \return API that can use this object to connect to an input port or 0 + * if no input API can be created. + */ + virtual MidiInApi * getInputApi(unsigned int queueSizeLimit = 100) const = 0; + + //! Get the MIDI output api for the current port. + /*! This is the only information RtMidi needs to know: Which + * API should handle this object. This can be used to get + * an API which can receive data from the given port. + * + * \return API that can use this object to connect to an output port. + */ + virtual MidiOutApi * getOutputApi() const = 0; + + //! Return the port name + /*! + * \param flags A description of the requirements of the returned name. + * \return A name that is formatted according to \ref flags. + * \sa NamingTypes + */ + virtual std::string getName(int flags = SHORT_NAME | UNIQUE_PORT_NAME) = 0; + + //! Get capabilities + /*! \return a capabilities flag describing the capabilities of the port. + * \sa PortCapabilities + */ + virtual int getCapabilities() const = 0; +}; + +//! A list of port descriptors. +/*! Port descriptors are stored as shared pointers. This avoids + unnecessary duplication of the data structure and handles automatic + deletion if all references have been removed. */ +typedef Pointer PortPointer; +typedef std::list > PortList; + +/* A deprecated type. See below for the documentation. We + split the definiton into several pieces to work around some + intended warnings. */ +typedef void (*ErrorCallback_t)( Error::Type type, const std::string &errorText, void * userdata ); +//! RtMidi error callback function prototype. +/*! + \param type Type of error. + \param errorText Error description. + + Note that class behaviour is undefined after a critical error (not + a warning) is reported. + \sa ErrorInterface + \deprecated +*/ +RTMIDI_DEPRECATED(typedef ErrorCallback_t ErrorCallback,"RtMidi now provides a class MidiInterface for error callbacks"); + +/* A deprecated type. See below for the documentation. We + split the definiton into several pieces to work around some + intended warnings. */ +#define ErrorCallback ErrorCallback_t + +typedef void (*MidiCallback_t)( double timeStamp, std::vector *message, void *userData); +//! C style user callback function type definition. +/*! + This interface type has been replaced by a MidiInterface class. + + \param timeStamp timestamp indicating when the event has been received + \param message a pointer to the binary MIDI message + \param userData a pointer that can be set using setUserdata + \sa MidiIn + \sa MidiInApi + \sa MidiInterface + \deprecated +*/ +RTMIDI_DEPRECATED(typedef MidiCallback_t MidiCallback,"RtMidi now provides a class MidiInterface for MIDI callbacks"); +#define MidiCallback MidiCallback_t + + +/*! \class Midi + \brief A global class that implements basic backend API handling. + + This class enhances \ref MidiApi by some functionality to handle + backend API objects. It serves as base class for the public RtMidi + API. + + by Gary P. Scavone, 2003-2014. +*/ +/**********************************************************************/ +#define RTMIDI_CLASSNAME "Midi" +class RTMIDI_DLL_PUBLIC Midi { +public: //! A static function to determine the current RtMidi version. static std::string getVersion( void ) throw(); @@ -145,87 +511,212 @@ class RTMIDI_DLL_PUBLIC RtMidi The values returned in the std::vector can be compared against the enumerated list values. Note that there can be more than one API compiled for certain operating systems. + + \param apis A vector apis must be provided for the + return value. All data in this vector will be + deleted prior to filling in the API data. + + \param preferSystem An opitonal boolean parameter + may be provided that tells wheter system or software + APIs shall be prefered. Passing \c true will prefer OS provided APIs */ - static void getCompiledApi( std::vector &apis ) throw(); + static void getCompiledApi( std::vector &apis, bool preferSystem = true ) throw(); - //! Pure virtual openPort() function. - virtual void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi" ) ) = 0; + //! A static function to determine the available compiled MIDI APIs. + /*! + The values returned in the std::vector can be compared against + the enumerated list values. Note that there can be more than one + API compiled for certain operating systems. + */ + static std::vector getCompiledApi( ) throw(); - //! Pure virtual openVirtualPort() function. - virtual void openVirtualPort( const std::string &portName = std::string( "RtMidi" ) ) = 0; + //! Returns the MIDI API specifier for the current instance of rtmidi::MidiIn. + ApiType getCurrentApi( void ) throw(); - //! Pure virtual getPortCount() function. - virtual unsigned int getPortCount() = 0; - //! Pure virtual getPortName() function. - virtual std::string getPortName( unsigned int portNumber = 0 ) = 0; + //! Pure virtual function to return a port descirptor if the port is open + Pointer getDescriptor(bool local=false); - //! Pure virtual closePort() function. - virtual void closePort( void ) = 0; + //! Return a list of all available ports of the current API. + /*! + \param capabilities an optional parameter that + describes which capabilities the returned devices + must support. The returned devices may have + additional capabilities to those which have been + requested, but not less. + + \return This function returns a list of port descriptors. + + \note Each API will request additonal + capabilites. An output API will set always add \ref + PortDescriptor::OUTPUT to the mask while an input + device will always add \ref PortDescriptor::OUTPUT. + + \note An input API may but need not necessarily + report output devices which cannot be used as input + if \ref PortDescriptor::OUTPUT is passed as \ref + capabilities parameter. + */ + PortList getPortList(int capabilities = 0); + + //! Close an open MIDI connection (if one exists). + void closePort( void ); void setClientName( const std::string &clientName ); void setPortName( const std::string &portName ); //! Returns true if a port is open and false if not. /*! - Note that this only applies to connections made with the openPort() - function, not to virtual ports. + Note that this only applies to connections made with the openPort() + function, not to virtual ports. + + \retval true if a port is open and + \retval false if the port is not open (e.g. not opend or closed). */ - virtual bool isPortOpen( void ) const = 0; + bool isPortOpen( void ) const; - //! Set an error callback function to be invoked when an error has occured. + //! Pure virtual function to set an error callback function to be invoked when an error has occured. /*! The callback function will be called whenever an error has occured. It is best to set the error callback function before opening a port. */ - virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ) = 0; + void setErrorCallback( ErrorInterface * callback); + + //! A basic error reporting function for RtMidi classes. + void error( Error e ); + + enum Api_t { + UNSPECIFIED = rtmidi::UNSPECIFIED, + MACOSX_CORE = rtmidi::MACOSX_CORE, + LINUX_ALSA = rtmidi::LINUX_ALSA, + UNIX_JACK = rtmidi::UNIX_JACK, + WINDOWS_MM = rtmidi::WINDOWS_MM, + RTMIDI_DUMMY = rtmidi::DUMMY + }; + + /* old functions */ + RTMIDI_DEPRECATED(typedef Api_t Api, + "enum RtMidi::Api has been replaced by enum rtmidi::ApiType"); +#define Api Api_t + RTMIDI_DEPRECATED(static void getCompiledApi( std::vector &apis, bool + preferSystem + = true ) throw(), "enum RtMidi::Api has been replaced by enum rtmidi::ApiType" ); + //! Compatibilty function for older code + virtual + RTMIDI_DEPRECATED(void openVirtualPort( const std::string &portName + = std::string( "RtMidi virtual port" ) ), + "For better usability you should call this function from a derived class") = 0; + + //! Pure virtual function to open a MIDI connection given by enumeration number. + /*! \param portNumber An optional port number greater than 0 + can be specified. Otherwise, the default or first port + found is opened. + + \param portName An optional name for the applicaction port + that will be generated to connect to portId can be + specified. + + \deprecated + */ + RTMIDI_DEPRECATED(void openPort( unsigned int portNumber = 0, + const std::string &portName = std::string( "RtMidi" ) + ),"Port numbers are unreliable. Use port descriptors instead (see examples for a demonstration)"); + + //! Pure virtual to return the number of available MIDI ports of the current API. + /*! + \return This function returns the number of MIDI ports of + the selected API. - protected: + \note Only ports are counted that can be used with the + current API so an input API does ignore all output devices + and vice versa. - RtMidi(); - virtual ~RtMidi(); + \sa getPortName + \deprecated + */ + RTMIDI_DEPRECATED(unsigned int getPortCount(),"Port numbers are unreliable. Use port descriptors instead (see examples for a demonstration)"); + + //! Pure virtual function to return a string identifier for the specified MIDI port number. + /*! + \param portNumber Number of the device to be referred to. + \return The name of the port with the given Id is returned. + \retval An empty string is returned if an invalid port specifier is provided. + + \note Only ports are counted that can be used with the + current API so an input API does ignore all output devices + and vice versa. + + \sa getPortCount() + \deprecated + */ + RTMIDI_DEPRECATED(std::string getPortName( unsigned int portNumber = 0 ),"Port numbers are unreliable. Use port descriptors instead (see examples for a demonstration)"); + + //! Pure virtual function to set an error callback function to be invoked when an error has occured. + /*! + The callback function will be called whenever an error has occured. It is best + to set the error callback function before opening a port. + */ + RTMIDI_DEPRECATED(void setErrorCallback( ErrorCallback errorCallback = NULL, void * userData = 0 ), "setErrorCallback now expects an object of type ErrorInterface"); + +protected: MidiApi *rtapi_; + MidiApiList * list; + bool preferSystem; + std::string clientName; + + Midi(MidiApiList * l, + bool pfsystem, + const std::string &name); + + virtual ~Midi(); }; +inline RTMIDI_DEPRECATED(std::string getApiName(Midi::Api type),"Use rtmidi::ApiType instead of RtMidi::Api"); +inline std::string getApiName(Midi::Api type) +{ + return getApiName((ApiType)type); +} + +#undef RTMIDI_CLASSNAME + /**********************************************************************/ -/*! \class RtMidiIn - \brief A realtime MIDI input class. - - This class provides a common, platform-independent API for - realtime MIDI input. It allows access to a single MIDI input - port. Incoming MIDI messages are either saved to a queue for - retrieval using the getMessage() function or immediately passed to - a user-specified callback function. Create multiple instances of - this class to connect to more than one MIDI device at the same - time. With the OS-X, Linux ALSA, and JACK MIDI APIs, it is also - possible to open a virtual input port to which other MIDI software - clients can connect. - - by Gary P. Scavone, 2003-2017. +/*! \class MidiIn + \brief A realtime MIDI input class. + + This class provides a common, platform-independent API for + realtime MIDI input. It allows access to a single MIDI input + port. Incoming MIDI messages are either saved to a queue for + retrieval using the getMessage() function or immediately passed to + a user-specified callback function. Create multiple instances of + this class to connect to more than one MIDI device at the same + time. With the OS-X, Linux ALSA, and JACK MIDI APIs, it is also + possible to open a virtual input port to which other MIDI software + clients can connect. + + by Gary P. Scavone, 2003-2017. */ /**********************************************************************/ // **************************************************************** // // -// RtMidiIn and RtMidiOut class declarations. +// MidiIn and MidiOut class declarations. // -// RtMidiIn / RtMidiOut are "controllers" used to select an available +// MidiIn / MidiOut are "controllers" used to select an available // MIDI input or output interface. They present common APIs for the // user to call but all functionality is implemented by the classes -// MidiInApi, MidiOutApi and their subclasses. RtMidiIn and RtMidiOut +// MidiInApi, MidiOutApi and their subclasses. MidiIn and MidiOut // each create an instance of a MidiInApi or MidiOutApi subclass based // on the user's API choice. If no choice is made, they attempt to // make a "logical" API selection. // // **************************************************************** // -class RTMIDI_DLL_PUBLIC RtMidiIn : public RtMidi +#define RTMIDI_CLASSNAME "MidiIn" +class RTMIDI_DLL_PUBLIC MidiIn : public Midi { - public: +public: - //! User callback function type definition. - typedef void (*RtMidiCallback)( double timeStamp, std::vector *message, void *userData); //! Default constructor that allows an optional api, client name and queue size. /*! @@ -236,44 +727,89 @@ class RTMIDI_DLL_PUBLIC RtMidiIn : public RtMidi incoming messages will be ignored. If no API argument is specified and multiple API support has been - compiled, the default order of use is ALSA, JACK (Linux) and CORE, + compiled, the default order of use is JACK, ALSA (Linux) and CORE, JACK (OS-X). \param api An optional API id can be specified. - \param clientName An optional client name can be specified. This - will be used to group the ports that are created - by the application. + \param clientName An optional Client name can be specified. This + will be used to group the ports that are created + by the application. \param queueSizeLimit An optional size of the MIDI input queue can be specified. + + \param pfsystem An optional boolean parameter can be + provided to indicate the API preferences of the user + code. If RtMidi is requested to autoselect a backend + this parameter tells which backend should be tried + first. If it is \c true the backend will prefer OS + provieded APIs (WinMM, ALSA, Core MIDI) over other + APIs (JACK). If \c false, the order will be vice + versa. */ - RtMidiIn( RtMidi::Api api=UNSPECIFIED, - const std::string& clientName = "RtMidi Input Client", - unsigned int queueSizeLimit = 100 ); + MidiIn( ApiType api=rtmidi::UNSPECIFIED, + const std::string &clientName = std::string( "RtMidi Input Client"), + unsigned int queueSizeLimit = 100, + bool pfsystem = true); //! If a MIDI connection is still open, it will be closed by the destructor. - ~RtMidiIn ( void ) throw(); + ~MidiIn ( void ) throw(); - //! Returns the MIDI API specifier for the current instance of RtMidiIn. - RtMidi::Api getCurrentApi( void ) throw(); + using Midi::openPort; - //! Open a MIDI input connection given by enumeration number. + //! Open a MIDI connection given by a port descriptor. /*! - \param portNumber An optional port number greater than 0 can be specified. - Otherwise, the default or first port found is opened. - \param portName An optional name for the application port that is used to connect to portId can be specified. + \param port A port descriptor of the port must be specified. + \param portName An optional name for the applicaction port that is used to connect to portId can be specified. */ - void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi Input" ) ); + void openPort( const PortDescriptor & port, + const std::string &portName = std::string( "RtMidi" ) ); - //! Create a virtual input port, with optional name, to allow software connections (OS X, JACK and ALSA only). + + //! Open a MIDI connection given by a port descriptor pointer. + /*! + \param port A pointer to a port descriptor of the port must be specified. + \param portName An optional name for the applicaction port that is used to connect to portId can be specified. + \sa openPort(const PortDescriptor &,const std::string &); + */ + void openPort( Pointer p, + const std::string &portName = std::string( "RtMidi" ) ); + + //! Function to create a virtual port, with optional name. /*! - This function creates a virtual MIDI input port to which other - software applications can connect. This type of functionality + This function creates a virtual MIDI port to which other + software applications can connect. This type of functionality is currently only supported by the Macintosh OS-X, any JACK, and Linux ALSA APIs (the function returns an error for the other APIs). \param portName An optional name for the application port that is - used to connect to portId can be specified. + used to connect to portId can be specified. */ - void openVirtualPort( const std::string &portName = std::string( "RtMidi Input" ) ); + void openVirtualPort( const std::string &portName = std::string( "RtMidi virtual input port" ) ); + + //! Return a list of all available ports of the current API. + /*! + \param capabilities an optional parameter that + describes which capabilities the returned devices + must support. The returned devices may have + additional capabilities to those which have been + requested, but not less. + + \return This function returns a list of port descriptors. + + \note Each API will request additonal + capabilites. An output API will set always add \ref + PortDescriptor::OUTPUT to the mask while an input + device will always add \ref PortDescriptor::OUTPUT. + + \note An input API may but need not necessarily + report output devices which cannot be used as input + if \ref PortDescriptor::OUTPUT is passed as \ref + capabilities parameter. + */ + PortList getPortList(int capabilities = PortDescriptor::INPUT) { + return Midi::getPortList(capabilities); + } + + //! Set a callback function to be invoked for incoming MIDI messages. /*! @@ -283,10 +819,10 @@ class RTMIDI_DLL_PUBLIC RtMidiIn : public RtMidi leaving some messages in the queue. \param callback A callback function must be given. - \param userData Optionally, a pointer to additional data can be - passed to the callback function whenever it is called. + \param userData Opitionally, a pointer to additional data can be + passed to the callback function whenever it is called. */ - void setCallback( RtMidiCallback callback, void *userData = 0 ); + void setCallback( MidiInterface * callback ); //! Cancel use of the current callback function (if one exists). /*! @@ -295,30 +831,6 @@ class RTMIDI_DLL_PUBLIC RtMidiIn : public RtMidi */ void cancelCallback(); - //! Close an open MIDI connection (if one exists). - void closePort( void ); - - //! Returns true if a port is open and false if not. - /*! - Note that this only applies to connections made with the openPort() - function, not to virtual ports. - */ - virtual bool isPortOpen() const; - - //! Return the number of available MIDI input ports. - /*! - \return This function returns the number of MIDI ports of the selected API. - */ - unsigned int getPortCount(); - - //! Return a string identifier for the specified MIDI input port number. - /*! - \return The name of the port with the given Id is returned. - \retval An empty string is returned if an invalid port specifier - is provided. User code should assume a UTF-8 encoding. - */ - std::string getPortName( unsigned int portNumber = 0 ); - //! Specify whether certain MIDI message types should be queued or ignored during input. /*! By default, MIDI timing and active sensing messages are ignored @@ -327,7 +839,9 @@ class RTMIDI_DLL_PUBLIC RtMidiIn : public RtMidi values of "true" imply that the respective message type will be ignored. */ - void ignoreTypes( bool midiSysex = true, bool midiTime = true, bool midiSense = true ); + void ignoreTypes( bool midiSysex = true, + bool midiTime = true, + bool midiSense = true ); //! Fill the user-provided vector with the data bytes for the next available MIDI message in the input queue and return the event delta-time in seconds. /*! @@ -337,125 +851,187 @@ class RTMIDI_DLL_PUBLIC RtMidiIn : public RtMidi message retrieval or an input connection was not previously established. */ - double getMessage( std::vector *message ); + double getMessage( std::vector &message ); + - //! Set an error callback function to be invoked when an error has occured. + //! Set a callback function to be invoked for incoming MIDI messages. /*! - The callback function will be called whenever an error has occured. It is best - to set the error callback function before opening a port. + The callback function will be called whenever an incoming MIDI + message is received. While not absolutely necessary, it is best + to set the callback function before opening a MIDI port to avoid + leaving some messages in the queue. + + \param callback A callback function must be given. + \param userData Opitionally, a pointer to additional data can be + passed to the callback function whenever it is called. */ - virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ); + RTMIDI_DEPRECATED(void setCallback( MidiCallback callback, void *userData = 0 ), + "RtMidi now provides a type-safe MidiInterface class."); + + //! Fill the user-provided vector with the data bytes for the next available MIDI message in the input queue and return the event delta-time in seconds. + /*! + This function returns immediately whether a new message is + available or not. A valid message is indicated by a non-zero + vector size. An exception is thrown if an error occurs during + message retrieval or an input connection was not previously + established. - protected: - void openMidiApi( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit ); + \deprecated + */ + RTMIDI_DEPRECATED(double getMessage( std::vector *message ), + "Please, use a C++ style reference to pass the message vector."); +protected: + static MidiApiList queryApis; + int queueSizeLimit; + void openMidiApi( ApiType api ); }; +#undef RTMIDI_CLASSNAME /**********************************************************************/ -/*! \class RtMidiOut - \brief A realtime MIDI output class. - - This class provides a common, platform-independent API for MIDI - output. It allows one to probe available MIDI output ports, to - connect to one such port, and to send MIDI bytes immediately over - the connection. Create multiple instances of this class to - connect to more than one MIDI device at the same time. With the - OS-X, Linux ALSA and JACK MIDI APIs, it is also possible to open a - virtual port to which other MIDI software clients can connect. - - by Gary P. Scavone, 2003-2017. +/*! \class MidiOut + \brief A realtime MIDI output class. + + This class provides a common, platform-independent API for MIDI + output. It allows one to probe available MIDI output ports, to + connect to one such port, and to send MIDI bytes immediately over + the connection. Create multiple instances of this class to + connect to more than one MIDI device at the same time. With the + OS-X, Linux ALSA and JACK MIDI APIs, it is also possible to open a + virtual port to which other MIDI software clients can connect. + + by Gary P. Scavone, 2003-2017. */ /**********************************************************************/ -class RTMIDI_DLL_PUBLIC RtMidiOut : public RtMidi +#define RTMIDI_CLASSNAME "MidiOut" +class RTMIDI_DLL_PUBLIC MidiOut : public Midi { - public: +public: //! Default constructor that allows an optional client name. /*! An exception will be thrown if a MIDI system initialization error occurs. If no API argument is specified and multiple API support has been - compiled, the default order of use is ALSA, JACK (Linux) and CORE, + compiled, the default order of use is JACK, ALSA (Linux) and CORE, JACK (OS-X). + + \param api An optional API id can be specified. + \param clientName An optional Client name can be specified. This + will be used to group the ports that are created + by the application. + \param queueSizeLimit An optional size of the MIDI input queue can be specified. + + \param pfsystem An optional boolean parameter can be + provided to indicate the API preferences of the user + code. If RtMidi is requested to autoselect a backend + this parameter tells which backend should be tried + first. If it is \c true the backend will prefer OS + provieded APIs (WinMM, ALSA, Core MIDI) over other + APIs (JACK). If \c false, the order will be vice + versa. */ - RtMidiOut( RtMidi::Api api=UNSPECIFIED, - const std::string& clientName = "RtMidi Output Client" ); + MidiOut( ApiType api=rtmidi::UNSPECIFIED, + const std::string &clientName = std::string( "RtMidi Output Client"), + bool pfsystem = true); //! The destructor closes any open MIDI connections. - ~RtMidiOut( void ) throw(); + ~MidiOut( void ) throw(); - //! Returns the MIDI API specifier for the current instance of RtMidiOut. - RtMidi::Api getCurrentApi( void ) throw(); + using Midi::openPort; - //! Open a MIDI output connection. + //! Open a MIDI connection given by a port descriptor. /*! - An optional port number greater than 0 can be specified. - Otherwise, the default or first port found is opened. An - exception is thrown if an error occurs while attempting to make - the port connection. + \param port A port descriptor of the port must be specified. + \param portName An optional name for the applicaction port that is used to connect to portId can be specified. */ - void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi Output" ) ); - - //! Close an open MIDI connection (if one exists). - void closePort( void ); - - //! Returns true if a port is open and false if not. + void openPort( const PortDescriptor & port, + const std::string &portName = std::string( "RtMidi" ) ); + //! Open a MIDI connection given by a port descriptor pointer. /*! - Note that this only applies to connections made with the openPort() - function, not to virtual ports. + \param port A pointer to a port descriptor of the port must be specified. + \param portName An optional name for the applicaction port that is used to connect to portId can be specified. + \sa openPort(const PortDescriptor &,const std::string &); */ - virtual bool isPortOpen() const; + void openPort( Pointer p, + const std::string &portName = std::string( "RtMidi" ) ); - //! Create a virtual output port, with optional name, to allow software connections (OS X, JACK and ALSA only). + //! Function to create a virtual port, with optional name. /*! - This function creates a virtual MIDI output port to which other - software applications can connect. This type of functionality - is currently only supported by the Macintosh OS-X, Linux ALSA - and JACK APIs (the function does nothing with the other APIs). - An exception is thrown if an error occurs while attempting to - create the virtual port. - */ - void openVirtualPort( const std::string &portName = std::string( "RtMidi Output" ) ); + This function creates a virtual MIDI port to which other + software applications can connect. This type of functionality + is currently only supported by the Macintosh OS-X, any JACK, + and Linux ALSA APIs (the function returns an error for the other APIs). - //! Return the number of available MIDI output ports. - unsigned int getPortCount( void ); + \param portName An optional name for the applicaction port that is + used to connect to portId can be specified. + */ + void openVirtualPort( const std::string &portName = std::string( "RtMidi virtual output port" ) ); - //! Return a string identifier for the specified MIDI port type and number. + //! Return a list of all available ports of the current API. /*! - \return The name of the port with the given Id is returned. - \retval An empty string is returned if an invalid port specifier - is provided. User code should assume a UTF-8 encoding. + \param capabilities an optional parameter that + describes which capabilities the returned devices + must support. The returned devices may have + additional capabilities to those which have been + requested, but not less. + + \return This function returns a list of port descriptors. + + \note Each API will request additonal + capabilites. An output API will set always add \ref + PortDescriptor::OUTPUT to the mask while an input + device will always add \ref PortDescriptor::OUTPUT. + + \note An input API may but need not necessarily + report output devices which cannot be used as input + if \ref PortDescriptor::OUTPUT is passed as \ref + capabilities parameter. */ - std::string getPortName( unsigned int portNumber = 0 ); + PortList getPortList(int capabilities = PortDescriptor::OUTPUT) { + return Midi::getPortList(capabilities); + } //! Immediately send a single message out an open MIDI output port. /*! - An exception is thrown if an error occurs during output or an - output connection was not previously established. + An exception is thrown if an error occurs during output or an + output connection was not previously established. + + \deprecated */ - void sendMessage( const std::vector *message ); + RTMIDI_DEPRECATED(void sendMessage( const std::vector *message ), + "Please, use a C++ style reference to pass the message vector.") + { + sendMessage(*message); + } + //! Immediately send a single message out an open MIDI output port. /*! - An exception is thrown if an error occurs during output or an - output connection was not previously established. - - \param message A pointer to the MIDI message as raw bytes - \param size Length of the MIDI message in bytes + An exception is thrown if an error occurs during output or an + output connection was not previously established. */ - void sendMessage( const unsigned char *message, size_t size ); - //! Set an error callback function to be invoked when an error has occured. + void sendMessage(const std::vector &message ) { + sendMessage(message.data(), message.size()); + } + //! Immediately send a single message out an open MIDI output port. /*! - The callback function will be called whenever an error has occured. It is best - to set the error callback function before opening a port. + An exception is thrown if an error occurs during output or an + output connection was not previously established. + + \param message A pointer to the MIDI message as raw bytes + \param size Length of the MIDI message in bytes */ - virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ); + void sendMessage( const unsigned char *message, size_t size ); - protected: - void openMidiApi( RtMidi::Api api, const std::string &clientName ); +protected: + static MidiApiList queryApis; + void openMidiApi( ApiType api ); + bool firstErrorOccurred_; }; +#undef RTMIDI_CLASSNAME // **************************************************************** // @@ -466,66 +1042,213 @@ class RTMIDI_DLL_PUBLIC RtMidiOut : public RtMidi // OS-specific code necessary to fully implement the RtMidi API. // // Note that MidiInApi and MidiOutApi are abstract base classes and -// cannot be explicitly instantiated. RtMidiIn and RtMidiOut will +// cannot be explicitly instantiated. MidiIn and MidiOut will // create instances of a MidiInApi or MidiOutApi subclass. // // **************************************************************** // +#define RTMIDI_CLASSNAME "MidiApi" class RTMIDI_DLL_PUBLIC MidiApi { - public: +public: MidiApi(); virtual ~MidiApi(); - virtual RtMidi::Api getCurrentApi( void ) = 0; - virtual void openPort( unsigned int portNumber, const std::string &portName ) = 0; - virtual void openVirtualPort( const std::string &portName ) = 0; + + //! Return whether the API supports virtual ports + /*! + \retval true The funcion returns true if the API supports virtual ports. + \retval false The funcion returns false if the API doesn't support virtual ports. + \sa openVirtualPort + */ + virtual bool hasVirtualPorts() const = 0; + + //! Pure virtal function to create a virtual port, with optional name. + /*! + This function creates a virtual MIDI port to which other + software applications can connect. This type of functionality + is currently only supported by the Macintosh OS-X, any JACK, + and Linux ALSA APIs (the function returns an error for the other APIs). + + \param portName An optional name for the applicaction port that is + used to connect to portId can be specified. + + \sa hasVirtualPorts + */ + virtual void openVirtualPort( const std::string &portName = std::string( "RtMidi virtual port" ) ) = 0; + + //! Pure virtual function to open a MIDI connection given by enumeration number. + /*! \param portNumber An optional port number greater than 0 + can be specified. Otherwise, the default or first port + found is opened. + + \param portName An optional name for the applicaction port + that will be generated to connect to portId can be + specified. + */ + virtual void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi" ) ) = 0; + + //! Pure virtual function to open a MIDI connection given by a port descriptor. + /*! + \param port A port descriptor of the port must be specified. + \param portName An optional name for the applicaction port that is used to connect to portId can be specified. + */ + virtual void openPort( const PortDescriptor & port, const std::string &portName = std::string( "RtMidi" ) ) = 0; + + //! Open a MIDI connection given by a port descriptor pointer. + /*! + \param port A pointer to a port descriptor of the port must be specified. + \param portName An optional name for the applicaction port that is used to connect to portId can be specified. + \sa openPort(const PortDescriptor &,const std::string &); + */ + void openPort( Pointer p, const std::string &portName = std::string( "RtMidi" ) ) { + if (!p) { + error(RTMIDI_ERROR( gettext_noopt("Passed NULL pointer."), + Error::INVALID_PARAMETER)); + return; + } + openPort(*p, portName); + } + + //! Pure virtual function to return a port descriptor if the port is open + /*! This function returns a port descriptor that can be used to open another port + either to the connected port or – if the backend supports it – the connecting port. + \param local The parameter local defines whether the function returns a descriptor to + the virtual port (true) or the remote port (false). The function returns 0 if the + port cannot be determined (e.g. if the port is not connected or the backend dosen't support it). + */ + virtual Pointer getDescriptor(bool local=false) = 0; + + //! Pure virtual function to return a list of all available ports of the current API. + /*! + \param capabilities an optional parameter that describes which + capabilities the device typu + + \return This function returns a list of port descriptors. + + \note An input API may but need not necessarily report + output devices which cannot be used as input if + \ref 0 is passed as \ref capabilities parameter. + \sa PortDescriptor::PortCapabilitiers + */ + virtual PortList getPortList(int capabilities = 0) = 0; + + //! Pure virtual to return the number of available MIDI ports of the current API. + /*! + \return This function returns the number of MIDI ports of + the selected API. + + \note Only ports are counted that can be used with the + current API so an input API does ignore all output devices + and vice versa. + + \sa getPortName + */ + virtual unsigned int getPortCount() = 0; + + //! Pure virtual function to return a string identifier for the specified MIDI port number. + /*! + \param portNumber Number of the device to be referred to. + \return The name of the port with the given Id is returned. + \retval An empty string is returned if an invalid port specifier is provided. + + \note Only ports are counted that can be used with the + current API so an input API does ignore all output devices + and vice versa. + + \sa getPortCount() + */ + virtual std::string getPortName( unsigned int portNumber = 0 ) = 0; + + //! Pure virtual function to close an open MIDI connection (if one exists). virtual void closePort( void ) = 0; virtual void setClientName( const std::string &clientName ) = 0; virtual void setPortName( const std::string &portName ) = 0; - virtual unsigned int getPortCount( void ) = 0; - virtual std::string getPortName( unsigned int portNumber ) = 0; + // ! A basic error reporting function for RtMidi classes. + // static void error( Error::Type type, std::string &errorString ); - inline bool isPortOpen() const { return connected_; } - void setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ); + //! Pure virtual function to return whether a port is open or not. + /*! \retval true if a port is open and + \retval false if the port is not open (e.g. not opend or closed). + */ + bool isPortOpen() const { return connected_; } + + + //! Virtual function to set the error callback object + /*! + Everytime an error is detected or a warning is issued the function + \r ErrorInterface::rtmidi_error of the callback object is called with an error object + that describes the situation. + + \param callback An objact that provides an ErrorInterface. + */ + virtual void setErrorCallback(ErrorInterface * callback); + + + //! Returns the MIDI API specifier for the current instance of RtMidiIn. + virtual ApiType getCurrentApi( void ) throw() = 0; //! A basic error reporting function for RtMidi classes. - void error( RtMidiError::Type type, std::string errorString ); + /*! This function hanles errors end warnings that + occur during runtime of RtMidi. If an error callback + has been set the function calls the callback and + returns quietly assuming the callback handled the + case correctly. + + Otherwise it depends on the type of the error. If it + is a warning, a message is displayed to + std::cerr. If it is an error the object is thrown as + an exception. + + \param e Error/Warning object describing the current + situation. + + \throw Error + */ + void error( Error e ); + + + //! Virtual function to set an error callback function to be invoked when an error has occured. + /*! + The callback function will be called whenever an error has occured. It is best + to set the error callback function before opening a port. + */ + RTMIDI_DEPRECATED(virtual void setErrorCallback( ErrorCallback errorCallback = NULL, void * userData = 0 ), "RtMidi now provides a typesafe ErrorInterface class"); protected: - virtual void initialize( const std::string& clientName ) = 0; + virtual void initialize( const std::string &clientName ) = 0; void *apiData_; bool connected_; - std::string errorString_; - RtMidiErrorCallback errorCallback_; bool firstErrorOccurred_; - void *errorCallbackUserData_; + std::string errorString_; + ErrorInterface * errorCallback_; }; +#undef RTMIDI_CLASSNAME +#define RTMIDI_CLASSNAME "MidiInApi" class RTMIDI_DLL_PUBLIC MidiInApi : public MidiApi { - public: +public: MidiInApi( unsigned int queueSizeLimit ); virtual ~MidiInApi( void ); - void setCallback( RtMidiIn::RtMidiCallback callback, void *userData ); + void setCallback( MidiInterface * callback); void cancelCallback( void ); virtual void ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ); - double getMessage( std::vector *message ); + double getMessage( std::vector &message ); // A MIDI structure used internally by the class to store incoming // messages. Each message represents one and only one MIDI message. - struct MidiMessage { - std::vector bytes; - + struct MidiMessage { + std::vector bytes; //! Time in seconds elapsed since the previous message double timeStamp; // Default constructor. - MidiMessage() - :bytes(0), timeStamp(0.0) {} + MidiMessage() + :bytes(0), timeStamp(0.0) {} }; struct MidiQueue { @@ -535,76 +1258,347 @@ class RTMIDI_DLL_PUBLIC MidiInApi : public MidiApi MidiMessage *ring; // Default constructor. - MidiQueue() - :front(0), back(0), ringSize(0), ring(0) {} + MidiQueue() + :front(0), back(0), ringSize(0), ring(0) {} bool push(const MidiMessage&); - bool pop(std::vector*, double*); + bool pop(std::vector& message, double& timestamp); unsigned int size(unsigned int *back=0, unsigned int *front=0); }; - // The RtMidiInData structure is used to pass private class data to - // the MIDI input handling function or thread. - struct RtMidiInData { - MidiQueue queue; - MidiMessage message; - unsigned char ignoreFlags; - bool doInput; - bool firstMessage; - void *apiData; - bool usingCallback; - RtMidiIn::RtMidiCallback userCallback; - void *userData; - bool continueSysex; - // Default constructor. - RtMidiInData() - : ignoreFlags(7), doInput(false), firstMessage(true), - apiData(0), usingCallback(false), userCallback(0), userData(0), - continueSysex(false) {} - }; - protected: - RtMidiInData inputData_; + RTMIDI_DEPRECATED(void setCallback( MidiCallback callback, void *userData = 0 ), + "RtMidi now provides a type-safe MidiInterface class."); + RTMIDI_DEPRECATED(double getMessage( std::vector *message ), + "Please, use a C++ style reference to pass the message vector.") + { + if (!message) { + error( RTMIDI_ERROR(gettext_noopt("Passed NULL pointer."), + Error::WARNING )); + } + return getMessage(*message); + } + +protected: + // The RtMidiInData structure is used to pass private class data to + // the MIDI input handling function or thread. + MidiQueue queue; + MidiMessage message; + unsigned char ignoreFlags; + bool doInput; + bool firstMessage; + MidiInterface * userCallback; + bool continueSysex; + friend struct JackBackendCallbacks; }; +#undef RTMIDI_CLASSNAME +#define RTMIDI_CLASSNAME "MidiOutApi" class RTMIDI_DLL_PUBLIC MidiOutApi : public MidiApi { - public: +public: MidiOutApi( void ); virtual ~MidiOutApi( void ); virtual void sendMessage( const unsigned char *message, size_t size ) = 0; + void sendMessage( const std::vector &message ) + { + if (message.empty()) { + error( RTMIDI_ERROR(gettext_noopt("No data in message argument."), + Error::WARNING)); + } + sendMessage(message.data(),message.size()); + } + RTMIDI_DEPRECATED(void sendMessage( const std::vector *message ), + "Please, use a C++ style reference to pass the message vector.") + { + if (!message) { + error( RTMIDI_ERROR(gettext_noopt("No data in message argument."), + Error::WARNING)); + } + sendMessage(*message); + } }; +#undef RTMIDI_CLASSNAME + // **************************************************************** // // -// Inline RtMidiIn and RtMidiOut definitions. +// Inline rtmidi::Midi, rtmidi::MidiIn and rtmidid:MidiOut definitions. // // **************************************************************** // +// rtmidi::Midi +#define RTMIDI_CLASSNAME "Midi" +inline std::vector Midi :: getCompiledApi( ) throw() { + std::vector apis; + getCompiledApi(apis); + return apis; +} +inline ApiType Midi :: getCurrentApi( void ) throw() { + if (rtapi_) return rtapi_->getCurrentApi(); + else return rtmidi::UNSPECIFIED; +} +inline Pointer Midi :: getDescriptor( bool local ) { + if (rtapi_) return rtapi_->getDescriptor(local); + return 0; +} +inline PortList Midi :: getPortList(int capabilities ) { + if (rtapi_) return rtapi_->getPortList(capabilities); + if (list && !list->empty()) { + PortList retval; + for (MidiApiList::iterator i = list->begin(); + i != list->end(); + ++i) { + PortList tmp = (*i)->getPortList(capabilities); + retval.splice(retval.end(), tmp); + } + return retval; + } + return PortList(); +} +inline void Midi :: closePort( void ) { + if (rtapi_) rtapi_->closePort(); +} +inline void Midi :: setClientName( const std::string &clientName ) +{ + if (rtapi_) rtapi_->setClientName(clientName); +} +inline void Midi :: setPortName( const std::string &portName ) +{ + if (rtapi_) rtapi_->setPortName(portName); +} +inline bool Midi :: isPortOpen( void ) const { + if (rtapi_) return rtapi_->isPortOpen(); + return false; +} +inline void Midi :: setErrorCallback( ErrorInterface * callback) { + if (rtapi_) rtapi_->setErrorCallback(callback); +} +inline void Midi :: getCompiledApi( std::vector &apis, bool + preferSystem ) throw() { + std::vector api2; + Midi::getCompiledApi(api2,preferSystem); + apis.reserve(api2.size()); + size_t s = api2.size(); + for (size_t i = 0; i < s; i++) { + apis.push_back((Api)api2[i]); + } +} +inline void Midi :: openPort( unsigned int portNumber, + const std::string &portName) { + if (rtapi_) rtapi_->openPort(portNumber,portName); +} +inline unsigned int Midi :: getPortCount() { + if (rtapi_) return rtapi_->getPortCount(); + return 0; +} +inline std::string Midi :: getPortName( unsigned int portNumber ) { + if (rtapi_) return rtapi_->getPortName(portNumber); + return ""; +} +inline void Midi :: setErrorCallback( ErrorCallback errorCallback , void * userData ) { +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + if (rtapi_) rtapi_->setErrorCallback(errorCallback, userData); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif +} +inline Midi :: Midi(MidiApiList * l, + bool pfsystem, + const std::string &name):rtapi_(0), + list(l), + preferSystem(pfsystem), + clientName(name) { } +inline Midi :: ~Midi() { + delete rtapi_; + rtapi_ = 0; +} +#undef RTMIDI_CLASSNAME + +#define RTMIDI_CLASSNAME "MidiIn" +// rtmidi::MidiIn +inline void MidiIn :: openPort( const PortDescriptor & port, + const std::string &portName ) { + if (!rtapi_) rtapi_ = port.getInputApi(); + if (rtapi_) rtapi_->openPort(port,portName); +} +inline void MidiIn :: openPort( Pointer p, + const std::string &portName ) { + if (!p) { + error(RTMIDI_ERROR(gettext_noopt("A NULL pointer has been passed as port descriptor"), + Error::INVALID_PARAMETER)); + return; + } + openPort(*p, portName); +} +inline void MidiIn :: openVirtualPort( const std::string &portName ) { + if (!rtapi_ && list && !list->empty()) { + Pointer api = list->front(); + + std::vector< ApiType > apis; + getCompiledApi( apis ); + for (size_t i = 0 ; i < apis.size() ; i++) { + openMidiApi( apis[0] ); + if (rtapi_ && rtapi_->hasVirtualPorts()) break; + } + } + + if (rtapi_) rtapi_->openVirtualPort(portName); + else { + error(RTMIDI_ERROR(gettext_noopt("No valid MIDI system has been selected."), + Error::INVALID_DEVICE)); + } +} +inline void MidiIn :: setCallback( MidiInterface * callback ) { + if (rtapi_) + static_cast(rtapi_)->setCallback(callback); +} +inline void MidiIn :: cancelCallback() { + if (rtapi_) + static_cast(rtapi_)->cancelCallback(); +} +inline void MidiIn :: ignoreTypes( bool midiSysex, + bool midiTime, + bool midiSense ) { + if (rtapi_) + static_cast(rtapi_)->ignoreTypes(midiSysex, midiTime, midiSense); +} +inline double MidiIn :: getMessage( std::vector &message ) { + if (rtapi_) + return static_cast(rtapi_)->getMessage(message); + error( RTMIDI_ERROR(gettext_noopt("Could not find any valid MIDI system."), + Error::WARNING)); + return 0.0; +} +inline void MidiIn :: setCallback( MidiCallback callback, void *userData) { +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + if (rtapi_) + static_cast(rtapi_)->setCallback(callback,userData); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif +} +inline double MidiIn :: getMessage( std::vector *message ) { + if (!message) { + error( RTMIDI_ERROR(gettext_noopt("Passed NULL pointer."), + Error::WARNING)); + } + if (rtapi_) + return static_cast(rtapi_)->getMessage(*message); + error( RTMIDI_ERROR(gettext_noopt("No valid MIDI system has been found."), + Error::WARNING)); + return 0.0; +} +#undef RTMIDI_CLASSNAME + +// rtmidi::MidiOut +#define RTMIDI_CLASSNAME "MidiOut" +inline void MidiOut :: openPort( const PortDescriptor & port, + const std::string &portName ) { + if (!rtapi_) rtapi_ = port.getOutputApi(); + if (rtapi_) rtapi_->openPort(port,portName); +} +inline void MidiOut :: openPort( Pointer p, + const std::string &portName ) { + if (!p) { + error(RTMIDI_ERROR(gettext_noopt("Passed NULL pointer."), + Error::INVALID_PARAMETER)); + return; + } + openPort(*p, portName); +} +inline void MidiOut :: openVirtualPort( const std::string &portName ) { + if (!rtapi_ && list && !list->empty()) { + Pointer api = list->front(); + + std::vector< ApiType > apis; + getCompiledApi( apis ); + for (size_t i = 0 ; i < apis.size() ; i++) { + openMidiApi( apis[0] ); + if (rtapi_ && rtapi_->hasVirtualPorts()) break; + } + } + + if (rtapi_) rtapi_->openVirtualPort(portName); + else { + error(RTMIDI_ERROR(gettext_noopt("No valid MIDI system has been selected."), + Error::INVALID_DEVICE)); + } +} +inline void MidiOut :: sendMessage( const unsigned char *message, size_t size ) { + if (!message) { + error( RTMIDI_ERROR(gettext_noopt("No data in MIDI message."), + Error::WARNING)); + } + if (rtapi_) + static_cast(rtapi_)->sendMessage(message,size); + else + error( RTMIDI_ERROR(gettext_noopt("No valid MIDI system has been selected."), + Error::WARNING)); +} +#undef RTMIDI_CLASSNAME + + +// old API + + +//! Compatibility interface to hold a C style callback function +struct CompatibilityMidiInterface: MidiInterface { + CompatibilityMidiInterface(MidiCallback cb, void * ud): + callback(cb), + userData(ud) {} + void rtmidi_midi_in(double timestamp, std::vector &message) { + callback (timestamp, &message, userData); + } + void delete_me() { delete this; } + MidiCallback callback; + void * userData; +}; + + +struct CompatibilityErrorInterface: ErrorInterface { + CompatibilityErrorInterface(ErrorCallback cb, void * ud): callback(cb), + userdata(ud) {} + void rtmidi_error(Error e) { + callback(e.getType(),e.getMessage(),userdata); + } + + void delete_me() { delete this; } +private: + ErrorCallback callback; + void * userdata; +}; + + + +RTMIDI_NAMESPACE_END + +typedef rtmidi::Midi RTMIDI_DEPRECATED(RtMidi,"RtMidi has been replaced by rtmidi::Midi"); + +class RtMidiIn: public rtmidi::MidiIn { +public: + RTMIDI_DEPRECATED(RtMidiIn( RtMidi::Api api = (Api_t)rtmidi::UNSPECIFIED, + const std::string &clientName = std::string( "RtMidi Input Client")), + "Class RtMidiIn has been replaced by rtmidi::MidiIn"): + MidiIn((rtmidi::ApiType)api, + clientName) {} +}; +class RtMidiOut: public rtmidi::MidiOut { +public: + RTMIDI_DEPRECATED(RtMidiOut( RtMidi::Api api = (Api_t)rtmidi::UNSPECIFIED, + const std::string &clientName = std::string( "RtMidi Output Client")), + "Class RtMidiOut has been replaced by rtmidi::MidiOut"): + MidiOut((rtmidi::ApiType)api, + clientName) {} +}; +typedef rtmidi::Error RtMidiError; -inline RtMidi::Api RtMidiIn :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); } -inline void RtMidiIn :: openPort( unsigned int portNumber, const std::string &portName ) { rtapi_->openPort( portNumber, portName ); } -inline void RtMidiIn :: openVirtualPort( const std::string &portName ) { rtapi_->openVirtualPort( portName ); } -inline void RtMidiIn :: closePort( void ) { rtapi_->closePort(); } -inline bool RtMidiIn :: isPortOpen() const { return rtapi_->isPortOpen(); } -inline void RtMidiIn :: setCallback( RtMidiCallback callback, void *userData ) { ((MidiInApi *)rtapi_)->setCallback( callback, userData ); } -inline void RtMidiIn :: cancelCallback( void ) { ((MidiInApi *)rtapi_)->cancelCallback(); } -inline unsigned int RtMidiIn :: getPortCount( void ) { return rtapi_->getPortCount(); } -inline std::string RtMidiIn :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); } -inline void RtMidiIn :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) { ((MidiInApi *)rtapi_)->ignoreTypes( midiSysex, midiTime, midiSense ); } -inline double RtMidiIn :: getMessage( std::vector *message ) { return ((MidiInApi *)rtapi_)->getMessage( message ); } -inline void RtMidiIn :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ) { rtapi_->setErrorCallback(errorCallback, userData); } - -inline RtMidi::Api RtMidiOut :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); } -inline void RtMidiOut :: openPort( unsigned int portNumber, const std::string &portName ) { rtapi_->openPort( portNumber, portName ); } -inline void RtMidiOut :: openVirtualPort( const std::string &portName ) { rtapi_->openVirtualPort( portName ); } -inline void RtMidiOut :: closePort( void ) { rtapi_->closePort(); } -inline bool RtMidiOut :: isPortOpen() const { return rtapi_->isPortOpen(); } -inline unsigned int RtMidiOut :: getPortCount( void ) { return rtapi_->getPortCount(); } -inline std::string RtMidiOut :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); } -inline void RtMidiOut :: sendMessage( const std::vector *message ) { ((MidiOutApi *)rtapi_)->sendMessage( &message->at(0), message->size() ); } -inline void RtMidiOut :: sendMessage( const unsigned char *message, size_t size ) { ((MidiOutApi *)rtapi_)->sendMessage( message, size ); } -inline void RtMidiOut :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ) { rtapi_->setErrorCallback(errorCallback, userData); } #endif diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..35505301 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,65 @@ +environment: + matrix: + ### + - TOOLCHAIN: "default" + CONFIG: Release + _CC: MINGW + + - TOOLCHAIN: "default" + CONFIG: Debug + _CC: MINGW + +# example see: https://github.com/wang-bin/avbuild/blob/master/appveyor.yml +init: + - echo NUMBER_OF_PROCESSORS=%NUMBER_OF_PROCESSORS% + - echo PROCESSOR_IDENTIFIER=%PROCESSOR_IDENTIFIER% + - set MSYS2_PATH_TYPE=inherit + - set MSYS2_DIR=C:\msys64 + +install: +# can not starts with % +# - git submodule update --init + - if /i %_CC%==MinGW ( + C:\msys64\usr\bin\pacman -Syyuu --noconfirm && + C:\msys64\usr\bin\pacman -Suu --noconfirm && + C:\msys64\usr\bin\pacman -S --noconfirm --needed mingw-w64-i686-gcc mingw-w64-x86_64-gcc && + C:\msys64\usr\bin\pacman -Sc --noconfirm + ) + - C:\msys64\usr\bin\pacman -Ss --noconfirm boost + - C:\msys64\usr\bin\pacman -Ss --noconfirm autoconf + - C:\msys64\usr\bin\pacman -Ss --noconfirm automake + - C:\msys64\usr\bin\pacman -Ss --noconfirm libtool + - C:\msys64\usr\bin\pacman -Ss --noconfirm gettext + - C:\msys64\usr\bin\pacman -S --noconfirm --needed + mingw-w64-x86_64-boost autoconf mingw-w64-x86_64-libtool automake1.15 mingw-w64-x86_64-gettext + +build_script: + - dir C:\ + - dir C:\msys64 + - dir C:\msys64\usr\bin + - dir C:\msys64\opt + - dir C:\msys64\mingw64 + - dir c:\msys64\mingw64\bin + - dir C:\msys64\mingw64\x86_64-w64-mingw32 + - dir C:\msys64\mingw64\x86_64-w64-mingw32\bin + - dir C:\mingw-w64 + - dir C:\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev1\mingw64\bin + - dir C:\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev1\mingw64\opt\bin + - SET PATH=C:\mingw-w64\bin;C:\msys64\mingw64\bin;C:\msys64\usr\bin;%PATH% + - echo "%PATH%" + - path + - bash -c "env" + - bash -c pwd + - bash -c ls + - bash -c "#aclocal -I m4 --install" + - bash -c "#automake --add-missing -f" + - bash -c "#autopoint -f" + - bash -c "#libtoolize -i -f" + - bash -c "#autoconf --add-missing" + - bash -c "mkdir -p config" + - bash -c "touch config/config.rpath" + - bash -c "autoreconf -i -f" + - bash -c "./configure" + - bash -c make + - bash -c "make check" + - bash -c "make distcheck" diff --git a/configure.ac b/configure.ac index c16d8043..586fa831 100644 --- a/configure.ac +++ b/configure.ac @@ -1,236 +1,34 @@ # Process this file with autoconf to produce a configure script. -AC_INIT(RtMidi, 3.0.0, gary@music.mcgill.ca, rtmidi) +#AC_INIT(RtMidi, 2.1.0, gary@music.mcgill.ca, rtmidi) + +m4_include(configure.version) + +m4_define([ac_rtmidi_version],[lt_current.lt_revision.lt_age]) + + +AC_INIT(RtMidi-ts, ac_rtmidi_version, keinstein_junior@gmx.net, rtmidi-ts) AC_CONFIG_AUX_DIR(config) AC_CONFIG_SRCDIR(RtMidi.cpp) -AC_CONFIG_FILES([rtmidi-config rtmidi.pc Makefile tests/Makefile doc/Makefile doc/doxygen/Doxyfile]) +AC_CONFIG_FILES([Makefile doc/Makefile po/Makefile.in rtmidi-config rtmidi.pc doc/doxygen/Doxyfile],[chmod oug+x rtmidi-config]) AM_INIT_AUTOMAKE([1.14 -Wall -Werror foreign subdir-objects]) -# libtool version: current:revision:age -# -# If the library source code has changed at all since the last update, then -# increment revision (`c:r:a' becomes `c:r+1:a'). -# -# If any interfaces have been added, removed, or changed since the last update, -# increment current, and set revision to 0. -# -# If any interfaces have been added since the last public release, then -# increment age. -# -# If any interfaces have been removed since the last public release, then set -# age to 0. -m4_define([lt_current], 4) -m4_define([lt_revision], 0) -m4_define([lt_age], 0) -m4_define([lt_version_info], [lt_current:lt_revision:lt_age]) -m4_define([lt_current_minus_age], [m4_eval(lt_current - lt_age)]) - -SO_VERSION=lt_version_info -AC_SUBST(SO_VERSION) -AC_SUBST(LIBS) -AC_SUBST(api) -AC_SUBST(req) - -api="" -req="" # Fill GXX with something before test. GXX="no" -# if the user did not provide any CXXFLAGS, we can override them -AS_IF([test "x$CXXFLAGS" = "x" ], [override_cxx=yes], [override_cxx=no]) -AS_IF([test "x$CFLAGS" = "x" ], [override_c=yes], [override_c=no]) - -# Check version number coherency between RtMidi.h and configure.ac -AC_MSG_CHECKING([that version numbers are coherent]) -RTMIDI_VERSION=`sed -n 's/#define RTMIDI_VERSION "\(.*\)"/\1/p' $srcdir/RtMidi.h` -AS_IF( - [test "x$RTMIDI_VERSION" != "x$PACKAGE_VERSION"], - [AC_MSG_FAILURE([testing RTMIDI_VERSION==PACKAGE_VERSION failed, check that RtMidi.h defines RTMIDI_VERSION as "$PACKAGE_VERSION" or that the first line of configure.ac has been updated.])]) - # Enable some nice automake features if they are available m4_ifdef([AM_MAINTAINER_MODE], [AM_MAINTAINER_MODE]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) -# configure flags -AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug], [enable various debugging output])]) -AC_ARG_WITH(jack, [AS_HELP_STRING([--with-jack], [choose JACK server support])]) -AC_ARG_WITH(alsa, [AS_HELP_STRING([--with-alsa], [choose native ALSA sequencer API support (linux only)])]) -AC_ARG_WITH(core, [AS_HELP_STRING([--with-core], [ choose CoreMIDI API support (mac only)])]) -AC_ARG_WITH(winmm, [AS_HELP_STRING([--with-winmm], [ choose Windows MultiMedia (MM) API support (win32 only)])]) -AC_ARG_WITH(winks, [AS_HELP_STRING([--with-winks], [ choose kernel streaming support (win32 only)])]) - -# Checks for programs. -AC_PROG_CXX(g++ CC c++ cxx) -AM_PROG_AR -AC_PATH_PROG(AR, ar, no) -AS_IF([test "x$AR" = "xno"], [ - AC_MSG_ERROR([Could not find ar - needed to create a library]) -]) - -LT_INIT([win32-dll]) AC_CONFIG_MACRO_DIR([m4]) -# Checks for header files. -AC_HEADER_STDC -#AC_CHECK_HEADERS(sys/ioctl.h unistd.h) - -# Check for POSIX semaphore support -AC_CHECK_HEADER([semaphore.h], [ - AC_CHECK_LIB(pthread, sem_init, - AC_DEFINE([HAVE_SEMAPHORE],[1],[Define to 1 if you have POSIX semaphore support on your system.]), - AC_MSG_WARN([POSIX semaphore support not found; data may be lost on closePort])) -]) - - -# check for debug -AC_MSG_CHECKING(whether to compile debug version) -debugflags="" -object_path=Release -AS_CASE([${enable_debug}], - [ yes ], [ - AC_MSG_RESULT([yes]) - CPPFLAGS="-D__RTMIDI_DEBUG ${CPPFLAGS}" - debugflags="${debugflags} -g" - object_path=Debug - ], - [ no ], [ - AC_MSG_RESULT([no!]) - debugflags="${debugflags} -O3" - ], [ - AC_MSG_RESULT([no]) - ]) -# For debugging and optimization ... overwrite default because it has both -g and -O2 -AS_IF([test "x$debugflags" != x], - AS_IF([test "x$override_cxx" = "xyes" ], CXXFLAGS="$CXXFLAGS $debugflags", CXXFLAGS="$debugflags $CXXFLAGS") - AS_IF([test "x$override_c" = "xyes" ], CFLAGS="$CFLAGS $debugflags", CFLAGS="$debugflags $CFLAGS") - ) - -# Check compiler and use -Wall if gnu. -AS_IF([test "x$GXX" = "xyes"], [ - CXXFLAGS="-Wall -Wextra ${CXXFLAGS}" -]) - -# Checks for doxygen -AC_CHECK_PROG( DOXYGEN, [doxygen], [doxygen] ) -AM_CONDITIONAL( MAKE_DOC, [test "x${DOXYGEN}" != x] ) - -# Copy doc files to build dir if necessary -AC_CONFIG_LINKS( [doc/doxygen/footer.html:doc/doxygen/footer.html] ) -AC_CONFIG_LINKS( [doc/doxygen/header.html:doc/doxygen/header.html] ) -AC_CONFIG_LINKS( [doc/doxygen/tutorial.txt:doc/doxygen/tutorial.txt] ) -AC_CONFIG_LINKS( [doc/images/ccrma.gif:doc/images/ccrma.gif] ) -AC_CONFIG_LINKS( [doc/images/mcgill.gif:doc/images/mcgill.gif] ) - -# Checks for package options and external software -AC_CANONICAL_HOST - -# Aggregate options into a single string. -AS_IF([test "x$with_jack" = "xyes"], [systems="$systems jack"]) -AS_IF([test "x$with_alsa" = "xyes"], [systems="$systems alsa"]) -AS_IF([test "x$with_core" = "xyes"], [systems="$systems core"]) -AS_IF([test "x$with_winmm" = "xyes"], [systems="$systems winmm"]) -AS_IF([test "x$with_winks" = "xyes"], [systems="$systems winks"]) -AS_IF([test "x$with_dummy" = "xyes"], [systems="$systems dummy"]) -required=" $systems " - -# If none, assign defaults if any are known for this OS. -# User must specified with-* options for any unknown OS. -AS_IF([test "x$systems" = "x"], - AS_CASE([$host], - [*-*-linux*], [systems="alsa jack"], - [*-apple*], [systems="core jack"], - [*-mingw32*], [systems="winmm winks jack"], - [*-mingw64*], [systems="winmm winks jack"] - )) - -# If any were specifically requested disabled, do it. -AS_IF([test "x$with_jack" = "xno"], [systems=`echo $systems|tr ' ' \\\\n|grep -v jack`]) -AS_IF([test "x$with_alsa" = "xno"], [systems=`echo $systems|tr ' ' \\\\n|grep -v alsa`]) -AS_IF([test "x$with_winmm" = "xno"], [systems=`echo $systems|tr ' ' \\\\n|grep -v winmm`]) -AS_IF([test "x$with_winks" = "xno"], [systems=`echo $systems|tr ' ' \\\\n|grep -v winks`]) -AS_IF([test "x$with_core" = "xno"], [systems=`echo $systems|tr ' ' \\\\n|grep -v core`]) -AS_IF([test "x$with_dummy" = "xno"], [systems=`echo $systems|tr ' ' \\\\n|grep -v dummy`]) -systems=" `echo $systems|tr \\\\n ' '` " - -# For each MIDI system, check if it is selected and found. -# Note: Order specified above is not necessarily respected. However, -# *actual* priority is set at run-time, see RtMidiIn::openMidiApi and RtMidiOut::openMidiApi. -# One AS_CASE per system, since they are not mutually-exclusive. - -AS_CASE(["$systems"], [*" jack "*], [ - AC_CHECK_LIB(jack, jack_client_open, - [api="$api -D__UNIX_JACK__" - req="$req jack" - need_pthread=yes - found="$found Jack" - LIBS="-ljack $LIBS"], - AS_CASE(["$required"], [*" jack "*], - AC_MSG_ERROR([JACK support requires the jack library!]))) - AC_CHECK_LIB(jack, jack_port_rename, AC_DEFINE(JACK_HAS_PORT_RENAME), ) -]) - -AS_CASE(["$systems"], [*" alsa "*], [ - AC_CHECK_LIB(asound, snd_seq_open, - [api="$api -D__LINUX_ALSA__" - req="$req alsa" - need_pthread=yes - found="$found ALSA" - LIBS="-lasound $LIBS"], - AS_CASE(["$required"], [*" alsa "*], - AC_MSG_ERROR([ALSA support requires the asound library!]))) -]) - -AS_CASE(["$systems"], [*" core "*], [ - AC_CHECK_HEADER(CoreMIDI/CoreMIDI.h, - [api="$api -D__MACOSX_CORE__" - need_pthread=yes - found="$found CoreMIDI", - LIBS="$LIBS -framework CoreMIDI -framework CoreFoundation -framework CoreAudio"], - AS_CASE(["$required"], [*" core "*], - AC_MSG_ERROR([CoreMIDI header files not found!]))) -]) - -AS_CASE(["$systems"], [*" winmm "*], [ - AC_CHECK_HEADERS(windows.h mmsystem.h, - [api="$api -D__WINDOWS_MM__" - CPPFLAGS="-I$srcdir/include $CPPFLAGS" - need_ole32=yes - found="$found WinMM" - api="$api -D__WINDOWS_MM__" - LIBS="-lwinmm ${LIBS}"]) -]) - -AS_CASE(["$systems"], [*" winks "*], [ - AC_CHECK_HEADERS(windows.h audioclient.h avrt.h mmdeviceapi.h, - [api="$api -D__WINDOWS_WINKS__" - CPPFLAGS="-I$srcdir/include $CPPFLAGS" - need_ole32=yes - found="$found kernel-streaming" - api="$api -D__WINDOWS_WINKS__" - LIBS="-lsetupapi -lksuser ${LIBS}"]) -]) - -AS_IF([test -n "$need_ole32"], [LIBS="-lole32 $LIBS"]) - -AS_IF([test -n "$need_pthread"],[ - AC_MSG_CHECKING([for pthread]) - AC_CHECK_LIB(pthread, pthread_create, , - AC_MSG_ERROR([RtAudio requires the pthread library!]))]) - -AC_MSG_CHECKING([for MIDI API]) - -# Error case: no known realtime systems found. -AS_IF([test x"$api" = "x"], [ - AC_MSG_RESULT([none]) - AC_MSG_ERROR([No known system type found for MIDI support!]) -], [ - AC_MSG_RESULT([$found]) -]) - -CPPFLAGS="$CPPFLAGS $api" +rtmidi_suffix="-ts" +rtmidi_standalone="yes" +SUBDIRS="$SUBDIRS %D%/po" +rtmidi_subdir="$srcdir" +m4_include(configure.library) AC_OUTPUT -chmod oug+x rtmidi-config diff --git a/configure.library b/configure.library new file mode 100644 index 00000000..b84b5393 --- /dev/null +++ b/configure.library @@ -0,0 +1,383 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. +ifdef([lt_current],,[m4_include(configure.version)]) + +m4_define([lt_version_info], [lt_current:lt_revision:lt_age]) +m4_define([lt_current_minus_age], [m4_eval(lt_current - lt_age)]) + +RTMIDI_SO_VERSION=lt_version_info +AC_SUBST(RTMIDI_SO_VERSION) + +# Checks for package options and external software +AC_CANONICAL_HOST + +# initialize the variables for the makefile +AC_SUBST(noinst_LIBRARIES) +AC_SUBST(noinst_PROGRAMS) +AC_SUBST(TESTS) +dnl AC_SUBST(SUBDIRS) +AC_SUBST(lib_LTLIBRARIES) +AC_SUBST(EXTRA_DIST) +AC_SUBST(RTMIDI_TS_SUFFIX,[ts]) +AC_SUBST(rtmidi_visibility) +AC_SUBST(rtmidi_pkconfig_requirements) +AM_CONDITIONAL(RTMIDI_STANDALONE,test "$rtmidi_standalone" = "yes") +# Checks for programs. +AC_PROG_CXX +AM_PROG_AR +dnl AC_PATH_PROG(AR, ar, no) +dnl if [[ $AR = "no" ]] ; then +dnl AC_MSG_ERROR("Could not find ar - needed to create a library"); +dnl fi + + + +LT_INIT([win32-dll]) + +AC_LANG_PUSH(C++) +AX_CXX_COMPILE_STDCXX(11) + +rtmidi_test_flags="-Wall -Wextra" +rtmidi_visibility="-fvisibility=hidden" + +# Checks for header files. +AC_HEADER_STDC +#AC_CHECK_HEADERS(sys/ioctl.h unistd.h) + +# Check version number coherency between RtMidi.h and configure.ac +AC_MSG_CHECKING([that version numbers are coherent]) +RTMIDI_VERSION=`sed -n 's/#define RTMIDI_VERSION "\(.*\)"/\1/p' "$rtmidi_subdir/RtMidi.h"` +AS_IF( + [test "x$RTMIDI_VERSION" != "x$PACKAGE_VERSION"], + [AC_MSG_FAILURE([testing RTMIDI_VERSION==PACKAGE_VERSION failed, check that RtMidi.h defines RTMIDI_VERSION as "$PACKAGE_VERSION" or that the first line of configure.ac has been updated.])]) + + + + +AC_ARG_WITH(suffix, +AS_HELP_STRING([--with-suffix],[add a suffix to the library. Default is $rtmidi_suffix]), +[AS_IF(test "x$with_suffix" = "xno", + [ rtmidi_suffix=""], + [ rtmidi_suffix="$with_suffix"])]) +AC_SUBST(rtmidi_suffix) + + + +# Check for debug +AC_MSG_CHECKING(whether to compile debug version) +AC_ARG_ENABLE(debug, +AS_HELP_STRING([--enable-debug],[enable various debug output]), + [ + AC_SUBST( RTMIDI_CPPFLAGS, [-D__RTMIDI_DEBUG__] ) + AC_SUBST( RTMIDI_CXXFLAGS, [-g] ) + AC_SUBST( object_path, [Debug] ) + AC_MSG_RESULT(yes)], + [AC_SUBST( RTMIDI_CPPFLAGS, [] ) + AC_SUBST( RTMIDI_CXXFLAGS, [-O3] ) + AC_SUBST( object_path, [Release] ) + rtmidi_visibility="${rtmidi_visibility} -fvisibility=hidden" + AC_MSG_RESULT(no)]) + + +# Checks for doxygen +AC_CHECK_PROG( DOXYGEN, [doxygen], [doxygen] ) +AM_CONDITIONAL( MAKE_DOC, [test "x${DOXYGEN}" != x] ) + +# Copy doc files to build dir if necessary +AC_CONFIG_LINKS( [doc/doxygen/footer.html:doc/doxygen/footer.html] ) +AC_CONFIG_LINKS( [doc/doxygen/header.html:doc/doxygen/header.html] ) +AC_CONFIG_LINKS( [doc/doxygen/tutorial.txt:doc/doxygen/tutorial.txt] ) +AC_CONFIG_LINKS( [doc/images/ccrma.gif:doc/images/ccrma.gif] ) +AC_CONFIG_LINKS( [doc/images/mcgill.gif:doc/images/mcgill.gif] ) + + +dnl # Set paths if prefix is defined +dnl if test "x$prefix" != "x" && test "x$prefix" != "xNONE"; then +dnl LIBS="$LIBS -L$prefix/lib" +dnl CPPFLAGS="$CPPFLAGS -I$prefix/include" +dnl fi + +# Check compiler and use -Wall if supported. +for flag in $rtmidi_test_flags +do + tmpcxxflags="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $flag" + AC_MSG_CHECKING([whether $CXX supports $flag]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]],[[]])], + [AC_MSG_RESULT([yes])], + [CXXFLAGS="$tmpcxxflags" + AC_MSG_RESULT([no])]) + AC_LANG_PUSH(C) + tmpcflags="$CFLAGS" + CFLAGS="$CFLAGS $flag" + AC_MSG_CHECKING([whether $CC supports $flag]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]],[[]])], + [AC_MSG_RESULT([yes])], + [CFLAGS="$tmpcflags" + AC_MSG_RESULT([no])]) + AC_LANG_POP(C) +done + +# Check compiler and use -Wall if supported. +tmpcflags="$CFLAGS" +tmpcxxflags="$CXXFLAGS" +rtmidi_visibility_result="" +for flag in $rtmidi_visibility +do + CXXFLAGS="$tmpcxxflags $rtmidi_visibility_result $flag" + AC_MSG_CHECKING([whether $CXX supports $flag]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]],[[]])], + [ + rtmidi_visibility_result="$rtmidi_visibility_result $flag" + AC_MSG_RESULT([yes]) + ],[AC_MSG_RESULT([no])]) +done +rtmidi_visibility="$rtmidi_visibility_result" + + +dnl mudflap is not possible with g++ 4.7.1 and wxWidgets 2.8 +dnl tmpcxxflags="$CXXFLAGS" +dnl tmplibs="$LIBS" +dnl CXXFLAGS="$CXXFLAGS -fmudflapth" +dnl LIBS="-lmudflapth" +dnl AC_MSG_CHECKING([whether $CXX supports -fmudflapth -lmudflapth]) +dnl AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]],[[]])], +dnl [AC_MSG_RESULT([yes])], +dnl [CXXFLAGS="$tmpcxxflags" +dnl LIBS="$tmplibs"] +dnl [AC_MSG_RESULT([no])]) +dnl + +AC_ARG_ENABLE(address-sanitizer, + AS_HELP_STRING([--enable-address-sanitizer],[Enable gcc/clang address santizer]), + [AS_IF(test "$enableval" = yes, + + AC_LANG_PUSH(C++) + for flag in -fsanitize=address -fno-omit-frame-pointer -fno-common + do + tmpcxxflags="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $flag" + AC_MSG_CHECKING([whether $CXX supports $flag]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]],[[]])], + [AC_MSG_RESULT([yes])], + [CXXFLAGS="$tmpcxxflags" + AC_MSG_RESULT([no])]) + AC_LANG_PUSH(C) + tmpcflags="$CFLAGS" + CFLAGS="$CFLAGS $flag" + AC_MSG_CHECKING([whether $CC supports $flag]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]],[[]])], + [AC_MSG_RESULT([yes])], + [CFLAGS="$tmpcflags" + AC_MSG_RESULT([no])]) + AC_LANG_POP(C) + done + AC_LANG_POP(C++) + ) + ] +) + +AC_ARG_ENABLE(thread-sanitizer, + AS_HELP_STRING([--enable-thread-sanitizer],[Enable gcc/clang thread santizer]), + [AS_IF(test "$enableval" = yes, + AC_LANG_PUSH(C++) + for flag in -fsanitize=thread -fno-omit-frame-pointer -fno-common -fPIC + do + tmpcxxflags="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $flag" + AC_MSG_CHECKING([whether $CXX supports $flag]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]],[[]])], + [AC_MSG_RESULT([yes])], + [CXXFLAGS="$tmpcxxflags" + AC_MSG_RESULT([no])]) + AC_LANG_PUSH(C) + tmpcflags="$CFLAGS" + CFLAGS="$CFLAGS $flag" + AC_MSG_CHECKING([whether $CC supports $flag]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]],[[]])], + [AC_MSG_RESULT([yes])], + [CFLAGS="$tmpcflags" + AC_MSG_RESULT([no])]) + AC_LANG_POP(C) + done + for flag in -pie + do + tmpldflags="$LDFLAGS" + LDFLAGS="$LDFLAGS $flag" + AC_MSG_CHECKING([whether $LD supports $flag]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])], + [AC_MSG_RESULT([yes])], + [LDFLAGS="$tmpldflags" + AC_MSG_RESULT([no])]) + done + AC_LANG_POP(C++) + ) + ] +) + +dnl AC_SUBST( sharedlib, ["librtmidi.so"] ) +dnl AC_SUBST( sharedname, ["librtmidi.so.\$(RELEASE)"] ) +dnl AC_SUBST( libflags, ["-shared -Wl,-soname,\$(SHARED).\$(MAJOR) -o \$(SHARED).\$(RELEASE)"] ) +dnl case $host in +dnl *-apple*) +dnl AC_SUBST( sharedlib, ["librtmidi.dylib"] ) +dnl AC_SUBST( sharedname, ["librtmidi.\$(RELEASE).dylib"] ) +dnl AC_SUBST( libflags, ["-dynamiclib -o librtmidi.\$(RELEASE).dylib"] ) +dnl esac + + + +dnl sem_timedwait may not be availlable on Mac OS X +# Check for POSIX semaphore support +AC_CHECK_HEADER([semaphore.h], [ + AC_CHECK_LIB(pthread, sem_timedwait, + AC_DEFINE([HAVE_SEMAPHORE],[1],[Define to 1 if you have POSIX semaphore support on your system.]), + AC_MSG_WARN([POSIX semaphore support not found; data may be lost on closePort])) +]) + +AC_MSG_CHECKING(whether to check all apis) +AC_ARG_ENABLE(apisearch, + AS_HELP_STRING([--disable-apisearch],[disable all unrequested apis]), + [ + AS_IF([test "x$enableval" = "xno"],[ + AC_MSG_RESULT(no) + AS_IF([test "x$with_alsa" = "x"],[with_alsa=no]) + AS_IF([test "x$with_jack" = "x"],[with_jack=no]) + AS_IF([test "x$with_winmm" = "x"],[with_winmm=no]) + AS_IF([test "x$with_winks" = "x"],[with_winks=no]) + AS_IF([test "x$with_coremidi" = "x"],[with_coremidi=no]) + ],[AC_MSG_RESULT(yes)]) + ],[AC_MSG_RESULT(yes)]) + +AC_SUBST( RTMIDI_API, [""] ) +AC_SUBST( RTMIDI_LIB_CFLAGS, [""] ) +AC_SUBST( RTMIDI_LIBS, [""] ) +AC_MSG_CHECKING(for MIDI API) +RTMIDI_HAVE_VIRTUAL_DEVICES=no +rtmidi_check_jack=auto +case $host in +*linux*) + PLATFORM=linux +dnl with_winmm=no +dnl with_winks=no +dnl with_coremidi=no + + # Checks for pthread library. + ;; + +darwin*|*apple*) + PLATFORM=macosx + # Check for CoreAudio framework +dnl with_winmm=no +dnl with_winks=no +dnl with_alsa=no + + ;; + +*winnt*|*interix*|cygwin*|*mingw*|uwin*) + PLATFORM=windows +dnl with_alsa=no +dnl with_coremidi=no + + # I can't get the following check to work so just manually add the library + # or could try the following? AC_LIB_WINMM([midiOutGetNumDevs]) + # AC_CHECK_LIB(winmm, midiInGetNumDevs, , AC_MSG_ERROR(Windows MIDI support requires the winmm library!) )],) + AC_CACHE_CHECK([where to find support dlls], [ac_cv_rtmidi_ts_mingw_dll_dirs], + [ + gccsearchdirs=`$CXX $CXXFLAGS -print-search-dirs|\ + sed -e '/^libraries:/ { s/^libraries: *=\?//; /;\([0-9a-zA-Z]\):/ { s,;\([0-9a-zA-Z]\):,:/\1/,g;s,^\([0-9a-zA-Z]\):,/\1/,g; s,\\\\,/,g } ; p } ; d'` ; \ + gccsearchdirs="$WINEDLLPATH:$gccsearchdirs:\ +/usr/share/doc/mingw32-runtime:\ +/usr/$host/bin:\ +/usr/$host/lib:\ +/usr/bin/$host/:\ +/usr/lib/$host/:\ +/usr/lib/$host/lib:\ +/usr/lib/$host/bin:\ +/usr/bin/$host/lib:\ +/usr/bin/$host/bin:\ +/mingw/lib:\ +/mingw/bin:\ +/usr/lib:\ +/lib:\ +/usr/bin:\ +/bin" + SEARCHDIRS="$PWD:$PWD/.libs" + old_FS="$IFS" + IFS=: + for d in $gccsearchdirs ; + do + AS_IF(test -d "$d",[ SEARCHDIRS="$SEARCHDIRS:$d" ]) + done + IFS="$old_FS" + ac_cv_rtmidi_ts_mingw_dll_dirs="$SEARCHDIRS" + ]) + + AC_SUBST(DLLSEARCHPATH,"$ac_cv_rtmidi_ts_mingw_dll_dirs") + rtmidicopydlls=true + ;; + +*) + # Default case for unknown realtime systems. + AC_MSG_WARN([ + Your target system could not be recognized. $PACKAGE depends on + a working MIDI configuration for your System. We are trying all + supported APIs. The result may be varying. + + Please contact the development team at + + $PACKAGE_BUGREPORT + + and provide the following information: + + * The output of configure + * The description of your System. + + ]) + ;; +esac + +AC_ARG_WITH(jack, + AS_HELP_STRING([--with-jack],[choose JACK server support (needs libjack to be installed)]),[ + echo running jack: + AS_IF([test "x$with_jack" = "xno"],,[RTMIDI_LIB_JACK ]) + ],[ echo runnning jack 2 ; RTMIDI_LIB_JACK(,[true]) ]) + +AC_ARG_WITH(alsa, AS_HELP_STRING([--with-alsa],[choose native ALSA sequencer API support]),[ + AS_IF([test "x$with_alsa" = "xno"],,[ RTMIDI_LIB_ALSA ]) + ],[ RTMIDI_LIB_ALSA(,[true]) ]) + +AC_ARG_WITH(winmm, AS_HELP_STRING([--with-winmm],[choose native Windows Multimedia API support]),[ + AS_IF([test "x$with_winmm" = "xno"],,[RTMIDI_LIB_WINMM ]) + ],[ RTMIDI_LIB_WINMM(,[true]) ]) + +AC_ARG_WITH(winks, AS_HELP_STRING([--with-winks],[choose native Windows kernel streaming API support]),[ + AS_IF([test "x$with_winks" = "xno"],,[ RTMIDI_LIB_WINKS ]) + ],[ RTMIDI_LIB_WINKS(,[true]) ]) + +AC_ARG_WITH(coremidi, AS_HELP_STRING([--with-coremidi],[choose native CoreAudio API support]),[ + AS_IF([test "x$with_coremidi" = "xno"],,[RTMIDI_LIB_COREMIDI ]) + ],[ RTMIDI_LIB_COREMIDI(,[true]) ]) + +AS_IF([test "x$RTMIDI_API" = "x"],[ + AC_MSG_WARN("No suitable MIDI interface found. Using dummy driver") + RTMIDI_API="-D__RTMIDI_DUMMY__" +]) + +AM_CONDITIONAL(RTMIDI_HAVE_VIRTUAL_DEVICES,test "$RTMIDI_HAVE_VIRTUAL_DEVICES" == yes) +AM_GNU_GETTEXT([external]) +AM_GNU_GETTEXT_VERSION([0.18.3]) + +if test "x$rtmidicopydlls" = "x" +then + rtmidicopydlls=false +fi + +AM_CONDITIONAL(RTMIDICOPYDLLS,$rtmidicopydlls && test -n "$DLLSEARCHPATH") + + +AX_CHECK_FALLTHROUGH_SYNTAX +AC_DEFINE_UNQUOTED(RTMIDI_FALLTHROUGH, + [$ax_cv_check_cxx_fallthrough_syntax_], + [syntax for switch case fallthrough attributes in C++]) +AC_LANG_POP(C++) diff --git a/configure.version b/configure.version new file mode 100644 index 00000000..015bd7f5 --- /dev/null +++ b/configure.version @@ -0,0 +1,16 @@ +# libtool version: current:revision:age +# +# If the library source code has changed at all since the last update, then +# increment revision (`c:r:a' becomes `c:r+1:a'). +# +# If any interfaces have been added, removed, or changed since the last update, +# increment current, and set revision to 0. +# +# If any interfaces have been added since the last public release, then +# increment age. +# +# If any interfaces have been removed since the last public release, then set +# age to 0. +m4_define([lt_current], 4) +m4_define([lt_revision], 0) +m4_define([lt_age], 0) diff --git a/doc/Makefile.am b/doc/Makefile.am index f22d2a3d..92b337f1 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -15,10 +15,16 @@ DOC_DIR=$(HTML_DIR) all-local: doxygen-build.stamp +if MAKE_DOC doxygen-build.stamp: doxygen/$(DOX) $(top_srcdir)/RtMidi.h $(top_srcdir)/rtmidi_c.h @echo '*** Running doxygen ***' cd doxygen; $(DOXYGEN) $(DOX) touch doxygen-build.stamp +else +doxygen-build.stamp: + touch "$@" + mkdir html +endif clean-local: rm -f *~ *.bak $(DOC_STAMPS) || true diff --git a/doc/doxygen/Doxyfile.in b/doc/doxygen/Doxyfile.in index b6e4db0f..8864d73a 100644 --- a/doc/doxygen/Doxyfile.in +++ b/doc/doxygen/Doxyfile.in @@ -669,7 +669,7 @@ WARN_LOGFILE = INPUT = tutorial.txt \ ../../RtMidi.h \ - ../../RtError.h + ../../tests # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is @@ -729,7 +729,7 @@ EXCLUDE_SYMBOLS = # directories that contain example code fragments that are included (see # the \include command). -EXAMPLE_PATH = samples +EXAMPLE_PATH = ../../tests samples # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp @@ -1580,7 +1580,7 @@ INCLUDE_FILE_PATTERNS = # undefined via #undef or recursively expanded use the := operator # instead of the = operator. -PREDEFINED = __MACOSX_CORE__ \ +PREDEFINED = __MACOSX_COREMIDI__ \ __WINDOWS_MM__ \ __UNIX_JACK__ \ __LINUX_ALSA__ \ diff --git a/doc/doxygen/header.html b/doc/doxygen/header.html index 3b6d60ba..62a1f347 100644 --- a/doc/doxygen/header.html +++ b/doc/doxygen/header.html @@ -5,5 +5,5 @@
-Tutorial   Class/Enum List   File List   Compound Members  
+Tutorial   Examples   Class/Enum List   File List   Compound Members  
diff --git a/doc/doxygen/samples/getting_started.cpp b/doc/doxygen/samples/getting_started.cpp index d6f63771..cc0cf0e1 100644 --- a/doc/doxygen/samples/getting_started.cpp +++ b/doc/doxygen/samples/getting_started.cpp @@ -2,10 +2,12 @@ int main() { try { - RtMidiIn midiin; - } catch (RtMidiError &error) { + rtmidi::MidiIn midiin; + } catch (const rtmidi::Error &error) { // Handle the exception here error.printMessage(); + // opitionally rethrow: + // throw; } return 0; } diff --git a/doc/doxygen/tutorial.txt b/doc/doxygen/tutorial.txt index c9932b0d..c1ce51b8 100644 --- a/doc/doxygen/tutorial.txt +++ b/doc/doxygen/tutorial.txt @@ -2,18 +2,68 @@
\ref intro    \ref download    \ref start    \ref error    \ref probing    \ref output    \ref input    \ref virtual    \ref compiling    \ref debug    \ref multi    \ref apinotes    \ref acknowledge    \ref license
-\section intro Introduction +\section warning Warning: + +This documentation refers to an API suggested to RtMidi (and available at GitHub). So far it is not officially accepted in the RtMidi distribution. -RtMidi is a set of C++ classes (RtMidiIn, RtMidiOut and API-specific classes) that provides a common API (Application Programming Interface) for realtime MIDI input/output across Linux (ALSA & JACK), Macintosh OS X (CoreMIDI & JACK), and Windows (Multimedia Library) operating systems. RtMidi significantly simplifies the process of interacting with computer MIDI hardware and software. It was designed with the following goals: +\section intro Introduction -- object oriented C++ design -- simple, common API across all supported platforms -- only one header and one source file for easy inclusion in programming projects -- MIDI device enumeration +RtMidi is a set of C++ classes (rtmidi::MidiIn, rtmidi::MidiOut and API-specific classes) that provides a common API (Application Programming Interface) for realtime MIDI input/output across Linux (ALSA & JACK), Macintosh OS X (CoreMIDI & JACK), and Windows (Multimedia Library) operating systems. RtMidi significantly simplifies the process of interacting with computer MIDI hardware and software. It was designed with the following goals: + - object oriented C++ design + - simple, common API across all supported platforms + - only one header and one source file for easy inclusion in programming projects + - query availlable MIDI devices Where applicable, multiple API support can be compiled and a particular API specified when creating an RtAudio instance. -MIDI input and output functionality are separated into two classes, RtMidiIn and RtMidiOut. Each class instance supports only a single MIDI connection. RtMidi does not provide timing functionality (i.e., output messages are sent immediately). Input messages are timestamped with delta times in seconds (via a \c double floating point type). MIDI data is passed to the user as raw bytes using an std::vector. +MIDI input and output functionality are separated into two classes, rtmidi::MidiIn and rtmidi::MidiOut. Each class instance supports only a single MIDI connection. RtMidi does not provide timing functionality (i.e., output messages are sent immediately). Input messages are timestamped with delta times in seconds (via a \c double floating point type). MIDI data is passed to the user as raw bytes using an std::vector. + +\section whatsnewts What's New in the Fork (Version 5.0) + + - The classes of RtMidi now reside in the namespace rtmidi. + - The beginning letters “Rt” are dropped from the names + - For easy adoption of the new interface wrappers for the old API are provided. + - The library uses backend provided port descriptors, now. This provides a more reliable port handling for changing environments (See below). + - Allow to use all supported backends in parallel + +The way MIDI devices are enumerated has changed. The old way, using the ordinal number of MIDI devices works only in cases where MIDI devices are not added or removed during the program session. When a virtual MIDI port or USB MIDI device is added or removed the ordinal number of each of the other devices may change. + +Suppose your computer has the following list of MIDI devices. + 1. MIDI loopback device + 2. Removable USB MIDI device + 3. Another MIDI device + 4. Software MIDI Synth + 5. A virtual MIDI port + +After the software obtained this list, your friend remembers that he +must catch the next bus and unplugs his removable USB MIDI device. +The software does not recognize this removal and keeps the above list, +while the system has a new one: + 1. MIDI loopback device + 2. Another MIDI device + 3. Software MIDI Synth + 4. A virtual MIDI port + +Somehow you told the software to use the Software MIDI Synth. The +program stores the number 4 as it obtained during enumeration of the +MIDI devices. Instead of playing the music using your sound card it +sends the music to a different port. + +While this behaviour is only annoying in interactive environments it +results in unpredictable behaviour if several ports are opened at +once. E.g. in the ALSA backend every opened port results in an +aditional newly created virtual port. + +In order to avoid such problems, most backends identify ports (except +WinMM) by different data structures. + +The current version introduces a new class \ref rtmidi::PortDescriptor +in order to hide this implementation detail from the user code. In +order to avoid the above problems these are retrieved at once using \ref rtmidi::Midi::getPortList. +This new feature also allows to retreive the port descriptor of an open device using +\ref rtmidi::Midi::getDescriptor. The latter can be used to obtain + + \section whatsnew What's New (Version 3.0.0) @@ -31,98 +81,103 @@ Latest Release (31 August 2017): #include +#include #include "RtMidi.h" int main() { - RtMidiIn *midiin = 0; - RtMidiOut *midiout = 0; + // Create an api map. + std::map apiMap; + apiMap[RtMidi::MACOSX_CORE] = "OS-X CoreMidi"; + apiMap[RtMidi::WINDOWS_MM] = "Windows MultiMedia"; + apiMap[RtMidi::WINDOWS_KS] = "Windows Kernel Straming"; + apiMap[RtMidi::UNIX_JACK] = "Jack Client"; + apiMap[RtMidi::LINUX_ALSA] = "Linux ALSA"; + apiMap[RtMidi::RTMIDI_DUMMY] = "RtMidi Dummy"; - // RtMidiIn constructor - try { - midiin = new RtMidiIn(); - } - catch ( RtMidiError &error ) { - error.printMessage(); - exit( EXIT_FAILURE ); - } + std::vector< rtmidi::ApiType > apis; + rtmidi::Midi :: getCompiledApi( apis ); + + std::cout << "\nCompiled APIs:\n"; + for ( unsigned int i=0; igetPortCount(); - std::cout << "\nThere are " << nPorts << " MIDI input sources available.\n"; - std::string portName; - for ( unsigned int i=0; igetPortName(i); - } - catch ( RtMidiError &error ) { - error.printMessage(); - goto cleanup; - } - std::cout << " Input Port #" << i+1 << ": " << portName << '\n'; - } - // RtMidiOut constructor try { - midiout = new RtMidiOut(); - } - catch ( RtMidiError &error ) { - error.printMessage(); - exit( EXIT_FAILURE ); - } - // Check outputs. - nPorts = midiout->getPortCount(); - std::cout << "\nThere are " << nPorts << " MIDI output ports available.\n"; - for ( unsigned int i=0; igetPortName(i); + // rtmidi::MidiIn constructor ... exception possible + rtmidi::MidiIn midiin; + + std::cout << "\nCurrent input API: " << apiMap[ midiin.getCurrentApi() ] << std::endl; + + + rtmidi::PortList list = midiin.getPortList(); + // Check inputs. + std::cout << "\nThere are " << list.size() << " MIDI input sources available.\n"; + for (rtmidi::PortList::iterator i = list.begin(); + i != list.end(); + i++) { + std::cout << " Input Port: " << (*i)->getName() << std::endl; } - catch (RtMidiError &error) { - error.printMessage(); - goto cleanup; + + std::cout << "**********************************************************************" << std::endl; + + // rtmidi::MidiOut constructor ... exception possible + rtmidi::MidiOut midiout; + + std::cout << "\nCurrent output API: " << apiMap[ midiout.getCurrentApi() ] << std::endl; + + list = midiout.getPortList(); + + // Check inputs. + std::cout << "\nThere are " << list.size() << " MIDI output sinks available.\n"; + + for (rtmidi::PortList::iterator i = list.begin(); + i != list.end(); + i++) { + std::cout << " Output Port: " << (*i)->getName() << std::endl; } - std::cout << " Output Port #" << i+1 << ": " << portName << '\n'; - } - std::cout << '\n'; - // Clean up - cleanup: - delete midiin; - delete midiout; + } catch ( rtmidi::Error &error ) { + error.printMessage(); + } return 0; } @@ -130,9 +185,9 @@ int main() \section output MIDI Output -The RtMidiOut class provides simple functionality to immediately send messages over a MIDI connection. No timing functionality is provided. Note that there is an overloaded RtMidiOut::sendMessage() function that does not use std::vectors. +The rtmidi::MidiOut class provides simple functionality to immediately send messages over a MIDI connection. No timing functionality is provided. Note that there is are overloaded \ref rtmidi::MidiOut::sendMessage() functions for C arrays as well as std::vectors. -In the following example, we omit necessary error checking and details regarding OS-dependent sleep functions. For a complete example, see the \c midiout.cpp program in the \c tests directory. +In the following example, we omit necessary error checking and details regarding OS-dependent sleep functions. For a complete example, see the \link midiout2.cpp \c midiout2.cpp \endlink program in the \c tests directory. \code // midiout.cpp @@ -143,50 +198,51 @@ In the following example, we omit necessary error checking and details regarding int main() { - RtMidiOut *midiout = new RtMidiOut(); - std::vector message; - - // Check available ports. - unsigned int nPorts = midiout->getPortCount(); - if ( nPorts == 0 ) { - std::cout << "No ports available!\n"; - goto cleanup; - } - - // Open first available port. - midiout->openPort( 0 ); + try { + rtmidi::MidiOut midiout; + std::vector message; + + // Check available ports. + rtmidi::PortList list = midiout.getPortList(); + if (list.empty()) { + std::cerr << "No devices found." << std::endl; + exit(EXIT_FAILURE); + } - // Send out a series of MIDI messages. + // Open first available port. + midiout.openPort( list.front ); - // Program change: 192, 5 - message.push_back( 192 ); - message.push_back( 5 ); - midiout->sendMessage( &message ); + // Send out a series of MIDI messages. - // Control Change: 176, 7, 100 (volume) - message[0] = 176; - message[1] = 7; - message.push_back( 100 ); - midiout->sendMessage( &message ); + // Program change: 192, 5 + message.push_back( 192 ); + message.push_back( 5 ); + midiout.sendMessage( &message ); - // Note On: 144, 64, 90 - message[0] = 144; - message[1] = 64; - message[2] = 90; - midiout->sendMessage( &message ); + // Control Change: 176, 7, 100 (volume) + message[0] = 176; + message[1] = 7; + message.push_back( 100 ); + midiout.sendMessage( &message ); - SLEEP( 500 ); // Platform-dependent ... see example in tests directory. + // Note On: 144, 64, 90 + message[0] = 144; + message[1] = 64; + message[2] = 90; + midiout.sendMessage( &message ); - // Note Off: 128, 64, 40 - message[0] = 128; - message[1] = 64; - message[2] = 40; - midiout->sendMessage( &message ); + SLEEP( 500 ); // Platform-dependent ... see example in tests directory. - // Clean up - cleanup: - delete midiout; + // Note Off: 128, 64, 40 + message[0] = 128; + message[1] = 64; + message[2] = 40; + midiout.sendMessage( &message ); + } catch (rtmidi::Error & error) { + error.printMessage(); + exit(EXIT_FAILURE); + } return 0; } \endcode @@ -194,15 +250,15 @@ int main() \section input MIDI Input -The RtMidiIn class uses an internal callback function or thread to receive incoming MIDI messages from a port or device. These messages are then either queued and read by the user via calls to the RtMidiIn::getMessage() function or immediately passed to a user-specified callback function (which must be "registered" using the RtMidiIn::setCallback() function). We'll provide examples of both usages. +The rtmidi::MidiIn class uses an internal callback function or thread to receive incoming MIDI messages from a port or device. These messages are then either queued and read by the user via calls to the rtmidi::MidiIn::getMessage() function or immediately passed to a user-specified callback function (which must be "registered" using the rtmidi::MidiIn::setCallback() function). We'll provide examples of both usages. -The RtMidiIn class provides the RtMidiIn::ignoreTypes() function to specify that certain MIDI message types be ignored. By default, system exclusive, timing, and active sensing messages are ignored. +The rtmidi::MidiIn class provides the rtmidi::MidiIn::ignoreTypes() function to specify that certain MIDI message types be ignored. By default, system exclusive, timing, and active sensing messages are ignored. \subsection qmidiin Queued MIDI Input -The RtMidiIn::getMessage() function does not block. If a MIDI message is available in the queue, it is copied to the user-provided \c std::vector container. When no MIDI message is available, the function returns an empty container. The default maximum MIDI queue size is 1024 messages. This value may be modified with the RtMidiIn::setQueueSizeLimit() function. If the maximum queue size limit is reached, subsequent incoming MIDI messages are discarded until the queue size is reduced. +The rtmidi::MidiIn::getMessage() function does not block. If a MIDI message is available in the queue, it is copied to the user-provided \c std::vector container. When no MIDI message is available, the function returns an empty container. The default maximum MIDI queue size is 1024 messages. This value may be modified with the rtmidi::MidiIn::setQueueSizeLimit() function. If the maximum queue size limit is reached, subsequent incoming MIDI messages are discarded until the queue size is reduced. -In the following example, we omit some necessary error checking and details regarding OS-dependent sleep functions. For a more complete example, see the \c qmidiin.cpp program in the \c tests directory. +In the following example, we omit some necessary error checking and details regarding OS-dependent sleep functions. For a more complete example, see the \link qmidiin2.cpp \c qmidiin2.cpp \endlink program in the \c tests directory. \code // qmidiin.cpp @@ -217,51 +273,54 @@ static void finish(int ignore){ done = true; } int main() { - RtMidiIn *midiin = new RtMidiIn(); - std::vector message; - int nBytes, i; - double stamp; - - // Check available ports. - unsigned int nPorts = midiin->getPortCount(); - if ( nPorts == 0 ) { - std::cout << "No ports available!\n"; - goto cleanup; - } - midiin->openPort( 0 ); - - // Don't ignore sysex, timing, or active sensing messages. - midiin->ignoreTypes( false, false, false ); - - // Install an interrupt handler function. - done = false; - (void) signal(SIGINT, finish); - - // Periodically check input queue. - std::cout << "Reading MIDI from port ... quit with Ctrl-C.\n"; - while ( !done ) { - stamp = midiin->getMessage( &message ); - nBytes = message.size(); - for ( i=0; i 0 ) - std::cout << "stamp = " << stamp << std::endl; - - // Sleep for 10 milliseconds ... platform-dependent. - SLEEP( 10 ); - } + try { + rtmidi::MidiIn midiin; + std::vector message; + int nBytes, i; + double stamp; + + // Check available ports. + rtmidi::PortList list = midiin.getPortList(); + if (list.empty()) { + std::cerr << "No devices found." << std::endl; + exit(EXIT_FAILURE); + } - // Clean up - cleanup: - delete midiin; + // Open first available port. + midiin.openPort( list.front ); + // Don't ignore sysex, timing, or active sensing messages. + midiin.ignoreTypes( false, false, false ); + + // Install an interrupt handler function. + done = false; + (void) signal(SIGINT, finish); + + // Periodically check input queue. + std::cout << "Reading MIDI from port ... quit with Ctrl-C.\n"; + while ( !done ) { + stamp = midiin.getMessage( &message ); + nBytes = message.size(); + for ( i=0; i 0 ) + std::cout << "stamp = " << stamp << std::endl; + + // Sleep for 10 milliseconds ... platform-dependent. + SLEEP( 10 ); + } + + } catch (rtmidi::Error & error) { + error.printMessage(); + exit(EXIT_FAILURE); + } return 0; } \endcode \subsection cmidiin MIDI Input with User Callback -When set, a user-provided callback function will be invoked after the input of a complete MIDI message. It is possible to provide a pointer to user data that can be accessed in the callback function (not shown here). It is necessary to set the callback function immediately after opening the port to avoid having incoming messages written to the queue (which is not emptied when a callback function is set). If you are worried about this happening, you can check the queue using the RtMidi::getMessage() function to verify it is empty (after the callback function is set). +When set, a user-provided callback function will be invoked after the input of a complete MIDI message. It is possible to provide a pointer to user data that can be accessed in the callback function (not shown here). It is necessary to set the callback function immediately after opening the port to avoid having incoming messages written to the queue (which is not emptied when a callback function is set). If you are worried about this happening, you can check the queue using the rtmidi::Midi::getMessage() function to verify it is empty (after the callback function is set). In the following example, we omit some necessary error checking. For a more complete example, see the \c cmidiin.cpp program in the \c tests directory. @@ -283,40 +342,42 @@ void mycallback( double deltatime, std::vector< unsigned char > *message, void * int main() { - RtMidiIn *midiin = new RtMidiIn(); - - // Check available ports. - unsigned int nPorts = midiin->getPortCount(); - if ( nPorts == 0 ) { - std::cout << "No ports available!\n"; - goto cleanup; - } + try { + rtmidi::MidiIn midiin; - midiin->openPort( 0 ); + // Check available ports. + rtmidi::PortList list = midiin.getPortList(); + if (list.empty()) { + std::cerr << "No ports available." << std::endl; + exit(EXIT_FAILURE); + } - // Set our callback function. This should be done immediately after - // opening the port to avoid having incoming messages written to the - // queue. - midiin->setCallback( &mycallback ); + // Open first available port. + midiin.openPort( list.front ); - // Don't ignore sysex, timing, or active sensing messages. - midiin->ignoreTypes( false, false, false ); + // Set our callback function. This should be done immediately after + // opening the port to avoid having incoming messages written to the + // queue. + midiin.setCallback( &mycallback ); - std::cout << "\nReading MIDI input ... press to quit.\n"; - char input; - std::cin.get(input); + // Don't ignore sysex, timing, or active sensing messages. + midiin.ignoreTypes( false, false, false ); - // Clean up - cleanup: - delete midiin; + std::cout << "\nReading MIDI input ... press to quit.\n"; + char input; + std::cin.get(input); + } catch (rtmidi::Error & error) { + error.printMessage(); + exit(EXIT_FAILURE); + } return 0; } \endcode \section virtual Virtual Ports -The Linux ALSA, Macintosh CoreMIDI and JACK APIs allow for the establishment of virtual input and output MIDI ports to which other software clients can connect. RtMidi incorporates this functionality with the RtMidiIn::openVirtualPort() and RtMidiOut::openVirtualPort() functions. Any messages sent with the RtMidiOut::sendMessage() function will also be transmitted through an open virtual output port. If a virtual input port is open and a user callback function is set, the callback function will be invoked when messages arrive via that port. If a callback function is not set, the user must poll the input queue to check whether messages have arrived. No notification is provided for the establishment of a client connection via a virtual port. The RtMidi::isPortOpen() function does not report the status of ports created with the RtMidi::openVirtualPort() function. +The Linux ALSA, Macintosh CoreMIDI and JACK APIs allow for the establishment of virtual input and output MIDI ports to which other software clients can connect. RtMidi incorporates this functionality with the rtmidi::MidiIn::openVirtualPort() and rtmidi::MidiOut::openVirtualPort() functions. Any messages sent with the rtmidi::MidiOut::sendMessage() function will also be transmitted through an open virtual output port. If a virtual input port is open and a user callback function is set, the callback function will be invoked when messages arrive via that port. If a callback function is not set, the user must poll the input queue to check whether messages have arrived. No notification is provided for the establishment of a client connection via a virtual port. The RtMidi::isPortOpen() function does not report the status of ports created with the RtMidi::openVirtualPort() function. \section compiling Compiling @@ -336,21 +397,21 @@ In order to compile RtMidi for a specific OS and API, it is necessary to supply ALSA Sequencer __LINUX_ALSA__ asound, pthread - g++ -Wall -D__LINUX_ALSA__ -o midiprobe midiprobe.cpp RtMidi.cpp -lasound -lpthread + g++ -Wall -D__LINUX_ALSA__ -o midiprobe midiprobe2.cpp RtMidi.cpp -lasound -lpthread Linux or Mac JACK MIDI __UNIX_JACK__ jack - g++ -Wall -D__UNIX_JACK__ -o midiprobe midiprobe.cpp RtMidi.cpp -ljack + g++ -Wall -D__UNIX_JACK__ -o midiprobe midiprobe2.cpp RtMidi.cpp -ljack Macintosh OS X CoreMIDI - __MACOSX_CORE__ + __MACOSX_COREMIDI__ CoreMIDI, CoreAudio, CoreFoundation - g++ -Wall -D__MACOSX_CORE__ -o midiprobe midiprobe.cpp RtMidi.cpp -framework CoreMIDI -framework CoreAudio -framework CoreFoundation + g++ -Wall -D__MACOSX_COREMIDI__ -o midiprobe midiprobe2.cpp RtMidi.cpp -framework CoreMIDI -framework CoreAudio -framework CoreFoundation Windows @@ -362,7 +423,7 @@ In order to compile RtMidi for a specific OS and API, it is necessary to supply

-The example compiler statements above could be used to compile the midiprobe.cpp example file, assuming that midiprobe.cpp, RtMidi.h and RtMidi.cpp all exist in the same directory. +The example compiler statements above could be used to compile the midiprobe2.cpp example file, assuming that midiprobe2.cpp, RtMidi.h and RtMidi.cpp all exist in the same directory. \section debug Debugging @@ -370,9 +431,13 @@ If you are having problems getting RtMidi to run on your system, try passing the \section multi Using Simultaneous Multiple APIs -Support for each MIDI API is encapsulated in specific MidiInApi or MidiOutApi subclasses, making it possible to compile and instantiate multiple API-specific subclasses on a given operating system. For example, one can compile both CoreMIDI and JACK support on the OS-X operating system by providing the appropriate preprocessor definitions for each. In a run-time situation, one might first attempt to determine whether any JACK ports are available. This can be done by specifying the api argument RtMidi::UNIX_JACK when attempting to create an instance of RtMidiIn or RtMidiOut. If no available ports are found, then an instance of RtMidi with the api argument RtMidi::MACOSX_CORE can be created. Alternately, if no api argument is specified, RtMidi will first look for JACK ports and if none are found, then CoreMIDI ports (in linux, the search order is JACK and then ALSA. In theory, it should also be possible to have separate instances of RtMidi open at the same time with different underlying API support, though this has not been tested. +Support for each MIDI API is encapsulated in specific \ref rtmidi::MidiInApi or \ref rtmidi::MidiOutApi subclasses, making it possible to compile and instantiate multiple API-specific subclasses on a given operating system. For example, one can compile both CoreMIDI and JACK support on the OS-X operating system by providing the appropriate preprocessor definitions for each. In a run-time situation, one might first attempt to determine whether any JACK ports are available. This can be done by specifying the api argument rtmidi::UNIX_JACK when attempting to create an instance of rtmidi::MidiIn or rtmidi::MidiOut. If no available ports are found, then an instance of rtmidi::Midi with the api argument rtmidi::MACOSX_CORE can be created. Alternately, if no api argument is specified, RtMidi will first look for OS provided ports and if none are found, then software demons (currently JACK) ports (in linux, the search order is ALSA and then JACK). + +It is also possible to use all compiled backends simultaneously by passing the special api argument rtmidi::ALL_API to the constructor. In that case rtmidi::midi::getPortList will return a list of all port descriptors from all backends that are available. On the other hand rtmidi::midi::openVirtualPort will open only one port on the first API that is tested in the automatic port selecton as described above. -The static function RtMidi::getCompiledApi() is provided to determine the available compiled API support. The function RtMidi::getCurrentApi() indicates the API selected for a given RtMidi instance. +The static function rtmidi::Midi::getCompiledApi() is provided to determine the available compiled API support. The function rtmidi::Midi::getCurrentApi() indicates the API selected for a given RtMidi instance. + +\note It is recommended to avoid the atomatic API selection for virtual ports. In most cases it is a better option to let the end user choose the API for this port or simply generate an array of ports, each with a different backend API. \section apinotes API Notes @@ -380,7 +445,7 @@ RtMidi is designed to provide a common API across the various supported operatin \subsection linux Linux: -RtMidi for Linux was developed using the Fedora distribution. Two different MIDI APIs are supported on Linux platforms: ALSA and JACK. A decision was made to not include support for the OSS API because the OSS API provides very limited functionality and because ALSA support is now incorporated in the Linux kernel. The ALSA sequencer and JACK APIs allows for virtual software input and output ports. +Two different MIDI APIs are supported on Linux platforms: ALSA and JACK. A decision was made to not include support for the OSS API because the OSS API provides very limited functionality and because ALSA support is incorporated in the Linux kernel. The ALSA sequencer and JACK APIs allows for virtual software input and output ports. \subsection macosx Macintosh OS X (CoreAudio): @@ -392,7 +457,7 @@ The RtMidi JACK support can be compiled on Macintosh OS-X systems, as well as in The \c configure script provides support for the MinGW compiler. -The Windows Multimedia library MIDI calls used in RtMidi do not make use of streaming functionality. Incoming system exclusive messages read by RtMidiIn are limited to a length as defined by the preprocessor definition RT_SYSEX_BUFFER_SIZE (set in RtMidi.cpp). The default value is 1024. There is no such limit for outgoing sysex messages via RtMidiOut. +The Windows Multimedia library MIDI calls used in RtMidi do not make use of streaming functionality. Incoming system exclusive messages read by rtimidi::MidiIn are limited to a length as defined by the preprocessor definition RT_SYSEX_BUFFER_SIZE (set in RtMidi.cpp). The default value is 1024. There is no such limit for outgoing sysex messages via rtmidi::MidiOut. RtMidi was originally developed with Visual C++ version 6.0 but has been tested with Virtual Studio 2010. @@ -426,6 +491,7 @@ In years past, the following people provided bug fixes and improvements: RtMidi: realtime MIDI i/o C++ classes
Copyright (c) 2003-2017 Gary P. Scavone + Forked by Tobias Schlemmer, 2014-2018. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files @@ -452,3 +518,4 @@ In years past, the following people provided bug fixes and improvements: WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + diff --git a/doc/release.txt b/doc/release.txt index c1a2c4ea..4bd52f96 100644 --- a/doc/release.txt +++ b/doc/release.txt @@ -2,6 +2,17 @@ RtMidi - a set of C++ classes that provides a common API for realtime MIDI input By Gary P. Scavone, 2003-2017 (with help from many others!) +Fork by Tobias Schlemmer: (August 2018) +- see git history for complete list of changes +- The old API has been deprecated as there is no way to rely on + consecutive port numbers. Currently, it is still available, but a + compiler warning is generated if applicable +- `__MACOSX_CORE__` has been renamed to `__MACOSX_COREMIDI__` +- The classes of RtMidi now reside in the namespace rtmidi. +- The beginning letters “Rt” are dropped from the names +- For easy adoption of the new interface wrappers for the old API are provided. +- The library uses backend provided port descriptors, now. This provides a more reliable port handling for changing environments. + v.3.0.0: (31 August 2017) - see git history for complete list of changes - new sendMessage() function that does not use std::vector diff --git a/m4/ChangeLog b/m4/ChangeLog new file mode 100644 index 00000000..33ccb9bb --- /dev/null +++ b/m4/ChangeLog @@ -0,0 +1,11 @@ +2018-01-07 gettextize + + * gettext.m4: Upgrade to gettext-0.19.8.1. + * iconv.m4: Upgrade to gettext-0.19.8.1. + * lib-ld.m4: Upgrade to gettext-0.19.8.1. + * lib-link.m4: Upgrade to gettext-0.19.8.1. + * lib-prefix.m4: Upgrade to gettext-0.19.8.1. + * nls.m4: Upgrade to gettext-0.19.8.1. + * po.m4: Upgrade to gettext-0.19.8.1. + * progtest.m4: Upgrade to gettext-0.19.8.1. + diff --git a/m4/alsa.m4 b/m4/alsa.m4 new file mode 100644 index 00000000..e12310df --- /dev/null +++ b/m4/alsa.m4 @@ -0,0 +1,143 @@ +dnl Configure Paths for Alsa +dnl Some modifications by Richard Boulton +dnl Christopher Lansdown +dnl Jaroslav Kysela +dnl Last modification: $Id: alsa.m4,v 1.24 2004/09/15 18:48:07 tiwai Exp $ +dnl AM_PATH_ALSA([MINIMUM-VERSION [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl Test for libasound, and define ALSA_CFLAGS and ALSA_LIBS as appropriate. +dnl enables arguments --with-alsa-prefix= +dnl --with-alsa-enc-prefix= +dnl --disable-alsatest +dnl +dnl For backwards compatibility, if ACTION_IF_NOT_FOUND is not specified, +dnl and the alsa libraries are not found, a fatal AC_MSG_ERROR() will result. +dnl +AC_DEFUN([AM_PATH_ALSA], +[dnl Save the original CFLAGS, LDFLAGS, and LIBS +alsa_save_CFLAGS="$CFLAGS" +alsa_save_LDFLAGS="$LDFLAGS" +alsa_save_LIBS="$LIBS" +alsa_found=yes + +dnl +dnl Get the cflags and libraries for alsa +dnl +AC_ARG_WITH(alsa-prefix, +[ --with-alsa-prefix=PFX Prefix where Alsa library is installed(optional)], +[alsa_prefix="$withval"], [alsa_prefix=""]) + +AC_ARG_WITH(alsa-inc-prefix, +[ --with-alsa-inc-prefix=PFX Prefix where include libraries are (optional)], +[alsa_inc_prefix="$withval"], [alsa_inc_prefix=""]) + +dnl FIXME: this is not yet implemented +AC_ARG_ENABLE(alsatest, +[ --disable-alsatest Do not try to compile and run a test Alsa program], +[enable_alsatest="$enableval"], +[enable_alsatest=yes]) + +dnl Add any special include directories +AC_MSG_CHECKING(for ALSA CFLAGS) +if test "$alsa_inc_prefix" != "" ; then + ALSA_CFLAGS="$ALSA_CFLAGS -I$alsa_inc_prefix" + CFLAGS="$CFLAGS -I$alsa_inc_prefix" +fi +AC_MSG_RESULT($ALSA_CFLAGS) + +dnl add any special lib dirs +AC_MSG_CHECKING(for ALSA LDFLAGS) +if test "$alsa_prefix" != "" ; then + ALSA_LIBS="$ALSA_LIBS -L$alsa_prefix" + LDFLAGS="$LDFLAGS $ALSA_LIBS" +fi + +dnl add the alsa library +ALSA_LIBS="$ALSA_LIBS -lasound -lm -ldl -lpthread" +LIBS="$ALSA_LIBS $LIBS" +AC_MSG_RESULT($ALSA_LIBS) + +dnl Check for a working version of libasound that is of the right version. +if test "x$enable_alsatest" = "xyes"; then +min_alsa_version=ifelse([$1], ,0.1.1,$1) +AC_MSG_CHECKING(for libasound headers version >= $min_alsa_version) +no_alsa="" + alsa_min_major_version=`echo $min_alsa_version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + alsa_min_minor_version=`echo $min_alsa_version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + alsa_min_micro_version=`echo $min_alsa_version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + +AC_LANG_SAVE +AC_LANG_C +AC_TRY_COMPILE([ +#include +], [ +/* ensure backward compatibility */ +#if !defined(SND_LIB_MAJOR) && defined(SOUNDLIB_VERSION_MAJOR) +#define SND_LIB_MAJOR SOUNDLIB_VERSION_MAJOR +#endif +#if !defined(SND_LIB_MINOR) && defined(SOUNDLIB_VERSION_MINOR) +#define SND_LIB_MINOR SOUNDLIB_VERSION_MINOR +#endif +#if !defined(SND_LIB_SUBMINOR) && defined(SOUNDLIB_VERSION_SUBMINOR) +#define SND_LIB_SUBMINOR SOUNDLIB_VERSION_SUBMINOR +#endif + +# if(SND_LIB_MAJOR > $alsa_min_major_version) + exit(0); +# else +# if(SND_LIB_MAJOR < $alsa_min_major_version) +# error not present +# endif + +# if(SND_LIB_MINOR > $alsa_min_minor_version) + exit(0); +# else +# if(SND_LIB_MINOR < $alsa_min_minor_version) +# error not present +# endif + +# if(SND_LIB_SUBMINOR < $alsa_min_micro_version) +# error not present +# endif +# endif +# endif +exit(0); +], + [AC_MSG_RESULT(found.)], + [AC_MSG_RESULT(not present.) + ifelse([$3], , [AC_MSG_ERROR(Sufficiently new version of libasound not found.)]) + alsa_found=no] +) +AC_LANG_RESTORE +fi + +dnl Now that we know that we have the right version, let's see if we have the library and not just the headers. +if test "x$enable_alsatest" = "xyes"; then +AC_CHECK_LIB([asound], [snd_ctl_open],, + [ifelse([$3], , [AC_MSG_ERROR(No linkable libasound was found.)]) + alsa_found=no] +) +fi + +if test "x$alsa_found" = "xyes" ; then + ifelse([$2], , :, [$2]) + LIBS=`echo $LIBS | sed 's/-lasound//g'` + LIBS=`echo $LIBS | sed 's/ //'` + LIBS="-lasound $LIBS" +fi +if test "x$alsa_found" = "xno" ; then + ifelse([$3], , :, [$3]) + CFLAGS="$alsa_save_CFLAGS" + LDFLAGS="$alsa_save_LDFLAGS" + LIBS="$alsa_save_LIBS" + ALSA_CFLAGS="" + ALSA_LIBS="" +fi + +dnl That should be it. Now just export out symbols: +AC_SUBST(ALSA_CFLAGS) +AC_SUBST(ALSA_LIBS) +]) + diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 new file mode 100644 index 00000000..dcabb92a --- /dev/null +++ b/m4/ax_check_compile_flag.m4 @@ -0,0 +1,74 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the current language's compiler +# or gives an error. (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 5 + +AC_DEFUN([AX_CHECK_COMPILE_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl +AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ + ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS + _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" + AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_COMPILE_FLAGS diff --git a/m4/ax_cxx_compile_stdcxx.m4 b/m4/ax_cxx_compile_stdcxx.m4 new file mode 100644 index 00000000..5032bba8 --- /dev/null +++ b/m4/ax_cxx_compile_stdcxx.m4 @@ -0,0 +1,982 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the specified +# version of the C++ standard. If necessary, add switches to CXX and +# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) +# or '14' (for the C++14 standard). +# +# The second argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for an extended mode. +# +# The third argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline support for the specified C++ standard is +# required and that the macro should error out if no mode with that +# support is found. If specified 'optional', then configuration proceeds +# regardless, after defining HAVE_CXX${VERSION} if and only if a +# supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov +# Copyright (c) 2015 Paul Norman +# Copyright (c) 2015 Moritz Klammler +# Copyright (c) 2016 Krzesimir Nowak +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 7 + +dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro +dnl (serial version number 13). + +AX_REQUIRE_DEFINED([AC_MSG_WARN]) +AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl + m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], + [$1], [14], [ax_cxx_compile_alternatives="14 1y"], + [$1], [17], [ax_cxx_compile_alternatives="17 1z"], + [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$2], [], [], + [$2], [ext], [], + [$2], [noext], [], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], + [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], + [$3], [optional], [ax_cxx_compile_cxx$1_required=false], + [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) + AC_LANG_PUSH([C++])dnl + ac_success=no + AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, + ax_cv_cxx_compile_cxx$1, + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [ax_cv_cxx_compile_cxx$1=yes], + [ax_cv_cxx_compile_cxx$1=no])]) + if test x$ax_cv_cxx_compile_cxx$1 = xyes; then + ac_success=yes + fi + + m4_if([$2], [noext], [], [dnl + if test x$ac_success = xno; then + for alternative in ${ax_cxx_compile_alternatives}; do + switch="-std=gnu++${alternative}" + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + fi]) + + m4_if([$2], [ext], [], [dnl + if test x$ac_success = xno; then + dnl HP's aCC needs +std=c++11 according to: + dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf + dnl Cray's crayCC needs "-h std=c++11" + for alternative in ${ax_cxx_compile_alternatives}; do + for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + if test x$ac_success = xyes; then + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx$1_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) + fi + fi + if test x$ac_success = xno; then + HAVE_CXX$1=0 + AC_MSG_NOTICE([No compiler with C++$1 support was found]) + else + HAVE_CXX$1=1 + AC_DEFINE(HAVE_CXX$1,1, + [define if the compiler supports basic C++$1 syntax]) + fi + AC_SUBST(HAVE_CXX$1) + m4_if([$1], [17], [AC_MSG_WARN([C++17 is not yet standardized, so the checks may change in incompatible ways anytime])]) +]) + + +dnl Test body for checking C++11 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 +) + + +dnl Test body for checking C++14 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 +) + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 +) + +dnl Tests for new features in C++11 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ + +// If the compiler admits that it is not ready for C++11, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201103L + +#error "This is not a C++11 compiler" + +#else + +namespace cxx11 +{ + + namespace test_static_assert + { + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + } + + namespace test_final_override + { + + struct Base + { + virtual void f() {} + }; + + struct Derived : public Base + { + virtual void f() override {} + }; + + } + + namespace test_double_right_angle_brackets + { + + template < typename T > + struct check {}; + + typedef check single_type; + typedef check> double_type; + typedef check>> triple_type; + typedef check>>> quadruple_type; + + } + + namespace test_decltype + { + + int + f() + { + int a = 1; + decltype(a) b = 2; + return a + b; + } + + } + + namespace test_type_deduction + { + + template < typename T1, typename T2 > + struct is_same + { + static const bool value = false; + }; + + template < typename T > + struct is_same + { + static const bool value = true; + }; + + template < typename T1, typename T2 > + auto + add(T1 a1, T2 a2) -> decltype(a1 + a2) + { + return a1 + a2; + } + + int + test(const int c, volatile int v) + { + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == false, ""); + auto ac = c; + auto av = v; + auto sumi = ac + av + 'x'; + auto sumf = ac + av + 1.0; + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == true, ""); + return (sumf > 0.0) ? sumi : add(c, v); + } + + } + + namespace test_noexcept + { + + int f() { return 0; } + int g() noexcept { return 0; } + + static_assert(noexcept(f()) == false, ""); + static_assert(noexcept(g()) == true, ""); + + } + + namespace test_constexpr + { + + template < typename CharT > + unsigned long constexpr + strlen_c_r(const CharT *const s, const unsigned long acc) noexcept + { + return *s ? strlen_c_r(s + 1, acc + 1) : acc; + } + + template < typename CharT > + unsigned long constexpr + strlen_c(const CharT *const s) noexcept + { + return strlen_c_r(s, 0UL); + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("1") == 1UL, ""); + static_assert(strlen_c("example") == 7UL, ""); + static_assert(strlen_c("another\0example") == 7UL, ""); + + } + + namespace test_rvalue_references + { + + template < int N > + struct answer + { + static constexpr int value = N; + }; + + answer<1> f(int&) { return answer<1>(); } + answer<2> f(const int&) { return answer<2>(); } + answer<3> f(int&&) { return answer<3>(); } + + void + test() + { + int i = 0; + const int c = 0; + static_assert(decltype(f(i))::value == 1, ""); + static_assert(decltype(f(c))::value == 2, ""); + static_assert(decltype(f(0))::value == 3, ""); + } + + } + + namespace test_uniform_initialization + { + + struct test + { + static const int zero {}; + static const int one {1}; + }; + + static_assert(test::zero == 0, ""); + static_assert(test::one == 1, ""); + + } + + namespace test_lambdas + { + + void + test1() + { + auto lambda1 = [](){}; + auto lambda2 = lambda1; + lambda1(); + lambda2(); + } + + int + test2() + { + auto a = [](int i, int j){ return i + j; }(1, 2); + auto b = []() -> int { return '0'; }(); + auto c = [=](){ return a + b; }(); + auto d = [&](){ return c; }(); + auto e = [a, &b](int x) mutable { + const auto identity = [](int y){ return y; }; + for (auto i = 0; i < a; ++i) + a += b--; + return x + identity(a + b); + }(0); + return a + b + c + d + e; + } + + int + test3() + { + const auto nullary = [](){ return 0; }; + const auto unary = [](int x){ return x; }; + using nullary_t = decltype(nullary); + using unary_t = decltype(unary); + const auto higher1st = [](nullary_t f){ return f(); }; + const auto higher2nd = [unary](nullary_t f1){ + return [unary, f1](unary_t f2){ return f2(unary(f1())); }; + }; + return higher1st(nullary) + higher2nd(nullary)(unary); + } + + } + + namespace test_variadic_templates + { + + template + struct sum; + + template + struct sum + { + static constexpr auto value = N0 + sum::value; + }; + + template <> + struct sum<> + { + static constexpr auto value = 0; + }; + + static_assert(sum<>::value == 0, ""); + static_assert(sum<1>::value == 1, ""); + static_assert(sum<23>::value == 23, ""); + static_assert(sum<1, 2>::value == 3, ""); + static_assert(sum<5, 5, 11>::value == 21, ""); + static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); + + } + + // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function + // because of this. + namespace test_template_alias_sfinae + { + + struct foo {}; + + template + using member = typename T::member_type; + + template + void func(...) {} + + template + void func(member*) {} + + void test(); + + void test() { func(0); } + + } + +} // namespace cxx11 + +#endif // __cplusplus >= 201103L + +]]) + + +dnl Tests for new features in C++14 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ + +// If the compiler admits that it is not ready for C++14, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201402L + +#error "This is not a C++14 compiler" + +#else + +namespace cxx14 +{ + + namespace test_polymorphic_lambdas + { + + int + test() + { + const auto lambda = [](auto&&... args){ + const auto istiny = [](auto x){ + return (sizeof(x) == 1UL) ? 1 : 0; + }; + const int aretiny[] = { istiny(args)... }; + return aretiny[0]; + }; + return lambda(1, 1L, 1.0f, '1'); + } + + } + + namespace test_binary_literals + { + + constexpr auto ivii = 0b0000000000101010; + static_assert(ivii == 42, "wrong value"); + + } + + namespace test_generalized_constexpr + { + + template < typename CharT > + constexpr unsigned long + strlen_c(const CharT *const s) noexcept + { + auto length = 0UL; + for (auto p = s; *p; ++p) + ++length; + return length; + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("x") == 1UL, ""); + static_assert(strlen_c("test") == 4UL, ""); + static_assert(strlen_c("another\0test") == 7UL, ""); + + } + + namespace test_lambda_init_capture + { + + int + test() + { + auto x = 0; + const auto lambda1 = [a = x](int b){ return a + b; }; + const auto lambda2 = [a = lambda1(x)](){ return a; }; + return lambda2(); + } + + } + + namespace test_digit_separators + { + + constexpr auto ten_million = 100'000'000; + static_assert(ten_million == 100000000, ""); + + } + + namespace test_return_type_deduction + { + + auto f(int& x) { return x; } + decltype(auto) g(int& x) { return x; } + + template < typename T1, typename T2 > + struct is_same + { + static constexpr auto value = false; + }; + + template < typename T > + struct is_same + { + static constexpr auto value = true; + }; + + int + test() + { + auto x = 0; + static_assert(is_same::value, ""); + static_assert(is_same::value, ""); + return x; + } + + } + +} // namespace cxx14 + +#endif // __cplusplus >= 201402L + +]]) + + +dnl Tests for new features in C++17 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ + +// If the compiler admits that it is not ready for C++17, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus <= 201402L + +#error "This is not a C++17 compiler" + +#else + +#if defined(__clang__) + #define REALLY_CLANG +#else + #if defined(__GNUC__) + #define REALLY_GCC + #endif +#endif + +#include +#include +#include + +namespace cxx17 +{ + +#if !defined(REALLY_CLANG) + namespace test_constexpr_lambdas + { + + // TODO: test it with clang++ from git + + constexpr int foo = [](){return 42;}(); + + } +#endif // !defined(REALLY_CLANG) + + namespace test::nested_namespace::definitions + { + + } + + namespace test_fold_expression + { + + template + int multiply(Args... args) + { + return (args * ... * 1); + } + + template + bool all(Args... args) + { + return (args && ...); + } + + } + + namespace test_extended_static_assert + { + + static_assert (true); + + } + + namespace test_auto_brace_init_list + { + + auto foo = {5}; + auto bar {5}; + + static_assert(std::is_same, decltype(foo)>::value); + static_assert(std::is_same::value); + } + + namespace test_typename_in_template_template_parameter + { + + template typename X> struct D; + + } + + namespace test_fallthrough_nodiscard_maybe_unused_attributes + { + + int f1() + { + return 42; + } + + [[nodiscard]] int f2() + { + [[maybe_unused]] auto unused = f1(); + + switch (f1()) + { + case 17: + f1(); + [[fallthrough]]; + case 42: + f1(); + } + return f1(); + } + + } + + namespace test_extended_aggregate_initialization + { + + struct base1 + { + int b1, b2 = 42; + }; + + struct base2 + { + base2() { + b3 = 42; + } + int b3; + }; + + struct derived : base1, base2 + { + int d; + }; + + derived d1 {{1, 2}, {}, 4}; // full initialization + derived d2 {{}, {}, 4}; // value-initialized bases + + } + + namespace test_general_range_based_for_loop + { + + struct iter + { + int i; + + int& operator* () + { + return i; + } + + const int& operator* () const + { + return i; + } + + iter& operator++() + { + ++i; + return *this; + } + }; + + struct sentinel + { + int i; + }; + + bool operator== (const iter& i, const sentinel& s) + { + return i.i == s.i; + } + + bool operator!= (const iter& i, const sentinel& s) + { + return !(i == s); + } + + struct range + { + iter begin() const + { + return {0}; + } + + sentinel end() const + { + return {5}; + } + }; + + void f() + { + range r {}; + + for (auto i : r) + { + [[maybe_unused]] auto v = i; + } + } + + } + + namespace test_lambda_capture_asterisk_this_by_value + { + + struct t + { + int i; + int foo() + { + return [*this]() + { + return i; + }(); + } + }; + + } + + namespace test_enum_class_construction + { + + enum class byte : unsigned char + {}; + + byte foo {42}; + + } + + namespace test_constexpr_if + { + + template + int f () + { + if constexpr(cond) + { + return 13; + } + else + { + return 42; + } + } + + } + + namespace test_selection_statement_with_initializer + { + + int f() + { + return 13; + } + + int f2() + { + if (auto i = f(); i > 0) + { + return 3; + } + + switch (auto i = f(); i + 4) + { + case 17: + return 2; + + default: + return 1; + } + } + + } + +#if !defined(REALLY_CLANG) + namespace test_template_argument_deduction_for_class_templates + { + + // TODO: test it with clang++ from git + + template + struct pair + { + pair (T1 p1, T2 p2) + : m1 {p1}, + m2 {p2} + {} + + T1 m1; + T2 m2; + }; + + void f() + { + [[maybe_unused]] auto p = pair{13, 42u}; + } + + } +#endif // !defined(REALLY_CLANG) + + namespace test_non_type_auto_template_parameters + { + + template + struct B + {}; + + B<5> b1; + B<'a'> b2; + + } + +#if !defined(REALLY_CLANG) + namespace test_structured_bindings + { + + // TODO: test it with clang++ from git + + int arr[2] = { 1, 2 }; + std::pair pr = { 1, 2 }; + + auto f1() -> int(&)[2] + { + return arr; + } + + auto f2() -> std::pair& + { + return pr; + } + + struct S + { + int x1 : 2; + volatile double y1; + }; + + S f3() + { + return {}; + } + + auto [ x1, y1 ] = f1(); + auto& [ xr1, yr1 ] = f1(); + auto [ x2, y2 ] = f2(); + auto& [ xr2, yr2 ] = f2(); + const auto [ x3, y3 ] = f3(); + + } +#endif // !defined(REALLY_CLANG) + +#if !defined(REALLY_CLANG) + namespace test_exception_spec_type_system + { + + // TODO: test it with clang++ from git + + struct Good {}; + struct Bad {}; + + void g1() noexcept; + void g2(); + + template + Bad + f(T*, T*); + + template + Good + f(T1*, T2*); + + static_assert (std::is_same_v); + + } +#endif // !defined(REALLY_CLANG) + + namespace test_inline_variables + { + + template void f(T) + {} + + template inline T g(T) + { + return T{}; + } + + template<> inline void f<>(int) + {} + + template<> int g<>(int) + { + return 5; + } + + } + +} // namespace cxx17 + +#endif // __cplusplus <= 201402L + +]]) diff --git a/m4/ax_require_defined.m4 b/m4/ax_require_defined.m4 new file mode 100644 index 00000000..17c3eab7 --- /dev/null +++ b/m4/ax_require_defined.m4 @@ -0,0 +1,37 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_require_defined.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_REQUIRE_DEFINED(MACRO) +# +# DESCRIPTION +# +# AX_REQUIRE_DEFINED is a simple helper for making sure other macros have +# been defined and thus are available for use. This avoids random issues +# where a macro isn't expanded. Instead the configure script emits a +# non-fatal: +# +# ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found +# +# It's like AC_REQUIRE except it doesn't expand the required macro. +# +# Here's an example: +# +# AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG]) +# +# LICENSE +# +# Copyright (c) 2014 Mike Frysinger +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 2 + +AC_DEFUN([AX_REQUIRE_DEFINED], [dnl + m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])]) +])dnl AX_REQUIRE_DEFINED diff --git a/m4/codeset.m4 b/m4/codeset.m4 new file mode 100644 index 00000000..a53c0426 --- /dev/null +++ b/m4/codeset.m4 @@ -0,0 +1,21 @@ +# codeset.m4 serial 4 (gettext-0.18) +dnl Copyright (C) 2000-2002, 2006, 2008-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +AC_DEFUN([AM_LANGINFO_CODESET], +[ + AC_CACHE_CHECK([for nl_langinfo and CODESET], [am_cv_langinfo_codeset], + [AC_TRY_LINK([#include ], + [char* cs = nl_langinfo(CODESET); return !cs;], + [am_cv_langinfo_codeset=yes], + [am_cv_langinfo_codeset=no]) + ]) + if test $am_cv_langinfo_codeset = yes; then + AC_DEFINE([HAVE_LANGINFO_CODESET], [1], + [Define if you have and nl_langinfo(CODESET).]) + fi +]) diff --git a/m4/fcntl-o.m4 b/m4/fcntl-o.m4 new file mode 100644 index 00000000..d416a61c --- /dev/null +++ b/m4/fcntl-o.m4 @@ -0,0 +1,81 @@ +# fcntl-o.m4 serial 1 +dnl Copyright (C) 2006, 2009-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl Written by Paul Eggert. + +# Test whether the flags O_NOATIME and O_NOFOLLOW actually work. +# Define HAVE_WORKING_O_NOATIME to 1 if O_NOATIME works, or to 0 otherwise. +# Define HAVE_WORKING_O_NOFOLLOW to 1 if O_NOFOLLOW works, or to 0 otherwise. +AC_DEFUN([gl_FCNTL_O_FLAGS], +[ + dnl Persuade glibc to define O_NOATIME and O_NOFOLLOW. + AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) + AC_CACHE_CHECK([for working fcntl.h], [gl_cv_header_working_fcntl_h], + [AC_RUN_IFELSE( + [AC_LANG_PROGRAM( + [[#include + #include + #include + #include + #ifndef O_NOATIME + #define O_NOATIME 0 + #endif + #ifndef O_NOFOLLOW + #define O_NOFOLLOW 0 + #endif + static int const constants[] = + { + O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC, O_APPEND, + O_NONBLOCK, O_SYNC, O_ACCMODE, O_RDONLY, O_RDWR, O_WRONLY + }; + ]], + [[ + int status = !constants; + { + static char const sym[] = "conftest.sym"; + if (symlink (".", sym) != 0 + || close (open (sym, O_RDONLY | O_NOFOLLOW)) == 0) + status |= 32; + unlink (sym); + } + { + static char const file[] = "confdefs.h"; + int fd = open (file, O_RDONLY | O_NOATIME); + char c; + struct stat st0, st1; + if (fd < 0 + || fstat (fd, &st0) != 0 + || sleep (1) != 0 + || read (fd, &c, 1) != 1 + || close (fd) != 0 + || stat (file, &st1) != 0 + || st0.st_atime != st1.st_atime) + status |= 64; + } + return status;]])], + [gl_cv_header_working_fcntl_h=yes], + [case $? in #( + 32) gl_cv_header_working_fcntl_h='no (bad O_NOFOLLOW)';; #( + 64) gl_cv_header_working_fcntl_h='no (bad O_NOATIME)';; #( + 96) gl_cv_header_working_fcntl_h='no (bad O_NOATIME, O_NOFOLLOW)';; #( + *) gl_cv_header_working_fcntl_h='no';; + esac], + [gl_cv_header_working_fcntl_h=cross-compiling])]) + + case $gl_cv_header_working_fcntl_h in #( + *O_NOATIME* | no | cross-compiling) ac_val=0;; #( + *) ac_val=1;; + esac + AC_DEFINE_UNQUOTED([HAVE_WORKING_O_NOATIME], [$ac_val], + [Define to 1 if O_NOATIME works.]) + + case $gl_cv_header_working_fcntl_h in #( + *O_NOFOLLOW* | no | cross-compiling) ac_val=0;; #( + *) ac_val=1;; + esac + AC_DEFINE_UNQUOTED([HAVE_WORKING_O_NOFOLLOW], [$ac_val], + [Define to 1 if O_NOFOLLOW works.]) +]) diff --git a/m4/gettext.m4 b/m4/gettext.m4 new file mode 100644 index 00000000..eef5073b --- /dev/null +++ b/m4/gettext.m4 @@ -0,0 +1,420 @@ +# gettext.m4 serial 68 (gettext-0.19.8) +dnl Copyright (C) 1995-2014, 2016 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. +dnl +dnl This file can be used in projects which are not available under +dnl the GNU General Public License or the GNU Library General Public +dnl License but which still want to provide support for the GNU gettext +dnl functionality. +dnl Please note that the actual code of the GNU gettext library is covered +dnl by the GNU Library General Public License, and the rest of the GNU +dnl gettext package is covered by the GNU General Public License. +dnl They are *not* in the public domain. + +dnl Authors: +dnl Ulrich Drepper , 1995-2000. +dnl Bruno Haible , 2000-2006, 2008-2010. + +dnl Macro to add for using GNU gettext. + +dnl Usage: AM_GNU_GETTEXT([INTLSYMBOL], [NEEDSYMBOL], [INTLDIR]). +dnl INTLSYMBOL can be one of 'external', 'no-libtool', 'use-libtool'. The +dnl default (if it is not specified or empty) is 'no-libtool'. +dnl INTLSYMBOL should be 'external' for packages with no intl directory, +dnl and 'no-libtool' or 'use-libtool' for packages with an intl directory. +dnl If INTLSYMBOL is 'use-libtool', then a libtool library +dnl $(top_builddir)/intl/libintl.la will be created (shared and/or static, +dnl depending on --{enable,disable}-{shared,static} and on the presence of +dnl AM-DISABLE-SHARED). If INTLSYMBOL is 'no-libtool', a static library +dnl $(top_builddir)/intl/libintl.a will be created. +dnl If NEEDSYMBOL is specified and is 'need-ngettext', then GNU gettext +dnl implementations (in libc or libintl) without the ngettext() function +dnl will be ignored. If NEEDSYMBOL is specified and is +dnl 'need-formatstring-macros', then GNU gettext implementations that don't +dnl support the ISO C 99 formatstring macros will be ignored. +dnl INTLDIR is used to find the intl libraries. If empty, +dnl the value '$(top_builddir)/intl/' is used. +dnl +dnl The result of the configuration is one of three cases: +dnl 1) GNU gettext, as included in the intl subdirectory, will be compiled +dnl and used. +dnl Catalog format: GNU --> install in $(datadir) +dnl Catalog extension: .mo after installation, .gmo in source tree +dnl 2) GNU gettext has been found in the system's C library. +dnl Catalog format: GNU --> install in $(datadir) +dnl Catalog extension: .mo after installation, .gmo in source tree +dnl 3) No internationalization, always use English msgid. +dnl Catalog format: none +dnl Catalog extension: none +dnl If INTLSYMBOL is 'external', only cases 2 and 3 can occur. +dnl The use of .gmo is historical (it was needed to avoid overwriting the +dnl GNU format catalogs when building on a platform with an X/Open gettext), +dnl but we keep it in order not to force irrelevant filename changes on the +dnl maintainers. +dnl +AC_DEFUN([AM_GNU_GETTEXT], +[ + dnl Argument checking. + ifelse([$1], [], , [ifelse([$1], [external], , [ifelse([$1], [no-libtool], , [ifelse([$1], [use-libtool], , + [errprint([ERROR: invalid first argument to AM_GNU_GETTEXT +])])])])]) + ifelse(ifelse([$1], [], [old])[]ifelse([$1], [no-libtool], [old]), [old], + [AC_DIAGNOSE([obsolete], [Use of AM_GNU_GETTEXT without [external] argument is deprecated.])]) + ifelse([$2], [], , [ifelse([$2], [need-ngettext], , [ifelse([$2], [need-formatstring-macros], , + [errprint([ERROR: invalid second argument to AM_GNU_GETTEXT +])])])]) + define([gt_included_intl], + ifelse([$1], [external], + ifdef([AM_GNU_GETTEXT_][INTL_SUBDIR], [yes], [no]), + [yes])) + define([gt_libtool_suffix_prefix], ifelse([$1], [use-libtool], [l], [])) + gt_NEEDS_INIT + AM_GNU_GETTEXT_NEED([$2]) + + AC_REQUIRE([AM_PO_SUBDIRS])dnl + ifelse(gt_included_intl, yes, [ + AC_REQUIRE([AM_INTL_SUBDIR])dnl + ]) + + dnl Prerequisites of AC_LIB_LINKFLAGS_BODY. + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + AC_REQUIRE([AC_LIB_RPATH]) + + dnl Sometimes libintl requires libiconv, so first search for libiconv. + dnl Ideally we would do this search only after the + dnl if test "$USE_NLS" = "yes"; then + dnl if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" != "yes"; }; then + dnl tests. But if configure.in invokes AM_ICONV after AM_GNU_GETTEXT + dnl the configure script would need to contain the same shell code + dnl again, outside any 'if'. There are two solutions: + dnl - Invoke AM_ICONV_LINKFLAGS_BODY here, outside any 'if'. + dnl - Control the expansions in more detail using AC_PROVIDE_IFELSE. + dnl Since AC_PROVIDE_IFELSE is only in autoconf >= 2.52 and not + dnl documented, we avoid it. + ifelse(gt_included_intl, yes, , [ + AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY]) + ]) + + dnl Sometimes, on Mac OS X, libintl requires linking with CoreFoundation. + gt_INTL_MACOSX + + dnl Set USE_NLS. + AC_REQUIRE([AM_NLS]) + + ifelse(gt_included_intl, yes, [ + BUILD_INCLUDED_LIBINTL=no + USE_INCLUDED_LIBINTL=no + ]) + LIBINTL= + LTLIBINTL= + POSUB= + + dnl Add a version number to the cache macros. + case " $gt_needs " in + *" need-formatstring-macros "*) gt_api_version=3 ;; + *" need-ngettext "*) gt_api_version=2 ;; + *) gt_api_version=1 ;; + esac + gt_func_gnugettext_libc="gt_cv_func_gnugettext${gt_api_version}_libc" + gt_func_gnugettext_libintl="gt_cv_func_gnugettext${gt_api_version}_libintl" + + dnl If we use NLS figure out what method + if test "$USE_NLS" = "yes"; then + gt_use_preinstalled_gnugettext=no + ifelse(gt_included_intl, yes, [ + AC_MSG_CHECKING([whether included gettext is requested]) + AC_ARG_WITH([included-gettext], + [ --with-included-gettext use the GNU gettext library included here], + nls_cv_force_use_gnu_gettext=$withval, + nls_cv_force_use_gnu_gettext=no) + AC_MSG_RESULT([$nls_cv_force_use_gnu_gettext]) + + nls_cv_use_gnu_gettext="$nls_cv_force_use_gnu_gettext" + if test "$nls_cv_force_use_gnu_gettext" != "yes"; then + ]) + dnl User does not insist on using GNU NLS library. Figure out what + dnl to use. If GNU gettext is available we use this. Else we have + dnl to fall back to GNU NLS library. + + if test $gt_api_version -ge 3; then + gt_revision_test_code=' +#ifndef __GNU_GETTEXT_SUPPORTED_REVISION +#define __GNU_GETTEXT_SUPPORTED_REVISION(major) ((major) == 0 ? 0 : -1) +#endif +changequote(,)dnl +typedef int array [2 * (__GNU_GETTEXT_SUPPORTED_REVISION(0) >= 1) - 1]; +changequote([,])dnl +' + else + gt_revision_test_code= + fi + if test $gt_api_version -ge 2; then + gt_expression_test_code=' + * ngettext ("", "", 0)' + else + gt_expression_test_code= + fi + + AC_CACHE_CHECK([for GNU gettext in libc], [$gt_func_gnugettext_libc], + [AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include +#ifndef __GNU_GETTEXT_SUPPORTED_REVISION +extern int _nl_msg_cat_cntr; +extern int *_nl_domain_bindings; +#define __GNU_GETTEXT_SYMBOL_EXPRESSION (_nl_msg_cat_cntr + *_nl_domain_bindings) +#else +#define __GNU_GETTEXT_SYMBOL_EXPRESSION 0 +#endif +$gt_revision_test_code + ]], + [[ +bindtextdomain ("", ""); +return * gettext ("")$gt_expression_test_code + __GNU_GETTEXT_SYMBOL_EXPRESSION + ]])], + [eval "$gt_func_gnugettext_libc=yes"], + [eval "$gt_func_gnugettext_libc=no"])]) + + if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" != "yes"; }; then + dnl Sometimes libintl requires libiconv, so first search for libiconv. + ifelse(gt_included_intl, yes, , [ + AM_ICONV_LINK + ]) + dnl Search for libintl and define LIBINTL, LTLIBINTL and INCINTL + dnl accordingly. Don't use AC_LIB_LINKFLAGS_BODY([intl],[iconv]) + dnl because that would add "-liconv" to LIBINTL and LTLIBINTL + dnl even if libiconv doesn't exist. + AC_LIB_LINKFLAGS_BODY([intl]) + AC_CACHE_CHECK([for GNU gettext in libintl], + [$gt_func_gnugettext_libintl], + [gt_save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $INCINTL" + gt_save_LIBS="$LIBS" + LIBS="$LIBS $LIBINTL" + dnl Now see whether libintl exists and does not depend on libiconv. + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include +#ifndef __GNU_GETTEXT_SUPPORTED_REVISION +extern int _nl_msg_cat_cntr; +extern +#ifdef __cplusplus +"C" +#endif +const char *_nl_expand_alias (const char *); +#define __GNU_GETTEXT_SYMBOL_EXPRESSION (_nl_msg_cat_cntr + *_nl_expand_alias ("")) +#else +#define __GNU_GETTEXT_SYMBOL_EXPRESSION 0 +#endif +$gt_revision_test_code + ]], + [[ +bindtextdomain ("", ""); +return * gettext ("")$gt_expression_test_code + __GNU_GETTEXT_SYMBOL_EXPRESSION + ]])], + [eval "$gt_func_gnugettext_libintl=yes"], + [eval "$gt_func_gnugettext_libintl=no"]) + dnl Now see whether libintl exists and depends on libiconv. + if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" != yes; } && test -n "$LIBICONV"; then + LIBS="$LIBS $LIBICONV" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include +#ifndef __GNU_GETTEXT_SUPPORTED_REVISION +extern int _nl_msg_cat_cntr; +extern +#ifdef __cplusplus +"C" +#endif +const char *_nl_expand_alias (const char *); +#define __GNU_GETTEXT_SYMBOL_EXPRESSION (_nl_msg_cat_cntr + *_nl_expand_alias ("")) +#else +#define __GNU_GETTEXT_SYMBOL_EXPRESSION 0 +#endif +$gt_revision_test_code + ]], + [[ +bindtextdomain ("", ""); +return * gettext ("")$gt_expression_test_code + __GNU_GETTEXT_SYMBOL_EXPRESSION + ]])], + [LIBINTL="$LIBINTL $LIBICONV" + LTLIBINTL="$LTLIBINTL $LTLIBICONV" + eval "$gt_func_gnugettext_libintl=yes" + ]) + fi + CPPFLAGS="$gt_save_CPPFLAGS" + LIBS="$gt_save_LIBS"]) + fi + + dnl If an already present or preinstalled GNU gettext() is found, + dnl use it. But if this macro is used in GNU gettext, and GNU + dnl gettext is already preinstalled in libintl, we update this + dnl libintl. (Cf. the install rule in intl/Makefile.in.) + if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" = "yes"; } \ + || { { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; } \ + && test "$PACKAGE" != gettext-runtime \ + && test "$PACKAGE" != gettext-tools; }; then + gt_use_preinstalled_gnugettext=yes + else + dnl Reset the values set by searching for libintl. + LIBINTL= + LTLIBINTL= + INCINTL= + fi + + ifelse(gt_included_intl, yes, [ + if test "$gt_use_preinstalled_gnugettext" != "yes"; then + dnl GNU gettext is not found in the C library. + dnl Fall back on included GNU gettext library. + nls_cv_use_gnu_gettext=yes + fi + fi + + if test "$nls_cv_use_gnu_gettext" = "yes"; then + dnl Mark actions used to generate GNU NLS library. + BUILD_INCLUDED_LIBINTL=yes + USE_INCLUDED_LIBINTL=yes + LIBINTL="ifelse([$3],[],\${top_builddir}/intl,[$3])/libintl.[]gt_libtool_suffix_prefix[]a $LIBICONV $LIBTHREAD" + LTLIBINTL="ifelse([$3],[],\${top_builddir}/intl,[$3])/libintl.[]gt_libtool_suffix_prefix[]a $LTLIBICONV $LTLIBTHREAD" + LIBS=`echo " $LIBS " | sed -e 's/ -lintl / /' -e 's/^ //' -e 's/ $//'` + fi + + CATOBJEXT= + if test "$gt_use_preinstalled_gnugettext" = "yes" \ + || test "$nls_cv_use_gnu_gettext" = "yes"; then + dnl Mark actions to use GNU gettext tools. + CATOBJEXT=.gmo + fi + ]) + + if test -n "$INTL_MACOSX_LIBS"; then + if test "$gt_use_preinstalled_gnugettext" = "yes" \ + || test "$nls_cv_use_gnu_gettext" = "yes"; then + dnl Some extra flags are needed during linking. + LIBINTL="$LIBINTL $INTL_MACOSX_LIBS" + LTLIBINTL="$LTLIBINTL $INTL_MACOSX_LIBS" + fi + fi + + if test "$gt_use_preinstalled_gnugettext" = "yes" \ + || test "$nls_cv_use_gnu_gettext" = "yes"; then + AC_DEFINE([ENABLE_NLS], [1], + [Define to 1 if translation of program messages to the user's native language + is requested.]) + else + USE_NLS=no + fi + fi + + AC_MSG_CHECKING([whether to use NLS]) + AC_MSG_RESULT([$USE_NLS]) + if test "$USE_NLS" = "yes"; then + AC_MSG_CHECKING([where the gettext function comes from]) + if test "$gt_use_preinstalled_gnugettext" = "yes"; then + if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; }; then + gt_source="external libintl" + else + gt_source="libc" + fi + else + gt_source="included intl directory" + fi + AC_MSG_RESULT([$gt_source]) + fi + + if test "$USE_NLS" = "yes"; then + + if test "$gt_use_preinstalled_gnugettext" = "yes"; then + if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; }; then + AC_MSG_CHECKING([how to link with libintl]) + AC_MSG_RESULT([$LIBINTL]) + AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCINTL]) + fi + + dnl For backward compatibility. Some packages may be using this. + AC_DEFINE([HAVE_GETTEXT], [1], + [Define if the GNU gettext() function is already present or preinstalled.]) + AC_DEFINE([HAVE_DCGETTEXT], [1], + [Define if the GNU dcgettext() function is already present or preinstalled.]) + fi + + dnl We need to process the po/ directory. + POSUB=po + fi + + ifelse(gt_included_intl, yes, [ + dnl If this is used in GNU gettext we have to set BUILD_INCLUDED_LIBINTL + dnl to 'yes' because some of the testsuite requires it. + if test "$PACKAGE" = gettext-runtime || test "$PACKAGE" = gettext-tools; then + BUILD_INCLUDED_LIBINTL=yes + fi + + dnl Make all variables we use known to autoconf. + AC_SUBST([BUILD_INCLUDED_LIBINTL]) + AC_SUBST([USE_INCLUDED_LIBINTL]) + AC_SUBST([CATOBJEXT]) + + dnl For backward compatibility. Some configure.ins may be using this. + nls_cv_header_intl= + nls_cv_header_libgt= + + dnl For backward compatibility. Some Makefiles may be using this. + DATADIRNAME=share + AC_SUBST([DATADIRNAME]) + + dnl For backward compatibility. Some Makefiles may be using this. + INSTOBJEXT=.mo + AC_SUBST([INSTOBJEXT]) + + dnl For backward compatibility. Some Makefiles may be using this. + GENCAT=gencat + AC_SUBST([GENCAT]) + + dnl For backward compatibility. Some Makefiles may be using this. + INTLOBJS= + if test "$USE_INCLUDED_LIBINTL" = yes; then + INTLOBJS="\$(GETTOBJS)" + fi + AC_SUBST([INTLOBJS]) + + dnl Enable libtool support if the surrounding package wishes it. + INTL_LIBTOOL_SUFFIX_PREFIX=gt_libtool_suffix_prefix + AC_SUBST([INTL_LIBTOOL_SUFFIX_PREFIX]) + ]) + + dnl For backward compatibility. Some Makefiles may be using this. + INTLLIBS="$LIBINTL" + AC_SUBST([INTLLIBS]) + + dnl Make all documented variables known to autoconf. + AC_SUBST([LIBINTL]) + AC_SUBST([LTLIBINTL]) + AC_SUBST([POSUB]) +]) + + +dnl gt_NEEDS_INIT ensures that the gt_needs variable is initialized. +m4_define([gt_NEEDS_INIT], +[ + m4_divert_text([DEFAULTS], [gt_needs=]) + m4_define([gt_NEEDS_INIT], []) +]) + + +dnl Usage: AM_GNU_GETTEXT_NEED([NEEDSYMBOL]) +AC_DEFUN([AM_GNU_GETTEXT_NEED], +[ + m4_divert_text([INIT_PREPARE], [gt_needs="$gt_needs $1"]) +]) + + +dnl Usage: AM_GNU_GETTEXT_VERSION([gettext-version]) +AC_DEFUN([AM_GNU_GETTEXT_VERSION], []) + + +dnl Usage: AM_GNU_GETTEXT_REQUIRE_VERSION([gettext-version]) +AC_DEFUN([AM_GNU_GETTEXT_REQUIRE_VERSION], []) diff --git a/m4/glibc2.m4 b/m4/glibc2.m4 new file mode 100644 index 00000000..f148c12c --- /dev/null +++ b/m4/glibc2.m4 @@ -0,0 +1,30 @@ +# glibc2.m4 serial 2 +dnl Copyright (C) 2000-2002, 2004, 2008-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +# Test for the GNU C Library, version 2.0 or newer. +# From Bruno Haible. + +AC_DEFUN([gt_GLIBC2], + [ + AC_CACHE_CHECK([whether we are using the GNU C Library 2 or newer], + [ac_cv_gnu_library_2], + [AC_EGREP_CPP([Lucky GNU user], + [ +#include +#ifdef __GNU_LIBRARY__ + #if (__GLIBC__ >= 2) + Lucky GNU user + #endif +#endif + ], + [ac_cv_gnu_library_2=yes], + [ac_cv_gnu_library_2=no]) + ] + ) + AC_SUBST([GLIBC2]) + GLIBC2="$ac_cv_gnu_library_2" + ] +) diff --git a/m4/glibc21.m4 b/m4/glibc21.m4 new file mode 100644 index 00000000..68ada9d4 --- /dev/null +++ b/m4/glibc21.m4 @@ -0,0 +1,30 @@ +# glibc21.m4 serial 4 +dnl Copyright (C) 2000-2002, 2004, 2008-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +# Test for the GNU C Library, version 2.1 or newer. +# From Bruno Haible. + +AC_DEFUN([gl_GLIBC21], + [ + AC_CACHE_CHECK([whether we are using the GNU C Library 2.1 or newer], + [ac_cv_gnu_library_2_1], + [AC_EGREP_CPP([Lucky GNU user], + [ +#include +#ifdef __GNU_LIBRARY__ + #if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) || (__GLIBC__ > 2) + Lucky GNU user + #endif +#endif + ], + [ac_cv_gnu_library_2_1=yes], + [ac_cv_gnu_library_2_1=no]) + ] + ) + AC_SUBST([GLIBC21]) + GLIBC21="$ac_cv_gnu_library_2_1" + ] +) diff --git a/m4/iconv.m4 b/m4/iconv.m4 new file mode 100644 index 00000000..aa159c53 --- /dev/null +++ b/m4/iconv.m4 @@ -0,0 +1,271 @@ +# iconv.m4 serial 19 (gettext-0.18.2) +dnl Copyright (C) 2000-2002, 2007-2014, 2016 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +AC_DEFUN([AM_ICONV_LINKFLAGS_BODY], +[ + dnl Prerequisites of AC_LIB_LINKFLAGS_BODY. + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + AC_REQUIRE([AC_LIB_RPATH]) + + dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV + dnl accordingly. + AC_LIB_LINKFLAGS_BODY([iconv]) +]) + +AC_DEFUN([AM_ICONV_LINK], +[ + dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and + dnl those with the standalone portable GNU libiconv installed). + AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles + + dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV + dnl accordingly. + AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY]) + + dnl Add $INCICONV to CPPFLAGS before performing the following checks, + dnl because if the user has installed libiconv and not disabled its use + dnl via --without-libiconv-prefix, he wants to use it. The first + dnl AC_LINK_IFELSE will then fail, the second AC_LINK_IFELSE will succeed. + am_save_CPPFLAGS="$CPPFLAGS" + AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCICONV]) + + AC_CACHE_CHECK([for iconv], [am_cv_func_iconv], [ + am_cv_func_iconv="no, consider installing GNU libiconv" + am_cv_lib_iconv=no + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include +#include + ]], + [[iconv_t cd = iconv_open("",""); + iconv(cd,NULL,NULL,NULL,NULL); + iconv_close(cd);]])], + [am_cv_func_iconv=yes]) + if test "$am_cv_func_iconv" != yes; then + am_save_LIBS="$LIBS" + LIBS="$LIBS $LIBICONV" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include +#include + ]], + [[iconv_t cd = iconv_open("",""); + iconv(cd,NULL,NULL,NULL,NULL); + iconv_close(cd);]])], + [am_cv_lib_iconv=yes] + [am_cv_func_iconv=yes]) + LIBS="$am_save_LIBS" + fi + ]) + if test "$am_cv_func_iconv" = yes; then + AC_CACHE_CHECK([for working iconv], [am_cv_func_iconv_works], [ + dnl This tests against bugs in AIX 5.1, AIX 6.1..7.1, HP-UX 11.11, + dnl Solaris 10. + am_save_LIBS="$LIBS" + if test $am_cv_lib_iconv = yes; then + LIBS="$LIBS $LIBICONV" + fi + am_cv_func_iconv_works=no + for ac_iconv_const in '' 'const'; do + AC_RUN_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include +#include + +#ifndef ICONV_CONST +# define ICONV_CONST $ac_iconv_const +#endif + ]], + [[int result = 0; + /* Test against AIX 5.1 bug: Failures are not distinguishable from successful + returns. */ + { + iconv_t cd_utf8_to_88591 = iconv_open ("ISO8859-1", "UTF-8"); + if (cd_utf8_to_88591 != (iconv_t)(-1)) + { + static ICONV_CONST char input[] = "\342\202\254"; /* EURO SIGN */ + char buf[10]; + ICONV_CONST char *inptr = input; + size_t inbytesleft = strlen (input); + char *outptr = buf; + size_t outbytesleft = sizeof (buf); + size_t res = iconv (cd_utf8_to_88591, + &inptr, &inbytesleft, + &outptr, &outbytesleft); + if (res == 0) + result |= 1; + iconv_close (cd_utf8_to_88591); + } + } + /* Test against Solaris 10 bug: Failures are not distinguishable from + successful returns. */ + { + iconv_t cd_ascii_to_88591 = iconv_open ("ISO8859-1", "646"); + if (cd_ascii_to_88591 != (iconv_t)(-1)) + { + static ICONV_CONST char input[] = "\263"; + char buf[10]; + ICONV_CONST char *inptr = input; + size_t inbytesleft = strlen (input); + char *outptr = buf; + size_t outbytesleft = sizeof (buf); + size_t res = iconv (cd_ascii_to_88591, + &inptr, &inbytesleft, + &outptr, &outbytesleft); + if (res == 0) + result |= 2; + iconv_close (cd_ascii_to_88591); + } + } + /* Test against AIX 6.1..7.1 bug: Buffer overrun. */ + { + iconv_t cd_88591_to_utf8 = iconv_open ("UTF-8", "ISO-8859-1"); + if (cd_88591_to_utf8 != (iconv_t)(-1)) + { + static ICONV_CONST char input[] = "\304"; + static char buf[2] = { (char)0xDE, (char)0xAD }; + ICONV_CONST char *inptr = input; + size_t inbytesleft = 1; + char *outptr = buf; + size_t outbytesleft = 1; + size_t res = iconv (cd_88591_to_utf8, + &inptr, &inbytesleft, + &outptr, &outbytesleft); + if (res != (size_t)(-1) || outptr - buf > 1 || buf[1] != (char)0xAD) + result |= 4; + iconv_close (cd_88591_to_utf8); + } + } +#if 0 /* This bug could be worked around by the caller. */ + /* Test against HP-UX 11.11 bug: Positive return value instead of 0. */ + { + iconv_t cd_88591_to_utf8 = iconv_open ("utf8", "iso88591"); + if (cd_88591_to_utf8 != (iconv_t)(-1)) + { + static ICONV_CONST char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337"; + char buf[50]; + ICONV_CONST char *inptr = input; + size_t inbytesleft = strlen (input); + char *outptr = buf; + size_t outbytesleft = sizeof (buf); + size_t res = iconv (cd_88591_to_utf8, + &inptr, &inbytesleft, + &outptr, &outbytesleft); + if ((int)res > 0) + result |= 8; + iconv_close (cd_88591_to_utf8); + } + } +#endif + /* Test against HP-UX 11.11 bug: No converter from EUC-JP to UTF-8 is + provided. */ + if (/* Try standardized names. */ + iconv_open ("UTF-8", "EUC-JP") == (iconv_t)(-1) + /* Try IRIX, OSF/1 names. */ + && iconv_open ("UTF-8", "eucJP") == (iconv_t)(-1) + /* Try AIX names. */ + && iconv_open ("UTF-8", "IBM-eucJP") == (iconv_t)(-1) + /* Try HP-UX names. */ + && iconv_open ("utf8", "eucJP") == (iconv_t)(-1)) + result |= 16; + return result; +]])], + [am_cv_func_iconv_works=yes], , + [case "$host_os" in + aix* | hpux*) am_cv_func_iconv_works="guessing no" ;; + *) am_cv_func_iconv_works="guessing yes" ;; + esac]) + test "$am_cv_func_iconv_works" = no || break + done + LIBS="$am_save_LIBS" + ]) + case "$am_cv_func_iconv_works" in + *no) am_func_iconv=no am_cv_lib_iconv=no ;; + *) am_func_iconv=yes ;; + esac + else + am_func_iconv=no am_cv_lib_iconv=no + fi + if test "$am_func_iconv" = yes; then + AC_DEFINE([HAVE_ICONV], [1], + [Define if you have the iconv() function and it works.]) + fi + if test "$am_cv_lib_iconv" = yes; then + AC_MSG_CHECKING([how to link with libiconv]) + AC_MSG_RESULT([$LIBICONV]) + else + dnl If $LIBICONV didn't lead to a usable library, we don't need $INCICONV + dnl either. + CPPFLAGS="$am_save_CPPFLAGS" + LIBICONV= + LTLIBICONV= + fi + AC_SUBST([LIBICONV]) + AC_SUBST([LTLIBICONV]) +]) + +dnl Define AM_ICONV using AC_DEFUN_ONCE for Autoconf >= 2.64, in order to +dnl avoid warnings like +dnl "warning: AC_REQUIRE: `AM_ICONV' was expanded before it was required". +dnl This is tricky because of the way 'aclocal' is implemented: +dnl - It requires defining an auxiliary macro whose name ends in AC_DEFUN. +dnl Otherwise aclocal's initial scan pass would miss the macro definition. +dnl - It requires a line break inside the AC_DEFUN_ONCE and AC_DEFUN expansions. +dnl Otherwise aclocal would emit many "Use of uninitialized value $1" +dnl warnings. +m4_define([gl_iconv_AC_DEFUN], + m4_version_prereq([2.64], + [[AC_DEFUN_ONCE( + [$1], [$2])]], + [m4_ifdef([gl_00GNULIB], + [[AC_DEFUN_ONCE( + [$1], [$2])]], + [[AC_DEFUN( + [$1], [$2])]])])) +gl_iconv_AC_DEFUN([AM_ICONV], +[ + AM_ICONV_LINK + if test "$am_cv_func_iconv" = yes; then + AC_MSG_CHECKING([for iconv declaration]) + AC_CACHE_VAL([am_cv_proto_iconv], [ + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include +#include +extern +#ifdef __cplusplus +"C" +#endif +#if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) +size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft); +#else +size_t iconv(); +#endif + ]], + [[]])], + [am_cv_proto_iconv_arg1=""], + [am_cv_proto_iconv_arg1="const"]) + am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"]) + am_cv_proto_iconv=`echo "[$]am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'` + AC_MSG_RESULT([ + $am_cv_proto_iconv]) + AC_DEFINE_UNQUOTED([ICONV_CONST], [$am_cv_proto_iconv_arg1], + [Define as const if the declaration of iconv() needs const.]) + dnl Also substitute ICONV_CONST in the gnulib generated . + m4_ifdef([gl_ICONV_H_DEFAULTS], + [AC_REQUIRE([gl_ICONV_H_DEFAULTS]) + if test -n "$am_cv_proto_iconv_arg1"; then + ICONV_CONST="const" + fi + ]) + fi +]) diff --git a/m4/intdiv0.m4 b/m4/intdiv0.m4 new file mode 100644 index 00000000..289c4df5 --- /dev/null +++ b/m4/intdiv0.m4 @@ -0,0 +1,84 @@ +# intdiv0.m4 serial 3 (gettext-0.18) +dnl Copyright (C) 2002, 2007-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +AC_DEFUN([gt_INTDIV0], +[ + AC_REQUIRE([AC_PROG_CC])dnl + AC_REQUIRE([AC_CANONICAL_HOST])dnl + + AC_CACHE_CHECK([whether integer division by zero raises SIGFPE], + gt_cv_int_divbyzero_sigfpe, + [ + gt_cv_int_divbyzero_sigfpe= +changequote(,)dnl + case "$host_os" in + macos* | darwin[6-9]* | darwin[1-9][0-9]*) + # On MacOS X 10.2 or newer, just assume the same as when cross- + # compiling. If we were to perform the real test, 1 Crash Report + # dialog window would pop up. + case "$host_cpu" in + i[34567]86 | x86_64) + gt_cv_int_divbyzero_sigfpe="guessing yes" ;; + esac + ;; + esac +changequote([,])dnl + if test -z "$gt_cv_int_divbyzero_sigfpe"; then + AC_TRY_RUN([ +#include +#include + +static void +sigfpe_handler (int sig) +{ + /* Exit with code 0 if SIGFPE, with code 1 if any other signal. */ + exit (sig != SIGFPE); +} + +int x = 1; +int y = 0; +int z; +int nan; + +int main () +{ + signal (SIGFPE, sigfpe_handler); +/* IRIX and AIX (when "xlc -qcheck" is used) yield signal SIGTRAP. */ +#if (defined (__sgi) || defined (_AIX)) && defined (SIGTRAP) + signal (SIGTRAP, sigfpe_handler); +#endif +/* Linux/SPARC yields signal SIGILL. */ +#if defined (__sparc__) && defined (__linux__) + signal (SIGILL, sigfpe_handler); +#endif + + z = x / y; + nan = y / y; + exit (1); +} +], [gt_cv_int_divbyzero_sigfpe=yes], [gt_cv_int_divbyzero_sigfpe=no], + [ + # Guess based on the CPU. +changequote(,)dnl + case "$host_cpu" in + alpha* | i[34567]86 | x86_64 | m68k | s390*) + gt_cv_int_divbyzero_sigfpe="guessing yes";; + *) + gt_cv_int_divbyzero_sigfpe="guessing no";; + esac +changequote([,])dnl + ]) + fi + ]) + case "$gt_cv_int_divbyzero_sigfpe" in + *yes) value=1;; + *) value=0;; + esac + AC_DEFINE_UNQUOTED([INTDIV0_RAISES_SIGFPE], [$value], + [Define if integer division by zero raises signal SIGFPE.]) +]) diff --git a/m4/intl.m4 b/m4/intl.m4 new file mode 100644 index 00000000..335b23c2 --- /dev/null +++ b/m4/intl.m4 @@ -0,0 +1,294 @@ +# intl.m4 serial 17 (gettext-0.18) +dnl Copyright (C) 1995-2009 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. +dnl +dnl This file can can be used in projects which are not available under +dnl the GNU General Public License or the GNU Library General Public +dnl License but which still want to provide support for the GNU gettext +dnl functionality. +dnl Please note that the actual code of the GNU gettext library is covered +dnl by the GNU Library General Public License, and the rest of the GNU +dnl gettext package package is covered by the GNU General Public License. +dnl They are *not* in the public domain. + +dnl Authors: +dnl Ulrich Drepper , 1995-2000. +dnl Bruno Haible , 2000-2009. + +AC_PREREQ([2.52]) + +dnl Checks for all prerequisites of the intl subdirectory, +dnl except for INTL_LIBTOOL_SUFFIX_PREFIX (and possibly LIBTOOL), INTLOBJS, +dnl USE_INCLUDED_LIBINTL, BUILD_INCLUDED_LIBINTL. +AC_DEFUN([AM_INTL_SUBDIR], +[ + AC_REQUIRE([AC_PROG_INSTALL])dnl + AC_REQUIRE([AM_PROG_MKDIR_P])dnl defined by automake + AC_REQUIRE([AC_PROG_CC])dnl + AC_REQUIRE([AC_CANONICAL_HOST])dnl + AC_REQUIRE([gt_GLIBC2])dnl + AC_REQUIRE([AC_PROG_RANLIB])dnl + AC_REQUIRE([gl_VISIBILITY])dnl + AC_REQUIRE([gt_INTL_SUBDIR_CORE])dnl + AC_REQUIRE([AC_TYPE_LONG_LONG_INT])dnl + AC_REQUIRE([gt_TYPE_WCHAR_T])dnl + AC_REQUIRE([gt_TYPE_WINT_T])dnl + AC_REQUIRE([gl_AC_HEADER_INTTYPES_H]) + AC_REQUIRE([gt_TYPE_INTMAX_T]) + AC_REQUIRE([gt_PRINTF_POSIX]) + AC_REQUIRE([gl_GLIBC21])dnl + AC_REQUIRE([gl_XSIZE])dnl + AC_REQUIRE([gl_FCNTL_O_FLAGS])dnl + AC_REQUIRE([gt_INTL_MACOSX])dnl + + dnl Support for automake's --enable-silent-rules. + case "$enable_silent_rules" in + yes) INTL_DEFAULT_VERBOSITY=0;; + no) INTL_DEFAULT_VERBOSITY=1;; + *) INTL_DEFAULT_VERBOSITY=1;; + esac + AC_SUBST([INTL_DEFAULT_VERBOSITY]) + + AC_CHECK_TYPE([ptrdiff_t], , + [AC_DEFINE([ptrdiff_t], [long], + [Define as the type of the result of subtracting two pointers, if the system doesn't define it.]) + ]) + AC_CHECK_HEADERS([stddef.h stdlib.h string.h]) + AC_CHECK_FUNCS([asprintf fwprintf newlocale putenv setenv setlocale \ + snprintf strnlen wcslen wcsnlen mbrtowc wcrtomb]) + + dnl Use the _snprintf function only if it is declared (because on NetBSD it + dnl is defined as a weak alias of snprintf; we prefer to use the latter). + gt_CHECK_DECL(_snprintf, [#include ]) + gt_CHECK_DECL(_snwprintf, [#include ]) + + dnl Use the *_unlocked functions only if they are declared. + dnl (because some of them were defined without being declared in Solaris + dnl 2.5.1 but were removed in Solaris 2.6, whereas we want binaries built + dnl on Solaris 2.5.1 to run on Solaris 2.6). + dnl Don't use AC_CHECK_DECLS because it isn't supported in autoconf-2.13. + gt_CHECK_DECL(getc_unlocked, [#include ]) + + case $gt_cv_func_printf_posix in + *yes) HAVE_POSIX_PRINTF=1 ;; + *) HAVE_POSIX_PRINTF=0 ;; + esac + AC_SUBST([HAVE_POSIX_PRINTF]) + if test "$ac_cv_func_asprintf" = yes; then + HAVE_ASPRINTF=1 + else + HAVE_ASPRINTF=0 + fi + AC_SUBST([HAVE_ASPRINTF]) + if test "$ac_cv_func_snprintf" = yes; then + HAVE_SNPRINTF=1 + else + HAVE_SNPRINTF=0 + fi + AC_SUBST([HAVE_SNPRINTF]) + if test "$ac_cv_func_newlocale" = yes; then + HAVE_NEWLOCALE=1 + else + HAVE_NEWLOCALE=0 + fi + AC_SUBST([HAVE_NEWLOCALE]) + if test "$ac_cv_func_wprintf" = yes; then + HAVE_WPRINTF=1 + else + HAVE_WPRINTF=0 + fi + AC_SUBST([HAVE_WPRINTF]) + + AM_LANGINFO_CODESET + gt_LC_MESSAGES + + dnl Compilation on mingw and Cygwin needs special Makefile rules, because + dnl 1. when we install a shared library, we must arrange to export + dnl auxiliary pointer variables for every exported variable, + dnl 2. when we install a shared library and a static library simultaneously, + dnl the include file specifies __declspec(dllimport) and therefore we + dnl must arrange to define the auxiliary pointer variables for the + dnl exported variables _also_ in the static library. + if test "$enable_shared" = yes; then + case "$host_os" in + mingw* | cygwin*) is_woe32dll=yes ;; + *) is_woe32dll=no ;; + esac + else + is_woe32dll=no + fi + WOE32DLL=$is_woe32dll + AC_SUBST([WOE32DLL]) + + dnl On mingw and Cygwin, we can activate special Makefile rules which add + dnl version information to the shared libraries and executables. + case "$host_os" in + mingw* | cygwin*) is_woe32=yes ;; + *) is_woe32=no ;; + esac + WOE32=$is_woe32 + AC_SUBST([WOE32]) + if test $WOE32 = yes; then + dnl Check for a program that compiles Windows resource files. + AC_CHECK_TOOL([WINDRES], [windres]) + fi + + dnl Determine whether when creating a library, "-lc" should be passed to + dnl libtool or not. On many platforms, it is required for the libtool option + dnl -no-undefined to work. On HP-UX, however, the -lc - stored by libtool + dnl in the *.la files - makes it impossible to create multithreaded programs, + dnl because libtool also reorders the -lc to come before the -pthread, and + dnl this disables pthread_create() . + case "$host_os" in + hpux*) LTLIBC="" ;; + *) LTLIBC="-lc" ;; + esac + AC_SUBST([LTLIBC]) + + dnl Rename some macros and functions used for locking. + AH_BOTTOM([ +#define __libc_lock_t gl_lock_t +#define __libc_lock_define gl_lock_define +#define __libc_lock_define_initialized gl_lock_define_initialized +#define __libc_lock_init gl_lock_init +#define __libc_lock_lock gl_lock_lock +#define __libc_lock_unlock gl_lock_unlock +#define __libc_lock_recursive_t gl_recursive_lock_t +#define __libc_lock_define_recursive gl_recursive_lock_define +#define __libc_lock_define_initialized_recursive gl_recursive_lock_define_initialized +#define __libc_lock_init_recursive gl_recursive_lock_init +#define __libc_lock_lock_recursive gl_recursive_lock_lock +#define __libc_lock_unlock_recursive gl_recursive_lock_unlock +#define glthread_in_use libintl_thread_in_use +#define glthread_lock_init_func libintl_lock_init_func +#define glthread_lock_lock_func libintl_lock_lock_func +#define glthread_lock_unlock_func libintl_lock_unlock_func +#define glthread_lock_destroy_func libintl_lock_destroy_func +#define glthread_rwlock_init_multithreaded libintl_rwlock_init_multithreaded +#define glthread_rwlock_init_func libintl_rwlock_init_func +#define glthread_rwlock_rdlock_multithreaded libintl_rwlock_rdlock_multithreaded +#define glthread_rwlock_rdlock_func libintl_rwlock_rdlock_func +#define glthread_rwlock_wrlock_multithreaded libintl_rwlock_wrlock_multithreaded +#define glthread_rwlock_wrlock_func libintl_rwlock_wrlock_func +#define glthread_rwlock_unlock_multithreaded libintl_rwlock_unlock_multithreaded +#define glthread_rwlock_unlock_func libintl_rwlock_unlock_func +#define glthread_rwlock_destroy_multithreaded libintl_rwlock_destroy_multithreaded +#define glthread_rwlock_destroy_func libintl_rwlock_destroy_func +#define glthread_recursive_lock_init_multithreaded libintl_recursive_lock_init_multithreaded +#define glthread_recursive_lock_init_func libintl_recursive_lock_init_func +#define glthread_recursive_lock_lock_multithreaded libintl_recursive_lock_lock_multithreaded +#define glthread_recursive_lock_lock_func libintl_recursive_lock_lock_func +#define glthread_recursive_lock_unlock_multithreaded libintl_recursive_lock_unlock_multithreaded +#define glthread_recursive_lock_unlock_func libintl_recursive_lock_unlock_func +#define glthread_recursive_lock_destroy_multithreaded libintl_recursive_lock_destroy_multithreaded +#define glthread_recursive_lock_destroy_func libintl_recursive_lock_destroy_func +#define glthread_once_func libintl_once_func +#define glthread_once_singlethreaded libintl_once_singlethreaded +#define glthread_once_multithreaded libintl_once_multithreaded +]) +]) + + +dnl Checks for the core files of the intl subdirectory: +dnl dcigettext.c +dnl eval-plural.h +dnl explodename.c +dnl finddomain.c +dnl gettextP.h +dnl gmo.h +dnl hash-string.h hash-string.c +dnl l10nflist.c +dnl libgnuintl.h.in (except the *printf stuff) +dnl loadinfo.h +dnl loadmsgcat.c +dnl localealias.c +dnl log.c +dnl plural-exp.h plural-exp.c +dnl plural.y +dnl Used by libglocale. +AC_DEFUN([gt_INTL_SUBDIR_CORE], +[ + AC_REQUIRE([AC_C_INLINE])dnl + AC_REQUIRE([AC_TYPE_SIZE_T])dnl + AC_REQUIRE([gl_AC_HEADER_STDINT_H]) + AC_REQUIRE([AC_FUNC_ALLOCA])dnl + AC_REQUIRE([AC_FUNC_MMAP])dnl + AC_REQUIRE([gt_INTDIV0])dnl + AC_REQUIRE([gl_AC_TYPE_UINTMAX_T])dnl + AC_REQUIRE([gt_INTTYPES_PRI])dnl + AC_REQUIRE([gl_LOCK])dnl + + AC_TRY_LINK( + [int foo (int a) { a = __builtin_expect (a, 10); return a == 10 ? 0 : 1; }], + [], + [AC_DEFINE([HAVE_BUILTIN_EXPECT], [1], + [Define to 1 if the compiler understands __builtin_expect.])]) + + AC_CHECK_HEADERS([argz.h inttypes.h limits.h unistd.h sys/param.h]) + AC_CHECK_FUNCS([getcwd getegid geteuid getgid getuid mempcpy munmap \ + stpcpy strcasecmp strdup strtoul tsearch uselocale argz_count \ + argz_stringify argz_next __fsetlocking]) + + dnl Use the *_unlocked functions only if they are declared. + dnl (because some of them were defined without being declared in Solaris + dnl 2.5.1 but were removed in Solaris 2.6, whereas we want binaries built + dnl on Solaris 2.5.1 to run on Solaris 2.6). + dnl Don't use AC_CHECK_DECLS because it isn't supported in autoconf-2.13. + gt_CHECK_DECL([feof_unlocked], [#include ]) + gt_CHECK_DECL([fgets_unlocked], [#include ]) + + AM_ICONV + + dnl intl/plural.c is generated from intl/plural.y. It requires bison, + dnl because plural.y uses bison specific features. It requires at least + dnl bison-1.26 because earlier versions generate a plural.c that doesn't + dnl compile. + dnl bison is only needed for the maintainer (who touches plural.y). But in + dnl order to avoid separate Makefiles or --enable-maintainer-mode, we put + dnl the rule in general Makefile. Now, some people carelessly touch the + dnl files or have a broken "make" program, hence the plural.c rule will + dnl sometimes fire. To avoid an error, defines BISON to ":" if it is not + dnl present or too old. + AC_CHECK_PROGS([INTLBISON], [bison]) + if test -z "$INTLBISON"; then + ac_verc_fail=yes + else + dnl Found it, now check the version. + AC_MSG_CHECKING([version of bison]) +changequote(<<,>>)dnl + ac_prog_version=`$INTLBISON --version 2>&1 | sed -n 's/^.*GNU Bison.* \([0-9]*\.[0-9.]*\).*$/\1/p'` + case $ac_prog_version in + '') ac_prog_version="v. ?.??, bad"; ac_verc_fail=yes;; + 1.2[6-9]* | 1.[3-9][0-9]* | [2-9].*) +changequote([,])dnl + ac_prog_version="$ac_prog_version, ok"; ac_verc_fail=no;; + *) ac_prog_version="$ac_prog_version, bad"; ac_verc_fail=yes;; + esac + AC_MSG_RESULT([$ac_prog_version]) + fi + if test $ac_verc_fail = yes; then + INTLBISON=: + fi +]) + + +dnl gt_CHECK_DECL(FUNC, INCLUDES) +dnl Check whether a function is declared. +AC_DEFUN([gt_CHECK_DECL], +[ + AC_CACHE_CHECK([whether $1 is declared], [ac_cv_have_decl_$1], + [AC_TRY_COMPILE([$2], [ +#ifndef $1 + char *p = (char *) $1; +#endif +], ac_cv_have_decl_$1=yes, ac_cv_have_decl_$1=no)]) + if test $ac_cv_have_decl_$1 = yes; then + gt_value=1 + else + gt_value=0 + fi + AC_DEFINE_UNQUOTED([HAVE_DECL_]translit($1, [a-z], [A-Z]), [$gt_value], + [Define to 1 if you have the declaration of `$1', and to 0 if you don't.]) +]) diff --git a/m4/intldir.m4 b/m4/intldir.m4 new file mode 100644 index 00000000..ebae76d3 --- /dev/null +++ b/m4/intldir.m4 @@ -0,0 +1,19 @@ +# intldir.m4 serial 2 (gettext-0.18) +dnl Copyright (C) 2006, 2009-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. +dnl +dnl This file can can be used in projects which are not available under +dnl the GNU General Public License or the GNU Library General Public +dnl License but which still want to provide support for the GNU gettext +dnl functionality. +dnl Please note that the actual code of the GNU gettext library is covered +dnl by the GNU Library General Public License, and the rest of the GNU +dnl gettext package package is covered by the GNU General Public License. +dnl They are *not* in the public domain. + +AC_PREREQ([2.52]) + +dnl Tells the AM_GNU_GETTEXT macro to consider an intl/ directory. +AC_DEFUN([AM_GNU_GETTEXT_INTL_SUBDIR], []) diff --git a/m4/intlmacosx.m4 b/m4/intlmacosx.m4 new file mode 100644 index 00000000..dd910259 --- /dev/null +++ b/m4/intlmacosx.m4 @@ -0,0 +1,51 @@ +# intlmacosx.m4 serial 3 (gettext-0.18) +dnl Copyright (C) 2004-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. +dnl +dnl This file can can be used in projects which are not available under +dnl the GNU General Public License or the GNU Library General Public +dnl License but which still want to provide support for the GNU gettext +dnl functionality. +dnl Please note that the actual code of the GNU gettext library is covered +dnl by the GNU Library General Public License, and the rest of the GNU +dnl gettext package package is covered by the GNU General Public License. +dnl They are *not* in the public domain. + +dnl Checks for special options needed on MacOS X. +dnl Defines INTL_MACOSX_LIBS. +AC_DEFUN([gt_INTL_MACOSX], +[ + dnl Check for API introduced in MacOS X 10.2. + AC_CACHE_CHECK([for CFPreferencesCopyAppValue], + [gt_cv_func_CFPreferencesCopyAppValue], + [gt_save_LIBS="$LIBS" + LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation" + AC_TRY_LINK([#include ], + [CFPreferencesCopyAppValue(NULL, NULL)], + [gt_cv_func_CFPreferencesCopyAppValue=yes], + [gt_cv_func_CFPreferencesCopyAppValue=no]) + LIBS="$gt_save_LIBS"]) + if test $gt_cv_func_CFPreferencesCopyAppValue = yes; then + AC_DEFINE([HAVE_CFPREFERENCESCOPYAPPVALUE], [1], + [Define to 1 if you have the MacOS X function CFPreferencesCopyAppValue in the CoreFoundation framework.]) + fi + dnl Check for API introduced in MacOS X 10.3. + AC_CACHE_CHECK([for CFLocaleCopyCurrent], [gt_cv_func_CFLocaleCopyCurrent], + [gt_save_LIBS="$LIBS" + LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation" + AC_TRY_LINK([#include ], [CFLocaleCopyCurrent();], + [gt_cv_func_CFLocaleCopyCurrent=yes], + [gt_cv_func_CFLocaleCopyCurrent=no]) + LIBS="$gt_save_LIBS"]) + if test $gt_cv_func_CFLocaleCopyCurrent = yes; then + AC_DEFINE([HAVE_CFLOCALECOPYCURRENT], [1], + [Define to 1 if you have the MacOS X function CFLocaleCopyCurrent in the CoreFoundation framework.]) + fi + INTL_MACOSX_LIBS= + if test $gt_cv_func_CFPreferencesCopyAppValue = yes || test $gt_cv_func_CFLocaleCopyCurrent = yes; then + INTL_MACOSX_LIBS="-Wl,-framework -Wl,CoreFoundation" + fi + AC_SUBST([INTL_MACOSX_LIBS]) +]) diff --git a/m4/intmax.m4 b/m4/intmax.m4 new file mode 100644 index 00000000..74aaaf5e --- /dev/null +++ b/m4/intmax.m4 @@ -0,0 +1,33 @@ +# intmax.m4 serial 5 (gettext-0.18) +dnl Copyright (C) 2002-2005, 2008-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. +dnl Test whether the system has the 'intmax_t' type, but don't attempt to +dnl find a replacement if it is lacking. + +AC_DEFUN([gt_TYPE_INTMAX_T], +[ + AC_REQUIRE([gl_AC_HEADER_INTTYPES_H]) + AC_REQUIRE([gl_AC_HEADER_STDINT_H]) + AC_CACHE_CHECK([for intmax_t], [gt_cv_c_intmax_t], + [AC_TRY_COMPILE([ +#include +#include +#if HAVE_STDINT_H_WITH_UINTMAX +#include +#endif +#if HAVE_INTTYPES_H_WITH_UINTMAX +#include +#endif +], [intmax_t x = -1; + return !x;], + [gt_cv_c_intmax_t=yes], + [gt_cv_c_intmax_t=no])]) + if test $gt_cv_c_intmax_t = yes; then + AC_DEFINE([HAVE_INTMAX_T], [1], + [Define if you have the 'intmax_t' type in or .]) + fi +]) diff --git a/m4/inttypes-pri.m4 b/m4/inttypes-pri.m4 new file mode 100644 index 00000000..718a4f4e --- /dev/null +++ b/m4/inttypes-pri.m4 @@ -0,0 +1,36 @@ +# inttypes-pri.m4 serial 6 (gettext-0.18) +dnl Copyright (C) 1997-2002, 2006, 2008-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +AC_PREREQ([2.52]) + +# Define PRI_MACROS_BROKEN if exists and defines the PRI* +# macros to non-string values. This is the case on AIX 4.3.3. + +AC_DEFUN([gt_INTTYPES_PRI], +[ + AC_CHECK_HEADERS([inttypes.h]) + if test $ac_cv_header_inttypes_h = yes; then + AC_CACHE_CHECK([whether the inttypes.h PRIxNN macros are broken], + [gt_cv_inttypes_pri_broken], + [ + AC_TRY_COMPILE([#include +#ifdef PRId32 +char *p = PRId32; +#endif +], [], [gt_cv_inttypes_pri_broken=no], [gt_cv_inttypes_pri_broken=yes]) + ]) + fi + if test "$gt_cv_inttypes_pri_broken" = yes; then + AC_DEFINE_UNQUOTED([PRI_MACROS_BROKEN], [1], + [Define if exists and defines unusable PRI* macros.]) + PRI_MACROS_BROKEN=1 + else + PRI_MACROS_BROKEN=0 + fi + AC_SUBST([PRI_MACROS_BROKEN]) +]) diff --git a/m4/inttypes_h.m4 b/m4/inttypes_h.m4 new file mode 100644 index 00000000..782d77ed --- /dev/null +++ b/m4/inttypes_h.m4 @@ -0,0 +1,26 @@ +# inttypes_h.m4 serial 9 +dnl Copyright (C) 1997-2004, 2006, 2008-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Paul Eggert. + +# Define HAVE_INTTYPES_H_WITH_UINTMAX if exists, +# doesn't clash with , and declares uintmax_t. + +AC_DEFUN([gl_AC_HEADER_INTTYPES_H], +[ + AC_CACHE_CHECK([for inttypes.h], [gl_cv_header_inttypes_h], + [AC_TRY_COMPILE( + [#include +#include ], + [uintmax_t i = (uintmax_t) -1; return !i;], + [gl_cv_header_inttypes_h=yes], + [gl_cv_header_inttypes_h=no])]) + if test $gl_cv_header_inttypes_h = yes; then + AC_DEFINE_UNQUOTED([HAVE_INTTYPES_H_WITH_UINTMAX], [1], + [Define if exists, doesn't clash with , + and declares uintmax_t. ]) + fi +]) diff --git a/m4/lcmessage.m4 b/m4/lcmessage.m4 new file mode 100644 index 00000000..1a705431 --- /dev/null +++ b/m4/lcmessage.m4 @@ -0,0 +1,31 @@ +# lcmessage.m4 serial 6 (gettext-0.18) +dnl Copyright (C) 1995-2002, 2004-2005, 2008-2010 Free Software Foundation, +dnl Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. +dnl +dnl This file can can be used in projects which are not available under +dnl the GNU General Public License or the GNU Library General Public +dnl License but which still want to provide support for the GNU gettext +dnl functionality. +dnl Please note that the actual code of the GNU gettext library is covered +dnl by the GNU Library General Public License, and the rest of the GNU +dnl gettext package package is covered by the GNU General Public License. +dnl They are *not* in the public domain. + +dnl Authors: +dnl Ulrich Drepper , 1995. + +# Check whether LC_MESSAGES is available in . + +AC_DEFUN([gt_LC_MESSAGES], +[ + AC_CACHE_CHECK([for LC_MESSAGES], [gt_cv_val_LC_MESSAGES], + [AC_TRY_LINK([#include ], [return LC_MESSAGES], + [gt_cv_val_LC_MESSAGES=yes], [gt_cv_val_LC_MESSAGES=no])]) + if test $gt_cv_val_LC_MESSAGES = yes; then + AC_DEFINE([HAVE_LC_MESSAGES], [1], + [Define if your file defines LC_MESSAGES.]) + fi +]) diff --git a/m4/lib-ld.m4 b/m4/lib-ld.m4 new file mode 100644 index 00000000..6209de65 --- /dev/null +++ b/m4/lib-ld.m4 @@ -0,0 +1,119 @@ +# lib-ld.m4 serial 6 +dnl Copyright (C) 1996-2003, 2009-2016 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl Subroutines of libtool.m4, +dnl with replacements s/_*LT_PATH/AC_LIB_PROG/ and s/lt_/acl_/ to avoid +dnl collision with libtool.m4. + +dnl From libtool-2.4. Sets the variable with_gnu_ld to yes or no. +AC_DEFUN([AC_LIB_PROG_LD_GNU], +[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], [acl_cv_prog_gnu_ld], +[# I'd rather use --version here, but apparently some GNU lds only accept -v. +case `$LD -v 2>&1 /dev/null 2>&1 \ + && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 \ + || PATH_SEPARATOR=';' + } +fi + +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + AC_MSG_CHECKING([for ld used by $CC]) + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [[\\/]]* | ?:[[\\/]]*) + re_direlt='/[[^/]][[^/]]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`echo "$ac_prog"| sed 's%\\\\%/%g'` + while echo "$ac_prog" | grep "$re_direlt" > /dev/null 2>&1; do + ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + AC_MSG_CHECKING([for GNU ld]) +else + AC_MSG_CHECKING([for non-GNU ld]) +fi +AC_CACHE_VAL([acl_cv_path_LD], +[if test -z "$LD"; then + acl_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$acl_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + acl_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$acl_cv_path_LD" -v 2>&1 = 1.10 to complain if config.rpath is missing. + m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([config.rpath])]) + AC_REQUIRE([AC_PROG_CC]) dnl we use $CC, $GCC, $LDFLAGS + AC_REQUIRE([AC_LIB_PROG_LD]) dnl we use $LD, $with_gnu_ld + AC_REQUIRE([AC_CANONICAL_HOST]) dnl we use $host + AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT]) dnl we use $ac_aux_dir + AC_CACHE_CHECK([for shared library run path origin], [acl_cv_rpath], [ + CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \ + ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh + . ./conftest.sh + rm -f ./conftest.sh + acl_cv_rpath=done + ]) + wl="$acl_cv_wl" + acl_libext="$acl_cv_libext" + acl_shlibext="$acl_cv_shlibext" + acl_libname_spec="$acl_cv_libname_spec" + acl_library_names_spec="$acl_cv_library_names_spec" + acl_hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec" + acl_hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator" + acl_hardcode_direct="$acl_cv_hardcode_direct" + acl_hardcode_minus_L="$acl_cv_hardcode_minus_L" + dnl Determine whether the user wants rpath handling at all. + AC_ARG_ENABLE([rpath], + [ --disable-rpath do not hardcode runtime library paths], + :, enable_rpath=yes) +]) + +dnl AC_LIB_FROMPACKAGE(name, package) +dnl declares that libname comes from the given package. The configure file +dnl will then not have a --with-libname-prefix option but a +dnl --with-package-prefix option. Several libraries can come from the same +dnl package. This declaration must occur before an AC_LIB_LINKFLAGS or similar +dnl macro call that searches for libname. +AC_DEFUN([AC_LIB_FROMPACKAGE], +[ + pushdef([NAME],[m4_translit([$1],[abcdefghijklmnopqrstuvwxyz./+-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ____])]) + define([acl_frompackage_]NAME, [$2]) + popdef([NAME]) + pushdef([PACK],[$2]) + pushdef([PACKUP],[m4_translit(PACK,[abcdefghijklmnopqrstuvwxyz./+-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ____])]) + define([acl_libsinpackage_]PACKUP, + m4_ifdef([acl_libsinpackage_]PACKUP, [m4_defn([acl_libsinpackage_]PACKUP)[, ]],)[lib$1]) + popdef([PACKUP]) + popdef([PACK]) +]) + +dnl AC_LIB_LINKFLAGS_BODY(name [, dependencies]) searches for libname and +dnl the libraries corresponding to explicit and implicit dependencies. +dnl Sets the LIB${NAME}, LTLIB${NAME} and INC${NAME} variables. +dnl Also, sets the LIB${NAME}_PREFIX variable to nonempty if libname was found +dnl in ${LIB${NAME}_PREFIX}/$acl_libdirstem. +AC_DEFUN([AC_LIB_LINKFLAGS_BODY], +[ + AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) + pushdef([NAME],[m4_translit([$1],[abcdefghijklmnopqrstuvwxyz./+-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ____])]) + pushdef([PACK],[m4_ifdef([acl_frompackage_]NAME, [acl_frompackage_]NAME, lib[$1])]) + pushdef([PACKUP],[m4_translit(PACK,[abcdefghijklmnopqrstuvwxyz./+-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ____])]) + pushdef([PACKLIBS],[m4_ifdef([acl_frompackage_]NAME, [acl_libsinpackage_]PACKUP, lib[$1])]) + dnl Autoconf >= 2.61 supports dots in --with options. + pushdef([P_A_C_K],[m4_if(m4_version_compare(m4_defn([m4_PACKAGE_VERSION]),[2.61]),[-1],[m4_translit(PACK,[.],[_])],PACK)]) + dnl By default, look in $includedir and $libdir. + use_additional=yes + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + AC_ARG_WITH(P_A_C_K[-prefix], +[[ --with-]]P_A_C_K[[-prefix[=DIR] search for ]PACKLIBS[ in DIR/include and DIR/lib + --without-]]P_A_C_K[[-prefix don't search for ]PACKLIBS[ in includedir and libdir]], +[ + if test "X$withval" = "Xno"; then + use_additional=no + else + if test "X$withval" = "X"; then + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + else + additional_includedir="$withval/include" + additional_libdir="$withval/$acl_libdirstem" + if test "$acl_libdirstem2" != "$acl_libdirstem" \ + && ! test -d "$withval/$acl_libdirstem"; then + additional_libdir="$withval/$acl_libdirstem2" + fi + fi + fi +]) + dnl Search the library and its dependencies in $additional_libdir and + dnl $LDFLAGS. Using breadth-first-seach. + LIB[]NAME= + LTLIB[]NAME= + INC[]NAME= + LIB[]NAME[]_PREFIX= + dnl HAVE_LIB${NAME} is an indicator that LIB${NAME}, LTLIB${NAME} have been + dnl computed. So it has to be reset here. + HAVE_LIB[]NAME= + rpathdirs= + ltrpathdirs= + names_already_handled= + names_next_round='$1 $2' + while test -n "$names_next_round"; do + names_this_round="$names_next_round" + names_next_round= + for name in $names_this_round; do + already_handled= + for n in $names_already_handled; do + if test "$n" = "$name"; then + already_handled=yes + break + fi + done + if test -z "$already_handled"; then + names_already_handled="$names_already_handled $name" + dnl See if it was already located by an earlier AC_LIB_LINKFLAGS + dnl or AC_LIB_HAVE_LINKFLAGS call. + uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./+-|ABCDEFGHIJKLMNOPQRSTUVWXYZ____|'` + eval value=\"\$HAVE_LIB$uppername\" + if test -n "$value"; then + if test "$value" = yes; then + eval value=\"\$LIB$uppername\" + test -z "$value" || LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$value" + eval value=\"\$LTLIB$uppername\" + test -z "$value" || LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$value" + else + dnl An earlier call to AC_LIB_HAVE_LINKFLAGS has determined + dnl that this library doesn't exist. So just drop it. + : + fi + else + dnl Search the library lib$name in $additional_libdir and $LDFLAGS + dnl and the already constructed $LIBNAME/$LTLIBNAME. + found_dir= + found_la= + found_so= + found_a= + eval libname=\"$acl_libname_spec\" # typically: libname=lib$name + if test -n "$acl_shlibext"; then + shrext=".$acl_shlibext" # typically: shrext=.so + else + shrext= + fi + if test $use_additional = yes; then + dir="$additional_libdir" + dnl The same code as in the loop below: + dnl First look for a shared library. + if test -n "$acl_shlibext"; then + if test -f "$dir/$libname$shrext"; then + found_dir="$dir" + found_so="$dir/$libname$shrext" + else + if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then + ver=`(cd "$dir" && \ + for f in "$libname$shrext".*; do echo "$f"; done \ + | sed -e "s,^$libname$shrext\\\\.,," \ + | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ + | sed 1q ) 2>/dev/null` + if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then + found_dir="$dir" + found_so="$dir/$libname$shrext.$ver" + fi + else + eval library_names=\"$acl_library_names_spec\" + for f in $library_names; do + if test -f "$dir/$f"; then + found_dir="$dir" + found_so="$dir/$f" + break + fi + done + fi + fi + fi + dnl Then look for a static library. + if test "X$found_dir" = "X"; then + if test -f "$dir/$libname.$acl_libext"; then + found_dir="$dir" + found_a="$dir/$libname.$acl_libext" + fi + fi + if test "X$found_dir" != "X"; then + if test -f "$dir/$libname.la"; then + found_la="$dir/$libname.la" + fi + fi + fi + if test "X$found_dir" = "X"; then + for x in $LDFLAGS $LTLIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + case "$x" in + -L*) + dir=`echo "X$x" | sed -e 's/^X-L//'` + dnl First look for a shared library. + if test -n "$acl_shlibext"; then + if test -f "$dir/$libname$shrext"; then + found_dir="$dir" + found_so="$dir/$libname$shrext" + else + if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then + ver=`(cd "$dir" && \ + for f in "$libname$shrext".*; do echo "$f"; done \ + | sed -e "s,^$libname$shrext\\\\.,," \ + | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ + | sed 1q ) 2>/dev/null` + if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then + found_dir="$dir" + found_so="$dir/$libname$shrext.$ver" + fi + else + eval library_names=\"$acl_library_names_spec\" + for f in $library_names; do + if test -f "$dir/$f"; then + found_dir="$dir" + found_so="$dir/$f" + break + fi + done + fi + fi + fi + dnl Then look for a static library. + if test "X$found_dir" = "X"; then + if test -f "$dir/$libname.$acl_libext"; then + found_dir="$dir" + found_a="$dir/$libname.$acl_libext" + fi + fi + if test "X$found_dir" != "X"; then + if test -f "$dir/$libname.la"; then + found_la="$dir/$libname.la" + fi + fi + ;; + esac + if test "X$found_dir" != "X"; then + break + fi + done + fi + if test "X$found_dir" != "X"; then + dnl Found the library. + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$found_dir -l$name" + if test "X$found_so" != "X"; then + dnl Linking with a shared library. We attempt to hardcode its + dnl directory into the executable's runpath, unless it's the + dnl standard /usr/lib. + if test "$enable_rpath" = no \ + || test "X$found_dir" = "X/usr/$acl_libdirstem" \ + || test "X$found_dir" = "X/usr/$acl_libdirstem2"; then + dnl No hardcoding is needed. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + else + dnl Use an explicit option to hardcode DIR into the resulting + dnl binary. + dnl Potentially add DIR to ltrpathdirs. + dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $found_dir" + fi + dnl The hardcoding into $LIBNAME is system dependent. + if test "$acl_hardcode_direct" = yes; then + dnl Using DIR/libNAME.so during linking hardcodes DIR into the + dnl resulting binary. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + else + if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then + dnl Use an explicit option to hardcode DIR into the resulting + dnl binary. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + dnl Potentially add DIR to rpathdirs. + dnl The rpathdirs will be appended to $LIBNAME at the end. + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $found_dir" + fi + else + dnl Rely on "-L$found_dir". + dnl But don't add it if it's already contained in the LDFLAGS + dnl or the already constructed $LIBNAME + haveit= + for x in $LDFLAGS $LIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir" + fi + if test "$acl_hardcode_minus_L" != no; then + dnl FIXME: Not sure whether we should use + dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" + dnl here. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + else + dnl We cannot use $acl_hardcode_runpath_var and LD_RUN_PATH + dnl here, because this doesn't fit in flags passed to the + dnl compiler. So give up. No hardcoding. This affects only + dnl very old systems. + dnl FIXME: Not sure whether we should use + dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" + dnl here. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" + fi + fi + fi + fi + else + if test "X$found_a" != "X"; then + dnl Linking with a static library. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_a" + else + dnl We shouldn't come here, but anyway it's good to have a + dnl fallback. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir -l$name" + fi + fi + dnl Assume the include files are nearby. + additional_includedir= + case "$found_dir" in + */$acl_libdirstem | */$acl_libdirstem/) + basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'` + if test "$name" = '$1'; then + LIB[]NAME[]_PREFIX="$basedir" + fi + additional_includedir="$basedir/include" + ;; + */$acl_libdirstem2 | */$acl_libdirstem2/) + basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem2/"'*$,,'` + if test "$name" = '$1'; then + LIB[]NAME[]_PREFIX="$basedir" + fi + additional_includedir="$basedir/include" + ;; + esac + if test "X$additional_includedir" != "X"; then + dnl Potentially add $additional_includedir to $INCNAME. + dnl But don't add it + dnl 1. if it's the standard /usr/include, + dnl 2. if it's /usr/local/include and we are using GCC on Linux, + dnl 3. if it's already present in $CPPFLAGS or the already + dnl constructed $INCNAME, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_includedir" != "X/usr/include"; then + haveit= + if test "X$additional_includedir" = "X/usr/local/include"; then + if test -n "$GCC"; then + case $host_os in + linux* | gnu* | k*bsd*-gnu) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + for x in $CPPFLAGS $INC[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-I$additional_includedir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_includedir"; then + dnl Really add $additional_includedir to $INCNAME. + INC[]NAME="${INC[]NAME}${INC[]NAME:+ }-I$additional_includedir" + fi + fi + fi + fi + fi + dnl Look for dependencies. + if test -n "$found_la"; then + dnl Read the .la file. It defines the variables + dnl dlname, library_names, old_library, dependency_libs, current, + dnl age, revision, installed, dlopen, dlpreopen, libdir. + save_libdir="$libdir" + case "$found_la" in + */* | *\\*) . "$found_la" ;; + *) . "./$found_la" ;; + esac + libdir="$save_libdir" + dnl We use only dependency_libs. + for dep in $dependency_libs; do + case "$dep" in + -L*) + additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'` + dnl Potentially add $additional_libdir to $LIBNAME and $LTLIBNAME. + dnl But don't add it + dnl 1. if it's the standard /usr/lib, + dnl 2. if it's /usr/local/lib and we are using GCC on Linux, + dnl 3. if it's already present in $LDFLAGS or the already + dnl constructed $LIBNAME, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_libdir" != "X/usr/$acl_libdirstem" \ + && test "X$additional_libdir" != "X/usr/$acl_libdirstem2"; then + haveit= + if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem" \ + || test "X$additional_libdir" = "X/usr/local/$acl_libdirstem2"; then + if test -n "$GCC"; then + case $host_os in + linux* | gnu* | k*bsd*-gnu) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + haveit= + for x in $LDFLAGS $LIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + dnl Really add $additional_libdir to $LIBNAME. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$additional_libdir" + fi + fi + haveit= + for x in $LDFLAGS $LTLIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + dnl Really add $additional_libdir to $LTLIBNAME. + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$additional_libdir" + fi + fi + fi + fi + ;; + -R*) + dir=`echo "X$dep" | sed -e 's/^X-R//'` + if test "$enable_rpath" != no; then + dnl Potentially add DIR to rpathdirs. + dnl The rpathdirs will be appended to $LIBNAME at the end. + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $dir" + fi + dnl Potentially add DIR to ltrpathdirs. + dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $dir" + fi + fi + ;; + -l*) + dnl Handle this in the next round. + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'` + ;; + *.la) + dnl Handle this in the next round. Throw away the .la's + dnl directory; it is already contained in a preceding -L + dnl option. + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'` + ;; + *) + dnl Most likely an immediate library name. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$dep" + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$dep" + ;; + esac + done + fi + else + dnl Didn't find the library; assume it is in the system directories + dnl known to the linker and runtime loader. (All the system + dnl directories known to the linker should also be known to the + dnl runtime loader, otherwise the system is severely misconfigured.) + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-l$name" + fi + fi + fi + done + done + if test "X$rpathdirs" != "X"; then + if test -n "$acl_hardcode_libdir_separator"; then + dnl Weird platform: only the last -rpath option counts, the user must + dnl pass all path elements in one option. We can arrange that for a + dnl single library, but not when more than one $LIBNAMEs are used. + alldirs= + for found_dir in $rpathdirs; do + alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$found_dir" + done + dnl Note: acl_hardcode_libdir_flag_spec uses $libdir and $wl. + acl_save_libdir="$libdir" + libdir="$alldirs" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" + else + dnl The -rpath options are cumulative. + for found_dir in $rpathdirs; do + acl_save_libdir="$libdir" + libdir="$found_dir" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" + done + fi + fi + if test "X$ltrpathdirs" != "X"; then + dnl When using libtool, the option that works for both libraries and + dnl executables is -R. The -R options are cumulative. + for found_dir in $ltrpathdirs; do + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-R$found_dir" + done + fi + popdef([P_A_C_K]) + popdef([PACKLIBS]) + popdef([PACKUP]) + popdef([PACK]) + popdef([NAME]) +]) + +dnl AC_LIB_APPENDTOVAR(VAR, CONTENTS) appends the elements of CONTENTS to VAR, +dnl unless already present in VAR. +dnl Works only for CPPFLAGS, not for LIB* variables because that sometimes +dnl contains two or three consecutive elements that belong together. +AC_DEFUN([AC_LIB_APPENDTOVAR], +[ + for element in [$2]; do + haveit= + for x in $[$1]; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X$element"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + [$1]="${[$1]}${[$1]:+ }$element" + fi + done +]) + +dnl For those cases where a variable contains several -L and -l options +dnl referring to unknown libraries and directories, this macro determines the +dnl necessary additional linker options for the runtime path. +dnl AC_LIB_LINKFLAGS_FROM_LIBS([LDADDVAR], [LIBSVALUE], [USE-LIBTOOL]) +dnl sets LDADDVAR to linker options needed together with LIBSVALUE. +dnl If USE-LIBTOOL evaluates to non-empty, linking with libtool is assumed, +dnl otherwise linking without libtool is assumed. +AC_DEFUN([AC_LIB_LINKFLAGS_FROM_LIBS], +[ + AC_REQUIRE([AC_LIB_RPATH]) + AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) + $1= + if test "$enable_rpath" != no; then + if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then + dnl Use an explicit option to hardcode directories into the resulting + dnl binary. + rpathdirs= + next= + for opt in $2; do + if test -n "$next"; then + dir="$next" + dnl No need to hardcode the standard /usr/lib. + if test "X$dir" != "X/usr/$acl_libdirstem" \ + && test "X$dir" != "X/usr/$acl_libdirstem2"; then + rpathdirs="$rpathdirs $dir" + fi + next= + else + case $opt in + -L) next=yes ;; + -L*) dir=`echo "X$opt" | sed -e 's,^X-L,,'` + dnl No need to hardcode the standard /usr/lib. + if test "X$dir" != "X/usr/$acl_libdirstem" \ + && test "X$dir" != "X/usr/$acl_libdirstem2"; then + rpathdirs="$rpathdirs $dir" + fi + next= ;; + *) next= ;; + esac + fi + done + if test "X$rpathdirs" != "X"; then + if test -n ""$3""; then + dnl libtool is used for linking. Use -R options. + for dir in $rpathdirs; do + $1="${$1}${$1:+ }-R$dir" + done + else + dnl The linker is used for linking directly. + if test -n "$acl_hardcode_libdir_separator"; then + dnl Weird platform: only the last -rpath option counts, the user + dnl must pass all path elements in one option. + alldirs= + for dir in $rpathdirs; do + alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$dir" + done + acl_save_libdir="$libdir" + libdir="$alldirs" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + $1="$flag" + else + dnl The -rpath options are cumulative. + for dir in $rpathdirs; do + acl_save_libdir="$libdir" + libdir="$dir" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + $1="${$1}${$1:+ }$flag" + done + fi + fi + fi + fi + fi + AC_SUBST([$1]) +]) diff --git a/m4/lib-prefix.m4 b/m4/lib-prefix.m4 new file mode 100644 index 00000000..6851031d --- /dev/null +++ b/m4/lib-prefix.m4 @@ -0,0 +1,224 @@ +# lib-prefix.m4 serial 7 (gettext-0.18) +dnl Copyright (C) 2001-2005, 2008-2016 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +dnl AC_LIB_ARG_WITH is synonymous to AC_ARG_WITH in autoconf-2.13, and +dnl similar to AC_ARG_WITH in autoconf 2.52...2.57 except that is doesn't +dnl require excessive bracketing. +ifdef([AC_HELP_STRING], +[AC_DEFUN([AC_LIB_ARG_WITH], [AC_ARG_WITH([$1],[[$2]],[$3],[$4])])], +[AC_DEFUN([AC_][LIB_ARG_WITH], [AC_ARG_WITH([$1],[$2],[$3],[$4])])]) + +dnl AC_LIB_PREFIX adds to the CPPFLAGS and LDFLAGS the flags that are needed +dnl to access previously installed libraries. The basic assumption is that +dnl a user will want packages to use other packages he previously installed +dnl with the same --prefix option. +dnl This macro is not needed if only AC_LIB_LINKFLAGS is used to locate +dnl libraries, but is otherwise very convenient. +AC_DEFUN([AC_LIB_PREFIX], +[ + AC_BEFORE([$0], [AC_LIB_LINKFLAGS]) + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AC_CANONICAL_HOST]) + AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + dnl By default, look in $includedir and $libdir. + use_additional=yes + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + AC_LIB_ARG_WITH([lib-prefix], +[ --with-lib-prefix[=DIR] search for libraries in DIR/include and DIR/lib + --without-lib-prefix don't search for libraries in includedir and libdir], +[ + if test "X$withval" = "Xno"; then + use_additional=no + else + if test "X$withval" = "X"; then + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + else + additional_includedir="$withval/include" + additional_libdir="$withval/$acl_libdirstem" + fi + fi +]) + if test $use_additional = yes; then + dnl Potentially add $additional_includedir to $CPPFLAGS. + dnl But don't add it + dnl 1. if it's the standard /usr/include, + dnl 2. if it's already present in $CPPFLAGS, + dnl 3. if it's /usr/local/include and we are using GCC on Linux, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_includedir" != "X/usr/include"; then + haveit= + for x in $CPPFLAGS; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-I$additional_includedir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test "X$additional_includedir" = "X/usr/local/include"; then + if test -n "$GCC"; then + case $host_os in + linux* | gnu* | k*bsd*-gnu) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + if test -d "$additional_includedir"; then + dnl Really add $additional_includedir to $CPPFLAGS. + CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }-I$additional_includedir" + fi + fi + fi + fi + dnl Potentially add $additional_libdir to $LDFLAGS. + dnl But don't add it + dnl 1. if it's the standard /usr/lib, + dnl 2. if it's already present in $LDFLAGS, + dnl 3. if it's /usr/local/lib and we are using GCC on Linux, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then + haveit= + for x in $LDFLAGS; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then + if test -n "$GCC"; then + case $host_os in + linux*) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + dnl Really add $additional_libdir to $LDFLAGS. + LDFLAGS="${LDFLAGS}${LDFLAGS:+ }-L$additional_libdir" + fi + fi + fi + fi + fi +]) + +dnl AC_LIB_PREPARE_PREFIX creates variables acl_final_prefix, +dnl acl_final_exec_prefix, containing the values to which $prefix and +dnl $exec_prefix will expand at the end of the configure script. +AC_DEFUN([AC_LIB_PREPARE_PREFIX], +[ + dnl Unfortunately, prefix and exec_prefix get only finally determined + dnl at the end of configure. + if test "X$prefix" = "XNONE"; then + acl_final_prefix="$ac_default_prefix" + else + acl_final_prefix="$prefix" + fi + if test "X$exec_prefix" = "XNONE"; then + acl_final_exec_prefix='${prefix}' + else + acl_final_exec_prefix="$exec_prefix" + fi + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + eval acl_final_exec_prefix=\"$acl_final_exec_prefix\" + prefix="$acl_save_prefix" +]) + +dnl AC_LIB_WITH_FINAL_PREFIX([statement]) evaluates statement, with the +dnl variables prefix and exec_prefix bound to the values they will have +dnl at the end of the configure script. +AC_DEFUN([AC_LIB_WITH_FINAL_PREFIX], +[ + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + $1 + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" +]) + +dnl AC_LIB_PREPARE_MULTILIB creates +dnl - a variable acl_libdirstem, containing the basename of the libdir, either +dnl "lib" or "lib64" or "lib/64", +dnl - a variable acl_libdirstem2, as a secondary possible value for +dnl acl_libdirstem, either the same as acl_libdirstem or "lib/sparcv9" or +dnl "lib/amd64". +AC_DEFUN([AC_LIB_PREPARE_MULTILIB], +[ + dnl There is no formal standard regarding lib and lib64. + dnl On glibc systems, the current practice is that on a system supporting + dnl 32-bit and 64-bit instruction sets or ABIs, 64-bit libraries go under + dnl $prefix/lib64 and 32-bit libraries go under $prefix/lib. We determine + dnl the compiler's default mode by looking at the compiler's library search + dnl path. If at least one of its elements ends in /lib64 or points to a + dnl directory whose absolute pathname ends in /lib64, we assume a 64-bit ABI. + dnl Otherwise we use the default, namely "lib". + dnl On Solaris systems, the current practice is that on a system supporting + dnl 32-bit and 64-bit instruction sets or ABIs, 64-bit libraries go under + dnl $prefix/lib/64 (which is a symlink to either $prefix/lib/sparcv9 or + dnl $prefix/lib/amd64) and 32-bit libraries go under $prefix/lib. + AC_REQUIRE([AC_CANONICAL_HOST]) + acl_libdirstem=lib + acl_libdirstem2= + case "$host_os" in + solaris*) + dnl See Solaris 10 Software Developer Collection > Solaris 64-bit Developer's Guide > The Development Environment + dnl . + dnl "Portable Makefiles should refer to any library directories using the 64 symbolic link." + dnl But we want to recognize the sparcv9 or amd64 subdirectory also if the + dnl symlink is missing, so we set acl_libdirstem2 too. + AC_CACHE_CHECK([for 64-bit host], [gl_cv_solaris_64bit], + [AC_EGREP_CPP([sixtyfour bits], [ +#ifdef _LP64 +sixtyfour bits +#endif + ], [gl_cv_solaris_64bit=yes], [gl_cv_solaris_64bit=no]) + ]) + if test $gl_cv_solaris_64bit = yes; then + acl_libdirstem=lib/64 + case "$host_cpu" in + sparc*) acl_libdirstem2=lib/sparcv9 ;; + i*86 | x86_64) acl_libdirstem2=lib/amd64 ;; + esac + fi + ;; + *) + searchpath=`(LC_ALL=C $CC -print-search-dirs) 2>/dev/null | sed -n -e 's,^libraries: ,,p' | sed -e 's,^=,,'` + if test -n "$searchpath"; then + acl_save_IFS="${IFS= }"; IFS=":" + for searchdir in $searchpath; do + if test -d "$searchdir"; then + case "$searchdir" in + */lib64/ | */lib64 ) acl_libdirstem=lib64 ;; + */../ | */.. ) + # Better ignore directories of this form. They are misleading. + ;; + *) searchdir=`cd "$searchdir" && pwd` + case "$searchdir" in + */lib64 ) acl_libdirstem=lib64 ;; + esac ;; + esac + fi + done + IFS="$acl_save_IFS" + fi + ;; + esac + test -n "$acl_libdirstem2" || acl_libdirstem2="$acl_libdirstem" +]) diff --git a/m4/lock.m4 b/m4/lock.m4 new file mode 100644 index 00000000..9da8465e --- /dev/null +++ b/m4/lock.m4 @@ -0,0 +1,37 @@ +# lock.m4 serial 10 (gettext-0.18) +dnl Copyright (C) 2005-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +AC_DEFUN([gl_LOCK], +[ + AC_REQUIRE([gl_THREADLIB]) + if test "$gl_threads_api" = posix; then + # OSF/1 4.0 and MacOS X 10.1 lack the pthread_rwlock_t type and the + # pthread_rwlock_* functions. + AC_CHECK_TYPE([pthread_rwlock_t], + [AC_DEFINE([HAVE_PTHREAD_RWLOCK], [1], + [Define if the POSIX multithreading library has read/write locks.])], + [], + [#include ]) + # glibc defines PTHREAD_MUTEX_RECURSIVE as enum, not as a macro. + AC_TRY_COMPILE([#include ], + [#if __FreeBSD__ == 4 +error "No, in FreeBSD 4.0 recursive mutexes actually don't work." +#else +int x = (int)PTHREAD_MUTEX_RECURSIVE; +return !x; +#endif], + [AC_DEFINE([HAVE_PTHREAD_MUTEX_RECURSIVE], [1], + [Define if the defines PTHREAD_MUTEX_RECURSIVE.])]) + fi + gl_PREREQ_LOCK +]) + +# Prerequisites of lib/lock.c. +AC_DEFUN([gl_PREREQ_LOCK], [ + AC_REQUIRE([AC_C_INLINE]) +]) diff --git a/m4/longlong.m4 b/m4/longlong.m4 new file mode 100644 index 00000000..cca3c1a9 --- /dev/null +++ b/m4/longlong.m4 @@ -0,0 +1,106 @@ +# longlong.m4 serial 14 +dnl Copyright (C) 1999-2007, 2009-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Paul Eggert. + +# Define HAVE_LONG_LONG_INT if 'long long int' works. +# This fixes a bug in Autoconf 2.61, but can be removed once we +# assume 2.62 everywhere. + +# Note: If the type 'long long int' exists but is only 32 bits large +# (as on some very old compilers), HAVE_LONG_LONG_INT will not be +# defined. In this case you can treat 'long long int' like 'long int'. + +AC_DEFUN([AC_TYPE_LONG_LONG_INT], +[ + AC_CACHE_CHECK([for long long int], [ac_cv_type_long_long_int], + [AC_LINK_IFELSE( + [_AC_TYPE_LONG_LONG_SNIPPET], + [dnl This catches a bug in Tandem NonStop Kernel (OSS) cc -O circa 2004. + dnl If cross compiling, assume the bug isn't important, since + dnl nobody cross compiles for this platform as far as we know. + AC_RUN_IFELSE( + [AC_LANG_PROGRAM( + [[@%:@include + @%:@ifndef LLONG_MAX + @%:@ define HALF \ + (1LL << (sizeof (long long int) * CHAR_BIT - 2)) + @%:@ define LLONG_MAX (HALF - 1 + HALF) + @%:@endif]], + [[long long int n = 1; + int i; + for (i = 0; ; i++) + { + long long int m = n << i; + if (m >> i != n) + return 1; + if (LLONG_MAX / 2 < m) + break; + } + return 0;]])], + [ac_cv_type_long_long_int=yes], + [ac_cv_type_long_long_int=no], + [ac_cv_type_long_long_int=yes])], + [ac_cv_type_long_long_int=no])]) + if test $ac_cv_type_long_long_int = yes; then + AC_DEFINE([HAVE_LONG_LONG_INT], [1], + [Define to 1 if the system has the type `long long int'.]) + fi +]) + +# Define HAVE_UNSIGNED_LONG_LONG_INT if 'unsigned long long int' works. +# This fixes a bug in Autoconf 2.61, but can be removed once we +# assume 2.62 everywhere. + +# Note: If the type 'unsigned long long int' exists but is only 32 bits +# large (as on some very old compilers), AC_TYPE_UNSIGNED_LONG_LONG_INT +# will not be defined. In this case you can treat 'unsigned long long int' +# like 'unsigned long int'. + +AC_DEFUN([AC_TYPE_UNSIGNED_LONG_LONG_INT], +[ + AC_CACHE_CHECK([for unsigned long long int], + [ac_cv_type_unsigned_long_long_int], + [AC_LINK_IFELSE( + [_AC_TYPE_LONG_LONG_SNIPPET], + [ac_cv_type_unsigned_long_long_int=yes], + [ac_cv_type_unsigned_long_long_int=no])]) + if test $ac_cv_type_unsigned_long_long_int = yes; then + AC_DEFINE([HAVE_UNSIGNED_LONG_LONG_INT], [1], + [Define to 1 if the system has the type `unsigned long long int'.]) + fi +]) + +# Expands to a C program that can be used to test for simultaneous support +# of 'long long' and 'unsigned long long'. We don't want to say that +# 'long long' is available if 'unsigned long long' is not, or vice versa, +# because too many programs rely on the symmetry between signed and unsigned +# integer types (excluding 'bool'). +AC_DEFUN([_AC_TYPE_LONG_LONG_SNIPPET], +[ + AC_LANG_PROGRAM( + [[/* For now, do not test the preprocessor; as of 2007 there are too many + implementations with broken preprocessors. Perhaps this can + be revisited in 2012. In the meantime, code should not expect + #if to work with literals wider than 32 bits. */ + /* Test literals. */ + long long int ll = 9223372036854775807ll; + long long int nll = -9223372036854775807LL; + unsigned long long int ull = 18446744073709551615ULL; + /* Test constant expressions. */ + typedef int a[((-9223372036854775807LL < 0 && 0 < 9223372036854775807ll) + ? 1 : -1)]; + typedef int b[(18446744073709551615ULL <= (unsigned long long int) -1 + ? 1 : -1)]; + int i = 63;]], + [[/* Test availability of runtime routines for shift and division. */ + long long int llmax = 9223372036854775807ll; + unsigned long long int ullmax = 18446744073709551615ull; + return ((ll << 63) | (ll >> 63) | (ll < i) | (ll > i) + | (llmax / ll) | (llmax % ll) + | (ull << 63) | (ull >> 63) | (ull << i) | (ull >> i) + | (ullmax / ull) | (ullmax % ull));]]) +]) diff --git a/m4/nls.m4 b/m4/nls.m4 new file mode 100644 index 00000000..afdb9cac --- /dev/null +++ b/m4/nls.m4 @@ -0,0 +1,32 @@ +# nls.m4 serial 5 (gettext-0.18) +dnl Copyright (C) 1995-2003, 2005-2006, 2008-2014, 2016 Free Software +dnl Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. +dnl +dnl This file can be used in projects which are not available under +dnl the GNU General Public License or the GNU Library General Public +dnl License but which still want to provide support for the GNU gettext +dnl functionality. +dnl Please note that the actual code of the GNU gettext library is covered +dnl by the GNU Library General Public License, and the rest of the GNU +dnl gettext package is covered by the GNU General Public License. +dnl They are *not* in the public domain. + +dnl Authors: +dnl Ulrich Drepper , 1995-2000. +dnl Bruno Haible , 2000-2003. + +AC_PREREQ([2.50]) + +AC_DEFUN([AM_NLS], +[ + AC_MSG_CHECKING([whether NLS is requested]) + dnl Default is enabled NLS + AC_ARG_ENABLE([nls], + [ --disable-nls do not use Native Language Support], + USE_NLS=$enableval, USE_NLS=yes) + AC_MSG_RESULT([$USE_NLS]) + AC_SUBST([USE_NLS]) +]) diff --git a/m4/pkg.m4 b/m4/pkg.m4 new file mode 100644 index 00000000..4688002e --- /dev/null +++ b/m4/pkg.m4 @@ -0,0 +1,275 @@ +dnl pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +dnl serial 11 (pkg-config-0.29) +dnl +dnl Copyright © 2004 Scott James Remnant . +dnl Copyright © 2012-2015 Dan Nicholson +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2 of the License, or +dnl (at your option) any later version. +dnl +dnl This program is distributed in the hope that it will be useful, but +dnl WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +dnl 02111-1307, USA. +dnl +dnl As a special exception to the GNU General Public License, if you +dnl distribute this file as part of a program that contains a +dnl configuration script generated by Autoconf, you may include it under +dnl the same distribution terms that you use for the rest of that +dnl program. + +dnl PKG_PREREQ(MIN-VERSION) +dnl ----------------------- +dnl Since: 0.29 +dnl +dnl Verify that the version of the pkg-config macros are at least +dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's +dnl installed version of pkg-config, this checks the developer's version +dnl of pkg.m4 when generating configure. +dnl +dnl To ensure that this macro is defined, also add: +dnl m4_ifndef([PKG_PREREQ], +dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) +dnl +dnl See the "Since" comment for each macro you use to see what version +dnl of the macros you require. +m4_defun([PKG_PREREQ], +[m4_define([PKG_MACROS_VERSION], [0.29]) +m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, + [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) +])dnl PKG_PREREQ + +dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) +dnl ---------------------------------- +dnl Since: 0.16 +dnl +dnl Search for the pkg-config tool and set the PKG_CONFIG variable to +dnl first found in the path. Checks that the version of pkg-config found +dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is +dnl used since that's the first version where most current features of +dnl pkg-config existed. +AC_DEFUN([PKG_PROG_PKG_CONFIG], +[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) +m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) +AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) +AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=m4_default([$1], [0.9.0]) + AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi +fi[]dnl +])dnl PKG_PROG_PKG_CONFIG + +dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ------------------------------------------------------------------- +dnl Since: 0.18 +dnl +dnl Check to see whether a particular set of modules exists. Similar to +dnl PKG_CHECK_MODULES(), but does not set variables or print errors. +dnl +dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +dnl only at the first occurence in configure.ac, so if the first place +dnl it's called might be skipped (such as if it is within an "if", you +dnl have to call PKG_CHECK_EXISTS manually +AC_DEFUN([PKG_CHECK_EXISTS], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then + m4_default([$2], [:]) +m4_ifvaln([$3], [else + $3])dnl +fi]) + +dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) +dnl --------------------------------------------- +dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting +dnl pkg_failed based on the result. +m4_define([_PKG_CONFIG], +[if test -n "$$1"; then + pkg_cv_[]$1="$$1" + elif test -n "$PKG_CONFIG"; then + PKG_CHECK_EXISTS([$3], + [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes ], + [pkg_failed=yes]) + else + pkg_failed=untried +fi[]dnl +])dnl _PKG_CONFIG + +dnl _PKG_SHORT_ERRORS_SUPPORTED +dnl --------------------------- +dnl Internal check to see if pkg-config supports short errors. +AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi[]dnl +])dnl _PKG_SHORT_ERRORS_SUPPORTED + + +dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +dnl [ACTION-IF-NOT-FOUND]) +dnl -------------------------------------------------------------- +dnl Since: 0.4.0 +dnl +dnl Note that if there is a possibility the first call to +dnl PKG_CHECK_MODULES might not happen, you should be sure to include an +dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac +AC_DEFUN([PKG_CHECK_MODULES], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + +pkg_failed=no +AC_MSG_CHECKING([for $1]) + +_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) +_PKG_CONFIG([$1][_LIBS], [libs], [$2]) + +m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS +and $1[]_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details.]) + +if test $pkg_failed = yes; then + AC_MSG_RESULT([no]) + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + + m4_default([$4], [AC_MSG_ERROR( +[Package requirements ($2) were not met: + +$$1_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +_PKG_TEXT])[]dnl + ]) +elif test $pkg_failed = untried; then + AC_MSG_RESULT([no]) + m4_default([$4], [AC_MSG_FAILURE( +[The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +_PKG_TEXT + +To get pkg-config, see .])[]dnl + ]) +else + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + AC_MSG_RESULT([yes]) + $3 +fi[]dnl +])dnl PKG_CHECK_MODULES + + +dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +dnl [ACTION-IF-NOT-FOUND]) +dnl --------------------------------------------------------------------- +dnl Since: 0.29 +dnl +dnl Checks for existence of MODULES and gathers its build flags with +dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags +dnl and VARIABLE-PREFIX_LIBS from --libs. +dnl +dnl Note that if there is a possibility the first call to +dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to +dnl include an explicit call to PKG_PROG_PKG_CONFIG in your +dnl configure.ac. +AC_DEFUN([PKG_CHECK_MODULES_STATIC], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +_save_PKG_CONFIG=$PKG_CONFIG +PKG_CONFIG="$PKG_CONFIG --static" +PKG_CHECK_MODULES($@) +PKG_CONFIG=$_save_PKG_CONFIG[]dnl +])dnl PKG_CHECK_MODULES_STATIC + + +dnl PKG_INSTALLDIR([DIRECTORY]) +dnl ------------------------- +dnl Since: 0.27 +dnl +dnl Substitutes the variable pkgconfigdir as the location where a module +dnl should install pkg-config .pc files. By default the directory is +dnl $libdir/pkgconfig, but the default can be changed by passing +dnl DIRECTORY. The user can override through the --with-pkgconfigdir +dnl parameter. +AC_DEFUN([PKG_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([pkgconfigdir], + [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, + [with_pkgconfigdir=]pkg_default) +AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +])dnl PKG_INSTALLDIR + + +dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) +dnl -------------------------------- +dnl Since: 0.27 +dnl +dnl Substitutes the variable noarch_pkgconfigdir as the location where a +dnl module should install arch-independent pkg-config .pc files. By +dnl default the directory is $datadir/pkgconfig, but the default can be +dnl changed by passing DIRECTORY. The user can override through the +dnl --with-noarch-pkgconfigdir parameter. +AC_DEFUN([PKG_NOARCH_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([noarch-pkgconfigdir], + [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, + [with_noarch_pkgconfigdir=]pkg_default) +AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +])dnl PKG_NOARCH_INSTALLDIR + + +dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, +dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ------------------------------------------- +dnl Since: 0.28 +dnl +dnl Retrieves the value of the pkg-config variable for the given module. +AC_DEFUN([PKG_CHECK_VAR], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl + +_PKG_CONFIG([$1], [variable="][$3]["], [$2]) +AS_VAR_COPY([$1], [pkg_cv_][$1]) + +AS_VAR_IF([$1], [""], [$5], [$4])dnl +])dnl PKG_CHECK_VAR diff --git a/m4/po.m4 b/m4/po.m4 new file mode 100644 index 00000000..c5a2f6bf --- /dev/null +++ b/m4/po.m4 @@ -0,0 +1,453 @@ +# po.m4 serial 24 (gettext-0.19) +dnl Copyright (C) 1995-2014, 2016 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. +dnl +dnl This file can be used in projects which are not available under +dnl the GNU General Public License or the GNU Library General Public +dnl License but which still want to provide support for the GNU gettext +dnl functionality. +dnl Please note that the actual code of the GNU gettext library is covered +dnl by the GNU Library General Public License, and the rest of the GNU +dnl gettext package is covered by the GNU General Public License. +dnl They are *not* in the public domain. + +dnl Authors: +dnl Ulrich Drepper , 1995-2000. +dnl Bruno Haible , 2000-2003. + +AC_PREREQ([2.60]) + +dnl Checks for all prerequisites of the po subdirectory. +AC_DEFUN([AM_PO_SUBDIRS], +[ + AC_REQUIRE([AC_PROG_MAKE_SET])dnl + AC_REQUIRE([AC_PROG_INSTALL])dnl + AC_REQUIRE([AC_PROG_MKDIR_P])dnl + AC_REQUIRE([AC_PROG_SED])dnl + AC_REQUIRE([AM_NLS])dnl + + dnl Release version of the gettext macros. This is used to ensure that + dnl the gettext macros and po/Makefile.in.in are in sync. + AC_SUBST([GETTEXT_MACRO_VERSION], [0.19]) + + dnl Perform the following tests also if --disable-nls has been given, + dnl because they are needed for "make dist" to work. + + dnl Search for GNU msgfmt in the PATH. + dnl The first test excludes Solaris msgfmt and early GNU msgfmt versions. + dnl The second test excludes FreeBSD msgfmt. + AM_PATH_PROG_WITH_TEST(MSGFMT, msgfmt, + [$ac_dir/$ac_word --statistics /dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1 && + (if $ac_dir/$ac_word --statistics /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi)], + :) + AC_PATH_PROG([GMSGFMT], [gmsgfmt], [$MSGFMT]) + + dnl Test whether it is GNU msgfmt >= 0.15. +changequote(,)dnl + case `$MSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in + '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) MSGFMT_015=: ;; + *) MSGFMT_015=$MSGFMT ;; + esac +changequote([,])dnl + AC_SUBST([MSGFMT_015]) +changequote(,)dnl + case `$GMSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in + '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) GMSGFMT_015=: ;; + *) GMSGFMT_015=$GMSGFMT ;; + esac +changequote([,])dnl + AC_SUBST([GMSGFMT_015]) + + dnl Search for GNU xgettext 0.12 or newer in the PATH. + dnl The first test excludes Solaris xgettext and early GNU xgettext versions. + dnl The second test excludes FreeBSD xgettext. + AM_PATH_PROG_WITH_TEST(XGETTEXT, xgettext, + [$ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1 && + (if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi)], + :) + dnl Remove leftover from FreeBSD xgettext call. + rm -f messages.po + + dnl Test whether it is GNU xgettext >= 0.15. +changequote(,)dnl + case `$XGETTEXT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in + '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) XGETTEXT_015=: ;; + *) XGETTEXT_015=$XGETTEXT ;; + esac +changequote([,])dnl + AC_SUBST([XGETTEXT_015]) + + dnl Search for GNU msgmerge 0.11 or newer in the PATH. + AM_PATH_PROG_WITH_TEST(MSGMERGE, msgmerge, + [$ac_dir/$ac_word --update -q /dev/null /dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1], :) + + dnl Installation directories. + dnl Autoconf >= 2.60 defines localedir. For older versions of autoconf, we + dnl have to define it here, so that it can be used in po/Makefile. + test -n "$localedir" || localedir='${datadir}/locale' + AC_SUBST([localedir]) + + dnl Support for AM_XGETTEXT_OPTION. + test -n "${XGETTEXT_EXTRA_OPTIONS+set}" || XGETTEXT_EXTRA_OPTIONS= + AC_SUBST([XGETTEXT_EXTRA_OPTIONS]) + + AC_CONFIG_COMMANDS([po-directories], [[ + for ac_file in $CONFIG_FILES; do + # Support "outfile[:infile[:infile...]]" + case "$ac_file" in + *:*) ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + esac + # PO directories have a Makefile.in generated from Makefile.in.in. + case "$ac_file" in */Makefile.in) + # Adjust a relative srcdir. + ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'` + ac_dir_suffix=/`echo "$ac_dir"|sed 's%^\./%%'` + ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'` + # In autoconf-2.13 it is called $ac_given_srcdir. + # In autoconf-2.50 it is called $srcdir. + test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir" + case "$ac_given_srcdir" in + .) top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;; + /*) top_srcdir="$ac_given_srcdir" ;; + *) top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + # Treat a directory as a PO directory if and only if it has a + # POTFILES.in file. This allows packages to have multiple PO + # directories under different names or in different locations. + if test -f "$ac_given_srcdir/$ac_dir/POTFILES.in"; then + rm -f "$ac_dir/POTFILES" + test -n "$as_me" && echo "$as_me: creating $ac_dir/POTFILES" || echo "creating $ac_dir/POTFILES" + gt_tab=`printf '\t'` + cat "$ac_given_srcdir/$ac_dir/POTFILES.in" | sed -e "/^#/d" -e "/^[ ${gt_tab}]*\$/d" -e "s,.*, $top_srcdir/& \\\\," | sed -e "\$s/\(.*\) \\\\/\1/" > "$ac_dir/POTFILES" + POMAKEFILEDEPS="POTFILES.in" + # ALL_LINGUAS, POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES depend + # on $ac_dir but don't depend on user-specified configuration + # parameters. + if test -f "$ac_given_srcdir/$ac_dir/LINGUAS"; then + # The LINGUAS file contains the set of available languages. + if test -n "$OBSOLETE_ALL_LINGUAS"; then + test -n "$as_me" && echo "$as_me: setting ALL_LINGUAS in configure.in is obsolete" || echo "setting ALL_LINGUAS in configure.in is obsolete" + fi + ALL_LINGUAS_=`sed -e "/^#/d" -e "s/#.*//" "$ac_given_srcdir/$ac_dir/LINGUAS"` + # Hide the ALL_LINGUAS assignment from automake < 1.5. + eval 'ALL_LINGUAS''=$ALL_LINGUAS_' + POMAKEFILEDEPS="$POMAKEFILEDEPS LINGUAS" + else + # The set of available languages was given in configure.in. + # Hide the ALL_LINGUAS assignment from automake < 1.5. + eval 'ALL_LINGUAS''=$OBSOLETE_ALL_LINGUAS' + fi + # Compute POFILES + # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).po) + # Compute UPDATEPOFILES + # as $(foreach lang, $(ALL_LINGUAS), $(lang).po-update) + # Compute DUMMYPOFILES + # as $(foreach lang, $(ALL_LINGUAS), $(lang).nop) + # Compute GMOFILES + # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).gmo) + case "$ac_given_srcdir" in + .) srcdirpre= ;; + *) srcdirpre='$(srcdir)/' ;; + esac + POFILES= + UPDATEPOFILES= + DUMMYPOFILES= + GMOFILES= + for lang in $ALL_LINGUAS; do + POFILES="$POFILES $srcdirpre$lang.po" + UPDATEPOFILES="$UPDATEPOFILES $lang.po-update" + DUMMYPOFILES="$DUMMYPOFILES $lang.nop" + GMOFILES="$GMOFILES $srcdirpre$lang.gmo" + done + # CATALOGS depends on both $ac_dir and the user's LINGUAS + # environment variable. + INST_LINGUAS= + if test -n "$ALL_LINGUAS"; then + for presentlang in $ALL_LINGUAS; do + useit=no + if test "%UNSET%" != "$LINGUAS"; then + desiredlanguages="$LINGUAS" + else + desiredlanguages="$ALL_LINGUAS" + fi + for desiredlang in $desiredlanguages; do + # Use the presentlang catalog if desiredlang is + # a. equal to presentlang, or + # b. a variant of presentlang (because in this case, + # presentlang can be used as a fallback for messages + # which are not translated in the desiredlang catalog). + case "$desiredlang" in + "$presentlang"*) useit=yes;; + esac + done + if test $useit = yes; then + INST_LINGUAS="$INST_LINGUAS $presentlang" + fi + done + fi + CATALOGS= + if test -n "$INST_LINGUAS"; then + for lang in $INST_LINGUAS; do + CATALOGS="$CATALOGS $lang.gmo" + done + fi + test -n "$as_me" && echo "$as_me: creating $ac_dir/Makefile" || echo "creating $ac_dir/Makefile" + sed -e "/^POTFILES =/r $ac_dir/POTFILES" -e "/^# Makevars/r $ac_given_srcdir/$ac_dir/Makevars" -e "s|@POFILES@|$POFILES|g" -e "s|@UPDATEPOFILES@|$UPDATEPOFILES|g" -e "s|@DUMMYPOFILES@|$DUMMYPOFILES|g" -e "s|@GMOFILES@|$GMOFILES|g" -e "s|@CATALOGS@|$CATALOGS|g" -e "s|@POMAKEFILEDEPS@|$POMAKEFILEDEPS|g" "$ac_dir/Makefile.in" > "$ac_dir/Makefile" + for f in "$ac_given_srcdir/$ac_dir"/Rules-*; do + if test -f "$f"; then + case "$f" in + *.orig | *.bak | *~) ;; + *) cat "$f" >> "$ac_dir/Makefile" ;; + esac + fi + done + fi + ;; + esac + done]], + [# Capture the value of obsolete ALL_LINGUAS because we need it to compute + # POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES, CATALOGS. But hide it + # from automake < 1.5. + eval 'OBSOLETE_ALL_LINGUAS''="$ALL_LINGUAS"' + # Capture the value of LINGUAS because we need it to compute CATALOGS. + LINGUAS="${LINGUAS-%UNSET%}" + ]) +]) + +dnl Postprocesses a Makefile in a directory containing PO files. +AC_DEFUN([AM_POSTPROCESS_PO_MAKEFILE], +[ + # When this code is run, in config.status, two variables have already been + # set: + # - OBSOLETE_ALL_LINGUAS is the value of LINGUAS set in configure.in, + # - LINGUAS is the value of the environment variable LINGUAS at configure + # time. + +changequote(,)dnl + # Adjust a relative srcdir. + ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'` + ac_dir_suffix=/`echo "$ac_dir"|sed 's%^\./%%'` + ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'` + # In autoconf-2.13 it is called $ac_given_srcdir. + # In autoconf-2.50 it is called $srcdir. + test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir" + case "$ac_given_srcdir" in + .) top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;; + /*) top_srcdir="$ac_given_srcdir" ;; + *) top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + # Find a way to echo strings without interpreting backslash. + if test "X`(echo '\t') 2>/dev/null`" = 'X\t'; then + gt_echo='echo' + else + if test "X`(printf '%s\n' '\t') 2>/dev/null`" = 'X\t'; then + gt_echo='printf %s\n' + else + echo_func () { + cat < "$ac_file.tmp" + tab=`printf '\t'` + if grep -l '@TCLCATALOGS@' "$ac_file" > /dev/null; then + # Add dependencies that cannot be formulated as a simple suffix rule. + for lang in $ALL_LINGUAS; do + frobbedlang=`echo $lang | sed -e 's/\..*$//' -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'` + cat >> "$ac_file.tmp" < /dev/null; then + # Add dependencies that cannot be formulated as a simple suffix rule. + for lang in $ALL_LINGUAS; do + frobbedlang=`echo $lang | sed -e 's/_/-/g' -e 's/^sr-CS/sr-SP/' -e 's/@latin$/-Latn/' -e 's/@cyrillic$/-Cyrl/' -e 's/^sr-SP$/sr-SP-Latn/' -e 's/^uz-UZ$/uz-UZ-Latn/'` + cat >> "$ac_file.tmp" <> "$ac_file.tmp" < +#include +/* The string "%2$d %1$d", with dollar characters protected from the shell's + dollar expansion (possibly an autoconf bug). */ +static char format[] = { '%', '2', '$', 'd', ' ', '%', '1', '$', 'd', '\0' }; +static char buf[100]; +int main () +{ + sprintf (buf, format, 33, 55); + return (strcmp (buf, "55 33") != 0); +}], gt_cv_func_printf_posix=yes, gt_cv_func_printf_posix=no, + [ + AC_EGREP_CPP([notposix], [ +#if defined __NetBSD__ || defined __BEOS__ || defined _MSC_VER || defined __MINGW32__ || defined __CYGWIN__ + notposix +#endif + ], + [gt_cv_func_printf_posix="guessing no"], + [gt_cv_func_printf_posix="guessing yes"]) + ]) + ]) + case $gt_cv_func_printf_posix in + *yes) + AC_DEFINE([HAVE_POSIX_PRINTF], [1], + [Define if your printf() function supports format strings with positions.]) + ;; + esac +]) diff --git a/m4/progtest.m4 b/m4/progtest.m4 new file mode 100644 index 00000000..9ace7c34 --- /dev/null +++ b/m4/progtest.m4 @@ -0,0 +1,91 @@ +# progtest.m4 serial 7 (gettext-0.18.2) +dnl Copyright (C) 1996-2003, 2005, 2008-2016 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. +dnl +dnl This file can be used in projects which are not available under +dnl the GNU General Public License or the GNU Library General Public +dnl License but which still want to provide support for the GNU gettext +dnl functionality. +dnl Please note that the actual code of the GNU gettext library is covered +dnl by the GNU Library General Public License, and the rest of the GNU +dnl gettext package is covered by the GNU General Public License. +dnl They are *not* in the public domain. + +dnl Authors: +dnl Ulrich Drepper , 1996. + +AC_PREREQ([2.50]) + +# Search path for a program which passes the given test. + +dnl AM_PATH_PROG_WITH_TEST(VARIABLE, PROG-TO-CHECK-FOR, +dnl TEST-PERFORMED-ON-FOUND_PROGRAM [, VALUE-IF-NOT-FOUND [, PATH]]) +AC_DEFUN([AM_PATH_PROG_WITH_TEST], +[ +# Prepare PATH_SEPARATOR. +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + # Determine PATH_SEPARATOR by trying to find /bin/sh in a PATH which + # contains only /bin. Note that ksh looks also at the FPATH variable, + # so we have to set that as well for the test. + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 \ + && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 \ + || PATH_SEPARATOR=';' + } +fi + +# Find out how to test for executable files. Don't use a zero-byte file, +# as systems may use methods other than mode bits to determine executability. +cat >conf$$.file <<_ASEOF +#! /bin/sh +exit 0 +_ASEOF +chmod +x conf$$.file +if test -x conf$$.file >/dev/null 2>&1; then + ac_executable_p="test -x" +else + ac_executable_p="test -f" +fi +rm -f conf$$.file + +# Extract the first word of "$2", so it can be a program name with args. +set dummy $2; ac_word=[$]2 +AC_MSG_CHECKING([for $ac_word]) +AC_CACHE_VAL([ac_cv_path_$1], +[case "[$]$1" in + [[\\/]]* | ?:[[\\/]]*) + ac_cv_path_$1="[$]$1" # Let the user override the test with a path. + ;; + *) + ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in ifelse([$5], , $PATH, [$5]); do + IFS="$ac_save_IFS" + test -z "$ac_dir" && ac_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then + echo "$as_me: trying $ac_dir/$ac_word..." >&AS_MESSAGE_LOG_FD + if [$3]; then + ac_cv_path_$1="$ac_dir/$ac_word$ac_exec_ext" + break 2 + fi + fi + done + done + IFS="$ac_save_IFS" +dnl If no 4th arg is given, leave the cache variable unset, +dnl so AC_PATH_PROGS will keep looking. +ifelse([$4], , , [ test -z "[$]ac_cv_path_$1" && ac_cv_path_$1="$4" +])dnl + ;; +esac])dnl +$1="$ac_cv_path_$1" +if test ifelse([$4], , [-n "[$]$1"], ["[$]$1" != "$4"]); then + AC_MSG_RESULT([$][$1]) +else + AC_MSG_RESULT([no]) +fi +AC_SUBST([$1])dnl +]) diff --git a/m4/size_max.m4 b/m4/size_max.m4 new file mode 100644 index 00000000..ce992db1 --- /dev/null +++ b/m4/size_max.m4 @@ -0,0 +1,75 @@ +# size_max.m4 serial 9 +dnl Copyright (C) 2003, 2005-2006, 2008-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +AC_DEFUN([gl_SIZE_MAX], +[ + AC_CHECK_HEADERS([stdint.h]) + dnl First test whether the system already has SIZE_MAX. + AC_CACHE_CHECK([for SIZE_MAX], [gl_cv_size_max], [ + gl_cv_size_max= + AC_EGREP_CPP([Found it], [ +#include +#if HAVE_STDINT_H +#include +#endif +#ifdef SIZE_MAX +Found it +#endif +], [gl_cv_size_max=yes]) + if test -z "$gl_cv_size_max"; then + dnl Define it ourselves. Here we assume that the type 'size_t' is not wider + dnl than the type 'unsigned long'. Try hard to find a definition that can + dnl be used in a preprocessor #if, i.e. doesn't contain a cast. + AC_COMPUTE_INT([size_t_bits_minus_1], [sizeof (size_t) * CHAR_BIT - 1], + [#include +#include ], [size_t_bits_minus_1=]) + AC_COMPUTE_INT([fits_in_uint], [sizeof (size_t) <= sizeof (unsigned int)], + [#include ], [fits_in_uint=]) + if test -n "$size_t_bits_minus_1" && test -n "$fits_in_uint"; then + if test $fits_in_uint = 1; then + dnl Even though SIZE_MAX fits in an unsigned int, it must be of type + dnl 'unsigned long' if the type 'size_t' is the same as 'unsigned long'. + AC_TRY_COMPILE([#include + extern size_t foo; + extern unsigned long foo; + ], [], [fits_in_uint=0]) + fi + dnl We cannot use 'expr' to simplify this expression, because 'expr' + dnl works only with 'long' integers in the host environment, while we + dnl might be cross-compiling from a 32-bit platform to a 64-bit platform. + if test $fits_in_uint = 1; then + gl_cv_size_max="(((1U << $size_t_bits_minus_1) - 1) * 2 + 1)" + else + gl_cv_size_max="(((1UL << $size_t_bits_minus_1) - 1) * 2 + 1)" + fi + else + dnl Shouldn't happen, but who knows... + gl_cv_size_max='((size_t)~(size_t)0)' + fi + fi + ]) + if test "$gl_cv_size_max" != yes; then + AC_DEFINE_UNQUOTED([SIZE_MAX], [$gl_cv_size_max], + [Define as the maximum value of type 'size_t', if the system doesn't define it.]) + fi + dnl Don't redefine SIZE_MAX in config.h if config.h is re-included after + dnl . Remember that the #undef in AH_VERBATIM gets replaced with + dnl #define by AC_DEFINE_UNQUOTED. + AH_VERBATIM([SIZE_MAX], +[/* Define as the maximum value of type 'size_t', if the system doesn't define + it. */ +#ifndef SIZE_MAX +# undef SIZE_MAX +#endif]) +]) + +dnl Autoconf >= 2.61 has AC_COMPUTE_INT built-in. +dnl Remove this when we can assume autoconf >= 2.61. +m4_ifdef([AC_COMPUTE_INT], [], [ + AC_DEFUN([AC_COMPUTE_INT], [_AC_COMPUTE_INT([$2],[$1],[$3],[$4])]) +]) diff --git a/m4/stdint_h.m4 b/m4/stdint_h.m4 new file mode 100644 index 00000000..b8e3c6cc --- /dev/null +++ b/m4/stdint_h.m4 @@ -0,0 +1,26 @@ +# stdint_h.m4 serial 8 +dnl Copyright (C) 1997-2004, 2006, 2008-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Paul Eggert. + +# Define HAVE_STDINT_H_WITH_UINTMAX if exists, +# doesn't clash with , and declares uintmax_t. + +AC_DEFUN([gl_AC_HEADER_STDINT_H], +[ + AC_CACHE_CHECK([for stdint.h], [gl_cv_header_stdint_h], + [AC_TRY_COMPILE( + [#include +#include ], + [uintmax_t i = (uintmax_t) -1; return !i;], + [gl_cv_header_stdint_h=yes], + [gl_cv_header_stdint_h=no])]) + if test $gl_cv_header_stdint_h = yes; then + AC_DEFINE_UNQUOTED([HAVE_STDINT_H_WITH_UINTMAX], [1], + [Define if exists, doesn't clash with , + and declares uintmax_t. ]) + fi +]) diff --git a/m4/threadlib.m4 b/m4/threadlib.m4 new file mode 100644 index 00000000..05cc4ffa --- /dev/null +++ b/m4/threadlib.m4 @@ -0,0 +1,347 @@ +# threadlib.m4 serial 5 (gettext-0.18) +dnl Copyright (C) 2005-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +dnl gl_THREADLIB +dnl ------------ +dnl Tests for a multithreading library to be used. +dnl Defines at most one of the macros USE_POSIX_THREADS, USE_SOLARIS_THREADS, +dnl USE_PTH_THREADS, USE_WIN32_THREADS +dnl Sets the variables LIBTHREAD and LTLIBTHREAD to the linker options for use +dnl in a Makefile (LIBTHREAD for use without libtool, LTLIBTHREAD for use with +dnl libtool). +dnl Sets the variables LIBMULTITHREAD and LTLIBMULTITHREAD similarly, for +dnl programs that really need multithread functionality. The difference +dnl between LIBTHREAD and LIBMULTITHREAD is that on platforms supporting weak +dnl symbols, typically LIBTHREAD="" whereas LIBMULTITHREAD="-lpthread". +dnl Adds to CPPFLAGS the flag -D_REENTRANT or -D_THREAD_SAFE if needed for +dnl multithread-safe programs. + +AC_DEFUN([gl_THREADLIB_EARLY], +[ + AC_REQUIRE([gl_THREADLIB_EARLY_BODY]) +]) + +dnl The guts of gl_THREADLIB_EARLY. Needs to be expanded only once. + +AC_DEFUN([gl_THREADLIB_EARLY_BODY], +[ + dnl Ordering constraints: This macro modifies CPPFLAGS in a way that + dnl influences the result of the autoconf tests that test for *_unlocked + dnl declarations, on AIX 5 at least. Therefore it must come early. + AC_BEFORE([$0], [gl_FUNC_GLIBC_UNLOCKED_IO])dnl + AC_BEFORE([$0], [gl_ARGP])dnl + + AC_REQUIRE([AC_CANONICAL_HOST]) + dnl _GNU_SOURCE is needed for pthread_rwlock_t on glibc systems. + dnl AC_USE_SYSTEM_EXTENSIONS was introduced in autoconf 2.60 and obsoletes + dnl AC_GNU_SOURCE. + m4_ifdef([AC_USE_SYSTEM_EXTENSIONS], + [AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])], + [AC_REQUIRE([AC_GNU_SOURCE])]) + dnl Check for multithreading. + m4_divert_text([DEFAULTS], [gl_use_threads_default=]) + AC_ARG_ENABLE([threads], +AC_HELP_STRING([--enable-threads={posix|solaris|pth|win32}], [specify multithreading API]) +AC_HELP_STRING([--disable-threads], [build without multithread safety]), + [gl_use_threads=$enableval], + [if test -n "$gl_use_threads_default"; then + gl_use_threads="$gl_use_threads_default" + else +changequote(,)dnl + case "$host_os" in + dnl Disable multithreading by default on OSF/1, because it interferes + dnl with fork()/exec(): When msgexec is linked with -lpthread, its + dnl child process gets an endless segmentation fault inside execvp(). + dnl Disable multithreading by default on Cygwin 1.5.x, because it has + dnl bugs that lead to endless loops or crashes. See + dnl . + osf*) gl_use_threads=no ;; + cygwin*) + case `uname -r` in + 1.[0-5].*) gl_use_threads=no ;; + *) gl_use_threads=yes ;; + esac + ;; + *) gl_use_threads=yes ;; + esac +changequote([,])dnl + fi + ]) + if test "$gl_use_threads" = yes || test "$gl_use_threads" = posix; then + # For using : + case "$host_os" in + osf*) + # On OSF/1, the compiler needs the flag -D_REENTRANT so that it + # groks . cc also understands the flag -pthread, but + # we don't use it because 1. gcc-2.95 doesn't understand -pthread, + # 2. putting a flag into CPPFLAGS that has an effect on the linker + # causes the AC_TRY_LINK test below to succeed unexpectedly, + # leading to wrong values of LIBTHREAD and LTLIBTHREAD. + CPPFLAGS="$CPPFLAGS -D_REENTRANT" + ;; + esac + # Some systems optimize for single-threaded programs by default, and + # need special flags to disable these optimizations. For example, the + # definition of 'errno' in . + case "$host_os" in + aix* | freebsd*) CPPFLAGS="$CPPFLAGS -D_THREAD_SAFE" ;; + solaris*) CPPFLAGS="$CPPFLAGS -D_REENTRANT" ;; + esac + fi +]) + +dnl The guts of gl_THREADLIB. Needs to be expanded only once. + +AC_DEFUN([gl_THREADLIB_BODY], +[ + AC_REQUIRE([gl_THREADLIB_EARLY_BODY]) + gl_threads_api=none + LIBTHREAD= + LTLIBTHREAD= + LIBMULTITHREAD= + LTLIBMULTITHREAD= + if test "$gl_use_threads" != no; then + dnl Check whether the compiler and linker support weak declarations. + AC_CACHE_CHECK([whether imported symbols can be declared weak], + [gl_cv_have_weak], + [gl_cv_have_weak=no + dnl First, test whether the compiler accepts it syntactically. + AC_TRY_LINK([extern void xyzzy (); +#pragma weak xyzzy], [xyzzy();], [gl_cv_have_weak=maybe]) + if test $gl_cv_have_weak = maybe; then + dnl Second, test whether it actually works. On Cygwin 1.7.2, with + dnl gcc 4.3, symbols declared weak always evaluate to the address 0. + AC_TRY_RUN([ +#include +#pragma weak fputs +int main () +{ + return (fputs == NULL); +}], [gl_cv_have_weak=yes], [gl_cv_have_weak=no], + [dnl When cross-compiling, assume that only ELF platforms support + dnl weak symbols. + AC_EGREP_CPP([Extensible Linking Format], + [#ifdef __ELF__ + Extensible Linking Format + #endif + ], + [gl_cv_have_weak="guessing yes"], + [gl_cv_have_weak="guessing no"]) + ]) + fi + ]) + if test "$gl_use_threads" = yes || test "$gl_use_threads" = posix; then + # On OSF/1, the compiler needs the flag -pthread or -D_REENTRANT so that + # it groks . It's added above, in gl_THREADLIB_EARLY_BODY. + AC_CHECK_HEADER([pthread.h], + [gl_have_pthread_h=yes], [gl_have_pthread_h=no]) + if test "$gl_have_pthread_h" = yes; then + # Other possible tests: + # -lpthreads (FSU threads, PCthreads) + # -lgthreads + gl_have_pthread= + # Test whether both pthread_mutex_lock and pthread_mutexattr_init exist + # in libc. IRIX 6.5 has the first one in both libc and libpthread, but + # the second one only in libpthread, and lock.c needs it. + AC_TRY_LINK([#include ], + [pthread_mutex_lock((pthread_mutex_t*)0); + pthread_mutexattr_init((pthread_mutexattr_t*)0);], + [gl_have_pthread=yes]) + # Test for libpthread by looking for pthread_kill. (Not pthread_self, + # since it is defined as a macro on OSF/1.) + if test -n "$gl_have_pthread"; then + # The program links fine without libpthread. But it may actually + # need to link with libpthread in order to create multiple threads. + AC_CHECK_LIB([pthread], [pthread_kill], + [LIBMULTITHREAD=-lpthread LTLIBMULTITHREAD=-lpthread + # On Solaris and HP-UX, most pthread functions exist also in libc. + # Therefore pthread_in_use() needs to actually try to create a + # thread: pthread_create from libc will fail, whereas + # pthread_create will actually create a thread. + case "$host_os" in + solaris* | hpux*) + AC_DEFINE([PTHREAD_IN_USE_DETECTION_HARD], [1], + [Define if the pthread_in_use() detection is hard.]) + esac + ]) + else + # Some library is needed. Try libpthread and libc_r. + AC_CHECK_LIB([pthread], [pthread_kill], + [gl_have_pthread=yes + LIBTHREAD=-lpthread LTLIBTHREAD=-lpthread + LIBMULTITHREAD=-lpthread LTLIBMULTITHREAD=-lpthread]) + if test -z "$gl_have_pthread"; then + # For FreeBSD 4. + AC_CHECK_LIB([c_r], [pthread_kill], + [gl_have_pthread=yes + LIBTHREAD=-lc_r LTLIBTHREAD=-lc_r + LIBMULTITHREAD=-lc_r LTLIBMULTITHREAD=-lc_r]) + fi + fi + if test -n "$gl_have_pthread"; then + gl_threads_api=posix + AC_DEFINE([USE_POSIX_THREADS], [1], + [Define if the POSIX multithreading library can be used.]) + if test -n "$LIBMULTITHREAD" || test -n "$LTLIBMULTITHREAD"; then + if case "$gl_cv_have_weak" in *yes) true;; *) false;; esac; then + AC_DEFINE([USE_POSIX_THREADS_WEAK], [1], + [Define if references to the POSIX multithreading library should be made weak.]) + LIBTHREAD= + LTLIBTHREAD= + fi + fi + fi + fi + fi + if test -z "$gl_have_pthread"; then + if test "$gl_use_threads" = yes || test "$gl_use_threads" = solaris; then + gl_have_solaristhread= + gl_save_LIBS="$LIBS" + LIBS="$LIBS -lthread" + AC_TRY_LINK([#include +#include ], + [thr_self();], + [gl_have_solaristhread=yes]) + LIBS="$gl_save_LIBS" + if test -n "$gl_have_solaristhread"; then + gl_threads_api=solaris + LIBTHREAD=-lthread + LTLIBTHREAD=-lthread + LIBMULTITHREAD="$LIBTHREAD" + LTLIBMULTITHREAD="$LTLIBTHREAD" + AC_DEFINE([USE_SOLARIS_THREADS], [1], + [Define if the old Solaris multithreading library can be used.]) + if case "$gl_cv_have_weak" in *yes) true;; *) false;; esac; then + AC_DEFINE([USE_SOLARIS_THREADS_WEAK], [1], + [Define if references to the old Solaris multithreading library should be made weak.]) + LIBTHREAD= + LTLIBTHREAD= + fi + fi + fi + fi + if test "$gl_use_threads" = pth; then + gl_save_CPPFLAGS="$CPPFLAGS" + AC_LIB_LINKFLAGS([pth]) + gl_have_pth= + gl_save_LIBS="$LIBS" + LIBS="$LIBS -lpth" + AC_TRY_LINK([#include ], [pth_self();], [gl_have_pth=yes]) + LIBS="$gl_save_LIBS" + if test -n "$gl_have_pth"; then + gl_threads_api=pth + LIBTHREAD="$LIBPTH" + LTLIBTHREAD="$LTLIBPTH" + LIBMULTITHREAD="$LIBTHREAD" + LTLIBMULTITHREAD="$LTLIBTHREAD" + AC_DEFINE([USE_PTH_THREADS], [1], + [Define if the GNU Pth multithreading library can be used.]) + if test -n "$LIBMULTITHREAD" || test -n "$LTLIBMULTITHREAD"; then + if case "$gl_cv_have_weak" in *yes) true;; *) false;; esac; then + AC_DEFINE([USE_PTH_THREADS_WEAK], [1], + [Define if references to the GNU Pth multithreading library should be made weak.]) + LIBTHREAD= + LTLIBTHREAD= + fi + fi + else + CPPFLAGS="$gl_save_CPPFLAGS" + fi + fi + if test -z "$gl_have_pthread"; then + if test "$gl_use_threads" = yes || test "$gl_use_threads" = win32; then + if { case "$host_os" in + mingw*) true;; + *) false;; + esac + }; then + gl_threads_api=win32 + AC_DEFINE([USE_WIN32_THREADS], [1], + [Define if the Win32 multithreading API can be used.]) + fi + fi + fi + fi + AC_MSG_CHECKING([for multithread API to use]) + AC_MSG_RESULT([$gl_threads_api]) + AC_SUBST([LIBTHREAD]) + AC_SUBST([LTLIBTHREAD]) + AC_SUBST([LIBMULTITHREAD]) + AC_SUBST([LTLIBMULTITHREAD]) +]) + +AC_DEFUN([gl_THREADLIB], +[ + AC_REQUIRE([gl_THREADLIB_EARLY]) + AC_REQUIRE([gl_THREADLIB_BODY]) +]) + + +dnl gl_DISABLE_THREADS +dnl ------------------ +dnl Sets the gl_THREADLIB default so that threads are not used by default. +dnl The user can still override it at installation time, by using the +dnl configure option '--enable-threads'. + +AC_DEFUN([gl_DISABLE_THREADS], [ + m4_divert_text([INIT_PREPARE], [gl_use_threads_default=no]) +]) + + +dnl Survey of platforms: +dnl +dnl Platform Available Compiler Supports test-lock +dnl flavours option weak result +dnl --------------- --------- --------- -------- --------- +dnl Linux 2.4/glibc posix -lpthread Y OK +dnl +dnl GNU Hurd/glibc posix +dnl +dnl FreeBSD 5.3 posix -lc_r Y +dnl posix -lkse ? Y +dnl posix -lpthread ? Y +dnl posix -lthr Y +dnl +dnl FreeBSD 5.2 posix -lc_r Y +dnl posix -lkse Y +dnl posix -lthr Y +dnl +dnl FreeBSD 4.0,4.10 posix -lc_r Y OK +dnl +dnl NetBSD 1.6 -- +dnl +dnl OpenBSD 3.4 posix -lpthread Y OK +dnl +dnl MacOS X 10.[123] posix -lpthread Y OK +dnl +dnl Solaris 7,8,9 posix -lpthread Y Sol 7,8: 0.0; Sol 9: OK +dnl solaris -lthread Y Sol 7,8: 0.0; Sol 9: OK +dnl +dnl HP-UX 11 posix -lpthread N (cc) OK +dnl Y (gcc) +dnl +dnl IRIX 6.5 posix -lpthread Y 0.5 +dnl +dnl AIX 4.3,5.1 posix -lpthread N AIX 4: 0.5; AIX 5: OK +dnl +dnl OSF/1 4.0,5.1 posix -pthread (cc) N OK +dnl -lpthread (gcc) Y +dnl +dnl Cygwin posix -lpthread Y OK +dnl +dnl Any of the above pth -lpth 0.0 +dnl +dnl Mingw win32 N OK +dnl +dnl BeOS 5 -- +dnl +dnl The test-lock result shows what happens if in test-lock.c EXPLICIT_YIELD is +dnl turned off: +dnl OK if all three tests terminate OK, +dnl 0.5 if the first test terminates OK but the second one loops endlessly, +dnl 0.0 if the first test already loops endlessly. diff --git a/m4/uintmax_t.m4 b/m4/uintmax_t.m4 new file mode 100644 index 00000000..03b51bcf --- /dev/null +++ b/m4/uintmax_t.m4 @@ -0,0 +1,30 @@ +# uintmax_t.m4 serial 12 +dnl Copyright (C) 1997-2004, 2007-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Paul Eggert. + +AC_PREREQ([2.13]) + +# Define uintmax_t to 'unsigned long' or 'unsigned long long' +# if it is not already defined in or . + +AC_DEFUN([gl_AC_TYPE_UINTMAX_T], +[ + AC_REQUIRE([gl_AC_HEADER_INTTYPES_H]) + AC_REQUIRE([gl_AC_HEADER_STDINT_H]) + if test $gl_cv_header_inttypes_h = no && test $gl_cv_header_stdint_h = no; then + AC_REQUIRE([AC_TYPE_UNSIGNED_LONG_LONG_INT]) + test $ac_cv_type_unsigned_long_long_int = yes \ + && ac_type='unsigned long long' \ + || ac_type='unsigned long' + AC_DEFINE_UNQUOTED([uintmax_t], [$ac_type], + [Define to unsigned long or unsigned long long + if and don't define.]) + else + AC_DEFINE([HAVE_UINTMAX_T], [1], + [Define if you have the 'uintmax_t' type in or .]) + fi +]) diff --git a/m4/visibility.m4 b/m4/visibility.m4 new file mode 100644 index 00000000..077c4765 --- /dev/null +++ b/m4/visibility.m4 @@ -0,0 +1,74 @@ +# visibility.m4 serial 3 (gettext-0.18) +dnl Copyright (C) 2005, 2008-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +dnl Tests whether the compiler supports the command-line option +dnl -fvisibility=hidden and the function and variable attributes +dnl __attribute__((__visibility__("hidden"))) and +dnl __attribute__((__visibility__("default"))). +dnl Does *not* test for __visibility__("protected") - which has tricky +dnl semantics (see the 'vismain' test in glibc) and does not exist e.g. on +dnl MacOS X. +dnl Does *not* test for __visibility__("internal") - which has processor +dnl dependent semantics. +dnl Does *not* test for #pragma GCC visibility push(hidden) - which is +dnl "really only recommended for legacy code". +dnl Set the variable CFLAG_VISIBILITY. +dnl Defines and sets the variable HAVE_VISIBILITY. + +AC_DEFUN([gl_VISIBILITY], +[ + AC_REQUIRE([AC_PROG_CC]) + CFLAG_VISIBILITY= + HAVE_VISIBILITY=0 + if test -n "$GCC"; then + dnl First, check whether -Werror can be added to the command line, or + dnl whether it leads to an error because of some other option that the + dnl user has put into $CC $CFLAGS $CPPFLAGS. + AC_MSG_CHECKING([whether the -Werror option is usable]) + AC_CACHE_VAL([gl_cv_cc_vis_werror], [ + gl_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -Werror" + AC_TRY_COMPILE([], [], + [gl_cv_cc_vis_werror=yes], + [gl_cv_cc_vis_werror=no]) + CFLAGS="$gl_save_CFLAGS"]) + AC_MSG_RESULT([$gl_cv_cc_vis_werror]) + dnl Now check whether visibility declarations are supported. + AC_MSG_CHECKING([for simple visibility declarations]) + AC_CACHE_VAL([gl_cv_cc_visibility], [ + gl_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -fvisibility=hidden" + dnl We use the option -Werror and a function dummyfunc, because on some + dnl platforms (Cygwin 1.7) the use of -fvisibility triggers a warning + dnl "visibility attribute not supported in this configuration; ignored" + dnl at the first function definition in every compilation unit, and we + dnl don't want to use the option in this case. + if test $gl_cv_cc_vis_werror = yes; then + CFLAGS="$CFLAGS -Werror" + fi + AC_TRY_COMPILE( + [extern __attribute__((__visibility__("hidden"))) int hiddenvar; + extern __attribute__((__visibility__("default"))) int exportedvar; + extern __attribute__((__visibility__("hidden"))) int hiddenfunc (void); + extern __attribute__((__visibility__("default"))) int exportedfunc (void); + void dummyfunc (void) {}], + [], + [gl_cv_cc_visibility=yes], + [gl_cv_cc_visibility=no]) + CFLAGS="$gl_save_CFLAGS"]) + AC_MSG_RESULT([$gl_cv_cc_visibility]) + if test $gl_cv_cc_visibility = yes; then + CFLAG_VISIBILITY="-fvisibility=hidden" + HAVE_VISIBILITY=1 + fi + fi + AC_SUBST([CFLAG_VISIBILITY]) + AC_SUBST([HAVE_VISIBILITY]) + AC_DEFINE_UNQUOTED([HAVE_VISIBILITY], [$HAVE_VISIBILITY], + [Define to 1 or 0, depending whether the compiler supports simple visibility declarations.]) +]) diff --git a/m4/wchar_t.m4 b/m4/wchar_t.m4 new file mode 100644 index 00000000..ed804e66 --- /dev/null +++ b/m4/wchar_t.m4 @@ -0,0 +1,20 @@ +# wchar_t.m4 serial 3 (gettext-0.18) +dnl Copyright (C) 2002-2003, 2008-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. +dnl Test whether has the 'wchar_t' type. +dnl Prerequisite: AC_PROG_CC + +AC_DEFUN([gt_TYPE_WCHAR_T], +[ + AC_CACHE_CHECK([for wchar_t], [gt_cv_c_wchar_t], + [AC_TRY_COMPILE([#include + wchar_t foo = (wchar_t)'\0';], , + [gt_cv_c_wchar_t=yes], [gt_cv_c_wchar_t=no])]) + if test $gt_cv_c_wchar_t = yes; then + AC_DEFINE([HAVE_WCHAR_T], [1], [Define if you have the 'wchar_t' type.]) + fi +]) diff --git a/m4/wint_t.m4 b/m4/wint_t.m4 new file mode 100644 index 00000000..a6c7d15c --- /dev/null +++ b/m4/wint_t.m4 @@ -0,0 +1,28 @@ +# wint_t.m4 serial 4 (gettext-0.18) +dnl Copyright (C) 2003, 2007-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. +dnl Test whether has the 'wint_t' type. +dnl Prerequisite: AC_PROG_CC + +AC_DEFUN([gt_TYPE_WINT_T], +[ + AC_CACHE_CHECK([for wint_t], [gt_cv_c_wint_t], + [AC_TRY_COMPILE([ +/* Tru64 with Desktop Toolkit C has a bug: must be included before + . + BSD/OS 4.0.1 has a bug: , and must be included + before . */ +#include +#include +#include +#include + wint_t foo = (wchar_t)'\0';], , + [gt_cv_c_wint_t=yes], [gt_cv_c_wint_t=no])]) + if test $gt_cv_c_wint_t = yes; then + AC_DEFINE([HAVE_WINT_T], [1], [Define if you have the 'wint_t' type.]) + fi +]) diff --git a/m4/xsize.m4 b/m4/xsize.m4 new file mode 100644 index 00000000..b653693a --- /dev/null +++ b/m4/xsize.m4 @@ -0,0 +1,13 @@ +# xsize.m4 serial 4 +dnl Copyright (C) 2003-2004, 2008-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_XSIZE], +[ + dnl Prerequisites of lib/xsize.h. + AC_REQUIRE([gl_SIZE_MAX]) + AC_REQUIRE([AC_C_INLINE]) + AC_CHECK_HEADERS([stdint.h]) +]) diff --git a/po/ChangeLog b/po/ChangeLog new file mode 100644 index 00000000..000012d2 --- /dev/null +++ b/po/ChangeLog @@ -0,0 +1,17 @@ +2018-01-07 gettextize + + * Makefile.in.in: Upgrade to gettext-0.19.8.1. + * Rules-quot: Upgrade to gettext-0.19.8.1. + +2014-06-08 gettextize + + * Makefile.in.in: New file, from gettext-0.18.3. + * Rules-quot: New file, from gettext-0.18.3. + * boldquot.sed: New file, from gettext-0.18.3. + * en@boldquot.header: New file, from gettext-0.18.3. + * en@quot.header: New file, from gettext-0.18.3. + * insert-header.sin: New file, from gettext-0.18.3. + * quot.sed: New file, from gettext-0.18.3. + * remove-potcdate.sin: New file, from gettext-0.18.3. + * POTFILES.in: New file. + diff --git a/po/LINGUAS b/po/LINGUAS new file mode 100644 index 00000000..c42e816f --- /dev/null +++ b/po/LINGUAS @@ -0,0 +1 @@ +de \ No newline at end of file diff --git a/po/Makefile.in.in b/po/Makefile.in.in new file mode 100644 index 00000000..38c293d2 --- /dev/null +++ b/po/Makefile.in.in @@ -0,0 +1,483 @@ +# Makefile for PO directory in any package using GNU gettext. +# Copyright (C) 1995-1997, 2000-2007, 2009-2010 by Ulrich Drepper +# +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. This file is offered as-is, +# without any warranty. +# +# Origin: gettext-0.19.8 +GETTEXT_MACRO_VERSION = 0.19 + +PACKAGE = @PACKAGE@ +VERSION = @VERSION@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ + +SED = @SED@ +SHELL = /bin/sh +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +datarootdir = @datarootdir@ +datadir = @datadir@ +localedir = @localedir@ +gettextsrcdir = $(datadir)/gettext/po + +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ + +# We use $(mkdir_p). +# In automake <= 1.9.x, $(mkdir_p) is defined either as "mkdir -p --" or as +# "$(mkinstalldirs)" or as "$(install_sh) -d". For these automake versions, +# @install_sh@ does not start with $(SHELL), so we add it. +# In automake >= 1.10, @mkdir_p@ is derived from ${MKDIR_P}, which is defined +# either as "/path/to/mkdir -p" or ".../install-sh -c -d". For these automake +# versions, $(mkinstalldirs) and $(install_sh) are unused. +mkinstalldirs = $(SHELL) @install_sh@ -d +install_sh = $(SHELL) @install_sh@ +MKDIR_P = @MKDIR_P@ +mkdir_p = @mkdir_p@ + +# When building gettext-tools, we prefer to use the built programs +# rather than installed programs. However, we can't do that when we +# are cross compiling. +CROSS_COMPILING = @CROSS_COMPILING@ + +GMSGFMT_ = @GMSGFMT@ +GMSGFMT_no = @GMSGFMT@ +GMSGFMT_yes = @GMSGFMT_015@ +GMSGFMT = $(GMSGFMT_$(USE_MSGCTXT)) +MSGFMT_ = @MSGFMT@ +MSGFMT_no = @MSGFMT@ +MSGFMT_yes = @MSGFMT_015@ +MSGFMT = $(MSGFMT_$(USE_MSGCTXT)) +XGETTEXT_ = @XGETTEXT@ +XGETTEXT_no = @XGETTEXT@ +XGETTEXT_yes = @XGETTEXT_015@ +XGETTEXT = $(XGETTEXT_$(USE_MSGCTXT)) +MSGMERGE = msgmerge +MSGMERGE_UPDATE = @MSGMERGE@ --update +MSGINIT = msginit +MSGCONV = msgconv +MSGFILTER = msgfilter + +POFILES = @POFILES@ +GMOFILES = @GMOFILES@ +UPDATEPOFILES = @UPDATEPOFILES@ +DUMMYPOFILES = @DUMMYPOFILES@ +DISTFILES.common = Makefile.in.in remove-potcdate.sin \ +$(DISTFILES.common.extra1) $(DISTFILES.common.extra2) $(DISTFILES.common.extra3) +DISTFILES = $(DISTFILES.common) Makevars POTFILES.in \ +$(POFILES) $(GMOFILES) \ +$(DISTFILES.extra1) $(DISTFILES.extra2) $(DISTFILES.extra3) + +POTFILES = \ + +CATALOGS = @CATALOGS@ + +POFILESDEPS_ = $(srcdir)/$(DOMAIN).pot +POFILESDEPS_yes = $(POFILESDEPS_) +POFILESDEPS_no = +POFILESDEPS = $(POFILESDEPS_$(PO_DEPENDS_ON_POT)) + +DISTFILESDEPS_ = update-po +DISTFILESDEPS_yes = $(DISTFILESDEPS_) +DISTFILESDEPS_no = +DISTFILESDEPS = $(DISTFILESDEPS_$(DIST_DEPENDS_ON_UPDATE_PO)) + +# Makevars gets inserted here. (Don't remove this line!) + +.SUFFIXES: +.SUFFIXES: .po .gmo .mo .sed .sin .nop .po-create .po-update + +.po.mo: + @echo "$(MSGFMT) -c -o $@ $<"; \ + $(MSGFMT) -c -o t-$@ $< && mv t-$@ $@ + +.po.gmo: + @lang=`echo $* | sed -e 's,.*/,,'`; \ + test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ + echo "$${cdcmd}rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics --verbose -o $${lang}.gmo $${lang}.po"; \ + cd $(srcdir) && rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics --verbose -o t-$${lang}.gmo $${lang}.po && mv t-$${lang}.gmo $${lang}.gmo + +.sin.sed: + sed -e '/^#/d' $< > t-$@ + mv t-$@ $@ + + +all: all-@USE_NLS@ + +all-yes: stamp-po +all-no: + +# Ensure that the gettext macros and this Makefile.in.in are in sync. +CHECK_MACRO_VERSION = \ + test "$(GETTEXT_MACRO_VERSION)" = "@GETTEXT_MACRO_VERSION@" \ + || { echo "*** error: gettext infrastructure mismatch: using a Makefile.in.in from gettext version $(GETTEXT_MACRO_VERSION) but the autoconf macros are from gettext version @GETTEXT_MACRO_VERSION@" 1>&2; \ + exit 1; \ + } + +# $(srcdir)/$(DOMAIN).pot is only created when needed. When xgettext finds no +# internationalized messages, no $(srcdir)/$(DOMAIN).pot is created (because +# we don't want to bother translators with empty POT files). We assume that +# LINGUAS is empty in this case, i.e. $(POFILES) and $(GMOFILES) are empty. +# In this case, stamp-po is a nop (i.e. a phony target). + +# stamp-po is a timestamp denoting the last time at which the CATALOGS have +# been loosely updated. Its purpose is that when a developer or translator +# checks out the package via CVS, and the $(DOMAIN).pot file is not in CVS, +# "make" will update the $(DOMAIN).pot and the $(CATALOGS), but subsequent +# invocations of "make" will do nothing. This timestamp would not be necessary +# if updating the $(CATALOGS) would always touch them; however, the rule for +# $(POFILES) has been designed to not touch files that don't need to be +# changed. +stamp-po: $(srcdir)/$(DOMAIN).pot + @$(CHECK_MACRO_VERSION) + test ! -f $(srcdir)/$(DOMAIN).pot || \ + test -z "$(GMOFILES)" || $(MAKE) $(GMOFILES) + @test ! -f $(srcdir)/$(DOMAIN).pot || { \ + echo "touch stamp-po" && \ + echo timestamp > stamp-poT && \ + mv stamp-poT stamp-po; \ + } + +# Note: Target 'all' must not depend on target '$(DOMAIN).pot-update', +# otherwise packages like GCC can not be built if only parts of the source +# have been downloaded. + +# This target rebuilds $(DOMAIN).pot; it is an expensive operation. +# Note that $(DOMAIN).pot is not touched if it doesn't need to be changed. +# The determination of whether the package xyz is a GNU one is based on the +# heuristic whether some file in the top level directory mentions "GNU xyz". +# If GNU 'find' is available, we avoid grepping through monster files. +$(DOMAIN).pot-update: $(POTFILES) $(srcdir)/POTFILES.in remove-potcdate.sed + package_gnu="$(PACKAGE_GNU)"; \ + test -n "$$package_gnu" || { \ + if { if (LC_ALL=C find --version) 2>/dev/null | grep GNU >/dev/null; then \ + LC_ALL=C find -L $(top_srcdir) -maxdepth 1 -type f \ + -size -10000000c -exec grep 'GNU @PACKAGE@' \ + /dev/null '{}' ';' 2>/dev/null; \ + else \ + LC_ALL=C grep 'GNU @PACKAGE@' $(top_srcdir)/* 2>/dev/null; \ + fi; \ + } | grep -v 'libtool:' >/dev/null; then \ + package_gnu=yes; \ + else \ + package_gnu=no; \ + fi; \ + }; \ + if test "$$package_gnu" = "yes"; then \ + package_prefix='GNU '; \ + else \ + package_prefix=''; \ + fi; \ + if test -n '$(MSGID_BUGS_ADDRESS)' || test '$(PACKAGE_BUGREPORT)' = '@'PACKAGE_BUGREPORT'@'; then \ + msgid_bugs_address='$(MSGID_BUGS_ADDRESS)'; \ + else \ + msgid_bugs_address='$(PACKAGE_BUGREPORT)'; \ + fi; \ + case `$(XGETTEXT) --version | sed 1q | sed -e 's,^[^0-9]*,,'` in \ + '' | 0.[0-9] | 0.[0-9].* | 0.1[0-5] | 0.1[0-5].* | 0.16 | 0.16.[0-1]*) \ + $(XGETTEXT) --default-domain=$(DOMAIN) --directory=$(top_srcdir) \ + --add-comments=TRANSLATORS: $(XGETTEXT_OPTIONS) @XGETTEXT_EXTRA_OPTIONS@ \ + --files-from=$(srcdir)/POTFILES.in \ + --copyright-holder='$(COPYRIGHT_HOLDER)' \ + --msgid-bugs-address="$$msgid_bugs_address" \ + ;; \ + *) \ + $(XGETTEXT) --default-domain=$(DOMAIN) --directory=$(top_srcdir) \ + --add-comments=TRANSLATORS: $(XGETTEXT_OPTIONS) @XGETTEXT_EXTRA_OPTIONS@ \ + --files-from=$(srcdir)/POTFILES.in \ + --copyright-holder='$(COPYRIGHT_HOLDER)' \ + --package-name="$${package_prefix}@PACKAGE@" \ + --package-version='@VERSION@' \ + --msgid-bugs-address="$$msgid_bugs_address" \ + ;; \ + esac + test ! -f $(DOMAIN).po || { \ + if test -f $(srcdir)/$(DOMAIN).pot-header; then \ + sed -e '1,/^#$$/d' < $(DOMAIN).po > $(DOMAIN).1po && \ + cat $(srcdir)/$(DOMAIN).pot-header $(DOMAIN).1po > $(DOMAIN).po; \ + rm -f $(DOMAIN).1po; \ + fi; \ + if test -f $(srcdir)/$(DOMAIN).pot; then \ + sed -f remove-potcdate.sed < $(srcdir)/$(DOMAIN).pot > $(DOMAIN).1po && \ + sed -f remove-potcdate.sed < $(DOMAIN).po > $(DOMAIN).2po && \ + if cmp $(DOMAIN).1po $(DOMAIN).2po >/dev/null 2>&1; then \ + rm -f $(DOMAIN).1po $(DOMAIN).2po $(DOMAIN).po; \ + else \ + rm -f $(DOMAIN).1po $(DOMAIN).2po $(srcdir)/$(DOMAIN).pot && \ + mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \ + fi; \ + else \ + mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \ + fi; \ + } + +# This rule has no dependencies: we don't need to update $(DOMAIN).pot at +# every "make" invocation, only create it when it is missing. +# Only "make $(DOMAIN).pot-update" or "make dist" will force an update. +$(srcdir)/$(DOMAIN).pot: + $(MAKE) $(DOMAIN).pot-update + +# This target rebuilds a PO file if $(DOMAIN).pot has changed. +# Note that a PO file is not touched if it doesn't need to be changed. +$(POFILES): $(POFILESDEPS) + @lang=`echo $@ | sed -e 's,.*/,,' -e 's/\.po$$//'`; \ + if test -f "$(srcdir)/$${lang}.po"; then \ + test -f $(srcdir)/$(DOMAIN).pot || $(MAKE) $(srcdir)/$(DOMAIN).pot; \ + test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ + echo "$${cdcmd}$(MSGMERGE_UPDATE) $(MSGMERGE_OPTIONS) --lang=$${lang} $${lang}.po $(DOMAIN).pot"; \ + cd $(srcdir) \ + && { case `$(MSGMERGE) --version | sed 1q | sed -e 's,^[^0-9]*,,'` in \ + '' | 0.[0-9] | 0.[0-9].* | 0.1[0-7] | 0.1[0-7].*) \ + $(MSGMERGE_UPDATE) $(MSGMERGE_OPTIONS) $${lang}.po $(DOMAIN).pot;; \ + *) \ + $(MSGMERGE_UPDATE) $(MSGMERGE_OPTIONS) --lang=$${lang} $${lang}.po $(DOMAIN).pot;; \ + esac; \ + }; \ + else \ + $(MAKE) $${lang}.po-create; \ + fi + + +install: install-exec install-data +install-exec: +install-data: install-data-@USE_NLS@ + if test "$(PACKAGE)" = "gettext-tools"; then \ + $(mkdir_p) $(DESTDIR)$(gettextsrcdir); \ + for file in $(DISTFILES.common) Makevars.template; do \ + $(INSTALL_DATA) $(srcdir)/$$file \ + $(DESTDIR)$(gettextsrcdir)/$$file; \ + done; \ + for file in Makevars; do \ + rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \ + done; \ + else \ + : ; \ + fi +install-data-no: all +install-data-yes: all + @catalogs='$(CATALOGS)'; \ + for cat in $$catalogs; do \ + cat=`basename $$cat`; \ + lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \ + dir=$(localedir)/$$lang/LC_MESSAGES; \ + $(mkdir_p) $(DESTDIR)$$dir; \ + if test -r $$cat; then realcat=$$cat; else realcat=$(srcdir)/$$cat; fi; \ + $(INSTALL_DATA) $$realcat $(DESTDIR)$$dir/$(DOMAIN).mo; \ + echo "installing $$realcat as $(DESTDIR)$$dir/$(DOMAIN).mo"; \ + for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \ + if test -n "$$lc"; then \ + if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 2>/dev/null) | grep ' -> ' >/dev/null; then \ + link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc | sed -e 's/^.* -> //'`; \ + mv $(DESTDIR)$(localedir)/$$lang/$$lc $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ + mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ + (cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \ + for file in *; do \ + if test -f $$file; then \ + ln -s ../$$link/$$file $(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \ + fi; \ + done); \ + rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ + else \ + if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \ + :; \ + else \ + rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \ + mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ + fi; \ + fi; \ + rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \ + ln -s ../LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \ + ln $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \ + cp -p $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \ + echo "installing $$realcat link as $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo"; \ + fi; \ + done; \ + done + +install-strip: install + +installdirs: installdirs-exec installdirs-data +installdirs-exec: +installdirs-data: installdirs-data-@USE_NLS@ + if test "$(PACKAGE)" = "gettext-tools"; then \ + $(mkdir_p) $(DESTDIR)$(gettextsrcdir); \ + else \ + : ; \ + fi +installdirs-data-no: +installdirs-data-yes: + @catalogs='$(CATALOGS)'; \ + for cat in $$catalogs; do \ + cat=`basename $$cat`; \ + lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \ + dir=$(localedir)/$$lang/LC_MESSAGES; \ + $(mkdir_p) $(DESTDIR)$$dir; \ + for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \ + if test -n "$$lc"; then \ + if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 2>/dev/null) | grep ' -> ' >/dev/null; then \ + link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc | sed -e 's/^.* -> //'`; \ + mv $(DESTDIR)$(localedir)/$$lang/$$lc $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ + mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ + (cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \ + for file in *; do \ + if test -f $$file; then \ + ln -s ../$$link/$$file $(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \ + fi; \ + done); \ + rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ + else \ + if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \ + :; \ + else \ + rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \ + mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ + fi; \ + fi; \ + fi; \ + done; \ + done + +# Define this as empty until I found a useful application. +installcheck: + +uninstall: uninstall-exec uninstall-data +uninstall-exec: +uninstall-data: uninstall-data-@USE_NLS@ + if test "$(PACKAGE)" = "gettext-tools"; then \ + for file in $(DISTFILES.common) Makevars.template; do \ + rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \ + done; \ + else \ + : ; \ + fi +uninstall-data-no: +uninstall-data-yes: + catalogs='$(CATALOGS)'; \ + for cat in $$catalogs; do \ + cat=`basename $$cat`; \ + lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \ + for lc in LC_MESSAGES $(EXTRA_LOCALE_CATEGORIES); do \ + rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \ + done; \ + done + +check: all + +info dvi ps pdf html tags TAGS ctags CTAGS ID: + +mostlyclean: + rm -f remove-potcdate.sed + rm -f stamp-poT + rm -f core core.* $(DOMAIN).po $(DOMAIN).1po $(DOMAIN).2po *.new.po + rm -fr *.o + +clean: mostlyclean + +distclean: clean + rm -f Makefile Makefile.in POTFILES *.mo + +maintainer-clean: distclean + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + rm -f stamp-po $(GMOFILES) + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) +dist distdir: + test -z "$(DISTFILESDEPS)" || $(MAKE) $(DISTFILESDEPS) + @$(MAKE) dist2 +# This is a separate target because 'update-po' must be executed before. +dist2: stamp-po $(DISTFILES) + dists="$(DISTFILES)"; \ + if test "$(PACKAGE)" = "gettext-tools"; then \ + dists="$$dists Makevars.template"; \ + fi; \ + if test -f $(srcdir)/$(DOMAIN).pot; then \ + dists="$$dists $(DOMAIN).pot stamp-po"; \ + fi; \ + if test -f $(srcdir)/ChangeLog; then \ + dists="$$dists ChangeLog"; \ + fi; \ + for i in 0 1 2 3 4 5 6 7 8 9; do \ + if test -f $(srcdir)/ChangeLog.$$i; then \ + dists="$$dists ChangeLog.$$i"; \ + fi; \ + done; \ + if test -f $(srcdir)/LINGUAS; then dists="$$dists LINGUAS"; fi; \ + for file in $$dists; do \ + if test -f $$file; then \ + cp -p $$file $(distdir) || exit 1; \ + else \ + cp -p $(srcdir)/$$file $(distdir) || exit 1; \ + fi; \ + done + +update-po: Makefile + $(MAKE) $(DOMAIN).pot-update + test -z "$(UPDATEPOFILES)" || $(MAKE) $(UPDATEPOFILES) + $(MAKE) update-gmo + +# General rule for creating PO files. + +.nop.po-create: + @lang=`echo $@ | sed -e 's/\.po-create$$//'`; \ + echo "File $$lang.po does not exist. If you are a translator, you can create it through 'msginit'." 1>&2; \ + exit 1 + +# General rule for updating PO files. + +.nop.po-update: + @lang=`echo $@ | sed -e 's/\.po-update$$//'`; \ + if test "$(PACKAGE)" = "gettext-tools" && test "$(CROSS_COMPILING)" != "yes"; then PATH=`pwd`/../src:$$PATH; fi; \ + tmpdir=`pwd`; \ + echo "$$lang:"; \ + test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ + echo "$${cdcmd}$(MSGMERGE) $(MSGMERGE_OPTIONS) --lang=$$lang $$lang.po $(DOMAIN).pot -o $$lang.new.po"; \ + cd $(srcdir); \ + if { case `$(MSGMERGE) --version | sed 1q | sed -e 's,^[^0-9]*,,'` in \ + '' | 0.[0-9] | 0.[0-9].* | 0.1[0-7] | 0.1[0-7].*) \ + $(MSGMERGE) $(MSGMERGE_OPTIONS) -o $$tmpdir/$$lang.new.po $$lang.po $(DOMAIN).pot;; \ + *) \ + $(MSGMERGE) $(MSGMERGE_OPTIONS) --lang=$$lang -o $$tmpdir/$$lang.new.po $$lang.po $(DOMAIN).pot;; \ + esac; \ + }; then \ + if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \ + rm -f $$tmpdir/$$lang.new.po; \ + else \ + if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \ + :; \ + else \ + echo "msgmerge for $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \ + exit 1; \ + fi; \ + fi; \ + else \ + echo "msgmerge for $$lang.po failed!" 1>&2; \ + rm -f $$tmpdir/$$lang.new.po; \ + fi + +$(DUMMYPOFILES): + +update-gmo: Makefile $(GMOFILES) + @: + +# Recreate Makefile by invoking config.status. Explicitly invoke the shell, +# because execution permission bits may not work on the current file system. +# Use @SHELL@, which is the shell determined by autoconf for the use by its +# scripts, not $(SHELL) which is hardwired to /bin/sh and may be deficient. +Makefile: Makefile.in.in Makevars $(top_builddir)/config.status @POMAKEFILEDEPS@ + cd $(top_builddir) \ + && @SHELL@ ./config.status $(subdir)/$@.in po-directories + +force: + +# Tell versions [3.59,3.63) of GNU make not to export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/po/Makevars b/po/Makevars new file mode 100644 index 00000000..57406079 --- /dev/null +++ b/po/Makevars @@ -0,0 +1,85 @@ +# Makefile variables for PO directory in any package using GNU gettext. + +# Usually the message domain is the same as the package name. +DOMAIN = $(PACKAGE) + +# These two variables depend on the location of this directory. +subdir = po +top_builddir = .. + +# These options get passed to xgettext. +XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ --keyword=gettext_noopt --keyword=rtmidi_gettext + +# This is the copyright holder that gets inserted into the header of the +# $(DOMAIN).pot file. Set this to the copyright holder of the surrounding +# package. (Note that the msgstr strings, extracted from the package's +# sources, belong to the copyright holder of the package.) Translators are +# expected to transfer the copyright for their translations to this person +# or entity, or to disclaim their copyright. The empty string stands for +# the public domain; in this case the translators are expected to disclaim +# their copyright. +COPYRIGHT_HOLDER = Gary P. Scavone, Tobias Schlemmer + +# This tells whether or not to prepend "GNU " prefix to the package +# name that gets inserted into the header of the $(DOMAIN).pot file. +# Possible values are "yes", "no", or empty. If it is empty, try to +# detect it automatically by scanning the files in $(top_srcdir) for +# "GNU packagename" string. +PACKAGE_GNU = no + +# This is the email address or URL to which the translators shall report +# bugs in the untranslated strings: +# - Strings which are not entire sentences, see the maintainer guidelines +# in the GNU gettext documentation, section 'Preparing Strings'. +# - Strings which use unclear terms or require additional context to be +# understood. +# - Strings which make invalid assumptions about notation of date, time or +# money. +# - Pluralisation problems. +# - Incorrect English spelling. +# - Incorrect formatting. +# It can be your email address, or a mailing list address where translators +# can write to without being subscribed, or the URL of a web page through +# which the translators can contact you. +MSGID_BUGS_ADDRESS = https://github.com/keinstein/rtmidi + +# This is the list of locale categories, beyond LC_MESSAGES, for which the +# message catalogs shall be used. It is usually empty. +EXTRA_LOCALE_CATEGORIES = + +# This tells whether the $(DOMAIN).pot file contains messages with an 'msgctxt' +# context. Possible values are "yes" and "no". Set this to yes if the +# package uses functions taking also a message context, like pgettext(), or +# if in $(XGETTEXT_OPTIONS) you define keywords with a context argument. +USE_MSGCTXT = no + +# These options get passed to msgmerge. +# Useful options are in particular: +# --previous to keep previous msgids of translated messages, +# --quiet to reduce the verbosity. +MSGMERGE_OPTIONS = + +# These options get passed to msginit. +# If you want to disable line wrapping when writing PO files, add +# --no-wrap to MSGMERGE_OPTIONS, XGETTEXT_OPTIONS, and +# MSGINIT_OPTIONS. +MSGINIT_OPTIONS = + +# This tells whether or not to regenerate a PO file when $(DOMAIN).pot +# has changed. Possible values are "yes" and "no". Set this to no if +# the POT file is checked in the repository and the version control +# program ignores timestamps. +PO_DEPENDS_ON_POT = yes + +# This tells whether or not to forcibly update $(DOMAIN).pot and +# regenerate PO files on "make dist". Possible values are "yes" and +# "no". Set this to no if the POT file and PO files are maintained +# externally. +DIST_DEPENDS_ON_UPDATE_PO = yes + + +# Extra rules + +all: + +tags: diff --git a/po/Makevars.template b/po/Makevars.template new file mode 100644 index 00000000..0648ec75 --- /dev/null +++ b/po/Makevars.template @@ -0,0 +1,78 @@ +# Makefile variables for PO directory in any package using GNU gettext. + +# Usually the message domain is the same as the package name. +DOMAIN = $(PACKAGE) + +# These two variables depend on the location of this directory. +subdir = po +top_builddir = .. + +# These options get passed to xgettext. +XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ + +# This is the copyright holder that gets inserted into the header of the +# $(DOMAIN).pot file. Set this to the copyright holder of the surrounding +# package. (Note that the msgstr strings, extracted from the package's +# sources, belong to the copyright holder of the package.) Translators are +# expected to transfer the copyright for their translations to this person +# or entity, or to disclaim their copyright. The empty string stands for +# the public domain; in this case the translators are expected to disclaim +# their copyright. +COPYRIGHT_HOLDER = Free Software Foundation, Inc. + +# This tells whether or not to prepend "GNU " prefix to the package +# name that gets inserted into the header of the $(DOMAIN).pot file. +# Possible values are "yes", "no", or empty. If it is empty, try to +# detect it automatically by scanning the files in $(top_srcdir) for +# "GNU packagename" string. +PACKAGE_GNU = + +# This is the email address or URL to which the translators shall report +# bugs in the untranslated strings: +# - Strings which are not entire sentences, see the maintainer guidelines +# in the GNU gettext documentation, section 'Preparing Strings'. +# - Strings which use unclear terms or require additional context to be +# understood. +# - Strings which make invalid assumptions about notation of date, time or +# money. +# - Pluralisation problems. +# - Incorrect English spelling. +# - Incorrect formatting. +# It can be your email address, or a mailing list address where translators +# can write to without being subscribed, or the URL of a web page through +# which the translators can contact you. +MSGID_BUGS_ADDRESS = + +# This is the list of locale categories, beyond LC_MESSAGES, for which the +# message catalogs shall be used. It is usually empty. +EXTRA_LOCALE_CATEGORIES = + +# This tells whether the $(DOMAIN).pot file contains messages with an 'msgctxt' +# context. Possible values are "yes" and "no". Set this to yes if the +# package uses functions taking also a message context, like pgettext(), or +# if in $(XGETTEXT_OPTIONS) you define keywords with a context argument. +USE_MSGCTXT = no + +# These options get passed to msgmerge. +# Useful options are in particular: +# --previous to keep previous msgids of translated messages, +# --quiet to reduce the verbosity. +MSGMERGE_OPTIONS = + +# These options get passed to msginit. +# If you want to disable line wrapping when writing PO files, add +# --no-wrap to MSGMERGE_OPTIONS, XGETTEXT_OPTIONS, and +# MSGINIT_OPTIONS. +MSGINIT_OPTIONS = + +# This tells whether or not to regenerate a PO file when $(DOMAIN).pot +# has changed. Possible values are "yes" and "no". Set this to no if +# the POT file is checked in the repository and the version control +# program ignores timestamps. +PO_DEPENDS_ON_POT = yes + +# This tells whether or not to forcibly update $(DOMAIN).pot and +# regenerate PO files on "make dist". Possible values are "yes" and +# "no". Set this to no if the POT file and PO files are maintained +# externally. +DIST_DEPENDS_ON_UPDATE_PO = yes diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 00000000..b4596b4b --- /dev/null +++ b/po/POTFILES.in @@ -0,0 +1,3 @@ +# List of source files which contain translatable strings. +RtMidi.h +RtMidi.cpp \ No newline at end of file diff --git a/po/Rules-quot b/po/Rules-quot new file mode 100644 index 00000000..baf65285 --- /dev/null +++ b/po/Rules-quot @@ -0,0 +1,58 @@ +# This file, Rules-quot, can be copied and used freely without restrictions. +# Special Makefile rules for English message catalogs with quotation marks. + +DISTFILES.common.extra1 = quot.sed boldquot.sed en@quot.header en@boldquot.header insert-header.sin Rules-quot + +.SUFFIXES: .insert-header .po-update-en + +en@quot.po-create: + $(MAKE) en@quot.po-update +en@boldquot.po-create: + $(MAKE) en@boldquot.po-update + +en@quot.po-update: en@quot.po-update-en +en@boldquot.po-update: en@boldquot.po-update-en + +.insert-header.po-update-en: + @lang=`echo $@ | sed -e 's/\.po-update-en$$//'`; \ + if test "$(PACKAGE)" = "gettext-tools" && test "$(CROSS_COMPILING)" != "yes"; then PATH=`pwd`/../src:$$PATH; GETTEXTLIBDIR=`cd $(top_srcdir)/src && pwd`; export GETTEXTLIBDIR; fi; \ + tmpdir=`pwd`; \ + echo "$$lang:"; \ + ll=`echo $$lang | sed -e 's/@.*//'`; \ + LC_ALL=C; export LC_ALL; \ + cd $(srcdir); \ + if $(MSGINIT) $(MSGINIT_OPTIONS) -i $(DOMAIN).pot --no-translator -l $$lang -o - 2>/dev/null \ + | $(SED) -f $$tmpdir/$$lang.insert-header | $(MSGCONV) -t UTF-8 | \ + { case `$(MSGFILTER) --version | sed 1q | sed -e 's,^[^0-9]*,,'` in \ + '' | 0.[0-9] | 0.[0-9].* | 0.1[0-8] | 0.1[0-8].*) \ + $(MSGFILTER) $(SED) -f `echo $$lang | sed -e 's/.*@//'`.sed \ + ;; \ + *) \ + $(MSGFILTER) `echo $$lang | sed -e 's/.*@//'` \ + ;; \ + esac } 2>/dev/null > $$tmpdir/$$lang.new.po \ + ; then \ + if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \ + rm -f $$tmpdir/$$lang.new.po; \ + else \ + if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \ + :; \ + else \ + echo "creation of $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \ + exit 1; \ + fi; \ + fi; \ + else \ + echo "creation of $$lang.po failed!" 1>&2; \ + rm -f $$tmpdir/$$lang.new.po; \ + fi + +en@quot.insert-header: insert-header.sin + sed -e '/^#/d' -e 's/HEADER/en@quot.header/g' $(srcdir)/insert-header.sin > en@quot.insert-header + +en@boldquot.insert-header: insert-header.sin + sed -e '/^#/d' -e 's/HEADER/en@boldquot.header/g' $(srcdir)/insert-header.sin > en@boldquot.insert-header + +mostlyclean: mostlyclean-quot +mostlyclean-quot: + rm -f *.insert-header diff --git a/po/boldquot.sed b/po/boldquot.sed new file mode 100644 index 00000000..4b937aa5 --- /dev/null +++ b/po/boldquot.sed @@ -0,0 +1,10 @@ +s/"\([^"]*\)"/“\1”/g +s/`\([^`']*\)'/‘\1’/g +s/ '\([^`']*\)' / ‘\1’ /g +s/ '\([^`']*\)'$/ ‘\1’/g +s/^'\([^`']*\)' /‘\1’ /g +s/“”/""/g +s/“/“/g +s/”/”/g +s/‘/‘/g +s/’/’/g diff --git a/po/de.gmo b/po/de.gmo new file mode 100644 index 00000000..ba5cfe20 Binary files /dev/null and b/po/de.gmo differ diff --git a/po/de.mo b/po/de.mo new file mode 100644 index 00000000..ba5cfe20 Binary files /dev/null and b/po/de.mo differ diff --git a/po/de.po b/po/de.po new file mode 100644 index 00000000..8a4f2fed --- /dev/null +++ b/po/de.po @@ -0,0 +1,628 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Gary P. Scavone +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: rtmidi 2.1.0\n" +"Report-Msgid-Bugs-To: https://github.com/keinstein/rtmidi\n" +"POT-Creation-Date: 2018-08-13 18:56+0200\n" +"PO-Revision-Date: 2018-08-13 19:04+0200\n" +"Last-Translator: Tobias Schlemmer \n" +"Language-Team: German translation team \n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 2.1.1\n" +"X-Poedit-SourceCharset: UTF-8\n" +"X-Poedit-Basepath: .\n" +"X-Poedit-KeywordsList: gettext_noopt\n" +"X-Poedit-SearchPath-0: /home/tobias/macbookbackup/Entwicklung/rtmidi-beides\n" + +#: RtMidi.h:129 +msgid "Automatic selection" +msgstr "Automatische Auswahl" + +#: RtMidi.h:130 +msgid "Core MIDI" +msgstr "Core MIDI" + +#: RtMidi.h:131 +msgid "ALSA" +msgstr "ALSA" + +#: RtMidi.h:132 +msgid "JACK" +msgstr "JACK" + +#: RtMidi.h:133 +msgid "Windows Multimedia" +msgstr "Windows Multimedia" + +#: RtMidi.h:134 +msgid "DirectX/Kernel Streaming" +msgstr "DirectX/Kernel Streaming" + +#: RtMidi.h:135 +msgid "NULL device" +msgstr "Keine Funktion." + +#: RtMidi.h:136 +msgid "All available MIDI systems" +msgstr "Alle verfügbaren MIDI-Systeme." + +#: RtMidi.h:1106 RtMidi.h:1277 RtMidi.h:1490 RtMidi.h:1511 +msgid "Passed NULL pointer." +msgstr "Nullzeiger übergeben." + +#: RtMidi.h:1308 RtMidi.h:1317 RtMidi.cpp:2596 +msgid "No data in message argument." +msgstr "Keine Daten in der MIDI-Nachricht." + +#: RtMidi.h:1432 +msgid "A NULL pointer has been passed as port descriptor" +msgstr "Ein leerer Zeiger wurde als Schnittstellenbeschreibung übergeben." + +#: RtMidi.h:1452 RtMidi.h:1531 RtMidi.h:1543 +msgid "No valid MIDI system has been selected." +msgstr "Es wurde kein benutzbares MIDI-System ausgewählt." + +#: RtMidi.h:1473 +msgid "Could not find any valid MIDI system." +msgstr "Es konnte kein benutzbares MIDI-System gefunden werden." + +#: RtMidi.h:1495 +msgid "No valid MIDI system has been found." +msgstr "Es konnte kein benutzbares MIDI-System gefunden werden." + +#: RtMidi.h:1537 +msgid "No data in MIDI message." +msgstr "Keine Daten in der MIDI-Nachricht." + +#: RtMidi.cpp:293 +msgid "" +"No valid MIDI interfaces. I'm using a dummy input interface that never " +"receives anything." +msgstr "" +"Keine gültige MIDI-Schnittstelle gefunden. Ich imitiere ein Eingabegerät, " +"das niemals etwas empfängt." + +#: RtMidi.cpp:322 +msgid "" +"No valid MIDI interfaces. I'm using a dummy output interface that does " +"nothing." +msgstr "" +"Keine gültige MIDI-Schnittstelle gefunden. Ich imitiere ein Ausgabegerät, " +"das nichts tut." + +#: RtMidi.cpp:396 +#, c-format +msgid "" +"Error formatting the error string:\n" +"'%s'\n" +"Found in %s::%s at \n" +"%s:%d" +msgstr "" +"Fehler bei der Formatierung der Fehlermeldung:\n" +"'%s'\n" +"gefunden in %s::%s bei \n" +"%s:%d" + +#: RtMidi.cpp:408 +msgid "Error: could not format the error message" +msgstr "Fehler: Ich konnte eine Fehlermeldung nicht formatieren." + +#: RtMidi.cpp:579 RtMidi.cpp:692 +#, c-format +msgid "" +"Support for the selected MIDI system %d has not been compiled into the " +"RtMidi library." +msgstr "" +"Die Unterstützung für das gewählte MIDI-System %d wurde nicht in die RtMidi-" +"Bibliothek eingebaut." + +#: RtMidi.cpp:597 RtMidi.cpp:709 +msgid "No supported MIDI system has been found." +msgstr "Es konnte kein benutzbares MIDI-System gefunden werden." + +#: RtMidi.cpp:798 RtMidi.cpp:815 +msgid "A callback function is already set." +msgstr "Es wurde schon eine Rückruffunktion gesetzt." + +#: RtMidi.cpp:804 RtMidi.cpp:821 +msgid "The callback function value is invalid." +msgstr "Der Funktionswert einer aufgerufenen Rückruffunktion ist ungültig." + +#: RtMidi.cpp:832 +msgid "No callback function was set." +msgstr "Es wurde keine Rückruffunktion gesetzt." + +#: RtMidi.cpp:854 +msgid "" +"Returning an empty MIDI message as all input is handled by a callback " +"function." +msgstr "" +"Es wird eine leere MIDI-Nachricht generiert, weil alle Eingaben durch eine " +"Rückruf-Funktion (engl. callback) abgefangen werden." + +#: RtMidi.cpp:1585 +msgid "Could not get the unique identifier of a midi endpoint." +msgstr "" +"Konnte die eindeutige Identifikation (UUID) eines MIDI-Endpunktes nicht " +"bekommen." + +#: RtMidi.cpp:1595 +msgid "" +"Could not get the endpoint back from the unique identifier of a midi " +"endpoint." +msgstr "" +"Konnte den MIDI-Endpunkt einer eindeutige Identifikation (UUID) nicht " +"bekommen." + +#: RtMidi.cpp:1610 +msgid "Could not get the entity of a midi endpoint." +msgstr "Konnte die Geräteeinheit eines MIDI-Endpunktes nicht bekommen." + +#: RtMidi.cpp:1671 RtMidi.cpp:1707 +msgid "Error creating OS X MIDI port because of invalid port flags." +msgstr "" +"Konnte die OS X-MIDI-Schnittstelle nicht anlegen, da ungültige Eigenschaften " +"angegeben wurden." + +#: RtMidi.cpp:1675 RtMidi.cpp:1711 RtMidi.cpp:2254 +msgid "Error creating OS-X MIDI port." +msgstr "Es gab einen Fehler beim Erzeugen der MIDI-Schnittstelle." + +#: RtMidi.cpp:1760 +#, c-format +msgid "Error creating OS-X MIDI client object (Error no: %d)." +msgstr "Fehler beim erzeugen des OS-X-MIDI-Klienten (Fehlernummer: %d)." + +#: RtMidi.cpp:2014 RtMidi.cpp:2080 RtMidi.cpp:3476 RtMidi.cpp:4720 +#: RtMidi.cpp:5931 +msgid "Error: Message queue limit reached." +msgstr "Fehler: Die Nachrichten-Warteschlange ist überlang." + +#: RtMidi.cpp:2141 RtMidi.cpp:2235 RtMidi.cpp:2412 RtMidi.cpp:2526 +#: RtMidi.cpp:3623 RtMidi.cpp:3740 RtMidi.cpp:4032 RtMidi.cpp:4194 +#: RtMidi.cpp:4777 RtMidi.cpp:5089 RtMidi.cpp:5161 RtMidi.cpp:6084 +#: RtMidi.cpp:6317 +msgid "A valid connection already exists." +msgstr "Es existiert schon eine gültige Verbindung." + +#: RtMidi.cpp:2149 RtMidi.cpp:3630 RtMidi.cpp:4784 +msgid "No MIDI input sources found." +msgstr "Keine MIDI-Eingabe-Geräte gefunden." + +#: RtMidi.cpp:2158 RtMidi.cpp:2337 RtMidi.cpp:2395 RtMidi.cpp:2426 +#: RtMidi.cpp:3640 RtMidi.cpp:4052 RtMidi.cpp:4989 RtMidi.cpp:5063 +#: RtMidi.cpp:5102 RtMidi.cpp:6182 RtMidi.cpp:6408 +#, c-format +msgid "The 'portNumber' argument (%d) is invalid." +msgstr "Die Portnummer %d ist ungültig." + +#: RtMidi.cpp:2170 +msgid "Error creating OS-X MIDI input port." +msgstr "Fehler beim Erzeugen der OS-X-MIDI-Eingabe-Schnittstelle." + +#: RtMidi.cpp:2181 +msgid "Error getting MIDI input source reference." +msgstr "Konnte keine Referenz zum MIDI-Eingang bekommen." + +#: RtMidi.cpp:2192 +msgid "Error connecting OS-X MIDI input port." +msgstr "Fehler beim verbinden mit dem OS-X-MIDI-Eingang." + +#: RtMidi.cpp:2214 +msgid "Error creating virtual OS-X MIDI destination." +msgstr "Konnte den virtuellen OS-X-MIDI-Ausgang nicht erzeugen." + +#: RtMidi.cpp:2230 RtMidi.cpp:2521 RtMidi.cpp:3735 RtMidi.cpp:4189 +#: RtMidi.cpp:6078 RtMidi.cpp:6311 +msgid "Data has not been allocated." +msgstr "Daten konnten nicht angelegt werden." + +#: RtMidi.cpp:2240 RtMidi.cpp:2531 +msgid "" +"Core MIDI has been instructed to open a non-Core MIDI port. This doesn't " +"work." +msgstr "" +"Core-MIDI wurde angewiesen, eine nicht-Core-MIDI-Schnittstelle zu öffnen. " +"Das geht nicht." + +#: RtMidi.cpp:2312 RtMidi.cpp:2479 +msgid "Setting client names is not implemented for Mac OS X CoreMIDI." +msgstr "" +"Das Ändern des Namens des MIDI-Klienten ist für Mac OS X CoreMIDI nicht " +"implementiert." + +#: RtMidi.cpp:2318 RtMidi.cpp:2485 +msgid "Setting port names is not implemented for Mac OS X CoreMIDI." +msgstr "" +"Das Ändern des Namens einer MIDI-Schnittstelle ist für Mac OS X CoreMIDI " +"nicht implementiert." + +#: RtMidi.cpp:2420 +msgid "No MIDI output destinations found." +msgstr "Es wurden keine MIDI-Ausgabegeräte gefunden." + +#: RtMidi.cpp:2438 +msgid "Error creating OS-X MIDI output port." +msgstr "Fehler beim Erzeugen der OS-X-MIDI-Ausgabe-Schnittstelle." + +#: RtMidi.cpp:2449 +msgid "Error getting MIDI output destination reference." +msgstr "Konnte keine Referenz zum MIDI-Ausgang bekommen." + +#: RtMidi.cpp:2494 +msgid "A virtual output port already exists." +msgstr "Es gibt schon einen virtuellen MIDI-Ausgang." + +#: RtMidi.cpp:2505 +msgid "Error creating OS-X virtual MIDI source." +msgstr "Konnte den virtuellen OS-X-MIDI-Eingang nicht erzeugen." + +#: RtMidi.cpp:2608 +msgid "message format problem ... not sysex but > 3 bytes?" +msgstr "" +"Nachrichtenformatproblem: Eine MIDI-Nachricht ist größer als 3 Bytes (und " +"keine SysEx-Nachricht)." + +#: RtMidi.cpp:2627 +msgid "Could not allocate packet list." +msgstr "Konnte die Paketliste nicht anlegen." + +#: RtMidi.cpp:2636 +msgid "Error sending MIDI to virtual destinations." +msgstr "Konnte MIDI-Daten nicht zu virtuellen Ausgängen senden." + +#: RtMidi.cpp:2645 RtMidi.cpp:4175 +msgid "Error sending MIDI message to port." +msgstr "Fehler beim Senden der MIDI-Nachricht zum Ausgang." + +#: RtMidi.cpp:2810 +msgid "Could not allocate ALSA port info structure." +msgstr "" +"Konnte keinen Speicher für die ALSA-MIDI-Schnittstelleninformation bekommen." + +#: RtMidi.cpp:2814 +#, c-format +msgid "Could not get ALSA port information: %s" +msgstr "Konnte keine ALSA-Schnittstelleninformation bekommen: %s" + +#: RtMidi.cpp:2820 +#, c-format +msgid "Could not set ALSA port information: %s" +msgstr "Konnte ALSA-Schnittstelleninformation nicht ändern: %s" + +#: RtMidi.cpp:2873 RtMidi.cpp:3149 RtMidi.cpp:3684 RtMidi.cpp:3745 +#: RtMidi.cpp:4079 +msgid "Could not allocate ALSA port subscription." +msgstr "" +"Konnte keinen Speicher für das Abonnoment der ALSA-MIDI-Schnittstelle " +"bekommen." + +#: RtMidi.cpp:2888 RtMidi.cpp:3693 RtMidi.cpp:4090 +msgid "Error making ALSA port connection." +msgstr "Konnte zwei ALSA-Schnittstellen nicht miteinander verbinden." + +#: RtMidi.cpp:3219 RtMidi.cpp:3719 RtMidi.cpp:3868 +msgid "Error starting MIDI input thread!" +msgstr "Konnte den MIDI-Eingabe-Prozess nicht starten." + +#: RtMidi.cpp:3257 RtMidi.cpp:3974 +msgid "Error initializing MIDI event parser." +msgstr "Fehler bei der Initialisierung des MIDI-Ereeignisparsers." + +#: RtMidi.cpp:3270 +msgid "Error initializing buffer memory." +msgstr "Fehler beim Anlegen des Pufferspeichers." + +#: RtMidi.cpp:3304 +msgid "MIDI input buffer overrun." +msgstr "MIDI-Eingabepuffer übergelaufen." + +#: RtMidi.cpp:3314 +msgid "ALSA returned without providing a MIDI event." +msgstr "ALSA kam zurück ohne ein MIDI-Ereignis mitzubringen." + +#: RtMidi.cpp:3324 +#, c-format +msgid "" +"Unknown MIDI input error.\n" +"The system reports:\n" +"%s" +msgstr "" +"Unbekannter MIDI-Eingabefehler.\n" +"Das System meldet:\n" +"%s" + +#: RtMidi.cpp:3382 +msgid "Error resizing buffer memory." +msgstr "Fehler beim Ändern der Grüße des Pufferspeichers." + +#: RtMidi.cpp:3455 +msgid "Event parsing error or not a MIDI event." +msgstr "" +"Analyse eines Ereignisses fehlgeschlagen oder es war kein MIDI-Ereignis." + +#: RtMidi.cpp:3532 +msgid "Error creating pipe objects." +msgstr "Fehler beim anlegen einer Pipe." + +#: RtMidi.cpp:3615 RtMidi.cpp:4024 +msgid "Error looking for port name." +msgstr "Fehler beim Suchen nach einem Schnittstellennamen." + +#: RtMidi.cpp:3671 +msgid "Error creating ALSA input port." +msgstr "Fehler beim Erzeugen der ALSA-Eingabe-Schnittstelle." + +#: RtMidi.cpp:3750 RtMidi.cpp:4204 +msgid "" +"ALSA has been instructed to open a non-ALSA MIDI port. This doesn't work." +msgstr "" +"ALSA wurde angewiesen, eine nicht-ALSA-MIDI-Schnittstelle zu öffnen. Das " +"geht nicht." + +#: RtMidi.cpp:3834 RtMidi.cpp:4130 +msgid "Error creating ALSA virtual port." +msgstr "Fehler beim Erzeugen einer virtuellen ALSA-Schnittstelle." + +#: RtMidi.cpp:3957 +msgid "Error creating ALSA sequencer client object." +msgstr "Fehler beim erzeugen des ALSA-Klienten." + +#: RtMidi.cpp:3981 RtMidi.cpp:4153 +msgid "Error while allocating buffer memory." +msgstr "Fehler beim Anlegen des Pufferspeichers." + +#: RtMidi.cpp:4040 +msgid "No MIDI output sinks found." +msgstr "Es wurden kiene Midi-Ausgabegeräte gefunden." + +#: RtMidi.cpp:4067 +msgid "Error creating ALSA output port." +msgstr "Fehler beim Erzeugen der ALSA-Ausgangs." + +#: RtMidi.cpp:4146 +msgid "ALSA error resizing MIDI event buffer." +msgstr "ALSA-Fehler beim Verändern der Größe des MIDI-Nachrichten-Puffers." + +#: RtMidi.cpp:4167 +msgid "Event parsing error." +msgstr "Fehler bei der Analyse eines Ereignisses." + +#: RtMidi.cpp:4199 +msgid "Error allocating ALSA port subscription." +msgstr "" +"Konnte keinen Speicher für das Abonnoment der ALSA-MIDI-Schnittstelle " +"bekommen." + +#: RtMidi.cpp:4377 +#, c-format +msgid "The port argument %d is invalid." +msgstr "Das Schnittstellenargument %d ist ungültig." + +#: RtMidi.cpp:4698 +msgid "Error sending sysex to Midi device." +msgstr "Fehler beim Senden der SysEx-Nachricht zum MIDI-Gerät." + +#: RtMidi.cpp:4759 +msgid "No MIDI input devices currently available." +msgstr "Es gibt momentan keine MIDI-Eingabegeräte." + +#: RtMidi.cpp:4769 +msgid "Failed to initialize a critical section." +msgstr "Konnte kritischen Bereich nicht initialisieren." + +#: RtMidi.cpp:4793 +#, c-format +msgid "the 'portNumber' argument (%d) is invalid." +msgstr "Die Portnummer %d ist ungültig." + +#: RtMidi.cpp:4805 +msgid "Error creating Windows MM MIDI input port." +msgstr "Konnte den Windows-Multimedia-MIDI-Eingang nicht erzeugen." + +#: RtMidi.cpp:4822 +msgid "Error initializing data for Windows MM MIDI input port." +msgstr "" +"Fehler beim Erzeugen der daten für einen Windows Multimedia MIDI-Eingang." + +#: RtMidi.cpp:4832 +msgid "Could not register the input buffer for Windows MM MIDI input port." +msgstr "" +"Konnte den Eingabe-Puffer eines Windows-Multimedia-MIDI-Eingangs nicht " +"erzeugen." + +#: RtMidi.cpp:4842 +msgid "Error starting Windows MM MIDI input port." +msgstr "Konnte einen Windows-Multimedia-MIDI-Eingang nicht aktivieren." + +#: RtMidi.cpp:4853 RtMidi.cpp:5148 +msgid "Virtual ports are not available Windows Multimedia MIDI API." +msgstr "" +"Windows Multimedia unterstützt keine virtuellen MIDI-Ein- und -Ausgänge." + +#: RtMidi.cpp:4860 RtMidi.cpp:5156 +msgid "" +"Windows Multimedia (WinMM) has been instructed to open a non-WinMM MIDI " +"port. This doesn't work." +msgstr "" +"Windows Multimedia (WinMM) wurde angewiesen, eine nicht-WinMM-MIDI-" +"Schnittstelle zu öffnen. Das geht nicht." + +#: RtMidi.cpp:4865 +msgid "" +"We are overwriting an existing connection. This is probably a programming " +"error." +msgstr "" +"Wir überschreiben eine vorhandene MIDI-Verbindung. Das ist vermutlich ein " +"Programmierfehler." + +#: RtMidi.cpp:4870 +msgid "Trying to open a non-input port as input MIDI port. This doesn't work." +msgstr "" +"Versuche eine Nicht-Eingang als MIDI-Eingang zu öffnen. Das geht nicht." + +#: RtMidi.cpp:4884 RtMidi.cpp:5180 +msgid "" +"Some change in the arrangement of MIDI input ports invalidated the port " +"descriptor." +msgstr "" +"Eine Veränderung bei den MIDI-Schnittstellen hat die interne " +"Schnittstellenbeschreibung ungültig gemacht." + +#: RtMidi.cpp:4899 +msgid "The handle is invalid. Did you disconnect the device?" +msgstr "" +"Ein Schnittstellenverweis ist ungültig. Haben Sie das zugehörige Gerät " +"entfernt?" + +#: RtMidi.cpp:4903 RtMidi.cpp:5199 +msgid "" +"The system has no driver for our handle :-(. Did you disconnect the device?" +msgstr "" +"Das System hat keinen Treiber für unseren Schnittstellenverweis (mehr) :-(. " +"Haben Sie das zugehörige Gerät entfernt?" + +#: RtMidi.cpp:4907 RtMidi.cpp:5203 +msgid "Out of memory." +msgstr "Hauptspeicher erschöpft." + +#: RtMidi.cpp:4954 +msgid "Error closing Windows MM MIDI input port." +msgstr "" +"Konnte den Windows Multimedia MIDI-Eingang nicht ordnungsgemäß schließen." + +#: RtMidi.cpp:4969 RtMidi.cpp:5135 +msgid "Setting the client name is not supported by Windows MM." +msgstr "" +"Das Ändern des Namens des MIDI-Klienten wird vom Windows MIDI Mapper nicht " +"unterstützt." + +#: RtMidi.cpp:4975 RtMidi.cpp:5141 +msgid "Setting the port name is not supported by Windows MM." +msgstr "" +"Das Ändern des Namens der MIDI-Schnittstelle wird vom Windows MIDI Mapper " +"nicht unterstützt." + +#: RtMidi.cpp:5041 +msgid "No MIDI output devices currently available." +msgstr "Es gibt momentan keine MIDI-Ausgabegeräte." + +#: RtMidi.cpp:5096 +msgid "No MIDI output destinations found!" +msgstr "Es wurden keine MIDI-Ausgabegeräte gefunden." + +#: RtMidi.cpp:5114 +msgid "Error creating Windows MM MIDI output port." +msgstr "Konnte den Windows Multimedia MIDI-Ausgang nicht erzeugen." + +#: RtMidi.cpp:5166 +msgid "The port descriptor cannot be used to open an output port." +msgstr "" +"Eine Schnittstellenbeschreibung kann nicht benutzt werden, um einen MIDI-" +"Ausgang zu erzeugen." + +#: RtMidi.cpp:5195 +msgid "The internal handle is invalid. Did you disconnect the device?" +msgstr "" +"Ein interner Schnittstellenverweis ist ungültig. Haben sie das zugehörige " +"Gerät entfernt?" + +#: RtMidi.cpp:5230 +msgid "Message argument is empty." +msgstr "Das Nachrichtenargument ist leer." + +#: RtMidi.cpp:5242 +msgid "Error while allocating sysex message memory." +msgstr "Fehler beim Anlegen des Speichers für SysEx-Nachrichten." + +#: RtMidi.cpp:5258 +msgid "Error preparing sysex header." +msgstr "Fehler beim Erstellen des SysEx-Kopfes." + +#: RtMidi.cpp:5267 +msgid "Error sending sysex message." +msgstr "Fehler beim Senden der SysEx-Nachricht." + +#: RtMidi.cpp:5280 +msgid "Message size is greater than 3 bytes (and not sysex)." +msgstr "" +"Eine MIDI-Nachricht ist größer als 3 Bytes (und keine SysEx-Nachricht)." + +#: RtMidi.cpp:5296 +msgid "Error sending MIDI message." +msgstr "Fehler beim Senden der MIDI-Nachricht." + +#: RtMidi.cpp:5535 +msgid "Could not connect to JACK server. Is it runnig?" +msgstr "Ich konnte mich nicht mit dem JACK-Server verbinden. Läuft er?" + +#: RtMidi.cpp:5767 +msgid "Error opening JACK port subscription." +msgstr "Fehler beim abonnieren einer JACK-Schnittstelle." + +#: RtMidi.cpp:6002 RtMidi.cpp:6248 +msgid "JACK server not running?" +msgstr "Läuft der JACK-Server?" + +#: RtMidi.cpp:6046 RtMidi.cpp:6279 +msgid "Error creating JACK port." +msgstr "Es gab einen Fehler beim Erzeugen der JACK-Schnittstelle." + +#: RtMidi.cpp:6066 RtMidi.cpp:6299 +msgid "Error creating JACK virtual port." +msgstr "Fehler beim Erzeugen einer virtuellen JACK-Schnittstelle." + +#: RtMidi.cpp:6090 RtMidi.cpp:6323 +msgid "" +"JACK has been instructed to open a non-JACK MIDI port. This doesn't work." +msgstr "" +"JACK wurde angewiesen, eine nicht-JACK-Schnittstelle zu öffnen. Das geht " +"nicht." + +#: RtMidi.cpp:6173 RtMidi.cpp:6401 +msgid "No ports available." +msgstr "Keine Schnittstellen verfügbar." + +#: RtMidi.cpp:6201 RtMidi.cpp:6429 +msgid "Setting the client name is not supported by JACK." +msgstr "" +"Das Ändern des Namens des MIDI-Klienten wird von JACK nicht unterstützt." + +#~ msgid "MidiInDummy: This class provides no functionality." +#~ msgstr "MidiInDummy: Diese Klasse stellt keine Funktionen zur verfügung." + +#, fuzzy +#~ msgid "InitializeCriticalSectionAndSpinCount failed." +#~ msgstr "%s: malloc ist fehlgeschlagen: %s\n" + +#, fuzzy +#~ msgid "ALSA error allocation port subscription." +#~ msgstr "" +#~ "Konnte keinen Speicher für das Abonnoments der MIDI-Schnittstelle " +#~ "bekommen." + +#, fuzzy +#~ msgid "A valid connection already exists" +#~ msgstr "Es existiert schon eine gültige Verbindung." + +#, fuzzy +#~ msgid "No ports available" +#~ msgstr "Keine Adresse verfügbar" + +#~ msgid "" +#~ "An invalid (i.e. non-CORE) port descriptor has been passed to openPort." +#~ msgstr "Es wurde eine ungültige Schnittstellenbeschreibung (" + +#~ msgid "Internal error: data has not been allocated." +#~ msgstr " " + +#~ msgid "No data in message argument!" +#~ msgstr "Keine Daten in der MIDI-Nachricht." diff --git a/po/en@boldquot.header b/po/en@boldquot.header new file mode 100644 index 00000000..fedb6a06 --- /dev/null +++ b/po/en@boldquot.header @@ -0,0 +1,25 @@ +# All this catalog "translates" are quotation characters. +# The msgids must be ASCII and therefore cannot contain real quotation +# characters, only substitutes like grave accent (0x60), apostrophe (0x27) +# and double quote (0x22). These substitutes look strange; see +# http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html +# +# This catalog translates grave accent (0x60) and apostrophe (0x27) to +# left single quotation mark (U+2018) and right single quotation mark (U+2019). +# It also translates pairs of apostrophe (0x27) to +# left single quotation mark (U+2018) and right single quotation mark (U+2019) +# and pairs of quotation mark (0x22) to +# left double quotation mark (U+201C) and right double quotation mark (U+201D). +# +# When output to an UTF-8 terminal, the quotation characters appear perfectly. +# When output to an ISO-8859-1 terminal, the single quotation marks are +# transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to +# grave/acute accent (by libiconv), and the double quotation marks are +# transliterated to 0x22. +# When output to an ASCII terminal, the single quotation marks are +# transliterated to apostrophes, and the double quotation marks are +# transliterated to 0x22. +# +# This catalog furthermore displays the text between the quotation marks in +# bold face, assuming the VT100/XTerm escape sequences. +# diff --git a/po/en@quot.header b/po/en@quot.header new file mode 100644 index 00000000..a9647fc3 --- /dev/null +++ b/po/en@quot.header @@ -0,0 +1,22 @@ +# All this catalog "translates" are quotation characters. +# The msgids must be ASCII and therefore cannot contain real quotation +# characters, only substitutes like grave accent (0x60), apostrophe (0x27) +# and double quote (0x22). These substitutes look strange; see +# http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html +# +# This catalog translates grave accent (0x60) and apostrophe (0x27) to +# left single quotation mark (U+2018) and right single quotation mark (U+2019). +# It also translates pairs of apostrophe (0x27) to +# left single quotation mark (U+2018) and right single quotation mark (U+2019) +# and pairs of quotation mark (0x22) to +# left double quotation mark (U+201C) and right double quotation mark (U+201D). +# +# When output to an UTF-8 terminal, the quotation characters appear perfectly. +# When output to an ISO-8859-1 terminal, the single quotation marks are +# transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to +# grave/acute accent (by libiconv), and the double quotation marks are +# transliterated to 0x22. +# When output to an ASCII terminal, the single quotation marks are +# transliterated to apostrophes, and the double quotation marks are +# transliterated to 0x22. +# diff --git a/po/insert-header.sin b/po/insert-header.sin new file mode 100644 index 00000000..b26de01f --- /dev/null +++ b/po/insert-header.sin @@ -0,0 +1,23 @@ +# Sed script that inserts the file called HEADER before the header entry. +# +# At each occurrence of a line starting with "msgid ", we execute the following +# commands. At the first occurrence, insert the file. At the following +# occurrences, do nothing. The distinction between the first and the following +# occurrences is achieved by looking at the hold space. +/^msgid /{ +x +# Test if the hold space is empty. +s/m/m/ +ta +# Yes it was empty. First occurrence. Read the file. +r HEADER +# Output the file's contents by reading the next line. But don't lose the +# current line while doing this. +g +N +bb +:a +# The hold space was nonempty. Following occurrences. Do nothing. +x +:b +} diff --git a/po/quot.sed b/po/quot.sed new file mode 100644 index 00000000..0122c463 --- /dev/null +++ b/po/quot.sed @@ -0,0 +1,6 @@ +s/"\([^"]*\)"/“\1”/g +s/`\([^`']*\)'/‘\1’/g +s/ '\([^`']*\)' / ‘\1’ /g +s/ '\([^`']*\)'$/ ‘\1’/g +s/^'\([^`']*\)' /‘\1’ /g +s/“”/""/g diff --git a/po/remove-potcdate.sin b/po/remove-potcdate.sin new file mode 100644 index 00000000..2436c49e --- /dev/null +++ b/po/remove-potcdate.sin @@ -0,0 +1,19 @@ +# Sed script that remove the POT-Creation-Date line in the header entry +# from a POT file. +# +# The distinction between the first and the following occurrences of the +# pattern is achieved by looking at the hold space. +/^"POT-Creation-Date: .*"$/{ +x +# Test if the hold space is empty. +s/P/P/ +ta +# Yes it was empty. First occurrence. Remove the line. +g +d +bb +:a +# The hold space was nonempty. Following occurrences. Do nothing. +x +:b +} diff --git a/po/rtmidi-ts.pot b/po/rtmidi-ts.pot new file mode 100644 index 00000000..ff848fa5 --- /dev/null +++ b/po/rtmidi-ts.pot @@ -0,0 +1,527 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Gary P. Scavone, Tobias Schlemmer +# This file is distributed under the same license as the rtmidi-ts package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: rtmidi-ts 5.0.0\n" +"Report-Msgid-Bugs-To: https://github.com/keinstein/rtmidi\n" +"POT-Creation-Date: 2018-08-13 18:56+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: RtMidi.h:129 +msgid "Automatic selection" +msgstr "" + +#: RtMidi.h:130 +msgid "Core MIDI" +msgstr "" + +#: RtMidi.h:131 +msgid "ALSA" +msgstr "" + +#: RtMidi.h:132 +msgid "JACK" +msgstr "" + +#: RtMidi.h:133 +msgid "Windows Multimedia" +msgstr "" + +#: RtMidi.h:134 +msgid "DirectX/Kernel Streaming" +msgstr "" + +#: RtMidi.h:135 +msgid "NULL device" +msgstr "" + +#: RtMidi.h:136 +msgid "All available MIDI systems" +msgstr "" + +#: RtMidi.h:1106 RtMidi.h:1277 RtMidi.h:1490 RtMidi.h:1511 +msgid "Passed NULL pointer." +msgstr "" + +#: RtMidi.h:1308 RtMidi.h:1317 RtMidi.cpp:2596 +msgid "No data in message argument." +msgstr "" + +#: RtMidi.h:1432 +msgid "A NULL pointer has been passed as port descriptor" +msgstr "" + +#: RtMidi.h:1452 RtMidi.h:1531 RtMidi.h:1543 +msgid "No valid MIDI system has been selected." +msgstr "" + +#: RtMidi.h:1473 +msgid "Could not find any valid MIDI system." +msgstr "" + +#: RtMidi.h:1495 +msgid "No valid MIDI system has been found." +msgstr "" + +#: RtMidi.h:1537 +msgid "No data in MIDI message." +msgstr "" + +#: RtMidi.cpp:293 +msgid "" +"No valid MIDI interfaces. I'm using a dummy input interface that never " +"receives anything." +msgstr "" + +#: RtMidi.cpp:322 +msgid "" +"No valid MIDI interfaces. I'm using a dummy output interface that does " +"nothing." +msgstr "" + +#: RtMidi.cpp:396 +#, c-format +msgid "" +"Error formatting the error string:\n" +"'%s'\n" +"Found in %s::%s at \n" +"%s:%d" +msgstr "" + +#: RtMidi.cpp:408 +msgid "Error: could not format the error message" +msgstr "" + +#: RtMidi.cpp:579 RtMidi.cpp:692 +#, c-format +msgid "" +"Support for the selected MIDI system %d has not been compiled into the " +"RtMidi library." +msgstr "" + +#: RtMidi.cpp:597 RtMidi.cpp:709 +msgid "No supported MIDI system has been found." +msgstr "" + +#: RtMidi.cpp:798 RtMidi.cpp:815 +msgid "A callback function is already set." +msgstr "" + +#: RtMidi.cpp:804 RtMidi.cpp:821 +msgid "The callback function value is invalid." +msgstr "" + +#: RtMidi.cpp:832 +msgid "No callback function was set." +msgstr "" + +#: RtMidi.cpp:854 +msgid "" +"Returning an empty MIDI message as all input is handled by a callback " +"function." +msgstr "" + +#: RtMidi.cpp:1585 +msgid "Could not get the unique identifier of a midi endpoint." +msgstr "" + +#: RtMidi.cpp:1595 +msgid "" +"Could not get the endpoint back from the unique identifier of a midi " +"endpoint." +msgstr "" + +#: RtMidi.cpp:1610 +msgid "Could not get the entity of a midi endpoint." +msgstr "" + +#: RtMidi.cpp:1671 RtMidi.cpp:1707 +msgid "Error creating OS X MIDI port because of invalid port flags." +msgstr "" + +#: RtMidi.cpp:1675 RtMidi.cpp:1711 RtMidi.cpp:2254 +msgid "Error creating OS-X MIDI port." +msgstr "" + +#: RtMidi.cpp:1760 +#, c-format +msgid "Error creating OS-X MIDI client object (Error no: %d)." +msgstr "" + +#: RtMidi.cpp:2014 RtMidi.cpp:2080 RtMidi.cpp:3476 RtMidi.cpp:4720 +#: RtMidi.cpp:5931 +msgid "Error: Message queue limit reached." +msgstr "" + +#: RtMidi.cpp:2141 RtMidi.cpp:2235 RtMidi.cpp:2412 RtMidi.cpp:2526 +#: RtMidi.cpp:3623 RtMidi.cpp:3740 RtMidi.cpp:4032 RtMidi.cpp:4194 +#: RtMidi.cpp:4777 RtMidi.cpp:5089 RtMidi.cpp:5161 RtMidi.cpp:6084 +#: RtMidi.cpp:6317 +msgid "A valid connection already exists." +msgstr "" + +#: RtMidi.cpp:2149 RtMidi.cpp:3630 RtMidi.cpp:4784 +msgid "No MIDI input sources found." +msgstr "" + +#: RtMidi.cpp:2158 RtMidi.cpp:2337 RtMidi.cpp:2395 RtMidi.cpp:2426 +#: RtMidi.cpp:3640 RtMidi.cpp:4052 RtMidi.cpp:4989 RtMidi.cpp:5063 +#: RtMidi.cpp:5102 RtMidi.cpp:6182 RtMidi.cpp:6408 +#, c-format +msgid "The 'portNumber' argument (%d) is invalid." +msgstr "" + +#: RtMidi.cpp:2170 +msgid "Error creating OS-X MIDI input port." +msgstr "" + +#: RtMidi.cpp:2181 +msgid "Error getting MIDI input source reference." +msgstr "" + +#: RtMidi.cpp:2192 +msgid "Error connecting OS-X MIDI input port." +msgstr "" + +#: RtMidi.cpp:2214 +msgid "Error creating virtual OS-X MIDI destination." +msgstr "" + +#: RtMidi.cpp:2230 RtMidi.cpp:2521 RtMidi.cpp:3735 RtMidi.cpp:4189 +#: RtMidi.cpp:6078 RtMidi.cpp:6311 +msgid "Data has not been allocated." +msgstr "" + +#: RtMidi.cpp:2240 RtMidi.cpp:2531 +msgid "" +"Core MIDI has been instructed to open a non-Core MIDI port. This doesn't " +"work." +msgstr "" + +#: RtMidi.cpp:2312 RtMidi.cpp:2479 +msgid "Setting client names is not implemented for Mac OS X CoreMIDI." +msgstr "" + +#: RtMidi.cpp:2318 RtMidi.cpp:2485 +msgid "Setting port names is not implemented for Mac OS X CoreMIDI." +msgstr "" + +#: RtMidi.cpp:2420 +msgid "No MIDI output destinations found." +msgstr "" + +#: RtMidi.cpp:2438 +msgid "Error creating OS-X MIDI output port." +msgstr "" + +#: RtMidi.cpp:2449 +msgid "Error getting MIDI output destination reference." +msgstr "" + +#: RtMidi.cpp:2494 +msgid "A virtual output port already exists." +msgstr "" + +#: RtMidi.cpp:2505 +msgid "Error creating OS-X virtual MIDI source." +msgstr "" + +#: RtMidi.cpp:2608 +msgid "message format problem ... not sysex but > 3 bytes?" +msgstr "" + +#: RtMidi.cpp:2627 +msgid "Could not allocate packet list." +msgstr "" + +#: RtMidi.cpp:2636 +msgid "Error sending MIDI to virtual destinations." +msgstr "" + +#: RtMidi.cpp:2645 RtMidi.cpp:4175 +msgid "Error sending MIDI message to port." +msgstr "" + +#: RtMidi.cpp:2810 +msgid "Could not allocate ALSA port info structure." +msgstr "" + +#: RtMidi.cpp:2814 +#, c-format +msgid "Could not get ALSA port information: %s" +msgstr "" + +#: RtMidi.cpp:2820 +#, c-format +msgid "Could not set ALSA port information: %s" +msgstr "" + +#: RtMidi.cpp:2873 RtMidi.cpp:3149 RtMidi.cpp:3684 RtMidi.cpp:3745 +#: RtMidi.cpp:4079 +msgid "Could not allocate ALSA port subscription." +msgstr "" + +#: RtMidi.cpp:2888 RtMidi.cpp:3693 RtMidi.cpp:4090 +msgid "Error making ALSA port connection." +msgstr "" + +#: RtMidi.cpp:3219 RtMidi.cpp:3719 RtMidi.cpp:3868 +msgid "Error starting MIDI input thread!" +msgstr "" + +#: RtMidi.cpp:3257 RtMidi.cpp:3974 +msgid "Error initializing MIDI event parser." +msgstr "" + +#: RtMidi.cpp:3270 +msgid "Error initializing buffer memory." +msgstr "" + +#: RtMidi.cpp:3304 +msgid "MIDI input buffer overrun." +msgstr "" + +#: RtMidi.cpp:3314 +msgid "ALSA returned without providing a MIDI event." +msgstr "" + +#: RtMidi.cpp:3324 +#, c-format +msgid "" +"Unknown MIDI input error.\n" +"The system reports:\n" +"%s" +msgstr "" + +#: RtMidi.cpp:3382 +msgid "Error resizing buffer memory." +msgstr "" + +#: RtMidi.cpp:3455 +msgid "Event parsing error or not a MIDI event." +msgstr "" + +#: RtMidi.cpp:3532 +msgid "Error creating pipe objects." +msgstr "" + +#: RtMidi.cpp:3615 RtMidi.cpp:4024 +msgid "Error looking for port name." +msgstr "" + +#: RtMidi.cpp:3671 +msgid "Error creating ALSA input port." +msgstr "" + +#: RtMidi.cpp:3750 RtMidi.cpp:4204 +msgid "" +"ALSA has been instructed to open a non-ALSA MIDI port. This doesn't work." +msgstr "" + +#: RtMidi.cpp:3834 RtMidi.cpp:4130 +msgid "Error creating ALSA virtual port." +msgstr "" + +#: RtMidi.cpp:3957 +msgid "Error creating ALSA sequencer client object." +msgstr "" + +#: RtMidi.cpp:3981 RtMidi.cpp:4153 +msgid "Error while allocating buffer memory." +msgstr "" + +#: RtMidi.cpp:4040 +msgid "No MIDI output sinks found." +msgstr "" + +#: RtMidi.cpp:4067 +msgid "Error creating ALSA output port." +msgstr "" + +#: RtMidi.cpp:4146 +msgid "ALSA error resizing MIDI event buffer." +msgstr "" + +#: RtMidi.cpp:4167 +msgid "Event parsing error." +msgstr "" + +#: RtMidi.cpp:4199 +msgid "Error allocating ALSA port subscription." +msgstr "" + +#: RtMidi.cpp:4377 +#, c-format +msgid "The port argument %d is invalid." +msgstr "" + +#: RtMidi.cpp:4698 +msgid "Error sending sysex to Midi device." +msgstr "" + +#: RtMidi.cpp:4759 +msgid "No MIDI input devices currently available." +msgstr "" + +#: RtMidi.cpp:4769 +msgid "Failed to initialize a critical section." +msgstr "" + +#: RtMidi.cpp:4793 +#, c-format +msgid "the 'portNumber' argument (%d) is invalid." +msgstr "" + +#: RtMidi.cpp:4805 +msgid "Error creating Windows MM MIDI input port." +msgstr "" + +#: RtMidi.cpp:4822 +msgid "Error initializing data for Windows MM MIDI input port." +msgstr "" + +#: RtMidi.cpp:4832 +msgid "Could not register the input buffer for Windows MM MIDI input port." +msgstr "" + +#: RtMidi.cpp:4842 +msgid "Error starting Windows MM MIDI input port." +msgstr "" + +#: RtMidi.cpp:4853 RtMidi.cpp:5148 +msgid "Virtual ports are not available Windows Multimedia MIDI API." +msgstr "" + +#: RtMidi.cpp:4860 RtMidi.cpp:5156 +msgid "" +"Windows Multimedia (WinMM) has been instructed to open a non-WinMM MIDI " +"port. This doesn't work." +msgstr "" + +#: RtMidi.cpp:4865 +msgid "" +"We are overwriting an existing connection. This is probably a programming " +"error." +msgstr "" + +#: RtMidi.cpp:4870 +msgid "Trying to open a non-input port as input MIDI port. This doesn't work." +msgstr "" + +#: RtMidi.cpp:4884 RtMidi.cpp:5180 +msgid "" +"Some change in the arrangement of MIDI input ports invalidated the port " +"descriptor." +msgstr "" + +#: RtMidi.cpp:4899 +msgid "The handle is invalid. Did you disconnect the device?" +msgstr "" + +#: RtMidi.cpp:4903 RtMidi.cpp:5199 +msgid "" +"The system has no driver for our handle :-(. Did you disconnect the device?" +msgstr "" + +#: RtMidi.cpp:4907 RtMidi.cpp:5203 +msgid "Out of memory." +msgstr "" + +#: RtMidi.cpp:4954 +msgid "Error closing Windows MM MIDI input port." +msgstr "" + +#: RtMidi.cpp:4969 RtMidi.cpp:5135 +msgid "Setting the client name is not supported by Windows MM." +msgstr "" + +#: RtMidi.cpp:4975 RtMidi.cpp:5141 +msgid "Setting the port name is not supported by Windows MM." +msgstr "" + +#: RtMidi.cpp:5041 +msgid "No MIDI output devices currently available." +msgstr "" + +#: RtMidi.cpp:5096 +msgid "No MIDI output destinations found!" +msgstr "" + +#: RtMidi.cpp:5114 +msgid "Error creating Windows MM MIDI output port." +msgstr "" + +#: RtMidi.cpp:5166 +msgid "The port descriptor cannot be used to open an output port." +msgstr "" + +#: RtMidi.cpp:5195 +msgid "The internal handle is invalid. Did you disconnect the device?" +msgstr "" + +#: RtMidi.cpp:5230 +msgid "Message argument is empty." +msgstr "" + +#: RtMidi.cpp:5242 +msgid "Error while allocating sysex message memory." +msgstr "" + +#: RtMidi.cpp:5258 +msgid "Error preparing sysex header." +msgstr "" + +#: RtMidi.cpp:5267 +msgid "Error sending sysex message." +msgstr "" + +#: RtMidi.cpp:5280 +msgid "Message size is greater than 3 bytes (and not sysex)." +msgstr "" + +#: RtMidi.cpp:5296 +msgid "Error sending MIDI message." +msgstr "" + +#: RtMidi.cpp:5535 +msgid "Could not connect to JACK server. Is it runnig?" +msgstr "" + +#: RtMidi.cpp:5767 +msgid "Error opening JACK port subscription." +msgstr "" + +#: RtMidi.cpp:6002 RtMidi.cpp:6248 +msgid "JACK server not running?" +msgstr "" + +#: RtMidi.cpp:6046 RtMidi.cpp:6279 +msgid "Error creating JACK port." +msgstr "" + +#: RtMidi.cpp:6066 RtMidi.cpp:6299 +msgid "Error creating JACK virtual port." +msgstr "" + +#: RtMidi.cpp:6090 RtMidi.cpp:6323 +msgid "" +"JACK has been instructed to open a non-JACK MIDI port. This doesn't work." +msgstr "" + +#: RtMidi.cpp:6173 RtMidi.cpp:6401 +msgid "No ports available." +msgstr "" + +#: RtMidi.cpp:6201 RtMidi.cpp:6429 +msgid "Setting the client name is not supported by JACK." +msgstr "" diff --git a/po/rtmidi.pot b/po/rtmidi.pot new file mode 100644 index 00000000..60eb9ba2 --- /dev/null +++ b/po/rtmidi.pot @@ -0,0 +1,484 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Gary P. Scavone +# This file is distributed under the same license as the rtmidi package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: rtmidi 2.1.0\n" +"Report-Msgid-Bugs-To: https://github.com/thestk/rtmidi\n" +"POT-Creation-Date: 2016-02-28 19:22+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: RtMidi.h:107 +msgid "Automatic selection" +msgstr "" + +#: RtMidi.h:108 +msgid "Core MIDI" +msgstr "" + +#: RtMidi.h:109 +msgid "ALSA" +msgstr "" + +#: RtMidi.h:110 +msgid "JACK" +msgstr "" + +#: RtMidi.h:111 +msgid "Windows Multimedia" +msgstr "" + +#: RtMidi.h:112 +msgid "DirectX/Kernel Streaming" +msgstr "" + +#: RtMidi.h:113 +msgid "NULL device" +msgstr "" + +#: RtMidi.h:114 +msgid "All available MIDI systems" +msgstr "" + +#: RtMidi.h:530 RtMidi.h:692 RtMidi.h:1221 RtMidi.h:1313 +msgid "Passed NULL pointer." +msgstr "" + +#: RtMidi.h:724 RtMidi.cpp:2165 +msgid "No data in message argument." +msgstr "" + +#: RtMidi.h:1083 +msgid "A NULL pointer has been passed as port descriptor" +msgstr "" + +#: RtMidi.h:1115 RtMidi.h:1346 RtMidi.h:1370 RtMidi.h:1383 +msgid "No valid MIDI system has been selected." +msgstr "" + +#: RtMidi.h:1185 +msgid "Could not find any valid MIDI system." +msgstr "" + +#: RtMidi.h:1226 +msgid "No valid MIDI system has been found." +msgstr "" + +#: RtMidi.h:1364 +msgid "No data in MIDI message." +msgstr "" + +#: RtMidi.h:1610 RtMidi.h:1632 +msgid "MidiInDummy: This class provides no functionality." +msgstr "" + +#: RtMidi.cpp:87 +#, c-format +msgid "" +"Error formatting the error string:\n" +"'%s'\n" +"Found in %s::%s at \n" +"%s:%d" +msgstr "" + +#: RtMidi.cpp:99 +msgid "Error: could not format the error message" +msgstr "" + +#: RtMidi.cpp:267 RtMidi.cpp:370 +#, c-format +msgid "" +"Support for the selected MIDI system %d has not been compiled into the " +"RtMidi library." +msgstr "" + +#: RtMidi.cpp:285 RtMidi.cpp:387 +msgid "No supported MIDI system has been found." +msgstr "" + +#: RtMidi.cpp:476 RtMidi.cpp:493 +msgid "A callback function is already set." +msgstr "" + +#: RtMidi.cpp:482 RtMidi.cpp:499 +msgid "The callback function value is invalid." +msgstr "" + +#: RtMidi.cpp:510 +msgid "No callback function was set." +msgstr "" + +#: RtMidi.cpp:532 +msgid "" +"Returning an empty MIDI message as all input is handled by a callback " +"function." +msgstr "" + +#: RtMidi.cpp:1187 +msgid "Could not get the unique identifier of a midi endpoint." +msgstr "" + +#: RtMidi.cpp:1197 +msgid "" +"Could not get the endpoint back from the unique identifier of a midi " +"endpoint." +msgstr "" + +#: RtMidi.cpp:1212 +msgid "Could not get the entity of a midi endpoint." +msgstr "" + +#: RtMidi.cpp:1277 RtMidi.cpp:1317 +msgid "Error creating OS X MIDI port because of invalid port flags." +msgstr "" + +#: RtMidi.cpp:1281 RtMidi.cpp:1321 RtMidi.cpp:1862 +msgid "Error creating OS-X MIDI port." +msgstr "" + +#: RtMidi.cpp:1374 +msgid "Error creating OS-X MIDI client object." +msgstr "" + +#: RtMidi.cpp:1630 RtMidi.cpp:1700 RtMidi.cpp:2983 RtMidi.cpp:4162 +#: RtMidi.cpp:5236 +msgid "Error: Message queue limit reached." +msgstr "" + +#: RtMidi.cpp:1751 RtMidi.cpp:1843 RtMidi.cpp:2001 RtMidi.cpp:2094 +#: RtMidi.cpp:3127 RtMidi.cpp:3244 RtMidi.cpp:3512 RtMidi.cpp:3660 +#: RtMidi.cpp:4218 RtMidi.cpp:4521 RtMidi.cpp:4580 RtMidi.cpp:5424 +#: RtMidi.cpp:5641 +msgid "A valid connection already exists." +msgstr "" + +#: RtMidi.cpp:1759 RtMidi.cpp:3134 RtMidi.cpp:4225 +msgid "No MIDI input sources found." +msgstr "" + +#: RtMidi.cpp:1768 RtMidi.cpp:1926 RtMidi.cpp:1984 RtMidi.cpp:2015 +#: RtMidi.cpp:3144 RtMidi.cpp:3532 RtMidi.cpp:4413 RtMidi.cpp:4491 +#: RtMidi.cpp:4534 RtMidi.cpp:5520 RtMidi.cpp:5732 +#, c-format +msgid "The 'portNumber' argument (%d) is invalid." +msgstr "" + +#: RtMidi.cpp:1780 +msgid "Error creating OS-X MIDI input port." +msgstr "" + +#: RtMidi.cpp:1790 +msgid "Error getting MIDI input source reference." +msgstr "" + +#: RtMidi.cpp:1800 +msgid "Error connecting OS-X MIDI input port." +msgstr "" + +#: RtMidi.cpp:1822 +msgid "Error creating virtual OS-X MIDI destination." +msgstr "" + +#: RtMidi.cpp:1838 RtMidi.cpp:2089 RtMidi.cpp:3239 RtMidi.cpp:3655 +#: RtMidi.cpp:5418 RtMidi.cpp:5635 +msgid "Data has not been allocated." +msgstr "" + +#: RtMidi.cpp:1848 RtMidi.cpp:2099 +msgid "" +"Core MIDI has been instructed to open a non-Core MIDI port. This doesn't " +"work." +msgstr "" + +#: RtMidi.cpp:2009 +msgid "No MIDI output destinations found." +msgstr "" + +#: RtMidi.cpp:2027 +msgid "Error creating OS-X MIDI output port." +msgstr "" + +#: RtMidi.cpp:2037 +msgid "Error getting MIDI output destination reference." +msgstr "" + +#: RtMidi.cpp:2062 +msgid "A virtual output port already exists." +msgstr "" + +#: RtMidi.cpp:2073 +msgid "Error creating OS-X virtual MIDI source." +msgstr "" + +#: RtMidi.cpp:2177 +msgid "message format problem ... not sysex but > 3 bytes?" +msgstr "" + +#: RtMidi.cpp:2196 +msgid "Could not allocate packet list." +msgstr "" + +#: RtMidi.cpp:2205 +msgid "Error sending MIDI to virtual destinations." +msgstr "" + +#: RtMidi.cpp:2214 RtMidi.cpp:3641 +msgid "Error sending MIDI message to port." +msgstr "" + +#: RtMidi.cpp:2418 RtMidi.cpp:2681 RtMidi.cpp:3188 RtMidi.cpp:3249 +#: RtMidi.cpp:3558 +msgid "Could not allocate ALSA port subscription." +msgstr "" + +#: RtMidi.cpp:2433 RtMidi.cpp:3197 RtMidi.cpp:3568 +msgid "Error making ALSA port connection." +msgstr "" + +#: RtMidi.cpp:2503 RtMidi.cpp:3439 +msgid "Error creating ALSA sequencer client object." +msgstr "" + +#: RtMidi.cpp:2743 RtMidi.cpp:3223 RtMidi.cpp:3372 +msgid "Error starting MIDI input thread!" +msgstr "" + +#: RtMidi.cpp:2781 RtMidi.cpp:3456 +msgid "Error initializing MIDI event parser." +msgstr "" + +#: RtMidi.cpp:2794 +msgid "Error initializing buffer memory." +msgstr "" + +#: RtMidi.cpp:2828 +msgid "MIDI input buffer overrun." +msgstr "" + +#: RtMidi.cpp:2838 +msgid "ALSA returned without providing a MIDI event." +msgstr "" + +#: RtMidi.cpp:2848 +#, c-format +msgid "" +"Unknown MIDI input error.\n" +"The system reports:\n" +"%s" +msgstr "" + +#: RtMidi.cpp:2906 +msgid "Error resizing buffer memory." +msgstr "" + +#: RtMidi.cpp:2957 +msgid "Event parsing error or not a MIDI event." +msgstr "" + +#: RtMidi.cpp:3038 +msgid "Error creating pipe objects." +msgstr "" + +#: RtMidi.cpp:3119 RtMidi.cpp:3504 +msgid "Error looking for port name." +msgstr "" + +#: RtMidi.cpp:3175 +msgid "Error creating ALSA input port." +msgstr "" + +#: RtMidi.cpp:3254 RtMidi.cpp:3670 +msgid "" +"ALSA has been instructed to open a non-ALSA MIDI port. This doesn't work." +msgstr "" + +#: RtMidi.cpp:3338 RtMidi.cpp:3595 +msgid "Error creating ALSA virtual port." +msgstr "" + +#: RtMidi.cpp:3463 RtMidi.cpp:3619 +msgid "Error while allocating buffer memory." +msgstr "" + +#: RtMidi.cpp:3520 +msgid "No MIDI output sinks found." +msgstr "" + +#: RtMidi.cpp:3547 +msgid "Error creating ALSA output port." +msgstr "" + +#: RtMidi.cpp:3612 +msgid "ALSA error resizing MIDI event buffer." +msgstr "" + +#: RtMidi.cpp:3633 +msgid "Event parsing error." +msgstr "" + +#: RtMidi.cpp:3665 +msgid "Error allocating ALSA port subscription." +msgstr "" + +#: RtMidi.cpp:3815 +#, c-format +msgid "The port argument %d is invalid." +msgstr "" + +#: RtMidi.cpp:4137 +msgid "Error sending sysex to Midi device." +msgstr "" + +#: RtMidi.cpp:4200 +msgid "No MIDI input devices currently available." +msgstr "" + +#: RtMidi.cpp:4210 +msgid "Failed to initialize a critical section." +msgstr "" + +#: RtMidi.cpp:4234 +#, c-format +msgid "the 'portNumber' argument (%d) is invalid." +msgstr "" + +#: RtMidi.cpp:4246 +msgid "Error creating Windows MM MIDI input port." +msgstr "" + +#: RtMidi.cpp:4262 +msgid "Error initializing data for Windows MM MIDI input port." +msgstr "" + +#: RtMidi.cpp:4271 +msgid "Could not register the input buffer for Windows MM MIDI input port." +msgstr "" + +#: RtMidi.cpp:4280 +msgid "Error starting Windows MM MIDI input port." +msgstr "" + +#: RtMidi.cpp:4291 RtMidi.cpp:4567 +msgid "Virtual ports are not available Windows Multimedia MIDI API." +msgstr "" + +#: RtMidi.cpp:4298 RtMidi.cpp:4575 +msgid "" +"Windows Multimedia (WinMM) has been instructed to open a non-WinMM MIDI " +"port. This doesn't work." +msgstr "" + +#: RtMidi.cpp:4303 +msgid "" +"We are overwriting an existing connection. This is probably a programming " +"error." +msgstr "" + +#: RtMidi.cpp:4308 +msgid "Trying to open a non-input port as input MIDI port. This doesn't work." +msgstr "" + +#: RtMidi.cpp:4322 RtMidi.cpp:4599 +msgid "" +"Some change in the arrangement of MIDI input ports invalidated the port " +"descriptor." +msgstr "" + +#: RtMidi.cpp:4337 +msgid "The handle is invalid. Did you disconnect the device?" +msgstr "" + +#: RtMidi.cpp:4341 RtMidi.cpp:4618 +msgid "" +"The system has no driver for our handle :-(. Did you disconnect the device?" +msgstr "" + +#: RtMidi.cpp:4345 RtMidi.cpp:4622 +msgid "Out of memory." +msgstr "" + +#: RtMidi.cpp:4391 +msgid "Error closing Windows MM MIDI input port." +msgstr "" + +#: RtMidi.cpp:4469 +msgid "No MIDI output devices currently available." +msgstr "" + +#: RtMidi.cpp:4528 +msgid "No MIDI output destinations found!" +msgstr "" + +#: RtMidi.cpp:4546 +msgid "Error creating Windows MM MIDI output port." +msgstr "" + +#: RtMidi.cpp:4585 +msgid "The port descriptor cannot be used to open an output port." +msgstr "" + +#: RtMidi.cpp:4614 +msgid "The internal handle is invalid. Did you disconnect the device?" +msgstr "" + +#: RtMidi.cpp:4650 +msgid "Message argument is empty." +msgstr "" + +#: RtMidi.cpp:4662 +msgid "Error while allocating sysex message memory." +msgstr "" + +#: RtMidi.cpp:4678 +msgid "Error preparing sysex header." +msgstr "" + +#: RtMidi.cpp:4687 +msgid "Error sending sysex message." +msgstr "" + +#: RtMidi.cpp:4700 +msgid "Message size is greater than 3 bytes (and not sysex)." +msgstr "" + +#: RtMidi.cpp:4716 +msgid "Error sending MIDI message." +msgstr "" + +#: RtMidi.cpp:4947 +msgid "Could not connect to JACK server. Is it runnig?" +msgstr "" + +#: RtMidi.cpp:5158 +msgid "Error opening JACK port subscription." +msgstr "" + +#: RtMidi.cpp:5342 RtMidi.cpp:5570 +msgid "JACK server not running?" +msgstr "" + +#: RtMidi.cpp:5386 RtMidi.cpp:5603 +msgid "Error creating JACK port." +msgstr "" + +#: RtMidi.cpp:5406 RtMidi.cpp:5623 +msgid "Error creating JACK virtual port." +msgstr "" + +#: RtMidi.cpp:5430 RtMidi.cpp:5647 +msgid "" +"JACK has been instructed to open a non-JACK MIDI port. This doesn't work." +msgstr "" + +#: RtMidi.cpp:5513 RtMidi.cpp:5725 +msgid "No ports available." +msgstr "" diff --git a/rtmidi-config.in b/rtmidi-config.in index 34d2a25d..0cff7f37 100644 --- a/rtmidi-config.in +++ b/rtmidi-config.in @@ -5,15 +5,20 @@ if (test "x$#" != "x1") ; then fi LIBRARY="@LIBS@" -CXXFLAGS="@CXXFLAGS@" -CPPFLAGS="@CPPFLAGS@" +CXXFLAGS="@CXXFLAGS@ @RTMIDI_API@ @RTMIDI_API_CFLAGS@" +CPPFLAGS="@CPPFLAGS@ @RTMIDI_API@ @RTMIDI_API_CFLAGS@" + +prefix=@prefix@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include/rtmidi@rtmidi_suffix@ if (test "x$1" = "x--libs") ; then - echo "$LIBRARY -lrtmidi" + echo "$LIBRARY -L${libdir} -lrtmidi@rtmidi_suffix@" elif (test "x$1" = "x--cxxflags") ; then - echo "$CXXFLAGS" + echo "$CXXFLAGS -I${includedir}" elif (test "x$1" = "x--cppflags") ; then - echo "$CPPFLAGS" + echo "$CPPFLAGS -I${includedir}" else echo "Unknown option: $1" fi diff --git a/rtmidi-m4/ac_lib_winmm.m4 b/rtmidi-m4/ac_lib_winmm.m4 new file mode 100644 index 00000000..8d7089a1 --- /dev/null +++ b/rtmidi-m4/ac_lib_winmm.m4 @@ -0,0 +1,43 @@ +# AC_LIB_WINMM(FUNCTION, +# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND], +# [OTHER-LIBRARIES]) +# ------------------------------------------------------ +# +# Use a cache variable name containing both the library and function name, +# because the test really is for library $1 defining function $2, not +# just for library $1. Separate tests with the same $1 and different $2s +# may have different results. +# +# Note that using directly AS_VAR_PUSHDEF([ac_Lib], [ac_cv_lib_$1_$2]) +# is asking for troubles, since AC_CHECK_LIB($lib, fun) would give +# ac_cv_lib_$lib_fun, which is definitely not what was meant. Hence +# the AS_LITERAL_IF indirection. +# +# FIXME: This macro is extremely suspicious. It DEFINEs unconditionally, +# whatever the FUNCTION, in addition to not being a *S macro. Note +# that the cache does depend upon the function we are looking for. +# +# It is on purpose we used `ac_check_lib_save_LIBS' and not just +# `ac_save_LIBS': there are many macros which don't want to see `LIBS' +# changed but still want to use AC_CHECK_LIB, so they save `LIBS'. +# And ``ac_save_LIBS' is too tempting a name, so let's leave them some +# freedom. +AC_DEFUN([AC_LIB_WINMM],[ + m4_ifval([$2], , [AH_CHECK_LIB([winmm])])dnl + AC_CACHE_CHECK([for $1 in -lwinmm], [ac_cv_lib_winmm_$1],[ + ac_check_lib_save_LIBS="$LIBS" + LIBS="-lwinmm $4 $LIBS" + AC_LINK_IFELSE([AC_LANG_PROGRAM([ +#include "windows.h" +#include "mmsystem.h" + ],[[void * x= &$1;]])], + [ac_cv_lib_winmm_$1=yes], + [ac_cv_lib_winmm_$1=no]) + LIBS=$ac_check_lib_save_LIBS]) + AS_IF([test "${ac_cv_lib_winmm_$1}" = yes], + [m4_default([$2], + [AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_LIBWINMM)) + LIBS="-lwinmm $LIBS" + ])],[$3] + )dnl +])# AC_CHECK_LIB diff --git a/rtmidi-m4/ax_check_fallthrough_syntax.m4 b/rtmidi-m4/ax_check_fallthrough_syntax.m4 new file mode 100644 index 00000000..583ad827 --- /dev/null +++ b/rtmidi-m4/ax_check_fallthrough_syntax.m4 @@ -0,0 +1,90 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_fallthrough_syntax.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_FALLTHROUGH_SYNTAX([ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the current language's compiler +# or gives an error. (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 1 +AC_DEFUN([_AX_CHECK_FALLTHROUGH_SYNTAX_COMPILE], +[ + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([ +int j; +void test(int i) { + switch (i) { + case 1: j = 3; + $1 ; + case 2: j++; + } +} + ],[])],$2,$3) +]) + +AC_DEFUN([AX_CHECK_FALLTHROUGH_SYNTAX], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]_fallthrough_syntax_$1])dnl +AC_CACHE_CHECK([syntax of _AC_LANG fallthrough attribute ], CACHEVAR, [ + ax_check_save_flags_fallthrough="$[]_AC_LANG_PREFIX[]FLAGS" + _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $1" + AS_UNSET([ax_cv_check_[]_AC_LANG_ABBREV[]flags___Werror]) + AX_CHECK_COMPILE_FLAG([-Werror],[_AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS -Werror"]) + AS_VAR_SET(CACHEVAR,[]) + for flag in "[[[fallthrough]]]" "[[[gnu::fallthrough]]]" "__attribute__((fallthrough))" + do + _AX_CHECK_FALLTHROUGH_SYNTAX_COMPILE([$flag],[ + AS_VAR_SET(CACHEVAR,[$flag]) + break; + ],[:]) + done + _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags_fallthrough + ]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_FALLTHROUGH_SYNTAX diff --git a/rtmidi-m4/rtmidi_lib_alsa.m4 b/rtmidi-m4/rtmidi_lib_alsa.m4 new file mode 100644 index 00000000..5d4ee1eb --- /dev/null +++ b/rtmidi-m4/rtmidi_lib_alsa.m4 @@ -0,0 +1,46 @@ +# AC_LIB_ALSA(FUNCTION, +# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND], +# [OTHER-LIBRARIES]) +# ------------------------------------------------------ +# +# Use a cache variable name containing both the library and function name, +# because the test really is for library $1 defining function $2, not +# just for library $1. Separate tests with the same $1 and different $2s +# may have different results. +# +# Note that using directly AS_VAR_PUSHDEF([ac_Lib], [ac_cv_lib_$1_$2]) +# is asking for troubles, since AC_CHECK_LIB($lib, fun) would give +# ac_cv_lib_$lib_fun, which is definitely not what was meant. Hence +# the AS_LITERAL_IF indirection. +# +# FIXME: This macro is extremely suspicious. It DEFINEs unconditionally, +# whatever the FUNCTION, in addition to not being a *S macro. Note +# that the cache does depend upon the function we are looking for. +# +# It is on purpose we used `ac_check_lib_save_LIBS' and not just +# `ac_save_LIBS': there are many macros which don't want to see `LIBS' +# changed but still want to use AC_CHECK_LIB, so they save `LIBS'. +# And ``ac_save_LIBS' is too tempting a name, so let's leave them some +# freedom. +AC_DEFUN([RTMIDI_LIB_ALSA],[ + AC_LANG_PUSH(C++) + rtmidi_have_alsa=no + AM_PATH_ALSA(1.0.27,[rtmidi_have_alsa=yes],[rtmidi_have_alsa=no]) + AS_IF(test "x$rtmidi_have_alsa" = "xyes",,[ + AC_CHECK_LIB(pthread, pthread_create,[ + AC_MSG_WARN([RtMidi requires the pthread library! Disabling Alsa!]) + rtmidi_have_alsa=no + ]) + ]) + AS_IF(test "x$rtmidi_have_alsa" = "xyes",[ + RTMIDI_API="$RTMIDI_API -D__LINUX_ALSA__" + RTMIDI_LIB_CFLAGS="$RTMIDI_LIB_CFLAGS $ALSA_CFLAGS" + RTMIDI_LIBS="$RTMIDI_LIBS $ALSA_LIBS" + rtmidi_pkconfig_requirements="$rtmidi_pkconfig_requirements alsa" + RTMIDI_HAVE_VIRTUAL_DEVICES=yes + $1 + ],[ + m4_default([$2],[AC_MSG_WARN(ALSA support requires the asound and pthread libraries!)]) + ]) + AC_LANG_POP(C++) +])dnl RTMIDI_LIB_ALSA diff --git a/rtmidi-m4/rtmidi_lib_coremidi.m4 b/rtmidi-m4/rtmidi_lib_coremidi.m4 new file mode 100644 index 00000000..d4eb29e5 --- /dev/null +++ b/rtmidi-m4/rtmidi_lib_coremidi.m4 @@ -0,0 +1,46 @@ +# AC_LIB_COREMIDI(FUNCTION, +# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND], +# [OTHER-LIBRARIES]) +# ------------------------------------------------------ +# +# Use a cache variable name containing both the library and function name, +# because the test really is for library $1 defining function $2, not +# just for library $1. Separate tests with the same $1 and different $2s +# may have different results. +# +# Note that using directly AS_VAR_PUSHDEF([ac_Lib], [ac_cv_lib_$1_$2]) +# is asking for troubles, since AC_CHECK_LIB($lib, fun) would give +# ac_cv_lib_$lib_fun, which is definitely not what was meant. Hence +# the AS_LITERAL_IF indirection. +# +# FIXME: This macro is extremely suspicious. It DEFINEs unconditionally, +# whatever the FUNCTION, in addition to not being a *S macro. Note +# that the cache does depend upon the function we are looking for. +# +# It is on purpose we used `ac_check_lib_save_LIBS' and not just +# `ac_save_LIBS': there are many macros which don't want to see `LIBS' +# changed but still want to use AC_CHECK_LIB, so they save `LIBS'. +# And ``ac_save_LIBS' is too tempting a name, so let's leave them some +# freedom. +AC_DEFUN([RTMIDI_LIB_COREMIDI],[ + AC_LANG_PUSH(C++) + ac_cv_rtmidi_lib_coremidi="$LIBS" + LIBS="$LIBS -framework CoreMIDI -framework CoreFoundation -framework CoreAudio" + rtmidi_have_coremidi=no + AC_CHECK_HEADER(CoreMIDI/CoreMIDI.h, [rtmidi_have_coremidi=yes]) + AS_IF(test "x$rtmidi_have_coremidi" = "xyes",[ + AC_CHECK_LIB(pthread, pthread_create, ,[ + rtmidi_have_coremidi=no + AC_MSG_WARN([RtMidi requires the pthread library!]) + ]) + ]) + AS_IF(test "x$rtmidi_have_coremidi" = "xyes",[ + RTMIDI_API="$RTMIDI_API -D__MACOSX_COREMIDI__" + RTMIDI_LIBS="$RTMIDI_LIBS -framework CoreMIDI -framework CoreFoundation -framework CoreAudio" + $1 + ], [ + m4_default([$2],[AC_MSG_ERROR(CoreMIDI header files not found!)] ) + ]) + LIBS="$ac_cv_rtmidi_lib_coremidi" + AC_LANG_POP(C++) +])dnl RTMIDI_LIB_COREMIDI diff --git a/rtmidi-m4/rtmidi_lib_jack.m4 b/rtmidi-m4/rtmidi_lib_jack.m4 new file mode 100644 index 00000000..2523f2bc --- /dev/null +++ b/rtmidi-m4/rtmidi_lib_jack.m4 @@ -0,0 +1,69 @@ +# AC_LIB_JACK(FUNCTION, +# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND], +# [OTHER-LIBRARIES]) +# ------------------------------------------------------ +# +# Use a cache variable name containing both the library and function name, +# because the test really is for library $1 defining function $2, not +# just for library $1. Separate tests with the same $1 and different $2s +# may have different results. +# +# Note that using directly AS_VAR_PUSHDEF([ac_Lib], [ac_cv_lib_$1_$2]) +# is asking for troubles, since AC_CHECK_LIB($lib, fun) would give +# ac_cv_lib_$lib_fun, which is definitely not what was meant. Hence +# the AS_LITERAL_IF indirection. +# +# FIXME: This macro is extremely suspicious. It DEFINEs unconditionally, +# whatever the FUNCTION, in addition to not being a *S macro. Note +# that the cache does depend upon the function we are looking for. +# +# It is on purpose we used `ac_check_lib_save_LIBS' and not just +# `ac_save_LIBS': there are many macros which don't want to see `LIBS' +# changed but still want to use AC_CHECK_LIB, so they save `LIBS'. +# And ``ac_save_LIBS' is too tempting a name, so let's leave them some +# freedom. +AC_DEFUN([RTMIDI_LIB_JACK],[ + AC_REQUIRE([PKG_PROG_PKG_CONFIG]) + AC_LANG_PUSH(C++) + rtmidi_have_jack=no + PKG_CHECK_MODULES(JACK,[jack],[rtmidi_have_jack=yes],[rtmidi_have_jack=no]) + AS_IF(test "x$rtmidi_have_jack" = "xyes",[ + rtmidi_save_libs="$LIBS" + LIBS="$LIBS $JACK_LIBS" + AC_TRY_LINK([ +#include + ],[ +return jack_client_close(NULL); + ],,[ + rtmidi_have_jack=no + AC_MSG_WARN([JACK is not usable. Disabling JACK.]) + ]) + LIBS="$rtmidi_save_libs" + ]) + AS_IF(test "x$rtmidi_have_jack" = "xyes",[ + AC_CHECK_LIB(pthread, pthread_create, ,[ + rtmidi_have_jack=no + AC_MSG_WARN([RtMidi requires the pthread library disabling JACK!]) + ]) + ]) + AS_IF(test "x$rtmidi_have_jack" = "xyes",[ + AC_CHECK_LIB(jack, jack_port_rename, AC_DEFINE(JACK_HAS_PORT_RENAME), ) + rtmidi_save_libs="$LIBS" + LIBS="$LIBS $JACK_LIBS" + AC_TRY_LINK([ +#include + ],[ +return jack_port_uuid(NULL); + ],[RTMIDI_API="$RTMIDI_API -D__UNIX_JACK_HAS_UUID__"]) + LIBS="$rtmidi_save_libs" + RTMIDI_API="$RTMIDI_API -D__UNIX_JACK__" + RTMIDI_LIB_CFLAGS="$RTMIDI_LIB_CFLAGS $JACK_CFLAGS" + RTMIDI_LIBS="$RTMIDI_LIBS $JACK_LIBS" + rtmidi_pkconfig_requirements="$rtmidi_pkconfig_requirements jack" + RTMIDI_HAVE_VIRTUAL_DEVICES=yes + $1 + ],[ + m4_default([$2],[AC_MSG_ERROR(JACK support requires the jack library!)]) + ]) + AC_LANG_POP(C++) +])dnl RTMIDI_LIB_JACK diff --git a/rtmidi-m4/rtmidi_lib_winks.m4 b/rtmidi-m4/rtmidi_lib_winks.m4 new file mode 100644 index 00000000..d440476c --- /dev/null +++ b/rtmidi-m4/rtmidi_lib_winks.m4 @@ -0,0 +1,47 @@ +# AC_LIB_WINKS(FUNCTION, +# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND], +# [OTHER-LIBRARIES]) +# ------------------------------------------------------ +# +# Use a cache variable name containing both the library and function name, +# because the test really is for library $1 defining function $2, not +# just for library $1. Separate tests with the same $1 and different $2s +# may have different results. +# +# Note that using directly AS_VAR_PUSHDEF([ac_Lib], [ac_cv_lib_$1_$2]) +# is asking for troubles, since AC_CHECK_LIB($lib, fun) would give +# ac_cv_lib_$lib_fun, which is definitely not what was meant. Hence +# the AS_LITERAL_IF indirection. +# +# FIXME: This macro is extremely suspicious. It DEFINEs unconditionally, +# whatever the FUNCTION, in addition to not being a *S macro. Note +# that the cache does depend upon the function we are looking for. +# +# It is on purpose we used `ac_check_lib_save_LIBS' and not just +# `ac_save_LIBS': there are many macros which don't want to see `LIBS' +# changed but still want to use AC_CHECK_LIB, so they save `LIBS'. +# And ``ac_save_LIBS' is too tempting a name, so let's leave them some +# freedom. +AC_DEFUN([RTMIDI_LIB_WINKS],[ + AC_LANG_PUSH(C++) + AC_CACHE_CHECK([for midiOutGetNumDevs in -lksuser], [ac_cv_lib_winks],[ + ac_check_lib_save_LIBS="$LIBS" + LIBS="-lsetupapi -lksuser $LIBS" + AC_LINK_IFELSE([AC_LANG_PROGRAM([ +#include "windows.h" +#include "mmsystem.h" + ],[[void * x= &midiOutGetNumDevs;]])], + [ac_cv_lib_winks=yes], + [ac_cv_lib_winks=no]) + LIBS=$ac_check_lib_save_LIBS]) + AS_IF([test "${ac_cv_lib_winks}" = yes], + [ + AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_LIBWINKS),1,[Define this to 1 if windows kernel streaming libraries are present]) + RTMIDI_LIBS="$RTMIDI_LIBS -lsetupapi -lksuser" + RTMIDI_API="$RTMIDI_API -D__WINDOWS_KS__" + $1 + ],[ + m4_default([$2],[AC_MSG_ERROR(Windows mulitmedia library not found!)] ) + ]) + AC_LANG_POP(C++) +])dnl RTMIDI_LIB_WINKS diff --git a/rtmidi-m4/rtmidi_lib_winmm.m4 b/rtmidi-m4/rtmidi_lib_winmm.m4 new file mode 100644 index 00000000..9be4513d --- /dev/null +++ b/rtmidi-m4/rtmidi_lib_winmm.m4 @@ -0,0 +1,47 @@ +# AC_LIB_WINMM(FUNCTION, +# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND], +# [OTHER-LIBRARIES]) +# ------------------------------------------------------ +# +# Use a cache variable name containing both the library and function name, +# because the test really is for library $1 defining function $2, not +# just for library $1. Separate tests with the same $1 and different $2s +# may have different results. +# +# Note that using directly AS_VAR_PUSHDEF([ac_Lib], [ac_cv_lib_$1_$2]) +# is asking for troubles, since AC_CHECK_LIB($lib, fun) would give +# ac_cv_lib_$lib_fun, which is definitely not what was meant. Hence +# the AS_LITERAL_IF indirection. +# +# FIXME: This macro is extremely suspicious. It DEFINEs unconditionally, +# whatever the FUNCTION, in addition to not being a *S macro. Note +# that the cache does depend upon the function we are looking for. +# +# It is on purpose we used `ac_check_lib_save_LIBS' and not just +# `ac_save_LIBS': there are many macros which don't want to see `LIBS' +# changed but still want to use AC_CHECK_LIB, so they save `LIBS'. +# And ``ac_save_LIBS' is too tempting a name, so let's leave them some +# freedom. +AC_DEFUN([RTMIDI_LIB_WINMM],[ + AC_LANG_PUSH(C++) + AC_CACHE_CHECK([for midiOutGetNumDevs in -lwinmm], [ac_cv_lib_winmm],[ + ac_check_lib_save_LIBS="$LIBS" + LIBS="-lwinmm $LIBS" + AC_LINK_IFELSE([AC_LANG_PROGRAM([ +#include "windows.h" +#include "mmsystem.h" + ],[[int x= midiOutGetNumDevs();]])], + [ac_cv_lib_winmm=yes], + [ac_cv_lib_winmm=no]) + LIBS=$ac_check_lib_save_LIBS]) + AS_IF([test "${ac_cv_lib_winmm}" = yes], + [ + AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_LIBWINMM)) + RTMIDI_LIBS="$RTMIDI_LIBS -lwinmm" + RTMIDI_API="$RTMIDI_API -D__WINDOWS_MM__" + $1 + ],[ + m4_default([$2],[AC_MSG_ERROR(Windows mulitmedia library not found!)] ) + ]) + AC_LANG_POP(C++) +])dnl RTMIDI_LIB_WINMM diff --git a/rtmidi.pc.in b/rtmidi.pc.in index 9af1a049..2d017c89 100644 --- a/rtmidi.pc.in +++ b/rtmidi.pc.in @@ -1,12 +1,12 @@ prefix=@prefix@ exec_prefix=${prefix} libdir=${exec_prefix}/lib -includedir=${prefix}/include/rtmidi +includedir=${prefix}/include/rtmidi@rtmidi_suffix@ -Name: librtmidi -Description: RtMidi - a set of C++ classes that provide a common API for realtime MIDI input/output +Name: librtmidi@rtmidi_suffix@ +Description: RtMidi@rtmidi_suffix@ - a set of C++ classes that provide a common API for realtime MIDI input/output Version: @PACKAGE_VERSION@ -Requires: @req@ -Libs: -L${libdir} -lrtmidi +Requires: @rtmidi_pkconfig_requirements@ +Libs: -L${libdir} -lrtmidi@rtmidi_suffix@ Libs.private: -lpthread -Cflags: -pthread -I${includedir} @api@ +Cflags: -pthread -I${includedir} @RTMIDI_API@ diff --git a/rtmidi_c.cpp b/rtmidi_c.cpp index 08e13e5b..d4a0b0fa 100644 --- a/rtmidi_c.cpp +++ b/rtmidi_c.cpp @@ -3,6 +3,8 @@ #include "rtmidi_c.h" #include "RtMidi.h" +#define RTMIDI_CLASSNAME "C interface" + /* Compile-time assertions that will break if the enums are changed in * the future without synchronizing them properly. If you get (g++) * "error: ‘StaticAssert::StaticAssert() [with bool b = false]’ is @@ -45,9 +47,9 @@ class CallbackProxyUserData /* RtMidi API */ int rtmidi_get_compiled_api (enum RtMidiApi *apis, unsigned int apis_size) { - std::vector v; + std::vector v; try { - RtMidi::getCompiledApi(v); + rtmidi::Midi::getCompiledApi(v); } catch (...) { return -1; } @@ -61,17 +63,17 @@ int rtmidi_get_compiled_api (enum RtMidiApi *apis, unsigned int apis_size) return v.size(); } -void rtmidi_error (MidiApi *api, enum RtMidiErrorType type, const char* errorString) +void rtmidi_error (rtmidi::MidiApi *api, enum RtMidiErrorType type, const char* errorString) { - std::string msg = errorString; - api->error ((RtMidiError::Type) type, msg); + // std::string msg = errorString; + api->error (RTMIDI_ERROR(errorString,(rtmidi::Error::Type)type)); /* (RtMidiError::Type) type, msg); */ } void rtmidi_open_port (RtMidiPtr device, unsigned int portNumber, const char *portName) { std::string name = portName; try { - ((RtMidi*) device->ptr)->openPort (portNumber, name); + ((rtmidi::Midi*)device->ptr)->openPort (portNumber, name); } catch (const RtMidiError & err) { device->ok = false; @@ -83,7 +85,7 @@ void rtmidi_open_virtual_port (RtMidiPtr device, const char *portName) { std::string name = portName; try { - ((RtMidi*) device->ptr)->openVirtualPort (name); + ((rtmidi::Midi*)device->ptr)->openVirtualPort (name); } catch (const RtMidiError & err) { device->ok = false; @@ -95,7 +97,7 @@ void rtmidi_open_virtual_port (RtMidiPtr device, const char *portName) void rtmidi_close_port (RtMidiPtr device) { try { - ((RtMidi*) device->ptr)->closePort (); + ((rtmidi::Midi*)device->ptr)->closePort (); } catch (const RtMidiError & err) { device->ok = false; @@ -106,7 +108,7 @@ void rtmidi_close_port (RtMidiPtr device) unsigned int rtmidi_get_port_count (RtMidiPtr device) { try { - return ((RtMidi*) device->ptr)->getPortCount (); + return ((rtmidi::Midi *)device->ptr)->getPortCount (); } catch (const RtMidiError & err) { device->ok = false; @@ -118,7 +120,7 @@ unsigned int rtmidi_get_port_count (RtMidiPtr device) const char* rtmidi_get_port_name (RtMidiPtr device, unsigned int portNumber) { try { - std::string name = ((RtMidi*) device->ptr)->getPortName (portNumber); + std::string name = ((rtmidi::Midi*)device->ptr)->getPortName (portNumber); return strdup (name.c_str ()); } catch (const RtMidiError & err) { @@ -128,13 +130,13 @@ const char* rtmidi_get_port_name (RtMidiPtr device, unsigned int portNumber) } } -/* RtMidiIn API */ +/* MidiIn API */ RtMidiInPtr rtmidi_in_create_default () { - RtMidiWrapper* wrp = new RtMidiWrapper; + RtMidiInWrapper* wrp = new RtMidiInWrapper; try { - RtMidiIn* rIn = new RtMidiIn (); + rtmidi::MidiIn* rIn = new rtmidi::MidiIn (); wrp->ptr = (void*) rIn; wrp->data = 0; @@ -154,10 +156,10 @@ RtMidiInPtr rtmidi_in_create_default () RtMidiInPtr rtmidi_in_create (enum RtMidiApi api, const char *clientName, unsigned int queueSizeLimit) { std::string name = clientName; - RtMidiWrapper* wrp = new RtMidiWrapper; + RtMidiInWrapper* wrp = new RtMidiInWrapper; try { - RtMidiIn* rIn = new RtMidiIn ((RtMidi::Api) api, name, queueSizeLimit); + rtmidi::MidiIn* rIn = new rtmidi::MidiIn ((rtmidi::ApiType) api, name, queueSizeLimit); wrp->ptr = (void*) rIn; wrp->data = 0; @@ -178,14 +180,14 @@ void rtmidi_in_free (RtMidiInPtr device) { if (device->data) delete (CallbackProxyUserData*) device->data; - delete (RtMidiIn*) device->ptr; + delete (rtmidi::MidiIn*) device->ptr; delete device; } -enum RtMidiApi rtmidi_in_get_current_api (RtMidiPtr device) +enum RtMidiApi rtmidi_in_get_current_api (RtMidiInPtr device) { try { - return (RtMidiApi) ((RtMidiIn*) device->ptr)->getCurrentApi (); + return (RtMidiApi) ((rtmidi::MidiIn*)device->ptr)->getCurrentApi (); } catch (const RtMidiError & err) { device->ok = false; @@ -206,7 +208,7 @@ void rtmidi_in_set_callback (RtMidiInPtr device, RtMidiCCallback callback, void { device->data = (void*) new CallbackProxyUserData (callback, userData); try { - ((RtMidiIn*) device->ptr)->setCallback (callback_proxy, device->data); + ((rtmidi::MidiIn*)device->ptr)->setCallback (callback_proxy, device->data); } catch (const RtMidiError & err) { device->ok = false; device->msg = err.what (); @@ -218,7 +220,7 @@ void rtmidi_in_set_callback (RtMidiInPtr device, RtMidiCCallback callback, void void rtmidi_in_cancel_callback (RtMidiInPtr device) { try { - ((RtMidiIn*) device->ptr)->cancelCallback (); + ((rtmidi::MidiIn*)device->ptr)->cancelCallback (); delete (CallbackProxyUserData*) device->data; device->data = 0; } catch (const RtMidiError & err) { @@ -229,7 +231,7 @@ void rtmidi_in_cancel_callback (RtMidiInPtr device) void rtmidi_in_ignore_types (RtMidiInPtr device, bool midiSysex, bool midiTime, bool midiSense) { - ((RtMidiIn*) device->ptr)->ignoreTypes (midiSysex, midiTime, midiSense); + ((rtmidi::MidiIn*)device->ptr)->ignoreTypes (midiSysex, midiTime, midiSense); } double rtmidi_in_get_message (RtMidiInPtr device, @@ -239,7 +241,7 @@ double rtmidi_in_get_message (RtMidiInPtr device, try { // FIXME: use allocator to achieve efficient buffering std::vector v; - double ret = ((RtMidiIn*) device->ptr)->getMessage (&v); + double ret = ((rtmidi::MidiIn*)device->ptr)->getMessage (v); if (v.size () > 0 && v.size() <= *size) { memcpy (message, v.data (), (int) v.size ()); @@ -263,10 +265,10 @@ double rtmidi_in_get_message (RtMidiInPtr device, /* RtMidiOut API */ RtMidiOutPtr rtmidi_out_create_default () { - RtMidiWrapper* wrp = new RtMidiWrapper; + RtMidiOutWrapper* wrp = new RtMidiOutWrapper; try { - RtMidiOut* rOut = new RtMidiOut (); + rtmidi::MidiOut* rOut = new rtmidi::MidiOut (); wrp->ptr = (void*) rOut; wrp->data = 0; @@ -285,11 +287,11 @@ RtMidiOutPtr rtmidi_out_create_default () RtMidiOutPtr rtmidi_out_create (enum RtMidiApi api, const char *clientName) { - RtMidiWrapper* wrp = new RtMidiWrapper; + RtMidiOutWrapper* wrp = new RtMidiOutWrapper; std::string name = clientName; try { - RtMidiOut* rOut = new RtMidiOut ((RtMidi::Api) api, name); + rtmidi::MidiOut* rOut = new rtmidi::MidiOut ((rtmidi::ApiType) api, name); wrp->ptr = (void*) rOut; wrp->data = 0; @@ -309,14 +311,14 @@ RtMidiOutPtr rtmidi_out_create (enum RtMidiApi api, const char *clientName) void rtmidi_out_free (RtMidiOutPtr device) { - delete (RtMidiOut*) device->ptr; + delete ((rtmidi::MidiOut*)device->ptr); delete device; } enum RtMidiApi rtmidi_out_get_current_api (RtMidiPtr device) { try { - return (RtMidiApi) ((RtMidiOut*) device->ptr)->getCurrentApi (); + return (RtMidiApi) ((rtmidi::MidiOut*)device->ptr)->getCurrentApi (); } catch (const RtMidiError & err) { device->ok = false; diff --git a/rtmidi_c.h b/rtmidi_c.h index eeafb0be..b996f1f6 100644 --- a/rtmidi_c.h +++ b/rtmidi_c.h @@ -26,15 +26,27 @@ struct RtMidiWrapper { //! If an error occured (ok != true), set to an error message. const char* msg; }; +struct RtMidiInWrapper { + void* ptr; + void* data; + bool ok; + const char* msg; +}; +struct RtMidiOutWrapper { + void* ptr; + void* data; + bool ok; + const char* msg; +}; //! Typedef for a generic RtMidi pointer. typedef struct RtMidiWrapper* RtMidiPtr; //! Typedef for a generic RtMidiIn pointer. -typedef struct RtMidiWrapper* RtMidiInPtr; +typedef struct RtMidiInWrapper* RtMidiInPtr; //! Typedef for a generic RtMidiOut pointer. -typedef struct RtMidiWrapper* RtMidiOutPtr; +typedef struct RtMidiOutWrapper* RtMidiOutPtr; enum RtMidiApi { @@ -43,7 +55,9 @@ enum RtMidiApi { RT_MIDI_API_LINUX_ALSA, /*!< The Advanced Linux Sound Architecture API. */ RT_MIDI_API_UNIX_JACK, /*!< The Jack Low-Latency MIDI Server API. */ RT_MIDI_API_WINDOWS_MM, /*!< The Microsoft Multimedia MIDI API. */ - RT_MIDI_API_RTMIDI_DUMMY /*!< A compilable but non-functional API. */ + RT_MIDI_API_WINDOWS_KS, /*!< The Microsoft Kernel Streaming MIDI API. */ + RT_MIDI_API_RTMIDI_DUMMY, /*!< A compilable but non-functional API. */ + RT_MIDI_API_ALL_API /*!< Use all available APIs for port selection. */ }; enum RtMidiErrorType { diff --git a/tests/Makefile.am b/tests/Makefile.am index d120d982..07851ea9 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,28 +1,185 @@ +### RtMidi tests Makefile - for various flavors of unix +#RTMIDITESTCXXFLAGS += -I$(top_srcdir)/%D% +#RTMIDITESTLDFLAGS += -L$(top_builddir)/%D% -lrtmidi@rtmidi_suffix@ @LIBS@ -noinst_PROGRAMS = midiprobe midiout qmidiin cmidiin sysextest midiclock_in midiclock_out +check_PROGRAMS += \ + %D%/midiprobe \ + %D%/midiout \ + %D%/qmidiin \ + %D%/cmidiin \ + %D%/sysextest \ + %D%/midiprobe2 \ + %D%/midiprobe-all \ + %D%/cmidiin2 \ + %D%/qmidiin2 \ + %D%/midiout2 \ + %D%/loopback \ + %D%/errors \ + %D%/midiclock_in \ + %D%/midiclock_out -AM_CXXFLAGS = -Wall -I$(top_srcdir) +TESTS += \ + %D%/midiprobe \ + %D%/midiprobe2 \ + %D%/midiprobe-all \ + %D%/errors -midiprobe_SOURCES = midiprobe.cpp -midiprobe_LDADD = $(top_builddir)/librtmidi.la +CLEANFILES += \ + %D%/*.class -midiout_SOURCES = midiout.cpp -midiout_LDADD = $(top_builddir)/librtmidi.la +EXTRA_DIST += \ + %D%/cmidiin.dsp \ + %D%/midiout.dsp \ + %D%/midiprobe.dsp \ + %D%/qmidiin.dsp \ + %D%/sysextest.dsp \ + %D%/RtMidi.dsw -qmidiin_SOURCES = qmidiin.cpp -qmidiin_LDADD = $(top_builddir)/librtmidi.la -cmidiin_SOURCES = cmidiin.cpp -cmidiin_LDADD = $(top_builddir)/librtmidi.la +if RTMIDI_HAVE_VIRTUAL_DEVICES +TESTS += %D%/loopback +endif -sysextest_SOURCES = sysextest.cpp -sysextest_LDADD = $(top_builddir)/librtmidi.la -midiclock_in_SOURCES = midiclock.cpp -midiclock_in_LDADD = $(top_builddir)/librtmidi.la +%C%_midiprobe_SOURCES = %D%/midiprobe.cpp +%C%_midiout_SOURCES = %D%/midiout.cpp +%C%_qmidiin_SOURCES = %D%/qmidiin.cpp +%C%_cmidiin_SOURCES = %D%/cmidiin.cpp +%C%_sysextest_SOURCES = %D%/sysextest.cpp +%C%_midiprobe2_SOURCES = %D%/midiprobe2.cpp +%C%_midiprobe_all_SOURCES = %D%/midiprobe-all.cpp +%C%_cmidiin2_SOURCES = %D%/cmidiin2.cpp +%C%_qmidiin2_SOURCES = %D%/qmidiin2.cpp +%C%_midiout2_SOURCES = %D%/midiout2.cpp +%C%_loopback_SOURCES = %D%/loopback.cpp +%C%_errors_SOURCES = %D%/errors.cpp +%C%_midiclock_in_SOURCES = %D%/midiclock.cpp +%C%_midiclock_out_SOURCES = %D%/midiclock.cpp -midiclock_out_SOURCES = midiclock.cpp -midiclock_out_LDADD = $(top_builddir)/librtmidi.la +# When a nonstandard gettext library or wrapper is used, +# we need extra flags. +%C%_midiprobe_CXXFLAGS = $(AM_CXXFLAGS) $(RTMIDITESTCXXFLAGS) -DRTMIDI_NO_WARN_DEPRECATED +%C%_midiout_CXXFLAGS = $(AM_CXXFLAGS) $(RTMIDITESTCXXFLAGS) -DRTMIDI_NO_WARN_DEPRECATED +%C%_qmidiin_CXXFLAGS = $(AM_CXXFLAGS) $(RTMIDITESTCXXFLAGS) -DRTMIDI_NO_WARN_DEPRECATED +%C%_cmidiin_CXXFLAGS = $(AM_CXXFLAGS) $(RTMIDITESTCXXFLAGS) -DRTMIDI_NO_WARN_DEPRECATED +%C%_sysextest_CXXFLAGS = $(AM_CXXFLAGS) $(RTMIDITESTCXXFLAGS) -DRTMIDI_NO_WARN_DEPRECATED +%C%_midiprobe2_CXXFLAGS = $(AM_CXXFLAGS) $(RTMIDITESTCXXFLAGS) -DRTMIDI_NO_WARN_DEPRECATED +%C%_midiprobe_all_CXXFLAGS = $(AM_CXXFLAGS) $(RTMIDITESTCXXFLAGS) -DRTMIDI_NO_WARN_DEPRECATED +%C%_cmidiin2_CXXFLAGS = $(AM_CXXFLAGS) $(RTMIDITESTCXXFLAGS) -DRTMIDI_NO_WARN_DEPRECATED +%C%_qmidiin2_CXXFLAGS = $(AM_CXXFLAGS) $(RTMIDITESTCXXFLAGS) -DRTMIDI_NO_WARN_DEPRECATED +%C%_midiout2_CXXFLAGS = $(AM_CXXFLAGS) $(RTMIDITESTCXXFLAGS) -DRTMIDI_NO_WARN_DEPRECATED +%C%_loopback_CXXFLAGS = $(AM_CXXFLAGS) $(RTMIDITESTCXXFLAGS) -DRTMIDI_NO_WARN_DEPRECATED +%C%_errors_CXXFLAGS = $(AM_CXXFLAGS) $(RTMIDITESTCXXFLAGS) -DRTMIDI_NO_WARN_DEPRECATED +%C%_midiclock_in_CXXFLAGS = $(AM_CXXFLAGS) $(RTMIDITESTCXXFLAGS) -DRTMIDI_NO_WARN_DEPRECATED +%C%_midiclock_out_CXXFLAGS = $(AM_CXXFLAGS) $(RTMIDITESTCXXFLAGS) -DRTMIDI_NO_WARN_DEPRECATED + + +%C%_midiprobe_LDFLAGS = $(AM_LDFLAGS) $(RTMIDITESTLDFLAGS) +%C%_midiout_LDFLAGS = $(AM_LDFLAGS) $(RTMIDITESTLDFLAGS) +%C%_qmidiin_LDFLAGS = $(AM_LDFLAGS) $(RTMIDITESTLDFLAGS) +%C%_cmidiin_LDFLAGS = $(AM_LDFLAGS) $(RTMIDITESTLDFLAGS) +%C%_sysextest_LDFLAGS = $(AM_LDFLAGS) $(RTMIDITESTLDFLAGS) +%C%_midiprobe2_LDFLAGS = $(AM_LDFLAGS) $(RTMIDITESTLDFLAGS) +%C%_midiprobe_all_LDFLAGS = $(AM_LDFLAGS) $(RTMIDITESTLDFLAGS) +%C%_cmidiin2_LDFLAGS = $(AM_LDFLAGS) $(RTMIDITESTLDFLAGS) +%C%_qmidiin2_LDFLAGS = $(AM_LDFLAGS) $(RTMIDITESTLDFLAGS) +%C%_midiout2_LDFLAGS = $(AM_LDFLAGS) $(RTMIDITESTLDFLAGS) +%C%_loopback_LDFLAGS = $(AM_LDFLAGS) $(RTMIDITESTLDFLAGS) +%C%_errors_LDFLAGS = $(AM_LDFLAGS) $(RTMIDITESTLDFLAGS) +%C%_midiclock_in_LDFLAGS = $(AM_LDFLAGS) $(RTMIDITESTLDFLAGS) +%C%_midiclock_out_LDFLAGS = $(AM_LDFLAGS) $(RTMIDITESTLDFLAGS) + + +%C%_midiprobe_LDADD = $(RTMIDILIBRARYNAME) +%C%_midiout_LDADD = $(RTMIDILIBRARYNAME) +%C%_qmidiin_LDADD = $(RTMIDILIBRARYNAME) +%C%_cmidiin_LDADD = $(RTMIDILIBRARYNAME) +%C%_sysextest_LDADD = $(RTMIDILIBRARYNAME) +%C%_midiprobe2_LDADD = $(RTMIDILIBRARYNAME) +%C%_midiprobe_all_LDADD = $(RTMIDILIBRARYNAME) +%C%_cmidiin2_LDADD = $(RTMIDILIBRARYNAME) +%C%_qmidiin2_LDADD = $(RTMIDILIBRARYNAME) +%C%_midiout2_LDADD = $(RTMIDILIBRARYNAME) +%C%_loopback_LDADD = $(RTMIDILIBRARYNAME) +%C%_errors_LDADD = $(RTMIDILIBRARYNAME) +%C%_midiclock_in_LDADD = $(RTMIDILIBRARYNAME) +%C%_midiclock_out_LDADD = $(RTMIDILIBRARYNAME) + + +if RTMIDICOPYDLLS + +#------------------------------------------------------------------------------------- +# Installing DLLs +#------------------------------------------------------------------------------------- + + + +rtmidi_installdll: + @echo 'solving references for $(DLLLINKFILE)... ' + set -e -x; \ + DLLSEARCHPATH="$(DLLSEARCHPATH)$(LOCALDLLSEARCHPATH)" ; \ + for f in `LANG=C $(OBJDUMP) -p $(DLLEXEDIR)/$(DLLLINKFILE) |sed '/^\s*DLL Name:.*\(lib\|thread\|wx\|mingw\|gcc\|stdc++\)/ { s/^\s*DLL Name:\s*//; p } ; d '`; \ + do \ + echo -n checking "$$f ... " ; \ + if [ ! -f $(DLLEXEDIR)/$$f ] ; then \ + echo -n "searching... " ; \ + IFS=: ; \ + for d in $$DLLSEARCHPATH ; do \ + echo "looking in $$d" ; \ + IFS= ; \ + fullname="$$d/$$f" ; \ + echo "testing $$fullname" ; \ + if test -f "$$fullname" ; \ + then \ + if cmp "$$fullname" "`pwd`/$(DLLEXEDIR)/$$f"; \ + then \ + echo "ok." ;\ + else \ + echo "installing $$fullname " ; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_DATA) "$$fullname" "$(DLLEXEDIR)" ; \ + fi ; \ + $(MAKE) $(AM_MAKEFLAGS) DLLLINKFILE="$$f" DLLEXEDIR="$(DLLEXEDIR)" rtmidi_installdll ; \ + elif test -f "$${fullname}.gz" ; \ + then \ + if cmp "$${fullname}.gz" "`pwd`/$(DLLEXEDIR)/$${f}.gz"; \ + then \ + echo "ok." ;\ + else \ + echo "installing $$fullname " ; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_DATA) "$${fullname}.gz" "$(DLLEXEDIR)" ; \ + fi; \ + GZIP=$(GZIP_ENV) gzip -dc $(DLLEXEDIR)/`basename "$${fullname}.gz"` >$(DLLEXEDIR)/"$$f" ; \ + $(MAKE) $(AM_MAKEFLAGS) DLLLINKFILE="$$f" DLLEXEDIR="$(DLLEXEDIR)" rtmidi_installdll ; \ + fi ; \ + done ; \ + fi ; \ + echo "done." ;\ + done ; + +#------------------------------------------------------------------------------------- +# Certain fixed files +#------------------------------------------------------------------------------------- + + + +check-am: check-dll-rtmidi + +check-dll-rtmidi: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) linkchecks-rtmidi + +linkchecks-rtmidi: + set -e -x ; \ + for d in `echo $(DLLPROGRAMS)| tr ' ' '\n' | grep -i -e '$(EXEEXT)$$\|.dll$$'` ; \ + do \ + ls -l "$$d" ; \ + file=`basename "$$d"` ; \ + dir=`dirname "$$d"` ; \ + $(MAKE) $(AM_MAKEFLAGS) DLLLINKFILE="$$file" DLLEXEDIR="$$dir" rtmidi_installdll ; \ + if test -f "$$dir/.libs/$$file"; \ + then \ + $(MAKE) $(AM_MAKEFLAGS) DLLLINKFILE="$$file" DLLEXEDIR="$$dir/.libs" rtmidi_installdll ; \ + fi ; \ + done +endif -EXTRA_DIST = cmidiin.dsp midiout.dsp midiprobe.dsp qmidiin.dsp \ - sysextest.dsp RtMidi.dsw diff --git a/tests/cmidiin.cpp b/tests/cmidiin.cpp index be78dbbd..49e48edb 100644 --- a/tests/cmidiin.cpp +++ b/tests/cmidiin.cpp @@ -19,7 +19,7 @@ void usage( void ) { exit( 0 ); } -void mycallback( double deltatime, std::vector< unsigned char > *message, void */*userData*/ ) +void mycallback( double deltatime, std::vector< unsigned char > *message, void * /* userData */ ) { unsigned int nBytes = message->size(); for ( unsigned int i=0; i +#include +#include "RtMidi.h" + +void mycallback( double deltatime, std::vector< unsigned char > *message, void * /* userData */ ) +{ + unsigned int nBytes = message->size(); + for ( unsigned int i=0; i 0 ) + std::cout << "stamp = " << deltatime << std::endl; +} + +// This function should be embedded in a try/catch block in case of +// an exception. It offers the user a choice of MIDI ports to open. +// It returns false if there are no ports available. +bool chooseMidiPort( rtmidi::MidiIn &rtmidi ); + +int main( int /* argc */, char */*argv*/[] ) +{ + + std::vector message; + std::cout << "\nWould you like to check all output ports? [Y/n] "; + + std::string keyHit; + std::getline( std::cin, keyHit ); + rtmidi::ApiType type = rtmidi::ALL_API; + if ( keyHit == "n" ) { + type = rtmidi::UNSPECIFIED; + } + + try { + + // rtmidi::MidiIn constructor + rtmidi::MidiIn midiin(type); + + // Call function to select port. + if ( chooseMidiPort( midiin ) == false ) return 0; + + // Set our callback function. This should be done immediately after + // opening the port to avoid having incoming messages written to the + // queue instead of sent to the callback function. + midiin.setCallback( &mycallback ); + + // Don't ignore sysex, timing, or active sensing messages. + midiin.ignoreTypes( false, false, false ); + + std::cout << "\nReading MIDI input ... press to quit.\n"; + char input; + std::cin.get(input); + + } catch ( rtmidi::Error &error ) { + error.printMessage(); + } +} + +bool chooseMidiPort( rtmidi::MidiIn &midi ) +{ + std::cout << "\nWould you like to open a virtual input port? [y/N] "; + + std::string keyHit; + std::getline( std::cin, keyHit ); + if ( keyHit == "y" ) { + midi.openVirtualPort("RtMidi virtual input"); + return true; + } + + std::string portName; + rtmidi::PortList list = midi.getPortList(rtmidi::PortDescriptor::INPUT); + if ( list.empty() ) { + std::cout << "No input ports available!" << std::endl; + return false; + } + + rtmidi::Pointer selected = list.front(); + if ( list.size() == 1 ) { + std::cout << "\nOpening " << selected->getName() << std::endl; + } else { + int nr; + std::vector > pointers(list.size()); + // copy the data into a structure that is used by the user interface. + std::copy(list.begin(),list.end(),pointers.begin()); + for (nr = 0 ; nr < (int)pointers.size(); nr++) { + portName = pointers[nr]->getName(rtmidi::PortDescriptor::LONG_NAME + | rtmidi::PortDescriptor::UNIQUE_PORT_NAME + | rtmidi::PortDescriptor::INCLUDE_API); + std::cout << " Input port #" << nr << ": " << portName << '\n'; + } + + do { + std::cout << "\nChoose a port number: "; + std::cin >> nr; + } while ( nr >= (int)pointers.size() ); + std::getline( std::cin, keyHit ); // used to clear out stdin + selected = pointers[nr]; + } + + /* The midi setup might have changed meanwhile. + Our portlist is under our control. So we enumerate this list. */ + // midi.openPort( i ); + midi.openPort(selected); + return true; +} diff --git a/tests/errors.cpp b/tests/errors.cpp new file mode 100644 index 00000000..f6b1ecf0 --- /dev/null +++ b/tests/errors.cpp @@ -0,0 +1,69 @@ +//*****************************************// +// loopback +// by Tobis Schlemmer, 2014. +// inspired by virtual-loopback-test-automated.js from the node-midi project. +// donated to RtMidi. +// +/*! \example loopback.cpp + Simple program to test MIDI input and + output in an internal loop using a user callback function. +*/ +//*****************************************// + +#include +#include +#include "RtMidi.h" + +// Platform-dependent sleep routines. +#if defined(__WINDOWS_MM__) +#include +#define SLEEP( milliseconds ) Sleep( (DWORD) milliseconds ) +#else // Unix variants +#include +#define SLEEP( milliseconds ) usleep( (unsigned long) (milliseconds * 1000.0) ) +#endif + +#define rtmidi_abort \ + std::cerr << __FILE__ << ":" << __LINE__ << ": rtmidi_aborting" << std::endl; \ + abort + +using namespace rtmidi; + +bool ok = false; + + +int main( int /* argc */, char * /*argv*/[] ) +{ + + std::vector types = Midi::getCompiledApi(); + if (types.size() > 1) { + for (size_t i = 0 ; i < types.size() ; i++) { + try { + MidiIn in (types[i]); + PortList ports = in.getPortList(PortDescriptor::INPUT); + if (ports.size()>1) { + MidiIn in2 (types[i?0:1]); + try { + in2.openPort(ports.front()); + } catch (Error & e) { + std::string msg = e.getMessage(); + e.printMessage(); + if (e.getType() != Error::INVALID_DEVICE) { + rtmidi_abort(); + } + } + } + } catch (Error & e) { + std::string msg = e.getMessage(); + e.printMessage(); + if (msg != rtmidi_gettext("")) { + if (e.getType() != Error::WARNING && + e.getType() != Error::NO_DEVICES_FOUND) { + rtmidi_abort(); + } + } + } + } + } + return 0; +} diff --git a/tests/loopback.cpp b/tests/loopback.cpp new file mode 100644 index 00000000..7bc1fc38 --- /dev/null +++ b/tests/loopback.cpp @@ -0,0 +1,210 @@ +//*****************************************// +// loopback +// by Tobis Schlemmer, 2014. +// inspired by virtual-loopback-test-automated.js from the node-midi project. +// donated to RtMidi. +// +/*! \example loopback.cpp + Simple program to test MIDI input and + output in an internal loop using a user callback function. +*/ +//*****************************************// + +#include +#include +#include "RtMidi.h" + +// Platform-dependent sleep routines. +#if defined(__WINDOWS_MM__) +#include +#define SLEEP( milliseconds ) Sleep( (DWORD) milliseconds ) +#else // Unix variants +#include +#define SLEEP( milliseconds ) usleep( (unsigned long) (milliseconds * 1000.0) ) +#endif + + +/* Here, we store the expected output. Timing is not tested */ +std::vector virtualinstring; +const char * virtualinstringgoal = + "\xc0\x5\xf1\x3c\xb0\x7\x64\x90\x40\x5a\x80\x40\x28\xb0\x7\x28\xf0\x43\x4\x3\x2\xf7"; +std::vector instring; +const char * instringgoal = + "\xc0\x6\xf1\x3d\xb0\x8\x64\x90\x41\x5a\x80\x41\x28\xb0\x8\x28\xf0\x43\x4\x3\x3\xf7"; + +inline size_t getlength(const char * messages) { + size_t retval = 0; + const unsigned char * c = reinterpret_cast(messages); + while (*(c++) != 0xf7) retval++; + return ++retval; +} + +void mycallback1( double /* deltatime */, std::vector< unsigned char > *message, void * /* userData */ ) +{ + unsigned int nBytes = message->size(); + for ( unsigned int i=0; iat(i)); + // std::cout << "\\x" << std::hex << (int)message->at(i) << std::flush; + } + /* if ( nBytes > 0 ) + std::cout << "stamp = " << deltatime << std::endl; + */ +} + +void mycallback2( double /* deltatime */, std::vector< unsigned char > *message, void * /* userData */ ) +{ + unsigned int nBytes = message->size(); + for ( unsigned int i=0; iat(i)); + // std::cout << "\\x" << std::hex << (int)message->at(i); + } + /* + if ( nBytes > 0 ) + std::cout << "stamp = " << deltatime << std::endl; + */ +} + + + +int main( int /* argc */, char * /*argv*/[] ) +{ + + std::vector message; + + try { + + // rtmidi::MidiIn constructor + rtmidi::MidiIn virtualin; + // rtmidi::MidiIn constructor + rtmidi::MidiOut virtualout; + + virtualin.openVirtualPort("RtMidi Test Virtual In"); + virtualout.openVirtualPort("RtMidi Test Virtual Out"); + + rtmidi::Pointer indescriptor + = virtualin.getDescriptor(true); + + rtmidi::Pointer outdescriptor + = virtualout.getDescriptor(true); + + { // avoid problems with wrong destruction order + /* use smart pointers to handle deletion */ + rtmidi::Pointer midiin(outdescriptor->getInputApi()); + if (!midiin) abort(); + rtmidi::Pointer midiout(indescriptor->getOutputApi()); + if (!midiout) abort(); + + + + midiin->openPort(outdescriptor); + midiout->openPort(indescriptor); + + + // Set our callback function. This should be done immediately after + // opening the port to avoid having incoming messages written to the + // queue instead of sent to the callback function. + midiin->setCallback( &mycallback1 ); + virtualin.setCallback( &mycallback2 ); + + // Don't ignore sysex, timing, or active sensing messages. + // Don't ignore sysex, timing, or active sensing messages. + midiin->ignoreTypes( false, false, false ); + virtualin.ignoreTypes( false, false, false ); + + SLEEP( 5000 ); + + // Send out a series of MIDI messages. + + // Program change: 192, 5 + message.push_back( 192 ); + message.push_back( 5 ); + midiout->sendMessage( &message ); + message[1] = 6; + virtualout.sendMessage(&message); + + SLEEP( 500 ); + + message[0] = 0xF1; + message[1] = 60; + midiout->sendMessage( &message ); + message[1] = 61; + virtualout.sendMessage(&message); + + // Control Change: 176, 7, 100 (volume) + message[0] = 176; + message[1] = 7; + message.push_back( 100 ); + midiout->sendMessage( &message ); + message[1] = 8; + virtualout.sendMessage ( &message ); + + // Note On: 144, 64, 90 + message[0] = 144; + message[1] = 64; + message[2] = 90; + midiout->sendMessage( &message ); + message[1] = 65; + virtualout.sendMessage( &message ); + + SLEEP( 500 ); + + // Note Off: 128, 64, 40 + message[0] = 128; + message[1] = 64; + message[2] = 40; + midiout->sendMessage( &message ); + message[1] = 65; + virtualout.sendMessage( &message ); + + SLEEP( 500 ); + + // Control Change: 176, 7, 40 + message[0] = 176; + message[1] = 7; + message[2] = 40; + midiout->sendMessage( &message ); + message[1] = 8; + virtualout.sendMessage( &message ); + + SLEEP( 500 ); + + // Sysex: 240, 67, 4, 3, 2, 247 + message[0] = 240; + message[1] = 67; + message[2] = 4; + message.push_back( 3 ); + message.push_back( 2 ); + message.push_back( 247 ); + midiout->sendMessage( &message ); + message[4] = 3; + virtualout.sendMessage( &message ); + + SLEEP( 500 ); + } + const unsigned char * goal = reinterpret_cast(instringgoal); + size_t i; + std::cout << "Virtual output -> input:" << std::endl; + if (instring.size() != getlength(instringgoal)) abort(); + for (i = 0 ; i < instring.size() && goal[i] ; i++){ + std::cout << " " << std::hex << (int) instring[i]; + std::cout << "/" << std::hex << (int) goal[i] << std::flush; + if (instring[i] != goal[i]) abort(); + } + std::cout << std::endl; + if (i != instring.size()) abort(); + goal = reinterpret_cast(virtualinstringgoal); + std::cout << "Output -> virtual input:" << std::endl; + if (instring.size() != getlength(virtualinstringgoal)) abort(); + for (i = 0 ; i < virtualinstring.size() && goal[i] ; i++) { + std::cout << " " << std::hex << (int) virtualinstring[i]; + std::cout << "/" << std::hex << (int) goal[i] << std::flush; + if (virtualinstring[i] != goal[i]) abort(); + } + std::cout << std::endl; + if (i != virtualinstring.size()) abort(); + + } catch ( rtmidi::Error &error ) { + error.printMessage(); + } + return 0; +} diff --git a/tests/midiout.cpp b/tests/midiout.cpp index 05505cc6..49c5b081 100644 --- a/tests/midiout.cpp +++ b/tests/midiout.cpp @@ -24,7 +24,7 @@ // It returns false if there are no ports available. bool chooseMidiPort( RtMidiOut *rtmidi ); -int main( void ) +int main( int /* argc*/, char */*argv*/[] ) { RtMidiOut *midiout = 0; std::vector message; diff --git a/tests/midiout2.cpp b/tests/midiout2.cpp new file mode 100644 index 00000000..c68e045c --- /dev/null +++ b/tests/midiout2.cpp @@ -0,0 +1,173 @@ +//*****************************************// +// midiout.cpp +// by Gary Scavone, 2003-2014. +// and Tobias Schlemmer 2014 +// +/*! \example midiout2.cpp + Simple program to test MIDI output. +*/ +// *****************************************// + + + +#include +#include +#include "RtMidi.h" + +// Platform-dependent sleep routines. +#if defined(__WINDOWS_MM__) +#include +#define SLEEP( milliseconds ) Sleep( (DWORD) milliseconds ) +#undef UNIQUE_PORT_NAME +#else // Unix variants +#include +#define SLEEP( milliseconds ) usleep( (unsigned long) (milliseconds * 1000.0) ) +#endif + +// This function should be embedded in a try/catch block in case of +// an exception. It offers the user a choice of MIDI ports to open. +// It returns false if there are no ports available. +bool chooseMidiPort( rtmidi::MidiOut &midi ); + +//! The main program +int main( int /* argc*/, char */*argv*/[] ) +{ + std::vector message; + std::cout << "\nWould you like to check all output ports? [Y/n] "; + + std::string keyHit; + std::getline( std::cin, keyHit ); + rtmidi::ApiType type = rtmidi::ALL_API; + if ( keyHit == "n" ) { + type = rtmidi::UNSPECIFIED; + } + + // rtmidi::MidiOut constructor + try { + rtmidi::MidiOut midiout(type); + + // Call function to select port. + try { + if ( chooseMidiPort( midiout ) == false ) return 1; + } + catch ( rtmidi::Error &error ) { + error.printMessage(); + return 2; + } + + + SLEEP( 5000 ); + + // Send out a series of MIDI messages. + + // Program change: 192, 5 + message.push_back( 192 ); + message.push_back( 5 ); + midiout.sendMessage( &message ); + + SLEEP( 500 ); + + message[0] = 0xF1; + message[1] = 60; + midiout.sendMessage( &message ); + + // Control Change: 176, 7, 100 (volume) + message[0] = 176; + message[1] = 7; + message.push_back( 100 ); + midiout.sendMessage( &message ); + + // Note On: 144, 64, 90 + message[0] = 144; + message[1] = 64; + message[2] = 90; + midiout.sendMessage( &message ); + + SLEEP( 500 ); + + // Note Off: 128, 64, 40 + message[0] = 128; + message[1] = 64; + message[2] = 40; + midiout.sendMessage( &message ); + + SLEEP( 500 ); + + // Control Change: 176, 7, 40 + message[0] = 176; + message[1] = 7; + message[2] = 40; + midiout.sendMessage( &message ); + + SLEEP( 500 ); + + // Sysex: 240, 67, 4, 3, 2, 247 + message[0] = 240; + message[1] = 67; + message[2] = 4; + message.push_back( 3 ); + message.push_back( 2 ); + message.push_back( 247 ); + midiout.sendMessage( &message ); + + + } + catch ( rtmidi::Error &error ) { + error.printMessage(); + exit( EXIT_FAILURE ); + } + + return 0; +} + +bool chooseMidiPort( rtmidi::MidiOut &midi ) +{ + std::cout << "\nWould you like to open a virtual output port? [y/N] "; + + std::string keyHit; + std::getline( std::cin, keyHit ); + if ( keyHit == "y" ) { + midi.openVirtualPort("RtMidi virtual output"); + std::cout << "Press return to start the transmission." << std::endl; + std::getline( std::cin, keyHit ); + + return true; + } + + std::string portName; + rtmidi::PortList list = midi.getPortList(rtmidi::PortDescriptor::OUTPUT); + if ( list.empty() ) { + std::cout << "No output ports available!" << std::endl; + return false; + } + + rtmidi::Pointer selected = list.front(); + if ( list.size() == 1 ) { + std::cout << "\nOpening " << selected->getName() << std::endl; + } else { + int nr; + std::vector > pointers(list.size()); + // copy the data into a structure that is used by the user interface. + std::copy(list.begin(),list.end(),pointers.begin()); + for (nr = 0 ; nr < (int)pointers.size(); nr++) { + portName = pointers[nr]->getName(rtmidi::PortDescriptor::LONG_NAME + | rtmidi::PortDescriptor::UNIQUE_PORT_NAME + | rtmidi::PortDescriptor::INCLUDE_API); + std::cout << " Output port #" << nr << ": " << portName << '\n'; + } + + do { + std::cout << "\nChoose a port number: "; + std::cin >> nr; + } while ( nr >= (int)pointers.size() ); + std::getline( std::cin, keyHit ); // used to clear out stdin + selected = pointers[nr]; + } + + /* The midi setup might have changed meanwhile. + Our portlist is under our control. So we enumerate this list. */ + // midi.openPort( i ); + midi.openPort(selected); + + return true; +} diff --git a/tests/midiprobe-all.cpp b/tests/midiprobe-all.cpp new file mode 100644 index 00000000..90839fe0 --- /dev/null +++ b/tests/midiprobe-all.cpp @@ -0,0 +1,122 @@ +// midiprobe.cpp +// +// Simple program to check MIDI inputs and outputs. +// +// by Gary Scavone, 2003-2012. + +#include +#include +#include +#include "RtMidi.h" + +int main() +{ + // Create an api map. + + std::vector< RtMidi::Api > apis; + rtmidi::Midi :: getCompiledApi( apis ); + + std::cout << "\nCompiled APIs:\n"; + for ( unsigned int i=0; igetName() << std::endl; + for (int j = 0 ; j < 4 ; j++ ) { + std::cout << j << ":f:f: " << (*i)->getName (j) << std::endl; + std::cout << j << ":t:f: " << (*i)->getName (j | rtmidi::PortDescriptor::UNIQUE_PORT_NAME) << std::endl; + std::cout << j << ":f:t: " << (*i)->getName (j | rtmidi::PortDescriptor::INCLUDE_API) << std::endl; + std::cout << j << ":t:t: " << (*i)->getName (j | rtmidi::PortDescriptor::UNIQUE_PORT_NAME + | rtmidi::PortDescriptor::INCLUDE_API) << std::endl; + std::cout << std::endl; + } + std::cout << std::endl; + } + + std::cout << "**********************************************************************" << std::endl; + + // RtMidiOut constructor ... exception possible + rtmidi::MidiOut midiout (rtmidi::ALL_API); + list = midiout.getPortList(); + + std::cout << "\nCurrent output API: " << rtmidi::getApiName(midiout.getCurrentApi()) << std::endl; + + // Check inputs. + std::cout << "\nThere are " << list.size() << " MIDI output sinks available.\n"; + + for (rtmidi::PortList::iterator i = list.begin(); + i != list.end(); + i++) { + std::cout << " Output Port: " << (*i)->getName() << std::endl; + for (int j = 0 ; j < 4 ; j++ ) { + std::cout << j << ":f:f: " << (*i)->getName (j) << std::endl; + std::cout << j << ":t:f: " << (*i)->getName (j | rtmidi::PortDescriptor::UNIQUE_PORT_NAME) << std::endl; + std::cout << j << ":f:t: " << (*i)->getName (j | rtmidi::PortDescriptor::INCLUDE_API) << std::endl; + std::cout << j << ":t:t: " << (*i)->getName (j | rtmidi::PortDescriptor::UNIQUE_PORT_NAME + | rtmidi::PortDescriptor::INCLUDE_API) << std::endl; + std::cout << std::endl; + } + std::cout << std::endl; + } + + std::cout << "**********************************************************************" << std::endl; + std::cout << "* entering unlimited mode *" << std::endl; + std::cout << "**********************************************************************" << std::endl; + + list = midiin.getPortList(rtmidi::PortDescriptor::UNLIMITED); + // Check inputs. + std::cout << "\nThere are " << list.size() << " MIDI input sources available in unlimited mode.\n"; + for (rtmidi::PortList::iterator i = list.begin(); + i != list.end(); + i++) { + std::cout << " Input Port: " << (*i)->getName() << std::endl; + for (int j = 0 ; j < 4 ; j++ ) { + std::cout << j << ":f:f: " << (*i)->getName (j) << std::endl; + std::cout << j << ":t:f: " << (*i)->getName (j | rtmidi::PortDescriptor::UNIQUE_PORT_NAME) << std::endl; + std::cout << j << ":f:t: " << (*i)->getName (j | rtmidi::PortDescriptor::INCLUDE_API) << std::endl; + std::cout << j << ":t:t: " << (*i)->getName (j | rtmidi::PortDescriptor::UNIQUE_PORT_NAME + | rtmidi::PortDescriptor::INCLUDE_API) << std::endl; + std::cout << std::endl; + } + std::cout << std::endl; + } + + std::cout << "**********************************************************************" << std::endl; + list = midiout.getPortList(rtmidi::PortDescriptor::UNLIMITED); + + // Check inputs. + std::cout << "\nThere are " << list.size() << " MIDI output sinks available in unlimited mode.\n"; + + for (rtmidi::PortList::iterator i = list.begin(); + i != list.end(); + i++) { + std::cout << " Output Port: " << (*i)->getName() << std::endl; + for (int j = 0 ; j < 4 ; j++ ) { + std::cout << j << ":f:f: " << (*i)->getName (j) << std::endl; + std::cout << j << ":t:f: " << (*i)->getName (j | rtmidi::PortDescriptor::UNIQUE_PORT_NAME) << std::endl; + std::cout << j << ":f:t: " << (*i)->getName (j | rtmidi::PortDescriptor::INCLUDE_API) << std::endl; + std::cout << j << ":t:t: " << (*i)->getName (j | rtmidi::PortDescriptor::UNIQUE_PORT_NAME + | rtmidi::PortDescriptor::INCLUDE_API) << std::endl; + std::cout << std::endl; + } + std::cout << std::endl; + } + + } catch ( rtmidi::Error &error ) { + error.printMessage(); + } + + return 0; +} diff --git a/tests/midiprobe2.cpp b/tests/midiprobe2.cpp new file mode 100644 index 00000000..0fae3d8e --- /dev/null +++ b/tests/midiprobe2.cpp @@ -0,0 +1,137 @@ +/*! \example midiprobe2.cpp + + Simple program to check MIDI inputs and outputs. +*/ +// +// by Gary Scavone, 2003-2014. +// and Tobias Schlemmer 2014 + +#include +#include +#include +#include "RtMidi.h" + +int main() +{ + // Create an api map. + std::map apiMap; + apiMap[rtmidi::MACOSX_CORE] = "OS-X CoreMidi"; + apiMap[rtmidi::WINDOWS_MM] = "Windows MultiMedia"; + apiMap[rtmidi::WINDOWS_KS] = "Windows Kernel Straming"; + apiMap[rtmidi::UNIX_JACK] = "Jack Client"; + apiMap[rtmidi::LINUX_ALSA] = "Linux ALSA"; + apiMap[rtmidi::DUMMY] = "RtMidi Dummy"; + apiMap[rtmidi::ALL_API] = "All RtMidi APIs"; + + std::vector< rtmidi::ApiType > apis; + rtmidi::Midi :: getCompiledApi( apis ); + + std::cout << "\nCompiled APIs:\n"; + for ( unsigned int i=0; igetName() << std::endl; + for (int j = 0 ; j < 4 ; j++ ) { + std::cout << j << ":f:f: " << (*i)->getName (j) << std::endl; + std::cout << j << ":t:f: " << (*i)->getName (j | rtmidi::PortDescriptor::UNIQUE_PORT_NAME) << std::endl; + std::cout << j << ":f:t: " << (*i)->getName (j | rtmidi::PortDescriptor::INCLUDE_API) << std::endl; + std::cout << j << ":t:t: " << (*i)->getName (j | rtmidi::PortDescriptor::UNIQUE_PORT_NAME + | rtmidi::PortDescriptor::INCLUDE_API) << std::endl; + std::cout << std::endl; + } + std::cout << std::endl; + } + + std::cout << "**********************************************************************" << std::endl; + + // rtmidi::MidiOut constructor ... exception possible + rtmidi::MidiOut midiout; + + std::cout << "\nCurrent output API: " << apiMap[ midiout.getCurrentApi() ] << std::endl; + + list = midiout.getPortList(); + + // Check inputs. + std::cout << "\nThere are " << list.size() << " MIDI output sinks available.\n"; + + for (rtmidi::PortList::iterator i = list.begin(); + i != list.end(); + i++) { + std::cout << " Output Port: " << (*i)->getName() << std::endl; + for (int j = 0 ; j < 4 ; j++ ) { + std::cout << j << ":f:f: " << (*i)->getName (j) << std::endl; + std::cout << j << ":t:f: " << (*i)->getName (j | rtmidi::PortDescriptor::UNIQUE_PORT_NAME) << std::endl; + std::cout << j << ":f:t: " << (*i)->getName (j | rtmidi::PortDescriptor::INCLUDE_API) << std::endl; + std::cout << j << ":t:t: " << (*i)->getName (j | rtmidi::PortDescriptor::UNIQUE_PORT_NAME + | rtmidi::PortDescriptor::INCLUDE_API) << std::endl; + std::cout << std::endl; + } + std::cout << std::endl; + } + + std::cout << "**********************************************************************" << std::endl; + std::cout << "* entering unlimited mode *" << std::endl; + std::cout << "**********************************************************************" << std::endl; + + list = midiin.getPortList(rtmidi::PortDescriptor::UNLIMITED); + // Check inputs. + std::cout << "\nThere are " << list.size() << " MIDI input sources available in unlimited mode.\n"; + for (rtmidi::PortList::iterator i = list.begin(); + i != list.end(); + i++) { + std::cout << " Input Port: " << (*i)->getName() << std::endl; + for (int j = 0 ; j < 4 ; j++ ) { + std::cout << j << ":f:f: " << (*i)->getName (j) << std::endl; + std::cout << j << ":t:f: " << (*i)->getName (j | rtmidi::PortDescriptor::UNIQUE_PORT_NAME) << std::endl; + std::cout << j << ":f:t: " << (*i)->getName (j | rtmidi::PortDescriptor::INCLUDE_API) << std::endl; + std::cout << j << ":t:t: " << (*i)->getName (j | rtmidi::PortDescriptor::UNIQUE_PORT_NAME + | rtmidi::PortDescriptor::INCLUDE_API) << std::endl; + std::cout << std::endl; + } + std::cout << std::endl; + } + + std::cout << "**********************************************************************" << std::endl; + list = midiout.getPortList(rtmidi::PortDescriptor::UNLIMITED); + + // Check inputs. + std::cout << "\nThere are " << list.size() << " MIDI output sinks available in unlimited mode.\n"; + + for (rtmidi::PortList::iterator i = list.begin(); + i != list.end(); + i++) { + std::cout << " Output Port: " << (*i)->getName() << std::endl; + for (int j = 0 ; j < 4 ; j++ ) { + std::cout << j << ":f:f: " << (*i)->getName (j) << std::endl; + std::cout << j << ":t:f: " << (*i)->getName (j | rtmidi::PortDescriptor::UNIQUE_PORT_NAME) << std::endl; + std::cout << j << ":f:t: " << (*i)->getName (j | rtmidi::PortDescriptor::INCLUDE_API) << std::endl; + std::cout << j << ":t:t: " << (*i)->getName (j | rtmidi::PortDescriptor::UNIQUE_PORT_NAME + | rtmidi::PortDescriptor::INCLUDE_API) << std::endl; + std::cout << std::endl; + } + std::cout << std::endl; + } + + + + } catch ( rtmidi::Error &error ) { + error.printMessage(); + } + + return 0; +} diff --git a/tests/qmidiin2.cpp b/tests/qmidiin2.cpp new file mode 100644 index 00000000..20956c8d --- /dev/null +++ b/tests/qmidiin2.cpp @@ -0,0 +1,126 @@ +//*****************************************// +// qmidiin.cpp +// by Gary Scavone, 2003-2014. +// and Tobias Schlemmer, 2014 +// +/*! \example qmidiin2.cpp + Simple program to test MIDI input and + retrieval from the queue. +*/ +//*****************************************// + +#include +#include +#include +#include "RtMidi.h" + +// Platform-dependent sleep routines. +#if defined(__WINDOWS_MM__) +#include +#define SLEEP( milliseconds ) Sleep( (DWORD) milliseconds ) +#undef UNIQUE_PORT_NAME +#else // Unix variants +#include +#define SLEEP( milliseconds ) usleep( (unsigned long) (milliseconds * 1000.0) ) +#endif + +bool done; +static void finish( int /*ignore*/ ){ done = true; } + +void usage( rtmidi::PortList list ) { + // Error function in case of incorrect command-line + // argument specifications. + std::cout << "\nusage: qmidiin \n"; + std::cout << " where port = the device to use (default = first available port).\n\n"; + + std::cout << "Available ports:" << std::endl; + int flags = rtmidi::PortDescriptor::SESSION_PATH | + rtmidi::PortDescriptor::UNIQUE_PORT_NAME | + rtmidi::PortDescriptor::INCLUDE_API; + for (rtmidi::PortList::iterator i = list.begin(); + i != list.end(); i++) { + std::cout << "\"" + << (*i)->getName(rtmidi::PortDescriptor::SESSION_PATH | + rtmidi::PortDescriptor::UNIQUE_PORT_NAME | + rtmidi::PortDescriptor::INCLUDE_API) + << "\""; + std::cout << "\t"; + std::cout << (*i)->getName() << std::endl; + } + exit( 0 ); +} + +int main( int argc, char *argv[] ) +{ + std::vector message; + int nBytes, i; + double stamp; + + + // rtmidi::MidiIn constructor + try { + rtmidi::MidiIn midiin(rtmidi::ALL_API); + + + rtmidi::PortList list = midiin.getPortList(); + + + // Minimal command-line check. + if ( argc > 2 ) usage(list); + + rtmidi::Pointer port = 0; + // Check available ports vs. specified. + if ( argc == 2 ) { + for (rtmidi::PortList::iterator i = list.begin(); + i != list.end(); i++) { + if (argv[1] == (*i)->getName(rtmidi::PortDescriptor::SESSION_PATH | + rtmidi::PortDescriptor::UNIQUE_PORT_NAME | + rtmidi::PortDescriptor::INCLUDE_API)) { + port = *i; + break; + } + } + } else { + port = list.front(); + } + if ( !port ) { + std::cout << "Invalid port specifier!\n"; + usage(list); + } + + try { + midiin.openPort( port ); + } + catch ( rtmidi::Error &error ) { + error.printMessage(); + return 1; + } + + // Don't ignore sysex, timing, or active sensing messages. + midiin.ignoreTypes( false, false, false ); + + // Install an interrupt handler function. + done = false; + (void) signal(SIGINT, finish); + + // Periodically check input queue. + std::cout << "Reading MIDI from port ... quit with Ctrl-C.\n"; + while ( !done ) { + stamp = midiin.getMessage( &message ); + nBytes = message.size(); + for ( i=0; i 0 ) + std::cout << "stamp = " << stamp << std::endl; + + // Sleep for 10 milliseconds. + SLEEP( 10 ); + } + } + catch ( rtmidi::Error &error ) { + error.printMessage(); + exit( EXIT_FAILURE ); + } + + return 0; +} diff --git a/tests/sysextest.cpp b/tests/sysextest.cpp index 8402c74c..e2cf2a2b 100644 --- a/tests/sysextest.cpp +++ b/tests/sysextest.cpp @@ -1,9 +1,9 @@ //*****************************************// -// sysextest.cpp // by Gary Scavone, 2003-2005. -// -// Simple program to test MIDI sysex sending and receiving. -// +/*! \example sysextest.cpp + + program to test MIDI sysex sending and receiving. +*/ //*****************************************// #include diff --git a/throwing-functions.txt b/throwing-functions.txt new file mode 100644 index 00000000..b6ca765b --- /dev/null +++ b/throwing-functions.txt @@ -0,0 +1,66 @@ +-*- Org -*- +* throw +|------------------------------------+-------------------+--------------------------------| +| Function | Type | Should be caught inside RtMidi | +|------------------------------------+-------------------+--------------------------------| +| Midi::error | all | n | +| MidiApi :: error(Error e) | all | n | +| MidiIn :: MidiIn | INVALID_PARAMETER | impossible | +| | NO_DEVICES_FOUND | | +| MidiOut :: MidiOut | INVALID_PARAMETER | impossible | +| | NO_DEVICES_FOUND | | +| CoreSequencer::getPortCapabilities | WARNING | y | +| CoreSequencer::createPort | INVALID_PARAMETER | y | +| | DRIVER_ERROR | | +| CoreSequencer::createVirtualPort | INVALID_PARAMETER | y | +| | DRIVER_ERROR | | +| CoreSequencer::init | DRIVER_ERROR | y | +| AlsaSequencer::connectPorts | DRIVER_ERROR | y | +| AlsaSequencer::init | DRIVER_ERROR | y | +| WinMMSequencer::getPortName | INVALID_PARAMETER | Y | +| JackSequencer::init | NO_DEVICES_FOUND | y | +| JackMidiData::openPort | DRIVER_ERROR | | +|------------------------------------+-------------------+--------------------------------| +* error +|----------------------------+-----------------+-----------------------------| +| Function | Type | Can be caught inside RtMidi | +|----------------------------+-----------------+-----------------------------| +| MidiIn :: openMidiApi | vom Konstructor | | +| MidiInWinMM :: initialize | WARNING | yes | +| MidiOutWinMM :: initialize | WARNING | yes | +| | | | +| | | | +|----------------------------+-----------------+-----------------------------| + +* backtrace +** CoreSequencer::getPortCapabilities +|-------------------------------------+-------------------| +| Function | Type | +|-------------------------------------+-------------------| +| CorePortDescriptor::getCapabilities | WARNING | +| CorePortDescriptor::getPortList | -- | +| CoreMidiData::openPort | INVALID_PARAMETER | +| | DRIVER_ERROR | +| CoreSequencer::CoreSequencer | DRIVER_ERROR | +| AlsaSequencer::GetPortName | DRIVER_ERROR | +| AlsaSequencer::getPortCapabilities | DRIVER_ERROR | +| AlsaSequencer::getNextClient | DRIVER_ERROR | +| AlsaSequencer::getNextPort | DRIVER_ERROR | +| AlsaSequencer::DRIVER_ERROR | DRIVER_ERROR | +| AlsaSequencer::deletePort | DRIVER_ERROR | +| AlsaSequencer::connectPorts | DRIVER_ERROR | +| AlsaSequencer::closePort | DRIVER_ERROR | +| AlsaSequencer::startQueue | DRIVER_ERROR | +| AlsaSequencer::GetClient | DRIVER_ERROR | +| AlsaMidiData::connectPorts | DRIVER_ERROR | +| JackSequencer::getPortList | NO_DEVICES_FOUND | +| JackSequencer::getPort | NO_DEVICES_FOUND | +| JackSequencer::getPortName | NO_DEVICES_FOUND | +| JackSequencer::createPort | NO_DEVICES_FOUND | +| JackSequencer::deletePort | NO_DEVICES_FOUND | +| JackSequencer::connectPorts | NO_DEVICES_FOUND | +| JackSequencer::closePort | NO_DEVICES_FOUND | +| JackMidiData::init | NO_DEVICES_FOUND | +| JackMidiData::connectPorts | NO_DEVICES_FOUND | +| MidiOutJack :: initialize | NO_DEVICES_FOUND | +| | |