FreeBSD Bugzilla – Attachment 229566 Details for
Bug 259911
[NEW PORT]: audio/chuck: Strongly-timed, Concurrent, and On-the-fly Music Programming Language
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
audio_chuck.shar
audio_chuck.shar (text/plain), 420.48 KB, created by
Timothy Beyer
on 2021-11-18 07:28:07 UTC
(
hide
)
Description:
audio_chuck.shar
Filename:
MIME Type:
Creator:
Timothy Beyer
Created:
2021-11-18 07:28:07 UTC
Size:
420.48 KB
patch
obsolete
># 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 <beyert@cs.ucr.edu> >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 <bsd.port.options.mk> >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 <bsd.port.mk> >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 <termios.h> >X static struct termios g_save; >X+#elif defined(__PLATFORM_FREEBSD__) >X+ #include <termios.h> >X+ static struct termios g_save; >X #else >X #include <termio.h> >X static struct termio g_save; >X@@ -102,8 +105,8 @@ void io_addhistory( const char * addme ) >X >X #include <unistd.h> >X #include <sys/ioctl.h> >X-#else >X- #include <conio.h> >X+//#else >X+// #include <conio.h> >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<RtAudio::A >X #if defined(__LINUX_ALSA__) >X apis.push_back( LINUX_ALSA ); >X #endif >X+#if defined(__FREEBSD_ALSA__) >X+ apis.push_back( FREEBSD_ALSA ); >X+#endif >X #if defined(__LINUX_PULSE__) >X apis.push_back( LINUX_PULSE ); >X #endif >X+#if defined(__FREEBSD_OSS__) >X+ apis.push_back( FREEBSD_OSS ); >X+#endif >X #if defined(__LINUX_OSS__) >X apis.push_back( LINUX_OSS ); >X #endif >X@@ -121,10 +127,18 @@ void RtAudio :: openRtApi( RtAudio::Api api ) >X if ( api == LINUX_ALSA ) >X rtapi_ = new RtApiAlsa(); >X #endif >X+#if defined(__FREEBSD_ALSA__) >X+ if ( api == FREEBSD_ALSA ) >X+ rtapi_ = new RtApiAlsa(); >X+#endif >X #if defined(__LINUX_PULSE__) >X if ( api == LINUX_PULSE ) >X rtapi_ = new RtApiPulse(); >X #endif >X+#if defined(__FREEBSD_OSS__) >X+ if ( api == FREEBSD_OSS ) >X+ rtapi_ = new RtApiOss(); >X+#endif >X #if defined(__LINUX_OSS__) >X if ( api == LINUX_OSS ) >X rtapi_ = new RtApiOss(); >X@@ -5262,7 +5276,7 @@ static const char* getErrorString( int code ) >X #endif >X >X >X-#if defined(__LINUX_ALSA__) >X+#if defined(__LINUX_ALSA__) || defined(__FREEBSD_ALSA__) >X >X #include <alsa/asoundlib.h> >X #include <unistd.h> >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 <unistd.h> >X #include <sys/ioctl.h> >X #include <unistd.h> >X #include <fcntl.h> >X+#if defined(__FREEBSD_OSS__) >X+#include <sys/soundcard.h> >X+#elif defined(__LINUX_OSS__) >X #include "soundcard.h" >X+#endif >X #include <errno.h> >X #include <math.h> >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 <pthread.h> >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 <linux/limits.h> >X #endif // __PLATFORM_LINUX__ >X >X+#ifdef __PLATFORM_FREEBSD__ >X+#include <limits.h> >X+#endif >X+ >X #include <stdio.h> >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 <sys/stat.h> >X #include <unistd.h> >X #include <dirent.h> >X+#ifdef __PLATFORM_LINUX__ >X #include <linux/unistd.h> >X+#endif >X #include <string.h> >X #include <fcntl.h> >X #include <sys/poll.h> >X@@ -6326,6 +6328,9 @@ Linux general HID support >X #include <errno.h> >X #include <linux/joystick.h> >X #include <linux/input.h> >X+#ifdef __PLATFORM_FREEBSD__ >X+#include <linux/types.h> >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 <exception> >X #include <iostream> >X #include <string> >X+#include <vector> >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 <string> >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<RtMidi::Api> &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 <vector> >X-#include <queue> >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<unsigned char> *message, void *userData); >X+ typedef void (*RtMidiCallback)( double timeStamp, std::vector<unsigned char> *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<unsigned char> *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<unsigned char> 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<MidiMessage> 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<unsigned char> *message ); >X+ void sendMessage( const std::vector<unsigned char> *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<unsigned char> *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<unsigned char> 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<unsigned char>*, 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<MidiInApi *>(rtapi_)->setCallback( callback, userData ); } >X+inline void RtMidiIn :: cancelCallback( void ) { static_cast<MidiInApi *>(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<MidiInApi *>(rtapi_)->ignoreTypes( midiSysex, midiTime, midiSense ); } >X+inline double RtMidiIn :: getMessage( std::vector<unsigned char> *message ) { return static_cast<MidiInApi *>(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<unsigned char> *message ) { static_cast<MidiOutApi *>(rtapi_)->sendMessage( &message->at(0), message->size() ); } >X+inline void RtMidiOut :: sendMessage( const unsigned char *message, size_t size ) { static_cast<MidiOutApi *>(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 <limits.h> >X+#endif >X+ >X #if defined(__MACOSX_CORE__) && !defined(__CHIP_MODE__) >X >X #include <CoreFoundation/CoreFoundation.h> >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 <sys/types.h> >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 <sstream> >X-#include <iostream> >X-#include <stdio.h> >X >X+#if defined(TARGET_OS_IPHONE) >X >X+ #define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime >X+ #define AudioConvertHostTimeToNanos CAHostTimeBase::ConvertToNanos >X+ >X+ #include <mach/mach_time.h> >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<bool b> class StaticAssert { public: StaticAssert() {} }; >X+template<> class StaticAssert<true>{ public: StaticAssert() {} }; >X+class StaticAssertions { StaticAssertions() { >X+ StaticAssert<rtmidi_num_api_names == RtMidi::NUM_APIS>(); >X+}}; >X+ >X+void RtMidi :: getCompiledApi( std::vector<RtMidi::Api> &apis ) throw() >X+{ >X+ apis = std::vector<RtMidi::Api>(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; i<apis.size(); i++ ) { >X+ openMidiApi( apis[i], clientName, queueSizeLimit ); >X+ if ( rtapi_ && rtapi_->getPortCount() ) 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; i<apis.size(); i++ ) { >X+ openMidiApi( apis[i], clientName ); >X+ if ( rtapi_ && rtapi_->getPortCount() ) 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<unsigned char> *message ) >X+double MidiInApi :: getMessage( std::vector<unsigned char> *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<unsigned char> *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<unsigned char> *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 <CoreMIDI/CoreMIDI.h> >X-#include <CoreAudio/HostTime.h> >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 <CoreAudio/HostTime.h> >X+ #include <CoreServices/CoreServices.h> >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<RtMidiIn::RtMidiInData *> (procRef); >X+ MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (procRef); >X CoreMidiData *apiData = static_cast<CoreMidiData *> (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; i<list->numPackets; ++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; j<nBytes; j++ ) >X+ for ( unsigned int j=0; j<nBytes; ++j ) >X message.bytes.push_back( packet->data[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<CoreMidiData *> (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<CoreMidiData *> (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<CoreMidiData *> (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<CoreMidiData *> (apiData_); >X+ CoreMidiData *data = static_cast<CoreMidiData *> (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<CoreMidiData *> (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<nConnected; ++i, ++pid ) { >X+ MIDIUniqueID id = EndianS32_BtoN( *pid ); >X+ MIDIObjectRef connObject; >X+ MIDIObjectType connObjectType; >X+ err = MIDIObjectFindByUniqueID( id, &connObject, &connObjectType ); >X+ if ( err == noErr ) { >X+ if ( connObjectType == kMIDIObjectType_ExternalSource || >X+ connObjectType == kMIDIObjectType_ExternalDestination ) { >X+ // Connected to an external device's endpoint (10.3 and later). >X+ str = EndpointName( (MIDIEndpointRef)(connObject), true ); >X+ } else { >X+ // Connected to an external device (10.2) (or something else, catch- >X+ str = NULL; >X+ MIDIObjectGetStringProperty( connObject, kMIDIPropertyName, &str ); >X+ } >X+ if ( str != NULL ) { >X+ if ( anyStrings ) >X+ CFStringAppend( result, CFSTR(", ") ); >X+ else >X+ anyStrings = true; >X+ CFStringAppend( result, str ); >X+ CFRelease( str ); >X+ } >X+ } >X+ } >X+ } >X+ CFRelease( connections ); >X+ } >X+ if ( anyStrings ) >X+ return result; >X+ >X+ CFRelease( result ); >X+ >X+ // Here, either the endpoint had no connections, or we failed to obtain names >X+ return EndpointName( endpoint, false ); >X+} >X+ >X+std::string MidiInCore :: getPortName( unsigned int portNumber ) >X+{ >X+ CFStringRef nameRef; >X MIDIEndpointRef portRef; >X- std::ostringstream ost; >X char name[128]; >X >X+ std::string stringName; >X+ CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); >X if ( portNumber >= 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<CoreMidiData *> (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<CoreMidiData *> (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<CoreMidiData *> (apiData_); >X+ CoreMidiData *data = static_cast<CoreMidiData *> (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<CoreMidiData *> (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<unsigned int> (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<CoreMidiData *> (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<unsigned char> *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<CoreMidiData *> (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<unsigned ch >X // API information found at: >X // - http://www.alsa-project.org/documentation.php#Library >X >X-// chuck >X-#if defined(__LINUX_ALSASEQ__) || defined(__PLATFORM_LINUX__) >X+#if defined(__LINUX_ALSA__) || defined(__FREEBSD_ALSA__) || defined(__FREEBSD_OSS__) || defined(__LINUX_OSS__) >X >X // The ALSA Sequencer API is based on the use of a callback function for >X // MIDI input. >X@@ -798,6 +1519,10 @@ void RtMidiOut :: sendMessage( std::vector<unsigned ch >X // Thanks to Pedro Lopez-Cabanillas for help with the ALSA sequencer >X // time stamps and other assorted fixes!!! >X >X+// If you don't need timestamping for incoming MIDI events, define the >X+// preprocessor definition AVOID_TIMESTAMPING to save resources >X+// associated with the ALSA sequencer queues. >X+ >X #include <pthread.h> >X #include <sys/time.h> >X >X@@ -808,32 +1533,38 @@ void RtMidiOut :: sendMessage( std::vector<unsigned ch >X // implementation. >X struct AlsaMidiData { >X snd_seq_t *seq; >X+ unsigned int portNum; >X int vport; >X snd_seq_port_subscribe_t *subscription; >X snd_midi_event_t *coder; >X unsigned int bufferSize; >X unsigned char *buffer; >X pthread_t thread; >X- unsigned long long lastTime; >X+ pthread_t dummy_thread_id; >X+ snd_seq_real_time_t lastTime; >X int queue_id; // an input queue is needed to get timestamped events >X+ int trigger_fds[2]; >X }; >X >X #define PORT_TYPE( pinfo, bits ) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits)) >X >X //*********************************************************************// >X // API: LINUX ALSA >X-// Class Definitions: RtMidiIn >X+// Class Definitions: MidiInAlsa >X //*********************************************************************// >X >X-extern "C" void *alsaMidiHandler( void *ptr ) >X+static void *alsaMidiHandler( void *ptr ) >X { >X- RtMidiIn::RtMidiInData *data = static_cast<RtMidiIn::RtMidiInData *> (ptr); >X+ MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (ptr); >X AlsaMidiData *apiData = static_cast<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (apiData_); >X+ >X if ( connected_ ) { >X- AlsaMidiData *data = static_cast<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (apiData_); >X- return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 ); >X+ AlsaMidiData *data = static_cast<AlsaMidiData *> ( 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<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> ( 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<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (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<unsigned char> *message ) >X-{ >X- int result; >X- AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_); >X- unsigned int nBytes = message->size(); >X+ unsigned int nBytes = static_cast<unsigned int> (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; i<nBytes; i++ ) data->buffer[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; i<nBytes; ++i ) data->buffer[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 <pthread.h> >X-#include <sys/time.h> >X-#include <unistd.h> >X- >X-// Irix MIDI header file. >X-#include <dmedia/midi.h> >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<RtMidiIn::RtMidiInData *> (ptr); >X- IrixMidiData *apiData = static_cast<IrixMidiData *> (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; i<event.msglen; i++ ) >X- message.bytes.push_back( event.sysexmsg[i] ); >X- if ( event.sysexmsg[event.msglen-1] == 0xF7 ) continueSysex = false; >X- if ( !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- 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<IrixMidiData *> (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<IrixMidiData *> (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<IrixMidiData *> (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<IrixMidiData *> (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<IrixMidiData *> (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<IrixMidiData *> (apiData_); >X- delete data; >X-} >X- >X-void RtMidiOut :: sendMessage( std::vector<unsigned char> *message ) >X-{ >X- int result; >X- MDevent event; >X- IrixMidiData *data = static_cast<IrixMidiData *> (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; i<nBytes; i++ ) buffer[i] = message->at(i); >X- event.sysexmsg = buffer; >X- } >X- else { >X- for ( int i=0; i<nBytes; i++ ) >X- event.msg[i] = message->at(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<unsigned ch >X #include <windows.h> >X #include <mmsystem.h> >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<RtMidiIn::RtMidiInData *> (instancePtr); >X- RtMidiIn::RtMidiInData *data = (RtMidiIn::RtMidiInData *)instancePtr; >X+ //MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (instancePtr); >X+ MidiInApi::RtMidiInData *data = (MidiInApi::RtMidiInData *)instancePtr; >X WinMidiData *apiData = static_cast<WinMidiData *> (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; i<nBytes; i++ ) apiData->message.bytes.push_back( *ptr++ ); >X+ for ( int i=0; i<nBytes; ++i ) apiData->message.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<WinMidiData *> (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<WinMidiData *> (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; i<RT_SYSEX_BUFFER_COUNT; ++i ) { >X+ data->sysexBuffer[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<WinMidiData *> (apiData_); >X+ EnterCriticalSection( &(data->_mutex) ); >X midiInReset( data->inHandle ); >X midiInStop( data->inHandle ); >X+ >X+ for ( int i=0; i<RT_SYSEX_BUFFER_COUNT; ++i ) { >X+ int result = midiInUnprepareHeader(data->inHandle, 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<WinMidiData *> (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<WinMidiData *> (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<WinMidiData *> (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<WinMidiData *> (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<WinMidiData *> (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<unsigned char> *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<unsigned int>(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<WinMidiData *> (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; i<nBytes; i++ ) buffer[i] = message->at(i); >X+ for ( unsigned int i=0; i<nBytes; ++i ) buffer[i] = message[i]; >X >X // Create and prepare MIDIHDR structure. >X MIDIHDR sysex; >X sysex.lpData = (LPSTR) buffer; >X sysex.dwBufferLength = nBytes; >X sysex.dwFlags = 0; >X- result = midiOutPrepareHeader( data->outHandle, &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; i<nBytes; i++ ) { >X- *ptr = message->at(i); >X- ptr++; >X+ for ( unsigned int i=0; i<nBytes; ++i ) { >X+ *ptr = message[i]; >X+ ++ptr; >X } >X >X // Send the message immediately. >X result = midiOutShortMsg( data->outHandle, 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<unsigned ch >X #endif // __WINDOWS_MM__ >X >X >X-#if defined(__LINUX_OSS__) // dummy >X+//*********************************************************************// >X+// API: UNIX JACK >X+// >X+// Written primarily by Alexander Svetalkin, with updates for delta >X+// time by Gary Scavone, April 2011. >X+// >X+// *********************************************************************// >X >X-/*void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef ) >X+#if defined(__UNIX_JACK__) >X+ >X+// JACK header files >X+#include <jack/jack.h> >X+#include <jack/midiport.h> >X+#include <jack/ringbuffer.h> >X+#ifdef HAVE_SEMAPHORE >X+ #include <semaphore.h> >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<JackMidiData *> (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<JackMidiData *> (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<JackMidiData *> (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<JackMidiData *> (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<JackMidiData *> (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<JackMidiData *> (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<portNumber && ports[i]; i++ ) {} >X+ if ( i < portNumber || !ports[portNumber] ) { >X+ std::ostringstream ost; >X+ ost << "MidiInJack::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+ jack_free( ports ); >X+ return retStr; >X } >X >X+void MidiInJack :: closePort() >X+{ >X+ JackMidiData *data = static_cast<JackMidiData *> (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<JackMidiData *> (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<JackMidiData *> (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<JackMidiData *> (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<JackMidiData *> (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<JackMidiData *> (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<unsigned char> *message ) >X+unsigned int MidiOutJack :: getPortCount() >X { >X+ int count = 0; >X+ JackMidiData *data = static_cast<JackMidiData *> (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<JackMidiData *> (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<JackMidiData *> (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<JackMidiData *> (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<int>(size); >X+ JackMidiData *data = static_cast<JackMidiData *> (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 <sstream> >X-#include <iostream> >X-#include <stdio.h> >X >X+#if defined(TARGET_OS_IPHONE) >X >X+ #define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime >X+ #define AudioConvertHostTimeToNanos CAHostTimeBase::ConvertToNanos >X+ >X+ #include <mach/mach_time.h> >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<bool b> class StaticAssert { public: StaticAssert() {} }; >X+template<> class StaticAssert<true>{ public: StaticAssert() {} }; >X+class StaticAssertions { StaticAssertions() { >X+ StaticAssert<rtmidi_num_api_names == RtMidi::NUM_APIS>(); >X+}}; >X+ >X+void RtMidi :: getCompiledApi( std::vector<RtMidi::Api> &apis ) throw() >X+{ >X+ apis = std::vector<RtMidi::Api>(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; i<apis.size(); i++ ) { >X+ openMidiApi( apis[i], clientName, queueSizeLimit ); >X+ if ( rtapi_ && rtapi_->getPortCount() ) 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; i<apis.size(); i++ ) { >X+ openMidiApi( apis[i], clientName ); >X+ if ( rtapi_ && rtapi_->getPortCount() ) 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<unsigned char> *message ) >X+double MidiInApi :: getMessage( std::vector<unsigned char> *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<unsigned char> *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<unsigned char> *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 <CoreMIDI/CoreMIDI.h> >X-#include <CoreAudio/HostTime.h> >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 <CoreAudio/HostTime.h> >X+ #include <CoreServices/CoreServices.h> >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<RtMidiIn::RtMidiInData *> (procRef); >X+ MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (procRef); >X CoreMidiData *apiData = static_cast<CoreMidiData *> (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; i<list->numPackets; ++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; j<nBytes; j++ ) >X+ for ( unsigned int j=0; j<nBytes; ++j ) >X message.bytes.push_back( packet->data[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<CoreMidiData *> (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<CoreMidiData *> (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<CoreMidiData *> (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<CoreMidiData *> (apiData_); >X+ CoreMidiData *data = static_cast<CoreMidiData *> (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<CoreMidiData *> (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<nConnected; ++i, ++pid ) { >X+ MIDIUniqueID id = EndianS32_BtoN( *pid ); >X+ MIDIObjectRef connObject; >X+ MIDIObjectType connObjectType; >X+ err = MIDIObjectFindByUniqueID( id, &connObject, &connObjectType ); >X+ if ( err == noErr ) { >X+ if ( connObjectType == kMIDIObjectType_ExternalSource || >X+ connObjectType == kMIDIObjectType_ExternalDestination ) { >X+ // Connected to an external device's endpoint (10.3 and later). >X+ str = EndpointName( (MIDIEndpointRef)(connObject), true ); >X+ } else { >X+ // Connected to an external device (10.2) (or something else, catch- >X+ str = NULL; >X+ MIDIObjectGetStringProperty( connObject, kMIDIPropertyName, &str ); >X+ } >X+ if ( str != NULL ) { >X+ if ( anyStrings ) >X+ CFStringAppend( result, CFSTR(", ") ); >X+ else >X+ anyStrings = true; >X+ CFStringAppend( result, str ); >X+ CFRelease( str ); >X+ } >X+ } >X+ } >X+ } >X+ CFRelease( connections ); >X+ } >X+ if ( anyStrings ) >X+ return result; >X+ >X+ CFRelease( result ); >X+ >X+ // Here, either the endpoint had no connections, or we failed to obtain names >X+ return EndpointName( endpoint, false ); >X+} >X+ >X+std::string MidiInCore :: getPortName( unsigned int portNumber ) >X+{ >X+ CFStringRef nameRef; >X MIDIEndpointRef portRef; >X- std::ostringstream ost; >X char name[128]; >X >X+ std::string stringName; >X+ CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); >X if ( portNumber >= 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<CoreMidiData *> (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<CoreMidiData *> (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<CoreMidiData *> (apiData_); >X+ CoreMidiData *data = static_cast<CoreMidiData *> (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<CoreMidiData *> (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<unsigned int> (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<CoreMidiData *> (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<unsigned char> *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<CoreMidiData *> (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<unsigned ch >X // API information found at: >X // - http://www.alsa-project.org/documentation.php#Library >X >X-// chuck >X-#if defined(__LINUX_ALSASEQ__) || defined(__PLATFORM_LINUX__) >X+#if defined(__LINUX_ALSA__) || defined(__FREEBSD_ALSA__) >X >X // The ALSA Sequencer API is based on the use of a callback function for >X // MIDI input. >X@@ -798,6 +1511,10 @@ void RtMidiOut :: sendMessage( std::vector<unsigned ch >X // Thanks to Pedro Lopez-Cabanillas for help with the ALSA sequencer >X // time stamps and other assorted fixes!!! >X >X+// If you don't need timestamping for incoming MIDI events, define the >X+// preprocessor definition AVOID_TIMESTAMPING to save resources >X+// associated with the ALSA sequencer queues. >X+ >X #include <pthread.h> >X #include <sys/time.h> >X >X@@ -808,32 +1525,38 @@ void RtMidiOut :: sendMessage( std::vector<unsigned ch >X // implementation. >X struct AlsaMidiData { >X snd_seq_t *seq; >X+ unsigned int portNum; >X int vport; >X snd_seq_port_subscribe_t *subscription; >X snd_midi_event_t *coder; >X unsigned int bufferSize; >X unsigned char *buffer; >X pthread_t thread; >X- unsigned long long lastTime; >X+ pthread_t dummy_thread_id; >X+ snd_seq_real_time_t lastTime; >X int queue_id; // an input queue is needed to get timestamped events >X+ int trigger_fds[2]; >X }; >X >X #define PORT_TYPE( pinfo, bits ) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits)) >X >X //*********************************************************************// >X // API: LINUX ALSA >X-// Class Definitions: RtMidiIn >X+// Class Definitions: MidiInAlsa >X //*********************************************************************// >X >X-extern "C" void *alsaMidiHandler( void *ptr ) >X+static void *alsaMidiHandler( void *ptr ) >X { >X- RtMidiIn::RtMidiInData *data = static_cast<RtMidiIn::RtMidiInData *> (ptr); >X+ MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (ptr); >X AlsaMidiData *apiData = static_cast<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (apiData_); >X+ >X if ( connected_ ) { >X- AlsaMidiData *data = static_cast<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (apiData_); >X- return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 ); >X+ AlsaMidiData *data = static_cast<AlsaMidiData *> ( 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<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> ( 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<AlsaMidiData *> (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<AlsaMidiData *> (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<AlsaMidiData *> (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<unsigned char> *message ) >X-{ >X- int result; >X- AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_); >X- unsigned int nBytes = message->size(); >X+ unsigned int nBytes = static_cast<unsigned int> (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; i<nBytes; i++ ) data->buffer[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; i<nBytes; ++i ) data->buffer[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 <pthread.h> >X-#include <sys/time.h> >X-#include <unistd.h> >X- >X-// Irix MIDI header file. >X-#include <dmedia/midi.h> >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<RtMidiIn::RtMidiInData *> (ptr); >X- IrixMidiData *apiData = static_cast<IrixMidiData *> (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; i<event.msglen; i++ ) >X- message.bytes.push_back( event.sysexmsg[i] ); >X- if ( event.sysexmsg[event.msglen-1] == 0xF7 ) continueSysex = false; >X- if ( !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- 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<IrixMidiData *> (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<IrixMidiData *> (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<IrixMidiData *> (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<IrixMidiData *> (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<IrixMidiData *> (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<IrixMidiData *> (apiData_); >X- delete data; >X-} >X- >X-void RtMidiOut :: sendMessage( std::vector<unsigned char> *message ) >X-{ >X- int result; >X- MDevent event; >X- IrixMidiData *data = static_cast<IrixMidiData *> (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; i<nBytes; i++ ) buffer[i] = message->at(i); >X- event.sysexmsg = buffer; >X- } >X- else { >X- for ( int i=0; i<nBytes; i++ ) >X- event.msg[i] = message->at(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<unsigned ch >X #include <windows.h> >X #include <mmsystem.h> >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<RtMidiIn::RtMidiInData *> (instancePtr); >X- RtMidiIn::RtMidiInData *data = (RtMidiIn::RtMidiInData *)instancePtr; >X+ //MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (instancePtr); >X+ MidiInApi::RtMidiInData *data = (MidiInApi::RtMidiInData *)instancePtr; >X WinMidiData *apiData = static_cast<WinMidiData *> (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; i<nBytes; i++ ) apiData->message.bytes.push_back( *ptr++ ); >X+ for ( int i=0; i<nBytes; ++i ) apiData->message.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<WinMidiData *> (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<WinMidiData *> (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; i<RT_SYSEX_BUFFER_COUNT; ++i ) { >X+ data->sysexBuffer[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<WinMidiData *> (apiData_); >X+ EnterCriticalSection( &(data->_mutex) ); >X midiInReset( data->inHandle ); >X midiInStop( data->inHandle ); >X+ >X+ for ( int i=0; i<RT_SYSEX_BUFFER_COUNT; ++i ) { >X+ int result = midiInUnprepareHeader(data->inHandle, 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<WinMidiData *> (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<WinMidiData *> (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<WinMidiData *> (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<WinMidiData *> (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<WinMidiData *> (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<unsigned char> *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<unsigned int>(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<WinMidiData *> (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; i<nBytes; i++ ) buffer[i] = message->at(i); >X+ for ( unsigned int i=0; i<nBytes; ++i ) buffer[i] = message[i]; >X >X // Create and prepare MIDIHDR structure. >X MIDIHDR sysex; >X sysex.lpData = (LPSTR) buffer; >X sysex.dwBufferLength = nBytes; >X sysex.dwFlags = 0; >X- result = midiOutPrepareHeader( data->outHandle, &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; i<nBytes; i++ ) { >X- *ptr = message->at(i); >X- ptr++; >X+ for ( unsigned int i=0; i<nBytes; ++i ) { >X+ *ptr = message[i]; >X+ ++ptr; >X } >X >X // Send the message immediately. >X result = midiOutShortMsg( data->outHandle, 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<unsigned ch >X #endif // __WINDOWS_MM__ >X >X >X-#if defined(__LINUX_OSS__) // dummy >X+//*********************************************************************// >X+// API: UNIX JACK >X+// >X+// Written primarily by Alexander Svetalkin, with updates for delta >X+// time by Gary Scavone, April 2011. >X+// >X+// *********************************************************************// >X >X-/*void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef ) >X+#if defined(__UNIX_JACK__) || defined(__FREEBSD_OSS__) || defined(__LINUX_OSS__) >X+ >X+// JACK header files >X+#include <jack/jack.h> >X+#include <jack/midiport.h> >X+#include <jack/ringbuffer.h> >X+#ifdef HAVE_SEMAPHORE >X+ #include <semaphore.h> >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<JackMidiData *> (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<JackMidiData *> (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<JackMidiData *> (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<JackMidiData *> (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<JackMidiData *> (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<JackMidiData *> (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<portNumber && ports[i]; i++ ) {} >X+ if ( i < portNumber || !ports[portNumber] ) { >X+ std::ostringstream ost; >X+ ost << "MidiInJack::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+ jack_free( ports ); >X+ return retStr; >X } >X >X+void MidiInJack :: closePort() >X+{ >X+ JackMidiData *data = static_cast<JackMidiData *> (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<JackMidiData *> (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<JackMidiData *> (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<JackMidiData *> (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<JackMidiData *> (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<JackMidiData *> (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<unsigned char> *message ) >X+unsigned int MidiOutJack :: getPortCount() >X { >X+ int count = 0; >X+ JackMidiData *data = static_cast<JackMidiData *> (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<JackMidiData *> (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<JackMidiData *> (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<JackMidiData *> (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<int>(size); >X+ JackMidiData *data = static_cast<JackMidiData *> (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 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 259911
: 229566