Тема 9. Проектирование мультимедиа и анимационных приложений

1 Процедуры воспроизведения звуков Windows - Веер

2 Начала анимации - создание мультипликаций

3 Универсальный проигрыватель MediaPlayer

4 Воспроизведение немых видео клипов - компонент Animate

Презентация к лекции

 

1.   Процедуры воспроизведения звуков Windows - Веер

 

Так же, как существует множество форматов графических файлов, существует немало файлов звуковых и мультимедиа. Коротко охарактеризуем обе эти группы файлов, так как файлы мультимедиа часто содержат звуковую дорожку.

1. Волновой файл .wav. - в нем записано цифровое представление информации о волновой форме электрического сигнала, соответствующего каждому звуку.

2. Файлы цифрового интерфейса музыкальных инструментов .midi -используются для хранения музыкальных фрагментов. В этих файлах звук хранится в виде данных о том, на каких инструментах исполняются определенные ноты и как долго они звучат. Волновые и MIDI файлы могут хранить только звук или музыку.

3. Файлы AVI и MPEG - для хранения видео информации. Большинство видеофайлов поддерживают также хранение звуковой дорожки, так что звук воспроизводится синхронно с картинкой.

Наиболее простой процедурой, управляющей звуком, является процедура Веер, которая воспроизводит стандартный звуковой сигнал, установленный в Windows.

Поместите на форму кнопку и в обработчике щелчка на кнопку напишите оператор:

Веер();

После запуска приложения при щелчке на кнопке раздастся стандартный звук Windows.

 

2 Начала анимации - создание простой мультипликации

 

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

Попробуем сделать простую мультипликацию, например условное изображение человечка, шагающего и бьющего при этом в литавры.

Перенесем на форму компоненты Image, кнопку Button и таймер Timer. Кнопку разместим внизу формы. Основную площадь формы должен занимать компонент Image, на котором и будет рисоваться изображение.

Вначале рассмотрим пример приложения, в котором мультипликация состоит из двух кадров. Таймер будет задавать темп смены кадров, поэтому зададим значение свойства Interval таймера достаточно большим, например, 500 (поскольку интервалы задаются в миллисекундах, то это значение соответствует 0,5 сек). Значение параметра Enabled таймера установим в false. Таймер будет управляться кнопкой.

В заголовочном файле модуля формы (unit1.h) добавим строку

void _fastcall Draw();

Это объявление функции,  которая будет рисовать изображение.

А текст самого модуля может иметь вид:  

short int cadr = 0;

short int H=30;              // шаг

short int Xpos = 3 * H;           // координата туловища

short int Ypos = 120;             // "земля"

short int Hrasn = 30;             // высота тела

short int Rhead =10;             // радиус головы

short int Rhead2 = Rhead /2;        // радиус литавров

short int revers =1;                // направление движения

short int L = H * 1.41;            // длина ноги

 

void    _fastcall TForm1::Draw()

{

short int Yhead;                     // координата низа головы

switch (cadr)

{

 case 0:

Yhead = Ypos-H- Hrasn;

Image1->Canvas->MoveTo(Xpos-H,Ypos);

Image1->Canvas->LineTo (Xpos,Ypos-H);               // нога

Image1->Canvas->LineTo(Xpos+H,Ypos);                // другая нога

Image1->Canvas->MoveTo(Xpos, Ypos-H);

Image1->Canvas->LineTo(Xpos,Yhead);                    // туловище

Image1->Canvas->MoveTo(Xpos+revers*H, Yhead-H) ;

Image1->Canvas->LineTo(Xpos, Yhead+4) ;           // рука

Image1->Canvas->Ellipse (Xpos+revers*H-Rhead2, Yhead-H-Rhead2,

Xpos+revers*H+Rhead2,Yhead-H+Rhead2);

Image1->Canvas->LineTo(Xpos + revers*H, Yhead+H);    // другая рука

Image1->Canvas->Ellipse(Xpos+revers*H-Rhead2,Yhead+H-Rhead2,

Xpos+revers*H+Rhead2,Yhead+H+Rhead2);

Image1->Canvas->Ellipse (Xpos-Rhead,Yhead,Xpos+Rhead,

Yhead-2*Rhead);

Image1->Canvas->Rectangle(Xpos-Rhead, Yhead-2*Rhead-1,

Xpos+Rhead,Yhead-2*Rhead-4);                              //шляпа

 break;

case 1:

Yhead = Ypos-L- Hrasn;

Image1->Canvas->MoveTo(Xpos, Ypos) ;

Image1->Canvas->LineTo(Xpos, Yhead) ;

Image1->Canvas->MoveTo(Xpos, Yhead+4) ;

Image1->Canvas->LineTo(Xpos+revers*L,Yhead+4);

Image1->Canvas->Ellipse(Xpos+revers*L-Rhead2,Yhead+4-Rhead2,

Xpos+revers*L+Rhead2,Yhead+4+Rhead2) ;

Image1->Canvas->Ellipse (Xpos-Rhead, Yhead, Xpos+Rhead, Yhead-2*Rhead);

Image1->Canvas->Rectangle(Xpos-H/2, Yhead-2*Rhead-1, Xpos+H/2,Yhead-2*Rhead-4);

}

}

 

void    _fastcall TForm1::TimerlTimer(TObject *Sender)

{

Draw ();

if ((Xpos >= Image1->Picture->Width-H) || (Xpos <= H) )

revers = -revers;

Xpos = Xpos + revers * H;

cadr = 1 - cadr;

Draw ();

}

void    _fastcall TForm1::BRunClick(TObject *Sender)

{

Timer1->Enabled = !Timer1->Enabled;

}

 

void    _fastcall TForm1::FormCreate(TObject *Sender)

{

Image1->Canvas->MoveTo(0,Ypos + 3) ;

Image1->Canvas->Pen->Width = 4;

Image1->Canvas->LineTo(Image1->ClientWidth, Ypos+3); // земля

Image1->Canvas->Pen->Width = 1;

Image1->Canvas->Pen->Mode = pmNotXor;

Draw();

}

Начнем анализ этого кода с процедуры FormCreate, являющейся обработчиком события OnCreate формы. В этой процедуре рисуется линия, отображающая «землю», по которой будет ходить человечек. Затем устанавливается режим пера pmNotXor. И в заключение вызывается процедура Draw, которая рисует исходное положение человечка.

Процедура BRunClick является обработчиком события OnClick кнопки. Каждый щелчок на кнопке включает или выключает таймер, в результате чего человечек идет или останавливается.

Процедура Timer1Timer является обработчиком события OnTimer таймера. Это событие означает, что надо стереть прежний кадр и нарисовать новый.

Сначала вызывается процедура Draw. Поскольку позиция человечка с момента показа предыдущего кадра не изменилась, то этот вызов рисует на том же самом месте, на котором рисовался предыдущий кадр.

Следовательно, предыдущий рисунок стирается. Затем анализируется позиция человечка Xpos. Если эта позиция отстоит от какого-либо конца холста Image1 на величину, меньшую шага Н, то изменяется на обратный знак переменной revers, характеризующей направление движения.

Если revers = 1, человечек шагает вправо; если revers = -1, человечек шагает влево. Затем позиция Xpos изменяется на величину revers * Н, т.е. на шаг вправо или влево. Изменяется переменная cadr, которая указывает номер высвечиваемого кадра: 0 или 1.

В заключение вызывается процедура Draw, которая рисует указанный кадр в указанной позиции.

Последняя процедура Draw рисует кадр. В зависимости от значения cadr рисуется один или другой кадр, причем в рисунке учитывается позиция Xpos и направление движения revers.

Запустим приложение. Щелкнув на кнопке можем заставить человечка перемещаться. Достигнув края формы он будет поворачиваться и шагать в обратном направлении. При вторичном щелчке на кнопке он будет останавливаться.

Конечно, пока движения человечка очень неуклюжи. Чуть позже научим его двигаться более плавно. А пока обсудим некоторые проблемы, связанные с построением даже простеньких мультипликаций.

Первая из них - создание фона. Человечек движется в пустом пространстве. Попробуем вставить в программу какой-нибудь фон. Например, включим в процедуру FormCreate следующие операторы, рисующие черный прямоугольник:

Image1->Canvas->Brush->Color = 0;

Image1->Canvas->Rectangle (90,0,200,100);

Image1->Canvas->Brush->Color = clWhite;

Выполним приложение. Во время движения человечка на черном фоне черный человечек становится белым. Если мультипликация черно-белая, то такой результат может только радовать, поскольку черное на черном просто исчезало бы. Но при разноцветном пестром фоне картина становится безрадостной. Можно в этом убедиться, введя в приложение компонент OpenPictureDialog и в начале процедуры FormCreate вставим оператор

if (OpenPictureDialog1->Execute())

Image1->Picture->LoadFromFile(0penPictureDialogl->FileName);

который позволит перед началом работы приложения загрузить в виде фона какой-нибудь графический файл. Тогда увидим, что на пестром фоне человечек будет совершенно теряться.

Какие возможны выходы из положения? Наиболее простой - ограничиваться черно-белой мультипликацией или отказаться от режима пера pmNotXor и использовать буферизацию фона.

В примере это сделаем следующим образом.

Введем глобальную переменную типа TBitmap:

Graphics::TBitmap *BitMap; - это объект, в котором будем сохранять фрагменты фона, испорченные очередным кадром. Изменим процедуру FormCreate следующим образом:

 

void _fastcall TForm1::FormCreate(TObject *Sender)

{

BitMap = new Graphics::TBitmap;

BitMap->Width = 2 * (L + Rhead);

BitMap->Height = L + Hmen + 2 * Rhead + 6;

Image1->Canvas->MoveTo(0,Ypos+3);

Image1->Canvas->Pen->Width = 4;

Image1->Canvas->LineTo(Image1->ClientWidth, Ypos+3);         // земля

Imagel->Canvas->Pen->Width = 2;

BitMap->Canvas->CopyRect(Rect(0,0,BitMap->Width, BitMap->Height);

Imagel->Canvas-> Rect(Xpos-L-Rhead, Ypos-(L+Hmen+2*Rhead+5), Xpos+L+Rhead, Ypos+1));

Draw();

}

Первыми операторами этой процедуры создается объект BitMap и задаются его размеры равными максимальным размерам изображения человечка. В конце процедуры, перед вызовом Draw в компонент BitMap методом CopyRect копируется фрагмент изображения, внутри которого будет расположен рисунок человечка.

После этого процедурой Draw рисуется соответствующий кадр. Обратите внимание, что в данном приложении отсутствует оператор, задававший ранее режим пера pmNotXor. Так что по умолчанию рисунок будет делаться обычным образом.

Поскольку приложение создало объект Bitmap, надо добавить в него обработчик события OnDestroy формы, в который вставить оператор BitMap->Free();

Изменим обработчик Timer1Timer следующим образом:

 

void    fastcall TForm1::TimerlTimer(TObject *Sender)

{

 Image1->Canvas->Draw(Xpos-L-Rhead, Ypos-L-Hmen-2*Rhead-5,BitMap);

if ((Xpos >= Image1->Picture->Width - H) || (Xpos <= H))

revers = -revers;

Xpos = Xpos + revers * H;

cadr = 1 - cadr;

BitMap->Canvas->CopyRect(Rect(0,0,BitMap->Width, BitMap->Height);

Image1->Canvas-> Rect(Xpos-L-Rhead, Ypos-(L+Hmen+2*Rhead+5) , Xpos+L+Rhead,Ypos+l));

Draw();

}

Если сравнить с предыдущим приложением, то вместо первого вызова процедуры Draw, который стирал предыдущий кадр, вводится оператор Image1->Canvas->Draw, который выполняет ту же функцию, но путем восстановления запомненного ранее фрагмента фона под рисунком. Вторым отличием является наличие оператора BitMap->Canvas->CopyRect, который перед вызовом Draw запоминает новый фрагмент фона.

Вот и все изменения. Выполним проект и увидим, что без фона приложение работает как и прежде. Добавим пестрый фон, увидим, что изображение стало несколько лучше. Так что, использовать для мультипликаций пестрые фоны крайне нежелательно.

Изображение было очень простым и рисовалось быстро. Но при сложных изображениях время рисования может быть заметным и приводить к мерцанию картинки и другим неприятным зрительным эффектам. В этих случаях используют буферизацию изображения. А после того, как рисунок сделан, он переносится на видимый холст методами Draw или CopyRect. Эти методы работают очень быстро и никаких неприятных мерцаний не происходит.

Еще одна проблема анимации - определение того, какие элементы изображаемого объекта видны, а какие - нет. Несмотря на простоту приведенного примера с человечком, даже в нем возникла такая проблема. Если внимательно посмотреть, то видно, что изображение неправильное. Конец одной из рук должен быть скрыт за литаврами, которые держит человечек. Эту ошибку в данном случае не трудно было бы убрать, но код несколько усложнился бы. А вот в трехмерной графике при вращении изображения объекта подобная проблема встает очень остро и должна соответствующим образом решаться.

Попробуем сделать мультипликацию более плавной. Здесь достаточно ограничиться простыми механическими движениями, которые всегда можно описать соответствующими функциями. Это относится к любым динамическим иллюстрациям работы механизмов, к любым схематическим перемещениям.  Вполне допустимо считать, что его руки и ноги движутся по окружностям с соответствующими центрами. Тогда легко рассчитать их положение в любой момент времени и соответственно разбить это движение на любое число кадров.

Откройте первое приложение с анимацией. Нужно внести следующие изменения в тексты обработчиков событий. Теперь они будут иметь следующий вид.

#define Pi 3.1415926535897932385

short int cadr = 0;          // номер кадра

short int H=30;              // длина ноги и руки

short int Xpos = 3 * H;           // координата опорной ноги

short int Ypos = 120;             // "земля"

short int Hrasn = 30;             // высота тела

short int Rhead =10;             // радиус головы

short int revers =1;                // направление движения

short int L = H * 1.41;            // длина ноги

short int Ncadr=16;               // число кадров на шаг

 

void    _fastcall TForm1::Draw()

{

float Angl = Pi/4*(l+(2.*cadr)/(Ncadr-l));

short int Yb = Ypos-H*sin(Angl);

short int Yt = Yb-Hmen;

short int X = Xpos-revers*H*cos(Angl);

Image1->Canvas->MoveTo(X-(Xpos-X),Ypos);

Image1->Canvas->LineTo(X,Yb);  // нога

if (cadr!= Ncadr/2-1)

Image1->Canvas->LineTo (Xpos,Ypos);   // другая нога

Image1->Canvas->MoveTo(X,Yb);

Image1->Canvas->LineTo(X,Yt);   // туловище

short int X1 = X - revers * (Yb-Ypos);

Image1->Canvas->MoveTo(X1,Yt+5-(Xpos-X));

Image1->Canvas->Ellipse(X1-Rhead / 2, Yt+5-(Xpos-X)-Rhead/2, X1+Rhead/2,

Yt+5-(Xpos-X)+Rhead/2);

Image1->Canvas->LineTo(X,Yt+5);       // рука

if (cadr!= Ncadr / 2-1)

{

Image1->Canvas->Ellipse(X1-Rhead/2, Yt+5+(Xpos-X)-Rhead1/2, Xl+Rhead/2, Yt+5+(Xpos-X)+Rhead/2);

Image1->Canvas->LineTo(X1,Yt+5+(Xpos-X)); // другая рука

}

Image1->Canvas->Ellipse(X-Rhead,Yt-2*Rhead, X+Rhead, Yt); Image1->Canvas->Rectangle(X-Rhead,Yt-2*Rhead-4, X+Rhead,Yt-2*Rhead-1); //шляпа

}

 

void    _fastcall TForm1::BRunClick(TDbject *Sender)

{

Timer1->Enabled = !Timer1->Enabled;

}

 

void    _fastcall TForm1::Tiimer1Timer(TObject *Sender)

{

Draw () ;

cadr = (cadr+1) % Ncadr;

if (cadr == 0)

if ((Xpos < Image1->Picture->Width-revers*3*H) && (Xpos>-revers*3*H))

Xpos += revers*H*l.41;

else

revers = -revers;

Draw ();

}

 

void _fastcall TForm1::FormCreate(TObject *Sender)

{

Image1->Canvas->MoveTo(0, Ypos+3) ;

Image1->Canvas->Pen->Width = 4;

Image1->Canvas->LineTo(Image1->ClientWidth,Ypos+3); // земля

Image1->Canvas->Pen->Width = 1;

Image1->Canvas->Pen->Mode = pmNotXor;

Timerl->lnterval = 600/Ncadr;

Draw ()

}

 

В этом коде использованы функции sin и cos.

Рассмотрим коротко приведенный код.

В процедуре FormCreate добавлен оператор, задающий выдержку таймера (свойство Interval) путем деления 600 на константу Ncadr, которая задает число кадров на один цикл движения - на один шаг человечка. Как видно, длительность одного шага выбрана равной 600 миллисекунд.

Процедура BRunClick не изменилась.

Процедура TimerITimer начинается и кончается вызовами Draw, первый из которых стирает изображение предыдущего кадра, а второй - рисует новый кадр. После первого вызова Draw рассчитывается значение переменной cadr оператором

cadr = (cadr+l) % Ncadr;

Поскольку тут используется операция вычисления остатка, от деления cadr + 1 на Ncadr, то значение cadr последовательно получает значения 0, 1, 2, ..., Ncadr - 1, 0, 1, ... . Шаг начинается с cadr = 0. При этом проверяется, не приблизился ли человечек к краю формы, и если приблизился - изменяется направление движения (знак переменной revers).

Наиболее серьезно изменилась процедура Draw. Она начинается с определения угла наклона ног и рук Angl, исходя из номера кадра:

float Angl = Pi/4*(l+(2.*cadr)/(Ncadr-1)) ;

Обратить внимание: константа 2 в этом выражении записана с точкой и произведение 2.*cadr заключено в скобки. Это принципиально, ибо в этом случае константа воспринимается как значение с плавающей запятой и все выражение (2.*cadr)/(Ncadr-1) вычисляется как значение с плавающей запятой. Если не поставить точку после 2, то будут применяться целочисленные вычисления и пока 2*cadr меньше, чем Ncadr-1, значения этого выражения будут равны 0 и угол изменяться не будет.

После вычисления угла на его основе строится изображение, подобное тому, которое было в первом приложении. Обратите внимание на то, что вторые нога и рука человечка рисуются при выполнении условия cadr!= Ncadr/2-1. Это связано с тем, что, если число кадров Ncadr четное, то в этот момент одна нога накладывается на другую и руки также накладываются друг на друга. Поскольку рисование идет в режиме pmNotXor, то это наложение приведет к тому, что у человечка вообще исчезнут в этом кадре руки и ноги.

Приложение готово. Движения человечка стали плавными.

 

3. Универсальный проигрыватель MediaPlayer

 

Компонент MediaPlayer (страница System) - универсальный проигрыватель аудио- и видео- информации. Он инкапсулирует интерфейс управления носителями (Media Control Interface - MCI) 32-разрядных Windows.

Компонент можно использовать в двух режимах.

1. Можно предоставить возможность управлять воспроизведением информации с помощью кнопочного интерфейса (панель управления различными проигрывателями).

2. Можно сделать сам компонент невидимым и управлять воспроизведением информации с помощью его методов.

Пользовательский интерфейс медиаплеера имеет ряд кнопок, управляемых мышью или клавишей пробела и клавишами со стрелками.

Кнопка

Действие

Play

Воспроизведение.

Pause

Пауза воспроизведения или записи. Если медиа-плеер в момент щелчка уже в состоянии паузы, то воспроизведение или запись возобновляются.

Stop

Останов воспроизведения или записи.

Next

Переход на следующий трек или на конец.

Prev

Переход на предыдущий трек или на начало.

Step

Перемещение вперед на заданное число кадров.

Back

Перемещение назад на заданное число кадров.

Record

Начало записи.

Eject

Освобождение объекта, загруженного в устройство.

 

Каждой кнопке медиа-плеера соответствует одноименный метод, осуществляющий требуемую операцию: Play, Pause, Stop, Next, Previous, Step, Back, StartRecording, Eject.

Тип устройства мультимедиа, с которым работает медиа-плеер, определяется свойством DeviceType. Если устройство мультимедиа хранит объект воспроизведения в файле, то имя файла задается свойством FileName. По умолчанию свойство DeviceType имеет значение dtAutoSelect. Это означает, что медиа-плеер пытается определить тип устройства исходя их расширения имени файла FileName.

Если свойство AutoOpen установлено в true, то медиа-плеер пытается открыть устройство, указанное свойством DeviceType, автоматически во время своего создания в процессе выполнения приложения.

Воспроизведение видео информации по умолчанию производится в окно, которое создает само открытое устройство мультимедиа. Однако это можно изменить, если в свойстве Display указать оконный элемент, в котором должно отображаться изображение. Это может быть, например, форма или панель.

В свойстве DisplayRect можно также задать прямоугольную область этого окна, в которую должно выводиться изображение с помощью функции Rect. Однако в данном свойстве использование этого типа не совсем обычно. Первые две координаты, как и обычно, задают положение левого верхнего угла изображения. А два следующих числа задают ширину и высоту изображения, а не координаты правого нижнего угла. Например, оператор

MediaPlayer1->DisplayRect = Rect(10,10,200,200);

задает для вывода область с координатами левого верхнего угла (10, 10), длиной и шириной, равными 200.

В компоненте MediaPlayer определены события OnClick и OnNotify. Первое из них происходит при выборе пользователем одной из кнопок медиа-плеера и определено как

 

enum TMPBtnType (btPlay, btPause, btstop, btNext, btPrev, btstep, btBack, btRecord, btEject);

void    _fastcall Click(TObject *Sender, TMPBtnType Button, bool &DoDefault)

Параметр Button указывает выбранную кнопку. Параметр DoDefault, передаваемый по ссылке, определяет выполнение (при значении true по умолчанию) или отказ от выполнения стандартного метода, соответствующего выбранной кнопке.

Событие OnNotify происходит после возвращения очередного метода, если свойство медиаплеера Notify было установлено в true. Способ возврата любого метода медиаплеера определяется свойством Wait. Если установить Wait равным false, то возвращение управления в приложение происходит сразу после вызова метода, не дожидаясь завершения его выполнения.

Таким образом, задав Notify равным true и Wait равным false, можно обеспечить немедленный возврат в приложение и отображения пользователю текущего состояния объекта мультимедиа.

Свойства Notify и Wait действуют только на один очередной метод. Поэтому их значения надо каждый раз восстанавливать в обработчиках событий OnClick или OnNotify.

В обработчиках событий можно читать свойство Mode, характеризующее текущее состояние устройства мультимедиа. Можно также читать и устанавливать ряд свойств, характеризующих размер воспроизводимого файла и текущую позицию в нем.

Рассмотрим пример использования компонента MediaPlayer. Поместим на форму компоненты MediaPlayer, MainMenu и OpenDialog. В свойстве Filter компонента OpenDialog введем следующие фильтры:

 

аудио и видео (*.wav,*.mid,*.avi)

*.wav; *.mid; *.avi

аудио (*.wav,*.mid)

*.wav;*.mid

видео (*.avi)

*.avi

все файлы

* *

 

В меню зададим одну команду: Файл | Открыть и в его обработчике события OnClick поместим оператор

if (OpenDialogl->Execute())

{

MediaPlayer1->FileName = OpenDialogl->FileName;

MediaPlayer1->Open() ;

}

который открывает устройство мультимедиа, соответствующее выбранному пользователем файлу. При этом надо проследить, чтобы в компоненте MediaPlayer свойство DeviceType равнялось dtAutoSelect. Это обеспечит автоматический выбор соответствующего устройства мультимедиа исходя из расширения выбранного файла.

В компоненте MediaPlayer при желании можно указать имя файла FileName, открываемого в момент начала выполнения приложения. Тогда надо установить свойство AutoOpen в true.

Усложним приложение. Введем в него четыре метки. В Label1 укажем надпись «Файл:». В Label2 будем программно отображать состояние проигрывателя, в Label3 - последнюю вызванную операцию, а Label4 расположим рядом с меткой Label1 так, чтобы она служила ее продолжением. В ней отобразим имя загруженного файла в сокращенном виде с многоточиями, если имя файла не помещается в отведенном для него месте.

Код, обеспечивающий обратную связь в приложении, может быть следующим.

 

#include "filectrl.hpp"

AnsiString ModeStr[7] = {"He готово", "Остановлено","Воспроизведение", "Запись", "Поиск", "Пауза", "Открыто"};

Ansistring ButtonStr[9] = {"Воспроизведение", "Пауза", "Стоп", "Следующий", "Предыдущий", "Вперед", "Назад", "Запись", "Конец"};

void _fastcall TForm1::FormCreate(TObject *Sender)

{

Label4->Caption = MinimizeName(MediaPlayer1->FileName, Label4->Canvas,200);

Label2->Caption = "Состояние: " + ModeStr[MediaPlayer1->Mode];

MediaPlayer1->Notify = true;

}

 

void _fastcall TForm1::OpenClick(TObject *Sender)

{

 if (OpenDialogl->Execute())

{

MediaPlayer1->FileName = OpenDialogl->FileName;

Label4->Caption = MinimizeName(MediaPlayer1->FileName, Label4->Canvas,200);

MediaPlayer1->Open();

MediaPlayer1->Notify = true;

}

}

void _fastcall TForm1::MediaPlayer1Notifу(TObject *Sender)

{

Label2->Caption = "Состояние: "+ModeStr[MediaPlayer1->Mode];

// Переустановка Notify, чтобы событие произошло в следующий раз

MediaPlayer1->Notify = true;

{

void _fastcall TForm1::MediaPlayer1Click(TObject *Sender, TMPBtnType Button, bool &DoDefault)

{

Label3->Caption = "Операция: " + ButtonStr[Button];

// Переустановка Notify, чтобы произошло событие OnNotify

MediaPlayer1->Notifу = true;

}

В свойстве FileName компонента MediaPlayer1 задано имя файла, загружаемого в момент создания MediaPlayer1, т.е. в момент создания приложения. Соответственно в обработчике FormCreate записаны операторы, задающие имя файла и текущее состояние проигрывателя в метки Label4 и Label2 соответственно.

Для записи имени файла использована функция MinimizeName, которая обеспечивает сокращенное отображение пути к файлу в случае, если полный путь не помещается в отведенном месте (в операторе указана максимальная длина - 200 пикселов). Чтобы эта функция работала, в модуль добавлена директива компилятора     #include "filectrl.hpp"

В дальнейшем отображение соответствующей информации предусмотрено в процедурах, соответствующих открытию файла (OpenClick), нажатии пользователем какой-нибудь кнопки проигрывателя (MediaPlayer1Click), возвращении любого метода проигрывателя (MediaPlayer1Notify). После каждого события выполняется оператор MediaPlayer1->Notify = true; обеспечивающий наличие события OnNotify после возвращения следующего метода проигрывателя.

 

4. Воспроизведение немых видео клипов - компонент Animate

 

Теперь рассмотрим способ воспроизведения в приложении C++Builder стандартных мультипликаций Windows и файлов .avi - клипов без звукового сопровождения. Это позволяет сделать компонент Animate, расположенный на странице Win32 библиотеки.

Компонент Animate позволяет воспроизводить на форме стандартные видео клипы Windows (типа копирования файлов, поиска файлов и т.п.) и немые видео файлы .avi - Audio Video Interleaved. Эти файлы представляют собой последовательность кадров битовых матриц. Они могут содержать и звуковую дорожку, но компонент Animate воспроизводит только немые клипы AVI.

TAnimate может воспроизводить клипы AVI из ресурсов, из файлов.

Создадим новое приложение, перенесем на форму компонент Animate.

Воспроизводимое им изображение задается одним из двух свойств: FileName или CommonAVI. Первое из этих свойств позволяет в процессе проектирования или программно задать имя воспроизводимого файла. А свойство CommonAVI позволяет воспроизводить стандартные мультипликации Windows. Это свойство типа TCommonAVI, объявленного следующим образом:

enum TCommonAVI {aviNone, aviFindFolder, aviFindFile,

aviFindComputer, aviCopyFiles, aviCopyFile, aviRecycleFile, aviEmptyRecycle, aviDeleteFile };

Тип TCommonAVI определяет множество предопределенных в Windows мультипликаций типа копирования файлов, поиска файлов, удаления файлов и т.п.

Установите значение CommonAVI, например, равным aviCopyFile. Это соответствует стандартному изображению копирования файла. Соответствующий начальный рисунок немедленно появится на форме.

Свойство Repetitions задает число повторений воспроизведения клипа. Если оно равно 0 (значение по умолчанию), то воспроизведение повторяется вновь и вновь до тех пор, пока не будет выполнен метод Stop. При выполнении этого метода генерируется событие OnStop, которое можно использовать, например, чтобы стереть изображение - сделать его невидимым.

Если же свойство Repetitions задать больше нуля, оно определит число повторений клипа. Задайте его, например, равным 3. А теперь установите свойство Active компонента Animate в true. Вы увидите, что еще в процессе проектирования ваше приложение заработает. Изображение оживет и клип будет повторен 3 раза.

Можно посмотреть воспроизводимое изображение по кадрам. Для этого щелкните на компоненте правой кнопкой мыши и из всплывшего меню выберите разделы Next Frame (следующий кадр) или Previous Frame (предыдущий кадр). Это позволит выбрать фрагмент клипа, если вы не хотите воспроизводить клип полностью. Воспроизвести фрагмент клипа можно, установив соответствующие значения свойств StartFrame - начальный кадр воспроизведения, и StopFrame - последний кадр воспроизведения.

Воспроизводить фрагмент клипа можно и методом Play, который определен следующим образом:

void _fastcall Play(FromFrame, ToFrame, Count);

Метод воспроизводит заданную последовательность кадров клипа от FromFrame до ToFrame включительно и воспроизведение повторяется Count раз. Если FromFrame = 1, то воспроизведение начинается с первого кадра. Значение ToFrame должно быть не меньше FromFrame и не больше значения, определяемого свойством FrameCount (свойство только для чтения), указывающим полное число кадров в клипе. Если Count = 0, то воспроизведение повторяется до тех пор, пока не будет выполнен метод Stop.

Выполнение Play идентично заданию StartFrame равным FromFrame, Stop-Frame равным ToFrame, Repetitions равным Count и последующей установке Active в true.

В компоненте Animate предусмотрены события OnClose, OnOpen, OnStart и OnStop, генерируемые соответственно в моменты закрытия и открытия компонента, начала и окончания воспроизведения.

Построим приложение, показывающее возможности компонента Animate. Установите свойство Visible компонента Animate в false. Это надо для того, чтобы изображение возникало только тогда, когда произойдет соответствующее событие: копирование файлов, поиск файлов и т.п. В приложении будем имитировать начало и окончание события, которое должно сопровождаться мультипликацией, нажатиями кнопок запуска и останова воспроизведения. Поэтому установим значение свойства Repetitions в 0, чтобы воспроизведение длилось до окончания события. Свойства Active установите в false и AutoSize в false, а свойство Center в true, чтобы изображение всегда появлялось в центре экрана.

Поместим еще 3 кнопки. Первая из них (BWind) будет начинать процесс воспроизведения поочередно всех стандартных клипов Windows. Вторая кнопка (BStop) завершает воспроизведение очередного клипа. А третья кнопка (BFile) чтобы показать, что компонент может воспроизводить изображения из заданного файла .avi. Чтобы пользователь мог выбрать файл изображения, добавим на форму компонент OpenDialog и установим свойство Filter равным «видео *.avi|*.avi».

Теперь напишем обработчики событий:

int i;

void _fastcall TForm1::BWindClick(TObject *Sender)

{

Animate1->Visible = true;

i = 1;

Animate1->CommonAVI = aviFindFolder;

Animate1->Active = true;

}

void _fastcall TForm1::BStopClick(TObject *Sender)

{

 Animate1->Stop();

}

 

void _fastcall TForm1::Animate1Stop(TObject *Sender)

{

 i++;

switch (i)

{

case 2: Animate1->CommonAVI = aviFindFile; break;

case 3: Animate1->CommonAVI = aviFindComputer; break;

case 4: Animate1->ComraonAVI - aviCopyFiles; break;

case 5: Animate1->CommonAVI = aviCopyFile; break;

case 6: Animate1->CommonAVI = aviRecycleFile; break;

case 7: Animate1->CommonAVI = aviEmptyRecycle; break;

case 8: Animate1->CommonAVI = aviDeleteFile;

}

 if (i < 9) Animate1->Active = true;

else

Animate1->Visible = false;

}

void    _fastcall TForm1::BFileClick(TObject *Sender)

{

if (OpenDialogl->Execute())

{

 i = 9;

Animate1->FileName = OpenDialogl->FileName;

Animate1->Visible = true;

Animate1->Active = true;

} }

Обработчик OnClick кнопки BWind задает начальное значение свойства CommonAVI, сбрасывает счетчик на 1, делает компонент Animate1 видимым и активизирует его.

Обработчик OnCIick кнопки BStop останавливает воспроизведение методом Stop.

Обработчик OnStop компонента Animate1 увеличивает счетчик на 1, в зависимости от значения счетчика загружает в компонент соответствующий клип Windows, и активизирует компонент. Если все клипы уже воспроизведены, то компонент делается невидимым. Обработчик OnClick кнопки BFile загружает в компонент видеофайл, выбранный пользователем.