📄 mmtrigg.pas
字号:
{========================================================================}
{= (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/mmtools.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: 06.03.98 - 15:58:36 $ =}
{========================================================================}
unit MMTrigg;
{$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,
MMRegs,
MMPCMSup,
MMAlloc,
MMWaveIO
{$IFDEF _MMDEBUG}
,MMDebug
{$ENDIF}
;
{$IFDEF _MMDEBUG}
const
DEBUGLEVEL = 2; { 0,1,2 }
{$ENDIF}
const
{$IFDEF WIN32}
TRIGGER_PRIORITY : TThreadPriority = tpNormal;
{$ENDIF}
MINBUFFERSIZE = 32;
type
TMMTriggerStates = (trClose, trOpen, trPlay, trPause);
TMMTriggerState = set of TMMTriggerStates;
EMMTriggerError = class(Exception);
TMMErrorEvent = procedure (Sender: TObject; var Handled: Boolean) of object;
{$IFDEF WIN32}
TMMTrigger = class;
{-- TMMTriggerThread --------------------------------------------------}
TMMTriggerThread = class(TMMDSPThread)
private
procedure Execute; override;
end;
{$ENDIF}
{-- TMMTrigger ---------------------------------------------------------}
TMMTrigger = class(TMMCustomSoundComponent)
private
FHandle : THandle; { handle used for callback window }
FState : TMMTriggerState;{ Current device state }
FWaveHdr : PWaveHdr; { Wave Headers and Buffer }
FInHandler : integer; { marks that we in any event handler }
FThreadError : Boolean; { Error in Thread 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 }
FBytesPlayed : Longint; { total bytes we have realy played }
FMoreBuffers : Boolean; { more buffers to write ? }
FTimeFormat : TMMTimeFormats; { the actual time format for Position}
FInterval : integer; { trigger interval in ms }
FAllocator : TMMAllocator;
{$IFDEF WIN32}
FTriggerThread : TMMTriggerThread;{ Trigger Thread for callback handling}
DataSection : TRtlCriticalSection;{ CriticalSection Object }
DataSectionOK : Boolean; { CriticalSection is prepared }
FGeneralEvent : THandle; { event object for general purpose }
FTriggerEvent : THandle; { event object for notify handling }
FCloseEvent : THandle; { event object to close the device }
{$ENDIF}
FHandled : Boolean;
{ Events }
FOnError : TNotifyEvent; { There was an error }
FOnErrorEx : TMMErrorEvent; { There was an error }
FOnBufferFilled: TMMBufferEvent; { Wave buffer filled event }
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 }
procedure SetTimeFormat(aValue: TMMTimeFormats);
procedure SetInterval(aValue: integer);
function GetPosition: Longint;
procedure TriggerHandler(var Msg: TMessage);
procedure AllocWaveHeader(var lpWaveHdr: PWaveHdr);
procedure FreeWaveHeader;
function LoadWaveHeader(lpWaveHdr: PWaveHdr): Longint;
procedure QueueWaveHeader(lpWaveHdr: PWaveHdr);
procedure ProcessWaveHeader(lpWaveHdr: PWaveHdr);
{$IFDEF WIN32}
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 Error(Msg: string); virtual;
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;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Open; virtual;
procedure Close; virtual;
procedure Start; virtual;
procedure Pause; virtual;
procedure Restart; virtual;
procedure Stop; virtual;
{$IFDEF WIN32}
procedure SynchronizeVCL(VCLProc: TThreadMethod);
{$ENDIF}
property State: TMMTriggerState read FState;
property Position: Longint read GetPosition;
published
{ Events }
property OnError: TNotifyEvent read FOnError write FOnError;
property OnErrorEx: TMMErrorEvent read FOnErrorEx write FOnErrorEx;
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 OnBufferFilled: TMMBufferEvent read FOnBufferFilled write FOnBufferFilled;
property OnBufferReady;
property OnBufferLoad;
property Input;
property Output;
property BufferSize;
property Interval: integer read FInterval write SetInterval default 0;
property TimeFormat: TMMTimeFormats read FTimeFormat write SetTimeFormat default tfByte;
end;
implementation
uses consts;
const
MM_WOM_STOP = MM_USER+1;
{-------------------------------------------------------------------------}
procedure DebugStr(Level: integer; s: String);
begin
{$IFDEF _MMDEBUG}
if (s <> ' ') then s := 'Trigger: '+s;
DB_WriteStrLn(Level,s);
{$ENDIF}
end;
{== TMMTrigger ===========================================================}
constructor TMMTrigger.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
{ Set defaults }
FState := [trClose];
FBytesPlayed := 0;
FTimeFormat := tfByte;
FMoreBuffers := False;
FClosing := False;
FStopping := False;
FInterval := 0;
FAllocator := TMMAllocator.Create;
{$IFDEF WIN32}
DataSectionOK := False;
{$ENDIF}
ErrorCode := ComponentRegistered(InitCode, Self, ClassName);
if (ErrorCode <> 0) then RegisterFailed(InitCode, Self , ClassName);
end;
{-- TMMTrigger -----------------------------------------------------------}
destructor TMMTrigger.Destroy;
begin
{ Close the device if it's open }
Close;
{ Destroy the window for callback notification }
if (FHandle <> 0) then DeallocateHwnd(FHandle);
if assigned(FAllocator) then FAllocator.Free;
inherited Destroy;
end;
{-- TMMTrigger -----------------------------------------------------------}
procedure TMMTrigger.Error(Msg: string);
begin
if assigned(FOnError) then FOnError(Self);
raise EMMTriggerError.Create(Msg);
end;
{-- TMMTrigger -----------------------------------------------------------}
{ Allocate memory for the Trigger header and buffer }
procedure TMMTrigger.AllocWaveHeader(var lpWaveHdr: PWaveHdr);
begin
if (lpWaveHdr = Nil) then
begin
{ set up a wave header for playing and lock. }
lpWaveHdr := FAllocator.AllocBuffer(GHND,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;
{-- TMMTrigger -----------------------------------------------------------}
procedure TMMTrigger.FreeWaveHeader;
begin
{ unlock and free memory for TriggerHdr }
if FWaveHdr <> nil then
begin
FAllocator.FreeBuffer(Pointer(FWaveHdr));
FWaveHdr := Nil;
end;
end;
{-- TMMTrigger ------------------------------------------------------------}
procedure TMMTrigger.SetInterval(aValue: integer);
begin
if (aValue <> FInterval) then
begin
FInterval := Max(aValue,0);
end;
end;
{-- TMMTrigger ------------------------------------------------------------}
procedure TMMTrigger.SetTimeFormat(aValue: TMMTimeFormats);
begin
if (aValue <> FTimeFormat) then
begin
FTimeFormat := aValue;
end;
end;
{-- TMMTrigger ------------------------------------------------------------}
function TMMTrigger.GetPosition: Longint;
Var
Bytes: Longint;
begin
Result := 0;
if (trOpen in FState) and (PWaveFormat <> Nil) and not FClosing then
begin
EnterCritical;
try
Bytes := FBytesPlayed;
case FTimeFormat of
tfMilliSecond: Result := wioBytesToTime(PWaveFormat,Bytes);
tfByte : Result := Bytes;
tfSample : Result := wioBytesToSamples(PWaveFormat,Bytes);
end;
finally
LeaveCritical;
end;
end;
end;
{-- TMMTrigger -----------------------------------------------------------}
Procedure TMMTrigger.SetPWaveFormat(aValue: PWaveFormatEx);
begin
{ stop and close the device }
Close;
inherited SetPWaveFormat(aValue);
end;
{-- TMMTrigger -----------------------------------------------------------}
Procedure TMMTrigger.SetBufferSize(aValue: Longint);
begin
if (aValue <> inherited GetBufferSize) then
begin
if (trOpen in FState) then
Error(LoadResStr(IDS_PROPERTYOPEN));
if assigned(FAllocator) then
FAllocator.Discard;
inherited SetBufferSize(Max(aValue,MINBUFFERSIZE));
end;
end;
{-- TMMTrigger -----------------------------------------------------------}
function TMMTrigger.GetBufferSize: Longint;
begin
Result := inherited GetBufferSize;
end;
{-- TMMTrigger -----------------------------------------------------------}
function TMMTrigger.LoadWaveHeader(lpWaveHdr: PWaveHdr): Longint;
begin
FMoreBuffers := False;
BufferLoad(lpWaveHdr, FMoreBuffers);
Result := lpWaveHdr^.dwBytesRecorded;
if Result <= 0 then FMoreBuffers := False;
end;
{-- TMMTrigger -----------------------------------------------------------}
procedure TMMTrigger.QueueWaveHeader(lpWaveHdr: PWaveHdr);
begin
{ this is the chance to modify the data in the buffer !!! }
DoBufferFilled(lpWaveHdr);
end;
{$IFDEF WIN32}
{-- TMMTrigger -----------------------------------------------------------}
procedure TMMTrigger.SynchronizeVCL(VCLProc: TThreadMethod);
begin
if (FGeneralEvent <> 0) then
begin
FTriggerThread.Synchronize(VCLProc);
end
else VCLProc;
end;
{-- TMMTrigger -----------------------------------------------------------}
procedure TMMTrigger.InitThread;
begin
EnterCritical;
try
FThreadError := False;
{ create event objects }
FGeneralEvent:= CreateEvent(nil, False, False, nil);
FTriggerEvent:= CreateEvent(nil, False, False, nil);
FCloseEvent := CreateEvent(nil, False, False, nil);
{ create the output thread }
FTriggerThread := TMMTriggerThread.CreateSuspended(Self);
if (FTriggerThread = nil) then
Error('Trigger:'#10#13+LoadResStr(IDS_THREADERROR));
FTriggerThread.FreeOnTerminate := True;
FTriggerThread.Resume;
{$IFDEF _MMDEBUG}
DebugStr(0,'Wait for Thread start...');
{$ENDIF}
{ Wait for it to start... }
if WaitForSingleObject(FGeneralEvent, 5000) <> WAIT_OBJECT_0 then
Error('Trigger:'#10#13+LoadResStr(IDS_THREADERROR));
{$IFDEF _MMDEBUG}
DebugStr(0,'Thread Started');
{$ENDIF}
finally
LeaveCritical;
end;
end;
{-- TMMTrigger -----------------------------------------------------------}
procedure TMMTrigger.DoneThread;
begin
if (FGeneralEvent <> 0) and not FThreadError then
begin
while FTriggerThread.Suspended do FTriggerThread.Resume;
{ Force the trigger thread to close... }
SetEvent(FCloseEvent);
{ ...and wait for it to die }
WaitForSingleObject(FGeneralEvent, 5000);
{ close all events and remove critical section }
CloseEvents;
{$IFDEF _MMDEBUG}
DebugStr(0,'Thread Terminated');
{$ENDIF}
end;
end;
{-- TMMTrigger -----------------------------------------------------------}
procedure TMMTrigger.CloseEvents;
begin
if (FGeneralEvent <> 0) then
begin
{ release events }
CloseHandle(FGeneralEvent);
CloseHandle(FTriggerEvent);
CloseHandle(FCloseEvent);
FGeneralEvent := 0;
FTriggerEvent := 0;
FCloseEvent := 0;
{ Free the critical section }
DoneCritical;
end;
end;
{$ENDIF}
{-- TMMTrigger -----------------------------------------------------------}
procedure TMMTrigger.InitCritical;
begin
{$IFDEF WIN32}
{ create critical section object }
FillChar(DataSection, SizeOf(DataSection), 0);
InitializeCriticalSection(DataSection);
DataSectionOK := True;
{$ENDIF}
end;
{-- TMMTrigger -----------------------------------------------------------}
procedure TMMTrigger.EnterCritical;
begin
{$IFDEF WIN32}
if DataSectionOK then
EnterCriticalSection(DataSection);
{$ENDIF}
end;
{-- TMMTrigger -----------------------------------------------------------}
procedure TMMTrigger.LeaveCritical;
begin
{$IFDEF WIN32}
if DataSectionOK then
LeaveCriticalSection(DataSection);
{$ENDIF}
end;
{-- TMMTrigger -----------------------------------------------------------}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -