List processes that have sound?

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
Lamron750
Posts: 8
Joined: 02 Feb 2019, 19:31

List processes that have sound?

Post by Lamron750 » 19 Sep 2021, 15:43

I'm trying to figure out a way to get a list of processes that have sound and appear in the Volume Mixer (WIN7).

I have tried parsing through ALL processes and checking each for volume. This method works to find all the processes with sound, but its slow and inefficient.

I'm using this function I found on the forums to get the volume, but I don't understand how it works. I see that it checks if "currentProcessId" matches the PID you want to change. Would it be possible to adapt this to return all PIDs that have volume/sound? Or is there some other way of getting a list of processes with sound?

Code: Select all

GetAppVolume(PID)
{
    Local MasterVolume := ""

    IMMDeviceEnumerator := ComObjCreate("{BCDE0395-E52F-467C-8E3D-C4579291692E}", "{A95664D2-9614-4F35-A746-DE8DB63617E6}")
    DllCall(NumGet(NumGet(IMMDeviceEnumerator+0)+4*A_PtrSize), "UPtr", IMMDeviceEnumerator, "UInt", 0, "UInt", 1, "UPtrP", IMMDevice, "UInt")
    ObjRelease(IMMDeviceEnumerator)

    VarSetCapacity(GUID, 16)
    DllCall("Ole32.dll\CLSIDFromString", "Str", "{77AA99A0-1BD6-484F-8BC7-2C654C9A9B6F}", "UPtr", &GUID)
    DllCall(NumGet(NumGet(IMMDevice+0)+3*A_PtrSize), "UPtr", IMMDevice, "UPtr", &GUID, "UInt", 23, "UPtr", 0, "UPtrP", IAudioSessionManager2, "UInt")
    ObjRelease(IMMDevice)

    DllCall(NumGet(NumGet(IAudioSessionManager2+0)+5*A_PtrSize), "UPtr", IAudioSessionManager2, "UPtrP", IAudioSessionEnumerator, "UInt")
    ObjRelease(IAudioSessionManager2)

    DllCall(NumGet(NumGet(IAudioSessionEnumerator+0)+3*A_PtrSize), "UPtr", IAudioSessionEnumerator, "UIntP", SessionCount, "UInt")
    Loop % SessionCount
    {
        DllCall(NumGet(NumGet(IAudioSessionEnumerator+0)+4*A_PtrSize), "UPtr", IAudioSessionEnumerator, "Int", A_Index-1, "UPtrP", IAudioSessionControl, "UInt")
        IAudioSessionControl2 := ComObjQuery(IAudioSessionControl, "{BFB7FF88-7239-4FC9-8FA2-07C950BE9C6D}")
        ObjRelease(IAudioSessionControl)

        DllCall(NumGet(NumGet(IAudioSessionControl2+0)+14*A_PtrSize), "UPtr", IAudioSessionControl2, "UIntP", currentProcessId, "UInt")
        If (PID == currentProcessId)
        {
            ISimpleAudioVolume := ComObjQuery(IAudioSessionControl2, "{87CE5498-68D6-44E5-9215-6DA47EF883D8}")
            DllCall(NumGet(NumGet(ISimpleAudioVolume+0)+4*A_PtrSize), "UPtr", ISimpleAudioVolume, "FloatP", MasterVolume, "UInt")
            ObjRelease(ISimpleAudioVolume)
        }
        ObjRelease(IAudioSessionControl2)
    }
    ObjRelease(IAudioSessionEnumerator)

    Return Round(MasterVolume * 100)
}

User avatar
mikeyww
Posts: 26602
Joined: 09 Sep 2014, 18:38

Re: List processes that have sound?

Post by mikeyww » 19 Sep 2021, 16:01

Code: Select all

text =
WinGet, win, List
Loop, %win% {
 WinGet, pid, PID, % wTitle := "ahk_id " win%A_Index%
 If !vol := GetAppVolume(pid)
  Continue
 WinGet, proc, ProcessName, %wTitle%
 text .= "`n" proc ": " vol
}
text := SubStr(text, 2)
Sort, text, U
MsgBox, 64, Result, %text%
This seemed to miss a program here or there, not sure why.

Lamron750
Posts: 8
Joined: 02 Feb 2019, 19:31

Re: List processes that have sound?

Post by Lamron750 » 19 Sep 2021, 16:24

Searching through the windows instead of processes is a great idea. This method takes 1/4 the time. Almost anything making sound should have a window, so this should work just fine. Thanks!

Bunker D
Posts: 6
Joined: 11 Dec 2021, 16:07

Re: List processes that have sound?

Post by Bunker D » 11 Dec 2021, 18:29

mikeyww wrote:
19 Sep 2021, 16:01
This seemed to miss a program here or there, not sure why.
Going through the windows doesn't seem to be a great idea actually. A good number of application uses several processes, and from what I've found, often, the process found in the volume mixer is not the same as the one tied to the window.
You can use this function to list the processes listed in the Volume Mixer:

Code: Select all

SoundPIDs()
{
    PIDs := []

    IMMDeviceEnumerator := ComObjCreate( "{BCDE0395-E52F-467C-8E3D-C4579291692E}", "{A95664D2-9614-4F35-A746-DE8DB63617E6}" )
    DllCall( NumGet( NumGet( IMMDeviceEnumerator+0 ) + 4*A_PtrSize ), "UPtr", IMMDeviceEnumerator, "UInt", 0, "UInt", 1, "UPtrP", IMMDevice, "UInt" )
    ObjRelease(IMMDeviceEnumerator)

    VarSetCapacity( GUID, 16 )
    DllCall( "Ole32.dll\CLSIDFromString", "Str", "{77AA99A0-1BD6-484F-8BC7-2C654C9A9B6F}", "UPtr", &GUID)
    DllCall( NumGet( NumGet( IMMDevice+0 ) + 3*A_PtrSize ), "UPtr", IMMDevice, "UPtr", &GUID, "UInt", 23, "UPtr", 0, "UPtrP", IAudioSessionManager2, "UInt" )
    ObjRelease( IMMDevice )

    DllCall( NumGet( NumGet( IAudioSessionManager2+0 ) + 5*A_PtrSize ), "UPtr", IAudioSessionManager2, "UPtrP", IAudioSessionEnumerator, "UInt" )
    ObjRelease( IAudioSessionManager2 )

    DllCall( NumGet( NumGet( IAudioSessionEnumerator+0 ) + 3*A_PtrSize ), "UPtr", IAudioSessionEnumerator, "UIntP", SessionCount, "UInt" )
    Loop % SessionCount
    {
        DllCall( NumGet( NumGet( IAudioSessionEnumerator+0 ) + 4*A_PtrSize ), "UPtr", IAudioSessionEnumerator, "Int", A_Index-1, "UPtrP", IAudioSessionControl, "UInt" )
        IAudioSessionControl2 := ComObjQuery( IAudioSessionControl, "{BFB7FF88-7239-4FC9-8FA2-07C950BE9C6D}" )
        ObjRelease( IAudioSessionControl )

        DllCall( NumGet( NumGet( IAudioSessionControl2+0 ) + 14*A_PtrSize ), "UPtr", IAudioSessionControl2, "UIntP", PID, "UInt" )
        PIDs.push( PID )
        ObjRelease( IAudioSessionControl2 )
    }
    ObjRelease( IAudioSessionEnumerator )

    return PIDs
}
For example:

Code: Select all

PIDs := SoundPIDs()

result := "Process IDs in the Volume Mixer:"
for i, PID in PIDs
{
    if PID
        result .= "`n" . PID . "  (" . ExeFromPID( PID ) . ")"
    else
        result .= "`n" . PID . "  (System Sounds)"
}
MsgBox % result

ExeFromPID( PID )
{
    PHandle := DllCall( "OpenProcess", "uint", 0x0010|0x0400, "Int", false, "UInt", PID )
    if ( ErrorLevel or PHandle = 0 )
        return "???"
    name_size = 1023
    VarSetCapacity( PName, name_size )
    DllCall( "psapi.dll\GetModuleFileNameEx" . ( A_IsUnicode ? "W" : "A" ), "uint", PHandle, "uint", 0, "str", PName, "uint", name_size )
    DllCall( "CloseHandle", PHandle )
    SplitPath PName, PName
    return PName
}
However, I have no idea how to identify what applications are currently making sound. There should be a way to now (after all, the volume mixer shows the sound), but I have no idea how to identify the proper dll call.

Also, note that I haven't checked if the volume is > 0. You could replace:

Code: Select all

PIDs.push( PID )
by:

Code: Select all

ISimpleAudioVolume := ComObjQuery(IAudioSessionControl2, "{87CE5498-68D6-44E5-9215-6DA47EF883D8}")
DllCall( NumGet( NumGet( ISimpleAudioVolume+0 ) + 4*A_PtrSize ), "UPtr", ISimpleAudioVolume, "FloatP", Volume, "UInt" )
if Volume > 0
    PIDs.push( PID )
ObjRelease(ISimpleAudioVolume)
but it will still list the processes that are muted (while their unmuted volume is remembered to be > 0).
(I'd love to know how to read/write the mute status.)

Post Reply

Return to “Ask for Help (v1)”