Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Vista Audio Control Functions


  • Please log in to reply
182 replies to this topic
codex
  • Members
  • 8 posts
  • Last active: Sep 19 2009 08:48 AM
  • Joined: 27 Aug 2009
Thank you for help!

As I said I tried it from VA_SetMute(false, 1) to VA_SetMute(false, 10) - doesn't work at all.

New test script now quits at
if mute = 
            break
Because VA_GetMute(subunit, "device_name") always returns Null...

This code:
^k::
COM_Init() 
dev := VA_GetDevice("playback") 
MsgBox % VA_GetDeviceName(dev)
if dev 
    COM_Release(dev) 

return

Shows message box: "Speakers (Realtek Hidh Definition Audio)"

VA_SetMasterMute() works just well so COM_Init is not cause ?

I don't have actual code. What I need is just a right line with VA_SetMute to mute/unmute Mic playback.
So whole code may look like that:

^m::
COM_Init()
VA_SetMute(!VA_GetMute(7),7)
return

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
I've uploaded a "verbose" version of my previous script to help us get to the bottom of this. It copies its results to the clipboard and shows a msgbox:
<!-- m -->https://ahknet.autoh...m/paste/1bc4a7b<!-- m -->

codex
  • Members
  • 8 posts
  • Last active: Sep 19 2009 08:48 AM
  • Joined: 27 Aug 2009
Thanks! Here is output:

> Master Mute
> Speakers
  no interface
> Sum
  no interface
> Mute
> CD Audio
  no interface
> Mute
> Rear Green In
  no interface
> Mute
> Rear Black In
  no interface
> Mute
> Rear Orange In
  no interface
> Mute
> Rear Grey In
  no interface
> Mute
> Rear Pink In
  no interface
> Mute
> Front Pink In
  no interface
> Mute
> Rear Blue In
  no interface
> Mute
> Front Green In
  no interface
> Side
  no interface
> Center
  no interface
> Subwoofer
  no interface
> Rear
  no interface
> Front
  no interface
Subunit not found: 1
Device not found: playback:2


Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
Do you have data execution prevention enabled for AutoHotkey.exe? VA.ahk uses RegisterCallback, which generates a machine code function to allow native code to call script functions. Data execution prevents these functions from being called, since they are in read/write (not executable) memory.

Actually, VA.ahk only used them to dynamically call a script function. Since that code was written, direct support for dynamic function calls has been added to the language. I've updated VA.ahk to use these instead of RegisterCallback.

If this solves your issue, I'd be curious to know what ErrorLevel is set to in this case:
DllCall(cb := RegisterCallback("F"))
MsgBox ErrorLevel: %ErrorLevel%
DllCall("GlobalFree", "uint", cb)
F(){
    MsgBox Called F.
}
"Called F" should only appear if DEP is disabled.

codex
  • Members
  • 8 posts
  • Last active: Sep 19 2009 08:48 AM
  • Joined: 27 Aug 2009
You right. I added autohotkey.exe to DEP exceptions and VA_SetMute() works now on my system!

If DEP is enabled, errorlevel is 5 (Access Denied ?).

I think some message should be added to VA.ahk to display a warning about DEP ;)

Thank you alot !

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
I suppose you missed the part of my previous post where I said I've updated VA.ahk. It no longer uses RegisterCallback and therefore should not conflict with DEP.

If DEP is enabled, errorlevel is 5 (Access Denied ?).

Thanks. If I'd thought to suggest DebugBIF, it would've saved us a lot of time; i.e. as soon as VA.ahk fails to call the callback, DebugBIF pops up.

codex
  • Members
  • 8 posts
  • Last active: Sep 19 2009 08:48 AM
  • Joined: 27 Aug 2009
Oh sorry I really missed that. And thanks again !

codex
  • Members
  • 8 posts
  • Last active: Sep 19 2009 08:48 AM
  • Joined: 27 Aug 2009
BTW is it possible to add a wrapper for ISimpleAudioVolume::SetMasterVolume to control session volume ?
Something like VA_SetSessionMasterVolume(level,title) ?

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
I've looked into controlling sessions of other applications, but it doesn't seem to be allowed.

codex
  • Members
  • 8 posts
  • Last active: Sep 19 2009 08:48 AM
  • Joined: 27 Aug 2009
I'm sure it's possible. Sndvol application have controls for that.

There is good example at msdn: <!-- m -->http://msdn.microsof.../dd374921(VS.85<!-- m -->).aspx

The chain to set volume could look like that:
//Get IAudioSessionManager object for device
hr = pDevice->Activate( 
        __uuidof(IAudioSessionManager), 
        CLSCTX_INPROC_SERVER, 
        NULL, 
        (void**) &pAudioSessionManager 
        );


//Get pointer to ISimpleAudioVolume object
hr = pAudioSessionManager->GetSimpleAudioVolume( 
        &GUID_NULL, 0, &m_pSimpleAudioVolume 
        );

//Set volume
return m_pSimpleAudioVolume->SetMasterVolume( 
            flVolume, 
            &AudioSessionVolumeCtx  // Event context.
            );

The only thing I can't figure is where to get session's AudioSessionVolumeCtx (in example it is declared in code)

There is session enumerator IAudioSessionEnumerator <!-- m -->http://msdn.microsof.../dd368281(VS.85<!-- m -->).aspx which could be used to get IAudioSessionControl.
Then IAudioSessionControl::QueryInterface could be used to get IAudioSessionControl2 <!-- m -->http://msdn.microsof.../dd368248(VS.85<!-- m -->).aspx
And IAudioSessionControl2 have methods: GetSessionIdentifier and GetSessionInstanceIdentifier but I not sure if its same as AudioSessionVolumeCtx...

...something wrong with text at forum...


Edited later:
Yes IAudioSessionControl2::GetSessionInstanceIdentifier returns value for "AudioSessionVolumeCtx" as stated in msdn: <!-- m -->http://msdn.microsof.../dd940395(VS.85<!-- m -->).aspx

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
Last time I researched this, there was no way to enumerate the audio sessions or otherwise access sessions associated with other processes. IAudioSessionEnumerator looks like it might be the go, so thanks for pointing that out. However, that interface requires Windows 7 at minimum. That's OK for me since I'm running 7 RC (soon to be running 7 RTM), but it's "off-topic" for this thread. ;)

I'm sure it's possible. Sndvol application have controls for that.

Last time I researched this, the popular consensus was that Sndvol had access to undocumented interfaces. Not knowing for sure that these interfaces even existed was obviously a significant hindrance.

The chain to set volume could look like that:
...
The only thing I can't figure is where to get session's AudioSessionVolumeCtx (in example it is declared in code)

No. GetSimpleAudioVolume accepts AudioSessionGuid, which identifies the session which you want an interface for. That example passes &GUID_NULL, which identifies the default session for the current process. If it is even possible to use this interface to control any session associated with another program, you must know what GUID to pass. (As far as I know it is not possible to find these values; This is what I was talking about in the first sentence of this post.)

As you've noticed, AudioSessionVolumeCtx is defined by the application; it is used in a similar way to the guidEventContext of IAudioMute:

EventContext

[in] Pointer to the event-context GUID. If a call to this method generates a volume-change event, the session manager sends notifications to all clients that have registered IAudioSessionEvents interfaces with the session manager. The session manager includes the EventContext pointer value with each notification. Upon receiving a notification, a client can determine whether it or another client is the source of the event by inspecting the EventContext value. This scheme depends on the client selecting a value for this parameter that is unique among all clients in the session. If the caller supplies a NULL pointer for this parameter, the client's notification method receives a NULL context pointer.



Yogui
  • Members
  • 56 posts
  • Last active: Sep 13 2011 07:09 AM
  • Joined: 14 Jun 2008
Hi,

Lex, you mention Win 7, is it confirm that this fuctions run in Win 7 (the 1 post mention Vista only)

Also half way in the post (Posted: Sun Nov 11, 2007 5:09 am) you gave a code to "Detect when sound is playing through the default output device. "

I can't find the function VA_GetPeakValue(peakMeter) is VA.ahk been updated and not compatible with some example previouly given.

If that the case would you be able to show the new examples:

I've collected these 2:

;http://www.autohotkey.com/forum/topic23792.html&sid=bc6f73dd1e6a2eca3a762b14154e4f11
^1::
COM_CoInitialize() 
if peakMeter := VA_GetDefaultAudioMeter()
{ 
   Loop {
        if GetKeyState("End") 
            break 
        ToolTip % VA_GetPeakValue(peakMeter)				;% display peak value since last call 
        Sleep, 100 
		} 
    COM_Release(peakMeter) 
} else 
    MsgBox Failed to get peak meter. 
COM_CoUninitialize()
Return

;http://www.autohotkey.com/forum/topic27425-15.html
^2::
COM_Init() 
device := VA_GetDefaultAudioDevice()
device_name := VA_GetDeviceName(device) 
COM_Release(device) 
COM_Term() 

Run, mmsys.cpl 
WinWait, Sound ahk_class #32770 

if InStr(device_name, "Speakers")    
	ControlSend, SysListView321, {Down 2}  ; Headphones 
else 
	ControlSend, SysListView321, {Down}    ; Speakers 

ControlClick, Button2  ; Set Default 
ControlClick, Button4  ; OK 
Return 

Thanks, Yogui.
_____________________________

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

Lex, you mention Win 7, is it confirm that this fuctions run in Win 7

Yes.

I can't find the function VA_GetPeakValue(peakMeter)

VA_GetPeakValue was changed in v2.0 (released on Feb 11, 2008) to be more consistent with the other "interface" functions. Please see the example in the included documentation.

cruzer
  • Members
  • 13 posts
  • Last active: Apr 12 2010 08:09 PM
  • Joined: 11 Feb 2009
I can't get it working on Windows 7 RTM 64bit.

tried to use the example script from the VA documentation:

#Include COM.ahk
#Include VA.ahk

COM_Init()

; Get the master volume of the default playback device.
volume := VA_GetMasterVolume()

; Get the volume of the first and second channels.
volume1 := VA_GetMasterVolume(1)
volume2 := VA_GetMasterVolume(2)

; Get the master volume of a device by name.
lineout_volume := VA_GetMasterVolume("", "Line Out")

; Get the master volume of the default recording device.
recording_volume := VA_GetMasterVolume("", "capture")

MsgBox, % "Playback volume:`t" volume
        . "`n  Channel 1:`t" volume1
        . "`n  Channel 2:`t" volume2
        . "`nLine Out volume:`t" lineout_volume
        . "`nRecording volume:`t" recording_volume

and I'm getting this error:
Call to nonexistent function

in Line 147:
defaultDevice := VA_GetDevice()

the com.ahk is this:
[Moderator's note: Removed! Post a link instead.]

and va.ahk is this:

#NoEnv

COM_CoInitialize()
pIPart := VA_GetSpeakersIPart()
text1 := VA_EnumerateSubParts_List(pIPart)
text2 := VA_EnumerateSubParts_Tree(pIPart)
COM_Release(pIPart)

Gui, Font,, Lucida Console
Gui, Add, Edit, H550 W140, %text1%
Gui, Font, s10
Gui, Add, Edit, H550 W620 YM -Wrap +0x100000, %text2%
Gui, Show,, Vista Audio Device Topology
Gui, +LastFound
WinWaitActive
Send ^{Home}

return

GuiClose:
ExitApp

; Shows a list of mute/volume components.
VA_EnumerateSubParts_List(part)
{
    iid_vol  := "{7FB7B48F-531D-44A2-BCB3-5AD5A134B3DC}", COM_GUID4String(iid_vol,iid_vol)
    iid_mute := "{DF45AEEA-B74A-4B6B-AFAD-2366B6AA012E}", COM_GUID4String(iid_mute,iid_mute)
    VA_EnumerateSubParts_List_(part, list_vol, list_mute, &iid_vol, &iid_mute)
    return "VOLUME SUBUNITS`n`n" list_vol "`n`nMUTE SUBUNITS`n`n" list_mute
}
VA_EnumerateSubParts_List_(part, ByRef list_vol, ByRef list_mute, piid_vol, piid_mute)
{
    static S_OK=0
   
    ; part->GetPartType()
    DllCall(NumGet(NumGet(part+0)+24), "uint",part, "uint*",type)
   
    if (type = 1) ; Subunit
    {
        ; part->GetName(...)
        ;   [out] LPWSTR* pwname -- (pointer to Unicode string)
        DllCall(NumGet(NumGet(part+0)+12), "uint",part, "uint*",pwname)
        name := COM_Ansi4Unicode(pwname), COM_CoTaskMemFree(pwname)
       
        ; part->Activate(...)
        ;   [out] IAudioVolumeLevel  iface
        if (S_OK = DllCall(NumGet(NumGet(part+0)+52), "uint",part, "uint",1, "uint",piid_vol, "uint*",iface))
            COM_Release(iface), list_vol .= name "`n"
        ;   [out] IAudioMute  iface
        if (S_OK = DllCall(NumGet(NumGet(part+0)+52), "uint",part, "uint",1, "uint",piid_mute, "uint*",iface))
            COM_Release(iface), list_mute .= name "`n"
    }

    ; part->EnumPartsIncoming(...)
    ;   [out] IPartsList  parts
    DllCall(NumGet(NumGet(part+0)+40), "uint",part, "uint*",parts)
    ; parts->GetCount()
    DllCall(NumGet(NumGet(parts+0)+12), "uint",parts, "uint*",count)
    Loop, %count%
    {
        ; parts->GetPart(A_Index-1, [out] subpart)
        DllCall(NumGet(NumGet(parts+0)+16), "uint",parts, "uint",A_Index-1, "uint*",subpart)
        ; RECURSE
        VA_EnumerateSubParts_List_(subpart, list_vol, list_mute, piid_vol, piid_mute)
       
        COM_Release(subpart)
    }
}

; Shows the device topology in tree form.
VA_EnumerateSubParts_Tree(part)
{
    static indent, indent_size=3
    ; Friendly names for common interfaces.
    static iid_DF45AEEA_B74A_4B6B_AFAD_2366B6AA012E="IAudioMute"
         , iid_7FB7B48F_531D_44A2_BCB3_5AD5A134B3DC="IAudioVolumeLevel"
         , iid_85401FD4_6DE4_4B9D_9869_2D6753A82F3C="IAudioAutoGainControl"
   
    ; part->GetName(...)
    ;   [out] LPWSTR* pwname -- (pointer to Unicode string)
    DllCall(NumGet(NumGet(part+0)+12), "uint",part, "uint*",pwname)
   
    ; part->GetPartType()
    DllCall(NumGet(NumGet(part+0)+24), "uint",part, "uint*",type)
   
    name := COM_Ansi4Unicode(pwname), COM_CoTaskMemFree(pwname)

    text .= indent "+ "
    if (type != 1) ; not a subunit
        text .= type=0 ? "(CONNECTOR) " : "(UNKNOWN) "
    text .= name
   
    ; connPart->GetControlInterfaceCount(count)
    DllCall(NumGet(NumGet(part+0)+32), "uint",part, "uint*",count)
    Loop, %count%
    {
        ; connPart->GetControlInterface(...)
        ;   [out] IControlInterface  idesc
        DllCall(NumGet(NumGet(part+0)+36), "uint",part, "uint",A_Index-1, "uint*",idesc)
       
        ; idesc->GetIID(...)
        ;   [out] GUID  iid
        VarSetCapacity(iid, 16)
        DllCall(NumGet(NumGet(idesc+0)+16), "uint",idesc, "uint",&iid)
        iid := COM_String4GUID(&iid)
       
        StringReplace, iids, iid, -, _, All
        StringTrimLeft, iids, iids, 1
        StringTrimRight, iids, iids, 1
        if iid_%iids%
            iids := iid_%iids%
        else
            iids := iid
        text .= A_Index>1 ? ", " iids : " : " iids
       
        COM_Release(idesc), idesc=0
    }

    text .= "`n"
    Loop, %indent_size%
        indent .= A_Space
   
    ; part->EnumPartsIncoming(...)
    ;   [out] IPartsList  parts
    DllCall(NumGet(NumGet(part+0)+40), "uint",part, "uint*",parts)

    ; parts->GetCount()
    DllCall(NumGet(NumGet(parts+0)+12), "uint",parts, "uint*",count)
    Loop, %count%
    {
        ; parts->GetPart(A_Index-1)
        DllCall(NumGet(NumGet(parts+0)+16), "uint",parts, "uint",A_Index-1, "uint*",subpart)
       
        text .= VA_EnumerateSubParts_Tree(subpart)
       
        COM_Release(subpart)
    }
   
    indent := SubStr(indent,1,-indent_size)
   
    return text
}

; Gets a pointer to an IPart interface that represents the Speakers.
VA_GetSpeakersIPart()
{
    defaultDevice := VA_GetDevice()
   
    ; defaultDevice->Activate(...)
    ;   [out] IDeviceTopology  deviceTopology
    iid := "{2A07407E-6497-4A18-9787-32F79BD0D98F}"
    DllCall(NumGet(NumGet(defaultDevice+0)+12), "uint",defaultDevice, "uint",COM_GUID4String(iid,iid), "uint",1, "uint",0, "uint*",deviceTopology)
    COM_Release(defaultDevice), defaultDevice=0
   
    ; deviceTopology->GetConnector(0,...)
    ;   [out] IConnector  endptConnector
    DllCall(NumGet(NumGet(deviceTopology+0)+16), "uint",deviceTopology, "uint",0, "uint*",endptConnector)
    COM_Release(deviceTopology), deviceTopology=0
   
    ; endptConnector->GetConnectedTo(...)
    ;   [out] IConnector  hwdevConnector
    DllCall(NumGet(NumGet(endptConnector+0)+32), "uint",endptConnector, "uint*",hwdevConnector)
    COM_Release(endptConnector), endptConnector=0
   
    ; hwdevConnector->QueryInterface(...)
    ;   [out] IPart  connPart
    iid := "{AE2DE0E4-5BCA-4F2D-AA46-5D13F8FDB3A9}"
    connPart := COM_QueryInterface(hwdevConnector, iid)
    COM_Release(hwdevConnector), hwdevConnector=0
   
    return connPart
}


Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
You've copied the wrong script, which clearly doesn't contain VA_GetDevice. In my first post click the Download with documentation link. I've replaced the device topology script with a link to avoid any further confusion.