NAudio
NAudio copied to clipboard
Hang when disposing a WaveOut after the playback device has been removed
I have a WaveOut object which uses a device attached to the system. When I'm finished with this WaveOut I can call .Dispose() and all is well. If the device associated with this WaveOut is removed from the system before I call Dispose(), the Dispose call hangs indefinitely.
I can reproduce this by starting SteamVR (which adds an audio device to the system) and loading a wave file into a WaveOut to be played by this device. If I shut down SteamVR (thereby removing that device), calling Dispose() just hangs.
Is there something I'm doing wrong here? In this case, do I even need to call Dispose?
I am working with mrbelowski and I'll just add a bit of info: it looks like .Dispose is blocked on another call. When device is removed, we try to re-enumerate devices:
1 ntdll.dll!NtWaitForSingleObject() 2 KernelBase.dll!WaitForSingleObjectEx() 3 wdmaud.drv!wodMessage() 4 winmmbase.dll!waveOutDesertHandle(struct HWAVEOUT__ *) 5 winmmbase.dll!CleanUpHandles(unsigned int,unsigned short const *) 6 winmmbase.dll!wdmDrvInstall() 7 winmmbase.dll!wdmDriverLoadClass() 8 winmmbase.dll!wdmDriverLoadAllClasses() 9 winmmbase.dll!wdmPnpUpdateDriver() 10 winmmbase.dll!ClientPnpChange() 11 winmmbase.dll!ClientUpdatePnpInfo() 12 winmmbase.dll!waveOutGetNumDevs() 13 [External Code] 14 CrewChiefV4.exe!CrewChiefV4.Audio.AudioPlayer.NotificationClientImplementation.OnDefaultDeviceChanged(NAudio.CoreAudioApi.DataFlow dataFlow, NAudio.CoreAudioApi.Role deviceRole, string defaultDeviceId) 15 [External Code] 16 MMDevAPI.dll!CDeviceEnumerator::OnDefaultDeviceChangedForPolicy(enum __MIDL___MIDL_itf_mmdeviceapi_0000_0000_0001,enum __MIDL___MIDL_itf_mmdeviceapip_0000_0000_0001,unsigned short const *) 17 MMDevAPI.dll!CLocalEndpointEnumerator::OnMediaNotification() 18 MMDevAPI.dll!CMediaNotifications::OnMediaNotificationWorkerHandler() 19 ntdll.dll!TppSimplepExecuteCallback() 20 ntdll.dll!TppWorkerThread() 21 kernel32.dll!BaseThreadInitThunk() 22 ntdll.dll!RtlUserThreadStart()
Eventually, on shutdown .Dispose is blocked on this. Why would enumeration hang? Are we required to Dispose all WaveOuts associated with this device on device disconnect?
Attaching stacks. stacks.txt
What callback model were you using? The Function callback model has a lot of problems with deadlocks. But otherwise it could just be a problem with the underlying drivers for that audio device
Mark, I am not 100% sure what do you mean by callback model, but we do use IMMNotificationClient to get notifications about device changes. Are you saying this way is not thread safe?
I searched through readme on this repo, but I've not found any info on threading considerations. Are there any threading rules clients have to follow to be thread safe (for example, is it a requirement to Dispose WaveOut on the same thread that initialized it?) WMP interface in .NET audio, for example, requires that all playback happens on the UI thread. Anything similar is required with nAudio objects?
To close this it was bug in our app - we mistakely mixed WaveOut and WaveOutEvent. Thanks for your help!
Hello, I'm having an issue similar to this one. When connecting and then disconnecting with Remote Desktop, the underlying audio device the waveOutEvent was initialized to cannot be disposed anymore and stops working. It looks like when the Remote Desktop connects it creates an output audio device that is then removed when disconnecting. Sometimes the application completely crashes, without raising any exception or system log; the waveOutEvent is not working anymore after reconnecting, and it hangs completely if I try to dispose it. I've also tried with the directSoundOut, with the same results. Are there any workarounds to this kind of issues? Thank you very much.
I was having similar hanging code when disposing Wave*** classes (we tried both Wave*** and WaveEvent). The issue was resolved for my project by using the Synchronization context in our application. It turned out that the IMMNotificationClient is raising the notification on a different thread than the one, in which the Wave classes were created.