summaryrefslogtreecommitdiff
path: root/RtMidi.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'RtMidi.cpp')
-rw-r--r--RtMidi.cpp148
1 files changed, 115 insertions, 33 deletions
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;