2013-03-06 229 views
2

我在C++ Builder中使用以下代碼來處理盲人的文本到語音應用程序控制(很可能類似的示例可以在Delphi中使用)。主窗體已檢查KeyPreview屬性以啓用鍵F11預覽以開始說話活動(聚焦)控件。代碼是可行的,但有一些問題。這個例子在C++ Builder代碼中,但是從我發現的,Delphi遇到了同樣的問題,我發現的解決方案是一樣的。如果你有德爾福解決方案,隨時發佈,它是相似的無論如何。語音API(SAPI)在Windows 7上使用C++ Builder進行零點浮點除法

#include <sapi.h> 
#include <WTypes.h> 

//--------------------------------------------------------------------------- 
// Speak text string (synchronous function) 
//--------------------------------------------------------------------------- 

bool SpeakText(UnicodeString Text) 
{ 
ISpVoice* pVoice = NULL; 

if (FAILED(::CoInitialize(NULL))) return false; 

Word Saved8087CW = Default8087CW;            // Disable floating point division by zero exception caused by Speak 
Set8087CW(0x133f); 

HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice); 
if (SUCCEEDED(hr)) 
    { 
    //pVoice->SpeakCompleteEvent() 
    //pVoice->SetSyncSpeakTimeout(1000); 
    hr = pVoice->Speak(WideString(Text).c_bstr(), SPF_DEFAULT, NULL); 
    pVoice->Release(); 
    pVoice = NULL; 
    } 

Set8087CW(Saved8087CW); 

::CoUninitialize(); 
return true; 
} 

//--------------------------------------------------------------------------- 
void __fastcall TForm1::FormKeyUp(TObject *Sender, WORD &Key, TShiftState Shift) 
{ 
UnicodeString Speaker; 

if (Key == VK_F11) 
    { 
    if  (Screen->ActiveControl->InheritsFrom(__classid(TButton))) { Speaker += "Button, " + static_cast<TButton*>(Screen->ActiveControl)->Caption + "."; } 
    else if (Screen->ActiveControl->InheritsFrom(__classid(TEdit)))  { Speaker += "Edit box, " + static_cast<TEdit*>(Screen->ActiveControl)->Text  + "."; } 
    } 

if (Speaker != "") SpeakText(Speaker); 
} 
//--------------------------------------------------------------------------- 

問題:

  1. pVoice->在線原因浮點除法,如果我不使用Set8087CW功能覆蓋例外。這種情況只發生在Windows 7(可能是Vista和Windows 8)上,而不是Windows XP中的同一個程序中(編譯後的exe)。有沒有不使用Set8087CW的解決方案?刪除這些行會導致問題和異常。我有BCB2010。

  2. 功能是同步的,在程序結束讀取文本之前不會關閉或返回控制權。這是較長文本的問題。它也阻止程序事件。有沒有辦法讓它異步或引入一個事件來定期檢查F11鍵狀態,如果F11再次按下,它會停止讀取和取消初始化對象?例如輪詢每300毫秒(或每個單詞等後)按鍵F11,如果按下,停止說話?或者運行它線程?

  3. SAPI是否有內存泄漏,因爲有些在各種網站上寫入?

  4. 以上代碼可以使用OleCheck而不是CoCreateInstanceCoUninitialize

UPDATE爲那些尋找溶液通過雷米勒博的建議:

SavedCW = Get8087CW(); 
Set8087CW(SavedCW | 0x4); 
hr = pVoice->Speak(WideString(Text).c_bstr(), SPF_DEFAULT | SPF_ASYNC, NULL); 
pVoice->WaitUntilDone(-1); // Waits until text is done... if F11 is pressed simply go out of scope and speech will stop 
Set8087CW(SavedCW); 

還找到詳細的例子在CodeRage 4會話:http://cc.embarcadero.com/item/27264

+0

我嘗試了上面的代碼,但沒有講話聲。我將hr的值設爲null。我已經打開了一個新的幫助線索:http://stackoverflow.com/questions/27820940/ms-sapi-5-1-issue-with-c你能幫我嗎? – programmer 2015-01-07 14:26:06

+0

@programmer您的問題不適用於C++ Builder編譯器,上述解決方案適用於C++ Builder。 – Coder12345 2015-01-07 18:00:41

+0

感謝您的評論。對於我發佈的問題,你有什麼建議嗎?我希望我能使它工作。 – programmer 2015-01-08 02:25:21

回答

3
  1. 錯誤確實發生在Vista也是如此。屏蔽浮點異常是唯一的解決方案。

  2. 要使Speak()異步運行,您需要在調用它時包含SPF_ASYNC標誌。如果您需要在異步演講結束後進行檢測,你可以使用ISpVoice::WaitUntilDone(),或撥打ISpVoice::SpeakCompleteEvent()並通過返回HANDLEWaitFor...()家庭的功能之一,像WaitForSingleObject().

  3. 做其他網站談論什麼樣的泄漏?

  4. 不是,不是。 OleCheck()僅檢查HRESULT值的值,如果它是錯誤值則引發異常。您仍然必須調用COM函數,該函數首先返回實際的HRESULT值。如果有的話,OleCheck()將替代SUCCEEDED()

對於您正在嘗試什麼,我建議以下辦法來代替:

struct s8087CW 
{ 
    Word Saved8087CW; 

    s8087CW(Word NewCW) 
    { 
     Saved8087CW = Default8087CW; 
     Set8087CW(NewCW); 
     // alternatively, the VCL documentation says to use SetExceptionMask() instead of Set8087CW() directly... 
    } 

    ~s8087CW() 
    { 
     Set8087CW(Saved8087CW); 
    } 
}; 

//--------------------------------------------------------------------------- 
__fastcall TForm1::TForm1(TComponent *Owner) 
    : TForm(Owner) 
{ 
    ::CoInitialize(NULL); 
} 

//--------------------------------------------------------------------------- 
__fastcall TForm1::~TForm1() 
{ 
    if (pVoice) pVoice->Release(); 
    ::CoUninitialize(); 
} 

//--------------------------------------------------------------------------- 
void __fastcall TForm1::FormKeyUp(TObject *Sender, WORD &Key, TShiftState Shift) 
{ 
    if (Key == VK_F11) 
    { 
     TWinControl *Ctrl = Screen->ActiveControl; 
     if (Ctrl) 
     { 
      TButton *btn; 
      TEdit *edit; 

      if ((btn = dynamic_cast<TButton*>(Ctrl)) != NULL) 
       SpeakText("Button, " + btn->Caption); 

      else if ((edit = dynamic_cast<TEdit*>(Ctrl)) != NULL) 
       SpeakText("Edit box, " + edit->Text); 
     } 
    } 
} 

//--------------------------------------------------------------------------- 
ISpVoice* pVoice = NULL; 

bool __fastcall TForm1::SpeakText(const String &Text) 
{ 
    s8087CW cw(0x133f); 

    if (!pVoice) 
    { 
     if (FAILED(CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice))) 
      return false; 
    } 

    SPVOICESTATUS stat; 
    pVoice->GetStatus(&stat, NULL); 
    while (stat.dwRunningState == SPRS_IS_SPEAKING) 
    { 
     ULONG skipped; 
     pVoice->Skip(L"SENTENCE", 1000, &skipped); 
     pVoice->GetStatus(&stat, NULL); 
    } 

    return SUCCEEDED(pVoice->Speak(WideString(Text).c_bstr(), SPF_ASYNC, NULL)); 
} 
+1

這是非常優雅的解決方案,做得好,比我想出的要好得多!我喜歡'8087CW'結構解決方案。小修正 - 結構名稱以數字開頭。否則完美! – Coder12345 2013-03-07 01:04:07

+1

我改變了結構名稱。 – 2013-03-07 01:13:21