본문 바로가기
Delphi Tip/컴포넌트

델파이 타이머(TTimer) Interval 시간오차 해결방법

by MonoSoft 2023. 10. 11.
728x90
반응형

델파이 타이머

728x90

 

 

델파이에서 컴포넌트로 제공되는

TTimer의 경우에는 정확한 시간에 맞게 콜백을 하지 않는다.

 

Interval 을 1/1000 초까지 조정할 수 있지만

실제로 호출되는 것은 1/18.2 초 단위로 끊어 지게 되기때문이다.

 

만약 TTimer로 MP3 플레이어나 동영상 재생기를 만든다면 어떻게 될까 ?

 

MP3 음향이 정확한 박자로 재생하지 못할 것이고 동영상도

프레임이 정확하지 못해서 눈이 상당히 피로해질 거다.

 

하지만 지금 소개하는 타이머를 쓰게 되면

1ms 의 오차정도만을 허용하는 고급의 타이머가 생성되게 됩니다.

 

어려운 내용이 아니기 때문에 그냥 소스에

주석을 붙이는 차원으로 끝내겠습니다.

 

그리고 dfm 의 내용도 Text 로 뒤에 첨가하였으니

직접 폼을 만들어서 테스트 해보면 된다 폼에서

마우스 오른쪽 버튼을 누른후 제일 아래의 View As Text 를 선택하고

그 내용을 아래의 dfm 의 Text로 바꾸면 된다

 

 

타이머 소스

unit Unit1;

 

interface

 

uses

  Windows, Messages, SysUtils, Classes, Graphics, Controls,

  Forms, Dialogs, ExtCtrls, StdCtrls, mmSystem;

 

const

  USR_TIMER_ELAPSED = WM_USER+100;

 

type

  TForm1 = class(TForm)

    Edit1 : TEdit; // 타이머 콜백된 회수 출력

    Edit2 : TEdit; // 초당 콜백 회수

    Button1 : TButton; // 타이머 종료 버튼

    StaticText1 : TStaticText; // 라벨 1

    StaticText2 : TStaticText; // 라벨 2

    Panel1 : TPanel; // 현재 설정된 콜백 주기 출력

    procedure FormShow(Sender: TObject);

    procedure FormDestroy(Sender: TObject);

    procedure Button1Click(Sender: TObject);

  private

    FTimerID: UINT;

    Fms : UINT;

    FCouter : integer; FTime : longint;

  procedure EditCallback(var M : TMessage); message USR_TIMER_ELAPSED;

   // 사용자 메시지를 받기 위한 메시지 처리 루틴

  end;

 

var

  Form1 : TForm1;

 

implementation

 

{$R *.DFM}

 

procedure TForm1.EditCallback(var M : TMessage);

var

  f : real;

  s : string;

begin

  inc(Form1.FCouter);

  str(Form1.FCouter,s);

 

  Form1.Edit1.Text := s;

 

  f := (Form1.FCouter * 1000) / (GetCurrentTime - Form1.FTime);

  str(f : 6 : 3,s);

 

  Form1.Edit2.Text := s; // 초당 콜백 회수 출력

 

  if Form1.FCouter = 0 then

  begin

    Form1.FTime := GetCurrentTime; // 콜백 카운트의 초기화 end;

  end;

 

procedure TimerProc(uTimer, uMessage : UNIT; dwUser, dw1, dw2 : DWORD); stdcall;

begin

  PostMessage(dwUser,USR_TIMER_ELAPSED,0,0); // 멀티미디어 타이머 콜백 함수에는 대부분의 API가 사용 불가하다.

  // 그러므로 다시 메시지를 사용자 메시지 루틴으로 돌려야 한다.

end;

 

procedure TForm1.FormShow(Sender: TObject);

var

  s : string;

  TimeCaps : TTimeCaps;

begin

  Fms := 100; // 타이머 주기를 100ms 마다 한 번씩 호출되게 한다.

  if timeGetDevCaps(@TimeCaps,sizeof(TimeCaps)) <> TIMERR_NOERROR then

    raise Exception.Create(' Error !!'); // 타이머 디바이스에 대한 정보를 얻어 온다.

 

  if Fms < TimeCaps.wPeriodMin then

    Fms := TimeCaps.wPeriodMin; // 실제 가능한 호출 주기보다 작으면

 

  if Fms > TimeCaps.wPeriodMax then

    Fms := TimeCaps.wPeriodMax; // 실제 가능한 호출 주기보다 크면

 

  timeBeginPeriod(Fms); // 타이머 주기를 지정한다.

 

  FTimerID := timeSetEvent(Fms,Fms,@TimerProc,integer(handle), TIME_PERIODIC);

  // 타이머를 설정한다. 콜백 함수를 지정하고, TIME_PERIODIC를 사용하여

  // 주기적으로 계속 호출되게 만든다.

 

  if FTimerID = 0 then

  begin

    timeEndPeriod(Fms);

    raise Exception.Create(' Error !!');

    Close;

    Exit; // 에러 처리 루틴

  end;

 

  str(Fms,s);

  Panel1.Caption := '타이머 설정 :' + s + 'ms'; FCouter := -1;

end;

 

procedure TForm1.FormDestroy(Sender: TObject);

begin

  if FTimerID <> 0 then

  begin

    timeKillEvent(FTimerID); // 타이머를 해제한다.

    timeEndPeriod(Fms); // 타이머 주기를 해제한다.

  end;

end;

 

procedure TForm1.Button1Click(Sender: TObject);

begin

  Close;

end;

end.

 

 

화면 소스

object Form1: TForm1

Left = 276

Top = 145

Width = 124

Height = 214

Caption = 'Form1'

Font.Charset = DEFAULT_CHARSET

Font.Color = clWindowText

Font.Height = -11

Font.Name = 'MS Sans Serif'

Font.Style = [] OnDestroy = FormDestroy OnShow = FormShow PixelsPerInch = 96 TextHeight = 13

object

Edit1: TEdit

Left = 0

Top = 56

Width = 113

Height = 21

ImeName = '한국어(한글)'

TabOrder = 0

end

object Edit2: TEdit

Left = 0

Top = 104

Width = 113

Height = 21

ImeName = '한국어(한글)'

TabOrder = 1

end

object Button1: TButton

Left = 24

Top = 144

Width = 75

Height = 25

Caption = 'Stop !!'

TabOrder = 2

OnClick = Button1Click end

object StaticText1: TStaticText

Left = 0

Top = 40

Width = 116

Height = 17

Alignment = taCenter

Caption = '타이머 콜백 호출'

Font.Charset = HANGEUL_CHARSET

Font.Color = clWindowText

Font.Height = -13

Font.Name = '돋움체'

Font.Style = []

ParentFont = False

TabOrder = 3

end

object StaticText2: TStaticText

Left = 8

Top = 88

Width = 102

Height = 17

Caption = '초당 콜백 회수'

Font.Charset = HANGEUL_CHARSET

Font.Color = clWindowText

Font.Height = -13

Font.Name = '돋움체'

Font.Style = []

ParentFont = False

TabOrder = 4

end

object Panel1: TPanel

Left = 0

Top = 0

Width = 116

Height = 25

Align = alTop

BevelInner = bvLowered

Font.Charset = HANGEUL_CHARSET

Font.Color = clWindowText

Font.Height = -11

Font.Name = '돋움체'

Font.Style = []

ParentFont = False

TabOrder = 5

end

end

 

위의 소스를 실행해 보면 항상 0.005 초 정도의

오차만을 가지고 초당 10 회라는 콜백 회수를 계속 유지한다.

( 10.000 초를 기준으로 진동한다고 보는 게 나을 것 같죠 ? )

 

728x90
반응형

댓글