Golang getting 'playing audio' system status - preferably cross platform

Does anyone know of a package to do this - I’ve tried searching - but it’s really hard to find a useful search.

Here is my use case - I want to be able to detect (at system level) when audio is playing (through another application/browser, etc.) so that I can pause audio to make an announcement, then start it again after the announcement.

Note that I’m using the system keyboard media control to send ‘audio_play’ to pause/resume - but if NO audio is playing, this will start the audio and play over the announcement, then pause it again afterwards…

Preferably cross platform - but windows only is better than nothing…

Thank you - Andy

For Windows I believe you’re going to want to use WASAPI. Here are some Windows Core Audio bindings:

But - detecting whether audio is playing might not be adequate because it could be a system sound, etc. Anyway, I got Gemini to generate a completely broken example using this lib and I then got it working:

package main

import (
	"fmt"
	"log"
	"time"
	"unsafe"

	"github.com/go-ole/go-ole"
	"github.com/moutend/go-wca/pkg/wca"
)

func main() {
	// 1. Initialize COM
	if err := ole.CoInitializeEx(0, ole.COINIT_APARTMENTTHREADED); err != nil {
		log.Fatal("Init error: ", err)
	}
	defer ole.CoUninitialize()

	// 2. Create the Device Enumerator using ole.CreateInstance
	// This function returns an *ole.IUnknown, which matches the memory layout
	// of the specific interface we need, so we can cast it.
	unknown, err := ole.CreateInstance(wca.CLSID_MMDeviceEnumerator, wca.IID_IMMDeviceEnumerator)
	if err != nil {
		log.Fatal("Could not create instance: ", err)
	}

	// Cast the generic IUnknown to the specific MMDeviceEnumerator
	enumerator := (*wca.IMMDeviceEnumerator)(unsafe.Pointer(unknown))
	defer enumerator.Release()

	// 3. Get Default Speaker
	var mmDevice *wca.IMMDevice
	if err := enumerator.GetDefaultAudioEndpoint(wca.ERender, wca.EMultimedia, &mmDevice); err != nil {
		log.Fatal("Endpoint error: ", err)
	}
	defer mmDevice.Release()

	// 4. Activate Audio Meter
	var audioMeter *wca.IAudioMeterInformation
	if err := mmDevice.Activate(wca.IID_IAudioMeterInformation, wca.CLSCTX_ALL, nil, &audioMeter); err != nil {
		log.Fatal("Activate error: ", err)
	}
	defer audioMeter.Release()

	fmt.Println("Listening on Windows Default Output...")

	// 5. Loop and measure
	for {
		var peak float32
		if err := audioMeter.GetPeakValue(&peak); err != nil {
			log.Printf("Error reading peak: %v", err)
			continue
		}

		// Visualizer
		bars := int(peak * 30)
		visual := ""
		for i := 0; i < bars; i++ {
			visual += "█"
		}

		fmt.Printf("\rVol: [%-30s] %.2f", visual, peak)
		time.Sleep(50 * time.Millisecond)
	}
}

That worked fine for me in Windows 10.

Every linux distro I’ve used has used PulseAudio. You could try using the pulse audio CLI. Something like this (completely untested so take with a grain of salt!):

package main

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"strings"
	"time"
)

func isAudioPlayingLinux() bool {
	// We use pactl to list sink inputs (apps playing sound)
	cmd := exec.Command("pactl", "list", "sink-inputs")
	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	if err != nil {
		fmt.Printf("ERROR: %v\n", err)
		os.Exit(1)
	}

	// Check if any input is "Running" (actively playing)
	// "State: RUNNING" indicates active playback.
	// "State: CORKED" indicates paused.
	return strings.Contains(out.String(), "State: RUNNING")
}

func main() {
	for {
		if isAudioPlayingLinux() {
			fmt.Println("Audio is playing!")
		} else {
			fmt.Println("Silence.")
		}
		time.Sleep(1 * time.Second)
	}
}

ALSO - it looks like PulseAudio might be getting phased out. So you might need to look into pipewire.

OK so for Mac OS:

macOS is the most difficult platform for this task. Apple creates a strict separation between applications. You cannot easily query the “Master Volume Meter” without writing a Kernel Extension (deprecated) or an Audio Server Plugin (complex C/C++).

The Workaround: If you simply need to know if the user is playing music via Music.app or Spotify, you can use AppleScript via Go. If you need to detect system-wide audio (like YouTube in Chrome), there is no native Go solution. You would likely need to use CGo to bridge into CoreAudio AudioObjectGetPropertyData, which is non-trivial.

func isMusicPlayingMac() bool {
    cmd := exec.Command("osascript", "-e", "tell application \"Music\" to player state as string")
    out, _ := cmd.Output()
    return strings.TrimSpace(string(out)) == "playing"
}

I tested this and it works, but it asks for permissions on MacOS.

Oh and I forgot to mention: if you want a cross-platform solution, normalize these functions and test them. Then use build tags to separate out implementations by OS:

// +build windows