/***************************************************/ /*! \class RtMidi \brief STK realtime MIDI class. At the moment, this object only handles MIDI input, though MIDI output code can go here when someone decides they need it (and writes it). This object opens a MIDI input device and parses MIDI messages into a MIDI buffer. Time stamp info is converted to a delta-time value. MIDI data is stored as MY_FLOAT to conform with SKINI. System exclusive messages are currently ignored. An optional argument to the constructor can be used to specify a device or card. When no argument is given, a default device is opened. If a device argument fails, a list of available devices is printed to allow selection by the user. This code is based in part on work of Perry Cook (SGI), Paul Leonard (Linux), the RoseGarden team (Linux), and Bill Putnam (Windows). by Perry R. Cook and Gary P. Scavone, 1995 - 2002. */ /***************************************************/ #include "RtMidi.h" #include #include #include #include #define MIDI_BUFFER_SIZE 256 int writeIndex; #if defined(__MACOSX_CORE__) #include #include MIDIClientRef client; MIDIPortRef port; typedef unsigned char byte; /* MIDI System Messages */ #define MD_SYSTEM_MSG ((byte)0xF0) #define MD_PITCH_BEND ((byte)0xE0) #define MessageType(MSG) (byte)((MSG) & ((byte)0xF0)) typedef struct { byte data[3]; float delta_time; } MIDIMESSAGE; MIDIMESSAGE *midiBuffer; UInt64 lastTime = 0; void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef ) { MIDIMESSAGE newMessage; UInt64 deltaTime; const MIDIPacket *packet = &list->packet[0]; for (unsigned int i=0; inumPackets; ++i){ // Block unwanted messages. if ( packet->length > 3 ) continue; if (MessageType(packet->data[0]) == MD_SYSTEM_MSG) continue; memcpy( newMessage.data, packet->data, packet->length ); deltaTime = packet->timeStamp - lastTime; deltaTime = AudioConvertHostTimeToNanos( deltaTime ); newMessage.delta_time = deltaTime * 0.000000001; // Put newMessage in the circular buffer midiBuffer[writeIndex] = newMessage; writeIndex++; if( writeIndex >= MIDI_BUFFER_SIZE ) writeIndex = 0; packet = MIDIPacketNext(packet); } } RtMidi :: RtMidi(int device) { char msg[256]; int nSrc = MIDIGetNumberOfSources(); if (nSrc < 1) { sprintf(msg, "RtMidi: No OS X MIDI input devices available."); handleError(msg, StkError::MIDI_SYSTEM); } OSStatus result = MIDIClientCreate( CFSTR("Stk MIDI Input Client"), NULL, NULL, &client ); if ( result != noErr ) { sprintf(msg, "RtMidi: OSX error creating MIDI client object."); handleError(msg, StkError::MIDI_SYSTEM); } result = MIDIInputPortCreate( client, CFSTR("Stk MIDI Input Port"), midiInputCallback, (void *)this, &port ); if ( result != noErr ) { MIDIClientDispose( client ); sprintf(msg, "RtMidi: OSX error creating MIDI input port."); handleError(msg, StkError::MIDI_SYSTEM); } // Set up the circular buffer for the Midi input messages. midiBuffer = new MIDIMESSAGE[MIDI_BUFFER_SIZE]; readIndex = 0; writeIndex = 0; // If specified device is bogus, print a list of available devices. int iSrc = device; MIDIEndpointRef src; if ( device < 0 || device >= nSrc ) { CFStringRef nameRef; char name[128]; printf("\n"); for (int i=0; i= nSrc ) { printf("\nType a MIDI interface number from above: "); fgets(choice, 16, stdin); iSrc = atoi(choice); } printf("\n"); } // Open the source. src = MIDIGetSource(iSrc); MIDIPortConnectSource(port, src, NULL ); lastTime = AudioGetCurrentHostTime(); } RtMidi :: ~RtMidi() { MIDIClientDispose( client ); MIDIPortDispose( port ); delete [] midiBuffer; } int RtMidi::nextMessage() { MIDIMESSAGE lastEvent; if ( readIndex == writeIndex ) return 0; lastEvent = midiBuffer[readIndex]; readIndex++; if ( readIndex >= MIDI_BUFFER_SIZE ) readIndex = 0; messageType = (int) (lastEvent.data[0] & 0xf0); channel = (int) (lastEvent.data[0] & 0x0f); byteTwo = (float) lastEvent.data[1]; if (messageType == (int) MD_PITCH_BEND) byteTwo = (float) lastEvent.data[2] + (byteTwo / 128.0); else byteThree = (float) lastEvent.data[2]; deltaTime = (float) lastEvent.delta_time; return messageType; } #elif defined(__OS_IRIX__) #include #include #include #include MDport inport; MDevent *midiBuffer; pthread_t midi_input_thread; void *midiInputThread(void *) { MDevent newMessage; int status; while (1) { mdReceive(inport, &newMessage, 1); status = (newMessage.msg[0] & MD_STATUSMASK); // Ignore all system messages if (status != 0xf0) { midiBuffer[writeIndex] = newMessage; writeIndex++; if( writeIndex >= MIDI_BUFFER_SIZE ) writeIndex = 0; } } } RtMidi :: RtMidi(int device) { int nports, card; char msg[256]; char *device_name = 0; nports = mdInit(); if (nports < 1) { sprintf(msg, "RtMidi: No Irix MIDI device available."); handleError(msg, StkError::MIDI_SYSTEM); } if (device == 0) { // Open default MIDI interface. inport = mdOpenInPort(NULL); if (inport == NULL) { sprintf(msg, "RtMidi: Cannot open default Irix MIDI device."); handleError(msg, StkError::MIDI_SYSTEM); } } else { card = device; if ( (card < 0) || (card >= nports) ) { printf("\n"); for (card=0; card= nports ) { printf("\nType a MIDI interface number from above: "); fgets(choice, 16, stdin); card = atoi(choice); } printf("\n"); } inport = mdOpenInPort(mdGetName(card)); if (inport == NULL) { sprintf(msg, "RtMidi: Cannot open Irix MIDI interface %d!", card); handleError(msg, StkError::MIDI_SYSTEM); } } mdSetStampMode(inport, MD_NOSTAMP); // Set up the circular buffer for the Midi input messages. midiBuffer = new MDevent[MIDI_BUFFER_SIZE]; readIndex = 0; writeIndex = 0; if ( pthread_create(&midi_input_thread, NULL, midiInputThread, NULL) ) { sprintf(msg, "RtMidi: unable to create MIDI input thread."); handleError(msg, StkError::PROCESS_THREAD); } } RtMidi :: ~RtMidi() { pthread_cancel(midi_input_thread); pthread_join(midi_input_thread, NULL); mdClosePort(inport); delete [] midiBuffer; } int RtMidi :: nextMessage() { int status; int byte1; int byte2; MDevent lastEvent; static unsigned long long lastTimeStamp = 0; if ( readIndex == writeIndex ) return 0; lastEvent = midiBuffer[readIndex]; readIndex++; if ( readIndex >= MIDI_BUFFER_SIZE ) readIndex = 0; status = (lastEvent.msg[0] & MD_STATUSMASK); byte1 = lastEvent.msg[1]; byte2 = lastEvent.msg[2]; channel = (lastEvent.msg[0] & MD_CHANNELMASK); if ((status == MD_PROGRAMCHANGE) || (status == MD_CHANNELPRESSURE)) { messageType = status; byteTwo = (float) byte1; deltaTime = (MY_FLOAT) ((lastEvent.stamp - lastTimeStamp) * 0.000000001); lastTimeStamp = lastEvent.stamp; } else if ((status == MD_NOTEON) || (status == MD_NOTEOFF) || (status == MD_CONTROLCHANGE) || (status == MD_POLYKEYPRESSURE)) { messageType = status; byteTwo = (float) byte1; byteThree = (float) byte2; deltaTime = (MY_FLOAT) ((lastEvent.stamp - lastTimeStamp) * 0.000000001); lastTimeStamp = lastEvent.stamp; } else if (status == MD_PITCHBENDCHANGE) { messageType = status; byteTwo = (float) byte1 * ONE_OVER_128; byteTwo += (float) byte2; deltaTime = (MY_FLOAT) ((lastEvent.stamp - lastTimeStamp) * 0.000000001); lastTimeStamp = lastEvent.stamp; } else { messageType = -1; } return messageType; } #elif ( defined(__LINUX_OSS__) || defined(__LINUX_ALSA__) ) #include #include #if defined(__MIDIATOR__) #include #include #include #include int midi_in; #elif defined(__LINUX_OSS__) #include #include #include #include #include int midi_in; #else // __LINUX_ALSA__ #include snd_rawmidi_t *midi_in = 0; #endif typedef unsigned char byte; /* MIDI System Messages */ #define MD_SYSTEM_MSG ((byte)0xF0) #define MD_PGM_CHANGE ((byte)0xC0) #define MD_CHN_PRESSURE ((byte)0xD0) #define MD_PITCH_BEND ((byte)0xE0) #define MessageType(MSG) (byte)((MSG) & ((byte)0xF0)) typedef struct { byte data[3]; float delta_time; } MIDIMESSAGE; MIDIMESSAGE *midiBuffer; pthread_t midi_input_thread; void *midiInputThread(void *) { int numArgs = 2, argsLeft = 0; double lastTime = 0.0, newTime = 0.0; byte newByte; MIDIMESSAGE newMessage; int n; struct timeval tv; (void)gettimeofday(&tv, (struct timezone *)NULL); lastTime = (double) (tv.tv_sec + (tv.tv_usec * 0.000001)); for (;;) { #if defined(__LINUX_OSS__) || defined(__MIDIATOR__) // Normally, you should check the return value of this read() call. // A return of -1 usually indicates an error. However, for OSS // compatability in ALSA, we need to ignore such values. n = read(midi_in, &newByte, 1); #else // ALSA_API if ((n = snd_rawmidi_read(midi_in, &newByte, 1)) == -1) fprintf(stderr, "RtMidi: Error reading ALSA raw MIDI data from device!\n"); #endif while (n > 0) { if (newByte & 0x80) { // status byte if (MessageType(newByte) == MD_SYSTEM_MSG) { n--; continue; } else if (MessageType(newByte) == MD_PGM_CHANGE || MessageType(newByte) == MD_CHN_PRESSURE) { numArgs = 1; } else { numArgs = 2; } newMessage.data[0] = newByte; newMessage.data[1] = 0; newMessage.data[2] = 0; argsLeft = numArgs; } else { // data byte if ( argsLeft == numArgs ) newMessage.data[1] = newByte; else { newMessage.data[2] = newByte; } argsLeft--; if ( !argsLeft ) { // MIDI message complete // setup for running status mode (another event of the // same type without status byte) if (MessageType(newMessage.data[0]) == (int) MD_PGM_CHANGE || MessageType(newMessage.data[0]) == (int) MD_CHN_PRESSURE) { argsLeft = 1; } else { argsLeft = 2; } // determine the delta time since the last event (void)gettimeofday(&tv, (struct timezone *)NULL); newTime = (double) ((double)tv.tv_sec + (((double)tv.tv_usec) * 0.000001)); newMessage.delta_time = (float) (newTime - lastTime); lastTime = newTime; // Put newMessage in the circular buffer midiBuffer[writeIndex] = newMessage; writeIndex++; if( writeIndex >= MIDI_BUFFER_SIZE ) writeIndex = 0; } } n--; } } return 0; } #if defined(__MIDIATOR__) /* Hopefully, this special support for the MIDIator serial port MIDI device is temporary and it will eventually be incorporated into the OSS and ALSA APIs. This code is based almost entirely on David Topper's driver code, which is available from: ftp://presto.music.virginia.edu/pub/midiator See the README-Linux STK document for details on how to get the MIDIator setup correctly for use under linux. */ #include #include #define BAUD_RATE B19200 #define MAX_DEVICES 3 #define MIDI_NAME "/dev/ttyS" void initializeMidiator(); RtMidi :: RtMidi(int device) { char msg[256]; char name[16]; char deviceNames[MAX_DEVICES][16]; midi_in = 0; int i, nDevices = 0; for ( i=0; i= 0 && device < nDevices ) deveyes = device; else if ( nDevices == 1 ) deveyes = 0; else { // Invalid device argument ... print list. printf("\n"); for ( i=0; i= nDevices ) { printf("\nType a MIDI device number from above: "); fgets(choice, 16, stdin); deveyes = atoi(choice); } printf("\n"); } midi_in = open(deviceNames[deveyes], O_RDONLY, 0); if ( midi_in == -1) { sprintf(msg, "RtMidi: Unable to open serial port (%s) for MIDI input!", deviceNames[deveyes]); handleError(msg, StkError::MIDI_SYSTEM); } printf("\nInitializing MIDIator MS-124w ... "); initializeMidiator(); printf("ready on serial port %s.\n", deviceNames[deveyes]); // Set up the circular buffer for the MIDI input messages midiBuffer = new MIDIMESSAGE[MIDI_BUFFER_SIZE]; readIndex = 0; writeIndex = 0; int result = pthread_create(&midi_input_thread, NULL, midiInputThread, NULL); if (result) { sprintf(msg, "RtMidi: unable to create MIDI input thread."); handleError(msg, StkError::PROCESS_THREAD); } } #elif defined(__LINUX_OSS__) // normal OSS setup #define MAX_DEVICES 8 #define MIDI_NAME "/dev/midi" RtMidi :: RtMidi(int device) { char msg[256]; char name[16]; char deviceNames[MAX_DEVICES][16]; midi_in = 0; // /dev/midi should be a link to the default midi device under OSS strcpy(name, MIDI_NAME); // The OSS API doesn't really give us a means for probing the // capabilities of devices. Thus, we'll just pursue a brute // force method of opening devices until we either find something // that doesn't complain or we have to give up. We'll start with // the default device, then try /dev/midi00, /dev/midi01, etc... int i, nDevices = 0; for ( i=0; i 0) sprintf(name, "%s%d%d", MIDI_NAME, 0, i-1); midi_in = open(name, O_RDONLY | O_NONBLOCK, 0); if ( midi_in != -1 ) { strncpy( deviceNames[nDevices++], name, 16 ); close( midi_in ); } else if ( errno == EBUSY ) fprintf(stderr,"RtMidi: MIDI device (%s) is busy and cannot be opened.\n", name); } if (nDevices == 0) { sprintf(msg, "RtMidi: no OSS MIDI cards reported available."); handleError(msg, StkError::MIDI_SYSTEM); } // Check device argument and print list if necessary. int deveyes; if ( device >= 0 && device < nDevices ) deveyes = device; else if ( nDevices == 1 ) deveyes = 0; else { // Invalid device argument ... print list. printf("\n"); for ( i=0; i= nDevices ) { printf("\nType a MIDI device number from above: "); fgets(choice, 16, stdin); deveyes = atoi(choice); } printf("\n"); } midi_in = open(deviceNames[deveyes], O_RDONLY, 0); if ( midi_in == -1) { sprintf(msg, "RtMidi: Unable to open OSS device (%s) for MIDI input!", deviceNames[deveyes]); handleError(msg, StkError::MIDI_SYSTEM); } // Set up the circular buffer for the MIDI input messages midiBuffer = new MIDIMESSAGE[MIDI_BUFFER_SIZE]; readIndex = 0; writeIndex = 0; int result = pthread_create(&midi_input_thread, NULL, midiInputThread, NULL); if (result) { sprintf(msg, "RtMidi: unable to create MIDI input thread."); handleError(msg, StkError::PROCESS_THREAD); } } #else // ALSA_API #define MAX_DEVICES 8 RtMidi :: RtMidi(int device) { midi_in = 0; char msg[256]; int result, card, deveyes, nDevices; char name[32]; char deviceNames[MAX_DEVICES][32]; snd_ctl_t *handle; snd_ctl_card_info_t *info; snd_ctl_card_info_alloca(&info); // Count cards and devices card = -1; nDevices = 0; snd_card_next(&card); while ( card >= 0 ) { sprintf(name, "hw:%d", card); result = snd_ctl_open(&handle, name, 0); if (result < 0) { sprintf(msg, "RtMidi: ALSA control open (%i): %s.", card, snd_strerror(result)); handleError(msg, StkError::WARNING); goto next_card; } result = snd_ctl_card_info(handle, info); if (result < 0) { sprintf(msg, "RtMidi: ALSA control hardware info (%i): %s.", card, snd_strerror(result)); handleError(msg, StkError::WARNING); goto next_card; } deveyes = -1; while (1) { result = snd_ctl_rawmidi_next_device(handle, &deveyes); if (result < 0) { sprintf(msg, "RtMidi: ALSA control next rawmidi device (%i): %s.", card, snd_strerror(result)); handleError(msg, StkError::WARNING); break; } if (deveyes < 0) break; sprintf( deviceNames[nDevices++], "hw:%d,%d", card, deveyes ); if ( nDevices > MAX_DEVICES ) break; } if ( nDevices > MAX_DEVICES ) break; next_card: snd_ctl_close(handle); snd_card_next(&card); } if (nDevices == 0) { sprintf(msg, "RtMidi: no ALSA MIDI cards reported available."); handleError(msg, StkError::MIDI_SYSTEM); } // Check device argument and print list if necessary. if ( device >= 0 && device < nDevices ) deveyes = device; else if ( nDevices == 1 ) deveyes = 0; else { // Invalid device argument ... print list. printf("\n"); for ( int i=0; i= nDevices ) { printf("\nType a MIDI device number from above: "); fgets(choice, 16, stdin); deveyes = atoi(choice); } printf("\n"); } result = snd_rawmidi_open(&midi_in, NULL, deviceNames[deveyes], 0); if (result) { sprintf(msg, "RtMidi: Error opening ALSA raw MIDI device: %s.", deviceNames[deveyes]); handleError(msg, StkError::MIDI_SYSTEM); } // Set up the circular buffer for the MIDI input messages midiBuffer = new MIDIMESSAGE[MIDI_BUFFER_SIZE]; readIndex = 0; writeIndex = 0; result = pthread_create(&midi_input_thread, NULL, midiInputThread, NULL); if (result) { sprintf(msg, "RtMidi: unable to create MIDI input thread."); handleError(msg, StkError::PROCESS_THREAD); } } #endif RtMidi :: ~RtMidi() { pthread_cancel(midi_input_thread); delete [] midiBuffer; #if defined(__MIDIATOR__) tcdrain(midi_in); if (midi_in != 0) close(midi_in); #elif defined(__LINUX_OSS__) if (midi_in != 0) close(midi_in); #else // ALSA_API if (midi_in != 0) snd_rawmidi_close(midi_in); #endif } int RtMidi::nextMessage() { MIDIMESSAGE lastEvent; if ( readIndex == writeIndex ) return 0; lastEvent = midiBuffer[readIndex]; readIndex++; if ( readIndex >= MIDI_BUFFER_SIZE ) readIndex = 0; messageType = (int) (lastEvent.data[0] & 0xf0); channel = (int) (lastEvent.data[0] & 0x0f); byteTwo = (float) lastEvent.data[1]; if (messageType == (int) MD_PITCH_BEND) byteTwo = (float) lastEvent.data[2] + (byteTwo / 128.0); else byteThree = (float) lastEvent.data[2]; deltaTime = (float) lastEvent.delta_time; return messageType; } #if defined(__MIDIATOR__) void initializeMidiator() { struct termios info; // serial port configuration info int status; // serial port status struct timeval tv; // to do a little time delay // Get the current serial port attributes, so we can change // the ones we care about. if (tcgetattr(midi_in, &info) < 0) { fprintf(stderr, "RtMidi: ioctl to get tty info failed (MIDIator support)!"); return; } bzero(&info, sizeof(info)); info.c_cflag = BAUD_RATE | CRTSCTS | CS8 | CLOCAL | CREAD; info.c_iflag &= ~IGNCR; info.c_oflag &= ~IGNCR; // set input mode (non-canonical, no echo,...) info.c_lflag = 0; info.c_cc[VTIME] = 1; // inter-character timer unused info.c_cc[VMIN] = 1; // blocking read until 5 chars received tcflush(midi_in, TCIFLUSH); // Set the attributes tcsetattr(midi_in, TCSANOW, &info); // Startup sequence, as per ron@MIDI_DEV's instructions // Many thanks to Ron for supporting Linux // Step 1 // Power down and deassert DTR and RTS ioctl(midi_in, TIOCMGET, &status); status &= ~TIOCM_DTR; status &= ~TIOCM_RTS; ioctl(midi_in, TIOCMSET, status); // Wait 600 ms to make sure everything is stable tv.tv_sec = 0; tv.tv_usec = 600000; select(0, NULL, NULL, NULL, &tv); // Step 2 // Power up and assert break ioctl(midi_in, TIOCMGET, &status); status |= TCSBRK; ioctl(midi_in, TIOCMSET, status); // Wait 300 ms to make sure everything is stable tv.tv_sec = 0; tv.tv_usec = 300000; select(0, NULL, NULL, NULL, &tv); // Step 3 // Set input mode ioctl(midi_in, TIOCMGET, &status); status &= ~TIOCM_DTR; status |= TIOCM_RTS; ioctl(midi_in, TIOCMSET, status); // Wait 40 us to make sure everything is stable tv.tv_sec = 0; tv.tv_usec = 40; select(0, NULL, NULL, NULL, &tv); // Step 4 // Set input mode ioctl(midi_in, TIOCMGET, &status); status |= TIOCM_DTR; status &= TIOCM_RTS; ioctl(midi_in, TIOCMSET, status); // Wait 40 us to make sure everything is stable tv.tv_sec = 0; tv.tv_usec = 40; select(0, NULL, NULL, NULL, &tv); // Step 5 // Set output mode // Bitval = RTS, clock = DTR // 1 ioctl(midi_in, TIOCMGET, &status); status &= ~TIOCM_DTR; /* 0 */ status |= TIOCM_RTS; /* 1 */ ioctl(midi_in, TIOCMSET, status); // Wait 40 us to make sure everything is stable tv.tv_sec = 0; tv.tv_usec = 40; select(0, NULL, NULL, NULL, &tv); // ioctl(midi_in, TIOCMGET, &status); status |= TIOCM_DTR; // 1 rising edge clock status |= TIOCM_RTS; // 1 ioctl(midi_in, TIOCMSET, status); // Wait 40 us to make sure everything is stable tv.tv_sec = 0; tv.tv_usec = 40; select(0, NULL, NULL, NULL, &tv); // 1 ioctl(midi_in, TIOCMGET, &status); status &= ~TIOCM_DTR; // 0 status |= TIOCM_RTS; // 1 ioctl(midi_in, TIOCMSET, status); // Wait 40 us to make sure everything is stable tv.tv_sec = 0; tv.tv_usec = 40; select(0, NULL, NULL, NULL, &tv); // ioctl(midi_in, TIOCMGET, &status); status |= TIOCM_DTR; // 1 rising edge clock status |= TIOCM_RTS; // 1 ioctl(midi_in, TIOCMSET, status); // Wait 40 us to make sure everything is stable tv.tv_sec = 0; tv.tv_usec = 40; select(0, NULL, NULL, NULL, &tv); // 0 ioctl(midi_in, TIOCMGET, &status); status &= ~TIOCM_DTR; // 0 status &= ~TIOCM_RTS; // 0 ioctl(midi_in, TIOCMSET, status); // Wait 40 us to make sure everything is stable tv.tv_sec = 0; tv.tv_usec = 40; select(0, NULL, NULL, NULL, &tv); // ioctl(midi_in, TIOCMGET, &status); status |= TIOCM_DTR; // 1 rising edge clock status &= ~TIOCM_RTS; // 0 ioctl(midi_in, TIOCMSET, status); // Wait 40 us to make sure everything is stable tv.tv_sec = 0; tv.tv_usec = 40; select(0, NULL, NULL, NULL, &tv); // Step 6 ... necessary ? // Set RTS=0,DTR=1 ... but they already are from previous ^ // ioctl(midi_in, TIOCMGET, &status); status |= TIOCM_DTR; // 1 rising edge clock status &= ~TIOCM_RTS; // 0 ioctl(midi_in, TIOCMSET, status); // Wait 40 us to make sure everything is stable tv.tv_sec = 0; tv.tv_usec = 40; select(0, NULL, NULL, NULL, &tv); // Step 7 // Deassert break ioctl(midi_in, TIOCMGET, &status); status &= ~TCSBRK; ioctl(midi_in, TIOCMSET, status); // Wait 100 ms to make sure everything is stable tv.tv_sec = 0; tv.tv_usec = 100000; select(0, NULL, NULL, NULL, &tv); // End Midiator startup sequence -- midi_dev_type = MIDIATOR } #endif // MIDIator #elif defined(__OS_WINDOWS__) #include #include static void CALLBACK midiInputCallback( HMIDIOUT hmin, UINT inputStatus, DWORD instancePtr, DWORD midiMessage, DWORD timestamp); #define MIDI_NOTEON 0x90 #define MIDI_NOTEOFF 0x80 #define MIDI_POLYKEYPRESSURE 0xA0 #define MIDI_CHANNELPRESSURE 0xD0 #define MIDI_PROGRAMCHANGE 0xC0 #define MIDI_CONTROLCHANGE 0xB0 #define MIDI_PITCHBEND 0xE0 typedef struct { DWORD data; DWORD time; } MIDIMESSAGE; MIDIMESSAGE *midiBuffer; static void CALLBACK midiInputCallback( HMIDIOUT hmin, UINT inputStatus, DWORD instancePtr, DWORD midiMessage, DWORD timestamp) { MIDIMESSAGE newMessage; switch (inputStatus) { case MIM_DATA: // Ignore Active Sensing messages if ((midiMessage & 0xff) == 0xfe || (midiMessage & 0xff) == 0xf8) { break; } newMessage.data = midiMessage; newMessage.time = timestamp; // Put newMessage in the circular buffer midiBuffer[writeIndex] = newMessage; writeIndex++; if( writeIndex >= MIDI_BUFFER_SIZE ) writeIndex = 0; break; default: break; } } HMIDIIN hMidiIn ; // Handle to Midi Input Device RtMidi :: RtMidi(int device) { MMRESULT result; UINT uDeviceID; int deveyes; MIDIINCAPS deviceCaps; UINT i; char msg[256]; uDeviceID = midiInGetNumDevs(); if (uDeviceID < 1) { sprintf(msg, "RtMidi: No windoze MIDI device available."); handleError(msg, StkError::MIDI_SYSTEM); } // Our normal scheme is to use the default device if no argument // is supplied to RtMidi() or if the argument = 0. However, // there is no way to specify a default MIDI device under windoze. // So, I'm going to print the list if device is not a valid identifier. if ( device >= 0 && device < (int)uDeviceID ) { // try to open device specified as argument result = midiInOpen(&hMidiIn, device, (DWORD)&midiInputCallback, (DWORD)NULL, CALLBACK_FUNCTION); if (result == MMSYSERR_NOERROR) goto have_good_device; } printf("\nMIDI input interfaces available: %i\n", uDeviceID); for (i=0; i 1) { char choice[16]; while ( deveyes < 0 || deveyes >= (int)uDeviceID ) { printf("\nType a MIDI device number from above: "); fgets(choice, 16, stdin); deveyes = atoi(choice); } } else deveyes = 0; // Open the port and return any errors result = midiInOpen(&hMidiIn, (UINT)deveyes, (DWORD)&midiInputCallback, (DWORD)NULL, CALLBACK_FUNCTION); if (result != MMSYSERR_NOERROR) { sprintf(msg, "RtMidi: Cannot open Windoze MIDI interface %d.", deveyes); handleError(msg, StkError::MIDI_SYSTEM); } have_good_device: // the current device is what we will use // Set up the circular buffer for the Midi Input Messages midiBuffer = new MIDIMESSAGE[MIDI_BUFFER_SIZE]; readIndex = 0; writeIndex = 0; midiInStart( hMidiIn ); } RtMidi :: ~RtMidi() { midiInReset( hMidiIn ); midiInStop( hMidiIn ); midiInClose( hMidiIn ); delete [] midiBuffer; } int RtMidi :: nextMessage() { int status; int byte1; int byte2; MIDIMESSAGE lastEvent; static DWORD lastTime = 0; static DWORD newTime = 0; if ( readIndex == writeIndex ) return 0; lastEvent = midiBuffer[readIndex]; readIndex++; if ( readIndex >= MIDI_BUFFER_SIZE ) readIndex = 0; status = (int) (lastEvent.data & 0xff); byte1 = (int) (lastEvent.data & 0xff00) >> 8; byte2 = (int) (lastEvent.data & 0xff0000) >> 16; channel = (int) (status & 0x0f); status &= 0xf0; // Clear lower byte of status newTime = lastEvent.time; deltaTime = (float) (newTime - lastTime) * 0.001; lastTime = newTime; if ((status == MIDI_PROGRAMCHANGE) || (status == MIDI_CHANNELPRESSURE)) { messageType = status; byteTwo = (float) byte1; } else if ((status == MIDI_NOTEON) || (status == MIDI_NOTEOFF) || (status == MIDI_CONTROLCHANGE) || (status == MIDI_POLYKEYPRESSURE)) { messageType = status; byteTwo = (float) byte1; byteThree = (float) byte2; } else if (status == MIDI_PITCHBEND) { messageType = status; byteTwo = (float) (byte1 * ONE_OVER_128); byteTwo += (float) byte2; } else { messageType = -1; } return messageType; } #endif void RtMidi :: printMessage() const { printf("type = %d, channel = %d, byte2 = %f, byte3 = %f\n", this->getType(), this->getChannel(), this->getByteTwo(), this->getByteThree()); } int RtMidi :: getType() const { return messageType; } int RtMidi :: getChannel() const { return channel; } MY_FLOAT RtMidi :: getByteTwo() const { return byteTwo; } MY_FLOAT RtMidi :: getByteThree() const { return byteThree; } MY_FLOAT RtMidi :: getDeltaTime() const { return deltaTime; }