Главная » Delphi » Компонент TddgWaveFile: пример использования функции DefineBinaryProperty()

0

Как уже говорилось, функцию DefineBinaryProperty() лучше всего  использо вать для сохранения вместе  с компонентом графической или звуковой  информации. Фактически в библиотеке VCL эта методика используется для сохранения изображе ний,  связанных с компонентом, например значка  (Glyph) компонента TBitBtn или пиктограммы (Icon) компонента TForm. В этом  разделе изложено, как использовать подобную  методику  для сохранения звуковых  данных, связанных с компонентом TddgWaveFile.

НА ЗАМЕТКУ

ddgWaveFile — это полнофункциональный компонент, содержащий пользовательское свойство, редактор данного свойства и редактор компонента, позволяющий воспроиз- водить звуки во время разработки. Об этом речь пойдет несколько позже, а пока рас- смотрим механизм сохранения бинарных данных свойства.

Ниже  приведен исходный код метода  DefineProperties() компонента TddgWave- File.

procedure TddgWaveFile.DefineProperties(Filer: TFiler);

{ Определяет двоичное свойство "Data" для поля FData. Благодаря этому значение поля FData может быть считано и записано в файл DFM.

}

function DoWrite: Boolean;

begin

if Filer.Ancestor <> nil then

Result := not (Filer.Ancestor is TddgWaveFile) or

not Equal(TddgWaveFile(Filer.Ancestor))

else

Result := not Empty;

end;

begin

inherited DefineProperties(Filer);

Filer.DefineBinaryProperty(‘Data’, ReadData,

WriteData, DoWrite);

end;

Данный метод  определяет бинарное свойство Data, которое считывается и запи сывается с помощью методов  ReadData() и WriteData() компонента. Причем дан ные записываются только  в том случае, когда функция DoWrite() возвращает значе ние  True. Более  подробная информация об  этой  функции приведена в настоящей главе далее.

Методы  ReadData() и WriteData() определены следующим образом:

procedure TddgWaveFile.ReadData(Stream: TStream);

{ Читает данные WAV из потока DFM. }

begin

LoadFromStream(Stream);

end;

procedure TddgWaveFile.WriteData(Stream: TStream);

{ Записывает данные WAV из потока DFM. }

begin

SaveToStream(Stream);

end;

Как видите, оба этих  небольших метода  просто вызывают методы  LoadFrom- Stream() и  SaveToStream(),  которые  также   определены  в  классе   компонента TddgWaveFile. Вот исходный код метода LoadFromStream():

procedure TddgWaveFile.LoadFromStream(S: TStream);

{ Загружает данные WAV из потока S. Эта процедура освобождает

память, предварительно выделенную для данных FData. }

begin

if not Empty then

FreeMem(FData, FDataSize);

FDataSize := 0;

FData := AllocMem(S.Size);

FDataSize := S.Size;

S.Read(FData^, FDataSize);

end;

Вначале  данный метод  определяет, была  ли ранее выделена память  (проверяя зна чение поля FDataSize). Если это так (т.е. это значение больше  нуля), то память, на ко торую указывает значение поля FData, освобождается. Затем  для поля FData выделяет ся новый блок памяти и полю  FDataSize присваивается размер потока поступающих данных. После чего содержимое потока считывается по указателю поля FData.

Метод SaveToStream() значительно проще вышеописанного. Вот его определение.

procedure TddgWaveFile.SaveToStream(S: TStream);

{ Сохраняет данные WAV в поток S. }

begin

if FDataSize > 0 then S.Write(FData^, FDataSize);

end;Этот  метод  записывает данные, на которые ссылается указатель  FData, в поток  S

типа TStream.

Локальная функция DoWrite(), вызываемая внутри  метода  DefineProperties(),

определяет необходимость записи в поток значения свойства Data. Конечно, если поле

FData “пустует”, то ничего в поток  записывать не нужно. Кроме  того,  следует принять

дополнительные меры для того,  чтобы  компонент корректно  работал  при наследова

нии  формы: необходимо убедиться, что  свойство Ancestor объекта TFiler не равно nil. Если это  так и свойство указывает на некоторую версию предка  текущего  компо нента, то следует также удостовериться, что данные, приготовленные для записи, отли чаются  от данных  предка.  Если не выполнить такую дополнительную проверку, то ко пия данных  (в данном  случае файла  wav) будет записана в каждую форму  потомка и из менение файла wav предка не приведет к изменению форм потомков.

В листинге 12.9  представлен модуль  Wavez.pas, содержащий полный исходный

код этого компонента.

Листинг 12.9. Wavez.pas — пример компонента, инкапсулирующего  файл WAV

unit Wavez;

interface uses

SysUtils, Classes;

type

{ Для создания редактора используется специализированный

тип string. }

TWaveFileString = type string;

EWaveError = class(Exception); TWavePause = (wpAsync, wpsSync);

TWaveLoop = (wlNoLoop, wlLoop);

TddgWaveFile = class(TComponent)

private

FData: Pointer;

FDataSize: Integer;

FWaveName: TWaveFileString;

FWavePause: TWavePause;

FWaveLoop: TWaveLoop;

FOnPlay: TNotifyEvent;

FOnStop: TNotifyEvent;

procedure SetWaveName(const Value: TWaveFileString);

procedure WriteData(Stream: TStream);

procedure ReadData(Stream: TStream);

protected

procedure DefineProperties(Filer: TFiler); override;

public

destructor Destroy; override;

function Empty: Boolean;function Equal(Wav: TddgWaveFile): Boolean; procedure LoadFromFile(const FileName: String); procedure LoadFromStream(S: TStream);

procedure Play;

procedure SaveToFile(const FileName: String);

procedure SaveToStream(S: TStream);

procedure Stop;

published

property WaveLoop: TWaveLoop read FWaveLoop write FWaveLoop;

property WaveName: TWaveFileString read FWaveName

write SetWaveName;

property WavePause: TWavePause read FWavePause

write FWavePause;

property OnPlay: TNotifyEvent read FOnPlay write FOnPlay;

property OnStop: TNotifyEvent read FOnStop write FOnStop;

end;

implementation

uses MMSystem, Windows;

{ TddgWaveFile }

destructor TddgWaveFile.Destroy;

{ Гарантирует освобождение всей выделенной ранее памяти. }

begin

if not Empty then

FreeMem(FData, FDataSize);

inherited Destroy;

end;

function StreamsEqual(S1, S2: TMemoryStream): Boolean;

begin

Result := (S1.Size = S2.Size) and

CompareMem(S1.Memory, S2.Memory, S1.Size);

end;

procedure TddgWaveFile.DefineProperties(Filer: TFiler);

{ Определяет бинарное свойство "Data" для поля FData. Благодаря

этому данные, на которые указывает поле Fdata, могут быть считаны и

записаны в файл DFM. }

function DoWrite: Boolean;

begin

if Filer.Ancestor <> nil then

Result := not (Filer.Ancestor is TddgWaveFile) or

not Equal(TddgWaveFile(Filer.Ancestor))

else

Result := not Empty;

end;

begin

inherited DefineProperties(Filer);Filer.DefineBinaryProperty(‘Data’, ReadData, WriteData, DoWrite);

end;

function TddgWaveFile.Empty: Boolean;

begin

Result := FDataSize = 0;

end;

function TddgWaveFile.Equal(Wav: TddgWaveFile): Boolean;

var

MyImage, WavImage: TMemoryStream;

begin

Result := (Wav <> nil) and (ClassType = Wav.ClassType);

if Empty or Wav.Empty then begin

Result := Empty and Wav.Empty;

Exit;

end;

if Result then begin

MyImage := TMemoryStream.Create;

try

SaveToStream(MyImage);

WavImage := TMemoryStream.Create;

try

Wav.SaveToStream(WavImage);

Result := StreamsEqual(MyImage, WavImage);

finally

WavImage.Free;

end;

finally

MyImage.Free;

end;

end;

end;

procedure TddgWaveFile.LoadFromFile(const FileName: String);

{ Загружает данные WAV из файла FileName. Обратите внимание: эта

процедура не присваивает значения свойству WaveName. }

var

F: TFileStream;

begin

F := TFileStream.Create(FileName, fmOpenRead);

try

LoadFromStream(F);

finally

F.Free;

end;

end;

procedure TddgWaveFile.LoadFromStream(S: TStream);

{ Загружает данные WAV из потока S. Эта процедура освобождает

память, выделенную перед этим для FData. }

beginif not Empty then FreeMem(FData, FDataSize); FDataSize := 0;

FData := AllocMem(S.Size); FDataSize := S.Size; S.Read(FData^, FDataSize);

end;

procedure TddgWaveFile.Play;

{ Воспроизводит звук WAV из FData с учетом параметров, содержащихся

в FWaveLoop и FWavePause. }

const

LoopArray: array[TWaveLoop] of DWORD = (0, SND_LOOP);

PauseArray: array[TWavePause] of DWORD = (SND_ASYNC, SND_SYNC);

begin

{ Убедимся, что компонент содержит данные. }

if Empty then

raise EWaveError.Create(‘No wave data’);

if Assigned(FOnPlay) then FOnPlay(Self);       // произошло событие

{ Попытка воспроизведения звука wave. }

if not PlaySound(FData, 0, SND_MEMORY or

PauseArray[FWavePause] or

LoopArray[FWaveLoop]) then

raise EWaveError.Create(‘Error playing sound’);

end;

procedure TddgWaveFile.ReadData(Stream: TStream);

{ Считывает данные WAV из потока DFM. }

begin

LoadFromStream(Stream);

end;

procedure TddgWaveFile.SaveToFile(const FileName: String);

{ Сохраняет данные WAV в файл FileName. }

var

F: TFileStream;

begin

F := TFileStream.Create(FileName, fmCreate);

try

SaveToStream(F);

finally

F.Free;

end;

end;

procedure TddgWaveFile.SaveToStream(S: TStream);

{ Сохраняет данные WAV в поток S. }

begin

if not Empty then

S.Write(FData^, FDataSize);

end;

procedure TddgWaveFile.SetWaveName(const Value: TWaveFileString);

{ Метод записи свойства WaveName. Этот метод отвечает за установкусвойства WaveName и загрузку данных WAV из файла Value. }

begin

if Value <> ” then begin

FWaveName := ExtractFileName(Value);

{ Не загружать из файла при загрузке из потока DFM, так

как поток DFM уже содержит данные. }

if (not (csLoading in ComponentState)) and

FileExists(Value) then

LoadFromFile(Value);

end

else begin

{ Если строка Value пуста, то необходимо освободить память,

выделенную для данных WAV. }

FWaveName := ”;

if not Empty then

FreeMem(FData, FDataSize);

FDataSize := 0;

end;

end;

procedure TddgWaveFile.Stop;

{ Останавливает воспроизведение текущего звука WAV. }

begin

if Assigned(FOnStop) then FOnStop(Self);     // произошло событие

PlaySound(Nil, 0, SND_PURGE);

end;

procedure TddgWaveFile.WriteData(Stream: TStream);

{ Записывает данные WAV в поток DFM. }

begin

SaveToStream(Stream);

end;

end.

Источник: Тейксейра, Стив, Пачеко, Ксавье.   Borland Delphi 6. Руководство разработчика. : Пер.  с англ. — М. : Издательский дом “Вильямс”, 2002. —  1120 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии