[SOLUTION] Voicemeeter prevents sleep - Legacy Kernel Caller

The Virtual Audio Mixer discussions and support...
Post Reply
Dschogo
Posts: 1
Joined: Tue Apr 17, 2018 4:16 pm

[SOLUTION] Voicemeeter prevents sleep - Legacy Kernel Caller

Post by Dschogo »

Hey there,

after wrapping my head around for 2 days with various approaches how to get back the precious windows sleep while using Voicemeeter, here is a potential solution :D (Only a prototype without all edge cases but it'll work)

First: whats the issue preventing windows from sleep?

Voicemeeter streams audio from all devices selected within - even if there is "none". Windows interprets that as "You're listening to smt, I shouldn't go sleep since it could be important" - Fair enough, usually audio devices are only "opened/streamed to" when there is actual audio. Most Audio devices can be blacklisted via `powercfg /requestsoverride`. Most, sadly USB devices without proper drivers usually fall back to "Legacy Kernel Caller" (At least that's what I've observed/makes the most sense to me). That one can be "blacklisted" but windows kindly ignores that and wont go to sleep.

What can we do about it?
First approach - abuse the screensaver, upon start of that, windows emits an event and we can use that in the task scheduler to go to hibernation. See: http://english.edgeoftheworld.cz/2019/0 ... ound-card/ But we lose our good black screen before the idle, since the screensaver wont start if we are already in our lets say 5 min idle black screen.

The current "ultimate" solution - for me at least ^^
It Requires AHK2.

The AHK script runs all the time in the background - don't worry ~2mb of ram. It checks manually the powercfg /requests if any program wants to keep the PC awake. (Spotify/YouTube in a Browser/Netflix all make a request separate to the audio) We can use that to differ if its only Voicemeeter or actually a user activity. (The code looks a bit cryptic there since we don't want to have a flashing CMD window every minute for the powercfg command, it just runs that in the background)
You can see my blacklist below, I intentionally didn't include VAIO3 since that gets only used when I'm in a Discord call, and Discord sadly doesn't send a request. So I'm using that to figure out if I'm in a call. (If another Program behaves differently, AHK gives you plenty of functions to determine if you're active)

Furthermore the script checks if the user made an input in the last 10mins, since we don't want to sleep if we use the PC but no program "forbids" windows to do so. -As far as I can tell, that's the same Timer variable Windows Itself uses for the idle blackscreen and in combination with powercfg if it should go to sleep.

If we don't sign in, Voicemeeter and the AHK isn't running, so the default behavior with sleep is used. The Script should also work on the lockscreen (after sign in win+L).

How do I use it?
Install AHKV2, download that script, change timings, filters, put it in the task-scheduler on login. WITH HIGHEST PRIVILEGES AND ONLY ON CURRENT USER, otherwise the script runs in another session and cant check for user input. Also don't forget to append the "force" argument, otherwise it will only give you an MsgBox. (Designed for testing purposes, while setting up filters)

After that explanation- Happy Sleeping :D

PS:
If you want to sleep and not hibernate you have to disable it completly via powercfg -H off. (THe link above has a good explanation)
You can also check various things of Voicemeeter inside of AHK - just google there are ton of examples.
If you uncomment the following before the last loop, you can use the pause button to initiate the "blackscreen" for idle (for powersaving)

Code: Select all

;Pause:: {
;    Sleep(1000)
;    SendMessage(0x0112, 0xF170, 2,, "Program Manager")
;    return
;}

Code: Select all

#Requires AutoHotkey v2.0
#SingleInstance Force

TestIdle() {
    strData := JEE_RunGetStdOut(A_ComSpec " /c powercfg /requests")
    if (StrLen(strData) < 10) {
        return false
    }

    blacklist := [
        "DISPLAY:`r`nNone.`r`n`r`nSYSTEM:`r`n",
        "[DRIVER] VB-Audio VoiceMeeter AUX VAIO (ROOT\MEDIA\0000)`r`nAn audio stream is currently in use.`r`n",
        "[DRIVER] VB-Audio VoiceMeeter VAIO (ROOT\MEDIA\0001)`r`nAn audio stream is currently in use.`r`n",
        ; "[DRIVER] VB-Audio VoiceMeeter VAIO3 (ROOT\\MEDIA\\0002)`nAn audio stream is currently in use.`n"
        "[DRIVER] VB-Audio Virtual Cable (ROOT\MEDIA\0003)`r`nAn audio stream is currently in use.`r`n",
        "[DRIVER] Realtek High Definition Audio (HDAUDIO\FUNC_01&VEN_10EC&DEV_1220&SUBSYS_1458A0C3&REV_1001\5&1eda78b&0&0001)`r`nAn audio stream is currently in use.`r`n",
        "[DRIVER] USB Audio Device (USB\VID_B58E&PID_6E77&MI_00\8&31b4449a&0&0000)`r`nAn audio stream is currently in use.`r`n",
        "[DRIVER] USB Audio Device (USB\VID_0D8C&PID_013C&MI_00\7&1245e82a&0&0000)`r`nAn audio stream is currently in use.`r`n",
        "[DRIVER] Legacy Kernel Caller`r`n",
        "`r`nAWAYMODE:`r`nNone.`r`n`r`nEXECUTION:`r`nNone.`r`n`r`nPERFBOOST:`r`nNone.`r`n`r`nACTIVELOCKSCREEN:`r`nNone.`r`n`r`n",
    ]
    for index, item in blacklist {
        strData := StrReplace(strData, item, "")
    }

    return strData == ""
}

; https://www.reddit.com/r/AutoHotkey/comments/ey3pmb/comment/fggmaqk/
; ported to ahk v2 by me
JEE_RunGetStdOut(vTarget, vSize:="")
{
	DetectHiddenWindows(True)
    run(A_ComSpec,, "Hide", &vPID)
    WinWait("ahk_pid " vPID)
	DllCall("kernel32\AttachConsole", "UInt",vPID)
	oShell := ComObject("WScript.Shell")
	oExec := oShell.Exec(vTarget)
	vStdOut := ""
	if !(vSize = "")
		VarSetStrCapacity(vStdOut, vSize)
	while !oExec.StdOut.AtEndOfStream
		vStdOut := oExec.StdOut.ReadAll()
	DllCall("kernel32\FreeConsole")
	; Process, Close, % vPID
    ProcessClose(vPID)
	return vStdOut
}

; sleep until 10 minutes system time passed, so we dont directly sleep after boot - would be annoying if we mess with the filter
if (10 * 60 * 1000 - A_TickCount > 0) {
    Sleep(10 * 60 * 1000 - A_TickCount)
}


; handle hotkey pause button
;Pause:: {
;    Sleep(1000)
;    SendMessage(0x0112, 0xF170, 2,, "Program Manager")
;    return
;}
; turn off monitors

Loop {
    if (A_TimeIdle > 10 * 60 * 1000) { ; change here the timeout
        if (TestIdle()) {
            if (A_Args.Length > 0 && A_Args[1] == "force") {
                Run("rundll32.exe powrprof.dll,SetSuspendState 0,1,0")
                ; MsgBox("Idle for more than 10 minutes, suspending")
            } else {
                MsgBox("Idle for more than 10 minutes, but not suspending because tests")
            }
        }
    }
    Sleep(60 * 1000) ; 1 minute
}
Post Reply