Speech API 4 в Delphi

В этой статье мы познакомимся с программированием речи в Delphi. Хотите чтобы Delphi заговорил — читайте эту страницу!

MSAgent & SpeechAPI

Нудеюсь, что вы уже установили пакет функций Speech API и хотя бы один речевой движок. Речевые движки — это те модули, которые синтезируют речь. Для доступа к ним нужно использовать специальные функции, которые описаны в наборе функций Speech API. Поэтому для того, чтобы написать минимальную программу умеющую говорить, нужны эти два компонента. Сначала мы попробуем написать как раз эту программу-минимум а потом заставим персонаж MS Agent заговорить. То есть постараемся совместить технологию MSAgent и SpeechAPI. Тогда агент станет еще более приятным.

Ну, а для того чтобы всем эти заняться нам потребуется полезный модулек speech.pas, в котором объявлены полезные константы, типы, функции и интрефейсы. Не забудьте подключить его в разделе uses главного модуля приложения.

Попытаемся создать программу, где мы будем вводить текст для чтения в редакторе, выбирать движок для чтения в ComboBox’е и пару кнопок для чтения, паузы и остановки.

Объявим в секции private нужные для работы переменные:

private
  { Private declarations }
  fITTSCentral: ITTSCentral; {Центральный интерфейс,
    через который производятся все действия с речью}
  fIAMM: IAudioMultimediaDevice;
   {Интерфейс для связи с аудиоустройством}
  aTTSEnum: ITTSEnum;
   {Интерфейс для перебора движков}
  fpModeInfo: PTTSModeInfo;
   {Указатель на параметры движка}

Во ходе создания главной формы приложения нам нужно проверить установленные движки в системе, и их названия поместить в ComboBox. Это мы и сделаем:

procedure TForm1.FormCreate(Sender: TObject);
var
  NumFound : DWord;
  ModeInfo : TTSModeInfo;
begin
  try
    {Инициализация аудиоустройства}
    CoCreateInstance(CLSID_MMAudioDest, Nil, CLSCTX_ALL,
           IID_IAudioMultiMediaDevice, fIAMM);
  except
  end;
  {Создание перечисляемого объекта для перебора всех движков в системе
   с помощью интерфейса ITTSEnum}
  CoCreateInstance(CLSID_TTSEnumerator, Nil, CLSCTX_ALL, 
                                         IID_ITTSEnum, aTTSEnum);
  aTTSEnum.Reset;//Сбрасываем на первый
  aTTSEnum.Next(1, ModeInfo, @NumFound); {Получаем первый движок}
  While NumFound > 0 do
   begin
     ComboBox1.Items.Add(String(ModeInfo.szModeName));
     aTTSEnum.Next(1, ModeInfo, @NumFound); {Получаем остальные}
   end;
end;

В этом коде происходит создания экземпляров COM объектов класса CLSID_MMAudioDest и CLSID_TTSEnumerator. Для этого используется метод CoCreateInstance из библиотеки COM. Он создает экземпляр объекта на основании нужного класса CLSID(Class Identifier) и идентификатора интерфейса IID(Interface Identifier). В первом случае мы образуем объект для связи с аудио устройством, во втором создаем экземпляр TTS, перечисляемого класса для перебора всех речевых синтезаторов, установленных в системе. После этого экземпляры классов окажутся соответственно в fIAMM и в aTTSEnum. После этого начинаем перебирать движки, узнавать их имена и добавлять в ComboBox’ы. Кстате, не забудьте объвить в разделе uses модуль activex.pas, иначе Delphi будет ругаться насчет процедуры CoCreateInstance, и модуль ComObj.pas, иначе после выполнения процедуры CoCreateInstance переменная aTTSEnum будет оставаться равна nil, а значит программа будет обваливаться на следующей строке.

Теперь если вы запустите программку, то в списке ComboBox’а увидите названия всех движков, установленных у вас в системе:

SAPI test

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

Теперь займемся загрузкой речевых синтезаторов для чтения текста. Поэтому пропишем событие изменения движка в ComboBox’е:

procedure TForm1.ComboBox1Change(Sender: TObject);
var
 NumFound: DWord;
 ModeInfo : TTSModeInfo;{Для хранения информации о текущем движке}
begin
   try
     CoCreateInstance(CLSID_MMAudioDest, nil, CLSCTX_ALL,
          IID_IAudioMultiMediaDevice, fIAMM);
     CoCreateInstance(CLSID_TTSEnumerator, nil, CLSCTX_ALL, 
                                                  IID_ITTSEnum, aTTSEnum);
     aTTSEnum.Reset;
     {Перескакиваем на нужный движок}
     Form1.aTTSEnum.skip(ComboBox1.ItemIndex);
     aTTSEnum.Next(1, ModeInfo, @NumFound);
     if assigned(fpModeInfo) then
     {если fpModeInfo не равен nil}
      dispose(fpModeInfo);
     new(fpModeInfo);
     fpModeInfo^:=ModeInfo;
     {загружаем движок по его GUID}
     aTTSEnum.Select(fpModeInfo^.gModeID, fITTSCentral, IUnknown(fIAMM));
   except
   end;
end;

Ну и все, теперь следует сказать, что «механизм чтения» приводится в действие следующим образом:

procedure TForm1.SpeakClick(Sender: TObject);
var
  SData: TSData;
  BufRich: string;
begin
  if not assigned(fITTSCentral) then
   begin
    ShowMessage('Не выбран движок!');
    exit;
   end;
  BufRich:='Это текст!'; {Этот текст будет прочитан}
  SData.dwSize := length(BufRich) + 1;
  SData.pData := pChar(BufRich);
  try
    fITTSCentral.TextData(CHARSET_TEXT, 0, SData, nil, IID_ITTSBufNotifySink);
  except
  end;
end;

Подставляйте в BufRich все что угодно, содержание этой строки и будет синтезироваться в речь!

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

...
 fITTSCentral.AudioPause; {Пауза в чтении}
 fITTSCentral.AudioResume; {Восстановление чтения после паузы}
 fITTSCentral.AudioReset; {Полная остановка чтения}
 ...

Остальные процедуры на кнопочки чтения, паузы и стоп, думаю, вы сами нафантазируете, а чтобы получить полный исходный текст проекта воспользуйтесь ссылочкой в конце страницы. Это пока все! Собираюсь в ближайшее время рассказать о том как работать с другими атрибутами чтения, такими как скорость, тон, громкость и т.д. Но это потом, а пока то ,что есть!

Speak up! Let us know what you think.