18.4.1.1. Мультимедийный таймер

В ряде практически важных применений (при разработке игр, в системах реального времени для управления внешними устройствам и т. п.) интервал 55 мс может оказаться слишком велик. Современный ПК имеет мультимедийный таймер, период срабатывания которого может быть от 1 мс и выше. Однако этот таймер не имеет компонентного воплощения, поэтому для доступа к нему приходится использовать функции API.

Общая схема его использования такова. Сначала готовится процедура обратного вызова (call back) с таким заголовком:

procedure TimeProc(uID, uMsg: UINT; dwUser, dwi, dw2: DWORD);

stdcall;

Здесь uID - идентификатор события таймера (см. ниже); uMsg - не используется; dwuser - произвольное число, передаваемое процедуре в момент запуска таймера; dwi, dw2 - не используются. Запуск таймера реализуется функцией

function timeSetEnet(uDelay, uResolution: UINT;

IpTimeProc:Pointer;

dwUser: DWORD;

fuEvent: UINT): UINT;

stdcall;

external 'winmm.dll';

Здесь uDelay - требуемый период срабатывания таймера (в мс);

uResolution - разрешение таймера; значение 0 означает, что события срабатывания таймера будут возникать с максимально возможной частотой; в целях снижения нагрузки на систему вы можете увеличить это значение; IpTimeProc - адрес процедуры обратного вызова; dwUser - произвольное число, которое передается процедуре обратного вызова и которым программист может распоряжаться по своему усмотрению; fuEvent - параметр, управляющий периодичностью возникновения события таймера: time_oneshot (0) - событие возникает только один раз через uDelay мс; time_periodic (1) - события возникают периодически каждые uDelay мс. При успешном обращении функция возвращает идентификатор события таймера или 0, если обращение было ошибочным.

Таймер останавливается, и связанные с ним системные ресурсы освобождаются функцией

function timeKillEvent(uID: UINT): UINT;

stdcall; external 'winmm.dll';

Здесь uID - идентификатор события таймера, полученный с помощью timeSetEvent.

В следующем примере иллюстрируется особенность использования мультимедийного таймера.

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons, ExtCtrls;

type

TfmExample = class(TForm)

Panell: TPanel;

bbRun: TBitBtn;

bbClose: TBitBtn;

edinput: TEdit;

IbOutput: TLabel;

mmOutput: TMemo;

procedure bbRunClick(Sender: TObject);

procedure FormActivate(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations

end;

var

fmExample: TfmExample;

implementation

{$R *.DFM} // Объявление экспортируемых функций:

function timeSetEvent(uDelay, uReolution: UINT; IpTimeProc:

Pointer;

dwUser: DWORD; fuEvent: UINT): Integer;

stdcall;

external

'winmm';

function timeKillEvent (uiD: UINT): Integer;

stdcall;

external 'winmm' ;

// Объявление глобальных переменных

var

uEventID: UINT; // Идентификатор события таймера

BegTime: TDateTime; // Засечка времени

Counter: Integer; // Счетчик повторений

Delay: Word; // Период срабатывания

procedure ProcTime(uID, msg:. UINT; dwUse, dwi, dw2: DWORD);

stdcall;

// Реакция на срабатывание таймера (процедура обратного вызова)

var

h, m, s, ms: Word; // Переменные для декодирования времени

const

MaxCount = 55; // Количество повторений

begin

timeKillEvent(uEventID); // Останавливаем таймер

Counter := Counter+1; // Наращиваем счетчик

if Counter=MaxCount then // Конец цикла?

begin // - Да: декодируем время

DecodeTime((Time-BegTime)/MaxCount, h, m, s, ms);

fmExample.mmOutput.Lines.Add(

// Сообщаем результат

Format('Задано %s ms. Получено %d ms', [fmExample.edinput.Text, ms ] ) ) ;

fmExample.edinput.Text := ''; // Готовим повторение fmExample.edinput.SetFocus

end

else // - Нет: вновь пускаем таймер

uEventID := timeSetEvent(Delay,0,@ProcTime,0,1);

end;

procedure TfmExample.bbRunClick(Sender: TObject);

// Запускает таймер, edinput содержит требуемый период.

begin

// Проверяем задание периода

if edinput.Text= '' then Exit;

try

Delay := StrToInt(edinput.Text)

except

ShowMessage('Ошибка ввода числа');

edinput.SelectAll;

edinput.SetFocus ;

Exit

end;

Counter := 0;

// Сбрасываем счетчик

BegTime := Time;

// Засекаем время

// Запускаем таймер:

uEventID := timeSetEvent(Delay,0,@ProcTime,О,1);

if uEventID=0 then

ShowMessage('Ошибка запуска таймера ' )

end;

procedure TfmExample.FormActivate(Sender: TObject);

begin

edinput.SetFocus

end;

end.