Subsequent SpVoice instances are silent - c++

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...).

Related

Windows 10 COM crash under loader lock

I stumbled across a Windows 10 crash that's probably new since the Windows 10 update to 2004.
The problem is a crash in ntdll when COM is starting. COM is needed to use Core Audio.
Code which triggers it:
IMMDeviceEnumerator* enumerator = nullptr;
CoInitializeEx(nullptr, COINIT_MULTITHREADED); // Also with STA
HRESULT hr = CoCreateInstance( CLSID_MMDeviceEnumerator, 0, CLSCTX_ALL, IID_IMMDeviceEnumerator,
(void**)&enumerator);
Popup:
Unhandled exception at 0x00007FFE7846D3D1 (ntdll.dll) in MicTest.exe:
A LIST_ENTRY has been corrupted (i.e. double remove).
Callstack:
ntdll.dll!LdrpInsertDataTableEntry()
ntdll.dll!LdrpMapDllWithSectionHandle()
ntdll.dll!LdrpMapDllNtFileName()
ntdll.dll!LdrpMapDllFullPath()
ntdll.dll!LdrpProcessWork()
ntdll.dll!LdrpLoadDllInternal()
ntdll.dll!LdrpLoadForwardedDll()
ntdll.dll!LdrpGetDelayloadExportDll()
ntdll.dll!LdrpHandleProtectedDelayload()
ntdll.dll!LdrResolveDelayLoadedAPI()
combase.dll!00007ffe769ab1c2()
combase.dll!00007ffe769c56fd()
combase.dll!00007ffe769b3b27()
ntdll.dll!RtlRunOnceExecuteOnce()
KernelBase.dll!InitOnceExecuteOnce()
combase.dll!00007ffe7696c11f()
combase.dll!00007ffe7696baf8()
combase.dll!00007ffe7696b9e6()
combase.dll!00007ffe76907df2()
combase.dll!00007ffe76906fce()
combase.dll!00007ffe76907928()
combase.dll!00007ffe76907718()
MicTest.exe!Microphone::Microphone() Line 23 C++
[External Code]
MicTest.exe!main(int argc, char * * argv) Line 67 C++
It's not 100% reproducible there, sometimes the crash is a few lines later in IAudioClient::Initialize when ntdll crashes under loader lock in
> ntdll.dll!LdrpInitializeThread()
ntdll.dll!_LdrpInitialize()
ntdll.dll!LdrpInitialize()
ntdll.dll!LdrInitializeThunk()
This happens in one of the new Windows 10 threadpool threads that Windows itself uses to load a DLL. The offending code shows up as AudioSes.dll!CAudioClient::CreateRemoteStream (Note: AudioSes.dll is Core Audio, symbols per the Microsoft Symbol Server.)
Is this a known problem? Is there a workaround?
For completeness, complete code:
Microphone::Microphone()
{
IMMDeviceEnumerator* enumerator = nullptr;
CoInitializeEx(nullptr, COINIT_MULTITHREADED); // crash near here
HRESULT hr = CoCreateInstance( CLSID_MMDeviceEnumerator, 0, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&enumerator);
if (!enumerator) throw std::system_error(hr, std::system_category());
IMMDevice* device = nullptr;
hr = enumerator->GetDefaultAudioEndpoint(eCapture, eConsole, &device);
enumerator->Release();
if (!device) throw std::system_error(hr, std::system_category());
hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&client);
device->Release();
if (!client) throw std::system_error(hr, std::system_category());
WAVEFORMATEXTENSIBLE *pwfx = nullptr;
hr = client->GetMixFormat(reinterpret_cast<WAVEFORMATEX**>(&pwfx));
if (SUCCEEDED(hr) && pwfx)
{
this->span.sampleRate = pwfx->Format.nSamplesPerSec;
this->blockAlign = pwfx->Format.nBlockAlign;
// In general, this is NOT pwfx->wBitsPerSample;
if (pwfx->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
{
this->format = fmt_FP32;
}
else if (pwfx->SubFormat == KSDATAFORMAT_SUBTYPE_PCM)
{
this->format = fmt_PCM;
}
else
{
// Can't deal with that format, and Core Audio ought to support FP32.
pwfx->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
this->format = fmt_FP32;
}
}
// or crash here
hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, 1000000, 0, &pwfx->Format, NULL);
if (!SUCCEEDED(hr))
{
throw std::system_error(hr, std::system_category());
}
CoTaskMemFree(pwfx);
// Start capture
hr = client->GetService( IID_IAudioCaptureClient, (void**)&capture);
client->Start();
As RbMm pointed out, this requires looking at the assembly. It turns out that Windows 10 2004 corrupts its PEB_LDR_DATA data structure. This is a per-process structure which contains important data. In particular, LdrpInitializeThread uses PEB_LDR_DATA::InLoadOrderModuleList directly after acquiring the Loader Lock. This can be a null pointer for unclear reasons.
Since this happens in a Windows-created thread in the default threadpool, it's unrelated to and unsynchronized with user code. The exact point of crash can vary - the damage to the PEB_LDR_DATA structure appears to be more extensive.
Workaround
While this is not a solution for the CoInitializeEx crash, it appears that the second crash due to PEB_LDR_DATA::InLoadOrderModuleList corruption can be avoided with minor changes:
Don't set a buffer size, but let Windows determine one and roll with that:
hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, 0, 0, &pwfx->Format, NULL);
UINT32 bufSize = 0;
hr = client->GetBufferSize(&bufSize);
Determine when to call GetBuffer
auto samplePollRatio = pwfx->Format.nSamplesPerSec / 2666;
auto nextPoll = std::chrono::system_clock::now() +
std::chrono::milliseconds(bufSize / samplePollRatio);
And wait for it
std::this_thread::sleep_until(nextPoll);
auto hr = capture->GetBuffer(&buf, &samples, &flags, nullptr, nullptr);
This is a reasonable pattern anyway. The MSDN example also has a Sleep call, but sleep_until is more robust against variable delays in processing. (E.g. on the first call you may have some more delay when opening a WAV file to save the results etc). But by delaying the first sample acquisition you bypass an apparent race condition in Windows 10.

the program runs less than 2 sec

I got this code from MSDN to change the Text to speech and I did all the sitting which were in the tutorial the code works without errors but when I run the code the code run for less than 2 second and then come back to the main code without read the code. my question here is what is the problem exactly?
this is the code:
#include "stdafx.h"
#include <sapi.h>
#include <initguid.h>
int main(int argc, char* argv[])
{
ISpVoice * pVoice = NULL;
if (FAILED(::CoInitialize(NULL)))
return FALSE;
HRESULT hr = CoCreateInstance(__uuidof(SpVoice), NULL, CLSCTX_ALL, __uuidof(SpVoice), (void **)&pVoice);
if( SUCCEEDED( hr ) )
{
hr = pVoice->Speak(L"Hello world", 0, NULL);
hr = pVoice->Speak(L"This sounds normal <pitch middle = '-10'/> but the pitch drops half way through", SPF_IS_XML, NULL );
pVoice->Release();
pVoice = NULL;
}
::CoUninitialize();
return TRUE;
}
Your call to CoCreateInstance() is wrong. You are passing the CLSID of the SpVoice CoClass where the IID of the ISpVoice interface is expected instead (so it should be failing with an E_NOINTERFACE error).
Try this instead:
CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice);
Alternatively:
CoCreateInstance(__uuidof(SpVoice), NULL, CLSCTX_ALL, IID_PPV_ARGS(&pVoice));

DShow Sample Code for playing video does not play the video MSDN C++

I was referring to the example program provided here to run a video file, in my case mp4 format using DShow.
Refer to the complete code:
#include <dshow.h>
#pragma comment (lib, "strmiids.lib")
void main(void)
{
IGraphBuilder *pGraph = NULL;
IMediaControl *pControl = NULL;
IMediaEvent *pEvent = NULL;
// Initialize the COM library.
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr))
{
printf("ERROR - Could not initialize COM library");
return;
}
// Create the filter graph manager and query for interfaces.
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **)&pGraph);
if (FAILED(hr))
{
printf("ERROR - Could not create the Filter Graph Manager.");
return;
}
hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
// Build the graph. IMPORTANT: Change this string to a file on your system.
hr = pGraph->RenderFile(L"C:\\Example.avi", NULL);
if (SUCCEEDED(hr))
{
// Run the graph.
hr = pControl->Run();
if (SUCCEEDED(hr))
{
// Wait for completion.
long evCode;
pEvent->WaitForCompletion(INFINITE, &evCode);
// Note: Do not use INFINITE in a real application, because it
// can block indefinitely.
}
}
pControl->Release();
pEvent->Release();
pGraph->Release();
CoUninitialize();
}
However when I build the program, the build is sucessful but when I run it, the console window pops up and disappears within a second.
I referred to the comments on the same page and many others faced the same issue. However a few were able to successfully run the program.
What is it that I am doing wrong?
Is it the type of project I am selecting? I am selecting win32 console application. Should I select something else? Or is there something else that I am doing wrong?

DeadLock in directShow, mutlti video resizing

1,I'd like to show 9 videos at same time.
2,I have a main thread which create all the windows and handle the WM_* message, also in charge of: A), init vmrvideorender9, MediaControl B), connect filter C), stop and release the MediaControl. and other things.
3,For every video, there is also a render thread which is for sample delivering.
Now, I have many deadlock, they hang at different place and CPU is very high.
The deadlock most happens when resizing the windows.
for example: when I call the following function, sometimes it can consume 40 seconds and sometimes it just hang inside the function.
Void GetVMR9VideoRender()
{
hr = CoCreateInstance(CLSID_VideoMixingRenderer9, NULL, CLSCTX_INPROC,
IID_IBaseFilter, (LPVOID *)&pRender.p);
if (FAILED(hr))
{
return NULL;
}
CComPtr <IVMRFilterConfig9> pConfig;
hr =pRender->QueryInterface(IID_IVMRFilterConfig9, (void**)&pConfig);
if (FAILED(hr))
return NULL;
pConfig->SetRenderingMode(VMRMode_Windowless);
pConfig->SetNumberOfStreams(1);
CComPtr<IVMRWindowlessControl9> lpDefWC;
hr = pRender->QueryInterface(IID_IVMRWindowlessControl9, (LPVOID*)&lpDefWC.p);
if (FAILED(hr))
{
return NULL;
}
m_lpDefWC = (IVMRWindowlessControl*)lpDefWC.p ;
CComPtr<IVMRAspectRatioControl9> lpARC;
hr = pRender->QueryInterface(IID_IVMRWindowlessControl9, (LPVOID*)&lpARC.p);
if (FAILED(hr))
{
return NULL;
}
m_lpARC = (IVMRAspectRatioControl*)lpARC.p ;
return pRender;
}

DirectShow dilemma - Not able to record

That's the actual main code, preview works fine:
int main()
{
HRESULT hr = CoInitialize(NULL);
ICaptureGraphBuilder2 *pBuild;
IGraphBuilder *pGraph;
IMoniker *pMoniker;
IMediaControl *pControl;
IMediaEvent *pEvent;
InitCaptureGraphBuilder(&pGraph, &pBuild);
hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
IBaseFilter *pCap; // Video capture filter
IEnumMoniker *pEnum;
hr = EnumerateDevices(CLSID_VideoInputDeviceCategory, &pEnum);
DisplayDeviceInformation(pEnum, &pMoniker);
hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pCap);
if (SUCCEEDED(hr))
{
hr = pGraph->AddFilter(pCap, L"Capture Filter");
}
hr = pBuild->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pCap, NULL, NULL);
hr = pControl->Run();
_getch();
pControl->Release();
pCap->Release();
pGraph->Release();
pBuild->Release();
CoUninitialize();
return 0;
}
Now, I know that for recording I need this piece of code:
IBaseFilter *pMux;
hr = pBuild->SetOutputFileName(&MEDIASUBTYPE_Avi, L"D:\\test.avi", &pMux, NULL);
hr = pBuild->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pCap, NULL, pMux);
If I replace this to the preview code, it actually create the AVI file (a very big one), but it's empty, no video.
I mean I'm replacing the:
hr = pBuild->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pCap, NULL, NULL);
code, with the one above.
What I'm doing wrong, or better, what I'm missing?
RenderStream is high level method that internally embeds other calls through public documented APIs, typically for the ease of use. While it looks simple, it's not so easy to troubleshoot in case something does not work well and as expected. Even harder to tell inspecting just code visually. It also is not the most efficient because there is something you can do yourself to get closer to the solution, which is:
Your further steps are along either of the two:
You take working sample code and compare to yours looking at differences and locating the source of the problem.
You inspect the resulting filter graph topology putting your graph onto ROT, and checking using GraphEdit or a similar tool to ensure the topology is matching your expectations.
You also certainly need to check HRESULT codes, what you already seem to be doing.