Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine if windows is currently playing sound

So I've been pondering on this problem for a while and I can't figure out what the right way to go about this is. I want to determine if Windows is outputting sound at a certain time using a Powershell script. I can determine whether or not the audio driver has an error, but I cannot for the life of me figure out if the system is playing sound.

I looked at the .NET class for System.Media and the three classes inside all had to do with playing sound or manipulating the system sounds.

I'm not asking for code to be written for me, I just need to know where to start to check if the windows system is currently playing sound.

I have a sound monitor that is continuously monitoring sound on the Node.js platform and when it loses sound it sends me a text. Well, I also want it to go through all the systems its hooked up to and see where the fault lies. That's why I want to see whether or not the windows computer is playing sound.

like image 357
Adam McGurk Avatar asked Jul 31 '17 17:07

Adam McGurk


People also ask

How do you find what is playing sound on Windows?

Right click on volume icon in systray, select mixer and you can see all applications that are using sound devices, can see VU bars showing sound levels, mute each one separately to see which application is doing the sound etc.

How can I see what is playing sound?

There is no way to tell, you're supposed to be able to identify them from experience. You can easily browse the Windows system sounds from the Sound Control Panel, using the Test button in the Sounds tab. For other sounds, each application is configured differently, there is no single rule.

How can you tell where sound is coming from?

Your brain is able to do this by comparing tiny differences in the way that sounds affect each ear. A noise coming from the right is slightly louder in your right ear, and reaches it fractionally earlier than your left. A sound in front or behind affects each ear the same way, with intermediate effects in-between.

How do I find out where music is coming from on my computer?

If the sound starts as soon as your computer logs in to Windows, or after rebooting the system, check your Startup programs list for programs that could produce background sound. You can view the Startup tab by pressing "Ctrl-Shift-Esc" and selecting the "Startup" tab.

How can I tell if a sound is Windows 10 or not?

There is no way to tell, you're supposed to be able to identify them from experience. You can easily browse the Windows system sounds from the Sound Control Panel, using the Test button in the Sounds tab. For other sounds, each application is configured differently, there is no single rule.

How to tell if a sound is an application or not?

There is no way to tell, you're supposed to be able to identify them from experience. For other sounds, each application is configured differently, there is no single rule. Can't you identify the application from what you or the computer were doing at the time?

Why does my Windows 10 computer make a beep sound after login?

If these sounds are actually in Windows 10 after login then they are a part of the specific Sound Theme and that's where you need to look to find the definitions of what they mean. On the other hand, if they are sounds that occur when the Computer is booting then they aren't Windows Sounds at all, they are Beep Codes produced by the Computer BIOS.

Do websites automatically play sound?

Websites that automatically play sound are evil, pure and simple. And there is only one way to quickly get rid of the intrusive sounds. When I load several tabs, I have no audio sounds at all at first.


3 Answers

Here is a sample C# code that determines if Windows is rendering any audio stream. It uses Windows Core Audio API (specifically the IAudioMeterInformation interface) and is supported on Vista and higher.

public static bool IsWindowsPlayingSound()
{
    var enumerator = (IMMDeviceEnumerator)(new MMDeviceEnumerator());
    var speakers = enumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia);
    var meter = (IAudioMeterInformation)speakers.Activate(typeof(IAudioMeterInformation).GUID, 0, IntPtr.Zero);
    var value = meter.GetPeakValue();

    // this is a bit tricky. 0 is the official "no sound" value
    // but for example, if you open a video and plays/stops with it (w/o killing the app/window/stream),
    // the value will not be zero, but something really small (around 1E-09)
    // so, depending on your context, it is up to you to decide
    // if you want to test for 0 or for a small value
    return value > 1E-08;
}

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

private enum EDataFlow
{
    eRender,
    eCapture,
    eAll,
}

private enum ERole
{
    eConsole,
    eMultimedia,
    eCommunications,
}

[InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("A95664D2-9614-4F35-A746-DE8DB63617E6")]
private interface IMMDeviceEnumerator
{
    void NotNeeded();
    IMMDevice GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role);
    // the rest is not defined/needed
}

[InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("D666063F-1587-4E43-81F1-B948E807363F")]
private interface IMMDevice
{
    [return: MarshalAs(UnmanagedType.IUnknown)]
    object Activate([MarshalAs(UnmanagedType.LPStruct)] Guid iid, int dwClsCtx, IntPtr pActivationParams);
    // the rest is not defined/needed
}

[InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("C02216F6-8C67-4B5B-9D00-D008E73E0064")]
private interface IAudioMeterInformation
{
    float GetPeakValue();
    // the rest is not defined/needed
}

As said in my comment, I have also created an open source c++ project, a simple friction-free zero-dependencies console application, available here: https://github.com/smourier/IsWindowsPlayingSound. I have added one x86 release binary that should support 32 and 64 bit OSes: https://github.com/smourier/IsWindowsPlayingSound/releases

You can use it in PowerShell like any external .exe program. It will return an error level that you can retrieve using standard ways, for example: https://blogs.msdn.microsoft.com/powershell/2006/09/15/errorlevel-equivalent/

Here is the equivalent C++ code:

  #include "stdafx.h" // includes <Endpointvolume.h> and <Mmdeviceapi.h>

  #define WIDEN2(x) L ## x
  #define WIDEN(x) WIDEN2(x)
  #define __WFILE__ WIDEN(__FILE__)
  #define HRCHECK(__expr) {hr=(__expr);if(FAILED(hr)){wprintf(L"FAILURE 0x%08X (%i)\n\tline: %u file: '%s'\n\texpr: '" WIDEN(#__expr) L"'\n",hr, hr, __LINE__,__WFILE__);goto cleanup;}}
  #define RELEASE(__p) {if(__p!=nullptr){__p->Release();__p=nullptr;}}

  int main(int argc, char *argv[])
  {
    BOOL playing = FALSE;
    BOOL loopmode = FALSE;
    float epsilon = 1E-07;
    float value = 0;
    HRESULT hr = S_OK;
    IMMDeviceEnumerator* pEnumerator = NULL;
    IMMDevice *pDevice = NULL;
    IAudioMeterInformation *pMeter = NULL;

    // Parse optional args
    // "loop" -> sets a loop mode for easy testing
    // <float value> -> changes epsilon
    for (int i = 1; i < argc; i++)
    {
      if (!strcmp(argv[i], "loop"))
      {
        loopmode = TRUE;
        continue;
      }

      float eps = atof(argv[i]);
      if (eps != 0.0)
      {
        epsilon = eps;
        continue;
      }
    }

    CoInitialize(NULL);
    HRCHECK(CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator));
    HRCHECK(pEnumerator->GetDefaultAudioEndpoint(EDataFlow::eRender, ERole::eMultimedia, &pDevice));
    HRCHECK(pDevice->Activate(__uuidof(IAudioMeterInformation), CLSCTX_ALL, NULL, (void**)&pMeter));
    do
    {
      HRCHECK(pMeter->GetPeakValue(&value));
      playing = value > epsilon;
      if (!loopmode)
        break;

      printf("%.10f playing:%i\n", value, playing);
      Sleep(100);
    } while (TRUE);

  cleanup:
    RELEASE(pMeter);
    RELEASE(pDevice);
    RELEASE(pEnumerator);
    CoUninitialize();
    if (FAILED(hr))
    {
      printf("An error occurred: 0x%08X\n", hr);
      return hr;
    }

    if (playing)
    {
      printf("Windows is playing a sound.\n");
    }
    else
    {
      printf("Windows is not playing a sound.\n");
    }
    return playing;
  }
like image 147
Simon Mourier Avatar answered Oct 17 '22 18:10

Simon Mourier


Here's how to use the code that Simon Mourier provided.

Run the code below:

Add-Type -TypeDefinition @'
using System;
using System.Runtime.InteropServices;

namespace Foo
{
    public class Bar
    {
        public static bool IsWindowsPlayingSound()
        {
            IMMDeviceEnumerator enumerator = (IMMDeviceEnumerator)(new MMDeviceEnumerator());
            IMMDevice speakers = enumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia);
            IAudioMeterInformation meter = (IAudioMeterInformation)speakers.Activate(typeof(IAudioMeterInformation).GUID, 0, IntPtr.Zero);
            float value = meter.GetPeakValue();

            // this is a bit tricky. 0 is the official "no sound" value
            // but for example, if you open a video and plays/stops with it (w/o killing the app/window/stream),
            // the value will not be zero, but something really small (around 1E-09)
            // so, depending on your context, it is up to you to decide
            // if you want to test for 0 or for a small value
            return value > 1E-08;
        }

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

        private enum EDataFlow
        {
            eRender,
            eCapture,
            eAll,
        }

        private enum ERole
        {
            eConsole,
            eMultimedia,
            eCommunications,
        }

        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("A95664D2-9614-4F35-A746-DE8DB63617E6")]
        private interface IMMDeviceEnumerator
        {
            void NotNeeded();
            IMMDevice GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role);
            // the rest is not defined/needed
        }

        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("D666063F-1587-4E43-81F1-B948E807363F")]
        private interface IMMDevice
        {
            [return: MarshalAs(UnmanagedType.IUnknown)]
            object Activate([MarshalAs(UnmanagedType.LPStruct)] Guid iid, int dwClsCtx, IntPtr pActivationParams);
            // the rest is not defined/needed
        }

        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("C02216F6-8C67-4B5B-9D00-D008E73E0064")]
        private interface IAudioMeterInformation
        {
            float GetPeakValue();
            // the rest is not defined/needed
        }
    }
}
'@

I replaced all var types as that seems to fix the issue with the code not compiling on PowerShell version 2.

Once loaded you can check the state like so:

[Foo.Bar]::IsWindowsPlayingSound()
True or False

I've tested this working with Windows 10 1703 on PowerShell 5.1


But there are caveats:

this is a bit tricky. 0 is the official "no sound" value
but for example, if you open a video and plays/stops with it (w/o killing the app/window/stream),
the value will not be zero, but something really small (around 1E-09)
so, depending on your context, it is up to you to decide
if you want to test for 0 or for a small value

So if you change return value > 1E-08 to return value > 0 you will get true when a video is paused.

like image 25
Persistent13 Avatar answered Oct 17 '22 19:10

Persistent13


You could use AudioDeviceCmdlets module written by Chris Hunt

Write-DefaultAudioDeviceValue -StreamValue looks like what you are looking for. Otherwise you can take a look at his source on how he pulling those values using the CoreAudioApi

like image 23
BenH Avatar answered Oct 17 '22 19:10

BenH