Jump to content

How can I get/set mute state of Windows Volume Mixer?


Go to solution Solved by CYCho,

Recommended Posts

Hi,
I'm trying to synchronize my zPlayer's volume controls with Windows Volume Mixer. I have done the volume part alright using waveOutSetVolume function, but I am stuck on mute/unmute part. I can mute zPlayer with winmm.dll command string (mciSendString("set myMedia audio all off"). If I issue this command, Windows Volume Mixer turns down the volume to 0, not turning the speaker icon to mute state. Conversely, I want zPlayer to detect if the user mutes the sound by clicking the speaker icon in Windows Volume Mixer.
So could anyone please guide me to the method of getting and setting the mute state of Windows Volume Mixer, usng only standard AutoIt UDFs if at all possible? Thanks in advance.

Link to comment
Share on other sites

Posted (edited)
24 minutes ago, argumentum said:

would this do it ?

Thanks for your prompt response. My answer to your question is maybe and maybe not. I have no mic connected to my  PC, so your code stops probably trying to detect a mic. I thought I would need a simple code snippet and your code seems to be an overkill for my small task.

Edited by CYCho
Link to comment
Share on other sites

  • Moderators

_SetMute(False/True) ; verify with _GetMute() ... 

This still works on my system... it's the _AudioEndpointVolume.au3 download from the first post.  Although, I believe that's only windows os specific, I believe I saw one once that does it per app.

Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer.

Link to comment
Share on other sites

Posted (edited)
38 minutes ago, SmOke_N said:

This still works on my system... it's the _AudioEndpointVolume.au3 download from the first post.  Although, I believe that's only windows os specific, I believe I saw one once that does it per app.

Thanks, it works on my Windows 11, too. However, it acts on Windows master volume rather than on app specific volume.  Have you tried to make it apply only to a certain app? Maybe some of the golbal constants need to be changed.

Edited by CYCho
Link to comment
Share on other sites

  • Moderators
1 hour ago, CYCho said:

Thanks, it works on my Windows 11, too. However, it acts on Windows master volume rather than on app specific volume.  Have you tried to make it apply only to a certain app? Maybe some of the golbal constants need to be changed.

I've never had a need or desire unfortunately.

Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer.

Link to comment
Share on other sites

  • Moderators
2 hours ago, Nine said:

Try this version. 

https://www.autoitscript.com/forum/topic/201808-control-application-sound-volume/#comment-1447975

In SimpleAudioVolume, there is a SetMute method.  I did not test the code, but it looks right to me...Should work.

 

That's the one I was talking about... nice find

Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer.

Link to comment
Share on other sites

2 hours ago, Nine said:

I am trying to understand what it does. It seems to be able to adjust the volume from outside of the process. waveOutSetVolume can do it only from inside of a process. As far as my task is concerned, waveOutSetVolume is good enough for volume adjustment.
 

3 hours ago, Nine said:

In SimpleAudioVolume, there is a SetMute method.  I did not test the code, but it looks right to me...Should work.

I read it, too. My problem is I can't implement it. I coudn't find an example of setMute code implemented in AutoIt.

@SmOke_N and @Nine, Thanks for your kind attention. It's late at night now. I will come back tomorrow.

Link to comment
Share on other sites

I don't remember where I found it and I don't know if it can be useful here:
if you save the following command line in a file with the .vbs extension and then execute it, each time it is executed it switches the audio from ON to OFF and vice versa (it toggles the audio state with each execution)

CreateObject("WScript.Shell").SendKeys(chr(173))

 

 

image.jpeg.9f1a974c98e9f77d824b358729b089b0.jpeg Chimp

small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....

Link to comment
Share on other sites

Gash darn, am slow. You wanna have a mute button in your script.
The simplest would be to run SoundVolumeView (NirSoft) via command line.
Internally I've seen an AKH but it'd need translation to au3.

hopefully it can be translated by someone that understand all these DLL stuff.  
Edit: it was here all along I guess https://www.autoitscript.com/forum/topic/200529-unmute-autoit-application/
https://www.autoitscript.com/forum/topic/202969-how-to-change-any-program-volume-by-it-handle/?do=findComment&comment=1457263

 

Edited by argumentum

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Link to comment
Share on other sites

Posted (edited)
8 hours ago, Gianni said:

if you save the following command line in a file with the .vbs extension and then execute it

  I don't quite get it. I don't know vbs and, even if I do, this code seems to be affecting the master volume, not specific to a single process.

7 hours ago, argumentum said:

Gash darn, am slow. You wanna have a mute button in your script.

A mute button should turn the sound off, not reducing the volume level to 0. In that sense, I have my mute button and it works. The problem is that I cannot sync the mute state with Windows Volume Mixer. If I click tmy mute button, the sound is off but the mute button in Windows Volume Mixer for my app remains unchanged. Conversely, if the mute button in Windows Volume Mixer is clicked, I have no way to detect it and sync my mute button.

I'm sorry I cannot make a short reproducible code. You can download my zPlayer, even though it is a bit long, and do the test.

Edited by CYCho
Link to comment
Share on other sites

  • Solution
Posted (edited)

Based on the posts by @Danyfirex and @careca , I created the following example code. If this code is compiled and executed, it shows the results of SetVolume, GetVolume, SetMute and GetMute commands of ISimpleAudioVolume interface. With this, I would consider my problem SOLVED. Thank you everyone for your kind support.
Edit: The code was revised to work in uncompiled script. too.

; File name: VolumeControlForProcess_Example.au3

#AutoIt3Wrapper_Outfile=VolumeControlForProcess_Example.exe

#include-once
#include <WinAPIProc.au3>
#include <Array.au3>
#include <Misc.au3>
#include <Sound.au3>


Global Const $sCLSID_MMDeviceEnumerator = "{BCDE0395-E52F-467C-8E3D-C4579291692E}"
Global Const $sIID_IMMDeviceEnumerator = "{A95664D2-9614-4F35-A746-DE8DB63617E6}"
Global Const $sTagIMMDeviceEnumerator = _
        "EnumAudioEndpoints hresult(int;dword;ptr*);" & _
        "GetDefaultAudioEndpoint hresult(int;int;ptr*);" & _
        "GetDevice hresult(wstr;ptr*);" & _
        "RegisterEndpointNotificationCallback hresult(ptr);" & _
        "UnregisterEndpointNotificationCallback hresult(ptr)"

Global Const $sIID_IMMDevice = "{D666063F-1587-4E43-81F1-B948E807363F}"
Global Const $sTagIMMDevice = _
        "Activate hresult(struct*;dword;ptr;ptr*);" & _
        "OpenPropertyStore hresult(dword;ptr*);" & _
        "GetId hresult(wstr*);" & _
        "GetState hresult(dword*)"

Global Const $sIID_IAudioSessionManager2 = "{77aa99a0-1bd6-484f-8bc7-2c654c9a9b6f}"
Global Const $sTagIAudioSessionManager = "GetAudioSessionControl hresult(ptr;dword;ptr*);" & _
        "GetSimpleAudioVolume hresult(ptr;dword;ptr*);"
Global Const $sTagIAudioSessionManager2 = $sTagIAudioSessionManager & "GetSessionEnumerator hresult(ptr*);" & _
        "RegisterSessionNotification hresult(ptr);" & _
        "UnregisterSessionNotification hresult(ptr);" & _
        "RegisterDuckNotification hresult(wstr;ptr);" & _
        "UnregisterDuckNotification hresult(ptr)"

Global Const $sIID_IAudioSessionEnumerator = "{e2f5bb11-0570-40ca-acdd-3aa01277dee8}"
Global Const $sTagIAudioSessionEnumerator = "GetCount hresult(int*);GetSession hresult(int;ptr*)"

Global Const $sIID_IAudioSessionControl = "{f4b1a599-7266-4319-a8ca-e70acb11e8cd}"
Global Const $sTagIAudioSessionControl = "GetState hresult(int*);GetDisplayName hresult(ptr);" & _
        "SetDisplayName hresult(wstr);GetIconPath hresult(ptr);" & _
        "SetIconPath hresult(wstr;ptr);GetGroupingParam hresult(ptr*);" & _
        "SetGroupingParam hresult(ptr;ptr);RegisterAudioSessionNotification hresult(ptr);" & _
        "UnregisterAudioSessionNotification hresult(ptr);"

Global Const $sIID_IAudioSessionControl2 = "{bfb7ff88-7239-4fc9-8fa2-07c950be9c6d}"
Global Const $sTagIAudioSessionControl2 = $sTagIAudioSessionControl & "GetSessionIdentifier hresult(ptr)" & _
        "GetSessionInstanceIdentifier hresult(ptr);" & _
        "GetProcessId hresult(dword*);IsSystemSoundsSession hresult();" & _
        "SetDuckingPreferences hresult(bool);"

; http://answers.awesomium.com/questions/3398/controlling-the-sound-using-pinvoke-the-volume-mix.html
Global Const $sIID_ISimpleAudioVolume = "{87CE5498-68D6-44E5-9215-6DA47EF883D8}"
Global Const $sTagISimpleAudioVolume = _
        "SetMasterVolume hresult(float;ptr);" & _
        "GetMasterVolume hresult(float*);" & _
        "SetMute hresult(int;ptr);" & _
        "GetMute hresult(int*)"
Global $__oIAudioSessionManager2

Opt("WinTitleMatchMode", 2)

If @Compiled Then
    $processName = "VolumeControlForProcess_Example.exe"
    $nTh = 1
Else
    $processName = "AutoIt3.exe"
    $nTh = 2
EndIf

$sFile = "D:\Music\Phone\Pops\Adele - Hello.mp3"    ; full path of your mp3 file
$aSound = _SoundOpen($sFile)
If @error Then
    MsgBox(0, 'Open Error', "The file cannot be opened.")
    Exit
EndIf

ProcessClose("sndvol.exe")
Run(@SystemDir &"\sndvol.exe")
$hWnd = WinWaitActive(" Mixer - ")
Sleep(3000)

_SoundPlay($aSound)
Sleep(5000)

$PID = _GetProcessID_by_name($processName)

Local $muteState = _Audio_Action("GetMute", $PID, "")
If $muteState <> 0 Then
    _Audio_Action("SetMute", $PID, False)
EndIf

_Audio_Action("SetVolume", $PID, 50/100)

$iVolume = Round(_Audio_Action("GetVolume", $PID, "")*100)
MsgBox(0, '', "Initial Volume: " & $iVolume)

_Audio_Action("SetVolume", $PID, 100/100)

$iVolume = Round(_Audio_Action("GetVolume", $PID, "")*100)
MsgBox(0, '', "Final Volume: " & $iVolume)

_Audio_Action("SetMute", $PID, True)
$muteState = _Audio_Action("GetMute", $PID, "") ; 1 if muted, 0 if unmuted
; 0 if unmuted, 1 if muted via Mixer, -1 if muted via SetMute

If $muteState = 0 Then
    $muteState = "Unmuted"
ElseIf $muteState = -1 Then
    $muteState = "Muted"
EndIf
MsgBox(0, '', "Result of GetMute Command: " & $muteState)

_Audio_Action("SetMute", $PID, False)
$muteState = _Audio_Action("GetMute", $PID, "")
; 0 if unmuted, 1 if muted via Mixer, -1 if muted via SetMute

If $muteState = 0 Then
    $muteState = "Unmuted"
ElseIf $muteState = -1 Then
    $muteState = "Muted"
EndIf
MsgBox(0, '', "Result of GetMute Command: " & $muteState)

Sleep(5000)

_SoundClose($aSound)

Func _GetProcessID_by_name($process_name)
    Local $Process_list = ProcessList()
    Local $ProcessID_Func = 0, $n = 0
    For $i = UBound($Process_list)-1 To 1 Step -1
        If $Process_list[$i][0] = $process_name Then
            $n += 1
            If $n = $nTh Then
                $ProcessID_Func = $Process_list[$i][1]
                ExitLoop
            EndIf
        EndIf
    Next
Return  $ProcessID_Func
EndFunc   ;==>_SoundAPP_setVolume

Func _Audio_Action($action, $pid, $value)   ; Each action takes about 6 milliseconds
    If Not IsObj($__oIAudioSessionManager2) Then
        $__oIAudioSessionManager2 = Audio_GetIAudioSessionManager2()
    EndIf
    If Not IsObj($__oIAudioSessionManager2) Then Return

    Local $pIAudioSessionEnumerator, $oIAudioSessionEnumerator
    If $__oIAudioSessionManager2.GetSessionEnumerator($pIAudioSessionEnumerator) < 0 Then Return
    $oIAudioSessionEnumerator = ObjCreateInterface($pIAudioSessionEnumerator, $sIID_IAudioSessionEnumerator, $sTagIAudioSessionEnumerator)
    If Not IsObj($oIAudioSessionEnumerator) Then Return SetError(1)

    Local $i, $nSessions, $pIAudioSessionControl2, $oIAudioSessionControl2
    Local $ProcessID, $oISimpleAudioVolume
    Local $bMute = 0, $error = 1
    If $oIAudioSessionEnumerator.GetCount($nSessions) >= 0 Then
        For $i = 0 To $nSessions - 1
            If $oIAudioSessionEnumerator.GetSession($i, $pIAudioSessionControl2) >= 0 Then
                $oIAudioSessionControl2 = ObjCreateInterface($pIAudioSessionControl2, $sIID_IAudioSessionControl2, $sTagIAudioSessionControl2)
                If @error Then ContinueLoop
                $oIAudioSessionControl2.GetProcessId($ProcessID)
                If $ProcessID = $pid Then
                    $oISimpleAudioVolume = ObjCreateInterface($pIAudioSessionControl2, $sIID_ISimpleAudioVolume, $sTagISimpleAudioVolume)
                    If @error Then ContinueLoop
                    $oIAudioSessionControl2.AddRef() ;stabilize
                    Switch $action
                        Case "SetVolume"
                            If $oISimpleAudioVolume.SetMasterVolume($value, 0) >= 0 Then
                                $error = 0
                                ExitLoop
                            EndIf
                        Case "GetVolume"
                            If $oISimpleAudioVolume.GetMasterVolume($value) >= 0 Then
                                $error = 0
                                ExitLoop
                            EndIf
                        Case "SetMute"
                            If $oISimpleAudioVolume.SetMute($value, Null) >= 0 Then
                                $error = 0
                                ExitLoop
                            EndIf
                        Case "GetMute"
                            If $oISimpleAudioVolume.GetMute($value) >= 0 Then
                                $error = 0
                                ExitLoop
                            EndIf
                    EndSwitch
                EndIf
            EndIf
        Next
    EndIf
    $oISimpleAudioVolume = 0
    $oIAudioSessionControl2 = 0
    $oIAudioSessionEnumerator = 0
    Return SetError($error, 0, $value)
EndFunc   ;==>_Audio_Action

Func Audio_GetIAudioSessionManager2()
    Local $oIAudioSessionManager2 = 0
    Local Const $eMultimedia = 1, $CLSCTX_INPROC_SERVER = 0x01
    Local $pIMMDevice, $oMMDevice, $pIAudioSessionManager2
    Local $oMMDeviceEnumerator = ObjCreateInterface($sCLSID_MMDeviceEnumerator, $sIID_IMMDeviceEnumerator, $sTagIMMDeviceEnumerator)
    If IsObj($oMMDeviceEnumerator) Then
        If $oMMDeviceEnumerator.GetDefaultAudioEndpoint(0, $eMultimedia, $pIMMDevice) >= 0 Then
            $oMMDevice = ObjCreateInterface($pIMMDevice, $sIID_IMMDevice, $sTagIMMDevice)
            If IsObj($oMMDevice) Then
                If $oMMDevice.Activate(_CLSIDFromString($sIID_IAudioSessionManager2), $CLSCTX_INPROC_SERVER, 0, $pIAudioSessionManager2) >= 0 Then
                    $oIAudioSessionManager2 = ObjCreateInterface($pIAudioSessionManager2, $sIID_IAudioSessionManager2, $sTagIAudioSessionManager2)
                EndIf
                $oMMDevice = 0
            EndIf
        EndIf
        $oMMDeviceEnumerator = 0
    EndIf

    If IsObj($oIAudioSessionManager2) Then
        Return $oIAudioSessionManager2
    EndIf
EndFunc   ;==>Audio_GetIAudioSessionManager2

Func _CLSIDFromString($sGUID)
    Local $tGUID = DllStructCreate("ulong Data1;ushort Data2;ushort Data3;byte Data4[8]")
    DllCall("ole32.dll", "long", "CLSIDFromString", "wstr", $sGUID, "struct*", $tGUID)
    Return $tGUID
EndFunc   ;==>CLSIDFromString

 

Edited by CYCho
Link to comment
Share on other sites

Posted (edited)

According to my observation, 3 AutoIt3.exe processes are created when I run an uncompiled script. I would conjecture  that ProcessList() function lists the processes in chronological order. Out of these 3 processes, I found that the 2nd from the last was the one registered in Windows Volume Mixer. So I revised the code in the previous post so that it works in both compiled and uncompiled scripts.

Edited by CYCho
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...