уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

Не так давно (26 марта 2010 года) я рассказывал Вам о том, как применить технологию Text-To-Speech (чтение текста голосом) в Delphi 2010 с использованием Speech API (SAPI) Windows. И всё бы было хорошо, если б не одно маленькое, но суровое «НО». Дело в том, что при запуске проекта не из IDE Delphi вызов метода Speak у интерфейсов IspVoice и ISpeechVoice вызывал ошибку «Floating Point Division By Zero» (деление на ноль). При этом в Windows XP программа работала без проблем. Ошибку эту обнаружил читатель с ником ziz.
В поисках решения проблемы я решил немного расширить статью и рассмотреть ещё несколько моментов по работе с технологией Text-To-Speech в Delphi. Сегодня рассмотрим работу SAPI 5.4. с использованием объекта TspVoice. Так что импортируйте библиотеку SAPI 5.4. для Windows 7, генерируйте модуль SpeechLib_TLB.pas, как это рассказано в предыдущем посте и начнем.

Вначале реализуем чтение голосом какого-либо отрывка текста с использованием класса TspVoice.

var gpIVTxt: TSpVoice;
begin
  gpIVTxt:=TSpVoice.Create(nil);
  gpIVTxt.Speak('Hello',SVSFlagsAsync);
end;

Здесь мы используем асинхронный вывод. К слову сказать, синхронный вывод от асинхронного отличается тем, что асинхронный вывод используется в случае, когда в процессе синтеза речи программа должна выполнять какие-то дополнительные действия (подсвечивать синтаксис, обрабатывать данные в потоке и т.д.). В Windows 7 мне так и не удалось реализовать синхронный вывод (флаг SVSFDefoult) и с чем это связано пока сказать не могу. У кого-нибудь есть идеи на этот счёт?
Так что пока будем использовать везде асинхронный вывод.
Двигаемся дальше и попробуем изменить свойства голосового движка.

Характеристики голоса

Любой голосовой движок, использующий SAPI имеет следующие характеристики:
Volume — громкость голоса. Целочисленное значение. Изменяется линейно в диапазоне от 0 до 100. То есть значение 50 будет соответствовать половине предельной громкости используемого голоса.
Rate — скорость воспроизведения текста голосом. Может принимать значения от -10 до 10. Значение по умолчанию 0. Соответственно отрицательные значение характеристики замедляют произношение, положительные — ускоряют. SAPI 5-х версий не поддерживают значения Rate больше 10 или меньше -10, т.е. если Вы устанавливаете значение Rate=11, то голос будет воспроизводиться на скорости 10.
Попробуем реализовать изменение характеристик голоса в нашей программе. Разместим на форме два компонента TrackBar и 2 Label как показано на рисунке:

Соответственно первый TrackBar будет изменять громкость от 0 до 10, а второй — скорость от -10 до 10.
Теперь перенесем переменную gpIVTxt: TSpVoice в секцию public класса TForm1 и будем создавать класс TspVoice в момент создания формы, т.е.:

procedure TForm1.FormCreate(Sender: TObject);
begin
  gpIVTxt:=TSpVoice.Create(nil);
end;

Обработчики OnChange у TrackBar’ов будут следующими:

procedure TForm1.TrackBar1Change(Sender: TObject);
begin
  VolumeLabel.Caption:=IntToStr(TrackBar1.Position);
  gpIVTxt.Volume:=TrackBar1.Position;
end;
 
procedure TForm1.TrackBar2Change(Sender: TObject);
begin
  RateLabel.Caption:=IntToStr(TrackBar2.Position);
  gpIVTxt.Rate:=TrackBar2.Position;
end;

Теперь запустите приложение и попробуйте изменять значение Rate и Volume во время произношения фразы. Т.к. используется асинхронный вывод, то характеристики голоса изменяются «на лету».
Английский голос — это конечно здорово, но как быть с русскими голосами в SAPI? Посмотрим как можно использовать русскоязычные движки в своей программе.

SAPI и русские голоса.

В качестве русскоязычного движка я выбрал движок от Acapela Group c названием «Алёна». Неплохой движок с пробным периодом в 30 дней. Какой движок выберите Вы — значения не имеет, т.к. работать мы будем с SAPI не влезая в особенности каждого движка, т.е., следуя схеме:

Будем использовать два верхних уровня.
Итак, для того, чтобы получить сведения о всех голосовых движках, установленных в системе, нам понадобится воспользоваться интерфейсом ISpeechObjectTokens, который имеет всего одно свойство Count — количество голосовых движков, установленных в системе. И один метод:

Item(i:index):ISpObjectToken

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

public
  ...
  Voices:ISpeechObjectTokens;
...
 
procedure TForm1.GetMyVoices(List: TListBox);
var i:integer;
begin
  List.Clear;
  Voices:=gpIVTxt.GetVoices('','');
  for i:=0 to Voices.Count - 1 do
    List.Items.Add(Voices.Item(i).GetDescription(0));
end;

Здесь мы вначале запрашиваем методом GetVoices все голоса поддерживаемые объектом TspVoice. И затем выводим в ListBox описания голосов. Результат будет выглядеть следующим образом:

Теперь мы можем назначить нашему объекту голос, например так:

gpIVTxt.Voice:=Voices.Item(0);

В этом случае будет использоваться голос «Anna (English)». А так:

gpIVTxt.Voice:=Voices.Item(1);

Голос «Alyona (Ruusian)».
При этом для нас нет разницы какой из голосов мы используем — русский или английский, встроенный в систему или скачаный с какого-нибудь варезника и установленный вручную — подход к управлению один и тот же (см. схему).
Для того, чтобы вывести сведения о голосов ListBox мы использовали в качестве данных — описание голоса. Кроме этого, каждый голос в системе может содержать следующие полезные атрибуты:

  1. Name — имя;
  2. Gender — пол;
  3. Age — возраст;
  4. Language — язык;
  5. Vendor — создатель;

Для того, чтобы получить значения какого-либо атрибута необходимо воспользоваться методом ISpeechObjectToken:

function GetAttribute(const AttributeName: WideString): WideString;

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

procedure TForm1.ListBox1Click(Sender: TObject);
begin
Label7.Caption:=Voices.Item(ListBox1.ItemIndex).GetAttribute('Name');
Label8.Caption:=Voices.Item(ListBox1.ItemIndex).GetAttribute('Age');
Label9.Caption:=Voices.Item(ListBox1.ItemIndex).GetAttribute('Gender');
Label12.Caption:=Voices.Item(ListBox1.ItemIndex).GetAttribute('Language');
Label13.Caption:=Voices.Item(ListBox1.ItemIndex).GetAttribute('Vendor');
end;

А в запущенном приложении так:

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

  AttributeName=Value
  AttributeName!=Value

Следующие два вызова метода GetVoices абсолютно идентичны и в результате вернут 2 установленных в моей системе голоса:

  Voices:=gpIVTxt.GetVoices('Gender=female','');
  Voices:=gpIVTxt.GetVoices('Gender!=male','');
Скачать исходник: Исходники —> Прочие
5 1 голос
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
21 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
ziz
ziz
05/05/2010 22:40

спасибо что помог)
офигенная статья, блог, и знания delphi)

ziz
ziz
05/05/2010 23:33

http://ancher.ru/content/19-02-2009/audioknigi-delaem-sami
мануал по установке алёны)

Kanzaki
Kanzaki
12/07/2010 07:39

Спасибо за метериал. Очень помог!

Suvlehim
Suvlehim
29/09/2010 20:53

А у меня наоборот,
gpIVTxt.Speak(‘Hello’,SVSFlagsAsync);
с ассинхроном на 7 проблемы, а с Default все отлично работает

Xatrix
Xatrix
18/10/2010 15:40

Почему-то при компиляции вашего исходника у меня вылезает сообщение об отсутствии SAPIEngineLib_TLB.dcu   Обыскал весь компьютер! Нету у меня не SAPIEngineLib_TLB.dcu не SAPIEngineLib_TLB.pas
p.s Есть только SpeechLib_TLB.pas и SpeechLib_TLB.dcu

Xatrix
Xatrix
18/10/2010 15:41

Да, забыл сказать, у меня Win7 и Delphi 2010

Xatrix
Xatrix
24/10/2010 00:48

А как его импортировать?

Xatrix
Xatrix
04/11/2010 15:33

Так я ее имопортировал, но у меня появляется только SpeechLib_TLB, а он требует еще SAPIEngineLib_TLB. Скиньте мне пожалуйста его на wlad-mozg@yandex.ru если вас не затруднит.

Oleg
18/11/2011 04:53

Какую именно библиотеку импортировать? Speech 5.4 импортировал, но у меня тоже самое, как и у Xatrix нет SAPIEngineLib_TLB.

Oleg
18/11/2011 14:11

И галку ставлю и сохраняю.

Albert
Albert
20/07/2012 13:51

Подскажите пожалуйста, как не воспроизводить голос, а сохранить его в wav-файл?

Albert
Albert
20/07/2012 16:03

Разобрался, может кому пригодится:
[code]
var
SpFileStream: TSpFileStream;
SpVoice: TSpVoice;
Voices:ISpeechObjectTokens;
i:integer;
begin
SpFileStream := TSpFilestream.Create(nil);
SpVoice := TSpVoice.Create(nil);
try
SpFileStream.Format.type_ := SAFT11kHz8BitMono;
SpFileStream.Open('test.wav', SSFMCreateForWrite, FALSE);
SpVoice.AudioOutputStream := SPFileStream.DefaultInterface;
Voices:=SpVoice.GetVoices('', '');
for i := 0 to Voices.Count - 1 do
if Pos('Alyona', Voices.Item(i).GetAttribute('Name')) = 1 then
begin
SpVoice.Voice := Voices.Item(i);
Break;
end;
SpVoice.SynchronousSpeakTimeout := 1000;
SpVoice.Speak('Привет!', SVSFlagsAsync);
SpVoice.WaitUntilDone(3000);
finally
SpVoice.Free;;
SpFileStream.Free;
end;
end;
[/code]

Вадим
Вадим
15/04/2014 09:30
Ответить на  Albert

Очень пригодилось. Огромное спасибо, Albert

ProgMasterX
ProgMasterX
07/01/2013 13:44

Стоит движок Acapela Infovox Desktop и голос Ryan.
Скачал исходник, запустил, выбираю голос Ryan, громкость прибавляю, давлю кнопку Speech
Ошибка: Priveleged instruction.
Жму еще раз кнопку Speech, ошибка:
Access violation at address 03A25D06. Write of address 00080003
Это все на Windows 8
Может Мелкомягкие там что то переделали в Windows 8?
Помогите!!!

ProgMasterX
ProgMasterX
18/01/2013 13:54
Ответить на  Vlad

Запускаю с правами админа

Абылай
Абылай
06/06/2015 03:44

Огромное спасибо !!!! скиньте исходник с Алёной 23adda@mail.ru спасибо

Сергей
Сергей
25/08/2016 21:32

Подскажите, пожалуйста, как регулировать тон синтезируемой речи?
Вродебы оно должно регулироваться через свойство pitch но такого свойства у меня почему-то нет.
Или в delphi такой возможности нет?