瀏覽代碼

Version 1.0.13

Gary Scavone 11 年之前
父節點
當前提交
330e5ca540
共有 8 個文件被更改,包括 409 次插入28 次删除
  1. 16 16
      RtError.h
  2. 362 2
      RtMidi.cpp
  3. 1 1
      RtMidi.h
  4. 13 4
      configure.ac
  5. 1 1
      doc/doxygen/Doxyfile
  6. 10 2
      doc/doxygen/tutorial.txt
  7. 5 1
      doc/release.txt
  8. 1 1
      readme

+ 16 - 16
RtError.h

@@ -12,12 +12,13 @@
 #ifndef RTERROR_H
 #define RTERROR_H
 
+#include <exception>
 #include <iostream>
 #include <string>
 
-class RtError
+class RtError : public std::exception
 {
-public:
+ public:
   //! Defined RtError types.
   enum Type {
     WARNING,           /*!< A non-critical error. */
@@ -25,36 +26,35 @@ public:
     UNSPECIFIED,       /*!< The default, unspecified error type. */
     NO_DEVICES_FOUND,  /*!< No devices found on system. */
     INVALID_DEVICE,    /*!< An invalid device ID was specified. */
-    INVALID_STREAM,    /*!< An invalid stream ID was specified. */
     MEMORY_ERROR,      /*!< An error occured during memory allocation. */
     INVALID_PARAMETER, /*!< An invalid parameter was specified to a function. */
+    INVALID_USE,       /*!< The function was called incorrectly. */
     DRIVER_ERROR,      /*!< A system driver error occured. */
     SYSTEM_ERROR,      /*!< A system error occured. */
     THREAD_ERROR       /*!< A thread error occured. */
   };
 
-protected:
-  std::string message_;
-  Type type_;
-
-public:
   //! The constructor.
-  RtError(const std::string& message, Type type = RtError::UNSPECIFIED) : message_(message), type_(type) {}
-
+  RtError( const std::string& message, Type type = RtError::UNSPECIFIED ) throw() : message_(message), type_(type) {}
+ 
   //! The destructor.
-  virtual ~RtError(void) {};
+  virtual ~RtError( void ) throw() {}
 
   //! Prints thrown error message to stderr.
-  virtual void printMessage(void) { std::cerr << '\n' << message_ << "\n\n"; }
+  virtual void printMessage( void ) const throw() { std::cerr << '\n' << message_ << "\n\n"; }
 
   //! Returns the thrown error message type.
-  virtual const Type& getType(void) { return type_; }
+  virtual const Type& getType(void) const throw() { return type_; }
 
   //! Returns the thrown error message string.
-  virtual const std::string& getMessage(void) { return message_; }
+  virtual const std::string& getMessage(void) const throw() { return message_; }
 
-  //! Returns the thrown error message as a C string.
-  virtual const char *getMessageString(void) { return message_.c_str(); }
+  //! Returns the thrown error message as a c-style string.
+  virtual const char* what( void ) const throw() { return message_.c_str(); }
+
+ protected:
+  std::string message_;
+  Type type_;
 };
 
 #endif

+ 362 - 2
RtMidi.cpp

@@ -35,7 +35,7 @@
 */
 /**********************************************************************/
 
-// RtMidi: Version 1.0.12
+// RtMidi: Version 1.0.13
 
 #include "RtMidi.h"
 #include <sstream>
@@ -1970,7 +1970,7 @@ struct WinMidiData {
 //  Class Definitions: RtMidiIn
 //*********************************************************************//
 
-static void CALLBACK midiInputCallback( HMIDIOUT hmin,
+static void CALLBACK midiInputCallback( HMIDIIN hmin,
                                         UINT inputStatus, 
                                         DWORD_PTR instancePtr,
                                         DWORD_PTR midiMessage,
@@ -2405,3 +2405,363 @@ void RtMidiOut :: sendMessage( std::vector<unsigned char> *message )
 }
 
 #endif  // __WINDOWS_MM__
+
+//*********************************************************************//
+//  API: LINUX JACK
+//
+//  Written primarily by Alexander Svetalkin, with updates for delta
+//  time by Gary Scavone, April 2011.
+//
+//  *********************************************************************//
+
+#if defined(__LINUX_JACK__)
+
+// JACK header files
+#include <jack/jack.h>
+#include <jack/midiport.h>
+#include <jack/ringbuffer.h>
+
+#define JACK_RINGBUFFER_SIZE 16384 // Default size for ringbuffer
+
+struct JackMidiData {
+  jack_client_t *client;
+  jack_port_t *port;
+  jack_ringbuffer_t *buffSize;
+  jack_ringbuffer_t *buffMessage;
+  jack_time_t lastTime;
+  };
+
+struct Arguments {
+  JackMidiData *jackData;
+  RtMidiIn :: RtMidiInData *rtMidiIn;
+  };
+
+//*********************************************************************//
+//  API: JACK
+//  Class Definitions: RtMidiIn
+//*********************************************************************//
+
+int jackProcessIn( jack_nframes_t nframes, void *arg )
+{
+  JackMidiData *jData = ( (Arguments *) arg )->jackData;
+  RtMidiIn :: RtMidiInData *rtData = ( (Arguments *) arg )->rtMidiIn;
+  jack_midi_event_t event;
+  jack_time_t long long time;
+
+  // Is port created?
+  if ( jData->port == NULL ) return 0;
+  void *buff = jack_port_get_buffer( jData->port, nframes );
+
+  // We have midi events in buffer
+  int evCount = jack_midi_get_event_count( buff );
+  if ( evCount > 0 ) {
+    RtMidiIn::MidiMessage message;
+    message.bytes.clear();
+
+    jack_midi_event_get( &event, buff, 0 );
+
+    for (unsigned int i = 0; i < event.size; i++ )
+      message.bytes.push_back( event.buffer[i] );
+
+    // Compute the delta time.
+    time = jack_get_time();
+    if ( rtData->firstMessage == true )
+      rtData->firstMessage = false;
+    else
+      message.timeStamp = ( time - jData->lastTime ) * 0.000001;
+
+    jData->lastTime = time;
+
+    if ( rtData->usingCallback && !rtData->continueSysex ) {
+      RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) rtData->userCallback;
+      callback( message.timeStamp, &message.bytes, rtData->userData );
+    }
+    else {
+      // As long as we haven't reached our queue size limit, push the message.
+      if ( rtData->queueLimit > rtData->queue.size() )
+        rtData->queue.push( message );
+      else
+        std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n";
+    }
+  }
+
+  return 0;
+}
+
+void RtMidiIn :: initialize( const std::string& clientName )
+{
+  JackMidiData *data = new JackMidiData;
+
+  // Initialize JACK client
+  if (( data->client = jack_client_open( clientName.c_str(), JackNullOption, NULL )) == 0)
+    {
+    errorString_ = "RtMidiOut::initialize: JACK server not running?";
+    error( RtError::DRIVER_ERROR );
+    return;
+    }
+
+  Arguments *arg = new Arguments;
+  arg->jackData = data;
+
+  arg->rtMidiIn = &inputData_;
+  jack_set_process_callback( data->client, jackProcessIn, arg );
+  data->port = NULL;
+  jack_activate( data->client );
+
+  apiData_ = (void *) data;
+}
+
+RtMidiIn :: ~RtMidiIn()
+{
+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);
+  jack_client_close( data->client );
+}
+
+void RtMidiIn :: openPort( unsigned int portNumber, const std::string portName )
+{
+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);
+
+  // Creating new port
+  if ( data->port == NULL)
+    data->port = jack_port_register( data->client, portName.c_str(),
+                                     JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 );
+
+  if ( data->port == NULL) {
+    errorString_ = "RtMidiOut::openVirtualPort: JACK error creating virtual port";
+    error( RtError::DRIVER_ERROR );
+  }
+
+  // Connecting to the output
+  std::string name = getPortName( portNumber );
+  jack_connect( data->client, name.c_str(), jack_port_name( data->port ) );
+}
+
+void RtMidiIn :: openVirtualPort( const std::string portName )
+{
+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);
+
+  if ( data->port == NULL )
+    data->port = jack_port_register( data->client, portName.c_str(),
+                                     JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 );
+
+  if ( data->port == NULL ) {
+    errorString_ = "RtMidiOut::openVirtualPort: JACK error creating virtual port";
+    error( RtError::DRIVER_ERROR );
+  }
+}
+
+unsigned int RtMidiIn :: getPortCount()
+{
+  int count = 0;
+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);
+
+  // List of available ports
+  const char **ports = jack_get_ports( data->client, NULL,
+    JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput );
+
+  if ( ports == NULL ) return 0;
+  while ( ports[count] != NULL )
+    count++;
+
+  free( ports );
+
+  return count;
+}
+
+std::string RtMidiIn :: getPortName( unsigned int portNumber )
+{
+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);
+  std::ostringstream ost;
+  std::string retStr("");
+
+  // List of available ports
+  const char **ports = jack_get_ports( data->client, NULL,
+    JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput );
+
+  // Check port validity
+  if ( ports == NULL ) {
+    errorString_ = "RtMidiOut::getPortName: no ports available!";
+    error( RtError::WARNING );
+    return retStr;
+  }
+
+  if ( ports[portNumber] == NULL ) {
+    ost << "RtMidiOut::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
+    errorString_ = ost.str();
+    error( RtError::WARNING );
+  }
+  else retStr.assign( ports[portNumber] );
+
+  free( ports );
+
+  return retStr;
+}
+
+void RtMidiIn :: closePort()
+{
+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);
+
+  if ( data->port == NULL ) return;
+  jack_port_unregister( data->client, data->port );
+}
+
+//*********************************************************************//
+//  API: JACK
+//  Class Definitions: RtMidiOut
+//*********************************************************************//
+
+// Jack process callback
+int jackProcessOut( jack_nframes_t nframes, void *arg )
+{
+  JackMidiData *data = (JackMidiData *) arg;
+  jack_midi_data_t *midiData;
+  int space;
+
+  // Is port created?
+  if ( data->port == NULL ) return 0;
+
+  void *buff = jack_port_get_buffer( data->port, nframes );
+  jack_midi_clear_buffer( buff );
+
+  while ( jack_ringbuffer_read_space( data->buffSize ) > 0 ) {
+    jack_ringbuffer_read( data->buffSize, (char *) &space, (size_t) sizeof(space) );
+    midiData = jack_midi_event_reserve( buff, 0, space );
+
+    jack_ringbuffer_read( data->buffMessage, (char *) midiData, (size_t) space );
+  }
+
+  return 0;
+}
+
+void RtMidiOut :: initialize( const std::string& clientName )
+{
+  JackMidiData *data = new JackMidiData;
+
+  // Initialize JACK client
+  if (( data->client = jack_client_open( clientName.c_str(), JackNullOption, NULL )) == 0)
+    {
+    errorString_ = "RtMidiOut::initialize: JACK server not running?";
+    error( RtError::DRIVER_ERROR );
+    return;
+    }
+
+  jack_set_process_callback( data->client, jackProcessOut, data );
+  data->buffSize = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );
+  data->buffMessage = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );
+  jack_activate( data->client );
+
+  data->port = NULL;
+
+  apiData_ = (void *) data;
+}
+
+RtMidiOut :: ~RtMidiOut()
+{
+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);
+
+  // Cleanup
+  jack_ringbuffer_free( data->buffSize );
+  jack_ringbuffer_free( data->buffMessage );
+  jack_client_close( data->client );
+}
+
+void RtMidiOut :: openPort( unsigned int portNumber, const std::string portName )
+{
+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);
+
+  // Creating new port
+  if ( data->port == NULL )
+    data->port = jack_port_register( data->client, portName.c_str(),
+      JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 );
+
+  if ( data->port == NULL ) {
+    errorString_ = "RtMidiOut::openVirtualPort: JACK error creating virtual port";
+    error( RtError::DRIVER_ERROR );
+  }
+
+  // Connecting to the output
+  std::string name = getPortName( portNumber );
+  jack_connect( data->client, jack_port_name( data->port ), name.c_str() );
+}
+
+void RtMidiOut :: openVirtualPort( const std::string portName )
+{
+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);
+
+  if ( data->port == NULL )
+    data->port = jack_port_register( data->client, portName.c_str(),
+      JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 );
+
+  if ( data->port == NULL ) {
+    errorString_ = "RtMidiOut::openVirtualPort: JACK error creating virtual port";
+    error( RtError::DRIVER_ERROR );
+  }
+}
+
+unsigned int RtMidiOut :: getPortCount()
+{
+  int count = 0;
+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);
+
+  // List of available ports
+  const char **ports = jack_get_ports( data->client, NULL,
+    JACK_DEFAULT_MIDI_TYPE, JackPortIsInput );
+
+  if ( ports == NULL ) return 0;
+  while ( ports[count] != NULL )
+    count++;
+
+  free( ports );
+
+  return count;
+}
+
+std::string RtMidiOut :: getPortName( unsigned int portNumber )
+{
+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);
+  std::ostringstream ost;
+  std::string retStr("");
+
+  // List of available ports
+  const char **ports = jack_get_ports( data->client, NULL,
+    JACK_DEFAULT_MIDI_TYPE, JackPortIsInput );
+
+  // Check port validity
+  if ( ports == NULL) {
+    errorString_ = "RtMidiOut::getPortName: no ports available!";
+    error( RtError::WARNING );
+    return retStr;
+  }
+
+  if ( ports[portNumber] == NULL) {
+    ost << "RtMidiOut::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
+    errorString_ = ost.str();
+    error( RtError::WARNING );
+  }
+  else retStr.assign( ports[portNumber] );
+
+  free( ports );
+
+  return retStr;
+}
+
+void RtMidiOut :: closePort()
+{
+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);
+
+  if ( data->port == NULL ) return;
+  jack_port_unregister( data->client, data->port );
+}
+
+void RtMidiOut :: sendMessage( std::vector<unsigned char> *message )
+{
+  int nBytes = message->size();
+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);
+
+  // Write full message to buffer
+  jack_ringbuffer_write( data->buffMessage, ( const char * ) &( *message )[0],
+                         message->size() );
+  jack_ringbuffer_write( data->buffSize, ( char * ) &nBytes, sizeof( nBytes ) );
+}
+
+#endif  // __LINUX_JACK__

+ 1 - 1
RtMidi.h

@@ -35,7 +35,7 @@
 */
 /**********************************************************************/
 
-// RtMidi: Version 1.0.12
+// RtMidi: Version 1.0.13
 
 #ifndef RTMIDI_H
 #define RTMIDI_H

+ 13 - 4
configure.ac

@@ -40,10 +40,19 @@ AC_CANONICAL_HOST
 AC_MSG_CHECKING(for MIDI API)
 case $host in
   *-*-linux*)
-  AC_SUBST( api, [-D__LINUX_ALSASEQ__] )
-  AC_CHECK_LIB(asound, snd_seq_open, , AC_MSG_ERROR(RtMidi in Linux requires the ALSA asound library!))
-  # Checks for pthread library.
-  AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR(RtMidi requires the pthread library!))
+  AC_SUBST( api, [""] )
+  AC_ARG_WITH(jack, [  --with-jack = choose JACK server support (linux only)], [
+  AC_SUBST( api, [-D__LINUX_JACK__] )
+  AC_MSG_RESULT(using JACK)
+  AC_CHECK_LIB(jack, jack_client_open, , AC_MSG_ERROR(JACK support requires the jack library!))], )
+
+  if [test "$api" == "";] then
+    AC_SUBST( api, [-D__LINUX_ALSASEQ__] )
+    AC_CHECK_LIB(asound, snd_seq_open, , AC_MSG_ERROR(RtMidi in Linux requires the ALSA asound library!))
+    # Checks for pthread library.
+    AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR(RtMidi requires the pthread library!))
+  fi
+
   ;;
 
   *-sgi*)

+ 1 - 1
doc/doxygen/Doxyfile

@@ -31,7 +31,7 @@ PROJECT_NAME           = RtMidi
 # This could be handy for archiving the generated documentation or 
 # if some version control system is used.
 
-PROJECT_NUMBER         = 1.0.12
+PROJECT_NUMBER         = 1.0.13
 
 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
 # base path where the generated documentation will be put. 

+ 10 - 2
doc/doxygen/tutorial.txt

@@ -4,7 +4,7 @@
 
 \section intro Introduction
 
-RtMidi is a set of C++ classes (RtMidiIn and RtMidiOut) that provides a common API (Application Programming Interface) for realtime MIDI input/output across Linux (ALSA), Macintosh OS X, SGI, and Windows (Multimedia Library) operating systems.  RtMidi significantly simplifies the process of interacting with computer MIDI hardware and software.  It was designed with the following goals:
+RtMidi is a set of C++ classes (RtMidiIn and RtMidiOut) that provides a common API (Application Programming Interface) for realtime MIDI input/output across Linux (ALSA & Jack), Macintosh OS X, Windows (Multimedia Library), and SGI operating systems.  RtMidi significantly simplifies the process of interacting with computer MIDI hardware and software.  It was designed with the following goals:
 
 <UL>
   <LI>object oriented C++ design</LI>
@@ -17,7 +17,7 @@ MIDI input and output functionality are separated into two classes, RtMidiIn and
 
 \section download Download
 
-Latest Release (17 February 2011): <A href="http://www.music.mcgill.ca/~gary/rtmidi/release/rtmidi-1.0.12.tar.gz">Version 1.0.12</A>
+Latest Release (7 April 2011): <A href="http://www.music.mcgill.ca/~gary/rtmidi/release/rtmidi-1.0.13.tar.gz">Version 1.0.13</A>
 
 \section start Getting Started
 
@@ -348,6 +348,13 @@ In order to compile RtMidi for a specific OS and API, it is necessary to supply
   <TD><TT>asound, pthread</TT></TD>
   <TD><TT>g++ -Wall -D__LINUX_ALSASEQ__ -o midiprobe midiprobe.cpp RtMidi.cpp -lasound -lpthread</TT></TD>
 </TR>
+<TR>
+  <TD>Linux</TD>
+  <TD>Jack MIDI</TD>
+  <TD>__LINUX_JACK__</TD>
+  <TD><TT>jack</TT></TD>
+  <TD><TT>g++ -Wall -D__LINUX_JACK__ -o midiprobe midiprobe.cpp RtMidi.cpp -ljack</TT></TD>
+</TR>
 <TR>
   <TD>Macintosh OS X</TD>
   <TD>CoreMidi</TD>
@@ -418,6 +425,7 @@ Many thanks to the following people for providing bug fixes and improvements:
 <LI>Immanuel Litzroth (OS-X sysex fix)</LI>
 <LI>Jon McCormack (Snow Leopard updates)</LI>
 <LI>Axel Schmidt (client naming)</LI>
+<LI>Alexander Svetalkin (Jack MIDI)</LI>
 <LI>Casey Tucker (OS-X driver information, sysex sending)</LI>
 <LI>Bastiaan Verreijt (Windows sysex multi-buffer code)</LI>
 </UL>

+ 5 - 1
doc/release.txt

@@ -1,7 +1,11 @@
-RtMidi - a set of C++ classes that provides a common API for realtime MIDI input/output across Linux (ALSA), SGI, Macintosh OS X (CoreMidi), and Windows (Multimedia) operating systems.
+RtMidi - a set of C++ classes that provides a common API for realtime MIDI input/output across Linux (ALSA & Jack), Macintosh OS X (CoreMidi), Windows (Multimedia), and SGI operating systems.
 
 By Gary P. Scavone, 2003-2011.
 
+v1.0.13: (7 April 2011)
+- updated RtError.h to the same version as in RtAudio
+- new Jack MIDI support in Linux (thanks to Alexander Svetalkin)
+
 v1.0.12: (17 February 2011)
 - Windows 64-bit pointer fixes (thanks to Ward Kockelkorn)
 - removed possible exceptions from getPortName() functions

+ 1 - 1
readme

@@ -1,4 +1,4 @@
-RtMidi - a set of C++ classes that provide a common API for realtime MIDI input/output across Linux (ALSA), SGI, Macintosh OS X (CoreMidi), and Windows (Multimedia) operating systems.
+RtMidi - a set of C++ classes that provide a common API for realtime MIDI input/output across Linux (ALSA & Jack), Macintosh OS X (CoreMidi), Windows (Multimedia) and SGI operating systems.
 
 By Gary P. Scavone, 2003-2011.