3

C ++ Builderの視覚障害者用のText-To-Speechアプリケーションコントロールには、次のコードを使用します(Delphiでも同様の例を使用できる可能性があります)。メインフォームのKeyPreviewプロパティがチェックされ、キーF11プレビューがアクティブな(フォーカスされた)コントロールの発話を開始できるようになっています。コードはそのままで動作しますが、いくつか問題があります。この例はC++Builderコードですが、私が見つけたものから、Delphiは同じ問題に苦しんでおり、私が見つけた解決策は同じです。Delphiソリューションをお持ちの場合は、お気軽に投稿してください。とにかく似ています。

#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->Speakは浮動小数点のゼロ除算Set8087CWを引き起こします。これは、Windows 7(おそらくVistaとWindows 8も)でのみ発生し、同じプログラム(コンパイル済みexe)のWindowsXPでは発生しません。使用せずに解決策はありSet8087CWますか?これらの行を削除すると、問題と例外が発生します。私はBCB2010を持っています。

  2. 関数は同期しており、テキストの読み取りが完了するまで、シャットダウンしたり、制御をプログラムに戻したりすることはありません。これは、長いテキストの場合の問題です。また、プログラムイベントをブロックします。非同期にする方法や、F11キーのステータスを定期的にチェックするイベントを導入する方法はありますか?F11をもう一度押すと、読み取りが停止し、オブジェクトの初期化が解除されますか?たとえば、300ミリ秒ごと(または各単語の後など)にF11キーを押してポーリングし、押された場合は話すのをやめますか?またはスレッドで実行しますか?

  3. SAPIには、さまざまなサイトに書き込むときにメモリリークが発生しますか?

  4. 上記のコードはとOleCheckの代わりに使用できますか?CoCreateInstanceCoUninitialize

Remy Lebeauによって提案された解決策を探している人のための更新:

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

4

1 に答える 1

4
  1. このエラーはVistaでも発生します。浮動小数点例外のマスキングが唯一の解決策です。

  2. Speak()非同期で実行するには、呼び出すときにフラグを含める必要がありますSPF_ASYNC。非同期スピーキングが終了したことを検出する必要がある場合は、を使用するISpVoice::WaitUntilDone()か、呼び出して、次のような関数ファミリーの1つにISpVoice::SpeakCompleteEvent()返されたものを渡すことができます。HANDLEWaitFor...()WaitForSingleObject().

  3. 他のサイトはどのようなリークについて話しているのですか?

  4. 代わりにではなく、いいえ。 OleCheck()値の値をチェックしHRESULT、エラー値の場合は例外をスローするだけです。HRESULTそもそも実際の値を返すCOM関数を呼​​び出す必要があります。どちらかといえば、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));
}
于 2013-03-06T23:04:02.853 に答える