home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Borland / Borland Pascal with Objects 7.0 / MCISOUND.ZIP / MCISOUND.PAS next >
Encoding:
Pascal/Delphi Source File  |  1992-10-27  |  19.1 KB  |  697 lines

  1. {***************************************************}
  2. {                                                   }
  3. {   Windows 3.1 MCI API Sound Support               }
  4. {   Demonstration Program                           }
  5. {   Copyright (c) 1992 by Borland International     }
  6. {                                                   }
  7. {***************************************************}
  8.  
  9. program MCISound;
  10.  
  11. { This example demonstrates the use of MCI APIs in Windows 3.1 in an
  12.   OWL application.  You must have a sound board and its device driver
  13.   properly installed under Windows 3.1.
  14.  
  15.   You may copy one of the .WAV files from the WINDOW subdirectory in
  16.   your system to this example's subdirectory.
  17.  
  18.   Run the .EXE choose Open from the File menu and select a .WAV file.
  19.   Choose Play from the Options menu and control of the sound is done
  20.   via the Options menu and the scroll bar. The Options menu lets you
  21.   stop/play/pause and resume.  The scrollbar allows random access
  22.   through the waveform while it is playing.
  23.  
  24.   This example demostrates the use MCI API and use of a callback
  25. }
  26.  
  27. uses Strings, WinTypes, WinProcs, OWindows, ODialogs, WinDOS, Win31,
  28.   ShellAPI, MMSystem, CommDlg, BWCC;
  29.  
  30. {$R MCISOUND}
  31.  
  32. const
  33.  
  34. { Resource IDs }
  35.  
  36.   id_Menu  = 100;
  37.   id_About = 100;
  38.   id_Instr = 101;   { Instructions }
  39.   id_Icon  = 100;
  40.  
  41. { Menu command IDs }
  42.  
  43.   cm_FileOpen   = 201;
  44.   cm_HelpAbout  = 300;
  45.   cm_SoundPlay  = 301;
  46.   cm_SoundPause = 302;
  47.  
  48.   id_Scroll = 150;  { Scroll bar }
  49.   Timer_Id  = 264;  { Unique timer ID. }
  50.  
  51. type
  52.  
  53. { Filename string }
  54.  
  55.   TFilename = array[0..255] of Char;
  56.  
  57. { Sound Control Scroll Bar }
  58.  
  59.   PSoundBar = ^TSoundBar;
  60.   TSoundBar = object(TScrollBar)
  61.     WaveRatio  : Integer;
  62.     WaveLength : Longint;
  63.     ElementName: TFilename;
  64.  
  65.     procedure RePosAndPlay(NewPos: Longint); virtual;
  66.  
  67.     procedure ScrollSetInfo(WRatio: Integer; WLength: Longint); virtual;
  68.     procedure ScrollSetName(EName: PChar); virtual;
  69.  
  70.     procedure SBLineUp(var Msg: TMessage);
  71.       virtual nf_First + sb_LineUp;
  72.     procedure SBLineDown(var Msg: TMessage);
  73.       virtual nf_First + sb_LineDown;
  74.     procedure SBPageUp(var Msg: TMessage);
  75.       virtual nf_First + sb_PageUp;
  76.     procedure SBPageDown(var Msg: TMessage);
  77.       virtual nf_First + sb_PageDown;
  78.     procedure SBThumbPosition(var Msg: TMessage);
  79.       virtual nf_First + sb_ThumbPosition;
  80.     procedure SBTop(var Msg: TMessage);
  81.       virtual nf_First + sb_Top;
  82.     procedure SBBottom(var Msg: TMessage);
  83.       virtual nf_First + sb_Bottom;
  84.   end;
  85.  
  86. { Application main window }
  87.  
  88.   PSoundWindow = ^TSoundWindow;
  89.   TSoundWindow = object(TWindow)
  90.     ElementName: TFilename;
  91.     IsRunning  : Boolean;
  92.     Paused     : Boolean;
  93.     TimerGoing : Boolean;
  94.     WaveRatio  : Integer;
  95.     WaveLength : Longint;
  96.     SoundBar   : PSoundBar;
  97.  
  98.     MciGenParm   : TMCI_Generic_Parms;
  99.     MciOpenParm  : TMCI_Open_Parms;
  100.     MciPlayParm  : TMCI_Play_Parms;
  101.     MciStatusParm: TMCI_Status_Parms;
  102.     MciSetParm   : TMCI_Set_Parms;
  103.  
  104.     constructor Init(AParent: PWindowsObject; ATitle: PChar);
  105.     destructor  Done; virtual;
  106.  
  107.     procedure GetDeviceInfo;     virtual;
  108.     procedure StopWave;          virtual;
  109.     procedure UpdateSoundWindow; virtual;
  110.  
  111.     procedure GetWindowClass(var AWndClass: TWndClass); virtual;
  112.     function  GetClassName: PChar; virtual;
  113.     procedure SetupWindow; virtual;
  114.  
  115.     procedure MciNotify(var Msg: TMessage);
  116.       virtual wm_First + mm_MCINotify;
  117.  
  118.     procedure Paint(PaintDC: HDC; var PaintInfo: TPaintStruct); virtual;
  119.  
  120.     procedure CMFileOpen(var Msg: TMessage);
  121.       virtual cm_First + cm_FileOpen;
  122.     procedure CMSoundPlay(var Msg: TMessage);
  123.       virtual cm_First + cm_SoundPlay;
  124.     procedure CMSoundPause(var Msg: TMessage);
  125.       virtual cm_First + cm_SoundPause;
  126.     procedure CMHelpAbout(var Msg: TMessage);
  127.       virtual cm_First + cm_HelpAbout;
  128.  
  129.     procedure WMIdleStuff(var Msg: TMessage);
  130.       virtual wm_First + wm_Timer;
  131.   end;
  132.  
  133. { Application object }
  134.  
  135.   TSoundApp = object(TApplication)
  136.     procedure InitInstance;   virtual;
  137.     procedure InitMainWindow; virtual;
  138.   end;
  139.  
  140. { Initialized globals }
  141.  
  142. const
  143.   DemoTitle  : PChar   = 'MCI Sound Demo Program';
  144.   DeviceID   : Word    = 0;
  145.   FlushNotify: Boolean = FALSE;
  146.  
  147. { Global variables }
  148.  
  149. var
  150.   App: TSoundApp;
  151.  
  152.  
  153. { TSoundBar Methods }
  154.  
  155. procedure TSoundBar.RePosAndPlay(NewPos: Longint);
  156. var
  157.   MciSeekParm  : TMCI_Seek_Parms;
  158.   MciGenParm   : TMCI_Generic_Parms;
  159.   MciOpenParm  : TMCI_Open_Parms;
  160.   MciPlayParm  : TMCI_Play_Parms;
  161.   MciStatusParm: TMCI_Status_Parms;
  162.   MciSetParm   : TMCI_Set_Parms;
  163. begin
  164. { Only allow SEEK if playing. }
  165.  
  166.   if DeviceID = 0 then
  167.     Exit;
  168.  
  169. { Close the currently playing wave.
  170. }
  171.   FlushNotify := True;
  172.   MciGenParm.dwCallback := 0;
  173.   mciSendCommand(DeviceID, mci_Stop,  mci_Wait, Longint(@MciGenParm));
  174.   mciSendCommand(DeviceID, mci_Close, mci_Wait, Longint(@MciGenParm));
  175.  
  176. { Open the wave again and seek to new position.
  177. }
  178.   MciOpenParm.dwCallback := 0;
  179.   MciOpenParm.wDeviceID  := DeviceID;
  180.   MciOpenParm.wReserved0 := 0;
  181.   MciOpenParm.lpstrDeviceType := nil;
  182.   MciOpenParm.lpstrElementName:= ElementName;
  183.   MciOpenParm.lpstrAlias      := nil;
  184.  
  185.   if mciSendCommand(DeviceID, mci_Open, mci_Wait or mci_Open_Element,
  186.       Longint(@MciOpenParm)) <> 0 then
  187.     MessageBox(HWindow, 'Open Error', DemoTitle, mb_OK)
  188.   else
  189.   begin
  190.     DeviceID := MciOpenParm.wDeviceID;
  191.  
  192. { Our time scale is in SAMPLES.
  193. }
  194.     MciSetParm.dwTimeFormat := mci_Format_Samples;
  195.     if mciSendCommand(DeviceID, mci_Set, mci_Set_Time_Format,
  196.         Longint(@MciSetParm)) <> 0 then
  197.       MessageBox(HWindow, 'Set Time Error', DemoTitle, mb_OK)
  198.     else
  199.     begin
  200. { Compute new position, remember the scrollbar range has been scaled based
  201.   on waveRatio.
  202. }
  203.       MciSeekParm.dwCallback:= 0;
  204.       if (NewPos * WaveRatio) > WaveLength then
  205.         MciSeekParm.dwTo := WaveLength
  206.       else
  207.         MciSeekParm.dwTo := NewPos * WaveRatio;
  208.       
  209.       if mciSendCommand(DeviceID, mci_Seek, mci_To,
  210.           Longint(@MciSeekParm)) <> 0 then
  211.         MessageBox(HWindow, 'Seek Error', DemoTitle, mb_OK)
  212.       else
  213.       begin
  214.     MciPlayParm.dwCallback:= HWindow;
  215.     MciPlayParm.dwFrom    := 0;
  216.     MciPlayParm.dwTo      := 0;
  217.     if mciSendCommand(DeviceID, mci_Play, mci_Notify,
  218.         Longint(@MciPlayParm)) <> 0 then
  219.           MessageBox(HWindow, 'Play Error', DemoTitle, mb_OK);
  220.       end;
  221.     end;
  222.   end;  { Playing }
  223. end;
  224.  
  225. { Sets the given ratio and length as the current WaveRatio and WaveLength
  226.   of the Sound Bar.
  227. }
  228. procedure TSoundBar.ScrollSetInfo(WRatio: Integer; WLength: Longint);
  229. begin
  230.   WaveRatio  := WRatio;
  231.   WaveLength := WLength;
  232. end;
  233.  
  234. { Sets the given string as the name of the SoundBar.
  235. }
  236. procedure TSoundBar.ScrollSetName(EName: PChar);
  237. begin
  238.   StrCopy(ElementName, EName);
  239. end;
  240.  
  241. { Responds to a click on the Scroll Bar's up-arrow by stepping
  242.   the wave.  Calls on the inherited SBLineUp to do the actual
  243.   update of the scroll bar, then uses the new position for the
  244.   sound.
  245. }
  246. procedure TSoundBar.SBLineUp(var Msg: TMessage);
  247. begin
  248.   TScrollBar.SBLineUp(Msg);
  249.   RePosAndPlay(GetPosition);
  250. end;
  251.  
  252. { Responds to a click on the Scroll Bar's down-arrow as above.
  253. }
  254. procedure TSoundBar.SBLineDown(var Msg: TMessage);
  255. begin
  256.   TScrollBar.SBLineDown(Msg);
  257.   RePosAndPlay(GetPosition);
  258. end;
  259.  
  260. { Responds to a click on the Scroll Bar's page-up area as above.
  261. }
  262. procedure TSoundBar.SBPageUp(var Msg: TMessage);
  263. begin
  264.   TScrollBar.SBPageUp(Msg);
  265.   RePosAndPlay(GetPosition);
  266. end;
  267.  
  268. { Responds to a click on the Scroll Bar's page-down area as above.
  269. }
  270. procedure TSoundBar.SBPageDown(var Msg: TMessage);
  271. begin
  272.   TScrollBar.SBPageDown(Msg);
  273.   RePosAndPlay(GetPosition);
  274. end;
  275.  
  276. { Responds to a movement of the Scroll Bar's thumb as above.
  277. }
  278. procedure TSoundBar.SBThumbPosition(var Msg: TMessage);
  279. begin
  280.   TScrollBar.SBThumbPosition(Msg);
  281.   RePosAndPlay(GetPosition);
  282. end;
  283.  
  284. { Responds to movement of the scroll bar to the Top as above.
  285. }
  286. procedure TSoundBar.SBTop(var Msg: TMessage);
  287. begin
  288.   TScrollBar.SBTop(Msg);
  289.   RePosAndPlay(GetPosition);
  290. end;
  291.  
  292. { Responds to movement of the scroll bar to the Bottom as above.
  293. }
  294. procedure TSoundBar.SBBottom(var Msg: TMessage);
  295. begin
  296.   TScrollBar.SBBottom(Msg);
  297.   RePosAndPlay(GetPosition);
  298. end;
  299.  
  300.  
  301. { TSoundWindow Methods }
  302.  
  303. { Constructs an instance of the TSoundWindow, positioning it and setting
  304.   its data fields to their initial values.
  305. }
  306. constructor TSoundWindow.Init(AParent: PWindowsObject; ATitle: PChar);
  307. begin
  308.   TWindow.Init(AParent, ATitle);
  309.  
  310.   Attr.X := 50;
  311.   Attr.Y := 100;
  312.   Attr.W := 400;
  313.   Attr.H := 150;
  314.  
  315.   IsRunning  := False;
  316.   Paused     := False;
  317.   WaveLength := 0;
  318.   WaveRatio  := 0;
  319.   StrCopy(ElementName, '');
  320.  
  321.   SoundBar := New(PSoundBar, Init(@Self, id_Scroll, 50, 50, 300, 0, True));
  322.   SoundBar^.SetRange(0, 0);
  323. end;
  324.  
  325. { Destroys an instance of the Sound Window.  Before calling the ancestral
  326.   destructor to remove the object, stops the current wave.
  327. }
  328. destructor TSoundWindow.Done;
  329. begin
  330.   StopWave;
  331.   TWindow.Done;
  332. end;
  333.  
  334.  
  335. { Repaints the window, posting information about the current sound.
  336. }
  337. procedure TSoundWindow.Paint(PaintDC: HDC; var PaintInfo: TPaintStruct);
  338. var
  339.   Buffer: array [0..100] of Char;
  340. begin
  341. { File Name }
  342.   if StrLen(ElementName) > 0 then
  343.     TextOut(PaintDC, 5, 5, ElementName, StrLen(ElementName))
  344.   else
  345.     TextOut(PaintDC, 5, 5, '<No WAVEFORM file loaded>', 25);
  346.  
  347. { Beginning value }
  348.   TextOut (PaintDC, 50, 30, '0', 1);
  349.  
  350. { Ending number of samples }
  351.   if WaveLength <> 0 then
  352.     Str(WaveLength * WaveRatio, Buffer)
  353.   else
  354.     StrCopy(Buffer, 'Unknown');
  355.   TextOut(PaintDC, 325, 30, Buffer, StrLen(Buffer));
  356. end;
  357.  
  358. { Redefines GetWindowClass to give this application an icon and a menu.
  359. }
  360. procedure TSoundWindow.GetWindowClass(var AWndClass: TWndClass);
  361. begin
  362.   TWindow.GetWindowClass(AWndClass);
  363.   AWndClass.lpszMenuName := PChar(id_Menu);
  364. end;
  365.  
  366. { Returns the class name of this window.  This is necessary since we
  367.   redefine the inherited GetWindowClass method, above.
  368. }
  369. function TSoundWindow.GetClassName: PChar;
  370. begin
  371.   GetClassName := 'SoundPlay';
  372. end;
  373.  
  374. { Completes the initialization of the Window, performing
  375.   those functions which require a valid window handle. 
  376. }
  377. procedure TSoundWindow.SetupWindow;
  378. begin
  379.   TWindow.SetupWindow;
  380.   if WaveOutGetNumDevs = 0 then
  381.   begin
  382.     MessageBox(HWindow, 'No Wave Output device is available', 'Sound Demo',
  383.       mb_OK or mb_IconStop);
  384.     PostQuitMessage(0);
  385.   end;
  386. end;
  387.  
  388. { Obtains information about the system's sound generating capabilities.
  389. }
  390. procedure TSoundWindow.GetDeviceInfo;
  391. var
  392.   WOutCaps: TWaveOutCaps;
  393. begin
  394.   if WaveOutGetDevCaps(DeviceID, @WOutCaps, SizeOf(WOutCaps)) <> 0 then
  395.     MessageBox(HWindow, 'GetDevCaps Error', 'Sound Demo', mb_OK);
  396. end;
  397.  
  398.  
  399. { Plays the wave on request.
  400. }
  401. procedure TSoundWindow.CMSoundPlay(var Msg: TMessage);
  402. var
  403.   MyMenu : HMenu;
  404.   Res    : Longint;
  405.   ErrMsg : array [0..255] of Char;
  406. begin
  407.   if not IsRunning then
  408.   begin
  409. { MCI APIs to open a device and play a .WAV file, using notification to close
  410. }
  411.     MciOpenParm.dwCallback := 0;
  412.     MciOpenParm.wDeviceID  := 0;
  413.     MciOpenParm.wReserved0 := 0;
  414.     MciOpenParm.lpstrDeviceType  := nil;
  415.     MciOpenParm.lpstrElementName := ElementName;
  416.     MciOpenParm.lpstrAlias       := nil;
  417.  
  418.     if mciSendCommand(0, mci_Open, (mci_Wait or mci_Open_Element),
  419.         Longint(@MciOpenParm)) <> 0 then
  420.       MessageBox(HWindow, 'Open Error - A waveForm output device is ' +
  421.           'necessary to use this demo.', 'Sound Demo', mb_OK)
  422.     else
  423.     begin
  424.       DeviceID := MciOpenParm.wDeviceID;
  425.  
  426. { The time format in this demo is in Samples.
  427. }
  428.       MciSetParm.dwCallback   := 0;
  429.       MciSetParm.dwTimeFormat := mci_Format_Samples;
  430.       if mciSendCommand(DeviceID, mci_Set, mci_Set_Time_Format,
  431.       Longint(@MciSetParm)) <> 0 then
  432.       begin
  433.         StopWave;
  434.     MessageBox(HWindow, 'SetTime Error', 'Sound Demo', mb_OK)
  435.       end
  436.       else
  437.       begin
  438.         MciPlayParm.dwCallback := HWindow;
  439.         MciPlayParm.dwFrom     := 0;
  440.         MciPlayParm.dwTo       := 0;
  441.  
  442.     Res := mciSendCommand(DeviceID, mci_Play, mci_Notify,
  443.       Longint(@MciPlayParm));
  444.         if Res <> 0 then
  445.         begin
  446.           mciGetErrorString(Res, ErrMsg, SizeOf(ErrMsg));
  447.       MessageBox(HWindow, ErrMsg, 'Sound Demo', mb_OK or mb_IconStop);
  448.           StopWave;
  449.         end
  450.         else
  451.         begin
  452. { Modify the menu to toggle PLAY to STOP, and enable PAUSE.
  453. }
  454.           MyMenu := GetMenu(HWindow);
  455.           ModifyMenu(MyMenu, cm_SoundPlay, mf_String, cm_SoundPlay, '&Stop');
  456.       EnableMenuItem(MyMenu, cm_SoundPause, mf_Enabled);
  457.  
  458. { Make sure the Play/Stop toggle menu knows we're running.
  459. }
  460.       IsRunning := True; 
  461.  
  462. { Start a timer to show our progress through the waveform file.
  463. }
  464.           TimerGoing := (SetTimer(HWindow, Timer_Id, 500, nil) <> 0);
  465.  
  466. { Give enough information to the scrollbar to monitor the progress and issue a re-mci_Open.
  467. }
  468.           SoundBar^.ScrollSetName(ElementName);
  469.         end;
  470.       end;
  471.     end;
  472.   end
  473.   else
  474.   begin
  475. { Stop menu is toggled so kill the timer and stop the wave.
  476. }
  477.     KillTimer(HWindow, Timer_Id);
  478.     StopWave;
  479.   end;
  480. end;
  481.  
  482. { Pauses or resumes the playback in response to requests to do so from
  483.   the menu.  The File | Pause selection acts as a toggle.
  484. }
  485. procedure TSoundWindow.CMSoundPause(var Msg: TMessage);
  486. var
  487.   MyMenu: HMenu;
  488. begin
  489.   MyMenu := GetMenu(HWindow);
  490.  
  491.   if not Paused then
  492.   begin       { Pause the playing. }
  493.     MciGenParm.dwCallback := 0;
  494.     mciSendCommand(DeviceID, mci_Pause, mci_Wait, Longint(@MciGenParm));
  495.  
  496.     ModifyMenu(MyMenu, cm_SoundPause, mf_String, cm_SoundPause,
  497.       '&Resume'^I'Ctrl+P');
  498.   end
  499.   else
  500.   begin       { Resume the playing. }
  501.     MciGenParm.dwCallback := 0;
  502.     mciSendCommand(DeviceID, mci_Resume, mci_Wait, Longint(@MciGenParm));
  503.  
  504.     ModifyMenu(MyMenu, cm_SoundPause, mf_String, cm_SoundPause,
  505.       'P&ause'^I'Ctrl+P');
  506.   end;
  507.  
  508.   Paused := not Paused;
  509. end;
  510.  
  511. { Posts the About Box for the Sound Demo.
  512. }
  513. procedure TSoundWindow.CMHelpAbout(var Msg: TMessage);
  514. begin
  515.   Application^.ExecDialog(New(PDialog, Init(@Self, PChar(id_About))));
  516. end;
  517.  
  518. { Stops the playing waveform file, and closes the waveform device.
  519. }
  520. procedure TSoundWindow.StopWave;
  521. var
  522.   MyMenu: HMenu;
  523. begin
  524.   if DeviceID <> 0 then
  525.   begin
  526.     MciGenParm.dwCallback := 0;
  527.     mciSendCommand(DeviceID, mci_Stop,  mci_Wait, Longint(@MciGenParm));
  528.     mciSendCommand(DeviceID, mci_Close, mci_Wait, Longint(@MciGenParm));
  529.  
  530. { Reset the menus to Play menu and gray the Pause menu.
  531. }
  532.     MyMenu := GetMenu(HWindow);
  533.     ModifyMenu(MyMenu, cm_SoundPlay,  mf_String, cm_SoundPlay,
  534.       '&Play'^I'Ctrl+P');
  535.     ModifyMenu(MyMenu, cm_SoundPause, mf_String or mf_Grayed, cm_SoundPause,
  536.       'P&ause'^I'Ctrl+A');
  537.  
  538.     IsRunning := FALSE;
  539.     DeviceID  := 0;
  540.   end;
  541. end;
  542.  
  543. { Posts the file open dialog, gets a wave file name, and updates the sound
  544.   window to use it.
  545. }
  546. procedure TSoundWindow.CMFileOpen(var Msg: TMessage);
  547. const
  548.   DefExt = 'wav';
  549. var
  550.   OpenFN   : TOpenFileName;
  551.   Filter   : array [0..100] of Char;
  552.   FileName : TFilename;
  553.   WinDir   : array [0..145] of Char;
  554.   MyMenu   : HMenu;
  555. begin
  556.   GetWindowsDirectory(WinDir, SizeOf(WinDir));
  557.   SetCurDir(WinDir);
  558.   StrCopy(FileName, '');
  559.  
  560. { Set up a filter buffer to look for Wave files only.  Recall that filter
  561.   buffer is a set of string pairs, with the last one terminated by a
  562.   double-null.
  563. }
  564.   FillChar(Filter, SizeOf(Filter), #0);  { Set up for double null at end }
  565.   StrCopy(Filter, 'Wave Files');
  566.   StrCopy(@Filter[StrLen(Filter)+1], '*.wav');
  567.  
  568.   FillChar(OpenFN, SizeOf(TOpenFileName), #0);
  569.  
  570.   with OpenFN do
  571.   begin
  572.     hInstance     := HInstance;
  573.     hwndOwner     := HWindow;
  574.     lpstrDefExt   := DefExt;
  575.     lpstrFile     := ElementName;
  576.     lpstrFilter   := Filter;
  577.     lpstrFileTitle:= nil;     {Title not needed right now ... use full path }
  578.     flags         := ofn_FileMustExist;
  579.     lStructSize   := SizeOf(TOpenFileName);
  580.     nFilterIndex  := 1;       {Use first Filter String in lpstrFilter}
  581.     nMaxFile      := SizeOf(FileName);
  582.   end;
  583. { If a file is selected, turn the Play menu on, and update the sound
  584.   window to show the new file name.
  585. }
  586.   if GetOpenFileName(OpenFN) then
  587.   begin
  588.     MyMenu := GetMenu(HWindow);
  589.     EnableMenuItem(MyMenu, cm_SoundPlay, mf_Enabled);
  590.  
  591.     WaveLength := 0;
  592.     WaveRatio  := 0;
  593.     UpdateSoundWindow;
  594.   end;
  595. end;
  596.  
  597. { Responds to mm_MCINotify messages when mci_Play is complete.  If the
  598.   Stop/Close is from the thumb movement, then ignore it.  Otherwise,
  599.   kill the timer and reset the scroller.
  600. }
  601. procedure TSoundWindow.MciNotify(var Msg: TMessage);
  602. var
  603.   LoVal, HiVal: Integer;
  604. begin
  605.   if not FlushNotify then
  606.   begin               { Internal STOP/CLOSE, from thumb re-pos? }
  607.     if TimerGoing then
  608.     begin               { No, normal close. }
  609.       KillTimer(HWindow, Timer_Id);
  610. { Make sure the thumb is at the end. There could be some wm_Timer
  611.   messages on the queue when we kill it, thereby flushing wm_Timer's
  612.   from the message queue.
  613. }
  614.       SoundBar^.GetRange(LoVal, HiVal);
  615.       SoundBar^.SetPosition(HiVal);
  616.     end;
  617.  
  618.     StopWave;
  619.   end;
  620.   FlushNotify := False;  { Yes, so ignore the close. }
  621. end;
  622.  
  623. { Invalidates the client area of the Sound Window so that the
  624.   information display will get updated.
  625. }
  626. procedure TSoundWindow.UpdateSoundWindow;
  627. begin
  628.   InvalidateRect(HWindow, nil, True);
  629. end;
  630.  
  631. { Processes wm_Timer events.
  632. }
  633. procedure TSoundWindow.WMIdleStuff(var Msg: TMessage);
  634. begin
  635.   if not FlushNotify then
  636.   begin            { Internal STOP/CLOSE, from thumb re-pos? }
  637.     MciStatusParm.dwCallback := 0;     { No, normal close. }
  638.     MciStatusParm.dwItem     := mci_Status_Length;
  639.     mciSendCommand(DeviceID, mci_Status, mci_Status_Item,
  640.       Longint(@MciStatusParm));
  641.  
  642. { If the wavelength has changed, update the scroll bar numbers.
  643. }
  644.     if WaveLength <> MciStatusParm.dwReturn then
  645.     begin
  646.       UpdateSoundWindow;
  647.       WaveLength := MciStatusParm.dwReturn;
  648.     end;
  649.  
  650. { Compute the length and ratio and update SoundBar info.
  651. }
  652.     WaveRatio := Round((WaveLength / 32000) + 0.5);
  653.     SoundBar^.ScrollSetInfo(WaveRatio, WaveLength);
  654.     SoundBar^.SetRange(0, Round(WaveLength / WaveRatio));
  655.  
  656. { Update the current position.
  657. }
  658.     MciStatusParm.dwCallback := 0;
  659.     MciStatusParm.dwItem     := mci_Status_Position;
  660.     mciSendCommand(DeviceID, mci_Status, mci_Status_Item,
  661.       Longint(@MciStatusParm));
  662.  
  663.     SoundBar^.SetPosition(Round(MciStatusParm.dwReturn / WaveRatio));
  664.   end;
  665.  
  666.   FlushNotify := False;   { Yes, ignore this close. }
  667. end;
  668.  
  669.  
  670. { TDragApp Methods }
  671.  
  672. { Creates the application's main window.
  673. }
  674. procedure TSoundApp.InitMainWindow;
  675. begin
  676.   MainWindow := New(PSoundWindow, Init(nil, Application^.Name));
  677. end;
  678.  
  679. { Initializes this instance of the Sound Application.  Redefined
  680.   to load the accelerators.
  681. }
  682. procedure TSoundApp.InitInstance;
  683. begin
  684.   TApplication.InitInstance;
  685.   HAccTable := LoadAccelerators(HInstance, 'ACCELERATORS_1');
  686. end;
  687.  
  688.  
  689. { Main Program }
  690.  
  691. begin
  692.   App.Init(DemoTitle);
  693.   App.Run;
  694.   App.Done;
  695. end.
  696.  
  697.