|
@@ -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__
|