filoe / cscore

An advanced audio library, written in C#. Provides tons of features. From playing/recording audio to decoding/encoding audio streams/files to processing audio data in realtime (e.g. applying custom effects during playback, create visualizations,...). The possibilities are nearly unlimited.
Other
2.21k stars 458 forks source link

WASAPILoopback 7.1 issues #120

Closed njbmartin closed 8 years ago

njbmartin commented 8 years ago

I've stumbled on a really odd issue with a virtual 7.1 USB Headset. So, it reports itself as having 8 channels, which is technically correct. However, the odd issue is that it strangely requires admin rights for the Loopback to work with all 8 channels. Without admin rights, it throws this error:

Exception: Exception caught: 'CSCore.CoreAudioAPI.CoreAudioAPIException' in CSCore.dll ("IAudioClient::Initialize caused an error: 0x88890008, "Unknown HRESULT"."). Exception caught: 'CSCore.CoreAudioAPI.CoreAudioAPIException' in CSCore.dll ("IAudioClient::Initialize caused an error: 0x88890008, "Unknown HRESULT".") 14.06s [224] Main Thread

With admin rights, it works just fine.

So I modified the WasapiLoopbackCapture to have an overload which allows me to define the WaveFormat and thus decrease the channels to just 2 to see if that helped, which it did.

Here is the code I used (not an overload, but using an overload on the base):

public WasapiLoopbackCapture()
            : base(false, AudioClientShareMode.Shared, 10, new WaveFormatExtensible(MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Render, Role.Console).DeviceFormat.SampleRate, MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Render, Role.Console).DeviceFormat.BitsPerSample, MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Render, Role.Console).DeviceFormat.Channels, System.Guid.Empty))

Increased it to 7 channels out of curiosity, and worked fine again. So, while I was still curious, I tried this:

public WasapiLoopbackCapture()
            : base(false, AudioClientShareMode.Shared, 10, new WaveFormatExtensible(MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Render, Role.Console).DeviceFormat.SampleRate, MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Render, Role.Console).DeviceFormat.BitsPerSample, MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Render, Role.Console).DeviceFormat.Channels, System.Guid.Empty))

Which gives it all 8 channels again, but to my surprise, works absolutely fine!

Firstly, how?

Secondly, I'd therefore like to suggest adding in overload methods to match the base WasapiCapture, which would allow people to customise the loopback themselves.

filoe commented 8 years ago

How does your original code look like? Would be nice to know what the finalFormat here: https://github.com/filoe/cscore/blob/master/CSCore/SoundIn/WasapiCapture.cs#L523 and how it's getting there.

njbmartin commented 8 years ago

Well that certainly explains why it now works.

Before WasapiLoopbackCapture was amended:

finalResult: {ChannelsAvailable: 8|SampleRate: 44100|Bps: 1411200|BlockAlign: 32|BitsPerSample: 32|Encoding: Extensible|SubFormat: 00000003-0000-0010-8000-00aa00389b71|ChannelMask: SpeakerFrontLeft, SpeakerFrontRight, SpeakerFrontCenter, SpeakerLowFrequency, SpeakerBackLeft, SpeakerBackRight, SpeakerSideLeft, SpeakerSideRight}

After:

finalFormat: {ChannelsAvailable: 2|SampleRate: 44100|Bps: 352800|BlockAlign: 8|BitsPerSample: 32|Encoding: Extensible|SubFormat: 00000003-0000-0010-8000-00aa00389b71|ChannelMask: SpeakerFrontLeft, SpeakerFrontRight}

njbmartin commented 8 years ago

As for the issue with admin rights, well... here's the waveFormat from the device itself while in admin mode:

{ChannelsAvailable: 2|SampleRate: 44100|Bps: 176400|BlockAlign: 4|BitsPerSample: 16|Encoding: Extensible|SubFormat: 00000001-0000-0010-8000-00aa00389b71|ChannelMask: SpeakerFrontLeft, SpeakerFrontRight}

and when it's run as a normal user:

{ChannelsAvailable: 8|SampleRate: 44100|Bps: 1411200|BlockAlign: 32|BitsPerSample: 32|Encoding: Extensible|SubFormat: 00000003-0000-0010-8000-00aa00389b71|ChannelMask: SpeakerFrontLeft, SpeakerFrontRight, SpeakerFrontCenter, SpeakerLowFrequency, SpeakerBackLeft, SpeakerBackRight, SpeakerSideLeft, SpeakerSideRight}

I'm definitely confused by that, but to me it points to the issue being at the driver level.

filoe commented 8 years ago

It is definitely confusing. The finalFormat gets always choosen by the driver (or at least the driver confirms that this format is supported). Later on the Initialize method of the AudioClient reports that the format is not supported. I have to think about this issue but I don't see a clear solution yet.

njbmartin commented 8 years ago

As you've suggested, it's most likely an issue with the Headset's driver, but I think it's definitely worth adding more overloads for WasapiLoopbackCapture to match better match WasapiCapture to allow for more customisation of the capture.

njbmartin commented 8 years ago

Ok, so I've spoken to the manufacturer who has given me a detailed reason as to why the headset would have issues.

Essentially it boils down to the fact that the headset is really just a stereo headset, the driver manipulates the system into thinking it's a 7.1 and it converts it into virtual surround.

So the remedy here would be to catch the exception and retry using stereo channels, and adding the overload methods as discussed would therefore allow the developer to implement it in their own code.