how to change Text to Speech voice and how to insert characters into char array

559 views Asked by At

I need to change the voice of the Text To Speech engine. When a menu is selected (ID_SPEAK_PLAY), I get the text of an edit box and simply read it.

My situation can be solved in two ways :

  1. insert the XML code at the begining of ptrData without using strncat or other functions that involve creating other wchar_t* buffers (memory issues ). StringCchPrintf is not working.
  2. change the voice in some other way that i don't know.

Here is my code :

    case ID_SPEAK_PLAY:
             text_size = SendMessage(h_edit, WM_GETTEXTLENGTH, 0, 0);
             text_size += 100;
             ptrData = new wchar_t[text_size];
             SendMessage(h_edit, WM_GETTEXT, text_size, (LPARAM)ptrData);
             StringCchPrintf(ptrData, text_size, L"<voice required = \"Gender=Female;Age=Teen\"> %s", ptrData);
             pVoice->Speak(ptrData, SPF_ASYNC | SPF_IS_XML, NULL);
             delete [] ptrData;
             break;
1

There are 1 answers

3
Remy Lebeau On BEST ANSWER

StringCchPrintf is not working.

That is because you ignored the warning in the documentation:

Behavior is undefined if the strings pointed to by pszDest, pszFormat, or any argument strings overlap.

You are specifying ptrData as both pszDest and an argument string, so your code has undefined behavior. You must use separate buffers when using StringCchPrintf():

case ID_SPEAK_PLAY:
         text_size = SendMessage(h_edit, WM_GETTEXTLENGTHW, 0, 0) + 1;
         ptrData = new wchar_t[text_size];
         SendMessage(h_edit, WM_GETTEXTW, text_size, (LPARAM)ptrData);
         speak_size = text_size + 100;
         speakData = new wchar_t[speak_size];
         StringCchPrintf(speakData, speak_size, L"<voice required = \"Gender=Female;Age=Teen\"> %s", ptrData);
         pVoice->Speak(speakData, SPF_ASYNC | SPF_IS_XML, NULL);
         delete [] speakData;
         delete [] ptrData;
         break;

Alternatively, just skip StringCchPrintf() and let WM_GETTEXT populate your single buffer directly:

case ID_SPEAK_PLAY:
{
         const wchar_t *xml = L"<voice required = \"Gender=Female;Age=Teen\"> ";
         const int xml_size = lstrlenW(xml);
         text_size = SendMessage(h_edit, WM_GETTEXTLENGTHW, 0, 0);
         ptrData = new wchar_t[text_size + xml_size + 1];
         lstrcpyW(ptrData, xml);
         SendMessage(h_edit, WM_GETTEXTW, text_size+1, (LPARAM)(ptrData+xml_size));
         pVoice->Speak(ptrData, SPF_ASYNC | SPF_IS_XML, NULL);
         delete [] ptrData;
         break;
}

change the voice in some other way that i don't know.

Instead of inserting XML in front of your text, you can call the ISpVoice::SetVoice() method before calling ISpVoice::Speak(). Use SpEnumTokens() to know which voices are installed, or use SpFindBestToken() to search for a voice that matches the criteria you need.