# This is a shell archive. Save it in a file, remove anything before # this line, and then unpack it by entering "sh file". Note, it may # create directories; files and directories will be owned by you and # have default permissions. # # This archive contains: # # audio # audio/chuck # audio/chuck/Makefile # audio/chuck/pkg-descr # audio/chuck/distinfo # audio/chuck/files # audio/chuck/files/patch-src_core_util__console.cpp # audio/chuck/files/patch-src_host_RtAudio_RtAudio.cpp # audio/chuck/files/patch-src_host_RtAudio_RtAudio.h # audio/chuck/files/patch-src_core_util__string.cpp # audio/chuck/files/patch-src_core_util__hid.cpp # audio/chuck/files/patch-src_host_chuck__main.cpp # audio/chuck/files/patch-src_core_rtmidi.h # audio/chuck/files/patch-src_core_util__serial.cpp # audio/chuck/files/extra-patch-src_core_rtmidi-oss_alsa.cpp # audio/chuck/files/extra-patch-src_core_rtmidi-oss_jack.cpp # audio/chuck/pkg-plist # echo c - audio mkdir -p audio > /dev/null 2>&1 echo c - audio/chuck mkdir -p audio/chuck > /dev/null 2>&1 echo x - audio/chuck/Makefile sed 's/^X//' >audio/chuck/Makefile << '07ac3234b9409852984528b28d19981a' X# Created by: Timothy Beyer X XPORTNAME= chuck XPORTVERSION= 20210815 XCATEGORIES= audio X XMAINTAINER= beyert@cs.ucr.edu XCOMMENT= Strongly-timed, Concurrent, and On-the-fly Music Programming Language X XLICENSE= GPLv2 XLICENSE_FILE= ${WRKSRC}/notes/COPYING X XBUILD_DEPENDS= ${LOCALBASE}/include/linux/joystick.h:devel/evdev-proto \ X ${LOCALBASE}/include/linux/types.h:multimedia/v4l_compat XLIB_DEPENDS= libsndfile.so:audio/libsndfile \ X libudev.so:devel/libudev-devd XRUN_DEPENDS= ${LOCALBASE}/include/linux/joystick.h:devel/evdev-proto \ X ${LOCALBASE}/include/linux/types.h:multimedia/v4l_compat X XUSES= compiler:c++11-lib gmake dos2unix X XOPTIONS_DEFINE= DOCS EXAMPLES XOPTIONS_DEFAULT= JACK SAME X XOPTIONS_SINGLE= BACKEND MIDI XOPTIONS_SINGLE_BACKEND= JACK ALSA OSSV4 XOPTIONS_SINGLE_MIDI= SAME OSSJACK OSSALSA X XJACK_DESC= Jack sound system XALSA_DESC= ALSA sound system XOSSV4_DESC= OSSv4 sound system X XSAME_DESC= Default MIDI for JACK or ALSA backends XOSSJACK_DESC= JACK MIDI for OSSV4 backend XOSSALSA_DESC= ALSA MIDI for OSSV4 backend X XOSSV4_BUILD_DEPENDS+= ${LOCALBASE}/lib/oss/include/sys/soundcard.h:audio/oss XALSA_LIB_DEPENDS+= libasound.so:audio/alsa-lib XJACK_LIB_DEPENDS+= libjack.so:audio/jack X XOSSALSA_LIB_DEPENDS+= libasound.so:audio/alsa-lib XOSSJACK_LIB_DEPENDS+= libjack.so:audio/jack X X.include X X.if ${PORT_OPTIONS:MDOCS} XUSE_TEX= latex:formats dvipsk:build X.endif X XDOS2UNIX_FILES= ${BUILD_WRKSRC}/host/RtAudio/RtAudio.cpp X XUSE_GITHUB= yes XGH_ACCOUNT= ccrma XGH_PROJECT= chuck XGH_TAGNAME= 461d11f X XCFLAGS+= -I${LOCALBASE}/include X X.if ${PORT_OPTIONS:MOSSV4} XCFLAGS+= -I${LOCALBASE}/lib/oss/include X.endif X XLDFLAGS+= -L${LOCALBASE}/lib -L/usr/lib -L/lib X XBUILD_WRKSRC= ${WRKSRC}/src XINSTALL_WRKSRC= ${BUILD_WRKSRC} X.if ${PORT_OPTIONS:MOSSV4} XALL_TARGET= freebsd-oss X.elif ${PORT_OPTIONS:MALSA} XALL_TARGET= freebsd-alsa X.elif ${PORT_OPTIONS:MJACK} XALL_TARGET= freebsd-jack X.else XALL_TARGET= freebsd-oss X.endif XMAKEFILE= makefile X X.if ${PORT_OPTIONS:MOSSV4} && ${PORT_OPTIONS:MSAME} XIGNORE+= backend OSSV4 lacks MIDI, so for OSSV4 MIDI, use OSSJACK or OSSALSA X.elif ${PORT_OPTIONS:MJACK} && ! ${PORT_OPTIONS:MSAME} XIGNORE+= backend JACK must use SAME MIDI X.elif ${PORT_OPTIONS:MALSA} && ! ${PORT_OPTIONS:MSAME} XIGNORE+= backend ALSA must use SAME MIDI X.elif ${PORT_OPTIONS:MOSSJACK} || ${PORT_OPTIONS:MJACK} XEXTRA_PATCHES= ${PATCHDIR}/extra-patch-src_core_rtmidi-oss_jack.cpp X.elif ${PORT_OPTIONS:MOSSALSA} || ${PORT_OPTIONS:MALSA} XEXTRA_PATCHES= ${PATCHDIR}/extra-patch-src_core_rtmidi-oss_alsa.cpp X.endif X Xpost-patch: X @${REINPLACE_CMD} -e 's|make $(MAKECMDGOALS)|gmake $(MAKECMDGOALS)|' \ X -e 's|CC=gcc|CC=${CC}|' \ X -e 's|CXX=g++|CXX=${CXX}|' \ X -e 's|LD=g++|LD=${CXX}|' \ X -e 's|CFLAGS+= -O3|CFLAGS+=|' \ X ${BUILD_WRKSRC}/${MAKEFILE} ${BUILD_WRKSRC}/core/${MAKEFILE} X.if ${PORT_OPTIONS:MOSSALSA} X @${REINPLACE_CMD} \ X -e 's|linux-alsa|freebsd-oss linux-oss linux-alsa freebsd-alsa|g' \ X -e 's|linux-jack|linux-jack freebsd-jack|g' \ X ${BUILD_WRKSRC}/${MAKEFILE} ${BUILD_WRKSRC}/core/${MAKEFILE} X.elif ${PORT_OPTIONS:MOSSJACK} X @${REINPLACE_CMD} \ X -e 's|linux-alsa|linux-alsa freebsd-alsa|g' \ X -e 's|linux-jack|freebsd-oss linux-oss linux-jack freebsd-jack|g' \ X ${BUILD_WRKSRC}/${MAKEFILE} ${BUILD_WRKSRC}/core/${MAKEFILE} X.else X @${REINPLACE_CMD} \ X -e 's|linux-alsa|linux-alsa freebsd-alsa|g' \ X -e 's|linux-jack|linux-jack freebsd-jack|g' \ X ${BUILD_WRKSRC}/${MAKEFILE} ${BUILD_WRKSRC}/core/${MAKEFILE} X.endif X.if ${PORT_OPTIONS:MOSSV4} && ${PORT_OPTIONS:MOSSALSA} X @${REINPLACE_CMD} -e 's|__PLATFORM_LINUX__|__PLATFORM_FREEBSD__|' \ X -e 's|__LINUX_ALSA__|__FREEBSD_OSS__|' \ X -e 's| -lstdc++| -lc++ -lc|' \ X -e 's| -ldl||' \ X -e 's|-O3||' \ X ${BUILD_WRKSRC}/core/makefile.x/makefile.alsa X.elif ${PORT_OPTIONS:MOSSV4} && ${PORT_OPTIONS:MOSSJACK} X @${REINPLACE_CMD} -e 's|__PLATFORM_LINUX__|__PLATFORM_FREEBSD__|' \ X -e 's|__UNIX_JACK__|__FREEBSD_OSS__|' \ X -e 's| -lstdc++| -lc++ -lc|' \ X -e 's| -ldl||' \ X -e 's|-O3||' \ X -e 's| -lasound||' \ X ${BUILD_WRKSRC}/core/makefile.x/makefile.jack X.elif ${PORT_OPTIONS:MALSA} X @${REINPLACE_CMD} -e 's|__PLATFORM_LINUX__|__PLATFORM_FREEBSD__|' \ X -e 's|__LINUX_ALSA__|__FREEBSD_ALSA__|' \ X -e 's| -lstdc++| -lc++ -lc|' \ X -e 's| -ldl||' \ X -e 's|-O3||' \ X ${BUILD_WRKSRC}/core/makefile.x/makefile.alsa X.else X @${REINPLACE_CMD} -e 's|__PLATFORM_LINUX__|__PLATFORM_FREEBSD__|' \ X -e 's| -lstdc++| -lc++ -lc|' \ X -e 's| -ldl||' \ X -e 's|-O3||' \ X -e 's| -lasound||' \ X ${BUILD_WRKSRC}/core/makefile.x/makefile.jack \ X ${BUILD_WRKSRC}/core/makefile.x/makefile.pulse X.endif X @${REINPLACE_CMD} 's/__PLATFORM_LINUX__/__PLATFORM_FREEBSD__/g' \ X ${BUILD_WRKSRC}/core/chuck_io.cpp \ X ${BUILD_WRKSRC}/core/lo/lo_endian.h X @${REINPLACE_CMD} 's/defined(__PLATFORM_LINUX__)/defined(__PLATFORM_LINUX__) || defined(__PLATFORM_FREEBSD__)/g' \ X ${BUILD_WRKSRC}/core/chuck_shell.cpp \ X ${BUILD_WRKSRC}/core/lo/config.h \ X ${BUILD_WRKSRC}/core/ugen_stk.h \ X ${BUILD_WRKSRC}/core/util_sndfile.h \ X ${BUILD_WRKSRC}/core/util_thread.cpp \ X ${BUILD_WRKSRC}/core/util_thread.h \ X ${BUILD_WRKSRC}/host/chuck_audio.h X @${REINPLACE_CMD} 's/defined(__LINUX_OSS__)/defined(__FREEBSD_OSS__) || defined(__LINUX_OSS__)/g' \ X ${BUILD_WRKSRC}/core/chuck_def.h X @${REINPLACE_CMD} 's/\#ifdef __LINUX_ALSA__/\#if defined(__LINUX_ALSA__) || defined(__FREEBSD_ALSA__)/g' \ X ${BUILD_WRKSRC}/core/util_sndfile.h X @${REINPLACE_CMD} 's|\#define TYPEOF_SF_COUNT_T loff_t|\#define TYPEOF_SF_COUNT_T off_t|g' \ X ${BUILD_WRKSRC}/core/util_sndfile.h X Xpost-build: X.if ${PORT_OPTIONS:MDOCS} X @cd ${WRKSRC}/doc/manual && ${MAKE} pdf X.endif X Xpost-install: X.if ${PORT_OPTIONS:MDOCS} X @${MKDIR} ${STAGEDIR}${DOCSDIR} X @${CP} -p ${WRKSRC}/doc/manual/ChucK_manual.pdf ${STAGEDIR}${DOCSDIR}/ X.endif X.if ${PORT_OPTIONS:MEXAMPLES} X @${MKDIR} ${STAGEDIR}${EXAMPLESDIR} X @${CP} -pR ${WRKSRC}/doc/manual/examples/* ${STAGEDIR}${EXAMPLESDIR}/ X @${CP} -pR ${WRKSRC}/examples/* ${STAGEDIR}${EXAMPLESDIR}/ X.endif X @${MV} ${STAGEDIR}/${PORTNAME} ${STAGEDIR}${PREFIX}/bin/${PORTNAME} X X.include 07ac3234b9409852984528b28d19981a echo x - audio/chuck/pkg-descr sed 's/^X//' >audio/chuck/pkg-descr << '30c256e484ecc839de0a63355dcf35e1' XWelcome to ChucK! X Xwhat is it? : Chuck is a programming language for real-time sound synthesis and Xmusic creation. It is open-source and freely available on macOS, Windows, and XLinux. Chuck presents a unique time-based, concurrent programming model that's Xprecise and expressive (we call this strongly-timed), with dynamic control Xrates and the ability to add and modify code on-the-fly. In addition, ChucK Xsupports MIDI, OpenSoundControl, HID device, and multi-channel audio. It's fun Xand easy to learn, and offers composers, researchers, and performers a powerful Xprogramming tool for building and experimenting with complex audio Xsynthesis/analysis programs, and real-time interactive music. X XFor more information, including documentation, research publications, and Xcommunity resources, please check out the ChucK website. X XWWW: http://chuck.stanford.edu 30c256e484ecc839de0a63355dcf35e1 echo x - audio/chuck/distinfo sed 's/^X//' >audio/chuck/distinfo << '40f0f059e4cf66800818700bfea7b624' XTIMESTAMP = 1634804306 XSHA256 (ccrma-chuck-20210815-461d11f_GH0.tar.gz) = ecb5d42f99c206cb70681e3599036e6c61ce9b5c200289e524e2bb57b2401139 XSIZE (ccrma-chuck-20210815-461d11f_GH0.tar.gz) = 18016760 40f0f059e4cf66800818700bfea7b624 echo c - audio/chuck/files mkdir -p audio/chuck/files > /dev/null 2>&1 echo x - audio/chuck/files/patch-src_core_util__console.cpp sed 's/^X//' >audio/chuck/files/patch-src_core_util__console.cpp << '77ad14670023a10ac9b49b210e464037' X--- src/core/util_console.cpp.orig 2021-08-16 04:08:51 UTC X+++ src/core/util_console.cpp X@@ -95,6 +95,9 @@ void io_addhistory( const char * addme ) X #ifdef __PLATFORM_MACOSX__ X #include X static struct termios g_save; X+#elif defined(__PLATFORM_FREEBSD__) X+ #include X+ static struct termios g_save; X #else X #include X static struct termio g_save; X@@ -102,8 +105,8 @@ void io_addhistory( const char * addme ) X X #include X #include X-#else X- #include X+//#else X+// #include X #endif X X X@@ -122,6 +125,9 @@ t_CKBOOL kb_initscr() X #ifdef __PLATFORM_MACOSX__ X struct termios term; X if( ioctl( 0, TIOCGETA, &term ) == -1 ) X+#elif defined(__PLATFORM_FREEBSD__) X+ struct termios term; X+ if( ioctl( 0, TIOCGETA, &term ) == -1 ) X #else X struct termio term; X if( ioctl( 0, TCGETA, &term ) == -1 ) X@@ -144,6 +150,8 @@ t_CKBOOL kb_initscr() X X #ifdef __PLATFORM_MACOSX__ X ioctl( 0, TIOCSETA, &term ); X+#elif defined(__PLATFORM_FREEBSD__) X+ ioctl( 0, TIOCSETA, &term ); X #else X ioctl( 0, TCSETA, &term ); X #endif X@@ -162,6 +170,8 @@ void kb_endwin() X X #ifndef __PLATFORM_WIN32__ X #ifdef __PLATFORM_MACOSX__ X+ ioctl( 0, TIOCSETA, &g_save ); X+#elif defined(__PLATFORM_FREEBSD__) X ioctl( 0, TIOCSETA, &g_save ); X #else X ioctl( 0, TCSETA, &g_save ); 77ad14670023a10ac9b49b210e464037 echo x - audio/chuck/files/patch-src_host_RtAudio_RtAudio.cpp sed 's/^X//' >audio/chuck/files/patch-src_host_RtAudio_RtAudio.cpp << 'dc799f32a7b3259b6a2791342511777a' X--- src/host/RtAudio/RtAudio.cpp.orig 2021-10-23 23:48:40 UTC X+++ src/host/RtAudio/RtAudio.cpp X@@ -58,7 +58,7 @@ const unsigned int RtApi::SAMPLE_RATES[] = { X #define MUTEX_DESTROY(A) DeleteCriticalSection(A) X #define MUTEX_LOCK(A) EnterCriticalSection(A) X #define MUTEX_UNLOCK(A) LeaveCriticalSection(A) X-#elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__) X+#elif defined(__LINUX_ALSA__) || defined(__FREEBSD_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__FREEBSD_OSS__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__) X // pthread API X #define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL) X #define MUTEX_DESTROY(A) pthread_mutex_destroy(A) X@@ -87,9 +87,15 @@ void RtAudio :: getCompiledApi( std::vector X #include X@@ -6529,7 +6543,7 @@ static void *alsaCallbackHandler( void *ptr ) X pthread_exit( NULL ); X } X X-//******************** End of __LINUX_ALSA__ *********************// X+//******************** End of __LINUX_ALSA__ || __FREEBSD_ALSA__ *********************// X #endif X X #if defined(__LINUX_PULSE__) X@@ -7006,13 +7020,17 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, X //******************** End of __LINUX_PULSE__ *********************// X #endif X X-#if defined(__LINUX_OSS__) X+#if defined(__FREEBSD_OSS__) ||defined(__LINUX_OSS__) X X #include X #include X #include X #include X+#if defined(__FREEBSD_OSS__) X+#include X+#elif defined(__LINUX_OSS__) X #include "soundcard.h" X+#endif X #include X #include X X@@ -7444,7 +7462,7 @@ bool RtApiOss :: probeDeviceOpen( unsigned int device, X } X X // Verify the sample rate setup worked. X- if ( abs( srate - sampleRate ) > 100 ) { X+ if ( abs(int(srate - sampleRate)) > 100 ) { X close( fd ); X errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support sample rate (" << sampleRate << ")."; X errorText_ = errorStream_.str(); dc799f32a7b3259b6a2791342511777a echo x - audio/chuck/files/patch-src_host_RtAudio_RtAudio.h sed 's/^X//' >audio/chuck/files/patch-src_host_RtAudio_RtAudio.h << '8288a2fe8ff00c8098c7fc2162f3b83f' X--- src/host/RtAudio/RtAudio.h.orig 2021-08-16 04:08:51 UTC X+++ src/host/RtAudio/RtAudio.h X@@ -222,7 +222,9 @@ class RtAudio X enum Api { X UNSPECIFIED, /*!< Search for a working compiled API. */ X LINUX_ALSA, /*!< The Advanced Linux Sound Architecture API. */ X+ FREEBSD_ALSA, /*!< FreeBSD Advanced Linux Sound Architecture API. */ X LINUX_PULSE, /*!< The Linux PulseAudio API. */ X+ FREEBSD_OSS, /*!< The FreeBSD Open Sound System API. */ X LINUX_OSS, /*!< The Linux Open Sound System API. */ X UNIX_JACK, /*!< The Jack Low-Latency Audio Server API. */ X MACOSX_CORE, /*!< Macintosh OS-X Core Audio API. */ X@@ -532,7 +534,7 @@ class RtAudio X typedef unsigned long ThreadHandle; X typedef CRITICAL_SECTION StreamMutex; X X-#elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__) X+#elif defined(__LINUX_ALSA__) || defined (__FREEBSD_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__FREEBSD_OSS__) || defined(__FREEBSD_OSS__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__) X // Using pthread library for various flavors of unix. X #include X X@@ -935,7 +937,7 @@ class RtApiDs: public RtApi (public) X X #endif X X-#if defined(__LINUX_ALSA__) X+#if defined(__LINUX_ALSA__) || defined(__FREEBSD_ALSA__) X X class RtApiAlsa: public RtApi X { X@@ -943,7 +945,11 @@ class RtApiAlsa: public RtApi (public) X X RtApiAlsa(); X ~RtApiAlsa(); X+#if defined(__LINUX_ALSA__) X RtAudio::Api getCurrentApi() { return RtAudio::LINUX_ALSA; } X+#elif defined(__FREEBSD_ALSA__) X+ RtAudio::Api getCurrentApi() { return RtAudio::FREEBSD_ALSA; } X+#endif X unsigned int getDeviceCount( void ); X RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); X void closeStream( void ); X@@ -1001,7 +1007,7 @@ class RtApiPulse: public RtApi (public) X X #endif X X-#if defined(__LINUX_OSS__) X+#if defined(__FREEBSD_OSS__) || defined(__LINUX_OSS__) X X class RtApiOss: public RtApi X { X@@ -1009,7 +1015,11 @@ class RtApiOss: public RtApi (public) X X RtApiOss(); X ~RtApiOss(); X+#if defined(__FREEBSD_OSS__) X+ RtAudio::Api getCurrentApi() { return RtAudio::FREEBSD_OSS; } X+#elif defined(__LINUX_OSS__) X RtAudio::Api getCurrentApi() { return RtAudio::LINUX_OSS; } X+#endif X unsigned int getDeviceCount( void ); X RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); X void closeStream( void ); 8288a2fe8ff00c8098c7fc2162f3b83f echo x - audio/chuck/files/patch-src_core_util__string.cpp sed 's/^X//' >audio/chuck/files/patch-src_core_util__string.cpp << '2f47a84654c52ee85377d84a215c418d' X--- src/core/util_string.cpp.orig 2021-08-16 04:08:51 UTC X+++ src/core/util_string.cpp X@@ -41,6 +41,10 @@ X #include X #endif // __PLATFORM_LINUX__ X X+#ifdef __PLATFORM_FREEBSD__ X+#include X+#endif X+ X #include X using namespace std; X 2f47a84654c52ee85377d84a215c418d echo x - audio/chuck/files/patch-src_core_util__hid.cpp sed 's/^X//' >audio/chuck/files/patch-src_core_util__hid.cpp << '93331269820b38180291fead72f4ac71' X--- src/core/util_hid.cpp.orig 2021-08-16 04:08:51 UTC X+++ src/core/util_hid.cpp X@@ -6308,7 +6308,7 @@ int Keyboard_close( int js ) X X X X-#elif defined(__PLATFORM_LINUX__) X+#elif defined(__PLATFORM_LINUX__) || defined(__PLATFORM_FREEBSD__) X /***************************************************************************** X Linux general HID support X *****************************************************************************/ X@@ -6318,7 +6318,9 @@ Linux general HID support X #include X #include X #include X+#ifdef __PLATFORM_LINUX__ X #include X+#endif X #include X #include X #include X@@ -6326,6 +6328,9 @@ Linux general HID support X #include X #include X #include X+#ifdef __PLATFORM_FREEBSD__ X+#include X+#endif X X #define CK_HID_DIR ("/dev/input") X #define CK_HID_MOUSEFILE ("mouse%d") 93331269820b38180291fead72f4ac71 echo x - audio/chuck/files/patch-src_host_chuck__main.cpp sed 's/^X//' >audio/chuck/files/patch-src_host_chuck__main.cpp << '0f24a606c8a601d8820fdc8950eccb01' X--- src/host/chuck_main.cpp.orig 2021-08-16 04:08:51 UTC X+++ src/host/chuck_main.cpp X@@ -170,6 +170,10 @@ static void version() X platform = "microsoft win32 + cygwin"; X #elif defined(__LINUX_ALSA__) X platform = "linux (alsa)"; X+#elif defined(__FREEBSD_ALSA__) X+ platform = "freebsd (alsa)"; X+#elif defined(__FREEBSD_OSS__) X+ platform = "freebsd (oss)"; X #elif defined(__LINUX_OSS__) X platform = "linux (oss)"; X #elif defined(__LINUX_JACK__) || defined(__UNIX_JACK__) 0f24a606c8a601d8820fdc8950eccb01 echo x - audio/chuck/files/patch-src_core_rtmidi.h sed 's/^X//' >audio/chuck/files/patch-src_core_rtmidi.h << 'beb363e02ee68f4652545d1559ab90f3' X--- src/core/rtmidi.h.orig 2021-08-16 04:08:51 UTC X+++ src/core/rtmidi.h X@@ -5,10 +5,11 @@ X This class implements some common functionality for the realtime X MIDI input/output subclasses RtMidiIn and RtMidiOut. X X- RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/ X+ RtMidi GitHub site: https://github.com/thestk/rtmidi X+ RtMidi WWW site: http://www.music.mcgill.ca/~gary/rtmidi/ X X RtMidi: realtime MIDI i/o C++ classes X- Copyright (c) 2003-2005 Gary P. Scavone X+ Copyright (c) 2003-2019 Gary P. Scavone X X Permission is hereby granted, free of charge, to any person X obtaining a copy of this software and associated documentation files X@@ -22,8 +23,9 @@ X included in all copies or substantial portions of the Software. X X Any person wishing to distribute modifications to the Software is X- requested to send the modifications to the original developer so that X- they can be incorporated into the canonical version. X+ asked to send the modifications to the original developer so that X+ they can be incorporated into the canonical version. This is, X+ however, not a binding provision of this license. X X THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, X EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF X@@ -35,86 +37,152 @@ X */ X /**********************************************************************/ X X-/************************************************************************/ X-/*! \class RtMidiError X- \brief Exception handling class for RtAudio & RtMidi. X- X- The RtMidiError class is quite simple but it does allow errors to be X- "caught" by RtMidiError::Type. See the RtAudio and RtMidi X- documentation to know which methods can throw an RtMidiError. X- X+/*! X+ \file RtMidi.h X */ X-/************************************************************************/ X X-#ifndef RT_MIDI_ERROR_H // REFACTOR-2017: new abomination! X-#define RT_MIDI_ERROR_H X+#ifndef RTMIDI_H X+#define RTMIDI_H X X+#if defined _WIN32 || defined __CYGWIN__ X+ #if defined(RTMIDI_EXPORT) X+ #define RTMIDI_DLL_PUBLIC __declspec(dllexport) X+ #else X+ #define RTMIDI_DLL_PUBLIC X+ #endif X+#else X+ #if __GNUC__ >= 4 X+ #define RTMIDI_DLL_PUBLIC __attribute__( (visibility( "default" )) ) X+ #else X+ #define RTMIDI_DLL_PUBLIC X+ #endif X+#endif X+ X+#define RTMIDI_VERSION "4.0.0" X+ X #include X #include X #include X+#include X X-class RtMidiError : public std::exception X+/************************************************************************/ X+/*! \class RtMidiError X+ \brief Exception handling class for RtMidi. X+ X+ The RtMidiError class is quite simple but it does allow errors to be X+ "caught" by RtMidiError::Type. See the RtMidi documentation to know X+ which methods can throw an RtMidiError. X+*/ X+/************************************************************************/ X+ X+class RTMIDI_DLL_PUBLIC RtMidiError : public std::exception X { X-public: X- //! Defined RtMidiError types. X- enum Type { X- WARNING, /*!< A non-critical error. */ X- DEBUG_WARNING, /*!< A non-critical error which might be useful for debugging. */ X- UNSPECIFIED, /*!< The default, unspecified error type. */ X- NO_DEVICES_FOUND, /*!< No devices found on system. */ X- INVALID_DEVICE, /*!< An invalid device ID was specified. */ X- MEMORY_ERROR, /*!< An error occured during memory allocation. */ X- INVALID_PARAMETER, /*!< An invalid parameter was specified to a function. */ X- INVALID_USE, /*!< The function was called incorrectly. */ X- DRIVER_ERROR, /*!< A system driver error occured. */ X- SYSTEM_ERROR, /*!< A system error occured. */ X- THREAD_ERROR /*!< A thread error occured. */ X- }; X- X- //! The constructor. X- RtMidiError( const std::string& message, Type type = RtMidiError::UNSPECIFIED ) X- throw() : message_(message), type_(type) {} X- X- //! The destructor. X- virtual ~RtMidiError( void ) throw() {} X- X- //! Prints thrown error message to stderr. X- virtual void printMessage( void ) const throw() { std::cerr << '\n' << message_ << "\n\n"; } X+ public: X+ //! Defined RtMidiError types. X+ enum Type { X+ WARNING, /*!< A non-critical error. */ X+ DEBUG_WARNING, /*!< A non-critical error which might be useful for debugging. */ X+ UNSPECIFIED, /*!< The default, unspecified error type. */ X+ NO_DEVICES_FOUND, /*!< No devices found on system. */ X+ INVALID_DEVICE, /*!< An invalid device ID was specified. */ X+ MEMORY_ERROR, /*!< An error occured during memory allocation. */ X+ INVALID_PARAMETER, /*!< An invalid parameter was specified to a function. */ X+ INVALID_USE, /*!< The function was called incorrectly. */ X+ DRIVER_ERROR, /*!< A system driver error occured. */ X+ SYSTEM_ERROR, /*!< A system error occured. */ X+ THREAD_ERROR /*!< A thread error occured. */ X+ }; X X- //! Returns the thrown error message type. X- virtual const Type& getType(void) const throw() { return type_; } X- X- //! Returns the thrown error message string. X- virtual const std::string& getMessage(void) const throw() { return message_; } X- X- //! Returns the thrown error message as a c-style string. X- virtual const char* what( void ) const throw() { return message_.c_str(); } X- X-protected: X- std::string message_; X- Type type_; X-}; X+ //! The constructor. X+ RtMidiError( const std::string& message, Type type = RtMidiError::UNSPECIFIED ) throw() X+ : message_(message), type_(type) {} X X-#endif X+ //! The destructor. X+ virtual ~RtMidiError( void ) throw() {} X X+ //! Prints thrown error message to stderr. X+ virtual void printMessage( void ) const throw() { std::cerr << '\n' << message_ << "\n\n"; } X X+ //! Returns the thrown error message type. X+ virtual const Type& getType( void ) const throw() { return type_; } X X-// RtMidi: Version 1.0.4, 14 October 2005 X+ //! Returns the thrown error message string. X+ virtual const std::string& getMessage( void ) const throw() { return message_; } X X-#ifndef RTMIDI_H X-#define RTMIDI_H X+ //! Returns the thrown error message as a c-style string. X+ virtual const char* what( void ) const throw() { return message_.c_str(); } X X-#include X+ protected: X+ std::string message_; X+ Type type_; X+}; X X-class RtMidi X+//! RtMidi error callback function prototype. X+/*! X+ \param type Type of error. X+ \param errorText Error description. X+ X+ Note that class behaviour is undefined after a critical error (not X+ a warning) is reported. X+ */ X+typedef void (*RtMidiErrorCallback)( RtMidiError::Type type, const std::string &errorText, void *userData ); X+ X+class MidiApi; X+ X+class RTMIDI_DLL_PUBLIC RtMidi X { X public: X+ //! MIDI API specifier arguments. X+ enum Api { X+ UNSPECIFIED, /*!< Search for a working compiled API. */ X+ MACOSX_CORE, /*!< Macintosh OS-X CoreMIDI API. */ X+ LINUX_ALSA, /*!< The Advanced Linux Sound Architecture API. */ X+ FREEBSD_ALSA, /*!< FreeBSD Advanced Linux Sound Architecture API. */ X+ UNIX_JACK, /*!< The JACK Low-Latency MIDI Server API. */ X+ WINDOWS_MM, /*!< The Microsoft Multimedia MIDI API. */ X+ RTMIDI_DUMMY, /*!< A compilable but non-functional API. */ X+ NUM_APIS /*!< Number of values in this enum. */ X+ }; X X+ //! A static function to determine the current RtMidi version. X+ static std::string getVersion( void ) throw(); X+ X+ //! A static function to determine the available compiled MIDI APIs. X+ /*! X+ The values returned in the std::vector can be compared against X+ the enumerated list values. Note that there can be more than one X+ API compiled for certain operating systems. X+ */ X+ static void getCompiledApi( std::vector &apis ) throw(); X+ X+ //! Return the name of a specified compiled MIDI API. X+ /*! X+ This obtains a short lower-case name used for identification purposes. X+ This value is guaranteed to remain identical across library versions. X+ If the API is unknown, this function will return the empty string. X+ */ X+ static std::string getApiName( RtMidi::Api api ); X+ X+ //! Return the display name of a specified compiled MIDI API. X+ /*! X+ This obtains a long name used for display purposes. X+ If the API is unknown, this function will return the empty string. X+ */ X+ static std::string getApiDisplayName( RtMidi::Api api ); X+ X+ //! Return the compiled MIDI API having the given name. X+ /*! X+ A case insensitive comparison will check the specified name X+ against the list of compiled APIs, and return the one which X+ matches. On failure, the function returns UNSPECIFIED. X+ */ X+ static RtMidi::Api getCompiledApiByName( const std::string &name ); X+ X //! Pure virtual openPort() function. X- virtual void openPort( unsigned int portNumber = 0 ) = 0; X+ virtual void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi" ) ) = 0; X X //! Pure virtual openVirtualPort() function. X- virtual void openVirtualPort() = 0; X+ virtual void openVirtualPort( const std::string &portName = std::string( "RtMidi" ) ) = 0; X X //! Pure virtual getPortCount() function. X virtual unsigned int getPortCount() = 0; X@@ -125,24 +193,29 @@ class RtMidi X //! Pure virtual closePort() function. X virtual void closePort( void ) = 0; X X- protected: X+ void setClientName( const std::string &clientName ); X+ void setPortName( const std::string &portName ); X X- RtMidi(); X- virtual ~RtMidi() {}; X+ //! Returns true if a port is open and false if not. X+ /*! X+ Note that this only applies to connections made with the openPort() X+ function, not to virtual ports. X+ */ X+ virtual bool isPortOpen( void ) const = 0; X X- // A basic error reporting function for internal use in the RtMidi X- // subclasses. The behavior of this function can be modified to X- // suit specific needs. X- void error( RtMidiError::Type type ); X+ //! Set an error callback function to be invoked when an error has occured. X+ /*! X+ The callback function will be called whenever an error has occured. It is best X+ to set the error callback function before opening a port. X+ */ X+ virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ) = 0; X X- void *apiData_; X- bool connected_; X- std::string errorString_; X+ protected: X+ RtMidi(); X+ virtual ~RtMidi(); X+ MidiApi *rtapi_; X }; X X- X- X- X /**********************************************************************/ X /*! \class RtMidiIn X \brief A realtime MIDI input class. X@@ -153,139 +226,154 @@ class RtMidi X retrieval using the getMessage() function or immediately passed to X a user-specified callback function. Create multiple instances of X this class to connect to more than one MIDI device at the same X- time. With the OS-X and Linux ALSA MIDI APIs, it is also possible X- to open a virtual input port to which other MIDI software clients X- can connect. X- X- by Gary P. Scavone, 2003-2004. X+ time. With the OS-X, Linux ALSA, and JACK MIDI APIs, it is also X+ possible to open a virtual input port to which other MIDI software X+ clients can connect. X */ X /**********************************************************************/ X X-#include X-#include X+// **************************************************************** // X+// X+// RtMidiIn and RtMidiOut class declarations. X+// X+// RtMidiIn / RtMidiOut are "controllers" used to select an available X+// MIDI input or output interface. They present common APIs for the X+// user to call but all functionality is implemented by the classes X+// MidiInApi, MidiOutApi and their subclasses. RtMidiIn and RtMidiOut X+// each create an instance of a MidiInApi or MidiOutApi subclass based X+// on the user's API choice. If no choice is made, they attempt to X+// make a "logical" API selection. X+// X+// **************************************************************** // X X-class RtMidiIn : public RtMidi X+class RTMIDI_DLL_PUBLIC RtMidiIn : public RtMidi X { X public: X X //! User callback function type definition. X- typedef void (*RtMidiCallback)( double timeStamp, std::vector *message, void *userData); X+ typedef void (*RtMidiCallback)( double timeStamp, std::vector *message, void *userData ); X X- //! Default constructor. X+ //! Default constructor that allows an optional api, client name and queue size. X /*! X- An exception will be thrown if a MIDI system initialization error occurs. X+ An exception will be thrown if a MIDI system initialization X+ error occurs. The queue size defines the maximum number of X+ messages that can be held in the MIDI queue (when not using a X+ callback function). If the queue size limit is reached, X+ incoming messages will be ignored. X+ X+ If no API argument is specified and multiple API support has been X+ compiled, the default order of use is ALSA, JACK (Linux) and CORE, X+ JACK (OS-X). X+ X+ \param api An optional API id can be specified. X+ \param clientName An optional client name can be specified. This X+ will be used to group the ports that are created X+ by the application. X+ \param queueSizeLimit An optional size of the MIDI input queue can be specified. X */ X- RtMidiIn(); X+ RtMidiIn( RtMidi::Api api=UNSPECIFIED, X+ const std::string& clientName = "RtMidi Input Client", X+ unsigned int queueSizeLimit = 100 ); X X //! If a MIDI connection is still open, it will be closed by the destructor. X- ~RtMidiIn(); X+ ~RtMidiIn ( void ) throw(); X X- //! Open a MIDI input connection. X+ //! Returns the MIDI API specifier for the current instance of RtMidiIn. X+ RtMidi::Api getCurrentApi( void ) throw(); X+ X+ //! Open a MIDI input connection given by enumeration number. X /*! X- An optional port number greater than 0 can be specified. X- Otherwise, the default or first port found is opened. X+ \param portNumber An optional port number greater than 0 can be specified. X+ Otherwise, the default or first port found is opened. X+ \param portName An optional name for the application port that is used to connect to portId can be specified. X */ X- void openPort( unsigned int portNumber = 0 ); X+ void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi Input" ) ); X X- //! Create a virtual input port to allow software connections (OS X and ALSA only). X+ //! Create a virtual input port, with optional name, to allow software connections (OS X, JACK and ALSA only). X /*! X- This function creates a virtual MIDI input port to which other X- software applications can connect. This type of functionality X- is currently only supported by the Macintosh OS-X and Linux ALSA X- APIs (the function does nothing for the other APIs). X+ This function creates a virtual MIDI input port to which other X+ software applications can connect. This type of functionality X+ is currently only supported by the Macintosh OS-X, any JACK, X+ and Linux ALSA APIs (the function returns an error for the other APIs). X+ X+ \param portName An optional name for the application port that is X+ used to connect to portId can be specified. X */ X- void openVirtualPort(); X+ void openVirtualPort( const std::string &portName = std::string( "RtMidi Input" ) ); X X //! Set a callback function to be invoked for incoming MIDI messages. X /*! X- The callback function will be called whenever an incoming MIDI X- message is received. While not absolutely necessary, it is best X- to set the callback function before opening a MIDI port to avoid X- leaving some messages in the queue. X+ The callback function will be called whenever an incoming MIDI X+ message is received. While not absolutely necessary, it is best X+ to set the callback function before opening a MIDI port to avoid X+ leaving some messages in the queue. X+ X+ \param callback A callback function must be given. X+ \param userData Optionally, a pointer to additional data can be X+ passed to the callback function whenever it is called. X */ X void setCallback( RtMidiCallback callback, void *userData = 0 ); X X //! Cancel use of the current callback function (if one exists). X /*! X- Subsequent incoming MIDI messages will be written to the queue X- and can be retrieved with the \e getMessage function. X+ Subsequent incoming MIDI messages will be written to the queue X+ and can be retrieved with the \e getMessage function. X */ X void cancelCallback(); X X //! Close an open MIDI connection (if one exists). X void closePort( void ); X X+ //! Returns true if a port is open and false if not. X+ /*! X+ Note that this only applies to connections made with the openPort() X+ function, not to virtual ports. X+ */ X+ virtual bool isPortOpen() const; X+ X //! Return the number of available MIDI input ports. X+ /*! X+ \return This function returns the number of MIDI ports of the selected API. X+ */ X unsigned int getPortCount(); X X //! Return a string identifier for the specified MIDI input port number. X /*! X- An exception is thrown if an invalid port specifier is provided. X+ \return The name of the port with the given Id is returned. X+ \retval An empty string is returned if an invalid port specifier X+ is provided. User code should assume a UTF-8 encoding. X */ X std::string getPortName( unsigned int portNumber = 0 ); X X- //! Set the maximum number of MIDI messages to be saved in the queue. X- /*! X- If the queue size limit is reached, incoming messages will be X- ignored. The default limit is 1024. X- */ X- void setQueueSizeLimit( unsigned int queueSize ); X- X //! Specify whether certain MIDI message types should be queued or ignored during input. X /*! X- By default, active sensing messages are ignored X- during message input because of their relative high data rates. X- MIDI sysex messages are ignored by default as well. Variable X- values of "true" imply that the respective message type will be X- ignored. X+ By default, MIDI timing and active sensing messages are ignored X+ during message input because of their relative high data rates. X+ MIDI sysex messages are ignored by default as well. Variable X+ values of "true" imply that the respective message type will be X+ ignored. X */ X- void ignoreTypes( bool midiSysex = true, bool midiTime = false, bool midiSense = true ); X+ void ignoreTypes( bool midiSysex = true, bool midiTime = true, bool midiSense = true ); X X //! 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. X /*! X- This function returns immediately whether a new message is X- available or not. A valid message is indicated by a non-zero X- vector size. An exception is thrown if an error occurs during X- message retrieval or an input connection was not previously X- established. X+ This function returns immediately whether a new message is X+ available or not. A valid message is indicated by a non-zero X+ vector size. An exception is thrown if an error occurs during X+ message retrieval or an input connection was not previously X+ established. X */ X double getMessage( std::vector *message ); X X- // A MIDI structure used internally by the class to store incoming X- // messages. Each message represents one and only one MIDI message. X- struct MidiMessage { X- std::vector bytes; X- double timeStamp; X+ //! Set an error callback function to be invoked when an error has occured. X+ /*! X+ The callback function will be called whenever an error has occured. It is best X+ to set the error callback function before opening a port. X+ */ X+ virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ); X X- // Default constructor. X- MidiMessage() X- :bytes(3), timeStamp(0.0) {} X- }; X- X- // The RtMidiInData structure is used to pass private class data to X- // the MIDI input handling function or thread. X- struct RtMidiInData { X- std::queue queue; X- unsigned int queueLimit; X- unsigned char ignoreFlags; X- bool doInput; X- bool firstMessage; X- void *apiData; X- bool usingCallback; X- void *userCallback; X- void *userData; X- X- // Default constructor. X- RtMidiInData() X- : queueLimit(1024), ignoreFlags(5), doInput(false), firstMessage(true), X- apiData(0), usingCallback(false), userCallback(0), userData(0) {} X- }; X- X- private: X- X- void initialize( void ); X- RtMidiInData inputData_; X- X+ protected: X+ void openMidiApi( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit ); X }; X X /**********************************************************************/ X@@ -296,25 +384,32 @@ class RtMidiIn : public RtMidi X output. It allows one to probe available MIDI output ports, to X connect to one such port, and to send MIDI bytes immediately over X the connection. Create multiple instances of this class to X- connect to more than one MIDI device at the same time. X- X- by Gary P. Scavone, 2003-2004. X+ connect to more than one MIDI device at the same time. With the X+ OS-X, Linux ALSA and JACK MIDI APIs, it is also possible to open a X+ virtual port to which other MIDI software clients can connect. X */ X /**********************************************************************/ X X-class RtMidiOut : public RtMidi X+class RTMIDI_DLL_PUBLIC RtMidiOut : public RtMidi X { X public: X- X- //! Default constructor. X+ //! Default constructor that allows an optional client name. X /*! X- An exception will be thrown if a MIDI system initialization error occurs. X+ An exception will be thrown if a MIDI system initialization error occurs. X+ X+ If no API argument is specified and multiple API support has been X+ compiled, the default order of use is ALSA, JACK (Linux) and CORE, X+ JACK (OS-X). X */ X- RtMidiOut(); X+ RtMidiOut( RtMidi::Api api=UNSPECIFIED, X+ const std::string& clientName = "RtMidi Output Client" ); X X //! The destructor closes any open MIDI connections. X- ~RtMidiOut(); X+ ~RtMidiOut( void ) throw(); X X+ //! Returns the MIDI API specifier for the current instance of RtMidiOut. X+ RtMidi::Api getCurrentApi( void ) throw(); X+ X //! Open a MIDI output connection. X /*! X An optional port number greater than 0 can be specified. X@@ -322,41 +417,214 @@ class RtMidiOut : public RtMidi X exception is thrown if an error occurs while attempting to make X the port connection. X */ X- void openPort( unsigned int portNumber = 0 ); X+ void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi Output" ) ); X X //! Close an open MIDI connection (if one exists). X- void closePort(); X+ void closePort( void ); X X- //! Create a virtual output port to allow software connections (OS X and ALSA only). X+ //! Returns true if a port is open and false if not. X /*! X+ Note that this only applies to connections made with the openPort() X+ function, not to virtual ports. X+ */ X+ virtual bool isPortOpen() const; X+ X+ //! Create a virtual output port, with optional name, to allow software connections (OS X, JACK and ALSA only). X+ /*! X This function creates a virtual MIDI output port to which other X software applications can connect. This type of functionality X- is currently only supported by the Macintosh OS-X and Linux ALSA X- APIs (the function does nothing with the other APIs). An X- exception is thrown if an error occurs while attempting to create X- the virtual port. X+ is currently only supported by the Macintosh OS-X, Linux ALSA X+ and JACK APIs (the function does nothing with the other APIs). X+ An exception is thrown if an error occurs while attempting to X+ create the virtual port. X */ X- void openVirtualPort(); X+ void openVirtualPort( const std::string &portName = std::string( "RtMidi Output" ) ); X X //! Return the number of available MIDI output ports. X- unsigned int getPortCount(); X+ unsigned int getPortCount( void ); X X //! Return a string identifier for the specified MIDI port type and number. X /*! X- An exception is thrown if an invalid port specifier is provided. X+ \return The name of the port with the given Id is returned. X+ \retval An empty string is returned if an invalid port specifier X+ is provided. User code should assume a UTF-8 encoding. X */ X- std::string getPortName( unsigned int portNumber ); X+ std::string getPortName( unsigned int portNumber = 0 ); X X //! Immediately send a single message out an open MIDI output port. X /*! X An exception is thrown if an error occurs during output or an X output connection was not previously established. X */ X- void sendMessage( std::vector *message ); X+ void sendMessage( const std::vector *message ); X X- private: X+ //! Immediately send a single message out an open MIDI output port. X+ /*! X+ An exception is thrown if an error occurs during output or an X+ output connection was not previously established. X X- void initialize( void ); X+ \param message A pointer to the MIDI message as raw bytes X+ \param size Length of the MIDI message in bytes X+ */ X+ void sendMessage( const unsigned char *message, size_t size ); X+ X+ //! Set an error callback function to be invoked when an error has occured. X+ /*! X+ The callback function will be called whenever an error has occured. It is best X+ to set the error callback function before opening a port. X+ */ X+ virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ); X+ X+ protected: X+ void openMidiApi( RtMidi::Api api, const std::string &clientName ); X }; X+ X+ X+// **************************************************************** // X+// X+// MidiInApi / MidiOutApi class declarations. X+// X+// Subclasses of MidiInApi and MidiOutApi contain all API- and X+// OS-specific code necessary to fully implement the RtMidi API. X+// X+// Note that MidiInApi and MidiOutApi are abstract base classes and X+// cannot be explicitly instantiated. RtMidiIn and RtMidiOut will X+// create instances of a MidiInApi or MidiOutApi subclass. X+// X+// **************************************************************** // X+ X+class RTMIDI_DLL_PUBLIC MidiApi X+{ X+ public: X+ X+ MidiApi(); X+ virtual ~MidiApi(); X+ virtual RtMidi::Api getCurrentApi( void ) = 0; X+ virtual void openPort( unsigned int portNumber, const std::string &portName ) = 0; X+ virtual void openVirtualPort( const std::string &portName ) = 0; X+ virtual void closePort( void ) = 0; X+ virtual void setClientName( const std::string &clientName ) = 0; X+ virtual void setPortName( const std::string &portName ) = 0; X+ X+ virtual unsigned int getPortCount( void ) = 0; X+ virtual std::string getPortName( unsigned int portNumber ) = 0; X+ X+ inline bool isPortOpen() const { return connected_; } X+ void setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ); X+ X+ //! A basic error reporting function for RtMidi classes. X+ void error( RtMidiError::Type type, std::string errorString ); X+ X+protected: X+ virtual void initialize( const std::string& clientName ) = 0; X+ X+ void *apiData_; X+ bool connected_; X+ std::string errorString_; X+ RtMidiErrorCallback errorCallback_; X+ bool firstErrorOccurred_; X+ void *errorCallbackUserData_; X+}; X+ X+class RTMIDI_DLL_PUBLIC MidiInApi : public MidiApi X+{ X+ public: X+ X+ MidiInApi( unsigned int queueSizeLimit ); X+ virtual ~MidiInApi( void ); X+ void setCallback( RtMidiIn::RtMidiCallback callback, void *userData ); X+ void cancelCallback( void ); X+ virtual void ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ); X+ double getMessage( std::vector *message ); X+ X+ // A MIDI structure used internally by the class to store incoming X+ // messages. Each message represents one and only one MIDI message. X+ struct MidiMessage { X+ std::vector bytes; X+ X+ //! Time in seconds elapsed since the previous message X+ double timeStamp; X+ X+ // Default constructor. X+ MidiMessage() X+ : bytes(0), timeStamp(0.0) {} X+ }; X+ X+ struct MidiQueue { X+ unsigned int front; X+ unsigned int back; X+ unsigned int ringSize; X+ MidiMessage *ring; X+ X+ // Default constructor. X+ MidiQueue() X+ : front(0), back(0), ringSize(0), ring(0) {} X+ bool push( const MidiMessage& ); X+ bool pop( std::vector*, double* ); X+ unsigned int size( unsigned int *back=0, unsigned int *front=0 ); X+ }; X+ X+ // The RtMidiInData structure is used to pass private class data to X+ // the MIDI input handling function or thread. X+ struct RtMidiInData { X+ MidiQueue queue; X+ MidiMessage message; X+ unsigned char ignoreFlags; X+ bool doInput; X+ bool firstMessage; X+ void *apiData; X+ bool usingCallback; X+ RtMidiIn::RtMidiCallback userCallback; X+ void *userData; X+ bool continueSysex; X+ X+ // Default constructor. X+ RtMidiInData() X+ : ignoreFlags(7), doInput(false), firstMessage(true), apiData(0), usingCallback(false), X+ userCallback(0), userData(0), continueSysex(false) {} X+ }; X+ X+ protected: X+ RtMidiInData inputData_; X+}; X+ X+class RTMIDI_DLL_PUBLIC MidiOutApi : public MidiApi X+{ X+ public: X+ X+ MidiOutApi( void ); X+ virtual ~MidiOutApi( void ); X+ virtual void sendMessage( const unsigned char *message, size_t size ) = 0; X+}; X+ X+// **************************************************************** // X+// X+// Inline RtMidiIn and RtMidiOut definitions. X+// X+// **************************************************************** // X+ X+inline RtMidi::Api RtMidiIn :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); } X+inline void RtMidiIn :: openPort( unsigned int portNumber, const std::string &portName ) { rtapi_->openPort( portNumber, portName ); } X+inline void RtMidiIn :: openVirtualPort( const std::string &portName ) { rtapi_->openVirtualPort( portName ); } X+inline void RtMidiIn :: closePort( void ) { rtapi_->closePort(); } X+inline bool RtMidiIn :: isPortOpen() const { return rtapi_->isPortOpen(); } X+inline void RtMidiIn :: setCallback( RtMidiCallback callback, void *userData ) { static_cast(rtapi_)->setCallback( callback, userData ); } X+inline void RtMidiIn :: cancelCallback( void ) { static_cast(rtapi_)->cancelCallback(); } X+inline unsigned int RtMidiIn :: getPortCount( void ) { return rtapi_->getPortCount(); } X+inline std::string RtMidiIn :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); } X+inline void RtMidiIn :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) { static_cast(rtapi_)->ignoreTypes( midiSysex, midiTime, midiSense ); } X+inline double RtMidiIn :: getMessage( std::vector *message ) { return static_cast(rtapi_)->getMessage( message ); } X+inline void RtMidiIn :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ) { rtapi_->setErrorCallback(errorCallback, userData); } X+ X+inline RtMidi::Api RtMidiOut :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); } X+inline void RtMidiOut :: openPort( unsigned int portNumber, const std::string &portName ) { rtapi_->openPort( portNumber, portName ); } X+inline void RtMidiOut :: openVirtualPort( const std::string &portName ) { rtapi_->openVirtualPort( portName ); } X+inline void RtMidiOut :: closePort( void ) { rtapi_->closePort(); } X+inline bool RtMidiOut :: isPortOpen() const { return rtapi_->isPortOpen(); } X+inline unsigned int RtMidiOut :: getPortCount( void ) { return rtapi_->getPortCount(); } X+inline std::string RtMidiOut :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); } X+inline void RtMidiOut :: sendMessage( const std::vector *message ) { static_cast(rtapi_)->sendMessage( &message->at(0), message->size() ); } X+inline void RtMidiOut :: sendMessage( const unsigned char *message, size_t size ) { static_cast(rtapi_)->sendMessage( message, size ); } X+inline void RtMidiOut :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ) { rtapi_->setErrorCallback(errorCallback, userData); } X X #endif beb363e02ee68f4652545d1559ab90f3 echo x - audio/chuck/files/patch-src_core_util__serial.cpp sed 's/^X//' >audio/chuck/files/patch-src_core_util__serial.cpp << '0298de1c47f71a6b4ee08c41f5651d7f' X--- src/core/util_serial.cpp.orig 2021-08-16 04:08:51 UTC X+++ src/core/util_serial.cpp X@@ -34,6 +34,10 @@ X #include "chuck_errmsg.h" X using namespace std; X X+#ifdef __PLATFORM_FREEBSD__ X+#include X+#endif X+ X #if defined(__MACOSX_CORE__) && !defined(__CHIP_MODE__) X X #include X@@ -114,7 +118,7 @@ cleanup: X return devices; X } X X-#elif defined(__PLATFORM_LINUX__) X+#elif defined(__PLATFORM_LINUX__) || defined(__PLATFORM_FREEBSD__) X X X #include 0298de1c47f71a6b4ee08c41f5651d7f echo x - audio/chuck/files/extra-patch-src_core_rtmidi-oss_alsa.cpp sed 's/^X//' >audio/chuck/files/extra-patch-src_core_rtmidi-oss_alsa.cpp << '3c42ae2a8d4c5b3fcaeeac76ccfc6d53' X--- src/core/rtmidi.cpp.orig 2021-08-16 04:08:51 UTC X+++ src/core/rtmidi.cpp X@@ -5,10 +5,11 @@ X This class implements some common functionality for the realtime X MIDI input/output subclasses RtMidiIn and RtMidiOut. X X- RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/ X+ RtMidi GitHub site: https://github.com/thestk/rtmidi X+ RtMidi WWW site: http://www.music.mcgill.ca/~gary/rtmidi/ X X RtMidi: realtime MIDI i/o C++ classes X- Copyright (c) 2003-2005 Gary P. Scavone X+ Copyright (c) 2003-2019 Gary P. Scavone X X Permission is hereby granted, free of charge, to any person X obtaining a copy of this software and associated documentation files X@@ -22,8 +23,9 @@ X included in all copies or substantial portions of the Software. X X Any person wishing to distribute modifications to the Software is X- requested to send the modifications to the original developer so that X- they can be incorporated into the canonical version. X+ asked to send the modifications to the original developer so that X+ they can be incorporated into the canonical version. This is, X+ however, not a binding provision of this license. X X THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, X EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF X@@ -35,75 +37,617 @@ X */ X /**********************************************************************/ X X-// RtMidi: Version 1.0.4, 14 October 2005 X- X-// chuck X #include "rtmidi.h" X #include X-#include X-#include X X+#if defined(TARGET_OS_IPHONE) X X+ #define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime X+ #define AudioConvertHostTimeToNanos CAHostTimeBase::ConvertToNanos X+ X+ #include X+ class CTime2nsFactor X+ { X+ public: X+ CTime2nsFactor() X+ { X+ mach_timebase_info_data_t tinfo; X+ mach_timebase_info(&tinfo); X+ Factor = (double)tinfo.numer / tinfo.denom; X+ } X+ static double Factor; X+ }; X+ double CTime2nsFactor::Factor; X+ static CTime2nsFactor InitTime2nsFactor; X+ #undef AudioGetCurrentHostTime X+ #undef AudioConvertHostTimeToNanos X+ #define AudioGetCurrentHostTime (uint64_t) mach_absolute_time X+ #define AudioConvertHostTimeToNanos(t) t *CTime2nsFactor::Factor X+ #define EndianS32_BtoN(n) n X+ X+#endif X+ X+// Default for Windows is to add an identifier to the port names; this X+// flag can be defined (e.g. in your project file) to disable this behaviour. X+//#define RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES X+ X+// **************************************************************** // X+// X+// MidiInApi and MidiOutApi subclass prototypes. X+// X+// **************************************************************** // X+ X+#if !defined(__LINUX_ALSA__) && !defined(__FREEBSD_ALSA__) && !defined(__FREEBSD_OSS__) && !defined(__LINUX_OSS__) && !defined(__UNIX_JACK__) && !defined(__MACOSX_CORE__) && !defined(__WINDOWS_MM__) && !defined(TARGET_IPHONE_OS) X+ #define __RTMIDI_DUMMY__ X+#endif X+ X+#if defined(__MACOSX_CORE__) X+ X+class MidiInCore: public MidiInApi X+{ X+ public: X+ MidiInCore( const std::string &clientName, unsigned int queueSizeLimit ); X+ ~MidiInCore( void ); X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; }; X+ void openPort( unsigned int portNumber, const std::string &portName ); X+ void openVirtualPort( const std::string &portName ); X+ void closePort( void ); X+ void setClientName( const std::string &clientName ); X+ void setPortName( const std::string &portName ); X+ unsigned int getPortCount( void ); X+ std::string getPortName( unsigned int portNumber ); X+ X+ protected: X+ void initialize( const std::string& clientName ); X+}; X+ X+class MidiOutCore: public MidiOutApi X+{ X+ public: X+ MidiOutCore( const std::string &clientName ); X+ ~MidiOutCore( void ); X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; }; X+ void openPort( unsigned int portNumber, const std::string &portName ); X+ void openVirtualPort( const std::string &portName ); X+ void closePort( void ); X+ void setClientName( const std::string &clientName ); X+ void setPortName( const std::string &portName ); X+ unsigned int getPortCount( void ); X+ std::string getPortName( unsigned int portNumber ); X+ void sendMessage( const unsigned char *message, size_t size ); X+ X+ protected: X+ void initialize( const std::string& clientName ); X+}; X+ X+#endif X+ X+#if defined(__UNIX_JACK__) X+ X+class MidiInJack: public MidiInApi X+{ X+ public: X+ MidiInJack( const std::string &clientName, unsigned int queueSizeLimit ); X+ ~MidiInJack( void ); X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; }; X+ void openPort( unsigned int portNumber, const std::string &portName ); X+ void openVirtualPort( const std::string &portName ); X+ void closePort( void ); X+ void setClientName( const std::string &clientName ); X+ void setPortName( const std::string &portName); X+ unsigned int getPortCount( void ); X+ std::string getPortName( unsigned int portNumber ); X+ X+ protected: X+ std::string clientName; X+ X+ void connect( void ); X+ void initialize( const std::string& clientName ); X+}; X+ X+class MidiOutJack: public MidiOutApi X+{ X+ public: X+ MidiOutJack( const std::string &clientName ); X+ ~MidiOutJack( void ); X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; }; X+ void openPort( unsigned int portNumber, const std::string &portName ); X+ void openVirtualPort( const std::string &portName ); X+ void closePort( void ); X+ void setClientName( const std::string &clientName ); X+ void setPortName( const std::string &portName); X+ unsigned int getPortCount( void ); X+ std::string getPortName( unsigned int portNumber ); X+ void sendMessage( const unsigned char *message, size_t size ); X+ X+ protected: X+ std::string clientName; X+ X+ void connect( void ); X+ void initialize( const std::string& clientName ); X+}; X+ X+#endif X+ X+#if defined(__LINUX_ALSA__) || defined(__FREEBSD_ALSA__) || defined(__FREEBSD_OSS__) || defined(__LINUX_OSS__) X+ X+class MidiInAlsa: public MidiInApi X+{ X+ public: X+ MidiInAlsa( const std::string &clientName, unsigned int queueSizeLimit ); X+ ~MidiInAlsa( void ); X+#if defined(__LINUX_ALSA__) || defined(__LINUX_OSS__) X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; }; X+#else X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::FREEBSD_ALSA; }; X+#endif X+ void openPort( unsigned int portNumber, const std::string &portName ); X+ void openVirtualPort( const std::string &portName ); X+ void closePort( void ); X+ void setClientName( const std::string &clientName ); X+ void setPortName( const std::string &portName); X+ unsigned int getPortCount( void ); X+ std::string getPortName( unsigned int portNumber ); X+ X+ protected: X+ void initialize( const std::string& clientName ); X+}; X+ X+class MidiOutAlsa: public MidiOutApi X+{ X+ public: X+ MidiOutAlsa( const std::string &clientName ); X+ ~MidiOutAlsa( void ); X+#if defined(__LINUX_ALSA__) || defined(__LINUX_OSS__) X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; }; X+#else X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::FREEBSD_ALSA; }; X+#endif X+ void openPort( unsigned int portNumber, const std::string &portName ); X+ void openVirtualPort( const std::string &portName ); X+ void closePort( void ); X+ void setClientName( const std::string &clientName ); X+ void setPortName( const std::string &portName ); X+ unsigned int getPortCount( void ); X+ std::string getPortName( unsigned int portNumber ); X+ void sendMessage( const unsigned char *message, size_t size ); X+ X+ protected: X+ void initialize( const std::string& clientName ); X+}; X+ X+#endif X+ X+#if defined(__WINDOWS_MM__) X+ X+class MidiInWinMM: public MidiInApi X+{ X+ public: X+ MidiInWinMM( const std::string &clientName, unsigned int queueSizeLimit ); X+ ~MidiInWinMM( void ); X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; }; X+ void openPort( unsigned int portNumber, const std::string &portName ); X+ void openVirtualPort( const std::string &portName ); X+ void closePort( void ); X+ void setClientName( const std::string &clientName ); X+ void setPortName( const std::string &portName ); X+ unsigned int getPortCount( void ); X+ std::string getPortName( unsigned int portNumber ); X+ X+ protected: X+ void initialize( const std::string& clientName ); X+}; X+ X+class MidiOutWinMM: public MidiOutApi X+{ X+ public: X+ MidiOutWinMM( const std::string &clientName ); X+ ~MidiOutWinMM( void ); X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; }; X+ void openPort( unsigned int portNumber, const std::string &portName ); X+ void openVirtualPort( const std::string &portName ); X+ void closePort( void ); X+ void setClientName( const std::string &clientName ); X+ void setPortName( const std::string &portName ); X+ unsigned int getPortCount( void ); X+ std::string getPortName( unsigned int portNumber ); X+ void sendMessage( const unsigned char *message, size_t size ); X+ X+ protected: X+ void initialize( const std::string& clientName ); X+}; X+ X+#endif X+ X+#if defined(__RTMIDI_DUMMY__) X+ X+class MidiInDummy: public MidiInApi X+{ X+ public: X+ MidiInDummy( const std::string &/*clientName*/, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { errorString_ = "MidiInDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); } X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; } X+ void openPort( unsigned int /*portNumber*/, const std::string &/*portName*/ ) {} X+ void openVirtualPort( const std::string &/*portName*/ ) {} X+ void closePort( void ) {} X+ void setClientName( const std::string &/*clientName*/ ) {}; X+ void setPortName( const std::string &/*portName*/ ) {}; X+ unsigned int getPortCount( void ) { return 0; } X+ std::string getPortName( unsigned int /*portNumber*/ ) { return ""; } X+ X+ protected: X+ void initialize( const std::string& /*clientName*/ ) {} X+}; X+ X+class MidiOutDummy: public MidiOutApi X+{ X+ public: X+ MidiOutDummy( const std::string &/*clientName*/ ) { errorString_ = "MidiOutDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); } X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; } X+ void openPort( unsigned int /*portNumber*/, const std::string &/*portName*/ ) {} X+ void openVirtualPort( const std::string &/*portName*/ ) {} X+ void closePort( void ) {} X+ void setClientName( const std::string &/*clientName*/ ) {}; X+ void setPortName( const std::string &/*portName*/ ) {}; X+ unsigned int getPortCount( void ) { return 0; } X+ std::string getPortName( unsigned int /*portNumber*/ ) { return ""; } X+ void sendMessage( const unsigned char * /*message*/, size_t /*size*/ ) {} X+ X+ protected: X+ void initialize( const std::string& /*clientName*/ ) {} X+}; X+ X+#endif X+ X //*********************************************************************// X-// Common RtMidi Definitions X+// RtMidi Definitions X //*********************************************************************// X X RtMidi :: RtMidi() X- : apiData_( 0 ), connected_( false ) X+ : rtapi_(0) X { X } X X-// chuck X-void RtMidi :: error( RtMidiError::Type type ) X+RtMidi :: ~RtMidi() X { X- if (type == RtMidiError::WARNING) { X- std::cerr << "[chuck](via RtMidi): " << errorString_ << "\n"; X+ delete rtapi_; X+ rtapi_ = 0; X+} X+ X+std::string RtMidi :: getVersion( void ) throw() X+{ X+ return std::string( RTMIDI_VERSION ); X+} X+ X+// Define API names and display names. X+// Must be in same order as API enum. X+extern "C" { X+const char* rtmidi_api_names[][2] = { X+ { "unspecified" , "Unknown" }, X+ { "core" , "CoreMidi" }, X+ { "alsa" , "ALSA" }, X+ { "jack" , "Jack" }, X+ { "winmm" , "Windows MultiMedia" }, X+ { "dummy" , "Dummy" }, X+}; X+const unsigned int rtmidi_num_api_names = X+ sizeof(rtmidi_api_names)/sizeof(rtmidi_api_names[0]); X+ X+// The order here will control the order of RtMidi's API search in X+// the constructor. X+extern "C" const RtMidi::Api rtmidi_compiled_apis[] = { X+#if defined(__MACOSX_CORE__) X+ RtMidi::MACOSX_CORE, X+#endif X+#if defined(__LINUX_ALSA__) || defined(__LINUX_OSS__) X+ RtMidi::LINUX_ALSA, X+#endif X+#if defined(__FREEBSD_ALSA__) || defined(__FREEBSD_OSS__) X+ RtMidi::FREEBSD_ALSA, X+#endif X+#if defined(__UNIX_JACK__) X+ RtMidi::UNIX_JACK, X+#endif X+#if defined(__WINDOWS_MM__) X+ RtMidi::WINDOWS_MM, X+#endif X+#if defined(__RTMIDI_DUMMY__) X+ RtMidi::RTMIDI_DUMMY, X+#endif X+ RtMidi::UNSPECIFIED, X+}; X+extern "C" const unsigned int rtmidi_num_compiled_apis = X+ sizeof(rtmidi_compiled_apis)/sizeof(rtmidi_compiled_apis[0])-1; X+} X+ X+// This is a compile-time check that rtmidi_num_api_names == RtMidi::NUM_APIS. X+// If the build breaks here, check that they match. X+template class StaticAssert { public: StaticAssert() {} }; X+template<> class StaticAssert{ public: StaticAssert() {} }; X+class StaticAssertions { StaticAssertions() { X+ StaticAssert(); X+}}; X+ X+void RtMidi :: getCompiledApi( std::vector &apis ) throw() X+{ X+ apis = std::vector(rtmidi_compiled_apis, X+ rtmidi_compiled_apis + rtmidi_num_compiled_apis); X+} X+ X+std::string RtMidi :: getApiName( RtMidi::Api api ) X+{ X+ if (api < 0 || api >= RtMidi::NUM_APIS) X+ return ""; X+ return rtmidi_api_names[api][0]; X+} X+ X+std::string RtMidi :: getApiDisplayName( RtMidi::Api api ) X+{ X+ if (api < 0 || api >= RtMidi::NUM_APIS) X+ return "Unknown"; X+ return rtmidi_api_names[api][1]; X+} X+ X+RtMidi::Api RtMidi :: getCompiledApiByName( const std::string &name ) X+{ X+ unsigned int i=0; X+ for (i = 0; i < rtmidi_num_compiled_apis; ++i) X+ if (name == rtmidi_api_names[rtmidi_compiled_apis[i]][0]) X+ return rtmidi_compiled_apis[i]; X+ return RtMidi::UNSPECIFIED; X+} X+ X+void RtMidi :: setClientName( const std::string &clientName ) X+{ X+ rtapi_->setClientName( clientName ); X+} X+ X+void RtMidi :: setPortName( const std::string &portName ) X+{ X+ rtapi_->setPortName( portName ); X+} X+ X+ X+//*********************************************************************// X+// RtMidiIn Definitions X+//*********************************************************************// X+ X+void RtMidiIn :: openMidiApi( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit ) X+{ X+ delete rtapi_; X+ rtapi_ = 0; X+ X+#if defined(__UNIX_JACK__) X+ if ( api == UNIX_JACK ) X+ rtapi_ = new MidiInJack( clientName, queueSizeLimit ); X+#endif X+#if defined(__LINUX_ALSA__) || defined(__LINUX_OSS__) X+ if ( api == LINUX_ALSA ) X+ rtapi_ = new MidiInAlsa( clientName, queueSizeLimit ); X+#endif X+#if defined(__FREEBSD_ALSA__) || defined(__FREEBSD_OSS__) X+ if ( api == FREEBSD_ALSA ) X+ rtapi_ = new MidiInAlsa( clientName, queueSizeLimit ); X+#endif X+#if defined(__WINDOWS_MM__) X+ if ( api == WINDOWS_MM ) X+ rtapi_ = new MidiInWinMM( clientName, queueSizeLimit ); X+#endif X+#if defined(__MACOSX_CORE__) X+ if ( api == MACOSX_CORE ) X+ rtapi_ = new MidiInCore( clientName, queueSizeLimit ); X+#endif X+#if defined(__RTMIDI_DUMMY__) X+ if ( api == RTMIDI_DUMMY ) X+ rtapi_ = new MidiInDummy( clientName, queueSizeLimit ); X+#endif X+} X+ X+RTMIDI_DLL_PUBLIC RtMidiIn :: RtMidiIn( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit ) X+ : RtMidi() X+{ X+ if ( api != UNSPECIFIED ) { X+ // Attempt to open the specified API. X+ openMidiApi( api, clientName, queueSizeLimit ); X+ if ( rtapi_ ) return; X+ X+ // No compiled support for specified API value. Issue a warning X+ // and continue as if no API was specified. X+ std::cerr << "\nRtMidiIn: no compiled support for specified API argument!\n\n" << std::endl; X } X- else if (type == RtMidiError::DEBUG_WARNING) { X+ X+ // Iterate through the compiled APIs and return as soon as we find X+ // one with at least one port or we reach the end of the list. X+ std::vector< RtMidi::Api > apis; X+ getCompiledApi( apis ); X+ for ( unsigned int i=0; igetPortCount() ) break; X+ } X+ X+ if ( rtapi_ ) return; X+ X+ // It should not be possible to get here because the preprocessor X+ // definition __RTMIDI_DUMMY__ is automatically defined if no X+ // API-specific definitions are passed to the compiler. But just in X+ // case something weird happens, we'll throw an error. X+ std::string errorText = "RtMidiIn: no compiled API support found ... critical error!!"; X+ throw( RtMidiError( errorText, RtMidiError::UNSPECIFIED ) ); X+} X+ X+RtMidiIn :: ~RtMidiIn() throw() X+{ X+} X+ X+ X+//*********************************************************************// X+// RtMidiOut Definitions X+//*********************************************************************// X+ X+void RtMidiOut :: openMidiApi( RtMidi::Api api, const std::string &clientName ) X+{ X+ delete rtapi_; X+ rtapi_ = 0; X+ X+#if defined(__UNIX_JACK__) X+ if ( api == UNIX_JACK ) X+ rtapi_ = new MidiOutJack( clientName ); X+#endif X+#if defined(__LINUX_ALSA__) X+ if ( api == LINUX_ALSA ) X+ rtapi_ = new MidiOutAlsa( clientName ); X+#endif X+#ifdef __FREEBSD_ALSA__ X+ if ( api == FREEBSD_ALSA ) X+ rtapi_ = new MidiOutAlsa( clientName ); X+#endif X+#ifdef __FREEBSD_OSS__ X+ if ( api == FREEBSD_ALSA ) X+ rtapi_ = new MidiOutAlsa( clientName ); X+#endif X+#ifdef __LINUX_OSS__ X+ if ( api == LINUX_ALSA ) X+ rtapi_ = new MidiOutAlsa( clientName ); X+#endif X+#if defined(__WINDOWS_MM__) X+ if ( api == WINDOWS_MM ) X+ rtapi_ = new MidiOutWinMM( clientName ); X+#endif X+#if defined(__MACOSX_CORE__) X+ if ( api == MACOSX_CORE ) X+ rtapi_ = new MidiOutCore( clientName ); X+#endif X+#if defined(__RTMIDI_DUMMY__) X+ if ( api == RTMIDI_DUMMY ) X+ rtapi_ = new MidiOutDummy( clientName ); X+#endif X+} X+ X+RTMIDI_DLL_PUBLIC RtMidiOut :: RtMidiOut( RtMidi::Api api, const std::string &clientName) X+{ X+ if ( api != UNSPECIFIED ) { X+ // Attempt to open the specified API. X+ openMidiApi( api, clientName ); X+ if ( rtapi_ ) return; X+ X+ // No compiled support for specified API value. Issue a warning X+ // and continue as if no API was specified. X+ std::cerr << "\nRtMidiOut: no compiled support for specified API argument!\n\n" << std::endl; X+ } X+ X+ // Iterate through the compiled APIs and return as soon as we find X+ // one with at least one port or we reach the end of the list. X+ std::vector< RtMidi::Api > apis; X+ getCompiledApi( apis ); X+ for ( unsigned int i=0; igetPortCount() ) break; X+ } X+ X+ if ( rtapi_ ) return; X+ X+ // It should not be possible to get here because the preprocessor X+ // definition __RTMIDI_DUMMY__ is automatically defined if no X+ // API-specific definitions are passed to the compiler. But just in X+ // case something weird happens, we'll thrown an error. X+ std::string errorText = "RtMidiOut: no compiled API support found ... critical error!!"; X+ throw( RtMidiError( errorText, RtMidiError::UNSPECIFIED ) ); X+} X+ X+RtMidiOut :: ~RtMidiOut() throw() X+{ X+} X+ X+//*********************************************************************// X+// Common MidiApi Definitions X+//*********************************************************************// X+ X+MidiApi :: MidiApi( void ) X+ : apiData_( 0 ), connected_( false ), errorCallback_(0), firstErrorOccurred_(false), errorCallbackUserData_(0) X+{ X+} X+ X+MidiApi :: ~MidiApi( void ) X+{ X+} X+ X+void MidiApi :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData = 0 ) X+{ X+ errorCallback_ = errorCallback; X+ errorCallbackUserData_ = userData; X+} X+ X+void MidiApi :: error( RtMidiError::Type type, std::string errorString ) X+{ X+ if ( errorCallback_ ) { X+ X+ if ( firstErrorOccurred_ ) X+ return; X+ X+ firstErrorOccurred_ = true; X+ const std::string errorMessage = errorString; X+ X+ errorCallback_( type, errorMessage, errorCallbackUserData_ ); X+ firstErrorOccurred_ = false; X+ return; X+ } X+ X+ if ( type == RtMidiError::WARNING ) { X+ std::cerr << '\n' << errorString << "\n\n"; X+ } X+ else if ( type == RtMidiError::DEBUG_WARNING ) { X #if defined(__RTMIDI_DEBUG__) X- std::cerr << "[chuck](via RtMidi): " << errorString_ << "\n"; X+ std::cerr << '\n' << errorString << "\n\n"; X #endif X } X else { X- // std::cerr << "[chuck](via RtMidi): " << errorString_ << "\n"; X- throw RtMidiError( errorString_, type ); X+ std::cerr << '\n' << errorString << "\n\n"; X+ throw RtMidiError( errorString, type ); X } X } X X- X //*********************************************************************// X-// Common RtMidiIn Definitions X+// Common MidiInApi Definitions X //*********************************************************************// X X-RtMidiIn :: RtMidiIn() : RtMidi() X+MidiInApi :: MidiInApi( unsigned int queueSizeLimit ) X+ : MidiApi() X { X- this->initialize(); X+ // Allocate the MIDI queue. X+ inputData_.queue.ringSize = queueSizeLimit; X+ if ( inputData_.queue.ringSize > 0 ) X+ inputData_.queue.ring = new MidiMessage[ inputData_.queue.ringSize ]; X } X X-void RtMidiIn :: setCallback( RtMidiCallback callback, void *userData ) X+MidiInApi :: ~MidiInApi( void ) X { X+ // Delete the MIDI queue. X+ if ( inputData_.queue.ringSize > 0 ) delete [] inputData_.queue.ring; X+} X+ X+void MidiInApi :: setCallback( RtMidiIn::RtMidiCallback callback, void *userData ) X+{ X if ( inputData_.usingCallback ) { X- errorString_ = "RtMidiIn::setCallback: callback function is already set!"; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiInApi::setCallback: a callback function is already set!"; X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X if ( !callback ) { X errorString_ = "RtMidiIn::setCallback: callback function value is invalid!"; X- error( RtMidiError::WARNING ); X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X- inputData_.userCallback = (void *) callback; X+ inputData_.userCallback = callback; X inputData_.userData = userData; X inputData_.usingCallback = true; X } X X-void RtMidiIn :: cancelCallback() X+void MidiInApi :: cancelCallback() X { X if ( !inputData_.usingCallback ) { X errorString_ = "RtMidiIn::cancelCallback: no callback function was set!"; X- error( RtMidiError::WARNING ); X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X@@ -112,57 +656,107 @@ void RtMidiIn :: cancelCallback() X inputData_.usingCallback = false; X } X X-void RtMidiIn :: setQueueSizeLimit( unsigned int queueSize ) X+void MidiInApi :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) X { X- inputData_.queueLimit = queueSize; X-} X- X-void RtMidiIn :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) X-{ X inputData_.ignoreFlags = 0; X if ( midiSysex ) inputData_.ignoreFlags = 0x01; X if ( midiTime ) inputData_.ignoreFlags |= 0x02; X if ( midiSense ) inputData_.ignoreFlags |= 0x04; X } X X-double RtMidiIn :: getMessage( std::vector *message ) X+double MidiInApi :: getMessage( std::vector *message ) X { X message->clear(); X X if ( inputData_.usingCallback ) { X- errorString_ = "RtMidiIn::getNextMessage: user callback is currently set for this port."; X- error( RtMidiError::WARNING ); X+ errorString_ = "RtMidiIn::getNextMessage: a user callback is currently set for this port."; X+ error( RtMidiError::WARNING, errorString_ ); X return 0.0; X } X X- if ( inputData_.queue.size() == 0 ) return 0.0; X+ double timeStamp; X+ if ( !inputData_.queue.pop( message, &timeStamp ) ) X+ return 0.0; X X+ return timeStamp; X+} X+ X+unsigned int MidiInApi::MidiQueue::size( unsigned int *__back, X+ unsigned int *__front ) X+{ X+ // Access back/front members exactly once and make stack copies for X+ // size calculation X+ unsigned int _back = back, _front = front, _size; X+ if ( _back >= _front ) X+ _size = _back - _front; X+ else X+ _size = ringSize - _front + _back; X+ X+ // Return copies of back/front so no new and unsynchronized accesses X+ // to member variables are needed. X+ if ( __back ) *__back = _back; X+ if ( __front ) *__front = _front; X+ return _size; X+} X+ X+// As long as we haven't reached our queue size limit, push the message. X+bool MidiInApi::MidiQueue::push( const MidiInApi::MidiMessage& msg ) X+{ X+ // Local stack copies of front/back X+ unsigned int _back, _front, _size; X+ X+ // Get back/front indexes exactly once and calculate current size X+ _size = size( &_back, &_front ); X+ X+ if ( _size < ringSize-1 ) X+ { X+ ring[_back] = msg; X+ back = (back+1)%ringSize; X+ return true; X+ } X+ X+ return false; X+} X+ X+bool MidiInApi::MidiQueue::pop( std::vector *msg, double* timeStamp ) X+{ X+ // Local stack copies of front/back X+ unsigned int _back, _front, _size; X+ X+ // Get back/front indexes exactly once and calculate current size X+ _size = size( &_back, &_front ); X+ X+ if ( _size == 0 ) X+ return false; X+ X // Copy queued message to the vector pointer argument and then "pop" it. X- std::vector *bytes = &(inputData_.queue.front().bytes); X- message->assign( bytes->begin(), bytes->end() ); X- double deltaTime = inputData_.queue.front().timeStamp; X- inputData_.queue.pop(); X+ msg->assign( ring[_front].bytes.begin(), ring[_front].bytes.end() ); X+ *timeStamp = ring[_front].timeStamp; X X- return deltaTime; X+ // Update front X+ front = (front+1)%ringSize; X+ return true; X } X X //*********************************************************************// X-// Common RtMidiOut Definitions X+// Common MidiOutApi Definitions X //*********************************************************************// X X-RtMidiOut :: RtMidiOut() : RtMidi() X+MidiOutApi :: MidiOutApi( void ) X+ : MidiApi() X { X- this->initialize(); X } X X+MidiOutApi :: ~MidiOutApi( void ) X+{ X+} X X-//*********************************************************************// X-// API: Macintosh OS-X X-//*********************************************************************// X+// *************************************************** // X+// X+// OS/API-specific methods. X+// X+// *************************************************** // X X-// API information found at: X-// - http://developer. apple .com/audio/pdf/coreaudio.pdf X- X #if defined(__MACOSX_CORE__) X X // The CoreMIDI API is based on the use of a callback function for X@@ -171,11 +765,12 @@ RtMidiOut :: RtMidiOut() : RtMidi() X X // OS-X CoreMIDI header files. X #include X-#include X X-// CoreMIDI naming helper function prototypes X-static void readable_name(MIDIEndpointRef end, char *buffer, int bufsize); X-static int get_device_name(SInt32 uniqueid, char*buffer, int bufsize); X+// these are not available on iOS. X+#if (TARGET_OS_IPHONE == 0) X+ #include X+ #include X+#endif X X // A structure to hold variables related to the CoreMIDI API X // implementation. X@@ -185,217 +780,84 @@ struct CoreMidiData { X MIDIEndpointRef endpoint; X MIDIEndpointRef destinationId; X unsigned long long lastTime; X+ MIDISysexSendRequest sysexreq; X }; X X-// ****** X-// API: OS-X X-// helper functions X-// ***** X- X-// coreMIDI port naming helper function X-/* X- This wraps up the code to take a passed-in endpoint and work out X- a nice human-readable name for it. X- X- For the moment, this function will return firstly the device name + X- port name, or if we can work out what external devices are hooked X- in, that name. It only grabs the very first connected device name. X- X- NB. won't get connected devices correctly before 10.3. X-*/ X-static void readable_name(MIDIEndpointRef end, char *buffer, int bufsize) X-{ X- MIDIEntityRef ent = 0; // NULL; X- MIDIDeviceRef dev = 0; //NULL; X- int ii, count, length;//, ret; X- SInt32 *idarray; X- CFDataRef data = NULL; X- CFStringRef s; X- X- buffer[0] = '\0'; X- X- if( MIDIObjectGetDataProperty(end, kMIDIPropertyConnectionUniqueID, &data) == 0) X- { X- length = CFDataGetLength(data) / sizeof(SInt32); X- idarray = (SInt32 *) CFDataGetBytePtr(data); X- count = 0; X- for (ii = 0; ii < length; ii++) { X- if (bufsize < 3) X- break; X- if (count > 0) { X- strcpy(buffer, ", "); X- buffer += 2; X- bufsize -= 2; X- } X- X- if (get_device_name(idarray[ii], buffer, bufsize) == 0) { X- count++; X- bufsize -= strlen(buffer); X- buffer += strlen(buffer); X- } X- } X- X- CFRelease(data); X- if (count > 0) X- return; X- } X- X- char * mid = buffer; X- // build up the name of the enclosing device, if it can be found. X- if (MIDIEndpointGetEntity(end, &ent) == 0) { X- if (MIDIEntityGetDevice(ent, &dev) == 0) { X- if (MIDIObjectGetStringProperty(dev, kMIDIPropertyName, &s) == 0) { X- CFStringGetCString(s, buffer, bufsize, 0); X- bufsize -= strlen(buffer) + 1; X- buffer += strlen(buffer); X- X- CFRelease(s); X- } X- } X- } X- X- // ge X- char buffy[128]; X- // Now add the port/endpoint name. X- // no need to update buffer pointer. Last item. X- if (MIDIObjectGetStringProperty(end, kMIDIPropertyName, &s) == 0) { X- CFStringGetCString(s, buffy, 128, 0); X- CFRelease(s); X- X- // copy if different X- if( strcmp( mid, buffy ) && strlen(buffy) < bufsize ) { X- *buffer = ' '; X- buffer++; X- strcpy( buffer, buffy ); X- } X- } X-} X- X- X-// port naming helper function (External devices) X-/* X- Uses the midiojectfindbyuniqueid function to hunt down the relevant X- device and copies its name into the buffer provided. We don't know X- what type of device we'll get back, so we first throw away any X- external flags (We don't care), and then cast it up to device which X- is the thing which has a useful name. X- X- If it can't find one, return -1. 0 returned on success. X-*/ X-static int get_device_name(SInt32 uniqueid, char *buffer, int bufsize) X-{ X- int ret; X- MIDIObjectRef object = 0; // NULL; // 1.3.1.0 X- MIDIObjectType type; X- X- MIDIDeviceRef dev = 0; // NULL; X- MIDIEntityRef ent = 0; // NULL; X- MIDIEndpointRef end = 0; // NULL; X- CFStringRef name = NULL; X- X- ret = MIDIObjectFindByUniqueID(uniqueid, &object, &type); X- if (ret < 0) X- return -1; X- X- // now clear any external flag. X- if (type > 0) X- type = (MIDIObjectType) (type & (~kMIDIObjectType_ExternalMask)); X- X- if (type == kMIDIObjectType_Device) { X- dev = (MIDIDeviceRef) object; X- } else if (type == kMIDIObjectType_Entity) { X- ent = (MIDIEntityRef) object; X- if (MIDIEntityGetDevice(ent, &dev)) { X- return -1; X- } X- } if (type == kMIDIObjectType_Source || X- type == kMIDIObjectType_Destination) { X- end = (MIDIEndpointRef) object; X- if (MIDIEndpointGetEntity(end, &ent)) { X- return -1; X- } X- if (MIDIEntityGetDevice(ent, &dev)) { X- return -1; X- } X- } else { X- // unknown type X- printf("Unknown type %d returned from findobject\n", (int) type); X- CFRelease( &object ); // 1.3.1.0 X- return -1; X- } X- X- MIDIObjectGetStringProperty(dev, kMIDIPropertyName, &name); X- CFStringGetCString(name, buffer, bufsize, 0); X- CFRelease(name); X- X- return 0; X-} X- X- X- X- X- X //*********************************************************************// X // API: OS-X X-// Class Definitions: RtMidiIn X+// Class Definitions: MidiInCore X //*********************************************************************// X X-void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef ) X+static void midiInputCallback( const MIDIPacketList *list, void *procRef, void */*srcRef*/ ) X { X- RtMidiIn::RtMidiInData *data = static_cast (procRef); X+ MidiInApi::RtMidiInData *data = static_cast (procRef); X CoreMidiData *apiData = static_cast (data->apiData); X X- bool continueSysex = false; X unsigned char status; X unsigned short nBytes, iByte, size; X unsigned long long time; X- RtMidiIn::MidiMessage message; X+ X+ bool& continueSysex = data->continueSysex; X+ MidiInApi::MidiMessage& message = data->message; X+ X const MIDIPacket *packet = &list->packet[0]; X for ( unsigned int i=0; inumPackets; ++i ) { X X // My interpretation of the CoreMIDI documentation: all message X // types, except sysex, are complete within a packet and there may X // be several of them in a single packet. Sysex messages can be X- // broken across multiple packets but are bundled alone within a X- // packet. I'm assuming that sysex messages, if segmented, must X- // be complete within the same MIDIPacketList. X+ // broken across multiple packets and PacketLists but are bundled X+ // alone within each packet (these packets do not contain other X+ // message types). If sysex messages are split across multiple X+ // MIDIPacketLists, they must be handled by multiple calls to this X+ // function. X X nBytes = packet->length; X- if ( nBytes == 0 ) continue; X+ if ( nBytes == 0 ) { X+ packet = MIDIPacketNext( packet ); X+ continue; X+ } X X // Calculate time stamp. X- message.timeStamp = 0.0; X- if ( data->firstMessage ) X+ if ( data->firstMessage ) { X+ message.timeStamp = 0.0; X data->firstMessage = false; X+ } X else { X time = packet->timeStamp; X+ if ( time == 0 ) { // this happens when receiving asynchronous sysex messages X+ time = AudioGetCurrentHostTime(); X+ } X time -= apiData->lastTime; X time = AudioConvertHostTimeToNanos( time ); X- message.timeStamp = time * 0.000000001; X+ if ( !continueSysex ) X+ message.timeStamp = time * 0.000000001; X } X- apiData->lastTime = packet->timeStamp; X X+ // Track whether any non-filtered messages were found in this X+ // packet for timestamp calculation X+ bool foundNonFiltered = false; X+ X iByte = 0; X if ( continueSysex ) { X // We have a continuing, segmented sysex message. X- if ( !(data->ignoreFlags & 0x01) ) { X+ if ( !( data->ignoreFlags & 0x01 ) ) { X // If we're not ignoring sysex messages, copy the entire packet. X- for ( unsigned int j=0; jdata[j] ); X } X- if ( packet->data[nBytes] == 0xF7 ) continueSysex = false; X- if ( !continueSysex ) { X+ continueSysex = packet->data[nBytes-1] != 0xF7; X+ X+ if ( !( data->ignoreFlags & 0x01 ) && !continueSysex ) { X // If not a continuing sysex message, invoke the user callback function or queue the message. X- if ( data->usingCallback && message.bytes.size() > 0 ) { X+ if ( data->usingCallback ) { X RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; X callback( message.timeStamp, &message.bytes, data->userData ); X } X else { X // As long as we haven't reached our queue size limit, push the message. X- if ( data->queueLimit > data->queue.size() ) X- data->queue.push( message ); X- else X- std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; X+ if ( !data->queue.push( message ) ) X+ std::cerr << "\nMidiInCore: message queue limit reached!!\n\n"; X } X message.bytes.clear(); X } X@@ -417,26 +879,24 @@ void midiInputCallback( const MIDIPacketList *list, vo X iByte = nBytes; X } X else size = nBytes - iByte; X- if ( packet->data[nBytes] == 0xF7 ) continueSysex = false; X+ continueSysex = packet->data[nBytes-1] != 0xF7; X } X- else if ( status < 0xF3 ) { X- if ( status == 0xF1 && (data->ignoreFlags & 0x02) ) { X- // A MIDI time code message and we're ignoring it. X+ else if ( status == 0xF1 ) { X+ // A MIDI time code message X+ if ( data->ignoreFlags & 0x02 ) { X size = 0; X- iByte += 3; X+ iByte += 2; X } X- else size = 3; X+ else size = 2; X } X+ else if ( status == 0xF2 ) size = 3; X else if ( status == 0xF3 ) size = 2; X- else if ( status == 0xF8 ) { X- size = 1; X- if ( data->ignoreFlags & 0x02 ) { X- // A MIDI timing tick message and we're ignoring it. X- size = 0; X- iByte += 3; X- } X+ else if ( status == 0xF8 && ( data->ignoreFlags & 0x02 ) ) { X+ // A MIDI timing tick message and we're ignoring it. X+ size = 0; X+ iByte += 1; X } X- else if ( status == 0xFE && (data->ignoreFlags & 0x04) ) { X+ else if ( status == 0xFE && ( data->ignoreFlags & 0x04 ) ) { X // A MIDI active sensing message and we're ignoring it. X size = 0; X iByte += 1; X@@ -445,6 +905,7 @@ void midiInputCallback( const MIDIPacketList *list, vo X X // Copy the MIDI data to our vector. X if ( size ) { X+ foundNonFiltered = true; X message.bytes.assign( &packet->data[iByte], &packet->data[iByte+size] ); X if ( !continueSysex ) { X // If not a continuing sysex message, invoke the user callback function or queue the message. X@@ -454,10 +915,8 @@ void midiInputCallback( const MIDIPacketList *list, vo X } X else { X // As long as we haven't reached our queue size limit, push the message. X- if ( data->queueLimit > data->queue.size() ) X- data->queue.push( message ); X- else X- std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; X+ if ( !data->queue.push( message ) ) X+ std::cerr << "\nMidiInCore: message queue limit reached!!\n\n"; X } X message.bytes.clear(); X } X@@ -465,18 +924,49 @@ void midiInputCallback( const MIDIPacketList *list, vo X } X } X } X+ X+ // Save the time of the last non-filtered message X+ if ( foundNonFiltered ) { X+ apiData->lastTime = packet->timeStamp; X+ if ( apiData->lastTime == 0 ) { // this happens when receiving asynchronous sysex messages X+ apiData->lastTime = AudioGetCurrentHostTime(); X+ } X+ } X+ X packet = MIDIPacketNext(packet); X } X } X X-void RtMidiIn :: initialize( void ) X+MidiInCore :: MidiInCore( const std::string &clientName, unsigned int queueSizeLimit ) X+ : MidiInApi( queueSizeLimit ) X { X+ MidiInCore::initialize( clientName ); X+} X+ X+MidiInCore :: ~MidiInCore( void ) X+{ X+ // Close a connection if it exists. X+ MidiInCore::closePort(); X+ X+ // Cleanup. X+ CoreMidiData *data = static_cast (apiData_); X+ MIDIClientDispose( data->client ); X+ if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); X+ delete data; X+} X+ X+void MidiInCore :: initialize( const std::string& clientName ) X+{ X // Set up our client. X MIDIClientRef client; X- OSStatus result = MIDIClientCreate( CFSTR("RtMidi Input Client"), NULL, NULL, &client ); X+ CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ); X+ OSStatus result = MIDIClientCreate(name, NULL, NULL, &client ); X if ( result != noErr ) { X- errorString_ = "RtMidiIn::initialize: error creating OS-X MIDI client object."; X- error( RtMidiError::DRIVER_ERROR ); X+ std::ostringstream ost; X+ ost << "MidiInCore::initialize: error creating OS-X MIDI client object (" << result << ")."; X+ errorString_ = ost.str(); X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X // Save our api-specific connection information. X@@ -485,45 +975,56 @@ void RtMidiIn :: initialize( void ) X data->endpoint = 0; X apiData_ = (void *) data; X inputData_.apiData = (void *) data; X+ CFRelease( name ); X } X X-void RtMidiIn :: openPort( unsigned int portNumber ) X+void MidiInCore :: openPort( unsigned int portNumber, const std::string &portName ) X { X if ( connected_ ) { X- errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiInCore::openPort: a valid connection already exists!"; X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X+ CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); X unsigned int nSrc = MIDIGetNumberOfSources(); X- if (nSrc < 1) { X- errorString_ = "RtMidiIn::openPort: no MIDI input sources found!"; X- error( RtMidiError::NO_DEVICES_FOUND ); X+ if ( nSrc < 1 ) { X+ errorString_ = "MidiInCore::openPort: no MIDI input sources found!"; X+ error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); X+ return; X } X X- std::ostringstream ost; X if ( portNumber >= nSrc ) { X- ost << "RtMidiIn::openPort: 'portNumber' argument (" << portNumber << ") is invalid."; X+ std::ostringstream ost; X+ ost << "MidiInCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; X errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X+ error( RtMidiError::INVALID_PARAMETER, errorString_ ); X+ return; X } X X MIDIPortRef port; X CoreMidiData *data = static_cast (apiData_); X- OSStatus result = MIDIInputPortCreate( data->client, CFSTR("RtMidi MIDI Input Port"), midiInputCallback, (void *)&inputData_, &port ); X+ CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); X+ OSStatus result = MIDIInputPortCreate( data->client, X+ portNameRef, X+ midiInputCallback, (void *)&inputData_, &port ); X+ CFRelease( portNameRef ); X+ X if ( result != noErr ) { X MIDIClientDispose( data->client ); X- errorString_ = "RtMidiIn::openPort: error creating OS-X MIDI input port."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiInCore::openPort: error creating OS-X MIDI input port."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X // Get the desired input source identifier. X MIDIEndpointRef endpoint = MIDIGetSource( portNumber ); X- if ( endpoint == 0 ) { // 1.3.1.0: changed from NULL to 0 X+ if ( endpoint == 0 ) { X MIDIPortDispose( port ); X MIDIClientDispose( data->client ); X- errorString_ = "RtMidiIn::openPort: error getting MIDI input source reference."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiInCore::openPort: error getting MIDI input source reference."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X // Make the connection. X@@ -531,8 +1032,9 @@ void RtMidiIn :: openPort( unsigned int portNumber ) X if ( result != noErr ) { X MIDIPortDispose( port ); X MIDIClientDispose( data->client ); X- errorString_ = "RtMidiIn::openPort: error connecting OS-X MIDI input port."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiInCore::openPort: error connecting OS-X MIDI input port."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X // Save our api-specific port information. X@@ -541,114 +1043,254 @@ void RtMidiIn :: openPort( unsigned int portNumber ) X connected_ = true; X } X X-void RtMidiIn :: openVirtualPort() X+void MidiInCore :: openVirtualPort( const std::string &portName ) X { X CoreMidiData *data = static_cast (apiData_); X X // Create a virtual MIDI input destination. X MIDIEndpointRef endpoint; X- OSStatus result = MIDIDestinationCreate( data->client, CFSTR("RtMidi Input"), midiInputCallback, (void *)&inputData_, &endpoint ); X+ CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); X+ OSStatus result = MIDIDestinationCreate( data->client, X+ portNameRef, X+ midiInputCallback, (void *)&inputData_, &endpoint ); X+ CFRelease( portNameRef ); X+ X if ( result != noErr ) { X- errorString_ = "RtMidiIn::openVirtualPort: error creating virtual OS-X MIDI destination."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiInCore::openVirtualPort: error creating virtual OS-X MIDI destination."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X // Save our api-specific connection information. X data->endpoint = endpoint; X } X X-void RtMidiIn :: closePort( void ) X+void MidiInCore :: closePort( void ) X { X- if ( connected_ ) { X- CoreMidiData *data = static_cast (apiData_); X+ CoreMidiData *data = static_cast (apiData_); X+ X+ if ( data->endpoint ) { X+ MIDIEndpointDispose( data->endpoint ); X+ data->endpoint = 0; X+ } X+ X+ if ( data->port ) { X MIDIPortDispose( data->port ); X- connected_ = false; X+ data->port = 0; X } X+ X+ connected_ = false; X } X X-RtMidiIn :: ~RtMidiIn() X+void MidiInCore :: setClientName ( const std::string& ) X { X- // Close a connection if it exists. X- closePort(); X X- // Cleanup. X- CoreMidiData *data = static_cast (apiData_); X- MIDIClientDispose( data->client ); X- if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); X- delete data; X+ errorString_ = "MidiInCore::setClientName: this function is not implemented for the MACOSX_CORE API!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ X } X X-unsigned int RtMidiIn :: getPortCount() X+void MidiInCore :: setPortName ( const std::string& ) X { X+ X+ errorString_ = "MidiInCore::setPortName: this function is not implemented for the MACOSX_CORE API!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ X+} X+ X+unsigned int MidiInCore :: getPortCount() X+{ X+ CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); X return MIDIGetNumberOfSources(); X } X X-std::string RtMidiIn :: getPortName( unsigned int portNumber ) X+// This function was submitted by Douglas Casey Tucker and apparently X+// derived largely from PortMidi. X+CFStringRef EndpointName( MIDIEndpointRef endpoint, bool isExternal ) X { X-// CFStringRef nameRef; X+ CFMutableStringRef result = CFStringCreateMutable( NULL, 0 ); X+ CFStringRef str; X+ X+ // Begin with the endpoint's name. X+ str = NULL; X+ MIDIObjectGetStringProperty( endpoint, kMIDIPropertyName, &str ); X+ if ( str != NULL ) { X+ CFStringAppend( result, str ); X+ CFRelease( str ); X+ } X+ X+ MIDIEntityRef entity = 0; X+ MIDIEndpointGetEntity( endpoint, &entity ); X+ if ( entity == 0 ) X+ // probably virtual X+ return result; X+ X+ if ( CFStringGetLength( result ) == 0 ) { X+ // endpoint name has zero length -- try the entity X+ str = NULL; X+ MIDIObjectGetStringProperty( entity, kMIDIPropertyName, &str ); X+ if ( str != NULL ) { X+ CFStringAppend( result, str ); X+ CFRelease( str ); X+ } X+ } X+ // now consider the device's name X+ MIDIDeviceRef device = 0; X+ MIDIEntityGetDevice( entity, &device ); X+ if ( device == 0 ) X+ return result; X+ X+ str = NULL; X+ MIDIObjectGetStringProperty( device, kMIDIPropertyName, &str ); X+ if ( CFStringGetLength( result ) == 0 ) { X+ CFRelease( result ); X+ return str; X+ } X+ if ( str != NULL ) { X+ // if an external device has only one entity, throw away X+ // the endpoint name and just use the device name X+ if ( isExternal && MIDIDeviceGetNumberOfEntities( device ) < 2 ) { X+ CFRelease( result ); X+ return str; X+ } else { X+ if ( CFStringGetLength( str ) == 0 ) { X+ CFRelease( str ); X+ return result; X+ } X+ // does the entity name already start with the device name? X+ // (some drivers do this though they shouldn't) X+ // if so, do not prepend X+ if ( CFStringCompareWithOptions( result, /* endpoint name */ X+ str /* device name */, X+ CFRangeMake(0, CFStringGetLength( str ) ), 0 ) != kCFCompareEqualTo ) { X+ // prepend the device name to the entity name X+ if ( CFStringGetLength( result ) > 0 ) X+ CFStringInsert( result, 0, CFSTR(" ") ); X+ X+ CFStringInsert( result, 0, str ); X+ } X+ CFRelease( str ); X+ } X+ } X+ return result; X+} X+ X+// This function was submitted by Douglas Casey Tucker and apparently X+// derived largely from PortMidi. X+static CFStringRef ConnectedEndpointName( MIDIEndpointRef endpoint ) X+{ X+ CFMutableStringRef result = CFStringCreateMutable( NULL, 0 ); X+ CFStringRef str; X+ OSStatus err; X+ int i; X+ X+ // Does the endpoint have connections? X+ CFDataRef connections = NULL; X+ int nConnected = 0; X+ bool anyStrings = false; X+ err = MIDIObjectGetDataProperty( endpoint, kMIDIPropertyConnectionUniqueID, &connections ); X+ if ( connections != NULL ) { X+ // It has connections, follow them X+ // Concatenate the names of all connected devices X+ nConnected = CFDataGetLength( connections ) / sizeof(MIDIUniqueID); X+ if ( nConnected ) { X+ const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections)); X+ for ( i=0; i= MIDIGetNumberOfSources() ) { X- ost << "RtMidiIn::getPortName: 'portNumber' argument (" << portNumber << ") is invalid."; X+ std::ostringstream ost; X+ ost << "MidiInCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; X errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X+ error( RtMidiError::WARNING, errorString_ ); X+ return stringName; X } X+ X portRef = MIDIGetSource( portNumber ); X-#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 X- readable_name(portRef, name, sizeof(name)); X-#else X- MIDIObjectGetStringProperty( portRef, kMIDIPropertyName, &nameRef ); X- CFStringGetCString( nameRef, name, sizeof(name), 0); X+ nameRef = ConnectedEndpointName( portRef ); X+ CFStringGetCString( nameRef, name, sizeof(name), kCFStringEncodingUTF8 ); X CFRelease( nameRef ); X-#endif X- std::string stringName = name; X- return stringName; X+ X+ return stringName = name; X } X X //*********************************************************************// X // API: OS-X X-// Class Definitions: RtMidiOut X+// Class Definitions: MidiOutCore X //*********************************************************************// X X-unsigned int RtMidiOut :: getPortCount() X+MidiOutCore :: MidiOutCore( const std::string &clientName ) X+ : MidiOutApi() X { X- return MIDIGetNumberOfDestinations(); X+ MidiOutCore::initialize( clientName ); X } X X-std::string RtMidiOut :: getPortName( unsigned int portNumber ) X+MidiOutCore :: ~MidiOutCore( void ) X { X-// CFStringRef nameRef; X- MIDIEndpointRef portRef; X- std::ostringstream ost; X- char name[128]; X+ // Close a connection if it exists. X+ MidiOutCore::closePort(); X X- if ( portNumber >= MIDIGetNumberOfDestinations() ) { X- ost << "RtMidiOut::getPortName: 'portNumber' argument (" << portNumber << ") is invalid."; X- errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X- } X- portRef = MIDIGetDestination( portNumber ); X-#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 X- readable_name(portRef, name, sizeof(name)); X-#else X- MIDIObjectGetStringProperty( portRef, kMIDIPropertyName, &nameRef ); X- CFStringGetCString( nameRef, name, sizeof(name), 0); X- CFRelease( nameRef ); X-#endif X- std::string stringName = name; X- return stringName; X+ // Cleanup. X+ CoreMidiData *data = static_cast (apiData_); X+ MIDIClientDispose( data->client ); X+ if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); X+ delete data; X } X X-void RtMidiOut :: initialize( void ) X+void MidiOutCore :: initialize( const std::string& clientName ) X { X // Set up our client. X MIDIClientRef client; X- OSStatus result = MIDIClientCreate( CFSTR("RtMidi Output Client"), NULL, NULL, &client ); X+ CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ); X+ OSStatus result = MIDIClientCreate(name, NULL, NULL, &client ); X if ( result != noErr ) { X- errorString_ = "RtMidiOut::initialize: error creating OS-X MIDI client object."; X- error( RtMidiError::DRIVER_ERROR ); X+ std::ostringstream ost; X+ ost << "MidiInCore::initialize: error creating OS-X MIDI client object (" << result << ")."; X+ errorString_ = ost.str(); X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X // Save our api-specific connection information. X@@ -656,45 +1298,83 @@ void RtMidiOut :: initialize( void ) X data->client = client; X data->endpoint = 0; X apiData_ = (void *) data; X+ CFRelease( name ); X } X X-void RtMidiOut :: openPort( unsigned int portNumber ) X+unsigned int MidiOutCore :: getPortCount() X { X+ CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); X+ return MIDIGetNumberOfDestinations(); X+} X+ X+std::string MidiOutCore :: getPortName( unsigned int portNumber ) X+{ X+ CFStringRef nameRef; X+ MIDIEndpointRef portRef; X+ char name[128]; X+ X+ std::string stringName; X+ CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); X+ if ( portNumber >= MIDIGetNumberOfDestinations() ) { X+ std::ostringstream ost; X+ ost << "MidiOutCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; X+ errorString_ = ost.str(); X+ error( RtMidiError::WARNING, errorString_ ); X+ return stringName; X+ } X+ X+ portRef = MIDIGetDestination( portNumber ); X+ nameRef = ConnectedEndpointName(portRef); X+ CFStringGetCString( nameRef, name, sizeof(name), kCFStringEncodingUTF8 ); X+ CFRelease( nameRef ); X+ X+ return stringName = name; X+} X+ X+void MidiOutCore :: openPort( unsigned int portNumber, const std::string &portName ) X+{ X if ( connected_ ) { X- errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiOutCore::openPort: a valid connection already exists!"; X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X+ CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); X unsigned int nDest = MIDIGetNumberOfDestinations(); X if (nDest < 1) { X- errorString_ = "RtMidiOut::openPort: no MIDI output destinations found!"; X- error( RtMidiError::NO_DEVICES_FOUND ); X+ errorString_ = "MidiOutCore::openPort: no MIDI output destinations found!"; X+ error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); X+ return; X } X X- std::ostringstream ost; X if ( portNumber >= nDest ) { X- ost << "RtMidiOut::openPort: 'portNumber' argument (" << portNumber << ") is invalid."; X+ std::ostringstream ost; X+ ost << "MidiOutCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; X errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X+ error( RtMidiError::INVALID_PARAMETER, errorString_ ); X+ return; X } X X MIDIPortRef port; X CoreMidiData *data = static_cast (apiData_); X- OSStatus result = MIDIOutputPortCreate( data->client, CFSTR("RtMidi Virtual MIDI Output Port"), &port ); X+ CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); X+ OSStatus result = MIDIOutputPortCreate( data->client, portNameRef, &port ); X+ CFRelease( portNameRef ); X if ( result != noErr ) { X MIDIClientDispose( data->client ); X- errorString_ = "RtMidiOut::openPort: error creating OS-X MIDI output port."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiOutCore::openPort: error creating OS-X MIDI output port."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X // Get the desired output port identifier. X MIDIEndpointRef destination = MIDIGetDestination( portNumber ); X- if ( destination == 0 ) { // 1.3.1.0: changed from NULL to 0 X+ if ( destination == 0 ) { X MIDIPortDispose( port ); X MIDIClientDispose( data->client ); X- errorString_ = "RtMidiOut::openPort: error getting MIDI output destination reference."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiOutCore::openPort: error getting MIDI output destination reference."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X // Save our api-specific connection information. X@@ -703,78 +1383,120 @@ void RtMidiOut :: openPort( unsigned int portNumber ) X connected_ = true; X } X X-void RtMidiOut :: closePort( void ) X+void MidiOutCore :: closePort( void ) X { X- if ( connected_ ) { X- CoreMidiData *data = static_cast (apiData_); X+ CoreMidiData *data = static_cast (apiData_); X+ X+ if ( data->endpoint ) { X+ MIDIEndpointDispose( data->endpoint ); X+ data->endpoint = 0; X+ } X+ X+ if ( data->port ) { X MIDIPortDispose( data->port ); X- connected_ = false; X+ data->port = 0; X } X+ X+ connected_ = false; X } X X-void RtMidiOut :: openVirtualPort() X+void MidiOutCore :: setClientName ( const std::string& ) X { X+ X+ errorString_ = "MidiOutCore::setClientName: this function is not implemented for the MACOSX_CORE API!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ X+} X+ X+void MidiOutCore :: setPortName ( const std::string& ) X+{ X+ X+ errorString_ = "MidiOutCore::setPortName: this function is not implemented for the MACOSX_CORE API!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ X+} X+ X+void MidiOutCore :: openVirtualPort( const std::string &portName ) X+{ X CoreMidiData *data = static_cast (apiData_); X X if ( data->endpoint ) { X- errorString_ = "RtMidiOut::openVirtualPort: a virtual output port already exists!"; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiOutCore::openVirtualPort: a virtual output port already exists!"; X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X // Create a virtual MIDI output source. X MIDIEndpointRef endpoint; X- OSStatus result = MIDISourceCreate( data->client, CFSTR("RtMidi Output"), &endpoint ); X+ CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); X+ OSStatus result = MIDISourceCreate( data->client, portNameRef, &endpoint ); X+ CFRelease( portNameRef ); X+ X if ( result != noErr ) { X- errorString_ = "RtMidiOut::initialize: error creating OS-X virtual MIDI source."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiOutCore::initialize: error creating OS-X virtual MIDI source."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X // Save our api-specific connection information. X data->endpoint = endpoint; X } X X-RtMidiOut :: ~RtMidiOut() X+void MidiOutCore :: sendMessage( const unsigned char *message, size_t size ) X { X- // Close a connection if it exists. X- closePort(); X+ // We use the MIDISendSysex() function to asynchronously send sysex X+ // messages. Otherwise, we use a single CoreMidi MIDIPacket. X+ unsigned int nBytes = static_cast (size); X+ if ( nBytes == 0 ) { X+ errorString_ = "MidiOutCore::sendMessage: no data in message argument!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ return; X+ } X X- // Cleanup. X+ MIDITimeStamp timeStamp = AudioGetCurrentHostTime(); X CoreMidiData *data = static_cast (apiData_); X- MIDIClientDispose( data->client ); X- if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); X- delete data; X-} X+ OSStatus result; X X-void RtMidiOut :: sendMessage( std::vector *message ) X-{ X- unsigned int nBytes = message->size(); X- // Pad the buffer for extra (unknown) structure data. X- Byte buffer[nBytes+32]; X- MIDIPacketList *pktlist = (MIDIPacketList *) buffer; X- MIDIPacket *curPacket = MIDIPacketListInit( pktlist ); X+ if ( message[0] != 0xF0 && nBytes > 3 ) { X+ errorString_ = "MidiOutCore::sendMessage: message format problem ... not sysex but > 3 bytes?"; X+ error( RtMidiError::WARNING, errorString_ ); X+ return; X+ } X X- MIDITimeStamp timeStamp = 0; X- curPacket = MIDIPacketListAdd( pktlist, sizeof(buffer), curPacket, timeStamp, nBytes, &message->at(0) ); X+ Byte buffer[nBytes+(sizeof( MIDIPacketList ))]; X+ ByteCount listSize = sizeof( buffer ); X+ MIDIPacketList *packetList = (MIDIPacketList*)buffer; X+ MIDIPacket *packet = MIDIPacketListInit( packetList ); X X- CoreMidiData *data = static_cast (apiData_); X+ ByteCount remainingBytes = nBytes; X+ while ( remainingBytes && packet ) { X+ ByteCount bytesForPacket = remainingBytes > 65535 ? 65535 : remainingBytes; // 65535 = maximum size of a MIDIPacket X+ const Byte* dataStartPtr = (const Byte *) &message[nBytes - remainingBytes]; X+ packet = MIDIPacketListAdd( packetList, listSize, packet, timeStamp, bytesForPacket, dataStartPtr ); X+ remainingBytes -= bytesForPacket; X+ } X X+ if ( !packet ) { X+ errorString_ = "MidiOutCore::sendMessage: could not allocate packet list"; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X+ X // Send to any destinations that may have connected to us. X- OSStatus result; X if ( data->endpoint ) { X- result = MIDIReceived( data->endpoint, pktlist ); X+ result = MIDIReceived( data->endpoint, packetList ); X if ( result != noErr ) { X- errorString_ = "RtMidiOut::sendMessage: error sending MIDI to virtual destinations."; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations."; X+ error( RtMidiError::WARNING, errorString_ ); X } X } X X // And send to an explicit destination port if we're connected. X if ( connected_ ) { X- result = MIDISend( data->port, data->destinationId, pktlist ); X+ result = MIDISend( data->port, data->destinationId, packetList ); X if ( result != noErr ) { X- errorString_ = "RtMidiOut::sendMessage: error sending MIDI message to port."; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiOutCore::sendMessage: error sending MIDI message to port."; X+ error( RtMidiError::WARNING, errorString_ ); X } X } X } X@@ -789,8 +1511,7 @@ void RtMidiOut :: sendMessage( std::vector X #include X X@@ -808,32 +1533,38 @@ void RtMidiOut :: sendMessage( std::vector (ptr); X+ MidiInApi::RtMidiInData *data = static_cast (ptr); X AlsaMidiData *apiData = static_cast (data->apiData); X X long nBytes; X- unsigned long long time, lastTime; X+ double time; X bool continueSysex = false; X- RtMidiIn::MidiMessage message; X+ bool doDecode = false; X+ MidiInApi::MidiMessage message; X+ int poll_fd_count; X+ struct pollfd *poll_fds; X X snd_seq_event_t *ev; X int result; X@@ -841,63 +1572,93 @@ extern "C" void *alsaMidiHandler( void *ptr ) X result = snd_midi_event_new( 0, &apiData->coder ); X if ( result < 0 ) { X data->doInput = false; X- std::cerr << "\nRtMidiIn::alsaMidiHandler: error initializing MIDI event parser!\n\n"; X+ std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing MIDI event parser!\n\n"; X return 0; X } X unsigned char *buffer = (unsigned char *) malloc( apiData->bufferSize ); X if ( buffer == NULL ) { X data->doInput = false; X- std::cerr << "\nRtMidiIn::alsaMidiHandler: error initializing buffer memory!\n\n"; X+ snd_midi_event_free( apiData->coder ); X+ apiData->coder = 0; X+ std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing buffer memory!\n\n"; X return 0; X } X snd_midi_event_init( apiData->coder ); X snd_midi_event_no_status( apiData->coder, 1 ); // suppress running status messages X X+ poll_fd_count = snd_seq_poll_descriptors_count( apiData->seq, POLLIN ) + 1; X+ poll_fds = (struct pollfd*)alloca( poll_fd_count * sizeof( struct pollfd )); X+ snd_seq_poll_descriptors( apiData->seq, poll_fds + 1, poll_fd_count - 1, POLLIN ); X+ poll_fds[0].fd = apiData->trigger_fds[0]; X+ poll_fds[0].events = POLLIN; X+ X while ( data->doInput ) { X X if ( snd_seq_event_input_pending( apiData->seq, 1 ) == 0 ) { X- // No data pending ... sleep a bit. X- usleep( 1000 ); X+ // No data pending X+ if ( poll( poll_fds, poll_fd_count, -1) >= 0 ) { X+ if ( poll_fds[0].revents & POLLIN ) { X+ bool dummy; X+ int res = read( poll_fds[0].fd, &dummy, sizeof(dummy) ); X+ (void) res; X+ } X+ } X continue; X } X X // If here, there should be data. X result = snd_seq_event_input( apiData->seq, &ev ); X if ( result == -ENOSPC ) { X- std::cerr << "\nRtMidiIn::alsaMidiHandler: MIDI input buffer overrun!\n\n"; X+ std::cerr << "\nMidiInAlsa::alsaMidiHandler: MIDI input buffer overrun!\n\n"; X continue; X } X else if ( result <= 0 ) { X- std::cerr << "RtMidiIn::alsaMidiHandler: unknown MIDI input error!\n"; X+ std::cerr << "\nMidiInAlsa::alsaMidiHandler: unknown MIDI input error!\n"; X+ perror("System reports"); X continue; X } X X // This is a bit weird, but we now have to decode an ALSA MIDI X // event (back) into MIDI bytes. We'll ignore non-MIDI types. X- message.bytes.clear(); X+ if ( !continueSysex ) message.bytes.clear(); X+ X+ doDecode = false; X switch ( ev->type ) { X X- case SND_SEQ_EVENT_PORT_SUBSCRIBED: X+ case SND_SEQ_EVENT_PORT_SUBSCRIBED: X #if defined(__RTMIDI_DEBUG__) X- std::cout << "RtMidiIn::alsaMidiHandler: port connection made!\n"; X+ std::cout << "MidiInAlsa::alsaMidiHandler: port connection made!\n"; X #endif X break; X X- case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: X- std::cerr << "RtMidiIn::alsaMidiHandler: port connection has closed!\n"; X- data->doInput = false; X+ case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: X+#if defined(__RTMIDI_DEBUG__) X+ std::cerr << "MidiInAlsa::alsaMidiHandler: port connection has closed!\n"; X+ std::cout << "sender = " << (int) ev->data.connect.sender.client << ":" X+ << (int) ev->data.connect.sender.port X+ << ", dest = " << (int) ev->data.connect.dest.client << ":" X+ << (int) ev->data.connect.dest.port X+ << std::endl; X+#endif X break; X X case SND_SEQ_EVENT_QFRAME: // MIDI time code X- if ( data->ignoreFlags & 0x02 ) break; X+ if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true; X+ break; X X- case SND_SEQ_EVENT_TICK: // MIDI timing tick X- if ( data->ignoreFlags & 0x02 ) break; X+ case SND_SEQ_EVENT_TICK: // 0xF9 ... MIDI timing tick X+ if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true; X+ break; X X+ case SND_SEQ_EVENT_CLOCK: // 0xF8 ... MIDI timing (clock) tick X+ if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true; X+ break; X+ X case SND_SEQ_EVENT_SENSING: // Active sensing X- if ( data->ignoreFlags & 0x04 ) break; X+ if ( !( data->ignoreFlags & 0x04 ) ) doDecode = true; X+ break; X X- case SND_SEQ_EVENT_SYSEX: X+ case SND_SEQ_EVENT_SYSEX: X if ( (data->ignoreFlags & 0x01) ) break; X if ( ev->data.ext.len > apiData->bufferSize ) { X apiData->bufferSize = ev->data.ext.len; X@@ -905,58 +1666,85 @@ extern "C" void *alsaMidiHandler( void *ptr ) X buffer = (unsigned char *) malloc( apiData->bufferSize ); X if ( buffer == NULL ) { X data->doInput = false; X- std::cerr << "\nRtMidiIn::alsaMidiHandler: error resizing buffer memory!\n\n"; X+ std::cerr << "\nMidiInAlsa::alsaMidiHandler: error resizing buffer memory!\n\n"; X break; X } X } X+ doDecode = true; X+ break; X X default: X+ doDecode = true; X+ } X+ X+ if ( doDecode ) { X+ X nBytes = snd_midi_event_decode( apiData->coder, buffer, apiData->bufferSize, ev ); X- if ( nBytes <= 0 ) { X-#if defined(__RTMIDI_DEBUG__) X- std::cerr << "\nRtMidiIn::alsaMidiHandler: event parsing error or not a MIDI event!\n\n"; X-#endif X- break; X- } X+ if ( nBytes > 0 ) { X+ // The ALSA sequencer has a maximum buffer size for MIDI sysex X+ // events of 256 bytes. If a device sends sysex messages larger X+ // than this, they are segmented into 256 byte chunks. So, X+ // we'll watch for this and concatenate sysex chunks into a X+ // single sysex message if necessary. X+ if ( !continueSysex ) X+ message.bytes.assign( buffer, &buffer[nBytes] ); X+ else X+ message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] ); X X- // The ALSA sequencer has a maximum buffer size for MIDI sysex X- // events of 256 bytes. If a device sends sysex messages larger X- // than this, they are segmented into 256 byte chunks. So, X- // we'll watch for this and concatenate sysex chunks into a X- // single sysex message if necessary. X- if ( !continueSysex ) X- message.bytes.assign( buffer, &buffer[nBytes] ); X- else X- message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] ); X+ continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) ); X+ if ( !continueSysex ) { X X- if ( ev->type == SND_SEQ_EVENT_SYSEX && message.bytes.back() == 0xF7 ) X- continueSysex = false; X- else { X- continueSysex = true; X- break; X- } X+ // Calculate the time stamp: X+ message.timeStamp = 0.0; X X- // Calculate the time stamp: X- message.timeStamp = 0.0; X+ // Method 1: Use the system time. X+ //(void)gettimeofday(&tv, (struct timezone *)NULL); X+ //time = (tv.tv_sec * 1000000) + tv.tv_usec; X X- // Method 1: Use the system time. X- //(void)gettimeofday(&tv, (struct timezone *)NULL); X- //time = (tv.tv_sec * 1000000) + tv.tv_usec; X+ // Method 2: Use the ALSA sequencer event time data. X+ // (thanks to Pedro Lopez-Cabanillas!). X X- // Method 2: Use the ALSA sequencer event time data. X- // (thanks to Pedro Lopez-Cabanillas!). X- time = ( ev->time.time.tv_sec * 1000000 ) + ( ev->time.time.tv_nsec/1000 ); X- lastTime = time; X- time -= apiData->lastTime; X- apiData->lastTime = lastTime; X- if ( data->firstMessage == true ) X- data->firstMessage = false; X- else X- message.timeStamp = time * 0.000001; X+ // Using method from: X+ // https://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html X+ X+ // Perform the carry for the later subtraction by updating y. X+ // Temp var y is timespec because computation requires signed types, X+ // while snd_seq_real_time_t has unsigned types. X+ snd_seq_real_time_t &x( ev->time.time ); X+ struct timespec y; X+ y.tv_nsec = apiData->lastTime.tv_nsec; X+ y.tv_sec = apiData->lastTime.tv_sec; X+ if ( x.tv_nsec < y.tv_nsec ) { X+ int nsec = (y.tv_nsec - (int)x.tv_nsec) / 1000000000 + 1; X+ y.tv_nsec -= 1000000000 * nsec; X+ y.tv_sec += nsec; X+ } X+ if ( x.tv_nsec - y.tv_nsec > 1000000000 ) { X+ int nsec = ((int)x.tv_nsec - y.tv_nsec) / 1000000000; X+ y.tv_nsec += 1000000000 * nsec; X+ y.tv_sec -= nsec; X+ } X+ X+ // Compute the time difference. X+ time = (int)x.tv_sec - y.tv_sec + ((int)x.tv_nsec - y.tv_nsec)*1e-9; X+ X+ apiData->lastTime = ev->time.time; X+ X+ if ( data->firstMessage == true ) X+ data->firstMessage = false; X+ else X+ message.timeStamp = time; X+ } X+ else { X+#if defined(__RTMIDI_DEBUG__) X+ std::cerr << "\nMidiInAlsa::alsaMidiHandler: event parsing error or not a MIDI event!\n\n"; X+#endif X+ } X+ } X } X X- snd_seq_free_event(ev); X- if ( message.bytes.size() == 0 ) continue; X+ snd_seq_free_event( ev ); X+ if ( message.bytes.size() == 0 || continueSysex ) continue; X X if ( data->usingCallback ) { X RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; X@@ -964,106 +1752,201 @@ extern "C" void *alsaMidiHandler( void *ptr ) X } X else { X // As long as we haven't reached our queue size limit, push the message. X- if ( data->queueLimit > data->queue.size() ) X- data->queue.push( message ); X- else X- std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; X+ if ( !data->queue.push( message ) ) X+ std::cerr << "\nMidiInAlsa: message queue limit reached!!\n\n"; X } X } X X if ( buffer ) free( buffer ); X snd_midi_event_free( apiData->coder ); X apiData->coder = 0; X+ apiData->thread = apiData->dummy_thread_id; X return 0; X } X X-void RtMidiIn :: initialize( void ) X+MidiInAlsa :: MidiInAlsa( const std::string &clientName, unsigned int queueSizeLimit ) X+ : MidiInApi( queueSizeLimit ) X { X+ MidiInAlsa::initialize( clientName ); X+} X+ X+MidiInAlsa :: ~MidiInAlsa() X+{ X+ // Close a connection if it exists. X+ MidiInAlsa::closePort(); X+ X+ // Shutdown the input thread. X+ AlsaMidiData *data = static_cast (apiData_); X+ if ( inputData_.doInput ) { X+ inputData_.doInput = false; X+ int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof( inputData_.doInput ) ); X+ (void) res; X+ if ( !pthread_equal(data->thread, data->dummy_thread_id) ) X+ pthread_join( data->thread, NULL ); X+ } X+ X+ // Cleanup. X+ close ( data->trigger_fds[0] ); X+ close ( data->trigger_fds[1] ); X+ if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); X+#ifndef AVOID_TIMESTAMPING X+ snd_seq_free_queue( data->seq, data->queue_id ); X+#endif X+ snd_seq_close( data->seq ); X+ delete data; X+} X+ X+void MidiInAlsa :: initialize( const std::string& clientName ) X+{ X // Set up the ALSA sequencer client. X- snd_seq_t *seq; X- int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK); X+ snd_seq_t *seq; X+ int result = snd_seq_open( &seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK ); X if ( result < 0 ) { X- errorString_ = "RtMidiIn::initialize: error creating ALSA sequencer input client object."; X- error( RtMidiError::DRIVER_ERROR ); X- } X+ errorString_ = "MidiInAlsa::initialize: error creating ALSA sequencer client object."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X X // Set client name. X- snd_seq_set_client_name(seq, "RtMidi Input Client"); X+ snd_seq_set_client_name( seq, clientName.c_str() ); X X // Save our api-specific connection information. X AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; X data->seq = seq; X+ data->portNum = -1; X data->vport = -1; X+ data->subscription = 0; X+ data->dummy_thread_id = pthread_self(); X+ data->thread = data->dummy_thread_id; X+ data->trigger_fds[0] = -1; X+ data->trigger_fds[1] = -1; X apiData_ = (void *) data; X inputData_.apiData = (void *) data; X X+ if ( pipe(data->trigger_fds) == -1 ) { X+ errorString_ = "MidiInAlsa::initialize: error creating pipe objects."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X+ X // Create the input queue X- data->queue_id = snd_seq_alloc_named_queue(seq, "RtMidi Queue"); X+#ifndef AVOID_TIMESTAMPING X+ data->queue_id = snd_seq_alloc_named_queue( seq, "RtMidi Queue" ); X // Set arbitrary tempo (mm=100) and resolution (240) X snd_seq_queue_tempo_t *qtempo; X- snd_seq_queue_tempo_alloca(&qtempo); X- snd_seq_queue_tempo_set_tempo(qtempo, 600000); X- snd_seq_queue_tempo_set_ppq(qtempo, 240); X- snd_seq_set_queue_tempo(data->seq, data->queue_id, qtempo); X- snd_seq_drain_output(data->seq); X+ snd_seq_queue_tempo_alloca( &qtempo ); X+ snd_seq_queue_tempo_set_tempo( qtempo, 600000 ); X+ snd_seq_queue_tempo_set_ppq( qtempo, 240 ); X+ snd_seq_set_queue_tempo( data->seq, data->queue_id, qtempo ); X+ snd_seq_drain_output( data->seq ); X+#endif X } X X // This function is used to count or get the pinfo structure for a given port number. X unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int type, int portNumber ) X { X- snd_seq_client_info_t *cinfo; X+ snd_seq_client_info_t *cinfo; X int client; X int count = 0; X- snd_seq_client_info_alloca( &cinfo ); X+ snd_seq_client_info_alloca( &cinfo ); X X- snd_seq_client_info_set_client( cinfo, -1 ); X- while ( snd_seq_query_next_client( seq, cinfo ) >= 0 ) { X+ snd_seq_client_info_set_client( cinfo, -1 ); X+ while ( snd_seq_query_next_client( seq, cinfo ) >= 0 ) { X client = snd_seq_client_info_get_client( cinfo ); X if ( client == 0 ) continue; X- // Reset query info X- snd_seq_port_info_set_client( pinfo, client ); X- snd_seq_port_info_set_port( pinfo, -1 ); X- while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) { X- if ( !PORT_TYPE( pinfo, type ) ) continue; X+ // Reset query info X+ snd_seq_port_info_set_client( pinfo, client ); X+ snd_seq_port_info_set_port( pinfo, -1 ); X+ while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) { X+ unsigned int atyp = snd_seq_port_info_get_type( pinfo ); X+ if ( ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) && X+ ( ( atyp & SND_SEQ_PORT_TYPE_SYNTH ) == 0 ) && X+ ( ( atyp & SND_SEQ_PORT_TYPE_APPLICATION ) == 0 ) ) continue; X+ X+ unsigned int caps = snd_seq_port_info_get_capability( pinfo ); X+ if ( ( caps & type ) != type ) continue; X if ( count == portNumber ) return 1; X- count++; X- } X+ ++count; X } X+ } X X // If a negative portNumber was used, return the port count. X if ( portNumber < 0 ) return count; X return 0; X } X X-void RtMidiIn :: openPort( unsigned int portNumber ) X+unsigned int MidiInAlsa :: getPortCount() X { X+ snd_seq_port_info_t *pinfo; X+ snd_seq_port_info_alloca( &pinfo ); X+ X+ AlsaMidiData *data = static_cast (apiData_); X+ return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 ); X+} X+ X+std::string MidiInAlsa :: getPortName( unsigned int portNumber ) X+{ X+ snd_seq_client_info_t *cinfo; X+ snd_seq_port_info_t *pinfo; X+ snd_seq_client_info_alloca( &cinfo ); X+ snd_seq_port_info_alloca( &pinfo ); X+ X+ std::string stringName; X+ AlsaMidiData *data = static_cast (apiData_); X+ if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) { X+ int cnum = snd_seq_port_info_get_client( pinfo ); X+ snd_seq_get_any_client_info( data->seq, cnum, cinfo ); X+ std::ostringstream os; X+ os << snd_seq_client_info_get_name( cinfo ); X+ os << ":"; X+ os << snd_seq_port_info_get_name( pinfo ); X+ os << " "; // These lines added to make sure devices are listed X+ os << snd_seq_port_info_get_client( pinfo ); // with full portnames added to ensure individual device names X+ os << ":"; X+ os << snd_seq_port_info_get_port( pinfo ); X+ stringName = os.str(); X+ return stringName; X+ } X+ X+ // If we get here, we didn't find a match. X+ errorString_ = "MidiInAlsa::getPortName: error looking for port name!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ return stringName; X+} X+ X+void MidiInAlsa :: openPort( unsigned int portNumber, const std::string &portName ) X+{ X if ( connected_ ) { X- errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiInAlsa::openPort: a valid connection already exists!"; X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X unsigned int nSrc = this->getPortCount(); X- if (nSrc < 1) { X- errorString_ = "RtMidiIn::openPort: no MIDI input sources found!"; X- error( RtMidiError::NO_DEVICES_FOUND ); X+ if ( nSrc < 1 ) { X+ errorString_ = "MidiInAlsa::openPort: no MIDI input sources found!"; X+ error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); X+ return; X } X X- snd_seq_port_info_t *pinfo; X- snd_seq_port_info_alloca( &pinfo ); X- std::ostringstream ost; X+ snd_seq_port_info_t *src_pinfo; X+ snd_seq_port_info_alloca( &src_pinfo ); X AlsaMidiData *data = static_cast (apiData_); X- if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) { X- ost << "RtMidiIn::openPort: 'portNumber' argument (" << portNumber << ") is invalid."; X+ if ( portInfo( data->seq, src_pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) { X+ std::ostringstream ost; X+ ost << "MidiInAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; X errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X+ error( RtMidiError::INVALID_PARAMETER, errorString_ ); X+ return; X } X X- X snd_seq_addr_t sender, receiver; X- sender.client = snd_seq_port_info_get_client( pinfo ); X- sender.port = snd_seq_port_info_get_port( pinfo ); X+ sender.client = snd_seq_port_info_get_client( src_pinfo ); X+ sender.port = snd_seq_port_info_get_port( src_pinfo ); X receiver.client = snd_seq_client_id( data->seq ); X+ X+ snd_seq_port_info_t *pinfo; X+ snd_seq_port_info_alloca( &pinfo ); X if ( data->vport < 0 ) { X snd_seq_port_info_set_client( pinfo, 0 ); X snd_seq_port_info_set_port( pinfo, 0 ); X@@ -1074,253 +1957,306 @@ void RtMidiIn :: openPort( unsigned int portNumber ) X SND_SEQ_PORT_TYPE_MIDI_GENERIC | X SND_SEQ_PORT_TYPE_APPLICATION ); X snd_seq_port_info_set_midi_channels(pinfo, 16); X- snd_seq_port_info_set_timestamping(pinfo, 1); X- snd_seq_port_info_set_timestamp_real(pinfo, 1); X- snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id); X- snd_seq_port_info_set_name(pinfo, "RtMidi Input"); X- data->vport = snd_seq_create_port(data->seq, pinfo); X- X+#ifndef AVOID_TIMESTAMPING X+ snd_seq_port_info_set_timestamping( pinfo, 1 ); X+ snd_seq_port_info_set_timestamp_real( pinfo, 1 ); X+ snd_seq_port_info_set_timestamp_queue( pinfo, data->queue_id ); X+#endif X+ snd_seq_port_info_set_name( pinfo, portName.c_str() ); X+ data->vport = snd_seq_create_port( data->seq, pinfo ); X+ X if ( data->vport < 0 ) { X- errorString_ = "RtMidiIn::openPort: ALSA error creating input port."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiInAlsa::openPort: ALSA error creating input port."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X+ data->vport = snd_seq_port_info_get_port( pinfo ); X } X X receiver.port = data->vport; X X- // Make subscription X- snd_seq_port_subscribe_malloc( &data->subscription ); X- snd_seq_port_subscribe_set_sender(data->subscription, &sender); X- snd_seq_port_subscribe_set_dest(data->subscription, &receiver); X- if ( snd_seq_subscribe_port(data->seq, data->subscription) ) { X- errorString_ = "RtMidiIn::openPort: ALSA error making port connection."; X- error( RtMidiError::DRIVER_ERROR ); X+ if ( !data->subscription ) { X+ // Make subscription X+ if ( snd_seq_port_subscribe_malloc( &data->subscription ) < 0 ) { X+ errorString_ = "MidiInAlsa::openPort: ALSA error allocation port subscription."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X+ snd_seq_port_subscribe_set_sender( data->subscription, &sender ); X+ snd_seq_port_subscribe_set_dest( data->subscription, &receiver ); X+ if ( snd_seq_subscribe_port( data->seq, data->subscription ) ) { X+ snd_seq_port_subscribe_free( data->subscription ); X+ data->subscription = 0; X+ errorString_ = "MidiInAlsa::openPort: ALSA error making port connection."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X } X X if ( inputData_.doInput == false ) { X // Start the input queue X+#ifndef AVOID_TIMESTAMPING X snd_seq_start_queue( data->seq, data->queue_id, NULL ); X snd_seq_drain_output( data->seq ); X+#endif X // Start our MIDI input thread. X pthread_attr_t attr; X- pthread_attr_init(&attr); X- // chuck X- //pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); X- //pthread_attr_setschedpolicy(&attr, SCHED_OTHER); X+ pthread_attr_init( &attr ); X+ pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); X+ pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); X X inputData_.doInput = true; X- int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_); X- pthread_attr_destroy(&attr); X- if (err) { X+ int err = pthread_create( &data->thread, &attr, alsaMidiHandler, &inputData_ ); X+ pthread_attr_destroy( &attr ); X+ if ( err ) { X snd_seq_unsubscribe_port( data->seq, data->subscription ); X snd_seq_port_subscribe_free( data->subscription ); X+ data->subscription = 0; X inputData_.doInput = false; X- errorString_ = "RtMidiIn::openPort: error starting MIDI input thread!"; X- error( RtMidiError::THREAD_ERROR ); X+ errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!"; X+ error( RtMidiError::THREAD_ERROR, errorString_ ); X+ return; X } X } X X connected_ = true; X } X X-void RtMidiIn :: openVirtualPort() X+void MidiInAlsa :: openVirtualPort( const std::string &portName ) X { X AlsaMidiData *data = static_cast (apiData_); X if ( data->vport < 0 ) { X snd_seq_port_info_t *pinfo; X snd_seq_port_info_alloca( &pinfo ); X snd_seq_port_info_set_capability( pinfo, X- SND_SEQ_PORT_CAP_WRITE | X- SND_SEQ_PORT_CAP_SUBS_WRITE ); X+ SND_SEQ_PORT_CAP_WRITE | X+ SND_SEQ_PORT_CAP_SUBS_WRITE ); X snd_seq_port_info_set_type( pinfo, X- SND_SEQ_PORT_TYPE_MIDI_GENERIC | X- SND_SEQ_PORT_TYPE_APPLICATION ); X- snd_seq_port_info_set_midi_channels(pinfo, 16); X- snd_seq_port_info_set_timestamping(pinfo, 1); X- snd_seq_port_info_set_timestamp_real(pinfo, 1); X- snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id); X- snd_seq_port_info_set_name(pinfo, "RtMidi Input"); X- data->vport = snd_seq_create_port(data->seq, pinfo); X+ SND_SEQ_PORT_TYPE_MIDI_GENERIC | X+ SND_SEQ_PORT_TYPE_APPLICATION ); X+ snd_seq_port_info_set_midi_channels( pinfo, 16 ); X+#ifndef AVOID_TIMESTAMPING X+ snd_seq_port_info_set_timestamping( pinfo, 1 ); X+ snd_seq_port_info_set_timestamp_real( pinfo, 1 ); X+ snd_seq_port_info_set_timestamp_queue( pinfo, data->queue_id ); X+#endif X+ snd_seq_port_info_set_name( pinfo, portName.c_str() ); X+ data->vport = snd_seq_create_port( data->seq, pinfo ); X X if ( data->vport < 0 ) { X- errorString_ = "RtMidiIn::openVirtualPort: ALSA error creating virtual port."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiInAlsa::openVirtualPort: ALSA error creating virtual port."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X+ data->vport = snd_seq_port_info_get_port( pinfo ); X } X X if ( inputData_.doInput == false ) { X+ // Wait for old thread to stop, if still running X+ if ( !pthread_equal( data->thread, data->dummy_thread_id ) ) X+ pthread_join( data->thread, NULL ); X+ X // Start the input queue X+#ifndef AVOID_TIMESTAMPING X snd_seq_start_queue( data->seq, data->queue_id, NULL ); X snd_seq_drain_output( data->seq ); X+#endif X // Start our MIDI input thread. X pthread_attr_t attr; X- pthread_attr_init(&attr); X- // chuck X- //pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); X- //pthread_attr_setschedpolicy(&attr, SCHED_OTHER); X+ pthread_attr_init( &attr ); X+ pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); X+ pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); X X inputData_.doInput = true; X- int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_); X- pthread_attr_destroy(&attr); X- if (err) { X- snd_seq_unsubscribe_port( data->seq, data->subscription ); X- snd_seq_port_subscribe_free( data->subscription ); X+ int err = pthread_create( &data->thread, &attr, alsaMidiHandler, &inputData_ ); X+ pthread_attr_destroy( &attr ); X+ if ( err ) { X+ if ( data->subscription ) { X+ snd_seq_unsubscribe_port( data->seq, data->subscription ); X+ snd_seq_port_subscribe_free( data->subscription ); X+ data->subscription = 0; X+ } X inputData_.doInput = false; X- errorString_ = "RtMidiIn::openPort: error starting MIDI input thread!"; X- error( RtMidiError::THREAD_ERROR ); X+ errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!"; X+ error( RtMidiError::THREAD_ERROR, errorString_ ); X+ return; X } X } X } X X-void RtMidiIn :: closePort( void ) X+void MidiInAlsa :: closePort( void ) X { X+ AlsaMidiData *data = static_cast (apiData_); X+ X if ( connected_ ) { X- AlsaMidiData *data = static_cast (apiData_); X- snd_seq_unsubscribe_port( data->seq, data->subscription ); X- snd_seq_port_subscribe_free( data->subscription ); X+ if ( data->subscription ) { X+ snd_seq_unsubscribe_port( data->seq, data->subscription ); X+ snd_seq_port_subscribe_free( data->subscription ); X+ data->subscription = 0; X+ } X // Stop the input queue X+#ifndef AVOID_TIMESTAMPING X snd_seq_stop_queue( data->seq, data->queue_id, NULL ); X snd_seq_drain_output( data->seq ); X+#endif X connected_ = false; X } X-} X X-RtMidiIn :: ~RtMidiIn() X-{ X- // Close a connection if it exists. X- closePort(); X- X- // Shutdown the input thread. X- AlsaMidiData *data = static_cast (apiData_); X+ // Stop thread to avoid triggering the callback, while the port is intended to be closed X if ( inputData_.doInput ) { X inputData_.doInput = false; X- pthread_join( data->thread, NULL ); X+ int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof( inputData_.doInput ) ); X+ (void) res; X+ if ( !pthread_equal( data->thread, data->dummy_thread_id ) ) X+ pthread_join( data->thread, NULL ); X } X- X- // Cleanup. X- if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); X- snd_seq_free_queue( data->seq, data->queue_id ); X- snd_seq_close( data->seq ); X- delete data; X } X X-unsigned int RtMidiIn :: getPortCount() X+void MidiInAlsa :: setClientName( const std::string &clientName ) X { X- snd_seq_port_info_t *pinfo; X- snd_seq_port_info_alloca( &pinfo ); X X- AlsaMidiData *data = static_cast (apiData_); X- return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 ); X+ AlsaMidiData *data = static_cast ( apiData_ ); X+ snd_seq_set_client_name( data->seq, clientName.c_str() ); X+ X } X X-std::string RtMidiIn :: getPortName( unsigned int portNumber ) X+void MidiInAlsa :: setPortName( const std::string &portName ) X { X- snd_seq_port_info_t *pinfo; X- snd_seq_port_info_alloca( &pinfo ); X- X AlsaMidiData *data = static_cast (apiData_); X- if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) { X- std::string stringName = std::string( snd_seq_port_info_get_name( pinfo ) ); X- return stringName; X- } X- X- // If we get here, we didn't find a match. X- errorString_ = "RtMidiIn::getPortName: error looking for port name!"; X- error( RtMidiError::INVALID_PARAMETER ); X- return 0; X+ snd_seq_port_info_t *pinfo; X+ snd_seq_port_info_alloca( &pinfo ); X+ snd_seq_get_port_info( data->seq, data->vport, pinfo ); X+ snd_seq_port_info_set_name( pinfo, portName.c_str() ); X+ snd_seq_set_port_info( data->seq, data->vport, pinfo ); X } X X //*********************************************************************// X // API: LINUX ALSA X-// Class Definitions: RtMidiOut X+// Class Definitions: MidiOutAlsa X //*********************************************************************// X X-unsigned int RtMidiOut :: getPortCount() X+MidiOutAlsa :: MidiOutAlsa( const std::string &clientName ) : MidiOutApi() X { X- snd_seq_port_info_t *pinfo; X- snd_seq_port_info_alloca( &pinfo ); X- X- AlsaMidiData *data = static_cast (apiData_); X- return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, -1 ); X+ MidiOutAlsa::initialize( clientName ); X } X X-std::string RtMidiOut :: getPortName( unsigned int portNumber ) X+MidiOutAlsa :: ~MidiOutAlsa() X { X- snd_seq_port_info_t *pinfo; X- snd_seq_port_info_alloca( &pinfo ); X+ // Close a connection if it exists. X+ MidiOutAlsa::closePort(); X X+ // Cleanup. X AlsaMidiData *data = static_cast (apiData_); X- if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) ) { X- std::string stringName = std::string( snd_seq_port_info_get_name( pinfo ) ); X- return stringName; X- } X- X- // If we get here, we didn't find a match. X- errorString_ = "RtMidiOut::getPortName: error looking for port name!"; X- error( RtMidiError::INVALID_PARAMETER ); X- return 0; X+ if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); X+ if ( data->coder ) snd_midi_event_free( data->coder ); X+ if ( data->buffer ) free( data->buffer ); X+ snd_seq_close( data->seq ); X+ delete data; X } X X-void RtMidiOut :: initialize( void ) X+void MidiOutAlsa :: initialize( const std::string& clientName ) X { X // Set up the ALSA sequencer client. X- snd_seq_t *seq; X- int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_OUTPUT, 0); X- if ( result < 0 ) { X- errorString_ = "RtMidiOut::initialize: error creating ALSA sequencer client object."; X- error( RtMidiError::DRIVER_ERROR ); X- } X+ snd_seq_t *seq; X+ int result1 = snd_seq_open( &seq, "default", SND_SEQ_OPEN_OUTPUT, SND_SEQ_NONBLOCK ); X+ if ( result1 < 0 ) { X+ errorString_ = "MidiOutAlsa::initialize: error creating ALSA sequencer client object."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X X // Set client name. X- snd_seq_set_client_name(seq, "RtMidi Output Client"); X+ snd_seq_set_client_name( seq, clientName.c_str() ); X X // Save our api-specific connection information. X AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; X data->seq = seq; X+ data->portNum = -1; X data->vport = -1; X data->bufferSize = 32; X data->coder = 0; X data->buffer = 0; X- result = snd_midi_event_new( data->bufferSize, &data->coder ); X+ int result = snd_midi_event_new( data->bufferSize, &data->coder ); X if ( result < 0 ) { X delete data; X- data = NULL; // 1.4.1.0 (ge) X- errorString_ = "RtMidiOut::initialize: error initializing MIDI event parser!\n\n"; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiOutAlsa::initialize: error initializing MIDI event parser!\n\n"; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X data->buffer = (unsigned char *) malloc( data->bufferSize ); X if ( data->buffer == NULL ) { X delete data; X- data = NULL; // 1.4.1.0 (ge) X- errorString_ = "RtMidiOut::initialize: error allocating buffer memory!\n\n"; X- error( RtMidiError::MEMORY_ERROR ); X+ errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n"; X+ error( RtMidiError::MEMORY_ERROR, errorString_ ); X+ return; X } X snd_midi_event_init( data->coder ); X apiData_ = (void *) data; X } X X-void RtMidiOut :: openPort( unsigned int portNumber ) X+unsigned int MidiOutAlsa :: getPortCount() X { X+ snd_seq_port_info_t *pinfo; X+ snd_seq_port_info_alloca( &pinfo ); X+ X+ AlsaMidiData *data = static_cast (apiData_); X+ return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, -1 ); X+} X+ X+std::string MidiOutAlsa :: getPortName( unsigned int portNumber ) X+{ X+ snd_seq_client_info_t *cinfo; X+ snd_seq_port_info_t *pinfo; X+ snd_seq_client_info_alloca( &cinfo ); X+ snd_seq_port_info_alloca( &pinfo ); X+ X+ std::string stringName; X+ AlsaMidiData *data = static_cast (apiData_); X+ if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) ) { X+ int cnum = snd_seq_port_info_get_client( pinfo ); X+ snd_seq_get_any_client_info( data->seq, cnum, cinfo ); X+ std::ostringstream os; X+ os << snd_seq_client_info_get_name( cinfo ); X+ os << ":"; X+ os << snd_seq_port_info_get_name( pinfo ); X+ os << " "; // These lines added to make sure devices are listed X+ os << snd_seq_port_info_get_client( pinfo ); // with full portnames added to ensure individual device names X+ os << ":"; X+ os << snd_seq_port_info_get_port( pinfo ); X+ stringName = os.str(); X+ return stringName; X+ } X+ X+ // If we get here, we didn't find a match. X+ errorString_ = "MidiOutAlsa::getPortName: error looking for port name!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ return stringName; X+} X+ X+void MidiOutAlsa :: openPort( unsigned int portNumber, const std::string &portName ) X+{ X if ( connected_ ) { X- errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiOutAlsa::openPort: a valid connection already exists!"; X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X unsigned int nSrc = this->getPortCount(); X- if (nSrc < 1) { X- errorString_ = "RtMidiOut::openPort: no MIDI output sources found!"; X- error( RtMidiError::NO_DEVICES_FOUND ); X+ if ( nSrc < 1 ) { X+ errorString_ = "MidiOutAlsa::openPort: no MIDI output sources found!"; X+ error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); X+ return; X } X X- snd_seq_port_info_t *pinfo; X- snd_seq_port_info_alloca( &pinfo ); X- std::ostringstream ost; X+ snd_seq_port_info_t *pinfo; X+ snd_seq_port_info_alloca( &pinfo ); X AlsaMidiData *data = static_cast (apiData_); X if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) == 0 ) { X- ost << "RtMidiOut::openPort: 'portNumber' argument (" << portNumber << ") is invalid."; X+ std::ostringstream ost; X+ ost << "MidiOutAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; X errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X+ error( RtMidiError::INVALID_PARAMETER, errorString_ ); X+ return; X } X X snd_seq_addr_t sender, receiver; X@@ -1329,527 +2265,155 @@ void RtMidiOut :: openPort( unsigned int portNumber ) X sender.client = snd_seq_client_id( data->seq ); X X if ( data->vport < 0 ) { X- data->vport = snd_seq_create_simple_port( data->seq, "RtMidi Output", X+ data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(), X SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, X- SND_SEQ_PORT_TYPE_MIDI_GENERIC ); X+ SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION ); X if ( data->vport < 0 ) { X- errorString_ = "RtMidiOut::openPort: ALSA error creating output port."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiOutAlsa::openPort: ALSA error creating output port."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X } X X sender.port = data->vport; X X // Make subscription X- snd_seq_port_subscribe_malloc( &data->subscription ); X- snd_seq_port_subscribe_set_sender(data->subscription, &sender); X- snd_seq_port_subscribe_set_dest(data->subscription, &receiver); X- snd_seq_port_subscribe_set_time_update(data->subscription, 1); X- snd_seq_port_subscribe_set_time_real(data->subscription, 1); X- if ( snd_seq_subscribe_port(data->seq, data->subscription) ) { X- errorString_ = "RtMidiOut::openPort: ALSA error making port connection."; X- error( RtMidiError::DRIVER_ERROR ); X+ if ( snd_seq_port_subscribe_malloc( &data->subscription ) < 0 ) { X+ snd_seq_port_subscribe_free( data->subscription ); X+ errorString_ = "MidiOutAlsa::openPort: error allocating port subscription."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X+ snd_seq_port_subscribe_set_sender( data->subscription, &sender ); X+ snd_seq_port_subscribe_set_dest( data->subscription, &receiver ); X+ snd_seq_port_subscribe_set_time_update( data->subscription, 1 ); X+ snd_seq_port_subscribe_set_time_real( data->subscription, 1 ); X+ if ( snd_seq_subscribe_port( data->seq, data->subscription ) ) { X+ snd_seq_port_subscribe_free( data->subscription ); X+ errorString_ = "MidiOutAlsa::openPort: ALSA error making port connection."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X X connected_ = true; X } X X-void RtMidiOut :: closePort( void ) X+void MidiOutAlsa :: closePort( void ) X { X if ( connected_ ) { X AlsaMidiData *data = static_cast (apiData_); X snd_seq_unsubscribe_port( data->seq, data->subscription ); X snd_seq_port_subscribe_free( data->subscription ); X+ data->subscription = 0; X connected_ = false; X } X } X X-void RtMidiOut :: openVirtualPort() X+void MidiOutAlsa :: setClientName( const std::string &clientName ) X { X+ X+ AlsaMidiData *data = static_cast ( apiData_ ); X+ snd_seq_set_client_name( data->seq, clientName.c_str() ); X+ X+} X+ X+void MidiOutAlsa :: setPortName( const std::string &portName ) X+{ X AlsaMidiData *data = static_cast (apiData_); X+ snd_seq_port_info_t *pinfo; X+ snd_seq_port_info_alloca( &pinfo ); X+ snd_seq_get_port_info( data->seq, data->vport, pinfo ); X+ snd_seq_port_info_set_name( pinfo, portName.c_str() ); X+ snd_seq_set_port_info( data->seq, data->vport, pinfo ); X+} X+ X+void MidiOutAlsa :: openVirtualPort( const std::string &portName ) X+{ X+ AlsaMidiData *data = static_cast (apiData_); X if ( data->vport < 0 ) { X- data->vport = snd_seq_create_simple_port( data->seq, "RtMidi Output", X+ data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(), X SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, X- SND_SEQ_PORT_TYPE_MIDI_GENERIC ); X+ SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION ); X X if ( data->vport < 0 ) { X- errorString_ = "RtMidiOut::openVirtualPort: ALSA error creating virtual port."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiOutAlsa::openVirtualPort: ALSA error creating virtual port."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X } X } X } X X-RtMidiOut :: ~RtMidiOut() X+void MidiOutAlsa :: sendMessage( const unsigned char *message, size_t size ) X { X- // Close a connection if it exists. X- closePort(); X- X- // Cleanup. X+ long result; X AlsaMidiData *data = static_cast (apiData_); X- if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); X- if ( data->coder ) snd_midi_event_free( data->coder ); X- if ( data->buffer ) free( data->buffer ); X- snd_seq_close( data->seq ); X- delete data; X-} X- X-void RtMidiOut :: sendMessage( std::vector *message ) X-{ X- int result; X- AlsaMidiData *data = static_cast (apiData_); X- unsigned int nBytes = message->size(); X+ unsigned int nBytes = static_cast (size); X if ( nBytes > data->bufferSize ) { X data->bufferSize = nBytes; X- result = snd_midi_event_resize_buffer ( data->coder, nBytes); X+ result = snd_midi_event_resize_buffer( data->coder, nBytes ); X if ( result != 0 ) { X- errorString_ = "RtMidiOut::sendMessage: ALSA error resizing MIDI event buffer."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiOutAlsa::sendMessage: ALSA error resizing MIDI event buffer."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X free (data->buffer); X data->buffer = (unsigned char *) malloc( data->bufferSize ); X if ( data->buffer == NULL ) { X- errorString_ = "RtMidiOut::initialize: error allocating buffer memory!\n\n"; X- error( RtMidiError::MEMORY_ERROR ); X+ errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n"; X+ error( RtMidiError::MEMORY_ERROR, errorString_ ); X+ return; X } X } X X- snd_seq_event_t ev; X- snd_seq_ev_clear(&ev); X- snd_seq_ev_set_source(&ev, data->vport); X- snd_seq_ev_set_subs(&ev); X- snd_seq_ev_set_direct(&ev); X- for ( unsigned int i=0; ibuffer[i] = message->at(i); X- result = snd_midi_event_encode( data->coder, data->buffer, (long)nBytes, &ev ); X- // commenting this out allows sending MIDI messages < and > 3 | 1.4.1.0 (mariobuoninfante) X- // if( result < (int)nBytes ) { X- // errorString_ = "RtMidiOut::sendMessage: event parsing error!"; X- // error( RtMidiError::WARNING ); X- // return; X- // } X+ for ( unsigned int i=0; ibuffer[i] = message[i]; X X- // Send the event. X- result = snd_seq_event_output(data->seq, &ev); X- if ( result < 0 ) { X- errorString_ = "RtMidiOut::sendMessage: error sending MIDI message to port."; X- error( RtMidiError::WARNING ); X- } X- snd_seq_drain_output(data->seq); X-} X- X-#endif // __LINUX_ALSA__ X- X- X-//*********************************************************************// X-// API: IRIX MD X-//*********************************************************************// X- X-// API information gleamed from: X-// http://techpubs.sgi.com/library/tpl/cgi-bin/getdoc.cgi?cmd=getdoc&coll=0650&db=man&fname=3%20mdIntro X- X-// If the Makefile doesn't work, try the following: X-// CC -o midiinfo -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp midiinfo.cpp -lpthread -lmd X-// CC -o midiout -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp midiout.cpp -lpthread -lmd X-// CC -o qmidiin -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp qmidiin.cpp -lpthread -lmd X-// CC -o cmidiin -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp cmidiin.cpp -lpthread -lmd X- X-#if defined(__IRIX_MD__) X- X-#include X-#include X-#include X- X-// Irix MIDI header file. X-#include X- X-// A structure to hold variables related to the IRIX API X-// implementation. X-struct IrixMidiData { X- MDport port; X- pthread_t thread; X-}; X- X-//*********************************************************************// X-// API: IRIX X-// Class Definitions: RtMidiIn X-//*********************************************************************// X- X-extern "C" void *irixMidiHandler( void *ptr ) X-{ X- RtMidiIn::RtMidiInData *data = static_cast (ptr); X- IrixMidiData *apiData = static_cast (data->apiData); X- X- bool continueSysex = false; X- unsigned char status; X- unsigned short size; X- MDevent event; X- int fd = mdGetFd( apiData->port ); X- if ( fd < 0 ) { X- data->doInput = false; X- std::cerr << "\nRtMidiIn::irixMidiHandler: error getting port descriptor!\n\n"; X- return 0; X- } X- X- fd_set mask, rmask; X- FD_ZERO( &mask ); X- FD_SET( fd, &mask ); X- struct timeval timeout = {0, 0}; X- RtMidiIn::MidiMessage message; X- int result; X- X- while ( data->doInput ) { X- X- rmask = mask; X- timeout.tv_sec = 0; X- timeout.tv_usec = 0; X- if ( select( fd+1, &rmask, NULL, NULL, &timeout ) <= 0 ) { X- // No data pending ... sleep a bit. X- usleep( 1000 ); X- continue; X+ unsigned int offset = 0; X+ while (offset < nBytes) { X+ snd_seq_event_t ev; X+ snd_seq_ev_clear( &ev ); X+ snd_seq_ev_set_source( &ev, data->vport ); X+ snd_seq_ev_set_subs( &ev ); X+ snd_seq_ev_set_direct( &ev ); X+ result = snd_midi_event_encode( data->coder, data->buffer + offset, X+ (long)(nBytes - offset), &ev ); X+ if ( result < 0 ) { X+ errorString_ = "MidiOutAlsa::sendMessage: event parsing error!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ return; X } X X- // If here, there should be data. X- result = mdReceive( apiData->port, &event, 1); X- if ( result <= 0 ) { X- std::cerr << "\nRtMidiIn::irixMidiHandler: MIDI input read error!\n\n"; X- continue; X+ if ( ev.type == SND_SEQ_EVENT_NONE ) { X+ errorString_ = "MidiOutAlsa::sendMessage: incomplete message!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ return; X } X X- message.timeStamp = event.stamp * 0.000000001; X+ offset += result; X X- size = 0; X- status = event.msg[0]; X- if ( !(status & 0x80) ) continue; X- if ( status == 0xF0 ) { X- // Sysex message ... can be segmented across multiple messages. X- if ( !(data->ignoreFlags & 0x01) ) { X- if ( continueSysex ) { X- // We have a continuing, segmented sysex message. Append X- // the new bytes to our existing message. X- for ( int i=0; iusingCallback && message.bytes.size() > 0 ) { X- RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; X- callback( message.timeStamp, &message.bytes, data->userData ); X- } X- else { X- // As long as we haven't reached our queue size limit, push the message. X- if ( data->queueLimit > data->queue.size() ) X- data->queue.push( message ); X- else X- std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; X- } X- message.bytes.clear(); X- } X- } X- } X- mdFree( NULL ); X- continue; X+ // Send the event. X+ result = snd_seq_event_output( data->seq, &ev ); X+ if ( result < 0 ) { X+ errorString_ = "MidiOutAlsa::sendMessage: error sending MIDI message to port."; X+ error( RtMidiError::WARNING, errorString_ ); X+ return; X } X- else if ( status < 0xC0 ) size = 3; X- else if ( status < 0xE0 ) size = 2; X- else if ( status < 0xF0 ) size = 3; X- else if ( status < 0xF3 ) { X- if ( status == 0xF1 && !(data->ignoreFlags & 0x02) ) { X- // A MIDI time code message and we're not ignoring it. X- size = 3; X- } X- } X- else if ( status == 0xF3 ) size = 2; X- else if ( status == 0xF8 ) { X- if ( !(data->ignoreFlags & 0x02) ) { X- // A MIDI timing tick message and we're not ignoring it. X- size = 1; X- } X- } X- else if ( status == 0xFE ) { // MIDI active sensing X- if ( !(data->ignoreFlags & 0x04) ) X- size = 1; X- } X- else size = 1; X- X- // Copy the MIDI data to our vector. X- if ( size ) { X- message.bytes.assign( &event.msg[0], &event.msg[size] ); X- // Invoke the user callback function or queue the message. X- if ( data->usingCallback ) { X- RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; X- callback( message.timeStamp, &message.bytes, data->userData ); X- } X- else { X- // As long as we haven't reached our queue size limit, push the message. X- if ( data->queueLimit > data->queue.size() ) X- data->queue.push( message ); X- else X- std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; X- } X- message.bytes.clear(); X- } X } X- X- return 0; X+ snd_seq_drain_output( data->seq ); X } X X-void RtMidiIn :: initialize( void ) X-{ X- // Initialize the Irix MIDI system. At the moment, we will not X- // worry about a return value of zero (ports) because there is a X- // chance the user could plug something in after instantiation. X- int nPorts = mdInit(); X+#endif // __LINUX_ALSA__ || __FREEBSD_ALSA__ || __FREEBSD_OSS__ || __LINUX_OSS__ X X- // Create our api-specific connection information. X- IrixMidiData *data = (IrixMidiData *) new IrixMidiData; X- apiData_ = (void *) data; X- inputData_.apiData = (void *) data; X-} X X-void RtMidiIn :: openPort( unsigned int portNumber ) X-{ X- if ( connected_ ) { X- errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; X- error( RtMidiError::WARNING ); X- return; X- } X- X- int nPorts = mdInit(); X- if (nPorts < 1) { X- errorString_ = "RtMidiIn::openPort: no Irix MIDI input sources found!"; X- error( RtMidiError::NO_DEVICES_FOUND ); X- } X- X- std::ostringstream ost; X- if ( portNumber >= nPorts ) { X- ost << "RtMidiIn::openPort: 'portNumber' argument (" << portNumber << ") is invalid."; X- errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X- } X- X- IrixMidiData *data = static_cast (apiData_); X- data->port = mdOpenInPort( mdGetName(portNumber) ); X- if ( data->port == NULL ) { X- ost << "RtMidiIn::openPort: Irix error opening the port (" << portNumber << ")."; X- errorString_ = ost.str(); X- error( RtMidiError::DRIVER_ERROR ); X- } X- mdSetStampMode(data->port, MD_DELTASTAMP); X- X- // Start our MIDI input thread. X- pthread_attr_t attr; X- pthread_attr_init(&attr); X- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); X- pthread_attr_setschedpolicy(&attr, SCHED_RR); X- X- inputData_.doInput = true; X- int err = pthread_create(&data->thread, &attr, irixMidiHandler, &inputData_); X- pthread_attr_destroy(&attr); X- if (err) { X- mdClosePort( data->port ); X- inputData_.doInput = false; X- errorString_ = "RtMidiIn::openPort: error starting MIDI input thread!"; X- error( RtMidiError::THREAD_ERROR ); X- } X- X- connected_ = true; X-} X- X-void RtMidiIn :: openVirtualPort() X-{ X- // This function cannot be implemented for the Irix MIDI API. X- errorString_ = "RtMidiIn::openVirtualPort: cannot be implemented in Irix MIDI API!"; X- error( RtMidiError::WARNING ); X-} X- X-void RtMidiIn :: closePort( void ) X-{ X- if ( connected_ ) { X- IrixMidiData *data = static_cast (apiData_); X- mdClosePort( data->port ); X- connected_ = false; X- X- // Shutdown the input thread. X- inputData_.doInput = false; X- pthread_join( data->thread, NULL ); X- } X-} X- X-RtMidiIn :: ~RtMidiIn() X-{ X- // Close a connection if it exists. X- closePort(); X- X- // Cleanup. X- IrixMidiData *data = static_cast (apiData_); X- delete data; X-} X- X-unsigned int RtMidiIn :: getPortCount() X-{ X- int nPorts = mdInit(); X- if ( nPorts >= 0 ) return nPorts; X- else return 0; X-} X- X-std::string RtMidiIn :: getPortName( unsigned int portNumber ) X-{ X- int nPorts = mdInit(); X- X- std::ostringstream ost; X- if ( portNumber >= nPorts ) { X- ost << "RtMidiIn::getPortName: 'portNumber' argument (" << portNumber << ") is invalid."; X- errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X- } X- X- std::string stringName = std::string( mdGetName( portNumber ) ); X- return stringName; X-} X- X //*********************************************************************// X-// API: IRIX MD X-// Class Definitions: RtMidiOut X-//*********************************************************************// X- X-unsigned int RtMidiOut :: getPortCount() X-{ X- int nPorts = mdInit(); X- if ( nPorts >= 0 ) return nPorts; X- else return 0; X-} X- X-std::string RtMidiOut :: getPortName( unsigned int portNumber ) X-{ X- int nPorts = mdInit(); X- X- std::ostringstream ost; X- if ( portNumber >= nPorts ) { X- ost << "RtMidiIn::getPortName: 'portNumber' argument (" << portNumber << ") is invalid."; X- errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X- } X- X- std::string stringName = std::string( mdGetName( portNumber ) ); X- return stringName; X-} X- X-void RtMidiOut :: initialize( void ) X-{ X- // Initialize the Irix MIDI system. At the moment, we will not X- // worry about a return value of zero (ports) because there is a X- // chance the user could plug something in after instantiation. X- int nPorts = mdInit(); X- X- // Create our api-specific connection information. X- IrixMidiData *data = (IrixMidiData *) new IrixMidiData; X- apiData_ = (void *) data; X-} X- X-void RtMidiOut :: openPort( unsigned int portNumber ) X-{ X- if ( connected_ ) { X- errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; X- error( RtMidiError::WARNING ); X- return; X- } X- X- int nPorts = mdInit(); X- if (nPorts < 1) { X- errorString_ = "RtMidiOut::openPort: no Irix MIDI output sources found!"; X- error( RtMidiError::NO_DEVICES_FOUND ); X- } X- X- std::ostringstream ost; X- if ( portNumber >= nPorts ) { X- ost << "RtMidiOut::openPort: 'portNumber' argument (" << portNumber << ") is invalid."; X- errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X- } X- X- IrixMidiData *data = static_cast (apiData_); X- data->port = mdOpenOutPort( mdGetName(portNumber) ); X- if ( data->port == NULL ) { X- ost << "RtMidiOut::openPort: Irix error opening the port (" << portNumber << ")."; X- errorString_ = ost.str(); X- error( RtMidiError::DRIVER_ERROR ); X- } X- mdSetStampMode(data->port, MD_NOSTAMP); X- X- connected_ = true; X-} X- X-void RtMidiOut :: closePort( void ) X-{ X- if ( connected_ ) { X- IrixMidiData *data = static_cast (apiData_); X- mdClosePort( data->port ); X- connected_ = false; X- } X-} X- X-void RtMidiOut :: openVirtualPort() X-{ X- // This function cannot be implemented for the Irix MIDI API. X- errorString_ = "RtMidiOut::openVirtualPort: cannot be implemented in Irix MIDI API!"; X- error( RtMidiError::WARNING ); X-} X- X-RtMidiOut :: ~RtMidiOut() X-{ X- // Close a connection if it exists. X- closePort(); X- X- // Cleanup. X- IrixMidiData *data = static_cast (apiData_); X- delete data; X-} X- X-void RtMidiOut :: sendMessage( std::vector *message ) X-{ X- int result; X- MDevent event; X- IrixMidiData *data = static_cast (apiData_); X- char *buffer = 0; X- X- unsigned int nBytes = message->size(); X- if ( nBytes == 0 ) return; X- event.stamp = 0; X- if ( message->at(0) == 0xF0 ) { X- if ( nBytes < 3 ) return; // check for bogus sysex X- event.msg[0] = 0xF0; X- event.msglen = nBytes; X- buffer = (char *) malloc( nBytes ); X- for ( int i=0; iat(i); X- event.sysexmsg = buffer; X- } X- else { X- for ( int i=0; iat(i); X- } X- X- // Send the event. X- result = mdSend( data->port, &event, 1 ); X- if ( buffer ) free( buffer ); X- if ( result < 1 ) { X- errorString_ = "RtMidiOut::sendMessage: IRIX error sending MIDI message!"; X- error( RtMidiError::WARNING ); X- return; X- } X-} X- X-#endif // __IRIX_MD__ X- X-//*********************************************************************// X // API: Windows Multimedia Library (MM) X //*********************************************************************// X X // API information deciphered from: X // - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_midi_reference.asp X X-// chuck X-#if defined(__WINDOWS_DS__) X-//#if defined(__WINDOWS_MM__) X+// Thanks to Jean-Baptiste Berruchon for the sysex code. X X+#if defined(__WINDOWS_MM__) X+ X // The Windows MM API is based on the use of a callback function for X // MIDI input. We convert the system specific time stamps to delta X // time values. X@@ -1858,37 +2422,71 @@ void RtMidiOut :: sendMessage( std::vector X #include X X+// Convert a null-terminated wide string or ANSI-encoded string to UTF-8. X+static std::string ConvertToUTF8(const TCHAR *str) X+{ X+ std::string u8str; X+ const WCHAR *wstr = L""; X+#if defined( UNICODE ) || defined( _UNICODE ) X+ wstr = str; X+#else X+ // Convert from ANSI encoding to wide string X+ int wlength = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); X+ std::wstring wstrtemp; X+ if ( wlength ) X+ { X+ wstrtemp.assign( wlength - 1, 0 ); X+ MultiByteToWideChar( CP_ACP, 0, str, -1, &wstrtemp[0], wlength ); X+ wstr = &wstrtemp[0]; X+ } X+#endif X+ // Convert from wide string to UTF-8 X+ int length = WideCharToMultiByte( CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL ); X+ if ( length ) X+ { X+ u8str.assign( length - 1, 0 ); X+ length = WideCharToMultiByte( CP_UTF8, 0, wstr, -1, &u8str[0], length, NULL, NULL ); X+ } X+ return u8str; X+} X+ X+#define RT_SYSEX_BUFFER_SIZE 1024 X+#define RT_SYSEX_BUFFER_COUNT 4 X+ X // A structure to hold variables related to the CoreMIDI API X // implementation. X struct WinMidiData { X HMIDIIN inHandle; // Handle to Midi Input Device X HMIDIOUT outHandle; // Handle to Midi Output Device X DWORD lastTime; X- RtMidiIn::MidiMessage message; X+ MidiInApi::MidiMessage message; X+ LPMIDIHDR sysexBuffer[RT_SYSEX_BUFFER_COUNT]; X+ CRITICAL_SECTION _mutex; // [Patrice] see https://groups.google.com/forum/#!topic/mididev/6OUjHutMpEo X }; X X //*********************************************************************// X // API: Windows MM X-// Class Definitions: RtMidiIn X+// Class Definitions: MidiInWinMM X //*********************************************************************// X X-static void CALLBACK midiInputCallback( HMIDIOUT hmin, X- UINT inputStatus, X- DWORD_PTR instancePtr, // 1.4.1.0 DWORD changed to DWORD_PTR | PR #157 @dbadb X+static void CALLBACK midiInputCallback( HMIDIIN /*hmin*/, X+ UINT inputStatus, X+ DWORD_PTR instancePtr, X DWORD_PTR midiMessage, X- DWORD_PTR timestamp ) X+ DWORD timestamp ) X { X- if ( inputStatus != MIM_DATA && inputStatus != MIM_LONGDATA ) return; X+ if ( inputStatus != MIM_DATA && inputStatus != MIM_LONGDATA && inputStatus != MIM_LONGERROR ) return; X X- //RtMidiIn::RtMidiInData *data = static_cast (instancePtr); X- RtMidiIn::RtMidiInData *data = (RtMidiIn::RtMidiInData *)instancePtr; X+ //MidiInApi::RtMidiInData *data = static_cast (instancePtr); X+ MidiInApi::RtMidiInData *data = (MidiInApi::RtMidiInData *)instancePtr; X WinMidiData *apiData = static_cast (data->apiData); X X // Calculate time stamp. X- apiData->message.timeStamp = 0.0; X- if ( data->firstMessage == true ) data->firstMessage = false; X+ if ( data->firstMessage == true ) { X+ apiData->message.timeStamp = 0.0; X+ data->firstMessage = false; X+ } X else apiData->message.timeStamp = (double) ( timestamp - apiData->lastTime ) * 0.001; X- apiData->lastTime = timestamp; X X if ( inputStatus == MIM_DATA ) { // Channel or system message X X@@ -1901,58 +2499,97 @@ static void CALLBACK midiInputCallback( HMIDIOUT hmin, X if ( status < 0xC0 ) nBytes = 3; X else if ( status < 0xE0 ) nBytes = 2; X else if ( status < 0xF0 ) nBytes = 3; X- else if ( status < 0xF3 ) { X- // A MIDI time code message and we're ignoring it. X- if ( status == 0xF1 && (data->ignoreFlags & 0x02) ) return; X- nBytes = 3; X+ else if ( status == 0xF1 ) { X+ if ( data->ignoreFlags & 0x02 ) return; X+ else nBytes = 2; X } X+ else if ( status == 0xF2 ) nBytes = 3; X else if ( status == 0xF3 ) nBytes = 2; X- else if ( status == 0xF8 && (data->ignoreFlags & 0x02) ) { X+ else if ( status == 0xF8 && ( data->ignoreFlags & 0x02 ) ) { X // A MIDI timing tick message and we're ignoring it. X return; X } X- else if ( status == 0xFE && (data->ignoreFlags & 0x04) ) { X+ else if ( status == 0xFE && ( data->ignoreFlags & 0x04 ) ) { X // A MIDI active sensing message and we're ignoring it. X return; X } X X // Copy bytes to our MIDI message. X unsigned char *ptr = (unsigned char *) &midiMessage; X- for ( int i=0; imessage.bytes.push_back( *ptr++ ); X+ for ( int i=0; imessage.bytes.push_back( *ptr++ ); X } X- else { // Sysex message X+ else { // Sysex message ( MIM_LONGDATA or MIM_LONGERROR ) X MIDIHDR *sysex = ( MIDIHDR *) midiMessage; X- for ( int i=0; i<(int)sysex->dwBytesRecorded; i++ ) X- apiData->message.bytes.push_back( sysex->lpData[i] ); X- if ( apiData->message.bytes.back() != 0xF7 ) return; X+ if ( !( data->ignoreFlags & 0x01 ) && inputStatus != MIM_LONGERROR ) { X+ // Sysex message and we're not ignoring it X+ for ( int i=0; i<(int)sysex->dwBytesRecorded; ++i ) X+ apiData->message.bytes.push_back( sysex->lpData[i] ); X+ } X+ X+ // The WinMM API requires that the sysex buffer be requeued after X+ // input of each sysex message. Even if we are ignoring sysex X+ // messages, we still need to requeue the buffer in case the user X+ // decides to not ignore sysex messages in the future. However, X+ // it seems that WinMM calls this function with an empty sysex X+ // buffer when an application closes and in this case, we should X+ // avoid requeueing it, else the computer suddenly reboots after X+ // one or two minutes. X+ if ( apiData->sysexBuffer[sysex->dwUser]->dwBytesRecorded > 0 ) { X+ //if ( sysex->dwBytesRecorded > 0 ) { X+ EnterCriticalSection( &(apiData->_mutex) ); X+ MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer[sysex->dwUser], sizeof(MIDIHDR) ); X+ LeaveCriticalSection( &(apiData->_mutex) ); X+ if ( result != MMSYSERR_NOERROR ) X+ std::cerr << "\nRtMidiIn::midiInputCallback: error sending sysex to Midi device!!\n\n"; X+ X+ if ( data->ignoreFlags & 0x01 ) return; X+ } X+ else return; X } X X+ // Save the time of the last non-filtered message X+ apiData->lastTime = timestamp; X+ X if ( data->usingCallback ) { X RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; X callback( apiData->message.timeStamp, &apiData->message.bytes, data->userData ); X } X else { X // As long as we haven't reached our queue size limit, push the message. X- if ( data->queueLimit > data->queue.size() ) X- data->queue.push( apiData->message ); X- else X- std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; X+ if ( !data->queue.push( apiData->message ) ) X+ std::cerr << "\nMidiInWinMM: message queue limit reached!!\n\n"; X } X X- // Clear the vector for the next input message. Note that doing X- // this here allows our code to work for sysex messages which are X- // segmented across multiple buffers. X+ // Clear the vector for the next input message. X apiData->message.bytes.clear(); X } X X-void RtMidiIn :: initialize( void ) X+MidiInWinMM :: MidiInWinMM( const std::string &clientName, unsigned int queueSizeLimit ) X+ : MidiInApi( queueSizeLimit ) X { X+ MidiInWinMM::initialize( clientName ); X+} X+ X+MidiInWinMM :: ~MidiInWinMM() X+{ X+ // Close a connection if it exists. X+ MidiInWinMM::closePort(); X+ X+ WinMidiData *data = static_cast (apiData_); X+ DeleteCriticalSection( &(data->_mutex) ); X+ X+ // Cleanup. X+ delete data; X+} X+ X+void MidiInWinMM :: initialize( const std::string& /*clientName*/ ) X+{ X // We'll issue a warning here if no devices are available but not X // throw an error since the user can plugin something later. X unsigned int nDevices = midiInGetNumDevs(); X if ( nDevices == 0 ) { X- errorString_ = "RtMidiIn::initialize: no MIDI input devices currently available."; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiInWinMM::initialize: no MIDI input devices currently available."; X+ error( RtMidiError::WARNING, errorString_ ); X } X X // Save our api-specific connection information. X@@ -1960,135 +2597,201 @@ void RtMidiIn :: initialize( void ) X apiData_ = (void *) data; X inputData_.apiData = (void *) data; X data->message.bytes.clear(); // needs to be empty for first input message X+ X+ if ( !InitializeCriticalSectionAndSpinCount( &(data->_mutex), 0x00000400 ) ) { X+ errorString_ = "MidiInWinMM::initialize: InitializeCriticalSectionAndSpinCount failed."; X+ error( RtMidiError::WARNING, errorString_ ); X+ } X } X X-void RtMidiIn :: openPort( unsigned int portNumber ) X+void MidiInWinMM :: openPort( unsigned int portNumber, const std::string &/*portName*/ ) X { X if ( connected_ ) { X- errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiInWinMM::openPort: a valid connection already exists!"; X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X unsigned int nDevices = midiInGetNumDevs(); X if (nDevices == 0) { X- errorString_ = "RtMidiIn::openPort: no MIDI input sources found!"; X- error( RtMidiError::NO_DEVICES_FOUND ); X+ errorString_ = "MidiInWinMM::openPort: no MIDI input sources found!"; X+ error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); X+ return; X } X X- std::ostringstream ost; X if ( portNumber >= nDevices ) { X- ost << "RtMidiIn::openPort: 'portNumber' argument (" << portNumber << ") is invalid."; X+ std::ostringstream ost; X+ ost << "MidiInWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; X errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X+ error( RtMidiError::INVALID_PARAMETER, errorString_ ); X+ return; X } X X WinMidiData *data = static_cast (apiData_); X MMRESULT result = midiInOpen( &data->inHandle, X portNumber, X- (DWORD_PTR)&midiInputCallback, // 1.4.1.0 DWORD changed to DWORD_PTR | PR #157 @dbadb X+ (DWORD_PTR)&midiInputCallback, X (DWORD_PTR)&inputData_, X CALLBACK_FUNCTION ); X if ( result != MMSYSERR_NOERROR ) { X- errorString_ = "RtMidiIn::openPort: error creating Windows MM MIDI input port."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiInWinMM::openPort: error creating Windows MM MIDI input port."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X+ // Allocate and init the sysex buffers. X+ for ( int i=0; isysexBuffer[i] = (MIDIHDR*) new char[ sizeof(MIDIHDR) ]; X+ data->sysexBuffer[i]->lpData = new char[ RT_SYSEX_BUFFER_SIZE ]; X+ data->sysexBuffer[i]->dwBufferLength = RT_SYSEX_BUFFER_SIZE; X+ data->sysexBuffer[i]->dwUser = i; // We use the dwUser parameter as buffer indicator X+ data->sysexBuffer[i]->dwFlags = 0; X+ X+ result = midiInPrepareHeader( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) ); X+ if ( result != MMSYSERR_NOERROR ) { X+ midiInClose( data->inHandle ); X+ data->inHandle = 0; X+ errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (PrepareHeader)."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X+ X+ // Register the buffer. X+ result = midiInAddBuffer( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) ); X+ if ( result != MMSYSERR_NOERROR ) { X+ midiInClose( data->inHandle ); X+ data->inHandle = 0; X+ errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (AddBuffer)."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X+ } X+ X result = midiInStart( data->inHandle ); X if ( result != MMSYSERR_NOERROR ) { X midiInClose( data->inHandle ); X- errorString_ = "RtMidiIn::openPort: error starting Windows MM MIDI input port."; X- error( RtMidiError::DRIVER_ERROR ); X+ data->inHandle = 0; X+ errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X connected_ = true; X } X X-void RtMidiIn :: openVirtualPort() X+void MidiInWinMM :: openVirtualPort( const std::string &/*portName*/ ) X { X // This function cannot be implemented for the Windows MM MIDI API. X- errorString_ = "RtMidiIn::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiInWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; X+ error( RtMidiError::WARNING, errorString_ ); X } X X-void RtMidiIn :: closePort( void ) X+void MidiInWinMM :: closePort( void ) X { X if ( connected_ ) { X WinMidiData *data = static_cast (apiData_); X+ EnterCriticalSection( &(data->_mutex) ); X midiInReset( data->inHandle ); X midiInStop( data->inHandle ); X+ X+ for ( int i=0; iinHandle, data->sysexBuffer[i], sizeof(MIDIHDR)); X+ delete [] data->sysexBuffer[i]->lpData; X+ delete [] data->sysexBuffer[i]; X+ if ( result != MMSYSERR_NOERROR ) { X+ midiInClose( data->inHandle ); X+ data->inHandle = 0; X+ errorString_ = "MidiInWinMM::openPort: error closing Windows MM MIDI input port (midiInUnprepareHeader)."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X+ } X+ X midiInClose( data->inHandle ); X+ data->inHandle = 0; X connected_ = false; X+ LeaveCriticalSection( &(data->_mutex) ); X } X } X X-RtMidiIn :: ~RtMidiIn() X+void MidiInWinMM :: setClientName ( const std::string& ) X { X- // Close a connection if it exists. X- closePort(); X X- // Cleanup. X- WinMidiData *data = static_cast (apiData_); X- delete data; X+ errorString_ = "MidiInWinMM::setClientName: this function is not implemented for the WINDOWS_MM API!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ X } X X-unsigned int RtMidiIn :: getPortCount() X+void MidiInWinMM :: setPortName ( const std::string& ) X { X+ X+ errorString_ = "MidiInWinMM::setPortName: this function is not implemented for the WINDOWS_MM API!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ X+} X+ X+unsigned int MidiInWinMM :: getPortCount() X+{ X return midiInGetNumDevs(); X } X X-std::string RtMidiIn :: getPortName( unsigned int portNumber ) X+std::string MidiInWinMM :: getPortName( unsigned int portNumber ) X { X+ std::string stringName; X unsigned int nDevices = midiInGetNumDevs(); X if ( portNumber >= nDevices ) { X std::ostringstream ost; X- ost << "RtMidiIn::getPortName: 'portNumber' argument (" << portNumber << ") is invalid."; X+ ost << "MidiInWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; X errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X+ error( RtMidiError::WARNING, errorString_ ); X+ return stringName; X } X X MIDIINCAPS deviceCaps; X- MMRESULT result = midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS)); X+ midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS)); X+ stringName = ConvertToUTF8( deviceCaps.szPname ); X X- std::string stringName = std::string( deviceCaps.szPname ); X+ // Next lines added to add the portNumber to the name so that X+ // the device's names are sure to be listed with individual names X+ // even when they have the same brand name X+#ifndef RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES X+ std::ostringstream os; X+ os << " "; X+ os << portNumber; X+ stringName += os.str(); X+#endif X+ X return stringName; X } X X //*********************************************************************// X // API: Windows MM X-// Class Definitions: RtMidiOut X+// Class Definitions: MidiOutWinMM X //*********************************************************************// X X-unsigned int RtMidiOut :: getPortCount() X+MidiOutWinMM :: MidiOutWinMM( const std::string &clientName ) : MidiOutApi() X { X- return midiOutGetNumDevs(); X+ MidiOutWinMM::initialize( clientName ); X } X X-std::string RtMidiOut :: getPortName( unsigned int portNumber ) X+MidiOutWinMM :: ~MidiOutWinMM() X { X- unsigned int nDevices = midiOutGetNumDevs(); X- if ( portNumber >= nDevices ) { X- std::ostringstream ost; X- ost << "RtMidiOut::getPortName: 'portNumber' argument (" << portNumber << ") is invalid."; X- errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X- } X+ // Close a connection if it exists. X+ MidiOutWinMM::closePort(); X X- MIDIOUTCAPS deviceCaps; X- MMRESULT result = midiOutGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIOUTCAPS)); X- X- std::string stringName = std::string( deviceCaps.szPname ); X- return stringName; X+ // Cleanup. X+ WinMidiData *data = static_cast (apiData_); X+ delete data; X } X X-void RtMidiOut :: initialize( void ) X+void MidiOutWinMM :: initialize( const std::string& /*clientName*/ ) X { X // We'll issue a warning here if no devices are available but not X // throw an error since the user can plug something in later. X unsigned int nDevices = midiOutGetNumDevs(); X if ( nDevices == 0 ) { X- errorString_ = "RtMidiOut::initialize: no MIDI output devices currently available."; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiOutWinMM::initialize: no MIDI output devices currently available."; X+ error( RtMidiError::WARNING, errorString_ ); X } X X // Save our api-specific connection information. X@@ -2096,25 +2799,61 @@ void RtMidiOut :: initialize( void ) X apiData_ = (void *) data; X } X X-void RtMidiOut :: openPort( unsigned int portNumber ) X+unsigned int MidiOutWinMM :: getPortCount() X { X+ return midiOutGetNumDevs(); X+} X+ X+std::string MidiOutWinMM :: getPortName( unsigned int portNumber ) X+{ X+ std::string stringName; X+ unsigned int nDevices = midiOutGetNumDevs(); X+ if ( portNumber >= nDevices ) { X+ std::ostringstream ost; X+ ost << "MidiOutWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; X+ errorString_ = ost.str(); X+ error( RtMidiError::WARNING, errorString_ ); X+ return stringName; X+ } X+ X+ MIDIOUTCAPS deviceCaps; X+ midiOutGetDevCaps( portNumber, &deviceCaps, sizeof( MIDIOUTCAPS ) ); X+ stringName = ConvertToUTF8( deviceCaps.szPname ); X+ X+ // Next lines added to add the portNumber to the name so that X+ // the device's names are sure to be listed with individual names X+ // even when they have the same brand name X+ std::ostringstream os; X+#ifndef RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES X+ os << " "; X+ os << portNumber; X+ stringName += os.str(); X+#endif X+ X+ return stringName; X+} X+ X+void MidiOutWinMM :: openPort( unsigned int portNumber, const std::string &/*portName*/ ) X+{ X if ( connected_ ) { X- errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiOutWinMM::openPort: a valid connection already exists!"; X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X unsigned int nDevices = midiOutGetNumDevs(); X- if (nDevices < 1) { X- errorString_ = "RtMidiOut::openPort: no MIDI output destinations found!"; X- error( RtMidiError::NO_DEVICES_FOUND ); X+ if ( nDevices < 1 ) { X+ errorString_ = "MidiOutWinMM::openPort: no MIDI output destinations found!"; X+ error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); X+ return; X } X X- std::ostringstream ost; X if ( portNumber >= nDevices ) { X- ost << "RtMidiOut::openPort: 'portNumber' argument (" << portNumber << ") is invalid."; X+ std::ostringstream ost; X+ ost << "MidiOutWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; X errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X+ error( RtMidiError::INVALID_PARAMETER, errorString_ ); X+ return; X } X X WinMidiData *data = static_cast (apiData_); X@@ -2124,110 +2863,122 @@ void RtMidiOut :: openPort( unsigned int portNumber ) X (DWORD)NULL, X CALLBACK_NULL ); X if ( result != MMSYSERR_NOERROR ) { X- errorString_ = "RtMidiOut::openPort: error creating Windows MM MIDI output port."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiOutWinMM::openPort: error creating Windows MM MIDI output port."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X connected_ = true; X } X X-void RtMidiOut :: closePort( void ) X+void MidiOutWinMM :: closePort( void ) X { X if ( connected_ ) { X WinMidiData *data = static_cast (apiData_); X midiOutReset( data->outHandle ); X midiOutClose( data->outHandle ); X+ data->outHandle = 0; X connected_ = false; X } X } X X-void RtMidiOut :: openVirtualPort() X+void MidiOutWinMM :: setClientName ( const std::string& ) X { X- // This function cannot be implemented for the Windows MM MIDI API. X- errorString_ = "RtMidiOut::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; X- error( RtMidiError::WARNING ); X+ X+ errorString_ = "MidiOutWinMM::setClientName: this function is not implemented for the WINDOWS_MM API!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ X } X X-RtMidiOut :: ~RtMidiOut() X+void MidiOutWinMM :: setPortName ( const std::string& ) X { X- // Close a connection if it exists. X- closePort(); X X- // Cleanup. X- WinMidiData *data = static_cast (apiData_); X- delete data; X+ errorString_ = "MidiOutWinMM::setPortName: this function is not implemented for the WINDOWS_MM API!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ X } X X-void RtMidiOut :: sendMessage( std::vector *message ) X+void MidiOutWinMM :: openVirtualPort( const std::string &/*portName*/ ) X { X- unsigned int nBytes = message->size(); X+ // This function cannot be implemented for the Windows MM MIDI API. X+ errorString_ = "MidiOutWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; X+ error( RtMidiError::WARNING, errorString_ ); X+} X+ X+void MidiOutWinMM :: sendMessage( const unsigned char *message, size_t size ) X+{ X+ if ( !connected_ ) return; X+ X+ unsigned int nBytes = static_cast(size); X if ( nBytes == 0 ) { X- errorString_ = "RtMidiOut::sendMessage: message argument is empty!"; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiOutWinMM::sendMessage: message argument is empty!"; X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X MMRESULT result; X WinMidiData *data = static_cast (apiData_); X- if ( message->at(0) == 0xF0 ) { // Sysex message X+ if ( message[0] == 0xF0 ) { // Sysex message X X // Allocate buffer for sysex data. X char *buffer = (char *) malloc( nBytes ); X if ( buffer == NULL ) { X- errorString_ = "RtMidiOut::sendMessage: error allocating sysex message memory!"; X- error( RtMidiError::MEMORY_ERROR ); X+ errorString_ = "MidiOutWinMM::sendMessage: error allocating sysex message memory!"; X+ error( RtMidiError::MEMORY_ERROR, errorString_ ); X+ return; X } X X // Copy data to buffer. X- for ( unsigned int i=0; iat(i); X+ for ( unsigned int i=0; ioutHandle, &sysex, sizeof(MIDIHDR) ); X+ result = midiOutPrepareHeader( data->outHandle, &sysex, sizeof( MIDIHDR ) ); X if ( result != MMSYSERR_NOERROR ) { X free( buffer ); X- errorString_ = "RtMidiOut::sendMessage: error preparing sysex header."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiOutWinMM::sendMessage: error preparing sysex header."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X // Send the message. X- result = midiOutLongMsg( data->outHandle, &sysex, sizeof(MIDIHDR) ); X+ result = midiOutLongMsg( data->outHandle, &sysex, sizeof( MIDIHDR ) ); X if ( result != MMSYSERR_NOERROR ) { X free( buffer ); X- errorString_ = "RtMidiOut::sendMessage: error sending sysex message."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiOutWinMM::sendMessage: error sending sysex message."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X // Unprepare the buffer and MIDIHDR. X- while ( MIDIERR_STILLPLAYING == midiOutUnprepareHeader( data->outHandle, &sysex, sizeof (MIDIHDR) ) ) Sleep( 1 ); X+ while ( MIDIERR_STILLPLAYING == midiOutUnprepareHeader( data->outHandle, &sysex, sizeof ( MIDIHDR ) ) ) Sleep( 1 ); X free( buffer ); X- X } X else { // Channel or system message. X X // Make sure the message size isn't too big. X if ( nBytes > 3 ) { X- errorString_ = "RtMidiOut::sendMessage: message size is greater than 3 bytes (and not sysex)!"; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiOutWinMM::sendMessage: message size is greater than 3 bytes (and not sysex)!"; X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X // Pack MIDI bytes into double word. X DWORD packet; X unsigned char *ptr = (unsigned char *) &packet; X- for ( unsigned int i=0; iat(i); X- ptr++; X+ for ( unsigned int i=0; ioutHandle, packet ); X if ( result != MMSYSERR_NOERROR ) { X- errorString_ = "RtMidiOut::sendMessage: error sending MIDI message."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiOutWinMM::sendMessage: error sending MIDI message."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X } X } X } X@@ -2235,91 +2986,524 @@ void RtMidiOut :: sendMessage( std::vector X+#include X+#include X+#ifdef HAVE_SEMAPHORE X+ #include X+#endif X+ X+#define JACK_RINGBUFFER_SIZE 16384 // Default size for ringbuffer X+ X+struct JackMidiData { X+ jack_client_t *client; X+ jack_port_t *port; X+ jack_ringbuffer_t *buffSize; X+ jack_ringbuffer_t *buffMessage; X+ jack_time_t lastTime; X+#ifdef HAVE_SEMAPHORE X+ sem_t sem_cleanup; X+ sem_t sem_needpost; X+#endif X+ MidiInApi :: RtMidiInData *rtMidiIn; X+ }; X+ X+//*********************************************************************// X+// API: JACK X+// Class Definitions: MidiInJack X+//*********************************************************************// X+ X+static int jackProcessIn( jack_nframes_t nframes, void *arg ) X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X+ JackMidiData *jData = (JackMidiData *) arg; X+ MidiInApi :: RtMidiInData *rtData = jData->rtMidiIn; X+ jack_midi_event_t event; X+ jack_time_t time; X+ X+ // Is port created? X+ if ( jData->port == NULL ) return 0; X+ X+ void *buff = jack_port_get_buffer( jData->port, nframes ); X+ bool& continueSysex = rtData->continueSysex; X+ unsigned char& ignoreFlags = rtData->ignoreFlags; X+ X+ // We have midi events in buffer X+ int evCount = jack_midi_get_event_count( buff ); X+ for (int j = 0; j < evCount; j++) { X+ MidiInApi::MidiMessage& message = rtData->message; X+ jack_midi_event_get( &event, buff, j ); X+ X+ // Compute the delta time. X+ time = jack_get_time(); X+ if ( rtData->firstMessage == true ) { X+ message.timeStamp = 0.0; X+ rtData->firstMessage = false; X+ } else X+ message.timeStamp = ( time - jData->lastTime ) * 0.000001; X+ X+ jData->lastTime = time; X+ X+ if ( !continueSysex ) X+ message.bytes.clear(); X+ X+ if ( !( ( continueSysex || event.buffer[0] == 0xF0 ) && ( ignoreFlags & 0x01 ) ) ) { X+ // Unless this is a (possibly continued) SysEx message and we're ignoring SysEx, X+ // copy the event buffer into the MIDI message struct. X+ for ( unsigned int i = 0; i < event.size; i++ ) X+ message.bytes.push_back( event.buffer[i] ); X+ } X+ X+ switch ( event.buffer[0] ) { X+ case 0xF0: X+ // Start of a SysEx message X+ continueSysex = event.buffer[event.size - 1] != 0xF7; X+ if ( ignoreFlags & 0x01 ) continue; X+ break; X+ case 0xF1: X+ case 0xF8: X+ // MIDI Time Code or Timing Clock message X+ if ( ignoreFlags & 0x02 ) continue; X+ break; X+ case 0xFE: X+ // Active Sensing message X+ if ( ignoreFlags & 0x04 ) continue; X+ break; X+ default: X+ if ( continueSysex ) { X+ // Continuation of a SysEx message X+ continueSysex = event.buffer[event.size - 1] != 0xF7; X+ if ( ignoreFlags & 0x01 ) continue; X+ } X+ // All other MIDI messages X+ } X+ X+ if ( !continueSysex ) { X+ // If not a continuation of a SysEx message, X+ // invoke the user callback function or queue the message. X+ if ( rtData->usingCallback ) { X+ RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) rtData->userCallback; X+ callback( message.timeStamp, &message.bytes, rtData->userData ); X+ } X+ else { X+ // As long as we haven't reached our queue size limit, push the message. X+ if ( !rtData->queue.push( message ) ) X+ std::cerr << "\nMidiInJack: message queue limit reached!!\n\n"; X+ } X+ } X+ } X+ X+ return 0; X } X-*/ X-void RtMidiIn :: initialize( void ) X+ X+MidiInJack :: MidiInJack( const std::string &clientName, unsigned int queueSizeLimit ) X+ : MidiInApi( queueSizeLimit ) X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X+ MidiInJack::initialize( clientName ); X } X X-void RtMidiIn :: openPort( unsigned int portNumber ) X+void MidiInJack :: initialize( const std::string& clientName ) X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X+ JackMidiData *data = new JackMidiData; X+ apiData_ = (void *) data; X+ X+ data->rtMidiIn = &inputData_; X+ data->port = NULL; X+ data->client = NULL; X+ this->clientName = clientName; X+ X+ connect(); X } X X-void RtMidiIn :: openVirtualPort() X+void MidiInJack :: connect() X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X+ JackMidiData *data = static_cast (apiData_); X+ if ( data->client ) X+ return; X+ X+ // Initialize JACK client X+ if (( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) { X+ errorString_ = "MidiInJack::initialize: JACK server not running?"; X+ error( RtMidiError::WARNING, errorString_ ); X+ return; X+ } X+ X+ jack_set_process_callback( data->client, jackProcessIn, data ); X+ jack_activate( data->client ); X } X X-void RtMidiIn :: closePort( void ) X+MidiInJack :: ~MidiInJack() X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X+ JackMidiData *data = static_cast (apiData_); X+ MidiInJack::closePort(); X+ X+ if ( data->client ) X+ jack_client_close( data->client ); X+ delete data; X } X X-RtMidiIn :: ~RtMidiIn() X+void MidiInJack :: openPort( unsigned int portNumber, const std::string &portName ) X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X+ JackMidiData *data = static_cast (apiData_); X+ X+ connect(); X+ X+ // Creating new port X+ if ( data->port == NULL ) X+ data->port = jack_port_register( data->client, portName.c_str(), X+ JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 ); X+ X+ if ( data->port == NULL ) { X+ errorString_ = "MidiInJack::openPort: JACK error creating port"; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X+ X+ // Connecting to the output X+ std::string name = getPortName( portNumber ); X+ jack_connect( data->client, name.c_str(), jack_port_name( data->port ) ); X+ X+ connected_ = true; X } X X-unsigned int RtMidiIn :: getPortCount() X+void MidiInJack :: openVirtualPort( const std::string &portName ) X { X+ JackMidiData *data = static_cast (apiData_); X+ X+ connect(); X+ if ( data->port == NULL ) X+ data->port = jack_port_register( data->client, portName.c_str(), X+ JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 ); X+ X+ if ( data->port == NULL ) { X+ errorString_ = "MidiInJack::openVirtualPort: JACK error creating virtual port"; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ } X+} X+ X+unsigned int MidiInJack :: getPortCount() X+{ X+ int count = 0; X+ JackMidiData *data = static_cast (apiData_); X+ connect(); X+ if ( !data->client ) X return 0; X+ X+ // List of available ports X+ const char **ports = jack_get_ports( data->client, NULL, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput ); X+ X+ if ( ports == NULL ) return 0; X+ while ( ports[count] != NULL ) X+ count++; X+ X+ free( ports ); X+ X+ return count; X } X X-std::string RtMidiIn :: getPortName( unsigned int portNumber ) X+std::string MidiInJack :: getPortName( unsigned int portNumber ) X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X- return ""; X+ JackMidiData *data = static_cast (apiData_); X+ std::string retStr( "" ); X+ X+ connect(); X+ X+ // List of available ports X+ const char **ports = jack_get_ports( data->client, NULL, X+ JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput ); X+ X+ // Check port validity X+ if ( ports == NULL ) { X+ errorString_ = "MidiInJack::getPortName: no ports available!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ return retStr; X+ } X+ X+ unsigned int i; X+ for ( i=0; i (apiData_); X+ X+ if ( data->port == NULL ) return; X+ jack_port_unregister( data->client, data->port ); X+ data->port = NULL; X+ X+ connected_ = false; X+} X+ X+void MidiInJack:: setClientName( const std::string& ) X+{ X+ X+ errorString_ = "MidiInJack::setClientName: this function is not implemented for the UNIX_JACK API!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ X+} X+ X+void MidiInJack :: setPortName( const std::string &portName ) X+{ X+ JackMidiData *data = static_cast (apiData_); X+#ifdef JACK_HAS_PORT_RENAME X+ jack_port_rename( data->client, data->port, portName.c_str() ); X+#else X+ jack_port_set_name( data->port, portName.c_str() ); X+#endif X+} X+ X //*********************************************************************// X-// API: OS-X X-// Class Definitions: RtMidiOut X+// API: JACK X+// Class Definitions: MidiOutJack X //*********************************************************************// X X-unsigned int RtMidiOut :: getPortCount() X+// Jack process callback X+static int jackProcessOut( jack_nframes_t nframes, void *arg ) X { X- return 0; X+ JackMidiData *data = (JackMidiData *) arg; X+ jack_midi_data_t *midiData; X+ int space; X+ X+ // Is port created? X+ if ( data->port == NULL ) return 0; X+ X+ void *buff = jack_port_get_buffer( data->port, nframes ); X+ jack_midi_clear_buffer( buff ); X+ X+ while ( jack_ringbuffer_read_space( data->buffSize ) > 0 ) { X+ jack_ringbuffer_read( data->buffSize, (char *) &space, (size_t) sizeof( space ) ); X+ midiData = jack_midi_event_reserve( buff, 0, space ); X+ X+ jack_ringbuffer_read( data->buffMessage, (char *) midiData, (size_t) space ); X+ } X+ X+#ifdef HAVE_SEMAPHORE X+ if ( !sem_trywait( &data->sem_needpost ) ) X+ sem_post( &data->sem_cleanup ); X+#endif X+ X+ return 0; X } X X-std::string RtMidiOut :: getPortName( unsigned int portNumber ) X+MidiOutJack :: MidiOutJack( const std::string &clientName ) : MidiOutApi() X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X- return ""; X+ MidiOutJack::initialize( clientName ); X } X X-void RtMidiOut :: initialize( void ) X+void MidiOutJack :: initialize( const std::string& clientName ) X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X+ JackMidiData *data = new JackMidiData; X+ apiData_ = (void *) data; X+ X+ data->port = NULL; X+ data->client = NULL; X+#ifdef HAVE_SEMAPHORE X+ sem_init( &data->sem_cleanup, 0, 0 ); X+ sem_init( &data->sem_needpost, 0, 0 ); X+#endif X+ this->clientName = clientName; X+ X+ connect(); X } X X-void RtMidiOut :: openPort( unsigned int portNumber ) X+void MidiOutJack :: connect() X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X+ JackMidiData *data = static_cast (apiData_); X+ if ( data->client ) X+ return; X+ X+ // Initialize output ringbuffers X+ data->buffSize = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE ); X+ data->buffMessage = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE ); X+ X+ // Initialize JACK client X+ if ( ( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL ) ) == 0 ) { X+ errorString_ = "MidiOutJack::initialize: JACK server not running?"; X+ error( RtMidiError::WARNING, errorString_ ); X+ return; X+ } X+ X+ jack_set_process_callback( data->client, jackProcessOut, data ); X+ jack_activate( data->client ); X } X X-void RtMidiOut :: closePort( void ) X+MidiOutJack :: ~MidiOutJack() X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X+ JackMidiData *data = static_cast (apiData_); X+ MidiOutJack::closePort(); X+ X+ // Cleanup X+ jack_ringbuffer_free( data->buffSize ); X+ jack_ringbuffer_free( data->buffMessage ); X+ if ( data->client ) { X+ jack_client_close( data->client ); X+ } X+ X+#ifdef HAVE_SEMAPHORE X+ sem_destroy( &data->sem_cleanup ); X+ sem_destroy( &data->sem_needpost ); X+#endif X+ X+ delete data; X } X X-void RtMidiOut :: openVirtualPort() X+void MidiOutJack :: openPort( unsigned int portNumber, const std::string &portName ) X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X+ JackMidiData *data = static_cast (apiData_); X+ X+ connect(); X+ X+ // Creating new port X+ if ( data->port == NULL ) X+ data->port = jack_port_register( data->client, portName.c_str(), X+ JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 ); X+ X+ if ( data->port == NULL ) { X+ errorString_ = "MidiOutJack::openPort: JACK error creating port"; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X+ X+ // Connecting to the output X+ std::string name = getPortName( portNumber ); X+ jack_connect( data->client, jack_port_name( data->port ), name.c_str() ); X+ X+ connected_ = true; X } X X-RtMidiOut :: ~RtMidiOut() X+void MidiOutJack :: openVirtualPort( const std::string &portName ) X { X+ JackMidiData *data = static_cast (apiData_); X+ X+ connect(); X+ if ( data->port == NULL ) X+ data->port = jack_port_register( data->client, portName.c_str(), X+ JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 ); X+ X+ if ( data->port == NULL ) { X+ errorString_ = "MidiOutJack::openVirtualPort: JACK error creating virtual port"; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ } X } X X-void RtMidiOut :: sendMessage( std::vector *message ) X+unsigned int MidiOutJack :: getPortCount() X { X+ int count = 0; X+ JackMidiData *data = static_cast (apiData_); X+ connect(); X+ if ( !data->client ) X+ return 0; X+ X+ // List of available ports X+ const char **ports = jack_get_ports( data->client, NULL, X+ JACK_DEFAULT_MIDI_TYPE, JackPortIsInput ); X+ X+ if ( ports == NULL ) return 0; X+ while ( ports[count] != NULL ) X+ count++; X+ X+ free( ports ); X+ X+ return count; X } X X-#endif // dummy X+std::string MidiOutJack :: getPortName( unsigned int portNumber ) X+{ X+ JackMidiData *data = static_cast (apiData_); X+ std::string retStr(""); X+ X+ connect(); X+ X+ // List of available ports X+ const char **ports = jack_get_ports( data->client, NULL, X+ JACK_DEFAULT_MIDI_TYPE, JackPortIsInput ); X+ X+ // Check port validity X+ if ( ports == NULL ) { X+ errorString_ = "MidiOutJack::getPortName: no ports available!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ return retStr; X+ } X+ X+ if ( ports[portNumber] == NULL ) { X+ std::ostringstream ost; X+ ost << "MidiOutJack::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; X+ errorString_ = ost.str(); X+ error( RtMidiError::WARNING, errorString_ ); X+ } X+ else retStr.assign( ports[portNumber] ); X+ X+ free( ports ); X+ return retStr; X+} X+ X+void MidiOutJack :: closePort() X+{ X+ JackMidiData *data = static_cast (apiData_); X+ X+ if ( data->port == NULL ) return; X+ X+#ifdef HAVE_SEMAPHORE X+ struct timespec ts; X+ if ( clock_gettime( CLOCK_REALTIME, &ts ) != -1 ) { X+ ts.tv_sec += 1; // wait max one second X+ sem_post( &data->sem_needpost ); X+ sem_timedwait( &data->sem_cleanup, &ts ); X+ } X+#endif X+ X+ jack_port_unregister( data->client, data->port ); X+ data->port = NULL; X+ X+ connected_ = false; X+} X+ X+void MidiOutJack:: setClientName( const std::string& ) X+{ X+ X+ errorString_ = "MidiOutJack::setClientName: this function is not implemented for the UNIX_JACK API!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ X+} X+ X+void MidiOutJack :: setPortName( const std::string &portName ) X+{ X+ JackMidiData *data = static_cast (apiData_); X+#ifdef JACK_HAS_PORT_RENAME X+ jack_port_rename( data->client, data->port, portName.c_str() ); X+#else X+ jack_port_set_name( data->port, portName.c_str() ); X+#endif X+} X+ X+void MidiOutJack :: sendMessage( const unsigned char *message, size_t size ) X+{ X+ int nBytes = static_cast(size); X+ JackMidiData *data = static_cast (apiData_); X+ X+ // Write full message to buffer X+ jack_ringbuffer_write( data->buffMessage, ( const char * ) message, nBytes ); X+ jack_ringbuffer_write( data->buffSize, ( char * ) &nBytes, sizeof( nBytes ) ); X+} X+ X+#endif // __UNIX_JACK__ 3c42ae2a8d4c5b3fcaeeac76ccfc6d53 echo x - audio/chuck/files/extra-patch-src_core_rtmidi-oss_jack.cpp sed 's/^X//' >audio/chuck/files/extra-patch-src_core_rtmidi-oss_jack.cpp << '459d14ed15abfc1e9fd8ed74a506d6db' X--- src/core/rtmidi.cpp.orig 2021-08-16 04:08:51 UTC X+++ src/core/rtmidi.cpp X@@ -5,10 +5,11 @@ X This class implements some common functionality for the realtime X MIDI input/output subclasses RtMidiIn and RtMidiOut. X X- RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/ X+ RtMidi GitHub site: https://github.com/thestk/rtmidi X+ RtMidi WWW site: http://www.music.mcgill.ca/~gary/rtmidi/ X X RtMidi: realtime MIDI i/o C++ classes X- Copyright (c) 2003-2005 Gary P. Scavone X+ Copyright (c) 2003-2019 Gary P. Scavone X X Permission is hereby granted, free of charge, to any person X obtaining a copy of this software and associated documentation files X@@ -22,8 +23,9 @@ X included in all copies or substantial portions of the Software. X X Any person wishing to distribute modifications to the Software is X- requested to send the modifications to the original developer so that X- they can be incorporated into the canonical version. X+ asked to send the modifications to the original developer so that X+ they can be incorporated into the canonical version. This is, X+ however, not a binding provision of this license. X X THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, X EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF X@@ -35,75 +37,609 @@ X */ X /**********************************************************************/ X X-// RtMidi: Version 1.0.4, 14 October 2005 X- X-// chuck X #include "rtmidi.h" X #include X-#include X-#include X X+#if defined(TARGET_OS_IPHONE) X X+ #define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime X+ #define AudioConvertHostTimeToNanos CAHostTimeBase::ConvertToNanos X+ X+ #include X+ class CTime2nsFactor X+ { X+ public: X+ CTime2nsFactor() X+ { X+ mach_timebase_info_data_t tinfo; X+ mach_timebase_info(&tinfo); X+ Factor = (double)tinfo.numer / tinfo.denom; X+ } X+ static double Factor; X+ }; X+ double CTime2nsFactor::Factor; X+ static CTime2nsFactor InitTime2nsFactor; X+ #undef AudioGetCurrentHostTime X+ #undef AudioConvertHostTimeToNanos X+ #define AudioGetCurrentHostTime (uint64_t) mach_absolute_time X+ #define AudioConvertHostTimeToNanos(t) t *CTime2nsFactor::Factor X+ #define EndianS32_BtoN(n) n X+ X+#endif X+ X+// Default for Windows is to add an identifier to the port names; this X+// flag can be defined (e.g. in your project file) to disable this behaviour. X+//#define RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES X+ X+// **************************************************************** // X+// X+// MidiInApi and MidiOutApi subclass prototypes. X+// X+// **************************************************************** // X+ X+#if !defined(__LINUX_ALSA__) && !defined(__FREEBSD_ALSA__) && !defined(__FREEBSD_OSS__) && !defined(__LINUX_OSS__) && !defined(__UNIX_JACK__) && !defined(__MACOSX_CORE__) && !defined(__WINDOWS_MM__) && !defined(TARGET_IPHONE_OS) X+ #define __RTMIDI_DUMMY__ X+#endif X+ X+#if defined(__MACOSX_CORE__) X+ X+class MidiInCore: public MidiInApi X+{ X+ public: X+ MidiInCore( const std::string &clientName, unsigned int queueSizeLimit ); X+ ~MidiInCore( void ); X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; }; X+ void openPort( unsigned int portNumber, const std::string &portName ); X+ void openVirtualPort( const std::string &portName ); X+ void closePort( void ); X+ void setClientName( const std::string &clientName ); X+ void setPortName( const std::string &portName ); X+ unsigned int getPortCount( void ); X+ std::string getPortName( unsigned int portNumber ); X+ X+ protected: X+ void initialize( const std::string& clientName ); X+}; X+ X+class MidiOutCore: public MidiOutApi X+{ X+ public: X+ MidiOutCore( const std::string &clientName ); X+ ~MidiOutCore( void ); X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; }; X+ void openPort( unsigned int portNumber, const std::string &portName ); X+ void openVirtualPort( const std::string &portName ); X+ void closePort( void ); X+ void setClientName( const std::string &clientName ); X+ void setPortName( const std::string &portName ); X+ unsigned int getPortCount( void ); X+ std::string getPortName( unsigned int portNumber ); X+ void sendMessage( const unsigned char *message, size_t size ); X+ X+ protected: X+ void initialize( const std::string& clientName ); X+}; X+ X+#endif X+ X+#if defined(__UNIX_JACK__) || defined(__FREEBSD_OSS__) || defined(__LINUX_OSS__) X+ X+class MidiInJack: public MidiInApi X+{ X+ public: X+ MidiInJack( const std::string &clientName, unsigned int queueSizeLimit ); X+ ~MidiInJack( void ); X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; }; X+ void openPort( unsigned int portNumber, const std::string &portName ); X+ void openVirtualPort( const std::string &portName ); X+ void closePort( void ); X+ void setClientName( const std::string &clientName ); X+ void setPortName( const std::string &portName); X+ unsigned int getPortCount( void ); X+ std::string getPortName( unsigned int portNumber ); X+ X+ protected: X+ std::string clientName; X+ X+ void connect( void ); X+ void initialize( const std::string& clientName ); X+}; X+ X+class MidiOutJack: public MidiOutApi X+{ X+ public: X+ MidiOutJack( const std::string &clientName ); X+ ~MidiOutJack( void ); X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; }; X+ void openPort( unsigned int portNumber, const std::string &portName ); X+ void openVirtualPort( const std::string &portName ); X+ void closePort( void ); X+ void setClientName( const std::string &clientName ); X+ void setPortName( const std::string &portName); X+ unsigned int getPortCount( void ); X+ std::string getPortName( unsigned int portNumber ); X+ void sendMessage( const unsigned char *message, size_t size ); X+ X+ protected: X+ std::string clientName; X+ X+ void connect( void ); X+ void initialize( const std::string& clientName ); X+}; X+ X+#endif X+ X+#if defined(__LINUX_ALSA__) || defined(__FREEBSD_ALSA__) X+ X+class MidiInAlsa: public MidiInApi X+{ X+ public: X+ MidiInAlsa( const std::string &clientName, unsigned int queueSizeLimit ); X+ ~MidiInAlsa( void ); X+#if defined(__LINUX_ALSA__) X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; }; X+#else X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::FREEBSD_ALSA; }; X+#endif X+ void openPort( unsigned int portNumber, const std::string &portName ); X+ void openVirtualPort( const std::string &portName ); X+ void closePort( void ); X+ void setClientName( const std::string &clientName ); X+ void setPortName( const std::string &portName); X+ unsigned int getPortCount( void ); X+ std::string getPortName( unsigned int portNumber ); X+ X+ protected: X+ void initialize( const std::string& clientName ); X+}; X+ X+class MidiOutAlsa: public MidiOutApi X+{ X+ public: X+ MidiOutAlsa( const std::string &clientName ); X+ ~MidiOutAlsa( void ); X+#if defined(__LINUX_ALSA__) X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; }; X+#else X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::FREEBSD_ALSA; }; X+#endif X+ void openPort( unsigned int portNumber, const std::string &portName ); X+ void openVirtualPort( const std::string &portName ); X+ void closePort( void ); X+ void setClientName( const std::string &clientName ); X+ void setPortName( const std::string &portName ); X+ unsigned int getPortCount( void ); X+ std::string getPortName( unsigned int portNumber ); X+ void sendMessage( const unsigned char *message, size_t size ); X+ X+ protected: X+ void initialize( const std::string& clientName ); X+}; X+ X+#endif X+ X+#if defined(__WINDOWS_MM__) X+ X+class MidiInWinMM: public MidiInApi X+{ X+ public: X+ MidiInWinMM( const std::string &clientName, unsigned int queueSizeLimit ); X+ ~MidiInWinMM( void ); X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; }; X+ void openPort( unsigned int portNumber, const std::string &portName ); X+ void openVirtualPort( const std::string &portName ); X+ void closePort( void ); X+ void setClientName( const std::string &clientName ); X+ void setPortName( const std::string &portName ); X+ unsigned int getPortCount( void ); X+ std::string getPortName( unsigned int portNumber ); X+ X+ protected: X+ void initialize( const std::string& clientName ); X+}; X+ X+class MidiOutWinMM: public MidiOutApi X+{ X+ public: X+ MidiOutWinMM( const std::string &clientName ); X+ ~MidiOutWinMM( void ); X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; }; X+ void openPort( unsigned int portNumber, const std::string &portName ); X+ void openVirtualPort( const std::string &portName ); X+ void closePort( void ); X+ void setClientName( const std::string &clientName ); X+ void setPortName( const std::string &portName ); X+ unsigned int getPortCount( void ); X+ std::string getPortName( unsigned int portNumber ); X+ void sendMessage( const unsigned char *message, size_t size ); X+ X+ protected: X+ void initialize( const std::string& clientName ); X+}; X+ X+#endif X+ X+#if defined(__RTMIDI_DUMMY__) X+ X+class MidiInDummy: public MidiInApi X+{ X+ public: X+ MidiInDummy( const std::string &/*clientName*/, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { errorString_ = "MidiInDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); } X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; } X+ void openPort( unsigned int /*portNumber*/, const std::string &/*portName*/ ) {} X+ void openVirtualPort( const std::string &/*portName*/ ) {} X+ void closePort( void ) {} X+ void setClientName( const std::string &/*clientName*/ ) {}; X+ void setPortName( const std::string &/*portName*/ ) {}; X+ unsigned int getPortCount( void ) { return 0; } X+ std::string getPortName( unsigned int /*portNumber*/ ) { return ""; } X+ X+ protected: X+ void initialize( const std::string& /*clientName*/ ) {} X+}; X+ X+class MidiOutDummy: public MidiOutApi X+{ X+ public: X+ MidiOutDummy( const std::string &/*clientName*/ ) { errorString_ = "MidiOutDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); } X+ RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; } X+ void openPort( unsigned int /*portNumber*/, const std::string &/*portName*/ ) {} X+ void openVirtualPort( const std::string &/*portName*/ ) {} X+ void closePort( void ) {} X+ void setClientName( const std::string &/*clientName*/ ) {}; X+ void setPortName( const std::string &/*portName*/ ) {}; X+ unsigned int getPortCount( void ) { return 0; } X+ std::string getPortName( unsigned int /*portNumber*/ ) { return ""; } X+ void sendMessage( const unsigned char * /*message*/, size_t /*size*/ ) {} X+ X+ protected: X+ void initialize( const std::string& /*clientName*/ ) {} X+}; X+ X+#endif X+ X //*********************************************************************// X-// Common RtMidi Definitions X+// RtMidi Definitions X //*********************************************************************// X X RtMidi :: RtMidi() X- : apiData_( 0 ), connected_( false ) X+ : rtapi_(0) X { X } X X-// chuck X-void RtMidi :: error( RtMidiError::Type type ) X+RtMidi :: ~RtMidi() X { X- if (type == RtMidiError::WARNING) { X- std::cerr << "[chuck](via RtMidi): " << errorString_ << "\n"; X+ delete rtapi_; X+ rtapi_ = 0; X+} X+ X+std::string RtMidi :: getVersion( void ) throw() X+{ X+ return std::string( RTMIDI_VERSION ); X+} X+ X+// Define API names and display names. X+// Must be in same order as API enum. X+extern "C" { X+const char* rtmidi_api_names[][2] = { X+ { "unspecified" , "Unknown" }, X+ { "core" , "CoreMidi" }, X+ { "alsa" , "ALSA" }, X+ { "jack" , "Jack" }, X+ { "winmm" , "Windows MultiMedia" }, X+ { "dummy" , "Dummy" }, X+}; X+const unsigned int rtmidi_num_api_names = X+ sizeof(rtmidi_api_names)/sizeof(rtmidi_api_names[0]); X+ X+// The order here will control the order of RtMidi's API search in X+// the constructor. X+extern "C" const RtMidi::Api rtmidi_compiled_apis[] = { X+#if defined(__MACOSX_CORE__) X+ RtMidi::MACOSX_CORE, X+#endif X+#if defined(__LINUX_ALSA__) X+ RtMidi::LINUX_ALSA, X+#endif X+#if defined(__FREEBSD_ALSA__) X+ RtMidi::FREEBSD_ALSA, X+#endif X+#if defined(__UNIX_JACK__) || defined(__FREEBSD_OSS__) || defined(__LINUX_OSS__) X+ RtMidi::UNIX_JACK, X+#endif X+#if defined(__WINDOWS_MM__) X+ RtMidi::WINDOWS_MM, X+#endif X+#if defined(__RTMIDI_DUMMY__) X+ RtMidi::RTMIDI_DUMMY, X+#endif X+ RtMidi::UNSPECIFIED, X+}; X+extern "C" const unsigned int rtmidi_num_compiled_apis = X+ sizeof(rtmidi_compiled_apis)/sizeof(rtmidi_compiled_apis[0])-1; X+} X+ X+// This is a compile-time check that rtmidi_num_api_names == RtMidi::NUM_APIS. X+// If the build breaks here, check that they match. X+template class StaticAssert { public: StaticAssert() {} }; X+template<> class StaticAssert{ public: StaticAssert() {} }; X+class StaticAssertions { StaticAssertions() { X+ StaticAssert(); X+}}; X+ X+void RtMidi :: getCompiledApi( std::vector &apis ) throw() X+{ X+ apis = std::vector(rtmidi_compiled_apis, X+ rtmidi_compiled_apis + rtmidi_num_compiled_apis); X+} X+ X+std::string RtMidi :: getApiName( RtMidi::Api api ) X+{ X+ if (api < 0 || api >= RtMidi::NUM_APIS) X+ return ""; X+ return rtmidi_api_names[api][0]; X+} X+ X+std::string RtMidi :: getApiDisplayName( RtMidi::Api api ) X+{ X+ if (api < 0 || api >= RtMidi::NUM_APIS) X+ return "Unknown"; X+ return rtmidi_api_names[api][1]; X+} X+ X+RtMidi::Api RtMidi :: getCompiledApiByName( const std::string &name ) X+{ X+ unsigned int i=0; X+ for (i = 0; i < rtmidi_num_compiled_apis; ++i) X+ if (name == rtmidi_api_names[rtmidi_compiled_apis[i]][0]) X+ return rtmidi_compiled_apis[i]; X+ return RtMidi::UNSPECIFIED; X+} X+ X+void RtMidi :: setClientName( const std::string &clientName ) X+{ X+ rtapi_->setClientName( clientName ); X+} X+ X+void RtMidi :: setPortName( const std::string &portName ) X+{ X+ rtapi_->setPortName( portName ); X+} X+ X+ X+//*********************************************************************// X+// RtMidiIn Definitions X+//*********************************************************************// X+ X+void RtMidiIn :: openMidiApi( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit ) X+{ X+ delete rtapi_; X+ rtapi_ = 0; X+ X+#if defined(__UNIX_JACK__) || defined(__FREEBSD_OSS__) || defined(__LINUX_OSS__) X+ if ( api == UNIX_JACK ) X+ rtapi_ = new MidiInJack( clientName, queueSizeLimit ); X+#endif X+#if defined(__LINUX_ALSA__) X+ if ( api == LINUX_ALSA ) X+ rtapi_ = new MidiInAlsa( clientName, queueSizeLimit ); X+#endif X+#if defined(__FREEBSD_ALSA__) X+ if ( api == FREEBSD_ALSA ) X+ rtapi_ = new MidiInAlsa( clientName, queueSizeLimit ); X+#endif X+#if defined(__WINDOWS_MM__) X+ if ( api == WINDOWS_MM ) X+ rtapi_ = new MidiInWinMM( clientName, queueSizeLimit ); X+#endif X+#if defined(__MACOSX_CORE__) X+ if ( api == MACOSX_CORE ) X+ rtapi_ = new MidiInCore( clientName, queueSizeLimit ); X+#endif X+#if defined(__RTMIDI_DUMMY__) X+ if ( api == RTMIDI_DUMMY ) X+ rtapi_ = new MidiInDummy( clientName, queueSizeLimit ); X+#endif X+} X+ X+RTMIDI_DLL_PUBLIC RtMidiIn :: RtMidiIn( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit ) X+ : RtMidi() X+{ X+ if ( api != UNSPECIFIED ) { X+ // Attempt to open the specified API. X+ openMidiApi( api, clientName, queueSizeLimit ); X+ if ( rtapi_ ) return; X+ X+ // No compiled support for specified API value. Issue a warning X+ // and continue as if no API was specified. X+ std::cerr << "\nRtMidiIn: no compiled support for specified API argument!\n\n" << std::endl; X } X- else if (type == RtMidiError::DEBUG_WARNING) { X+ X+ // Iterate through the compiled APIs and return as soon as we find X+ // one with at least one port or we reach the end of the list. X+ std::vector< RtMidi::Api > apis; X+ getCompiledApi( apis ); X+ for ( unsigned int i=0; igetPortCount() ) break; X+ } X+ X+ if ( rtapi_ ) return; X+ X+ // It should not be possible to get here because the preprocessor X+ // definition __RTMIDI_DUMMY__ is automatically defined if no X+ // API-specific definitions are passed to the compiler. But just in X+ // case something weird happens, we'll throw an error. X+ std::string errorText = "RtMidiIn: no compiled API support found ... critical error!!"; X+ throw( RtMidiError( errorText, RtMidiError::UNSPECIFIED ) ); X+} X+ X+RtMidiIn :: ~RtMidiIn() throw() X+{ X+} X+ X+ X+//*********************************************************************// X+// RtMidiOut Definitions X+//*********************************************************************// X+ X+void RtMidiOut :: openMidiApi( RtMidi::Api api, const std::string &clientName ) X+{ X+ delete rtapi_; X+ rtapi_ = 0; X+ X+#if defined(__UNIX_JACK__) || defined(__FREEBSD_OSS__) || defined(__LINUX_OSS__) X+ if ( api == UNIX_JACK ) X+ rtapi_ = new MidiOutJack( clientName ); X+#endif X+#if defined(__LINUX_ALSA__) X+ if ( api == LINUX_ALSA ) X+ rtapi_ = new MidiOutAlsa( clientName ); X+#endif X+#ifdef __FREEBSD_ALSA__ X+ if ( api == FREEBSD_ALSA ) X+ rtapi_ = new MidiOutAlsa( clientName ); X+#endif X+#if defined(__WINDOWS_MM__) X+ if ( api == WINDOWS_MM ) X+ rtapi_ = new MidiOutWinMM( clientName ); X+#endif X+#if defined(__MACOSX_CORE__) X+ if ( api == MACOSX_CORE ) X+ rtapi_ = new MidiOutCore( clientName ); X+#endif X+#if defined(__RTMIDI_DUMMY__) X+ if ( api == RTMIDI_DUMMY ) X+ rtapi_ = new MidiOutDummy( clientName ); X+#endif X+} X+ X+RTMIDI_DLL_PUBLIC RtMidiOut :: RtMidiOut( RtMidi::Api api, const std::string &clientName) X+{ X+ if ( api != UNSPECIFIED ) { X+ // Attempt to open the specified API. X+ openMidiApi( api, clientName ); X+ if ( rtapi_ ) return; X+ X+ // No compiled support for specified API value. Issue a warning X+ // and continue as if no API was specified. X+ std::cerr << "\nRtMidiOut: no compiled support for specified API argument!\n\n" << std::endl; X+ } X+ X+ // Iterate through the compiled APIs and return as soon as we find X+ // one with at least one port or we reach the end of the list. X+ std::vector< RtMidi::Api > apis; X+ getCompiledApi( apis ); X+ for ( unsigned int i=0; igetPortCount() ) break; X+ } X+ X+ if ( rtapi_ ) return; X+ X+ // It should not be possible to get here because the preprocessor X+ // definition __RTMIDI_DUMMY__ is automatically defined if no X+ // API-specific definitions are passed to the compiler. But just in X+ // case something weird happens, we'll thrown an error. X+ std::string errorText = "RtMidiOut: no compiled API support found ... critical error!!"; X+ throw( RtMidiError( errorText, RtMidiError::UNSPECIFIED ) ); X+} X+ X+RtMidiOut :: ~RtMidiOut() throw() X+{ X+} X+ X+//*********************************************************************// X+// Common MidiApi Definitions X+//*********************************************************************// X+ X+MidiApi :: MidiApi( void ) X+ : apiData_( 0 ), connected_( false ), errorCallback_(0), firstErrorOccurred_(false), errorCallbackUserData_(0) X+{ X+} X+ X+MidiApi :: ~MidiApi( void ) X+{ X+} X+ X+void MidiApi :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData = 0 ) X+{ X+ errorCallback_ = errorCallback; X+ errorCallbackUserData_ = userData; X+} X+ X+void MidiApi :: error( RtMidiError::Type type, std::string errorString ) X+{ X+ if ( errorCallback_ ) { X+ X+ if ( firstErrorOccurred_ ) X+ return; X+ X+ firstErrorOccurred_ = true; X+ const std::string errorMessage = errorString; X+ X+ errorCallback_( type, errorMessage, errorCallbackUserData_ ); X+ firstErrorOccurred_ = false; X+ return; X+ } X+ X+ if ( type == RtMidiError::WARNING ) { X+ std::cerr << '\n' << errorString << "\n\n"; X+ } X+ else if ( type == RtMidiError::DEBUG_WARNING ) { X #if defined(__RTMIDI_DEBUG__) X- std::cerr << "[chuck](via RtMidi): " << errorString_ << "\n"; X+ std::cerr << '\n' << errorString << "\n\n"; X #endif X } X else { X- // std::cerr << "[chuck](via RtMidi): " << errorString_ << "\n"; X- throw RtMidiError( errorString_, type ); X+ std::cerr << '\n' << errorString << "\n\n"; X+ throw RtMidiError( errorString, type ); X } X } X X- X //*********************************************************************// X-// Common RtMidiIn Definitions X+// Common MidiInApi Definitions X //*********************************************************************// X X-RtMidiIn :: RtMidiIn() : RtMidi() X+MidiInApi :: MidiInApi( unsigned int queueSizeLimit ) X+ : MidiApi() X { X- this->initialize(); X+ // Allocate the MIDI queue. X+ inputData_.queue.ringSize = queueSizeLimit; X+ if ( inputData_.queue.ringSize > 0 ) X+ inputData_.queue.ring = new MidiMessage[ inputData_.queue.ringSize ]; X } X X-void RtMidiIn :: setCallback( RtMidiCallback callback, void *userData ) X+MidiInApi :: ~MidiInApi( void ) X { X+ // Delete the MIDI queue. X+ if ( inputData_.queue.ringSize > 0 ) delete [] inputData_.queue.ring; X+} X+ X+void MidiInApi :: setCallback( RtMidiIn::RtMidiCallback callback, void *userData ) X+{ X if ( inputData_.usingCallback ) { X- errorString_ = "RtMidiIn::setCallback: callback function is already set!"; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiInApi::setCallback: a callback function is already set!"; X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X if ( !callback ) { X errorString_ = "RtMidiIn::setCallback: callback function value is invalid!"; X- error( RtMidiError::WARNING ); X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X- inputData_.userCallback = (void *) callback; X+ inputData_.userCallback = callback; X inputData_.userData = userData; X inputData_.usingCallback = true; X } X X-void RtMidiIn :: cancelCallback() X+void MidiInApi :: cancelCallback() X { X if ( !inputData_.usingCallback ) { X errorString_ = "RtMidiIn::cancelCallback: no callback function was set!"; X- error( RtMidiError::WARNING ); X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X@@ -112,57 +648,107 @@ void RtMidiIn :: cancelCallback() X inputData_.usingCallback = false; X } X X-void RtMidiIn :: setQueueSizeLimit( unsigned int queueSize ) X+void MidiInApi :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) X { X- inputData_.queueLimit = queueSize; X-} X- X-void RtMidiIn :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) X-{ X inputData_.ignoreFlags = 0; X if ( midiSysex ) inputData_.ignoreFlags = 0x01; X if ( midiTime ) inputData_.ignoreFlags |= 0x02; X if ( midiSense ) inputData_.ignoreFlags |= 0x04; X } X X-double RtMidiIn :: getMessage( std::vector *message ) X+double MidiInApi :: getMessage( std::vector *message ) X { X message->clear(); X X if ( inputData_.usingCallback ) { X- errorString_ = "RtMidiIn::getNextMessage: user callback is currently set for this port."; X- error( RtMidiError::WARNING ); X+ errorString_ = "RtMidiIn::getNextMessage: a user callback is currently set for this port."; X+ error( RtMidiError::WARNING, errorString_ ); X return 0.0; X } X X- if ( inputData_.queue.size() == 0 ) return 0.0; X+ double timeStamp; X+ if ( !inputData_.queue.pop( message, &timeStamp ) ) X+ return 0.0; X X+ return timeStamp; X+} X+ X+unsigned int MidiInApi::MidiQueue::size( unsigned int *__back, X+ unsigned int *__front ) X+{ X+ // Access back/front members exactly once and make stack copies for X+ // size calculation X+ unsigned int _back = back, _front = front, _size; X+ if ( _back >= _front ) X+ _size = _back - _front; X+ else X+ _size = ringSize - _front + _back; X+ X+ // Return copies of back/front so no new and unsynchronized accesses X+ // to member variables are needed. X+ if ( __back ) *__back = _back; X+ if ( __front ) *__front = _front; X+ return _size; X+} X+ X+// As long as we haven't reached our queue size limit, push the message. X+bool MidiInApi::MidiQueue::push( const MidiInApi::MidiMessage& msg ) X+{ X+ // Local stack copies of front/back X+ unsigned int _back, _front, _size; X+ X+ // Get back/front indexes exactly once and calculate current size X+ _size = size( &_back, &_front ); X+ X+ if ( _size < ringSize-1 ) X+ { X+ ring[_back] = msg; X+ back = (back+1)%ringSize; X+ return true; X+ } X+ X+ return false; X+} X+ X+bool MidiInApi::MidiQueue::pop( std::vector *msg, double* timeStamp ) X+{ X+ // Local stack copies of front/back X+ unsigned int _back, _front, _size; X+ X+ // Get back/front indexes exactly once and calculate current size X+ _size = size( &_back, &_front ); X+ X+ if ( _size == 0 ) X+ return false; X+ X // Copy queued message to the vector pointer argument and then "pop" it. X- std::vector *bytes = &(inputData_.queue.front().bytes); X- message->assign( bytes->begin(), bytes->end() ); X- double deltaTime = inputData_.queue.front().timeStamp; X- inputData_.queue.pop(); X+ msg->assign( ring[_front].bytes.begin(), ring[_front].bytes.end() ); X+ *timeStamp = ring[_front].timeStamp; X X- return deltaTime; X+ // Update front X+ front = (front+1)%ringSize; X+ return true; X } X X //*********************************************************************// X-// Common RtMidiOut Definitions X+// Common MidiOutApi Definitions X //*********************************************************************// X X-RtMidiOut :: RtMidiOut() : RtMidi() X+MidiOutApi :: MidiOutApi( void ) X+ : MidiApi() X { X- this->initialize(); X } X X+MidiOutApi :: ~MidiOutApi( void ) X+{ X+} X X-//*********************************************************************// X-// API: Macintosh OS-X X-//*********************************************************************// X+// *************************************************** // X+// X+// OS/API-specific methods. X+// X+// *************************************************** // X X-// API information found at: X-// - http://developer. apple .com/audio/pdf/coreaudio.pdf X- X #if defined(__MACOSX_CORE__) X X // The CoreMIDI API is based on the use of a callback function for X@@ -171,11 +757,12 @@ RtMidiOut :: RtMidiOut() : RtMidi() X X // OS-X CoreMIDI header files. X #include X-#include X X-// CoreMIDI naming helper function prototypes X-static void readable_name(MIDIEndpointRef end, char *buffer, int bufsize); X-static int get_device_name(SInt32 uniqueid, char*buffer, int bufsize); X+// these are not available on iOS. X+#if (TARGET_OS_IPHONE == 0) X+ #include X+ #include X+#endif X X // A structure to hold variables related to the CoreMIDI API X // implementation. X@@ -185,217 +772,84 @@ struct CoreMidiData { X MIDIEndpointRef endpoint; X MIDIEndpointRef destinationId; X unsigned long long lastTime; X+ MIDISysexSendRequest sysexreq; X }; X X-// ****** X-// API: OS-X X-// helper functions X-// ***** X- X-// coreMIDI port naming helper function X-/* X- This wraps up the code to take a passed-in endpoint and work out X- a nice human-readable name for it. X- X- For the moment, this function will return firstly the device name + X- port name, or if we can work out what external devices are hooked X- in, that name. It only grabs the very first connected device name. X- X- NB. won't get connected devices correctly before 10.3. X-*/ X-static void readable_name(MIDIEndpointRef end, char *buffer, int bufsize) X-{ X- MIDIEntityRef ent = 0; // NULL; X- MIDIDeviceRef dev = 0; //NULL; X- int ii, count, length;//, ret; X- SInt32 *idarray; X- CFDataRef data = NULL; X- CFStringRef s; X- X- buffer[0] = '\0'; X- X- if( MIDIObjectGetDataProperty(end, kMIDIPropertyConnectionUniqueID, &data) == 0) X- { X- length = CFDataGetLength(data) / sizeof(SInt32); X- idarray = (SInt32 *) CFDataGetBytePtr(data); X- count = 0; X- for (ii = 0; ii < length; ii++) { X- if (bufsize < 3) X- break; X- if (count > 0) { X- strcpy(buffer, ", "); X- buffer += 2; X- bufsize -= 2; X- } X- X- if (get_device_name(idarray[ii], buffer, bufsize) == 0) { X- count++; X- bufsize -= strlen(buffer); X- buffer += strlen(buffer); X- } X- } X- X- CFRelease(data); X- if (count > 0) X- return; X- } X- X- char * mid = buffer; X- // build up the name of the enclosing device, if it can be found. X- if (MIDIEndpointGetEntity(end, &ent) == 0) { X- if (MIDIEntityGetDevice(ent, &dev) == 0) { X- if (MIDIObjectGetStringProperty(dev, kMIDIPropertyName, &s) == 0) { X- CFStringGetCString(s, buffer, bufsize, 0); X- bufsize -= strlen(buffer) + 1; X- buffer += strlen(buffer); X- X- CFRelease(s); X- } X- } X- } X- X- // ge X- char buffy[128]; X- // Now add the port/endpoint name. X- // no need to update buffer pointer. Last item. X- if (MIDIObjectGetStringProperty(end, kMIDIPropertyName, &s) == 0) { X- CFStringGetCString(s, buffy, 128, 0); X- CFRelease(s); X- X- // copy if different X- if( strcmp( mid, buffy ) && strlen(buffy) < bufsize ) { X- *buffer = ' '; X- buffer++; X- strcpy( buffer, buffy ); X- } X- } X-} X- X- X-// port naming helper function (External devices) X-/* X- Uses the midiojectfindbyuniqueid function to hunt down the relevant X- device and copies its name into the buffer provided. We don't know X- what type of device we'll get back, so we first throw away any X- external flags (We don't care), and then cast it up to device which X- is the thing which has a useful name. X- X- If it can't find one, return -1. 0 returned on success. X-*/ X-static int get_device_name(SInt32 uniqueid, char *buffer, int bufsize) X-{ X- int ret; X- MIDIObjectRef object = 0; // NULL; // 1.3.1.0 X- MIDIObjectType type; X- X- MIDIDeviceRef dev = 0; // NULL; X- MIDIEntityRef ent = 0; // NULL; X- MIDIEndpointRef end = 0; // NULL; X- CFStringRef name = NULL; X- X- ret = MIDIObjectFindByUniqueID(uniqueid, &object, &type); X- if (ret < 0) X- return -1; X- X- // now clear any external flag. X- if (type > 0) X- type = (MIDIObjectType) (type & (~kMIDIObjectType_ExternalMask)); X- X- if (type == kMIDIObjectType_Device) { X- dev = (MIDIDeviceRef) object; X- } else if (type == kMIDIObjectType_Entity) { X- ent = (MIDIEntityRef) object; X- if (MIDIEntityGetDevice(ent, &dev)) { X- return -1; X- } X- } if (type == kMIDIObjectType_Source || X- type == kMIDIObjectType_Destination) { X- end = (MIDIEndpointRef) object; X- if (MIDIEndpointGetEntity(end, &ent)) { X- return -1; X- } X- if (MIDIEntityGetDevice(ent, &dev)) { X- return -1; X- } X- } else { X- // unknown type X- printf("Unknown type %d returned from findobject\n", (int) type); X- CFRelease( &object ); // 1.3.1.0 X- return -1; X- } X- X- MIDIObjectGetStringProperty(dev, kMIDIPropertyName, &name); X- CFStringGetCString(name, buffer, bufsize, 0); X- CFRelease(name); X- X- return 0; X-} X- X- X- X- X- X //*********************************************************************// X // API: OS-X X-// Class Definitions: RtMidiIn X+// Class Definitions: MidiInCore X //*********************************************************************// X X-void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef ) X+static void midiInputCallback( const MIDIPacketList *list, void *procRef, void */*srcRef*/ ) X { X- RtMidiIn::RtMidiInData *data = static_cast (procRef); X+ MidiInApi::RtMidiInData *data = static_cast (procRef); X CoreMidiData *apiData = static_cast (data->apiData); X X- bool continueSysex = false; X unsigned char status; X unsigned short nBytes, iByte, size; X unsigned long long time; X- RtMidiIn::MidiMessage message; X+ X+ bool& continueSysex = data->continueSysex; X+ MidiInApi::MidiMessage& message = data->message; X+ X const MIDIPacket *packet = &list->packet[0]; X for ( unsigned int i=0; inumPackets; ++i ) { X X // My interpretation of the CoreMIDI documentation: all message X // types, except sysex, are complete within a packet and there may X // be several of them in a single packet. Sysex messages can be X- // broken across multiple packets but are bundled alone within a X- // packet. I'm assuming that sysex messages, if segmented, must X- // be complete within the same MIDIPacketList. X+ // broken across multiple packets and PacketLists but are bundled X+ // alone within each packet (these packets do not contain other X+ // message types). If sysex messages are split across multiple X+ // MIDIPacketLists, they must be handled by multiple calls to this X+ // function. X X nBytes = packet->length; X- if ( nBytes == 0 ) continue; X+ if ( nBytes == 0 ) { X+ packet = MIDIPacketNext( packet ); X+ continue; X+ } X X // Calculate time stamp. X- message.timeStamp = 0.0; X- if ( data->firstMessage ) X+ if ( data->firstMessage ) { X+ message.timeStamp = 0.0; X data->firstMessage = false; X+ } X else { X time = packet->timeStamp; X+ if ( time == 0 ) { // this happens when receiving asynchronous sysex messages X+ time = AudioGetCurrentHostTime(); X+ } X time -= apiData->lastTime; X time = AudioConvertHostTimeToNanos( time ); X- message.timeStamp = time * 0.000000001; X+ if ( !continueSysex ) X+ message.timeStamp = time * 0.000000001; X } X- apiData->lastTime = packet->timeStamp; X X+ // Track whether any non-filtered messages were found in this X+ // packet for timestamp calculation X+ bool foundNonFiltered = false; X+ X iByte = 0; X if ( continueSysex ) { X // We have a continuing, segmented sysex message. X- if ( !(data->ignoreFlags & 0x01) ) { X+ if ( !( data->ignoreFlags & 0x01 ) ) { X // If we're not ignoring sysex messages, copy the entire packet. X- for ( unsigned int j=0; jdata[j] ); X } X- if ( packet->data[nBytes] == 0xF7 ) continueSysex = false; X- if ( !continueSysex ) { X+ continueSysex = packet->data[nBytes-1] != 0xF7; X+ X+ if ( !( data->ignoreFlags & 0x01 ) && !continueSysex ) { X // If not a continuing sysex message, invoke the user callback function or queue the message. X- if ( data->usingCallback && message.bytes.size() > 0 ) { X+ if ( data->usingCallback ) { X RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; X callback( message.timeStamp, &message.bytes, data->userData ); X } X else { X // As long as we haven't reached our queue size limit, push the message. X- if ( data->queueLimit > data->queue.size() ) X- data->queue.push( message ); X- else X- std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; X+ if ( !data->queue.push( message ) ) X+ std::cerr << "\nMidiInCore: message queue limit reached!!\n\n"; X } X message.bytes.clear(); X } X@@ -417,26 +871,24 @@ void midiInputCallback( const MIDIPacketList *list, vo X iByte = nBytes; X } X else size = nBytes - iByte; X- if ( packet->data[nBytes] == 0xF7 ) continueSysex = false; X+ continueSysex = packet->data[nBytes-1] != 0xF7; X } X- else if ( status < 0xF3 ) { X- if ( status == 0xF1 && (data->ignoreFlags & 0x02) ) { X- // A MIDI time code message and we're ignoring it. X+ else if ( status == 0xF1 ) { X+ // A MIDI time code message X+ if ( data->ignoreFlags & 0x02 ) { X size = 0; X- iByte += 3; X+ iByte += 2; X } X- else size = 3; X+ else size = 2; X } X+ else if ( status == 0xF2 ) size = 3; X else if ( status == 0xF3 ) size = 2; X- else if ( status == 0xF8 ) { X- size = 1; X- if ( data->ignoreFlags & 0x02 ) { X- // A MIDI timing tick message and we're ignoring it. X- size = 0; X- iByte += 3; X- } X+ else if ( status == 0xF8 && ( data->ignoreFlags & 0x02 ) ) { X+ // A MIDI timing tick message and we're ignoring it. X+ size = 0; X+ iByte += 1; X } X- else if ( status == 0xFE && (data->ignoreFlags & 0x04) ) { X+ else if ( status == 0xFE && ( data->ignoreFlags & 0x04 ) ) { X // A MIDI active sensing message and we're ignoring it. X size = 0; X iByte += 1; X@@ -445,6 +897,7 @@ void midiInputCallback( const MIDIPacketList *list, vo X X // Copy the MIDI data to our vector. X if ( size ) { X+ foundNonFiltered = true; X message.bytes.assign( &packet->data[iByte], &packet->data[iByte+size] ); X if ( !continueSysex ) { X // If not a continuing sysex message, invoke the user callback function or queue the message. X@@ -454,10 +907,8 @@ void midiInputCallback( const MIDIPacketList *list, vo X } X else { X // As long as we haven't reached our queue size limit, push the message. X- if ( data->queueLimit > data->queue.size() ) X- data->queue.push( message ); X- else X- std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; X+ if ( !data->queue.push( message ) ) X+ std::cerr << "\nMidiInCore: message queue limit reached!!\n\n"; X } X message.bytes.clear(); X } X@@ -465,18 +916,49 @@ void midiInputCallback( const MIDIPacketList *list, vo X } X } X } X+ X+ // Save the time of the last non-filtered message X+ if ( foundNonFiltered ) { X+ apiData->lastTime = packet->timeStamp; X+ if ( apiData->lastTime == 0 ) { // this happens when receiving asynchronous sysex messages X+ apiData->lastTime = AudioGetCurrentHostTime(); X+ } X+ } X+ X packet = MIDIPacketNext(packet); X } X } X X-void RtMidiIn :: initialize( void ) X+MidiInCore :: MidiInCore( const std::string &clientName, unsigned int queueSizeLimit ) X+ : MidiInApi( queueSizeLimit ) X { X+ MidiInCore::initialize( clientName ); X+} X+ X+MidiInCore :: ~MidiInCore( void ) X+{ X+ // Close a connection if it exists. X+ MidiInCore::closePort(); X+ X+ // Cleanup. X+ CoreMidiData *data = static_cast (apiData_); X+ MIDIClientDispose( data->client ); X+ if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); X+ delete data; X+} X+ X+void MidiInCore :: initialize( const std::string& clientName ) X+{ X // Set up our client. X MIDIClientRef client; X- OSStatus result = MIDIClientCreate( CFSTR("RtMidi Input Client"), NULL, NULL, &client ); X+ CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ); X+ OSStatus result = MIDIClientCreate(name, NULL, NULL, &client ); X if ( result != noErr ) { X- errorString_ = "RtMidiIn::initialize: error creating OS-X MIDI client object."; X- error( RtMidiError::DRIVER_ERROR ); X+ std::ostringstream ost; X+ ost << "MidiInCore::initialize: error creating OS-X MIDI client object (" << result << ")."; X+ errorString_ = ost.str(); X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X // Save our api-specific connection information. X@@ -485,45 +967,56 @@ void RtMidiIn :: initialize( void ) X data->endpoint = 0; X apiData_ = (void *) data; X inputData_.apiData = (void *) data; X+ CFRelease( name ); X } X X-void RtMidiIn :: openPort( unsigned int portNumber ) X+void MidiInCore :: openPort( unsigned int portNumber, const std::string &portName ) X { X if ( connected_ ) { X- errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiInCore::openPort: a valid connection already exists!"; X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X+ CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); X unsigned int nSrc = MIDIGetNumberOfSources(); X- if (nSrc < 1) { X- errorString_ = "RtMidiIn::openPort: no MIDI input sources found!"; X- error( RtMidiError::NO_DEVICES_FOUND ); X+ if ( nSrc < 1 ) { X+ errorString_ = "MidiInCore::openPort: no MIDI input sources found!"; X+ error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); X+ return; X } X X- std::ostringstream ost; X if ( portNumber >= nSrc ) { X- ost << "RtMidiIn::openPort: 'portNumber' argument (" << portNumber << ") is invalid."; X+ std::ostringstream ost; X+ ost << "MidiInCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; X errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X+ error( RtMidiError::INVALID_PARAMETER, errorString_ ); X+ return; X } X X MIDIPortRef port; X CoreMidiData *data = static_cast (apiData_); X- OSStatus result = MIDIInputPortCreate( data->client, CFSTR("RtMidi MIDI Input Port"), midiInputCallback, (void *)&inputData_, &port ); X+ CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); X+ OSStatus result = MIDIInputPortCreate( data->client, X+ portNameRef, X+ midiInputCallback, (void *)&inputData_, &port ); X+ CFRelease( portNameRef ); X+ X if ( result != noErr ) { X MIDIClientDispose( data->client ); X- errorString_ = "RtMidiIn::openPort: error creating OS-X MIDI input port."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiInCore::openPort: error creating OS-X MIDI input port."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X // Get the desired input source identifier. X MIDIEndpointRef endpoint = MIDIGetSource( portNumber ); X- if ( endpoint == 0 ) { // 1.3.1.0: changed from NULL to 0 X+ if ( endpoint == 0 ) { X MIDIPortDispose( port ); X MIDIClientDispose( data->client ); X- errorString_ = "RtMidiIn::openPort: error getting MIDI input source reference."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiInCore::openPort: error getting MIDI input source reference."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X // Make the connection. X@@ -531,8 +1024,9 @@ void RtMidiIn :: openPort( unsigned int portNumber ) X if ( result != noErr ) { X MIDIPortDispose( port ); X MIDIClientDispose( data->client ); X- errorString_ = "RtMidiIn::openPort: error connecting OS-X MIDI input port."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiInCore::openPort: error connecting OS-X MIDI input port."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X // Save our api-specific port information. X@@ -541,114 +1035,254 @@ void RtMidiIn :: openPort( unsigned int portNumber ) X connected_ = true; X } X X-void RtMidiIn :: openVirtualPort() X+void MidiInCore :: openVirtualPort( const std::string &portName ) X { X CoreMidiData *data = static_cast (apiData_); X X // Create a virtual MIDI input destination. X MIDIEndpointRef endpoint; X- OSStatus result = MIDIDestinationCreate( data->client, CFSTR("RtMidi Input"), midiInputCallback, (void *)&inputData_, &endpoint ); X+ CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); X+ OSStatus result = MIDIDestinationCreate( data->client, X+ portNameRef, X+ midiInputCallback, (void *)&inputData_, &endpoint ); X+ CFRelease( portNameRef ); X+ X if ( result != noErr ) { X- errorString_ = "RtMidiIn::openVirtualPort: error creating virtual OS-X MIDI destination."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiInCore::openVirtualPort: error creating virtual OS-X MIDI destination."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X // Save our api-specific connection information. X data->endpoint = endpoint; X } X X-void RtMidiIn :: closePort( void ) X+void MidiInCore :: closePort( void ) X { X- if ( connected_ ) { X- CoreMidiData *data = static_cast (apiData_); X+ CoreMidiData *data = static_cast (apiData_); X+ X+ if ( data->endpoint ) { X+ MIDIEndpointDispose( data->endpoint ); X+ data->endpoint = 0; X+ } X+ X+ if ( data->port ) { X MIDIPortDispose( data->port ); X- connected_ = false; X+ data->port = 0; X } X+ X+ connected_ = false; X } X X-RtMidiIn :: ~RtMidiIn() X+void MidiInCore :: setClientName ( const std::string& ) X { X- // Close a connection if it exists. X- closePort(); X X- // Cleanup. X- CoreMidiData *data = static_cast (apiData_); X- MIDIClientDispose( data->client ); X- if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); X- delete data; X+ errorString_ = "MidiInCore::setClientName: this function is not implemented for the MACOSX_CORE API!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ X } X X-unsigned int RtMidiIn :: getPortCount() X+void MidiInCore :: setPortName ( const std::string& ) X { X+ X+ errorString_ = "MidiInCore::setPortName: this function is not implemented for the MACOSX_CORE API!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ X+} X+ X+unsigned int MidiInCore :: getPortCount() X+{ X+ CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); X return MIDIGetNumberOfSources(); X } X X-std::string RtMidiIn :: getPortName( unsigned int portNumber ) X+// This function was submitted by Douglas Casey Tucker and apparently X+// derived largely from PortMidi. X+CFStringRef EndpointName( MIDIEndpointRef endpoint, bool isExternal ) X { X-// CFStringRef nameRef; X+ CFMutableStringRef result = CFStringCreateMutable( NULL, 0 ); X+ CFStringRef str; X+ X+ // Begin with the endpoint's name. X+ str = NULL; X+ MIDIObjectGetStringProperty( endpoint, kMIDIPropertyName, &str ); X+ if ( str != NULL ) { X+ CFStringAppend( result, str ); X+ CFRelease( str ); X+ } X+ X+ MIDIEntityRef entity = 0; X+ MIDIEndpointGetEntity( endpoint, &entity ); X+ if ( entity == 0 ) X+ // probably virtual X+ return result; X+ X+ if ( CFStringGetLength( result ) == 0 ) { X+ // endpoint name has zero length -- try the entity X+ str = NULL; X+ MIDIObjectGetStringProperty( entity, kMIDIPropertyName, &str ); X+ if ( str != NULL ) { X+ CFStringAppend( result, str ); X+ CFRelease( str ); X+ } X+ } X+ // now consider the device's name X+ MIDIDeviceRef device = 0; X+ MIDIEntityGetDevice( entity, &device ); X+ if ( device == 0 ) X+ return result; X+ X+ str = NULL; X+ MIDIObjectGetStringProperty( device, kMIDIPropertyName, &str ); X+ if ( CFStringGetLength( result ) == 0 ) { X+ CFRelease( result ); X+ return str; X+ } X+ if ( str != NULL ) { X+ // if an external device has only one entity, throw away X+ // the endpoint name and just use the device name X+ if ( isExternal && MIDIDeviceGetNumberOfEntities( device ) < 2 ) { X+ CFRelease( result ); X+ return str; X+ } else { X+ if ( CFStringGetLength( str ) == 0 ) { X+ CFRelease( str ); X+ return result; X+ } X+ // does the entity name already start with the device name? X+ // (some drivers do this though they shouldn't) X+ // if so, do not prepend X+ if ( CFStringCompareWithOptions( result, /* endpoint name */ X+ str /* device name */, X+ CFRangeMake(0, CFStringGetLength( str ) ), 0 ) != kCFCompareEqualTo ) { X+ // prepend the device name to the entity name X+ if ( CFStringGetLength( result ) > 0 ) X+ CFStringInsert( result, 0, CFSTR(" ") ); X+ X+ CFStringInsert( result, 0, str ); X+ } X+ CFRelease( str ); X+ } X+ } X+ return result; X+} X+ X+// This function was submitted by Douglas Casey Tucker and apparently X+// derived largely from PortMidi. X+static CFStringRef ConnectedEndpointName( MIDIEndpointRef endpoint ) X+{ X+ CFMutableStringRef result = CFStringCreateMutable( NULL, 0 ); X+ CFStringRef str; X+ OSStatus err; X+ int i; X+ X+ // Does the endpoint have connections? X+ CFDataRef connections = NULL; X+ int nConnected = 0; X+ bool anyStrings = false; X+ err = MIDIObjectGetDataProperty( endpoint, kMIDIPropertyConnectionUniqueID, &connections ); X+ if ( connections != NULL ) { X+ // It has connections, follow them X+ // Concatenate the names of all connected devices X+ nConnected = CFDataGetLength( connections ) / sizeof(MIDIUniqueID); X+ if ( nConnected ) { X+ const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections)); X+ for ( i=0; i= MIDIGetNumberOfSources() ) { X- ost << "RtMidiIn::getPortName: 'portNumber' argument (" << portNumber << ") is invalid."; X+ std::ostringstream ost; X+ ost << "MidiInCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; X errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X+ error( RtMidiError::WARNING, errorString_ ); X+ return stringName; X } X+ X portRef = MIDIGetSource( portNumber ); X-#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 X- readable_name(portRef, name, sizeof(name)); X-#else X- MIDIObjectGetStringProperty( portRef, kMIDIPropertyName, &nameRef ); X- CFStringGetCString( nameRef, name, sizeof(name), 0); X+ nameRef = ConnectedEndpointName( portRef ); X+ CFStringGetCString( nameRef, name, sizeof(name), kCFStringEncodingUTF8 ); X CFRelease( nameRef ); X-#endif X- std::string stringName = name; X- return stringName; X+ X+ return stringName = name; X } X X //*********************************************************************// X // API: OS-X X-// Class Definitions: RtMidiOut X+// Class Definitions: MidiOutCore X //*********************************************************************// X X-unsigned int RtMidiOut :: getPortCount() X+MidiOutCore :: MidiOutCore( const std::string &clientName ) X+ : MidiOutApi() X { X- return MIDIGetNumberOfDestinations(); X+ MidiOutCore::initialize( clientName ); X } X X-std::string RtMidiOut :: getPortName( unsigned int portNumber ) X+MidiOutCore :: ~MidiOutCore( void ) X { X-// CFStringRef nameRef; X- MIDIEndpointRef portRef; X- std::ostringstream ost; X- char name[128]; X+ // Close a connection if it exists. X+ MidiOutCore::closePort(); X X- if ( portNumber >= MIDIGetNumberOfDestinations() ) { X- ost << "RtMidiOut::getPortName: 'portNumber' argument (" << portNumber << ") is invalid."; X- errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X- } X- portRef = MIDIGetDestination( portNumber ); X-#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 X- readable_name(portRef, name, sizeof(name)); X-#else X- MIDIObjectGetStringProperty( portRef, kMIDIPropertyName, &nameRef ); X- CFStringGetCString( nameRef, name, sizeof(name), 0); X- CFRelease( nameRef ); X-#endif X- std::string stringName = name; X- return stringName; X+ // Cleanup. X+ CoreMidiData *data = static_cast (apiData_); X+ MIDIClientDispose( data->client ); X+ if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); X+ delete data; X } X X-void RtMidiOut :: initialize( void ) X+void MidiOutCore :: initialize( const std::string& clientName ) X { X // Set up our client. X MIDIClientRef client; X- OSStatus result = MIDIClientCreate( CFSTR("RtMidi Output Client"), NULL, NULL, &client ); X+ CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ); X+ OSStatus result = MIDIClientCreate(name, NULL, NULL, &client ); X if ( result != noErr ) { X- errorString_ = "RtMidiOut::initialize: error creating OS-X MIDI client object."; X- error( RtMidiError::DRIVER_ERROR ); X+ std::ostringstream ost; X+ ost << "MidiInCore::initialize: error creating OS-X MIDI client object (" << result << ")."; X+ errorString_ = ost.str(); X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X // Save our api-specific connection information. X@@ -656,45 +1290,83 @@ void RtMidiOut :: initialize( void ) X data->client = client; X data->endpoint = 0; X apiData_ = (void *) data; X+ CFRelease( name ); X } X X-void RtMidiOut :: openPort( unsigned int portNumber ) X+unsigned int MidiOutCore :: getPortCount() X { X+ CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); X+ return MIDIGetNumberOfDestinations(); X+} X+ X+std::string MidiOutCore :: getPortName( unsigned int portNumber ) X+{ X+ CFStringRef nameRef; X+ MIDIEndpointRef portRef; X+ char name[128]; X+ X+ std::string stringName; X+ CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); X+ if ( portNumber >= MIDIGetNumberOfDestinations() ) { X+ std::ostringstream ost; X+ ost << "MidiOutCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; X+ errorString_ = ost.str(); X+ error( RtMidiError::WARNING, errorString_ ); X+ return stringName; X+ } X+ X+ portRef = MIDIGetDestination( portNumber ); X+ nameRef = ConnectedEndpointName(portRef); X+ CFStringGetCString( nameRef, name, sizeof(name), kCFStringEncodingUTF8 ); X+ CFRelease( nameRef ); X+ X+ return stringName = name; X+} X+ X+void MidiOutCore :: openPort( unsigned int portNumber, const std::string &portName ) X+{ X if ( connected_ ) { X- errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiOutCore::openPort: a valid connection already exists!"; X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X+ CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); X unsigned int nDest = MIDIGetNumberOfDestinations(); X if (nDest < 1) { X- errorString_ = "RtMidiOut::openPort: no MIDI output destinations found!"; X- error( RtMidiError::NO_DEVICES_FOUND ); X+ errorString_ = "MidiOutCore::openPort: no MIDI output destinations found!"; X+ error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); X+ return; X } X X- std::ostringstream ost; X if ( portNumber >= nDest ) { X- ost << "RtMidiOut::openPort: 'portNumber' argument (" << portNumber << ") is invalid."; X+ std::ostringstream ost; X+ ost << "MidiOutCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; X errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X+ error( RtMidiError::INVALID_PARAMETER, errorString_ ); X+ return; X } X X MIDIPortRef port; X CoreMidiData *data = static_cast (apiData_); X- OSStatus result = MIDIOutputPortCreate( data->client, CFSTR("RtMidi Virtual MIDI Output Port"), &port ); X+ CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); X+ OSStatus result = MIDIOutputPortCreate( data->client, portNameRef, &port ); X+ CFRelease( portNameRef ); X if ( result != noErr ) { X MIDIClientDispose( data->client ); X- errorString_ = "RtMidiOut::openPort: error creating OS-X MIDI output port."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiOutCore::openPort: error creating OS-X MIDI output port."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X // Get the desired output port identifier. X MIDIEndpointRef destination = MIDIGetDestination( portNumber ); X- if ( destination == 0 ) { // 1.3.1.0: changed from NULL to 0 X+ if ( destination == 0 ) { X MIDIPortDispose( port ); X MIDIClientDispose( data->client ); X- errorString_ = "RtMidiOut::openPort: error getting MIDI output destination reference."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiOutCore::openPort: error getting MIDI output destination reference."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X // Save our api-specific connection information. X@@ -703,78 +1375,120 @@ void RtMidiOut :: openPort( unsigned int portNumber ) X connected_ = true; X } X X-void RtMidiOut :: closePort( void ) X+void MidiOutCore :: closePort( void ) X { X- if ( connected_ ) { X- CoreMidiData *data = static_cast (apiData_); X+ CoreMidiData *data = static_cast (apiData_); X+ X+ if ( data->endpoint ) { X+ MIDIEndpointDispose( data->endpoint ); X+ data->endpoint = 0; X+ } X+ X+ if ( data->port ) { X MIDIPortDispose( data->port ); X- connected_ = false; X+ data->port = 0; X } X+ X+ connected_ = false; X } X X-void RtMidiOut :: openVirtualPort() X+void MidiOutCore :: setClientName ( const std::string& ) X { X+ X+ errorString_ = "MidiOutCore::setClientName: this function is not implemented for the MACOSX_CORE API!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ X+} X+ X+void MidiOutCore :: setPortName ( const std::string& ) X+{ X+ X+ errorString_ = "MidiOutCore::setPortName: this function is not implemented for the MACOSX_CORE API!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ X+} X+ X+void MidiOutCore :: openVirtualPort( const std::string &portName ) X+{ X CoreMidiData *data = static_cast (apiData_); X X if ( data->endpoint ) { X- errorString_ = "RtMidiOut::openVirtualPort: a virtual output port already exists!"; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiOutCore::openVirtualPort: a virtual output port already exists!"; X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X // Create a virtual MIDI output source. X MIDIEndpointRef endpoint; X- OSStatus result = MIDISourceCreate( data->client, CFSTR("RtMidi Output"), &endpoint ); X+ CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); X+ OSStatus result = MIDISourceCreate( data->client, portNameRef, &endpoint ); X+ CFRelease( portNameRef ); X+ X if ( result != noErr ) { X- errorString_ = "RtMidiOut::initialize: error creating OS-X virtual MIDI source."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiOutCore::initialize: error creating OS-X virtual MIDI source."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X // Save our api-specific connection information. X data->endpoint = endpoint; X } X X-RtMidiOut :: ~RtMidiOut() X+void MidiOutCore :: sendMessage( const unsigned char *message, size_t size ) X { X- // Close a connection if it exists. X- closePort(); X+ // We use the MIDISendSysex() function to asynchronously send sysex X+ // messages. Otherwise, we use a single CoreMidi MIDIPacket. X+ unsigned int nBytes = static_cast (size); X+ if ( nBytes == 0 ) { X+ errorString_ = "MidiOutCore::sendMessage: no data in message argument!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ return; X+ } X X- // Cleanup. X+ MIDITimeStamp timeStamp = AudioGetCurrentHostTime(); X CoreMidiData *data = static_cast (apiData_); X- MIDIClientDispose( data->client ); X- if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); X- delete data; X-} X+ OSStatus result; X X-void RtMidiOut :: sendMessage( std::vector *message ) X-{ X- unsigned int nBytes = message->size(); X- // Pad the buffer for extra (unknown) structure data. X- Byte buffer[nBytes+32]; X- MIDIPacketList *pktlist = (MIDIPacketList *) buffer; X- MIDIPacket *curPacket = MIDIPacketListInit( pktlist ); X+ if ( message[0] != 0xF0 && nBytes > 3 ) { X+ errorString_ = "MidiOutCore::sendMessage: message format problem ... not sysex but > 3 bytes?"; X+ error( RtMidiError::WARNING, errorString_ ); X+ return; X+ } X X- MIDITimeStamp timeStamp = 0; X- curPacket = MIDIPacketListAdd( pktlist, sizeof(buffer), curPacket, timeStamp, nBytes, &message->at(0) ); X+ Byte buffer[nBytes+(sizeof( MIDIPacketList ))]; X+ ByteCount listSize = sizeof( buffer ); X+ MIDIPacketList *packetList = (MIDIPacketList*)buffer; X+ MIDIPacket *packet = MIDIPacketListInit( packetList ); X X- CoreMidiData *data = static_cast (apiData_); X+ ByteCount remainingBytes = nBytes; X+ while ( remainingBytes && packet ) { X+ ByteCount bytesForPacket = remainingBytes > 65535 ? 65535 : remainingBytes; // 65535 = maximum size of a MIDIPacket X+ const Byte* dataStartPtr = (const Byte *) &message[nBytes - remainingBytes]; X+ packet = MIDIPacketListAdd( packetList, listSize, packet, timeStamp, bytesForPacket, dataStartPtr ); X+ remainingBytes -= bytesForPacket; X+ } X X+ if ( !packet ) { X+ errorString_ = "MidiOutCore::sendMessage: could not allocate packet list"; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X+ X // Send to any destinations that may have connected to us. X- OSStatus result; X if ( data->endpoint ) { X- result = MIDIReceived( data->endpoint, pktlist ); X+ result = MIDIReceived( data->endpoint, packetList ); X if ( result != noErr ) { X- errorString_ = "RtMidiOut::sendMessage: error sending MIDI to virtual destinations."; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations."; X+ error( RtMidiError::WARNING, errorString_ ); X } X } X X // And send to an explicit destination port if we're connected. X if ( connected_ ) { X- result = MIDISend( data->port, data->destinationId, pktlist ); X+ result = MIDISend( data->port, data->destinationId, packetList ); X if ( result != noErr ) { X- errorString_ = "RtMidiOut::sendMessage: error sending MIDI message to port."; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiOutCore::sendMessage: error sending MIDI message to port."; X+ error( RtMidiError::WARNING, errorString_ ); X } X } X } X@@ -789,8 +1503,7 @@ void RtMidiOut :: sendMessage( std::vector X #include X X@@ -808,32 +1525,38 @@ void RtMidiOut :: sendMessage( std::vector (ptr); X+ MidiInApi::RtMidiInData *data = static_cast (ptr); X AlsaMidiData *apiData = static_cast (data->apiData); X X long nBytes; X- unsigned long long time, lastTime; X+ double time; X bool continueSysex = false; X- RtMidiIn::MidiMessage message; X+ bool doDecode = false; X+ MidiInApi::MidiMessage message; X+ int poll_fd_count; X+ struct pollfd *poll_fds; X X snd_seq_event_t *ev; X int result; X@@ -841,63 +1564,93 @@ extern "C" void *alsaMidiHandler( void *ptr ) X result = snd_midi_event_new( 0, &apiData->coder ); X if ( result < 0 ) { X data->doInput = false; X- std::cerr << "\nRtMidiIn::alsaMidiHandler: error initializing MIDI event parser!\n\n"; X+ std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing MIDI event parser!\n\n"; X return 0; X } X unsigned char *buffer = (unsigned char *) malloc( apiData->bufferSize ); X if ( buffer == NULL ) { X data->doInput = false; X- std::cerr << "\nRtMidiIn::alsaMidiHandler: error initializing buffer memory!\n\n"; X+ snd_midi_event_free( apiData->coder ); X+ apiData->coder = 0; X+ std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing buffer memory!\n\n"; X return 0; X } X snd_midi_event_init( apiData->coder ); X snd_midi_event_no_status( apiData->coder, 1 ); // suppress running status messages X X+ poll_fd_count = snd_seq_poll_descriptors_count( apiData->seq, POLLIN ) + 1; X+ poll_fds = (struct pollfd*)alloca( poll_fd_count * sizeof( struct pollfd )); X+ snd_seq_poll_descriptors( apiData->seq, poll_fds + 1, poll_fd_count - 1, POLLIN ); X+ poll_fds[0].fd = apiData->trigger_fds[0]; X+ poll_fds[0].events = POLLIN; X+ X while ( data->doInput ) { X X if ( snd_seq_event_input_pending( apiData->seq, 1 ) == 0 ) { X- // No data pending ... sleep a bit. X- usleep( 1000 ); X+ // No data pending X+ if ( poll( poll_fds, poll_fd_count, -1) >= 0 ) { X+ if ( poll_fds[0].revents & POLLIN ) { X+ bool dummy; X+ int res = read( poll_fds[0].fd, &dummy, sizeof(dummy) ); X+ (void) res; X+ } X+ } X continue; X } X X // If here, there should be data. X result = snd_seq_event_input( apiData->seq, &ev ); X if ( result == -ENOSPC ) { X- std::cerr << "\nRtMidiIn::alsaMidiHandler: MIDI input buffer overrun!\n\n"; X+ std::cerr << "\nMidiInAlsa::alsaMidiHandler: MIDI input buffer overrun!\n\n"; X continue; X } X else if ( result <= 0 ) { X- std::cerr << "RtMidiIn::alsaMidiHandler: unknown MIDI input error!\n"; X+ std::cerr << "\nMidiInAlsa::alsaMidiHandler: unknown MIDI input error!\n"; X+ perror("System reports"); X continue; X } X X // This is a bit weird, but we now have to decode an ALSA MIDI X // event (back) into MIDI bytes. We'll ignore non-MIDI types. X- message.bytes.clear(); X+ if ( !continueSysex ) message.bytes.clear(); X+ X+ doDecode = false; X switch ( ev->type ) { X X- case SND_SEQ_EVENT_PORT_SUBSCRIBED: X+ case SND_SEQ_EVENT_PORT_SUBSCRIBED: X #if defined(__RTMIDI_DEBUG__) X- std::cout << "RtMidiIn::alsaMidiHandler: port connection made!\n"; X+ std::cout << "MidiInAlsa::alsaMidiHandler: port connection made!\n"; X #endif X break; X X- case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: X- std::cerr << "RtMidiIn::alsaMidiHandler: port connection has closed!\n"; X- data->doInput = false; X+ case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: X+#if defined(__RTMIDI_DEBUG__) X+ std::cerr << "MidiInAlsa::alsaMidiHandler: port connection has closed!\n"; X+ std::cout << "sender = " << (int) ev->data.connect.sender.client << ":" X+ << (int) ev->data.connect.sender.port X+ << ", dest = " << (int) ev->data.connect.dest.client << ":" X+ << (int) ev->data.connect.dest.port X+ << std::endl; X+#endif X break; X X case SND_SEQ_EVENT_QFRAME: // MIDI time code X- if ( data->ignoreFlags & 0x02 ) break; X+ if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true; X+ break; X X- case SND_SEQ_EVENT_TICK: // MIDI timing tick X- if ( data->ignoreFlags & 0x02 ) break; X+ case SND_SEQ_EVENT_TICK: // 0xF9 ... MIDI timing tick X+ if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true; X+ break; X X+ case SND_SEQ_EVENT_CLOCK: // 0xF8 ... MIDI timing (clock) tick X+ if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true; X+ break; X+ X case SND_SEQ_EVENT_SENSING: // Active sensing X- if ( data->ignoreFlags & 0x04 ) break; X+ if ( !( data->ignoreFlags & 0x04 ) ) doDecode = true; X+ break; X X- case SND_SEQ_EVENT_SYSEX: X+ case SND_SEQ_EVENT_SYSEX: X if ( (data->ignoreFlags & 0x01) ) break; X if ( ev->data.ext.len > apiData->bufferSize ) { X apiData->bufferSize = ev->data.ext.len; X@@ -905,58 +1658,85 @@ extern "C" void *alsaMidiHandler( void *ptr ) X buffer = (unsigned char *) malloc( apiData->bufferSize ); X if ( buffer == NULL ) { X data->doInput = false; X- std::cerr << "\nRtMidiIn::alsaMidiHandler: error resizing buffer memory!\n\n"; X+ std::cerr << "\nMidiInAlsa::alsaMidiHandler: error resizing buffer memory!\n\n"; X break; X } X } X+ doDecode = true; X+ break; X X default: X+ doDecode = true; X+ } X+ X+ if ( doDecode ) { X+ X nBytes = snd_midi_event_decode( apiData->coder, buffer, apiData->bufferSize, ev ); X- if ( nBytes <= 0 ) { X-#if defined(__RTMIDI_DEBUG__) X- std::cerr << "\nRtMidiIn::alsaMidiHandler: event parsing error or not a MIDI event!\n\n"; X-#endif X- break; X- } X+ if ( nBytes > 0 ) { X+ // The ALSA sequencer has a maximum buffer size for MIDI sysex X+ // events of 256 bytes. If a device sends sysex messages larger X+ // than this, they are segmented into 256 byte chunks. So, X+ // we'll watch for this and concatenate sysex chunks into a X+ // single sysex message if necessary. X+ if ( !continueSysex ) X+ message.bytes.assign( buffer, &buffer[nBytes] ); X+ else X+ message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] ); X X- // The ALSA sequencer has a maximum buffer size for MIDI sysex X- // events of 256 bytes. If a device sends sysex messages larger X- // than this, they are segmented into 256 byte chunks. So, X- // we'll watch for this and concatenate sysex chunks into a X- // single sysex message if necessary. X- if ( !continueSysex ) X- message.bytes.assign( buffer, &buffer[nBytes] ); X- else X- message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] ); X+ continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) ); X+ if ( !continueSysex ) { X X- if ( ev->type == SND_SEQ_EVENT_SYSEX && message.bytes.back() == 0xF7 ) X- continueSysex = false; X- else { X- continueSysex = true; X- break; X- } X+ // Calculate the time stamp: X+ message.timeStamp = 0.0; X X- // Calculate the time stamp: X- message.timeStamp = 0.0; X+ // Method 1: Use the system time. X+ //(void)gettimeofday(&tv, (struct timezone *)NULL); X+ //time = (tv.tv_sec * 1000000) + tv.tv_usec; X X- // Method 1: Use the system time. X- //(void)gettimeofday(&tv, (struct timezone *)NULL); X- //time = (tv.tv_sec * 1000000) + tv.tv_usec; X+ // Method 2: Use the ALSA sequencer event time data. X+ // (thanks to Pedro Lopez-Cabanillas!). X X- // Method 2: Use the ALSA sequencer event time data. X- // (thanks to Pedro Lopez-Cabanillas!). X- time = ( ev->time.time.tv_sec * 1000000 ) + ( ev->time.time.tv_nsec/1000 ); X- lastTime = time; X- time -= apiData->lastTime; X- apiData->lastTime = lastTime; X- if ( data->firstMessage == true ) X- data->firstMessage = false; X- else X- message.timeStamp = time * 0.000001; X+ // Using method from: X+ // https://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html X+ X+ // Perform the carry for the later subtraction by updating y. X+ // Temp var y is timespec because computation requires signed types, X+ // while snd_seq_real_time_t has unsigned types. X+ snd_seq_real_time_t &x( ev->time.time ); X+ struct timespec y; X+ y.tv_nsec = apiData->lastTime.tv_nsec; X+ y.tv_sec = apiData->lastTime.tv_sec; X+ if ( x.tv_nsec < y.tv_nsec ) { X+ int nsec = (y.tv_nsec - (int)x.tv_nsec) / 1000000000 + 1; X+ y.tv_nsec -= 1000000000 * nsec; X+ y.tv_sec += nsec; X+ } X+ if ( x.tv_nsec - y.tv_nsec > 1000000000 ) { X+ int nsec = ((int)x.tv_nsec - y.tv_nsec) / 1000000000; X+ y.tv_nsec += 1000000000 * nsec; X+ y.tv_sec -= nsec; X+ } X+ X+ // Compute the time difference. X+ time = (int)x.tv_sec - y.tv_sec + ((int)x.tv_nsec - y.tv_nsec)*1e-9; X+ X+ apiData->lastTime = ev->time.time; X+ X+ if ( data->firstMessage == true ) X+ data->firstMessage = false; X+ else X+ message.timeStamp = time; X+ } X+ else { X+#if defined(__RTMIDI_DEBUG__) X+ std::cerr << "\nMidiInAlsa::alsaMidiHandler: event parsing error or not a MIDI event!\n\n"; X+#endif X+ } X+ } X } X X- snd_seq_free_event(ev); X- if ( message.bytes.size() == 0 ) continue; X+ snd_seq_free_event( ev ); X+ if ( message.bytes.size() == 0 || continueSysex ) continue; X X if ( data->usingCallback ) { X RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; X@@ -964,106 +1744,201 @@ extern "C" void *alsaMidiHandler( void *ptr ) X } X else { X // As long as we haven't reached our queue size limit, push the message. X- if ( data->queueLimit > data->queue.size() ) X- data->queue.push( message ); X- else X- std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; X+ if ( !data->queue.push( message ) ) X+ std::cerr << "\nMidiInAlsa: message queue limit reached!!\n\n"; X } X } X X if ( buffer ) free( buffer ); X snd_midi_event_free( apiData->coder ); X apiData->coder = 0; X+ apiData->thread = apiData->dummy_thread_id; X return 0; X } X X-void RtMidiIn :: initialize( void ) X+MidiInAlsa :: MidiInAlsa( const std::string &clientName, unsigned int queueSizeLimit ) X+ : MidiInApi( queueSizeLimit ) X { X+ MidiInAlsa::initialize( clientName ); X+} X+ X+MidiInAlsa :: ~MidiInAlsa() X+{ X+ // Close a connection if it exists. X+ MidiInAlsa::closePort(); X+ X+ // Shutdown the input thread. X+ AlsaMidiData *data = static_cast (apiData_); X+ if ( inputData_.doInput ) { X+ inputData_.doInput = false; X+ int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof( inputData_.doInput ) ); X+ (void) res; X+ if ( !pthread_equal(data->thread, data->dummy_thread_id) ) X+ pthread_join( data->thread, NULL ); X+ } X+ X+ // Cleanup. X+ close ( data->trigger_fds[0] ); X+ close ( data->trigger_fds[1] ); X+ if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); X+#ifndef AVOID_TIMESTAMPING X+ snd_seq_free_queue( data->seq, data->queue_id ); X+#endif X+ snd_seq_close( data->seq ); X+ delete data; X+} X+ X+void MidiInAlsa :: initialize( const std::string& clientName ) X+{ X // Set up the ALSA sequencer client. X- snd_seq_t *seq; X- int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK); X+ snd_seq_t *seq; X+ int result = snd_seq_open( &seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK ); X if ( result < 0 ) { X- errorString_ = "RtMidiIn::initialize: error creating ALSA sequencer input client object."; X- error( RtMidiError::DRIVER_ERROR ); X- } X+ errorString_ = "MidiInAlsa::initialize: error creating ALSA sequencer client object."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X X // Set client name. X- snd_seq_set_client_name(seq, "RtMidi Input Client"); X+ snd_seq_set_client_name( seq, clientName.c_str() ); X X // Save our api-specific connection information. X AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; X data->seq = seq; X+ data->portNum = -1; X data->vport = -1; X+ data->subscription = 0; X+ data->dummy_thread_id = pthread_self(); X+ data->thread = data->dummy_thread_id; X+ data->trigger_fds[0] = -1; X+ data->trigger_fds[1] = -1; X apiData_ = (void *) data; X inputData_.apiData = (void *) data; X X+ if ( pipe(data->trigger_fds) == -1 ) { X+ errorString_ = "MidiInAlsa::initialize: error creating pipe objects."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X+ X // Create the input queue X- data->queue_id = snd_seq_alloc_named_queue(seq, "RtMidi Queue"); X+#ifndef AVOID_TIMESTAMPING X+ data->queue_id = snd_seq_alloc_named_queue( seq, "RtMidi Queue" ); X // Set arbitrary tempo (mm=100) and resolution (240) X snd_seq_queue_tempo_t *qtempo; X- snd_seq_queue_tempo_alloca(&qtempo); X- snd_seq_queue_tempo_set_tempo(qtempo, 600000); X- snd_seq_queue_tempo_set_ppq(qtempo, 240); X- snd_seq_set_queue_tempo(data->seq, data->queue_id, qtempo); X- snd_seq_drain_output(data->seq); X+ snd_seq_queue_tempo_alloca( &qtempo ); X+ snd_seq_queue_tempo_set_tempo( qtempo, 600000 ); X+ snd_seq_queue_tempo_set_ppq( qtempo, 240 ); X+ snd_seq_set_queue_tempo( data->seq, data->queue_id, qtempo ); X+ snd_seq_drain_output( data->seq ); X+#endif X } X X // This function is used to count or get the pinfo structure for a given port number. X unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int type, int portNumber ) X { X- snd_seq_client_info_t *cinfo; X+ snd_seq_client_info_t *cinfo; X int client; X int count = 0; X- snd_seq_client_info_alloca( &cinfo ); X+ snd_seq_client_info_alloca( &cinfo ); X X- snd_seq_client_info_set_client( cinfo, -1 ); X- while ( snd_seq_query_next_client( seq, cinfo ) >= 0 ) { X+ snd_seq_client_info_set_client( cinfo, -1 ); X+ while ( snd_seq_query_next_client( seq, cinfo ) >= 0 ) { X client = snd_seq_client_info_get_client( cinfo ); X if ( client == 0 ) continue; X- // Reset query info X- snd_seq_port_info_set_client( pinfo, client ); X- snd_seq_port_info_set_port( pinfo, -1 ); X- while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) { X- if ( !PORT_TYPE( pinfo, type ) ) continue; X+ // Reset query info X+ snd_seq_port_info_set_client( pinfo, client ); X+ snd_seq_port_info_set_port( pinfo, -1 ); X+ while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) { X+ unsigned int atyp = snd_seq_port_info_get_type( pinfo ); X+ if ( ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) && X+ ( ( atyp & SND_SEQ_PORT_TYPE_SYNTH ) == 0 ) && X+ ( ( atyp & SND_SEQ_PORT_TYPE_APPLICATION ) == 0 ) ) continue; X+ X+ unsigned int caps = snd_seq_port_info_get_capability( pinfo ); X+ if ( ( caps & type ) != type ) continue; X if ( count == portNumber ) return 1; X- count++; X- } X+ ++count; X } X+ } X X // If a negative portNumber was used, return the port count. X if ( portNumber < 0 ) return count; X return 0; X } X X-void RtMidiIn :: openPort( unsigned int portNumber ) X+unsigned int MidiInAlsa :: getPortCount() X { X+ snd_seq_port_info_t *pinfo; X+ snd_seq_port_info_alloca( &pinfo ); X+ X+ AlsaMidiData *data = static_cast (apiData_); X+ return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 ); X+} X+ X+std::string MidiInAlsa :: getPortName( unsigned int portNumber ) X+{ X+ snd_seq_client_info_t *cinfo; X+ snd_seq_port_info_t *pinfo; X+ snd_seq_client_info_alloca( &cinfo ); X+ snd_seq_port_info_alloca( &pinfo ); X+ X+ std::string stringName; X+ AlsaMidiData *data = static_cast (apiData_); X+ if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) { X+ int cnum = snd_seq_port_info_get_client( pinfo ); X+ snd_seq_get_any_client_info( data->seq, cnum, cinfo ); X+ std::ostringstream os; X+ os << snd_seq_client_info_get_name( cinfo ); X+ os << ":"; X+ os << snd_seq_port_info_get_name( pinfo ); X+ os << " "; // These lines added to make sure devices are listed X+ os << snd_seq_port_info_get_client( pinfo ); // with full portnames added to ensure individual device names X+ os << ":"; X+ os << snd_seq_port_info_get_port( pinfo ); X+ stringName = os.str(); X+ return stringName; X+ } X+ X+ // If we get here, we didn't find a match. X+ errorString_ = "MidiInAlsa::getPortName: error looking for port name!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ return stringName; X+} X+ X+void MidiInAlsa :: openPort( unsigned int portNumber, const std::string &portName ) X+{ X if ( connected_ ) { X- errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiInAlsa::openPort: a valid connection already exists!"; X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X unsigned int nSrc = this->getPortCount(); X- if (nSrc < 1) { X- errorString_ = "RtMidiIn::openPort: no MIDI input sources found!"; X- error( RtMidiError::NO_DEVICES_FOUND ); X+ if ( nSrc < 1 ) { X+ errorString_ = "MidiInAlsa::openPort: no MIDI input sources found!"; X+ error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); X+ return; X } X X- snd_seq_port_info_t *pinfo; X- snd_seq_port_info_alloca( &pinfo ); X- std::ostringstream ost; X+ snd_seq_port_info_t *src_pinfo; X+ snd_seq_port_info_alloca( &src_pinfo ); X AlsaMidiData *data = static_cast (apiData_); X- if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) { X- ost << "RtMidiIn::openPort: 'portNumber' argument (" << portNumber << ") is invalid."; X+ if ( portInfo( data->seq, src_pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) { X+ std::ostringstream ost; X+ ost << "MidiInAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; X errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X+ error( RtMidiError::INVALID_PARAMETER, errorString_ ); X+ return; X } X X- X snd_seq_addr_t sender, receiver; X- sender.client = snd_seq_port_info_get_client( pinfo ); X- sender.port = snd_seq_port_info_get_port( pinfo ); X+ sender.client = snd_seq_port_info_get_client( src_pinfo ); X+ sender.port = snd_seq_port_info_get_port( src_pinfo ); X receiver.client = snd_seq_client_id( data->seq ); X+ X+ snd_seq_port_info_t *pinfo; X+ snd_seq_port_info_alloca( &pinfo ); X if ( data->vport < 0 ) { X snd_seq_port_info_set_client( pinfo, 0 ); X snd_seq_port_info_set_port( pinfo, 0 ); X@@ -1074,253 +1949,306 @@ void RtMidiIn :: openPort( unsigned int portNumber ) X SND_SEQ_PORT_TYPE_MIDI_GENERIC | X SND_SEQ_PORT_TYPE_APPLICATION ); X snd_seq_port_info_set_midi_channels(pinfo, 16); X- snd_seq_port_info_set_timestamping(pinfo, 1); X- snd_seq_port_info_set_timestamp_real(pinfo, 1); X- snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id); X- snd_seq_port_info_set_name(pinfo, "RtMidi Input"); X- data->vport = snd_seq_create_port(data->seq, pinfo); X- X+#ifndef AVOID_TIMESTAMPING X+ snd_seq_port_info_set_timestamping( pinfo, 1 ); X+ snd_seq_port_info_set_timestamp_real( pinfo, 1 ); X+ snd_seq_port_info_set_timestamp_queue( pinfo, data->queue_id ); X+#endif X+ snd_seq_port_info_set_name( pinfo, portName.c_str() ); X+ data->vport = snd_seq_create_port( data->seq, pinfo ); X+ X if ( data->vport < 0 ) { X- errorString_ = "RtMidiIn::openPort: ALSA error creating input port."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiInAlsa::openPort: ALSA error creating input port."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X+ data->vport = snd_seq_port_info_get_port( pinfo ); X } X X receiver.port = data->vport; X X- // Make subscription X- snd_seq_port_subscribe_malloc( &data->subscription ); X- snd_seq_port_subscribe_set_sender(data->subscription, &sender); X- snd_seq_port_subscribe_set_dest(data->subscription, &receiver); X- if ( snd_seq_subscribe_port(data->seq, data->subscription) ) { X- errorString_ = "RtMidiIn::openPort: ALSA error making port connection."; X- error( RtMidiError::DRIVER_ERROR ); X+ if ( !data->subscription ) { X+ // Make subscription X+ if ( snd_seq_port_subscribe_malloc( &data->subscription ) < 0 ) { X+ errorString_ = "MidiInAlsa::openPort: ALSA error allocation port subscription."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X+ snd_seq_port_subscribe_set_sender( data->subscription, &sender ); X+ snd_seq_port_subscribe_set_dest( data->subscription, &receiver ); X+ if ( snd_seq_subscribe_port( data->seq, data->subscription ) ) { X+ snd_seq_port_subscribe_free( data->subscription ); X+ data->subscription = 0; X+ errorString_ = "MidiInAlsa::openPort: ALSA error making port connection."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X } X X if ( inputData_.doInput == false ) { X // Start the input queue X+#ifndef AVOID_TIMESTAMPING X snd_seq_start_queue( data->seq, data->queue_id, NULL ); X snd_seq_drain_output( data->seq ); X+#endif X // Start our MIDI input thread. X pthread_attr_t attr; X- pthread_attr_init(&attr); X- // chuck X- //pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); X- //pthread_attr_setschedpolicy(&attr, SCHED_OTHER); X+ pthread_attr_init( &attr ); X+ pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); X+ pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); X X inputData_.doInput = true; X- int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_); X- pthread_attr_destroy(&attr); X- if (err) { X+ int err = pthread_create( &data->thread, &attr, alsaMidiHandler, &inputData_ ); X+ pthread_attr_destroy( &attr ); X+ if ( err ) { X snd_seq_unsubscribe_port( data->seq, data->subscription ); X snd_seq_port_subscribe_free( data->subscription ); X+ data->subscription = 0; X inputData_.doInput = false; X- errorString_ = "RtMidiIn::openPort: error starting MIDI input thread!"; X- error( RtMidiError::THREAD_ERROR ); X+ errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!"; X+ error( RtMidiError::THREAD_ERROR, errorString_ ); X+ return; X } X } X X connected_ = true; X } X X-void RtMidiIn :: openVirtualPort() X+void MidiInAlsa :: openVirtualPort( const std::string &portName ) X { X AlsaMidiData *data = static_cast (apiData_); X if ( data->vport < 0 ) { X snd_seq_port_info_t *pinfo; X snd_seq_port_info_alloca( &pinfo ); X snd_seq_port_info_set_capability( pinfo, X- SND_SEQ_PORT_CAP_WRITE | X- SND_SEQ_PORT_CAP_SUBS_WRITE ); X+ SND_SEQ_PORT_CAP_WRITE | X+ SND_SEQ_PORT_CAP_SUBS_WRITE ); X snd_seq_port_info_set_type( pinfo, X- SND_SEQ_PORT_TYPE_MIDI_GENERIC | X- SND_SEQ_PORT_TYPE_APPLICATION ); X- snd_seq_port_info_set_midi_channels(pinfo, 16); X- snd_seq_port_info_set_timestamping(pinfo, 1); X- snd_seq_port_info_set_timestamp_real(pinfo, 1); X- snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id); X- snd_seq_port_info_set_name(pinfo, "RtMidi Input"); X- data->vport = snd_seq_create_port(data->seq, pinfo); X+ SND_SEQ_PORT_TYPE_MIDI_GENERIC | X+ SND_SEQ_PORT_TYPE_APPLICATION ); X+ snd_seq_port_info_set_midi_channels( pinfo, 16 ); X+#ifndef AVOID_TIMESTAMPING X+ snd_seq_port_info_set_timestamping( pinfo, 1 ); X+ snd_seq_port_info_set_timestamp_real( pinfo, 1 ); X+ snd_seq_port_info_set_timestamp_queue( pinfo, data->queue_id ); X+#endif X+ snd_seq_port_info_set_name( pinfo, portName.c_str() ); X+ data->vport = snd_seq_create_port( data->seq, pinfo ); X X if ( data->vport < 0 ) { X- errorString_ = "RtMidiIn::openVirtualPort: ALSA error creating virtual port."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiInAlsa::openVirtualPort: ALSA error creating virtual port."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X+ data->vport = snd_seq_port_info_get_port( pinfo ); X } X X if ( inputData_.doInput == false ) { X+ // Wait for old thread to stop, if still running X+ if ( !pthread_equal( data->thread, data->dummy_thread_id ) ) X+ pthread_join( data->thread, NULL ); X+ X // Start the input queue X+#ifndef AVOID_TIMESTAMPING X snd_seq_start_queue( data->seq, data->queue_id, NULL ); X snd_seq_drain_output( data->seq ); X+#endif X // Start our MIDI input thread. X pthread_attr_t attr; X- pthread_attr_init(&attr); X- // chuck X- //pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); X- //pthread_attr_setschedpolicy(&attr, SCHED_OTHER); X+ pthread_attr_init( &attr ); X+ pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); X+ pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); X X inputData_.doInput = true; X- int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_); X- pthread_attr_destroy(&attr); X- if (err) { X- snd_seq_unsubscribe_port( data->seq, data->subscription ); X- snd_seq_port_subscribe_free( data->subscription ); X+ int err = pthread_create( &data->thread, &attr, alsaMidiHandler, &inputData_ ); X+ pthread_attr_destroy( &attr ); X+ if ( err ) { X+ if ( data->subscription ) { X+ snd_seq_unsubscribe_port( data->seq, data->subscription ); X+ snd_seq_port_subscribe_free( data->subscription ); X+ data->subscription = 0; X+ } X inputData_.doInput = false; X- errorString_ = "RtMidiIn::openPort: error starting MIDI input thread!"; X- error( RtMidiError::THREAD_ERROR ); X+ errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!"; X+ error( RtMidiError::THREAD_ERROR, errorString_ ); X+ return; X } X } X } X X-void RtMidiIn :: closePort( void ) X+void MidiInAlsa :: closePort( void ) X { X+ AlsaMidiData *data = static_cast (apiData_); X+ X if ( connected_ ) { X- AlsaMidiData *data = static_cast (apiData_); X- snd_seq_unsubscribe_port( data->seq, data->subscription ); X- snd_seq_port_subscribe_free( data->subscription ); X+ if ( data->subscription ) { X+ snd_seq_unsubscribe_port( data->seq, data->subscription ); X+ snd_seq_port_subscribe_free( data->subscription ); X+ data->subscription = 0; X+ } X // Stop the input queue X+#ifndef AVOID_TIMESTAMPING X snd_seq_stop_queue( data->seq, data->queue_id, NULL ); X snd_seq_drain_output( data->seq ); X+#endif X connected_ = false; X } X-} X X-RtMidiIn :: ~RtMidiIn() X-{ X- // Close a connection if it exists. X- closePort(); X- X- // Shutdown the input thread. X- AlsaMidiData *data = static_cast (apiData_); X+ // Stop thread to avoid triggering the callback, while the port is intended to be closed X if ( inputData_.doInput ) { X inputData_.doInput = false; X- pthread_join( data->thread, NULL ); X+ int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof( inputData_.doInput ) ); X+ (void) res; X+ if ( !pthread_equal( data->thread, data->dummy_thread_id ) ) X+ pthread_join( data->thread, NULL ); X } X- X- // Cleanup. X- if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); X- snd_seq_free_queue( data->seq, data->queue_id ); X- snd_seq_close( data->seq ); X- delete data; X } X X-unsigned int RtMidiIn :: getPortCount() X+void MidiInAlsa :: setClientName( const std::string &clientName ) X { X- snd_seq_port_info_t *pinfo; X- snd_seq_port_info_alloca( &pinfo ); X X- AlsaMidiData *data = static_cast (apiData_); X- return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 ); X+ AlsaMidiData *data = static_cast ( apiData_ ); X+ snd_seq_set_client_name( data->seq, clientName.c_str() ); X+ X } X X-std::string RtMidiIn :: getPortName( unsigned int portNumber ) X+void MidiInAlsa :: setPortName( const std::string &portName ) X { X- snd_seq_port_info_t *pinfo; X- snd_seq_port_info_alloca( &pinfo ); X- X AlsaMidiData *data = static_cast (apiData_); X- if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) { X- std::string stringName = std::string( snd_seq_port_info_get_name( pinfo ) ); X- return stringName; X- } X- X- // If we get here, we didn't find a match. X- errorString_ = "RtMidiIn::getPortName: error looking for port name!"; X- error( RtMidiError::INVALID_PARAMETER ); X- return 0; X+ snd_seq_port_info_t *pinfo; X+ snd_seq_port_info_alloca( &pinfo ); X+ snd_seq_get_port_info( data->seq, data->vport, pinfo ); X+ snd_seq_port_info_set_name( pinfo, portName.c_str() ); X+ snd_seq_set_port_info( data->seq, data->vport, pinfo ); X } X X //*********************************************************************// X // API: LINUX ALSA X-// Class Definitions: RtMidiOut X+// Class Definitions: MidiOutAlsa X //*********************************************************************// X X-unsigned int RtMidiOut :: getPortCount() X+MidiOutAlsa :: MidiOutAlsa( const std::string &clientName ) : MidiOutApi() X { X- snd_seq_port_info_t *pinfo; X- snd_seq_port_info_alloca( &pinfo ); X- X- AlsaMidiData *data = static_cast (apiData_); X- return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, -1 ); X+ MidiOutAlsa::initialize( clientName ); X } X X-std::string RtMidiOut :: getPortName( unsigned int portNumber ) X+MidiOutAlsa :: ~MidiOutAlsa() X { X- snd_seq_port_info_t *pinfo; X- snd_seq_port_info_alloca( &pinfo ); X+ // Close a connection if it exists. X+ MidiOutAlsa::closePort(); X X+ // Cleanup. X AlsaMidiData *data = static_cast (apiData_); X- if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) ) { X- std::string stringName = std::string( snd_seq_port_info_get_name( pinfo ) ); X- return stringName; X- } X- X- // If we get here, we didn't find a match. X- errorString_ = "RtMidiOut::getPortName: error looking for port name!"; X- error( RtMidiError::INVALID_PARAMETER ); X- return 0; X+ if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); X+ if ( data->coder ) snd_midi_event_free( data->coder ); X+ if ( data->buffer ) free( data->buffer ); X+ snd_seq_close( data->seq ); X+ delete data; X } X X-void RtMidiOut :: initialize( void ) X+void MidiOutAlsa :: initialize( const std::string& clientName ) X { X // Set up the ALSA sequencer client. X- snd_seq_t *seq; X- int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_OUTPUT, 0); X- if ( result < 0 ) { X- errorString_ = "RtMidiOut::initialize: error creating ALSA sequencer client object."; X- error( RtMidiError::DRIVER_ERROR ); X- } X+ snd_seq_t *seq; X+ int result1 = snd_seq_open( &seq, "default", SND_SEQ_OPEN_OUTPUT, SND_SEQ_NONBLOCK ); X+ if ( result1 < 0 ) { X+ errorString_ = "MidiOutAlsa::initialize: error creating ALSA sequencer client object."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X X // Set client name. X- snd_seq_set_client_name(seq, "RtMidi Output Client"); X+ snd_seq_set_client_name( seq, clientName.c_str() ); X X // Save our api-specific connection information. X AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; X data->seq = seq; X+ data->portNum = -1; X data->vport = -1; X data->bufferSize = 32; X data->coder = 0; X data->buffer = 0; X- result = snd_midi_event_new( data->bufferSize, &data->coder ); X+ int result = snd_midi_event_new( data->bufferSize, &data->coder ); X if ( result < 0 ) { X delete data; X- data = NULL; // 1.4.1.0 (ge) X- errorString_ = "RtMidiOut::initialize: error initializing MIDI event parser!\n\n"; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiOutAlsa::initialize: error initializing MIDI event parser!\n\n"; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X data->buffer = (unsigned char *) malloc( data->bufferSize ); X if ( data->buffer == NULL ) { X delete data; X- data = NULL; // 1.4.1.0 (ge) X- errorString_ = "RtMidiOut::initialize: error allocating buffer memory!\n\n"; X- error( RtMidiError::MEMORY_ERROR ); X+ errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n"; X+ error( RtMidiError::MEMORY_ERROR, errorString_ ); X+ return; X } X snd_midi_event_init( data->coder ); X apiData_ = (void *) data; X } X X-void RtMidiOut :: openPort( unsigned int portNumber ) X+unsigned int MidiOutAlsa :: getPortCount() X { X+ snd_seq_port_info_t *pinfo; X+ snd_seq_port_info_alloca( &pinfo ); X+ X+ AlsaMidiData *data = static_cast (apiData_); X+ return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, -1 ); X+} X+ X+std::string MidiOutAlsa :: getPortName( unsigned int portNumber ) X+{ X+ snd_seq_client_info_t *cinfo; X+ snd_seq_port_info_t *pinfo; X+ snd_seq_client_info_alloca( &cinfo ); X+ snd_seq_port_info_alloca( &pinfo ); X+ X+ std::string stringName; X+ AlsaMidiData *data = static_cast (apiData_); X+ if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) ) { X+ int cnum = snd_seq_port_info_get_client( pinfo ); X+ snd_seq_get_any_client_info( data->seq, cnum, cinfo ); X+ std::ostringstream os; X+ os << snd_seq_client_info_get_name( cinfo ); X+ os << ":"; X+ os << snd_seq_port_info_get_name( pinfo ); X+ os << " "; // These lines added to make sure devices are listed X+ os << snd_seq_port_info_get_client( pinfo ); // with full portnames added to ensure individual device names X+ os << ":"; X+ os << snd_seq_port_info_get_port( pinfo ); X+ stringName = os.str(); X+ return stringName; X+ } X+ X+ // If we get here, we didn't find a match. X+ errorString_ = "MidiOutAlsa::getPortName: error looking for port name!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ return stringName; X+} X+ X+void MidiOutAlsa :: openPort( unsigned int portNumber, const std::string &portName ) X+{ X if ( connected_ ) { X- errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiOutAlsa::openPort: a valid connection already exists!"; X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X unsigned int nSrc = this->getPortCount(); X- if (nSrc < 1) { X- errorString_ = "RtMidiOut::openPort: no MIDI output sources found!"; X- error( RtMidiError::NO_DEVICES_FOUND ); X+ if ( nSrc < 1 ) { X+ errorString_ = "MidiOutAlsa::openPort: no MIDI output sources found!"; X+ error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); X+ return; X } X X- snd_seq_port_info_t *pinfo; X- snd_seq_port_info_alloca( &pinfo ); X- std::ostringstream ost; X+ snd_seq_port_info_t *pinfo; X+ snd_seq_port_info_alloca( &pinfo ); X AlsaMidiData *data = static_cast (apiData_); X if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) == 0 ) { X- ost << "RtMidiOut::openPort: 'portNumber' argument (" << portNumber << ") is invalid."; X+ std::ostringstream ost; X+ ost << "MidiOutAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; X errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X+ error( RtMidiError::INVALID_PARAMETER, errorString_ ); X+ return; X } X X snd_seq_addr_t sender, receiver; X@@ -1329,527 +2257,155 @@ void RtMidiOut :: openPort( unsigned int portNumber ) X sender.client = snd_seq_client_id( data->seq ); X X if ( data->vport < 0 ) { X- data->vport = snd_seq_create_simple_port( data->seq, "RtMidi Output", X+ data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(), X SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, X- SND_SEQ_PORT_TYPE_MIDI_GENERIC ); X+ SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION ); X if ( data->vport < 0 ) { X- errorString_ = "RtMidiOut::openPort: ALSA error creating output port."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiOutAlsa::openPort: ALSA error creating output port."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X } X X sender.port = data->vport; X X // Make subscription X- snd_seq_port_subscribe_malloc( &data->subscription ); X- snd_seq_port_subscribe_set_sender(data->subscription, &sender); X- snd_seq_port_subscribe_set_dest(data->subscription, &receiver); X- snd_seq_port_subscribe_set_time_update(data->subscription, 1); X- snd_seq_port_subscribe_set_time_real(data->subscription, 1); X- if ( snd_seq_subscribe_port(data->seq, data->subscription) ) { X- errorString_ = "RtMidiOut::openPort: ALSA error making port connection."; X- error( RtMidiError::DRIVER_ERROR ); X+ if ( snd_seq_port_subscribe_malloc( &data->subscription ) < 0 ) { X+ snd_seq_port_subscribe_free( data->subscription ); X+ errorString_ = "MidiOutAlsa::openPort: error allocating port subscription."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X+ snd_seq_port_subscribe_set_sender( data->subscription, &sender ); X+ snd_seq_port_subscribe_set_dest( data->subscription, &receiver ); X+ snd_seq_port_subscribe_set_time_update( data->subscription, 1 ); X+ snd_seq_port_subscribe_set_time_real( data->subscription, 1 ); X+ if ( snd_seq_subscribe_port( data->seq, data->subscription ) ) { X+ snd_seq_port_subscribe_free( data->subscription ); X+ errorString_ = "MidiOutAlsa::openPort: ALSA error making port connection."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X X connected_ = true; X } X X-void RtMidiOut :: closePort( void ) X+void MidiOutAlsa :: closePort( void ) X { X if ( connected_ ) { X AlsaMidiData *data = static_cast (apiData_); X snd_seq_unsubscribe_port( data->seq, data->subscription ); X snd_seq_port_subscribe_free( data->subscription ); X+ data->subscription = 0; X connected_ = false; X } X } X X-void RtMidiOut :: openVirtualPort() X+void MidiOutAlsa :: setClientName( const std::string &clientName ) X { X+ X+ AlsaMidiData *data = static_cast ( apiData_ ); X+ snd_seq_set_client_name( data->seq, clientName.c_str() ); X+ X+} X+ X+void MidiOutAlsa :: setPortName( const std::string &portName ) X+{ X AlsaMidiData *data = static_cast (apiData_); X+ snd_seq_port_info_t *pinfo; X+ snd_seq_port_info_alloca( &pinfo ); X+ snd_seq_get_port_info( data->seq, data->vport, pinfo ); X+ snd_seq_port_info_set_name( pinfo, portName.c_str() ); X+ snd_seq_set_port_info( data->seq, data->vport, pinfo ); X+} X+ X+void MidiOutAlsa :: openVirtualPort( const std::string &portName ) X+{ X+ AlsaMidiData *data = static_cast (apiData_); X if ( data->vport < 0 ) { X- data->vport = snd_seq_create_simple_port( data->seq, "RtMidi Output", X+ data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(), X SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, X- SND_SEQ_PORT_TYPE_MIDI_GENERIC ); X+ SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION ); X X if ( data->vport < 0 ) { X- errorString_ = "RtMidiOut::openVirtualPort: ALSA error creating virtual port."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiOutAlsa::openVirtualPort: ALSA error creating virtual port."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X } X } X } X X-RtMidiOut :: ~RtMidiOut() X+void MidiOutAlsa :: sendMessage( const unsigned char *message, size_t size ) X { X- // Close a connection if it exists. X- closePort(); X- X- // Cleanup. X+ long result; X AlsaMidiData *data = static_cast (apiData_); X- if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); X- if ( data->coder ) snd_midi_event_free( data->coder ); X- if ( data->buffer ) free( data->buffer ); X- snd_seq_close( data->seq ); X- delete data; X-} X- X-void RtMidiOut :: sendMessage( std::vector *message ) X-{ X- int result; X- AlsaMidiData *data = static_cast (apiData_); X- unsigned int nBytes = message->size(); X+ unsigned int nBytes = static_cast (size); X if ( nBytes > data->bufferSize ) { X data->bufferSize = nBytes; X- result = snd_midi_event_resize_buffer ( data->coder, nBytes); X+ result = snd_midi_event_resize_buffer( data->coder, nBytes ); X if ( result != 0 ) { X- errorString_ = "RtMidiOut::sendMessage: ALSA error resizing MIDI event buffer."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiOutAlsa::sendMessage: ALSA error resizing MIDI event buffer."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X free (data->buffer); X data->buffer = (unsigned char *) malloc( data->bufferSize ); X if ( data->buffer == NULL ) { X- errorString_ = "RtMidiOut::initialize: error allocating buffer memory!\n\n"; X- error( RtMidiError::MEMORY_ERROR ); X+ errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n"; X+ error( RtMidiError::MEMORY_ERROR, errorString_ ); X+ return; X } X } X X- snd_seq_event_t ev; X- snd_seq_ev_clear(&ev); X- snd_seq_ev_set_source(&ev, data->vport); X- snd_seq_ev_set_subs(&ev); X- snd_seq_ev_set_direct(&ev); X- for ( unsigned int i=0; ibuffer[i] = message->at(i); X- result = snd_midi_event_encode( data->coder, data->buffer, (long)nBytes, &ev ); X- // commenting this out allows sending MIDI messages < and > 3 | 1.4.1.0 (mariobuoninfante) X- // if( result < (int)nBytes ) { X- // errorString_ = "RtMidiOut::sendMessage: event parsing error!"; X- // error( RtMidiError::WARNING ); X- // return; X- // } X+ for ( unsigned int i=0; ibuffer[i] = message[i]; X X- // Send the event. X- result = snd_seq_event_output(data->seq, &ev); X- if ( result < 0 ) { X- errorString_ = "RtMidiOut::sendMessage: error sending MIDI message to port."; X- error( RtMidiError::WARNING ); X- } X- snd_seq_drain_output(data->seq); X-} X- X-#endif // __LINUX_ALSA__ X- X- X-//*********************************************************************// X-// API: IRIX MD X-//*********************************************************************// X- X-// API information gleamed from: X-// http://techpubs.sgi.com/library/tpl/cgi-bin/getdoc.cgi?cmd=getdoc&coll=0650&db=man&fname=3%20mdIntro X- X-// If the Makefile doesn't work, try the following: X-// CC -o midiinfo -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp midiinfo.cpp -lpthread -lmd X-// CC -o midiout -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp midiout.cpp -lpthread -lmd X-// CC -o qmidiin -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp qmidiin.cpp -lpthread -lmd X-// CC -o cmidiin -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp cmidiin.cpp -lpthread -lmd X- X-#if defined(__IRIX_MD__) X- X-#include X-#include X-#include X- X-// Irix MIDI header file. X-#include X- X-// A structure to hold variables related to the IRIX API X-// implementation. X-struct IrixMidiData { X- MDport port; X- pthread_t thread; X-}; X- X-//*********************************************************************// X-// API: IRIX X-// Class Definitions: RtMidiIn X-//*********************************************************************// X- X-extern "C" void *irixMidiHandler( void *ptr ) X-{ X- RtMidiIn::RtMidiInData *data = static_cast (ptr); X- IrixMidiData *apiData = static_cast (data->apiData); X- X- bool continueSysex = false; X- unsigned char status; X- unsigned short size; X- MDevent event; X- int fd = mdGetFd( apiData->port ); X- if ( fd < 0 ) { X- data->doInput = false; X- std::cerr << "\nRtMidiIn::irixMidiHandler: error getting port descriptor!\n\n"; X- return 0; X- } X- X- fd_set mask, rmask; X- FD_ZERO( &mask ); X- FD_SET( fd, &mask ); X- struct timeval timeout = {0, 0}; X- RtMidiIn::MidiMessage message; X- int result; X- X- while ( data->doInput ) { X- X- rmask = mask; X- timeout.tv_sec = 0; X- timeout.tv_usec = 0; X- if ( select( fd+1, &rmask, NULL, NULL, &timeout ) <= 0 ) { X- // No data pending ... sleep a bit. X- usleep( 1000 ); X- continue; X+ unsigned int offset = 0; X+ while (offset < nBytes) { X+ snd_seq_event_t ev; X+ snd_seq_ev_clear( &ev ); X+ snd_seq_ev_set_source( &ev, data->vport ); X+ snd_seq_ev_set_subs( &ev ); X+ snd_seq_ev_set_direct( &ev ); X+ result = snd_midi_event_encode( data->coder, data->buffer + offset, X+ (long)(nBytes - offset), &ev ); X+ if ( result < 0 ) { X+ errorString_ = "MidiOutAlsa::sendMessage: event parsing error!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ return; X } X X- // If here, there should be data. X- result = mdReceive( apiData->port, &event, 1); X- if ( result <= 0 ) { X- std::cerr << "\nRtMidiIn::irixMidiHandler: MIDI input read error!\n\n"; X- continue; X+ if ( ev.type == SND_SEQ_EVENT_NONE ) { X+ errorString_ = "MidiOutAlsa::sendMessage: incomplete message!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ return; X } X X- message.timeStamp = event.stamp * 0.000000001; X+ offset += result; X X- size = 0; X- status = event.msg[0]; X- if ( !(status & 0x80) ) continue; X- if ( status == 0xF0 ) { X- // Sysex message ... can be segmented across multiple messages. X- if ( !(data->ignoreFlags & 0x01) ) { X- if ( continueSysex ) { X- // We have a continuing, segmented sysex message. Append X- // the new bytes to our existing message. X- for ( int i=0; iusingCallback && message.bytes.size() > 0 ) { X- RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; X- callback( message.timeStamp, &message.bytes, data->userData ); X- } X- else { X- // As long as we haven't reached our queue size limit, push the message. X- if ( data->queueLimit > data->queue.size() ) X- data->queue.push( message ); X- else X- std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; X- } X- message.bytes.clear(); X- } X- } X- } X- mdFree( NULL ); X- continue; X+ // Send the event. X+ result = snd_seq_event_output( data->seq, &ev ); X+ if ( result < 0 ) { X+ errorString_ = "MidiOutAlsa::sendMessage: error sending MIDI message to port."; X+ error( RtMidiError::WARNING, errorString_ ); X+ return; X } X- else if ( status < 0xC0 ) size = 3; X- else if ( status < 0xE0 ) size = 2; X- else if ( status < 0xF0 ) size = 3; X- else if ( status < 0xF3 ) { X- if ( status == 0xF1 && !(data->ignoreFlags & 0x02) ) { X- // A MIDI time code message and we're not ignoring it. X- size = 3; X- } X- } X- else if ( status == 0xF3 ) size = 2; X- else if ( status == 0xF8 ) { X- if ( !(data->ignoreFlags & 0x02) ) { X- // A MIDI timing tick message and we're not ignoring it. X- size = 1; X- } X- } X- else if ( status == 0xFE ) { // MIDI active sensing X- if ( !(data->ignoreFlags & 0x04) ) X- size = 1; X- } X- else size = 1; X- X- // Copy the MIDI data to our vector. X- if ( size ) { X- message.bytes.assign( &event.msg[0], &event.msg[size] ); X- // Invoke the user callback function or queue the message. X- if ( data->usingCallback ) { X- RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; X- callback( message.timeStamp, &message.bytes, data->userData ); X- } X- else { X- // As long as we haven't reached our queue size limit, push the message. X- if ( data->queueLimit > data->queue.size() ) X- data->queue.push( message ); X- else X- std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; X- } X- message.bytes.clear(); X- } X } X- X- return 0; X+ snd_seq_drain_output( data->seq ); X } X X-void RtMidiIn :: initialize( void ) X-{ X- // Initialize the Irix MIDI system. At the moment, we will not X- // worry about a return value of zero (ports) because there is a X- // chance the user could plug something in after instantiation. X- int nPorts = mdInit(); X+#endif // __LINUX_ALSA__ || __FREEBSD_ALSA__ X X- // Create our api-specific connection information. X- IrixMidiData *data = (IrixMidiData *) new IrixMidiData; X- apiData_ = (void *) data; X- inputData_.apiData = (void *) data; X-} X X-void RtMidiIn :: openPort( unsigned int portNumber ) X-{ X- if ( connected_ ) { X- errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; X- error( RtMidiError::WARNING ); X- return; X- } X- X- int nPorts = mdInit(); X- if (nPorts < 1) { X- errorString_ = "RtMidiIn::openPort: no Irix MIDI input sources found!"; X- error( RtMidiError::NO_DEVICES_FOUND ); X- } X- X- std::ostringstream ost; X- if ( portNumber >= nPorts ) { X- ost << "RtMidiIn::openPort: 'portNumber' argument (" << portNumber << ") is invalid."; X- errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X- } X- X- IrixMidiData *data = static_cast (apiData_); X- data->port = mdOpenInPort( mdGetName(portNumber) ); X- if ( data->port == NULL ) { X- ost << "RtMidiIn::openPort: Irix error opening the port (" << portNumber << ")."; X- errorString_ = ost.str(); X- error( RtMidiError::DRIVER_ERROR ); X- } X- mdSetStampMode(data->port, MD_DELTASTAMP); X- X- // Start our MIDI input thread. X- pthread_attr_t attr; X- pthread_attr_init(&attr); X- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); X- pthread_attr_setschedpolicy(&attr, SCHED_RR); X- X- inputData_.doInput = true; X- int err = pthread_create(&data->thread, &attr, irixMidiHandler, &inputData_); X- pthread_attr_destroy(&attr); X- if (err) { X- mdClosePort( data->port ); X- inputData_.doInput = false; X- errorString_ = "RtMidiIn::openPort: error starting MIDI input thread!"; X- error( RtMidiError::THREAD_ERROR ); X- } X- X- connected_ = true; X-} X- X-void RtMidiIn :: openVirtualPort() X-{ X- // This function cannot be implemented for the Irix MIDI API. X- errorString_ = "RtMidiIn::openVirtualPort: cannot be implemented in Irix MIDI API!"; X- error( RtMidiError::WARNING ); X-} X- X-void RtMidiIn :: closePort( void ) X-{ X- if ( connected_ ) { X- IrixMidiData *data = static_cast (apiData_); X- mdClosePort( data->port ); X- connected_ = false; X- X- // Shutdown the input thread. X- inputData_.doInput = false; X- pthread_join( data->thread, NULL ); X- } X-} X- X-RtMidiIn :: ~RtMidiIn() X-{ X- // Close a connection if it exists. X- closePort(); X- X- // Cleanup. X- IrixMidiData *data = static_cast (apiData_); X- delete data; X-} X- X-unsigned int RtMidiIn :: getPortCount() X-{ X- int nPorts = mdInit(); X- if ( nPorts >= 0 ) return nPorts; X- else return 0; X-} X- X-std::string RtMidiIn :: getPortName( unsigned int portNumber ) X-{ X- int nPorts = mdInit(); X- X- std::ostringstream ost; X- if ( portNumber >= nPorts ) { X- ost << "RtMidiIn::getPortName: 'portNumber' argument (" << portNumber << ") is invalid."; X- errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X- } X- X- std::string stringName = std::string( mdGetName( portNumber ) ); X- return stringName; X-} X- X //*********************************************************************// X-// API: IRIX MD X-// Class Definitions: RtMidiOut X-//*********************************************************************// X- X-unsigned int RtMidiOut :: getPortCount() X-{ X- int nPorts = mdInit(); X- if ( nPorts >= 0 ) return nPorts; X- else return 0; X-} X- X-std::string RtMidiOut :: getPortName( unsigned int portNumber ) X-{ X- int nPorts = mdInit(); X- X- std::ostringstream ost; X- if ( portNumber >= nPorts ) { X- ost << "RtMidiIn::getPortName: 'portNumber' argument (" << portNumber << ") is invalid."; X- errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X- } X- X- std::string stringName = std::string( mdGetName( portNumber ) ); X- return stringName; X-} X- X-void RtMidiOut :: initialize( void ) X-{ X- // Initialize the Irix MIDI system. At the moment, we will not X- // worry about a return value of zero (ports) because there is a X- // chance the user could plug something in after instantiation. X- int nPorts = mdInit(); X- X- // Create our api-specific connection information. X- IrixMidiData *data = (IrixMidiData *) new IrixMidiData; X- apiData_ = (void *) data; X-} X- X-void RtMidiOut :: openPort( unsigned int portNumber ) X-{ X- if ( connected_ ) { X- errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; X- error( RtMidiError::WARNING ); X- return; X- } X- X- int nPorts = mdInit(); X- if (nPorts < 1) { X- errorString_ = "RtMidiOut::openPort: no Irix MIDI output sources found!"; X- error( RtMidiError::NO_DEVICES_FOUND ); X- } X- X- std::ostringstream ost; X- if ( portNumber >= nPorts ) { X- ost << "RtMidiOut::openPort: 'portNumber' argument (" << portNumber << ") is invalid."; X- errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X- } X- X- IrixMidiData *data = static_cast (apiData_); X- data->port = mdOpenOutPort( mdGetName(portNumber) ); X- if ( data->port == NULL ) { X- ost << "RtMidiOut::openPort: Irix error opening the port (" << portNumber << ")."; X- errorString_ = ost.str(); X- error( RtMidiError::DRIVER_ERROR ); X- } X- mdSetStampMode(data->port, MD_NOSTAMP); X- X- connected_ = true; X-} X- X-void RtMidiOut :: closePort( void ) X-{ X- if ( connected_ ) { X- IrixMidiData *data = static_cast (apiData_); X- mdClosePort( data->port ); X- connected_ = false; X- } X-} X- X-void RtMidiOut :: openVirtualPort() X-{ X- // This function cannot be implemented for the Irix MIDI API. X- errorString_ = "RtMidiOut::openVirtualPort: cannot be implemented in Irix MIDI API!"; X- error( RtMidiError::WARNING ); X-} X- X-RtMidiOut :: ~RtMidiOut() X-{ X- // Close a connection if it exists. X- closePort(); X- X- // Cleanup. X- IrixMidiData *data = static_cast (apiData_); X- delete data; X-} X- X-void RtMidiOut :: sendMessage( std::vector *message ) X-{ X- int result; X- MDevent event; X- IrixMidiData *data = static_cast (apiData_); X- char *buffer = 0; X- X- unsigned int nBytes = message->size(); X- if ( nBytes == 0 ) return; X- event.stamp = 0; X- if ( message->at(0) == 0xF0 ) { X- if ( nBytes < 3 ) return; // check for bogus sysex X- event.msg[0] = 0xF0; X- event.msglen = nBytes; X- buffer = (char *) malloc( nBytes ); X- for ( int i=0; iat(i); X- event.sysexmsg = buffer; X- } X- else { X- for ( int i=0; iat(i); X- } X- X- // Send the event. X- result = mdSend( data->port, &event, 1 ); X- if ( buffer ) free( buffer ); X- if ( result < 1 ) { X- errorString_ = "RtMidiOut::sendMessage: IRIX error sending MIDI message!"; X- error( RtMidiError::WARNING ); X- return; X- } X-} X- X-#endif // __IRIX_MD__ X- X-//*********************************************************************// X // API: Windows Multimedia Library (MM) X //*********************************************************************// X X // API information deciphered from: X // - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_midi_reference.asp X X-// chuck X-#if defined(__WINDOWS_DS__) X-//#if defined(__WINDOWS_MM__) X+// Thanks to Jean-Baptiste Berruchon for the sysex code. X X+#if defined(__WINDOWS_MM__) X+ X // The Windows MM API is based on the use of a callback function for X // MIDI input. We convert the system specific time stamps to delta X // time values. X@@ -1858,37 +2414,71 @@ void RtMidiOut :: sendMessage( std::vector X #include X X+// Convert a null-terminated wide string or ANSI-encoded string to UTF-8. X+static std::string ConvertToUTF8(const TCHAR *str) X+{ X+ std::string u8str; X+ const WCHAR *wstr = L""; X+#if defined( UNICODE ) || defined( _UNICODE ) X+ wstr = str; X+#else X+ // Convert from ANSI encoding to wide string X+ int wlength = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); X+ std::wstring wstrtemp; X+ if ( wlength ) X+ { X+ wstrtemp.assign( wlength - 1, 0 ); X+ MultiByteToWideChar( CP_ACP, 0, str, -1, &wstrtemp[0], wlength ); X+ wstr = &wstrtemp[0]; X+ } X+#endif X+ // Convert from wide string to UTF-8 X+ int length = WideCharToMultiByte( CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL ); X+ if ( length ) X+ { X+ u8str.assign( length - 1, 0 ); X+ length = WideCharToMultiByte( CP_UTF8, 0, wstr, -1, &u8str[0], length, NULL, NULL ); X+ } X+ return u8str; X+} X+ X+#define RT_SYSEX_BUFFER_SIZE 1024 X+#define RT_SYSEX_BUFFER_COUNT 4 X+ X // A structure to hold variables related to the CoreMIDI API X // implementation. X struct WinMidiData { X HMIDIIN inHandle; // Handle to Midi Input Device X HMIDIOUT outHandle; // Handle to Midi Output Device X DWORD lastTime; X- RtMidiIn::MidiMessage message; X+ MidiInApi::MidiMessage message; X+ LPMIDIHDR sysexBuffer[RT_SYSEX_BUFFER_COUNT]; X+ CRITICAL_SECTION _mutex; // [Patrice] see https://groups.google.com/forum/#!topic/mididev/6OUjHutMpEo X }; X X //*********************************************************************// X // API: Windows MM X-// Class Definitions: RtMidiIn X+// Class Definitions: MidiInWinMM X //*********************************************************************// X X-static void CALLBACK midiInputCallback( HMIDIOUT hmin, X- UINT inputStatus, X- DWORD_PTR instancePtr, // 1.4.1.0 DWORD changed to DWORD_PTR | PR #157 @dbadb X+static void CALLBACK midiInputCallback( HMIDIIN /*hmin*/, X+ UINT inputStatus, X+ DWORD_PTR instancePtr, X DWORD_PTR midiMessage, X- DWORD_PTR timestamp ) X+ DWORD timestamp ) X { X- if ( inputStatus != MIM_DATA && inputStatus != MIM_LONGDATA ) return; X+ if ( inputStatus != MIM_DATA && inputStatus != MIM_LONGDATA && inputStatus != MIM_LONGERROR ) return; X X- //RtMidiIn::RtMidiInData *data = static_cast (instancePtr); X- RtMidiIn::RtMidiInData *data = (RtMidiIn::RtMidiInData *)instancePtr; X+ //MidiInApi::RtMidiInData *data = static_cast (instancePtr); X+ MidiInApi::RtMidiInData *data = (MidiInApi::RtMidiInData *)instancePtr; X WinMidiData *apiData = static_cast (data->apiData); X X // Calculate time stamp. X- apiData->message.timeStamp = 0.0; X- if ( data->firstMessage == true ) data->firstMessage = false; X+ if ( data->firstMessage == true ) { X+ apiData->message.timeStamp = 0.0; X+ data->firstMessage = false; X+ } X else apiData->message.timeStamp = (double) ( timestamp - apiData->lastTime ) * 0.001; X- apiData->lastTime = timestamp; X X if ( inputStatus == MIM_DATA ) { // Channel or system message X X@@ -1901,58 +2491,97 @@ static void CALLBACK midiInputCallback( HMIDIOUT hmin, X if ( status < 0xC0 ) nBytes = 3; X else if ( status < 0xE0 ) nBytes = 2; X else if ( status < 0xF0 ) nBytes = 3; X- else if ( status < 0xF3 ) { X- // A MIDI time code message and we're ignoring it. X- if ( status == 0xF1 && (data->ignoreFlags & 0x02) ) return; X- nBytes = 3; X+ else if ( status == 0xF1 ) { X+ if ( data->ignoreFlags & 0x02 ) return; X+ else nBytes = 2; X } X+ else if ( status == 0xF2 ) nBytes = 3; X else if ( status == 0xF3 ) nBytes = 2; X- else if ( status == 0xF8 && (data->ignoreFlags & 0x02) ) { X+ else if ( status == 0xF8 && ( data->ignoreFlags & 0x02 ) ) { X // A MIDI timing tick message and we're ignoring it. X return; X } X- else if ( status == 0xFE && (data->ignoreFlags & 0x04) ) { X+ else if ( status == 0xFE && ( data->ignoreFlags & 0x04 ) ) { X // A MIDI active sensing message and we're ignoring it. X return; X } X X // Copy bytes to our MIDI message. X unsigned char *ptr = (unsigned char *) &midiMessage; X- for ( int i=0; imessage.bytes.push_back( *ptr++ ); X+ for ( int i=0; imessage.bytes.push_back( *ptr++ ); X } X- else { // Sysex message X+ else { // Sysex message ( MIM_LONGDATA or MIM_LONGERROR ) X MIDIHDR *sysex = ( MIDIHDR *) midiMessage; X- for ( int i=0; i<(int)sysex->dwBytesRecorded; i++ ) X- apiData->message.bytes.push_back( sysex->lpData[i] ); X- if ( apiData->message.bytes.back() != 0xF7 ) return; X+ if ( !( data->ignoreFlags & 0x01 ) && inputStatus != MIM_LONGERROR ) { X+ // Sysex message and we're not ignoring it X+ for ( int i=0; i<(int)sysex->dwBytesRecorded; ++i ) X+ apiData->message.bytes.push_back( sysex->lpData[i] ); X+ } X+ X+ // The WinMM API requires that the sysex buffer be requeued after X+ // input of each sysex message. Even if we are ignoring sysex X+ // messages, we still need to requeue the buffer in case the user X+ // decides to not ignore sysex messages in the future. However, X+ // it seems that WinMM calls this function with an empty sysex X+ // buffer when an application closes and in this case, we should X+ // avoid requeueing it, else the computer suddenly reboots after X+ // one or two minutes. X+ if ( apiData->sysexBuffer[sysex->dwUser]->dwBytesRecorded > 0 ) { X+ //if ( sysex->dwBytesRecorded > 0 ) { X+ EnterCriticalSection( &(apiData->_mutex) ); X+ MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer[sysex->dwUser], sizeof(MIDIHDR) ); X+ LeaveCriticalSection( &(apiData->_mutex) ); X+ if ( result != MMSYSERR_NOERROR ) X+ std::cerr << "\nRtMidiIn::midiInputCallback: error sending sysex to Midi device!!\n\n"; X+ X+ if ( data->ignoreFlags & 0x01 ) return; X+ } X+ else return; X } X X+ // Save the time of the last non-filtered message X+ apiData->lastTime = timestamp; X+ X if ( data->usingCallback ) { X RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; X callback( apiData->message.timeStamp, &apiData->message.bytes, data->userData ); X } X else { X // As long as we haven't reached our queue size limit, push the message. X- if ( data->queueLimit > data->queue.size() ) X- data->queue.push( apiData->message ); X- else X- std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; X+ if ( !data->queue.push( apiData->message ) ) X+ std::cerr << "\nMidiInWinMM: message queue limit reached!!\n\n"; X } X X- // Clear the vector for the next input message. Note that doing X- // this here allows our code to work for sysex messages which are X- // segmented across multiple buffers. X+ // Clear the vector for the next input message. X apiData->message.bytes.clear(); X } X X-void RtMidiIn :: initialize( void ) X+MidiInWinMM :: MidiInWinMM( const std::string &clientName, unsigned int queueSizeLimit ) X+ : MidiInApi( queueSizeLimit ) X { X+ MidiInWinMM::initialize( clientName ); X+} X+ X+MidiInWinMM :: ~MidiInWinMM() X+{ X+ // Close a connection if it exists. X+ MidiInWinMM::closePort(); X+ X+ WinMidiData *data = static_cast (apiData_); X+ DeleteCriticalSection( &(data->_mutex) ); X+ X+ // Cleanup. X+ delete data; X+} X+ X+void MidiInWinMM :: initialize( const std::string& /*clientName*/ ) X+{ X // We'll issue a warning here if no devices are available but not X // throw an error since the user can plugin something later. X unsigned int nDevices = midiInGetNumDevs(); X if ( nDevices == 0 ) { X- errorString_ = "RtMidiIn::initialize: no MIDI input devices currently available."; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiInWinMM::initialize: no MIDI input devices currently available."; X+ error( RtMidiError::WARNING, errorString_ ); X } X X // Save our api-specific connection information. X@@ -1960,135 +2589,201 @@ void RtMidiIn :: initialize( void ) X apiData_ = (void *) data; X inputData_.apiData = (void *) data; X data->message.bytes.clear(); // needs to be empty for first input message X+ X+ if ( !InitializeCriticalSectionAndSpinCount( &(data->_mutex), 0x00000400 ) ) { X+ errorString_ = "MidiInWinMM::initialize: InitializeCriticalSectionAndSpinCount failed."; X+ error( RtMidiError::WARNING, errorString_ ); X+ } X } X X-void RtMidiIn :: openPort( unsigned int portNumber ) X+void MidiInWinMM :: openPort( unsigned int portNumber, const std::string &/*portName*/ ) X { X if ( connected_ ) { X- errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiInWinMM::openPort: a valid connection already exists!"; X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X unsigned int nDevices = midiInGetNumDevs(); X if (nDevices == 0) { X- errorString_ = "RtMidiIn::openPort: no MIDI input sources found!"; X- error( RtMidiError::NO_DEVICES_FOUND ); X+ errorString_ = "MidiInWinMM::openPort: no MIDI input sources found!"; X+ error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); X+ return; X } X X- std::ostringstream ost; X if ( portNumber >= nDevices ) { X- ost << "RtMidiIn::openPort: 'portNumber' argument (" << portNumber << ") is invalid."; X+ std::ostringstream ost; X+ ost << "MidiInWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; X errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X+ error( RtMidiError::INVALID_PARAMETER, errorString_ ); X+ return; X } X X WinMidiData *data = static_cast (apiData_); X MMRESULT result = midiInOpen( &data->inHandle, X portNumber, X- (DWORD_PTR)&midiInputCallback, // 1.4.1.0 DWORD changed to DWORD_PTR | PR #157 @dbadb X+ (DWORD_PTR)&midiInputCallback, X (DWORD_PTR)&inputData_, X CALLBACK_FUNCTION ); X if ( result != MMSYSERR_NOERROR ) { X- errorString_ = "RtMidiIn::openPort: error creating Windows MM MIDI input port."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiInWinMM::openPort: error creating Windows MM MIDI input port."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X+ // Allocate and init the sysex buffers. X+ for ( int i=0; isysexBuffer[i] = (MIDIHDR*) new char[ sizeof(MIDIHDR) ]; X+ data->sysexBuffer[i]->lpData = new char[ RT_SYSEX_BUFFER_SIZE ]; X+ data->sysexBuffer[i]->dwBufferLength = RT_SYSEX_BUFFER_SIZE; X+ data->sysexBuffer[i]->dwUser = i; // We use the dwUser parameter as buffer indicator X+ data->sysexBuffer[i]->dwFlags = 0; X+ X+ result = midiInPrepareHeader( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) ); X+ if ( result != MMSYSERR_NOERROR ) { X+ midiInClose( data->inHandle ); X+ data->inHandle = 0; X+ errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (PrepareHeader)."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X+ X+ // Register the buffer. X+ result = midiInAddBuffer( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) ); X+ if ( result != MMSYSERR_NOERROR ) { X+ midiInClose( data->inHandle ); X+ data->inHandle = 0; X+ errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (AddBuffer)."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X+ } X+ X result = midiInStart( data->inHandle ); X if ( result != MMSYSERR_NOERROR ) { X midiInClose( data->inHandle ); X- errorString_ = "RtMidiIn::openPort: error starting Windows MM MIDI input port."; X- error( RtMidiError::DRIVER_ERROR ); X+ data->inHandle = 0; X+ errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X connected_ = true; X } X X-void RtMidiIn :: openVirtualPort() X+void MidiInWinMM :: openVirtualPort( const std::string &/*portName*/ ) X { X // This function cannot be implemented for the Windows MM MIDI API. X- errorString_ = "RtMidiIn::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiInWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; X+ error( RtMidiError::WARNING, errorString_ ); X } X X-void RtMidiIn :: closePort( void ) X+void MidiInWinMM :: closePort( void ) X { X if ( connected_ ) { X WinMidiData *data = static_cast (apiData_); X+ EnterCriticalSection( &(data->_mutex) ); X midiInReset( data->inHandle ); X midiInStop( data->inHandle ); X+ X+ for ( int i=0; iinHandle, data->sysexBuffer[i], sizeof(MIDIHDR)); X+ delete [] data->sysexBuffer[i]->lpData; X+ delete [] data->sysexBuffer[i]; X+ if ( result != MMSYSERR_NOERROR ) { X+ midiInClose( data->inHandle ); X+ data->inHandle = 0; X+ errorString_ = "MidiInWinMM::openPort: error closing Windows MM MIDI input port (midiInUnprepareHeader)."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X+ } X+ X midiInClose( data->inHandle ); X+ data->inHandle = 0; X connected_ = false; X+ LeaveCriticalSection( &(data->_mutex) ); X } X } X X-RtMidiIn :: ~RtMidiIn() X+void MidiInWinMM :: setClientName ( const std::string& ) X { X- // Close a connection if it exists. X- closePort(); X X- // Cleanup. X- WinMidiData *data = static_cast (apiData_); X- delete data; X+ errorString_ = "MidiInWinMM::setClientName: this function is not implemented for the WINDOWS_MM API!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ X } X X-unsigned int RtMidiIn :: getPortCount() X+void MidiInWinMM :: setPortName ( const std::string& ) X { X+ X+ errorString_ = "MidiInWinMM::setPortName: this function is not implemented for the WINDOWS_MM API!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ X+} X+ X+unsigned int MidiInWinMM :: getPortCount() X+{ X return midiInGetNumDevs(); X } X X-std::string RtMidiIn :: getPortName( unsigned int portNumber ) X+std::string MidiInWinMM :: getPortName( unsigned int portNumber ) X { X+ std::string stringName; X unsigned int nDevices = midiInGetNumDevs(); X if ( portNumber >= nDevices ) { X std::ostringstream ost; X- ost << "RtMidiIn::getPortName: 'portNumber' argument (" << portNumber << ") is invalid."; X+ ost << "MidiInWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; X errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X+ error( RtMidiError::WARNING, errorString_ ); X+ return stringName; X } X X MIDIINCAPS deviceCaps; X- MMRESULT result = midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS)); X+ midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS)); X+ stringName = ConvertToUTF8( deviceCaps.szPname ); X X- std::string stringName = std::string( deviceCaps.szPname ); X+ // Next lines added to add the portNumber to the name so that X+ // the device's names are sure to be listed with individual names X+ // even when they have the same brand name X+#ifndef RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES X+ std::ostringstream os; X+ os << " "; X+ os << portNumber; X+ stringName += os.str(); X+#endif X+ X return stringName; X } X X //*********************************************************************// X // API: Windows MM X-// Class Definitions: RtMidiOut X+// Class Definitions: MidiOutWinMM X //*********************************************************************// X X-unsigned int RtMidiOut :: getPortCount() X+MidiOutWinMM :: MidiOutWinMM( const std::string &clientName ) : MidiOutApi() X { X- return midiOutGetNumDevs(); X+ MidiOutWinMM::initialize( clientName ); X } X X-std::string RtMidiOut :: getPortName( unsigned int portNumber ) X+MidiOutWinMM :: ~MidiOutWinMM() X { X- unsigned int nDevices = midiOutGetNumDevs(); X- if ( portNumber >= nDevices ) { X- std::ostringstream ost; X- ost << "RtMidiOut::getPortName: 'portNumber' argument (" << portNumber << ") is invalid."; X- errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X- } X+ // Close a connection if it exists. X+ MidiOutWinMM::closePort(); X X- MIDIOUTCAPS deviceCaps; X- MMRESULT result = midiOutGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIOUTCAPS)); X- X- std::string stringName = std::string( deviceCaps.szPname ); X- return stringName; X+ // Cleanup. X+ WinMidiData *data = static_cast (apiData_); X+ delete data; X } X X-void RtMidiOut :: initialize( void ) X+void MidiOutWinMM :: initialize( const std::string& /*clientName*/ ) X { X // We'll issue a warning here if no devices are available but not X // throw an error since the user can plug something in later. X unsigned int nDevices = midiOutGetNumDevs(); X if ( nDevices == 0 ) { X- errorString_ = "RtMidiOut::initialize: no MIDI output devices currently available."; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiOutWinMM::initialize: no MIDI output devices currently available."; X+ error( RtMidiError::WARNING, errorString_ ); X } X X // Save our api-specific connection information. X@@ -2096,25 +2791,61 @@ void RtMidiOut :: initialize( void ) X apiData_ = (void *) data; X } X X-void RtMidiOut :: openPort( unsigned int portNumber ) X+unsigned int MidiOutWinMM :: getPortCount() X { X+ return midiOutGetNumDevs(); X+} X+ X+std::string MidiOutWinMM :: getPortName( unsigned int portNumber ) X+{ X+ std::string stringName; X+ unsigned int nDevices = midiOutGetNumDevs(); X+ if ( portNumber >= nDevices ) { X+ std::ostringstream ost; X+ ost << "MidiOutWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; X+ errorString_ = ost.str(); X+ error( RtMidiError::WARNING, errorString_ ); X+ return stringName; X+ } X+ X+ MIDIOUTCAPS deviceCaps; X+ midiOutGetDevCaps( portNumber, &deviceCaps, sizeof( MIDIOUTCAPS ) ); X+ stringName = ConvertToUTF8( deviceCaps.szPname ); X+ X+ // Next lines added to add the portNumber to the name so that X+ // the device's names are sure to be listed with individual names X+ // even when they have the same brand name X+ std::ostringstream os; X+#ifndef RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES X+ os << " "; X+ os << portNumber; X+ stringName += os.str(); X+#endif X+ X+ return stringName; X+} X+ X+void MidiOutWinMM :: openPort( unsigned int portNumber, const std::string &/*portName*/ ) X+{ X if ( connected_ ) { X- errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiOutWinMM::openPort: a valid connection already exists!"; X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X unsigned int nDevices = midiOutGetNumDevs(); X- if (nDevices < 1) { X- errorString_ = "RtMidiOut::openPort: no MIDI output destinations found!"; X- error( RtMidiError::NO_DEVICES_FOUND ); X+ if ( nDevices < 1 ) { X+ errorString_ = "MidiOutWinMM::openPort: no MIDI output destinations found!"; X+ error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); X+ return; X } X X- std::ostringstream ost; X if ( portNumber >= nDevices ) { X- ost << "RtMidiOut::openPort: 'portNumber' argument (" << portNumber << ") is invalid."; X+ std::ostringstream ost; X+ ost << "MidiOutWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; X errorString_ = ost.str(); X- error( RtMidiError::INVALID_PARAMETER ); X+ error( RtMidiError::INVALID_PARAMETER, errorString_ ); X+ return; X } X X WinMidiData *data = static_cast (apiData_); X@@ -2124,110 +2855,122 @@ void RtMidiOut :: openPort( unsigned int portNumber ) X (DWORD)NULL, X CALLBACK_NULL ); X if ( result != MMSYSERR_NOERROR ) { X- errorString_ = "RtMidiOut::openPort: error creating Windows MM MIDI output port."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiOutWinMM::openPort: error creating Windows MM MIDI output port."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X connected_ = true; X } X X-void RtMidiOut :: closePort( void ) X+void MidiOutWinMM :: closePort( void ) X { X if ( connected_ ) { X WinMidiData *data = static_cast (apiData_); X midiOutReset( data->outHandle ); X midiOutClose( data->outHandle ); X+ data->outHandle = 0; X connected_ = false; X } X } X X-void RtMidiOut :: openVirtualPort() X+void MidiOutWinMM :: setClientName ( const std::string& ) X { X- // This function cannot be implemented for the Windows MM MIDI API. X- errorString_ = "RtMidiOut::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; X- error( RtMidiError::WARNING ); X+ X+ errorString_ = "MidiOutWinMM::setClientName: this function is not implemented for the WINDOWS_MM API!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ X } X X-RtMidiOut :: ~RtMidiOut() X+void MidiOutWinMM :: setPortName ( const std::string& ) X { X- // Close a connection if it exists. X- closePort(); X X- // Cleanup. X- WinMidiData *data = static_cast (apiData_); X- delete data; X+ errorString_ = "MidiOutWinMM::setPortName: this function is not implemented for the WINDOWS_MM API!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ X } X X-void RtMidiOut :: sendMessage( std::vector *message ) X+void MidiOutWinMM :: openVirtualPort( const std::string &/*portName*/ ) X { X- unsigned int nBytes = message->size(); X+ // This function cannot be implemented for the Windows MM MIDI API. X+ errorString_ = "MidiOutWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; X+ error( RtMidiError::WARNING, errorString_ ); X+} X+ X+void MidiOutWinMM :: sendMessage( const unsigned char *message, size_t size ) X+{ X+ if ( !connected_ ) return; X+ X+ unsigned int nBytes = static_cast(size); X if ( nBytes == 0 ) { X- errorString_ = "RtMidiOut::sendMessage: message argument is empty!"; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiOutWinMM::sendMessage: message argument is empty!"; X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X MMRESULT result; X WinMidiData *data = static_cast (apiData_); X- if ( message->at(0) == 0xF0 ) { // Sysex message X+ if ( message[0] == 0xF0 ) { // Sysex message X X // Allocate buffer for sysex data. X char *buffer = (char *) malloc( nBytes ); X if ( buffer == NULL ) { X- errorString_ = "RtMidiOut::sendMessage: error allocating sysex message memory!"; X- error( RtMidiError::MEMORY_ERROR ); X+ errorString_ = "MidiOutWinMM::sendMessage: error allocating sysex message memory!"; X+ error( RtMidiError::MEMORY_ERROR, errorString_ ); X+ return; X } X X // Copy data to buffer. X- for ( unsigned int i=0; iat(i); X+ for ( unsigned int i=0; ioutHandle, &sysex, sizeof(MIDIHDR) ); X+ result = midiOutPrepareHeader( data->outHandle, &sysex, sizeof( MIDIHDR ) ); X if ( result != MMSYSERR_NOERROR ) { X free( buffer ); X- errorString_ = "RtMidiOut::sendMessage: error preparing sysex header."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiOutWinMM::sendMessage: error preparing sysex header."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X // Send the message. X- result = midiOutLongMsg( data->outHandle, &sysex, sizeof(MIDIHDR) ); X+ result = midiOutLongMsg( data->outHandle, &sysex, sizeof( MIDIHDR ) ); X if ( result != MMSYSERR_NOERROR ) { X free( buffer ); X- errorString_ = "RtMidiOut::sendMessage: error sending sysex message."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiOutWinMM::sendMessage: error sending sysex message."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X } X X // Unprepare the buffer and MIDIHDR. X- while ( MIDIERR_STILLPLAYING == midiOutUnprepareHeader( data->outHandle, &sysex, sizeof (MIDIHDR) ) ) Sleep( 1 ); X+ while ( MIDIERR_STILLPLAYING == midiOutUnprepareHeader( data->outHandle, &sysex, sizeof ( MIDIHDR ) ) ) Sleep( 1 ); X free( buffer ); X- X } X else { // Channel or system message. X X // Make sure the message size isn't too big. X if ( nBytes > 3 ) { X- errorString_ = "RtMidiOut::sendMessage: message size is greater than 3 bytes (and not sysex)!"; X- error( RtMidiError::WARNING ); X+ errorString_ = "MidiOutWinMM::sendMessage: message size is greater than 3 bytes (and not sysex)!"; X+ error( RtMidiError::WARNING, errorString_ ); X return; X } X X // Pack MIDI bytes into double word. X DWORD packet; X unsigned char *ptr = (unsigned char *) &packet; X- for ( unsigned int i=0; iat(i); X- ptr++; X+ for ( unsigned int i=0; ioutHandle, packet ); X if ( result != MMSYSERR_NOERROR ) { X- errorString_ = "RtMidiOut::sendMessage: error sending MIDI message."; X- error( RtMidiError::DRIVER_ERROR ); X+ errorString_ = "MidiOutWinMM::sendMessage: error sending MIDI message."; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X } X } X } X@@ -2235,91 +2978,524 @@ void RtMidiOut :: sendMessage( std::vector X+#include X+#include X+#ifdef HAVE_SEMAPHORE X+ #include X+#endif X+ X+#define JACK_RINGBUFFER_SIZE 16384 // Default size for ringbuffer X+ X+struct JackMidiData { X+ jack_client_t *client; X+ jack_port_t *port; X+ jack_ringbuffer_t *buffSize; X+ jack_ringbuffer_t *buffMessage; X+ jack_time_t lastTime; X+#ifdef HAVE_SEMAPHORE X+ sem_t sem_cleanup; X+ sem_t sem_needpost; X+#endif X+ MidiInApi :: RtMidiInData *rtMidiIn; X+ }; X+ X+//*********************************************************************// X+// API: JACK X+// Class Definitions: MidiInJack X+//*********************************************************************// X+ X+static int jackProcessIn( jack_nframes_t nframes, void *arg ) X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X+ JackMidiData *jData = (JackMidiData *) arg; X+ MidiInApi :: RtMidiInData *rtData = jData->rtMidiIn; X+ jack_midi_event_t event; X+ jack_time_t time; X+ X+ // Is port created? X+ if ( jData->port == NULL ) return 0; X+ X+ void *buff = jack_port_get_buffer( jData->port, nframes ); X+ bool& continueSysex = rtData->continueSysex; X+ unsigned char& ignoreFlags = rtData->ignoreFlags; X+ X+ // We have midi events in buffer X+ int evCount = jack_midi_get_event_count( buff ); X+ for (int j = 0; j < evCount; j++) { X+ MidiInApi::MidiMessage& message = rtData->message; X+ jack_midi_event_get( &event, buff, j ); X+ X+ // Compute the delta time. X+ time = jack_get_time(); X+ if ( rtData->firstMessage == true ) { X+ message.timeStamp = 0.0; X+ rtData->firstMessage = false; X+ } else X+ message.timeStamp = ( time - jData->lastTime ) * 0.000001; X+ X+ jData->lastTime = time; X+ X+ if ( !continueSysex ) X+ message.bytes.clear(); X+ X+ if ( !( ( continueSysex || event.buffer[0] == 0xF0 ) && ( ignoreFlags & 0x01 ) ) ) { X+ // Unless this is a (possibly continued) SysEx message and we're ignoring SysEx, X+ // copy the event buffer into the MIDI message struct. X+ for ( unsigned int i = 0; i < event.size; i++ ) X+ message.bytes.push_back( event.buffer[i] ); X+ } X+ X+ switch ( event.buffer[0] ) { X+ case 0xF0: X+ // Start of a SysEx message X+ continueSysex = event.buffer[event.size - 1] != 0xF7; X+ if ( ignoreFlags & 0x01 ) continue; X+ break; X+ case 0xF1: X+ case 0xF8: X+ // MIDI Time Code or Timing Clock message X+ if ( ignoreFlags & 0x02 ) continue; X+ break; X+ case 0xFE: X+ // Active Sensing message X+ if ( ignoreFlags & 0x04 ) continue; X+ break; X+ default: X+ if ( continueSysex ) { X+ // Continuation of a SysEx message X+ continueSysex = event.buffer[event.size - 1] != 0xF7; X+ if ( ignoreFlags & 0x01 ) continue; X+ } X+ // All other MIDI messages X+ } X+ X+ if ( !continueSysex ) { X+ // If not a continuation of a SysEx message, X+ // invoke the user callback function or queue the message. X+ if ( rtData->usingCallback ) { X+ RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) rtData->userCallback; X+ callback( message.timeStamp, &message.bytes, rtData->userData ); X+ } X+ else { X+ // As long as we haven't reached our queue size limit, push the message. X+ if ( !rtData->queue.push( message ) ) X+ std::cerr << "\nMidiInJack: message queue limit reached!!\n\n"; X+ } X+ } X+ } X+ X+ return 0; X } X-*/ X-void RtMidiIn :: initialize( void ) X+ X+MidiInJack :: MidiInJack( const std::string &clientName, unsigned int queueSizeLimit ) X+ : MidiInApi( queueSizeLimit ) X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X+ MidiInJack::initialize( clientName ); X } X X-void RtMidiIn :: openPort( unsigned int portNumber ) X+void MidiInJack :: initialize( const std::string& clientName ) X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X+ JackMidiData *data = new JackMidiData; X+ apiData_ = (void *) data; X+ X+ data->rtMidiIn = &inputData_; X+ data->port = NULL; X+ data->client = NULL; X+ this->clientName = clientName; X+ X+ connect(); X } X X-void RtMidiIn :: openVirtualPort() X+void MidiInJack :: connect() X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X+ JackMidiData *data = static_cast (apiData_); X+ if ( data->client ) X+ return; X+ X+ // Initialize JACK client X+ if (( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) { X+ errorString_ = "MidiInJack::initialize: JACK server not running?"; X+ error( RtMidiError::WARNING, errorString_ ); X+ return; X+ } X+ X+ jack_set_process_callback( data->client, jackProcessIn, data ); X+ jack_activate( data->client ); X } X X-void RtMidiIn :: closePort( void ) X+MidiInJack :: ~MidiInJack() X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X+ JackMidiData *data = static_cast (apiData_); X+ MidiInJack::closePort(); X+ X+ if ( data->client ) X+ jack_client_close( data->client ); X+ delete data; X } X X-RtMidiIn :: ~RtMidiIn() X+void MidiInJack :: openPort( unsigned int portNumber, const std::string &portName ) X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X+ JackMidiData *data = static_cast (apiData_); X+ X+ connect(); X+ X+ // Creating new port X+ if ( data->port == NULL ) X+ data->port = jack_port_register( data->client, portName.c_str(), X+ JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 ); X+ X+ if ( data->port == NULL ) { X+ errorString_ = "MidiInJack::openPort: JACK error creating port"; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X+ X+ // Connecting to the output X+ std::string name = getPortName( portNumber ); X+ jack_connect( data->client, name.c_str(), jack_port_name( data->port ) ); X+ X+ connected_ = true; X } X X-unsigned int RtMidiIn :: getPortCount() X+void MidiInJack :: openVirtualPort( const std::string &portName ) X { X+ JackMidiData *data = static_cast (apiData_); X+ X+ connect(); X+ if ( data->port == NULL ) X+ data->port = jack_port_register( data->client, portName.c_str(), X+ JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 ); X+ X+ if ( data->port == NULL ) { X+ errorString_ = "MidiInJack::openVirtualPort: JACK error creating virtual port"; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ } X+} X+ X+unsigned int MidiInJack :: getPortCount() X+{ X+ int count = 0; X+ JackMidiData *data = static_cast (apiData_); X+ connect(); X+ if ( !data->client ) X return 0; X+ X+ // List of available ports X+ const char **ports = jack_get_ports( data->client, NULL, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput ); X+ X+ if ( ports == NULL ) return 0; X+ while ( ports[count] != NULL ) X+ count++; X+ X+ free( ports ); X+ X+ return count; X } X X-std::string RtMidiIn :: getPortName( unsigned int portNumber ) X+std::string MidiInJack :: getPortName( unsigned int portNumber ) X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X- return ""; X+ JackMidiData *data = static_cast (apiData_); X+ std::string retStr( "" ); X+ X+ connect(); X+ X+ // List of available ports X+ const char **ports = jack_get_ports( data->client, NULL, X+ JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput ); X+ X+ // Check port validity X+ if ( ports == NULL ) { X+ errorString_ = "MidiInJack::getPortName: no ports available!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ return retStr; X+ } X+ X+ unsigned int i; X+ for ( i=0; i (apiData_); X+ X+ if ( data->port == NULL ) return; X+ jack_port_unregister( data->client, data->port ); X+ data->port = NULL; X+ X+ connected_ = false; X+} X+ X+void MidiInJack:: setClientName( const std::string& ) X+{ X+ X+ errorString_ = "MidiInJack::setClientName: this function is not implemented for the UNIX_JACK API!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ X+} X+ X+void MidiInJack :: setPortName( const std::string &portName ) X+{ X+ JackMidiData *data = static_cast (apiData_); X+#ifdef JACK_HAS_PORT_RENAME X+ jack_port_rename( data->client, data->port, portName.c_str() ); X+#else X+ jack_port_set_name( data->port, portName.c_str() ); X+#endif X+} X+ X //*********************************************************************// X-// API: OS-X X-// Class Definitions: RtMidiOut X+// API: JACK X+// Class Definitions: MidiOutJack X //*********************************************************************// X X-unsigned int RtMidiOut :: getPortCount() X+// Jack process callback X+static int jackProcessOut( jack_nframes_t nframes, void *arg ) X { X- return 0; X+ JackMidiData *data = (JackMidiData *) arg; X+ jack_midi_data_t *midiData; X+ int space; X+ X+ // Is port created? X+ if ( data->port == NULL ) return 0; X+ X+ void *buff = jack_port_get_buffer( data->port, nframes ); X+ jack_midi_clear_buffer( buff ); X+ X+ while ( jack_ringbuffer_read_space( data->buffSize ) > 0 ) { X+ jack_ringbuffer_read( data->buffSize, (char *) &space, (size_t) sizeof( space ) ); X+ midiData = jack_midi_event_reserve( buff, 0, space ); X+ X+ jack_ringbuffer_read( data->buffMessage, (char *) midiData, (size_t) space ); X+ } X+ X+#ifdef HAVE_SEMAPHORE X+ if ( !sem_trywait( &data->sem_needpost ) ) X+ sem_post( &data->sem_cleanup ); X+#endif X+ X+ return 0; X } X X-std::string RtMidiOut :: getPortName( unsigned int portNumber ) X+MidiOutJack :: MidiOutJack( const std::string &clientName ) : MidiOutApi() X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X- return ""; X+ MidiOutJack::initialize( clientName ); X } X X-void RtMidiOut :: initialize( void ) X+void MidiOutJack :: initialize( const std::string& clientName ) X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X+ JackMidiData *data = new JackMidiData; X+ apiData_ = (void *) data; X+ X+ data->port = NULL; X+ data->client = NULL; X+#ifdef HAVE_SEMAPHORE X+ sem_init( &data->sem_cleanup, 0, 0 ); X+ sem_init( &data->sem_needpost, 0, 0 ); X+#endif X+ this->clientName = clientName; X+ X+ connect(); X } X X-void RtMidiOut :: openPort( unsigned int portNumber ) X+void MidiOutJack :: connect() X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X+ JackMidiData *data = static_cast (apiData_); X+ if ( data->client ) X+ return; X+ X+ // Initialize output ringbuffers X+ data->buffSize = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE ); X+ data->buffMessage = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE ); X+ X+ // Initialize JACK client X+ if ( ( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL ) ) == 0 ) { X+ errorString_ = "MidiOutJack::initialize: JACK server not running?"; X+ error( RtMidiError::WARNING, errorString_ ); X+ return; X+ } X+ X+ jack_set_process_callback( data->client, jackProcessOut, data ); X+ jack_activate( data->client ); X } X X-void RtMidiOut :: closePort( void ) X+MidiOutJack :: ~MidiOutJack() X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X+ JackMidiData *data = static_cast (apiData_); X+ MidiOutJack::closePort(); X+ X+ // Cleanup X+ jack_ringbuffer_free( data->buffSize ); X+ jack_ringbuffer_free( data->buffMessage ); X+ if ( data->client ) { X+ jack_client_close( data->client ); X+ } X+ X+#ifdef HAVE_SEMAPHORE X+ sem_destroy( &data->sem_cleanup ); X+ sem_destroy( &data->sem_needpost ); X+#endif X+ X+ delete data; X } X X-void RtMidiOut :: openVirtualPort() X+void MidiOutJack :: openPort( unsigned int portNumber, const std::string &portName ) X { X- fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" ); X+ JackMidiData *data = static_cast (apiData_); X+ X+ connect(); X+ X+ // Creating new port X+ if ( data->port == NULL ) X+ data->port = jack_port_register( data->client, portName.c_str(), X+ JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 ); X+ X+ if ( data->port == NULL ) { X+ errorString_ = "MidiOutJack::openPort: JACK error creating port"; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ return; X+ } X+ X+ // Connecting to the output X+ std::string name = getPortName( portNumber ); X+ jack_connect( data->client, jack_port_name( data->port ), name.c_str() ); X+ X+ connected_ = true; X } X X-RtMidiOut :: ~RtMidiOut() X+void MidiOutJack :: openVirtualPort( const std::string &portName ) X { X+ JackMidiData *data = static_cast (apiData_); X+ X+ connect(); X+ if ( data->port == NULL ) X+ data->port = jack_port_register( data->client, portName.c_str(), X+ JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 ); X+ X+ if ( data->port == NULL ) { X+ errorString_ = "MidiOutJack::openVirtualPort: JACK error creating virtual port"; X+ error( RtMidiError::DRIVER_ERROR, errorString_ ); X+ } X } X X-void RtMidiOut :: sendMessage( std::vector *message ) X+unsigned int MidiOutJack :: getPortCount() X { X+ int count = 0; X+ JackMidiData *data = static_cast (apiData_); X+ connect(); X+ if ( !data->client ) X+ return 0; X+ X+ // List of available ports X+ const char **ports = jack_get_ports( data->client, NULL, X+ JACK_DEFAULT_MIDI_TYPE, JackPortIsInput ); X+ X+ if ( ports == NULL ) return 0; X+ while ( ports[count] != NULL ) X+ count++; X+ X+ free( ports ); X+ X+ return count; X } X X-#endif // dummy X+std::string MidiOutJack :: getPortName( unsigned int portNumber ) X+{ X+ JackMidiData *data = static_cast (apiData_); X+ std::string retStr(""); X+ X+ connect(); X+ X+ // List of available ports X+ const char **ports = jack_get_ports( data->client, NULL, X+ JACK_DEFAULT_MIDI_TYPE, JackPortIsInput ); X+ X+ // Check port validity X+ if ( ports == NULL ) { X+ errorString_ = "MidiOutJack::getPortName: no ports available!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ return retStr; X+ } X+ X+ if ( ports[portNumber] == NULL ) { X+ std::ostringstream ost; X+ ost << "MidiOutJack::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; X+ errorString_ = ost.str(); X+ error( RtMidiError::WARNING, errorString_ ); X+ } X+ else retStr.assign( ports[portNumber] ); X+ X+ free( ports ); X+ return retStr; X+} X+ X+void MidiOutJack :: closePort() X+{ X+ JackMidiData *data = static_cast (apiData_); X+ X+ if ( data->port == NULL ) return; X+ X+#ifdef HAVE_SEMAPHORE X+ struct timespec ts; X+ if ( clock_gettime( CLOCK_REALTIME, &ts ) != -1 ) { X+ ts.tv_sec += 1; // wait max one second X+ sem_post( &data->sem_needpost ); X+ sem_timedwait( &data->sem_cleanup, &ts ); X+ } X+#endif X+ X+ jack_port_unregister( data->client, data->port ); X+ data->port = NULL; X+ X+ connected_ = false; X+} X+ X+void MidiOutJack:: setClientName( const std::string& ) X+{ X+ X+ errorString_ = "MidiOutJack::setClientName: this function is not implemented for the UNIX_JACK API!"; X+ error( RtMidiError::WARNING, errorString_ ); X+ X+} X+ X+void MidiOutJack :: setPortName( const std::string &portName ) X+{ X+ JackMidiData *data = static_cast (apiData_); X+#ifdef JACK_HAS_PORT_RENAME X+ jack_port_rename( data->client, data->port, portName.c_str() ); X+#else X+ jack_port_set_name( data->port, portName.c_str() ); X+#endif X+} X+ X+void MidiOutJack :: sendMessage( const unsigned char *message, size_t size ) X+{ X+ int nBytes = static_cast(size); X+ JackMidiData *data = static_cast (apiData_); X+ X+ // Write full message to buffer X+ jack_ringbuffer_write( data->buffMessage, ( const char * ) message, nBytes ); X+ jack_ringbuffer_write( data->buffSize, ( char * ) &nBytes, sizeof( nBytes ) ); X+} X+ X+#endif // __UNIX_JACK__ || __FREEBSD_OSS__ || __LINUX_OSS__ 459d14ed15abfc1e9fd8ed74a506d6db echo x - audio/chuck/pkg-plist sed 's/^X//' >audio/chuck/pkg-plist << '498dd2134c33e50132a777983b7ec0bf' Xbin/chuck X%%PORTDOCS%%%%DOCSDIR%%/ChucK_manual.pdf X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/README X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/autocorr.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/concat.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/dct.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/features/centroid.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/features/flux.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/features/flux0.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/features/rms.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/features/rolloff.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/features/rolloff2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/features/zerox.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/fft.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/fft1.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/fft2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/fft3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/flip.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/ifft.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/ifft2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/ifft3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/tracking/Smacking.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/tracking/Tracking.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/tracking/harm.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/tracking/pitch-fifth.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/tracking/pitch-seventh.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/tracking/pitch-third.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/tracking/pitch-track.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/win.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/xcorr.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/analysis/xsynth.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/array/array_argument.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/array/array_assign.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/array/array_capacity.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/array/array_dynamic.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/array/array_mdim.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/array/array_mmixed.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/array/array_resize.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/array/array_storage.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/array/array_sub_assign.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/adc.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/adsr.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/alarm.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/args.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/bar.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/blit.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/blit2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/chirp.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/chirp2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/comb.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/curly++.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/curly.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/delay.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/demo0.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/demo1.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/demo2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/demo3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/echo.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/envelope.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/fm.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/fm2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/fm3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/foo.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/foo2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/func.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/i-robot.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/imp.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/infnan.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/larry++.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/larry.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/lfo.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/moe++.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/moe.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/oscillatronx.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/rec-auto-stereo.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/rec-auto.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/rec.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/ring.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/sndbuf.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/step.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/tick.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/tick2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/unchuck.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/valueat.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/whirl.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/whole.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/wind.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/wind2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/basic/zerox.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/README.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/audio/clap_01.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/audio/click_01.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/audio/click_02.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/audio/cowbell_01.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/audio/hihat_01.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/audio/hihat_02.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/audio/hihat_04.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/audio/kick_01.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/audio/kick_04.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/audio/snare_01.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/audio/snare_02.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/audio/snare_03.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/audio/stereo_fx_01.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/audio/stereo_fx_03.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter1/Listing1.1.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter1/Listing1.10.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter1/Listing1.11.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter1/Listing1.12.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter1/Listing1.13.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter1/Listing1.14.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter1/Listing1.15.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter1/Listing1.16.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter1/Listing1.17.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter1/Listing1.18.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter1/Listing1.19.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter1/Listing1.2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter1/Listing1.20.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter1/Listing1.3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter1/Listing1.4.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter1/Listing1.5.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter1/Listing1.6.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter1/Listing1.7.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter1/Listing1.7Shorthand.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter1/Listing1.8.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter1/Listing1.9.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter1/WowExample.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter10/Listing10.1.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter10/Listing10.2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter10/Listing10.3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter10/Listing10.4.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter10/Listing10.4b.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter10/Listing10.4c.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter10/Listing10.5.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter11/Listing11.1.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter11/Listing11.1Twinkle.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter11/Listing11.2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter11/Listing11.3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter11/Listing11.4.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter11/Listing11.5.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter2/Listing2.1.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter2/Listing2.2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter2/Listing2.3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter2/Listing2.4.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter2/Listing2.5.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter2/Listing2.6.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter2/Listing2.7.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter2/Listing2.8.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter3/Listing3.1.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter3/Listing3.2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter3/Listing3.3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter3/Listing3.4.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter3/Listing3.5.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter3/Listing3.6.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter3/Listing3.7.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter3/Listing3.8.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/Listing4.1.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/Listing4.10.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/Listing4.11.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/Listing4.12.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/Listing4.2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/Listing4.3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/Listing4.4.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/Listing4.5.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/Listing4.6.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/Listing4.7.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/Listing4.8.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/Listing4.9.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/audio/clap_01.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/audio/click_01.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/audio/click_02.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/audio/cowbell_01.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/audio/hihat_01.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/audio/hihat_04.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/audio/kick_01.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/audio/snare_01.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/audio/snare_02.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/audio/snare_03.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/audio/stereo_fx_01.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter4/audio/stereo_fx_03.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter5/Listing5.1.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter5/Listing5.10.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter5/Listing5.11.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter5/Listing5.12.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter5/Listing5.13.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter5/Listing5.14.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter5/Listing5.15.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter5/Listing5.16.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter5/Listing5.17.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter5/Listing5.18.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter5/Listing5.19.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter5/Listing5.2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter5/Listing5.20.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter5/Listing5.3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter5/Listing5.4.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter5/Listing5.5.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter5/Listing5.6.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter5/Listing5.7.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter5/Listing5.8.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter5/Listing5.9.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter5/audio/kick_01.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter5/audio/snare_03.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter5/audio/stereo_fx_01.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter6/Listing6.1.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter6/Listing6.10.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter6/Listing6.11.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter6/Listing6.12.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter6/Listing6.13.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter6/Listing6.14.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter6/Listing6.15.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter6/Listing6.2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter6/Listing6.3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter6/Listing6.4.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter6/Listing6.5.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter6/Listing6.6.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter6/Listing6.7.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter6/Listing6.8.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter6/Listing6.9.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter7/Listing7.1.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter7/Listing7.10.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter7/Listing7.11.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter7/Listing7.12.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter7/Listing7.13.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter7/Listing7.14.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter7/Listing7.15.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter7/Listing7.16.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter7/Listing7.17.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter7/Listing7.18.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter7/Listing7.2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter7/Listing7.3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter7/Listing7.4.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter7/Listing7.5.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter7/Listing7.6.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter7/Listing7.7.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter7/Listing7.8.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter7/Listing7.9.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter7/TryThis.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter8/Listing8.1.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter8/Listing8.10.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter8/Listing8.2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter8/Listing8.3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter8/Listing8.4.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter8/Listing8.5.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter8/Listing8.6.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter8/Listing8.7.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter8/Listing8.8.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter8/Listing8.9.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter8/audio/hihat_01.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter8/audio/kick_01.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter8/bass.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter8/drums.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter8/flute.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter8/initialize.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter8/piano.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter8/score.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/BPM.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/DrumMachine/BPM.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/DrumMachine/Listing9.11.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/DrumMachine/Listing9.12.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/DrumMachine/Listing9.13.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/DrumMachine/Listing9.14.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/DrumMachine/Listing9.15.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/DrumMachine/Listing9.16.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/DrumMachine/Listing9.17.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/DrumMachine/audio/clap_01.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/DrumMachine/audio/cowbell_01.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/DrumMachine/audio/hihat_02.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/DrumMachine/audio/kick_04.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/DrumMachine/audio/snare_01.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/DrumMachine/clap.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/DrumMachine/cowbell.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/DrumMachine/hihat.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/DrumMachine/initialize.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/DrumMachine/kick.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/DrumMachine/score.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/DrumMachine/snare.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/Listing9.1.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/Listing9.10.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/Listing9.18.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/Listing9.19.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/Listing9.2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/Listing9.3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/Listing9.4.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/Listing9.5.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/Listing9.6.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/Listing9.7.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/Listing9.8.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/Listing9.9.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/MyScore.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/SmartMandolin/Listing9.20.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/SmartMandolin/Listing9.21.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/SmartMandolin/Listing9.22.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/SmartMandolin/MandoPlayer.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/SmartMandolin/MandoScore.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/SmartMandolin/initialize.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/UseBPM.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/UseBPM2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/UseBPM3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/book/digital-artists/chapter9/initialize.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/class/dinky.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/class/try.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ctrl/ctrl_break.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ctrl/ctrl_break_nested.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ctrl/ctrl_continue.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ctrl/ctrl_do_until.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ctrl/ctrl_for_loop.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ctrl/ctrl_sequencer.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ctrl/ctrl_until.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/data/hihat-open.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/data/hihat.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/data/kick.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/data/snare-chili.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/data/snare-hop.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/data/snare.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/deep/chant.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/deep/dither.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/deep/dtmf.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/deep/follower.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/deep/plu.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/deep/plu2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/deep/plu3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/deep/say-chu.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/deep/shepard.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/deep/thx.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/deep/unclap.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/event/ALSO-SEE X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/event/broadcast.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/event/clix.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/event/clix2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/event/clix3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/event/event-extend.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/event/kb.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/event/kb2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/event/signal.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/event/signal4.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/extend/chugen.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/extend/chugraph.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/filter/bpf.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/filter/brf.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/filter/hpf.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/filter/lpf.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/filter/resonz.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/filter/rhpf.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/filter/rlpf.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/func/func_doozey.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/func/func_obj_arg.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/func/func_overload.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/func/func_overload_member.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/func/func_overload_multi.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/func/func_recursion.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/func/func_void.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/gain_example.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/hanoi++.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/hanoi.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/hanoi2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/hanoi3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/help.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/hid/joy-fm.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/hid/joy-noise.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/hid/joy-shake.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/hid/joy.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/hid/kb.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/hid/keyboard-organ.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/hid/mouse-fm.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/hid/mouse.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/hid/tilt.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/impulse_example.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/io/chout.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/io/int.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/io/line.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/io/read-int.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/io/read-line.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/io/read-str.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/io/str.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/io/write.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/io/write2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/midi/bwv772.mid X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/midi/gomidi.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/midi/gomidi2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/midi/midifile-play.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/midi/midiout.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/midi/polyfony.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/midi/polyfony2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/multi/i.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/multi/n.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/multi/we-robot.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/oper/oper_arith_chuck.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/oper/oper_assign.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/oper/oper_logic.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/oper/oper_post_inc.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/oper/oper_pre_inc.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/osc/osc-dump.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/osc/r.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/osc/s.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/otf_01.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/otf_02.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/otf_03.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/otf_04.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/otf_05.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/otf_06.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/otf_07.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/serial/arduino/ckserial.ino X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/serial/byte.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/serial/bytes.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/serial/ints-ascii.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/serial/ints-bin.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/serial/lines.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/serial/list.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/serial/write-bytes.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/serial/write.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/shred/dir.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/shred/machine.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/shred/powerup.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/shred/spork.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/shred/spork2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/Dyno-compress.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/Dyno-duck.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/Dyno-limit.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/Gen10-test.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/Gen17-test.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/Gen5-test.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/Gen7-test.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/Gen9-test.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/GenX-CurveTable-test.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/GenX-WarpTable-test.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/LiSa-load.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/LiSa-munger1.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/LiSa-munger2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/LiSa-munger3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/LiSa-simplelooping.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/LiSa-track1.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/LiSa-track2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/LiSa-track3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/LiSa-track4.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/LiSa-track5.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/LiSa-trigger.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/geetar.wav X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/readme-GenX.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/readme-LiSa1.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/readme-LiSa2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/twilight/twilight-granular-kb-interp.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/twilight/twilight-granular-kb.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/special/twilight/twilight-granular.aiff X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/status.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/step_example.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stereo/array.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stereo/curly2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stereo/larry2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stereo/moe2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stereo/powerup2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stereo/stereo-noise.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stereo/ugen-array.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/band-o-matic.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/bandedwg.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/bandedwg2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/blowbotl.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/blowbotl2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/blowhole.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/blowhole2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/bowed.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/bowed2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/brass.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/brass2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/clarinet.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/clarinet2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/flute.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/frenchrn-algo2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/hevymetl-acoustic-algo3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/hevymetl-algo3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/hevymetl-trumpet-algo3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/honkeytonk-algo1.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/krstlchr-algo7.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/mand-o-matic-simple.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/mand-o-matic.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/mandolin.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/modalbar.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/modalbar2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/mode-o-matic.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/mode-o-test.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/modulate.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/moog.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/moog2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/moogie.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/rhodey-song.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/rhodey.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/saxofony.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/shake-cycle.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/shake-o-matic.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/sitar.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/stif-o-karp.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/stifkarp.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/voic-o-form.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/wurley.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/wurley2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/stk/wurley3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/string/escape.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/string/readline.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/string/strops.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/string/strops2.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/string/token.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/string/tostr.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/time/time_operations.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/time/time_types.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/type/type_analysis.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/type/type_object.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/type/type_primitives.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/uana/uana_auto_correlation.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/uana/uana_cross_correlation.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/uana/uana_dct.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/uana/uana_feature_collector.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/uana/uana_fft.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/uana/uana_flip.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/uana/uana_idct.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/uana/uana_ifft.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/uana/uana_pilf.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/uana/uana_spectral_centroid.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/uana/uana_spectral_flux.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/uana/uana_spectral_rms.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/uana/uana_spectral_rolloff.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/uana/uana_uana.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/uana/uana_uanablob.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/uana/uana_window.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/uana/uana_zero_crossings.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/ADSR.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/BandedWG.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/BeeThree.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/BiQuad.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/BlowBotl.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/BlowHole.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/Bowed.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/Brass.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/Chorus.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/Clarinet.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/Curvetable.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/Delay.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/DelayA.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/DelayL.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/Echo.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/Envelope.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/FM.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/FMVoices.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/Filter.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/Flute.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/Gen10.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/Gen17.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/Gen5.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/Gen7.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/Gen9.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/GenX.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/HevyMetl.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/JCRev.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/LiSa.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/Mandolin.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/ModalBar.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/Modulate.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/Moog.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/NRev.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/OnePole.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/OneZero.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/PRCRev.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/PercFlut.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/PitShift.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/PoleZero.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/Rhodey.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/STKinstrument.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/Saxofony.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/Shakers.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/Sitar.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/StifKarp.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/SubNoise.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/TubeBell.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/TwoPole.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/TwoZero.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/VoicForm.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/Wurley.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/WvIn.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/WvLoop.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/ugen/WvOut.txt X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/vector/interpolate.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/vector/vec3.ck X%%PORTEXAMPLES%%%%EXAMPLESDIR%%/vector/vec4.ck 498dd2134c33e50132a777983b7ec0bf exit