?? dsoundrenderer.pas
字號:
(*********************************************************************
* *
* The contents of this file are used with permission, subject to *
* the Mozilla Public License Version 1.1 (the "License"); you may *
* not use this file except in compliance with the License. You may *
* obtain a copy of the License at *
* http://www.mozilla.org/MPL/MPL-1.1.html *
* *
* Software distributed under the License is distributed on an *
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or *
* implied. See the License for the specific language governing *
* rights and limitations under the License. *
* *
* (C) 2004 Milenko Mitrovic <dcoder@dsp-worx.de> *
* *
*********************************************************************)
unit DSoundRenderer;
interface
uses
BaseClass, DirectSound, ActiveX, Math, Windows, SysUtils, Classes,
DirectShow9, MMSystem, PropSettings, DSoundDevices, DSUtil;
const
CLSID_DelphiDSoundRenderer: TGUID = '{093A27B5-3AA1-4375-9389-71EB9D62BA97}';
PREBUFFER_SIZE_MS = 1500; // Buffersize in MilliSeconds of PreBuffering
BUFFER_SIZE_MS = 5000; // Buffersize in MilliSeconds
type
TDelphiDSoundRenderer = class(TBCBaseRenderer, IReferenceClock, IBasicAudio,
IPersist, IMediaFilter, ISpecifyPropertyPages)
private
fDispatch : TBCBaseDispatch;
fFormat : TWaveFormatExtensible;
fCurrentBufferSize : DWORD;
fPreBufferSize : DWORD;
fRefClock : TBCSystemClock;
fVolume : integer;
fBalance : integer;
fDevice : IDirectSound8;
fBuffer : IDirectSoundBuffer8;
fPos : DWORD;
fBufferSize : DWORD;
(*** Misc methods ***)
function CreateDirectSoundBuffer(MediaType : PAMMediaType) : HRESULT;
public
(*** TBCBaseRenderer methods ***)
constructor Create(ObjName: String; Unk: IUnknown; out hr : HResult);
constructor CreateFromFactory(Factory: TBCClassFactory; const Controller: IUnknown); override;
destructor Destroy; override;
function CheckMediaType(MediaType: PAMMediaType): HResult; override;
function DoRenderSample(MediaSample: IMediaSample): HResult; override;
function SetMediaType(MediaType: PAMMediaType): HResult; override;
function Run(StartTime: TReferenceTime): HResult; override; stdcall;
function Stop: HResult; override; stdcall;
function Pause: HResult; override; stdcall;
function Receive(MediaSample: IMediaSample): HResult; override;
function BeginFlush: HResult; override;
function PrepareReceive(MediaSample: IMediaSample): HResult; override;
(*** IDispatch methods ***)
function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
function GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
(*** IBasicAudio methods ***)
function put_Volume(lVolume: Longint): HResult; stdcall;
function get_Volume(out plVolume: Longint): HResult; stdcall;
function put_Balance(lBalance: Longint): HResult; stdcall;
function get_Balance(out plBalance: Longint): HResult; stdcall;
(*** IReferenceClock methods ***)
function GetTime(out pTime: int64): HResult; stdcall;
function AdviseTime(rtBaseTime, rtStreamTime: int64; hEvent: THandle; out pdwAdviseCookie: DWORD): HResult; stdcall;
function AdvisePeriodic(const rtStartTime, rtPeriodTime: int64; hSemaphore: THandle; out pdwAdviseCookie: DWORD): HResult; stdcall;
function Unadvise(dwAdviseCookie: DWORD): HResult; stdcall;
(*** ISpecifyPropertyPages methods ***)
function GetPages(out pages: TCAGUID): HResult; stdcall;
end;
implementation
{*** IDispatch methods *** taken from CBasicAudio *** ctlutil.cpp *************}
function TDelphiDSoundRenderer.GetTypeInfoCount(out Count: Integer): HResult; stdcall;
begin
Result := fDispatch.GetTypeInfoCount(Count);
end;
function TDelphiDSoundRenderer.GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
begin
Result := fDispatch.GetTypeInfo(IID_IBasicAudio,Index,LocaleID,TypeInfo);
end;
function TDelphiDSoundRenderer.GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
begin
Result := fDispatch.GetIDsOfNames(IID_IBasicAudio,Names,NameCount,LocaleID,DispIDs);
end;
function TDelphiDSoundRenderer.Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
var
pti : ITypeInfo;
begin
if not IsEqualGUID(GUID_NULL,IID) then
begin
Result := DISP_E_UNKNOWNINTERFACE;
Exit;
end;
Result := GetTypeInfo(0, LocaleID, pti);
if FAILED(Result) then Exit;
Result := pti.Invoke(Pointer(Self as IBasicAudio),DispID,Flags,
TDispParams(Params),VarResult,ExcepInfo,ArgErr);
pti := nil;
end;
{*** IBasicAudio methods ******************************************************}
function TDelphiDSoundRenderer.put_Volume(lVolume: Longint): HResult; stdcall;
begin
fVolume := EnsureRange(lVolume,-10000,0);
if Assigned(fBuffer) then fBuffer.SetVolume(fVolume);
Result := S_OK;
end;
function TDelphiDSoundRenderer.get_Volume(out plVolume: Longint): HResult; stdcall;
begin
plVolume := fVolume;
Result := S_OK;
end;
function TDelphiDSoundRenderer.put_Balance(lBalance: Longint): HResult; stdcall;
begin
fBalance := EnsureRange(lBalance,-10000,10000);
if Assigned(fBuffer) then fBuffer.SetPan(fBalance);
Result := S_OK;
end;
function TDelphiDSoundRenderer.get_Balance(out plBalance: Longint): HResult; stdcall;
begin
plBalance := fBalance;
Result := S_OK;
end;
{*** IReferenceClock methods **************************************************}
function TDelphiDSoundRenderer.GetTime(out pTime: int64): HResult; stdcall;
begin
Result := fRefClock.GetTime(pTime);
end;
function TDelphiDSoundRenderer.AdviseTime(rtBaseTime, rtStreamTime: int64; hEvent: THandle; out pdwAdviseCookie: DWORD): HResult; stdcall;
begin
Result := fRefClock.AdviseTime(rtBaseTime,rtStreamTime,hEvent,pdwAdviseCookie);
end;
function TDelphiDSoundRenderer.AdvisePeriodic(const rtStartTime, rtPeriodTime: int64; hSemaphore: THandle; out pdwAdviseCookie: DWORD): HResult; stdcall;
begin
Result := fRefClock.AdvisePeriodic(rtStartTime,rtPeriodTime,hSemaphore,pdwAdviseCookie);
end;
function TDelphiDSoundRenderer.Unadvise(dwAdviseCookie: DWORD): HResult; stdcall;
begin
Result := fRefClock.Unadvise(dwAdviseCookie);
end;
{*** ISpecifyPropertyPages methods ********************************************}
function TDelphiDSoundRenderer.GetPages(out pages: TCAGUID): HResult; stdcall;
begin
Pages.cElems := 1;
Pages.pElems := CoTaskMemAlloc(sizeof(TGUID) * Pages.cElems);
if (Pages.pElems = nil) then
begin
Result := E_OUTOFMEMORY;
Exit;
end;
Pages.pElems^[0] := CLSID_PropPageSettings;
Result := S_OK;
end;
{*** TBCBaseRenderer methods **************************************************}
constructor TDelphiDSoundRenderer.Create(ObjName: String; Unk: IUnknown; out hr: HResult);
var
Res : HRESULT;
Guid : TGUID;
begin
inherited Create(CLSID_DelphiDSoundRenderer, 'Delphi DirectSound Renderer', Unk, hr);
Guid := GetSavedDevice;
Res := DirectSoundCreate8(@Guid,fDevice,nil);
if (Res <> DS_OK) or not Assigned(fDevice) then
begin
hr := E_FAIL;
Exit;
end;
fDevice.SetCooperativeLevel(GetDesktopWindow,DSSCL_PRIORITY);
fDispatch := TBCBaseDispatch.Create;
fRefClock := TBCSystemClock.Create('TBCSystemClock',nil,hr);
fPos := 0;
fVolume := 0;
fBalance := 0;
fCurrentBufferSize := 0;
end;
constructor TDelphiDSoundRenderer.CreateFromFactory(Factory: TBCClassFactory; const Controller: IUnknown);
var
hr: HRESULT;
begin
Create(Factory.Name, Controller, hr);
end;
destructor TDelphiDSoundRenderer.Destroy;
begin
if Assigned(fBuffer) then fBuffer := nil;
if Assigned(fDevice) then fDevice := nil;
if Assigned(fRefClock) then FreeAndNil(fRefClock);
if Assigned(fDispatch) then FreeAndNil(fDispatch);
inherited Destroy;
end;
function TDelphiDSoundRenderer.PrepareReceive(MediaSample: IMediaSample): HResult;
begin
Result := inherited PrepareReceive(MediaSample);
FSignalTime := FSignalTime + (PREBUFFER_SIZE_MS * UNITS div MILLISECONDS);
end;
function TDelphiDSoundRenderer.Receive(MediaSample: IMediaSample): HResult;
var
lStatus : DWORD;
mi, mo : TReferenceTime;
lDelayMS : Int64;
begin
if fPreBufferSize > fCurrentBufferSize then
begin
MediaSample.SetTime(nil,nil);
fCurrentBufferSize := fCurrentBufferSize + int64(MediaSample.GetActualDataLength);
Result := inherited Receive(MediaSample);
Exit;
end;
if MediaSample.GetTime(mi,mo) = S_OK then
begin
lDelayMS := fCurrentBufferSize * UNITS div fFormat.Format.nAvgBytesPerSec;
mo := mo - lDelayMS;
mi := mi - lDelayMS;
MediaSample.SetTime(@mi,@mo);
end;
Result := inherited Receive(MediaSample);
if fPreBufferSize < fCurrentBufferSize then
begin
if Assigned(fBuffer) then
begin
fBuffer.GetStatus(lStatus);
if lStatus and DSBSTATUS_PLAYING = 0 then
begin
if (FState = State_Running) and Assigned(fBuffer)
then fBuffer.Play(0,0,DSBPLAY_LOOPING);
end;
end;
end;
end;
function TDelphiDSoundRenderer.BeginFlush: HResult;
begin
fBuffer.Stop;
fPos := 0;
fBuffer.SetCurrentPosition(0);
fCurrentBufferSize := 0;
Result := inherited BeginFlush;
end;
function TDelphiDSoundRenderer.Run(StartTime: TReferenceTime): HResult;
begin
Result := inherited Run(StartTime);
end;
function TDelphiDSoundRenderer.Stop: HResult;
begin
Result := inherited Stop;
if Assigned(fBuffer) then fBuffer.Stop;
end;
function TDelphiDSoundRenderer.Pause: HResult;
begin
Result := inherited Pause;
if Assigned(fBuffer) then fBuffer.Stop;
end;
function TDelphiDSoundRenderer.CheckMediaType(MediaType: PAMMediaType): HResult;
begin
if (MediaType = nil) then
begin
Result := E_POINTER;
Exit;
end;
if not IsEqualGUID(MediaType.majortype, MEDIATYPE_Audio) or
(not IsEqualGUID(MediaType.subtype, MEDIASUBTYPE_PCM) and
not IsEqualGUID(MediaType.subtype, MEDIASUBTYPE_IEEE_FLOAT) and
not IsEqualGUID(MediaType.subtype, MEDIASUBTYPE_DOLBY_AC3_SPDIF) and
not IsEqualGUID(MediaType.subtype, MEDIASUBTYPE_RAW_SPORT) and
not IsEqualGUID(MediaType.subtype, MEDIASUBTYPE_SPDIF_TAG_241h) and
not IsEqualGUID(MediaType.subtype, MEDIASUBTYPE_DRM_Audio)) or
not IsEqualGUID(MediaType.formattype, FORMAT_WaveFormatEx) then
begin
Result := E_INVALIDARG;
Exit;
end;
Result := S_OK;
end;
function TDelphiDSoundRenderer.DoRenderSample(MediaSample: IMediaSample): HResult;
var
pmt : PAMMediaType;
Buffer : PByte;
Size : Integer;
hr : HRESULT;
p1, p2 : Pointer;
s1, s2 : Cardinal;
begin
FLock.Lock;
try
if MediaSample.GetMediaType(pmt) = S_OK then
begin
hr := CheckMediaType(pmt);
if hr = S_OK then hr := CreateDirectSoundBuffer(pmt);
if hr <> DS_OK then
begin
Result := E_FAIL;
Exit;
end;
fCurrentBufferSize := 0;
fPos := 0;
end;
MediaSample.GetPointer(Buffer);
Size := MediaSample.GetActualDataLength;
if Assigned(fBuffer) then
begin
// Lock the Buffer
hr := fBuffer.Lock(fPos,Size,p1,s1,p2,s2,0);
// If the Buffer is lost, then Restore it and Lock it again
if hr = DSERR_BUFFERLOST then
begin
fBuffer.Restore;
fBuffer.Lock(fPos,Size,p1,s1,p2,s2,0);
end;
// store the next WriteCursor Position
inc(fPos, Size);
if fPos > fBufferSize then dec(fPos, fBufferSize);
// Copy the Buffer from the MediaSample to the Buffer(s)
Move(Buffer^,p1^,s1);
if s2 > 0 then Move(PChar(Buffer)[s1],p2^,s2);
fBuffer.Unlock(p1,s1,p2,s2);
end;
finally
fLock.UnLock;
end;
Result := NOERROR;
end;
function TDelphiDSoundRenderer.SetMediaType(MediaType: PAMMediaType): HResult;
begin
if (MediaType = nil) then
begin
Result := E_POINTER;
Exit;
end;
Result := CreateDirectSoundBuffer(MediaType);
end;
function TDelphiDSoundRenderer.CreateDirectSoundBuffer(MediaType : PAMMediaType) : HRESULT;
var
lDesc : TDSBufferDesc;
lBuffer : IDirectSoundBuffer;
begin
fLock.Lock;
try
ZeroMemory(@lDesc,SizeOf(TDSBufferDesc));
if Assigned(fBuffer) then
begin
fBuffer.Stop;
fBuffer := nil;
end;
lDesc.dwSize := SizeOf(TDSBufferDesc);
lDesc.dwFlags := DSBCAPS_GLOBALFOCUS or DSBCAPS_CTRLFREQUENCY or DSBCAPS_CTRLVOLUME or DSBCAPS_CTRLPAN;
lDesc.lpwfxFormat := CoTaskMemAlloc(MediaType.cbFormat);
Move(MediaType.pbFormat^, lDesc.lpwfxFormat^ ,MediaType.cbFormat);
lDesc.guid3DAlgorithm := GUID_NULL;
fBufferSize := PWaveformatEx(MediaType.pbFormat)^.nSamplesPerSec *
PWaveformatEx(MediaType.pbFormat)^.nChannels *
(PWaveformatEx(MediaType.pbFormat)^.wBitsPerSample div 8) *
BUFFER_SIZE_MS div 1000;
lDesc.dwBufferBytes := fBufferSize;
fPreBufferSize := PWaveformatEx(MediaType.pbFormat)^.nAvgBytesPerSec * PREBUFFER_SIZE_MS div 1000;
Result := fDevice.CreateSoundBuffer(lDesc,lBuffer,nil);
if (Result = DS_OK) and Assigned(lBuffer) then
begin
Result := lBuffer.QueryInterface(IID_IDirectSoundBuffer8,fBuffer);
if (Result = DS_OK) and Assigned(fBuffer) then
begin
fBuffer.SetVolume(fVolume);
fBuffer.SetPan(fBalance);
fBuffer.GetFormat(@fFormat,SizeOf(fFormat), nil);
end;
lBuffer := nil;
end;
finally
fLock.UnLock;
end;
end;
initialization
TBCClassFactory.CreateFilter(
TDelphiDSoundRenderer, 'Delphi DirectSound Renderer', CLSID_DelphiDSoundRenderer,
CLSID_AudioRendererCategory, MERIT_DO_NOT_USE, 0, nil
);
end.
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -