mirror of
https://github.com/cemu-project/DS4Windows.git
synced 2024-11-22 17:29:18 +01:00
275 lines
9.8 KiB
C#
275 lines
9.8 KiB
C#
using System;
|
|
using System.Runtime.InteropServices;
|
|
using DS4Windows.DS4Library.CoreAudio;
|
|
|
|
namespace DS4Windows.DS4Library
|
|
{
|
|
public class DS4Audio : IAudioEndpointVolumeCallback
|
|
{
|
|
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 vol;
|
|
public uint Volume
|
|
{
|
|
get
|
|
{
|
|
return vol;
|
|
}
|
|
}
|
|
|
|
public uint getVolume()
|
|
{
|
|
return vol;
|
|
}
|
|
|
|
private DataFlow instAudioFlags = DataFlow.Render;
|
|
|
|
public void RefreshVolume()
|
|
{
|
|
const float HALFPI = (float)Math.PI / 2.0f;
|
|
float pfLevel = 0;
|
|
|
|
if (endpointVolume != null)
|
|
endpointVolume.GetMasterVolumeLevelScalar(out pfLevel);
|
|
|
|
if (instAudioFlags == DataFlow.Render)
|
|
// Use QuadraticEaseOut curve for headphone volume level
|
|
//vol = pfLevel != 0.0 ? Convert.ToUInt32((80 - 30) * -(pfLevel * (pfLevel - 2.0)) + 30) : 0;
|
|
// Use SineEaseOut curve for headphone volume level
|
|
vol = pfLevel != 0.0 ? Convert.ToUInt32((80 - 30) * Math.Sin(pfLevel * HALFPI) + 30) : 0;
|
|
// Use CubicEaseOut curve for headphone volume level
|
|
//vol = pfLevel != 0.0 ? Convert.ToUInt32((80 - 30) * (--pfLevel * pfLevel * pfLevel + 1) + 30) : 0;
|
|
// Use Linear curve for headphone volume level
|
|
//vol = pfLevel != 0.0 ? Convert.ToUInt32((80 - 30) * pfLevel + 30) : 0;
|
|
else if (instAudioFlags == DataFlow.Capture)
|
|
vol = Convert.ToUInt32((60 - 0) * pfLevel + 0);
|
|
}
|
|
|
|
public void OnNotify(IntPtr pNotify)
|
|
{
|
|
RefreshVolume();
|
|
}
|
|
|
|
public DS4Audio(DataFlow audioFlags = DataFlow.Render,
|
|
string searchName = "Wireless Controller")
|
|
{
|
|
var audioEnumerator = new MMDeviceEnumeratorComObject() as IMMDeviceEnumerator;
|
|
IMMDeviceCollection audioDevices;
|
|
audioEnumerator.EnumAudioEndpoints(audioFlags, 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(searchName))
|
|
{
|
|
object interfacePointer;
|
|
Marshal.ThrowExceptionForHR(audioDevice.Activate(ref IID_IAudioEndpointVolume, ClsCtx.ALL, IntPtr.Zero, out interfacePointer));
|
|
instAudioFlags = audioFlags;
|
|
endpointVolume = interfacePointer as IAudioEndpointVolume;
|
|
endpointVolume.RegisterControlChangeNotify(this);
|
|
}
|
|
|
|
RefreshVolume();
|
|
Marshal.ReleaseComObject(audioDevice);
|
|
}
|
|
|
|
Marshal.ReleaseComObject(audioDevices);
|
|
Marshal.ReleaseComObject(audioEnumerator);
|
|
}
|
|
|
|
~DS4Audio()
|
|
{
|
|
if (endpointVolume != null)
|
|
{
|
|
endpointVolume.UnregisterControlChangeNotify(this);
|
|
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);
|
|
}
|
|
} |