PowerSHELL İle "Windows'ta Hasas Ondalıklı" Ses Seviyesi Ayarlayın/Kullanın...

Programlama ve Script dilleri konusunda bilgi paylaşım alanıdır.
Cevapla
Kullanıcı avatarı
TRWE_2012
Zettabyte1
Zettabyte1
Mesajlar: 15499
Kayıt: 25 Eyl 2013, 13:38
cinsiyet: Erkek
Teşekkür etti: 2657 kez
Teşekkür edildi: 5543 kez

PowerSHELL İle "Windows'ta Hasas Ondalıklı" Ses Seviyesi Ayarlayın/Kullanın...

Mesaj gönderen TRWE_2012 »

Merhabalar
Resim
KOD İÇERİĞİ : (WindowsVolumeManager.ps1)

Kod: Tümünü seç

# ================================================
# WindowsVolumeManager.ps1 - Core Audio API based precise volume manager
# Usage: .\WindowsVolumeManager.ps1 [-Increase] [-Decrease] [-Mute] [-Unmute]
#         [-ShowDevice] [-ListDevices] [-Menu] [-SetVolume <double>] [-Step <double>]
# Example: .\WindowsVolumeManager.ps1 -SetVolume 55.67
# ================================================

param(
    [switch]$Increase,
    [switch]$Decrease,
    [switch]$Mute,
    [switch]$Unmute,
    [switch]$ShowDevice,
    [switch]$ListDevices,
    [switch]$Menu,
    [string]$SetVolume,
    [string]$Step = "5"
)

$ErrorActionPreference = "Stop"

function ConvertTo-InvariantDouble {
    param([string]$Value)
    $result = 0.0
    $ok = [double]::TryParse(
        $Value,
        [System.Globalization.NumberStyles]::Float,
        [System.Globalization.CultureInfo]::InvariantCulture,
        [ref]$result
    )
    if (-not $ok) {
        throw "Invalid numeric value: '$Value'. Use a dot as decimal separator, e.g. 55.67"
    }
    return $result
}

# Parse Step once up front using invariant culture (avoids locale-dependent
# comma/dot decimal separator issues, e.g. on Turkish (tr-TR) system locale).
$StepValueParsed = ConvertTo-InvariantDouble -Value $Step

try {

# ---------- Core Audio API COM Interop Definitions ----------

$AudioInteropCode = @"
using System;
using System.Runtime.InteropServices;

[ComImport, Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IAudioEndpointVolume
{
    int RegisterControlChangeNotify(IntPtr pNotify);
    int UnregisterControlChangeNotify(IntPtr pNotify);
    int GetChannelCount(out int pnChannelCount);
    int SetMasterVolumeLevel(float fLevelDB, Guid pguidEventContext);
    int SetMasterVolumeLevelScalar(float fLevel, Guid pguidEventContext);
    int GetMasterVolumeLevel(out float pfLevelDB);
    int GetMasterVolumeLevelScalar(out float pfLevel);
    int SetChannelVolumeLevel(uint nChannel, float fLevelDB, Guid pguidEventContext);
    int SetChannelVolumeLevelScalar(uint nChannel, float fLevel, Guid pguidEventContext);
    int GetChannelVolumeLevel(uint nChannel, out float pfLevelDB);
    int GetChannelVolumeLevelScalar(uint nChannel, out float pfLevel);
    int SetMute(bool bMute, Guid pguidEventContext);
    int GetMute(out bool pbMute);
    int GetVolumeStepInfo(out uint pnStep, out uint pnStepCount);
    int VolumeStepUp(Guid pguidEventContext);
    int VolumeStepDown(Guid pguidEventContext);
    int QueryHardwareSupport(out uint pdwHardwareSupportMask);
    int GetVolumeRange(out float pflVolumeMindB, out float pflVolumeMaxdB, out float pflVolumeIncrementdB);
}

[ComImport, Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IMMDevice
{
    int Activate(ref Guid iid, int dwClsCtx, IntPtr pActivationParams, out IAudioEndpointVolume ppInterface);
    int OpenPropertyStore(int stgmAccess, out IntPtr ppProperties);
    int GetId(out IntPtr ppstrId);
    int GetState(out int pdwState);
}

[ComImport, Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IMMDeviceEnumerator
{
    int EnumAudioEndpoints(int dataFlow, int dwStateMask, out IntPtr ppDevices);
    int GetDefaultAudioEndpoint(int dataFlow, int role, out IMMDevice ppEndpoint);
    int GetDevice(string pwstrId, out IMMDevice ppDevice);
    int RegisterEndpointNotificationCallback(IntPtr pClient);
    int UnregisterEndpointNotificationCallback(IntPtr pClient);
}

[ComImport, Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")]
public class MMDeviceEnumeratorComObject
{
}

public static class AudioEndpointHelper
{
    // Internal only: raw COM interface objects must never be returned to PowerShell.
    // PowerShell resolves COM object members through IDispatch, and this interface
    // is a pure vtable (IUnknown-based) interface with no IDispatch support, so any
    // method call attempted directly from PowerShell script code fails with
    // "does not contain a method named ...". All COM calls must stay inside
    // compiled C# static methods below, exposing only plain .NET types (float, bool).
    private static IAudioEndpointVolume GetEndpointVolume()
    {
        var enumerator = (IMMDeviceEnumerator)(new MMDeviceEnumeratorComObject());
        IMMDevice device;
        // dataFlow: eRender = 0 (playback devices)
        // role: eMultimedia = 1
        enumerator.GetDefaultAudioEndpoint(0, 1, out device);
        Guid iidAudioEndpointVolume = typeof(IAudioEndpointVolume).GUID;
        IAudioEndpointVolume endpointVolume;
        // CLSCTX_ALL = 23
        device.Activate(ref iidAudioEndpointVolume, 23, IntPtr.Zero, out endpointVolume);
        return endpointVolume;
    }

    public static float GetVolumeScalar()
    {
        var endpointVolume = GetEndpointVolume();
        float level;
        endpointVolume.GetMasterVolumeLevelScalar(out level);
        return level;
    }

    public static void SetVolumeScalar(float level)
    {
        var endpointVolume = GetEndpointVolume();
        endpointVolume.SetMasterVolumeLevelScalar(level, Guid.Empty);
    }

    public static bool GetMuteState()
    {
        var endpointVolume = GetEndpointVolume();
        bool muted;
        endpointVolume.GetMute(out muted);
        return muted;
    }

    public static void SetMuteFlag(bool mute)
    {
        var endpointVolume = GetEndpointVolume();
        endpointVolume.SetMute(mute, Guid.Empty);
    }
}
"@

Add-Type -TypeDefinition $AudioInteropCode -Language CSharp -ErrorAction Stop

# ---------- Functions ----------

function Get-VolumeLevel {
    $level = [AudioEndpointHelper]::GetVolumeScalar()
    return [Math]::Round($level * 100, 2)
}

function Set-VolumeLevel {
    param([double]$Percent)
    if ($Percent -lt 0) { $Percent = 0 }
    if ($Percent -gt 100) { $Percent = 100 }
    $scalarValue = [float]($Percent / 100.0)
    [AudioEndpointHelper]::SetVolumeScalar($scalarValue)
    Write-Host "Volume set to -> $(Get-VolumeLevel)%"
}

function Add-VolumeStep {
    param([double]$StepValue)
    $currentLevel = Get-VolumeLevel
    $targetLevel = $currentLevel + $StepValue
    Set-VolumeLevel -Percent $targetLevel
}

function Remove-VolumeStep {
    param([double]$StepValue)
    $currentLevel = Get-VolumeLevel
    $targetLevel = $currentLevel - $StepValue
    Set-VolumeLevel -Percent $targetLevel
}

function Set-MuteState {
    param([bool]$MuteState)
    [AudioEndpointHelper]::SetMuteFlag($MuteState)
    if ($MuteState) {
        Write-Host "Audio muted."
    } else {
        Write-Host "Audio unmuted."
    }
}

function Show-DefaultDevice {
    Write-Host "=== Active Playback Device ==="
    try {
        $devices = Get-CimInstance -ClassName Win32_SoundDevice -ErrorAction Stop | Where-Object { $_.Status -eq "OK" }
        foreach ($device in $devices) {
            Write-Host "  Name: $($device.Name)"
            Write-Host "  Manufacturer: $($device.Manufacturer)"
        }
    } catch {
        Write-Host "  Unable to enumerate devices via CIM: $($_.Exception.Message)"
    }
}

function Show-DeviceList {
    Write-Host "=== Playback Device List ==="
    try {
        Get-CimInstance -ClassName Win32_SoundDevice -ErrorAction Stop | ForEach-Object {
            Write-Host "  - $($_.Name)"
        }
    } catch {
        Write-Host "  Unable to enumerate devices via CIM: $($_.Exception.Message)"
    }
}

function Show-MainMenu {
    while ($true) {
        Clear-Host
        Write-Host "==============================="
        Write-Host "  WINDOWS VOLUME MANAGER"
        Write-Host "  Level: $(Get-VolumeLevel)%"
        Write-Host "==============================="
        Write-Host "  1) Increase Volume (+$StepValueParsed%)"
        Write-Host "  2) Decrease Volume (-$StepValueParsed%)"
        Write-Host "  3) Mute"
        Write-Host "  4) Unmute"
        Write-Host "  5) Show Active Device"
        Write-Host "  6) Device List"
        Write-Host "  7) Set Precise Volume (e.g. 55.67)"
        Write-Host "  0) Exit"
        Write-Host "==============================="
        $userChoice = Read-Host "Choice"
        switch ($userChoice) {
            "1" { Add-VolumeStep -StepValue $StepValueParsed }
            "2" { Remove-VolumeStep -StepValue $StepValueParsed }
            "3" { Set-MuteState -MuteState $true }
            "4" { Set-MuteState -MuteState $false }
            "5" { Show-DefaultDevice }
            "6" { Show-DeviceList }
            "7" {
                $inputValue = Read-Host "Enter precise volume percent (0-100, e.g. 55.67)"
                try {
                    $parsedValue = ConvertTo-InvariantDouble -Value $inputValue
                    Set-VolumeLevel -Percent $parsedValue
                } catch {
                    Write-Host "Invalid value. Use a dot as decimal separator, e.g. 55.67"
                }
            }
            "0" { Write-Host "Exiting..."; break }
            default { Write-Host "Invalid choice." }
        }
        Read-Host "Press Enter to continue"
    }
}

# ---------- Argument Dispatcher ----------

if ($Increase) {
    Add-VolumeStep -StepValue $StepValueParsed
}
elseif ($Decrease) {
    Remove-VolumeStep -StepValue $StepValueParsed
}
elseif ($Mute) {
    Set-MuteState -MuteState $true
}
elseif ($Unmute) {
    Set-MuteState -MuteState $false
}
elseif ($ShowDevice) {
    Show-DefaultDevice
}
elseif ($ListDevices) {
    Show-DeviceList
}
elseif ($PSBoundParameters.ContainsKey('SetVolume')) {
    $parsedSetVolume = ConvertTo-InvariantDouble -Value $SetVolume
    Set-VolumeLevel -Percent $parsedSetVolume
}
elseif ($Menu) {
    Show-MainMenu
}
else {
    Show-MainMenu
}

}
catch {
    Write-Host ""
    Write-Host "=== ERROR ==="
    Write-Host $_.Exception.Message
    if ($_.Exception.InnerException) {
        Write-Host "Inner exception: $($_.Exception.InnerException.Message)"
    }
    Write-Host ""
    Write-Host "Possible causes:"
    Write-Host "  1. Execution Policy is blocking script execution."
    Write-Host "     Fix: run PowerShell as your normal user and execute:"
    Write-Host "     powershell -ExecutionPolicy Bypass -File .\WindowsVolumeManager.ps1"
    Write-Host "  2. No default playback device is currently set in Windows Sound settings."
    Write-Host "  3. Add-Type compilation failed (check .NET Framework / C# compiler availability)."
}
finally {
    Read-Host "`nPress ENTER to exit."
}
OLASI SORUNLAR :

Gerçekten sorun çıkarabilecek yerler:

Execution Policy : Bu sistemde muhtemelen Bypass ile çalıştırdım. Başka bir makinede (özellikle kurumsal/domain'e bağlı bir bilgisayarda) daha katı bir politika (Restricted veya AllSigned) olabilir; o zaman yine -ExecutionPolicy Bypass gerekir.

İndirilen dosya engeli : Betiği internetten indirip kopyaladıysanız (mail, USB, tarayıcı), Windows dosyaya "Zone.Identifier" damgası koyar ve "bu betik dijital olarak imzalanmamış" uyarısı çıkabilir. Gerekirse: Unblock-File .\WindowsVolumeManager.ps1
komutu ile çalıştırın

Antivirüs/EDR yazılımları : Bazı kurumsal antivirüs çözümleri, çalışma zamanında C# derleme (Add-Type) ve COM interop kalıbını şüpheli davranış olarak işaretleyebilir (malware'ler de benzer teknik kullanabildiği için). Ev bilgisayarında sorun olmaz ama şirket bilgisayarında karantinaya alınabilir.

Varsayılan oynatma cihazı yoksa : Sistemde hiç varsayılan ses çıkış cihazı ayarlanmamışsa (nadir ama mümkün),GetDefaultAudioEndpoint yordamı hata verebilir; şbundan dolayı try/catch ile hatayı yakalayıp anlamlı bir hata mesajı gösterdim (hata mesajını kullanıcı görebiliyor), yani script çökmüyor.

PowerShell 7 (pwsh) üzerinde çalıştırılırsa : Windows'ta sorun olmaz (COM interop hâlâ destekleniyor), çünkü IAudioEndpointVolume Windows'a özgü bir API.

Güle güle kullanın.Başka biryerde böyle bir otomasyon bulamazsınız.
Kullanıcı avatarı
velociraptor
Yottabyte4
Yottabyte4
Mesajlar: 54546
Kayıt: 14 Mar 2006, 02:33
cinsiyet: Erkek
Teşekkür etti: 20681 kez
Teşekkür edildi: 12321 kez

Re: PowerSHELL İle "Windows'ta Hasas Ondalıklı" Ses Seviyesi Ayarlayın/Kullanın...

Mesaj gönderen velociraptor »

Eline sağlık
Cevapla

“Programlama ve Script dilleri” sayfasına dön