Golang getting 'playing audio' system status - preferably cross platform (original) (raw)
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.