ООП IDE Borland C++ Builder Лекции 17. Проектирование мультимедиа и анимационных приложений 1 Содержание • Процедуры воспроизведения звуков Windows - Веер • Начала анимации - создание мультипликаций • Универсальный проигрыватель MediaPlayer • Воспроизведение немых видео клипов компонент Animate 2 Вопрос 1 Процедуры воспроизведения звуков Windows Веер 3 • • • • • • Воспроизведение звуков Существует немало файлов звуковых и мультимедиа. 1. Волновой файл .wav - в нем записано цифровое представление информации о волновой форме электрического сигнала, соответствующего каждому звуку. 2. Файлы цифрового интерфейса музыкальных инструментов .midi -используются для хранения музыкальных фрагментов - звук хранится в виде данных о том, на каких инструментах исполняются определенные ноты и как долго они звучат. Волновые и MIDI файлы могут хранить только звук или музыку. 3. Файлы AVI и MPEG - для хранения видео информации. Большинство видеофайлов поддерживают также хранение звуковой дорожки, и звук воспроизводится 4 синхронно с картинкой. Воспроизведение звуков • Наиболее простой процедурой, управляющей звуком, является процедура Веер, которая воспроизводит стандартный звуковой сигнал, установленный в Windows. • Поместим на форму кнопку и в обработчике щелчка на кнопку напишем оператор: Веер(); • После запуска приложения при щелчке на кнопке раздастся стандартный звук Windows. 5 Создание простой мультипликации • Мультипликационные фильмы представляют собой совокупность множества кадров, каждый из которых чуть-чуть отличается от предыдущего и при быстром поочередном просмотре кадров создает иллюзию движения. • Попробуем сделать простую мультипликацию, условное изображение человечка, шагающего и бьющего при этом в литавры. • Перенесем на форму компоненты Image, кнопку Button и таймер Timer. • Кнопку разместим внизу формы. • Основную площадь формы должен занимать компонент Image, на котором и будет рисоваться изображение. 6 Создание простой мультипликации • Рассмотрим пример приложения, в котором мультипликация состоит из двух кадров. • Таймер будет задавать темп смены кадров, поэтому зададим значение свойства Interval таймера достаточно большим, например, 500. • Значение параметра Enabled таймера установим в false. • Таймер будет управляться кнопкой. • В заголовочном файле модуля формы (unit1.h) добавим строку void _fastcall Draw(); • Это объявление функции, которая будет рисовать изображение. 7 Создание простой мультипликации short short short short short short short short short int int int int int int int int int Текст модуля может иметь вид: cadr = 0; H=30; // шаг Xpos = 3 * H; // координата туловища Ypos = 120; // "земля" Hrasn = 30; // высота тела Rhead =10; // радиус головы Rhead2 = Rhead /2; // радиус литавров revers =1; // направление движения L = H * 1.41; // длина ноги 8 Создание простой мультипликации 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-HRhead2, Xpos+revers*H+Rhead2,Yhead-H+Rhead2); 9 Создание простой мультипликации 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*Rhead1,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); 10 Создание простой мультипликации Image1->Canvas->Ellipse(Xpos+revers*L-Rhead2,Yhead+4Rhead2, 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 (); } 11 Создание простой мультипликации 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(); } 12 Создание простой мультипликации • FormCreate - обработчик события OnCreate формы, рисуется линия, отображающая «землю», по которой будет ходить человечек. • Затем устанавливается режим пера pmNotXor. • В заключение вызывается процедура Draw, которая рисует исходное положение человечка. • Процедура BRunClick - обработчик события OnClick кнопки, каждый щелчок включает или выключает таймер, в результате чего человечек идет или останавливается. • Timer1Timer - обработчик события OnTimer таймера, означает, что надо стереть прежний кадр и нарисовать новый. 13 Создание простой мультипликации • Сначала вызывается процедура Draw. • Поскольку позиция человечка с момента показа предыдущего кадра не изменилась, то этот вызов рисует на том же самом месте, на котором рисовался предыдущий кадр. • Следовательно, предыдущий рисунок стирается, затем анализируется позиция человечка Xpos. • Если эта позиция отстоит от какого-либо конца холста Image1 на величину, меньшую шага Н, то изменяется на обратный знак переменной revers, характеризующей направление движения. • Если revers = 1, человечек шагает вправо; если revers = -1, человечек шагает влево. • Затем позиция Xpos изменяется на величину revers * Н, т.е. на шаг вправо или влево. • Изменяется переменная cadr, которая указывает номер высвечиваемого кадра: 0 или 1. • В заключение вызывается процедура Draw, которая рисует указанный кадр в указанной позиции. 14 Создание простой мультипликации • Последняя процедура Draw рисует кадр. • В зависимости от значения cadr рисуется один или другой кадр, причем в рисунке учитывается позиция Xpos и направление движения revers. • Запустим приложение и щелкнув на кнопке, можем заставить человечка перемещаться. • Достигнув края формы он будет поворачиваться и шагать в обратном направлении. • При вторичном щелчке на кнопке он будет останавливаться. • Конечно, пока движения человечка очень неуклюжи. • Чуть позже научим его двигаться более плавно. • А пока обсудим некоторые проблемы, связанные с построением даже простеньких мультипликаций.15 Создание простой мультипликации • Первая из них - создание фона. Человечек движется в пустом пространстве. • Попробуем вставить в программу какой-нибудь фон, например, включим в FormCreate следующие операторы, рисующие черный прямоугольник: Image1->Canvas->Brush->Color = 0; Image1->Canvas->Rectangle (90,0,200,100); Image1->Canvas->Brush->Color = clWhite; • Выполним приложение, во время движения человечка на черном фоне черный человечек становится белым. • Если мультипликация черно-белая, то такой результат может только радовать, поскольку черное на черном просто исчезало бы. • Но при разноцветном пестром фоне картина 16 Создание простой мультипликации • Введем в приложение компонент OpenPictureDialog и в начале процедуры FormCreate вставим оператор if (OpenPictureDialog1->Execute()) Image1->Picture->LoadFromFile (0penPictureDialogl->FileName); • который позволит перед началом работы приложения загрузить в виде фона какой-нибудь графический файл. • Увидим, что на пестром фоне человечек будет совершенно теряться. 17 Создание простой мультипликации • Наиболее простой выход из положенияограничиваться черно-белой мультипликацией или отказаться от режима пера pmNotXor и использовать буферизацию фона. • В примере это сделаем следующим образом. • Введем глобальную переменную типа TBitmap: Graphics::TBitmap *BitMap; • это объект, в котором будем сохранять фрагменты фона, испорченные очередным кадром. 18 Создание простой мультипликации • Изменим 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(); } 19 Создание простой мультипликации • Первый оператор создает объект BitMap и задаются его размеры равными максимальным размерам изображения человечка. • В конце, перед вызовом Draw в компонент BitMap методом CopyRect копируется фрагмент изображения, внутри которого будет расположен рисунок человечка. • Затем процедурой Draw рисуется соответствующий кадр. • Внимание: в данном приложении отсутствует оператор, задававший ранее режим пера pmNotXor. • Поскольку приложение создало объект Bitmap, добавлен обработчик события OnDestroy формы, в который вставить оператор BitMap->Free(); 20 Создание простой мультипликации • Изменим обработчик Timer1Timer : void fastcall TForm1::TimerlTimer(TObject *Sender) { Image1->Canvas->Draw(Xpos-L-Rhead, Ypos-L-Hmen2*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(); } 21 Создание простой мультипликации • Если сравнить с предыдущим приложением, то вместо первого вызова процедуры Draw, который стирал предыдущий кадр, вводится оператор Image1->Canvas->Draw, который выполняет ту же функцию, но путем восстановления запомненного ранее фрагмента фона под рисунком. • Второе отличие - это наличие оператора BitMap>Canvas->CopyRect, который перед вызовом Draw запоминает новый фрагмент фона. • Выполним проект и увидим, что без фона приложение работает как и прежде. • Добавим пестрый фон, увидим, что изображение стало несколько лучше. • Так что, использовать для мультипликаций пестрые фоны крайне нежелательно. 22 Создание простой мультипликации • Изображение было очень простым и рисовалось быстро. • При сложных изображениях время рисования может быть заметным и приводить к мерцанию картинки и другим неприятным зрительным эффектам. • В этих случаях используют буферизацию изображения. • А после того, как рисунок сделан, он переносится на видимый холст методами Draw или CopyRect. • Эти методы работают очень быстро и никаких неприятных мерцаний не происходит. 23 Создание простой мультипликации • Еще одна проблема анимации - определение того, какие элементы изображаемого объекта видны, а какие - нет. • Несмотря на простоту приведенного примера с человечком, даже в нем возникла такая проблема. • Если внимательно посмотреть, то видно, что изображение неправильное. • Конец одной из рук должен быть скрыт за литаврами, которые держит человечек. • Эту ошибку в данном случае не трудно было бы убрать, но код несколько усложнился бы. • А вот в трехмерной графике при вращении изображения объекта подобная проблема встает очень остро и должна соответствующим образом решаться. 24 Создание простой мультипликации • Попробуем сделать мультипликацию более плавной. Здесь достаточно ограничиться простыми механическими движениями, которые всегда можно описать соответствующими функциями. • Это относится к любым динамическим иллюстрациям работы механизмов, к любым схематическим перемещениям. • Вполне допустимо считать, что его руки и ноги движутся по окружностям с соответствующими центрами. • Тогда легко рассчитать их положение в любой момент времени и соответственно разбить это движение на любое число кадров. • Откроем первое приложение с анимацией и внесем изменения в тексты обработчиков событий. 25 Создание простой мультипликации Они будут иметь следующий вид. #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; // число кадров на шаг 26 Создание простой мультипликации 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)); 27 Создание простой мультипликации Image1->Canvas->Ellipse(X1-Rhead / 2, Yt+5-(XposX)-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(XRhead,Yt-2*Rhead-4, X+Rhead,Yt-2*Rhead-1); } 28 Создание простой мультипликации 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 (); } 29 Создание простой мультипликации 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 () } 30 • • • • • • Создание простой мультипликации В этом коде использованы функции sin и cos. В FormCreate добавлен оператор, задающий выдержку таймера (свойство Interval) путем деления 600 на константу Ncadr, которая задает число кадров на один шаг человечка. Длительность одного шага выбрана равной 600 миллисекунд. BRunClick не изменилась. TimerITimer начинается и кончается вызовами Draw: • первый из которых стирает изображение предыдущего кадра • второй - рисует новый кадр. После первого вызова Draw рассчитывается значение переменной cadr оператором cadr = (cadr+l) % Ncadr; 31 • • • • • Создание простой мультипликации Поскольку тут используется операция вычисления остатка, от деления 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)) ; 32 Создание простой мультипликации • Константа 2 в этом выражении записана с точкой и произведение 2.*cadr заключено в скобки. • Это принципиально, ибо в этом случае константа воспринимается как значение с плавающей запятой и все выражение (2.*cadr)/(Ncadr-1) вычисляется как значение с плавающей запятой. • Если не поставить точку после 2, то будут применяться целочисленные вычисления и пока 2*cadr меньше, чем Ncadr-1, значения этого выражения будут равны 0 и угол изменяться не будет. • После вычисления угла на его основе строится изображение. • Внимание: вторые нога и рука человечка рисуются при выполнении условия cadr!= Ncadr/2-1. • Это связано с тем, что, если число кадров Ncadr четное, то в этот момент одна нога накладывается на другую и руки также накладываются друг на друга. • Поскольку рисование идет в режиме pmNotXor, то это наложение приведет к тому, что у человечка вообще исчезнут в этом кадре руки и ноги. 33 • • • • • • Универсальный проигрыватель MediaPlayer Компонент MediaPlayer (страница System) универсальный проигрыватель аудио- и видеоинформации. Он инкапсулирует интерфейс управления носителями (Media Control Interface - MCI) 32-разрядных Windows. Компонент можно использовать в двух режимах. 1. Можно предоставить возможность управлять воспроизведением информации с помощью кнопочного интерфейса (панель управления различными проигрывателями). 2. Можно сделать сам компонент невидимым и управлять воспроизведением информации с помощью его методов. Пользовательский интерфейс медиаплеера имеет ряд кнопок, управляемых мышью или клавишей пробела и клавишами со стрелками. 34 • • • • • • Универсальный проигрыватель MediaPlayer Компонент MediaPlayer (страница System) универсальный проигрыватель аудио- и видеоинформации. Он инкапсулирует интерфейс управления носителями (Media Control Interface - MCI) 32-разрядных Windows. Компонент можно использовать в двух режимах. 1. Можно предоставить возможность управлять воспроизведением информации с помощью кнопочного интерфейса (панель управления различными проигрывателями). 2. Можно сделать сам компонент невидимым и управлять воспроизведением информации с помощью его методов. Пользовательский интерфейс медиаплеера имеет ряд кнопок, управляемых мышью или клавишей пробела и клавишами со стрелками. 35 Универсальный проигрыватель MediaPlayer Кнопка Действие Play Воспроизведение. Pause Пауза воспроизведения или записи. Если медиаплеер в момент щелчка уже в состоянии паузы, то воспроизведение или запись возобновляются. Stop Останов воспроизведения или записи. Next Переход на следующий трек или на конец. Prev Переход на предыдущий трек или на начало. Step Перемещение вперед на заданное число кадров. Back Перемещение назад на заданное число кадров. Record Начало записи. Eject Освобождение объекта, загруженного в устройство. 36 Универсальный проигрыватель MediaPlayer • Каждой кнопке медиа-плеера соответствует одноименный метод: Play, Pause, Stop, Next, Previous, Step, Back, StartRecording, Eject. • Тип устройства мультимедиа, с которым работает медиаплеер, определяется свойством DeviceType. • Если устройство мультимедиа хранит объект воспроизведения в файле, то имя файла задается свойством FileName. • По умолчанию свойство DeviceType имеет значение dtAutoSelect. • Это означает, что медиа-плеер пытается определить тип устройства исходя их расширения имени файла FileName. • Если свойство AutoOpen установлено в true, то медиаплеер пытается открыть устройство, указанное свойством DeviceType, автоматически во время своего создания в процессе выполнения приложения 37 Универсальный проигрыватель MediaPlayer • Воспроизведение видео информации по умолчанию производится в окно, которое создает само устройство мультимедиа. • Это можно изменить, если в свойстве Display указать оконный элемент, в котором должно отображаться изображение. • В свойстве DisplayRect можно также задать прямоугольную область этого окна, в которую должно выводиться изображение с помощью функции Rect. • Первые две координаты задают положение левого верхнего угла изображения. • Два следующих числа задают ширину и высоту изображения, а не координаты правого нижнего угла. • Например, оператор MediaPlayer1->DisplayRect = Rect(10,10,200,200); • задает для вывода область с координатами левого верхнего угла (10, 10), длиной и шириной, равными 200. 38 Универсальный проигрыватель MediaPlayer • В компоненте 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 по умолчанию) или отказ от выполнения стандартного метода, соответствующего выбранной кнопке. 39 Универсальный проигрыватель MediaPlayer • Событие OnNotify происходит после возвращения очередного метода, если свойство медиаплеера Notify было установлено в true. • Способ возврата любого метода медиаплеера определяется свойством Wait. • Если установить Wait равным false, то возвращение управления в приложение происходит сразу после вызова метода, не дожидаясь завершения его выполнения. • Таким образом, задав Notify равным true и Wait равным false, можно обеспечить немедленный возврат в приложение и отображения пользователю текущего состояния объекта мультимедиа. • Свойства Notify и Wait действуют только на один очередной метод. • Поэтому их значения надо каждый раз восстанавливать в обработчиках событий OnClick или OnNotify. 40 Универсальный проигрыватель MediaPlayer • В обработчиках событий можно читать свойство Mode, характеризующее текущее состояние устройства мультимедиа. • Можно также читать и устанавливать ряд свойств, характеризующих размер воспроизводимого файла и текущую позицию в нем. • Рассмотрим пример использования компонента MediaPlayer. Поместим на форму компоненты MediaPlayer, MainMenu и OpenDialog. В свойстве Filter компонента OpenDialog введем следующие фильтры: аудио и видео (*.wav,*.mid,*.avi) *.wav; *.mid; *.avi аудио (*.wav,*.mid) *.wav;*.mid видео (*.avi) *.avi все файлы ** 41 Универсальный проигрыватель MediaPlayer • В меню зададим одну команду: Файл | Открыть и в его обработчике события OnClick поместим оператор if (OpenDialogl->Execute()) { MediaPlayer1->FileName = OpenDialogl->FileName; MediaPlayer1->Open() ; } • который открывает устройство мультимедиа, соответствующее выбранному пользователем файлу. • При этом надо проследить, чтобы в компоненте MediaPlayer свойство DeviceType равнялось dtAutoSelect. • Это обеспечит автоматический выбор соответствующего устройства мультимедиа исходя из расширения выбранного файла. 42 Универсальный проигрыватель MediaPlayer • В компоненте MediaPlayer при желании можно указать имя файла FileName, открываемого в момент начала выполнения приложения. • Тогда надо установить свойство AutoOpen в true. • Усложним приложение. Введем в него четыре метки. • В Label1 укажем надпись «Файл:». В Label2 будем программно отображать состояние проигрывателя, в Label3 - последнюю вызванную операцию, а Label4 расположим рядом с меткой Label1 так, чтобы она служила ее продолжением. • В ней отобразим имя загруженного файла в сокращенном виде с многоточиями, если имя файла не помещается в отведенном для него месте. 43 Универсальный проигрыватель MediaPlayer • Код, обеспечивающий обратную связь в приложении, может быть следующим. #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; } 44 Универсальный проигрыватель MediaPlayer 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; } } 45 Универсальный проигрыватель MediaPlayer 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; } 46 Универсальный проигрыватель MediaPlayer • В свойстве FileName компонента MediaPlayer1 задано имя файла, загружаемого в момент создания MediaPlayer1, т.е. в момент создания приложения. • Соответственно в обработчике FormCreate записаны операторы, задающие имя файла и текущее состояние проигрывателя в метки Label4 и Label2 соответственно. • Для записи имени файла использована функция MinimizeName, которая обеспечивает сокращенное отображение пути к файлу в случае, если полный путь не помещается в отведенном месте. • Чтобы эта функция работала, в модуль добавлена директива компилятора #include "filectrl.hpp" 47 Универсальный проигрыватель MediaPlayer • В дальнейшем отображение соответствующей информации предусмотрено в процедурах, соответствующих: • открытию файла (OpenClick), • нажатии пользователем какой-нибудь кнопки проигрывателя (MediaPlayer1Click), • возвращении любого метода проигрывателя (MediaPlayer1Notify). • После каждого события выполняется оператор MediaPlayer1->Notify = true;, обеспечивающий наличие события OnNotify после возвращения следующего метода проигрывателя. 48