Results 1 to 3 of 3

Thread: [VB6, Vista+] Core Audio - Change the system default audio device

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,711

    [VB6, Vista+] Core Audio - Change the system default audio device


    Changing the system-wide default input and output audio devices

    WARNING: This feature is not designed to be accessible to programs and uses a COM interface that is undocumented and unsupported by Microsoft. As such, it may not function in future versions of Windows.

    Several times I've come across people asking how to change the default input/output devices through code, and usually the reply is that it isn't possible. Changing the device per-app is well documented, but many people want to be able to set the system-wide default like the Sound control panel applet does. Tonight I was looking into that a little deeper, and the applet does it through an undocumented private COM interface called IPolicyConfig. So naturally I immediately found the definition and added it to oleexp.

    There's two versions of the interface included, one for Windows Vista (IPolicyConfigVista / CPolicyConfigVistaClient) and one for Windows 7 and higher (IPolicyConfig / PolicyConfigClient).
    Using this interface to set the defaults is very easy:
    Code:
    Private pPolicyCfg As PolicyConfigClient
    
    If (pPolicyCfg Is Nothing) Then
        Set pPolicyCfg = New PolicyConfigClient
    End If
    pPolicyCfg.SetDefaultEndpoint StrPtr(sDeviceID), eMultimedia
    pPolicyCfg.SetDefaultEndpoint StrPtr(sDeviceID), eCommunications
    It's actually far more complicated to figure out the device ID string that you need, as it's not name, it's a string like {0.0.1.00000000}.{b12f40bc-c3ec-4a74-afcc-4b6d0eb6914a}. The good news is enumerating all the devices and their IDs (as well as enabling them if you need to, as they need to be active to be set as default) was covered in my Core Audio Basics demo. The enumeration code is copied right out of that project.

    Requirements
    -Windows Vista or higher
    -oleexp.tlb v4.11 or higher (new release for this demo)
    -oleexp addon mIID.bas (included in oleexp download)
    -oleexp addon mCoreAudio.bas (included in oleexp download)
    -oleexp addon mPKEY.bas (included in oleexp download)
    Attached Files Attached Files

  2. #2
    Member
    Join Date
    Apr 2009
    Posts
    48

    Re: [VB6, Vista+] Core Audio - Change the system default audio device

    Code:
    Option Explicit
    
    Private pPolicyCfg As PolicyConfigClient
    
    Public Sub var_dump(Text() As String)
        Dim temp As Long
        Debug.Print "var_dump"
        For temp = 0 To UBound(Text)
            Debug.Print temp, Text(temp)
        Next
    End Sub
    
    Public Function EnumAudioDevices(Optional InputDevices As Boolean = True) As String()
        Dim sOut As String
        Dim i As Long
        Dim lp As Long
        Dim s1 As String
        Dim RET() As String
        Dim pDvEnum As MMDeviceEnumerator
        Set pDvEnum = New MMDeviceEnumerator
        Dim pDvCol As IMMDeviceCollection
        If InputDevices Then
            pDvEnum.EnumAudioEndpoints eCapture, DEVICE_STATE_ACTIVE, pDvCol
        Else
            pDvEnum.EnumAudioEndpoints eRender, DEVICE_STATE_ACTIVE, pDvCol
        End If
        If (pDvCol Is Nothing) = False Then
            Dim nCount As Long
            If pDvCol.GetCount(nCount) = S_OK Then
                ReDim RET(nCount - 1)
                For i = 0 To (nCount - 1)
                    RET(i) = GetDeviceName(pDvCol, i)
                Next
            End If
        End If
        EnumAudioDevices = RET
    End Function
    
    Private Function GetDeviceName(pCol As IMMDeviceCollection, nIdx As Long) As String
        Dim pDevice As IMMDevice
        Dim sID As String
        Dim pStore As IPropertyStore
        Dim pDesc As IPropertyDescription
        Dim lp As Long
        Dim lpID As Long
        Dim vrProp As Variant
        Dim vProp As Variant
        Dim j As Long
        If (pCol.Item(nIdx, pDevice)) = S_OK Then
            pDevice.GetId lpID
            sID = LPWSTRtoStr(lpID)
            'Debug.Print "Got device(" & nIdx & ") id=" & sID
            If (sID = "") Or (sID = vbNullChar) Then
                GetDeviceName = "<Invalid DeviceID>"
                Exit Function
            End If
            pDevice.OpenPropertyStore STGM_READ, pStore
            If (pStore Is Nothing) = False Then
                'these property stores aren't as full-featured as other ones
                'such as those associated with IShellItem's of files.. for
                'example we can't use the superior PSFormatPropertyValue b/c
                'we can't get an IPropertyDescription, or a property name
                'So we're stuck using PropVariants, a nightmare in VB
                Dim pcnt As Long
                pStore.GetCount pcnt
                'Debug.Print "prop cnt=" & pcnt & " outputting propvariant..."
                Dim pk As PROPERTYKEY
                pStore.GetValue PKEY_Device_FriendlyName, vProp
                PropVariantToVariant vProp, vrProp
                Dim vte As VbVarType
                vte = VarType(vrProp)
                If (vte And vbArray) = vbArray Then 'this always seems to be vbString and vbArray, haven't encountered other types
                    For j = LBound(vrProp) To UBound(vrProp)
                        Debug.Print "Array Value(" & j & ")=" & CStr(vrProp(j))
                    Next j
                Else
                    Select Case vte
                        Case vbDataObject, vbObject, vbUserDefinedType
                            GetDeviceName = "<cannot display this type>"
                        Case vbEmpty, vbNull
                            GetDeviceName = "<empty or null>"
                        Case vbError
                            GetDeviceName = "<vbError>"
                        Case Else
                            GetDeviceName = CStr(vrProp) & "|" & sID
                    End Select
                End If
            Else
                GetDeviceName = "<Failed to get IPropertyStore>"
            End If
        Else
            GetDeviceName = "<Failed to get device with pCol.Item, nIdx=" & nIdx & ">"
        End If
    End Function
    Private Function GetDeviceNameDirect(pDevice As IMMDevice) As String
        Dim sID As String
        Dim pStore As IPropertyStore
        Dim pDesc As IPropertyDescription
        Dim lp As Long
        Dim lpID As Long
        Dim vrProp As Variant
        Dim vProp As Variant
        Dim j As Long
        pDevice.GetId lpID
        sID = LPWSTRtoStr(lpID)
        Debug.Print "GetDeviceNameDirect------------"
        Debug.Print "Got device id=" & sID
        If (sID = "") Or (sID = vbNullChar) Then
            GetDeviceNameDirect = "<Invalid DeviceID>"
            Exit Function
        End If
        pDevice.OpenPropertyStore STGM_READ, pStore
        If (pStore Is Nothing) = False Then
            'these property stores aren't as full-featured as other ones
            'such as those associated with IShellItem's of files.. for
            'example we can't use the superior PSFormatPropertyValue b/c
            'we can't get an IPropertyDescription, or a property name
            'So we're stuck using PropVariants, a nightmare in VB
            Dim pcnt As Long
            pStore.GetCount pcnt
            Debug.Print "prop cnt=" & pcnt & " outputting propvariant..."
            Dim pk As PROPERTYKEY
            pStore.GetValue PKEY_Device_FriendlyName, vProp
            PropVariantToVariant vProp, vrProp
            Dim vte As VbVarType
            vte = VarType(vrProp)
            If (vte And vbArray) = vbArray Then 'this always seems to be vbString and vbArray, haven't encountered other types
                For j = LBound(vrProp) To UBound(vrProp)
                    Debug.Print "Array Value(" & j & ")=" & CStr(vrProp(j))
                Next j
            Else
                Select Case vte
                    Case vbDataObject, vbObject, vbUserDefinedType
                        GetDeviceNameDirect = "<cannot display this type>"
                    Case vbEmpty, vbNull
                        GetDeviceNameDirect = "<empty or null>"
                    Case vbError
                        GetDeviceNameDirect = "<vbError>"
                    Case Else
                        GetDeviceNameDirect = CStr(vrProp) & "|" & sID
                End Select
            End If
        Else
            GetDeviceNameDirect = "<Failed to get IPropertyStore>"
        End If
    End Function
    
    Public Function getSID(sID As String, Optional UseAsDefault As Boolean = False) As String
        Dim temp As Long
        If Left(sID, 1) <> "<" And Right(sID, 1) <> ">" Then
            temp = InStr(sID, "|")
            If temp > 0 Then sID = Mid$(sID, temp + 1)
            If UseAsDefault Then
                If (pPolicyCfg Is Nothing) Then Set pPolicyCfg = New PolicyConfigClient
                pPolicyCfg.SetDefaultEndpoint StrPtr(sID), eMultimedia
                pPolicyCfg.SetDefaultEndpoint StrPtr(sID), eCommunications
            End If
        Else
            sID = ""
        End If
        getSID = sID
    End Function
    
    Public Function FindDevice(Name As String, Optional InputDevices As Boolean = True, Optional UseAsDefault As Boolean = False) As String
        Dim AudioDevices() As String, temp As Long, sID As String, temp2 As Long
        AudioDevices = EnumAudioDevices(InputDevices)
        For temp = 0 To UBound(AudioDevices)
            sID = AudioDevices(temp)
            temp2 = InStr(sID, "|")
            If Left(sID, 1) <> "<" And Right(sID, 1) <> ">" And temp2 > 0 Then
                sID = Trim(Left(sID, temp2 - 1))
                If StrComp(Name, sID, vbTextCompare) = 0 Then
                    FindDevice = getSID(AudioDevices(temp), UseAsDefault)
                    Exit Function
                End If
            End If
        Next
    End Function
    I redid the code so it wasn't hardcoded to usercontrols, so I could re-use it in a command line interface.

    EnumRenderDevices and EnumCaptureDevices were combined into EnumAudioDevices since there was only 1 word different between the 2.
    It returns the list as a string array instead of putting it in a usercontrol

    GetDeviceName and GetDeviceNameDirect had the debug.prints switched to returning a text in the form of "<error>" like the other errors.

    Command1_Click and Command2_Click were combined into getSID which takes the ID text (example: BBY LCD TV-4 (NVIDIA High Definition Audio)|{0.0.0.00000000}.{a3654c19-7934-4b6b-8102-b0bb9f1921bf}) and spits out the ID code after the |
    If UseAsDefault is true, it sets the input/output device to that ID code

    FindDevice looks for the first device name (before the | ) and calls getSID

    I don't want to sound rude, but I wish more developers would make code this way. That way the code is reusable from the start

  3. #3

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,711

    Re: [VB6, Vista+] Core Audio - Change the system default audio device

    If you want to pay me to make it how you want instead of a 10 minute proof-of-concept job using whatever method strikes my mood, I'd be happy to oblige

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width