From 2a0cc058e73fa86b4f173d686e2f5067c2e1f6d7 Mon Sep 17 00:00:00 2001 From: Ondrej Patrovic Date: Mon, 21 Nov 2016 20:06:38 -0500 Subject: [PATCH] Fix disappearing audio with DS4 wireless dongle --- DS4Windows/DS4Library/DS4Audio.cs | 244 +++++++++++++++++++++++++++++ DS4Windows/DS4Library/DS4Device.cs | 3 + DS4Windows/DS4Windows.csproj | 2 + 3 files changed, 249 insertions(+) create mode 100644 DS4Windows/DS4Library/DS4Audio.cs diff --git a/DS4Windows/DS4Library/DS4Audio.cs b/DS4Windows/DS4Library/DS4Audio.cs new file mode 100644 index 0000000..74ba536 --- /dev/null +++ b/DS4Windows/DS4Library/DS4Audio.cs @@ -0,0 +1,244 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using DS4Windows.DS4Library.CoreAudio; + +namespace DS4Windows.DS4Library +{ + public class DS4Audio + { + private IAudioEndpointVolume endpointVolume; + + private static Guid IID_IAudioEndpointVolume = new Guid("5CDF2C82-841E-4546-9722-0CF74078229A"); + private static readonly PropertyKey PKEY_Device_FriendlyName = + new PropertyKey(new Guid(unchecked((int)0xa45c254e), unchecked((short)0xdf1c), 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0), 14); + + public uint Volume + { + get + { + float pfLevel = 0; + + if (endpointVolume != null) + endpointVolume.GetMasterVolumeLevelScalar(out pfLevel); + + return Convert.ToUInt32(pfLevel * 100); + } + } + + public DS4Audio() + { + var audioEnumerator = new MMDeviceEnumeratorComObject() as IMMDeviceEnumerator; + IMMDeviceCollection audioDevices; + audioEnumerator.EnumAudioEndpoints(DataFlow.Render, DeviceState.Active, out audioDevices); + + int numAudioDevices; + Marshal.ThrowExceptionForHR(audioDevices.GetCount(out numAudioDevices)); + + for (int deviceNumber = 0; deviceNumber < numAudioDevices; ++deviceNumber) + { + IMMDevice audioDevice; + Marshal.ThrowExceptionForHR(audioDevices.Item(deviceNumber, out audioDevice)); + string deviceName = GetAudioDeviceName(ref audioDevice); + + if (deviceName.Contains("DUALSHOCK®4 USB Wireless Adaptor")) + { + object interfacePointer; + Marshal.ThrowExceptionForHR(audioDevice.Activate(ref IID_IAudioEndpointVolume, ClsCtx.ALL, IntPtr.Zero, out interfacePointer)); + endpointVolume = interfacePointer as IAudioEndpointVolume; + } + + Marshal.ReleaseComObject(audioDevice); + } + + Marshal.ReleaseComObject(audioDevices); + Marshal.ReleaseComObject(audioEnumerator); + } + + ~DS4Audio() + { + if (endpointVolume != null) + { + Marshal.ReleaseComObject(endpointVolume); + endpointVolume = null; + } + } + + private string GetAudioDeviceName(ref IMMDevice audioDevice) + { + IPropertyStore propertyStore; + Marshal.ThrowExceptionForHR(audioDevice.OpenPropertyStore(StorageAccessMode.Read, out propertyStore)); + + int numProperties; + Marshal.ThrowExceptionForHR(propertyStore.GetCount(out numProperties)); + + string deviceName = String.Empty; + + for (int propertyNum = 0; propertyNum < numProperties; ++propertyNum) + { + PropertyKey propertyKey; + Marshal.ThrowExceptionForHR(propertyStore.GetAt(propertyNum, out propertyKey)); + + if ((propertyKey.formatId == PKEY_Device_FriendlyName.formatId) && (propertyKey.propertyId == PKEY_Device_FriendlyName.propertyId)) + { + PropVariant propertyValue; + Marshal.ThrowExceptionForHR(propertyStore.GetValue(ref propertyKey, out propertyValue)); + deviceName = Marshal.PtrToStringUni(propertyValue.pointerValue); + break; + } + } + + Marshal.ReleaseComObject(propertyStore); + return deviceName; + } + } +} + +namespace DS4Windows.DS4Library.CoreAudio +{ + public enum DataFlow + { + Render, + Capture, + All + }; + + [Flags] + public enum DeviceState + { + Active = 0x00000001, + Disabled = 0x00000002, + NotPresent = 0x00000004, + Unplugged = 0x00000008, + All = 0x0000000F + } + + enum StorageAccessMode + { + Read, + Write, + ReadWrite + } + + [Flags] + public enum ClsCtx + { + INPROC_SERVER = 0x1, + INPROC_HANDLER = 0x2, + LOCAL_SERVER = 0x4, + INPROC_SERVER16 = 0x8, + REMOTE_SERVER = 0x10, + INPROC_HANDLER16 = 0x20, + NO_CODE_DOWNLOAD = 0x400, + NO_CUSTOM_MARSHAL = 0x1000, + ENABLE_CODE_DOWNLOAD = 0x2000, + NO_FAILURE_LOG = 0x4000, + DISABLE_AAA = 0x8000, + ENABLE_AAA = 0x10000, + FROM_DEFAULT_CONTEXT = 0x20000, + ACTIVATE_32_BIT_SERVER = 0x40000, + ACTIVATE_64_BIT_SERVER = 0x80000, + ENABLE_CLOAKING = 0x100000, + PS_DLL = unchecked((int)0x80000000), + INPROC = INPROC_SERVER | INPROC_HANDLER, + SERVER = INPROC_SERVER | LOCAL_SERVER | REMOTE_SERVER, + ALL = SERVER | INPROC_HANDLER + } + + public struct PropertyKey + { + public Guid formatId; + public int propertyId; + public PropertyKey(Guid formatId, int propertyId) + { + this.formatId = formatId; + this.propertyId = propertyId; + } + } + + [StructLayout(LayoutKind.Explicit)] + public struct PropVariant + { + [FieldOffset(0)] private short vt; + [FieldOffset(2)] private short wReserved1; + [FieldOffset(4)] private short wReserved2; + [FieldOffset(6)] private short wReserved3; + [FieldOffset(8)] public IntPtr pointerValue; + } + + [Guid("886d8eeb-8cf2-4446-8d02-cdba1dbdcf99"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface IPropertyStore + { + int GetCount(out int propCount); + int GetAt(int property, out PropertyKey key); + int GetValue(ref PropertyKey key, out PropVariant value); + int SetValue(ref PropertyKey key, ref PropVariant value); + int Commit(); + } + + [Guid("D666063F-1587-4E43-81F1-B948E807363F"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface IMMDevice + { + int Activate(ref Guid id, ClsCtx clsCtx, IntPtr activationParams, + [MarshalAs(UnmanagedType.IUnknown)] out object interfacePointer); + + int OpenPropertyStore(StorageAccessMode stgmAccess, out IPropertyStore properties); + + int GetId([MarshalAs(UnmanagedType.LPWStr)] out string id); + } + + [Guid("0BD7A1BE-7A1A-44DB-8397-CC5392387B5E"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface IMMDeviceCollection + { + int GetCount(out int numDevices); + int Item(int deviceNumber, out IMMDevice device); + } + + [Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface IMMDeviceEnumerator + { + int EnumAudioEndpoints(DataFlow dataFlow, DeviceState stateMask, out IMMDeviceCollection devices); + } + + [ComImport, Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")] + class MMDeviceEnumeratorComObject + { + } + + [Guid("657804FA-D6AD-4496-8A60-352752AF4F89"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IAudioEndpointVolumeCallback + { + void OnNotify(IntPtr notifyData); + }; + + [Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IAudioEndpointVolume + { + int RegisterControlChangeNotify(IAudioEndpointVolumeCallback pNotify); + int UnregisterControlChangeNotify(IAudioEndpointVolumeCallback pNotify); + int GetChannelCount(out int pnChannelCount); + int SetMasterVolumeLevel(float fLevelDB, ref Guid pguidEventContext); + int SetMasterVolumeLevelScalar(float fLevel, ref Guid pguidEventContext); + int GetMasterVolumeLevel(out float pfLevelDB); + int GetMasterVolumeLevelScalar(out float pfLevel); + int SetChannelVolumeLevel(uint nChannel, float fLevelDB, ref Guid pguidEventContext); + int SetChannelVolumeLevelScalar(uint nChannel, float fLevel, ref Guid pguidEventContext); + int GetChannelVolumeLevel(uint nChannel, out float pfLevelDB); + int GetChannelVolumeLevelScalar(uint nChannel, out float pfLevel); + int SetMute([MarshalAs(UnmanagedType.Bool)] Boolean bMute, ref Guid pguidEventContext); + int GetMute(out bool pbMute); + int GetVolumeStepInfo(out uint pnStep, out uint pnStepCount); + int VolumeStepUp(ref Guid pguidEventContext); + int VolumeStepDown(ref Guid pguidEventContext); + int QueryHardwareSupport(out uint pdwHardwareSupportMask); + int GetVolumeRange(out float pflVolumeMindB, out float pflVolumeMaxdB, out float pflVolumeIncrementdB); + } +} \ No newline at end of file diff --git a/DS4Windows/DS4Library/DS4Device.cs b/DS4Windows/DS4Library/DS4Device.cs index 83de17f..6e1e12c 100644 --- a/DS4Windows/DS4Library/DS4Device.cs +++ b/DS4Windows/DS4Library/DS4Device.cs @@ -14,6 +14,7 @@ using System.Text; using System.IO; using System.Collections; using System.Drawing; +using DS4Windows.DS4Library; namespace DS4Windows { @@ -131,6 +132,7 @@ namespace DS4Windows private byte ledFlashOn, ledFlashOff; private Thread ds4Input, ds4Output; private int battery; + private DS4Audio audio = new DS4Audio(); public DateTime lastActive = DateTime.UtcNow; public DateTime firstActive = DateTime.UtcNow; private bool charging; @@ -564,6 +566,7 @@ namespace DS4Windows outputReportBuffer[8] = LightBarColor.blue; //blue outputReportBuffer[9] = ledFlashOn; //flash on duration outputReportBuffer[10] = ledFlashOff; //flash off duration + outputReportBuffer[19] = outputReportBuffer[20] = Convert.ToByte(audio.Volume); } lock (outputReport) { diff --git a/DS4Windows/DS4Windows.csproj b/DS4Windows/DS4Windows.csproj index 1bc2b7e..896b61d 100644 --- a/DS4Windows/DS4Windows.csproj +++ b/DS4Windows/DS4Windows.csproj @@ -98,6 +98,7 @@ X360Device.cs + @@ -1117,6 +1118,7 @@ +