/*------------------------------------------ DRUMDLL.C -- DLL module for DRUM program (c) Charles Petzold, 1992 ------------------------------------------*/ #include extern "C" { #include } #include #include "drumdll.h" #define min(a,b) (((a) < (b)) ? (a) : (b)) #define max(a,b) (((a) > (b)) ? (a) : (b)) #define minmax(a,x,b) (min (max (x, a), b)) #define TIMER_RES 5 void FAR PASCAL _export DrumTimerFunc (WORD, WORD, DWORD, DWORD, DWORD) ; BOOL bSequenceGoing, bEndSequence ; DRUM drum ; HMIDIOUT hMidiOut ; HWND hwndNotify ; int iIndex ; WORD wTimerRes, wTimerID ; int FAR PASCAL LibMain (HANDLE hInstance, WORD wDataSeg, WORD wHeapSize, LPSTR lpszCmdLine) { return 1 ; } DWORD MidiOutMessage (HMIDIOUT hMidi, int iStatus, int iChannel, int iData1, int iData2) { DWORD dwMessage ; dwMessage = iStatus | iChannel | (iData1 << 8) | ((long) iData2 << 16) ; return midiOutShortMsg (hMidi, dwMessage) ; } void FAR PASCAL _export DrumSetParams (PDRUM pdrum) { _fmemcpy (&drum, pdrum, sizeof (DRUM)) ; } BOOL FAR PASCAL _export DrumBeginSequence (HWND hwnd) { TIMECAPS tc ; hwndNotify = hwnd ; // Save window handle for notification DrumEndSequence (TRUE) ; // Stop current sequence if running // Open the MIDI Mapper output port if (midiOutOpen (&hMidiOut, (WORD) MIDIMAPPER, NULL, NULL, 0L)) return FALSE ; // Send Program Change messages for channels 9 and 15 MidiOutMessage (hMidiOut, 0xC0, 9, 0, 0) ; MidiOutMessage (hMidiOut, 0xC0, 15, 0, 0) ; // Begin sequence by setting a timer event timeGetDevCaps (&tc, sizeof (TIMECAPS)) ; wTimerRes = minmax (tc.wPeriodMin, TIMER_RES, tc.wPeriodMax) ; timeBeginPeriod (wTimerRes) ; wTimerID = timeSetEvent (max ((int) wTimerRes, drum.iMsecPerBeat), wTimerRes, DrumTimerFunc, 0L, TIME_ONESHOT) ; if (wTimerID == 0) { timeEndPeriod (wTimerRes) ; midiOutClose (hMidiOut) ; return FALSE ; } iIndex = -1 ; bEndSequence = FALSE ; bSequenceGoing = TRUE ; return TRUE ; } void FAR PASCAL _export DrumEndSequence (BOOL bRightAway) { if (bRightAway) { if (bSequenceGoing) { // stop the timer if (wTimerID) timeKillEvent (wTimerID) ; timeEndPeriod (wTimerRes) ; // turn off all notes MidiOutMessage (hMidiOut, 0xB0, 9, 123, 0) ; MidiOutMessage (hMidiOut, 0xB0, 15, 123, 0) ; // close the MIDI port midiOutClose (hMidiOut) ; bSequenceGoing = FALSE ; } } else bEndSequence = TRUE ; } void FAR PASCAL _export DrumTimerFunc (WORD wID, WORD wMsg, DWORD dwUser, DWORD dw1, DWORD dw2) { static DWORD dwSeqExtLast [NUM_PERC], dwSeqBasLast [NUM_PERC] ; int i ; // Note Off messages for channels 9 and 15 if (iIndex != -1) { for (i = 0 ; i < NUM_PERC ; i++) { if (dwSeqExtLast[i] & 1L << iIndex) MidiOutMessage (hMidiOut, 0x80, 9, i + 35, 0) ; if (dwSeqBasLast[i] & 1L << iIndex) ; MidiOutMessage (hMidiOut, 0x80, 15, i + 35, 0) ; } } // Increment index and notify window to advance bouncing ball iIndex = (iIndex + 1) % drum.iNumBeats ; PostMessage (hwndNotify, WM_USER_NOTIFY, iIndex, timeGetTime ()) ; // Check if ending the sequence if (bEndSequence && iIndex == 0) { PostMessage (hwndNotify, WM_USER_FINISHED, 0, 0L) ; return ; } // Note On messages for channels 9 and 15 for (i = 0 ; i < NUM_PERC ; i++) { if (drum.dwSeqExt[i] & 1L << iIndex) MidiOutMessage (hMidiOut, 0x90, 9, i + 35, drum.iVelocity) ; if (drum.dwSeqBas[i] & 1L << iIndex) MidiOutMessage (hMidiOut, 0x90, 15, i + 35, drum.iVelocity) ; dwSeqExtLast[i] = drum.dwSeqExt[i] ; dwSeqBasLast[i] = drum.dwSeqBas[i] ; } // Set a new timer event wTimerID = timeSetEvent (max ((int) wTimerRes, drum.iMsecPerBeat), wTimerRes, DrumTimerFunc, 0L, TIME_ONESHOT) ; if (wTimerID == 0) { PostMessage (hwndNotify, WM_USER_ERROR, 0, 0L) ; } }