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
bmcclure
  • Members
  • 774 posts
  • Last active: Jan 04 2014 10:44 PM
  • Joined: 24 Nov 2007
OK, I have confirmed that there is an issue.

Randomly, your functions stop returning proper values. It always seems to correct itself after a while, or after some unknown action, I don't know which.

But for instance, I'm always getting nothing for VA_GetMasterMute() at the moment even when it's muted. I'm also getting a blank value for VA_GetMasterVolume() at the moment.

Yet in a little while, it will start working properly again.

Could it not be releasing Dlls properly or something? I can't figure out what would cause this odd behavior.

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
Are you using any other VA functions? I only use the script rarely, to control line in and for VA_GetPeakValue. However, running VA_GetMasterMute and VA_GetMasterVolume in a loop gives predictably reliable results. I have left a script running, repeatedly polling volume/mute and counting the number of bad results (currently 0.)

Could it not be releasing Dlls properly or something?

I find that unlikely (even interpreting "Dlls" as "COM references.") VA doesn't explicitly load any Dlls, and it releases every COM interface reference it retrieves. (Re-confirmed for COM_GetMasterMute/Volume and functions called by them.)

Try these modified functions, and let me know the values of _hResult_, _ErrorLevel_ and endptVolume (directly) after the function fails. _hResult_ and _ErrorLevel_ should be zero, indicating success, while endptVolume should be a non-zero integer.
VA_GetMasterVolume(channel="")
{
    global _hResult_, _ErrorLevel_, endptVolume
    endptVolume := VA_GetDefaultAudioEndpointVolume()
    if channel =    ; endptVolume->GetMasterVolumeLevelScalar(vol)
        _hResult_:=DllCall(NumGet(NumGet(endptVolume+0)+36), "uint",endptVolume, "float*",vol)
    else            ; endptVolume->GetChannelVolumeLevelScalar(channel,vol)
        _hResult_:=DllCall(NumGet(NumGet(endptVolume+0)+52), "uint",endptVolume, "uint",channel, "float*",vol)
    _ErrorLevel_ := ErrorLevel
    COM_Release(endptVolume)
    return vol*100
}

VA_GetMasterMute()
{
    global _hResult_, _ErrorLevel_, endptVolume
    endptVolume := VA_GetDefaultAudioEndpointVolume()
    ; endptVolume->GetMute(mute)
    _hResult_:=DllCall(NumGet(NumGet(endptVolume+0)+60), "uint",endptVolume, "int*",mute)
    _ErrorLevel_ := ErrorLevel
    COM_Release(endptVolume)
    return mute
}


bmcclure
  • Members
  • 774 posts
  • Last active: Jan 04 2014 10:44 PM
  • Joined: 24 Nov 2007
Yes, please interpret DLL as COM, heh :) Though I thought the com include used dll calls, guess not.

Anyway, thanks for the tip. Next time it occurs, I'll use your function and let you know what values I receive.

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

Though I thought the com include used dll calls, guess not.

Well, Vista's audio API (MMDevice API) does reside in a dll/dlls. The point is that the script has no knowledge of the dll itself, only the COM interfaces.

bmcclure
  • Members
  • 774 posts
  • Last active: Jan 04 2014 10:44 PM
  • Joined: 24 Nov 2007
On GetMasterVolume:

_hResult_ : null
_ErrorLevel_ : -4
endptVolume : null
Note: above, 'null' means it was empty

On GetMasterMute:

same

Then it started working again a few minutes later, and stopped again a few minutes after that... ugh!

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
That means VA_GetDefaultAudioEndpointVolume (or VA_GetDefaultAudioDevice) is failing. DllCall sets ErrorLevel=-4 when "the specified function could not be found inside the dll", or as in this case, when the function pointer is invalid (since endptVolume is invalid.)

Now we need to narrow it down further... return VA_GetMasterVolume and VA_GetMasterMute to the way they were (or leave them as is), and use the following modified functions:
VA_GetDefaultAudioEndpointVolume()
{
    if defaultDevice := VA_GetDefaultAudioDevice()
    {
        ; defaultDevice->Activate(...)
        ;   [out] IAudioEndpointVolume  endptVolume
        iid := "{5CDF2C82-841E-4546-9722-0CF74078229A}"
        _hResult_:=DllCall(NumGet(NumGet(defaultDevice+0)+12), "uint",defaultDevice, "uint",COM_GUID4String(iid,iid), "uint",1, "uint",0, "uint*",endptVolume)
        COM_Release(defaultDevice), defaultDevice=0
        if !endptVolume
            MsgBox Failed to get IAudioEndpointVolume, HRESULT: %_hResult_%
    }
    
    return endptVolume
}

VA_GetDefaultAudioDevice()
{
    global _hResult_, deviceEnumerator
    ; deviceEnumerator := COM_CreateObject("MMDeviceEnumerator","IMMDeviceEnumerator",CLSCTX_INPROC_SERVER)
    ;   (CLSCTX_INPROC_SERVER = execution context: this process)
    deviceEnumerator := COM_CreateObject("{BCDE0395-E52F-467C-8E3D-C4579291692E}","{A95664D2-9614-4F35-A746-DE8DB63617E6}",1)
    if deviceEnumerator
    {
        ; deviceEnumerator->GetDefaultAudioEndpoint(...)
        ;   [out] IMMDevice  defaultDevice
        _hResult_:=DllCall(NumGet(NumGet(deviceEnumerator+0)+16), "uint",deviceEnumerator, "int",0, "int",0, "uint*",defaultDevice)
        COM_Release(deviceEnumerator), deviceEnumerator=0
        if !defaultDevice
            MsgBox GetDefaultAudioEndpoint failed, HRESULT: %_hResult_%
    }
    else
        MsgBox Failed to create MMDeviceEnumerator, HRESULT: %_hResult_%
    
    return defaultDevice
}
A MsgBox should be shown the instant either of them fail.

You'll also need to change COM_CreateObject for the "Failed to create MMDeviceEnumerator" message to have a meaningful HRESULT (assuming it fails.)
COM_CreateObject(CLSID, IID = "{00020400-0000-0000-C000-000000000046}", CLSCTX = 5)
{
    [color=olive]Global _hResult_[/color]
    [color=olive]_hResult_:=[/color]DllCall("ole32\CoCreateInstance", "Uint", SubStr(CLSID,1,1)="{" ? COM_GUID4String(CLSID,CLSID) : COM_CLSID4ProgID(CLSID,CLSID), "Uint", 0, "Uint", CLSCTX, "Uint", COM_GUID4String(IID,IID), "UintP", ppv)
	Return	ppv
}


bmcclure
  • Members
  • 774 posts
  • Last active: Jan 04 2014 10:44 PM
  • Joined: 24 Nov 2007
Thanks a lot for taking the time to help me with this.

With the 2 modified VA functions and one modified COM function, I get the following error whenever I experience this problem:

Failed to create MMDeviceEnumerator, HRESULT: -2147221008



Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
-2147221008 = 0x800401F0
//
// CoInitialize has not been called.
//
#define CO_E_NOTINITIALIZED              _HRESULT_TYPEDEF_(0x800401F0L)
:x

I had discarded that possibility, assuming if you don't initialize COM, the functions never work. Are you perhaps CoInitializing, CoUninitializing, and forgetting to CoInitialize again?

I simply call the following once at script start-up:
COM_Init()
(Same as COM_CoInitialize(), but shorter.)

bmcclure
  • Members
  • 774 posts
  • Last active: Jan 04 2014 10:44 PM
  • Joined: 24 Nov 2007
Oh, no, I haven't messed with COM at all outside of these VA functions. I completely missed the instructions in the first post to manually initialize COM in my script. I'll try that now. Sorry!

Though it usually works right away upon script start. That seems odd that it is sporadic.

I added COM_Init() to my script, and the world is still turning; I'll let you know if the problem crops up again now that I'm actually using your functions properly :)

Edit: It's been over 24 hours and everything's A-OK so far. I still don't understand why it was even working at all before, heh. But I'm happy now :)

bitlisz
  • Guests
  • Last active:
  • Joined: --
Hi!

I can use this script to toggle beetween audio outputs?
Like Digital to Analog?

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
If you mean toggle the default audio device, then no. Microsoft decided that only the user should be able to do this, so they did not provide an API for it. :(

There is a solution, but it requires a window to be shown briefly. See Select sound source.

  • Guests
  • Last active:
  • Joined: --
What is the easiest way in AHK to unmute and set the volume at 50?

Does it involve this script, or is there another way?

(Vista)

Thanks!

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
What is "easiest" is subjective. You could put AutoHotkey in XP compatiblity mode and use SoundSet (as explained on the SoundSet page in the AutoHotkey help file), or use this script. You may notice at the top of my first post:

Provide Windows Vista-compatible alternatives to some SoundSet/SoundGet subcommands. The script currently has the following capabilities:
[*:2qemgz2v]Get or Set volume, preserving balance.
[*:2qemgz2v]Get or Set the volume of each individual channel - left (0) or right (1); surround-sound not tested.
[*:2qemgz2v]Get or Set mute.
[*:2qemgz2v]Work with any component listed on the Levels tab of the Speakers Properties dialog. For me this includes the master volume, microphone, line in, and a bunch of components that don't really exist.
[*:2qemgz2v]New: Detect when sound is playing through the default output device. (see example further down this page.)

VA_SetMasterMute(false)
VA_SetMasterVolume(50)
Note also the script requirements:

- Windows Vista. The script should NOT be run in XP-compatibility mode.
- Sean's Standard Library COM.ahk



Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
Vista Audio Control Functions v2.0 has been released. I've rewritten most of the script and added a few new features. In particular, multiple devices are now supported.

The peak meter functions have completely changed, so if you used those, be sure to check out the example in the new help file.

bla
  • Guests
  • Last active:
  • Joined: --
i only got to AutoHotkey in the first place because i wanted to mute the microphone in vista.
and then it turns out exactly thats an issue :)

thanks to you it's not anymore tho. thank you!