home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / pcmagazi / 1992 / 17 / midplay / midpla.c < prev    next >
C/C++ Source or Header  |  1992-06-07  |  24KB  |  628 lines

  1. /*---------------------------------------
  2.    MIDPLA.C -- MIDI File Player
  3.                (c) Charles Petzold, 1992
  4.   ---------------------------------------*/
  5.  
  6. #include <windows.h>
  7. #include <mmsystem.h>
  8. #include <commdlg.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include "midpla.h"
  12.  
  13. #define ID_TIMER            1
  14. #define MIN_TEMPO_BPS      20
  15. #define MAX_TEMPO_BPS     480
  16. #define MIN_TEMPO_SMPTE     5
  17. #define MAX_TEMPO_SMPTE   120
  18.  
  19. #define min(a,b) (((a) < (b)) ? (a) : (b))
  20. #define max(a,b) (((a) > (b)) ? (a) : (b))
  21.  
  22. #define EnableDlgWindow(hwnd, wID, bEnable) \
  23.                (EnableWindow (GetDlgItem ((hwnd), (wID)), (bEnable)))
  24.  
  25. BOOL FAR PASCAL _export DlgProc (HWND, UINT, UINT, LONG) ;
  26.  
  27. static char szAppName [] = "MidPla" ;
  28.  
  29. int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
  30.                     LPSTR lpszCmdLine, int nCmdShow)
  31.      {
  32.      FARPROC lpDlgProc ;
  33.  
  34.      lpDlgProc = MakeProcInstance ((FARPROC) DlgProc, hInstance) ;
  35.      DialogBox (hInstance, szAppName, NULL, lpDlgProc) ;
  36.      FreeProcInstance (lpDlgProc) ;
  37.  
  38.      return 0 ;
  39.      }
  40.  
  41.      /*----------------------------------------------
  42.         mciSendCommand with an error message display
  43.        ----------------------------------------------*/
  44.  
  45. BOOL MciSend (UINT wDeviceID, UINT wMessage, DWORD dwParam1, DWORD dwParam2)
  46.      {
  47.      static char szBuffer [256] ;
  48.      DWORD       dwError ;
  49.      HWND        hwnd ;
  50.  
  51.      dwError = mciSendCommand (wDeviceID, wMessage, dwParam1, dwParam2) ;
  52.  
  53.      if (dwError)
  54.           {
  55.           hwnd = (HWND) ((LPMCI_GENERIC_PARMS) dwParam2)->dwCallback ;
  56.  
  57.           if (!mciGetErrorString (dwError, szBuffer, sizeof (szBuffer)))
  58.                strcpy (szBuffer, "Error not known") ;
  59.  
  60.           MessageBox (hwnd, szBuffer, szAppName, MB_OK | MB_ICONEXCLAMATION) ;
  61.           }
  62.  
  63.      return dwError == 0 ;
  64.      }
  65.  
  66.      /*---------------
  67.         Open function
  68.        ---------------*/
  69.  
  70. UINT MciMidiOpen (HWND hwnd, char * szFileName, DWORD dwFlags)
  71.      {
  72.      BOOL           bSuccess ;
  73.      MCI_OPEN_PARMS open ;
  74.  
  75.      open.dwCallback       = (DWORD) hwnd ;
  76.      open.lpstrDeviceType  = "sequencer" ;
  77.      open.lpstrElementName = szFileName ;
  78.      open.lpstrAlias       = NULL ;
  79.  
  80.      bSuccess = MciSend (0, MCI_OPEN,
  81.                          MCI_OPEN_ELEMENT | MCI_OPEN_TYPE | dwFlags,
  82.                          (DWORD) (LPMCI_OPEN_PARMS) &open) ;
  83.  
  84.      return bSuccess ? open.wDeviceID : 0 ;
  85.      }
  86.  
  87.      /*-----------------------------------------------------------------
  88.         Functions and macros for operations using the generic structure
  89.        -----------------------------------------------------------------*/
  90.  
  91. BOOL MciMidiGeneric (UINT wDeviceID, HWND hwnd, UINT wMessage, DWORD dwFlags)
  92.      {
  93.      MCI_GENERIC_PARMS generic ;
  94.  
  95.      generic.dwCallback = (DWORD) hwnd ;
  96.  
  97.      return MciSend (wDeviceID, wMessage, dwFlags,
  98.                      (DWORD) (LPMCI_GENERIC_PARMS) &generic) ;
  99.      }
  100.  
  101. #define MciMidiClose(wDeviceID, hwnd, dwFlags) \
  102.                (MciMidiGeneric ((wDeviceID), (hwnd), MCI_CLOSE,  (dwFlags)))
  103.  
  104. #define MciMidiPause(wDeviceID, hwnd, dwFlags) \
  105.                (MciMidiGeneric ((wDeviceID), (hwnd), MCI_PAUSE,  (dwFlags)))
  106.  
  107. #define MciMidiStop(wDeviceID, hwnd, dwFlags) \
  108.                (MciMidiGeneric ((wDeviceID), (hwnd), MCI_STOP,   (dwFlags)))
  109.  
  110.      /*------------------------------
  111.         MIDI Play and Seek functions
  112.        ------------------------------*/
  113.  
  114. BOOL MciMidiPlay (UINT wDeviceID, HWND hwnd, DWORD dwFlags, DWORD dwFrom,
  115.                                                             DWORD dwTo)
  116.      {
  117.      MCI_PLAY_PARMS play ;
  118.  
  119.      play.dwCallback = (DWORD) hwnd ;
  120.      play.dwFrom     = dwFrom ;
  121.      play.dwTo       = dwTo ;
  122.  
  123.      return MciSend (wDeviceID, MCI_PLAY, dwFlags,
  124.                      (DWORD) (LPMCI_PLAY_PARMS) &play) ;
  125.      }
  126.  
  127. BOOL MciMidiSeek (UINT wDeviceID, HWND hwnd, DWORD dwFlags, DWORD dwTo)
  128.      {
  129.      MCI_SEEK_PARMS seek ;
  130.  
  131.      seek.dwCallback = (DWORD) hwnd ;
  132.      seek.dwTo       = dwTo ;
  133.  
  134.      return MciSend (wDeviceID, MCI_SEEK, dwFlags,
  135.                      (DWORD) (LPMCI_SEEK_PARMS) &seek) ;
  136.      }
  137.  
  138.      /*---------------------------------------------------
  139.         Functions and macros to obtain status information
  140.        ---------------------------------------------------*/
  141.  
  142. DWORD MciMidiGet (UINT wDeviceID, HWND hwnd, DWORD dwFlags, DWORD dwItem)
  143.      {
  144.      BOOL             bSuccess ;
  145.      MCI_STATUS_PARMS status ;
  146.  
  147.      status.dwCallback = (HWND) hwnd ;
  148.      status.dwItem     = dwItem ;
  149.  
  150.      bSuccess = MciSend (wDeviceID, MCI_STATUS, dwFlags | MCI_STATUS_ITEM,
  151.                          (DWORD) (LPMCI_STATUS_PARMS) & status) ;
  152.  
  153.      return bSuccess ? status.dwReturn : 0 ;
  154.      }
  155.  
  156. #define MciMidiGetMode(wDeviceID, hwnd, dwFlags) \
  157.           MciMidiGet ((wDeviceID), (hwnd), (dwFlags), MCI_STATUS_MODE)
  158.  
  159. #define MciMidiGetDivType(wDeviceID, hwnd, dwFlags) \
  160.           MciMidiGet ((wDeviceID), (hwnd), (dwFlags), MCI_SEQ_STATUS_DIVTYPE)
  161.  
  162. #define MciMidiGetFormat(wDeviceID, hwnd, dwFlags) \
  163.           MciMidiGet ((wDeviceID), (hwnd), (dwFlags), MCI_STATUS_TIME_FORMAT)
  164.  
  165. #define MciMidiGetLength(wDeviceID, hwnd, dwFlags) \
  166.           MciMidiGet ((wDeviceID), (hwnd), (dwFlags), MCI_STATUS_LENGTH)
  167.  
  168. #define MciMidiGetPosition(wDeviceID, hwnd, dwFlags) \
  169.           MciMidiGet ((wDeviceID), (hwnd), (dwFlags), MCI_STATUS_POSITION)
  170.  
  171. #define MciMidiGetTempo(wDeviceID, hwnd, dwFlags) \
  172.           MciMidiGet ((wDeviceID), (hwnd), (dwFlags), MCI_SEQ_STATUS_TEMPO)
  173.  
  174.      /*----------------------------------------
  175.         Functions to set time format and tempo
  176.        ----------------------------------------*/
  177.  
  178. BOOL MciMidiSetFormat (UINT wDeviceID, HWND hwnd, DWORD dwFlags, DWORD dwFormat)
  179.      {
  180.      MCI_SEQ_SET_PARMS set ;
  181.  
  182.      set.dwCallback   = (HWND) hwnd ;
  183.      set.dwTimeFormat = dwFormat ;
  184.  
  185.      return MciSend (wDeviceID, MCI_SET, dwFlags | MCI_SET_TIME_FORMAT,
  186.                      (DWORD) (LPMCI_SEQ_SET_PARMS) & set) ;
  187.      }
  188.  
  189. BOOL MciMidiSetTempo (UINT wDeviceID, HWND hwnd, DWORD dwFlags, DWORD dwTempo)
  190.      {
  191.      MCI_SEQ_SET_PARMS set ;
  192.  
  193.      set.dwCallback = (HWND) hwnd ;
  194.      set.dwTempo    = dwTempo ;
  195.  
  196.      return MciSend (wDeviceID, MCI_SET, dwFlags | MCI_SEQ_SET_TEMPO,
  197.                      (DWORD) (LPMCI_SEQ_SET_PARMS) & set) ;
  198.      }
  199.  
  200.      /*---------------------------------
  201.         Update the labels on the window
  202.        ---------------------------------*/
  203.  
  204. void UpdateLabels (UINT wDeviceID, HWND hwnd)
  205.      {
  206.      char  szPosBeg [16], szPosEnd [16], szPosition [40], szTempo [40] ;
  207.      DWORD dwDivType, dwFormat, dwLength, dwPosition, dwTempo ;
  208.      UINT  iPosition, iTempo ;
  209.  
  210.           // Get information
  211.  
  212.      dwDivType  = MciMidiGetDivType  (wDeviceID, hwnd, MCI_WAIT) ;
  213.      dwFormat   = MciMidiGetFormat   (wDeviceID, hwnd, MCI_WAIT) ;
  214.      dwLength   = MciMidiGetLength   (wDeviceID, hwnd, MCI_WAIT) ;
  215.      dwPosition = MciMidiGetPosition (wDeviceID, hwnd, MCI_WAIT) ;
  216.      dwTempo    = MciMidiGetTempo    (wDeviceID, hwnd, MCI_WAIT) ;
  217.  
  218.           // Format the length and position values, calculate position scroll
  219.  
  220.      if (dwFormat == MCI_FORMAT_MILLISECONDS ||
  221.          dwFormat == MCI_SEQ_FORMAT_SONGPTR)
  222.           {
  223.           wsprintf (szPosBeg,   "0") ;
  224.           wsprintf (szPosEnd,   "%ld", dwLength) ;
  225.           wsprintf (szPosition, "Position: %ld %s", dwPosition,
  226.                     (LPSTR) (dwFormat == MCI_FORMAT_MILLISECONDS ?
  227.                                    "milliseconds" : "sixteenth notes")) ;
  228.  
  229.           iPosition = (UINT) (1000 * dwPosition / dwLength) ;
  230.           }
  231.      else
  232.           {
  233.           wsprintf (szPosBeg, "00:00:00:00") ;
  234.  
  235.           wsprintf (szPosEnd, "%02d:%02d:%02d:%02d",
  236.                     LOBYTE (LOWORD (dwLength)), HIBYTE (LOWORD (dwLength)),
  237.                     LOBYTE (HIWORD (dwLength)), HIBYTE (HIWORD (dwLength))) ;
  238.  
  239.           wsprintf (szPosition, "Position: %02d:%02d:%02d:%02d",
  240.                   LOBYTE (LOWORD (dwPosition)), HIBYTE (LOWORD (dwPosition)),
  241.                   LOBYTE (HIWORD (dwPosition)), HIBYTE (HIWORD (dwPosition))) ;
  242.  
  243.                     // (switch to millisecond format for scroll bar calc)
  244.  
  245.           MciMidiSetFormat (wDeviceID, hwnd,
  246.                             MCI_WAIT, MCI_FORMAT_MILLISECONDS) ;
  247.  
  248.           iPosition = (UINT) (1000 *
  249.                               MciMidiGetPosition (wDeviceID, hwnd, MCI_WAIT) /
  250.                               MciMidiGetLength   (wDeviceID, hwnd, MCI_WAIT)) ;
  251.  
  252.           MciMidiSetFormat (wDeviceID, hwnd, MCI_WAIT, dwFormat) ;
  253.           }
  254.  
  255.           // Format the tempo, calculate tempo scroll
  256.  
  257.      if (dwDivType == MCI_SEQ_DIV_PPQN)
  258.           {
  259.           wsprintf (szTempo,  "Tempo: %ld quarter notes per minute", dwTempo) ;
  260.  
  261.           iTempo = (UINT) (max (MIN_TEMPO_BPS, min (dwTempo, MAX_TEMPO_BPS))) ;
  262.           }
  263.      else
  264.           {
  265.           wsprintf (szTempo, "Tempo: %ld frames per second", dwTempo) ;
  266.  
  267.           iTempo = (UINT) (max (MIN_TEMPO_SMPTE,
  268.                            min (dwTempo, MAX_TEMPO_SMPTE))) ;
  269.           }
  270.  
  271.           // Set text and scroll bar positions
  272.  
  273.      SetDlgItemText (hwnd, ID_POSBEG,  szPosBeg) ;
  274.      SetDlgItemText (hwnd, ID_POSEND,  szPosEnd) ;
  275.      SetDlgItemText (hwnd, ID_POSTEXT, szPosition) ;
  276.      SetDlgItemText (hwnd, ID_TMPTEXT, szTempo) ;
  277.  
  278.      SetScrollPos (GetDlgItem (hwnd, ID_POSITION), SB_CTL, iPosition, TRUE) ;
  279.      SetScrollPos (GetDlgItem (hwnd, ID_TEMPO),    SB_CTL, iTempo,    TRUE) ;
  280.      }
  281.  
  282.      /*---------------------------
  283.         Main dialog box procedure
  284.        ---------------------------*/
  285.  
  286. BOOL FAR PASCAL _export DlgProc (HWND hwnd, UINT message, UINT wParam,
  287.                                                           LONG lParam)
  288.      {
  289.      static char         szFileName  [_MAX_PATH],
  290.                          szTitleName [_MAX_FNAME + _MAX_EXT] ;
  291.      static char *       szFilter[] = { "MIDI Sequencer (*.mid;*.rmi)",
  292.                                         "*.mid;*.rmi", "" } ;
  293.      static DWORD        dwFormats [] = { MCI_FORMAT_MILLISECONDS,
  294.                                           MCI_SEQ_FORMAT_SONGPTR,
  295.                                           MCI_FORMAT_SMPTE_24,
  296.                                           MCI_FORMAT_SMPTE_25,
  297.                                           MCI_FORMAT_SMPTE_30,
  298.                                           MCI_FORMAT_SMPTE_30DROP } ;
  299.      static OPENFILENAME ofn = { sizeof (OPENFILENAME) } ;
  300.      static UINT         wDeviceID ;
  301.      int                 i, iPosition, iTempo, iMin, iMax ;
  302.      DWORD               dwDivType, dwFormat, dwMode ;
  303.  
  304.      switch (message)
  305.           {
  306.           case WM_INITDIALOG:
  307.  
  308.                          // Initialize OPENFILENAME structure
  309.  
  310.                ofn.hwndOwner      = hwnd ;
  311.                ofn.lpstrFilter    = szFilter [0] ;
  312.                ofn.lpstrFile      = szFileName ;
  313.                ofn.nMaxFile       = sizeof (szFileName) ;
  314.                ofn.lpstrFileTitle = szTitleName ;
  315.                ofn.nMaxFileTitle  = sizeof (szTitleName) ;
  316.                ofn.Flags          = OFN_FILEMUSTEXIST |
  317.                                     OFN_PATHMUSTEXIST ;
  318.                ofn.lpstrDefExt    = "mid" ;
  319.  
  320.                          // Initialize position scroll bar
  321.  
  322.                SetScrollRange (GetDlgItem (hwnd, ID_POSITION),
  323.                                SB_CTL, 0, 1000, FALSE) ;
  324.  
  325.                SetScrollPos   (GetDlgItem (hwnd, ID_POSITION),
  326.                                SB_CTL, 0, FALSE) ;
  327.                return TRUE ;
  328.  
  329.           case WM_COMMAND:
  330.                switch (wParam)
  331.                     {
  332.                     case ID_OPEN:
  333.                                    // Display open-file dialog box
  334.  
  335.                          if (!GetOpenFileName (&ofn))
  336.                               return TRUE ;
  337.  
  338.                                    // Open the file
  339.  
  340.                          wDeviceID = MciMidiOpen (hwnd, szFileName, MCI_WAIT) ;
  341.  
  342.                          if (0 == wDeviceID)
  343.                               return TRUE ;
  344.  
  345.                                    // Possibly disable song pointer button
  346.  
  347.                          dwDivType = MciMidiGetDivType (wDeviceID, hwnd,
  348.                                                         MCI_WAIT) ;
  349.  
  350.                          EnableDlgWindow (hwnd, ID_SONGPTR,
  351.                                           dwDivType == MCI_SEQ_DIV_PPQN) ;
  352.  
  353.                                    // Set the tempo scroll bar range
  354.  
  355.                          if (dwDivType == MCI_SEQ_DIV_PPQN)
  356.                               {
  357.                               SetScrollRange (GetDlgItem (hwnd, ID_TEMPO),
  358.                                   SB_CTL, MIN_TEMPO_BPS, MAX_TEMPO_BPS, TRUE) ;
  359.  
  360.                               SetDlgItemInt (hwnd, ID_TMPMIN, MIN_TEMPO_BPS,
  361.                                              FALSE) ;
  362.  
  363.                               SetDlgItemInt (hwnd, ID_TMPMAX, MAX_TEMPO_BPS,
  364.                                              FALSE) ;
  365.                               }
  366.                          else
  367.                               {
  368.                               SetScrollRange (GetDlgItem (hwnd, ID_TEMPO),
  369.                                    SB_CTL, MIN_TEMPO_SMPTE, MAX_TEMPO_SMPTE,
  370.                                    TRUE) ;
  371.  
  372.                               SetDlgItemInt (hwnd, ID_TMPMIN, MIN_TEMPO_SMPTE,
  373.                                              FALSE) ;
  374.  
  375.                               SetDlgItemInt (hwnd, ID_TMPMAX, MAX_TEMPO_SMPTE,
  376.                                              FALSE) ;
  377.                               }
  378.  
  379.                                    // Find the default time format
  380.  
  381.                          dwFormat = MciMidiGetFormat (wDeviceID, hwnd,
  382.                                                       MCI_WAIT) ;
  383.  
  384.                          for (i = 0 ; i < 6 ; i++)
  385.                               if (dwFormat == dwFormats [i])
  386.                                    break ;
  387.  
  388.                          CheckRadioButton (hwnd, ID_MILLISECS,
  389.                                    ID_SMPTE30DF, ID_MILLISECS + i) ;
  390.  
  391.                                    // Display labels
  392.  
  393.                          iPosition = 0 ;
  394.                          UpdateLabels (wDeviceID, hwnd) ;
  395.  
  396.                                    // Ready to play
  397.  
  398.                          EnableDlgWindow (hwnd, ID_OPEN,  FALSE) ;
  399.                          EnableDlgWindow (hwnd, ID_PLAY,  TRUE)  ;
  400.                          EnableDlgWindow (hwnd, ID_CLOSE, TRUE)  ;
  401.                          SetFocus (GetDlgItem (hwnd, ID_PLAY)) ;
  402.  
  403.                          return TRUE ;
  404.  
  405.                     case ID_PLAY:
  406.                          if (MciMidiPlay (wDeviceID, hwnd, MCI_NOTIFY, 0, 0))
  407.                               {
  408.                               EnableDlgWindow (hwnd, ID_PLAY,  FALSE) ;
  409.                               EnableDlgWindow (hwnd, ID_PAUSE, TRUE)  ;
  410.                               EnableDlgWindow (hwnd, ID_STOP,  TRUE)  ;
  411.                               EnableDlgWindow (hwnd, ID_CLOSE, FALSE) ;
  412.  
  413.                               SetFocus (GetDlgItem (hwnd, ID_STOP)) ;
  414.  
  415.                               SetTimer (hwnd, 1, 250, NULL) ;
  416.                               }
  417.  
  418.                          return TRUE ;
  419.  
  420.                     case ID_PAUSE:
  421.                          if (MciMidiPause (wDeviceID, hwnd, MCI_WAIT))
  422.                               {
  423.                               EnableDlgWindow (hwnd, ID_PLAY,  TRUE)  ;
  424.                               EnableDlgWindow (hwnd, ID_PAUSE, FALSE) ;
  425.  
  426.                               SetFocus (GetDlgItem (hwnd, ID_PLAY)) ;
  427.  
  428.                               KillTimer (hwnd, 1) ;
  429.                               UpdateLabels (wDeviceID, hwnd) ;
  430.                               }
  431.  
  432.                          return TRUE ;
  433.  
  434.                     case ID_STOP:
  435.                          if (MciMidiStop (wDeviceID, hwnd, MCI_WAIT))
  436.                               {
  437.                               EnableDlgWindow (hwnd, ID_PLAY,  TRUE)  ;
  438.                               EnableDlgWindow (hwnd, ID_PAUSE, FALSE) ;
  439.                               EnableDlgWindow (hwnd, ID_STOP,  FALSE) ;
  440.                               EnableDlgWindow (hwnd, ID_CLOSE, TRUE)  ;
  441.  
  442.                               SetFocus (GetDlgItem (hwnd, ID_PLAY)) ;
  443.  
  444.                               KillTimer (hwnd, 1) ;
  445.                               UpdateLabels (wDeviceID, hwnd) ;
  446.                               }
  447.  
  448.                          return 0 ;
  449.  
  450.                     case ID_CLOSE:
  451.                          if (MciMidiClose (wDeviceID, hwnd, MCI_WAIT))
  452.                               {
  453.                               EnableDlgWindow (hwnd, ID_OPEN,  TRUE)  ;
  454.                               EnableDlgWindow (hwnd, ID_PLAY,  FALSE) ;
  455.                               EnableDlgWindow (hwnd, ID_CLOSE, FALSE) ;
  456.  
  457.                               SetFocus (GetDlgItem (hwnd, ID_OPEN)) ;
  458.  
  459.                               wDeviceID = 0 ;
  460.                               }
  461.  
  462.                          return TRUE ;
  463.  
  464.                     case ID_MILLISECS:
  465.                     case ID_SONGPTR:
  466.                     case ID_SMPTE24:
  467.                     case ID_SMPTE25:
  468.                     case ID_SMPTE30:
  469.                     case ID_SMPTE30DF:
  470.                          dwFormat = wParam - ID_MILLISECS ;
  471.  
  472.                          if (MciMidiSetFormat (wDeviceID, hwnd, MCI_WAIT,
  473.                                                dwFormats [dwFormat]))
  474.                               {
  475.                               CheckRadioButton (hwnd, ID_MILLISECS,
  476.                                         ID_SMPTE30DF, wParam) ;
  477.  
  478.                               UpdateLabels (wDeviceID, hwnd) ;
  479.                               }
  480.  
  481.                          return TRUE ;
  482.  
  483.                     default:
  484.                          break ;
  485.                     }
  486.  
  487.                return TRUE ;
  488.  
  489.           case WM_HSCROLL:
  490.                switch (GetDlgCtrlID (HIWORD (lParam)))
  491.                     {
  492.                     case ID_POSITION:
  493.                          iPosition = GetScrollPos (
  494.                                      GetDlgItem (hwnd, ID_POSITION), SB_CTL) ;
  495.  
  496.                          switch (wParam)
  497.                               {
  498.                               case SB_LEFT:       iPosition  =    0 ;  break ;
  499.                               case SB_LINELEFT:   iPosition -=   10 ;  break ;
  500.                               case SB_PAGELEFT:   iPosition -=  100 ;  break ;
  501.                               case SB_PAGERIGHT:  iPosition +=  100 ;  break ;
  502.                               case SB_LINERIGHT:  iPosition +=   10 ;  break ;
  503.                               case SB_RIGHT:      iPosition  = 1000 ;  break ;
  504.  
  505.                               case SB_THUMBPOSITION:
  506.                                    iPosition = LOWORD (lParam) ;
  507.                                    break ;
  508.  
  509.                               default:
  510.                                    return TRUE ;
  511.                               }
  512.  
  513.                          iPosition = max (0, min (iPosition, 1000)) ;
  514.  
  515.                          SetScrollPos (GetDlgItem (hwnd, ID_POSITION),
  516.                                        SB_CTL, iPosition, FALSE) ;
  517.  
  518.                          if (wDeviceID)
  519.                               {
  520.                                    // Save existing time format
  521.  
  522.                               dwFormat = MciMidiGetFormat (wDeviceID, hwnd,
  523.                                                            MCI_WAIT) ;
  524.  
  525.                                    // Set time format to milliseconds
  526.  
  527.                               MciMidiSetFormat (wDeviceID, hwnd, MCI_WAIT,
  528.                                                 MCI_FORMAT_MILLISECONDS) ;
  529.  
  530.                                    // Find out if we're currently playing
  531.  
  532.                               dwMode = MciMidiGetMode (wDeviceID, hwnd,
  533.                                                        MCI_WAIT) ;
  534.  
  535.                                    // Perform a seek
  536.  
  537.                               MciMidiSeek (wDeviceID, hwnd, MCI_WAIT | MCI_TO,
  538.                                    iPosition * MciMidiGetLength (wDeviceID,
  539.                                                   hwnd, MCI_WAIT) / 1000) ;
  540.  
  541.                                    // Reset the time format
  542.  
  543.                               MciMidiSetFormat (wDeviceID, hwnd,
  544.                                                 MCI_WAIT, dwFormat) ;
  545.  
  546.                                    // Update the scroll bar labels
  547.  
  548.                               UpdateLabels (wDeviceID, hwnd) ;
  549.  
  550.                                    // Continue playing
  551.  
  552.                               if (dwMode == MCI_MODE_PLAY)
  553.                                   SendMessage (hwnd, WM_COMMAND, ID_PLAY, 0L) ;
  554.                               }
  555.  
  556.                          return TRUE ;
  557.  
  558.                     case ID_TEMPO:
  559.                          iTempo = GetScrollPos (
  560.                                   GetDlgItem (hwnd, ID_TEMPO), SB_CTL) ;
  561.  
  562.                          GetScrollRange (GetDlgItem (hwnd, ID_TEMPO), SB_CTL,
  563.                                          &iMin, &iMax) ;
  564.  
  565.                          switch (wParam)
  566.                               {
  567.                               case SB_LEFT:       iTempo  = iMin ;  break ;
  568.                               case SB_LINELEFT:   iTempo -=    1 ;  break ;
  569.                               case SB_PAGELEFT:   iTempo -=   10 ;  break ;
  570.                               case SB_PAGERIGHT:  iTempo +=   10 ;  break ;
  571.                               case SB_LINERIGHT:  iTempo +=    1 ;  break ;
  572.                               case SB_RIGHT:      iTempo  = iMax ;  break ;
  573.  
  574.                               case SB_THUMBPOSITION:
  575.                                    iTempo = LOWORD (lParam) ;
  576.                                    break ;
  577.  
  578.                               default:
  579.                                    return TRUE ;
  580.                               }
  581.  
  582.                          iTempo = max (iMin, min (iTempo, iMax)) ;
  583.  
  584.                          SetScrollPos (GetDlgItem (hwnd, ID_TEMPO),
  585.                                        SB_CTL, iTempo, FALSE) ;
  586.  
  587.                          if (wDeviceID)
  588.                               {
  589.                                    // Set the new tempo
  590.  
  591.                               MciMidiSetTempo (wDeviceID, hwnd,
  592.                                                MCI_WAIT, iTempo) ;
  593.  
  594.                                    // Update the scroll bar labels
  595.  
  596.                               UpdateLabels (wDeviceID, hwnd) ;
  597.                               }
  598.  
  599.                          return TRUE ;
  600.                     }
  601.  
  602.                return TRUE ;
  603.  
  604.           case WM_TIMER:
  605.                UpdateLabels (wDeviceID, hwnd) ;
  606.                return TRUE ;
  607.  
  608.           case MM_MCINOTIFY:
  609.                if (wParam == MCI_NOTIFY_SUCCESSFUL)
  610.                     SendMessage (hwnd, WM_COMMAND, ID_STOP, 0L) ;
  611.  
  612.                return TRUE ;
  613.  
  614.           case WM_SYSCOMMAND:
  615.                switch (wParam)
  616.                     {
  617.                     case SC_CLOSE:
  618.                          if (wDeviceID)
  619.                               SendMessage (hwnd, WM_COMMAND, ID_CLOSE, 0L) ;
  620.  
  621.                          EndDialog (hwnd, 0) ;
  622.                          return TRUE ;
  623.                     }
  624.                break ;
  625.           }
  626.      return FALSE ;
  627.      }
  628.