Setting MMDevice WaveFormat Keeps Reverting to Default value

32 Views Asked by At

I am trying to modify the open source (https://github.com/frgnca/AudioDeviceCmdlets) of "AudioDeviceCmdlets.dll" to be able to set the DeviceFormat of the default recording device.

Using Windows PowerShell, I can get the current Device Format just fine with the code below, but when I set the Device Format using the "Set" function below, it seems to not be "committing" it. After setting it, I can call the "Get" function and see that the change has indeed stuck. But when I open up the sound settings and go to check the Device Format under the "Advanced" tab, it has reverted to the default value (as if the "Restore Defaults" button at the bottom was pressed), and then if I do another "Get" call, it returns this default value too.

[StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct WAVEFORMATEX
        {
            public ushort wFormatTag;
            public ushort nChannels;
            public uint nSamplesPerSec;
            public uint nAvgBytesPerSec;
            public ushort nBlockAlign;
            public ushort wBitsPerSample;
            public ushort cbSize;
        };

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct WAVEFORMATEXTENSIBLE
        {
            public WAVEFORMATEX Format;
            public ushort wValidBitsPerSample;
            public ushort wSamplesPerBlock;
            public ushort wReserved;
            public uint dwChannelMask;
            public Guid SubFormat;
        };

private void GetPropertyInformation(EStgmAccess mode = EStgmAccess.STGM_READ)
        {
            IPropertyStore propstore;
            Marshal.ThrowExceptionForHR(_RealDevice.OpenPropertyStore(mode, out propstore));
            _PropertyStore = new PropertyStore(propstore);
        }

    public object DeviceFormat
            {
                get
                {
                    if (_PropertyStore == null)
                        GetPropertyInformation();
                    if (_PropertyStore.Contains(PKEY.PKEY_AudioEngine_DeviceFormat))
                    {
                        GCHandle pinnedArray = GCHandle.Alloc(_PropertyStore[PKEY.PKEY_AudioEngine_DeviceFormat].Value, GCHandleType.Pinned);
                        IntPtr pointer = pinnedArray.AddrOfPinnedObject();
                        WAVEFORMATEXTENSIBLE format = (WAVEFORMATEXTENSIBLE) Marshal.PtrToStructure(pointer, typeof(WAVEFORMATEXTENSIBLE));
                        pinnedArray.Free();
    
                        return string.Format("Channels={0}\r\n" +
                            "SampleRate={1}\r\n" +
                            "BitDepth={2}\r\n" +
                            "Tag={3}\r\n" +
                            "AverageBytesPerSec={4}\r\n" +
                            "BlockAlign={5}\r\n" +
                            "Size={6}\r\n" +
                            "wValidBitsPerSample={7}\r\n" +
                            "wSamplesPerBlock={8}\r\n" +
                            "wReserved={9}\r\n" +
                            "dwChannelMask={10}\r\n" +
                            "SubFormat={11}",
                            format.Format.nChannels,
                            format.Format.nSamplesPerSec,
                            format.Format.wBitsPerSample,
                            format.Format.wFormatTag,
                            format.Format.nAvgBytesPerSec,
                            format.Format.nBlockAlign,
                            format.Format.cbSize,
                            format.wValidBitsPerSample,
                            format.wSamplesPerBlock,
                            format.wReserved,
                            format.dwChannelMask,
                            format.SubFormat);
                    }
                    else
                        return "Unknown";
                }
                set
                {
                    if (_PropertyStore == null)
                        GetPropertyInformation(EStgmAccess.STGM_READWRITE);
                    if (_PropertyStore.Contains(PKEY.PKEY_AudioEngine_DeviceFormat))
                    {
                        GCHandle pinnedArray = GCHandle.Alloc(_PropertyStore[PKEY.PKEY_AudioEngine_DeviceFormat].Value, GCHandleType.Pinned);
                        IntPtr pointer = pinnedArray.AddrOfPinnedObject();
                        WAVEFORMATEXTENSIBLE format = (WAVEFORMATEXTENSIBLE)Marshal.PtrToStructure(pointer, typeof(WAVEFORMATEXTENSIBLE));
                        pinnedArray.Free();
    
                        string properties = value.ToString();
                        string[] property = properties.Split((char) 0x20);
    
                        format.Format.nChannels = Convert.ToUInt16(property[0]);
                        format.Format.wBitsPerSample = Convert.ToUInt16(property[1]);
                        format.Format.nSamplesPerSec = Convert.ToUInt32(property[2]);
                        format.wValidBitsPerSample = format.Format.wBitsPerSample;
                        format.Format.nBlockAlign = (ushort) ((format.Format.nChannels * format.Format.wBitsPerSample) / 8);
                        format.Format.nAvgBytesPerSec = (format.Format.nSamplesPerSec * format.Format.nBlockAlign);
    
                        PropVariant prop = new PropVariant();
                        prop.Value = format;
    
                        _PropertyStore.SetValue(PKEY.PKEY_AudioEngine_DeviceFormat, prop);
                        _PropertyStore.Commit();
                    }
                }
            }

When I am setting the value of the PropVariant object, I added this "Set" function:

public object Value
        {
            get
            {
                VarEnum ve = (VarEnum)vt;
                switch (ve)
                {
                    case VarEnum.VT_I1:
                        return bVal;
                    case VarEnum.VT_I2:
                        return iVal;
                    case VarEnum.VT_I4:
                        return lVal;
                    case VarEnum.VT_I8:
                        return hVal;
                    case VarEnum.VT_INT:
                        return iVal;
                    case VarEnum.VT_UI4:
                        return ulVal;
                    case VarEnum.VT_LPWSTR:
                        return Marshal.PtrToStringUni(everything_else);
                    case VarEnum.VT_BLOB:
                        return GetBlob();
                }
                return "FIXME Type = " + ve.ToString();
            }
            set
            {
                vt = (short) VarEnum.VT_BLOB;
                int length = Marshal.SizeOf(value);
                IntPtr formatPointer = Marshal.AllocHGlobal(length);
                Marshal.StructureToPtr(value, formatPointer, false);
                blobVal.Length = length;
                blobVal.Data = formatPointer;
            }
        }

To test to make sure no permission or wacky things happening on my PC, I used a tool that can accomplish this called "SoundVolumeView" (https://www.nirsoft.net/utils/sound_volume_view.html). With this tool, it does successfully set the Device Format and it sticks when I go to the sound settings to check. So I know it is possible somehow.

Any suggestions are appreciated!

0

There are 0 best solutions below