mirror of
https://github.com/thestk/stk
synced 2026-01-19 15:41:52 +00:00
Version 4.4.0
This commit is contained in:
committed by
Stephen Sinclair
parent
d199342e86
commit
eccd8c9981
402
src/RtMidi.cpp
402
src/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-2007 Gary P. Scavone
|
||||
Copyright (c) 2003-2009 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.7
|
||||
// RtMidi: Version 1.0.9
|
||||
|
||||
#include "RtMidi.h"
|
||||
#include <sstream>
|
||||
@@ -69,9 +69,9 @@ void RtMidi :: error( RtError::Type type )
|
||||
// Common RtMidiIn Definitions
|
||||
//*********************************************************************//
|
||||
|
||||
RtMidiIn :: RtMidiIn() : RtMidi()
|
||||
RtMidiIn :: RtMidiIn( const std::string clientName ) : RtMidi()
|
||||
{
|
||||
this->initialize();
|
||||
this->initialize( clientName );
|
||||
}
|
||||
|
||||
void RtMidiIn :: setCallback( RtMidiCallback callback, void *userData )
|
||||
@@ -144,9 +144,9 @@ double RtMidiIn :: getMessage( std::vector<unsigned char> *message )
|
||||
// Common RtMidiOut Definitions
|
||||
//*********************************************************************//
|
||||
|
||||
RtMidiOut :: RtMidiOut() : RtMidi()
|
||||
RtMidiOut :: RtMidiOut( const std::string clientName ) : RtMidi()
|
||||
{
|
||||
this->initialize();
|
||||
this->initialize( clientName );
|
||||
}
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ RtMidiOut :: RtMidiOut() : RtMidi()
|
||||
//*********************************************************************//
|
||||
|
||||
// API information found at:
|
||||
// - http://developer. apple .com/audio/pdf/coreaudio.pdf
|
||||
// - http://developer.apple.com/audio/pdf/coreaudio.pdf
|
||||
|
||||
#if defined(__MACOSX_CORE__)
|
||||
|
||||
@@ -187,20 +187,24 @@ void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef
|
||||
RtMidiIn::RtMidiInData *data = static_cast<RtMidiIn::RtMidiInData *> (procRef);
|
||||
CoreMidiData *apiData = static_cast<CoreMidiData *> (data->apiData);
|
||||
|
||||
bool continueSysex = false;
|
||||
unsigned char status;
|
||||
unsigned short nBytes, iByte, size;
|
||||
unsigned long long time;
|
||||
RtMidiIn::MidiMessage message;
|
||||
|
||||
bool& continueSysex = data->continueSysex;
|
||||
RtMidiIn::MidiMessage& message = data->message;
|
||||
|
||||
const MIDIPacket *packet = &list->packet[0];
|
||||
for ( unsigned int i=0; i<list->numPackets; ++i ) {
|
||||
|
||||
// My interpretation of the CoreMIDI documentation: all message
|
||||
// types, except sysex, are complete within a packet and there may
|
||||
// be several of them in a single packet. Sysex messages can be
|
||||
// broken across multiple packets but are bundled alone within a
|
||||
// packet. I'm assuming that sysex messages, if segmented, must
|
||||
// be complete within the same MIDIPacketList.
|
||||
// broken across multiple packets and PacketLists but are bundled
|
||||
// alone within each packet (these packets do not contain other
|
||||
// message types). If sysex messages are split across multiple
|
||||
// MIDIPacketLists, they must be handled by multiple calls to this
|
||||
// function.
|
||||
|
||||
nBytes = packet->length;
|
||||
if ( nBytes == 0 ) continue;
|
||||
@@ -220,12 +224,13 @@ void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef
|
||||
iByte = 0;
|
||||
if ( continueSysex ) {
|
||||
// We have a continuing, segmented sysex message.
|
||||
if ( !(data->ignoreFlags & 0x01) ) {
|
||||
if ( !( data->ignoreFlags & 0x01 ) ) {
|
||||
// If we're not ignoring sysex messages, copy the entire packet.
|
||||
for ( unsigned int j=0; j<nBytes; j++ )
|
||||
message.bytes.push_back( packet->data[j] );
|
||||
}
|
||||
if ( packet->data[nBytes] == 0xF7 ) continueSysex = false;
|
||||
continueSysex = packet->data[nBytes-1] != 0xF7;
|
||||
|
||||
if ( !continueSysex ) {
|
||||
// If not a continuing sysex message, invoke the user callback function or queue the message.
|
||||
if ( data->usingCallback && message.bytes.size() > 0 ) {
|
||||
@@ -259,7 +264,7 @@ void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef
|
||||
iByte = nBytes;
|
||||
}
|
||||
else size = nBytes - iByte;
|
||||
if ( packet->data[nBytes] == 0xF7 ) continueSysex = false;
|
||||
continueSysex = packet->data[nBytes-1] != 0xF7;
|
||||
}
|
||||
else if ( status < 0xF3 ) {
|
||||
if ( status == 0xF1 && (data->ignoreFlags & 0x02) ) {
|
||||
@@ -311,11 +316,11 @@ void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef
|
||||
}
|
||||
}
|
||||
|
||||
void RtMidiIn :: initialize( void )
|
||||
void RtMidiIn :: initialize( const std::string& clientName )
|
||||
{
|
||||
// Set up our client.
|
||||
MIDIClientRef client;
|
||||
OSStatus result = MIDIClientCreate( CFSTR("RtMidi Input Client"), NULL, NULL, &client );
|
||||
OSStatus result = MIDIClientCreate( CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ), NULL, NULL, &client );
|
||||
if ( result != noErr ) {
|
||||
errorString_ = "RtMidiIn::initialize: error creating OS-X MIDI client object.";
|
||||
error( RtError::DRIVER_ERROR );
|
||||
@@ -329,7 +334,7 @@ void RtMidiIn :: initialize( void )
|
||||
inputData_.apiData = (void *) data;
|
||||
}
|
||||
|
||||
void RtMidiIn :: openPort( unsigned int portNumber )
|
||||
void RtMidiIn :: openPort( unsigned int portNumber, const std::string portName )
|
||||
{
|
||||
if ( connected_ ) {
|
||||
errorString_ = "RtMidiIn::openPort: a valid connection already exists!";
|
||||
@@ -352,7 +357,9 @@ void RtMidiIn :: openPort( unsigned int portNumber )
|
||||
|
||||
MIDIPortRef port;
|
||||
CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
|
||||
OSStatus result = MIDIInputPortCreate( data->client, CFSTR("RtMidi MIDI Input Port"), midiInputCallback, (void *)&inputData_, &port );
|
||||
OSStatus result = MIDIInputPortCreate( data->client,
|
||||
CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
|
||||
midiInputCallback, (void *)&inputData_, &port );
|
||||
if ( result != noErr ) {
|
||||
MIDIClientDispose( data->client );
|
||||
errorString_ = "RtMidiIn::openPort: error creating OS-X MIDI input port.";
|
||||
@@ -427,6 +434,130 @@ unsigned int RtMidiIn :: getPortCount()
|
||||
return MIDIGetNumberOfSources();
|
||||
}
|
||||
|
||||
// This function was submitted by Douglas Casey Tucker and apparently
|
||||
// derived largely from PortMidi.
|
||||
CFStringRef EndpointName( MIDIEndpointRef endpoint, bool isExternal )
|
||||
{
|
||||
CFMutableStringRef result = CFStringCreateMutable( NULL, 0 );
|
||||
CFStringRef str;
|
||||
|
||||
// Begin with the endpoint's name.
|
||||
str = NULL;
|
||||
MIDIObjectGetStringProperty( endpoint, kMIDIPropertyName, &str );
|
||||
if ( str != NULL ) {
|
||||
CFStringAppend( result, str );
|
||||
CFRelease( str );
|
||||
}
|
||||
|
||||
MIDIEntityRef entity = NULL;
|
||||
MIDIEndpointGetEntity( endpoint, &entity );
|
||||
if ( entity == NULL )
|
||||
// probably virtual
|
||||
return result;
|
||||
|
||||
if ( CFStringGetLength( result ) == 0 ) {
|
||||
// endpoint name has zero length -- try the entity
|
||||
str = NULL;
|
||||
MIDIObjectGetStringProperty( entity, kMIDIPropertyName, &str );
|
||||
if ( str != NULL ) {
|
||||
CFStringAppend( result, str );
|
||||
CFRelease( str );
|
||||
}
|
||||
}
|
||||
// now consider the device's name
|
||||
MIDIDeviceRef device = NULL;
|
||||
MIDIEntityGetDevice( entity, &device );
|
||||
if ( device == NULL )
|
||||
return result;
|
||||
|
||||
str = NULL;
|
||||
MIDIObjectGetStringProperty( device, kMIDIPropertyName, &str );
|
||||
if ( CFStringGetLength( result ) == 0 ) {
|
||||
CFRelease( result );
|
||||
return str;
|
||||
}
|
||||
if ( str != NULL ) {
|
||||
// if an external device has only one entity, throw away
|
||||
// the endpoint name and just use the device name
|
||||
if ( isExternal && MIDIDeviceGetNumberOfEntities( device ) < 2 ) {
|
||||
CFRelease( result );
|
||||
return str;
|
||||
} else {
|
||||
if ( CFStringGetLength( str ) == 0 ) {
|
||||
CFRelease( str );
|
||||
return result;
|
||||
}
|
||||
// does the entity name already start with the device name?
|
||||
// (some drivers do this though they shouldn't)
|
||||
// if so, do not prepend
|
||||
if ( CFStringCompareWithOptions( result, /* endpoint name */
|
||||
str /* device name */,
|
||||
CFRangeMake(0, CFStringGetLength( str ) ), 0 ) != kCFCompareEqualTo ) {
|
||||
// prepend the device name to the entity name
|
||||
if ( CFStringGetLength( result ) > 0 )
|
||||
CFStringInsert( result, 0, CFSTR(" ") );
|
||||
CFStringInsert( result, 0, str );
|
||||
}
|
||||
CFRelease( str );
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// This function was submitted by Douglas Casey Tucker and apparently
|
||||
// derived largely from PortMidi.
|
||||
static CFStringRef ConnectedEndpointName( MIDIEndpointRef endpoint )
|
||||
{
|
||||
CFMutableStringRef result = CFStringCreateMutable( NULL, 0 );
|
||||
CFStringRef str;
|
||||
OSStatus err;
|
||||
int i;
|
||||
|
||||
// Does the endpoint have connections?
|
||||
CFDataRef connections = NULL;
|
||||
int nConnected = 0;
|
||||
bool anyStrings = false;
|
||||
err = MIDIObjectGetDataProperty( endpoint, kMIDIPropertyConnectionUniqueID, &connections );
|
||||
if ( connections != NULL ) {
|
||||
// It has connections, follow them
|
||||
// Concatenate the names of all connected devices
|
||||
nConnected = CFDataGetLength( connections ) / sizeof(MIDIUniqueID);
|
||||
if ( nConnected ) {
|
||||
const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections));
|
||||
for ( i=0; i<nConnected; ++i, ++pid ) {
|
||||
MIDIUniqueID id = EndianS32_BtoN( *pid );
|
||||
MIDIObjectRef connObject;
|
||||
MIDIObjectType connObjectType;
|
||||
err = MIDIObjectFindByUniqueID( id, &connObject, &connObjectType );
|
||||
if ( err == noErr ) {
|
||||
if ( connObjectType == kMIDIObjectType_ExternalSource ||
|
||||
connObjectType == kMIDIObjectType_ExternalDestination ) {
|
||||
// Connected to an external device's endpoint (10.3 and later).
|
||||
str = EndpointName( (MIDIEndpointRef)(connObject), true );
|
||||
} else {
|
||||
// Connected to an external device (10.2) (or something else, catch-
|
||||
str = NULL;
|
||||
MIDIObjectGetStringProperty( connObject, kMIDIPropertyName, &str );
|
||||
}
|
||||
if ( str != NULL ) {
|
||||
if ( anyStrings )
|
||||
CFStringAppend( result, CFSTR(", ") );
|
||||
else anyStrings = true;
|
||||
CFStringAppend( result, str );
|
||||
CFRelease( str );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CFRelease( connections );
|
||||
}
|
||||
if ( anyStrings )
|
||||
return result;
|
||||
|
||||
// Here, either the endpoint had no connections, or we failed to obtain names
|
||||
return EndpointName( endpoint, false );
|
||||
}
|
||||
|
||||
std::string RtMidiIn :: getPortName( unsigned int portNumber )
|
||||
{
|
||||
CFStringRef nameRef;
|
||||
@@ -441,7 +572,10 @@ std::string RtMidiIn :: getPortName( unsigned int portNumber )
|
||||
}
|
||||
portRef = MIDIGetSource( portNumber );
|
||||
|
||||
MIDIObjectGetStringProperty( portRef, kMIDIPropertyName, &nameRef );
|
||||
nameRef = ConnectedEndpointName(portRef);
|
||||
//MIDIObjectGetStringProperty( portRef, kMIDIPropertyName, &nameRef );
|
||||
// modified by D. Casey Tucker 2009-03-10
|
||||
|
||||
CFStringGetCString( nameRef, name, sizeof(name), 0);
|
||||
CFRelease( nameRef );
|
||||
std::string stringName = name;
|
||||
@@ -472,18 +606,19 @@ std::string RtMidiOut :: getPortName( unsigned int portNumber )
|
||||
}
|
||||
portRef = MIDIGetDestination( portNumber );
|
||||
|
||||
MIDIObjectGetStringProperty( portRef, kMIDIPropertyName, &nameRef );
|
||||
nameRef = ConnectedEndpointName(portRef);
|
||||
//MIDIObjectGetStringProperty( portRef, kMIDIPropertyName, &nameRef );
|
||||
CFStringGetCString( nameRef, name, sizeof(name), 0);
|
||||
CFRelease( nameRef );
|
||||
std::string stringName = name;
|
||||
return stringName;
|
||||
}
|
||||
|
||||
void RtMidiOut :: initialize( void )
|
||||
void RtMidiOut :: initialize( const std::string& clientName )
|
||||
{
|
||||
// Set up our client.
|
||||
MIDIClientRef client;
|
||||
OSStatus result = MIDIClientCreate( CFSTR("RtMidi Output Client"), NULL, NULL, &client );
|
||||
OSStatus result = MIDIClientCreate( CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ), NULL, NULL, &client );
|
||||
if ( result != noErr ) {
|
||||
errorString_ = "RtMidiOut::initialize: error creating OS-X MIDI client object.";
|
||||
error( RtError::DRIVER_ERROR );
|
||||
@@ -496,7 +631,7 @@ void RtMidiOut :: initialize( void )
|
||||
apiData_ = (void *) data;
|
||||
}
|
||||
|
||||
void RtMidiOut :: openPort( unsigned int portNumber )
|
||||
void RtMidiOut :: openPort( unsigned int portNumber, const std::string portName )
|
||||
{
|
||||
if ( connected_ ) {
|
||||
errorString_ = "RtMidiOut::openPort: a valid connection already exists!";
|
||||
@@ -519,7 +654,9 @@ void RtMidiOut :: openPort( unsigned int portNumber )
|
||||
|
||||
MIDIPortRef port;
|
||||
CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
|
||||
OSStatus result = MIDIOutputPortCreate( data->client, CFSTR("RtMidi Virtual MIDI Output Port"), &port );
|
||||
OSStatus result = MIDIOutputPortCreate( data->client,
|
||||
CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
|
||||
&port );
|
||||
if ( result != noErr ) {
|
||||
MIDIClientDispose( data->client );
|
||||
errorString_ = "RtMidiOut::openPort: error creating OS-X MIDI output port.";
|
||||
@@ -588,33 +725,59 @@ RtMidiOut :: ~RtMidiOut()
|
||||
|
||||
void RtMidiOut :: sendMessage( std::vector<unsigned char> *message )
|
||||
{
|
||||
// The CoreMidi documentation indicates a maximum PackList size of
|
||||
// 64K, so we may need to break long sysex messages into pieces and
|
||||
// send via separate lists.
|
||||
unsigned int nBytes = message->size();
|
||||
// Pad the buffer for extra (unknown) structure data.
|
||||
Byte buffer[nBytes+32];
|
||||
MIDIPacketList *pktlist = (MIDIPacketList *) buffer;
|
||||
MIDIPacket *curPacket = MIDIPacketListInit( pktlist );
|
||||
|
||||
MIDITimeStamp timeStamp = 0;
|
||||
curPacket = MIDIPacketListAdd( pktlist, sizeof(buffer), curPacket, timeStamp, nBytes, &message->at(0) );
|
||||
|
||||
CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
|
||||
|
||||
// Send to any destinations that may have connected to us.
|
||||
OSStatus result;
|
||||
if ( data->endpoint ) {
|
||||
result = MIDIReceived( data->endpoint, pktlist );
|
||||
if ( result != noErr ) {
|
||||
errorString_ = "RtMidiOut::sendMessage: error sending MIDI to virtual destinations.";
|
||||
error( RtError::WARNING );
|
||||
}
|
||||
if ( nBytes == 0 ) {
|
||||
errorString_ = "RtMidiOut::sendMessage: no data in message argument!";
|
||||
error( RtError::WARNING );
|
||||
return;
|
||||
}
|
||||
|
||||
// And send to an explicit destination port if we're connected.
|
||||
if ( connected_ ) {
|
||||
result = MIDISend( data->port, data->destinationId, pktlist );
|
||||
if ( result != noErr ) {
|
||||
errorString_ = "RtMidiOut::sendMessage: error sending MIDI message to port.";
|
||||
error( RtError::WARNING );
|
||||
if ( nBytes > 3 && ( message->at(0) != 0xF0 ) ) {
|
||||
errorString_ = "RtMidiOut::sendMessage: message format problem ... not sysex but > 3 bytes?";
|
||||
error( RtError::WARNING );
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int packetBytes, bytesLeft = nBytes;
|
||||
unsigned int messageIndex = 0;
|
||||
MIDITimeStamp timeStamp = 0;
|
||||
CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
|
||||
|
||||
while ( bytesLeft > 0 ) {
|
||||
|
||||
packetBytes = ( bytesLeft > 32736 ) ? 32736 : bytesLeft;
|
||||
Byte buffer[packetBytes + 32]; // extra memory for other structure variables
|
||||
MIDIPacketList *packetList = (MIDIPacketList *) buffer;
|
||||
MIDIPacket *curPacket = MIDIPacketListInit( packetList );
|
||||
|
||||
curPacket = MIDIPacketListAdd( packetList, packetBytes+32, curPacket, timeStamp, packetBytes, (const Byte *) &message->at( messageIndex ) );
|
||||
if ( !curPacket ) {
|
||||
errorString_ = "RtMidiOut::sendMessage: could not allocate packet list";
|
||||
error( RtError::DRIVER_ERROR );
|
||||
}
|
||||
messageIndex += packetBytes;
|
||||
bytesLeft -= packetBytes;
|
||||
|
||||
// Send to any destinations that may have connected to us.
|
||||
OSStatus result;
|
||||
if ( data->endpoint ) {
|
||||
result = MIDIReceived( data->endpoint, packetList );
|
||||
if ( result != noErr ) {
|
||||
errorString_ = "RtMidiOut::sendMessage: error sending MIDI to virtual destinations.";
|
||||
error( RtError::WARNING );
|
||||
}
|
||||
}
|
||||
|
||||
// And send to an explicit destination port if we're connected.
|
||||
if ( connected_ ) {
|
||||
result = MIDISend( data->port, data->destinationId, packetList );
|
||||
if ( result != noErr ) {
|
||||
errorString_ = "RtMidiOut::sendMessage: error sending MIDI message to port.";
|
||||
error( RtError::WARNING );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -637,6 +800,10 @@ void RtMidiOut :: sendMessage( std::vector<unsigned char> *message )
|
||||
// Thanks to Pedro Lopez-Cabanillas for help with the ALSA sequencer
|
||||
// time stamps and other assorted fixes!!!
|
||||
|
||||
// If you don't need timestamping for incoming MIDI events, define the
|
||||
// preprocessor definition AVOID_TIMESTAMPING to save resources
|
||||
// associated with the ALSA sequencer queues.
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
@@ -713,7 +880,9 @@ 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();
|
||||
if ( !continueSysex )
|
||||
message.bytes.clear();
|
||||
|
||||
switch ( ev->type ) {
|
||||
|
||||
case SND_SEQ_EVENT_PORT_SUBSCRIBED:
|
||||
@@ -723,8 +892,14 @@ extern "C" void *alsaMidiHandler( void *ptr )
|
||||
break;
|
||||
|
||||
case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
|
||||
#if defined(__RTMIDI_DEBUG__)
|
||||
std::cerr << "RtMidiIn::alsaMidiHandler: port connection has closed!\n";
|
||||
data->doInput = false;
|
||||
std::cout << "sender = " << (int) ev->data.connect.sender.client << ":"
|
||||
<< (int) ev->data.connect.sender.port
|
||||
<< ", dest = " << (int) ev->data.connect.dest.client << ":"
|
||||
<< (int) ev->data.connect.dest.port
|
||||
<< std::endl;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case SND_SEQ_EVENT_QFRAME: // MIDI time code
|
||||
@@ -768,7 +943,7 @@ extern "C" void *alsaMidiHandler( void *ptr )
|
||||
else
|
||||
message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] );
|
||||
|
||||
continueSysex = ( ev->type == SND_SEQ_EVENT_SYSEX && message.bytes.back() != 0xF7 );
|
||||
continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) );
|
||||
if ( continueSysex )
|
||||
break;
|
||||
|
||||
@@ -794,7 +969,7 @@ extern "C" void *alsaMidiHandler( void *ptr )
|
||||
snd_seq_free_event(ev);
|
||||
if ( message.bytes.size() == 0 ) continue;
|
||||
|
||||
if ( data->usingCallback ) {
|
||||
if ( data->usingCallback && !continueSysex ) {
|
||||
RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
|
||||
callback( message.timeStamp, &message.bytes, data->userData );
|
||||
}
|
||||
@@ -813,10 +988,10 @@ extern "C" void *alsaMidiHandler( void *ptr )
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RtMidiIn :: initialize( void )
|
||||
void RtMidiIn :: initialize( const std::string& clientName )
|
||||
{
|
||||
// Set up the ALSA sequencer client.
|
||||
snd_seq_t *seq;
|
||||
snd_seq_t *seq;
|
||||
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.";
|
||||
@@ -824,7 +999,7 @@ void RtMidiIn :: initialize( void )
|
||||
}
|
||||
|
||||
// Set client name.
|
||||
snd_seq_set_client_name(seq, "RtMidi Input Client");
|
||||
snd_seq_set_client_name( seq, clientName.c_str() );
|
||||
|
||||
// Save our api-specific connection information.
|
||||
AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData;
|
||||
@@ -834,6 +1009,7 @@ void RtMidiIn :: initialize( void )
|
||||
inputData_.apiData = (void *) data;
|
||||
|
||||
// Create the input queue
|
||||
#ifndef AVOID_TIMESTAMPING
|
||||
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;
|
||||
@@ -842,6 +1018,7 @@ void RtMidiIn :: initialize( void )
|
||||
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);
|
||||
#endif
|
||||
}
|
||||
|
||||
// This function is used to count or get the pinfo structure for a given port number.
|
||||
@@ -860,7 +1037,10 @@ unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int
|
||||
snd_seq_port_info_set_client( pinfo, client );
|
||||
snd_seq_port_info_set_port( pinfo, -1 );
|
||||
while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) {
|
||||
if ( !PORT_TYPE( pinfo, type ) ) continue;
|
||||
unsigned int atyp = snd_seq_port_info_get_type( pinfo );
|
||||
if ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) continue;
|
||||
unsigned int caps = snd_seq_port_info_get_capability( pinfo );
|
||||
if ( ( caps & type ) != type ) continue;
|
||||
if ( count == portNumber ) return 1;
|
||||
count++;
|
||||
}
|
||||
@@ -871,7 +1051,7 @@ unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RtMidiIn :: openPort( unsigned int portNumber )
|
||||
void RtMidiIn :: openPort( unsigned int portNumber, const std::string portName )
|
||||
{
|
||||
if ( connected_ ) {
|
||||
errorString_ = "RtMidiIn::openPort: a valid connection already exists!";
|
||||
@@ -910,10 +1090,12 @@ void RtMidiIn :: openPort( unsigned int portNumber )
|
||||
SND_SEQ_PORT_TYPE_MIDI_GENERIC |
|
||||
SND_SEQ_PORT_TYPE_APPLICATION );
|
||||
snd_seq_port_info_set_midi_channels(pinfo, 16);
|
||||
#ifndef AVOID_TIMESTAMPING
|
||||
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");
|
||||
#endif
|
||||
snd_seq_port_info_set_name(pinfo, portName.c_str() );
|
||||
data->vport = snd_seq_create_port(data->seq, pinfo);
|
||||
|
||||
if ( data->vport < 0 ) {
|
||||
@@ -935,8 +1117,10 @@ void RtMidiIn :: openPort( unsigned int portNumber )
|
||||
|
||||
if ( inputData_.doInput == false ) {
|
||||
// Start the input queue
|
||||
#ifndef AVOID_TIMESTAMPING
|
||||
snd_seq_start_queue( data->seq, data->queue_id, NULL );
|
||||
snd_seq_drain_output( data->seq );
|
||||
#endif
|
||||
// Start our MIDI input thread.
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
@@ -971,9 +1155,11 @@ void RtMidiIn :: openVirtualPort( std::string portName )
|
||||
SND_SEQ_PORT_TYPE_MIDI_GENERIC |
|
||||
SND_SEQ_PORT_TYPE_APPLICATION );
|
||||
snd_seq_port_info_set_midi_channels(pinfo, 16);
|
||||
#ifndef AVOID_TIMESTAMPING
|
||||
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);
|
||||
#endif
|
||||
snd_seq_port_info_set_name(pinfo, portName.c_str());
|
||||
data->vport = snd_seq_create_port(data->seq, pinfo);
|
||||
|
||||
@@ -985,8 +1171,10 @@ void RtMidiIn :: openVirtualPort( std::string portName )
|
||||
|
||||
if ( inputData_.doInput == false ) {
|
||||
// Start the input queue
|
||||
#ifndef AVOID_TIMESTAMPING
|
||||
snd_seq_start_queue( data->seq, data->queue_id, NULL );
|
||||
snd_seq_drain_output( data->seq );
|
||||
#endif
|
||||
// Start our MIDI input thread.
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
@@ -1013,8 +1201,10 @@ void RtMidiIn :: closePort( void )
|
||||
snd_seq_unsubscribe_port( data->seq, data->subscription );
|
||||
snd_seq_port_subscribe_free( data->subscription );
|
||||
// Stop the input queue
|
||||
#ifndef AVOID_TIMESTAMPING
|
||||
snd_seq_stop_queue( data->seq, data->queue_id, NULL );
|
||||
snd_seq_drain_output( data->seq );
|
||||
#endif
|
||||
connected_ = false;
|
||||
}
|
||||
}
|
||||
@@ -1033,8 +1223,10 @@ RtMidiIn :: ~RtMidiIn()
|
||||
|
||||
// Cleanup.
|
||||
if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport );
|
||||
#ifndef AVOID_TIMESTAMPING
|
||||
snd_seq_free_queue( data->seq, data->queue_id );
|
||||
snd_seq_close( data->seq );
|
||||
#endif
|
||||
delete data;
|
||||
}
|
||||
|
||||
@@ -1049,12 +1241,20 @@ unsigned int RtMidiIn :: getPortCount()
|
||||
|
||||
std::string RtMidiIn :: getPortName( unsigned int portNumber )
|
||||
{
|
||||
snd_seq_port_info_t *pinfo;
|
||||
snd_seq_port_info_alloca( &pinfo );
|
||||
snd_seq_client_info_t *cinfo;
|
||||
snd_seq_port_info_t *pinfo;
|
||||
snd_seq_client_info_alloca( &cinfo );
|
||||
snd_seq_port_info_alloca( &pinfo );
|
||||
|
||||
AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
|
||||
if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) {
|
||||
std::string stringName = std::string( snd_seq_port_info_get_name( pinfo ) );
|
||||
int cnum = snd_seq_port_info_get_client( pinfo );
|
||||
snd_seq_get_any_client_info( data->seq, cnum, cinfo );
|
||||
std::ostringstream os;
|
||||
os << snd_seq_client_info_get_name( cinfo );
|
||||
os << ":";
|
||||
os << snd_seq_port_info_get_port( pinfo );
|
||||
std::string stringName = os.str();
|
||||
return stringName;
|
||||
}
|
||||
|
||||
@@ -1080,12 +1280,20 @@ unsigned int RtMidiOut :: getPortCount()
|
||||
|
||||
std::string RtMidiOut :: getPortName( unsigned int portNumber )
|
||||
{
|
||||
snd_seq_port_info_t *pinfo;
|
||||
snd_seq_port_info_alloca( &pinfo );
|
||||
snd_seq_client_info_t *cinfo;
|
||||
snd_seq_port_info_t *pinfo;
|
||||
snd_seq_client_info_alloca( &cinfo );
|
||||
snd_seq_port_info_alloca( &pinfo );
|
||||
|
||||
AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
|
||||
if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) ) {
|
||||
std::string stringName = std::string( snd_seq_port_info_get_name( pinfo ) );
|
||||
int cnum = snd_seq_port_info_get_client(pinfo);
|
||||
snd_seq_get_any_client_info( data->seq, cnum, cinfo );
|
||||
std::ostringstream os;
|
||||
os << snd_seq_client_info_get_name(cinfo);
|
||||
os << ":";
|
||||
os << snd_seq_port_info_get_port(pinfo);
|
||||
std::string stringName = os.str();
|
||||
return stringName;
|
||||
}
|
||||
|
||||
@@ -1095,18 +1303,18 @@ std::string RtMidiOut :: getPortName( unsigned int portNumber )
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RtMidiOut :: initialize( void )
|
||||
void RtMidiOut :: initialize( const std::string& clientName )
|
||||
{
|
||||
// Set up the ALSA sequencer client.
|
||||
snd_seq_t *seq;
|
||||
int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_OUTPUT, 0);
|
||||
snd_seq_t *seq;
|
||||
int result = snd_seq_open( &seq, "default", SND_SEQ_OPEN_OUTPUT, SND_SEQ_NONBLOCK );
|
||||
if ( result < 0 ) {
|
||||
errorString_ = "RtMidiOut::initialize: error creating ALSA sequencer client object.";
|
||||
error( RtError::DRIVER_ERROR );
|
||||
}
|
||||
|
||||
// Set client name.
|
||||
snd_seq_set_client_name(seq, "RtMidi Output Client");
|
||||
snd_seq_set_client_name( seq, clientName.c_str() );
|
||||
|
||||
// Save our api-specific connection information.
|
||||
AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData;
|
||||
@@ -1131,7 +1339,7 @@ void RtMidiOut :: initialize( void )
|
||||
apiData_ = (void *) data;
|
||||
}
|
||||
|
||||
void RtMidiOut :: openPort( unsigned int portNumber )
|
||||
void RtMidiOut :: openPort( unsigned int portNumber, const std::string portName )
|
||||
{
|
||||
if ( connected_ ) {
|
||||
errorString_ = "RtMidiOut::openPort: a valid connection already exists!";
|
||||
@@ -1161,7 +1369,7 @@ void RtMidiOut :: openPort( unsigned int portNumber )
|
||||
sender.client = snd_seq_client_id( data->seq );
|
||||
|
||||
if ( data->vport < 0 ) {
|
||||
data->vport = snd_seq_create_simple_port( data->seq, "RtMidi Output",
|
||||
data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(),
|
||||
SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
|
||||
SND_SEQ_PORT_TYPE_MIDI_GENERIC );
|
||||
if ( data->vport < 0 ) {
|
||||
@@ -1423,7 +1631,7 @@ extern "C" void *irixMidiHandler( void *ptr )
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RtMidiIn :: initialize( void )
|
||||
void RtMidiIn :: initialize( const std::string& /*clientName*/ )
|
||||
{
|
||||
// Initialize the Irix MIDI system. At the moment, we will not
|
||||
// worry about a return value of zero (ports) because there is a
|
||||
@@ -1436,7 +1644,7 @@ void RtMidiIn :: initialize( void )
|
||||
inputData_.apiData = (void *) data;
|
||||
}
|
||||
|
||||
void RtMidiIn :: openPort( unsigned int portNumber )
|
||||
void RtMidiIn :: openPort( unsigned int portNumber, const std::string /*portName*/ )
|
||||
{
|
||||
if ( connected_ ) {
|
||||
errorString_ = "RtMidiIn::openPort: a valid connection already exists!";
|
||||
@@ -1564,7 +1772,7 @@ std::string RtMidiOut :: getPortName( unsigned int portNumber )
|
||||
return stringName;
|
||||
}
|
||||
|
||||
void RtMidiOut :: initialize( void )
|
||||
void RtMidiOut :: initialize( const std::string& /*clientName*/ )
|
||||
{
|
||||
// Initialize the Irix MIDI system. At the moment, we will not
|
||||
// worry about a return value of zero (ports) because there is a
|
||||
@@ -1576,7 +1784,7 @@ void RtMidiOut :: initialize( void )
|
||||
apiData_ = (void *) data;
|
||||
}
|
||||
|
||||
void RtMidiOut :: openPort( unsigned int portNumber )
|
||||
void RtMidiOut :: openPort( unsigned int portNumber, const std::string /*portName*/ )
|
||||
{
|
||||
if ( connected_ ) {
|
||||
errorString_ = "RtMidiOut::openPort: a valid connection already exists!";
|
||||
@@ -1754,21 +1962,31 @@ static void CALLBACK midiInputCallback( HMIDIOUT hmin,
|
||||
unsigned char *ptr = (unsigned char *) &midiMessage;
|
||||
for ( int i=0; i<nBytes; i++ ) apiData->message.bytes.push_back( *ptr++ );
|
||||
}
|
||||
else if ( !(data->ignoreFlags & 0x01) ) {
|
||||
// Sysex message and we're not ignoring it
|
||||
MIDIHDR *sysex = ( MIDIHDR *) midiMessage;
|
||||
for ( int i=0; i<(int)sysex->dwBytesRecorded; i++ )
|
||||
apiData->message.bytes.push_back( sysex->lpData[i] );
|
||||
else { // Sysex message ( MIM_LONGDATA )
|
||||
MIDIHDR *sysex = ( MIDIHDR *) midiMessage;
|
||||
if ( !( data->ignoreFlags & 0x01 ) ) {
|
||||
// Sysex message and we're not ignoring it
|
||||
for ( int i=0; i<(int)sysex->dwBytesRecorded; i++ )
|
||||
apiData->message.bytes.push_back( sysex->lpData[i] );
|
||||
}
|
||||
|
||||
// When the callback has to be unaffected (application closes),
|
||||
// it seems WinMM calls it with an empty sysex to de-queue the buffer
|
||||
// If the buffer is requeued afer that message, the PC suddenly reboots
|
||||
// after one or two minutes (JB).
|
||||
// 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->dwBytesRecorded > 0 ) {
|
||||
//if ( sysex->dwBytesRecorded > 0 ) {
|
||||
MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer, sizeof(MIDIHDR) );
|
||||
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 ) {
|
||||
@@ -1783,13 +2001,11 @@ static void CALLBACK midiInputCallback( HMIDIOUT hmin,
|
||||
std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n";
|
||||
}
|
||||
|
||||
// Clear the vector for the next input message. Note that doing
|
||||
// this here allows our code to work for sysex messages which are
|
||||
// segmented across multiple buffers.
|
||||
// Clear the vector for the next input message.
|
||||
apiData->message.bytes.clear();
|
||||
}
|
||||
|
||||
void RtMidiIn :: initialize( void )
|
||||
void RtMidiIn :: 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.
|
||||
@@ -1806,7 +2022,7 @@ void RtMidiIn :: initialize( void )
|
||||
data->message.bytes.clear(); // needs to be empty for first input message
|
||||
}
|
||||
|
||||
void RtMidiIn :: openPort( unsigned int portNumber )
|
||||
void RtMidiIn :: openPort( unsigned int portNumber, const std::string /*portName*/ )
|
||||
{
|
||||
if ( connected_ ) {
|
||||
errorString_ = "RtMidiIn::openPort: a valid connection already exists!";
|
||||
@@ -1840,8 +2056,8 @@ void RtMidiIn :: openPort( unsigned int portNumber )
|
||||
|
||||
// Allocate and init the sysex buffer.
|
||||
data->sysexBuffer = (MIDIHDR*) new char[ sizeof(MIDIHDR) ];
|
||||
data->sysexBuffer->lpData = new char[1024];
|
||||
data->sysexBuffer->dwBufferLength = 1024;
|
||||
data->sysexBuffer->lpData = new char[ RT_SYSEX_BUFFER_SIZE ];
|
||||
data->sysexBuffer->dwBufferLength = RT_SYSEX_BUFFER_SIZE;
|
||||
data->sysexBuffer->dwFlags = 0;
|
||||
|
||||
result = midiInPrepareHeader( data->inHandle, data->sysexBuffer, sizeof(MIDIHDR) );
|
||||
@@ -1970,7 +2186,7 @@ std::string RtMidiOut :: getPortName( unsigned int portNumber )
|
||||
return stringName;
|
||||
}
|
||||
|
||||
void RtMidiOut :: initialize( void )
|
||||
void RtMidiOut :: 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.
|
||||
@@ -1985,7 +2201,7 @@ void RtMidiOut :: initialize( void )
|
||||
apiData_ = (void *) data;
|
||||
}
|
||||
|
||||
void RtMidiOut :: openPort( unsigned int portNumber )
|
||||
void RtMidiOut :: openPort( unsigned int portNumber, const std::string /*portName*/ )
|
||||
{
|
||||
if ( connected_ ) {
|
||||
errorString_ = "RtMidiOut::openPort: a valid connection already exists!";
|
||||
|
||||
Reference in New Issue
Block a user