www.pudn.com > LSJ_MIDI_READ.rar > Stream.c
/* You must link with winmm.lib. If using Visual C++, go to Build->Settings. Flip to the Link page, and add winmm.lib to the library/object modules. This app plays a musical phrase from the song "Twinkle, Twinkle Little Star". It sends the MIDI events to the default MIDI Out device using the Stream API. The Stream API does the actual timing out of each MIDI event. Therefore we don't have to use a multi-media timer nor delays. */ #include#include #include #include /* Here's an array of MIDI Note-On and Note-Off events to play our musical phrase. We declare the array as an array of unsigned longs. Makes it easier to statically declare the MIDIEVENT structures in an array. I've set the timing fields in terms of PPQN clocks, as they would be found in MIDI File using 96 PPQN Time Division. NOTES: The first event is a System Exclusive event to set Master Volume to full. Note that the high byte of the dwEvent field (ie, the 3rd unsigned long) must be MEVT_LONGMSG, and the remaining bytes must be the count of how many SysEx bytes follow. Also note that the data must be padded out to a doubleword boundary. That's implicit in our declaration of this as an unsigned long array. The second event is a Tempo event which sets tempo to 500,000 microseconds per quarter note. I put this event at the start of the array so that I don't have to use midiStreamProperty() to set tempo prior to playback. Note that the high byte of dwEvent must be MEVT_TEMPO to tell the stream device that this is a Tempo event. Also, note that the MIDI event is packed into a long the same way that it would be to send to midiOutShortMsg(). */ unsigned long Phrase[] = {0, 0, ((unsigned long)MEVT_LONGMSG<<24) | 8, 0x047F7FF0, 0xF77F7F01, 0, 0, ((unsigned long)MEVT_TEMPO<<24) | 0x0007A120, 0, 0, 0x007F3C90, 48, 0, 0x00003C90, 0, 0, 0x007F3C90, 48, 0, 0x00003C90, 0, 0, 0x007F4390, 48, 0, 0x00004390, 0, 0, 0x007F4390, 48, 0, 0x00004390, 0, 0, 0x007F4590, 48, 0, 0x00004590, 0, 0, 0x007F4590, 48, 0, 0x00004590, 0, 0, 0x007F4390, 86, 0, 0x00004390, 10, 0, 0x007F4190, 48, 0, 0x00004190, 0, 0, 0x007F4190, 48, 0, 0x00004190, 0, 0, 0x007F4090, 48, 0, 0x00004090, 0, 0, 0x007F4090, 48, 0, 0x00004090, 0, 0, 0x007F3E90, 48, 0, 0x00003E90, 0, 0, 0x007F3E90, 48, 0, 0x00003E90, 0, 0, 0x007F3C90, 96, 0, 0x00003C90}; /*********************** PrintMidiOutErrorMsg() ************************** * Retrieves and displays an error message for the passed MIDI Out error * number. It does this using midiOutGetErrorText(). *************************************************************************/ void PrintMidiOutErrorMsg(unsigned long err) { #define BUFFERSIZE 120 char buffer[BUFFERSIZE]; if (!(err = midiOutGetErrorText(err, &buffer[0], BUFFERSIZE))) { printf("%s\r\n", &buffer[0]); } else if (err == MMSYSERR_BADERRNUM) { printf("Strange error number returned!\r\n"); } else { printf("Specified pointer is invalid!\r\n"); } } /* ******************************** main() ******************************** */ int main(int argc, char **argv) { HMIDISTRM handle; MIDIHDR midiHdr; unsigned long err, tempo; MIDIPROPTIMEDIV prop; /* Open default MIDI Out stream device */ err = 0; if (!(err = midiStreamOpen(&handle, &err, 1, 0, 0, CALLBACK_NULL))) { /* Set the stream device's Time Division to 96 PPQN */ prop.cbStruct = sizeof(MIDIPROPTIMEDIV); prop.dwTimeDiv = 96; err = midiStreamProperty(handle, (LPBYTE)&prop, MIDIPROP_SET|MIDIPROP_TIMEDIV); if (err) { PrintMidiOutErrorMsg(err); } /* Store pointer to our stream (ie, buffer) of messages in MIDIHDR */ midiHdr.lpData = (LPBYTE)&Phrase[0]; /* Store its size in the MIDIHDR */ midiHdr.dwBufferLength = midiHdr.dwBytesRecorded = sizeof(Phrase); /* Flags must be set to 0 */ midiHdr.dwFlags = 0; /* Prepare the buffer and MIDIHDR */ err = midiOutPrepareHeader(handle, &midiHdr, sizeof(MIDIHDR)); if (!err) { /* Queue the Stream of messages. Output doesn't actually start until we later call midiStreamRestart(). */ err = midiStreamOut(handle, &midiHdr, sizeof(MIDIHDR)); if (err) { bad: PrintMidiOutErrorMsg(err); midiOutUnprepareHeader(handle, &midiHdr, sizeof(MIDIHDR)); } else { /* Start outputting the Stream of messages. This should return immediately as the stream device will time out and output the messages on its own in the background. */ err = midiStreamRestart(handle); if (err) { goto bad; } // The current (default) tempo tempo = 0x0007A120; /* Wait for playback to stop, and then unprepare the buffer and MIDIHDR */ while (MIDIERR_STILLPLAYING == midiOutUnprepareHeader(handle, &midiHdr, sizeof(MIDIHDR))) { /* Show how to change the Tempo in real-time. When user presses up or down arrows, raise or drop the tempo */ while(_kbhit()) { MIDIPROPTEMPO tprop; int chr; chr = _getch(); if (chr == 0xE0) { chr = _getch(); if (chr == 0x48) { if (tempo > 50000) tempo -= 50000; } if (chr == 0x50) { if (tempo < (0x00FFFFFF - 50000)) tempo += 50000; } tprop.cbStruct = sizeof(MIDIPROPTEMPO); tprop.dwTempo = tempo; err = midiStreamProperty(handle, (LPBYTE)&tprop, MIDIPROP_SET|MIDIPROP_TEMPO); if (err) { PrintMidiOutErrorMsg(err); } } } /* Delay to give playback time to finish. Normally, you wouldn't do this loop. You'd instead specify a callback function to midiOpenStream() and do the midiOutUnprepareHeader and midiStreamClose after Windows called your callback to indicate that the stream was done playing. But here I'm just interested in a quick and dirty example... */ Sleep(1000); } } } /* An error. Print a message */ else { PrintMidiOutErrorMsg(err); } /* Close the MIDI Stream */ midiStreamClose(handle); } else { printf("Error opening the default MIDI Stream!\r\n"); PrintMidiOutErrorMsg(err); } return(0); }