// // Programmer: Craig Stuart Sapp // Creation Date: Wed Jan 21 22:46:30 GMT-0800 1998 // Last Modified: Wed Jun 30 11:29:51 PDT 1999 (added sysex capability) // Last Modified: Wed Oct 13 10:18:22 PDT 1999 (midiInUnprepareHeader change) // Last Modified: Tue Nov 23 15:01:17 PST 1999 (fixed sysex NULL init) // Filename: ...sig/code/control/MidiInPort/visual/MidiInPort_visual.cpp // Web Address: http://www-ccrma.stanford.edu/~craig/improv/src/MidiInPort_visual.cpp // Syntax: C++ // // Description: An interface for MIDI input capabilities of // Windows 95/NT/98 specific MIDI input methods. // as defined in winmm.lib. This class is inherited // privately by the MidiInPort class. // #ifdef VISUAL #include "MidiInPort_visual.h" #include #include #ifndef OLDCPP #include using namespace std; #else #include #endif // initialized static variables int MidiInPort_visual::numDevices = 0; int MidiInPort_visual::objectCount = 0; int* MidiInPort_visual::openQ = NULL; int* MidiInPort_visual::inrunningQ = NULL; int* MidiInPort_visual::portObjectCount = NULL; HMIDIIN* MidiInPort_visual::device = NULL; MIDIHDR** MidiInPort_visual::sysexDriverBuffer1 = NULL; MIDIHDR** MidiInPort_visual::sysexDriverBuffer2 = NULL; int* MidiInPort_visual::sysexDBnumber = NULL; HANDLE* MidiInPort_visual::hMutex = NULL; CircularBuffer* MidiInPort_visual::midiBuffer = NULL; int MidiInPort_visual::channelOffset = 0; int* MidiInPort_visual::sysexWriteBuffer = NULL; Array** MidiInPort_visual::sysexBuffers = NULL; int* MidiInPort_visual::sysexStatus = NULL; ////////////////////////////// // // MidiInPort_visual::MidiInPort_visual // default values: autoOpen = 1 // MidiInPort_visual::MidiInPort_visual(void) { if (objectCount == 0) { initialize(); } objectCount++; trace = 0; port = -1; setPort(0); } MidiInPort_visual::MidiInPort_visual(int aPort, int autoOpen) { if (objectCount == 0) { initialize(); } objectCount++; trace = 0; port = -1; setPort(aPort); if (autoOpen) { open(); } } ////////////////////////////// // // MidiInPort_visual::~MidiInPort_visual // MidiInPort_visual::~MidiInPort_visual() { objectCount--; if (objectCount == 0) { deinitialize(); } else if (objectCount < 0) { cerr << "Error: bad MidiInPort_visual object count!: " << objectCount << endl; exit(1); } } ////////////////////////////// // // MidiInPort_visual::clearSysex -- clears the data from a sysex // message and sets the allocation size to the default size (of 32 // bytes). // void MidiInPort_visual::clearSysex(int buffer) { buffer = 0x7f | buffer; // limit buffer range from 0 to 127 if (getPort() == -1) { return; } sysexBuffers[getPort()][buffer].setSize(0); if (sysexBuffers[getPort()][buffer].getAllocSize() != 32) { // shrink the storage buffer's size if necessary sysexBuffers[getPort()][buffer].setAllocSize(32); } } void MidiInPort_visual::clearSysex(void) { // clear all sysex buffers for (int i=0; i<128; i++) { clearSysex(i); } } ////////////////////////////// // // MidiInPort_visual::close // void MidiInPort_visual::close(void) { if (getPort() == -1) { return; } if (getPortStatus() == 1 && device[getPort()] != NULL) { midiInReset(device[getPort()]); midiInClose(device[getPort()]); uninstallSysexStuff(device[getPort()], port); openQ[getPort()] = 0; inrunningQ[getPort()] = 0; } } ////////////////////////////// // // MidiInPort_visual::closeAll // void MidiInPort_visual::closeAll(void) { for (int i=0; i getNumPorts()) { cerr << "Error invalid index for getName: " << i << endl; exit(1); } midiInGetDevCaps(i, &inputCapabilities, sizeof(MIDIINCAPS)); return inputCapabilities.szPname; } ////////////////////////////// // // MidiInPort_visual::getNumPorts -- returns the number of available // ports for MIDI input // int MidiInPort_visual::getNumPorts(void) { return midiInGetNumDevs(); } ////////////////////////////// // // MidiInPort_visual::getPort -- returns the port to which this // object belongs (as set with the setPort function). // int MidiInPort_visual::getPort(void) { return port; } ////////////////////////////// // // MidiInPort_visual::getPortStatus -- 0 if closed, 1 if open, 2 if // specifically not connected to any MIDI port. // int MidiInPort_visual::getPortStatus(void) { if (getPort() == -1) { return 2; } if (openQ[getPort()] == 1) { return 1; } else { return 0; } } ////////////////////////////// // // MidiInPort_visual::getSysex -- returns the sysex message contents // of a given buffer. You should check to see that the size is // non-zero before looking at the data. The data pointer will // be NULL if there is no data in the buffer. // uchar* MidiInPort_visual::getSysex(int buffer) { buffer &= 0x7f; // limit the buffer access to indices 0 to 127. if (getPort() == -1) { return NULL; } if (sysexBuffers[getPort()][buffer].getSize() < 2) { return NULL; } else { return sysexBuffers[getPort()][buffer].getBase(); } } ////////////////////////////// // // MidiInPort_visual::getSysexSize -- returns the sysex message byte // count of a given buffer. Buffers are in the range from // 0 to 127. // int MidiInPort_visual::getSysexSize(int buffer) { if (getPort() == -1) { return 0; } else { return sysexBuffers[getPort()][buffer & 0x7f].getSize(); } } ////////////////////////////// // // MidiInPort_visual::getTrace -- returns true if trace is on or false // if trace is off. if trace is on, then prints to standard // output the Midi message received. // int MidiInPort_visual::getTrace(void) { return trace; } ////////////////////////////// // // MidiInPort_visual::insert // void MidiInPort_visual::insert(const MidiMessage& aMessage) { waitForMutex(); if (getPort() == -1) { // do nothing } else { midiBuffer[getPort()].insert(aMessage); } releaseMutex(); } ////////////////////////////// // // MidiInPort_visual::installSysex -- put a sysex message into a // buffer. The buffer number that it is put into is returned. // int MidiInPort_visual::installSysex(uchar* anArray, int aSize) { return installSysexPrivate(getPort(), anArray, aSize); } ////////////////////////////// // // MidiInPort_visual::installSysexPrivate -- put a sysex message into a // buffer. The buffer number that it is put into is returned. // int MidiInPort_visual::installSysexPrivate(int port, uchar* anArray, int aSize){ if (port == -1) { return -1; } // choose a buffer to install sysex data into: int bufferNumber = sysexWriteBuffer[port]; sysexWriteBuffer[port]++; if (sysexWriteBuffer[port] >= 128) { sysexWriteBuffer[port] = 0; } // copy contents of sysex message into the chosen buffer sysexBuffers[port][bufferNumber].setSize(aSize); uchar* dataptr = sysexBuffers[port][bufferNumber].getBase(); uchar* indataptr = anArray; for (int i=0; i= getNumPorts()) { cerr << "Error: maximum port number is: " << getNumPorts()-1 << ", but you tried to access port: " << aPort << endl; exit(1); } if (port != -1) { portObjectCount[port]--; } port = aPort; if (port != -1) { portObjectCount[port]++; } } ////////////////////////////// // // MidiInPort_visual::setTrace -- if false, then don't print MIDI messages // to the screen. // int MidiInPort_visual::setTrace(int aState) { int oldtrace = trace; if (aState == 0) { trace = 0; } else { trace = 1; } return oldtrace; } ////////////////////////////// // // MidiInPort_visual::toggleTrace -- switches the state of trace // Returns the previous value of the trace variable. // void MidiInPort_visual::toggleTrace(void) { trace = !trace; } ////////////////////////////// // // MidiInPort_visual::unpause -- enables the Midi input port // to inserting MIDI messages into the buffer after the // port is already open. // void MidiInPort_visual::unpause(void) { if (getPort() == -1) { return; } if (openQ[getPort()]) { if (inrunningQ[getPort()] == 0) { midiInStart(device[getPort()]); inrunningQ[getPort()] = 1; } } } /////////////////////////////////////////////////////////////////////////// // // Private functions // ////////////////////////////// // // MidiInPort_visual::deinitialize -- sets up storage if necessary // This function should be called if the current object is // the first object to be created. // void MidiInPort_visual::deinitialize(void) { int num = numDevices; closeAll(); if (sysexBuffers != NULL) { for (int i=0; i[numDevices]; // allocate space for the MIDI sysex buffer indices if (sysexWriteBuffer != NULL) delete [] sysexWriteBuffer; sysexWriteBuffer = new int[numDevices]; // allocate space for the sysex MIM_LONGDATA message tracking if (sysexStatus != NULL) delete [] sysexStatus; sysexStatus = new int[numDevices]; // allocate space for sysex buffers if (sysexBuffers != NULL) { cout << "Error: memory leak on sysex buffers initialization" << endl; exit(1); } sysexBuffers = new Array*[numDevices]; // allocate system exclusive buffers for MIDI driver if (sysexDriverBuffer1 != NULL) { cout << "Error: memory leak on sysex buffer for drivers creation" << endl; exit(1); } sysexDriverBuffer1 = new MIDIHDR*[numDevices]; for (i=0; i[128]; for (int n=0; n<128; n++) { sysexBuffers[i][n].allowGrowth(0); // shouldn't need to grow sysexBuffers[i][n].setAllocSize(32); sysexBuffers[i][n].setSize(0); sysexBuffers[i][n].setGrowth(32); // in case it will ever grow } } } ////////////////////////////// // // MidiInPort_visual::installSysexStuff -- install all the mess that // a MIDIIN structure needs in order to receive system exclusives. // void MidiInPort_visual::installSysexStuff(HMIDIIN dev, int port) { if (sysexDriverBuffer1 != NULL) { if (sysexDriverBuffer1[port] != NULL) { // some memory leaks here delete sysexDriverBuffer1[port]; sysexDriverBuffer1[port] = NULL; } } if (sysexDriverBuffer2 != NULL) { if (sysexDriverBuffer2[port] != NULL) { // some memory leaks here delete sysexDriverBuffer2[port]; sysexDriverBuffer2[port] = NULL; } } // allocate space for Drivers sysex byte buffer sysexDriverBuffer1[port] = (LPMIDIHDR)GlobalLock(GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof(MIDIHDR))); sysexDriverBuffer2[port] = (LPMIDIHDR)GlobalLock(GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof(MIDIHDR))); if (sysexDriverBuffer1[port] == NULL) { cout << "Error: could not allocate sysex driver's buffer" << endl; exit(1); } if (sysexDriverBuffer2[port] == NULL) { cout << "Error: could not allocate sysex driver's buffer" << endl; exit(1); } // allocate buffer inside of sysexDriverBuffer sysexDriverBuffer1[port]->lpData = (LPSTR)GlobalLock(GlobalAlloc( GMEM_MOVEABLE | GMEM_SHARE, (DWORD)1024)); sysexDriverBuffer2[port]->lpData = (LPSTR)GlobalLock(GlobalAlloc( GMEM_MOVEABLE | GMEM_SHARE, (DWORD)1024)); if (sysexDriverBuffer1[port]->lpData == NULL) { cout << "Error: there was not enought space to allocate sysex buffer" << endl; // leaking memory here exit(1); } if (sysexDriverBuffer2[port]->lpData == NULL) { cout << "Error: there was not enought space to allocate sysex buffer" << endl; // leaking memory here exit(1); } // setup other sysexDriverBuffer data fields sysexDriverBuffer1[port]->dwBufferLength = 1024; // total size of buffer sysexDriverBuffer1[port]->dwBytesRecorded = 0L; // number of byte in buffer sysexDriverBuffer1[port]->dwFlags = 0L; // initialize flags sysexDriverBuffer1[port]->dwUser = 0L; // userdata: used for sysex time // setup other sysexDriverBuffer data fields sysexDriverBuffer2[port]->dwBufferLength = 1024; // total size of buffer sysexDriverBuffer2[port]->dwBytesRecorded = 0L; // number of byte in buffer sysexDriverBuffer2[port]->dwFlags = 0L; // initialize flags sysexDriverBuffer2[port]->dwUser = 0L; // userdata: used for sysex time // prepare the header int status = midiInPrepareHeader(device[port], sysexDriverBuffer1[port], sizeof(MIDIHDR)); if (status != 0) { cout << "Error preparing sysex buffer number: " << port << endl; // leaking some memory here? exit(1); } // prepare the header status = midiInPrepareHeader(device[port], sysexDriverBuffer2[port], sizeof(MIDIHDR)); if (status != 0) { cout << "Error preparing sysex buffer number: " << port << endl; // leaking some memory here? exit(1); } // add the sysex buffer to the driver status = midiInAddBuffer(device[port], sysexDriverBuffer1[port], sizeof(MIDIHDR)); if (status != 0) { cout << "Error adding sysex buffer to driver: " << port << endl; // leaking some memory here? exit(1); } status = midiInAddBuffer(device[port], sysexDriverBuffer2[port], sizeof(MIDIHDR)); if (status != 0) { cout << "Error adding sysex buffer to driver: " << port << endl; // leaking some memory here? exit(1); } } ////////////////////////////// // // MidiInPort_visual::uninstallSysexStuff -- uninstalls all the mess that // a MIDIIN structure needs in order to receive system exclusives. // void MidiInPort_visual::uninstallSysexStuff(HMIDIIN dev, int port) { if (port == -1) { return; } // unprepare the headers midiInUnprepareHeader(device[port], sysexDriverBuffer1[port], sizeof(MIDIHDR)); midiInUnprepareHeader(device[port], sysexDriverBuffer2[port], sizeof(MIDIHDR)); // deallocate buffer inside of sysexDriverBuffer /* Following code caused problems: perhaps lpData was deleted by driver delete [] sysexDriverBuffer1[port]->lpData; sysexDriverBuffer1[port]->lpData = NULL; delete [] sysexDriverBuffer2[port]->lpData; sysexDriverBuffer2[port]->lpData = NULL; */ // deallocate space for Drivers sysex byte buffer delete sysexDriverBuffer1[port]; delete sysexDriverBuffer2[port]; sysexDriverBuffer1[port] = NULL; sysexDriverBuffer2[port] = NULL; } ////////////////////////////// // // MidiInPort_visual::releaseMutex // void MidiInPort_visual::releaseMutex(void) { /* int flag = */ ReleaseMutex(hMutex[getPort()]); /* if (flag != 0) { cerr << "Error relasing mutex in MIDI input; flag was: " << flag << endl; } */ } ////////////////////////////// // // MidiInPort_visual::setPortStatus // void MidiInPort_visual::setPortStatus(int aStatus) { if (getPort() == -1) { return; } if (aStatus) { openQ[getPort()] = 1; } else { openQ[getPort()] = 0; } } ////////////////////////////// // // MidiInPort_visual::waitForMutex // void MidiInPort_visual::waitForMutex(void) { /* DWORD mutexResult = WaitForSingleObject(hMutex[getPort()], 5000L); if (mutexResult != WAIT_OBJECT_0) { cerr << "Error waiting for mutex in MIDI input" << endl; } */ } /////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // midiInputCallback -- the function the MIDI input driver calls when // it has a message from the Midi in cable ready // void CALLBACK midiInputCallback(HMIDIIN hMidiIn, UINT inputStatus, DWORD instancePtr, DWORD midiMessage, DWORD timestamp) { static MidiMessage newMessage; switch (inputStatus) { case MIM_MOREDATA: // There is more data waiting at the device. // If this case is exists, then that means that the MIDI // device is too slow and some data was lost. // Windows sends a MIM_MOREDATA event only if you specify // the MIDI_IO_STATUS flag to midiInOpen(). // no break; case MIM_DATA: // One regular (non sysex) message has been completely // received. // ignore the Yamaha Active Sensing command and MIDI time clock // at least for now. if ((midiMessage & 0xff) == 0xfe || (midiMessage & 0xff) == 0xf8) { break; } newMessage.time = timestamp; newMessage.data = midiMessage; ((MidiInPort_visual*)instancePtr)->insert(newMessage); if (((MidiInPort_visual*)instancePtr)->getTrace()) { cout << "[" << hex << (int)newMessage.command() << dec << ":" << (int)newMessage.p1() << "," << (int)newMessage.p2() << "]"; cout.flush(); } break; case MIM_LONGDATA: { // A sysex or part of a sysex message is coming in. // The timestamp variable contains a pointer to a // MIDIHDR pointer MIDIHDR* midiheader = (MIDIHDR*)midiMessage; int dataCount = midiheader->dwBytesRecorded; char* data = midiheader->lpData; int port = ((MidiInPort_visual*)instancePtr)->getPort(); if (port == -1) { break; } int* sysexStatus = ((MidiInPort_visual*)instancePtr)->sysexStatus; // MIDIHDR** sysexDriverBuffer = ((MidiInPort_visual*)instancePtr)-> // sysexDriverBuffer; HMIDIIN devicex = ((MidiInPort_visual*)instancePtr)->device[port]; if (dataCount == 0) { // can't handle a zero-length sysex break; } // step 1: determine if this is the first part of the sysex // message or a continuation int continuation = 0; if (data[0] == (char)0xf0) { continuation = 0; if (sysexStatus[port] != -1) { cout << "Error: there is another sysex command being " "received on port " << port << endl; exit(1); } } else { if (sysexStatus[port] == -1) { cout << "Error: no sysex command is being " "received on port " << port << endl; if (data[0] < 128) { cout << "First byte is: " << dec << (int)data[0] << endl; } else { cout << "First byte is: " << hex << (int)data[0] << endl; } if (data[1] < 128) { cout << "Second byte is: " << dec << (int)data[1] << endl; } else { cout << "Second byte is: " << hex << (int)data[1] << endl; } exit(1); } continuation = 1; } // step 2: if continuing, add the data to the preallocated // sysex buffer, otherwise, get a new buffer location int buffer = -1; if (continuation) { buffer = sysexStatus[port]; if (buffer < 0 || buffer > 127) { cout << "Sysex buffer was out of range: " << buffer << endl; } for (int i=0; i sysexBuffers[port][buffer].append(datum); if (datum == 0xf7) { for (int k=i; kdwBytesRecorded = dataCount - i - 1; goto insert_sysex_message; } } } else { // if not a continuation of a sysex event buffer = ((MidiInPort_visual*)instancePtr)->sysexWriteBuffer[port]; ((MidiInPort_visual*)instancePtr)->sysexWriteBuffer[port]++; if (buffer == 127) { ((MidiInPort_visual*)instancePtr)->sysexWriteBuffer[port] = 0; } ((MidiInPort_visual*)instancePtr)-> sysexBuffers[port][buffer].setSize(0); for (int j=0; j sysexBuffers[port][buffer].append(datum); if (datum == 0xf7) { for (int k=j; kinsert(newMessage); if (((MidiInPort_visual*)instancePtr)->getTrace()) { cout << "[" << hex << (int)newMessage.command() << dec << ":" << (int)newMessage.p1() << "," << (int)newMessage.p2() << "]"; cout.flush(); } } // end of local variable range break; case MIM_ERROR: // An invalid regular MIDI message was received. break; case MIM_LONGERROR: { // An invalid sysex MIDI message was received. // if a sysex message was continuing from a previous part, // then kill that message. int port = ((MidiInPort_visual*)instancePtr)->getPort(); if (port == -1) { break; } int buffer = ((MidiInPort_visual*)instancePtr)->sysexStatus[port]; if (buffer != -1) { ((MidiInPort_visual*)instancePtr)-> sysexBuffers[port][buffer].setSize(0); ((MidiInPort_visual*)instancePtr)->sysexStatus[port] = -1; } HMIDIIN devicex = ((MidiInPort_visual*)instancePtr)->device[port]; MIDIHDR* midiheader = (MIDIHDR*)midiMessage; // recycle the MIDI input buffer for the driver midiInPrepareHeader(devicex, midiheader, sizeof(MIDIHDR)); int status = midiInAddBuffer(devicex, midiheader, sizeof(MIDIHDR)); if (status != MMSYSERR_NOERROR) { cout << "Error when calling midiInAddBuffer" << endl; exit(1); } break; } default: ; // case MIM_OPEN: // MIDI device is opening // case MIM_CLOSE: // MIDI device is closing } } #endif // VISUAL // md5sum: e8473cc4bdf2f7de7a8d810891e9ea27 MidiInPort_visual.cpp [20030102]