Skip to content
Snippets Groups Projects
RtMidi.cpp 91.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • SeeLook's avatar
    SeeLook committed
        // The WinMM API requires that the sysex buffer be requeued after
        // input of each sysex message.  Even if we are ignoring sysex
        // messages, we still need to requeue the buffer in case the user
        // decides to not ignore sysex messages in the future.  However,
        // it seems that WinMM calls this function with an empty sysex
        // buffer when an application closes and in this case, we should
        // avoid requeueing it, else the computer suddenly reboots after
        // one or two minutes.
    
        if ( apiData->sysexBuffer[sysex->dwUser]->dwBytesRecorded > 0 ) {
          //if ( sysex->dwBytesRecorded > 0 ) {
          EnterCriticalSection( &(apiData->_mutex) );
    
    SeeLook's avatar
    SeeLook committed
          MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer[sysex->dwUser], sizeof(MIDIHDR) );
    
          LeaveCriticalSection( &(apiData->_mutex) );
    
    SeeLook's avatar
    SeeLook committed
          if ( result != MMSYSERR_NOERROR )
            std::cerr << "\nRtMidiIn::midiInputCallback: error sending sysex to Midi device!!\n\n";
    
          if ( data->ignoreFlags & 0x01 ) return;
        }
        else return;
      }
    
      if ( data->usingCallback ) {
        RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
        callback( apiData->message.timeStamp, &apiData->message.bytes, data->userData );
      }
      else {
        // As long as we haven't reached our queue size limit, push the message.
        if ( data->queue.size < data->queue.ringSize ) {
          data->queue.ring[data->queue.back++] = apiData->message;
          if ( data->queue.back == data->queue.ringSize )
            data->queue.back = 0;
          data->queue.size++;
        }
        else
          std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n";
      }
    
      // Clear the vector for the next input message.
      apiData->message.bytes.clear();
    }
    
    MidiInWinMM :: MidiInWinMM( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit )
    {
      initialize( clientName );
    }
    
    MidiInWinMM :: ~MidiInWinMM()
    {
      // Close a connection if it exists.
      closePort();
    
      WinMidiData *data = static_cast<WinMidiData *> (apiData_);
    
      DeleteCriticalSection( &(data->_mutex) );
    
      // Cleanup.
    
    SeeLook's avatar
    SeeLook committed
      delete data;
    }
    
    void MidiInWinMM :: initialize( const std::string& /*clientName*/ )
    {
      // We'll issue a warning here if no devices are available but not
      // throw an error since the user can plugin something later.
      unsigned int nDevices = midiInGetNumDevs();
      if ( nDevices == 0 ) {
        errorString_ = "MidiInWinMM::initialize: no MIDI input devices currently available.";
    
        error( RtMidiError::WARNING, errorString_ );
    
    SeeLook's avatar
    SeeLook committed
      }
    
      // Save our api-specific connection information.
      WinMidiData *data = (WinMidiData *) new WinMidiData;
      apiData_ = (void *) data;
      inputData_.apiData = (void *) data;
      data->message.bytes.clear();  // needs to be empty for first input message
    
    
      if ( !InitializeCriticalSectionAndSpinCount(&(data->_mutex), 0x00000400) ) {
        errorString_ = "MidiInWinMM::initialize: InitializeCriticalSectionAndSpinCount failed.";
        error( RtMidiError::WARNING, errorString_ );
      }
    
    SeeLook's avatar
    SeeLook committed
    }
    
    void MidiInWinMM :: openPort( unsigned int portNumber, const std::string /*portName*/ )
    {
      if ( connected_ ) {
        errorString_ = "MidiInWinMM::openPort: a valid connection already exists!";
    
        error( RtMidiError::WARNING, errorString_ );
    
    SeeLook's avatar
    SeeLook committed
        return;
      }
    
      unsigned int nDevices = midiInGetNumDevs();
      if (nDevices == 0) {
        errorString_ = "MidiInWinMM::openPort: no MIDI input sources found!";
    
        error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
        return;
    
    SeeLook's avatar
    SeeLook committed
      }
    
      if ( portNumber >= nDevices ) {
    
    SeeLook's avatar
    SeeLook committed
        ost << "MidiInWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
        errorString_ = ost.str();
    
        error( RtMidiError::INVALID_PARAMETER, errorString_ );
        return;
    
    SeeLook's avatar
    SeeLook committed
      }
    
      WinMidiData *data = static_cast<WinMidiData *> (apiData_);
      MMRESULT result = midiInOpen( &data->inHandle,
                                    portNumber,
                                    (DWORD_PTR)&midiInputCallback,
                                    (DWORD_PTR)&inputData_,
                                    CALLBACK_FUNCTION );
      if ( result != MMSYSERR_NOERROR ) {
        errorString_ = "MidiInWinMM::openPort: error creating Windows MM MIDI input port.";
    
        error( RtMidiError::DRIVER_ERROR, errorString_ );
        return;
    
    SeeLook's avatar
    SeeLook committed
      }
    
      // Allocate and init the sysex buffers.
      for ( int i=0; i<RT_SYSEX_BUFFER_COUNT; ++i ) {
        data->sysexBuffer[i] = (MIDIHDR*) new char[ sizeof(MIDIHDR) ];
        data->sysexBuffer[i]->lpData = new char[ RT_SYSEX_BUFFER_SIZE ];
        data->sysexBuffer[i]->dwBufferLength = RT_SYSEX_BUFFER_SIZE;
        data->sysexBuffer[i]->dwUser = i; // We use the dwUser parameter as buffer indicator
        data->sysexBuffer[i]->dwFlags = 0;
    
        result = midiInPrepareHeader( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) );
        if ( result != MMSYSERR_NOERROR ) {
          midiInClose( data->inHandle );
          errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (PrepareHeader).";
    
          error( RtMidiError::DRIVER_ERROR, errorString_ );
          return;
    
    SeeLook's avatar
    SeeLook committed
        }
    
        // Register the buffer.
        result = midiInAddBuffer( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) );
        if ( result != MMSYSERR_NOERROR ) {
          midiInClose( data->inHandle );
          errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (AddBuffer).";
    
          error( RtMidiError::DRIVER_ERROR, errorString_ );
          return;
    
    SeeLook's avatar
    SeeLook committed
        }
      }
    
      result = midiInStart( data->inHandle );
      if ( result != MMSYSERR_NOERROR ) {
        midiInClose( data->inHandle );
        errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port.";
    
        error( RtMidiError::DRIVER_ERROR, errorString_ );
        return;
    
    SeeLook's avatar
    SeeLook committed
      }
    
      connected_ = true;
    }
    
    
    void MidiInWinMM :: openVirtualPort( std::string /*portName*/ )
    
    SeeLook's avatar
    SeeLook committed
    {
      // This function cannot be implemented for the Windows MM MIDI API.
      errorString_ = "MidiInWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!";
    
      error( RtMidiError::WARNING, errorString_ );
    
    SeeLook's avatar
    SeeLook committed
    }
    
    void MidiInWinMM :: closePort( void )
    {
      if ( connected_ ) {
        WinMidiData *data = static_cast<WinMidiData *> (apiData_);
    
        EnterCriticalSection( &(data->_mutex) );
    
    SeeLook's avatar
    SeeLook committed
        midiInReset( data->inHandle );
        midiInStop( data->inHandle );
    
        for ( int i=0; i<RT_SYSEX_BUFFER_COUNT; ++i ) {
          int result = midiInUnprepareHeader(data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR));
          delete [] data->sysexBuffer[i]->lpData;
          delete [] data->sysexBuffer[i];
          if ( result != MMSYSERR_NOERROR ) {
            midiInClose( data->inHandle );
            errorString_ = "MidiInWinMM::openPort: error closing Windows MM MIDI input port (midiInUnprepareHeader).";
    
            error( RtMidiError::DRIVER_ERROR, errorString_ );
            return;
    
    SeeLook's avatar
    SeeLook committed
          }
        }
    
        midiInClose( data->inHandle );
        connected_ = false;
    
        LeaveCriticalSection( &(data->_mutex) );
    
    SeeLook's avatar
    SeeLook committed
      }
    }
    
    unsigned int MidiInWinMM :: getPortCount()
    {
      return midiInGetNumDevs();
    }
    
    std::string MidiInWinMM :: getPortName( unsigned int portNumber )
    {
      std::string stringName;
      unsigned int nDevices = midiInGetNumDevs();
      if ( portNumber >= nDevices ) {
        std::ostringstream ost;
        ost << "MidiInWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
        errorString_ = ost.str();
    
        error( RtMidiError::WARNING, errorString_ );
    
    SeeLook's avatar
    SeeLook committed
        return stringName;
      }
    
      MIDIINCAPS deviceCaps;
      midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS));
    
    #if defined( UNICODE ) || defined( _UNICODE )
    
      int length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, -1, NULL, 0, NULL, NULL) - 1;
    
    SeeLook's avatar
    SeeLook committed
      stringName.assign( length, 0 );
    
      length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, static_cast<int>(wcslen(deviceCaps.szPname)), &stringName[0], length, NULL, NULL);
    
    SeeLook's avatar
    SeeLook committed
    #else
      stringName = std::string( deviceCaps.szPname );
    #endif
    
      // Next lines added to add the portNumber to the name so that 
      // the device's names are sure to be listed with individual names
      // even when they have the same brand name
      std::ostringstream os;
      os << " ";
      os << portNumber;
      stringName += os.str();
    
      return stringName;
    }
    
    //*********************************************************************//
    //  API: Windows MM
    //  Class Definitions: MidiOutWinMM
    //*********************************************************************//
    
    MidiOutWinMM :: MidiOutWinMM( const std::string clientName ) : MidiOutApi()
    {
      initialize( clientName );
    }
    
    MidiOutWinMM :: ~MidiOutWinMM()
    {
      // Close a connection if it exists.
      closePort();
    
      // Cleanup.
      WinMidiData *data = static_cast<WinMidiData *> (apiData_);
      delete data;
    }
    
    void MidiOutWinMM :: initialize( const std::string& /*clientName*/ )
    {
      // We'll issue a warning here if no devices are available but not
      // throw an error since the user can plug something in later.
      unsigned int nDevices = midiOutGetNumDevs();
      if ( nDevices == 0 ) {
        errorString_ = "MidiOutWinMM::initialize: no MIDI output devices currently available.";
    
        error( RtMidiError::WARNING, errorString_ );
    
    SeeLook's avatar
    SeeLook committed
      }
    
      // Save our api-specific connection information.
      WinMidiData *data = (WinMidiData *) new WinMidiData;
      apiData_ = (void *) data;
    }
    
    unsigned int MidiOutWinMM :: getPortCount()
    {
      return midiOutGetNumDevs();
    }
    
    std::string MidiOutWinMM :: getPortName( unsigned int portNumber )
    {
      std::string stringName;
      unsigned int nDevices = midiOutGetNumDevs();
      if ( portNumber >= nDevices ) {
        std::ostringstream ost;
        ost << "MidiOutWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
        errorString_ = ost.str();
    
        error( RtMidiError::WARNING, errorString_ );
    
    SeeLook's avatar
    SeeLook committed
        return stringName;
      }
    
      MIDIOUTCAPS deviceCaps;
      midiOutGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIOUTCAPS));
    
    #if defined( UNICODE ) || defined( _UNICODE )
    
      int length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, -1, NULL, 0, NULL, NULL) - 1;
    
    SeeLook's avatar
    SeeLook committed
      stringName.assign( length, 0 );
    
      length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, static_cast<int>(wcslen(deviceCaps.szPname)), &stringName[0], length, NULL, NULL);
    
    SeeLook's avatar
    SeeLook committed
    #else
      stringName = std::string( deviceCaps.szPname );
    #endif
    
      return stringName;
    }
    
    void MidiOutWinMM :: openPort( unsigned int portNumber, const std::string /*portName*/ )
    {
      if ( connected_ ) {
        errorString_ = "MidiOutWinMM::openPort: a valid connection already exists!";
    
        error( RtMidiError::WARNING, errorString_ );
    
    SeeLook's avatar
    SeeLook committed
        return;
      }
    
      unsigned int nDevices = midiOutGetNumDevs();
      if (nDevices < 1) {
        errorString_ = "MidiOutWinMM::openPort: no MIDI output destinations found!";
    
        error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
        return;
    
    SeeLook's avatar
    SeeLook committed
      }
    
      if ( portNumber >= nDevices ) {
    
    SeeLook's avatar
    SeeLook committed
        ost << "MidiOutWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
        errorString_ = ost.str();
    
        error( RtMidiError::INVALID_PARAMETER, errorString_ );
        return;
    
    SeeLook's avatar
    SeeLook committed
      }
    
      WinMidiData *data = static_cast<WinMidiData *> (apiData_);
      MMRESULT result = midiOutOpen( &data->outHandle,
                                     portNumber,
                                     (DWORD)NULL,
                                     (DWORD)NULL,
                                     CALLBACK_NULL );
      if ( result != MMSYSERR_NOERROR ) {
        errorString_ = "MidiOutWinMM::openPort: error creating Windows MM MIDI output port.";
    
        error( RtMidiError::DRIVER_ERROR, errorString_ );
        return;
    
    SeeLook's avatar
    SeeLook committed
      }
    
      connected_ = true;
    }
    
    void MidiOutWinMM :: closePort( void )
    {
      if ( connected_ ) {
        WinMidiData *data = static_cast<WinMidiData *> (apiData_);
        midiOutReset( data->outHandle );
        midiOutClose( data->outHandle );
        connected_ = false;
      }
    }
    
    
    void MidiOutWinMM :: openVirtualPort( std::string /*portName*/ )
    
    SeeLook's avatar
    SeeLook committed
    {
      // This function cannot be implemented for the Windows MM MIDI API.
      errorString_ = "MidiOutWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!";
    
      error( RtMidiError::WARNING, errorString_ );
    
    SeeLook's avatar
    SeeLook committed
    }
    
    void MidiOutWinMM :: sendMessage( std::vector<unsigned char> *message )
    {
    
    SeeLook's avatar
    SeeLook committed
      unsigned int nBytes = static_cast<unsigned int>(message->size());
      if ( nBytes == 0 ) {
        errorString_ = "MidiOutWinMM::sendMessage: message argument is empty!";
    
        error( RtMidiError::WARNING, errorString_ );
    
    SeeLook's avatar
    SeeLook committed
        return;
      }
    
      MMRESULT result;
      WinMidiData *data = static_cast<WinMidiData *> (apiData_);
      if ( message->at(0) == 0xF0 ) { // Sysex message
    
        // Allocate buffer for sysex data.
        char *buffer = (char *) malloc( nBytes );
        if ( buffer == NULL ) {
          errorString_ = "MidiOutWinMM::sendMessage: error allocating sysex message memory!";
    
          error( RtMidiError::MEMORY_ERROR, errorString_ );
          return;
    
    SeeLook's avatar
    SeeLook committed
        }
    
        // Copy data to buffer.
        for ( unsigned int i=0; i<nBytes; ++i ) buffer[i] = message->at(i);
    
        // Create and prepare MIDIHDR structure.
        MIDIHDR sysex;
        sysex.lpData = (LPSTR) buffer;
        sysex.dwBufferLength = nBytes;
        sysex.dwFlags = 0;
        result = midiOutPrepareHeader( data->outHandle,  &sysex, sizeof(MIDIHDR) ); 
        if ( result != MMSYSERR_NOERROR ) {
          free( buffer );
          errorString_ = "MidiOutWinMM::sendMessage: error preparing sysex header.";
    
          error( RtMidiError::DRIVER_ERROR, errorString_ );
          return;
    
    SeeLook's avatar
    SeeLook committed
        }
    
        // Send the message.
        result = midiOutLongMsg( data->outHandle, &sysex, sizeof(MIDIHDR) );
        if ( result != MMSYSERR_NOERROR ) {
          free( buffer );
          errorString_ = "MidiOutWinMM::sendMessage: error sending sysex message.";
    
          error( RtMidiError::DRIVER_ERROR, errorString_ );
          return;
    
    SeeLook's avatar
    SeeLook committed
        }
    
        // Unprepare the buffer and MIDIHDR.
        while ( MIDIERR_STILLPLAYING == midiOutUnprepareHeader( data->outHandle, &sysex, sizeof (MIDIHDR) ) ) Sleep( 1 );
        free( buffer );
      }
      else { // Channel or system message.
    
        // Make sure the message size isn't too big.
        if ( nBytes > 3 ) {
          errorString_ = "MidiOutWinMM::sendMessage: message size is greater than 3 bytes (and not sysex)!";
    
          error( RtMidiError::WARNING, errorString_ );
    
    SeeLook's avatar
    SeeLook committed
          return;
        }
    
        // Pack MIDI bytes into double word.
        DWORD packet;
        unsigned char *ptr = (unsigned char *) &packet;
        for ( unsigned int i=0; i<nBytes; ++i ) {
          *ptr = message->at(i);
          ++ptr;
        }
    
        // Send the message immediately.
        result = midiOutShortMsg( data->outHandle, packet );
        if ( result != MMSYSERR_NOERROR ) {
          errorString_ = "MidiOutWinMM::sendMessage: error sending MIDI message.";
    
          error( RtMidiError::DRIVER_ERROR, errorString_ );
    
    SeeLook's avatar
    SeeLook committed
        }
      }
    }
    
    #endif  // __WINDOWS_MM__
    
    
    
    //*********************************************************************//
    //  API: UNIX JACK
    
    SeeLook's avatar
    SeeLook committed
    //
    
    //  Written primarily by Alexander Svetalkin, with updates for delta
    //  time by Gary Scavone, April 2011.
    
    SeeLook's avatar
    SeeLook committed
    //
    
    //  *********************************************************************//
    
    SeeLook's avatar
    SeeLook committed
    
    
    SeeLook's avatar
    SeeLook committed
    
    
    // JACK header files
    #include <jack/jack.h>
    #include <jack/midiport.h>
    #include <jack/ringbuffer.h>
    
    SeeLook's avatar
    SeeLook committed
    
    
    #define JACK_RINGBUFFER_SIZE 16384 // Default size for ringbuffer
    
    SeeLook's avatar
    SeeLook committed
    
    
    struct JackMidiData {
      jack_client_t *client;
      jack_port_t *port;
      jack_ringbuffer_t *buffSize;
      jack_ringbuffer_t *buffMessage;
      jack_time_t lastTime;
      MidiInApi :: RtMidiInData *rtMidiIn;
      };
    
    SeeLook's avatar
    SeeLook committed
    
    
    //*********************************************************************//
    //  API: JACK
    //  Class Definitions: MidiInJack
    //*********************************************************************//
    
    SeeLook's avatar
    SeeLook committed
    
    
    static int jackProcessIn( jack_nframes_t nframes, void *arg )
    
    SeeLook's avatar
    SeeLook committed
    {
    
      JackMidiData *jData = (JackMidiData *) arg;
      MidiInApi :: RtMidiInData *rtData = jData->rtMidiIn;
      jack_midi_event_t event;
      jack_time_t time;
    
    SeeLook's avatar
    SeeLook committed
    
    
      // Is port created?
      if ( jData->port == NULL ) return 0;
      void *buff = jack_port_get_buffer( jData->port, nframes );
    
    SeeLook's avatar
    SeeLook committed
    
    
      // We have midi events in buffer
      int evCount = jack_midi_get_event_count( buff );
      for (int j = 0; j < evCount; j++) {
        MidiInApi::MidiMessage message;
        message.bytes.clear();
    
    SeeLook's avatar
    SeeLook committed
    
    
        jack_midi_event_get( &event, buff, j );
    
    SeeLook's avatar
    SeeLook committed
    
    
        for ( unsigned int i = 0; i < event.size; i++ )
          message.bytes.push_back( event.buffer[i] );
    
    SeeLook's avatar
    SeeLook committed
    
    
        // Compute the delta time.
        time = jack_get_time();
        if ( rtData->firstMessage == true )
          rtData->firstMessage = false;
        else
          message.timeStamp = ( time - jData->lastTime ) * 0.000001;
    
    SeeLook's avatar
    SeeLook committed
    
    
    SeeLook's avatar
    SeeLook committed
    
    
        if ( !rtData->continueSysex ) {
          if ( rtData->usingCallback ) {
            RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) rtData->userCallback;
            callback( message.timeStamp, &message.bytes, rtData->userData );
    
    SeeLook's avatar
    SeeLook committed
          }
    
          else {
            // As long as we haven't reached our queue size limit, push the message.
            if ( rtData->queue.size < rtData->queue.ringSize ) {
              rtData->queue.ring[rtData->queue.back++] = message;
              if ( rtData->queue.back == rtData->queue.ringSize )
                rtData->queue.back = 0;
              rtData->queue.size++;
            }
            else
              std::cerr << "\nMidiInJack: message queue limit reached!!\n\n";
    
    SeeLook's avatar
    SeeLook committed
          }
        }
      }
    
    
    SeeLook's avatar
    SeeLook committed
    
    
    MidiInJack :: MidiInJack( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit )
    
    SeeLook's avatar
    SeeLook committed
    {
      initialize( clientName );
    }
    
    
    void MidiInJack :: initialize( const std::string& clientName )
    
    SeeLook's avatar
    SeeLook committed
    {
    
      JackMidiData *data = new JackMidiData;
      apiData_ = (void *) data;
    
    SeeLook's avatar
    SeeLook committed
    
    
      data->rtMidiIn = &inputData_;
      data->port = NULL;
      data->client = NULL;
      this->clientName = clientName;
    
    SeeLook's avatar
    SeeLook committed
    
    
    SeeLook's avatar
    SeeLook committed
    }
    
    
    SeeLook's avatar
    SeeLook committed
    {
    
      JackMidiData *data = static_cast<JackMidiData *> (apiData_);
      if ( data->client )
        return;
    
    SeeLook's avatar
    SeeLook committed
    
      // Initialize JACK client
    
      if (( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) {
    
    SeeLook's avatar
    SeeLook committed
        errorString_ = "MidiInJack::initialize: JACK server not running?";
    
        error( RtMidiError::WARNING, errorString_ );
    
    SeeLook's avatar
    SeeLook committed
        return;
      }
    
      jack_set_process_callback( data->client, jackProcessIn, data );
      jack_activate( data->client );
    }
    
    MidiInJack :: ~MidiInJack()
    {
      JackMidiData *data = static_cast<JackMidiData *> (apiData_);
      closePort();
    
    
      if ( data->client )
        jack_client_close( data->client );
      delete data;
    
    SeeLook's avatar
    SeeLook committed
    }
    
    void MidiInJack :: openPort( unsigned int portNumber, const std::string portName )
    {
      JackMidiData *data = static_cast<JackMidiData *> (apiData_);
    
    
    SeeLook's avatar
    SeeLook committed
      // 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_ = "MidiInJack::openPort: JACK error creating port";
        error( RtMidiError::DRIVER_ERROR, errorString_ );
        return;
    
    SeeLook's avatar
    SeeLook committed
      }
    
      // Connecting to the output
      std::string name = getPortName( portNumber );
      jack_connect( data->client, name.c_str(), jack_port_name( data->port ) );
    }
    
    void MidiInJack :: openVirtualPort( const std::string portName )
    {
      JackMidiData *data = static_cast<JackMidiData *> (apiData_);
    
    
    SeeLook's avatar
    SeeLook committed
      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_ = "MidiInJack::openVirtualPort: JACK error creating virtual port";
    
        error( RtMidiError::DRIVER_ERROR, errorString_ );
    
    SeeLook's avatar
    SeeLook committed
      }
    }
    
    unsigned int MidiInJack :: getPortCount()
    {
      int count = 0;
      JackMidiData *data = static_cast<JackMidiData *> (apiData_);
    
    SeeLook's avatar
    SeeLook committed
    
      // 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 MidiInJack :: getPortName( unsigned int portNumber )
    {
      JackMidiData *data = static_cast<JackMidiData *> (apiData_);
      std::string retStr("");
    
    
    SeeLook's avatar
    SeeLook committed
      // 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_ = "MidiInJack::getPortName: no ports available!";
    
        error( RtMidiError::WARNING, errorString_ );
    
    SeeLook's avatar
    SeeLook committed
        return retStr;
      }
    
      if ( ports[portNumber] == NULL ) {
    
    SeeLook's avatar
    SeeLook committed
        ost << "MidiInJack::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
        errorString_ = ost.str();
    
        error( RtMidiError::WARNING, errorString_ );
    
    SeeLook's avatar
    SeeLook committed
      }
      else retStr.assign( ports[portNumber] );
    
      free( ports );
      return retStr;
    }
    
    void MidiInJack :: closePort()
    {
      JackMidiData *data = static_cast<JackMidiData *> (apiData_);
    
      if ( data->port == NULL ) return;
      jack_port_unregister( data->client, data->port );
      data->port = NULL;
    }
    
    //*********************************************************************//
    //  API: JACK
    //  Class Definitions: MidiOutJack
    //*********************************************************************//
    
    // Jack process callback
    
    static int jackProcessOut( jack_nframes_t nframes, void *arg )
    
    SeeLook's avatar
    SeeLook committed
    {
      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;
    }
    
    MidiOutJack :: MidiOutJack( const std::string clientName ) : MidiOutApi()
    {
      initialize( clientName );
    }
    
    void MidiOutJack :: initialize( const std::string& clientName )
    {
      JackMidiData *data = new JackMidiData;
    
    SeeLook's avatar
    SeeLook committed
    
      data->port = NULL;
    
      data->client = NULL;
      this->clientName = clientName;
    
      connect();
    }
    
    void MidiOutJack :: connect()
    {
      JackMidiData *data = static_cast<JackMidiData *> (apiData_);
      if ( data->client )
        return;
    
    SeeLook's avatar
    SeeLook committed
    
      // Initialize JACK client
    
      if (( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) {
    
    SeeLook's avatar
    SeeLook committed
        errorString_ = "MidiOutJack::initialize: JACK server not running?";
    
        error( RtMidiError::WARNING, errorString_ );
    
    SeeLook's avatar
    SeeLook committed
        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 );
    }
    
    MidiOutJack :: ~MidiOutJack()
    {
      JackMidiData *data = static_cast<JackMidiData *> (apiData_);
      closePort();
    
    
      if ( data->client ) {
        // Cleanup
        jack_client_close( data->client );
        jack_ringbuffer_free( data->buffSize );
        jack_ringbuffer_free( data->buffMessage );
      }
    
    SeeLook's avatar
    SeeLook committed
    
      delete data;
    }
    
    void MidiOutJack :: openPort( unsigned int portNumber, const std::string portName )
    {
      JackMidiData *data = static_cast<JackMidiData *> (apiData_);
    
    
    SeeLook's avatar
    SeeLook committed
      // 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_ = "MidiOutJack::openPort: JACK error creating port";
        error( RtMidiError::DRIVER_ERROR, errorString_ );
        return;
    
    SeeLook's avatar
    SeeLook committed
      }
    
      // Connecting to the output
      std::string name = getPortName( portNumber );
      jack_connect( data->client, jack_port_name( data->port ), name.c_str() );
    }
    
    void MidiOutJack :: openVirtualPort( const std::string portName )
    {
      JackMidiData *data = static_cast<JackMidiData *> (apiData_);
    
    
    SeeLook's avatar
    SeeLook committed
      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_ = "MidiOutJack::openVirtualPort: JACK error creating virtual port";
    
        error( RtMidiError::DRIVER_ERROR, errorString_ );
    
    SeeLook's avatar
    SeeLook committed
      }
    }
    
    unsigned int MidiOutJack :: getPortCount()
    {
      int count = 0;
      JackMidiData *data = static_cast<JackMidiData *> (apiData_);
    
    SeeLook's avatar
    SeeLook committed
    
      // 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 MidiOutJack :: getPortName( unsigned int portNumber )
    {
      JackMidiData *data = static_cast<JackMidiData *> (apiData_);
      std::string retStr("");
    
    
    SeeLook's avatar
    SeeLook committed
      // 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_ = "MidiOutJack::getPortName: no ports available!";
    
        error( RtMidiError::WARNING, errorString_ );
    
    SeeLook's avatar
    SeeLook committed
        return retStr;
      }
    
      if ( ports[portNumber] == NULL) {
    
    SeeLook's avatar
    SeeLook committed
        ost << "MidiOutJack::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
        errorString_ = ost.str();
    
        error( RtMidiError::WARNING, errorString_ );
    
    SeeLook's avatar
    SeeLook committed
      }
      else retStr.assign( ports[portNumber] );
    
      free( ports );
      return retStr;
    }
    
    void MidiOutJack :: closePort()
    {
      JackMidiData *data = static_cast<JackMidiData *> (apiData_);
    
      if ( data->port == NULL ) return;
      jack_port_unregister( data->client, data->port );
      data->port = NULL;
    }
    
    void MidiOutJack :: 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  // __UNIX_JACK__