Не так давно (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 мы использовали в качестве данных — описание голоса. Кроме этого, каждый голос в системе может содержать следующие полезные атрибуты:
- Name — имя;
- Gender — пол;
- Age — возраст;
- Language — язык;
- 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','');
спасибо что помог)
офигенная статья, блог, и знания delphi)
мануал по установке алёны)
Спасибо за метериал. Очень помог!
А у меня наоборот,
gpIVTxt.Speak(‘Hello’,SVSFlagsAsync);
с ассинхроном на 7 проблемы, а с Default все отлично работает
Почему-то при компиляции вашего исходника у меня вылезает сообщение об отсутствии SAPIEngineLib_TLB.dcu Обыскал весь компьютер! Нету у меня не SAPIEngineLib_TLB.dcu не SAPIEngineLib_TLB.pas
p.s Есть только SpeechLib_TLB.pas и SpeechLib_TLB.dcu
Да, забыл сказать, у меня Win7 и Delphi 2010
Импортировать его надо.
А как его импортировать?
Как обычную библиотеку типов. Через меню «Component — Import Component»
Так я ее имопортировал, но у меня появляется только SpeechLib_TLB, а он требует еще SAPIEngineLib_TLB. Скиньте мне пожалуйста его на wlad-mozg@yandex.ru если вас не затруднит.
Какую именно библиотеку импортировать? Speech 5.4 импортировал, но у меня тоже самое, как и у Xatrix нет SAPIEngineLib_TLB.
Oleg, у меня только Speech 5.4 и *_tlb.pas тоже есть. При импорте библиотеки галку ставите на создание обертки для библиотеки? А созданный файл сохраняете куда-то?
И галку ставлю и сохраняю.
Подскажите пожалуйста, как не воспроизводить голос, а сохранить его в wav-файл?
Разобрался, может кому пригодится:
[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]
Очень пригодилось. Огромное спасибо, Albert
Стоит движок Acapela Infovox Desktop и голос Ryan.
Скачал исходник, запустил, выбираю голос Ryan, громкость прибавляю, давлю кнопку Speech
Ошибка: Priveleged instruction.
Жму еще раз кнопку Speech, ошибка:
Access violation at address 03A25D06. Write of address 00080003
Это все на Windows 8
Может Мелкомягкие там что то переделали в Windows 8?
Помогите!!!
ProgMasterX, я с Windows 8 вообще не работал…да и как-то желания особого нет сносить свою Win 7. «Priveleged instruction»…может нужны какие-то особые права для работы со Speech API в Win8? Что говорит MSDN по этому поводу?
Запускаю с правами админа
Огромное спасибо !!!! скиньте исходник с Алёной 23adda@mail.ru спасибо
Подскажите, пожалуйста, как регулировать тон синтезируемой речи?
Вродебы оно должно регулироваться через свойство pitch но такого свойства у меня почему-то нет.
Или в delphi такой возможности нет?