Мы переехали

Новый адрес - http://delphiblog.ru

понедельник, 3 декабря 2007 г.

Microsoft Agent. Часть II

В прошлой части мы научились применять технологию Microsoft Agent в web-страничках. В этот раз мы будем работать с Delphi. Убедитесь, что установлены все необходимые компоненты (см. первую часть) и можно отправляться в увлекательное путешествие по миру MsAgent.
Запустите Delphi и в меню "Component" выберите пункт "Import ActiveX Control…". В появившемся диалоговом окне нужно выделить строку "Microsoft Agent Control…" и нажать кнопку "Install". Далее произойдёт стандартный процесс установки нового компонента. После окончания инсталляции создайте новое приложение и поместите на форму полученный компонент – Agent1:TAgent (он должен находиться на закладке ActiveX).
Вот этот вот почти «ноль-ноль-семь» будет помогать нам создавать приложения, которые будут работать с технологией MsAgent. Начнём с небольшого примера, с маленького костяка, который можно будет использовать при разработке более сложных программ.
Установите свойству Connected компонента Agent1 значение True, затем объявите две глобальные переменные:
Var
  //для хранения персонажа
  Character: IAgentCtlCharacterEx;
  //для получения состояния персонажа
  Request: IAgentCtlRequest;

  Для события OnCreate формы запишите процедуру:
  procedure TForm1.FormCreate(Sender: TObject);
  begin
    //загружаем персонаж «Джин»
    Request := Agent1.Characters.Load('genie', 'genie.acs');
    //получаем объект персонажа Джин
    Character := Agent1.Characters.Character('genie')as IAgentCtlCharacterEx;
    //Джин появляется
    Request := Character.Show(False);
    //Джин здоровается и вкратце рассказывает о себе
    Request := Character.Speak('Здравствуйте! '+Сharacter.Description, EmptyParam);
    //Джин играет анимацию «Greet»
    Request:=Character.Play('Greet');
  end;

При закрытии формы нужно выгрузить персонаж:
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Agent1.Characters.Unload(‘genie’);
end;

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

Procedure GetAgentAnim(st: TStrings);
var
  AnEnum: IEnumVariant;
  flag: Cardinal;
  V: OleVariant;
begin
  //получаем интерфейс анимаций агента
  AnEnum := (Character.AnimationNames.Enum) as IEnumVariant;
  //Сбрасываем указатель списка на начало
  AnEnum.Reset;
  //перебираем все анимации и добавляем их в наш список
  repeat
    AnEnum.Next(1, V, flag);
    if (VarToStr(V) <> '') then
      st.Add(V);
  until (flag = 0);
end;

Давайте проверим работоспособность данной процедуры. Добавьте на форму кнопку Button1 и список ListBox1. Дайте заголовок кнопке «Список анимаций» и запишите процедуру обработки её нажатия:

procedure TForm1.Button1Click(Sender: TObject);
begin
  GetAgentAnim(ListBox1.Items);
end;

Данная процедура записывает список анимаций загруженного персонажа в компонент ListBox1. А теперь сделаем так, чтобы при двойном щелчке мышкой по ListBox’у Джин проигрывал выбранную анимацию:

procedure TForm1.ListBox1DblClick(Sender: TObject);
begin
  Request := Character.Play(ListBox1.Items[ListBox1.ItemIndex]);
end;

Теперь посмотрим, зачем нам переменная Request (её мы объявили как глобальную). Именно она служит для синхронизации действий персонажа (либо персонажей, если их несколько). Интерфейс IAgentCtlRequest имеет одно очень полезное свойство – Status. В зависимости от состояния запроса, который в данный момент выполняет персонаж, это свойство принимает следующие значения:

Status | Пояснения
  0             Запрос успешно выполнен
  1             Запрос «провалился»
  2             Запрос не выполняется, т.к. ждёт завершения обработки других запросов
  3             Запрос прерван
  4             Запрос обрабатывается

На основе этой таблички легко написать процедуру, которая будет дожидаться выполнения всех запросов к персонажу, а затем вернёт программе управление:

procedure WaitFor(Request:IAgentCtlRequest);
begin
  repeat
    Application.ProcessMessages;
    Status := Request.Status;
  until (Request.Status <> 2) and (Request.Status <> 4);
end;

Для примера добавьте на форму кнопку «Пример WaitFor»:

procedure TForm1.Button3Click(Sender: TObject);
begin
  //Джин произносит фразу
  Request := Character.Speak('Доброе утро', EmptyParam);
  {waitfor(Request);}
  //Показываем сообщение
  ShowMessage('Всё');
end;

Если нажать на кнопку, то Джин начнёт говорить и сразу же появится сообщение. А если снять комментарии со строки waitfor(Request);, то сообщение появится только после того, как Джин закончит говорить.
А сейчас «научим» Джина вслух произносить текущее время. Для этого ему нельзя подсунуть строку типа «21 час 44 минуты 32 секунды», эту строку он произнесёт так: «двадцать один час, сорок четвёртого минуты, тридцать второго секунды». В связи с этим, Джину нужно полностью «разжевать», что именно он должен произносить. Так что будем давать ему такую строку: «21 час, 44 минуты, 30 две секунды». Т.е. число, стоящее в женском роде (32 секунды), разделяем на составляющие (30 две секунды).
Добавьте на форму очередную кнопку «Сколько времени?» и запишите следующую процедуру:

procedure TForm1.Button1Click(Sender: TObject);
var
  buf, str: string;
  Hour, Min, Sec, Msec: Word;
  Hours, Mins, Secs: string;
  hs, ms, ss: string;
  DT: TDateTime;
begin
  hs := '';
  ms := '';
  ss := '';
  //узнаём текущее время
  DT := Now;
  //разбиваем его на составляющие
  DecodeTime(DT, Hour, Min, Sec, MSec);
  //смотрим, какое окончание должно быть у слова «час»
  case hour of
    2..4, 22, 23: hs := 'а';
    0, 5..20: hs := 'ов';
  end;
  //окончание слова «минута»
  case min of
    1, 21, 31, 41, 51: ms := 'а';
    2..4, 22..24, 32..34, 42..44, 52..54: ms := 'ы';
  end;
  //окончание слова «секунда»
  case sec of
    1, 21, 31, 41, 51: ss := 'а';
    2..4, 22..24, 32..34, 42..44, 52..54: ss := 'ы';
  end;
  //часы не будем преобразовывать, т.к. слово «час» мужского рода
  hours := inttostr(hour);
  //при необходимости разбиваем минуты на слова
  mins := inttostr(min);
  if (mins[length(mins)] = '1') and (min <> 11) then
  begin
    if (min > 10) then buf := mins[1] + '0 '
    else buf:='';
    mins := buf + 'одна';
  end
  else
  begin
    if (mins[length(mins)]='2') and (min<>12) then
    begin
      if (min>10) then buf:=mins[1]+'0 '
      else buf:='';
      mins := buf + 'две';
    end
  end
;
  //при необходимости разбиваем секунды на слова
  secs := inttostr(sec);
  if (secs[length(secs)]='1') and (sec<>11) then
  begin
    if (sec>10) then buf := secs[1] + '0 '
    else buf := '';
    secs := buf + 'одна';
  end
  else
  begin
    if (secs[length(secs)]='2') and(sec<>12) then
    begin
      if (sec>10) then buf := secs[1] + '0 '
      else buf:='';
      secs := buf + 'две';
    end;
  end;
  //составляем строку, которую должен произнести Джин
  str := hours + ' час' + hs+ ', ' + mins + ' минут' + ms + ', ' + secs + ' секунд' + ss;
  //Джин произносит строку str, а над ним отображается текущее время (рис.1)
  Request := Character.Speak('\Pit=18400\\Spd=100\\Map="'+str+'"="'+timetostr(DT)+'"\',EmptyParam);
end;



Напоследок рассмотрим некоторые параметры голосового движка. На один компьютер можно установить поддержку нескольких языков. Чтобы переключаться между ними, нужно изменять параметр персонажа LanguageID. В таблице приведены идентификаторы к некоторым языкам. Символ «$» означает, что число представлено в шестнадцатеричной системе счисления.               


Пример: Character.LanguageID := $409; – переключаемся на английский язык.
Ещё можно изменять сам голос. Для этого есть свойство TTSModeID. Ниже приведён список голосов для русского и английского языков.



Пример:
Сharacter.TTSModeID:='{06377F80-D48E-11d1-B17B-0020AFED142E}';
устанавливает женский голос для русского языка. Так что можете поиздеваться над Джином, заставляя его разговаривать женским голосом.
На сегодня всё, в следующей части мы научимся управлять Джином при помощи голосовых команд.

Иван Ширко
ishyrko@gmail.com

Комментариев нет: