XAudio2 - Source voice hangs when active audio device gets removed - c++

I have a problem I am not able to solve. My application should be able to switch the default audio device during runtime. To achieve this I am using XAudio2 from the DirectXTK.
I implemented the IMMNotificationClient into my audio class to be able to react on default device changes.
For instance when the default device changes I stop my current source voice, reset the audio engine and start the source voice again. Everything works as expected.
However when my default device is a USB soundcard and I unplug it during the source voice is playing the application freezes.
The reason for this is that the source voice hangs when stopping the voice. Sometimes also when flushing the source buffer. Seems like the source voice could not be stopped anymore when the audio device was removed the source voice was using.
Does someone had the same problem and was able to solve this?
Here's the function I am using to reset the audio engine.
HRESULT DX::XAudioEngine::ResetAudioDevice()
{
HRESULT hr = S_OK;
this->m_retryAudio = TRUE;
if (SUCCEEDED(hr) && m_pSourceVoice)
{
hr = m_pSourceVoice->Stop();
}
if (SUCCEEDED(hr))
{
hr = m_pSourceVoice->FlushSourceBuffers();
}
if (m_audEngine && m_pSourceVoice)
{
// Get the source voice back from the smart pointer
IXAudio2SourceVoice* ptmpSrcVoice = nullptr;
ptmpSrcVoice = m_pSourceVoice.release();
// Destroy the voice
ptmpSrcVoice->DestroyVoice();
}
m_audEngine->Reset(&m_waveFormat, NULL);
// Create source voice
IXAudio2SourceVoice* ptmpSrcVoice = nullptr;
m_audEngine->AllocateVoice(
(WAVEFORMATEX*)&m_waveFormat,
SoundEffectInstance_Default,
false,
&ptmpSrcVoice
);
// Add source voice to smart pointer
m_pSourceVoice.reset(ptmpSrcVoice);
// Set the input volume
if (this->m_inputVolume != 1.0f) {
hr = m_pSourceVoice->SetVolume(this->m_inputVolume);
}
hr = m_pSourceVoice->Start(0);
this->m_retryAudio = FALSE;
return hr;
}

Related

DirectX 11 GetDisplayModeList() fails in Remote Desktop Connection

Good afternoon,
I have a barebone Direct3D App that works on a host PC, but fails to initialize DirectX while running via remote desktop.
I traced the failure to this call, where it fails with
result = adapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &numModes, NULL);
if(FAILED(result))
{
return false;
}
It fails with:
result = 0x887a0022 : A resource is not available at the time of the call, but may become available later.
The full initialization code is from Rastertek tutorials, found here:
http://www.rastertek.com/dx11tut03.html
Does anyone know a workaround for this problem?
Remote Desktop involves some corner-cases, and keep in mind it's sometimes using the 'Microsoft Basic Renderer' (a.k.a. the software WARP driver). See this blog post.
You can also guard your use of GetDisplayModeList in the remote scenario by detecting it in the first place. For example, the legacy DXUT sample framework did this in it's enumeration code:
// mode for the current screen resolution for the remote session.
if( 0 != GetSystemMetrics( SM_REMOTESESSION) )
{
DEVMODE DevMode;
DevMode.dmSize = sizeof( DEVMODE );
if( EnumDisplaySettings( nullptr, ENUM_CURRENT_SETTINGS, &DevMode ) )
{
NumModes = 1;
pDesc[0].Width = DevMode.dmPelsWidth;
pDesc[0].Height = DevMode.dmPelsHeight;
pDesc[0].Format = DXGI_FORMAT_R8G8B8A8_UNORM;
pDesc[0].RefreshRate.Numerator = 0;
pDesc[0].RefreshRate.Denominator = 0;
pDesc[0].ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE;
pDesc[0].Scaling = DXGI_MODE_SCALING_CENTERED;
}
}
You also can't use 'full-screen exclusive' mode in remote desktop:
if( GetSystemMetrics(SM_REMOTESESSION) != 0 )
{
sd.Windowed = TRUE;
}
You don't really need to use GetDisplayModeList at all. Just pick a reasonable starting size or start your window 'maximized'. See the directx-vs-templates for an approach that just uses the 'native resolution' of the desktop for both windowed and 'fake full screen'. It also all works well for remote desktop.
Another 'corner-case' with remote desktop is "raw input" for mouse. See the implementation of Mouse from the DirectX Tool Kit.
Not technically a solution, but the problem was in refresh rate initialization, bypassing this with a try{}-catch{} block allowed me to run with a default refresh rate via remote desktop. Everything else initialized without issues

Why does XAudio2 play .wav files only when the system is paused?

I've followed the tutorial along Microsoft's website, and created my own SoundEngine and Sound class structure to have the code abstracted away in main, however whenever I make a call such as batmanWav->play(), it will only play the audio if I write std::cin.get() or system("PAUSE")
Why is this? I will be trying to port this to a game that's already been worked on, but I obviously don't want the game to stop every time a sound is played.
EDIT: I was asked to show some code which has the issues
int main()
{
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
Sound batman("batman.wav");
Sound alien("alien.wav");
alien.play();
batman.play();
system("PAUSE");
CoUninitialize();
}
Sound.cpp
HRESULT Sound::play()
{
HRESULT hr = S_OK;
if (FAILED(hr = pSourceVoice->Start(0)))
return hr;
return hr;
}
For each Sound object I initialized a source voice, and each one refers to the same mastering voice and IXAudio2 *pXAudio2 object. The code I used to load wave file data was taken straight off MSDN docs.
As someone already noted, the problem is that your application exits before the sound really starts playing. XAudio2 is a non-blocking API, which means that you have to keep the memory live and the audio graph active until the sounds complete playing.
In other words, when you called IXAudio2SourceVoice::Start, nothing happens except that it logs that you want to start the audio. Another thread which was created as part of your IXAudio2 object then sees the request and begins to process the playback request. By that point, your application has exited and the process terminated unless you 'pause'.
Try something like the following from XAudio2BasicSound:
hr = pSourceVoice->Start( 0 );
// Let the sound play
BOOL isRunning = TRUE;
while( SUCCEEDED( hr ) && isRunning )
{
XAUDIO2_VOICE_STATE state;
pSourceVoice->GetState( &state );
isRunning = ( state.BuffersQueued > 0 ) != 0;
Sleep( 10 );
}
You should look at these samples and at DirectX Tool Kit for Audio.

DIrectshow function is blocked at system bootup

I have an Directshow based mediaplayer application . It works very well without any issues during normal playabck . But occasionally i am facing one issue when the Mediaplayer started just after system boot .
HRESULT CSDirectShow::RenderOutputPins (IBaseFilter* pFilter)
{
const char* funcName = "CSDirectShow::RenderOutputPins()";
HRESULT hr = S_OK;
// Enumerate all pins on the source filter,
// looking for the output pins so that I can call Render() on them
//
CComPtr< IEnumPins > pEnumPin;
if (!FAILED (pFilter->EnumPins (&pEnumPin)))
{
while (true)
{
// get the next pin
//
CComPtr< IPin > pPin;
if (pEnumPin->Next (1L, &pPin, NULL) != S_OK) break;
// I'm not interested in connected pins
// if this pin is an unconnected output pin, then render it.
//
CComPtr< IPin > pConnectedPin;
if (pPin->ConnectedTo (&pConnectedPin) == VFW_E_NOT_CONNECTED)
{
PIN_DIRECTION pinDirection;
PIN_INFO pinInfo;
//Get the information of the pin
if (pPin->QueryDirection (&pinDirection) == S_OK
&& pinDirection == PINDIR_OUTPUT
&& pPin->QueryPinInfo(&pinInfo) == S_OK
&& strstr((char*)pinInfo.achName,"~")==NULL)
{
if (FAILED (hr = m_pGB->Render (pPin)))
{
SafeRelease(&pinInfo.pFilter);
return hr;
}
}
SafeRelease(&pinInfo.pFilter);
}
}
}
TraceMsg ("%s: exit",funcName);
return S_OK;
}
When m_pGB->Render (pPin) is called ,This function never returns and it is blocked inside .I confirmed using logs .This issue happens only when i start my application immediately after bootup . When issues occures if I close and restart my application it works like a charm .Since application is designed to start automatically after system bootup this behaviour has become a bigger concern .Kindly help
IGraphBuilder.Render call does a lot internally, and specifically it goes over enumeration of potentially suitable filter, which in turn attempts to load additional DLLs registered with DirectShow environment. Such file could have missing dependencies, or dependencies on remote or temporarily inaccessible drivers (just one example).
If you experience a deadlock, you can troubleshoot it further (debug it) and get details on locked state, and on activity during Render call.
If the problem is caused by third party filters (esp. codec packs registering a collection of filters at once without thinking too much on compatibility) registered with the system in a not so good way, perhaps you could identify them and uninstall.
If you want to improve the player on your side, you should avoid Render call, and build your filter graph with smaller increments - adding specific filter and connecting pins, without leaving big tasks at mercy of Intelligent Connect, which works well in general but is sensitive to compatibility problems as mentioned above.

Able to render video to MUX in GraphEdit but get VFW_E_CANNOT_CONNECT in code

I am trying to get the 3ivxfilters working in my C++ Directshow application and it continually fails to connect the 3IVX Video Encoder output pin to the 3IVX Media Muxer input pin. I always get the error VFW_E_CANNOT_CONNECT.
All filters have been added to the graph by enumerating the monikers so there shouldn't be any issue due to adding using a CLSID directly.
When I open the graph via graph edit and right click / choose render on the Video Encoder output pin it works fine.
Here is my code for connecting the filter:
HRESULT ConnectFilters(IGraphBuilder *pGraph, IBaseFilter *pSrc, IBaseFilter *pDest)
{
IPin *pOut = NULL;
// Find an output pin on the first filter.
HRESULT hr = FindUnconnectedPin(pSrc, PINDIR_OUTPUT, &pOut);
if (SUCCEEDED(hr))
{
hr = ConnectFilters(pGraph, pOut, pDest);
pOut->Release();
}
return hr;
}
Basically once it finds the appropriate pins it uses the Connect method.
hr = pGraph->Connect(pOut, pIn);

DirectX pEnum->Next(1,&pPin,NULL) returns S_FALSE

I have built filters for USB TV TUNER in graph edit and its working fine in graphedit.
When I converted it to C++ code it seems ok for TV tuner Filter as I get pins connected by calling pEnum->Next(1,&pPin,NULL) but for USB TV Audio interface i am unable to find pins as i always get S_FALSE for call to pEnum->Next(1,&pPin,NULL) .
I have seen posts in different web sites telling that one needs to use enumerations but no example is given.
Also it is not understandable to me why tuners filter pins are accessible on same device and other's not.
Here is my code. please tell me how to change it for enumeration.
HRESULT hr=S_OK;
CComPtr<ICaptureGraphBuilder2> pBuilder;
hr=pBuilder.CoCreateInstance(CLSID_CaptureGraphBuilder2);
hr=pBuilder->SetFiltergraph(pGraph);
//now add tuner
CComPtr<IBaseFilter> pGadmeiAnalogTvTuner0001;
hr=pGadmeiAnalogTvTuner0001.CoCreateInstance(CLSID_GadmeiAnalogTvTuner0001);
if(hr<0)
return -1;
hr=pGraph->AddFilter(pGadmeiAnalogTvTuner0001,L"Gadmei Analog TvTuner 0001");
if(hr!=S_OK)
return -1;
printf("added tuner to graph \n");
//now add tv audio
CComPtr<IBaseFilter> pGadmeiAnalogTvAudiof;
hr=pGadmeiAnalogTvAudiof.CoCreateInstance(CLSID_GadmeiAnalogTvAudio);
if(hr<0)
return -1;
hr=pGraph->AddFilter(pGadmeiAnalogTvAudiof,L"GadmeiAnalogTvAudio");
if(hr!=S_OK)
return -1;
printf("added Tv Audio to graph \n");
//connect tv tuner and audio
hr=pGraph->ConnectDirect(GetPin(pGadmeiAnalogTvTuner0001,L"Analog Audio"), GetPin(pGadmeiAnalogTvAudiof,L"TVAudio In"),NULL);
if(hr!=S_OK)
printf("Can't Connect \n");
Capture filters are created through enumerator, where they are instantiated in proper context. CoCreateInstance through CLSID gives you a wrapper object without binding it to the hardware of interest, then you you have the typical symptom of valid inteface pointer, with however no pins available.