summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--RtError.h37
-rw-r--r--RtMidi.cpp148
-rw-r--r--RtMidi.h4
-rw-r--r--doc/doxygen/footer.html2
-rw-r--r--doc/doxygen/tutorial.txt14
-rw-r--r--doc/release.txt11
-rw-r--r--readme6
-rw-r--r--tests/Makefile.in5
-rw-r--r--tests/qmidiin.cpp2
-rw-r--r--tests/sysextest.cpp141
10 files changed, 304 insertions, 66 deletions
diff --git a/RtError.h b/RtError.h
index ae1eaf9..cb1283d 100644
--- a/RtError.h
+++ b/RtError.h
@@ -4,7 +4,7 @@
The RtError class is quite simple but it does allow errors to be
"caught" by RtError::Type. See the RtAudio and RtMidi
- documentation to know which methods can "throw" an RtError.
+ documentation to know which methods can throw an RtError.
*/
/************************************************************************/
@@ -20,17 +20,17 @@ class RtError
public:
//! Defined RtError types.
enum Type {
- WARNING,
- DEBUG_WARNING,
- UNSPECIFIED,
- NO_DEVICES_FOUND,
- INVALID_DEVICE,
- INVALID_STREAM,
- MEMORY_ERROR,
- INVALID_PARAMETER,
- DRIVER_ERROR,
- SYSTEM_ERROR,
- THREAD_ERROR
+ WARNING, /*!< A non-critical error. */
+ DEBUG_WARNING, /*!< A non-critical error which might be useful for debugging. */
+ 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. */
+ DRIVER_ERROR, /*!< A system driver error occured. */
+ SYSTEM_ERROR, /*!< A system error occured. */
+ THREAD_ERROR /*!< A thread error occured. */
};
protected:
@@ -42,16 +42,19 @@ public:
RtError(const std::string& message, Type type = RtError::UNSPECIFIED) : message_(message), type_(type) {}
//! The destructor.
- virtual ~RtError(void) {}
+ virtual ~RtError(void) {};
- //! Prints "thrown" error message to stdout.
- virtual void printMessage(void) { std::cout << '\n' << message_ << "\n\n"; }
+ //! Prints thrown error message to stderr.
+ virtual void printMessage(void) { std::cerr << '\n' << message_ << "\n\n"; }
- //! Returns the "thrown" error message type.
+ //! Returns the thrown error message type.
virtual const Type& getType(void) { return type_; }
- //! Returns the "thrown" error message string.
+ //! Returns the thrown error message string.
virtual const std::string& getMessage(void) { return message_; }
+
+ //! Returns the thrown error message as a C string.
+ virtual const char *getMessageString(void) { return message_.c_str(); }
};
#endif
diff --git a/RtMidi.cpp b/RtMidi.cpp
index bdfb508..d82c9f1 100644
--- a/RtMidi.cpp
+++ b/RtMidi.cpp
@@ -8,7 +8,7 @@
RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/
RtMidi: realtime MIDI i/o C++ classes
- Copyright (c) 2003-2004 Gary P. Scavone
+ Copyright (c) 2003-2005 Gary P. Scavone
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
@@ -35,7 +35,7 @@
*/
/**********************************************************************/
-// RtMidi: Version 1.0.3, 22 November 2004
+// RtMidi: Version 1.0.4, 14 October 2005
#include "RtMidi.h"
#include <sstream>
@@ -270,6 +270,14 @@ void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef
else size = 3;
}
else if ( status == 0xF3 ) size = 2;
+ else if ( status == 0xF8 ) {
+ size = 1;
+ if ( data->ignoreFlags & 0x02 ) {
+ // A MIDI timing tick message and we're ignoring it.
+ size = 0;
+ iByte += 3;
+ }
+ }
else if ( status == 0xFE && (data->ignoreFlags & 0x04) ) {
// A MIDI active sensing message and we're ignoring it.
size = 0;
@@ -620,8 +628,10 @@ void RtMidiOut :: sendMessage( std::vector<unsigned char> *message )
#if defined(__LINUX_ALSASEQ__)
// The ALSA Sequencer API is based on the use of a callback function for
-// MIDI input. We convert the system specific time stamps to delta
-// time values.
+// MIDI input.
+//
+// Thanks to Pedro Lopez-Cabanillas for help with the ALSA sequencer
+// time stamps and other assorted fixes!!!
#include <pthread.h>
#include <sys/time.h>
@@ -640,6 +650,7 @@ struct AlsaMidiData {
unsigned char *buffer;
pthread_t thread;
unsigned long long lastTime;
+ int queue_id; // an input queue is needed to get timestamped events
};
#define PORT_TYPE( pinfo, bits ) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits))
@@ -656,11 +667,10 @@ extern "C" void *alsaMidiHandler( void *ptr )
long nBytes;
unsigned long long time, lastTime;
- unsigned char lastStatus = 0;
+ bool continueSysex = false;
RtMidiIn::MidiMessage message;
snd_seq_event_t *ev;
- struct timeval tv;
int result;
apiData->bufferSize = 32;
result = snd_midi_event_new( 0, &apiData->coder );
@@ -669,13 +679,14 @@ extern "C" void *alsaMidiHandler( void *ptr )
std::cerr << "\nRtMidiIn::alsaMidiHandler: error initializing MIDI event parser!\n\n";
return 0;
}
- unsigned char *buffer = (unsigned char *) malloc(apiData->bufferSize);
+ unsigned char *buffer = (unsigned char *) malloc( apiData->bufferSize );
if ( buffer == NULL ) {
data->doInput = false;
std::cerr << "\nRtMidiIn::alsaMidiHandler: error initializing buffer memory!\n\n";
return 0;
}
snd_midi_event_init( apiData->coder );
+ snd_midi_event_no_status( apiData->coder, 1 ); // suppress running status messages
while ( data->doInput ) {
@@ -699,7 +710,7 @@ extern "C" void *alsaMidiHandler( void *ptr )
// This is a bit weird, but we now have to decode an ALSA MIDI
// event (back) into MIDI bytes. We'll ignore non-MIDI types.
message.bytes.clear();
- switch (ev->type) {
+ switch ( ev->type ) {
case SND_SEQ_EVENT_PORT_SUBSCRIBED:
#if defined(__RTMIDI_DEBUG__)
@@ -715,6 +726,9 @@ extern "C" void *alsaMidiHandler( void *ptr )
case SND_SEQ_EVENT_QFRAME: // MIDI time code
if ( data->ignoreFlags & 0x02 ) break;
+ case SND_SEQ_EVENT_TICK: // MIDI timing tick
+ if ( data->ignoreFlags & 0x02 ) break;
+
case SND_SEQ_EVENT_SENSING: // Active sensing
if ( data->ignoreFlags & 0x04 ) break;
@@ -722,8 +736,8 @@ extern "C" void *alsaMidiHandler( void *ptr )
if ( (data->ignoreFlags & 0x01) ) break;
if ( ev->data.ext.len > apiData->bufferSize ) {
apiData->bufferSize = ev->data.ext.len;
- free(buffer);
- buffer = (unsigned char *) malloc(apiData->bufferSize);
+ free( buffer );
+ buffer = (unsigned char *) malloc( apiData->bufferSize );
if ( buffer == NULL ) {
data->doInput = false;
std::cerr << "\nRtMidiIn::alsaMidiHandler: error resizing buffer memory!\n\n";
@@ -739,25 +753,41 @@ extern "C" void *alsaMidiHandler( void *ptr )
#endif
break;
}
- message.bytes.assign( buffer, &buffer[nBytes] );
-
- // Save last status byte in case of running status.
- if ( message.bytes[0] & 0x80 ) lastStatus = message.bytes[0];
- else if ( lastStatus ) message.bytes.insert( message.bytes.begin(), lastStatus );
- // I found the ALSA sequencer documentation to be very inadequate,
- // especially regarding timestamps. So, I ignore the event
- // timestamp and use system time to determine ours.
+
+ // The ALSA sequencer has a maximum buffer size for MIDI sysex
+ // events of 256 bytes. If a device sends sysex messages larger
+ // than this, they are segmented into 256 byte chunks. So,
+ // we'll watch for this and concatenate sysex chunks into a
+ // single sysex message if necessary.
+ if ( !continueSysex )
+ message.bytes.assign( buffer, &buffer[nBytes] );
+ else
+ message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] );
+
+ if ( ev->type == SND_SEQ_EVENT_SYSEX && message.bytes.back() == 0xF7 )
+ continueSysex = false;
+ else {
+ continueSysex = true;
+ break;
+ }
+
+ // Calculate the time stamp:
message.timeStamp = 0.0;
- (void)gettimeofday(&tv, (struct timezone *)NULL);
- time = (tv.tv_sec * 1000000) + tv.tv_usec;
+
+ // Method 1: Use the system time.
+ //(void)gettimeofday(&tv, (struct timezone *)NULL);
+ //time = (tv.tv_sec * 1000000) + tv.tv_usec;
+
+ // Method 2: Use the ALSA sequencer event time data.
+ // (thanks to Pedro Lopez-Cabanillas!).
+ time = ( ev->time.time.tv_sec * 1000000 ) + ( ev->time.time.tv_nsec/1000 );
lastTime = time;
time -= apiData->lastTime;
apiData->lastTime = lastTime;
if ( data->firstMessage == true )
data->firstMessage = false;
- else {
+ else
message.timeStamp = time * 0.000001;
- }
}
snd_seq_free_event(ev);
@@ -776,6 +806,7 @@ extern "C" void *alsaMidiHandler( void *ptr )
}
}
+ if ( buffer ) free( buffer );
snd_midi_event_free( apiData->coder );
apiData->coder = 0;
return 0;
@@ -785,7 +816,7 @@ void RtMidiIn :: initialize( void )
{
// Set up the ALSA sequencer client.
snd_seq_t *seq;
- int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_INPUT, 0);
+ int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK);
if ( result < 0 ) {
errorString_ = "RtMidiIn::initialize: error creating ALSA sequencer input client object.";
error( RtError::DRIVER_ERROR );
@@ -800,6 +831,16 @@ void RtMidiIn :: initialize( void )
data->vport = -1;
apiData_ = (void *) data;
inputData_.apiData = (void *) data;
+
+ // Create the input queue
+ 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;
+ snd_seq_queue_tempo_alloca(&qtempo);
+ snd_seq_queue_tempo_set_tempo(qtempo, 600000);
+ 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);
}
// This function is used to count or get the pinfo structure for a given port number.
@@ -859,9 +900,21 @@ void RtMidiIn :: openPort( unsigned int portNumber )
sender.port = snd_seq_port_info_get_port( pinfo );
receiver.client = snd_seq_client_id( data->seq );
if ( data->vport < 0 ) {
- data->vport = snd_seq_create_simple_port( data->seq, "RtMidi Input",
- SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE,
- SND_SEQ_PORT_TYPE_MIDI_GENERIC );
+ snd_seq_port_info_set_client( pinfo, 0 );
+ snd_seq_port_info_set_port( pinfo, 0 );
+ snd_seq_port_info_set_capability( pinfo,
+ SND_SEQ_PORT_CAP_WRITE |
+ SND_SEQ_PORT_CAP_SUBS_WRITE );
+ snd_seq_port_info_set_type( pinfo,
+ SND_SEQ_PORT_TYPE_MIDI_GENERIC |
+ SND_SEQ_PORT_TYPE_APPLICATION );
+ snd_seq_port_info_set_midi_channels(pinfo, 16);
+ 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);
+ snd_seq_port_info_set_name(pinfo, "RtMidi Input");
+ data->vport = snd_seq_create_port(data->seq, pinfo);
+
if ( data->vport < 0 ) {
errorString_ = "RtMidiIn::openPort: ALSA error creating input port.";
error( RtError::DRIVER_ERROR );
@@ -874,19 +927,20 @@ void RtMidiIn :: openPort( unsigned int portNumber )
snd_seq_port_subscribe_malloc( &data->subscription );
snd_seq_port_subscribe_set_sender(data->subscription, &sender);
snd_seq_port_subscribe_set_dest(data->subscription, &receiver);
- snd_seq_port_subscribe_set_time_update(data->subscription, 1);
- snd_seq_port_subscribe_set_time_real(data->subscription, 1);
if ( snd_seq_subscribe_port(data->seq, data->subscription) ) {
errorString_ = "RtMidiIn::openPort: ALSA error making port connection.";
error( RtError::DRIVER_ERROR );
}
if ( inputData_.doInput == false ) {
+ // Start the input queue
+ snd_seq_start_queue( data->seq, data->queue_id, NULL );
+ snd_seq_drain_output( data->seq );
// Start our MIDI input thread.
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
- pthread_attr_setschedpolicy(&attr, SCHED_RR);
+ pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
inputData_.doInput = true;
int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_);
@@ -907,9 +961,20 @@ void RtMidiIn :: openVirtualPort()
{
AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
if ( data->vport < 0 ) {
- data->vport = snd_seq_create_simple_port( data->seq, "RtMidi Input",
- SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE,
- SND_SEQ_PORT_TYPE_MIDI_GENERIC );
+ snd_seq_port_info_t *pinfo;
+ snd_seq_port_info_alloca( &pinfo );
+ snd_seq_port_info_set_capability( pinfo,
+ SND_SEQ_PORT_CAP_WRITE |
+ SND_SEQ_PORT_CAP_SUBS_WRITE );
+ snd_seq_port_info_set_type( pinfo,
+ SND_SEQ_PORT_TYPE_MIDI_GENERIC |
+ SND_SEQ_PORT_TYPE_APPLICATION );
+ snd_seq_port_info_set_midi_channels(pinfo, 16);
+ 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);
+ snd_seq_port_info_set_name(pinfo, "RtMidi Input");
+ data->vport = snd_seq_create_port(data->seq, pinfo);
if ( data->vport < 0 ) {
errorString_ = "RtMidiIn::openVirtualPort: ALSA error creating virtual port.";
@@ -918,11 +983,14 @@ void RtMidiIn :: openVirtualPort()
}
if ( inputData_.doInput == false ) {
+ // Start the input queue
+ snd_seq_start_queue( data->seq, data->queue_id, NULL );
+ snd_seq_drain_output( data->seq );
// Start our MIDI input thread.
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
- pthread_attr_setschedpolicy(&attr, SCHED_RR);
+ pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
inputData_.doInput = true;
int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_);
@@ -943,6 +1011,9 @@ void RtMidiIn :: closePort( void )
AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
snd_seq_unsubscribe_port( data->seq, data->subscription );
snd_seq_port_subscribe_free( data->subscription );
+ // Stop the input queue
+ snd_seq_stop_queue( data->seq, data->queue_id, NULL );
+ snd_seq_drain_output( data->seq );
connected_ = false;
}
}
@@ -961,6 +1032,7 @@ RtMidiIn :: ~RtMidiIn()
// Cleanup.
if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport );
+ snd_seq_free_queue( data->seq, data->queue_id );
snd_seq_close( data->seq );
delete data;
}
@@ -1316,6 +1388,12 @@ extern "C" void *irixMidiHandler( void *ptr )
}
}
else if ( status == 0xF3 ) size = 2;
+ else if ( status == 0xF8 ) {
+ if ( !(data->ignoreFlags & 0x02) ) {
+ // A MIDI timing tick message and we're not ignoring it.
+ size = 1;
+ }
+ }
else if ( status == 0xFE ) { // MIDI active sensing
if ( !(data->ignoreFlags & 0x04) )
size = 1;
@@ -1657,6 +1735,10 @@ static void CALLBACK midiInputCallback( HMIDIOUT hmin,
nBytes = 3;
}
else if ( status == 0xF3 ) nBytes = 2;
+ else if ( status == 0xF8 && (data->ignoreFlags & 0x02) ) {
+ // A MIDI timing tick message and we're ignoring it.
+ return;
+ }
else if ( status == 0xFE && (data->ignoreFlags & 0x04) ) {
// A MIDI active sensing message and we're ignoring it.
return;
diff --git a/RtMidi.h b/RtMidi.h
index 89dd962..c80462c 100644
--- a/RtMidi.h
+++ b/RtMidi.h
@@ -8,7 +8,7 @@
RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/
RtMidi: realtime MIDI i/o C++ classes
- Copyright (c) 2003-2004 Gary P. Scavone
+ Copyright (c) 2003-2005 Gary P. Scavone
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
@@ -35,7 +35,7 @@
*/
/**********************************************************************/
-// RtMidi: Version 1.0.3, 22 November 2004
+// RtMidi: Version 1.0.4, 14 October 2005
#ifndef RTMIDI_H
#define RTMIDI_H
diff --git a/doc/doxygen/footer.html b/doc/doxygen/footer.html
index 12486d2..c8a06bb 100644
--- a/doc/doxygen/footer.html
+++ b/doc/doxygen/footer.html
@@ -1,7 +1,7 @@
<HR>
<table><tr><td><img src="../images/mcgill.gif" width=165></td>
- <td>&copy;2003-2004 Gary P. Scavone, McGill University. All Rights Reserved.<br>
+ <td>&copy;2003-2005 Gary P. Scavone, McGill University. All Rights Reserved.<br>
Maintained by Gary P. Scavone, gary at music.mcgill.ca</td></tr>
</table>
diff --git a/doc/doxygen/tutorial.txt b/doc/doxygen/tutorial.txt
index 326f641..8af317e 100644
--- a/doc/doxygen/tutorial.txt
+++ b/doc/doxygen/tutorial.txt
@@ -1,12 +1,10 @@
/*! \mainpage The RtMidi Tutorial
-<BODY BGCOLOR="white">
-
-<CENTER>\ref intro &nbsp;&nbsp; \ref download &nbsp;&nbsp; \ref start &nbsp;&nbsp; \ref error &nbsp;&nbsp; \ref probing &nbsp;&nbsp; \ref output &nbsp;&nbsp; \ref input &nbsp;&nbsp; \ref virtual &nbsp;&nbsp; \ref compiling &nbsp;&nbsp; \ref debug &nbsp;&nbsp; \ref apinotes &nbsp;&nbsp; \ref license</CENTER>
+<CENTER>\ref intro &nbsp;&nbsp; \ref download &nbsp;&nbsp; \ref start &nbsp;&nbsp; \ref error &nbsp;&nbsp; \ref probing &nbsp;&nbsp; \ref output &nbsp;&nbsp; \ref input &nbsp;&nbsp; \ref virtual &nbsp;&nbsp; \ref compiling &nbsp;&nbsp; \ref debug &nbsp;&nbsp; \ref apinotes &nbsp;&nbsp; \ref acknowledge &nbsp;&nbsp; \ref license</CENTER>
\section intro Introduction
-RtMidi is a set of C++ classes (RtMidiIn and RtMidiOut) which provide 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), 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:
<UL>
<LI>object oriented C++ design</LI>
@@ -19,7 +17,7 @@ MIDI input and output functionality are separated into two classes, RtMidiIn and
\section download Download
-Latest Release (22 November 2004): <A href="http://music.mcgill.ca/~gary/rtmidi/release/rtmidi-1.0.3.tar.gz">Version 1.0.3</A>
+Latest Release (14 October 2005): <A href="http://music.mcgill.ca/~gary/rtmidi/release/rtmidi-1.0.4.tar.gz">Version 1.0.4</A>
\section start Getting Started
@@ -384,10 +382,14 @@ The Irix version of RtMidi was written and tested on an SGI Indy running Irix ve
The Windows Multimedia library MIDI calls used in RtMidi do not make use of streaming functionality. RtMidi was originally developed with Visual C++ version 6.0.
+\section acknowledge Acknowledgements
+
+Many thanks to Pedro Lopez-Cabanillas for his help with the ALSA sequencer API!
+
\section license License
RtMidi: realtime MIDI i/o C++ classes<BR>
- Copyright (c) 2003-2004 Gary P. Scavone
+ Copyright (c) 2003-2005 Gary P. Scavone
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
diff --git a/doc/release.txt b/doc/release.txt
index d6e7211..5522be7 100644
--- a/doc/release.txt
+++ b/doc/release.txt
@@ -1,6 +1,13 @@
-RtMidi - a set of C++ classes which 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 provides a common API for realtime MIDI input/output across Linux (ALSA), SGI, Macintosh OS X (CoreMidi), and Windows (Multimedia) operating systems.
-By Gary P. Scavone, 2003-2004.
+By Gary P. Scavone, 2003-2005.
+
+v1.0.4: (14 October 2005)
+- added check for status byte == 0xF8 if ignoring timing messages
+- changed pthread attribute to SCHED_OTHER (from SCHED_RR) to avoid thread problem when realtime cababilities are not enabled.
+- now using ALSA sequencer time stamp information (thanks to Pedro Lopez-Cabanillas)
+- fixed memory leak in ALSA implementation
+- now concatenate segmented sysex messages in ALSA
v1.0.3: (22 November 2004)
- added common pure virtual functions to RtMidi abstract base class
diff --git a/readme b/readme
index 72e80c0..6000b27 100644
--- a/readme
+++ b/readme
@@ -1,6 +1,6 @@
-RtMidi - a set of C++ classes which 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 provides a common API for realtime MIDI input/output across Linux (ALSA), SGI, Macintosh OS X (CoreMidi), and Windows (Multimedia) operating systems.
-By Gary P. Scavone, 2003-2004.
+By Gary P. Scavone, 2003-2005.
This distribution of RtMidi contains the following:
@@ -30,7 +30,7 @@ LEGAL AND ETHICAL:
The RtMidi license is similar to the the MIT License, with the added "feature" that modifications be sent to the developer.
RtMidi: realtime MIDI i/o C++ classes
- Copyright (c) 2003-2004 Gary P. Scavone
+ Copyright (c) 2003-2005 Gary P. Scavone
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
diff --git a/tests/Makefile.in b/tests/Makefile.in
index 4acc864..72bd19d 100644
--- a/tests/Makefile.in
+++ b/tests/Makefile.in
@@ -1,6 +1,6 @@
### RtMidi tests Makefile - for various flavors of unix
-PROGRAMS = midiinfo midiout qmidiin cmidiin
+PROGRAMS = midiinfo midiout qmidiin cmidiin sysextest
RM = /bin/rm
SRC_PATH = ../
INCLUDE = ../
@@ -34,6 +34,9 @@ qmidiin : qmidiin.cpp $(OBJECTS)
cmidiin : cmidiin.cpp $(OBJECTS)
$(CC) $(CFLAGS) $(DEFS) -o cmidiin cmidiin.cpp $(OBJECT_PATH)/RtMidi.o $(LIBRARY)
+sysextest : sysextest.cpp $(OBJECTS)
+ $(CC) $(CFLAGS) $(DEFS) -o sysextest sysextest.cpp $(OBJECT_PATH)/RtMidi.o $(LIBRARY)
+
clean :
-rm $(OBJECT_PATH)/*.o
-rm $(PROGRAMS)
diff --git a/tests/qmidiin.cpp b/tests/qmidiin.cpp
index 639820a..efdf218 100644
--- a/tests/qmidiin.cpp
+++ b/tests/qmidiin.cpp
@@ -26,7 +26,7 @@ static void finish(int ignore){ done = true; }
void usage(void) {
// Error function in case of incorrect command-line
// argument specifications.
- std::cout << "\nuseage: qmidiin <port>\n";
+ std::cout << "\nusage: qmidiin <port>\n";
std::cout << " where port = the device to use (default = 0).\n\n";
exit(0);
}
diff --git a/tests/sysextest.cpp b/tests/sysextest.cpp
new file mode 100644
index 0000000..b61170d
--- /dev/null
+++ b/tests/sysextest.cpp
@@ -0,0 +1,141 @@
+//*****************************************//
+// sysextest.cpp
+// by Gary Scavone, 2003-2005.
+//
+// Simple program to test MIDI sysex sending and receiving.
+//
+//*****************************************//
+
+#include <iostream>
+#include "RtMidi.h"
+
+void usage(void) {
+ std::cout << "\nuseage: sysextest N\n";
+ std::cout << " where N = length of sysex message to send / receive.\n\n";
+ exit(0);
+}
+
+// Platform-dependent sleep routines.
+#if defined(__WINDOWS_MM__)
+ #include <windows.h>
+ #define SLEEP( milliseconds ) Sleep( (DWORD) milliseconds )
+#else // Unix variants
+ #include <unistd.h>
+ #define SLEEP( milliseconds ) usleep( (unsigned long) (milliseconds * 1000.0) )
+#endif
+
+// This function should be embedded in a try/catch block in case of
+// an exception. It offers the user a choice of MIDI ports to open.
+// It returns false if there are no ports available.
+bool chooseMidiPort( RtMidi *rtmidi );
+
+int main(int argc, char *argv[])
+{
+ RtMidiOut *midiout = 0;
+ RtMidiIn *midiin = 0;
+ std::vector<unsigned char> message;
+ double stamp;
+ unsigned int i, nBytes;
+
+ // Minimal command-line check.
+ if ( argc != 2 ) usage();
+ nBytes = (unsigned int) atoi( argv[1] );
+
+ // RtMidiOut and RtMidiIn constructors
+ try {
+ midiout = new RtMidiOut();
+ midiin = new RtMidiIn();
+ }
+ catch (RtError &error) {
+ error.printMessage();
+ goto cleanup;
+ }
+
+ // Don't ignore sysex, timing, or active sensing messages.
+ midiin->ignoreTypes( false, true, true );
+
+ // Call function to select ports
+ try {
+ if ( chooseMidiPort( midiin ) == false ) goto cleanup;
+ if ( chooseMidiPort( midiout ) == false ) goto cleanup;
+ }
+ catch (RtError &error) {
+ error.printMessage();
+ goto cleanup;
+ }
+
+ // Create a long sysex messages of numbered bytes and send it out.
+ message.push_back( 240 );
+ for ( i=0; i<nBytes; i++ )
+ message.push_back( i % 128 );
+ message.push_back( 247 );
+ midiout->sendMessage( &message );
+
+ SLEEP( 50 ); // pause a little
+
+ // Look for one message (hopefully the previously sent sysex if the
+ // ports were connected together) and print out the values.
+ stamp = midiin->getMessage( &message );
+ nBytes = message.size();
+ for ( i=0; i<nBytes; i++ )
+ std::cout << "Byte " << i << " = " << (int)message[i] << std::endl;
+
+ // Clean up
+ cleanup:
+ delete midiout;
+ delete midiin;
+
+ return 0;
+}
+
+bool chooseMidiPort( RtMidi *rtmidi )
+{
+ bool isInput = false;
+ if ( typeid( *rtmidi ) == typeid( RtMidiIn ) )
+ isInput = true;
+
+ if ( isInput )
+ std::cout << "\nWould you like to open a virtual input port? [y/N] ";
+ else
+ std::cout << "\nWould you like to open a virtual output port? [y/N] ";
+
+ std::string keyHit;
+ std::getline( std::cin, keyHit );
+ if ( keyHit == "y" ) {
+ rtmidi->openVirtualPort();
+ return true;
+ }
+
+ std::string portName;
+ unsigned int i = 0, nPorts = rtmidi->getPortCount();
+ if ( nPorts == 0 ) {
+ if ( isInput )
+ std::cout << "No input ports available!" << std::endl;
+ else
+ std::cout << "No output ports available!" << std::endl;
+ return false;
+ }
+
+ if ( nPorts == 1 ) {
+ std::cout << "\nOpening " << rtmidi->getPortName() << std::endl;
+ }
+ else {
+ for ( i=0; i<nPorts; i++ ) {
+ portName = rtmidi->getPortName(i);
+ if ( isInput )
+ std::cout << " Input port #" << i << ": " << portName << '\n';
+ else
+ std::cout << " Output port #" << i << ": " << portName << '\n';
+ }
+
+ do {
+ std::cout << "\nChoose a port number: ";
+ std::cin >> i;
+ } while ( i >= nPorts );
+ }
+
+ std::cout << "\n";
+ rtmidi->openPort( i );
+
+ return true;
+}