home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1998 May / Pcwk5b98.iso / Borland / Cplus45 / BC45 / MCISOUND.PAK / MCISOUND.CPP < prev    next >
C/C++ Source or Header  |  1995-08-29  |  15KB  |  561 lines

  1. //----------------------------------------------------------------------------
  2. // ObjectWindows - (C) Copyright 1991, 1993 by Borland International
  3. //
  4. //
  5. // This example demonstrates the use of MCI APIs in Windows 3.1 in an OWL
  6. // application:
  7. //
  8. // You must have a sound board/Speaker and its device driver installed under
  9. // Windows 3.1 and be certain that it works -- Use the sound applet in Windows
  10. // Control Pannel to generate a sound.  You may copy one of the .WAV files
  11. // from the WINDOWS subdirectory in your system to this example's
  12. // subdirectory.
  13. //
  14. // Run the .EXE. Choose File:Open and select a .WAV file. Choose Control:Play
  15. // to play the waveform. The Control menu lets you stop/play/pause and resume.
  16. // The scrollbar allows random access through the waveform while it is
  17. // playing.
  18. // This example demonstrates the use of the MCI API and a callback via
  19. // WM_MCINOTIFY.
  20. //
  21. //----------------------------------------------------------------------------
  22. #include <owl\owlpch.h>
  23. #include <owl\applicat.h>
  24. #include <owl\opensave.h>
  25. #include <owl\framewin.h>
  26. #include <owl\dc.h>
  27. #include <owl\menu.h>
  28. #include <owl\button.h>
  29. #include <owl\slider.h>
  30. #include <owl\slider.rh>
  31. #include <math.h>
  32. #include <mmsystem.h>
  33. #include "mcisound.h"
  34.  
  35. #define ID_SCROLL    150         // Scroll bar
  36. #define TIMER_ID     264         // Unique timer ID.
  37. #define MCI_PARM2(p) ((long)(void far*)&p)
  38.  
  39. UINT  DeviceId = 0;              // The global waveform device opened handle
  40. BOOL  FlushNotify = FALSE;
  41.  
  42. //----------------------------------------------------------------------------
  43.  
  44. class TSoundBar : public THSlider {
  45.   public:
  46.     TSoundBar(TWindow* parent, int id, int x, int y, int w, int h, 
  47.               TModule* module = 0)
  48.          : THSlider(parent, id, x, y, w, h, IDB_HSLIDERTHUMB, module) {}
  49.  
  50.     void    SetInfo(int ratio, long length);
  51.     void    SetName(char* name);
  52.  
  53.     //
  54.     // Override TScrollBars virtual functions
  55.     //
  56.     void    SBLineUp();
  57.     void    SBLineDown();
  58.     void    SBPageUp();
  59.     void    SBPageDown();
  60.     void    SBThumbPosition(int thumbPos);
  61.     void    SBTop();
  62.     void    SBBottom();
  63.  
  64.   private:
  65.     int     WaveRatio;
  66.     long    WaveLength;
  67.     char    ElementName[255];
  68.  
  69.     void    ReposAndPlay(long newPos);
  70. };
  71.  
  72.  
  73. void
  74. TSoundBar::ReposAndPlay(long newPos)
  75. {
  76.   MCI_PLAY_PARMS    MciPlayParm;
  77.   MCI_SEEK_PARMS    MciSeekParm;
  78.   MCI_SET_PARMS     MciSetParm;
  79.   MCI_OPEN_PARMS    MciOpenParm;
  80.   MCI_GENERIC_PARMS MciGenParm;
  81.  
  82.   // Only allow SEEK if playing.
  83.   //
  84.   if (!DeviceId)
  85.      return;
  86.  
  87.   // Close the currently playing wave.
  88.   //
  89.   FlushNotify = TRUE;
  90.   MciGenParm.dwCallback = 0;
  91.   mciSendCommand(DeviceId, MCI_STOP, MCI_WAIT, MCI_PARM2(MciGenParm));
  92.   mciSendCommand(DeviceId, MCI_CLOSE, MCI_WAIT, MCI_PARM2(MciGenParm));
  93.  
  94.   // Open the wave again and seek to new position.
  95.   //
  96.   MciOpenParm.dwCallback = 0;
  97.   MciOpenParm.wDeviceID = DeviceId;
  98.   #if !defined(__WIN32__)
  99.     MciOpenParm.wReserved0 = 0;
  100.   #endif
  101.   MciOpenParm.lpstrDeviceType = 0;
  102.   MciOpenParm.lpstrElementName = ElementName;
  103.   MciOpenParm.lpstrAlias = 0;
  104.  
  105.   if (mciSendCommand(DeviceId, MCI_OPEN, MCI_WAIT| MCI_OPEN_ELEMENT, MCI_PARM2(MciOpenParm))) {
  106.         MessageBox("Open Error", "Sound Play", MB_OK);
  107.         return;
  108.      }
  109.   DeviceId = MciOpenParm.wDeviceID;
  110.  
  111.   // Our time scale is in SAMPLES.
  112.   //
  113.   MciSetParm.dwTimeFormat = MCI_FORMAT_SAMPLES;
  114.   if (mciSendCommand(DeviceId, MCI_SET, MCI_SET_TIME_FORMAT, MCI_PARM2(MciSetParm))) {
  115.      MessageBox("Set Time Error", "Sound Play", MB_OK);
  116.      return;
  117.   }
  118.  
  119.   // Compute new position, remember the scrollbar range has been scaled based
  120.   // on WaveRatio.
  121.   //
  122.   MciSeekParm.dwCallback = 0;
  123.   MciSeekParm.dwTo = (newPos*WaveRatio > WaveLength) ? WaveLength :
  124.                                                        newPos*WaveRatio;
  125.   if (mciSendCommand(DeviceId, MCI_SEEK, MCI_TO, MCI_PARM2(MciSeekParm))) {
  126.     MessageBox("Seek Error", "Sound Play", MB_OK);
  127.     return;
  128.   }
  129.  
  130.   MciPlayParm.dwCallback = (long)HWindow;
  131.   MciPlayParm.dwFrom = 0;
  132.   MciPlayParm.dwTo = 0;
  133.   if (mciSendCommand(DeviceId, MCI_PLAY, MCI_NOTIFY, MCI_PARM2(MciPlayParm))) {
  134.     MessageBox("Play Error", "Sound Play", MB_OK);
  135.     return;
  136.   }
  137. }
  138.  
  139. void
  140. TSoundBar::SetInfo(int ratio, long length)
  141. {
  142.   WaveRatio = ratio;
  143.   WaveLength = length;
  144. }
  145.  
  146. void
  147. TSoundBar::SetName(char* name)
  148. {
  149.   strcpy(ElementName, name);
  150. }
  151.  
  152. void
  153. TSoundBar::SBLineUp()
  154. {
  155.   THSlider::SBLineUp();
  156.   ReposAndPlay(GetPosition());
  157. }
  158.  
  159. void
  160. TSoundBar::SBLineDown()
  161. {
  162.   THSlider::SBLineDown();
  163.   ReposAndPlay(GetPosition());
  164. }
  165.  
  166. void
  167. TSoundBar::SBPageUp()
  168. {
  169.   THSlider::SBPageUp();
  170.   ReposAndPlay(GetPosition());
  171. }
  172.  
  173. void
  174. TSoundBar::SBPageDown()
  175. {
  176.   THSlider::SBPageDown();
  177.   ReposAndPlay(GetPosition());
  178. }
  179.  
  180. void
  181. TSoundBar::SBThumbPosition(int thumbPos)
  182. {
  183.   THSlider::SBThumbPosition(thumbPos);
  184.   ReposAndPlay(GetPosition());
  185. }
  186.  
  187. void
  188. TSoundBar::SBTop()
  189. {
  190.   THSlider::SBTop();
  191.   ReposAndPlay(GetPosition());
  192. }
  193.  
  194. void
  195. TSoundBar::SBBottom()
  196. {
  197.   THSlider::SBBottom();
  198.   ReposAndPlay(GetPosition());
  199. }
  200.  
  201.  
  202. //----------------------------------------------------------------------------
  203.  
  204. class TSoundWindow : public TFrameWindow {
  205.   public:
  206.     TSoundWindow(TWindow* parent, const char far* title);
  207.     ~TSoundWindow();
  208.  
  209.     void        SetupWindow();
  210.  
  211.     LRESULT     MciNotify(WPARAM, LPARAM);
  212.     void        EvPaint();
  213.     void        CmFileOpen();
  214.     void        CmFileExit();
  215.     void        CmPlayWave();
  216.     void        CmStopWave();
  217.     void        CmHelpAbout();
  218.     void        EvTimer(UINT id);
  219.  
  220.   private:
  221.     char        ElementName[255];
  222.     int         Running;
  223.     int         Pause;
  224.     UINT        TimeGoing;
  225.     int         WaveRatio;
  226.     long        WaveLength;
  227.     TSoundBar*  SoundBar;
  228.  
  229.     MCI_GENERIC_PARMS MciGenParm;
  230.     MCI_OPEN_PARMS    MciOpenParm;
  231.     MCI_PLAY_PARMS    MciPlayParm;
  232.     MCI_STATUS_PARMS  MciStatusParm;
  233.     MCI_SET_PARMS     MciSetParm;
  234.  
  235.     void        GetDeviceInfo();
  236.     void        StopWave();
  237.     void        StopMCI();
  238.  
  239.   DECLARE_RESPONSE_TABLE(TSoundWindow);
  240. };
  241.  
  242. DEFINE_RESPONSE_TABLE1(TSoundWindow, TFrameWindow)
  243.   EV_MESSAGE(MM_MCINOTIFY, MciNotify),
  244.   EV_WM_PAINT,
  245.   EV_COMMAND(SM_OPEN, CmFileOpen),
  246.   EV_COMMAND(SM_EXIT, CmFileExit),
  247.   EV_COMMAND(SM_PLAY, CmPlayWave),
  248.   EV_COMMAND(SM_STOP, CmStopWave),
  249.   EV_COMMAND(SM_ABOUT, CmHelpAbout),
  250.   EV_WM_TIMER,
  251. END_RESPONSE_TABLE;
  252.  
  253.  
  254. TSoundWindow::TSoundWindow(TWindow* parent, const char far* title)
  255.   : TFrameWindow(parent, title)
  256. {
  257.   AssignMenu(ID_MENU);
  258.   Attr.AccelTable = IDA_SOUNDPLY;
  259.   Attr.X = 50;
  260.   Attr.Y = 100;
  261.   Attr.W = 400;
  262.   Attr.H = 220;
  263.  
  264.   Running = 0;
  265.   Pause = 0;
  266.   WaveLength = WaveRatio = 0;
  267.   ElementName[0] = 0;
  268.  
  269.   SoundBar = new TSoundBar(this, ID_SCROLL, 50, 67, 300, 40);
  270.   
  271.   new TButton(this, SM_PLAY, "&Play", 50, 120, 50, 20, TRUE);
  272.   new TButton(this, SM_STOP, "&Stop", 120, 120, 50, 20, TRUE);
  273. }
  274.  
  275. void
  276. TSoundWindow::SetupWindow()
  277. {
  278.   TFrameWindow::SetupWindow();
  279.   SoundBar->SetRange(0, 0);
  280.   SoundBar->SetRuler(0);
  281. }
  282.  
  283. TSoundWindow::~TSoundWindow()
  284. {
  285.   StopMCI();
  286. }
  287.  
  288. //
  289. // EvPaint member function responds to WM_PAINT messages
  290. //
  291. void
  292. TSoundWindow::EvPaint()
  293. {
  294.   TPaintDC paintDC(HWindow);
  295.  
  296.   // File name.
  297.   //
  298.   if (strlen(ElementName))
  299.      paintDC.TextOut(5, 5, ElementName, strlen(ElementName));
  300.   else
  301.      paintDC.TextOut(5, 5, "<No WAVEFORM file loaded>", 25);
  302.  
  303.   paintDC.TextOut(160, 35, "Samples:", 8);
  304.   paintDC.TextOut(50, 48, "0", 1);     // Beginning value.
  305.  
  306.   // Ending number of samples.
  307.   //
  308.   char buffer[10];
  309.   if (WaveLength)
  310.     wsprintf(buffer, "%ld", WaveLength);
  311.   else
  312.     strcpy(buffer, "Unknown");
  313.   paintDC.TextOut(315, 48, buffer, strlen(buffer));
  314. }
  315.  
  316. void
  317. TSoundWindow::GetDeviceInfo()
  318. {
  319.   WAVEOUTCAPS waveOutCaps;
  320.  
  321.   if (!waveOutGetDevCaps(DeviceId, &waveOutCaps, sizeof(waveOutCaps))) {
  322.     MessageBox("GetDevCaps Error", "Sound Play", MB_OK);
  323.     return;
  324.   }
  325. }
  326.  
  327. //
  328. // Play the wave...
  329. //
  330. void
  331. TSoundWindow::CmPlayWave()
  332. {
  333.   if (!Running) {
  334.     //
  335.     // MCI APIs to open a device and play a .WAV file, using
  336.     // notification to close
  337.     //
  338.     memset(&MciOpenParm, 0, sizeof MciOpenParm);
  339.  
  340.     MciOpenParm.lpstrElementName = ElementName;
  341.  
  342.     if (mciSendCommand(0, MCI_OPEN, MCI_WAIT | MCI_OPEN_ELEMENT, 
  343.                        MCI_PARM2(MciOpenParm))) {
  344.       MessageBox(
  345.          "Open Error - a waveForm output device is necessary to use this demo.",
  346.          "Sound Play", MB_OK);
  347.         return;
  348.     }
  349.     DeviceId = MciOpenParm.wDeviceID;
  350.  
  351.     // The time format in this demo is in Samples.
  352.     //
  353.     MciSetParm.dwCallback = 0;
  354.     MciSetParm.dwTimeFormat = MCI_FORMAT_SAMPLES;
  355.     if (mciSendCommand(DeviceId, MCI_SET, MCI_SET_TIME_FORMAT, MCI_PARM2(MciSetParm))) {
  356.       MessageBox("SetTime Error", "Sound Play", MB_OK);
  357.       return;
  358.     }
  359.  
  360.     MciPlayParm.dwCallback = (long)HWindow;
  361.     MciPlayParm.dwFrom = 0;
  362.     MciPlayParm.dwTo = 0;
  363.     mciSendCommand(DeviceId, MCI_PLAY, MCI_NOTIFY, MCI_PARM2(MciPlayParm));
  364.  
  365.     // Modify the menu to toggle PLAY to STOP, and enable PAUSE.
  366.     //
  367.     TMenu menu(HWindow);
  368.     menu.ModifyMenu(SM_PLAY, MF_STRING, SM_PLAY, "P&ause\tCtrl+A");
  369.     menu.EnableMenuItem(SM_STOP, MF_ENABLED);
  370.  
  371.     // Make sure the Play/Stop toggle menu knows we're Running.
  372.     //
  373.     Running = TRUE;
  374.  
  375.     // Start a timer to show our progress through the waveform file.
  376.     //
  377.     TimeGoing = SetTimer(TIMER_ID, 200, 0);
  378.  
  379.     // Give enough information to the scrollbar to monitor the
  380.     // progress and issue a re-MCI_OPEN.
  381.     //
  382.     SoundBar->SetName(ElementName);
  383.  
  384.   } else {
  385.     if (!Pause) {
  386.       // Pause the playing.
  387.       //
  388.       MciGenParm.dwCallback = 0;
  389.       mciSendCommand(DeviceId, MCI_PAUSE, MCI_WAIT, MCI_PARM2(MciGenParm));
  390.  
  391.       // Toggle Pause menu to Resume.
  392.       //
  393.       TMenu menu(HWindow);
  394.       menu.ModifyMenu(SM_PLAY, MF_STRING, SM_PLAY, "&Play\tCtrl+P");
  395.       Pause = TRUE;
  396.  
  397.     } else {
  398.       // Resume the playing.
  399.       //
  400.       MciGenParm.dwCallback = 0;
  401.       mciSendCommand(DeviceId, MCI_RESUME, MCI_WAIT, MCI_PARM2(MciGenParm));
  402.  
  403.       // Toggle Resume menu to Pause.
  404.       //
  405.       TMenu menu(HWindow);
  406.       menu.ModifyMenu(SM_PLAY, MF_STRING, SM_PLAY, "P&ause\tCtrl+A");
  407.       Pause = FALSE;
  408.     }
  409.   }
  410. }
  411.  
  412. void
  413. TSoundWindow::CmStopWave()
  414. {
  415.   StopWave();
  416. }
  417.  
  418. void
  419. TSoundWindow::StopMCI()
  420. {
  421.   if (TimeGoing)       // if Timer is Running, then kill it now.
  422.     TimeGoing = !KillTimer(TIMER_ID);
  423.  
  424.   // Stop playing the waveform file and close the waveform device.
  425.   //
  426.   MciGenParm.dwCallback = 0;
  427.   mciSendCommand(DeviceId, MCI_STOP, MCI_WAIT, MCI_PARM2(MciGenParm));
  428.   mciSendCommand(DeviceId, MCI_CLOSE, MCI_WAIT,MCI_PARM2(MciGenParm));
  429.  
  430.   Running = FALSE;
  431.   DeviceId = 0;
  432. }
  433.  
  434. //
  435. // Reset the menus to Play menu and gray the Pause menu.
  436. //
  437. void
  438. TSoundWindow::StopWave()
  439. {
  440.   if (DeviceId) {
  441.     StopMCI();
  442.     TMenu menu(HWindow);
  443.     menu.ModifyMenu(SM_PLAY, MF_STRING, SM_PLAY, "&Play\tCtrl+P");
  444.   }
  445. }
  446.  
  447. void
  448. TSoundWindow::CmFileOpen()
  449. {
  450.   static TOpenSaveDialog::TData data (
  451.     OFN_HIDEREADONLY|OFN_FILEMUSTEXIST,
  452.     "Wave Files (*.WAV)|*.wav|",
  453.     0,
  454.     0,
  455.     "WAV"
  456.   );
  457.   if (TFileOpenDialog(this, data).Execute() == IDOK) {
  458.     if (CanClose()) {
  459.       strcpy(ElementName, data.FileName);  // Remember the wave file to open.
  460.  
  461.       // Turn the Play menu on.
  462.       TMenu(HWindow).EnableMenuItem(SM_PLAY, MF_ENABLED);
  463.  
  464.       WaveLength = 0;
  465.       WaveRatio = 0;
  466.       SoundBar->SetPosition(0);
  467.       Invalidate();
  468.     }
  469.   }
  470. }
  471.  
  472. void
  473. TSoundWindow::CmFileExit()
  474. {
  475.   CloseWindow();
  476. }
  477.  
  478. //
  479. // Response function MM_MCINOTIFY message when MCI_PLAY is complete.
  480. //
  481. LRESULT
  482. TSoundWindow::MciNotify(WPARAM, LPARAM)
  483. {
  484.   if (!FlushNotify) {        // Internal STOP/CLOSE, from thumb re-pos?
  485.     StopWave();
  486.  
  487.     // Make sure the thumb is at the end. There could be some WM_TIMER
  488.     // messages on the queue when we kill it, thereby flushing WM_TIMER's
  489.     // from the message queue.
  490.     //
  491.     int   loVal, hiVal;
  492.     SoundBar->GetRange(loVal, hiVal);
  493.     SoundBar->SetPosition(hiVal);
  494.  
  495.   } else
  496.     FlushNotify = FALSE;          // Yes, so ignore the close.
  497.   return 0;
  498. }
  499.  
  500. void
  501. TSoundWindow::CmHelpAbout()
  502. {
  503.   MessageBox("SoundPly loads and plays waveform sound "
  504.     "files (*.WAV).  The features of this demo are Play/Pause, Stop, "
  505.     "and random seeks through the file via the scollbar (while the sound is "
  506.     "playing).  NOTE: SoundPly will only play sounds if the machine contains "
  507.     "a waveForm device, e.g. SoundBlaster, etc.).",
  508.     "About Sound Play", MB_OK);
  509. }
  510.  
  511. void
  512. TSoundWindow::EvTimer(UINT)
  513. {
  514.   if (!FlushNotify) {               // Internal STOP/CLOSE, from thumb re-pos?
  515.     MciStatusParm.dwCallback = 0;   // No, normal close.
  516.     MciStatusParm.dwItem = MCI_STATUS_LENGTH;
  517.     mciSendCommand (DeviceId, MCI_STATUS, MCI_STATUS_ITEM, MCI_PARM2(MciStatusParm));
  518.  
  519.     if (WaveLength != MciStatusParm.dwReturn) {
  520.       Invalidate();  // First time it's different update the scrollbar nums
  521.       WaveLength = MciStatusParm.dwReturn;
  522.     }
  523.  
  524.     // Compute the length and ratio and update SoundBar info.
  525.     //
  526.     WaveRatio = int((WaveLength + 32000/2) / 32000);
  527.     if (!WaveRatio)
  528.       WaveRatio = 1;
  529.     SoundBar->SetInfo(WaveRatio, WaveLength);
  530.     SoundBar->SetRange(0, int(WaveLength / WaveRatio));
  531.     SoundBar->SetRuler(int((WaveLength / WaveRatio) / 10));
  532.  
  533.     // Update the current position.
  534.     //
  535.     MciStatusParm.dwCallback = 0;
  536.     MciStatusParm.dwItem = MCI_STATUS_POSITION;
  537.     mciSendCommand(DeviceId, MCI_STATUS, MCI_STATUS_ITEM, MCI_PARM2(MciStatusParm));
  538.  
  539.     SoundBar->SetPosition(int(MciStatusParm.dwReturn / WaveRatio));
  540.   }
  541.  
  542.   FlushNotify = FALSE;          // Yes, ignore this close.
  543. }
  544.  
  545. //----------------------------------------------------------------------------
  546.  
  547. class TSoundApp : public TApplication {
  548.   public:
  549.     TSoundApp() : TApplication() {}
  550.     void InitMainWindow() {
  551.       MainWindow = new TSoundWindow(0, "OWL MCI SoundPlay Demo");
  552.       EnableCtl3d();
  553.     }
  554. };
  555.  
  556. int
  557. OwlMain(int /*argc*/, char* /*argv*/ [])
  558. {
  559.   return TSoundApp().Run();
  560. }
  561.