From 88a60d510afa49c247f6d5ffb102a45b1a460003 Mon Sep 17 00:00:00 2001 From: Gary Scavone Date: Fri, 11 Oct 2013 14:49:11 +0200 Subject: Version 1.0.9 --- RtMidi.cpp | 165 ++++++++++++++++++++++++++++++++++++++++++++--- RtMidi.h | 2 +- doc/doxygen/Doxyfile | 2 +- doc/doxygen/tutorial.txt | 19 +++++- doc/release.txt | 4 ++ 5 files changed, 177 insertions(+), 15 deletions(-) diff --git a/RtMidi.cpp b/RtMidi.cpp index c2ebefd..8e05db9 100644 --- a/RtMidi.cpp +++ b/RtMidi.cpp @@ -35,7 +35,7 @@ */ /**********************************************************************/ -// RtMidi: Version 1.0.8 +// RtMidi: Version 1.0.9 #include "RtMidi.h" #include @@ -155,7 +155,7 @@ RtMidiOut :: RtMidiOut( const std::string clientName ) : RtMidi() //*********************************************************************// // API information found at: -// - http://developer. apple .com/audio/pdf/coreaudio.pdf +// - http://developer.apple.com/audio/pdf/coreaudio.pdf #if defined(__MACOSX_CORE__) @@ -434,6 +434,130 @@ unsigned int RtMidiIn :: getPortCount() return MIDIGetNumberOfSources(); } +// This function was submitted by Douglas Casey Tucker and apparently +// derived largely from PortMidi. +CFStringRef EndpointName( MIDIEndpointRef endpoint, bool isExternal ) +{ + CFMutableStringRef result = CFStringCreateMutable( NULL, 0 ); + CFStringRef str; + + // Begin with the endpoint's name. + str = NULL; + MIDIObjectGetStringProperty( endpoint, kMIDIPropertyName, &str ); + if ( str != NULL ) { + CFStringAppend( result, str ); + CFRelease( str ); + } + + MIDIEntityRef entity = NULL; + MIDIEndpointGetEntity( endpoint, &entity ); + if ( entity == NULL ) + // probably virtual + return result; + + if ( CFStringGetLength( result ) == 0 ) { + // endpoint name has zero length -- try the entity + str = NULL; + MIDIObjectGetStringProperty( entity, kMIDIPropertyName, &str ); + if ( str != NULL ) { + CFStringAppend( result, str ); + CFRelease( str ); + } + } + // now consider the device's name + MIDIDeviceRef device = NULL; + MIDIEntityGetDevice( entity, &device ); + if ( device == NULL ) + return result; + + str = NULL; + MIDIObjectGetStringProperty( device, kMIDIPropertyName, &str ); + if ( CFStringGetLength( result ) == 0 ) { + CFRelease( result ); + return str; + } + if ( str != NULL ) { + // if an external device has only one entity, throw away + // the endpoint name and just use the device name + if ( isExternal && MIDIDeviceGetNumberOfEntities( device ) < 2 ) { + CFRelease( result ); + return str; + } else { + if ( CFStringGetLength( str ) == 0 ) { + CFRelease( str ); + return result; + } + // does the entity name already start with the device name? + // (some drivers do this though they shouldn't) + // if so, do not prepend + if ( CFStringCompareWithOptions( result, /* endpoint name */ + str /* device name */, + CFRangeMake(0, CFStringGetLength( str ) ), 0 ) != kCFCompareEqualTo ) { + // prepend the device name to the entity name + if ( CFStringGetLength( result ) > 0 ) + CFStringInsert( result, 0, CFSTR(" ") ); + CFStringInsert( result, 0, str ); + } + CFRelease( str ); + } + } + return result; +} + +// This function was submitted by Douglas Casey Tucker and apparently +// derived largely from PortMidi. +static CFStringRef ConnectedEndpointName( MIDIEndpointRef endpoint ) +{ + CFMutableStringRef result = CFStringCreateMutable( NULL, 0 ); + CFStringRef str; + OSStatus err; + int i; + + // Does the endpoint have connections? + CFDataRef connections = NULL; + int nConnected = 0; + bool anyStrings = false; + err = MIDIObjectGetDataProperty( endpoint, kMIDIPropertyConnectionUniqueID, &connections ); + if ( connections != NULL ) { + // It has connections, follow them + // Concatenate the names of all connected devices + nConnected = CFDataGetLength( connections ) / sizeof(MIDIUniqueID); + if ( nConnected ) { + const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections)); + for ( i=0; i *message ) // Thanks to Pedro Lopez-Cabanillas for help with the ALSA sequencer // time stamps and other assorted fixes!!! +// If you don't need timestamping for incoming MIDI events, define the +// preprocessor definition AVOID_TIMESTAMPING to save resources +// associated with the ALSA sequencer queues. + #include #include @@ -762,13 +894,12 @@ extern "C" void *alsaMidiHandler( void *ptr ) case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: #if defined(__RTMIDI_DEBUG__) std::cerr << "RtMidiIn::alsaMidiHandler: port connection has closed!\n"; - // FIXME: this is called for all unsubscribe events, even ones - //not related to this particular connection. As it stands, I - //see no data provided in the "source" and "dest" fields so - //there is nothing we can do about this at this time. - // std::cout << "sender = " << ev->source.client << ", dest = " << ev->dest.port << std::endl; + std::cout << "sender = " << (int) ev->data.connect.sender.client << ":" + << (int) ev->data.connect.sender.port + << ", dest = " << (int) ev->data.connect.dest.client << ":" + << (int) ev->data.connect.dest.port + << std::endl; #endif - //data->doInput = false; break; case SND_SEQ_EVENT_QFRAME: // MIDI time code @@ -878,6 +1009,7 @@ void RtMidiIn :: initialize( const std::string& clientName ) inputData_.apiData = (void *) data; // Create the input queue +#ifndef AVOID_TIMESTAMPING data->queue_id = snd_seq_alloc_named_queue(seq, "RtMidi Queue"); // Set arbitrary tempo (mm=100) and resolution (240) snd_seq_queue_tempo_t *qtempo; @@ -886,6 +1018,7 @@ void RtMidiIn :: initialize( const std::string& clientName ) snd_seq_queue_tempo_set_ppq(qtempo, 240); snd_seq_set_queue_tempo(data->seq, data->queue_id, qtempo); snd_seq_drain_output(data->seq); +#endif } // This function is used to count or get the pinfo structure for a given port number. @@ -957,9 +1090,11 @@ void RtMidiIn :: openPort( unsigned int portNumber, const std::string portName ) SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION ); snd_seq_port_info_set_midi_channels(pinfo, 16); +#ifndef AVOID_TIMESTAMPING snd_seq_port_info_set_timestamping(pinfo, 1); snd_seq_port_info_set_timestamp_real(pinfo, 1); snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id); +#endif snd_seq_port_info_set_name(pinfo, portName.c_str() ); data->vport = snd_seq_create_port(data->seq, pinfo); @@ -982,8 +1117,10 @@ void RtMidiIn :: openPort( unsigned int portNumber, const std::string portName ) if ( inputData_.doInput == false ) { // Start the input queue +#ifndef AVOID_TIMESTAMPING snd_seq_start_queue( data->seq, data->queue_id, NULL ); snd_seq_drain_output( data->seq ); +#endif // Start our MIDI input thread. pthread_attr_t attr; pthread_attr_init(&attr); @@ -1018,9 +1155,11 @@ void RtMidiIn :: openVirtualPort( std::string portName ) SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION ); snd_seq_port_info_set_midi_channels(pinfo, 16); +#ifndef AVOID_TIMESTAMPING snd_seq_port_info_set_timestamping(pinfo, 1); snd_seq_port_info_set_timestamp_real(pinfo, 1); snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id); +#endif snd_seq_port_info_set_name(pinfo, portName.c_str()); data->vport = snd_seq_create_port(data->seq, pinfo); @@ -1032,8 +1171,10 @@ void RtMidiIn :: openVirtualPort( std::string portName ) if ( inputData_.doInput == false ) { // Start the input queue +#ifndef AVOID_TIMESTAMPING snd_seq_start_queue( data->seq, data->queue_id, NULL ); snd_seq_drain_output( data->seq ); +#endif // Start our MIDI input thread. pthread_attr_t attr; pthread_attr_init(&attr); @@ -1060,8 +1201,10 @@ void RtMidiIn :: closePort( void ) snd_seq_unsubscribe_port( data->seq, data->subscription ); snd_seq_port_subscribe_free( data->subscription ); // Stop the input queue +#ifndef AVOID_TIMESTAMPING snd_seq_stop_queue( data->seq, data->queue_id, NULL ); snd_seq_drain_output( data->seq ); +#endif connected_ = false; } } @@ -1080,8 +1223,10 @@ RtMidiIn :: ~RtMidiIn() // Cleanup. if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); +#ifndef AVOID_TIMESTAMPING snd_seq_free_queue( data->seq, data->queue_id ); snd_seq_close( data->seq ); +#endif delete data; } diff --git a/RtMidi.h b/RtMidi.h index 6da148c..2928eaf 100644 --- a/RtMidi.h +++ b/RtMidi.h @@ -35,7 +35,7 @@ */ /**********************************************************************/ -// RtMidi: Version 1.0.8 +// RtMidi: Version 1.0.9 #ifndef RTMIDI_H #define RTMIDI_H diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile index 3f24d67..eb22c66 100644 --- a/doc/doxygen/Doxyfile +++ b/doc/doxygen/Doxyfile @@ -5,7 +5,7 @@ #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = RtMidi -PROJECT_NUMBER = 1.0.8 +PROJECT_NUMBER = 1.0.9 OUTPUT_DIRECTORY = . CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English diff --git a/doc/doxygen/tutorial.txt b/doc/doxygen/tutorial.txt index 4522107..0e348d6 100644 --- a/doc/doxygen/tutorial.txt +++ b/doc/doxygen/tutorial.txt @@ -17,7 +17,7 @@ MIDI input and output functionality are separated into two classes, RtMidiIn and \section download Download -Latest Release (29 January 2009): Version 1.0.8 +Latest Release (30 April 2009): Version 1.0.9 \section start Getting Started @@ -50,7 +50,19 @@ Obviously, this example doesn't demonstrate any of the real functionality of RtM \section error Error Handling -RtMidi uses a C++ exception handler called RtError, which is declared and defined in RtError.h. The RtError class is quite simple but it does allow errors to be "caught" by RtError::Type. Many RtMidi methods can "throw" an RtError, most typically if a driver error occurs or an invalid function argument is specified. There are a number of cases within RtMidi where warning messages may be displayed but an exception is not thrown. There is a protected RtMidi method, error(), that can be modified to globally control how these messages are handled and reported. By default, error messages are not automatically displayed in RtMidi unless the preprocessor definition __RTMIDI_DEBUG__ is defined. Messages associated with caught exceptions can be displayed with, for example, the RtError::printMessage() function. +RtMidi uses a C++ exception handler called RtError, which is declared +and defined in RtError.h. The RtError class is quite simple but it +does allow errors to be "caught" by RtError::Type. Many RtMidi +methods can "throw" an RtError, most typically if a driver error +occurs or an invalid function argument is specified. There are a +number of cases within RtMidi where warning messages may be displayed +but an exception is not thrown. There is a protected RtMidi method, +error(), that can be modified to globally control how these messages +are handled and reported. By default, error messages are not +automatically displayed in RtMidi unless the preprocessor definition +__RTMIDI_DEBUG__ is defined during compilation. Messages associated +with caught exceptions can be displayed with, for example, the +RtError::printMessage() function. \section probing Probing Ports @@ -364,7 +376,7 @@ The example compiler statements above could be used to compile the midiprobe \section debug Debugging -If you are having problems getting RtMidi to run on your system, try passing the preprocessor definition __RTMIDI_DEBUG__ to the compiler (or uncomment the definition at the bottom of RtMidi.h). A variety of warning messages will be displayed that may help in determining the problem. Also try using the programs included in the test directory. The program midiprobe displays the queried capabilities of all MIDI ports found. +If you are having problems getting RtMidi to run on your system, try passing the preprocessor definition __RTMIDI_DEBUG__ to the compiler (or define it in RtMidi.h). A variety of warning messages will be displayed that may help in determining the problem. Also try using the programs included in the test directory. The program midiprobe displays the queried capabilities of all MIDI ports found. \section apinotes API Notes @@ -397,6 +409,7 @@ The \c configure script provides support for the MinGW compiler. Many thanks to the following people for providing bug fixes and improvements:
  • Pedro Lopez-Cabanillas (ALSA sequencer API, client naming)
  • +
  • Casey Tucker (OS-X driver information)
  • Eduardo Coutinho (Windows device names)
  • Jean-Baptiste Berruchon (Windows sysex code)
  • Christoph Eckert (ALSA sysex fixes)
  • diff --git a/doc/release.txt b/doc/release.txt index cb7b043..ff9f7d9 100644 --- a/doc/release.txt +++ b/doc/release.txt @@ -2,6 +2,10 @@ RtMidi - a set of C++ classes that provides a common API for realtime MIDI input By Gary P. Scavone, 2003-2009. +v1.0.9: (30 April 2009) +- added #ifdef AVOID_TIMESTAMPING to conditionally compile support for event timestamping of ALSA sequencer events. This is useful for programs not needing timestamps, saving valuable system resources. +- updated functionality in OSX_CORE for getting driver name (thanks to Casey Tucker) + v1.0.8: (29 January 2009) - bug fixes for concatenating segmented sysex messages in ALSA (thanks to Christoph Eckert) - update to ALSA sequencer port enumeration (thanks to Pedro Lopez-Cabonillas) -- cgit v1.2.3