Sigrid Solveig Haflínudóttircrude openbsd+openal port3ce12953 years agoHistory
2325 lines · 72.4 KB · C++
RawDownload
/**********************************************************************//*! \class RtMidi    \brief An abstract base class for realtime MIDI input/output.     This class implements some common functionality for the realtime    MIDI input/output subclasses RtMidiIn and RtMidiOut.     RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/     RtMidi: realtime MIDI i/o C++ classes    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    (the "Software"), to deal in the Software without restriction,    including without limitation the rights to use, copy, modify, merge,    publish, distribute, sublicense, and/or sell copies of the Software,    and to permit persons to whom the Software is furnished to do so,    subject to the following conditions:     The above copyright notice and this permission notice shall be    included in all copies or substantial portions of the Software.     Any person wishing to distribute modifications to the Software is    requested to send the modifications to the original developer so that    they can be incorporated into the canonical version.     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*//**********************************************************************/ // RtMidi: Version 1.0.4, 14 October 2005 // chuck#include "rtmidi.h"#include <sstream>#include <iostream>#include <stdio.h>  //*********************************************************************////  Common RtMidi Definitions//*********************************************************************// RtMidi :: RtMidi()  : apiData_( 0 ), connected_( false ){} // chuckvoid RtMidi :: error( RtMidiError::Type type ){  if (type == RtMidiError::WARNING) {    std::cerr << "[chuck](via RtMidi): " << errorString_ << "\n";  }  else if (type == RtMidiError::DEBUG_WARNING) {#if defined(__RTMIDI_DEBUG__)    std::cerr << "[chuck](via RtMidi): " << errorString_ << "\n";#endif  }  else {    // std::cerr << "[chuck](via RtMidi): " << errorString_ << "\n";    throw RtMidiError( errorString_, type );  }}  //*********************************************************************////  Common RtMidiIn Definitions//*********************************************************************// RtMidiIn :: RtMidiIn() : RtMidi(){  this->initialize();} void RtMidiIn :: setCallback( RtMidiCallback callback, void *userData ){  if ( inputData_.usingCallback ) {    errorString_ = "RtMidiIn::setCallback: callback function is already set!";    error( RtMidiError::WARNING );    return;  }   if ( !callback ) {    errorString_ = "RtMidiIn::setCallback: callback function value is invalid!";    error( RtMidiError::WARNING );    return;  }   inputData_.userCallback = (void *) callback;  inputData_.userData = userData;  inputData_.usingCallback = true;} void RtMidiIn :: cancelCallback(){  if ( !inputData_.usingCallback ) {    errorString_ = "RtMidiIn::cancelCallback: no callback function was set!";    error( RtMidiError::WARNING );    return;  }   inputData_.userCallback = 0;  inputData_.userData = 0;  inputData_.usingCallback = false;} void RtMidiIn :: setQueueSizeLimit( unsigned int queueSize ){  inputData_.queueLimit = queueSize;} void RtMidiIn :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ){  inputData_.ignoreFlags = 0;  if ( midiSysex ) inputData_.ignoreFlags = 0x01;  if ( midiTime ) inputData_.ignoreFlags |= 0x02;  if ( midiSense ) inputData_.ignoreFlags |= 0x04;} double RtMidiIn :: getMessage( std::vector<unsigned char> *message ){  message->clear();   if ( inputData_.usingCallback ) {    errorString_ = "RtMidiIn::getNextMessage: user callback is currently set for this port.";    error( RtMidiError::WARNING );    return 0.0;  }   if ( inputData_.queue.size() == 0 ) return 0.0;   // Copy queued message to the vector pointer argument and then "pop" it.  std::vector<unsigned char> *bytes = &(inputData_.queue.front().bytes);  message->assign( bytes->begin(), bytes->end() );  double deltaTime = inputData_.queue.front().timeStamp;  inputData_.queue.pop();   return deltaTime;} //*********************************************************************////  Common RtMidiOut Definitions//*********************************************************************// RtMidiOut :: RtMidiOut() : RtMidi(){  this->initialize();}  //*********************************************************************////  API: Macintosh OS-X//*********************************************************************// // API information found at://   - http://developer. apple .com/audio/pdf/coreaudio.pdf #if defined(__MACOSX_CORE__) // The CoreMIDI API is based on the use of a callback function for// MIDI input.  We convert the system specific time stamps to delta// time values. // OS-X CoreMIDI header files.#include <CoreMIDI/CoreMIDI.h>#include <CoreAudio/HostTime.h> // CoreMIDI naming helper function prototypesstatic void readable_name(MIDIEndpointRef end, char *buffer, int bufsize);static int get_device_name(SInt32 uniqueid, char*buffer, int bufsize); // A structure to hold variables related to the CoreMIDI API// implementation.struct CoreMidiData {  MIDIClientRef client;  MIDIPortRef port;  MIDIEndpointRef endpoint;  MIDIEndpointRef destinationId;  unsigned long long lastTime;}; // ******// API: OS-X// helper functions// ***** // coreMIDI port naming helper function/*  This wraps up the code to take a passed-in endpoint and work out  a nice human-readable name for it.   For the moment, this function will return firstly the device name +  port name, or if we can work out what external devices are hooked  in, that name. It only grabs the very first connected device name.   NB. won't get connected devices correctly before 10.3.*/static void readable_name(MIDIEndpointRef end, char *buffer, int bufsize){    MIDIEntityRef ent = 0; // NULL;    MIDIDeviceRef dev = 0; //NULL;    int ii, count, length;//, ret;    SInt32 *idarray;    CFDataRef data = NULL;    CFStringRef s;     buffer[0] = '\0';     if( MIDIObjectGetDataProperty(end, kMIDIPropertyConnectionUniqueID, &data) == 0)    {        length = CFDataGetLength(data) / sizeof(SInt32);        idarray = (SInt32 *) CFDataGetBytePtr(data);        count = 0;        for (ii = 0; ii < length; ii++) {            if (bufsize < 3)                break;            if (count > 0) {                strcpy(buffer, ", ");                buffer += 2;                bufsize -= 2;            }             if (get_device_name(idarray[ii], buffer, bufsize) == 0) {                count++;                bufsize -= strlen(buffer);                buffer += strlen(buffer);            }        }         CFRelease(data);        if (count > 0)            return;    }     char * mid = buffer;    // build up the name of the enclosing device, if it can be found.    if (MIDIEndpointGetEntity(end, &ent) == 0) {        if (MIDIEntityGetDevice(ent, &dev) == 0) {            if (MIDIObjectGetStringProperty(dev, kMIDIPropertyName, &s) == 0) {                CFStringGetCString(s, buffer, bufsize, 0);                bufsize -= strlen(buffer) + 1;                buffer += strlen(buffer);                 CFRelease(s);            }        }    }     // ge    char buffy[128];    // Now add the port/endpoint name.    // no need to update buffer pointer. Last item.    if (MIDIObjectGetStringProperty(end, kMIDIPropertyName, &s) == 0) {        CFStringGetCString(s, buffy, 128, 0);        CFRelease(s);         // copy if different        if( strcmp( mid, buffy ) && strlen(buffy) < bufsize ) {            *buffer = ' ';            buffer++;            strcpy( buffer, buffy );        }    }}  // port naming helper function (External devices)/*  Uses the midiojectfindbyuniqueid function to hunt down the relevant  device and copies its name into the buffer provided. We don't know  what type of device we'll get back, so we first throw away any  external flags (We don't care), and then cast it up to device which  is the thing which has a useful name.   If it can't find one, return -1. 0 returned on success.*/static int get_device_name(SInt32 uniqueid, char *buffer, int bufsize){    int ret;    MIDIObjectRef object = 0; // NULL; // 1.3.1.0    MIDIObjectType type;     MIDIDeviceRef dev = 0; // NULL;    MIDIEntityRef ent = 0; // NULL;    MIDIEndpointRef end = 0; // NULL;    CFStringRef name = NULL;     ret = MIDIObjectFindByUniqueID(uniqueid, &object, &type);    if (ret < 0)        return -1;     // now clear any external flag.    if (type > 0)        type = (MIDIObjectType) (type & (~kMIDIObjectType_ExternalMask));     if (type == kMIDIObjectType_Device) {        dev = (MIDIDeviceRef) object;    } else  if (type == kMIDIObjectType_Entity) {        ent = (MIDIEntityRef) object;        if (MIDIEntityGetDevice(ent, &dev)) {            return -1;        }    } if (type == kMIDIObjectType_Source ||        type == kMIDIObjectType_Destination) {        end = (MIDIEndpointRef) object;        if (MIDIEndpointGetEntity(end, &ent)) {            return -1;        }        if (MIDIEntityGetDevice(ent, &dev)) {            return -1;        }    } else {        // unknown type        printf("Unknown type %d returned from findobject\n", (int) type);        CFRelease( &object ); // 1.3.1.0        return -1;    }     MIDIObjectGetStringProperty(dev, kMIDIPropertyName, &name);    CFStringGetCString(name, buffer, bufsize, 0);    CFRelease(name);     return 0;}     //*********************************************************************////  API: OS-X//  Class Definitions: RtMidiIn//*********************************************************************// 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;  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.     nBytes = packet->length;    if ( nBytes == 0 ) continue;     // Calculate time stamp.    message.timeStamp = 0.0;    if ( data->firstMessage )      data->firstMessage = false;    else {      time = packet->timeStamp;      time -= apiData->lastTime;      time = AudioConvertHostTimeToNanos( time );      message.timeStamp = time * 0.000000001;    }    apiData->lastTime = packet->timeStamp;     iByte = 0;    if ( continueSysex ) {      // We have a continuing, segmented sysex message.      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;      if ( !continueSysex ) {        // If not a continuing sysex message, invoke the user callback function or queue the message.        if ( data->usingCallback && message.bytes.size() > 0 ) {          RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;          callback( message.timeStamp, &message.bytes, data->userData );        }        else {          // As long as we haven't reached our queue size limit, push the message.          if ( data->queueLimit > data->queue.size() )            data->queue.push( message );          else            std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n";        }        message.bytes.clear();      }    }    else {      while ( iByte < nBytes ) {        size = 0;        // We are expecting that the next byte in the packet is a status byte.        status = packet->data[iByte];        if ( !(status & 0x80) ) break;        // Determine the number of bytes in the MIDI message.        if ( status < 0xC0 ) size = 3;        else if ( status < 0xE0 ) size = 2;        else if ( status < 0xF0 ) size = 3;        else if ( status == 0xF0 ) {          // A MIDI sysex          if ( data->ignoreFlags & 0x01 ) {            size = 0;            iByte = nBytes;          }          else size = nBytes - iByte;          if ( packet->data[nBytes] == 0xF7 ) continueSysex = false;        }        else if ( status < 0xF3 ) {          if ( status == 0xF1 && (data->ignoreFlags & 0x02) ) {            // A MIDI time code message and we're ignoring it.            size = 0;            iByte += 3;          }          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;          iByte += 1;        }        else size = 1;         // Copy the MIDI data to our vector.        if ( size ) {          message.bytes.assign( &packet->data[iByte], &packet->data[iByte+size] );          if ( !continueSysex ) {            // If not a continuing sysex message, invoke the user callback function or queue the message.            if ( data->usingCallback ) {              RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;              callback( message.timeStamp, &message.bytes, data->userData );            }            else {              // As long as we haven't reached our queue size limit, push the message.              if ( data->queueLimit > data->queue.size() )                data->queue.push( message );              else                std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n";            }            message.bytes.clear();          }          iByte += size;        }      }    }    packet = MIDIPacketNext(packet);  }} void RtMidiIn :: initialize( void ){  // Set up our client.  MIDIClientRef client;  OSStatus result = MIDIClientCreate( CFSTR("RtMidi Input Client"), NULL, NULL, &client );  if ( result != noErr ) {    errorString_ = "RtMidiIn::initialize: error creating OS-X MIDI client object.";    error( RtMidiError::DRIVER_ERROR );  }   // Save our api-specific connection information.  CoreMidiData *data = (CoreMidiData *) new CoreMidiData;  data->client = client;  data->endpoint = 0;  apiData_ = (void *) data;  inputData_.apiData = (void *) data;} void RtMidiIn :: openPort( unsigned int portNumber ){  if ( connected_ ) {    errorString_ = "RtMidiIn::openPort: a valid connection already exists!";    error( RtMidiError::WARNING );    return;  }   unsigned int nSrc = MIDIGetNumberOfSources();  if (nSrc < 1) {    errorString_ = "RtMidiIn::openPort: no MIDI input sources found!";

Lines 1–500 of 2325.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t