MMWavOut.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:63k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1. {========================================================================}
  2. {=                (c) 1995-98 SwiftSoft Ronald Dittrich                 =}
  3. {========================================================================}
  4. {=                          All Rights Reserved                         =}
  5. {========================================================================}
  6. {=  D 01099 Dresden             = Fax.: +49(0)351-8037944               =}
  7. {=  Loewenstr.7a                = info@swiftsoft.de                     =}
  8. {========================================================================}
  9. {=  Actual versions on http://www.swiftsoft.de/index.html               =}
  10. {========================================================================}
  11. {=  This code is for reference purposes only and may not be copied or   =}
  12. {=  distributed in any format electronic or otherwise except one copy   =}
  13. {=  for backup purposes.                                                =}
  14. {=                                                                      =}
  15. {=  No Delphi Component Kit or Component individually or in a collection=}
  16. {=  subclassed or otherwise from the code in this unit, or associated   =}
  17. {=  .pas, .dfm, .dcu, .asm or .obj files may be sold or distributed     =}
  18. {=  without express permission from SwiftSoft.                          =}
  19. {=                                                                      =}
  20. {=  For more licence informations please refer to the associated        =}
  21. {=  HelpFile.                                                           =}
  22. {========================================================================}
  23. {=  $Date: 27.01.99 - 20:16:19 $                                        =}
  24. {========================================================================}
  25. unit MMWavOut;
  26. {$C FIXED PRELOAD PERMANENT}
  27. {$I COMPILER.INC}
  28. {.$DEFINE _MMDEBUG}
  29. interface
  30. uses
  31. {$IFDEF WIN32}
  32.   Windows,
  33. {$ELSE}
  34.   WinTypes,
  35.   WinProcs,
  36. {$ENDIF}
  37.   SysUtils,
  38.   Messages,
  39.   Classes,
  40.   Graphics,
  41.   Controls,
  42.   Forms,
  43.   Dialogs,
  44.   MMSystem,
  45.   MMUtils,
  46.   MMString,
  47.   MMObj,
  48.   MMDSPObj,
  49.   MMDSPMtr,
  50.   MMRegs,
  51.   MMPCMSup,
  52.   MMAlloc,
  53.   MMWaveIO
  54.   {$IFDEF _MMDEBUG}
  55.   ,MMDebug
  56.   {$ENDIF};
  57. {$IFDEF _MMDEBUG}
  58.    {$DEFINE _NUMERATE}
  59.    const
  60.         DEBUGLEVEL = 0; { 0,1,2 }
  61. {$ENDIF}
  62. const
  63.   {$IFDEF CBUILDER3} {$EXTERNALSYM MAXERRORLENGTH} {$ENDIF}
  64.   MAXERRORLENGTH   = 255;
  65.   {$IFDEF CBUILDER3} {$EXTERNALSYM MAXOUTBUFFERS} {$ENDIF}
  66.   MAXOUTBUFFERS    = 500;
  67.   {$IFDEF CBUILDER3} {$EXTERNALSYM MINBUFFERSIZE} {$ENDIF}
  68.   MINBUFFERSIZE    = 32;
  69.   {$IFDEF CBUILDER3} {$EXTERNALSYM MAXERRORLENGTH} {$ENDIF}
  70.   FIX_BUFFERS      : Boolean = True;
  71. type
  72.   EMMWaveOutError  = class(Exception);
  73.   TMMWaveOutStates = (wosClose, wosOpen, wosPlay, wosPause);
  74.   TMMWaveOutState  = set of TMMWaveOutStates;
  75.   { Pointers to waveOut headers }
  76.   TMMWaveOutHdrs   = array[0..MAXOUTBUFFERS-1] of PWaveHdr;
  77.   {$IFDEF WIN32}
  78.   {-- TMMWaveOutThread ---------------------------------------------------}
  79.   TMMWaveOutThread = class(TMMDSPThread)
  80.   private
  81.      procedure Execute; override;
  82.   end;
  83.   {$ENDIF}
  84.   {-- TMMWaveOut ---------------------------------------------------------}
  85.   TMMWaveOut = class(TMMCustomWaveOutComponent)
  86.   private
  87.     FHandle        : THandle;        { handle used for callback window    }
  88.     FDeviceID      : TMMDeviceID;    { WAVEOUT device ID                  }
  89.     FHWaveOut      : HWaveOut;       { Handle to output device            }
  90.     FState         : TMMWaveOutState;{ Current device state               }
  91.     FWaveOutHdrs   : TMMWaveOutHdrs; { WaveOut Headers and Buffers        }
  92.     FBufferOutIdx  : integer;        { the current Out Header/BufferIndex }
  93.     FCallbackMode  : TMMCBMode;      { use Window or Callback function    }
  94.     FError         : integer;        { Last WaveOut Error                 }
  95.     FNumdevs       : integer;      { Num. of output devices on system   }
  96.     FWaveOutCaps   : TWaveOutCaps;   { Stuff from WAVEOUTCAPS             }
  97.     FProductName   : String;         { the device Productname             }
  98.     FDriverVersion : integer;        { Specifies the driver version       }
  99.                                      { high-order byte is major version   }
  100.                                      { low-order byte is minor version    }
  101.     FInHandler     : integer;        { marks that we in any event handler }
  102.     FStarted       : Boolean;        { device is now started              }
  103.     FStopIt        : Boolean;        { we should stop playing if possible }
  104.     FCloseIt       : Boolean;        { we should close device if possible }
  105.     FStopping      : Boolean;        { we actually stop the device        }
  106.     FClosing       : Boolean;        { we actually close the device       }
  107.     FReseting      : Boolean;        { we actually reseting the device    }
  108.     FPosted        : Boolean;
  109.     FLooping       : Boolean;        { loop playing or not                }
  110.     FLoopCount     : Word;           { number of loops                    }
  111.     FLoopTempCount : integer;        { temp loop counter for playing      }
  112.     FLoopPos       : MM_int64;       { adjust for loop and GetPosition    }
  113.     FOldPosition   : MM_int64;       { the old play position before pause }
  114.     FLastPosition  : Cardinal;       { the last playback position         }
  115.     FWrapArrounds  : Cardinal;       { number of position wrap-arrounds   }
  116.     FWrapSize      : Cardinal;       { where has the position wrapped ?   }
  117.     FBytesPlayed   : MM_Int64;       { total bytes we have realy played   }
  118.     FMoreBuffers   : Boolean;        { more buffers to write ?            }
  119.     FNumBuffers    : integer;        { number of buffers for queue        }
  120.     FBuffersUsed   : integer;        { the real buffers we have in use    }
  121.     FBufferCounter : integer;        { buffer counter for buffers in use  }
  122.     FTimeFormat    : TMMTimeFormats; { the actual time format for Position}
  123.     FShowHourGlass : Boolean;
  124.     FEndingPosition: MM_int64;
  125.     FMapped        : Boolean;
  126.     FAllocator     : TMMAllocator;
  127.     {$IFDEF WIN32}
  128.     FPriority      : TThreadPriority;{ thread priority                    }
  129.     FThreadError   : Boolean;        { Error in Thread Handler            }
  130.     FOutThread     : TMMWaveOutThread;{ Output Thread for callback handling}
  131.     DataSection    : TRtlCriticalSection;{ CriticalSection Object         }
  132.     DataSectionOK  : Boolean;        { CriticalSection is prepared        }
  133.     FOutEvent      : THandle;        { event object for notify handling   }
  134.     FCloseEvent    : THandle;        { event object to close the device   }
  135.     FResetEvent    : THandle;        { event object to reset the device   }
  136.     {$ENDIF}
  137.     { Events }
  138.     FOnError       : TNotifyEvent;   { Error occured                      }
  139.     FOnBufferFilled: TMMBufferEvent; { Wave buffer filled event           }
  140.     FOnLooping     : TNotifyEvent;   { Wave was at end and is looped      }
  141.     FOnOpen        : TNotifyEvent;   { Wave Device succ. opened           }
  142.     FOnStart       : TNotifyEvent;   { Wave Device succ. started          }
  143.     FOnPause       : TNotifyEvent;   { Wave Device succ. paused           }
  144.     FOnRestart     : TNotifyEvent;   { Wave Device succ. restarted        }
  145.     FOnStop        : TNotifyEvent;   { Wave Device succ. stopped          }
  146.     FOnClose       : TNotifyEvent;   { Wave Device succ. closed           }
  147.     function  WaveOutErrorString(WError: integer): string;
  148.     procedure SetTimeFormat(aValue: TMMTimeFormats);
  149.     procedure SetLooping(aValue: Boolean);
  150.     procedure SetLoopCount(aValue: Word);
  151.     function  GetSamplePosition: Cardinal;
  152.     function  GetInternalPosition: int64;
  153.     function  GetPositionHigh: Cardinal;
  154.     procedure WaveOutHandler(var Msg: TMessage);
  155.     procedure AllocWaveHeader(var lpWaveHdr: PWaveHdr);
  156.     procedure FreeWaveHeaders;
  157.     procedure PrepareWaveHeader(lpWaveHdr: PWaveHdr);
  158.     procedure UnPrepareWaveHeaders;
  159.     function  LoadWaveHeader(lpWaveHdr: PWaveHdr): Longint;
  160.     procedure QueueWaveHeader(lpWaveHdr: PWaveHdr);
  161.     procedure ProcessWaveHeader(lpWaveHdr: PWaveHdr);
  162.     {$IFDEF WIN32}
  163.     procedure SetPriority(aValue: TThreadPriority);
  164.     procedure InitThread;
  165.     procedure DoneThread;
  166.     procedure CloseEvents;
  167.     {$ENDIF}
  168.     procedure InitCritical;
  169.     procedure EnterCritical;
  170.     procedure LeaveCritical;
  171.     procedure DoneCritical;
  172.     procedure DoOpened;
  173.     procedure DoClosed;
  174.     procedure DoStarted;
  175.     procedure DoPaused;
  176.     procedure DoRestarted;
  177.     procedure DoStopped;
  178.     procedure DoBufferFilled(lpwh: PWaveHdr);
  179.     procedure DoBufferReady(lpwh: PWaveHdr);
  180.   protected
  181.     procedure SetPWaveFormat(aValue: PWaveFormatEx); override;
  182.     procedure SetBufferSize(aValue: Longint); override;
  183.     function  GetBufferSize: Longint; override;
  184.     procedure SetNumBuffers(aValue: integer); override;
  185.     function  GetNumBuffers: integer; override;
  186.     procedure SetDeviceID(aValue: TMMDeviceID); override;
  187.     function  GetDeviceID: TMMDeviceID; override;
  188.     procedure SetProductName(aValue: string); override;
  189.     function  GetProductName: string; override;
  190.     procedure SetCallBackMode(aValue: TMMCBMode); override;
  191.     function  GetCallBackMode: TMMCBMode; override;
  192.     function  GetPosition: MM_int64; override;
  193.     procedure Error(Msg: string); virtual;
  194.   public
  195.     procedure Opened; override;
  196.     procedure Closed; override;
  197.     procedure Started; override;
  198.     procedure Paused; override;
  199.     procedure Restarted; override;
  200.     procedure Stopped; override;
  201.     procedure BufferReady(lpwh: PWaveHdr); override;
  202.     procedure BufferLoad(lpwh: PWaveHdr; var MoreBuffers: Boolean); override;
  203.     constructor Create(AOwner: TComponent); override;
  204.     destructor Destroy; override;
  205.     procedure Open; override;
  206.     procedure Close; override;
  207.     procedure Reset; override;
  208.     procedure Start; override;
  209.     procedure Pause; override;
  210.     procedure Restart; override;
  211.     procedure Stop; override;
  212.     function  QueryDevice(aDeviceID: TMMDeviceID; pwfx: PWaveFormatEx): Boolean;
  213.     {$IFDEF WIN32}
  214.     { maybe you must syncronize anything if UseThread = True ? }
  215.     procedure SynchronizeVCL(VCLProc: TThreadMethod);
  216.     {$ENDIF}
  217.     property Handle: HWaveOut read FHWaveOut;
  218.     property WaveOutCaps: TWaveOutCaps read FWaveOutCaps;
  219.     property Numdevs: integer read FNumdevs;
  220.     property State: TMMWaveOutState read FState;
  221.     property DriverVersion: integer read FDriverVersion;
  222.     property BytesPlayed: MM_Int64 read FBytesPlayed;
  223.     property Position: MM_int64 read GetPosition;
  224.     property PositionHigh: Cardinal read GetPositionHigh;
  225.     property EndingPosition: MM_int64 read FEndingPosition;
  226.     property BufferIndex: integer read FBufferOutIdx;
  227.     {$IFNDEF CBUILDER3}
  228.     property WaveHdrs: TMMWaveOutHdrs read FWaveOutHdrs;
  229.     {$ENDIF}
  230.   published
  231.     { Events }
  232.     property OnError: TNotifyEvent read FOnError write FOnError;
  233.     property OnOpen: TNotifyEvent read FOnOpen write FOnOpen;
  234.     property OnStart: TNotifyEvent read FOnStart write FOnStart;
  235.     property OnPause: TNotifyEvent read FOnPause write FOnPause;
  236.     property OnRestart: TNotifyEvent read FOnRestart write FOnRestart;
  237.     property OnStop: TNotifyEvent read FOnStop write FOnStop;
  238.     property OnClose: TNotifyEvent read FOnClose write FOnClose;
  239.     property OnLooping: TNotifyEvent read FOnLooping write FOnLooping;
  240.     property OnBufferFilled: TMMBufferEvent read FOnBufferFilled write FOnBufferFilled;
  241.     property OnBufferReady;
  242.     property OnBufferLoad;
  243.     property Input;
  244.     property Output;
  245.     property BufferSize;
  246.     property NumBuffers;
  247.     property DeviceID;
  248.     property ProductName;
  249.     property CallBackMode;
  250.     property TimeFormat: TMMTimeFormats read FTimeFormat write SetTimeFormat default tfByte;
  251.     property Looping: Boolean read FLooping write SetLooping default False;
  252.     property LoopCount: Word read FLoopCount write SetLoopCount default 0;
  253.     property ShowHourGlass: Boolean read FShowHourGlass write FShowHourGlass default True;
  254.     property Mapped: Boolean read FMapped write FMapped default False;
  255.     {$IFDEF WIN32}
  256.     property Priority: TThreadPriority read FPriority write SetPriority default tpHigher;
  257.     {$ENDIF}
  258.   end;
  259. function WaveOutGetDeviceName(DeviceID: TMMDeviceID): String;
  260. function WaveOutReady(DeviceID: TMMDeviceID): Boolean;
  261. implementation
  262. {$DEFINE _USE_CALLBACK}
  263. uses consts;
  264. const
  265.      MM_WOM_STOP = MM_USER+1;
  266. procedure WaveOutFunc(hWaveOut:HWaveOut;wMsg:UINT;dwInstance,dwParam1,dwParam2:Longint);
  267. export;{$IFDEF WIN32}stdcall;{$ENDIF}forward;
  268. {-------------------------------------------------------------------------}
  269. procedure DebugStr(Level: integer; s: String);
  270. begin
  271. {$IFDEF _MMDEBUG}
  272.    if (s <> ' ') then s := 'WaveOut: '+s;
  273.    DB_WriteStrLn(Level,s);
  274. {$ENDIF}
  275. end;
  276. {-------------------------------------------------------------------------}
  277. function WaveOutReady(DeviceID: TMMDeviceID): Boolean;
  278. var
  279.    OutHandle: HWAVEOUT;
  280.    Error: MMRESULT;
  281.    wfx: TWaveFormatEx;
  282. begin
  283.    Result := False;
  284.    if (DeviceID < integer(waveOutGetNumDevs)) and (DeviceID >= integer(WAVE_MAPPER)) then
  285.    try
  286.       OutHandle := 0;
  287.       pcmBuildWaveHeader(@wfx, 8, 1, 22050);
  288.       {$IFDEF WIN32}
  289.       Error := WaveOutOpen(@OutHandle, DeviceId, MMSystem.PWaveFormatEx(@wfx), 0, 0, CALLBACK_NULL);
  290.       {$ELSE}
  291.       Error := WaveOutOpen(@OutHandle, DeviceId, Pointer(@wfx), 0, 0, CALLBACK_NULL);
  292.       {$ENDIF}
  293.       if (Error = MMSYSERR_NOERROR) then
  294.       begin
  295.          Result := True;
  296.       end;
  297.    finally
  298.       if (OutHandle <> 0) then WaveOutClose(OutHandle);
  299.    end;
  300. end;
  301. {-------------------------------------------------------------------------}
  302. function WaveOutGetDeviceName(DeviceID: TMMDeviceID): String;
  303. var
  304.    Caps   : TWaveOutCaps;
  305. begin
  306.    Result := '';
  307.    if (DeviceID < integer(waveOutGetNumDevs)) and (DeviceID >= integer(WAVE_MAPPER)) then
  308.    begin
  309.       { Set the name and other WAVEOUTCAPS properties to match the ID }
  310.       if waveOutGetDevCaps(DeviceID, @Caps, sizeof(TWaveOutCaps)) = 0 then
  311.          Result := StrPas(Caps.szPname);
  312.    end;
  313. end;
  314. {== TMMWaveOut ===========================================================}
  315. constructor TMMWaveOut.Create(AOwner: TComponent);
  316. begin
  317.    inherited Create(AOwner);
  318.    { Set defaults }
  319.    FHWaveOut := 0;
  320.    FHandle   := 0;
  321.    FState := [wosClose];
  322.    FError := 0;
  323.    FNumBuffers := 10;
  324.    FProductName := '';
  325.    FDriverVersion := 0;
  326.    FBytesPlayed := 0;
  327.    FTimeFormat := tfByte;
  328.    FMoreBuffers := False;
  329.    FLooping := False;
  330.    FLoopCount := 0;
  331.    FCallBackMode := cmWindow;
  332.    FClosing := False;
  333.    FReseting := False;
  334.    FStopping := False;
  335.    FPosted := False;
  336.    FBufferOutIdx := 0;
  337.    FShowHourGlass := True;
  338.    FEndingPosition:= 0;
  339.    {$IFDEF WIN32}
  340.    FPriority := tpHigher;
  341.    {$ENDIF}
  342.    FAllocator := TMMAllocator.Create;
  343.    
  344.    {clear all pointers to Nil }
  345.    FillChar(FWaveOutHdrs, sizeOf(TMMWaveOutHdrs), 0);
  346.    FNumDevs := waveOutGetNumDevs;
  347.    SetDeviceID(0);
  348.    {$IFDEF WIN32}
  349.    DataSectionOK := False;
  350.    {$ENDIF}
  351.    ErrorCode := ComponentRegistered(InitCode, Self, ClassName);
  352.    if (ErrorCode <> 0) then RegisterFailed(InitCode, Self , ClassName);
  353. end;
  354. {-- TMMWaveOut -----------------------------------------------------------}
  355. destructor TMMWaveOut.Destroy;
  356. begin
  357.    { Close the device if it's open }
  358.    if (FHWaveOut <> 0) then Close;
  359.    { Destroy the window for callback notification }
  360.    if (FHandle <> 0) then DeallocateHwnd(FHandle);
  361.    if (FAllocator <> nil) then FAllocator.Free;
  362.    inherited Destroy;
  363. end;
  364. {-- TMMWaveOut -----------------------------------------------------------}
  365. procedure TMMWaveOut.Error(Msg: string);
  366. begin
  367.    if assigned(FOnError) then FOnError(Self);
  368.    {$IFDEF _MMDEBUG}
  369.    DebugStr(0,Msg);
  370.    {$ENDIF}
  371.    raise EMMWaveOutError.Create(Msg);
  372. end;
  373. {-- TMMWaveOut -----------------------------------------------------------}
  374. { Allocate memory for the WaveOut header and buffer }
  375. procedure TMMWaveOut.AllocWaveHeader(var lpWaveHdr: PWaveHdr);
  376. begin
  377.    if (lpWaveHdr = Nil) then
  378.    begin
  379.       { set up a wave header for playing and lock. }
  380.       lpWaveHdr := FAllocator.AllocBuffer(GPTR, SizeOf(TMMWaveHdr) + BufferSize);
  381.       if lpWaveHdr = nil then
  382.          Error(LoadResStr(IDS_HEADERMEMERROR));
  383.       { Data occurs directly after the header }
  384.       lpWaveHdr^.lpData         := PChar(lpWaveHdr) + sizeOf(TMMWaveHdr);
  385.       lpWaveHdr^.dwBufferLength := BufferSize;
  386.       lpWaveHdr^.dwBytesRecorded:= 0;
  387.       lpWaveHdr^.dwFlags        := 0;
  388.       lpWaveHdr^.dwLoops        := 0;
  389.       lpWaveHdr^.dwUser         := 0;
  390.       lpWaveHdr^.lpNext         := nil;
  391.    end;
  392. end;
  393. {-- TMMWaveOut -----------------------------------------------------------}
  394. procedure TMMWaveOut.FreeWaveHeaders;
  395. Var
  396.    i: integer;
  397. begin
  398.    for i := 0 to FNumBuffers-1 do
  399.    begin
  400.       { unlock and free memory for WaveOutHdr }
  401.       if FWaveOutHdrs[i] <> NIL then
  402.       begin
  403.          FAllocator.FreeBuffer(Pointer(FWaveOutHdrs[i]));
  404.          FWaveOutHdrs[i] := Nil;
  405.       end;
  406.    end;
  407. end;
  408. {-- TMMWaveOut -----------------------------------------------------------}
  409. procedure TMMWaveOut.SetLooping(aValue: Boolean);
  410. begin
  411.    if (aValue <> FLooping) then
  412.    begin
  413.       FLooping := aValue;
  414.       FLoopTempCount := FLoopCount;
  415.    end;
  416.    {$IFDEF WIN32}
  417.    {$IFDEF TRIAL}
  418.    {$DEFINE _HACK1}
  419.    {$I MMHACK.INC}
  420.    {$ENDIF}
  421.    {$ENDIF}
  422. end;
  423. {-- TMMWaveOut -----------------------------------------------------------}
  424. procedure TMMWaveOut.SetLoopCount(aValue: Word);
  425. begin
  426.    if (aValue <> FLoopCount) then
  427.    begin
  428.       FLoopCount := aValue;
  429.       FLoopTempCount := FLoopCount;
  430.    end;
  431. end;
  432. {-- TMMWaveOut -----------------------------------------------------------}
  433. function TMMWaveOut.WaveOutErrorString(WError: integer): String;
  434. Var
  435.    errorDesc: PChar;
  436. begin
  437.    { Convert the numeric return code from an MMSYSTEM function to a string }
  438.    errorDesc := Nil;
  439.    try
  440.       errorDesc := StrAlloc(MAXERRORLENGTH);
  441.       if waveOutGetErrorText(WError, errorDesc, MAXERRORLENGTH) = 0 then
  442.          Result := StrPas(errorDesc)
  443.       else
  444.          Result := LoadResStr(IDS_ERROROUTOFRANGE);
  445.    finally
  446.       StrDispose(errorDesc);
  447.    end;
  448. end;
  449. {-- TMMWaveOut -----------------------------------------------------------}
  450. Procedure TMMWaveOut.SetDeviceID(aValue: TMMDeviceID);
  451. begin
  452.    if (wosOpen in FState) then
  453.        Error(LoadResStr(IDS_PROPERTYOPEN));
  454.    FProductName := LoadResStr(IDS_WONODEVICE);
  455.    FDriverVersion := 0;
  456.    if (FNumDevs > 0) and (aValue >= MapperId) and (aValue < FNumDevs) then
  457.    begin
  458.       { Set the name and other WAVEOUTCAPS properties to match the ID }
  459.       FError := waveOutGetDevCaps(aValue, @FWaveOutCaps, sizeof(TWaveOutCaps));
  460.       if FError = 0 then
  461.       with FWaveOutCaps do
  462.       begin
  463.          FProductName := StrPas(szPname);
  464.          FDriverVersion := vDriverVersion;
  465.       end;
  466.    end;
  467.    { set the new device }
  468.    FDeviceID := aValue;
  469.    if (aValue < MapperId) or (aValue >= FNumDevs) then
  470.        FDeviceID := InvalidID;
  471.    {$IFDEF WIN32}
  472.    {$IFDEF TRIAL}
  473.    {$DEFINE _HACK1}
  474.    {$I MMHACK.INC}
  475.    {$ENDIF}
  476.    {$ENDIF}
  477. end;
  478. {-- TMMWaveOut -----------------------------------------------------------}
  479. function TMMWaveOut.GetDeviceID: TMMDeviceID;
  480. begin
  481.    Result := FDevicEID;
  482. end;
  483. {-- TMMWaveOut -----------------------------------------------------------}
  484. procedure TMMWaveOut.SetProductName(aValue: String);
  485. begin
  486.    ;
  487. end;
  488. {-- TMMWaveOut -----------------------------------------------------------}
  489. function TMMWaveOut.GetProductName: String;
  490. begin
  491.    Result := FProductName;
  492. end;
  493. {-- TMMWaveOut -----------------------------------------------------------}
  494. function TMMWaveOut.QueryDevice(aDeviceID: TMMDeviceID; pwfx: PWaveFormatEx): Boolean;
  495. Var
  496.    aHandle: HWaveOut;
  497. begin
  498.    if (aDeviceID < FNumDevs) and (aDeviceID >= MapperID) and (pwfx <> nil) then
  499.    begin
  500.       { query the Wave output device. }
  501.       Result := WaveOutOpen(@aHandle,
  502.                             aDeviceId,
  503.                             Pointer(pwfx),
  504.                             0, 0,
  505.                             WAVE_FORMAT_QUERY) = 0;
  506.    end
  507.    else Result := False;
  508. end;
  509. {-- TMMWaveOut ------------------------------------------------------------}
  510. procedure TMMWaveOut.SetTimeFormat(aValue: TMMTimeFormats);
  511. begin
  512.    if (aValue <> FTimeFormat) then
  513.    begin
  514.       FTimeFormat := aValue;
  515.    end;
  516. end;
  517. {-- TMMWaveOut ------------------------------------------------------------}
  518. function TMMWaveOut.GetSamplePosition: Cardinal;
  519. Var
  520.    MMTime: TMMTime;
  521. begin
  522.    Result := 0;
  523.    if (wosOpen in FState) then
  524.    begin
  525.       MMTime.wType := Time_Samples;
  526.       FError := WaveOutGetPosition(FHWaveOut, @MMTime, SizeOf(TMMTime));
  527.       if (FError <> 0) or (MMTime.wType <> Time_Samples) then
  528.       begin
  529.          MMTime.wType := Time_Bytes;
  530.          FError := WaveOutGetPosition(FHWaveOut, @MMTime, SizeOf(TMMTime));
  531.          if (FError <> 0) then
  532.              Error('WaveOutGetPosition:'#10#13+WaveOutErrorString(FError));
  533.          MMTime.Sample := wioBytesToSamples(PWaveFormat,MMTime.cb);
  534.       end;
  535.       Result := MMTime.Sample;
  536.       {asm
  537.          mov   eax, $FFFF0000
  538.          add   Result, eax
  539.       end;}
  540.    end;
  541. end;
  542. {-- TMMWaveOut ------------------------------------------------------------}
  543. function TMMWaveOut.GetInternalPosition: Int64;
  544. var
  545.    Samples,Pos: int64;
  546.    S: Cardinal;
  547.    WrapSize: int64;
  548. begin
  549.    Result := 0;
  550.    if (wosOpen in FState) and (PWaveFormat <> Nil) and not FCloseIt then
  551.    begin
  552.       { adjust if Looped or FullDuplex }
  553.       S := GetSamplePosition;
  554.       {$IFDEF WIN32}
  555.       asm
  556.          mov   eax, S
  557.          mov   dword ptr Pos[0], eax
  558.          xor   eax, eax
  559.          mov   dword ptr Pos[4], eax
  560.          mov   eax, Self
  561.          mov   eax, TMMWaveOut(eax).FWrapSize
  562.          mov   dword ptr WrapSize[0], eax
  563.          xor   eax, eax
  564.          mov   dword ptr WrapSize[4], eax
  565.       end;
  566.       Samples := (FWrapArrounds*WrapSize)+(Pos+FOldPosition)-FLoopPos;
  567.       {$ELSE}
  568.       Samples := (S+FOldPosition)-FLoopPos;
  569.       {$ENDIF}
  570.       case FTimeFormat of
  571.            tfMilliSecond: Result := wioSamplesToTime64(PWaveFormat,Samples);
  572.            tfByte       : Result := wioSamplesToBytes64(PWaveFormat,Samples);
  573.            tfSample     : Result := Samples;
  574.       end;
  575.    end;
  576. end;
  577. {-- TMMWaveOut ------------------------------------------------------------}
  578. function TMMWaveOut.GetPosition: MM_int64;
  579. {$IFNDEF DELPHI4}
  580. var
  581.    Temp: TLargeInteger;
  582. {$ENDIF}
  583. begin
  584.    {$IFDEF DELPHI4}
  585.    Result := GetInternalPosition;
  586.    {$ELSE}
  587.    Temp.QuadPart := GetInternalPosition;
  588.    Result := Temp.LowPart;
  589.    {$ENDIF}
  590. end;
  591. {-- TMMWaveOut ------------------------------------------------------------}
  592. function TMMWaveOut.GetPositionHigh: Cardinal;
  593. {$IFNDEF DELPHI4}
  594. var
  595.    Temp: TLargeInteger;
  596. {$ENDIF}
  597. begin
  598.    {$IFDEF DELPHI4}
  599.    Result := (GetInternalPosition shr 32);
  600.    {$ELSE}
  601.    Temp.QuadPart := GetInternalPosition;
  602.    Result := Temp.HighPart;
  603.    {$ENDIF}
  604. end;
  605. {-- TMMWaveOut -----------------------------------------------------------}
  606. Procedure TMMWaveOut.SetCallBackMode(aValue: TMMCBMode);
  607. begin
  608.    if (wosOpen in FState) then
  609.       Error(LoadResStr(IDS_PROPERTYOPEN));
  610.    if (aValue <> FCallBackMode) then
  611.    begin
  612.       if (aValue = cmCallBack) then
  613.       begin
  614.          {$IFDEF WIN32}
  615.          if not _Win95_ then
  616.          {$ENDIF}
  617.          begin
  618.             Application.MessageBox('"CallBacks" are called at interrupt time !'#10#13+
  619.                                    'This is currently only supported under Windows 95',
  620.                                    'TMMWaveOut', MB_OK);
  621.             exit;
  622.          end;
  623.       end;
  624.       FCallBackMode := aValue;
  625.    end;
  626.    {$IFDEF WIN32}
  627.    {$IFDEF TRIAL}
  628.    {$DEFINE _HACK3}
  629.    {$I MMHACK.INC}
  630.    {$ENDIF}
  631.    {$ENDIF}
  632. end;
  633. {-- TMMWaveOut -----------------------------------------------------------}
  634. function TMMWaveOut.GetCallBackMode: TMMCBMode;
  635. begin
  636.    Result := FCallBackMode;
  637. end;
  638. {-- TMMWaveOut -----------------------------------------------------------}
  639. Procedure TMMWaveOut.SetNumBuffers(aValue: integer);
  640. begin
  641.    if (aValue <> FNumBuffers) AND (aValue > 1) then
  642.    begin
  643.       if (wosOpen in FState) then
  644.           Error(LoadResStr(IDS_PROPERTYOPEN));
  645.       FNumBuffers := Min(aValue,MAXOUTBUFFERS);
  646.    end;
  647. end;
  648. {-- TMMWaveOut -----------------------------------------------------------}
  649. function TMMWaveOut.GetNumBuffers: integer;
  650. begin
  651.    Result := FNumBuffers;
  652. end;
  653. {-- TMMWaveOut -----------------------------------------------------------}
  654. Procedure TMMWaveOut.SetPWaveFormat(aValue: PWaveFormatEx);
  655. begin
  656.    { stop and close the device }
  657.    Close;
  658.    inherited SetPWaveFormat(aValue);
  659. end;
  660. {-- TMMWaveOut -----------------------------------------------------------}
  661. Procedure TMMWaveOut.SetBufferSize(aValue: Longint);
  662. begin
  663.    if (aValue <> inherited GetBufferSize) then
  664.    begin
  665.       if (wosOpen in FState) then
  666.           Error(LoadResStr(IDS_PROPERTYOPEN));
  667.       if assigned(FAllocator) then
  668.          FAllocator.Discard;
  669.       inherited SetBufferSize(Max(aValue,MINBUFFERSIZE));
  670.    end;
  671. end;
  672. {-- TMMWaveOut -----------------------------------------------------------}
  673. function TMMWaveOut.GetBufferSize: Longint;
  674. begin
  675.    Result := inherited GetBufferSize;
  676. end;
  677. {-- TMMWaveOut -----------------------------------------------------------}
  678. Procedure TMMWaveOut.PrepareWaveHeader(lpWaveHdr: PWaveHdr);
  679. begin
  680.    if lpWaveHdr <> Nil then
  681.    begin
  682.       { Prepare waveform header for playing }
  683.       WaveOutPrepareHeader(FHWaveOut, lpWaveHdr, sizeOf(TWaveHdr));
  684.    end;
  685. end;
  686. {-- TMMWaveOut -----------------------------------------------------------}
  687. Procedure TMMWaveOut.UnPrepareWaveHeaders;
  688. Var
  689.    i: integer;
  690.    TimeOut: Longint;
  691. begin
  692.    {$IFDEF _MMDEBUG}
  693.    DebugStr(1,' ');
  694.    {$ENDIF}
  695.    for i := 0 to FBuffersUsed-1 do
  696.    begin
  697.       if (FWaveOutHdrs[i] <> Nil) then
  698.       begin
  699.          TimeOut := 65000;
  700.          { wait until the buffer is marked as done }
  701.          repeat
  702.              dec(TimeOut);
  703.          until (FWaveOutHdrs[i]^.dwFlags and WHDR_DONE = WHDR_DONE) or (TimeOut = 0);
  704.          { mark buffer as done }
  705.          if (TimeOut = 0) then FWaveOutHdrs[i]^.dwFlags := WHDR_DONE;
  706.          { unprepare buffer }
  707.          WaveOutUnprepareHeader(FHWaveOut, FWaveOutHdrs[i], sizeOf(TWAVEHDR));
  708.          {$IFDEF _MMDEBUG}
  709.          DebugStr(1,'UnprepareHeader '+IntToStr(i));
  710.          {$ENDIF}
  711.       end;
  712.    end;
  713.    {$IFDEF _MMDEBUG}
  714.    DebugStr(1,' ');
  715.    {$ENDIF}
  716. end;
  717. {-- TMMWaveOut -----------------------------------------------------------}
  718. Function TMMWaveOut.LoadWaveHeader(lpWaveHdr: PWaveHdr): Longint;
  719. begin
  720.    Result := 0;
  721.    FMoreBuffers := False;
  722.    if (lpWaveHdr <> nil) then
  723.    begin
  724.       {$IFDEF _MMDEBUG}
  725.       DebugStr(2,'Try to load Buffer '+IntToStr(lpWaveHdr^.dwUser));
  726.       {$ENDIF}
  727.       BufferLoad(lpWaveHdr, FMoreBuffers);
  728.       Result := lpWaveHdr^.dwBytesRecorded;
  729.       if Result <= 0 then FMoreBuffers := False;
  730.       {$IFDEF _MMDEBUG}
  731.       DebugStr(2,'Buffer '+IntToStr(lpWaveHdr^.dwUser)+' loaded');
  732.       {$ENDIF}
  733.    end;
  734. end;
  735. {-- TMMWaveOut -----------------------------------------------------------}
  736. procedure TMMWaveOut.QueueWaveHeader(lpWaveHdr: PWaveHdr);
  737. begin
  738.    { this is the chance to modify the data in the buffer !!! }
  739.    DoBufferFilled(lpWaveHdr);
  740.    if not FStopping then
  741.    begin
  742.       if not FIX_BUFFERS then
  743.          WaveOutPrepareHeader(FHWaveOut, lpWaveHdr, sizeOf(TWaveHdr))
  744.       else
  745.          { reset flags field (remove WHDR_DONE attribute) }
  746.          lpWaveHdr^.dwFlags := lpWaveHdr^.dwFlags and not WHDR_DONE or WHDR_PREPARED;
  747.       {$IFDEF _MMDEBUG}
  748.       DebugStr(2,'Try to send Wave-Header '+IntToStr(lpWaveHdr^.dwUser)+' to driver');
  749.       {$ENDIF}
  750.       PMMWaveHdr(lpWaveHdr)^.dwUser2 := lpWaveHdr^.dwBytesRecorded;
  751.       { now queue the buffer for output }
  752.       FError := WaveOutWrite(FHWaveOut,
  753.                              lpWaveHdr,
  754.                              SizeOf(TWAVEHDR));
  755.       if FError <> 0 then
  756.          Error('WaveOutWrite:'#10#13+WaveOutErrorString(FError));
  757.       { BUG-FIX for NT 4.0 SP4, it does set dwBytesRecorded to zero }
  758.       lpWaveHdr^.dwBytesRecorded := PMMWaveHdr(lpWaveHdr)^.dwUser2;
  759.       EnterCritical;
  760.       inc(FBufferCounter);
  761.       LeaveCritical;
  762.       {$IFDEF _MMDEBUG}
  763.       DebugStr(2,'Wave-Header '+IntToStr(lpWaveHdr^.dwUser)+' queued');
  764.       {$ENDIF}
  765.    end;
  766. end;
  767. {$IFDEF WIN32}
  768. {-- TMMWaveOut -----------------------------------------------------------}
  769. procedure TMMWaveOut.SynchronizeVCL(VCLProc: TThreadMethod);
  770. begin
  771.    if (FCallBackMode = cmThread) and (FOutEvent <> 0) then
  772.    begin
  773.       FOutThread.Synchronize(VCLProc);
  774.    end
  775.    else VCLProc;
  776. end;
  777. {-- TMMWaveOut -----------------------------------------------------------}
  778. procedure TMMWaveOut.SetPriority(aValue: TThreadPriority);
  779. begin
  780.    FPriority := aValue;
  781.    if (FOutThread <> nil) then
  782.        FOutThread.Priority := FPriority;
  783. end;
  784. {-- TMMWaveOut -----------------------------------------------------------}
  785. procedure TMMWaveOut.InitThread;
  786. begin
  787.    if (FCallBackMode = cmThread) then
  788.    begin
  789.       EnterCritical;
  790.       try
  791.          FThreadError := False;
  792.          { create event objects }
  793.          FOutEvent    := CreateEvent(nil, False, False, nil);
  794.          FCloseEvent  := CreateEvent(nil, False, False, nil);
  795.          FResetEvent  := CreateEvent(nil, True, False, nil);
  796.          { create the output thread }
  797.          FOutThread := TMMWaveOutThread.CreateSuspended(Self);
  798.          if (FOutThread = nil) then
  799.              Error('WaveOut:'#10#13+LoadResStr(IDS_THREADERROR));
  800.          FOutThread.FreeOnTerminate := True;
  801.          FOutThread.Resume;
  802.          {$IFDEF _MMDEBUG}
  803.          DebugStr(0,'Wait for Thread start...');
  804.          {$ENDIF}
  805.          { Wait for it to start... }
  806.          if WaitForSingleObject(FOutEvent, 5000) <> WAIT_OBJECT_0 then
  807.             Error('WaveOut:'#10#13+LoadResStr(IDS_THREADERROR));
  808.          {$IFDEF _MMDEBUG}
  809.          DebugStr(0,'Thread Started');
  810.          {$ENDIF}
  811.       finally
  812.         LeaveCritical;
  813.       end;
  814.    end;
  815. end;
  816. {-- TMMWaveOut -----------------------------------------------------------}
  817. procedure TMMWaveOut.DoneThread;
  818. begin
  819.    if (FCallBackMode = cmThread) and (FOutEvent <> 0) and not FThreadError then
  820.    begin
  821.       { Force the output thread to close... }
  822.       SetEvent(FCloseEvent);
  823.       { ...and wait for it to die }
  824.       WaitForSingleObject(FOutEvent, 5000);
  825.       { close all events and remove critical section }
  826.       CloseEvents;
  827.       {$IFDEF _MMDEBUG}
  828.       DebugStr(0,'Thread Terminated');
  829.       {$ENDIF}
  830.    end;
  831. end;
  832. {-- TMMWaveOut -----------------------------------------------------------}
  833. procedure TMMWaveOut.CloseEvents;
  834. begin
  835.    if (FOutEvent <> 0) then
  836.    begin
  837.       { release events }
  838.       CloseHandle(FOutEvent);
  839.       CloseHandle(FCloseEvent);
  840.       CloseHandle(FResetEvent);
  841.       FOutEvent := 0;
  842.       FCloseEvent := 0;
  843.       FResetEvent := 0;
  844.       { Free the critical section }
  845.       DoneCritical;
  846.    end;
  847. end;
  848. {$ENDIF}
  849. {-- TMMWaveOut -----------------------------------------------------------}
  850. procedure TMMWaveOut.InitCritical;
  851. begin
  852.    {$IFDEF WIN32}
  853.    { create critical section object }
  854.    FillChar(DataSection, SizeOf(DataSection), 0);
  855.    InitializeCriticalSection(DataSection);
  856.    DataSectionOK := True;
  857.    {$ENDIF}
  858. end;
  859. {-- TMMWaveOut -----------------------------------------------------------}
  860. procedure TMMWaveOut.EnterCritical;
  861. begin
  862.    {$IFDEF WIN32}
  863.    if DataSectionOK then
  864.       EnterCriticalSection(DataSection);
  865.    {$ENDIF}
  866. end;
  867. {-- TMMWaveOut -----------------------------------------------------------}
  868. procedure TMMWaveOut.LeaveCritical;
  869. begin
  870.    {$IFDEF WIN32}
  871.    if DataSectionOK then
  872.       LeaveCriticalSection(DataSection);
  873.    {$ENDIF}
  874. end;
  875. {-- TMMWaveOut -----------------------------------------------------------}
  876. procedure TMMWaveOut.DoneCritical;
  877. begin
  878.    {$IFDEF WIN32}
  879.    if DataSectionOK then
  880.    begin
  881.       DataSectionOK := False;
  882.       DeleteCriticalSection(DataSection);
  883.    end;
  884.    {$ENDIF}
  885. end;
  886. {-- TMMWaveOut -----------------------------------------------------------}
  887. Procedure TMMWaveOut.Open;
  888. var
  889.    TimeOut: integer;
  890.    dwFlags: Longint;
  891. begin
  892.    if (FNumDevs = 0) then
  893.       Error(LoadResStr(IDS_WONODEVICE));
  894.    if (FDeviceID = InvalidId) then
  895.       Error(LoadResStr(IDS_INVALIDDEVICEID));
  896.    if (PWaveFormat = Nil) then
  897.        Error('WaveOutOpen:'#10#13+LoadResStr(IDS_NOFORMAT));
  898.    if (wosOpen in FState) then Close;
  899.    if (Not(wosOpen in FState)) and not FClosing then
  900.    begin
  901.       {$IFDEF _MMDEBUG}
  902.       //DB_Clear;
  903.       DB_WriteStrLn(0,'-----------------');
  904.       {$ENDIF}
  905.       FClosing := False;
  906.       FReseting := False;
  907.       FStopping := False;
  908.       FPosted   := False;
  909.       try
  910.          if not QueryDevice(FDeviceID, PWaveFormat) then
  911.             Error('WaveOutOpen:'#10#13+LoadResStr(IDS_CANTPLAY));
  912.          { Create the window for callback notification }
  913.          if (FHandle = 0) then FHandle := AllocateHwnd(WaveOutHandler);
  914.          {$IFDEF _MMDEBUG}
  915.          DebugStr(0,'Call inherited...');
  916.          {$ENDIF}
  917.          FHWaveOut := 0;
  918.          FCloseIt := False;
  919.          inherited Opened;
  920.          {$IFDEF _MMDEBUG}
  921.          DebugSTr(0,'Try to open device...');
  922.          {$ENDIF}
  923.          { create critical section object }
  924.          InitCritical;
  925.          {$IFDEF WIN32}
  926.          if (FCallBackMode = cmThread) then InitThread;
  927.          {$ENDIF}
  928.          TimeOut := 500;
  929.          {$IFDEF WIN32}
  930.          if FMapped and (FDeviceID >= 0) then
  931.             dwFlags := WAVE_MAPPED
  932.          else
  933.          {$ENDIF}
  934.             dwFlags := 0;
  935.          {$IFDEF _USE_CALLBACK}
  936.          if _Win9x_ or _WinNT4_ then
  937.          begin
  938.             { now open Wave output device. }
  939.             FError := WaveOutOpen(@FHWaveOut,
  940.                                   FDeviceId,
  941.                                   Pointer(PWaveFormat),
  942.                                   Longint(@WaveOutFunc),
  943.                                   Longint(Self),
  944.                                   CALLBACK_FUNCTION or dwFlags);
  945.          end
  946.          else
  947.          {$ENDIF}
  948.          begin
  949.             { now open Wave output device. }
  950.             FError := WaveOutOpen(@FHWaveOut,
  951.                                   FDeviceId,
  952.                                   Pointer(PWaveFormat),
  953.                                   FHandle,
  954.                                   0,
  955.                                   CALLBACK_WINDOW or dwFlags);
  956.          end;
  957.          if (FError <> 0) then
  958.              Error('WaveOutOpen:'#10#13+WaveOutErrorString(FError));
  959.          { wait until the device returns its status }
  960.          repeat
  961.              {$IFDEF _USE_CALLBACK}
  962.              if _Win9x_ or _WinNT4_ then
  963.                 Delay(10,False)
  964.              else
  965.              {$ENDIF}
  966.                 Delay(10,True);
  967.              dec(TimeOut);
  968.          until (wosOpen in FState) or (TimeOut <= 0);
  969.          if (TimeOut <= 0) then
  970.      Error('WaveOutOpen:'#10#13+LoadResStr(IDS_CANTOPENDEVICE));
  971.          DoOpened;
  972.       except
  973.          if assigned(FOnError) then FOnError(Self);
  974.          FState := [wosOpen];
  975.          Close;
  976.          FState := [wosClose];
  977.          raise;
  978.       end;
  979.    end;
  980. end;
  981. {-- TMMWaveOut -----------------------------------------------------------}
  982. Procedure TMMWaveOut.Close;
  983. var
  984.    TimeOut: integer;
  985. begin
  986.    if (wosOpen in FState) and (not FClosing or FCloseIt) then
  987.    try
  988.       FClosing := True;
  989.       { stop playing }
  990.       if (wosPlay in FState) OR (wosPause in FState) then Stop;
  991.       TimeOut := 500;
  992.       { Close the device (finally!) }
  993.       if FStopIt then FCloseIt := True
  994.       else
  995.       begin
  996.          FCloseIt := False;
  997.          if (FHWaveOut <> 0) then
  998.          begin
  999.             {$IFDEF _MMDEBUG}
  1000.             if (FInHandler > 0) then
  1001.                DebugStr(0,'Try to close device (while in Handler)...')
  1002.             else
  1003.                DebugStr(0,'Try to close device...');
  1004.             {$ENDIF}
  1005.     FError := WaveOutClose(FHWaveOut);
  1006.     if FError <> 0 then
  1007.        Error('WaveOutClose:'#10#13+WaveOutErrorString(FError));
  1008.             { wait until the device returns its status }
  1009.             repeat
  1010.                {$IFDEF _USE_CALLBACK}
  1011.                if _Win9x_ or _WinNT4_ then
  1012.                   Delay(10,False)
  1013.                else
  1014.                {$ENDIF}
  1015.                   Delay(10,True);
  1016.                dec(TimeOut);
  1017.             until (wosClose in FState) or (TimeOut <= 0);
  1018.             FEndingPosition := 0;
  1019.             FWrapArrounds   := 0;
  1020.             FWrapSize       := 0;
  1021.          end
  1022.          else
  1023.          begin
  1024.             FState := [wosClose];
  1025.          end;
  1026.          { notify all other components }
  1027.          inherited Closed;
  1028.          {$IFDEF WIN32}
  1029.          if (FCallBackMode = cmThread) then
  1030.              { shot down the thread }
  1031.              DoneThread
  1032.          else
  1033.              { Free the critical section }
  1034.              DoneCritical;
  1035.          {$ENDIF}
  1036.          DoClosed;
  1037.          if (TimeOut <= 0) then
  1038.           Error('WaveOutClose:'#10#13+LoadResStr(IDS_CANTCLOSEDEVICE));
  1039.       end;
  1040.    except
  1041.       FClosing := False;
  1042.    end;
  1043. end;
  1044. {-- TMMWaveOut ------------------------------------------------------------}
  1045. Procedure TMMWaveOut.Reset;
  1046. var
  1047.    i: integer;
  1048.    TimeOut: Longint;
  1049.    Msg: TMsg;
  1050. begin
  1051.    if ((wosPlay in FState) or (wosPause in FState)) and not FReseting then
  1052.    begin
  1053.       try
  1054.          FReseting := True;
  1055.          FError := WaveOutPause(FHWaveOut);
  1056.          if FError <> 0 then
  1057.             Error('WaveOutPause:'#10#13+WaveOutErrorString(FError));
  1058.          if (FCallBackMode = cmWindow) then
  1059.             { remove all pending Messages from the queue }
  1060.             while PeekMessage(Msg, FHandle, MM_WOM_DONE, MM_WOM_DONE, PM_REMOVE) do
  1061.          {$IFDEF WIN32}
  1062.          else if (FCallBackMode = cmThread) then
  1063.          begin
  1064.             { remove all pending messages from threads queue }
  1065.             SetEvent(FResetEvent);
  1066.             { Wait for it to reset... }
  1067.             while WaitForSingleObject(FResetEvent, 0) = WAIT_OBJECT_0 do Sleep(1);
  1068.          end;
  1069.          {$ENDIF};
  1070.          FError := WaveOutReset(FHWaveOut);
  1071.          if FError > 0 then
  1072.             Error('WaveOutReset:'#10#13+WaveOutErrorString(FError));
  1073.          TimeOut := 100;
  1074.          repeat
  1075.              {$IFDEF _USE_CALLBACK}
  1076.              if _Win9x_ or _WinNT4_ then
  1077.                 Delay(10,False)
  1078.              else
  1079.              {$ENDIF}
  1080.                 Delay(10,True);
  1081.              dec(TimeOut);
  1082.          until not FReseting or (TimeOut <= 0);
  1083.          { this buggy drivers... :-( }
  1084.          FError := WaveOutRestart(FHWaveOut);
  1085.          if FError <> 0 then
  1086.             Error('WaveOutRestart:'#10#13+WaveOutErrorString(FError));
  1087.          { pause the output so the buffers won't play until we tell it }
  1088.          FError := WaveOutPause(FHWaveOut);
  1089.          if FError <> 0 then
  1090.             Error('WaveOutPause:'#10#13+WaveOutErrorString(FError));
  1091.          FBufferOutIdx := 0;
  1092.          FBufferCounter := 0;
  1093.          FOldPosition := 0;
  1094.          FLastPosition := 0;
  1095.          FWrapArrounds := 0;
  1096.          FWrapSize := 0;
  1097.          FLoopPos := 0;
  1098.          { notify all other components }
  1099.          Reseting;
  1100.          FMoreBuffers := True;
  1101.          { Load the number of buffers required }
  1102.          i := 0;
  1103.          while (i < FNumBuffers) and FMoreBuffers do
  1104.          begin
  1105.             { fill the buffer and send to driver }
  1106.             if LoadWaveHeader(FWaveOutHdrs[i]) > 0 then
  1107.                QueueWaveHeader(FWaveOutHdrs[i])
  1108.             else break;
  1109.             inc(i);
  1110.          end;
  1111.          FBuffersUsed := i;
  1112.          { start the buffers playing (unpause) }
  1113.          if not (wosPause in FState) then
  1114.          begin
  1115.             FError := WaveOutRestart(FHWaveOut);
  1116.             if FError <> 0 then
  1117.                Error('WaveOutRestart:'#10#13+WaveOutErrorString(FError));
  1118.          end;
  1119.          if FBuffersUsed = 0 then Stop;
  1120.       except
  1121.         if assigned(FOnError) then FOnError(Self);
  1122.         Close;
  1123.         raise;
  1124.       end;
  1125.    end;
  1126. end;
  1127. {-- TMMWaveOut ------------------------------------------------------------}
  1128. Procedure TMMWaveOut.Start;
  1129. Var
  1130.    oldCursor: TCursor;
  1131.    i: integer;
  1132. begin
  1133.    try
  1134.       if not (wosOpen in FState) then Open;
  1135.       if (wosOpen in FState) and not (wosPlay in FState) then
  1136.       begin
  1137.          { setup for playing }
  1138.          { reset the total bytes played counter }
  1139.          FBytesPlayed := 0;
  1140.          FOldPosition := 0;
  1141.          FLastPosition := 0;
  1142.          FWrapArrounds := 0;
  1143.          FWrapSize := 0;
  1144.          FLoopPos := 0;
  1145.          FLoopTempCount := FLoopCount;
  1146.          FInHandler := 0;
  1147.          FStarted := False;
  1148.          FStopIt := False;
  1149.          FReseting := False;
  1150.          FStopping := False;
  1151.          FPosted := False;
  1152.          FBufferOutIdx := 0;
  1153.          FBufferCounter := 0;
  1154.          { change the cursor to HourGlass }
  1155.          oldCursor := Screen.Cursor;
  1156.          if FShowHourGlass and (BufferSize * NumBuffers > 100000) then
  1157.             Screen.Cursor := crHourGlass;
  1158.          try
  1159.             {$IFDEF _MMDEBUG}
  1160.             DebugStr(0,'Try to start device...');
  1161.             {$ENDIF}
  1162.             { pause the output so the buffers won't play until we tell it to }
  1163.             FError := WaveOutPause(FHWaveOut);
  1164.             if FError <> 0 then
  1165.                Error('WaveOutPause:'#10#13+WaveOutErrorString(FError));
  1166.             { now notify all other components }
  1167.             inherited Started;
  1168.             FMoreBuffers := True;
  1169.             i := 0; { Load the number of buffers required }
  1170.             while (i < FNumBuffers) and FMoreBuffers do
  1171.             begin
  1172.                { create the waveOut header and buffer }
  1173.                AllocWaveHeader(FWaveOutHdrs[i]);
  1174.                {$IFDEF _NUMERATE}
  1175.                FWaveOutHdrs[i]^.dwUser := i;
  1176.                {$ENDIF}
  1177.                { prepare the waveform header for playing }
  1178.                PrepareWaveHeader(FWaveOutHdrs[i]);
  1179.                { fill the buffer and send to driver }
  1180.                if LoadWaveHeader(FWaveOutHdrs[i]) > 0 then
  1181.                   QueueWaveHeader(FWaveOutHdrs[i])
  1182.                else break;
  1183.                inc(i);
  1184.             end;
  1185.             FBuffersUsed := i;
  1186.             FState := FState + [wosPlay];
  1187.          finally
  1188.             Screen.Cursor := oldCursor;
  1189.          end;
  1190.          DoStarted;
  1191.       end;
  1192.    except
  1193.       if assigned(FOnError) then FOnError(Self);
  1194.       FState := FState + [wosPlay];
  1195.       Close;
  1196.       FState := [wosClose];
  1197.       raise;
  1198.    end;
  1199. end;
  1200. {-- TMMWaveOut ------------------------------------------------------------}
  1201. procedure TMMWaveOut.Pause;
  1202. begin
  1203.    try
  1204.       if not (wosOpen in FState) then Open;
  1205.       if (wosOpen in FState) and (not (wosPause in FState)) then
  1206.       begin
  1207.          if (wosPlay in FState) then
  1208.          try
  1209.             EnterCritical;
  1210.             {$IFDEF _MMDEBUG}
  1211.             DebugStr(0,'Try to pause device...');
  1212.             {$ENDIF}
  1213.             FError := WaveOutPause(FHWaveOut);
  1214.             if FError <> 0 then
  1215.                Error('WaveOutPause:'#10#13+WaveOutErrorString(FError));
  1216.             FState := FState + [wosPause];
  1217.             if FFullDuplex then
  1218.             begin
  1219.                inc(FOldPosition, GetSamplePosition);
  1220.                FReseting := True;
  1221.                FError := WaveOutReset(FHWaveOut);
  1222.                if FError > 0 then
  1223.                   Error('WaveOutReset:'#10#13+WaveOutErrorString(FError));
  1224.                FBufferOutIdx := 0;
  1225.                FBufferCounter := 0;
  1226.             end;
  1227.          finally
  1228.             LeaveCritical;
  1229.          end;
  1230.          DoPaused;
  1231.       end;
  1232.    except
  1233.       if assigned(FOnError) then FOnError(Self);
  1234.       Close;
  1235.       raise;
  1236.    end;
  1237. end;
  1238. {-- TMMWaveOut ------------------------------------------------------------}
  1239. procedure TMMWaveOut.Restart;
  1240. begin
  1241.    try
  1242.       if (wosPlay in FState) and (wosPause in FState) then
  1243.       begin
  1244.          FReseting := False;
  1245.          {$IFDEF _MMDEBUG}
  1246.          DebugStr(0,'Try to restart device...');
  1247.          {$ENDIF}
  1248.          inherited Restarted;
  1249.          FError := WaveOutRestart(FHWaveOut);
  1250.          if FError <> 0 then
  1251.             Error('WaveOutRestart:'#10#13+WaveOutErrorString(FError));
  1252.          DoRestarted;
  1253.       end;
  1254.    except
  1255.       if assigned(FOnError) then FOnError(Self);
  1256.       Close;
  1257.       raise;
  1258.    end;
  1259. end;
  1260. {-- TMMWaveOut ------------------------------------------------------------}
  1261. procedure TMMWaveOut.Stop;
  1262. begin
  1263.    if (wosPlay in FState) or (wosPause in FState) then
  1264.    begin
  1265.       try
  1266.          EnterCritical;
  1267.          try
  1268.             FStopping := True;
  1269.             FReseting := True;
  1270.             {$IFDEF _MMDEBUG}
  1271.             if (FInHandler > 0) then
  1272.                DebugStr(0,'Try to stop device (while in Handler)...')
  1273.             else
  1274.                DebugStr(0,'Try to stop device...');
  1275.             {$ENDIF}
  1276.          finally
  1277.             LeaveCritical;
  1278.          end;
  1279.          { save the stop position }
  1280.          FEndingPosition := Position;
  1281.          FWrapArrounds   := 0;
  1282.          FWrapSize       := 0;
  1283.          FError := WaveOutReset(FHWaveOut);
  1284.          if FError > 0 then
  1285.             Error('WaveOutReset:'#10#13+WaveOutErrorString(FError));
  1286.          DoStopped;
  1287.       except
  1288.         if assigned(FOnError) then FOnError(Self);
  1289.         Close;
  1290.         raise;
  1291.       end;
  1292.    end;
  1293. end;
  1294. {-- TMMWaveOut -----------------------------------------------------------}
  1295. procedure TMMWaveOut.Opened;
  1296. begin
  1297.    Open;
  1298. end;
  1299. {-- TMMWaveOut -----------------------------------------------------------}
  1300. procedure TMMWaveOut.Closed;
  1301. begin
  1302.    Close;
  1303. end;
  1304. {-- TMMWaveOut -----------------------------------------------------------}
  1305. procedure TMMWaveOut.Started;
  1306. begin
  1307.    Start;
  1308. end;
  1309. {-- TMMWaveOut -----------------------------------------------------------}
  1310. procedure TMMWaveOut.Paused;
  1311. begin
  1312.    Pause;
  1313. end;
  1314. {-- TMMWaveOut -----------------------------------------------------------}
  1315. procedure TMMWaveOut.Restarted;
  1316. begin
  1317.    Restart;
  1318. end;
  1319. {-- TMMWaveOut -----------------------------------------------------------}
  1320. procedure TMMWaveOut.Stopped;
  1321. begin
  1322.    Stop;
  1323. end;
  1324. {-- TMMWaveOut -----------------------------------------------------------}
  1325. procedure TMMWaveOut.DoOpened;
  1326. begin
  1327.    {$IFDEF _MMDEBUG}
  1328.    DebugStr(0,'Device is now open...');
  1329.    {$ENDIF}
  1330.    if Assigned(FOnOpen) then FOnOpen(Self);
  1331. end;
  1332. {-- TMMWaveOut -----------------------------------------------------------}
  1333. procedure TMMWaveOut.DoClosed;
  1334. begin
  1335.    FHWaveOut := 0;
  1336.    {$IFDEF _MMDEBUG}
  1337.    DebugStr(0,'Device is now closed...');
  1338.    {$ENDIF}
  1339.    FClosing := False;
  1340.    if not (csDestroying in ComponentState) then
  1341.       if Assigned(FOnClose) then FOnClose(Self);
  1342. end;
  1343. {-- TMMWaveOut -----------------------------------------------------------}
  1344. procedure TMMWaveOut.DoStarted;
  1345. begin
  1346.    if (FBuffersUsed > 0) then
  1347.    begin
  1348.       if not (wosPause in FState) then
  1349.       begin
  1350.          { start the buffers playing (unpause) }
  1351.          FError := WaveOutRestart(FHWaveOut);
  1352.          if FError <> 0 then
  1353.             Error('WaveOutRestart:'#10#13+WaveOutErrorString(FError));
  1354.       end;
  1355.       {$IFDEF _MMDEBUG}
  1356.       DebugStr(0,'Device is now started...');
  1357.       {$ENDIF}
  1358.       InitDSPMeter;
  1359.       if assigned(FOnStart) then FOnStart(Self);
  1360.    end
  1361.    else
  1362.    try
  1363.       inherited Stopped;
  1364.       Error('WaveOutStart:'#10#13+LoadResStr(IDS_STARTERROR));
  1365.    finally
  1366.       Close;
  1367.    end;
  1368. end;
  1369. {-- TMMWaveOut -----------------------------------------------------------}
  1370. procedure TMMWaveOut.DoPaused;
  1371. begin
  1372.    FState := FState + [wosPause];
  1373.    inherited Paused;
  1374.    {$IFDEF _MMDEBUG}
  1375.    DebugStr(0,'Device is now paused...');
  1376.    {$ENDIF}
  1377.    if assigned(FOnPause) then FOnPause(Self);
  1378. end;
  1379. {-- TMMWaveOut -----------------------------------------------------------}
  1380. procedure TMMWaveOut.DoRestarted;
  1381. begin
  1382.    FState := FState - [wosPause];
  1383.    {$IFDEF _MMDEBUG}
  1384.    DebugStr(0,'Device is now restarted...');
  1385.    {$ENDIF}
  1386.    if assigned(FOnRestart) then FOnRestart(Self);
  1387. end;
  1388. {-- TMMWaveOut -----------------------------------------------------------}
  1389. procedure TMMWaveOut.DoStopped;
  1390. var
  1391.    TimeOut: integer;
  1392. begin
  1393.    if (wosPlay in FState) or (wosPause in FState) then
  1394.    begin
  1395.       if (FInHandler > 0) then FStopIt := True
  1396.       else
  1397.       begin
  1398.          FState := FState - [wosPlay,wosPause];
  1399.          DoneDSPMeter;
  1400.          TimeOut := 500;
  1401.          { wait until all buffers returned }
  1402.          repeat
  1403.              {$IFDEF _USE_CALLBACK}
  1404.              if _Win9x_ or _WinNT4_ then
  1405.                 Delay(10,False)
  1406.              else
  1407.              {$ENDIF}
  1408.                 Delay(10,True);
  1409.             dec(TimeOut);
  1410.          until (FBufferCounter = 0) or (TimeOut <= 0);
  1411.          {$IFDEF _MMDEBUG}
  1412.          if (FBufferCounter > 0) then
  1413.              DebugStr(0,'TimeOut while waiting for returned headers!');
  1414.          {$ENDIF}
  1415.          { notify all other components }
  1416.          inherited Stopped;
  1417.          { unprepare wave headers }
  1418.          UnPrepareWaveHeaders;
  1419.          { free header memory and remove }
  1420.          FreeWaveHeaders;
  1421.          FBuffersUsed := 0;
  1422.          FBufferCounter := 0;
  1423.          FBufferOutIdx := 0;
  1424.          FStopIt := False;
  1425.          {$IFDEF _MMDEBUG}
  1426.          DebugStr(0,'Device is now stopped...');
  1427.          {$ENDIF}
  1428.          if not (csDestroying in ComponentState) then
  1429.             if assigned(FOnStop) then FOnStop(Self);
  1430.          if FCloseIt then Close;
  1431.       end;
  1432.    end;
  1433. end;
  1434. {-- TMMWaveOut -----------------------------------------------------------}
  1435. procedure TMMWaveOut.DoBufferFilled(lpwh: PWaveHdr);
  1436. begin
  1437.    if assigned(FOnBufferFilled) then FOnBufferFilled(Self, lpwh);
  1438. end;
  1439. {-- TMMWaveOut -----------------------------------------------------------}
  1440. procedure TMMWaveOut.BufferLoad(lpwh: PWaveHdr; var MoreBuffers: Boolean);
  1441. begin
  1442.    StartDSPMeter;
  1443.    try
  1444.       with PMMWaveHdr(lpwh)^ do
  1445.       begin
  1446.          wh.dwBufferLength := BufferSize;
  1447.          wh.dwBytesRecorded := 0;
  1448.          LoopRec.dwLoop := FLooping;
  1449.          if FLooping then
  1450.          begin
  1451.             LoopRec.dwLoopCnt    := FLoopCount;
  1452.             LoopRec.dwLoopTmpCnt := FLoopTempCount;
  1453.             LoopRec.dwLooping    := False;
  1454.          end;
  1455.          inherited BufferLoad(lpwh, MoreBuffers);
  1456.          wh.dwBufferLength := wh.dwBytesRecorded;
  1457.          if FLooping then FLoopTempCount := LoopRec.dwLoopTmpCnt;
  1458.       end;
  1459.    finally
  1460.       StopDSPMeter;
  1461.    end;
  1462. end;
  1463. {-- TMMWaveOut -----------------------------------------------------------}
  1464. procedure TMMWaveOut.BufferReady(lpwh: PWaveHdr);
  1465. begin
  1466. end;
  1467. {-- TMMWaveOut -----------------------------------------------------------}
  1468. procedure TMMWaveOut.DoBufferReady(lpwh: PWaveHdr);
  1469. begin
  1470.    { buffer has returned from driver, notify the other components }
  1471.    StartDSPMeter;
  1472.    try
  1473.       inc(FBufferOutIdx);
  1474.       if FBufferOutIdx >= FBuffersUsed then FBufferOutIdx := 0;
  1475.       { BUG-FIX for NT 4.0 SP4, it does set dwBytesRecorded to zero }
  1476.       lpwh^.dwBytesRecorded := PMMWaveHdr(lpwh)^.dwUser2;
  1477.       { we use a trick here and point to the current header which is playing }
  1478.       PMMWaveHdr(lpwh)^.lpNext := FWaveOutHdrs[FBufferOutIdx];
  1479.       { BUG-FIX for NT 4.0 SP4, it does set dwBytesRecorded to zero }
  1480.       PMMWaveHdr(PMMWaveHdr(lpwh)^.lpNext)^.wh.dwBytesRecorded := PMMWaveHdr(PMMWaveHdr(lpwh)^.lpNext)^.dwUser2;
  1481.       inherited BufferReady(lpwh);
  1482.    finally
  1483.       StopDSPMeter;
  1484.    end;
  1485. end;
  1486. {-- TMMWaveOut -----------------------------------------------------------}
  1487. procedure TMMWaveOut.ProcessWaveHeader(lpWaveHdr: PWaveHdr);
  1488. var
  1489.    CurPos,LastPos: Cardinal;
  1490.    Wrapped: integer;
  1491.    TimeOut: Longint;
  1492. begin
  1493.    if (wosPlay in FState) and not FReseting and not FStopping then
  1494.    begin
  1495.       inc(FInHandler);
  1496.       try
  1497.          { some drivers, for example the SB return the buffers }
  1498.          { in bad order, so wee can try to fix this            }
  1499.          if FIX_BUFFERS then
  1500.             lpWaveHdr := FWaveOutHdrs[FBufferOutIdx]
  1501.          else
  1502.             WaveOutUnPrepareHeader(FHWaveOut, lpWaveHdr, sizeOf(TWaveHdr));
  1503.          {$IFDEF _MMDEBUG}
  1504.          DebugStr(2,'Buffer '+IntToStr(lpWaveHdr^.dwUser)+' returned');
  1505.          {$ENDIF}
  1506.          EnterCritical;
  1507.          FBytesPlayed := FBytesPlayed + lpWaveHdr^.dwBufferLength;
  1508.          LeaveCritical;
  1509.          try
  1510.             DoBufferReady(lpWaveHdr);
  1511.             {$IFDEF _MMDEBUG}
  1512.             DebugStr(2,'Buffer '+IntToStr(lpWaveHdr^.dwUser)+' ready for loading');
  1513.             {$ENDIF}
  1514.             {$IFDEF WIN32}
  1515.             { wrap arround handling }
  1516.             CurPos := GetSamplePosition;
  1517.             LastPos:= FLastPosition;
  1518.             asm
  1519.                mov  Wrapped, False
  1520.                mov  eax, CurPos
  1521.                cmp  eax, LastPos
  1522.                jnb  @@exit
  1523.                mov  eax, LastPos
  1524.                sub  eax, CurPos
  1525.                cmp  eax, $FFFF
  1526.                jb   @@exit
  1527.                mov  Wrapped, True
  1528.             @@exit:
  1529.             end;
  1530.             if (Wrapped = 1) then
  1531.             begin
  1532.                { every driver wraps at a different position           }
  1533.                { here we try to detect where the position has wrapped }
  1534.                { hey, this looks realy cool                           }
  1535.                FWrapSize := (FLastPosition and $FFF00000) or $FFFFF;
  1536.                inc(FWrapArrounds);
  1537.             end;
  1538.             {$IFDEF _MMDEBUG}
  1539.             if Wrapped <> 0 then
  1540.             begin
  1541.                DB_WriteStr(0,'Wrapped, LastPos: '+IntToStr(FLastPosition)+' (');
  1542.                DB_WriteHex(0,FLastPosition);
  1543.                DB_WriteStr(0,'), CurPos: '+IntToStr(CurPos)+' (');
  1544.                DB_WriteHex(0,CurPos);
  1545.                DB_WriteStr(0,'), WrapSize: '+IntToStr(FWrapSize)+' (');
  1546.                DB_WriteHex(0,FWrapSize);
  1547.                DB_WriteStr(0,'), Position: '+TimeToString64Ex(Position,True));
  1548.                DB_WriteStrLn(0,')');
  1549.             end;
  1550.             {$ENDIF}
  1551.             FLastPosition := CurPos;
  1552.             {$ENDIF}
  1553.             if FMoreBuffers and not FStopIt then
  1554.             begin
  1555.                { file restarted ? }
  1556.                if FLooping and PMMWaveHdr(lpWaveHdr)^.LoopRec.dwLooping then
  1557.                begin
  1558.                   EnterCritical;
  1559.                   { adjust GetPosition }
  1560.                   FLoopPos := CurPos;
  1561.                   PMMWaveHdr(lpWaveHdr)^.LoopRec.dwLooping := False;
  1562.                   LeaveCritical;
  1563.                   
  1564.                   { notify other components that we have looped }
  1565.                   Looped;
  1566.                   if assigned(FOnLooping) then FOnLooping(Self);
  1567.                end;
  1568.                { wait until the buffer is marked as done, or we get trouble ! }
  1569.                TimeOut := 65000;
  1570.                { wait until the buffer is marked as done }
  1571.                while (lpWaveHdr^.dwFlags and WHDR_DONE <> WHDR_DONE) and (TimeOut > 0) do
  1572.                begin
  1573.                   dec(TimeOut);
  1574.                   {$IFDEF WIN32}
  1575.                   Sleep(2);
  1576.                   {$ENDIF}
  1577.                end;
  1578.                { load the next buffer }
  1579.                if (LoadWaveHeader(lpWaveHdr) <= 0) and not FStopIt then
  1580.                    Error(LoadResStr(IDS_FILLERROR));
  1581.                { send the next buffer to the driver }
  1582.                if not FStopIt then QueueWaveHeader(lpWaveHdr);
  1583.             end;
  1584.          except
  1585.             if assigned(FOnError) then FOnError(Self);
  1586.             raise;
  1587.          end;
  1588.       finally
  1589.          dec(FInHandler);
  1590.          { can we stop it ? }
  1591.          if (FInHandler = 0) then  { no more buffers, stop }
  1592.              if (FStopIt or (FBufferCounter = 0)) and not FPosted then
  1593.              begin
  1594.                 FPosted := True;
  1595.                 FStopping := True;
  1596.                 {$IFDEF _MMDEBUG}
  1597.                 DebugStr(0,'Stop Message posted...');
  1598.                 {$ENDIF}
  1599.                 { pause the device first so it stops playing }
  1600.                 { some cards play the last buffer looped ! }
  1601.                 FError := WaveOutPause(FHWaveOut);
  1602.                 if FError <> 0 then
  1603.                    Error('WaveOutPause:'#10#13+WaveOutErrorString(FError));
  1604.                 PostMessage(FHandle,MM_WOM_STOP,FHWaveOut,0);
  1605.              end;
  1606.       end;
  1607.    end;
  1608. end;
  1609. {-- TMMWaveOut -----------------------------------------------------------}
  1610. procedure TMMWaveOut.WaveOutHandler(Var Msg: TMessage);
  1611. begin
  1612.   with Msg do
  1613.   try
  1614.       if (wParam = FHWaveOut) then
  1615.       case msg of
  1616.         MM_WOM_OPEN :
  1617.         begin
  1618.            { device is now open }
  1619.            FState := [wosOpen];
  1620.         end;
  1621.         MM_WOM_CLOSE:
  1622.         begin
  1623.            { device is now closed }
  1624.            FState := [wosClose];
  1625.         end;
  1626.         MM_WOM_DONE : begin
  1627.                          {$IFDEF _USE_CALLBACK}
  1628.                          if not _Win9x_ and not _WinNT4_ then
  1629.                          {$ENDIF}
  1630.                          begin
  1631.                             dec(FBufferCounter);
  1632.                             if FReseting then
  1633.                             begin
  1634.                                if FBufferCounter = 0 then FReseting := False;
  1635.                                exit;
  1636.                             end;
  1637.                          end;
  1638.                          if not FStopping then ProcessWaveHeader(PWaveHdr(lparam));
  1639.                          exit;
  1640.                       end;
  1641.          MM_WOM_STOP: begin
  1642.                          {$IFDEF _MMDEBUG}
  1643.                          DebugStr(0,'Stop message received...');
  1644.                          {$ENDIF}
  1645.                          { should stop the device }
  1646.                          Stop;
  1647.                          exit;
  1648.                       end;
  1649.       end;
  1650.       Result := DefWindowProc(FHandle, Msg, wParam, lParam);
  1651.   except
  1652.      if assigned(FOnError) then FOnError(Self);
  1653.      Close;
  1654.      Application.HandleException(Self);
  1655.   end;
  1656. end;
  1657. {-- WaveOutFunc ----------------------------------------------------------}
  1658. procedure WaveOutFunc(hWaveOut:HWaveOut;wMsg:UINT;dwInstance,dwParam1,dwParam2:Longint);
  1659. begin
  1660.    if (dwInstance <> 0) then
  1661.    with TMMWaveOut(dwInstance) do
  1662.    {$IFDEF WIN32}
  1663.    try
  1664.    {$ELSE}
  1665.    begin
  1666.    {$ENDIF}
  1667.       case wMsg of
  1668.          WOM_OPEN :
  1669.          begin
  1670.             { device is now open }
  1671.             FState := [wosOpen];
  1672.          end;
  1673.          WOM_CLOSE:
  1674.          begin
  1675.             { device is now closed }
  1676.             FState := [wosClose];
  1677.          end;
  1678.          WOM_DONE :
  1679.          begin
  1680.             { device has returnded a buffer }
  1681.             dec(FBufferCounter);
  1682.             if FReseting then
  1683.             begin
  1684.                if FBufferCounter = 0 then FReseting := False;
  1685.             end
  1686.             else
  1687.             begin
  1688.                if not FStopping then
  1689.                case FCallBackMode of
  1690.                     cmWindow: PostMessage(FHandle,MM_WOM_DONE,hWaveOut,dwParam1);
  1691.                     {$IFDEF WIN32}
  1692.                     cmCallBack: ProcessWaveHeader(PWaveHdr(dwparam1));
  1693.                     cmThread: PostThreadMessage(FOutThread.ThreadID,MM_WOM_DONE,hWaveOut,dwParam1);
  1694.                     {$ENDIF}
  1695.                end;
  1696.             end;
  1697.          end;
  1698.       end;
  1699.    {$IFDEF WIN32}
  1700.    except
  1701.       Close;
  1702.       Application.HandleException(TMMWaveOut(dwInstance));
  1703.    {$ENDIF}
  1704.    end;
  1705. end;
  1706. {$IFDEF WIN32}
  1707. {-------------------------------------------------------------------------}
  1708. procedure TMMWaveOutThread.Execute;
  1709. {- Wait for and process output messages }
  1710. var
  1711.    Res  : DWORD;
  1712.    Msg  : TMsg;
  1713.    {$IFDEF _MMDEBUG}
  1714.    Err  : DWORD;
  1715.    {$ENDIF}
  1716.    Handles: array[0..1] of THandle;
  1717. begin
  1718.    with TMMWaveOut(Owner) do
  1719.    try
  1720.       {$IFDEF _MMDEBUG}
  1721.       DebugStr(0,'Setting Thread Priority');
  1722.       {$ENDIF}
  1723.       SetPriority(FPriority);
  1724.       Handles[0] := FCloseEvent;
  1725.       Handles[1] := FResetEvent;
  1726.       { make sure we have a message queue... }
  1727.       PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE);
  1728.       {$IFDEF _MMDEBUG}
  1729.       DebugStr(0,'Setting OutEvent,ready to go !');
  1730.       {$ENDIF}
  1731.       { Ready to go, set the output event }
  1732.       SetEvent(FOutEvent);
  1733.       { Repeat until device is closed }
  1734.       while not Terminated do
  1735.       try
  1736.          if not PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
  1737.          begin
  1738.             Res := MsgWaitForMultipleObjects(2, Handles, False, INFINITE, QS_ALLEVENTS);
  1739.             case Res of
  1740.                 WAIT_FAILED:       { Wait failed.  Shouldn't happen. }
  1741.                 begin
  1742.                    {$IFDEF _MMDEBUG}
  1743.                    Err := GetLastError;
  1744.                    DebugStr(0,'Wait Failed... Error: '+SysErrorMessage(Err));
  1745.                    {$ENDIF}
  1746.                    Continue;
  1747.                 end;
  1748.                 WAIT_OBJECT_0:     { CloseEvent signaled!            }
  1749.                 begin
  1750.                    {$IFDEF _MMDEBUG}
  1751.                    DebugStr(0,'CloseEvent signaled...');
  1752.                    {$ENDIF}
  1753.                    { Finished here, okay to close device }
  1754.                    exit;
  1755.                 end;
  1756.                 WAIT_OBJECT_0+1:   { ResetEvent signaled!            }
  1757.                 begin
  1758.                    {$IFDEF _MMDEBUG}
  1759.                    DebugStr(0,'ResetEvent signaled...');
  1760.                    {$ENDIF}
  1761.                    { remove all pending Messages from the queue }
  1762.                    while PeekMessage(Msg, 0, MM_WOM_DONE, MM_WOM_DONE, PM_REMOVE) do;
  1763.                    ResetEvent(FResetEvent);
  1764.                    Continue;
  1765.                 end;
  1766.                 WAIT_OBJECT_0+2:    { New message was received.      }
  1767.                 begin
  1768.                    {$IFDEF _MMDEBUG}
  1769.                    DebugStr(2,'WaveOut message reveived...');
  1770.                    {$ENDIF}
  1771.                    { Get the message that woke us up by looping again.}
  1772.                    Continue;
  1773.                 end;
  1774.             end;
  1775.          end;
  1776.          { Process the message. }
  1777.          with msg do
  1778.          begin
  1779.             if (wParam = FHWaveOut) and (message = MM_WOM_DONE) then
  1780.             begin
  1781.                if not FStopping then ProcessWaveHeader(PWaveHdr(lparam));
  1782.             end
  1783.             else
  1784.             begin
  1785.                {$IFDEF _MMDEBUG}
  1786.                DebugStr(0,'Unknown message received...');
  1787.                {$ENDIF}
  1788.                TranslateMessage(Msg);
  1789.                DispatchMessage(msg);
  1790.             end;
  1791.          end;
  1792.       except
  1793.          FThreadError := True;
  1794.          if (FHWaveOut <> 0) then
  1795.          begin
  1796.             FClosing := True;
  1797.             Stop;
  1798.             WaveOutClose(FHWaveOut);
  1799.             DoClosed;
  1800.             CloseEvents;
  1801.          end;
  1802.          Application.HandleException(nil);
  1803.          exit;
  1804.       end;
  1805.    finally
  1806.      {$IFDEF _MMDEBUG}
  1807.      DebugStr(0,'Exit Thread-Proc');
  1808.      {$ENDIF}
  1809.      if not FThreadError then SetEvent(FOutEvent);
  1810.    end;
  1811. end;
  1812. {$ENDIF}
  1813. initialization
  1814. {$IFDEF _MMDEBUG}
  1815.    DB_Level(DEBUGLEVEl);
  1816.    DB_Clear;
  1817. {$ENDIF}
  1818. end.