home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / pcmagazi / 1992 / 20 / cdplayer / cdplayer.c < prev    next >
C/C++ Source or Header  |  1992-09-21  |  20KB  |  663 lines

  1. /********************************************************************
  2.  
  3.   CDPlayer 1.0 Copyright (c) 1992 Jeff Prosise
  4.   First published in PC Magazine, U.S. Edition, November 24, 1992
  5.  
  6.   CDPlayer uses Windows 3.1's Media Control Interface (MCI) to
  7.   control track selections on audio CDs played in CD-ROM drives.
  8.  
  9.  ********************************************************************/
  10.  
  11. #include <windows.h>
  12. #include <mmsystem.h>
  13. #include <stdlib.h>
  14. #include "cdplayer.h"
  15.  
  16. long FAR PASCAL WndProc (HWND, WORD, WORD, LONG);
  17. BOOL FAR PASCAL AboutDlgProc (HWND, WORD, WORD, LONG);
  18.  
  19. void BeginPlay (BYTE, HWND);
  20. void ResumePlay (DWORD, HWND);
  21. void UpdateTrackButtons (HWND);
  22. char *IntToMinSec (int, char *);
  23. char *TMSFtoMinSec (DWORD, char *);
  24.  
  25. MCI_STATUS_PARMS MCIStatus;            // MCI_STATUS structure
  26. MCI_OPEN_PARMS MCIOpen;                // MCI_OPEN structure
  27. MCI_PLAY_PARMS MCIPlay;                // MCI_PLAY structure
  28. MCI_SET_PARMS MCISet;                // MCI_SET structure
  29.  
  30. BOOL bMediaPresent = FALSE;            // Media status flag
  31. BOOL bScrolling = FALSE;            // Scroll flag
  32. BOOL bPaused = FALSE;                // Pause flag
  33.  
  34. WORD wNumberOfTracks;                // Number of tracks
  35. WORD wCurrentTrack = 0;                // Current track number
  36. WORD awTrackLength[99];                // Track lengths in seconds
  37. DWORD dwPosition;                    // Pause position
  38. WORD wPageNumber = 0;                // Page number
  39. WORD wPageCount = 1;                // Page count
  40.  
  41. WORD wTimerID;                        // Timer ID value
  42. HWND hwndTrack, hwndLength;            // Window handles
  43. HWND hwndTime, hwndScrollBar;
  44.  
  45. /********************************************************************
  46.   WinMain starts the program.
  47.  ********************************************************************/
  48.  
  49. int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
  50.                     LPSTR lpszCmdLine, int nCmdShow)
  51. {
  52.     static char szOpenError[] = "Unable to open the CD Audio device";
  53.     static char szTimerError[] = "Unable to allocate a timer";
  54.     static char szAppName[] = "CDPlayer";
  55.     WNDCLASS wndclass;
  56.     HWND hwnd;
  57.     MSG msg;
  58.  
  59.     // Register the window class
  60.     if (!hPrevInstance) {
  61.         wndclass.style = 0;
  62.         wndclass.lpfnWndProc = (WNDPROC) WndProc;
  63.         wndclass.cbClsExtra = 0;
  64.         wndclass.cbWndExtra = DLGWINDOWEXTRA;
  65.         wndclass.hInstance = hInstance;
  66.         wndclass.hIcon = LoadIcon (hInstance, szAppName);
  67.         wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
  68.         wndclass.hbrBackground = COLOR_WINDOW + 1;
  69.         wndclass.lpszMenuName = NULL;
  70.         wndclass.lpszClassName = szAppName;
  71.  
  72.         RegisterClass (&wndclass);
  73.     }
  74.  
  75.     // Create a window based on the registered class
  76.     hwnd = CreateDialog (hInstance, szAppName, 0 , NULL);
  77.  
  78.     // Open the CD Audio device and set the time format to TMSF
  79.     MCISet.dwTimeFormat = MCI_FORMAT_TMSF;
  80.     MCIOpen.lpstrDeviceType = (LPCSTR) MCI_DEVTYPE_CD_AUDIO;
  81.     if (!mciSendCommand (NULL, MCI_OPEN, MCI_OPEN_TYPE |
  82.         MCI_OPEN_TYPE_ID, (DWORD) (LPSTR) &MCIOpen))
  83.         mciSendCommand (MCIOpen.wDeviceID, MCI_SET,
  84.             MCI_SET_TIME_FORMAT, (DWORD) (LPSTR) &MCISet);
  85.     else if (!mciSendCommand (NULL, MCI_OPEN, MCI_OPEN_TYPE |
  86.         MCI_OPEN_TYPE_ID | MCI_OPEN_SHAREABLE, (DWORD) (LPSTR) &MCIOpen))
  87.         mciSendCommand (MCIOpen.wDeviceID, MCI_SET,
  88.             MCI_SET_TIME_FORMAT, (DWORD) (LPSTR) &MCISet);
  89.     else {
  90.         MessageBeep (MB_ICONEXCLAMATION);
  91.         MessageBox (hwnd, szOpenError, "Error", MB_ICONEXCLAMATION | MB_OK);
  92.         return FALSE;
  93.     }
  94.  
  95.     // Get window handles and font handle
  96.     hwndTrack = GetDlgItem (hwnd, IDD_TRACK);
  97.     hwndLength = GetDlgItem (hwnd, IDD_LENGTH);
  98.     hwndTime = GetDlgItem (hwnd, IDD_TIME);
  99.     hwndScrollBar = GetDlgItem (hwnd, IDD_SCROLLBAR);
  100.  
  101.     // Allocate a timer that calls WndProc once per second
  102.     if ((wTimerID = SetTimer (hwnd, 1, 1000, NULL)) == NULL) {
  103.         MessageBeep (MB_ICONEXCLAMATION);
  104.         MessageBox (hwnd, szTimerError, "Error", MB_ICONEXCLAMATION | MB_OK);
  105.         return FALSE;
  106.     }
  107.  
  108.     // Display the window
  109.     ShowWindow (hwnd, nCmdShow);
  110.     UpdateWindow (hwnd);
  111.  
  112.     // Enter the message loop
  113.     while (GetMessage (&msg, NULL, 0, 0))
  114.         if ((hwnd == 0) || (!IsDialogMessage (hwnd, &msg)))
  115.             DispatchMessage (&msg);
  116.  
  117.     // Terminate the program
  118.     return msg.wParam;
  119. }
  120.  
  121. /********************************************************************
  122.   WndProc processes messages to the main window.
  123.  ********************************************************************/
  124.  
  125. long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
  126. {
  127.     static char szSection[] = "Options";
  128.     static char szEntry[] = "MinimizeTimeSlice";
  129.     static char szIniFile[] = "CDPLAYER.INI";
  130.  
  131.     char szBuffer[8];
  132.     static HBRUSH hBrush;
  133.     static HMENU hSysMenu;
  134.     static HANDLE hInstance;
  135.     static FARPROC lpfnAboutDlgProc;
  136.     static int iThumbPos;
  137.     POINT point;
  138.     RECT rect;
  139.     WORD i;
  140.  
  141.     switch (message) {
  142.  
  143.     case WM_CREATE:
  144.         // Create a background brush for WM_CTLCOLOR messages
  145.         hBrush = CreateSolidBrush (GetSysColor (COLOR_WINDOW));
  146.  
  147.         // Obtain a far pointer to the About dialog box function
  148.         hInstance = ((LPCREATESTRUCT) lParam)->hInstance;
  149.         lpfnAboutDlgProc = MakeProcInstance (AboutDlgProc, hInstance);
  150.  
  151.         // Center the window on the screen
  152.         GetWindowRect (hwnd, &rect);
  153.         SetWindowPos (hwnd, 0,
  154.             (GetSystemMetrics (SM_CXSCREEN) - (rect.right - rect.left)) / 2,
  155.             (GetSystemMetrics (SM_CYSCREEN) - (rect.bottom - rect.top)) / 2,
  156.             0, 0, SWP_NOSIZE | SWP_NOZORDER);
  157.  
  158.         // Add new options to the system menu
  159.         hSysMenu = GetSystemMenu (hwnd, FALSE);
  160.         AppendMenu (hSysMenu, MF_SEPARATOR, 0, NULL);
  161.         AppendMenu (hSysMenu, MF_STRING, IDM_TOGGLEMTS,
  162.             "Minimize &Time Slice");
  163.         AppendMenu (hSysMenu, MF_STRING, IDM_ABOUT,
  164.             "&About CDPlayer...");
  165.  
  166.         // Read option information from the INI file
  167.         if (GetPrivateProfileInt (szSection, szEntry, 0, szIniFile) == 1)
  168.             CheckMenuItem (hSysMenu, IDM_TOGGLEMTS, MF_CHECKED);
  169.         return 0;
  170.  
  171.     case WM_COMMAND:
  172.         // Begin play if a track button was pressed
  173.         if ((wParam >= 101) && (wParam <= 120)) {
  174.             BeginPlay ((BYTE) (wParam + (wPageNumber * 20) - 100), hwnd);
  175.             return 0;
  176.         }
  177.  
  178.         switch (wParam) {
  179.  
  180.         case IDD_PLAY:
  181.             // Begin play at track 1
  182.             if (bMediaPresent)
  183.                 BeginPlay (1, hwnd);
  184.             return 0;
  185.  
  186.         case IDD_STOP:
  187.             // Stop play
  188.             if (bMediaPresent) {
  189.                 bPaused = FALSE;
  190.                 wCurrentTrack = 0;
  191.                 mciSendCommand (MCIOpen.wDeviceID, MCI_STOP, NULL, NULL);
  192.             }
  193.             return 0;
  194.  
  195.         case IDD_PAUSE:
  196.             if (!bMediaPresent)
  197.                 return 0;
  198.  
  199.             // Resume play
  200.             if (bPaused)
  201.                 ResumePlay (dwPosition, hwnd);
  202.  
  203.             // Pause play
  204.             else {
  205.                 MCIStatus.dwItem = MCI_STATUS_MODE;
  206.                 mciSendCommand (MCIOpen.wDeviceID, MCI_STATUS,
  207.                     MCI_STATUS_ITEM | MCI_WAIT, (DWORD) (LPSTR) &MCIStatus);
  208.                 if (MCIStatus.dwReturn == MCI_MODE_PLAY) {
  209.                     bPaused = TRUE;
  210.                     MCIStatus.dwItem = MCI_STATUS_POSITION;
  211.                     mciSendCommand (MCIOpen.wDeviceID, MCI_STATUS,
  212.                         MCI_STATUS_ITEM | MCI_WAIT,
  213.                         (DWORD) (LPSTR) &MCIStatus);
  214.                     dwPosition = MCIStatus.dwReturn;
  215.                     mciSendCommand (MCIOpen.wDeviceID, MCI_STOP, NULL, NULL);
  216.                 }
  217.             }
  218.             return 0;
  219.  
  220.         case IDD_EJECT:
  221.             // Eject the disk
  222.             if (bMediaPresent)
  223.                 mciSendCommand (MCIOpen.wDeviceID, MCI_SET,
  224.                     MCI_SET_DOOR_OPEN, NULL);
  225.             return 0;
  226.  
  227.         case IDD_PREVTRACK:
  228.             // Go to the previous track
  229.             if (bMediaPresent)
  230.                 BeginPlay ((BYTE) ((wCurrentTrack <= 1) ?
  231.                     wNumberOfTracks : wCurrentTrack - 1), hwnd);
  232.             return 0;
  233.  
  234.         case IDD_NEXTTRACK:
  235.             // Go to the next track
  236.             if (bMediaPresent)
  237.                 BeginPlay ((BYTE) ((wCurrentTrack == wNumberOfTracks) ?
  238.                     1 : wCurrentTrack + 1), hwnd);
  239.             return 0;
  240.  
  241.         case IDD_20PLUS:
  242.             if (wNumberOfTracks > 20) {
  243.                 wPageNumber++;
  244.                 if (wPageNumber == wPageCount)
  245.                     wPageNumber = 0;
  246.                 UpdateTrackButtons (hwnd);
  247.             }
  248.             return 0;
  249.  
  250.         case IDCANCEL:
  251.             // Terminate the program
  252.             DestroyWindow (hwnd);
  253.             return 0;
  254.         }
  255.  
  256.     case WM_TIMER:
  257.         // Ignore this message if the window is minimized and
  258.         // Minimize Time Slice is checked in the system menu
  259.         if ((IsIconic (hwnd)) && (GetMenuState (hSysMenu,
  260.             IDM_TOGGLEMTS, MF_BYCOMMAND) & MF_CHECKED))
  261.             return 0;
  262.  
  263.         // Find out if there is a CD in the drive
  264.         MCIStatus.dwItem = MCI_STATUS_MEDIA_PRESENT;
  265.         mciSendCommand (MCIOpen.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM |
  266.             MCI_WAIT, (DWORD) (LPSTR) &MCIStatus);
  267.  
  268.         // Initialize the program if a CD was inserted
  269.         if ((!bMediaPresent) && (MCIStatus.dwReturn == TRUE)) {
  270.             bMediaPresent = TRUE;
  271.             bScrolling = FALSE;
  272.             bPaused = FALSE;
  273.             wCurrentTrack = 0;
  274.             wPageNumber = 0;
  275.  
  276.             // Get the number of tracks
  277.             MCIStatus.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
  278.             mciSendCommand (MCIOpen.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM |
  279.                 MCI_WAIT, (DWORD) (LPSTR) &MCIStatus);
  280.             wNumberOfTracks = (WORD) MCIStatus.dwReturn;
  281.             if (wNumberOfTracks > 99)
  282.                 wNumberOfTracks = 99;
  283.             wPageCount = ((wNumberOfTracks - 1) / 20) + 1;
  284.  
  285.             // Enable the track button display
  286.             if (wNumberOfTracks <= 20)
  287.                 for (i=1; i<=wNumberOfTracks; i++)
  288.                     EnableWindow (GetDlgItem (hwnd, i+100), TRUE);
  289.             else {
  290.                 for (i=1; i<=20; i++)
  291.                     EnableWindow (GetDlgItem (hwnd, i+100), TRUE);
  292.                 EnableWindow (GetDlgItem (hwnd, IDD_20PLUS), TRUE);
  293.             }
  294.  
  295.             // Get the length of each track
  296.             for (i=1; i<=wNumberOfTracks; i++) {
  297.                 MCIStatus.dwItem = MCI_STATUS_LENGTH;
  298.                 MCIStatus.dwTrack = (DWORD) i;
  299.                 mciSendCommand (MCIOpen.wDeviceID, MCI_STATUS,
  300.                     MCI_TRACK | MCI_STATUS_ITEM | MCI_WAIT,
  301.                     (DWORD) (LPSTR) &MCIStatus);
  302.                 awTrackLength[i-1] =
  303.                     ((WORD) MCI_MSF_MINUTE (MCIStatus.dwReturn) * 60) +
  304.                     (WORD) MCI_MSF_SECOND (MCIStatus.dwReturn);
  305.             }
  306.         }
  307.  
  308.         // Reset the status indicators if a CD was removed
  309.         else if ((bMediaPresent) && (MCIStatus.dwReturn == FALSE)) {
  310.             bMediaPresent = FALSE;
  311.             for (i=1; i<=20; i++)
  312.                 EnableWindow (GetDlgItem (hwnd, i+100), FALSE);
  313.             EnableWindow (GetDlgItem (hwnd, IDD_20PLUS), FALSE);
  314.             if (wPageNumber != 0)
  315.                 for (i=1; i<=20; i++)
  316.                     SetDlgItemText (hwnd, i+100, _itoa (i, szBuffer, 10));
  317.             SetWindowText (hwndTrack, "");
  318.             SetWindowText (hwndLength, "");
  319.             SetWindowText (hwndTime, "");
  320.             SetScrollPos (hwndScrollBar, SB_CTL, 0, TRUE);
  321.             InvalidateRect (hwndScrollBar, NULL, TRUE);
  322.             return 0;
  323.         }
  324.  
  325.         // Find out if a CD is playing
  326.         if (bMediaPresent) {
  327.             MCIStatus.dwItem = MCI_STATUS_MODE;
  328.             mciSendCommand (MCIOpen.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM |
  329.                 MCI_WAIT, (DWORD) (LPSTR) &MCIStatus);
  330.  
  331.             // Update the status indicators if a CD is playing
  332.             if (MCIStatus.dwReturn == MCI_MODE_PLAY) {
  333.                 MCIStatus.dwItem = MCI_STATUS_POSITION;
  334.                 mciSendCommand (MCIOpen.wDeviceID, MCI_STATUS,
  335.                     MCI_STATUS_ITEM | MCI_WAIT,
  336.                     (DWORD) (LPSTR) &MCIStatus);
  337.  
  338.                 // Update track and length indicators if this is a new track
  339.                 if (wCurrentTrack !=
  340.                         (WORD) MCI_TMSF_TRACK (MCIStatus.dwReturn)) {
  341.                     wCurrentTrack =
  342.                         (WORD) MCI_TMSF_TRACK (MCIStatus.dwReturn);
  343.                     SetWindowText (hwndTrack,
  344.                         _itoa ((int) wCurrentTrack, szBuffer, 10));
  345.                     SetWindowText (hwndLength,
  346.                         IntToMinSec ((int) awTrackLength[wCurrentTrack-1],
  347.                         szBuffer));
  348.                     SetScrollRange (hwndScrollBar, SB_CTL, 0,
  349.                         (int) awTrackLength[wCurrentTrack-1], FALSE);
  350.                 }
  351.  
  352.                 // Update the time indicator and the scroll bar
  353.                 if (!bScrolling) {
  354.                     SetWindowText (hwndTime,
  355.                         TMSFtoMinSec (MCIStatus.dwReturn, szBuffer));
  356.                     SetScrollPos (hwndScrollBar, SB_CTL,
  357.                         (((int) MCI_TMSF_MINUTE
  358.                             (MCIStatus.dwReturn)) * 60) +
  359.                         (int) MCI_TMSF_SECOND
  360.                             (MCIStatus.dwReturn), TRUE);
  361.                 }
  362.             }
  363.  
  364.             // Reset the status indicators if there is no CD playing
  365.             else if (!bPaused) {
  366.                 GetWindowText (hwndTrack, szBuffer, sizeof (szBuffer));
  367.                 if (szBuffer[0] != 0x00) {
  368.                     SetWindowText (hwndTrack, "");
  369.                     SetWindowText (hwndLength, "");
  370.                     SetWindowText (hwndTime, "");
  371.                     SetScrollPos (hwndScrollBar, SB_CTL, 0, TRUE);
  372.                     InvalidateRect (hwndScrollBar, NULL, TRUE);
  373.                     wCurrentTrack = 0;
  374.                 }
  375.             }
  376.         }
  377.         return 0;
  378.  
  379.     case WM_HSCROLL:
  380.         // Ignore the scroll bar if there is not a CD playing
  381.         MCIStatus.dwItem = MCI_STATUS_MODE;
  382.         mciSendCommand (MCIOpen.wDeviceID, MCI_STATUS,
  383.             MCI_STATUS_ITEM | MCI_WAIT, (DWORD) (LPSTR) &MCIStatus);
  384.         if (MCIStatus.dwReturn != MCI_MODE_PLAY)
  385.             break;
  386.  
  387.         switch (wParam) {
  388.  
  389.         case SB_LINEUP:
  390.         case SB_PAGEUP:
  391.         case SB_LINEDOWN:
  392.         case SB_PAGEDOWN:
  393.             // Move the scroll bar thumb
  394.             if (!bScrolling) {
  395.                 bScrolling = TRUE;
  396.                 MCIStatus.dwItem = MCI_STATUS_POSITION;
  397.                 mciSendCommand (MCIOpen.wDeviceID, MCI_STATUS,
  398.                     MCI_STATUS_ITEM | MCI_WAIT, (DWORD) (LPSTR) &MCIStatus);
  399.                 iThumbPos = (int) ((MCI_TMSF_MINUTE (MCIStatus.dwReturn) * 60)
  400.                     + MCI_TMSF_SECOND (MCIStatus.dwReturn));
  401.             }
  402.  
  403.             if (wParam == SB_LINEUP)
  404.                 iThumbPos--;
  405.             else if (wParam == SB_PAGEUP)
  406.                 iThumbPos -= 8;
  407.             else if (wParam == SB_LINEDOWN)
  408.                 iThumbPos++;
  409.             else // wParam == SB_PAGEDOWN
  410.                 iThumbPos += 8;
  411.  
  412.             if (iThumbPos < 0)
  413.                 iThumbPos = 0;
  414.             else if (iThumbPos > (int) awTrackLength[wCurrentTrack-1])
  415.                 iThumbPos = (int) awTrackLength[wCurrentTrack-1];
  416.             else {
  417.                 IntToMinSec (iThumbPos, szBuffer);
  418.                 SetWindowText (hwndTime, szBuffer);
  419.                 SetScrollPos (hwndScrollBar, SB_CTL, iThumbPos, TRUE);
  420.             }
  421.             return 0;
  422.  
  423.         case SB_ENDSCROLL:
  424.             // Go to a new location in the current track
  425.             if (bScrolling) {
  426.                 bScrolling = FALSE;
  427.                 MCIPlay.dwFrom = MCI_MAKE_TMSF ((BYTE) wCurrentTrack,
  428.                     (BYTE) (iThumbPos / 60),
  429.                     (BYTE) (iThumbPos - ((iThumbPos / 60) * 60)), 30);
  430.                 mciSendCommand (MCIOpen.wDeviceID, MCI_PLAY,
  431.                     MCI_FROM | MCI_NOTIFY, (DWORD) (LPSTR) &MCIPlay);
  432.                 return 0;
  433.             }
  434.             else
  435.                 break;
  436.  
  437.         case SB_THUMBPOSITION:
  438.             // Go to the location specified by the scroll bar thumb
  439.             iThumbPos = (int) LOWORD (lParam);
  440.             SetWindowText (hwndTime, IntToMinSec (iThumbPos, szBuffer));
  441.             SetScrollPos (hwndScrollBar, SB_CTL, iThumbPos, TRUE);
  442.             InvalidateRect (hwndScrollBar, NULL, TRUE);
  443.             MCIPlay.dwFrom = MCI_MAKE_TMSF ((BYTE) wCurrentTrack,
  444.                 (BYTE) (iThumbPos / 60),
  445.                 (BYTE) (iThumbPos - ((iThumbPos / 60) * 60)), 30);
  446.             mciSendCommand (MCIOpen.wDeviceID, MCI_PLAY,
  447.                 MCI_FROM | MCI_NOTIFY, (DWORD) (LPSTR) &MCIPlay);
  448.             return 0;
  449.         }
  450.         break;
  451.  
  452.     case MM_MCINOTIFY:
  453.         // Restart the CD if "Continuous Play" is checked
  454.         if (wParam == MCI_NOTIFY_SUCCESSFUL) {
  455.             if (IsDlgButtonChecked (hwnd, IDD_CHECKBOX) == 1)
  456.                 BeginPlay ((BYTE) 1, hwnd);
  457.             else
  458.                 wCurrentTrack = 0;
  459.             return 0;
  460.         }
  461.         break;
  462.  
  463.     case WM_CTLCOLOR:
  464.         // Paint status indicators red
  465.         if ((LOWORD (lParam) == hwndTrack) ||
  466.             (LOWORD (lParam) == hwndLength) ||
  467.             (LOWORD (lParam) == hwndTime)) {
  468.             SetBkColor (wParam, GetSysColor (COLOR_WINDOW));
  469.             SetTextColor (wParam, RGB (255, 0, 0));
  470.             UnrealizeObject (hBrush);
  471.             point.x = point.y = 0;
  472.             ClientToScreen (hwnd, &point);
  473.             SetBrushOrg (wParam, point.x, point.y);
  474.             return ((DWORD) hBrush);
  475.         }
  476.         break;
  477.  
  478.     case WM_SYSCOLORCHANGE:
  479.         // Change the background brush when a system color changes
  480.         DeleteObject (hBrush);
  481.         hBrush = CreateSolidBrush (GetSysColor (COLOR_WINDOW));
  482.         return 0;
  483.  
  484.     case WM_SYSCOMMAND:
  485.         // Execute commands in system menu
  486.         if (wParam == IDM_ABOUT) {
  487.             DialogBox (hInstance, "AboutBox", hwnd, lpfnAboutDlgProc);
  488.             return 0;
  489.         }
  490.         else if (wParam == IDM_TOGGLEMTS) {
  491.             if (GetMenuState (hSysMenu, IDM_TOGGLEMTS, MF_BYCOMMAND) &
  492.                 MF_CHECKED) {
  493.                 CheckMenuItem (hSysMenu, IDM_TOGGLEMTS, MF_UNCHECKED);
  494.                 WritePrivateProfileString (szSection, szEntry, "0",
  495.                     szIniFile);
  496.             }
  497.             else {
  498.                 CheckMenuItem (hSysMenu, IDM_TOGGLEMTS, MF_CHECKED);
  499.                 WritePrivateProfileString (szSection, szEntry, "1",
  500.                     szIniFile);
  501.             }
  502.             return 0;
  503.         }
  504.         else
  505.             break;
  506.  
  507.     case WM_DESTROY:
  508.         // Close devices, deallocate resources, and terminate
  509.         mciSendCommand (MCIOpen.wDeviceID, MCI_CLOSE, NULL, NULL);
  510.         KillTimer (hwnd, wTimerID);
  511.         DeleteObject (hBrush);
  512.         PostQuitMessage (0);
  513.         return 0;
  514.     }
  515.     return DefDlgProc (hwnd, message, wParam, lParam);
  516. }
  517.  
  518. /********************************************************************
  519.   AboutDlgProc processes messages to the About dialog box.
  520.  ********************************************************************/
  521.  
  522. BOOL FAR PASCAL AboutDlgProc (HWND hwnd, WORD message,
  523.                               WORD wParam, LONG lParam)
  524. {
  525.     PAINTSTRUCT ps;
  526.     HPEN hPen;
  527.     HDC hdc;
  528.  
  529.     switch (message) {
  530.  
  531.     case WM_INITDIALOG:
  532.         return TRUE;
  533.  
  534.     case WM_PAINT:
  535.         hdc = BeginPaint (hwnd, &ps);
  536.         hPen = CreatePen (PS_SOLID, 16, RGB (255, 0, 255));
  537.         SelectObject (hdc, hPen);
  538.         MoveTo (hdc, 32, 0);
  539.         LineTo (hdc, 0, 32);
  540.         LineTo (hdc, 64, 0);
  541.         LineTo (hdc, 0, 64);
  542.         LineTo (hdc, 80, 0);
  543.         LineTo (hdc, 16, 80);
  544.         LineTo (hdc, 52, 56);
  545.         DeleteObject (hPen);
  546.         EndPaint (hwnd, &ps);
  547.         return TRUE;
  548.  
  549.     case WM_COMMAND:
  550.         switch (wParam) {
  551.             case IDOK:
  552.             case IDCANCEL:
  553.                 EndDialog (hwnd, 0);
  554.                 return TRUE;
  555.         }
  556.         break;
  557.     }
  558.     return FALSE;
  559. }
  560.  
  561. /********************************************************************
  562.   BeginPlay starts play at the beginning of the specified track.
  563.  ********************************************************************/
  564.  
  565. void BeginPlay (BYTE byTrackNumber, HWND hwnd)
  566. {
  567.     char *szBuffer[8];
  568.  
  569.     bPaused = FALSE;
  570.     wCurrentTrack = (WORD) byTrackNumber;
  571.  
  572.     // Update the track, length, and time indicators
  573.     SetWindowText (hwndTrack, _itoa ((int) byTrackNumber,
  574.         (char *) szBuffer, 10));
  575.     SetWindowText (hwndLength,
  576.         IntToMinSec ((int) awTrackLength[byTrackNumber-1],
  577.             (char *) szBuffer));
  578.     SetWindowText (hwndTime, "0:00");
  579.  
  580.     // Initialize the scroll bar for this track
  581.     SetScrollRange (hwndScrollBar, SB_CTL, 0,
  582.         (int) awTrackLength[byTrackNumber-1], FALSE);
  583.     SetScrollPos (hwndScrollBar, SB_CTL, 0, TRUE);
  584.     InvalidateRect (hwndScrollBar, NULL, TRUE);
  585.  
  586.     // Send an MCI_PLAY command to the CD audio driver
  587.     MCIPlay.dwCallback = (DWORD) hwnd;
  588.     MCIPlay.dwFrom = MCI_MAKE_TMSF (byTrackNumber, 0, 0, 1);
  589.     mciSendCommand (MCIOpen.wDeviceID, MCI_PLAY, MCI_FROM | MCI_NOTIFY,
  590.         (DWORD) (LPSTR) &MCIPlay);
  591. }
  592.  
  593. /********************************************************************
  594.   ResumePlay resumes play after the Pause button is pressed.
  595.  ********************************************************************/
  596.  
  597. void ResumePlay (DWORD dwPosition, HWND hwnd)
  598. {
  599.     bPaused = FALSE;
  600.     MCIPlay.dwCallback = (DWORD) hwnd;
  601.     MCIPlay.dwFrom = dwPosition;
  602.     mciSendCommand (MCIOpen.wDeviceID, MCI_PLAY, MCI_FROM | MCI_NOTIFY,
  603.         (DWORD) (LPSTR) &MCIPlay);
  604. }
  605.  
  606. /********************************************************************
  607.   UpdateTrackButtons changes the numbers on the track button display.
  608.  ********************************************************************/
  609.  
  610. void UpdateTrackButtons (HWND hwnd)
  611. {
  612.     int i, j;
  613.     char szBuffer[3];
  614.  
  615.     j = (int) wPageNumber * 20;
  616.     for (i=1; i<=20; i++)
  617.         SetDlgItemText (hwnd, i+100, _itoa (i+j, szBuffer, 10));
  618.     if (wPageNumber == (wPageCount-1)) {
  619.         j = (int) (wNumberOfTracks % 20) + 1;
  620.         for (i=j; i<=20; i++)
  621.             EnableWindow (GetDlgItem (hwnd, i+100), FALSE);
  622.     }
  623.     else
  624.         for (i=1; i<=20; i++)
  625.             EnableWindow (GetDlgItem (hwnd, i+100), TRUE);
  626. }
  627.  
  628. /********************************************************************
  629.   IntToMinSec converts a time value in seconds to a string in
  630.   minutes:seconds format.
  631.  ********************************************************************/
  632.  
  633. char *IntToMinSec (int iTime, char *szResult)
  634. {
  635.     int iSeconds;
  636.     char szBuffer[3];
  637.  
  638.     _itoa (iTime / 60, szResult, 10);
  639.     strcat (szResult, ":");
  640.     if ((iSeconds = (iTime - ((iTime / 60) * 60))) < 10)
  641.         strcat (szResult, "0");
  642.     strcat (szResult, _itoa (iSeconds, szBuffer, 10));
  643.     return szResult;
  644. }
  645.  
  646. /********************************************************************
  647.   TMSFtoMinSec converts a time value in TMSF format to a string
  648.   in minutes:seconds format.
  649.  ********************************************************************/
  650.  
  651. char *TMSFtoMinSec (DWORD dwTime, char *szResult)
  652. {
  653.     int iSeconds;
  654.     char szBuffer[3];
  655.  
  656.     _itoa ((int) MCI_TMSF_MINUTE (dwTime), szResult, 10);
  657.     strcat (szResult, ":");
  658.     if ((iSeconds = (int) MCI_TMSF_SECOND (dwTime)) < 10)
  659.         strcat (szResult, "0");
  660.     strcat (szResult, _itoa (iSeconds, szBuffer, 10));    
  661.     return szResult;
  662. }
  663.