Search code examples
windowspowershellaudio.net-assembly

How to get the name of application(s) currently playing audio with powershell using NAudio?


I'm trying to get the name of applications that are actively playing audio in Windows 11 using the NAudio library. I'm using the latest official precompiled NAudio.dll v.1.9 as I could not get it working with the latest nuget package 2.2.1. With this code I can get the playback devices info but the Audio Sessions are always empty.

Add-Type -Path "C:\NAudio.dll"
$devices = (New-Object -TypeName NAudio.CoreAudioApi.MMDeviceEnumerator).EnumerateAudioEndPoints([NAudio.CoreAudioApi.DataFlow]::Render, [NAudio.CoreAudioApi.DeviceState]::Active)
foreach ($device in $devices) {
    Write-Host "Device: $($device.FriendlyName)"
    $sessions = $device.AudioSessionManager2.Sessions
    Write-Host "Number of sessions: $($sessions.Count)"
    foreach ($session in $sessions) {
        Write-Host "Session: $($session.DisplayName)"
        Write-Host "Is Muted: $($session.SimpleAudioVolume.Mute)"
        Write-Host "Volume: $($session.SimpleAudioVolume.Volume)"
    }
}

Terminal output:

Device: Sound Blaster Z (Sound Blaster Z)
Number of sessions: 0

I did read the questions where people were able to get audio sessions using C# but I was hoping to be able to do the same in PowerShell.


Solution

  • The reason why downloading the latest package from Nuget didn't work for you is because the NAudio Package has a bunch of dependencies (including the NAudio.CoreAudioApi Namespace, where the MMDeviceEnumerator Class exists).


    Specifically, the MMDeviceEnumerator class exists in NAudio.Wasapi Pacakage:

    PS ..\pwsh> [NAudio.CoreAudioApi.MMDeviceEnumerator].Assembly
    
    Version    Name               PublicKeyToken    Target Culture
    -------    ----               --------------    ------ -------
    2.2.1.0    NAudio.Wasapi      e279aa5131008a41  MSIL   neutral
    

    Ideally, Install-Package should be able to handle dependencies for you (unsure on this, my experience with this cmdlet is really bad - see other option using the dotnet CLI below):

    Install-Package NAudio -Version 2.2.1
    

    Another option to download the assemblies is to use the dotnet CLI, available when installing the .NET SDK. I personally use this:

    try {
        $framework = 'netstandard2.0'
        $packagename = 'NAudio'
        dotnet new classlib -f $framework -o "$packagename.package"
        Push-Location "$packagename.package"
        dotnet add package $packagename
        dotnet publish --configuration Release -o lib
    }
    finally {
        Pop-Location
    }
    

    You should have all required assemblies after:

    deps

    Lastly, using the 2.2.1 version of this package, the following code should list all processes using an audio device.

    try {
        Add-Type -Path .\NAudio.package\lib\*.dll
        $deviceEnumerator = [NAudio.CoreAudioApi.MMDeviceEnumerator]::new()
        $audioEndPoints = $deviceEnumerator.EnumerateAudioEndPoints(
            [NAudio.CoreAudioApi.DataFlow]::Render,
            [NAudio.CoreAudioApi.DeviceState]::Active)
    
        $result = $audioEndPoints | ForEach-Object {
            $sessions = $_.AudioSessionManager.Sessions
    
            for ($i = 0; $i -lt $sessions.Count; $i++) {
                $session = $sessions[$i]
                # Idle process, we can skip this
                if ($session.GetProcessID -eq 0) {
                    continue
                }
    
                [pscustomobject]@{
                    Device             = $_.FriendlyName
                    ProcessId          = $session.GetProcessID
                    ProcessDisplayName = (Get-Process -Id $session.GetProcessID).ProcessName
                    State              = $session.State
                    IsMuted            = $session.SimpleAudioVolume.Mute
                    Volume             = $session.SimpleAudioVolume.Volume
                }
    
                $session.Dispose()
            }
    
            $_.Dispose()
        }
    }
    finally {
        if ($deviceEnumerator) {
            $deviceEnumerator.Dispose()
        }
    }
    
    # Here is the output:
    $result
    

    You could filter this output for those objects where the .State property is equal to AudioSessionState.AudioSessionStateActive to find only those processes reproducing audio at the moment.

    $result | Where-Object {
        $_.State -eq [NAudio.CoreAudioApi.Interfaces.AudioSessionState]::AudioSessionStateActive
    }