📄 rtmidi.cpp
字号:
}void RtMidiOut :: sendMessage( std::vector<unsigned char> *message ){ int result; MDevent event; IrixMidiData *data = static_cast<IrixMidiData *> (apiData_); char *buffer = 0; unsigned int nBytes = message->size(); if ( nBytes == 0 ) return; event.stamp = 0; if ( message->at(0) == 0xF0 ) { if ( nBytes < 3 ) return; // check for bogus sysex event.msg[0] = 0xF0; event.msglen = nBytes; buffer = (char *) malloc( nBytes ); for ( int i=0; i<nBytes; i++ ) buffer[i] = message->at(i); event.sysexmsg = buffer; } else { for ( int i=0; i<nBytes; i++ ) event.msg[i] = message->at(i); } // Send the event. result = mdSend( data->port, &event, 1 ); if ( buffer ) free( buffer ); if ( result < 1 ) { errorString_ = "RtMidiOut::sendMessage: IRIX error sending MIDI message!"; error( RtError::WARNING ); return; }}#endif // __IRIX_MD__//*********************************************************************//// API: Windows Multimedia Library (MM)//*********************************************************************//// API information deciphered from:// - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_midi_reference.asp#if defined(__WINDOWS_MM__)// The Windows MM API is based on the use of a callback function for// MIDI input. We convert the system specific time stamps to delta// time values.// Windows MM MIDI header files.#include <windows.h>#include <mmsystem.h>// A structure to hold variables related to the CoreMIDI API// implementation.struct WinMidiData { HMIDIIN inHandle; // Handle to Midi Input Device HMIDIOUT outHandle; // Handle to Midi Output Device DWORD lastTime; RtMidiIn::MidiMessage message;};//*********************************************************************//// API: Windows MM// Class Definitions: RtMidiIn//*********************************************************************//static void CALLBACK midiInputCallback( HMIDIOUT hmin, UINT inputStatus, DWORD instancePtr, DWORD midiMessage, DWORD timestamp ){ if ( inputStatus != MIM_DATA && inputStatus != MIM_LONGDATA ) return; //RtMidiIn::RtMidiInData *data = static_cast<RtMidiIn::RtMidiInData *> (instancePtr); RtMidiIn::RtMidiInData *data = (RtMidiIn::RtMidiInData *)instancePtr; WinMidiData *apiData = static_cast<WinMidiData *> (data->apiData); // Calculate time stamp. apiData->message.timeStamp = 0.0; if ( data->firstMessage == true ) data->firstMessage = false; else apiData->message.timeStamp = (double) ( timestamp - apiData->lastTime ) * 0.001; apiData->lastTime = timestamp; if ( inputStatus == MIM_DATA ) { // Channel or system message // Make sure the first byte is a status byte. unsigned char status = (unsigned char) (midiMessage & 0x000000FF); if ( !(status & 0x80) ) return; // Determine the number of bytes in the MIDI message. unsigned short nBytes = 1; if ( status < 0xC0 ) nBytes = 3; else if ( status < 0xE0 ) nBytes = 2; else if ( status < 0xF0 ) nBytes = 3; else if ( status < 0xF3 ) { // A MIDI time code message and we're ignoring it. if ( status == 0xF1 && (data->ignoreFlags & 0x02) ) return; 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; } // Copy bytes to our MIDI message. unsigned char *ptr = (unsigned char *) &midiMessage; for ( int i=0; i<nBytes; i++ ) apiData->message.bytes.push_back( *ptr++ ); } else { // Sysex message MIDIHDR *sysex = ( MIDIHDR *) midiMessage; for ( int i=0; i<(int)sysex->dwBytesRecorded; i++ ) apiData->message.bytes.push_back( sysex->lpData[i] ); if ( apiData->message.bytes.back() != 0xF7 ) return; } if ( data->usingCallback ) { RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; callback( apiData->message.timeStamp, &apiData->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( apiData->message ); else 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. apiData->message.bytes.clear();}void RtMidiIn :: initialize( void ){ // We'll issue a warning here if no devices are available but not // throw an error since the user can plugin something later. unsigned int nDevices = midiInGetNumDevs(); if ( nDevices == 0 ) { errorString_ = "RtMidiIn::initialize: no MIDI input devices currently available."; error( RtError::WARNING ); } // Save our api-specific connection information. WinMidiData *data = (WinMidiData *) new WinMidiData; apiData_ = (void *) data; inputData_.apiData = (void *) data; data->message.bytes.clear(); // needs to be empty for first input message}void RtMidiIn :: openPort( unsigned int portNumber ){ if ( connected_ ) { errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; error( RtError::WARNING ); return; } unsigned int nDevices = midiInGetNumDevs(); if (nDevices == 0) { errorString_ = "RtMidiIn::openPort: no MIDI input sources found!"; error( RtError::NO_DEVICES_FOUND ); } std::ostringstream ost; if ( portNumber >= nDevices ) { ost << "RtMidiIn::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; errorString_ = ost.str(); error( RtError::INVALID_PARAMETER ); } WinMidiData *data = static_cast<WinMidiData *> (apiData_); MMRESULT result = midiInOpen( &data->inHandle, portNumber, (DWORD)&midiInputCallback, (DWORD)&inputData_, CALLBACK_FUNCTION ); if ( result != MMSYSERR_NOERROR ) { errorString_ = "RtMidiIn::openPort: error creating Windows MM MIDI input port."; error( RtError::DRIVER_ERROR ); } result = midiInStart( data->inHandle ); if ( result != MMSYSERR_NOERROR ) { midiInClose( data->inHandle ); errorString_ = "RtMidiIn::openPort: error starting Windows MM MIDI input port."; error( RtError::DRIVER_ERROR ); } connected_ = true;}void RtMidiIn :: openVirtualPort(){ // This function cannot be implemented for the Windows MM MIDI API. errorString_ = "RtMidiIn::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; error( RtError::WARNING );}void RtMidiIn :: closePort( void ){ if ( connected_ ) { WinMidiData *data = static_cast<WinMidiData *> (apiData_); midiInReset( data->inHandle ); midiInStop( data->inHandle ); midiInClose( data->inHandle ); connected_ = false; }}RtMidiIn :: ~RtMidiIn(){ // Close a connection if it exists. closePort(); // Cleanup. WinMidiData *data = static_cast<WinMidiData *> (apiData_); delete data;}unsigned int RtMidiIn :: getPortCount(){ return midiInGetNumDevs();}std::string RtMidiIn :: getPortName( unsigned int portNumber ){ unsigned int nDevices = midiInGetNumDevs(); if ( portNumber >= nDevices ) { std::ostringstream ost; ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; errorString_ = ost.str(); error( RtError::INVALID_PARAMETER ); } MIDIINCAPS deviceCaps; MMRESULT result = midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS)); std::string stringName = std::string( deviceCaps.szPname ); return stringName;}//*********************************************************************//// API: Windows MM// Class Definitions: RtMidiOut//*********************************************************************//unsigned int RtMidiOut :: getPortCount(){ return midiOutGetNumDevs();}std::string RtMidiOut :: getPortName( unsigned int portNumber ){ unsigned int nDevices = midiOutGetNumDevs(); if ( portNumber >= nDevices ) { std::ostringstream ost; ost << "RtMidiOut::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; errorString_ = ost.str(); error( RtError::INVALID_PARAMETER ); } MIDIOUTCAPS deviceCaps; MMRESULT result = midiOutGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIOUTCAPS)); std::string stringName = std::string( deviceCaps.szPname ); return stringName;}void RtMidiOut :: initialize( void ){ // We'll issue a warning here if no devices are available but not // throw an error since the user can plug something in later. unsigned int nDevices = midiOutGetNumDevs(); if ( nDevices == 0 ) { errorString_ = "RtMidiOut::initialize: no MIDI output devices currently available."; error( RtError::WARNING ); } // Save our api-specific connection information. WinMidiData *data = (WinMidiData *) new WinMidiData; 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 nDevices = midiOutGetNumDevs(); if (nDevices < 1) { errorString_ = "RtMidiOut::openPort: no MIDI output destinations found!"; error( RtError::NO_DEVICES_FOUND ); } std::ostringstream ost; if ( portNumber >= nDevices ) { ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; errorString_ = ost.str(); error( RtError::INVALID_PARAMETER ); } WinMidiData *data = static_cast<WinMidiData *> (apiData_); MMRESULT result = midiOutOpen( &data->outHandle, portNumber, (DWORD)NULL, (DWORD)NULL, CALLBACK_NULL ); if ( result != MMSYSERR_NOERROR ) { errorString_ = "RtMidiOut::openPort: error creating Windows MM MIDI output port."; error( RtError::DRIVER_ERROR ); } connected_ = true;}void RtMidiOut :: closePort( void ){ if ( connected_ ) { WinMidiData *data = static_cast<WinMidiData *> (apiData_); midiOutReset( data->outHandle ); midiOutClose( data->outHandle ); connected_ = false; }}void RtMidiOut :: openVirtualPort(){ // This function cannot be implemented for the Windows MM MIDI API. errorString_ = "RtMidiOut::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; error( RtError::WARNING );}RtMidiOut :: ~RtMidiOut(){ // Close a connection if it exists. closePort(); // Cleanup. WinMidiData *data = static_cast<WinMidiData *> (apiData_); delete data;}void RtMidiOut :: sendMessage( std::vector<unsigned char> *message ){ unsigned int nBytes = message->size(); if ( nBytes == 0 ) { errorString_ = "RtMidiOut::sendMessage: message argument is empty!"; error( RtError::WARNING ); return; } MMRESULT result; WinMidiData *data = static_cast<WinMidiData *> (apiData_); if ( message->at(0) == 0xF0 ) { // Sysex message // Allocate buffer for sysex data. char *buffer = (char *) malloc( nBytes ); if ( buffer == NULL ) { errorString_ = "RtMidiOut::sendMessage: error allocating sysex message memory!"; error( RtError::MEMORY_ERROR ); } // Copy data to buffer. for ( unsigned int i=0; i<nBytes; i++ ) buffer[i] = message->at(i); // Create and prepare MIDIHDR structure. MIDIHDR sysex; sysex.lpData = (LPSTR) buffer; sysex.dwBufferLength = nBytes; sysex.dwFlags = 0; result = midiOutPrepareHeader( data->outHandle, &sysex, sizeof(MIDIHDR) ); if ( result != MMSYSERR_NOERROR ) { free( buffer ); errorString_ = "RtMidiOut::sendMessage: error preparing sysex header."; error( RtError::DRIVER_ERROR ); } // Send the message. result = midiOutLongMsg( data->outHandle, &sysex, sizeof(MIDIHDR) ); if ( result != MMSYSERR_NOERROR ) { free( buffer ); errorString_ = "RtMidiOut::sendMessage: error sending sysex message."; error( RtError::DRIVER_ERROR ); } // Unprepare the buffer and MIDIHDR. while ( MIDIERR_STILLPLAYING == midiOutUnprepareHeader( data->outHandle, &sysex, sizeof (MIDIHDR) ) ) Sleep( 1 ); free( buffer ); } else { // Channel or system message. // Make sure the message si
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -