• Offizieller Beitrag

    Seit Windows Vista verwaltet Windows im Soundmixer für jede Anwendung einen einzelnen Slider. Dadurch sind aber auch unsere ganzen Tools zur Lautstärkeregelung hinfällig.
    Ich habe auch bisher keine Möglichkeit entdeckt, per DLL auf diese Regelung zuzugreifen.
    Also habe ich auf die Wurzeln zurückgegriffen, mich durch die Childs und Child-Childs im Mixer gewühlt um an die Slider-Handle zu kommen. Diese sind sinnigerweise ohne Text und daher nicht direkt mit der Anwendung in Verbindung zu bringen. Aber über das darunter liegende Ctrl (Button für Stummschaltung) ist der Anwendungsname zu ermitteln und durch Positionsvergleich auch zuzuordnen.
    Somit ist es nun möglich diese Slider auszulesen und auch zu setzen. :thumbup:
    Das Mastervolume habe ich im Moment noch aussen vor, da kümmere ich mich später mal drum.

    Edit: Habe gerade einen Fehler entdeckt, ProcessClose() killt leider nicht den Soundvolume-Prozess. Mal schauen, wie sich das ändern läßt.
    Dadurch läßt sich der Mixer nicht per Klick auf das Lautsprechersymbol öffnen, da er bereits offen aber HIDE ist.

    Funktionen:

    _Win7_AppSoundGetApps
    Ermittelt die anwendungspezifischen Slider im Soundmixer (Handle Slider + Anwendungsname)

    _Win7_AppSoundRefresh
    Aktualisieren der Anwendungen / Handle

    _Win7_AppSoundGet
    Gibt die lineare Position des Sliders zurück (0-100)

    _Win7_AppSoundSet
    Setzt die lineare Position des Sliders (0-100)

    Win/_Sound.au3
    [autoit]

    #Region - TimeStamp
    ; 2012-07-26 23:34:55 v 0.1
    #EndRegion - TimeStamp

    [/autoit] [autoit][/autoit] [autoit]

    #include-once
    #include <GuiSlider.au3>
    #include <WinAPI.au3>

    [/autoit] [autoit][/autoit] [autoit]

    OnAutoItExitRegister('_CloseSound')

    [/autoit] [autoit][/autoit] [autoit]

    Global $__aSound_Apps = _Win7_AppSoundGetApps()
    Global $__aSound_iPID

    [/autoit] [autoit][/autoit] [autoit]

    ;===============================================================================
    ; Function Name....: _Win7_AppSoundGet
    ; Description......: Gibt die lineare Position des Sliders zurück (0-100)
    ; .................: für übergebenes Sliderhandle oder Anwendungsname
    ; Parameter(s).....: $hSlider Handle des Slider (aus _Win7_GetApps)
    ; .................: $sApp Name der Anwendung (aus _Win7_GetApps)
    ; Return Value(s)..: Linearer Wert des Sliders
    ; Author(s)........: BugFix ( [email='bugfix@autoit.de'][/email] )
    ;===============================================================================
    Func _Win7_AppSoundGet($hSlider=-1, $sApp='Systemsounds')
    For $i = 0 To UBound($__aSound_Apps) -1
    If $hSlider = -1 Then
    If $__aSound_Apps[$i][1] = $sApp Then
    Return 100 - _GUICtrlSlider_GetPos($__aSound_Apps[$i][0])
    EndIf
    Else
    Return 100 - _GUICtrlSlider_GetPos($hSlider)
    EndIf
    Next
    EndFunc ;==>_Win7_AppSoundGet

    [/autoit] [autoit][/autoit] [autoit]

    ;===============================================================================
    ; Function Name....: _Win7_AppSoundSet
    ; Description......: Setzt die lineare Position des Sliders (0-100)
    ; .................: für übergebenes Sliderhandle oder Anwendungsname
    ; Parameter(s).....: $iVal der zu setzende Wert
    ; .................: $hSlider Handle des Slider (aus _Win7_GetApps)
    ; .................: $sApp Name der Anwendung (aus _Win7_GetApps)
    ; Return Value(s)..: keiner
    ; Author(s)........: BugFix ( [email='bugfix@autoit.de'][/email] )
    ;===============================================================================
    Func _Win7_AppSoundSet($iVal, $hSlider=-1, $sApp='Systemsounds')
    For $i = 0 To UBound($__aSound_Apps) -1
    If $hSlider = -1 Then
    If $__aSound_Apps[$i][1] = $sApp Then
    _GUICtrlSlider_SetPos($__aSound_Apps[$i][0], 100 - $iVal)
    EndIf
    Else
    _GUICtrlSlider_SetPos($hSlider, 100 - $iVal)
    EndIf
    Next
    EndFunc ;==>_Win7_AppSoundSet

    [/autoit] [autoit][/autoit] [autoit]

    ;===============================================================================
    ; Function Name....: _Win7_AppSoundRefresh
    ; Description......: Aktualisieren der Anwendungen / Handle
    ; Author(s)........: BugFix ( [email='bugfix@autoit.de'][/email] )
    ;===============================================================================
    Func _Win7_AppSoundRefresh()
    $__aSound_Apps = _Win7_AppSoundGetApps()
    EndFunc ;==>_Win7_AppSoundRefresh

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ;===============================================================================
    ; Function Name....: _Win7_AppSoundGetApps
    ; Description......: Ermittelt die anwendungspezifischen Slider im Soundmixer
    ; Parameter(s).....: keine
    ; Requirement(s)...: OS Vista oder höher
    ; Return Value(s)..: 2D-Array [[Handle-Slider, Name Anwendung]]
    ; Author(s)........: BugFix ( [email='bugfix@autoit.de'][/email] )
    ;===============================================================================
    Func _Win7_AppSoundGetApps()
    Local Const $GW_CHILD = 5
    Local Const $GW_HWNDNEXT = 2
    $__aSound_iPID = Run("sndvol", @WorkingDir, @SW_HIDE)
    Local $hMixer = WinWait("Lautstärkemixer")
    Local $oldOpt = Opt("WinTitleMatchMode", 2)
    Opt("WinTitleMatchMode", $oldOpt)

    [/autoit] [autoit][/autoit] [autoit]

    Local $iTop, $iLeft, $aPos
    _SystemGetWindowBorder($iTop, $iLeft)
    Local $iInst = 2, $aWnd = WinGetPos("Lautstärkemixer")
    Local $sText = StringReplace(ControlGetText("Lautstärkemixer", '', '[CLASS:ToolbarWindow32; INSTANCE:' & $iInst & ']'), 'Stumm für ', '')
    Local $sApp
    While $sText <> ""
    $aPos = ControlGetPos("Lautstärkemixer", '', '[CLASS:ToolbarWindow32; INSTANCE:' & $iInst & ']')
    $sApp &= $iLeft+$aPos[0]+$aWnd[0] & ',' & $iLeft+$aPos[0]+$aPos[2]+$aWnd[0] +5 & '|' & $sText & @LF
    $iInst += 2
    $sText = StringReplace(ControlGetText("Lautstärkemixer", '', '[CLASS:ToolbarWindow32; INSTANCE:' & $iInst & ']'), 'Stumm für ', '')
    WEnd
    Local $aTmp = StringSplit(StringTrimRight($sApp, 1), @LF), $aSplit

    [/autoit] [autoit][/autoit] [autoit]

    Local $aApp[$aTmp[0]][2]
    For $i = 1 To $aTmp[0]
    $aSplit = StringSplit($aTmp[$i], '|')
    $aApp[$i-1][0] = $aSplit[1]
    $aApp[$i-1][1] = $aSplit[2]
    Next

    [/autoit] [autoit][/autoit] [autoit]

    Local $hChildMix = _WinAPI_GetWindow($hMixer, $GW_CHILD)
    Local $hAppsOuter = _WinAPI_GetWindow($hChildMix, $GW_HWNDNEXT)
    Local $hAppsInner = _WinAPI_GetWindow($hAppsOuter, $GW_CHILD)
    Local $aHWnd[$aTmp[0]][2]
    Local $hTmp = _WinAPI_GetWindow($hAppsInner, $GW_CHILD)
    Local $hSub, $n = 0, $i = 0, $aRange
    While $hTmp <> 0x00000000
    $aHWnd[$i][0] = $hTmp
    $hTmp = _WinAPI_GetWindow($hTmp, $GW_HWNDNEXT)
    $i += 1
    WEnd

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 0 To $aTmp[0] -1
    $hSub = _WinAPI_GetWindow($aHWnd[$i][0], $GW_CHILD)
    $n = 0
    While $hSub <> 0x00000000
    $n += 1
    If $n = 5 Then
    $aHWnd[$i][0] = $hSub
    $tRect = _WinAPI_GetWindowRect($hSub)
    $aHWnd[$i][1] = DllStructGetData($tRect, 3)
    For $j = 0 To $aTmp[0] -1
    $aRange = StringSplit($aApp[$j][0], ',')
    If $aHWnd[$i][1] > $aRange[1] And $aHWnd[$i][1] < $aRange[2] Then
    $aHWnd[$i][1] = $aApp[$j][1]
    ExitLoop
    EndIf
    Next
    ExitLoop
    EndIf
    $hSub = _WinAPI_GetWindow($hSub, $GW_HWNDNEXT)
    WEnd
    Next
    Return $aHWnd
    EndFunc ;==>_Win7_AppSoundGetApps

    [/autoit] [autoit][/autoit] [autoit]

    Func _CloseSound()
    ProcessClose($__aSound_iPID)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    ;===============================================================================
    ; Function Name....: _SystemGetWindowBorder
    ; Description......: Calculates side and top border of window
    ; Author(s)........: BugFix ( [email='bugfix@autoit.de'][/email] )
    ;===============================================================================
    Func _SystemGetWindowBorder(ByRef $_iTopBorder, ByRef $_iSideBorder)
    Local Const $SM_CYCAPTION = 4, $SM_CYEDGE = 46, $SM_CYBORDER = 6, $SM_CXBORDER = 5, $SM_CXEDGE = 45
    Local $aMetrics[5][2] = [[$SM_CYCAPTION], [$SM_CYEDGE], [$SM_CYBORDER], [$SM_CXBORDER], [$SM_CXEDGE]]
    Local $dll = DllOpen("user32.dll"), $aRet
    For $i = 0 To 4
    $aRet = DllCall($dll, "int", "GetSystemMetrics", "int", $aMetrics[$i][0])
    If IsArray($aRet) Then $aMetrics[$i][1] = $aRet[0]
    Next
    DllClose($dll)
    $_iTopBorder = $aMetrics[0][1] + $aMetrics[1][1] + $aMetrics[2][1]
    $_iSideBorder = $aMetrics[3][1] + $aMetrics[4][1]
    EndFunc ;==>_SystemGetWindowBorder

    [/autoit]
  • Der Lautstärkemixer behält nach dem Beenden noch die Eigenschaft @SW_HIDE.
    Nach WinSetState ('Lautstärkemixer', '', @SW_SHOW) kann man ihn wieder aufmachen... Eine Lösung dafür wäre nice... ;)

    Ansonsten sehr schön. Ist zwar nicht die sauberste Lösung, aber es funktioniert - und das sogar relativ schnell!

  • coole sache dass!

    hier meine version für open und close

    Spoiler anzeigen

    eine kleine gui kann man ja proggen damit das versteckte fenster sichtbar gemacht werden kann ... (in meinem fall geht das per LCD-button)
    sind ein par sachen drin die man nicht braucht, aber nur mal so als bsp
    anmerkung: ich brauche das "international", daher bissle umständlicher...
    ..

    2 Mal editiert, zuletzt von UPIA (27. Juli 2012 um 01:01)

    • Offizieller Beitrag

    ich brauche das "international", daher bissle umständlicher...


    Nun, die von mir genutzte Variante der Slider-Handle-Zuordnung ist im Moment auf die deutsche Version zugeschnitten. Aber da werde ich mal sehen, ob sich das noch verallgemeinern läßt.

    Und eine Bitte: Nutze nicht den "#"-Tag für Code, sondern verwende den Tag mit dem AutoIt-Logo (per Hand geschrieben: [ autoit ] Code [ /autoit ] - ohne Spaces).

  • ich hab das mal mit virtualbox auf verschiedenen betriebssystemen getestet, weil mir langweilig war :D

    und es funkt super :)

    Windows 7 64x Home Premium mit Aero Style (mein laptop) funkt hier so wie es soll
    Windows 7 32x Enterprise ohne Aero Style (VM) funkt hier auch wie geplant
    Windows 8 32x Customer Preview (VM) funkt auch hier wie geplant
    Win8 ist mir fast krepiert wegen dem IE (musste ja nur die 2 dateien hier downloaden), habe dann aber schnell FF installiert und dann ging alles problemlos <3
    hab leider kein Win Vista :D

    aber... hab es just 4 fun auch auf Windows XP 32x Professional (VM) getestet
    und dort ging es (natürlich) nicht
    in der Funktion _Win7_AppSoundGetApps() blieb das Script in irgenteiner schleife stecken
    man sollte eine OS prüfung mit einbauen und dann nen error zurück geben ;)

    mfg

  • okay, hatte den autoit-code button bislang übersehen :wacko:
    für den mixer ist die "multi-language version" über die pID recht einfach.
    für die Geräte komme ich nur über WinGetHandle("Sound", "") ran,
    EDIT: heisst aber scheinbar bei allen versionen so. nein nur englisch und deutsch, aber funk.eu/ssd zeight wie es moeglich ist über die pid das handle zu bekommen.

    [autoit]


    $iPID_Sound = Run("rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,0")

    [/autoit]


    bei den geräten ist der status das problem, bei mir: der user muss jeden einzelnen geräte-status ersma in ne config schreiben (aus dem "Sound" window abschreiben). evtl. würde das auch mit pixelsearch über die symbole im listview gehen .. aber auch das würde scrolling probleme mit sich führen, da das "Sound" window sich nicht resizen lässt ...

    EDIT2: kurze anmerkung zum "Internationalen" MasterMuteStatus
    da ich in meinem skript ohnehin die Geräte auslese, also das aktuelle Standaradausgabegerät ($currMaster) kenne mach ich einfach folgendes:
    1) mit WinGetText() den text1 auslesen mit der Zeile des Standaradausgabegerätes
    2) mit Send("{VOLUME_UP}"); Send("{VOLUME_DOWN}"); Send("{VOLUME_MUTE}") das gerät für 30ms auf gemutet setzen
    3) mit WinGetText() nochmal den text2 auslesen .. text1 = text2 => gerät war gemutet .. text1 <> text2 => gerät war laut => mit Send("{VOLUME_UP}") den orginalzustand wiederherstellen
    ausserdem merke ich mir den prefix....suffix damit ich beim gerätewechsel wieder bescheid weiss, welcher mute-status gerade gesetzt ist

    Spoiler anzeigen
    [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _MixerCheckMasterMute($currMaster)
    Local $txt = WinGetText($mixer_hwnd, "")
    Local $txtA = StringSplit($txt, @CRLF)
    local $txtID = -1
    local $txtMS_ini = ""
    local $txtMS_mute = ""
    Local $txtMS_unmute = ""
    For $i = 0 To $txtA[0]
    ;ConsoleWrite($txtA[$i] & @CRLF)
    If StringInStr($txtA[$i], $currMaster) > 0 Then
    $txtID = $txtA[0] - ($i + 1)
    $txtMS_ini = $txtA[$i]
    EndIf
    Next
    $mixer_MMtID = $txtID
    If $txtID > -1 Then
    ; Force Mute
    Send("{VOLUME_UP}")
    Send("{VOLUME_DOWN}")
    Send("{VOLUME_MUTE}")
    Sleep(50)
    $txt = WinGetText($mixer_hwnd, "")
    $txtA = StringSplit($txt, @CRLF)
    If $txtA[Int($txtA[0]-$txtID-1)] = $txtMS_ini Then
    $txtMS_mute = $txtMS_ini
    ;ConsoleWrite($currMaster & " was muted!" & @CRLF)
    Local $txtP = StringSplit($txtMS_mute, $currMaster, 1)
    If $txtP[0] = 2 Then
    $mixer_MMtID = $txtID
    $mixer_MMtM1 = $txtP[1]
    $mixer_MMtM2 = $txtP[2]
    ;ConsoleWrite($mixer_MMtID & ", " & $mixer_MMtM1 & ", " & $mixer_MMtM2 & @CRLF)
    EndIf
    Return 0
    Else
    $txtMS_mute = $txtA[Int($txtA[0]-$txtID-1)]
    ;ConsoleWrite($currMaster & " was loud!" & @CRLF)
    Send("{VOLUME_MUTE}")
    Local $txtP = StringSplit($txtMS_mute, $currMaster, 1)
    If $txtP[0] = 2 Then
    $mixer_MMtID = $txtID
    $mixer_MMtM1 = $txtP[1]
    $mixer_MMtM2 = $txtP[2]
    ;ConsoleWrite($mixer_MMtID & ", " & $mixer_MMtM1 & ", " & $mixer_MMtM2 & @CRLF)
    EndIf
    Return 1
    EndIf
    Else
    Return - 1
    EndIf
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit]

    4 Mal editiert, zuletzt von UPIA (7. August 2012 um 01:45)

  • Zitat

    ausserdem merke ich mir den prefix....suffix damit ich beim gerätewechsel wieder bescheid weiss, welcher mute-status gerade gesetzt ist

    so, hab jetzt noch ne loesung gefunden den Mute-Status der einzelnen programme auszulesen (per tooltip)
    + klappt auch im versteckten fenster
    + klappt ohne zu scrollen
    funzt natürlich auch 'international'

    Spoiler anzeigen
    [autoit]


    ;$no = Anzahl der Programme im Mixer (also ohne die 'Hauptlautstärke' gezählt) ; die haben wir ja schon s.o.
    Func _MixerGetMuteStrings($no)
    Local $delay1 = 1100
    Local $tt[$no+1]
    $tt[0] = $no
    Local $wl
    Local $lastList = '',$data = '',$List = ''
    local $prefix = "[CLASS:ToolbarWindow32; INSTANCE:"
    local $suffix = "]"
    For $i = 1 To $no
    Local $ctrl = $prefix & String($i*2) & $suffix
    ControlFocus($mixer_hwnd, "", $ctrl)
    Sleep($delay1)
    ; --> Get Open Tooltips (from someone else)
    $wl = WinList("[class:tooltips_class32]")
    For $n = 1 To $wl[0][0]
    If BitAND(WinGetState($wl[$n][1]), 2) Then
    $List = WinGetTitle($wl[$n][1])
    EndIf
    Next
    If $List <> $lastList And $List <> '' Then ; hier sollte noch geprüft werden ob der Appname im String $List ist (unique) falls noch ein tooltip offen ist
    $tt[$i] = $List
    EndIf
    ; -->
    Next
    Return $tt
    EndFunc

    [/autoit]

    den text kann man dann auf inhalt der variablen aus dem obigen script
    $mixer_MMtM1
    $mixer_MMtM2
    untersuchen

    mit dieser 'technik' lassen sich auch einzelne mute-states direkt abfragen, wenn man ihre listenposition und den namen kennt (s.o.)

    4 Mal editiert, zuletzt von UPIA (8. August 2012 um 02:21)