MMWavOut.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:63k
- {========================================================================}
- {= (c) 1995-98 SwiftSoft Ronald Dittrich =}
- {========================================================================}
- {= All Rights Reserved =}
- {========================================================================}
- {= D 01099 Dresden = Fax.: +49(0)351-8037944 =}
- {= Loewenstr.7a = info@swiftsoft.de =}
- {========================================================================}
- {= Actual versions on http://www.swiftsoft.de/index.html =}
- {========================================================================}
- {= This code is for reference purposes only and may not be copied or =}
- {= distributed in any format electronic or otherwise except one copy =}
- {= for backup purposes. =}
- {= =}
- {= No Delphi Component Kit or Component individually or in a collection=}
- {= subclassed or otherwise from the code in this unit, or associated =}
- {= .pas, .dfm, .dcu, .asm or .obj files may be sold or distributed =}
- {= without express permission from SwiftSoft. =}
- {= =}
- {= For more licence informations please refer to the associated =}
- {= HelpFile. =}
- {========================================================================}
- {= $Date: 27.01.99 - 20:16:19 $ =}
- {========================================================================}
- unit MMWavOut;
- {$C FIXED PRELOAD PERMANENT}
- {$I COMPILER.INC}
- {.$DEFINE _MMDEBUG}
- interface
- uses
- {$IFDEF WIN32}
- Windows,
- {$ELSE}
- WinTypes,
- WinProcs,
- {$ENDIF}
- SysUtils,
- Messages,
- Classes,
- Graphics,
- Controls,
- Forms,
- Dialogs,
- MMSystem,
- MMUtils,
- MMString,
- MMObj,
- MMDSPObj,
- MMDSPMtr,
- MMRegs,
- MMPCMSup,
- MMAlloc,
- MMWaveIO
- {$IFDEF _MMDEBUG}
- ,MMDebug
- {$ENDIF};
- {$IFDEF _MMDEBUG}
- {$DEFINE _NUMERATE}
- const
- DEBUGLEVEL = 0; { 0,1,2 }
- {$ENDIF}
- const
- {$IFDEF CBUILDER3} {$EXTERNALSYM MAXERRORLENGTH} {$ENDIF}
- MAXERRORLENGTH = 255;
- {$IFDEF CBUILDER3} {$EXTERNALSYM MAXOUTBUFFERS} {$ENDIF}
- MAXOUTBUFFERS = 500;
- {$IFDEF CBUILDER3} {$EXTERNALSYM MINBUFFERSIZE} {$ENDIF}
- MINBUFFERSIZE = 32;
- {$IFDEF CBUILDER3} {$EXTERNALSYM MAXERRORLENGTH} {$ENDIF}
- FIX_BUFFERS : Boolean = True;
- type
- EMMWaveOutError = class(Exception);
- TMMWaveOutStates = (wosClose, wosOpen, wosPlay, wosPause);
- TMMWaveOutState = set of TMMWaveOutStates;
- { Pointers to waveOut headers }
- TMMWaveOutHdrs = array[0..MAXOUTBUFFERS-1] of PWaveHdr;
- {$IFDEF WIN32}
- {-- TMMWaveOutThread ---------------------------------------------------}
- TMMWaveOutThread = class(TMMDSPThread)
- private
- procedure Execute; override;
- end;
- {$ENDIF}
- {-- TMMWaveOut ---------------------------------------------------------}
- TMMWaveOut = class(TMMCustomWaveOutComponent)
- private
- FHandle : THandle; { handle used for callback window }
- FDeviceID : TMMDeviceID; { WAVEOUT device ID }
- FHWaveOut : HWaveOut; { Handle to output device }
- FState : TMMWaveOutState;{ Current device state }
- FWaveOutHdrs : TMMWaveOutHdrs; { WaveOut Headers and Buffers }
- FBufferOutIdx : integer; { the current Out Header/BufferIndex }
- FCallbackMode : TMMCBMode; { use Window or Callback function }
- FError : integer; { Last WaveOut Error }
- FNumdevs : integer; { Num. of output devices on system }
- FWaveOutCaps : TWaveOutCaps; { Stuff from WAVEOUTCAPS }
- FProductName : String; { the device Productname }
- FDriverVersion : integer; { Specifies the driver version }
- { high-order byte is major version }
- { low-order byte is minor version }
- FInHandler : integer; { marks that we in any event handler }
- FStarted : Boolean; { device is now started }
- FStopIt : Boolean; { we should stop playing if possible }
- FCloseIt : Boolean; { we should close device if possible }
- FStopping : Boolean; { we actually stop the device }
- FClosing : Boolean; { we actually close the device }
- FReseting : Boolean; { we actually reseting the device }
- FPosted : Boolean;
- FLooping : Boolean; { loop playing or not }
- FLoopCount : Word; { number of loops }
- FLoopTempCount : integer; { temp loop counter for playing }
- FLoopPos : MM_int64; { adjust for loop and GetPosition }
- FOldPosition : MM_int64; { the old play position before pause }
- FLastPosition : Cardinal; { the last playback position }
- FWrapArrounds : Cardinal; { number of position wrap-arrounds }
- FWrapSize : Cardinal; { where has the position wrapped ? }
- FBytesPlayed : MM_Int64; { total bytes we have realy played }
- FMoreBuffers : Boolean; { more buffers to write ? }
- FNumBuffers : integer; { number of buffers for queue }
- FBuffersUsed : integer; { the real buffers we have in use }
- FBufferCounter : integer; { buffer counter for buffers in use }
- FTimeFormat : TMMTimeFormats; { the actual time format for Position}
- FShowHourGlass : Boolean;
- FEndingPosition: MM_int64;
- FMapped : Boolean;
- FAllocator : TMMAllocator;
- {$IFDEF WIN32}
- FPriority : TThreadPriority;{ thread priority }
- FThreadError : Boolean; { Error in Thread Handler }
- FOutThread : TMMWaveOutThread;{ Output Thread for callback handling}
- DataSection : TRtlCriticalSection;{ CriticalSection Object }
- DataSectionOK : Boolean; { CriticalSection is prepared }
- FOutEvent : THandle; { event object for notify handling }
- FCloseEvent : THandle; { event object to close the device }
- FResetEvent : THandle; { event object to reset the device }
- {$ENDIF}
- { Events }
- FOnError : TNotifyEvent; { Error occured }
- FOnBufferFilled: TMMBufferEvent; { Wave buffer filled event }
- FOnLooping : TNotifyEvent; { Wave was at end and is looped }
- FOnOpen : TNotifyEvent; { Wave Device succ. opened }
- FOnStart : TNotifyEvent; { Wave Device succ. started }
- FOnPause : TNotifyEvent; { Wave Device succ. paused }
- FOnRestart : TNotifyEvent; { Wave Device succ. restarted }
- FOnStop : TNotifyEvent; { Wave Device succ. stopped }
- FOnClose : TNotifyEvent; { Wave Device succ. closed }
- function WaveOutErrorString(WError: integer): string;
- procedure SetTimeFormat(aValue: TMMTimeFormats);
- procedure SetLooping(aValue: Boolean);
- procedure SetLoopCount(aValue: Word);
- function GetSamplePosition: Cardinal;
- function GetInternalPosition: int64;
- function GetPositionHigh: Cardinal;
- procedure WaveOutHandler(var Msg: TMessage);
- procedure AllocWaveHeader(var lpWaveHdr: PWaveHdr);
- procedure FreeWaveHeaders;
- procedure PrepareWaveHeader(lpWaveHdr: PWaveHdr);
- procedure UnPrepareWaveHeaders;
- function LoadWaveHeader(lpWaveHdr: PWaveHdr): Longint;
- procedure QueueWaveHeader(lpWaveHdr: PWaveHdr);
- procedure ProcessWaveHeader(lpWaveHdr: PWaveHdr);
- {$IFDEF WIN32}
- procedure SetPriority(aValue: TThreadPriority);
- procedure InitThread;
- procedure DoneThread;
- procedure CloseEvents;
- {$ENDIF}
- procedure InitCritical;
- procedure EnterCritical;
- procedure LeaveCritical;
- procedure DoneCritical;
- procedure DoOpened;
- procedure DoClosed;
- procedure DoStarted;
- procedure DoPaused;
- procedure DoRestarted;
- procedure DoStopped;
- procedure DoBufferFilled(lpwh: PWaveHdr);
- procedure DoBufferReady(lpwh: PWaveHdr);
- protected
- procedure SetPWaveFormat(aValue: PWaveFormatEx); override;
- procedure SetBufferSize(aValue: Longint); override;
- function GetBufferSize: Longint; override;
- procedure SetNumBuffers(aValue: integer); override;
- function GetNumBuffers: integer; override;
- procedure SetDeviceID(aValue: TMMDeviceID); override;
- function GetDeviceID: TMMDeviceID; override;
- procedure SetProductName(aValue: string); override;
- function GetProductName: string; override;
- procedure SetCallBackMode(aValue: TMMCBMode); override;
- function GetCallBackMode: TMMCBMode; override;
- function GetPosition: MM_int64; override;
- procedure Error(Msg: string); virtual;
- public
- procedure Opened; override;
- procedure Closed; override;
- procedure Started; override;
- procedure Paused; override;
- procedure Restarted; override;
- procedure Stopped; override;
- procedure BufferReady(lpwh: PWaveHdr); override;
- procedure BufferLoad(lpwh: PWaveHdr; var MoreBuffers: Boolean); override;
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure Open; override;
- procedure Close; override;
- procedure Reset; override;
- procedure Start; override;
- procedure Pause; override;
- procedure Restart; override;
- procedure Stop; override;
- function QueryDevice(aDeviceID: TMMDeviceID; pwfx: PWaveFormatEx): Boolean;
- {$IFDEF WIN32}
- { maybe you must syncronize anything if UseThread = True ? }
- procedure SynchronizeVCL(VCLProc: TThreadMethod);
- {$ENDIF}
- property Handle: HWaveOut read FHWaveOut;
- property WaveOutCaps: TWaveOutCaps read FWaveOutCaps;
- property Numdevs: integer read FNumdevs;
- property State: TMMWaveOutState read FState;
- property DriverVersion: integer read FDriverVersion;
- property BytesPlayed: MM_Int64 read FBytesPlayed;
- property Position: MM_int64 read GetPosition;
- property PositionHigh: Cardinal read GetPositionHigh;
- property EndingPosition: MM_int64 read FEndingPosition;
- property BufferIndex: integer read FBufferOutIdx;
- {$IFNDEF CBUILDER3}
- property WaveHdrs: TMMWaveOutHdrs read FWaveOutHdrs;
- {$ENDIF}
- published
- { Events }
- property OnError: TNotifyEvent read FOnError write FOnError;
- property OnOpen: TNotifyEvent read FOnOpen write FOnOpen;
- property OnStart: TNotifyEvent read FOnStart write FOnStart;
- property OnPause: TNotifyEvent read FOnPause write FOnPause;
- property OnRestart: TNotifyEvent read FOnRestart write FOnRestart;
- property OnStop: TNotifyEvent read FOnStop write FOnStop;
- property OnClose: TNotifyEvent read FOnClose write FOnClose;
- property OnLooping: TNotifyEvent read FOnLooping write FOnLooping;
- property OnBufferFilled: TMMBufferEvent read FOnBufferFilled write FOnBufferFilled;
- property OnBufferReady;
- property OnBufferLoad;
- property Input;
- property Output;
- property BufferSize;
- property NumBuffers;
- property DeviceID;
- property ProductName;
- property CallBackMode;
- property TimeFormat: TMMTimeFormats read FTimeFormat write SetTimeFormat default tfByte;
- property Looping: Boolean read FLooping write SetLooping default False;
- property LoopCount: Word read FLoopCount write SetLoopCount default 0;
- property ShowHourGlass: Boolean read FShowHourGlass write FShowHourGlass default True;
- property Mapped: Boolean read FMapped write FMapped default False;
- {$IFDEF WIN32}
- property Priority: TThreadPriority read FPriority write SetPriority default tpHigher;
- {$ENDIF}
- end;
- function WaveOutGetDeviceName(DeviceID: TMMDeviceID): String;
- function WaveOutReady(DeviceID: TMMDeviceID): Boolean;
- implementation
- {$DEFINE _USE_CALLBACK}
- uses consts;
- const
- MM_WOM_STOP = MM_USER+1;
- procedure WaveOutFunc(hWaveOut:HWaveOut;wMsg:UINT;dwInstance,dwParam1,dwParam2:Longint);
- export;{$IFDEF WIN32}stdcall;{$ENDIF}forward;
- {-------------------------------------------------------------------------}
- procedure DebugStr(Level: integer; s: String);
- begin
- {$IFDEF _MMDEBUG}
- if (s <> ' ') then s := 'WaveOut: '+s;
- DB_WriteStrLn(Level,s);
- {$ENDIF}
- end;
- {-------------------------------------------------------------------------}
- function WaveOutReady(DeviceID: TMMDeviceID): Boolean;
- var
- OutHandle: HWAVEOUT;
- Error: MMRESULT;
- wfx: TWaveFormatEx;
- begin
- Result := False;
- if (DeviceID < integer(waveOutGetNumDevs)) and (DeviceID >= integer(WAVE_MAPPER)) then
- try
- OutHandle := 0;
- pcmBuildWaveHeader(@wfx, 8, 1, 22050);
- {$IFDEF WIN32}
- Error := WaveOutOpen(@OutHandle, DeviceId, MMSystem.PWaveFormatEx(@wfx), 0, 0, CALLBACK_NULL);
- {$ELSE}
- Error := WaveOutOpen(@OutHandle, DeviceId, Pointer(@wfx), 0, 0, CALLBACK_NULL);
- {$ENDIF}
- if (Error = MMSYSERR_NOERROR) then
- begin
- Result := True;
- end;
- finally
- if (OutHandle <> 0) then WaveOutClose(OutHandle);
- end;
- end;
- {-------------------------------------------------------------------------}
- function WaveOutGetDeviceName(DeviceID: TMMDeviceID): String;
- var
- Caps : TWaveOutCaps;
- begin
- Result := '';
- if (DeviceID < integer(waveOutGetNumDevs)) and (DeviceID >= integer(WAVE_MAPPER)) then
- begin
- { Set the name and other WAVEOUTCAPS properties to match the ID }
- if waveOutGetDevCaps(DeviceID, @Caps, sizeof(TWaveOutCaps)) = 0 then
- Result := StrPas(Caps.szPname);
- end;
- end;
- {== TMMWaveOut ===========================================================}
- constructor TMMWaveOut.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- { Set defaults }
- FHWaveOut := 0;
- FHandle := 0;
- FState := [wosClose];
- FError := 0;
- FNumBuffers := 10;
- FProductName := '';
- FDriverVersion := 0;
- FBytesPlayed := 0;
- FTimeFormat := tfByte;
- FMoreBuffers := False;
- FLooping := False;
- FLoopCount := 0;
- FCallBackMode := cmWindow;
- FClosing := False;
- FReseting := False;
- FStopping := False;
- FPosted := False;
- FBufferOutIdx := 0;
- FShowHourGlass := True;
- FEndingPosition:= 0;
- {$IFDEF WIN32}
- FPriority := tpHigher;
- {$ENDIF}
- FAllocator := TMMAllocator.Create;
-
- {clear all pointers to Nil }
- FillChar(FWaveOutHdrs, sizeOf(TMMWaveOutHdrs), 0);
- FNumDevs := waveOutGetNumDevs;
- SetDeviceID(0);
- {$IFDEF WIN32}
- DataSectionOK := False;
- {$ENDIF}
- ErrorCode := ComponentRegistered(InitCode, Self, ClassName);
- if (ErrorCode <> 0) then RegisterFailed(InitCode, Self , ClassName);
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- destructor TMMWaveOut.Destroy;
- begin
- { Close the device if it's open }
- if (FHWaveOut <> 0) then Close;
- { Destroy the window for callback notification }
- if (FHandle <> 0) then DeallocateHwnd(FHandle);
- if (FAllocator <> nil) then FAllocator.Free;
- inherited Destroy;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.Error(Msg: string);
- begin
- if assigned(FOnError) then FOnError(Self);
- {$IFDEF _MMDEBUG}
- DebugStr(0,Msg);
- {$ENDIF}
- raise EMMWaveOutError.Create(Msg);
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- { Allocate memory for the WaveOut header and buffer }
- procedure TMMWaveOut.AllocWaveHeader(var lpWaveHdr: PWaveHdr);
- begin
- if (lpWaveHdr = Nil) then
- begin
- { set up a wave header for playing and lock. }
- lpWaveHdr := FAllocator.AllocBuffer(GPTR, SizeOf(TMMWaveHdr) + BufferSize);
- if lpWaveHdr = nil then
- Error(LoadResStr(IDS_HEADERMEMERROR));
- { Data occurs directly after the header }
- lpWaveHdr^.lpData := PChar(lpWaveHdr) + sizeOf(TMMWaveHdr);
- lpWaveHdr^.dwBufferLength := BufferSize;
- lpWaveHdr^.dwBytesRecorded:= 0;
- lpWaveHdr^.dwFlags := 0;
- lpWaveHdr^.dwLoops := 0;
- lpWaveHdr^.dwUser := 0;
- lpWaveHdr^.lpNext := nil;
- end;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.FreeWaveHeaders;
- Var
- i: integer;
- begin
- for i := 0 to FNumBuffers-1 do
- begin
- { unlock and free memory for WaveOutHdr }
- if FWaveOutHdrs[i] <> NIL then
- begin
- FAllocator.FreeBuffer(Pointer(FWaveOutHdrs[i]));
- FWaveOutHdrs[i] := Nil;
- end;
- end;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.SetLooping(aValue: Boolean);
- begin
- if (aValue <> FLooping) then
- begin
- FLooping := aValue;
- FLoopTempCount := FLoopCount;
- end;
- {$IFDEF WIN32}
- {$IFDEF TRIAL}
- {$DEFINE _HACK1}
- {$I MMHACK.INC}
- {$ENDIF}
- {$ENDIF}
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.SetLoopCount(aValue: Word);
- begin
- if (aValue <> FLoopCount) then
- begin
- FLoopCount := aValue;
- FLoopTempCount := FLoopCount;
- end;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- function TMMWaveOut.WaveOutErrorString(WError: integer): String;
- Var
- errorDesc: PChar;
- begin
- { Convert the numeric return code from an MMSYSTEM function to a string }
- errorDesc := Nil;
- try
- errorDesc := StrAlloc(MAXERRORLENGTH);
- if waveOutGetErrorText(WError, errorDesc, MAXERRORLENGTH) = 0 then
- Result := StrPas(errorDesc)
- else
- Result := LoadResStr(IDS_ERROROUTOFRANGE);
- finally
- StrDispose(errorDesc);
- end;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- Procedure TMMWaveOut.SetDeviceID(aValue: TMMDeviceID);
- begin
- if (wosOpen in FState) then
- Error(LoadResStr(IDS_PROPERTYOPEN));
- FProductName := LoadResStr(IDS_WONODEVICE);
- FDriverVersion := 0;
- if (FNumDevs > 0) and (aValue >= MapperId) and (aValue < FNumDevs) then
- begin
- { Set the name and other WAVEOUTCAPS properties to match the ID }
- FError := waveOutGetDevCaps(aValue, @FWaveOutCaps, sizeof(TWaveOutCaps));
- if FError = 0 then
- with FWaveOutCaps do
- begin
- FProductName := StrPas(szPname);
- FDriverVersion := vDriverVersion;
- end;
- end;
- { set the new device }
- FDeviceID := aValue;
- if (aValue < MapperId) or (aValue >= FNumDevs) then
- FDeviceID := InvalidID;
- {$IFDEF WIN32}
- {$IFDEF TRIAL}
- {$DEFINE _HACK1}
- {$I MMHACK.INC}
- {$ENDIF}
- {$ENDIF}
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- function TMMWaveOut.GetDeviceID: TMMDeviceID;
- begin
- Result := FDevicEID;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.SetProductName(aValue: String);
- begin
- ;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- function TMMWaveOut.GetProductName: String;
- begin
- Result := FProductName;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- function TMMWaveOut.QueryDevice(aDeviceID: TMMDeviceID; pwfx: PWaveFormatEx): Boolean;
- Var
- aHandle: HWaveOut;
- begin
- if (aDeviceID < FNumDevs) and (aDeviceID >= MapperID) and (pwfx <> nil) then
- begin
- { query the Wave output device. }
- Result := WaveOutOpen(@aHandle,
- aDeviceId,
- Pointer(pwfx),
- 0, 0,
- WAVE_FORMAT_QUERY) = 0;
- end
- else Result := False;
- end;
- {-- TMMWaveOut ------------------------------------------------------------}
- procedure TMMWaveOut.SetTimeFormat(aValue: TMMTimeFormats);
- begin
- if (aValue <> FTimeFormat) then
- begin
- FTimeFormat := aValue;
- end;
- end;
- {-- TMMWaveOut ------------------------------------------------------------}
- function TMMWaveOut.GetSamplePosition: Cardinal;
- Var
- MMTime: TMMTime;
- begin
- Result := 0;
- if (wosOpen in FState) then
- begin
- MMTime.wType := Time_Samples;
- FError := WaveOutGetPosition(FHWaveOut, @MMTime, SizeOf(TMMTime));
- if (FError <> 0) or (MMTime.wType <> Time_Samples) then
- begin
- MMTime.wType := Time_Bytes;
- FError := WaveOutGetPosition(FHWaveOut, @MMTime, SizeOf(TMMTime));
- if (FError <> 0) then
- Error('WaveOutGetPosition:'#10#13+WaveOutErrorString(FError));
- MMTime.Sample := wioBytesToSamples(PWaveFormat,MMTime.cb);
- end;
- Result := MMTime.Sample;
- {asm
- mov eax, $FFFF0000
- add Result, eax
- end;}
- end;
- end;
- {-- TMMWaveOut ------------------------------------------------------------}
- function TMMWaveOut.GetInternalPosition: Int64;
- var
- Samples,Pos: int64;
- S: Cardinal;
- WrapSize: int64;
- begin
- Result := 0;
- if (wosOpen in FState) and (PWaveFormat <> Nil) and not FCloseIt then
- begin
- { adjust if Looped or FullDuplex }
- S := GetSamplePosition;
- {$IFDEF WIN32}
- asm
- mov eax, S
- mov dword ptr Pos[0], eax
- xor eax, eax
- mov dword ptr Pos[4], eax
- mov eax, Self
- mov eax, TMMWaveOut(eax).FWrapSize
- mov dword ptr WrapSize[0], eax
- xor eax, eax
- mov dword ptr WrapSize[4], eax
- end;
- Samples := (FWrapArrounds*WrapSize)+(Pos+FOldPosition)-FLoopPos;
- {$ELSE}
- Samples := (S+FOldPosition)-FLoopPos;
- {$ENDIF}
- case FTimeFormat of
- tfMilliSecond: Result := wioSamplesToTime64(PWaveFormat,Samples);
- tfByte : Result := wioSamplesToBytes64(PWaveFormat,Samples);
- tfSample : Result := Samples;
- end;
- end;
- end;
- {-- TMMWaveOut ------------------------------------------------------------}
- function TMMWaveOut.GetPosition: MM_int64;
- {$IFNDEF DELPHI4}
- var
- Temp: TLargeInteger;
- {$ENDIF}
- begin
- {$IFDEF DELPHI4}
- Result := GetInternalPosition;
- {$ELSE}
- Temp.QuadPart := GetInternalPosition;
- Result := Temp.LowPart;
- {$ENDIF}
- end;
- {-- TMMWaveOut ------------------------------------------------------------}
- function TMMWaveOut.GetPositionHigh: Cardinal;
- {$IFNDEF DELPHI4}
- var
- Temp: TLargeInteger;
- {$ENDIF}
- begin
- {$IFDEF DELPHI4}
- Result := (GetInternalPosition shr 32);
- {$ELSE}
- Temp.QuadPart := GetInternalPosition;
- Result := Temp.HighPart;
- {$ENDIF}
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- Procedure TMMWaveOut.SetCallBackMode(aValue: TMMCBMode);
- begin
- if (wosOpen in FState) then
- Error(LoadResStr(IDS_PROPERTYOPEN));
- if (aValue <> FCallBackMode) then
- begin
- if (aValue = cmCallBack) then
- begin
- {$IFDEF WIN32}
- if not _Win95_ then
- {$ENDIF}
- begin
- Application.MessageBox('"CallBacks" are called at interrupt time !'#10#13+
- 'This is currently only supported under Windows 95',
- 'TMMWaveOut', MB_OK);
- exit;
- end;
- end;
- FCallBackMode := aValue;
- end;
- {$IFDEF WIN32}
- {$IFDEF TRIAL}
- {$DEFINE _HACK3}
- {$I MMHACK.INC}
- {$ENDIF}
- {$ENDIF}
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- function TMMWaveOut.GetCallBackMode: TMMCBMode;
- begin
- Result := FCallBackMode;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- Procedure TMMWaveOut.SetNumBuffers(aValue: integer);
- begin
- if (aValue <> FNumBuffers) AND (aValue > 1) then
- begin
- if (wosOpen in FState) then
- Error(LoadResStr(IDS_PROPERTYOPEN));
- FNumBuffers := Min(aValue,MAXOUTBUFFERS);
- end;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- function TMMWaveOut.GetNumBuffers: integer;
- begin
- Result := FNumBuffers;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- Procedure TMMWaveOut.SetPWaveFormat(aValue: PWaveFormatEx);
- begin
- { stop and close the device }
- Close;
- inherited SetPWaveFormat(aValue);
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- Procedure TMMWaveOut.SetBufferSize(aValue: Longint);
- begin
- if (aValue <> inherited GetBufferSize) then
- begin
- if (wosOpen in FState) then
- Error(LoadResStr(IDS_PROPERTYOPEN));
- if assigned(FAllocator) then
- FAllocator.Discard;
- inherited SetBufferSize(Max(aValue,MINBUFFERSIZE));
- end;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- function TMMWaveOut.GetBufferSize: Longint;
- begin
- Result := inherited GetBufferSize;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- Procedure TMMWaveOut.PrepareWaveHeader(lpWaveHdr: PWaveHdr);
- begin
- if lpWaveHdr <> Nil then
- begin
- { Prepare waveform header for playing }
- WaveOutPrepareHeader(FHWaveOut, lpWaveHdr, sizeOf(TWaveHdr));
- end;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- Procedure TMMWaveOut.UnPrepareWaveHeaders;
- Var
- i: integer;
- TimeOut: Longint;
- begin
- {$IFDEF _MMDEBUG}
- DebugStr(1,' ');
- {$ENDIF}
- for i := 0 to FBuffersUsed-1 do
- begin
- if (FWaveOutHdrs[i] <> Nil) then
- begin
- TimeOut := 65000;
- { wait until the buffer is marked as done }
- repeat
- dec(TimeOut);
- until (FWaveOutHdrs[i]^.dwFlags and WHDR_DONE = WHDR_DONE) or (TimeOut = 0);
- { mark buffer as done }
- if (TimeOut = 0) then FWaveOutHdrs[i]^.dwFlags := WHDR_DONE;
- { unprepare buffer }
- WaveOutUnprepareHeader(FHWaveOut, FWaveOutHdrs[i], sizeOf(TWAVEHDR));
- {$IFDEF _MMDEBUG}
- DebugStr(1,'UnprepareHeader '+IntToStr(i));
- {$ENDIF}
- end;
- end;
- {$IFDEF _MMDEBUG}
- DebugStr(1,' ');
- {$ENDIF}
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- Function TMMWaveOut.LoadWaveHeader(lpWaveHdr: PWaveHdr): Longint;
- begin
- Result := 0;
- FMoreBuffers := False;
- if (lpWaveHdr <> nil) then
- begin
- {$IFDEF _MMDEBUG}
- DebugStr(2,'Try to load Buffer '+IntToStr(lpWaveHdr^.dwUser));
- {$ENDIF}
- BufferLoad(lpWaveHdr, FMoreBuffers);
- Result := lpWaveHdr^.dwBytesRecorded;
- if Result <= 0 then FMoreBuffers := False;
- {$IFDEF _MMDEBUG}
- DebugStr(2,'Buffer '+IntToStr(lpWaveHdr^.dwUser)+' loaded');
- {$ENDIF}
- end;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.QueueWaveHeader(lpWaveHdr: PWaveHdr);
- begin
- { this is the chance to modify the data in the buffer !!! }
- DoBufferFilled(lpWaveHdr);
- if not FStopping then
- begin
- if not FIX_BUFFERS then
- WaveOutPrepareHeader(FHWaveOut, lpWaveHdr, sizeOf(TWaveHdr))
- else
- { reset flags field (remove WHDR_DONE attribute) }
- lpWaveHdr^.dwFlags := lpWaveHdr^.dwFlags and not WHDR_DONE or WHDR_PREPARED;
- {$IFDEF _MMDEBUG}
- DebugStr(2,'Try to send Wave-Header '+IntToStr(lpWaveHdr^.dwUser)+' to driver');
- {$ENDIF}
- PMMWaveHdr(lpWaveHdr)^.dwUser2 := lpWaveHdr^.dwBytesRecorded;
- { now queue the buffer for output }
- FError := WaveOutWrite(FHWaveOut,
- lpWaveHdr,
- SizeOf(TWAVEHDR));
- if FError <> 0 then
- Error('WaveOutWrite:'#10#13+WaveOutErrorString(FError));
- { BUG-FIX for NT 4.0 SP4, it does set dwBytesRecorded to zero }
- lpWaveHdr^.dwBytesRecorded := PMMWaveHdr(lpWaveHdr)^.dwUser2;
- EnterCritical;
- inc(FBufferCounter);
- LeaveCritical;
- {$IFDEF _MMDEBUG}
- DebugStr(2,'Wave-Header '+IntToStr(lpWaveHdr^.dwUser)+' queued');
- {$ENDIF}
- end;
- end;
- {$IFDEF WIN32}
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.SynchronizeVCL(VCLProc: TThreadMethod);
- begin
- if (FCallBackMode = cmThread) and (FOutEvent <> 0) then
- begin
- FOutThread.Synchronize(VCLProc);
- end
- else VCLProc;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.SetPriority(aValue: TThreadPriority);
- begin
- FPriority := aValue;
- if (FOutThread <> nil) then
- FOutThread.Priority := FPriority;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.InitThread;
- begin
- if (FCallBackMode = cmThread) then
- begin
- EnterCritical;
- try
- FThreadError := False;
- { create event objects }
- FOutEvent := CreateEvent(nil, False, False, nil);
- FCloseEvent := CreateEvent(nil, False, False, nil);
- FResetEvent := CreateEvent(nil, True, False, nil);
- { create the output thread }
- FOutThread := TMMWaveOutThread.CreateSuspended(Self);
- if (FOutThread = nil) then
- Error('WaveOut:'#10#13+LoadResStr(IDS_THREADERROR));
- FOutThread.FreeOnTerminate := True;
- FOutThread.Resume;
- {$IFDEF _MMDEBUG}
- DebugStr(0,'Wait for Thread start...');
- {$ENDIF}
- { Wait for it to start... }
- if WaitForSingleObject(FOutEvent, 5000) <> WAIT_OBJECT_0 then
- Error('WaveOut:'#10#13+LoadResStr(IDS_THREADERROR));
- {$IFDEF _MMDEBUG}
- DebugStr(0,'Thread Started');
- {$ENDIF}
- finally
- LeaveCritical;
- end;
- end;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.DoneThread;
- begin
- if (FCallBackMode = cmThread) and (FOutEvent <> 0) and not FThreadError then
- begin
- { Force the output thread to close... }
- SetEvent(FCloseEvent);
- { ...and wait for it to die }
- WaitForSingleObject(FOutEvent, 5000);
- { close all events and remove critical section }
- CloseEvents;
- {$IFDEF _MMDEBUG}
- DebugStr(0,'Thread Terminated');
- {$ENDIF}
- end;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.CloseEvents;
- begin
- if (FOutEvent <> 0) then
- begin
- { release events }
- CloseHandle(FOutEvent);
- CloseHandle(FCloseEvent);
- CloseHandle(FResetEvent);
- FOutEvent := 0;
- FCloseEvent := 0;
- FResetEvent := 0;
- { Free the critical section }
- DoneCritical;
- end;
- end;
- {$ENDIF}
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.InitCritical;
- begin
- {$IFDEF WIN32}
- { create critical section object }
- FillChar(DataSection, SizeOf(DataSection), 0);
- InitializeCriticalSection(DataSection);
- DataSectionOK := True;
- {$ENDIF}
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.EnterCritical;
- begin
- {$IFDEF WIN32}
- if DataSectionOK then
- EnterCriticalSection(DataSection);
- {$ENDIF}
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.LeaveCritical;
- begin
- {$IFDEF WIN32}
- if DataSectionOK then
- LeaveCriticalSection(DataSection);
- {$ENDIF}
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.DoneCritical;
- begin
- {$IFDEF WIN32}
- if DataSectionOK then
- begin
- DataSectionOK := False;
- DeleteCriticalSection(DataSection);
- end;
- {$ENDIF}
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- Procedure TMMWaveOut.Open;
- var
- TimeOut: integer;
- dwFlags: Longint;
- begin
- if (FNumDevs = 0) then
- Error(LoadResStr(IDS_WONODEVICE));
- if (FDeviceID = InvalidId) then
- Error(LoadResStr(IDS_INVALIDDEVICEID));
- if (PWaveFormat = Nil) then
- Error('WaveOutOpen:'#10#13+LoadResStr(IDS_NOFORMAT));
- if (wosOpen in FState) then Close;
- if (Not(wosOpen in FState)) and not FClosing then
- begin
- {$IFDEF _MMDEBUG}
- //DB_Clear;
- DB_WriteStrLn(0,'-----------------');
- {$ENDIF}
- FClosing := False;
- FReseting := False;
- FStopping := False;
- FPosted := False;
- try
- if not QueryDevice(FDeviceID, PWaveFormat) then
- Error('WaveOutOpen:'#10#13+LoadResStr(IDS_CANTPLAY));
- { Create the window for callback notification }
- if (FHandle = 0) then FHandle := AllocateHwnd(WaveOutHandler);
- {$IFDEF _MMDEBUG}
- DebugStr(0,'Call inherited...');
- {$ENDIF}
- FHWaveOut := 0;
- FCloseIt := False;
- inherited Opened;
- {$IFDEF _MMDEBUG}
- DebugSTr(0,'Try to open device...');
- {$ENDIF}
- { create critical section object }
- InitCritical;
- {$IFDEF WIN32}
- if (FCallBackMode = cmThread) then InitThread;
- {$ENDIF}
- TimeOut := 500;
- {$IFDEF WIN32}
- if FMapped and (FDeviceID >= 0) then
- dwFlags := WAVE_MAPPED
- else
- {$ENDIF}
- dwFlags := 0;
- {$IFDEF _USE_CALLBACK}
- if _Win9x_ or _WinNT4_ then
- begin
- { now open Wave output device. }
- FError := WaveOutOpen(@FHWaveOut,
- FDeviceId,
- Pointer(PWaveFormat),
- Longint(@WaveOutFunc),
- Longint(Self),
- CALLBACK_FUNCTION or dwFlags);
- end
- else
- {$ENDIF}
- begin
- { now open Wave output device. }
- FError := WaveOutOpen(@FHWaveOut,
- FDeviceId,
- Pointer(PWaveFormat),
- FHandle,
- 0,
- CALLBACK_WINDOW or dwFlags);
- end;
- if (FError <> 0) then
- Error('WaveOutOpen:'#10#13+WaveOutErrorString(FError));
- { wait until the device returns its status }
- repeat
- {$IFDEF _USE_CALLBACK}
- if _Win9x_ or _WinNT4_ then
- Delay(10,False)
- else
- {$ENDIF}
- Delay(10,True);
- dec(TimeOut);
- until (wosOpen in FState) or (TimeOut <= 0);
- if (TimeOut <= 0) then
- Error('WaveOutOpen:'#10#13+LoadResStr(IDS_CANTOPENDEVICE));
- DoOpened;
- except
- if assigned(FOnError) then FOnError(Self);
- FState := [wosOpen];
- Close;
- FState := [wosClose];
- raise;
- end;
- end;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- Procedure TMMWaveOut.Close;
- var
- TimeOut: integer;
- begin
- if (wosOpen in FState) and (not FClosing or FCloseIt) then
- try
- FClosing := True;
- { stop playing }
- if (wosPlay in FState) OR (wosPause in FState) then Stop;
- TimeOut := 500;
- { Close the device (finally!) }
- if FStopIt then FCloseIt := True
- else
- begin
- FCloseIt := False;
- if (FHWaveOut <> 0) then
- begin
- {$IFDEF _MMDEBUG}
- if (FInHandler > 0) then
- DebugStr(0,'Try to close device (while in Handler)...')
- else
- DebugStr(0,'Try to close device...');
- {$ENDIF}
- FError := WaveOutClose(FHWaveOut);
- if FError <> 0 then
- Error('WaveOutClose:'#10#13+WaveOutErrorString(FError));
- { wait until the device returns its status }
- repeat
- {$IFDEF _USE_CALLBACK}
- if _Win9x_ or _WinNT4_ then
- Delay(10,False)
- else
- {$ENDIF}
- Delay(10,True);
- dec(TimeOut);
- until (wosClose in FState) or (TimeOut <= 0);
- FEndingPosition := 0;
- FWrapArrounds := 0;
- FWrapSize := 0;
- end
- else
- begin
- FState := [wosClose];
- end;
- { notify all other components }
- inherited Closed;
- {$IFDEF WIN32}
- if (FCallBackMode = cmThread) then
- { shot down the thread }
- DoneThread
- else
- { Free the critical section }
- DoneCritical;
- {$ENDIF}
- DoClosed;
- if (TimeOut <= 0) then
- Error('WaveOutClose:'#10#13+LoadResStr(IDS_CANTCLOSEDEVICE));
- end;
- except
- FClosing := False;
- end;
- end;
- {-- TMMWaveOut ------------------------------------------------------------}
- Procedure TMMWaveOut.Reset;
- var
- i: integer;
- TimeOut: Longint;
- Msg: TMsg;
- begin
- if ((wosPlay in FState) or (wosPause in FState)) and not FReseting then
- begin
- try
- FReseting := True;
- FError := WaveOutPause(FHWaveOut);
- if FError <> 0 then
- Error('WaveOutPause:'#10#13+WaveOutErrorString(FError));
- if (FCallBackMode = cmWindow) then
- { remove all pending Messages from the queue }
- while PeekMessage(Msg, FHandle, MM_WOM_DONE, MM_WOM_DONE, PM_REMOVE) do
- {$IFDEF WIN32}
- else if (FCallBackMode = cmThread) then
- begin
- { remove all pending messages from threads queue }
- SetEvent(FResetEvent);
- { Wait for it to reset... }
- while WaitForSingleObject(FResetEvent, 0) = WAIT_OBJECT_0 do Sleep(1);
- end;
- {$ENDIF};
- FError := WaveOutReset(FHWaveOut);
- if FError > 0 then
- Error('WaveOutReset:'#10#13+WaveOutErrorString(FError));
- TimeOut := 100;
- repeat
- {$IFDEF _USE_CALLBACK}
- if _Win9x_ or _WinNT4_ then
- Delay(10,False)
- else
- {$ENDIF}
- Delay(10,True);
- dec(TimeOut);
- until not FReseting or (TimeOut <= 0);
- { this buggy drivers... :-( }
- FError := WaveOutRestart(FHWaveOut);
- if FError <> 0 then
- Error('WaveOutRestart:'#10#13+WaveOutErrorString(FError));
- { pause the output so the buffers won't play until we tell it }
- FError := WaveOutPause(FHWaveOut);
- if FError <> 0 then
- Error('WaveOutPause:'#10#13+WaveOutErrorString(FError));
- FBufferOutIdx := 0;
- FBufferCounter := 0;
- FOldPosition := 0;
- FLastPosition := 0;
- FWrapArrounds := 0;
- FWrapSize := 0;
- FLoopPos := 0;
- { notify all other components }
- Reseting;
- FMoreBuffers := True;
- { Load the number of buffers required }
- i := 0;
- while (i < FNumBuffers) and FMoreBuffers do
- begin
- { fill the buffer and send to driver }
- if LoadWaveHeader(FWaveOutHdrs[i]) > 0 then
- QueueWaveHeader(FWaveOutHdrs[i])
- else break;
- inc(i);
- end;
- FBuffersUsed := i;
- { start the buffers playing (unpause) }
- if not (wosPause in FState) then
- begin
- FError := WaveOutRestart(FHWaveOut);
- if FError <> 0 then
- Error('WaveOutRestart:'#10#13+WaveOutErrorString(FError));
- end;
- if FBuffersUsed = 0 then Stop;
- except
- if assigned(FOnError) then FOnError(Self);
- Close;
- raise;
- end;
- end;
- end;
- {-- TMMWaveOut ------------------------------------------------------------}
- Procedure TMMWaveOut.Start;
- Var
- oldCursor: TCursor;
- i: integer;
- begin
- try
- if not (wosOpen in FState) then Open;
- if (wosOpen in FState) and not (wosPlay in FState) then
- begin
- { setup for playing }
- { reset the total bytes played counter }
- FBytesPlayed := 0;
- FOldPosition := 0;
- FLastPosition := 0;
- FWrapArrounds := 0;
- FWrapSize := 0;
- FLoopPos := 0;
- FLoopTempCount := FLoopCount;
- FInHandler := 0;
- FStarted := False;
- FStopIt := False;
- FReseting := False;
- FStopping := False;
- FPosted := False;
- FBufferOutIdx := 0;
- FBufferCounter := 0;
- { change the cursor to HourGlass }
- oldCursor := Screen.Cursor;
- if FShowHourGlass and (BufferSize * NumBuffers > 100000) then
- Screen.Cursor := crHourGlass;
- try
- {$IFDEF _MMDEBUG}
- DebugStr(0,'Try to start device...');
- {$ENDIF}
- { pause the output so the buffers won't play until we tell it to }
- FError := WaveOutPause(FHWaveOut);
- if FError <> 0 then
- Error('WaveOutPause:'#10#13+WaveOutErrorString(FError));
- { now notify all other components }
- inherited Started;
- FMoreBuffers := True;
- i := 0; { Load the number of buffers required }
- while (i < FNumBuffers) and FMoreBuffers do
- begin
- { create the waveOut header and buffer }
- AllocWaveHeader(FWaveOutHdrs[i]);
- {$IFDEF _NUMERATE}
- FWaveOutHdrs[i]^.dwUser := i;
- {$ENDIF}
- { prepare the waveform header for playing }
- PrepareWaveHeader(FWaveOutHdrs[i]);
- { fill the buffer and send to driver }
- if LoadWaveHeader(FWaveOutHdrs[i]) > 0 then
- QueueWaveHeader(FWaveOutHdrs[i])
- else break;
- inc(i);
- end;
- FBuffersUsed := i;
- FState := FState + [wosPlay];
- finally
- Screen.Cursor := oldCursor;
- end;
- DoStarted;
- end;
- except
- if assigned(FOnError) then FOnError(Self);
- FState := FState + [wosPlay];
- Close;
- FState := [wosClose];
- raise;
- end;
- end;
- {-- TMMWaveOut ------------------------------------------------------------}
- procedure TMMWaveOut.Pause;
- begin
- try
- if not (wosOpen in FState) then Open;
- if (wosOpen in FState) and (not (wosPause in FState)) then
- begin
- if (wosPlay in FState) then
- try
- EnterCritical;
- {$IFDEF _MMDEBUG}
- DebugStr(0,'Try to pause device...');
- {$ENDIF}
- FError := WaveOutPause(FHWaveOut);
- if FError <> 0 then
- Error('WaveOutPause:'#10#13+WaveOutErrorString(FError));
- FState := FState + [wosPause];
- if FFullDuplex then
- begin
- inc(FOldPosition, GetSamplePosition);
- FReseting := True;
- FError := WaveOutReset(FHWaveOut);
- if FError > 0 then
- Error('WaveOutReset:'#10#13+WaveOutErrorString(FError));
- FBufferOutIdx := 0;
- FBufferCounter := 0;
- end;
- finally
- LeaveCritical;
- end;
- DoPaused;
- end;
- except
- if assigned(FOnError) then FOnError(Self);
- Close;
- raise;
- end;
- end;
- {-- TMMWaveOut ------------------------------------------------------------}
- procedure TMMWaveOut.Restart;
- begin
- try
- if (wosPlay in FState) and (wosPause in FState) then
- begin
- FReseting := False;
- {$IFDEF _MMDEBUG}
- DebugStr(0,'Try to restart device...');
- {$ENDIF}
- inherited Restarted;
- FError := WaveOutRestart(FHWaveOut);
- if FError <> 0 then
- Error('WaveOutRestart:'#10#13+WaveOutErrorString(FError));
- DoRestarted;
- end;
- except
- if assigned(FOnError) then FOnError(Self);
- Close;
- raise;
- end;
- end;
- {-- TMMWaveOut ------------------------------------------------------------}
- procedure TMMWaveOut.Stop;
- begin
- if (wosPlay in FState) or (wosPause in FState) then
- begin
- try
- EnterCritical;
- try
- FStopping := True;
- FReseting := True;
- {$IFDEF _MMDEBUG}
- if (FInHandler > 0) then
- DebugStr(0,'Try to stop device (while in Handler)...')
- else
- DebugStr(0,'Try to stop device...');
- {$ENDIF}
- finally
- LeaveCritical;
- end;
- { save the stop position }
- FEndingPosition := Position;
- FWrapArrounds := 0;
- FWrapSize := 0;
- FError := WaveOutReset(FHWaveOut);
- if FError > 0 then
- Error('WaveOutReset:'#10#13+WaveOutErrorString(FError));
- DoStopped;
- except
- if assigned(FOnError) then FOnError(Self);
- Close;
- raise;
- end;
- end;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.Opened;
- begin
- Open;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.Closed;
- begin
- Close;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.Started;
- begin
- Start;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.Paused;
- begin
- Pause;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.Restarted;
- begin
- Restart;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.Stopped;
- begin
- Stop;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.DoOpened;
- begin
- {$IFDEF _MMDEBUG}
- DebugStr(0,'Device is now open...');
- {$ENDIF}
- if Assigned(FOnOpen) then FOnOpen(Self);
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.DoClosed;
- begin
- FHWaveOut := 0;
- {$IFDEF _MMDEBUG}
- DebugStr(0,'Device is now closed...');
- {$ENDIF}
- FClosing := False;
- if not (csDestroying in ComponentState) then
- if Assigned(FOnClose) then FOnClose(Self);
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.DoStarted;
- begin
- if (FBuffersUsed > 0) then
- begin
- if not (wosPause in FState) then
- begin
- { start the buffers playing (unpause) }
- FError := WaveOutRestart(FHWaveOut);
- if FError <> 0 then
- Error('WaveOutRestart:'#10#13+WaveOutErrorString(FError));
- end;
- {$IFDEF _MMDEBUG}
- DebugStr(0,'Device is now started...');
- {$ENDIF}
- InitDSPMeter;
- if assigned(FOnStart) then FOnStart(Self);
- end
- else
- try
- inherited Stopped;
- Error('WaveOutStart:'#10#13+LoadResStr(IDS_STARTERROR));
- finally
- Close;
- end;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.DoPaused;
- begin
- FState := FState + [wosPause];
- inherited Paused;
- {$IFDEF _MMDEBUG}
- DebugStr(0,'Device is now paused...');
- {$ENDIF}
- if assigned(FOnPause) then FOnPause(Self);
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.DoRestarted;
- begin
- FState := FState - [wosPause];
- {$IFDEF _MMDEBUG}
- DebugStr(0,'Device is now restarted...');
- {$ENDIF}
- if assigned(FOnRestart) then FOnRestart(Self);
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.DoStopped;
- var
- TimeOut: integer;
- begin
- if (wosPlay in FState) or (wosPause in FState) then
- begin
- if (FInHandler > 0) then FStopIt := True
- else
- begin
- FState := FState - [wosPlay,wosPause];
- DoneDSPMeter;
- TimeOut := 500;
- { wait until all buffers returned }
- repeat
- {$IFDEF _USE_CALLBACK}
- if _Win9x_ or _WinNT4_ then
- Delay(10,False)
- else
- {$ENDIF}
- Delay(10,True);
- dec(TimeOut);
- until (FBufferCounter = 0) or (TimeOut <= 0);
- {$IFDEF _MMDEBUG}
- if (FBufferCounter > 0) then
- DebugStr(0,'TimeOut while waiting for returned headers!');
- {$ENDIF}
- { notify all other components }
- inherited Stopped;
- { unprepare wave headers }
- UnPrepareWaveHeaders;
- { free header memory and remove }
- FreeWaveHeaders;
- FBuffersUsed := 0;
- FBufferCounter := 0;
- FBufferOutIdx := 0;
- FStopIt := False;
- {$IFDEF _MMDEBUG}
- DebugStr(0,'Device is now stopped...');
- {$ENDIF}
- if not (csDestroying in ComponentState) then
- if assigned(FOnStop) then FOnStop(Self);
- if FCloseIt then Close;
- end;
- end;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.DoBufferFilled(lpwh: PWaveHdr);
- begin
- if assigned(FOnBufferFilled) then FOnBufferFilled(Self, lpwh);
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.BufferLoad(lpwh: PWaveHdr; var MoreBuffers: Boolean);
- begin
- StartDSPMeter;
- try
- with PMMWaveHdr(lpwh)^ do
- begin
- wh.dwBufferLength := BufferSize;
- wh.dwBytesRecorded := 0;
- LoopRec.dwLoop := FLooping;
- if FLooping then
- begin
- LoopRec.dwLoopCnt := FLoopCount;
- LoopRec.dwLoopTmpCnt := FLoopTempCount;
- LoopRec.dwLooping := False;
- end;
- inherited BufferLoad(lpwh, MoreBuffers);
- wh.dwBufferLength := wh.dwBytesRecorded;
- if FLooping then FLoopTempCount := LoopRec.dwLoopTmpCnt;
- end;
- finally
- StopDSPMeter;
- end;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.BufferReady(lpwh: PWaveHdr);
- begin
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.DoBufferReady(lpwh: PWaveHdr);
- begin
- { buffer has returned from driver, notify the other components }
- StartDSPMeter;
- try
- inc(FBufferOutIdx);
- if FBufferOutIdx >= FBuffersUsed then FBufferOutIdx := 0;
- { BUG-FIX for NT 4.0 SP4, it does set dwBytesRecorded to zero }
- lpwh^.dwBytesRecorded := PMMWaveHdr(lpwh)^.dwUser2;
- { we use a trick here and point to the current header which is playing }
- PMMWaveHdr(lpwh)^.lpNext := FWaveOutHdrs[FBufferOutIdx];
- { BUG-FIX for NT 4.0 SP4, it does set dwBytesRecorded to zero }
- PMMWaveHdr(PMMWaveHdr(lpwh)^.lpNext)^.wh.dwBytesRecorded := PMMWaveHdr(PMMWaveHdr(lpwh)^.lpNext)^.dwUser2;
- inherited BufferReady(lpwh);
- finally
- StopDSPMeter;
- end;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.ProcessWaveHeader(lpWaveHdr: PWaveHdr);
- var
- CurPos,LastPos: Cardinal;
- Wrapped: integer;
- TimeOut: Longint;
- begin
- if (wosPlay in FState) and not FReseting and not FStopping then
- begin
- inc(FInHandler);
- try
- { some drivers, for example the SB return the buffers }
- { in bad order, so wee can try to fix this }
- if FIX_BUFFERS then
- lpWaveHdr := FWaveOutHdrs[FBufferOutIdx]
- else
- WaveOutUnPrepareHeader(FHWaveOut, lpWaveHdr, sizeOf(TWaveHdr));
- {$IFDEF _MMDEBUG}
- DebugStr(2,'Buffer '+IntToStr(lpWaveHdr^.dwUser)+' returned');
- {$ENDIF}
- EnterCritical;
- FBytesPlayed := FBytesPlayed + lpWaveHdr^.dwBufferLength;
- LeaveCritical;
- try
- DoBufferReady(lpWaveHdr);
- {$IFDEF _MMDEBUG}
- DebugStr(2,'Buffer '+IntToStr(lpWaveHdr^.dwUser)+' ready for loading');
- {$ENDIF}
- {$IFDEF WIN32}
- { wrap arround handling }
- CurPos := GetSamplePosition;
- LastPos:= FLastPosition;
- asm
- mov Wrapped, False
- mov eax, CurPos
- cmp eax, LastPos
- jnb @@exit
- mov eax, LastPos
- sub eax, CurPos
- cmp eax, $FFFF
- jb @@exit
- mov Wrapped, True
- @@exit:
- end;
- if (Wrapped = 1) then
- begin
- { every driver wraps at a different position }
- { here we try to detect where the position has wrapped }
- { hey, this looks realy cool }
- FWrapSize := (FLastPosition and $FFF00000) or $FFFFF;
- inc(FWrapArrounds);
- end;
- {$IFDEF _MMDEBUG}
- if Wrapped <> 0 then
- begin
- DB_WriteStr(0,'Wrapped, LastPos: '+IntToStr(FLastPosition)+' (');
- DB_WriteHex(0,FLastPosition);
- DB_WriteStr(0,'), CurPos: '+IntToStr(CurPos)+' (');
- DB_WriteHex(0,CurPos);
- DB_WriteStr(0,'), WrapSize: '+IntToStr(FWrapSize)+' (');
- DB_WriteHex(0,FWrapSize);
- DB_WriteStr(0,'), Position: '+TimeToString64Ex(Position,True));
- DB_WriteStrLn(0,')');
- end;
- {$ENDIF}
- FLastPosition := CurPos;
- {$ENDIF}
- if FMoreBuffers and not FStopIt then
- begin
- { file restarted ? }
- if FLooping and PMMWaveHdr(lpWaveHdr)^.LoopRec.dwLooping then
- begin
- EnterCritical;
- { adjust GetPosition }
- FLoopPos := CurPos;
- PMMWaveHdr(lpWaveHdr)^.LoopRec.dwLooping := False;
- LeaveCritical;
-
- { notify other components that we have looped }
- Looped;
- if assigned(FOnLooping) then FOnLooping(Self);
- end;
- { wait until the buffer is marked as done, or we get trouble ! }
- TimeOut := 65000;
- { wait until the buffer is marked as done }
- while (lpWaveHdr^.dwFlags and WHDR_DONE <> WHDR_DONE) and (TimeOut > 0) do
- begin
- dec(TimeOut);
- {$IFDEF WIN32}
- Sleep(2);
- {$ENDIF}
- end;
- { load the next buffer }
- if (LoadWaveHeader(lpWaveHdr) <= 0) and not FStopIt then
- Error(LoadResStr(IDS_FILLERROR));
- { send the next buffer to the driver }
- if not FStopIt then QueueWaveHeader(lpWaveHdr);
- end;
- except
- if assigned(FOnError) then FOnError(Self);
- raise;
- end;
- finally
- dec(FInHandler);
- { can we stop it ? }
- if (FInHandler = 0) then { no more buffers, stop }
- if (FStopIt or (FBufferCounter = 0)) and not FPosted then
- begin
- FPosted := True;
- FStopping := True;
- {$IFDEF _MMDEBUG}
- DebugStr(0,'Stop Message posted...');
- {$ENDIF}
- { pause the device first so it stops playing }
- { some cards play the last buffer looped ! }
- FError := WaveOutPause(FHWaveOut);
- if FError <> 0 then
- Error('WaveOutPause:'#10#13+WaveOutErrorString(FError));
- PostMessage(FHandle,MM_WOM_STOP,FHWaveOut,0);
- end;
- end;
- end;
- end;
- {-- TMMWaveOut -----------------------------------------------------------}
- procedure TMMWaveOut.WaveOutHandler(Var Msg: TMessage);
- begin
- with Msg do
- try
- if (wParam = FHWaveOut) then
- case msg of
- MM_WOM_OPEN :
- begin
- { device is now open }
- FState := [wosOpen];
- end;
- MM_WOM_CLOSE:
- begin
- { device is now closed }
- FState := [wosClose];
- end;
- MM_WOM_DONE : begin
- {$IFDEF _USE_CALLBACK}
- if not _Win9x_ and not _WinNT4_ then
- {$ENDIF}
- begin
- dec(FBufferCounter);
- if FReseting then
- begin
- if FBufferCounter = 0 then FReseting := False;
- exit;
- end;
- end;
- if not FStopping then ProcessWaveHeader(PWaveHdr(lparam));
- exit;
- end;
- MM_WOM_STOP: begin
- {$IFDEF _MMDEBUG}
- DebugStr(0,'Stop message received...');
- {$ENDIF}
- { should stop the device }
- Stop;
- exit;
- end;
- end;
- Result := DefWindowProc(FHandle, Msg, wParam, lParam);
- except
- if assigned(FOnError) then FOnError(Self);
- Close;
- Application.HandleException(Self);
- end;
- end;
- {-- WaveOutFunc ----------------------------------------------------------}
- procedure WaveOutFunc(hWaveOut:HWaveOut;wMsg:UINT;dwInstance,dwParam1,dwParam2:Longint);
- begin
- if (dwInstance <> 0) then
- with TMMWaveOut(dwInstance) do
- {$IFDEF WIN32}
- try
- {$ELSE}
- begin
- {$ENDIF}
- case wMsg of
- WOM_OPEN :
- begin
- { device is now open }
- FState := [wosOpen];
- end;
- WOM_CLOSE:
- begin
- { device is now closed }
- FState := [wosClose];
- end;
- WOM_DONE :
- begin
- { device has returnded a buffer }
- dec(FBufferCounter);
- if FReseting then
- begin
- if FBufferCounter = 0 then FReseting := False;
- end
- else
- begin
- if not FStopping then
- case FCallBackMode of
- cmWindow: PostMessage(FHandle,MM_WOM_DONE,hWaveOut,dwParam1);
- {$IFDEF WIN32}
- cmCallBack: ProcessWaveHeader(PWaveHdr(dwparam1));
- cmThread: PostThreadMessage(FOutThread.ThreadID,MM_WOM_DONE,hWaveOut,dwParam1);
- {$ENDIF}
- end;
- end;
- end;
- end;
- {$IFDEF WIN32}
- except
- Close;
- Application.HandleException(TMMWaveOut(dwInstance));
- {$ENDIF}
- end;
- end;
- {$IFDEF WIN32}
- {-------------------------------------------------------------------------}
- procedure TMMWaveOutThread.Execute;
- {- Wait for and process output messages }
- var
- Res : DWORD;
- Msg : TMsg;
- {$IFDEF _MMDEBUG}
- Err : DWORD;
- {$ENDIF}
- Handles: array[0..1] of THandle;
- begin
- with TMMWaveOut(Owner) do
- try
- {$IFDEF _MMDEBUG}
- DebugStr(0,'Setting Thread Priority');
- {$ENDIF}
- SetPriority(FPriority);
- Handles[0] := FCloseEvent;
- Handles[1] := FResetEvent;
- { make sure we have a message queue... }
- PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE);
- {$IFDEF _MMDEBUG}
- DebugStr(0,'Setting OutEvent,ready to go !');
- {$ENDIF}
- { Ready to go, set the output event }
- SetEvent(FOutEvent);
- { Repeat until device is closed }
- while not Terminated do
- try
- if not PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
- begin
- Res := MsgWaitForMultipleObjects(2, Handles, False, INFINITE, QS_ALLEVENTS);
- case Res of
- WAIT_FAILED: { Wait failed. Shouldn't happen. }
- begin
- {$IFDEF _MMDEBUG}
- Err := GetLastError;
- DebugStr(0,'Wait Failed... Error: '+SysErrorMessage(Err));
- {$ENDIF}
- Continue;
- end;
- WAIT_OBJECT_0: { CloseEvent signaled! }
- begin
- {$IFDEF _MMDEBUG}
- DebugStr(0,'CloseEvent signaled...');
- {$ENDIF}
- { Finished here, okay to close device }
- exit;
- end;
- WAIT_OBJECT_0+1: { ResetEvent signaled! }
- begin
- {$IFDEF _MMDEBUG}
- DebugStr(0,'ResetEvent signaled...');
- {$ENDIF}
- { remove all pending Messages from the queue }
- while PeekMessage(Msg, 0, MM_WOM_DONE, MM_WOM_DONE, PM_REMOVE) do;
- ResetEvent(FResetEvent);
- Continue;
- end;
- WAIT_OBJECT_0+2: { New message was received. }
- begin
- {$IFDEF _MMDEBUG}
- DebugStr(2,'WaveOut message reveived...');
- {$ENDIF}
- { Get the message that woke us up by looping again.}
- Continue;
- end;
- end;
- end;
- { Process the message. }
- with msg do
- begin
- if (wParam = FHWaveOut) and (message = MM_WOM_DONE) then
- begin
- if not FStopping then ProcessWaveHeader(PWaveHdr(lparam));
- end
- else
- begin
- {$IFDEF _MMDEBUG}
- DebugStr(0,'Unknown message received...');
- {$ENDIF}
- TranslateMessage(Msg);
- DispatchMessage(msg);
- end;
- end;
- except
- FThreadError := True;
- if (FHWaveOut <> 0) then
- begin
- FClosing := True;
- Stop;
- WaveOutClose(FHWaveOut);
- DoClosed;
- CloseEvents;
- end;
- Application.HandleException(nil);
- exit;
- end;
- finally
- {$IFDEF _MMDEBUG}
- DebugStr(0,'Exit Thread-Proc');
- {$ENDIF}
- if not FThreadError then SetEvent(FOutEvent);
- end;
- end;
- {$ENDIF}
- initialization
- {$IFDEF _MMDEBUG}
- DB_Level(DEBUGLEVEl);
- DB_Clear;
- {$ENDIF}
- end.