»спользование MS Speech API 4.0 в программах на FreeBASIC

ƒата публикации:2007
Twitter Facebook Vkontakte

ѕредварительные замечани€

Ёта стать€ предназначена дл€ тех, кто уже знаком с FreeBASIC и хотел бы использовать в своих программах технологии синтеза речи, доступные посредством MS SAPI 4. ƒл€ понимани€ материала необходимо знать €зык программировани€ Ѕейсик, назначение директив компил€тора FreeBASIC, а также владеть базовыми знани€ми о том, как создавать приложени€ в MS Windows. ѕриветствуетс€ знание основ работы технологий COM и OLE автоматизации, однако это требование не €вл€етс€ об€зательным и отсутствие этих знаний не должно сильно затруднить понимание материала (ведь пользуютс€ же многие люди микроволновой печью, не име€ представлени€ о работе магнетрона).¬полне достаточно будет вашего опыта использовани€ функции CreateObject() в среде Visual Basic, Visual Basic for Application или скриптовом €зыке VBScript.

ќ чем пойдет разговор

Ќесмотр€ на р€д неоспоримых достоинств, присущих такому средству разработки программ, как компил€тор FreeBASIC, есть один существенный момент в работе с ним, который ставит в тупик многих программистов, пришедших к FreeBASIC после некоторого опыта программировани€ в среде Visual Basic. Ётим моментом €вл€етс€ работа с серверами OLE автоматизации (OLE Automation). ѕоскольку компоненты MS Speech API 4 (MS SAPI 4) реализованы как серверы OLE автоматизации, то, вз€в их в качестве примера, мы разберемс€ в том, как работать с подобными компонентами в программах на FreeBASIC. ≈ще одной причиной, по которой в качестве примера выбран набор компонентов SAPI 4, €вл€етс€ то обсто€тельство, что среди примеров исходного кода в каталоге, где установлен FreeBASIC, в папке "examples\disphelper\" отсутствуют именно примеры работы с компонентами речевых технологий (SAPI 4 и SAPI 5).

¬ средах Visual Basic, VBA и др., а также в скриптовом €зыке VBScript программисту достаточно использовать функцию CreateObject(), чтобы, ни о чем больше не задумыва€сь, получить доступ к нужному OLE-серверу. ¬ терминах Visual Basic этот OLE-сервер называетс€ "объектом". ¬ дальнейшем программист использует полученный объект, вызыва€ методы (methods) и получа€/устанавлива€ свойства (properties) этого объекта. Ќа стороне OLE-сервера этот механизм поддерживаетс€ при помощи интерфейса IDispatch, на который возложена об€занность вызывать нужную функцию в соответствии с именем метода, которое указал программист в программе на Visual Basic. “акой механизм взаимодействи€ прикладной программы (OLE-клиента) и сервера OLE автоматизации называетс€ "поздним" св€зыванием из-за того, что адрес функции, котора€ будет вызвана по запросу прикладной программы, определ€етс€ не на этапе компил€ции программы, а во врем€ ее исполнени€. Ётот механизм в программах на Visual Basic поддерживаетс€ самой средой разработки и библиотеками, которые должны быть включены в дистрибутив с программой.

¬ отличие от Visual Basic, FreeBASIC €вл€етс€ только лишь компил€тором, а не средой разработки, поэтому встроенных функций дл€ работы с серверами OLE автоматизации в нем не предусмотрено. ќднако этот недостаток можно восполнить, обратившись к библиотеке Disphelper, котора€ свободно распростран€етс€ в открытом коде и котора€ входит в состав пакета FreeBASIC. ѕрограммисты на €зыках C/C++ могут более подробно ознакомитьс€ с Disphelper на официальном сайте этого проекта (все необходимые ссылки приведены в соответствующем разделе этой статьи).

“аким образом, дл€ использовани€ синтеза речи в программах на FreeBASIC нам необходимо, во-первых, освоить работу с функци€ми Disphelper, а во-вторых, познакомитьс€ с методами и свойствами SAPI 4 и научитьс€ взаимодействовать с ними при помощи Disphelper.

Ѕыстрый старт

ѕриведем два фрагмента программ на Visual Basic и FreeBASIC, выполн€ющих схожие действи€.

' Visual Basic
SET tts = CreateObject("ActiveVoice.ActiveVoice.1")
tts.Speak ("Hello, world!")

' FreeBASIC dhCreateObject ("ActiveVoice.ActiveVoice.1", NULL, @tts) DHCallMethod (tts, ".Speak(%S)", "Hello, world!")

≈сли при взгл€де на эти фрагменты кода ¬ам стало все совершенно пон€тно и ¬ы горите желанием приступить к программированию, то ¬ы можете не читать дальше и дать волю своим желани€м. ћы же познакомимс€ поближе с возможност€ми Disphelper и SAPI 4.

ќсновы Disphelper

Ѕиблиотека Disphelper предназначена дл€ облегчени€ работы с интерфейсом IDispatch OLE-компонентов . ƒл€ вызова методов OLE-компонента используетс€ синтаксис, напоминающий синтаксис функции printf() €зыка C. ѕравильнее будет сказать, что речь идет не о синтаксисе самой функции printf(), а о символах строки формата. ѕри вызове функций Disphelper используютс€ похожие соглашени€ дл€ указани€ типа аргумента, что и в функции printf().

¬от небольшой перечень некоторых спецификаций аргументов из тех, что поддерживает Disphelper (более полный список следует изучать по файлу readme.htm, вход€щем в дистрибутив Disphelper):

  • %d - LONG (INTEGER)
  • %u - ULONG (UINTEGER, DWORD)
  • %e - DOUBLE
  • %b - BOOL
  • %s - LPSTR (указатель на строку однобайтовых символов, STRING PTR, ZSTRING PTR)
  • %S - LPWSTR (указатель на строку из двухбайтовых символов, WSTRING PTR)
  • %o - IDispatch PTR (указатель на интерфейс IDispatch OLE-компонента)
  • %p - LPVOID (используетс€ дл€ передачи дескрипторов различного вида)

ѕеречислим и охарактеризуем те функции Disphelper, которые мы будем использовать в дальнейшем.

  1. DISPATCH_OBJ() - макрос, служащий дл€ объ€влени€ переменной типа IDispatch PTR.
  2. DHCreateObject () - создает экземпл€р OLE-компонента и устанавливает взаимодействие с интерфейсом IDispatch. ƒействие этой функции схоже с действием функции CreateObject () в Visual Basic.
  3. DHCallMethod() - вызывает определенный метод (method) OLE-компонента.
  4. DHPutValue() - присваивает значение определенному свойству (property) OLE-компонента.
  5. DHGetValue () - получает значение определенного свойства (property) OLE-компонента.

Ќиже будет приведен исходный код, демонстрирующий использование этих функций. «десь же чуть подробнее остановимс€ на функци€х DHCallMethod, DHPutValue и DHGetValue. ќписанине прототипов этих функций можно найти в файле disphelper.bi, который расположен в каталоге компил€тора FreeBASIC в папке "inc\disphelper\"..

‘ункци€ DHCallMethod имеет переменное число параметров, завис€щее от количества параметров того метода, который нужно вызвать. D любом случае в качестве первого параметра передаетс€ переменна€ типа IDispatch PTR (или, по терминологии Visual Basic, объект), вторым параметром передаетс€ строка, содержаща€ им€ вызываемого метода с точкой в начале и списком форматов аргументов. ƒалее идут сами аргументы, которые будут переданы вызываемому методу. Ќапример, так выгл€дит вызов метода Method1 у объекта Object1, причем методу передаетс€ целочисленный аргумент:

DHCallMethod (Object1, ".Method1(%d)", 100)

ѕохожим образом вызываетс€ функци€ DHPutValue? только вместо метода указываетс€ в виде строки им€ свойства. Ќапример, так выгл€дит присваивание свойству Property1 целочисленного значени€:

DHPutValue (Object1, ".Property1=%d", 100)

Ќесколько иной пор€док параметров у функции DHGetValue. «десь первым идет строка формата получаемого значени€, затем указатель на переменную, в которую полученное значение будет сохранено, а затем уже идет объект и строка с именем свойства, значение которого нужно прочитать:

Dim Value As Integer
DHGetValue ("%d", @Value, Object1, ".Property1")

‘ункции Disphelper возвращают значение типа HRESULT, которое содержит код завершени€ операции с OLE-компонентом. Ёто может быть код успешного завершени€ или код ошибки. јнализиру€ этот код, можно определить причину возникновени€ ошибок. ≈сли установлен режим перехвата ошибок (например, в цел€х отладки), то в случае ошибки на экране по€витс€ сообщение с кодом возврата и текстовым описанием ее причины.

 омпонент Direct Speech Synthesis

ѕодготовка SAPI 4 к работе

Direct Speech Synthesis - это один из компонентов, вход€щих в состав MS SAPI 4. Ётот компонент предназначен дл€ непосредственного синтеза речи по тексту и позвол€ет не только озвучивать текст, но и управл€ть параметрами синтеза речи. ≈сли ¬ы используете MS Windows 2000, то компоненты SAPI 4 вход€т в состав этой операционной системы и, скорее всего, уже установлены на ¬ашем компьютере. ƒл€ операционных систем MS Windows 9x и XP ¬ам придетс€ загрузить пользовательский дистрибутив SAPI или весь MS SAPI 4 SDK4. ќднако после установки компонентов SAPI 4 ¬ы можете столкнутьс€ с ситуацией, когда приведенный ниже программный код не будет работать. ≈сли при запуске откомпилированной программы на экране по€вл€етс€ сообщение:

ќшибка
Member:  ActiveVoice.ActiveVoice.1 Function:  CreateObject
Error In:  CreateObjectEx
Error:  Ќедопустима€ строка с указанием класса
Code:  800401f3
Source:  Application

“о это означает, что в системном реестре в соответствующем разделе не сопоставлены строковое им€ компонента, называемое ProgId (в нашем случае это "ActiveVoice.ActiveVoice.1") и уникальный идентификатор компонента CLSID, по которому операционна€ система находит нужную информацию дл€ загрузки компонента.

¬ таком случае необходимо дополнить реестр информацией вручную. ƒл€ Windows XP нужно создать текстовй файл "activevoice.reg" следующего содержани€:

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\ActiveVoice.ActiveVoice.1]
@="Direct Speech Synthesis"

[HKEY_CLASSES_ROOT\ActiveVoice.ActiveVoice.1\Clsid]
@="{EEE78591-FE22-11D0-8BEF-0060081841DE}"

«атем в ѕроводнике Windows запустить этот файл на выполнение (нажать клавишу Enter) и согласитьс€ с внесением изменений в реестр. Ќа этом подготовка SAPI 4 к работе завершена.

≈сли ¬ы знакомы с функци€ми Win32 API, предназначенными дл€ работы с системным реестром, то, при разработке полноценного приложени€, проверку и модификацию вышеописанного ключа в системном реестре можно выполнить программно.

—войства и методы компонента Direct Speech Synthesis

¬ рамках этой статьи нам будет достаточно изучить те методы и свойства, которые используютс€ чаще других при создании приложений с поддержкой синтеза речи. «а более подробной информацией следует обращатьс€ к документации, вход€щей в комплект MS SAPI 4 SDK.

Ќиже имена свойств и методов указываютс€ с точкой в начале, потому что именно в таком виде эти имена нужно передавать функци€м Disphelper.

Ќачнем с методов:

  • .AudioPause() - приостанавливает синтез речи.
  • .AudioReset() - прерывает синтез речи.
  • .AudioResume() - возобнавл€ет синтез речи.
  • .Speak(text As String) - озвучивает текст, переданный в качестве параметра.
  • .Select(index As Long) - устанавливает текущий голос в соответствии с номером, указанным в параметре.

“еперь перейдем к свойствам, их будет несколько больше:

  • .CountEngines As Long - количество голосов, поддерживаемых всеми SAPI 4 синтезаторами.
  • .CurrentMode As Long - индекс текущего голоса.
  • .FileName As String - им€ файла в формате wav, куда будет записана синтезированна€ речь. ≈сли не указано, то речь будет воспроизводитьс€ через звуковую карту.
  • .Initialized As Short - устанавливаетс€ в 1 дл€ инициализации компонента.
  • .Pitch As Long - высота синтезируемой речи.
  • .ProductName(index As Long) As String - фирменное название синтезатора с указанным индексом.
  • .Speaker(index As Long) As String - им€ персонажа (голоса) с указанным индексом.
  • .Speaking As Short - если установлен в1, то синтезатор зан€т синтезом речи; иначе установлен в 0.
  • .Speed As Long - темп речи.
  • .Tagged As Boolean - указывает синтезатору на то, что необходимо обрабатывать специальные теги речевой разметки, помещенные в текст.

√овор€щее приложение на FreeBASIC

ѕрежде всего отметим некоторые особенности использовани€ функций Disphelper в программах на FreeBASIC. ¬о-первых, все строковые параметры передаютс€ функци€м Disphelper в кодировке Unicode. ѕо этой причине в тексте программы необходимо указать:

#define UNICODE

¬о-вторых, следует подключить заголовочный файл "disphelper.bi", который входит в пакет FreeBASIC. Ёто делаетс€ директивой:

#include "disphelper/disphelper.bi"

¬-третьих, дл€ нормальной работы Direct Speech Synthesis необходимо создать оконное приложение. ¬ консольном варианте с синтезом речи могут возникнуть сложности.

Ќиже приведен исходный код приложени€ Windows, которое отображает на экране диалоговое окно. ¬ этом окне можно в комбинированном списке выбрать голос, ввести текст в поле редактировани€ и нажать кнопку, чтобы преобразовать введенный текст в речь.

¬есь проект включает п€ть файлов:

  1. speech.bas - исходный код основной программы, реализующей вывод диалогового окна и обработку сообщений.
  2. sapi4.bas - исходный код библиотеки функций, реализующих взаимодействие с Direct Speech Synthesis при помощи Disphelper.
  3. sapi4.bi - заголовочный файл с объ€влени€ми функций sapi4.bas.
  4. resource.bi - заголовочный файл с идентификаторами ресурсов.
  5. speech.rc - файл описани€ ресурсов диалогового окна.

ѕо€сним назначение файла sapi4.bas. ћожно было поместить вызовы функций Disphelper непосредственно в процедуру обработки сообщений диалогового окна. ќднако мы вынесли эти вызовы в отдельную библиотеку функций sapi4.bas. ‘ункции этой библиотеки условно можно назвать "обертками", так как они как бы "оборачивают" вызовы функций Disphelper. “ака€ организаци€ исходного кода позвол€ет нам мен€ть код функций-оберток, но при этом не мен€ть (или почти не мен€ть) исходный код основного файла speech.bas. ¬ дальнейшем, когда будет обсуждатьс€ работа с MS SAPI 5 в приложени€х FreeBASIC, мы воспользуемс€ этой возможностью, подставив вместо sapi4.bas другую библиотеку, а именно sapi5.bas. ѕомимо этого, ¬ы можете использовать sapi4.bas в других проектах, при этом вам не придетс€ "вырезать" нужные фрагменты кода из основного приложени€.

≈ще следует обратить внимание, что мы не примен€ем макрос DISPATCH_OBJ(), потому что используемый нами объект (то есть указатель на интерфейс IDispatch OLE-компонента) €вл€етс€ глобальной переменной, а синтаксис FreeBASIC не позвол€ет совместить указанный макрос и объ€вление глобальной переменной. ѕо указанной причине мы используем конструкцию:

 
dim SHARED 	  as IDispatch ptr tts = NULL

‘айл speech.bas

' ‘айл speech.bas
' ѕример использовани€ SAPI 4 дл€ синтеза речи 
' в программах на FreeBASIC
''  омпилировать: fbc -s gui speech.rc speech.bas sapi4.bas 
'' 
'' 
option explicit 
#define UNICODE
#include once "windows.bi" 
#include "disphelper/disphelper.bi"

' «аголовочный файл функций дл€ работы с SAPI 4
#include "sapi4.bi"

' ‘айл с идентификаторами ресурсов
#include "resource.bi"

' ќбъ€вление диалоговой функции
DECLARE FUNCTION DlgProc (byval hwnd as HWND, byval umsg as UINT, byval wparam as WPARAM, byval lparam as LPARAM) as BOOL
             
' √лобальные переменные
dim SHARED 	  as IDispatch ptr tts = NULL

' »нициализаци€ Disphelper
	dhInitialize( TRUE )

' –азрешаем перехват и отображение сообщений об ошибках
	dhToggleExceptions( TRUE )

' ќтображаем диалоговое окно
  DialogBoxParam( GetModuleHandle( NULL ), cptr( LPCSTR, IDD_DLG1 ), NULL, @DlgProc, NULL ) 

' ќсвобождаем то, что занимали
	SAFE_RELEASE( tts)

' «авершаем работу с Disphelper
	dhUninitialize( TRUE )
  end 
' «десь заканчиваетс€ основна€ программа

                 ' ‘ункци€ диалогового окна
Function DlgProc (byval hwnd as HWND, byval umsg as UINT, byval wparam as WPARAM, byval lparam as LPARAM) as BOOL
  dim as long id, event
  
  select case uMsg
  case WM_INITDIALOG
' ѕытаемс€ создать Direct Speech Synthesis 
If TRUE <> SAPI4_Initialize(tts) Then
  	EndDialog( hwnd, 0 ) 
Return TRUE
End If

' «аполн€ем комбинированный список доступными голосами
SAPI4_GetVoices(tts, GetDlgItem(hWnd, IDC_VOICES_LIST))

' ¬ыбираем первый из голосов
SAPI4_SelectVoice (tts, 1)

' ”станавливаем темп речи
SAPI4_SetSpeed (tts, 80)
  case WM_CLOSE
  	EndDialog( hwnd, 0 ) 

  case WM_COMMAND
		id  = loword( wParam )
		event = hiword( wParam )

    select case id
    case IDOK 
' ќзвучиваем текст
scope
DIM szBuf AS WSTRING * 1024
GetDlgItemText(hWnd, IDC_TEXT_TO_SPEAK, szBuf, 1024)
SAPI4_Speak (tts, @szBuf)
End Scope

case IDCANCEL
    	EndDialog( hwnd, 0 )

case IDC_VOICES_LIST:
' –еагируем на смену голоса
If (event = CBN_SELCHANGE) Then
SAPI4_SelectVoice (tts, SendDlgItemMessage (hWnd, Id, CB_GETCURSEL, 0, 0)+1)
End If
  	end select
    
  case else
  	return FALSE
  
  end select
  
  return TRUE
end function 

‘айл sapi4.bas


' ‘айл sapi4.bas
' Ќабор функций дл€ взаимодействи€ с компонентом Direct Speech 
' при помощи функций Disphelper

option explicit 

#define UNICODE
#include once "windows.bi" 
#include "disphelper/disphelper.bi"
' SAPI4_Initialize  создает объект ActiveVoice.ActiveVoice.1 и инициализирует его.
' ¬озврат: TRUE, если ошибок не было; FALSE - в противном случае.
FUNCTION SAPI4_Initialize (byRef Object As IDispatch PTR) As BOOL
Dim As String ProgId = "ActiveVoice.ActiveVoice.1"
IF FAILED(dhCreateObject (ProgId, NULL, @Object) THEN
RETURN FALSE
END IF
dhPutValue(Object, ".Initialized = %u", 1)
RETURN TRUE
END FUNCTION

' ѕроцедура SAPI4_GetVoices  заполн€ет комбинированный список, 
'  дескриптор которого передаетс€ во втором параметре, названи€ми доступных голосов.
SUB SAPI4_GetVoices (byRef object As IDispatch PTR,byval hCtrl as HWND)
Dim MaxVoice AS INTEGER
Dim szBuf AS WSTRING * 1024
Dim szSpeaker AS WSTRING PTR
Dim szProduct AS WSTRING PTR
Dim i AS INTEGER

SendMessage (hCtrl, CB_RESETCONTENT, 0, 0)
DHGetValue("%d", @MaxVoice, Object, ".CountEngines")
FOR i = 1 TO MaxVoice
DHGetValue("%S", @szSpeaker, Object, ".Speaker(%d)", i)
DHGetValue("%S", @szProduct, Object, ".ProductName (%d)", i)
szBuf = *szSpeaker + ": " +*szProduct
SendMessage (hCtrl, CB_ADDSTRING, 0, @szBuf)
NEXT
SendMessage (hCtrl, CB_SETCURSEL, 0, 0)

			dhFreeString(szSpeaker)
			dhFreeString(szProduct)
END SUB

' ѕроцедура SAPI4_SelectVoice  устанавливает в качестве активного 
' голос с указанным номером.
SUB SAPI4_SelectVoice (byRef Object As IDispatch PTR, byVal Index As INTEGER)
Dim MaxVoice AS INTEGER
DHGetValue("%d", @MaxVoice, Object, ".CountEngines")
IF Index > 0 AND Index <= MaxVoice THEN 
dhCallMethod(Object, ".Select(%u)", Index)
END IF
END SUB

' ѕроцедура SAPI4_SetSpeed  устанавливает темп речи.
' ѕроверка на допустимость значени€ темпа речи не производитс€.
SUB SAPI4_SetSpeed (byRef Object As IDispatch PTR, byVal Speed As INTEGER)
dhPutValue(Object, ".Speed=%u", Speed) 
END SUB

' ѕроцедура SAPI4_Speak  вызывает метод, озвучивающий текст.
SUB SAPI4_Speak (byRef Object As IDispatch PTR, byVal Phrase As WString PTR)
dhCallMethod(Object, ".Speak(%S)", Phrase)
END SUB

‘айл sapi4.bi

' ‘айл sapi4.bi
' ќбъ€вление функций sapi4.bas
#ifndef __SAPI4_BI__
#define __SAPI4_BI__

DECLARE FUNCTION SAPI4_Initialize (byRef Object As IDispatch PTR) As BOOL
DECLARE SUB SAPI4_GetVoices (byRef Object As IDispatch PTR, byval hCtrl as HWND)
DECLARE SUB SAPI4_SelectVoice (byRef Object As IDispatch PTR, byVal Index As Integer)   
DECLARE SUB SAPI4_SetSpeed (byRef Object As IDispatch PTR, byVal Speed As Integer)
DECLARE SUB SAPI4_Speak (byRef Object As IDispatch PTR, byVal Phrase As WString PTR)
#endif

‘айл resource.bi


#ifndef IDC_STATIC 
#define IDC_STATIC -1
#endif
#define IDD_DLG1 1000 
#define IDC_VOICES_LIST	1001
#define IDC_TEXT_TO_SPEAK	1002

‘айл speech.rc

#include "resource.bi"

IDD_DLG1 DIALOGEX 0,0,200,100 
CAPTION "Text-to-speech" 
FONT 8,"MS Sans Serif" 
STYLE 0x10CC0000 
EXSTYLE 0x00000080 
BEGIN
	LTEXT "Voice:", IDC_STATIC, 5, 5, 40, 9
	COMBOBOX IDC_VOICES_LIST, 50, 5, 145, 10, WS_TABSTOP| CBS_DROPDOWNLIST | WS_VSCROLL 
	LTEXT "Text to speak:", IDC_STATIC, 5, 5+11, 100, 10
	EDITTEXT IDC_TEXT_TO_SPEAK, 5, 5+22, 190, 10

	 DEFPUSHBUTTON "Speak", IDOK, 120, 5+44, 35, 10 
	PUSHBUTTON  "«акрыть", IDCANCEL, 120+37, 5+44, 35, 10 
END


–аспространение материалов сайта означает, что распространитель прин€л услови€ лицензионного соглашени€.
»де€ и реализаци€: © ¬ладимир ƒовыденков и јнатолий  амынин,  2004-2021