Discovered key to the problem/crash, see the bottom of the post.
When creating instances of ISpVoice
using CoCreateInstance
, it seems like instances after the first one cannot speak as soon as the first one has spoken. That is, if pVoice
speaks first, pVoice2
will not speak. If pVoice2
speaks first, pVoice
will not speak. The order of creation/allocation does not seem to matter.
When I say "will not speak", I mean: calls to ISpVoice::Speak
return immediately with the result S_OK
; no voice was synthesized.
Correction: the above happens in the D version when the debugger is not attached. When the Visual Studio debugger is attached, an access violation occurs at vtjpnsapi50.dll!10004e65
. Also, the access violation happens in the C++ version regardless of whether a debugger is attached or debug information is generated.
Inserting a call to sleep
inbetween the calls to Speak does not change anything (except, of course, that it inserts a delay).
Reproduction in D (C++ equivalent is below):
import std.c.windows.com;
import core.sys.windows.windows;
import speech.windows.sapi;
import std.stdio;
int main()
{
if (FAILED(CoInitialize(null)))
return 1;
scope(exit) CoUninitialize();
ISpVoice pVoice;
HRESULT hr = CoCreateInstance(&CLSID_SpVoice, null, CLSCTX_ALL, &IID_ISpVoice, cast(void**)&pVoice);
assert(hr == S_OK);
hr = pVoice.Speak("Hello world", 0, null); // This speaks fine
assert(hr == S_OK);
ISpVoice pVoice2;
hr = CoCreateInstance(&CLSID_SpVoice, null, CLSCTX_ALL, &IID_ISpVoice, cast(void**)&pVoice2);
assert(hr == S_OK);
hr = pVoice2.Speak("hello again", 0, null); // This returns immediately
assert(hr == S_OK); // Yet it still returns S_OK
hr = pVoice.Speak("first voice again", 0, null); // This speaks fine too, immediately after "hello world" finishes
assert(hr == S_OK);
// The two objects are indeed at different memory addresses
writefln("voice 1: %s, voice 2: %s", cast(void*)pVoice, cast(void*)pVoice2);
pVoice.Release();
pVoice = null;
pVoice2.Release();
pVoice2 = null;
return 0;
}
This is the equivalent C++ program:
#include <sapi.h>
#include<Windows.h>
int main()
{
if (FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))
return 1;
ISpVoice* pVoice;
HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void**)&pVoice);
hr = pVoice->Speak(L"Hello world", 0, NULL); // This speaks fine
ISpVoice* pVoice2;
hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void**)&pVoice2);
hr = pVoice2->Speak(L"hello again", 0, NULL); // This causes an access violation
hr = pVoice->Speak(L"first voice again", 0, NULL);
pVoice->Release();
pVoice = NULL;
pVoice2->Release();
pVoice2 = NULL;
CoUninitialize();
return 0;
}
In the above example, both voices are allocated with the same parameters, and the first voice to speak works properly. In the D version when a debugger is not attached, the second call to Speak
on pVoice
also works.
If anyone has any idea what could cause this, or know about any open source software where multiple voice objects are used, please let me know, thanks!
edit This only happens with NeoSpeech's voices. It works fine with Microsoft's and eSpeak's voices. I'd still like to know if there's anything I could do to remedy the problem (NeoSpeech has some really, really good voices...).