📄 rtmidi.cpp
字号:
}}RtMidiIn :: ~RtMidiIn(){ // Close a connection if it exists. closePort(); // Cleanup. CoreMidiData *data = static_cast<CoreMidiData *> (apiData_); MIDIClientDispose( data->client ); if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); delete data;}unsigned int RtMidiIn :: getPortCount(){ return MIDIGetNumberOfSources();}std::string RtMidiIn :: getPortName( unsigned int portNumber ){ CFStringRef nameRef; MIDIEndpointRef portRef; std::ostringstream ost; char name[128]; if ( portNumber >= MIDIGetNumberOfSources() ) { ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; errorString_ = ost.str(); error( RtError::INVALID_PARAMETER ); } portRef = MIDIGetSource( portNumber ); MIDIObjectGetStringProperty( portRef, kMIDIPropertyName, &nameRef ); CFStringGetCString( nameRef, name, sizeof(name), 0); CFRelease( nameRef ); std::string stringName = name; return stringName;}//*********************************************************************//// API: OS-X// Class Definitions: RtMidiOut//*********************************************************************//unsigned int RtMidiOut :: getPortCount(){ return MIDIGetNumberOfDestinations();}std::string RtMidiOut :: getPortName( unsigned int portNumber ){ CFStringRef nameRef; MIDIEndpointRef portRef; std::ostringstream ost; char name[128]; if ( portNumber >= MIDIGetNumberOfDestinations() ) { ost << "RtMidiOut::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; errorString_ = ost.str(); error( RtError::INVALID_PARAMETER ); } portRef = MIDIGetDestination( portNumber ); MIDIObjectGetStringProperty( portRef, kMIDIPropertyName, &nameRef ); CFStringGetCString( nameRef, name, sizeof(name), 0); CFRelease( nameRef ); std::string stringName = name; return stringName;}void RtMidiOut :: initialize( void ){ // Set up our client. MIDIClientRef client; OSStatus result = MIDIClientCreate( CFSTR("RtMidi Output Client"), NULL, NULL, &client ); if ( result != noErr ) { errorString_ = "RtMidiOut::initialize: error creating OS-X MIDI client object."; error( RtError::DRIVER_ERROR ); } // Save our api-specific connection information. CoreMidiData *data = (CoreMidiData *) new CoreMidiData; data->client = client; data->endpoint = 0; apiData_ = (void *) data;}void RtMidiOut :: openPort( unsigned int portNumber ){ if ( connected_ ) { errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; error( RtError::WARNING ); return; } unsigned int nDest = MIDIGetNumberOfDestinations(); if (nDest < 1) { errorString_ = "RtMidiOut::openPort: no MIDI output destinations found!"; error( RtError::NO_DEVICES_FOUND ); } std::ostringstream ost; if ( portNumber >= nDest ) { ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; errorString_ = ost.str(); error( RtError::INVALID_PARAMETER ); } MIDIPortRef port; CoreMidiData *data = static_cast<CoreMidiData *> (apiData_); OSStatus result = MIDIOutputPortCreate( data->client, CFSTR("RtMidi Virtual MIDI Output Port"), &port ); if ( result != noErr ) { MIDIClientDispose( data->client ); errorString_ = "RtMidiOut::openPort: error creating OS-X MIDI output port."; error( RtError::DRIVER_ERROR ); } // Get the desired output port identifier. MIDIEndpointRef destination = MIDIGetDestination( portNumber ); if ( destination == NULL ) { MIDIPortDispose( port ); MIDIClientDispose( data->client ); errorString_ = "RtMidiOut::openPort: error getting MIDI output destination reference."; error( RtError::DRIVER_ERROR ); } // Save our api-specific connection information. data->port = port; data->destinationId = destination; connected_ = true;}void RtMidiOut :: closePort( void ){ if ( connected_ ) { CoreMidiData *data = static_cast<CoreMidiData *> (apiData_); MIDIPortDispose( data->port ); connected_ = false; }}void RtMidiOut :: openVirtualPort(){ CoreMidiData *data = static_cast<CoreMidiData *> (apiData_); if ( data->endpoint ) { errorString_ = "RtMidiOut::openVirtualPort: a virtual output port already exists!"; error( RtError::WARNING ); return; } // Create a virtual MIDI output source. MIDIEndpointRef endpoint; OSStatus result = MIDISourceCreate( data->client, CFSTR("RtMidi Output"), &endpoint ); if ( result != noErr ) { errorString_ = "RtMidiOut::initialize: error creating OS-X virtual MIDI source."; error( RtError::DRIVER_ERROR ); } // Save our api-specific connection information. data->endpoint = endpoint;}RtMidiOut :: ~RtMidiOut(){ // Close a connection if it exists. closePort(); // Cleanup. CoreMidiData *data = static_cast<CoreMidiData *> (apiData_); MIDIClientDispose( data->client ); if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); delete data;}void RtMidiOut :: sendMessage( std::vector<unsigned char> *message ){ 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 ); } } // 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 ); } }}#endif // __MACOSX_CORE__//*********************************************************************//// API: LINUX ALSA SEQUENCER//*********************************************************************//// API information found at:// - http://www.alsa-project.org/documentation.php#Library#if defined(__LINUX_ALSASEQ__)// The ALSA Sequencer API is based on the use of a callback function for// 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>// ALSA header file.#include <alsa/asoundlib.h>// A structure to hold variables related to the ALSA API// implementation.struct AlsaMidiData { snd_seq_t *seq; int vport; snd_seq_port_subscribe_t *subscription; snd_midi_event_t *coder; unsigned int bufferSize; 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))//*********************************************************************//// API: LINUX ALSA// Class Definitions: RtMidiIn//*********************************************************************//extern "C" void *alsaMidiHandler( void *ptr ){ RtMidiIn::RtMidiInData *data = static_cast<RtMidiIn::RtMidiInData *> (ptr); AlsaMidiData *apiData = static_cast<AlsaMidiData *> (data->apiData); long nBytes; unsigned long long time, lastTime; bool continueSysex = false; RtMidiIn::MidiMessage message; snd_seq_event_t *ev; int result; apiData->bufferSize = 32; result = snd_midi_event_new( 0, &apiData->coder ); if ( result < 0 ) { data->doInput = false; std::cerr << "\nRtMidiIn::alsaMidiHandler: error initializing MIDI event parser!\n\n"; return 0; } 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 ) { if ( snd_seq_event_input_pending( apiData->seq, 1 ) == 0 ) { // No data pending ... sleep a bit. usleep( 1000 ); continue; } // If here, there should be data. result = snd_seq_event_input( apiData->seq, &ev ); if ( result == -ENOSPC ) { std::cerr << "\nRtMidiIn::alsaMidiHandler: MIDI input buffer overrun!\n\n"; continue; } else if ( result <= 0 ) { std::cerr << "RtMidiIn::alsaMidiHandler: unknown MIDI input error!\n"; continue; } // 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 ) { case SND_SEQ_EVENT_PORT_SUBSCRIBED:#if defined(__RTMIDI_DEBUG__) std::cout << "RtMidiIn::alsaMidiHandler: port connection made!\n";#endif break; case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: std::cerr << "RtMidiIn::alsaMidiHandler: port connection has closed!\n"; data->doInput = false; break; 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; case SND_SEQ_EVENT_SYSEX: 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 ); if ( buffer == NULL ) { data->doInput = false; std::cerr << "\nRtMidiIn::alsaMidiHandler: error resizing buffer memory!\n\n"; break; } } default: nBytes = snd_midi_event_decode( apiData->coder, buffer, apiData->bufferSize, ev ); if ( nBytes <= 0 ) {#if defined(__RTMIDI_DEBUG__) std::cerr << "\nRtMidiIn::alsaMidiHandler: event parsing error or not a MIDI event!\n\n";#endif break; } // 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; // 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 message.timeStamp = time * 0.000001; } snd_seq_free_event(ev); if ( message.bytes.size() == 0 ) continue; 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"; } } if ( buffer ) free( buffer ); snd_midi_event_free( apiData->coder ); apiData->coder = 0; return 0;}void RtMidiIn :: initialize( void ){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -