Search code examples
c++visual-studio-2012java-native-interfacesapi

C++ and Microsoft SAPI 5: How to list all available voices and select a voice


First of all, C++ is not my language (I'm a java programmer).

I've seen examples of Microsoft SAPI 5.1, and I need a way to print all the available voices in a machine. I've seen the Object token of SAPI, and I'm kinda confuse of how CComPtr is use to iterate through. Can anyone help me with this. Additionally, how can I pick a specific voice instead of using the default.

Based from the examples, you can get the default voice by invoking this codes:

ISpVoice * pVoice = NULL;

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

HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice);

So in short: How can I list all the voices available and how can I choose the voice?

//PS: I don't need to put it in a combo box (just like all examples that I've seen), I'm implementing this for a JNI dll library so an example of iteration is much preferred.


Solution

  • First off, CComPtr is a smart pointer abstraction over COM interfaces, not an enumerator.

    To list all the voices, you need to enumerate the tokens in the SPCAT_VOICES category.

    HRESULT hr = S_OK;
    CComPtr<ISpObjectTokenCategory> cpSpCategory = NULL; 
    if (SUCCEEDED(hr = SpGetCategoryFromId(SPCAT_VOICES, &cpSpCategory))) 
    { 
        CComPtr<IEnumSpObjectTokens> cpSpEnumTokens; 
        if (SUCCEEDED(hr = cpSpCategory->EnumTokens(NULL, NULL, &cpSpEnumTokens))) 
       { 
            CComPtr<ISpObjectToken> pSpTok;
            while (SUCCEEDED(hr = cpSpEnumTokens->Next(1,&pSpTok, NULL)))) 
            {
                // do something with the token here; for example, set the voice
                pVoice->SetVoice(pSpTok,FALSE); 
                // NOTE:  IEnumSpObjectTokens::Next will *overwrite* the pointer; must manually release
                pSpTok.Release(); 
            }
       } 
    } 
    

    If you want to pick a specific voice, you need to specify an attribute that's unique to that voice. EnumTokens allows you to specify required and optional attributes. The attributes are actually defined by the creator of the voice, but some common attributes are

    • Name (e.g., "Microsoft Anna")
    • Language (specified by a numeric locale ID)
    • Gender (male/female)
    • Vendor (name of company that created the voice)

    So to specify "Microsoft Anna" instead of any voice, you would use

    if (SUCCEEDED(hr = cpSpCategory->EnumTokens(L"Name=Microsoft Anna", NULL, &cpSpEnumTokens))) 
    

    instead of

    if (SUCCEEDED(hr = cpSpCategory->EnumTokens(NULL, NULL, &cpSpEnumTokens)))