home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / C / BC_502 / MCISOUND.PAK / MCISOUND.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-06  |  14.2 KB  |  562 lines

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