home *** CD-ROM | disk | FTP | other *** search
- /*----------------------------------------------------
- DRUM.C -- MIDI Drum Machine for Multimedia Windows
- (c) Charles Petzold, 1992
- ----------------------------------------------------*/
-
- #include <windows.h>
- #include <stdlib.h>
- #include <string.h>
- #include <math.h>
- #include "drum.h"
- #include "drumdll.h"
- #include "drumfile.h"
-
- typedef unsigned int UINT ;
-
- #define min(a,b) (((a) < (b)) ? (a) : (b))
- #define max(a,b) (((a) > (b)) ? (a) : (b))
-
- long FAR PASCAL _export WndProc (HWND, UINT, UINT, LONG) ;
- BOOL FAR PASCAL _export AboutProc (HWND, UINT, UINT, LONG) ;
-
- void DrawRectangle (HDC, int, int, DWORD *, DWORD *) ;
- void ErrorMessage (HWND, char *, LPSTR) ;
- void DoCaption (HWND, char *) ;
- short AskAboutSave (HWND, char *) ;
-
- char *szPerc [NUM_PERC] =
- {
- "Acoustic Bass Drum", "Bass Drum 1", "Side Stick",
- "Acoustic Snare", "Hand Clap", "Electric Snare",
- "Low Floor Tom", "Closed High-Hat", "High Floor Tom",
- "Pedal High Hat", "Low Tom", "Open High Hat",
- "Low-Mid Tom", "High-Mid Tom", "Crash Cymbal 1",
- "High Tom", "Ride Cymbal 1", "Chinese Cymbal",
- "Ride Bell", "Tambourine", "Splash Cymbal",
- "Cowbell", "Crash Cymbal 2", "Vibraslap",
- "Ride Cymbal 2", "High Bongo", "Low Bongo",
- "Mute High Conga", "Open High Conga", "Low Conga",
- "High Timbale", "Low Timbale", "High Agogo",
- "Low Agogo", "Cabasa", "Maracas",
- "Short Whistle", "Long Whistle", "Short Guiro",
- "Long Guiro", "Claves", "High Wood Block",
- "Low Wood Block", "Mute Cuica", "Open Cuica",
- "Mute Triangle", "Open Triangle"
- } ;
-
- char szAppName [] = "Drum" ;
- char szUntitled [] = "(Untitled)" ;
- char szBuffer [80 + _MAX_FNAME + _MAX_EXT] ;
- HANDLE hInst ;
- short cxChar, cyChar ;
-
- int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
- LPSTR lpszCmdParam, int nCmdShow)
- {
- HWND hwnd ;
- MSG msg ;
- WNDCLASS wndclass ;
-
- if (hPrevInstance)
- {
- ErrorMessage (NULL, "Only one instance is allowed!", NULL) ;
- return 0 ;
- }
-
- hInst = hInstance ;
-
- wndclass.style = CS_HREDRAW | CS_VREDRAW ;
- wndclass.lpfnWndProc = WndProc ;
- wndclass.cbClsExtra = 0 ;
- wndclass.cbWndExtra = 0 ;
- wndclass.hInstance = hInstance ;
- wndclass.hIcon = LoadIcon (hInstance, szAppName) ;
- wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
- wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
- wndclass.lpszMenuName = szAppName ;
- wndclass.lpszClassName = szAppName ;
-
- RegisterClass (&wndclass) ;
-
- hwnd = CreateWindow (szAppName, NULL,
- WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
- CW_USEDEFAULT, CW_USEDEFAULT,
- CW_USEDEFAULT, CW_USEDEFAULT,
- NULL, NULL, hInstance, lpszCmdParam) ;
-
- ShowWindow (hwnd, SW_SHOWMAXIMIZED) ;
- UpdateWindow (hwnd) ;
-
- while (GetMessage (&msg, NULL, 0, 0))
- {
- TranslateMessage (&msg) ;
- DispatchMessage (&msg) ;
- }
- return msg.wParam ;
- }
-
- long FAR PASCAL _export WndProc (HWND hwnd, UINT message, UINT wParam,
- LONG lParam)
- {
- static BOOL bNeedSave ;
- static char szFileName [_MAX_PATH],
- szTitleName [_MAX_FNAME + _MAX_EXT] ;
- static DRUM drum ;
- static FARPROC lpfnAboutProc ;
- static HMENU hMenu ;
- static int iTempo = 50, iIndexLast ;
- char * szError ;
- DWORD dwCurrPos ;
- HDC hdc ;
- int i, x, y ;
- PAINTSTRUCT ps ;
-
- switch (message)
- {
- case WM_CREATE:
- // Initialize DRUM structure
-
- drum.iMsecPerBeat = 100 ;
- drum.iVelocity = 64 ;
- drum.iNumBeats = 32 ;
-
- DrumSetParams (&drum) ;
-
- // Other initialization
-
- cxChar = LOWORD (GetDialogBaseUnits ()) ;
- cyChar = HIWORD (GetDialogBaseUnits ()) ;
-
- lpfnAboutProc = MakeProcInstance ((FARPROC) AboutProc, hInst) ;
- hMenu = GetMenu (hwnd) ;
-
- // Process command line and start sequence
-
- lstrcpy (szFileName,
- ((LPCREATESTRUCT) lParam)->lpCreateParams) ;
-
- if (lstrlen (szFileName))
- {
- if (DrumFileParse (szFileName, szTitleName))
- {
- szError = DrumFileRead (&drum, szFileName) ;
-
- if (szError == NULL)
- {
- iTempo = (int) (50 *
- (log10 (drum.iMsecPerBeat) - 1)) ;
-
- DrumSetParams (&drum) ;
- }
- else
- {
- ErrorMessage (hwnd, szError, szTitleName) ;
- szTitleName [0] = '\0' ;
- }
- }
- else
- {
- ErrorMessage (hwnd, "Invalid command line: %s",
- ((LPCREATESTRUCT) lParam)->lpCreateParams) ;
- szTitleName [0] = '\0' ;
- }
- }
-
- // Initialize "Volume" scroll bar
-
- SetScrollRange (hwnd, SB_HORZ, 1, 127, FALSE) ;
- SetScrollPos (hwnd, SB_HORZ, drum.iVelocity, TRUE) ;
-
- // Initialize "Tempo" scroll bar
-
- SetScrollRange (hwnd, SB_VERT, 0, 100, FALSE) ;
- SetScrollPos (hwnd, SB_VERT, iTempo, TRUE) ;
-
- DoCaption (hwnd, szTitleName) ;
- return 0 ;
-
- case WM_COMMAND:
- switch (wParam)
- {
- case IDM_NEW:
- if (bNeedSave && IDCANCEL ==
- AskAboutSave (hwnd, szTitleName))
- return 0 ;
-
- // Clear drum pattern
-
- for (i = 0 ; i < NUM_PERC ; i++)
- {
- drum.dwSeqBas [i] = 0L ;
- drum.dwSeqExt [i] = 0L ;
- }
-
- InvalidateRect (hwnd, NULL, FALSE) ;
- DrumSetParams (&drum) ;
- bNeedSave = FALSE ;
- return 0 ;
-
- case IDM_OPEN:
- // Save previous file
-
- if (bNeedSave && IDCANCEL ==
- AskAboutSave (hwnd, szTitleName))
- return 0 ;
-
- // Open the selected file
-
- if (DrumFileOpenDlg (hwnd, szFileName, szTitleName))
- {
- szError = DrumFileRead (&drum, szFileName) ;
-
- if (szError != NULL)
- {
- ErrorMessage (hwnd, szError, szTitleName) ;
- szTitleName [0] = '\0' ;
- }
- else
- {
- // Set new parameters
-
- iTempo = (int) (50 *
- (log10 (drum.iMsecPerBeat) - 1)) ;
-
- SetScrollPos (hwnd, SB_VERT, iTempo, TRUE) ;
- SetScrollPos (hwnd, SB_HORZ,
- drum.iVelocity, TRUE) ;
-
- DrumSetParams (&drum) ;
- InvalidateRect (hwnd, NULL, FALSE) ;
- bNeedSave = FALSE ;
- }
-
- DoCaption (hwnd, szTitleName) ;
- }
- return 0 ;
-
- case IDM_SAVE:
- case IDM_SAVEAS:
- // Save the selected file
-
- if ((wParam == IDM_SAVE && szTitleName [0]) ||
- DrumFileSaveDlg (hwnd, szFileName, szTitleName))
- {
- szError = DrumFileWrite (&drum, szFileName) ;
-
- if (szError != NULL)
- {
- ErrorMessage (hwnd, szError, szTitleName) ;
- szTitleName [0] = '\0' ;
- }
- else
- bNeedSave = FALSE ;
-
- DoCaption (hwnd, szTitleName) ;
- }
- return 0 ;
-
- case IDM_EXIT:
- SendMessage (hwnd, WM_SYSCOMMAND, SC_CLOSE, 0L) ;
- return 0 ;
-
- case IDM_RUNNING:
- // Begin sequence
-
- if (!DrumBeginSequence (hwnd))
- {
- ErrorMessage (hwnd,
- "Could not start MIDI sequence -- "
- "MIDI Mapper device is unavailable!",
- szTitleName) ;
- }
- else
- {
- CheckMenuItem (hMenu, IDM_RUNNING, MF_CHECKED);
- CheckMenuItem (hMenu, IDM_STOPPED, MF_UNCHECKED);
- }
- return 0 ;
-
- case IDM_STOPPED:
- // Finish at end of sequence
-
- DrumEndSequence (FALSE) ;
- return 0 ;
-
- case IDM_ABOUT:
- DialogBox (hInst, "AboutBox", hwnd, lpfnAboutProc) ;
- return 0 ;
- }
- return 0 ;
-
- case WM_LBUTTONDOWN:
- case WM_RBUTTONDOWN:
- hdc = GetDC (hwnd) ;
-
- // Convert mouse coordinates to grid coordinates
-
- x = LOWORD (lParam) / cxChar - 40 ;
- y = 2 * HIWORD (lParam) / cyChar - 2 ;
-
- // Set a new number of beats of sequence
-
- if (x > 0 && x <= 32 && y < 0)
- {
- SetTextColor (hdc, RGB (255, 255, 255)) ;
- TextOut (hdc, (40 + drum.iNumBeats) * cxChar, 0, ":|", 2);
- SetTextColor (hdc, RGB (0, 0, 0)) ;
-
- if (drum.iNumBeats % 4 == 0)
- TextOut (hdc, (40 + drum.iNumBeats) * cxChar, 0,
- ".", 1) ;
-
- drum.iNumBeats = x ;
-
- TextOut (hdc, (40 + drum.iNumBeats) * cxChar, 0, ":|", 2) ;
-
- bNeedSave = TRUE ;
- }
-
- // Set or reset a percussion instrument beat
-
- if (x >= 0 && x < 32 && y >= 0 && y < NUM_PERC)
- {
- if (message == WM_LBUTTONDOWN)
- drum.dwSeqBas[y] ^= (1L << x) ;
- else
- drum.dwSeqExt[y] ^= (1L << x) ;
-
- DrawRectangle (hdc, x, y, drum.dwSeqBas, drum.dwSeqExt) ;
-
- bNeedSave = TRUE ;
- }
-
- ReleaseDC (hwnd, hdc) ;
- DrumSetParams (&drum) ;
- return 0 ;
-
- case WM_HSCROLL:
- // Change the note velocity
-
- switch (wParam)
- {
- case SB_LINEUP: drum.iVelocity -= 1 ; break ;
- case SB_LINEDOWN: drum.iVelocity += 1 ; break ;
- case SB_PAGEUP: drum.iVelocity -= 8 ; break ;
- case SB_PAGEDOWN: drum.iVelocity += 8 ; break ;
- case SB_THUMBPOSITION:
- drum.iVelocity = LOWORD (lParam) ;
- break ;
-
- default:
- return 0 ;
- }
-
- drum.iVelocity = max (1, min (drum.iVelocity, 127)) ;
- SetScrollPos (hwnd, SB_HORZ, drum.iVelocity, TRUE) ;
- DrumSetParams (&drum) ;
- bNeedSave = TRUE ;
- return 0 ;
-
- case WM_VSCROLL:
- // Change the tempo
-
- switch (wParam)
- {
- case SB_LINEUP: iTempo -= 1 ; break ;
- case SB_LINEDOWN: iTempo += 1 ; break ;
- case SB_PAGEUP: iTempo -= 10 ; break ;
- case SB_PAGEDOWN: iTempo += 10 ; break ;
- case SB_THUMBPOSITION:
- iTempo = LOWORD (lParam) ;
- break ;
-
- default:
- return 0 ;
- }
-
- iTempo = max (0, min (iTempo, 100)) ;
- SetScrollPos (hwnd, SB_VERT, iTempo, TRUE) ;
-
- drum.iMsecPerBeat = (WORD) (10 * pow (100, iTempo / 100.0)) ;
-
- DrumSetParams (&drum) ;
- bNeedSave = TRUE ;
- return 0 ;
-
- case WM_PAINT:
- hdc = BeginPaint (hwnd, &ps) ;
-
- SetTextAlign (hdc, TA_UPDATECP) ;
- SetBkMode (hdc, TRANSPARENT) ;
-
- // Draw the text strings and horizontal lines
-
- for (i = 0 ; i < NUM_PERC ; i++)
- {
- MoveTo (hdc, i & 1 ? 20 * cxChar : cxChar,
- (2 * i + 3) * cyChar / 4) ;
-
- TextOut (hdc, 0, 0, szPerc [i], strlen (szPerc [i])) ;
-
- dwCurrPos = GetCurrentPosition (hdc) ;
-
- x = LOWORD (dwCurrPos) ;
- y = HIWORD (dwCurrPos) ;
-
- MoveTo (hdc, x + cxChar, y + cyChar / 2) ;
- LineTo (hdc, 39 * cxChar, y + cyChar / 2) ;
- }
-
- SetTextAlign (hdc, 0) ;
-
- // Draw rectangular grid, repeat mark, and beat marks
-
- for (x = 0 ; x < 32 ; x++)
- {
- for (y = 0 ; y < NUM_PERC ; y++)
- DrawRectangle (hdc, x, y, drum.dwSeqBas,
- drum.dwSeqExt) ;
-
- SetTextColor (hdc, x == drum.iNumBeats - 1 ?
- RGB (0, 0, 0) : RGB (255, 255, 255)) ;
-
- TextOut (hdc, (41 + x) * cxChar, 0, ":|", 2) ;
-
- SetTextColor (hdc, RGB (0, 0, 0)) ;
-
- if (x % 4 == 0)
- TextOut (hdc, (40 + x) * cxChar, 0, ".", 1) ;
- }
-
- EndPaint (hwnd, &ps) ;
- return 0 ;
-
- case WM_USER_NOTIFY:
- // Draw the "bouncing ball"
-
- hdc = GetDC (hwnd) ;
-
- SelectObject (hdc, GetStockObject (NULL_PEN)) ;
- SelectObject (hdc, GetStockObject (WHITE_BRUSH)) ;
-
- for (i = 0 ; i < 2 ; i++)
- {
- x = iIndexLast ;
- y = NUM_PERC + 1 ;
-
- Ellipse (hdc, (x + 40) * cxChar, (2 * y + 3) * cyChar / 4,
- (x + 41) * cxChar, (2 * y + 5) * cyChar / 4);
-
- iIndexLast = wParam ;
- SelectObject (hdc, GetStockObject (BLACK_BRUSH)) ;
- }
-
- ReleaseDC (hwnd, hdc) ;
- return 0 ;
-
- case WM_USER_ERROR:
- ErrorMessage (hwnd, "Can't set timer event for tempo",
- szTitleName) ;
-
- // fall through
- case WM_USER_FINISHED:
- DrumEndSequence (TRUE) ;
- CheckMenuItem (hMenu, IDM_RUNNING, MF_UNCHECKED) ;
- CheckMenuItem (hMenu, IDM_STOPPED, MF_CHECKED) ;
- return 0 ;
-
- case WM_CLOSE:
- if (!bNeedSave || IDCANCEL != AskAboutSave (hwnd, szTitleName))
- DestroyWindow (hwnd) ;
-
- return 0 ;
-
- case WM_QUERYENDSESSION:
- if (!bNeedSave || IDCANCEL != AskAboutSave (hwnd, szTitleName))
- return 1L ;
-
- return 0 ;
-
- case WM_DESTROY:
- DrumEndSequence (TRUE) ;
- PostQuitMessage (0) ;
- return 0 ;
- }
-
- return DefWindowProc (hwnd, message, wParam, lParam) ;
- }
-
- BOOL FAR PASCAL _export AboutProc (HWND hDlg, UINT message, UINT wParam,
- LONG lParam)
- {
- switch (message)
- {
- case WM_INITDIALOG:
- return TRUE ;
-
- case WM_COMMAND:
- switch (wParam)
- {
- case IDOK:
- EndDialog (hDlg, 0) ;
- return TRUE ;
- }
- break ;
- }
- return FALSE ;
- }
-
- void DrawRectangle (HDC hdc, int x, int y, DWORD *dwSeqBas, DWORD *dwSeqExt)
- {
- int iBrush ;
-
- if (dwSeqBas [y] & dwSeqExt [y] & (1L << x))
- iBrush = BLACK_BRUSH ;
-
- else if (dwSeqBas [y] & (1L << x))
- iBrush = LTGRAY_BRUSH ;
-
- else if (dwSeqExt [y] & (1L << x))
- iBrush = DKGRAY_BRUSH ;
-
- else
- iBrush = WHITE_BRUSH ;
-
- SelectObject (hdc, GetStockObject (iBrush)) ;
-
- Rectangle (hdc, (x + 40) * cxChar , (2 * y + 4) * cyChar / 4,
- (x + 41) * cxChar + 1, (2 * y + 6) * cyChar / 4 + 1) ;
- }
-
- void ErrorMessage (HWND hwnd, char * szError, LPSTR szTitleName)
- {
- wsprintf (szBuffer, szError,
- (LPSTR) (szTitleName [0] ? szTitleName : szUntitled)) ;
-
- MessageBeep (MB_ICONEXCLAMATION) ;
- MessageBox (hwnd, szBuffer, szAppName, MB_OK | MB_ICONEXCLAMATION) ;
- }
-
- void DoCaption (HWND hwnd, char * szTitleName)
- {
- wsprintf (szBuffer, "MIDI Drum Machine - %s",
- (LPSTR) (szTitleName [0] ? szTitleName : szUntitled)) ;
-
- SetWindowText (hwnd, szBuffer) ;
- }
-
- short AskAboutSave (HWND hwnd, char * szTitleName)
- {
- short nReturn ;
-
- wsprintf (szBuffer, "Save current changes in %s?",
- (LPSTR) (szTitleName [0] ? szTitleName : szUntitled)) ;
-
- nReturn = MessageBox (hwnd, szBuffer, szAppName,
- MB_YESNOCANCEL | MB_ICONQUESTION) ;
-
- if (nReturn == IDYES)
- if (!SendMessage (hwnd, WM_COMMAND, IDM_SAVE, 0L))
- nReturn = IDCANCEL ;
-
- return nReturn ;
- }
-