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

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: 13.11.98 - 03:44:47 $                                        =}
  24. {========================================================================}
  25. unit MMDSystm;
  26. {$C FIXED PRELOAD PERMANENT}
  27. {$I COMPILER.INC}
  28. {.$DEFINE USE_NOTIFICATION}
  29. interface
  30. uses
  31.     Windows,
  32.     SysUtils,
  33.     Classes,
  34.     MMOLE2,
  35.     MMSystem,
  36.     MMObj,
  37.     MMUtils,
  38.     MMRegs,
  39.     MMWaveIO,
  40.     MMPCMSup,
  41.     MMDSound;
  42. { Emulated devices are very, very slow:
  43.   change this value to adjust the buffer return time for emulated drivers
  44. }
  45. const
  46.     {$IFDEF CBUILDER3} {$EXTERNALSYM TIMEADJUST} {$ENDIF}
  47.     TIMEADJUST    : integer = 60;
  48.     EXACTRETURN   : Boolean = True;
  49. const
  50.     {$IFDEF CBUILDER3} {$EXTERNALSYM DS_NEEDVOLUME} {$ENDIF}
  51.     DS_NEEDVOLUME = $10000000;
  52.     {$IFDEF CBUILDER3} {$EXTERNALSYM DS_NEEDPAN} {$ENDIF}
  53.     DS_NEEDPAN    = $20000000;
  54.     {$IFDEF CBUILDER3} {$EXTERNALSYM DS_NEEDFREQ} {$ENDIF}
  55.     DS_NEEDFREQ   = $40000000;
  56. procedure DSSetHWND(hWaveOut: HWAVEOUT; hw: HWND);
  57. function  DSCreatePrimaryBuffer(hWaveOut: HWAVEOUT; lpFormat: PWaveFormatEx): HRESULT;
  58. function  DSWaveOutOpen(lphWaveOut: PHWAVEOUT; uDeviceID: UINT; lpFormat: PWaveFormatEx;
  59.                         dwCallback, dwInstance, dwFlags: DWORD): MMRESULT; stdcall;
  60. function  DSWaveOutClose(hWaveOut: HWAVEOUT): MMRESULT;
  61. function  DSWaveOutPrepareHeader(hWaveOut: HWAVEOUT; lpWaveHdr: PWaveHdr;
  62.                                  uSize: UINT): MMRESULT;
  63. function  DSWaveOutUnprepareHeader(hWaveOut: HWAVEOUT; lpWaveHdr: PWaveHdr;
  64.                                    uSize: UINT): MMRESULT;
  65. function  DSWaveOutWrite(hWaveOut: HWAVEOUT; lpWaveHdr: PWaveHdr;
  66.                          uSize: UINT): MMRESULT;
  67. function  DSWaveOutPause(hWaveOut: HWAVEOUT): MMRESULT;
  68. function  DSWaveOutRestart(hWaveOut: HWAVEOUT): MMRESULT;
  69. function  DSWaveOutReset(hWaveOut: HWAVEOUT): MMRESULT;
  70. function  DSWaveOutGetPosition(hWaveOut: HWAVEOUT; lpInfo: PMMTime;
  71.                                uSize: UINT): MMRESULT;
  72. function  DSWaveOutSetVolume(hWaveOut: HWAVEOUT; dwVolume: DWORD): MMRESULT;
  73. function  DSWaveOutGetVolume(hWaveOut: HWAVEOUT; lpdwVolume: PDWORD): MMRESULT;
  74. function  DSWaveOutSetPan(hWaveOut: HWAVEOUT; dwPan: DWORD): MMRESULT;
  75. function  DSWaveOutGetPan(hWaveOut: HWAVEOUT; lpdwPan: PDWORD): MMRESULT;
  76. function  DSWaveOutSetPlaybackRate(hWaveOut: HWAVEOUT; dwRate: DWORD): MMRESULT;
  77. function  DSWaveOutGetPlaybackRate(hWaveOut: HWAVEOUT; lpdwRate: PDWORD): MMRESULT;
  78. implementation
  79. uses MMMulDiv,MMDSPObj;
  80. const
  81.    TIMERRATE            = 32;               { times per second              }
  82.    BUFFER_PARTS         = 4;                { Divisions of secondary buffer }
  83.    BUFFER_PRELOAD       = BUFFER_PARTS;     { Number of prefill buffers     }
  84. type
  85.     TMMThreadNotificationResources = set of (tnInterface, tnEvents, tnThread);
  86.     PMMft = ^TMMft;
  87.     TMMft = record
  88.        First       : PWaveHdr;
  89.        lpDS        : IDIRECTSOUND;
  90.        lpDSP       : IDIRECTSOUNDBUFFER;
  91.        lpDSB       : IDIRECTSOUNDBUFFER;
  92.        lpGUID      : PGUID;
  93.        NextMMFt    : PMMFt;
  94.        CallBackMode: DWORD;
  95.        CallBack    : DWORD;
  96.        CBInstance  : DWORD;
  97.        EachTick    : DWORD;
  98.        Buffersize  : DWORD;
  99.        NextPos     : DWORD;
  100.        TotalWritten: DWORD;
  101.        TotalPlayed : DWORD;
  102.        LastPlayPos : DWORD;
  103.        SilenceBytes: DWORD;
  104.        EndTime     : DWORD;
  105.        Volume      : DWORD;
  106.        UpdateVolume: Boolean;
  107.        Started     : Boolean;
  108.        Paused      : Boolean;
  109.        DataRate    : DWORD;
  110.        SilenceVal  : Byte;
  111.        Emulated    : Boolean;
  112.         { Playback notification via thread }
  113.        NtfResources: TMMThreadNotificationResources;
  114.        lpDSBN      : IDirectSoundNotify;
  115.        NotifyPts   : array[0..BUFFER_PARTS-1] of TDSBPOSITIONNOTIFY;
  116.     end;
  117. const
  118.    lpMMFt       : PMMFt = Nil;
  119.    DSoundHW     : HWND  = 0;
  120.    TimerInit    : DWORD = 0;
  121.    TimerID      : DWORD = 0;
  122.    AllNtfResources = [tnInterface, tnEvents, tnThread];
  123. var
  124.    DataSection  : TRtlCriticalSection;
  125.    DataSectionOK: Boolean = False;
  126. {------------------------------------------------------------------------}
  127. procedure InitCritical;
  128. begin
  129.    if (lpMMFt = nil) then
  130.    begin
  131.       { create critical section object }
  132.       FillChar(DataSection, SizeOf(DataSection), 0);
  133.       InitializeCriticalSection(DataSection);
  134.       DataSectionOK := True;
  135.    end;
  136. end;
  137. {------------------------------------------------------------------------}
  138. procedure DoneCritical;
  139. begin
  140.    if (lpMMFt = nil) and DataSectionOK then
  141.    begin
  142.       DataSectionOK := False;
  143.       DeleteCriticalSection(DataSection);
  144.    end;
  145. end;
  146. {------------------------------------------------------------------------}
  147. procedure EnterCritical;
  148. begin
  149.    if DataSectionOK then EnterCriticalSection(DataSection);
  150. end;
  151. {------------------------------------------------------------------------}
  152. procedure LeaveCritical;
  153. begin
  154.    if DataSectionOK then LeaveCriticalSection(DataSection);
  155. end;
  156. {------------------------------------------------------------------------}
  157. procedure NotifyMessage(lpft: PMMft; Msg: UINT; wParam: WPARAM; lParam: LPARAM);stdcall;
  158. type
  159.     TWaveOutFunc = procedure(hWaveOut: HWaveOut;wMsg:UINT;dwInstance,dwParam1,dwParam2:Longint);stdcall;
  160. begin
  161.    if (lpft <> nil) then
  162.    with lpft^ do
  163.    begin
  164.       case CallBackMode of
  165.          CALLBACK_WINDOW: PostMessage(CallBack,Msg,wParam,lParam);
  166.          CALLBACK_THREAD: PostThreadMessage(CallBack,Msg,wParam,lParam);
  167.        CALLBACK_FUNCTION: TWaveOutFunc(CallBack)(integer(lpft),Msg,CBInstance,wParam,lParam);
  168.       end;
  169.    end;
  170. end;
  171. {------------------------------------------------------------------------}
  172. procedure DoneMarker(uTimerID, uMessage: UINT; user, dw1, dw2: DWORD);stdcall;
  173. begin
  174.    if (user <> 0) then
  175.    with PMMWaveHdr(user)^ do
  176.    begin
  177.       wh.dwFlags := wh.dwFlags or WHDR_DONE; { Header is done }
  178.       NotifyMessage(PMMft(dwUser1), MM_WOM_DONE, dwUser1, user);
  179.       TimeKillEvent(uTimerID);      { kill the timer }
  180.       dwUser2 := 0;                 { reset the timerID for this buffer }
  181.    end;
  182. end;
  183. {------------------------------------------------------------------------}
  184. procedure CopySnd(pDest: PChar; len, rest, cDiff: DWORD; lpft: PMMft);
  185. Label loop;
  186. Var
  187.   bytes: DWORD;
  188.   lpwh : PWaveHdr;
  189.   ms   : Longint;
  190. begin
  191.    with lpft^ do
  192.    begin
  193. loop:
  194.      lpwh := First;
  195.      if (lpwh <> nil) then
  196.      begin
  197.         with lpwh^ do
  198.         begin
  199.            bytes := dwBufferLength - reserved;
  200.            if (bytes > len) then bytes := len;
  201.            Move((lpData+reserved)^, pDest^, bytes);
  202.            inc(reserved, bytes);
  203.            inc(pDest, bytes);
  204.            dec(len, bytes);
  205.            if (reserved >= dwBufferLength) then
  206.            begin
  207.               First := lpNext;
  208.               if EXACTRETURN or (First = nil) then
  209.               begin
  210.                  ms := MulDiv32(cDiff-(rest+len),1000,DataRate);
  211.                  if Emulated then inc(ms,TIMEADJUST);
  212.                  if (ms > 0) then
  213.                  begin
  214.                     PMMWaveHdr(lpwh)^.dwUser1 := DWORD(lpft);
  215.                     PMMWaveHdr(lpwh)^.dwUser2 := TimeSetEvent(ms, 0, @DoneMarker, DWORD(lpwh),TIME_ONESHOT);
  216.                  end;
  217.               end
  218.               else ms := 0;
  219.               if (PMMWaveHdr(lpwh)^.dwUser2 = 0) or (ms <= 0) then
  220.               begin
  221.                  lpwh^.dwFlags := lpwh^.dwFlags or WHDR_DONE;
  222.                  NotifyMessage(lpft, MM_WOM_DONE, DWORD(lpft), DWORD(lpwh));
  223.               end;
  224.               if (len > 0) then goto loop;
  225.            end;
  226.         end;
  227.      end;
  228.      if (len > 0) then
  229.      begin
  230.         FillChar(pDest^, len, SilenceVal);
  231.      end;
  232.    end;
  233. end;
  234. {------------------------------------------------------------------------}
  235. procedure ProcessData(lpft: PMMft);
  236. Var
  237.    cPlay, cWrite, cDiff: DWORD;
  238.    p1, p2: PChar;
  239.    l1, l2: DWORD;
  240.    dwNumToWrite: DWORD;
  241. begin
  242.    EnterCritical;
  243.    with lpft^ do
  244.    if Started and not Paused then
  245.    begin
  246.       if UpdateVolume then
  247.       begin
  248.          lpDSB.SetVolume(Volume);
  249.          UpdateVolume := False;
  250.       end;
  251.       lpDSB.GetCurrentPosition(cPlay, cWrite);
  252.       if (cPlay < LastPlayPos) then
  253.       begin
  254.          if (LastPlayPos-cPlay > 16) then
  255.              cDiff := BufferSize - LastPlayPos + cPlay
  256.          else
  257.          begin
  258.             TotalPlayed := LastPlayPos-cPlay;
  259.             cDiff := 0;
  260.          end;
  261.       end
  262.       else
  263.           cDiff := cPlay - LastPlayPos;
  264.       inc(TotalPlayed,cDiff);
  265.       LastPlayPos := cPlay;
  266.       dwNumToWrite := Min(((BufferSize-(TotalWritten-TotalPlayed))div EachTick)*EachTick,EachTick);
  267.       if (dwNumToWrite >= EachTick) then
  268.       begin
  269.          if lpDSB.Lock(NextPos,EachTick,p1,l1,p2,l2,0) = DS_OK then
  270.          begin
  271.             inc(NextPos, EachTick);
  272.             inc(TotalWritten, EachTick);
  273.             { calc the difference between play and write }
  274.             if (NextPos >= cPlay) then
  275.                 cDiff := NextPos-cPlay
  276.             else
  277.                 cDiff := (BufferSize-cPlay)+NextPos;
  278.             if (p1 <> Nil) then CopySnd(p1,l1,l2,cDiff,lpft);
  279.             if (p2 <> Nil) then CopySnd(p2,l2,0,cDiff,lpft);
  280.             if (NextPos >= BufferSize) then
  281.                 dec(NextPos, BufferSize);
  282.             lpDSB.Unlock(p1,l1,p2,l2);
  283.          end;
  284.       end;
  285.    end;
  286.    LeaveCritical;
  287. end;
  288. const
  289.    NoReEnter : DWORD = 0;
  290. {------------------------------------------------------------------------}
  291. procedure TimerFunc(uTimerID, uMessage: UINT; user, dw1, dw2: DWORD); stdcall;
  292. var
  293.    lpft: PMMft;
  294. begin
  295.    inc(NoReEnter);
  296.    if (NoReEnter = 1) then
  297.    begin
  298.       lpft := lpMMft;
  299.       while (lpft <> Nil) do
  300.       begin
  301.          ProcessData(lpft);
  302.          lpft := lpft^.NextMMft;
  303.       end;
  304.    end;
  305.    dec(NoReEnter);
  306. end;
  307. {--- Notifications with a thread ----------------------------------------------}
  308. procedure OleCheck(Result: HResult);
  309. const
  310.   strOleError = 'Ole Error, code = $%s';
  311. {$IFDEF DELPHI3} resourcestring {$ENDIF}
  312.   SOleError = strOleError;
  313. begin
  314.   if Result <> S_OK then
  315.     raise Exception.CreateFmt(SOleError, [IntToHex(Result, 8)]);
  316. end;
  317. const
  318.   NOTIFICATIONTHREAD_TIMEOUT = 10000;
  319. type
  320.   TDSNotificationThread = class(TMMThreadEx)
  321.   protected
  322.     FSystemEvent: THandle;
  323.     procedure Execute; override;
  324.   public
  325.     constructor Create;
  326.     destructor Destroy; override;
  327.   end;
  328. var
  329.   DSNotificationThread: TDSNotificationThread;
  330.   DSNotificationThread_RefCount: Integer;
  331. {------------------------------------------------------------------------}
  332. constructor TDSNotificationThread.Create;
  333. begin
  334.    inherited Create(False);
  335.    FSystemEvent := CreateEvent(nil, False, False, nil);
  336. end;
  337. {------------------------------------------------------------------------}
  338. destructor TDSNotificationThread.Destroy;
  339. begin
  340.    CloseHandle(FSystemEvent);
  341.    inherited;
  342. end;
  343. {------------------------------------------------------------------------}
  344. procedure TDSNotificationThread.Execute;
  345. type
  346.     TFtArray = array[0..0] of PMMFt;
  347.     PFtArray = ^TFtArray;
  348. var
  349.    HandleCount: Integer;
  350.    Handles: PWOHandleArray;
  351.    RecCount: Integer;
  352.    Recs: PFtArray;
  353.   procedure CollectHandles;
  354.   var
  355.      lpft: PMMft;
  356.      Index, RecIndex, i: Integer;
  357.   begin
  358.      EnterCritical;
  359.      try
  360.         HandleCount := 1;
  361.         RecCount := 0;
  362.         lpft := lpMMFt;
  363.         while lpft <> nil do
  364.         if tnEvents in lpft^.NtfResources then
  365.         begin
  366.            Inc(HandleCount, BUFFER_PARTS);
  367.            Inc(RecCount);
  368.            lpft := lpft^.NextMMFt;
  369.         end;
  370.        GetMem(Handles, HandleCount * SizeOf(THandle));
  371.        GetMem(Recs, RecCount * SizeOf(Recs^[0]));
  372.        Index := 0;
  373.        RecIndex := 0;
  374.        lpft := lpMMFt;
  375.        while lpft <> nil do
  376.        if tnEvents in lpft^.NtfResources then
  377.        begin
  378.           Recs[RecIndex] := lpft;
  379.           Inc(RecIndex);
  380.           for i := 0 to BUFFER_PARTS-1 do
  381.           begin
  382.             Handles^[Index] := lpft^.NotifyPts[i].hEventNotify;
  383.             Inc(Index);
  384.           end;
  385.           lpft := lpft^.NextMMFt;
  386.        end;
  387.        Handles^[Index] := FSystemEvent;
  388.      finally
  389.        LeaveCritical;
  390.      end;
  391.   end;
  392.   procedure FreeHandles;
  393.   begin
  394.      FreeMem(Handles, HandleCount * SizeOf(THandle));
  395.      FreeMem(Recs, RecCount * SizeOf(Recs^[0]));
  396.      Handles := nil;
  397.      Recs := nil;
  398.   end;
  399. var
  400.   WaitResult: Integer;
  401. begin
  402.    while not Terminated do
  403.    begin
  404.       Priority := tpHigher;
  405.       CollectHandles;
  406.       WaitResult := WaitForMultipleObjects(HandleCount, Handles,
  407.                                            False, NOTIFICATIONTHREAD_TIMEOUT);
  408.       if not Terminated then
  409.       begin
  410.          if WaitResult = WAIT_OBJECT_0 + HandleCount - 1 then
  411.            { System Event - do nothing just starting another loop }
  412.          else if (WaitResult >= WAIT_OBJECT_0) and (WaitResult < WAIT_OBJECT_0 + HandleCount - 1) then
  413.          begin
  414.             { Process next block ... }
  415.             ProcessData(Recs^[(WaitResult - WAIT_OBJECT_0) div BUFFER_PARTS]);
  416.          end;
  417.       end;
  418.       FreeHandles;
  419.    end;
  420. end;
  421. {------------------------------------------------------------------------}
  422. procedure DSNotificationThread_Addref;
  423. begin
  424.    if DSNotificationThread_RefCount = 0 then
  425.       DSNotificationThread := TDSNotificationThread.Create;
  426.    Inc(DSNotificationThread_RefCount);
  427. end;
  428. {------------------------------------------------------------------------}
  429. procedure DSNotificationThread_Release;
  430. begin
  431.    if DSNotificationThread_RefCount > 0 then
  432.    begin
  433.       Dec(DSNotificationThread_RefCount);
  434.       if DSNotificationThread_RefCount = 0 then
  435.       begin
  436.          DSNotificationThread.Terminate;
  437.          SetEvent(DSNotificationThread.FSystemEvent);
  438.          DSNotificationThread.Free;
  439.          DSNotificationThread := nil;
  440.       end;
  441.    end;
  442. end;
  443. {------------------------------------------------------------------------}
  444. function DoneNotifications(lpft: PMMft): HResult;
  445. var
  446.   i: integer;
  447. begin
  448.    with lpft^ do
  449.    begin
  450.      if tnThread in NtfResources then
  451.      begin
  452.        DSNotificationThread_Release;
  453.        Exclude(NtfResources, tnThread);
  454.      end;
  455.      if tnInterface in NtfResources then
  456.      begin
  457.        lpDSBN.Release;
  458.        lpDSBN := nil;
  459.        Exclude(NtfResources, tnInterface);
  460.      end;
  461.      if tnEvents in NtfResources then
  462.      begin
  463.        for i := 0 to BUFFER_PARTS-1 do
  464.          with NotifyPts[i] do
  465.            CloseHandle(hEventNotify);
  466.        Exclude(NtfResources, tnEvents);
  467.      end;
  468.   end;
  469.   Result := S_OK;
  470. end;
  471. {------------------------------------------------------------------------}
  472. function InitializeNotifications(lpft: PMMft): HResult;
  473. var
  474.   i: integer;
  475. begin
  476.    with lpft^ do
  477.    try
  478.       NtfResources := [];
  479.       {$IFDEF USE_NOTIFICATION}
  480.       if lpDSB.QueryInterface(IID_IDirectSoundNotify, lpDSBN) <> S_OK then
  481.       {$ENDIF}
  482.       begin
  483.          lpDSBN := nil;
  484.          Result := E_NOTIMPL;
  485.          exit;
  486.       end;
  487.       Include(NtfResources, tnInterface);
  488.       for i := 0 to BUFFER_PARTS-1 do
  489.       with NotifyPts[i] do
  490.       begin
  491.          dwOffset := (i + 1) * EachTick - 1;
  492.          hEventNotify := CreateEvent(nil, False, False, nil);
  493.       end;
  494.       Include(NtfResources, tnEvents);
  495.       OleCheck(lpDSBN.SetNotificationPositions(BUFFER_PARTS, @NotifyPts[0]));
  496.       DSNotificationThread_Addref;
  497.       Include(NtfResources, tnThread);
  498.       Result := S_OK;
  499.    except
  500.       DoneNotifications(lpft);
  501.       Result := E_FAIL;
  502.    end;
  503. end;
  504. {------------------------------------------------------------------------}
  505. procedure DSSetHWND(hWaveOut: HWAVEOUT; hw: HWND);
  506. begin
  507.    if hWaveOut = 0 then DSoundHW := hw
  508.    else if LoadDSoundDLL then
  509.            PMMft(hWaveOut)^.lpDS.SetCooperativeLevel(hw,DSSCL_PRIORITY);
  510. end;
  511. {------------------------------------------------------------------------}
  512. function DSDirectSoundCreate(lpGUID: PGUID; var lpDS: IDirectSound;
  513.                              pUnkOuter: IUnknown): HRESULT;
  514. Var
  515.    lpft: PMMft;
  516. begin
  517.    lpft := lpMMft;
  518.    while (lpft <> Nil) do
  519.    begin
  520.       if (lpft^.lpDS <> nil) and (lpft^.lpGUID = lpGUID) then
  521.       begin
  522.          lpDS   := lpft^.lpDS;
  523.          Result := 0;
  524.          exit;
  525.       end;
  526.       lpft := lpft^.NextMMft;
  527.    end;
  528.    Result := DirectSoundCreate(lpGUID, lpDS, nil);
  529. end;
  530. {------------------------------------------------------------------------}
  531. function DSCreatePrimaryBuffer(hWaveOut: HWAVEOUT; lpFormat: PWaveFormatEx): HRESULT;
  532. var
  533.    lpft: PMMft;
  534.    wfx: TWaveFormatEx;
  535.    BufferDesc: TDSBUFFERDESC;
  536.    Bits, Channels, Rate: integer;
  537. begin
  538.    lpft := lpMMft;
  539.    while (lpft <> Nil) do
  540.    begin
  541.       if (lpft^.lpDS = PMMft(hWaveOut)^.lpDS) and (lpft^.lpDSP <> nil) then
  542.       begin
  543.          PMMft(hWaveOut)^.lpDSP := lpft^.lpDSP;
  544.          PMMft(hWaveOut)^.lpDSP.GetFormat(@wfx, sizeOf(wfx), nil);
  545.          Bits := Max(wfx.wBitsPerSample,lpFormat^.wBitsPerSample);
  546.          Channels := Max(wfx.nChannels,lpFormat^.nChannels);
  547.          Rate := Max(wfx.nSamplesPerSec,lpFormat^.nSamplesPerSec);
  548.          pcmBuildWaveHeader(@wfx, Bits,Channels,Rate);
  549.          PMMft(hWaveOut)^.lpDSP.SetFormat(@wfx);
  550.          Result := DS_OK;
  551.          exit;
  552.       end;
  553.       lpft := lpft^.NextMMft;
  554.    end;
  555.    FillChar(BufferDesc, SizeOf(TDSBUFFERDESC), 0);
  556.    with BufferDesc do
  557.    begin
  558.       dwSize := SizeOf(TDSBUFFERDESC);
  559.       dwFlags := DSBCAPS_PRIMARYBUFFER;
  560.    end;
  561.    Result := PMMft(hWaveOut)^.lpDS.CreateSoundBuffer(BufferDesc,PMMft(hWaveOut)^.lpDSP,nil);
  562.    if Result = DS_OK then
  563.    begin
  564.       PMMft(hWaveOut)^.lpDSP.SetFormat(lpFormat);
  565.       PMMft(hWaveOut)^.lpDSP.Play(0,0,DSBPLAY_LOOPING);
  566.    end;
  567. end;
  568. {------------------------------------------------------------------------}
  569. function DSWaveOutOpen(lphWaveOut: PHWAVEOUT; uDeviceID: UINT;
  570.                        lpFormat: PWaveFormatEx;
  571.                        dwCallback, dwInstance, dwFlags: DWORD): MMRESULT;
  572. Label DSOPEN_EXIT,cont;
  573. Var
  574.    hw: HWND;
  575.    p1, p2: PChar;
  576.    l1, l2: DWORD;
  577.    lpft,lpft2: PMMFt;
  578.    DSBDescr: TDSBUFFERDESC;
  579.    DSCaps: TDSCAPS;
  580.    Proc,CurProc: DWORD;
  581.    m: integer;
  582. begin
  583.    Result := 1;
  584.    if (Not LoadDSoundDLL) or (lpFormat = Nil) or
  585.       (dwFlags and WAVE_ALLOWSYNC = WAVE_ALLOWSYNC) then exit;
  586.    if (DSoundHW <> 0) then hw := DSoundHW
  587.    else
  588.    begin
  589.       hw := GetTopWindow(0);
  590.       CurProc := GetCurrentProcessId;
  591.       while (hw <> 0) do
  592.       begin
  593.          GetWindowThreadProcessId(hw, @Proc);
  594.          if (Proc = CurProc) then break;
  595.          hw := GetWindow(hw, GW_HWNDNEXT);
  596.       end;
  597.       if (hw = 0) then hw := GetDesktopWindow;
  598.    end;
  599.    lpft := GlobalAllocPtr(GHND,sizeOf(TMMft));
  600.    if (lpft = Nil) then exit;
  601.    FillChar(lpft^, sizeOf(TMMft), 0);
  602.    if DSDirectSoundCreate(PGUID(uDeviceID), lpft^.lpDS, Nil) <> DS_OK then
  603.    begin
  604.       GlobalFreePtr(lpft);
  605.       exit;
  606.    end;
  607.    lpft^.lpGUID := PGUID(uDeviceID);
  608.    lpft^.lpDS.SetCooperativeLevel(hw,DSSCL_PRIORITY);
  609.    FillChar(DSBDescr, sizeOf(DSBDescr), 0);
  610.    DSBDescr.lpwfxFormat := lpFormat;
  611.    DSBDescr.dwSize := sizeOf(TDSBUFFERDESC);
  612.    DSBDescr.dwFlags := DSBCAPS_STICKYFOCUS or DSBCAPS_GETCURRENTPOSITION2 or
  613.                        DSBCAPS_CTRLPOSITIONNOTIFY or DSBCAPS_GLOBALFOCUS;
  614.    if (dwFlags and DS_NEEDVOLUME = DS_NEEDVOLUME) then
  615.       DSBDescr.dwFlags := DSBDescr.dwFlags or DSBCAPS_CTRLVOLUME;
  616.    if (dwFlags and DS_NEEDPAN = DS_NEEDPAN) then
  617.       DSBDescr.dwFlags := DSBDescr.dwFlags or DSBCAPS_CTRLPAN;
  618.    if (dwFlags and DS_NEEDFREQ = DS_NEEDFREQ) then
  619.       DSBDescr.dwFlags := DSBDescr.dwFlags or DSBCAPS_CTRLFREQUENCY;
  620.    { look if we have a emulated device }
  621.    FillChar(DSCaps, SizeOf(TDSCAPS), 0);
  622.    DSCaps.dwSize := SizeOf(TDSCAPS);
  623.    lpft^.lpDS.GetCaps(DSCaps);
  624.    lpft^.Emulated := (DSCaps.dwFlags and DSCAPS_EMULDRIVER) > 0;
  625.    lpft^.EachTick := (lpFormat^.nAvgBytesPerSec div (TIMERRATE div 2)) and not 3;
  626.    lpft^.BufferSize := lpft^.Eachtick * BUFFER_PARTS;
  627.    if lpft^.Emulated then lpft^.BufferSize := lpft^.BufferSize*2;
  628.    DSBDescr.dwBufferBytes := lpft^.BufferSize;
  629.    if lpFormat^.wBitsPerSample = 8 then
  630.       lpft^.SilenceVal := $80
  631.    else
  632.       lpft^.SilenceVal := 0;
  633.    lpft^.DataRate := lpFormat^.nAvgBytesPerSec;
  634.    if lpft^.lpDS.CreateSoundBuffer(DSBDescr,lpft^.lpDSB,Nil) <> DS_OK then
  635.    begin
  636.       { ev. older DSound version which doesn't support DSBCAPS_STICKYFOCUS }
  637.       DSBDescr.dwFlags := DSBDescr.dwFlags and not (DSBCAPS_STICKYFOCUS + DSBCAPS_GLOBALFOCUS);
  638.       if lpft^.lpDS.CreateSoundBuffer(DSBDescr,lpft^.lpDSB,Nil) <> DS_OK then
  639.          goto DSOPEN_EXIT;
  640.    end;
  641.    if (dwFlags and WAVE_FORMAT_QUERY = WAVE_FORMAT_QUERY) then
  642.    begin
  643.       Result := 0;
  644.       goto DSOPEN_EXIT;
  645.    end;
  646.    if InitializeNotifications(lpft) = E_FAIL then
  647.       goto DSOPEN_EXIT;
  648.    m := -10000;
  649.    lpft^.lpDSB.SetVolume(m);
  650.    if lpft^.lpDSB.Lock(0, lpft^.BufferSize,p1,l1,p2,l2,0) <> DS_OK then
  651.       goto DSOPEN_EXIT;
  652.    if (p1 <> Nil) then FillChar(p1^,l1, lpft^.SilenceVal);
  653.    if (p2 <> Nil) then FillChar(p2^,l2, lpft^.SilenceVal);
  654.    if lpft^.lpDSB.Unlock(p1,l1,p2,l2) <> DS_OK then
  655.       goto DSOPEN_EXIT;
  656.    if (dwFlags and CALLBACK_FUNCTION = CALLBACK_FUNCTION) then
  657.    begin
  658.       if (dwCallBack <> 0) then lpft^.CallBack := dwCallBack
  659.       else goto DSOPEN_EXIT;
  660.       lpft^.CBInstance := dwInstance;
  661.       lpft^.CallBackMode := CALLBACK_FUNCTION;
  662.    end
  663.    else if (dwFlags and CALLBACK_WINDOW = CALLBACK_WINDOW) then
  664.    begin
  665.       if (dwCallBack <> 0) then lpft^.CallBack := dwCallBack
  666.       else goto DSOPEN_EXIT;
  667.       lpft.CallBackMode := CALLBACK_WINDOW;
  668.    end
  669.    else if (dwFlags and CALLBACK_THREAD = CALLBACK_THREAD) then
  670.    begin
  671.       if (dwCallBack <> 0) then lpft^.CallBack := dwCallBack
  672.       else goto DSOPEN_EXIT;
  673.       lpft.CallBackMode := CALLBACK_THREAD;
  674.    end
  675.    else goto DSOPEN_EXIT;
  676.    InitCritical;
  677.    lpft^.NextMMft := lpMMft;
  678.    lpMMft := lpft;
  679.    lphWaveOut^ := HWAVEOUT(lpft);
  680.    NotifyMessage(lpft, MM_WOM_OPEN, lphWaveOut^, 0);
  681.    Result := 0;
  682.    exit;
  683. DSOPEN_EXIT:
  684.    DoneNotifications(lpft);
  685.    if (lpft^.lpDSB <> Nil) then lpft^.lpDSB.Release;
  686.    if (lpMMft = Nil) then lpft^.lpDS.Release
  687.    else
  688.    begin
  689.       lpft2 := lpMMft;
  690.       while lpft2 <> nil do
  691.       begin
  692.          if lpft2^.lpGUID = lpft^.lpGUID then goto cont;
  693.          lpft2 := lpft2^.NextMMft;
  694.       end;
  695.       lpft^.lpDS.Release;
  696.    end;
  697. cont:
  698.    GlobalFreePtr(lpft);
  699.    lphWaveOut^ := 0;
  700. end;
  701. {------------------------------------------------------------------------}
  702. { Used internally by DSWaveOutWrite, DSWaveOutRestart, DSWaveOutClose    }
  703. function Timer_Addref(lpft: PMMft): HResult;
  704. begin
  705.    Result := S_OK;
  706.    inc(TimerInit);
  707.    if (TimerInit = 1) then
  708.    begin
  709.       TimeBeginPeriod(1);
  710.       TimerID := TimeSetEvent(1000 div TIMERRATE, 0, @TimerFunc, 0, TIME_PERIODIC);
  711.       if TimerID = 0 then
  712.       begin
  713.         lpft^.lpDSB.Stop;
  714.         dec(TimerInit);
  715.         LeaveCritical;
  716.         Result := E_FAIL;
  717.       end;
  718.    end;
  719. end;
  720. {------------------------------------------------------------------------}
  721. procedure Timer_Release;
  722. begin
  723.    if (TimerInit > 0) then
  724.    begin
  725.       dec(TimerInit);
  726.       if (TimerInit = 0) then
  727.       begin
  728.         TimeKillEvent(TimerID);
  729.         TimeEndPeriod(1);
  730.       end;
  731.    end;
  732. end;
  733. {------------------------------------------------------------------------}
  734. function DSWaveOutClose(hWaveOut: HWAVEOUT): MMRESULT;
  735. Label cont,cont2;
  736. var
  737.    lpft,lpft2: PMMFt;
  738.    m: integer;
  739. begin
  740.    EnterCritical;
  741.    Result := 1;
  742.    lpft := lpMMft;
  743.    if (integer(lpft) = hWaveOut) then lpMMft := lpMMft^.NextMMft
  744.    else
  745.    begin
  746.       while (lpft^.NextMMft <> Nil) do
  747.       begin
  748.          if (integer(lpft^.NextMMft) = hWaveOut) then
  749.          begin
  750.             lpft^.NextMMft := lpft^.NextMMft^.NextMMft;
  751.             goto cont;
  752.          end;
  753.          lpft := lpft^.NextMMft;
  754.       end;
  755.       LeaveCritical;
  756.       exit;
  757.    end;
  758. cont:
  759.    DSWaveOutReset(hWaveOut);
  760.    lpft := PMMft(hWaveOut);
  761.    m := -10000;
  762.    lpft^.lpDSB.SetVolume(m);
  763.    lpft^.lpDSB.SetCurrentPosition(0);
  764.    lpft^.lpDSB.Stop;
  765.    if (lpft^.Started) and (lpft^.NtfResources <> AllNtfResources) then
  766.        Timer_Release;
  767.    DoneNotifications(lpft);
  768.    lpft^.lpDSB.Release;
  769.    NotifyMessage(lpft, MM_WOM_CLOSE, Longint(lpft), 0);
  770.    if (lpMMft = Nil) then
  771.    begin
  772.       if (lpft^.lpDSP <> nil) then lpft^.lpDSP.Release;
  773.       if (lpft^.lpDS <> nil) then lpft^.lpDS.Release;
  774.    end
  775.    else
  776.    begin
  777.       lpft2 := lpMMft;
  778.       while lpft2 <> nil do
  779.       begin
  780.          if lpft2^.lpGUID = lpft^.lpGUID then goto cont2;
  781.          lpft2 := lpft2^.NextMMft;
  782.       end;
  783.       if (lpft^.lpDSP <> nil) then lpft^.lpDSP.Release;
  784.       if (lpft^.lpDS <> nil) then lpft^.lpDS.Release;
  785.    end;
  786. cont2:
  787.    LeaveCritical;
  788.    DoneCritical;
  789.    GlobalFreePtr(lpft);
  790.    DSoundHW := 0;
  791.    Result := 0;
  792. end;
  793. {------------------------------------------------------------------------}
  794. function DSWaveOutPrepareHeader(hWaveOut: HWAVEOUT; lpWaveHdr: PWaveHdr;
  795.                                 uSize: UINT): MMRESULT;
  796. begin
  797.    lpWaveHdr^.dwFlags := WHDR_PREPARED;
  798.    Result := 0;
  799. end;
  800. {------------------------------------------------------------------------}
  801. function DSWaveOutUnprepareHeader(hWaveOut: HWAVEOUT; lpWaveHdr: PWaveHdr;
  802.                                   uSize: UINT): MMRESULT;
  803. begin
  804.    if (PMMWaveHdr(lpWaveHdr)^.dwUser2 <> 0) then
  805.       TimeKillEvent(PMMWaveHdr(lpWaveHdr)^.dwUser2);
  806.    PMMWaveHdr(lpWaveHdr)^.dwUser2 := 0;
  807.    lpWaveHdr^.dwFlags := WHDR_DONE;
  808.    Result := 0;
  809. end;
  810. {------------------------------------------------------------------------}
  811. function DSWaveOutWrite(hWaveOut: HWAVEOUT; lpWaveHdr: PWaveHdr;
  812.                         uSize: UINT): MMRESULT;
  813. var
  814.    i,m: integer;
  815.    lpft: PMMft;
  816.    lpwh: PWaveHdr;
  817. begin
  818.    EnterCritical;
  819.    Result := 1;
  820.    lpft := PMMFt(hWaveOut);
  821.    lpWaveHdr^.reserved := 0;
  822.    lpWaveHdr^.lpNext := Nil;
  823.    lpWaveHdr^.dwFlags := lpWaveHdr^.dwFlags and not WHDR_DONE;
  824.    if (lpft^.First = Nil) then
  825.    begin
  826.       lpft^.First := lpWaveHdr;
  827.    end
  828.    else
  829.    begin
  830.       lpwh := lpft^.First;
  831.       while (lpwh^.lpNext <> Nil) do lpwh := lpwh^.lpNext;
  832.       lpwh^.lpNext := lpWaveHdr;
  833.    end;
  834.    if (not lpft^.Started) and (not lpft^.Paused) then
  835.    begin
  836.       if (lpft^.NtfResources <> AllNtfResources) and
  837.          (Timer_Addref(lpft) <> S_OK) then exit;
  838.       lpft^.UpdateVolume := True;
  839.       m := -10000;
  840.       lpft^.lpDSB.SetVolume(m);
  841.       lpft^.lpDSB.SetCurrentPosition(0);
  842.       lpft^.Started := True;
  843.       LeaveCritical;
  844.       { pre-fill the SoundBuffer }
  845.       for i := 0 to BUFFER_PRELOAD-1 do ProcessData(lpft);
  846.       lpft^.lpDSB.Play(0,0,DSBPLAY_LOOPING);
  847.       Result := 0;
  848.       exit;
  849.    end;
  850.    LeaveCritical;
  851.    Result := 0;
  852. end;
  853. {------------------------------------------------------------------------}
  854. function DSWaveOutPause(hWaveOut: HWAVEOUT): MMRESULT;
  855. var
  856.    lpft: PMMft;
  857.    m: integer;
  858. begin
  859.    EnterCritical;
  860.    lpft := PMMFt(hWaveOut);
  861.    lpft^.Paused := True;
  862.    lpft^.lpDSB.Stop;
  863.    m :=-10000;
  864.    lpft^.lpDSB.SetVolume(m);
  865.    LeaveCritical;
  866.    Result := 0;
  867. end;
  868. {------------------------------------------------------------------------}
  869. function DSWaveOutRestart(hWaveOut: HWAVEOUT): MMRESULT;
  870. var
  871.    i: integer;
  872.    lpft: PMMft;
  873. begin
  874.    Result := 1;
  875.    lpft := PMMFt(hWaveOut);
  876.    if (lpft = nil) or not lpft^.Paused then exit;
  877.    EnterCritical;
  878.    if (not lpft^.Started) then
  879.        if (lpft^.NtfResources <> AllNtfResources) and
  880.           (Timer_Addref(lpft) <> S_OK) then
  881.            exit;
  882.    lpft^.Paused := False;
  883.    lpft^.lpDSB.SetVolume(lpft^.Volume);
  884.    lpft^.lpDSB.SetCurrentPosition(0);
  885.    lpft^.Started := True;
  886.    LeaveCritical;
  887.    { pre-fill the SoundBuffer }
  888.    for i := 0 to BUFFER_PRELOAD-1 do ProcessData(lpft);
  889.    lpft^.lpDSB.Play(0,0,DSBPLAY_LOOPING);
  890.    Result := 0;
  891. end;
  892. {------------------------------------------------------------------------}
  893. function DSWaveOutReset(hWaveOut: HWAVEOUT): MMRESULT;
  894. Label ResetExit;
  895. var
  896.    p1, p2: PChar;
  897.    l1, l2: DWORD;
  898.    lpft: PMMft;
  899.    lpwh: PWAVEHDR;
  900.    m: integer;
  901. begin
  902.    EnterCritical;
  903.    Result := 0;
  904.    lpft := PMMFt(hWaveOut);
  905.    if (lpft^.Started) and (lpft^.NtfResources <> AllNtfResources) then
  906.        Timer_Release;
  907.    lpft^.Started := False;
  908.    m := -10000;
  909.    lpft^.lpDSB.SetVolume(m);
  910.    lpft^.lpDSB.Stop;
  911.    lpft^.lpDSB.SetCurrentPosition(0);
  912.    lpwh := lpft^.First;
  913.    while (lpwh <> nil) do
  914.    begin
  915.       lpwh^.dwFlags := lpwh^.dwFlags or WHDR_DONE;
  916.        if (PMMWaveHdr(lpwh)^.dwUser2 <> 0) then
  917.           TimeKillEvent(PMMWaveHdr(lpwh)^.dwUser2);
  918.       PMMWaveHdr(lpwh)^.dwUser2 := 0;
  919.       NotifyMessage(lpft, MM_WOM_DONE, Longint(lpft), Longint(lpwh));
  920.       lpwh := lpwh^.lpNext;
  921.    end;
  922.    lpft^.First       := nil;
  923.    lpft^.TotalWritten:= 0;
  924.    lpft^.TotalPlayed := 0;
  925.    lpft^.LastPlayPos := 0;
  926.    lpft^.NextPos     := 0;
  927.    if lpft^.lpDSB.Lock(0, lpft^.BufferSize,p1,l1,p2,l2,0) <> DS_OK then
  928.       goto ResetExit;
  929.    if (p1 <> Nil) then FillChar(p1^,l1, lpft^.SilenceVal);
  930.    if (p2 <> Nil) then FillChar(p2^,l2, lpft^.SilenceVal);
  931.    if lpft^.lpDSB.Unlock(p1,l1,p2,l2) <> DS_OK then
  932.       goto ResetExit;
  933. ResetExit:
  934.    LeaveCritical;
  935. end;
  936. {------------------------------------------------------------------------}
  937. function DSWaveOutGetPosition(hWaveOut: HWAVEOUT; lpInfo: PMMTime;
  938.                               uSize: UINT): MMRESULT;
  939. var
  940.    wfx: TWaveFormatEx;
  941.    lpft: PMMft;
  942.    dwPlay,dwWrite: DWORD;
  943. begin
  944.    EnterCritical;
  945.    Result := 1;
  946.    lpft := PMMFt(hWaveOut);
  947.    if (lpft <> nil) and (lpInfo^.wType = Time_Samples) then
  948.    begin
  949.       if lpft^.lpDSB.GetFormat(@wfx, sizeOf(wfx), nil) = DS_OK then
  950.       begin
  951.          lpft^.lpDSB.GetCurrentPosition(dwPlay,dwWrite);
  952.          if (dwPlay < lpft^.LastPlayPos) then
  953.              dwPlay := lpft^.TotalPlayed+(lpft^.BufferSize-lpft^.LastPlayPos+dwPlay)
  954.          else
  955.              dwPlay := lpft^.TotalPlayed+(dwPlay-lpft^.LastPlayPos);
  956.          lpInfo^.Sample := wioBytesToSamples(@wfx,dwPlay);
  957.          Result := 0;
  958.       end;
  959.    end
  960.    else lpInfo^.wType := Time_Samples;
  961.    LeaveCritical;
  962. end;
  963. {------------------------------------------------------------------------}
  964. function DSWaveOutSetVolume(hWaveOut: HWAVEOUT; dwVolume: DWORD): MMRESULT;
  965. var
  966.    lpft: PMMft;
  967. begin
  968.    EnterCritical;
  969.    Result := 1;
  970.    lpft := PMMFt(hWaveOut);
  971.    if lpft^.lpDSB.SetVolume(dwVolume) = DS_OK then
  972.    begin
  973.      lpft^.Volume := dwVolume;
  974.      Result := 0;
  975.    end;
  976.    LeaveCritical;
  977. end;
  978. {------------------------------------------------------------------------}
  979. function DSWaveOutGetVolume(hWaveOut: HWAVEOUT; lpdwVolume: PDWORD): MMRESULT;
  980. var
  981.    lpft: PMMft;
  982. begin
  983.    EnterCritical;
  984.    Result := 1;
  985.    lpft := PMMFt(hWaveOut);
  986.    if lpft^.lpDSB.GetVolume(lpdwVolume^) = DS_OK then Result := 0;
  987.    LeaveCritical;
  988. end;
  989. {------------------------------------------------------------------------}
  990. function DSWaveOutSetPan(hWaveOut: HWAVEOUT; dwPan: DWORD): MMRESULT;
  991. var
  992.    lpft: PMMft;
  993. begin
  994.    EnterCritical;
  995.    Result := 1;
  996.    lpft := PMMFt(hWaveOut);
  997.    if lpft^.lpDSB.SetPan(dwPan) = DS_OK then Result := 0;
  998.    LeaveCritical;
  999. end;
  1000. {------------------------------------------------------------------------}
  1001. function DSWaveOutGetPan(hWaveOut: HWAVEOUT; lpdwPan: PDWORD): MMRESULT;
  1002. var
  1003.    lpft: PMMft;
  1004. begin
  1005.    EnterCritical;
  1006.    Result := 1;
  1007.    lpft := PMMFt(hWaveOut);
  1008.    if lpft^.lpDSB.GetPan(lpdwPan^) = DS_OK then Result := 0;
  1009.    LeaveCritical;
  1010. end;
  1011. {------------------------------------------------------------------------}
  1012. function DSWaveOutSetPlaybackRate(hWaveOut: HWAVEOUT; dwRate: DWORD): MMRESULT;
  1013. var
  1014.    lpft: PMMft;
  1015. begin
  1016.    EnterCritical;
  1017.    Result := 1;
  1018.    lpft := PMMFt(hWaveOut);
  1019.    if lpft^.lpDSB.SetFrequency(dwRate) = DS_OK then Result := 0;
  1020.    LeaveCritical;
  1021. end;
  1022. {------------------------------------------------------------------------}
  1023. function DSWaveOutGetPlaybackRate(hWaveOut: HWAVEOUT; lpdwRate: PDWORD): MMRESULT;
  1024. var
  1025.    lpft: PMMft;
  1026. begin
  1027.    EnterCritical;
  1028.    Result := 1;
  1029.    lpft := PMMFt(hWaveOut);
  1030.    if lpft^.lpDSB.GetFrequency(lpdwRate^) = DS_OK then Result := 0;
  1031.    LeaveCritical;
  1032. end;
  1033. end.