
本文介绍在 linux 环境下,利用 go 语言实时捕获系统音频输出流(而非文件)的技术方案,重点推荐 pulseaudio 和 portaudio 的 go 绑定库,并提供基础采集示例与关键注意事项。
在 Linux 系统中,直接访问正在播放的音频输出流(即“立体声混音”或“monitor source”)需依赖底层音频服务。与 Windows 的 WASAPI Loopback 或 macOS 的 Core Audio AVCapture 相比,Linux 主要通过 PulseAudio(主流桌面环境默认)或 ALSA(更底层但不直接支持混音捕获)实现。由于 ALSA 本身不提供对回环设备(如 hw:Loopback)的标准化音频监控能力,PulseAudio 是更可靠、更易用的选择——它原生支持 monitor sources(例如 alsa_output.pci-0000_00_1f.3.analog-stereo.monitor),可将任意输出流作为输入源实时采集。
推荐库与选型对比
| 库 | 地址 | 特点 | 适用场景 |
|---|---|---|---|
| pulsego | https://www.php.cn/link/4952d2ac78b534c78d7a21c60a236e4c | 基于 libpulse 的纯 Go 封装,轻量、无 CGO 依赖(部分版本)、支持 monitor 源发现与流式读取 | ✅ 推荐首选:专为 PulseAudio 设计,适合系统音频抓取 |
| portaudio-go | https://www.php.cn/link/83218450304f89053114eaa3b1487815 github.com/gordonklaus/portaudio) | 绑定跨平台 PortAudio C 库,需 CGO 和 libportaudio19-dev;可通过 pa_loopback 设备间接捕获 | ⚠️ 可用但间接:需确保 PortAudio 编译时启用了 PulseAudio 后端,且 loopback 设备配置正确 |
? 提示:运行 pactl list sources short 可列出所有可用源,含 monitor 类型(名称含 .monitor)。例如:pactl list sources short | grep -i monitor # 输出示例:2 alsa_output.pci-0000_00_1f.3.analog-stereo.monitor module-udev-detect.so s16le 2ch 44100Hz IDLE
快速上手 pulsego 示例(音频流采集)
以下是一个最小可行示例,使用 pulsego 从 monitor 源读取 PCM 数据(16-bit signed, stereo, 44.1kHz):
package main
import (
"fmt"
"log"
"time"
"github.com/moriyoshi/pulsego"
)
func main() {
ctx, err := pulsego.NewContext("audio-visualizer")
if err != nil {
log.Fatal("Failed to create PulseAudio context:", err)
}
defer ctx.Close()
// 连接上下文(阻塞直到就绪)
if err := ctx.Connect(nil, pulsego.ContextNoAutoSpawn, -1); err != nil {
log.Fatal("Failed to connect:", err)
}
// 创建录音流(指定 monitor 源名,可先用 pactl 确认)
spec := &pulsego.SampleSpec{
Format: pulsego.SAMPLE_S16LE,
Rate: 44100,
Channels: 2,
}
stream, err := ctx.NewInputStream("capture", "alsa_output.pci-0000_00_1f.3.analog-stereo.monitor", spec, nil)
if err != nil {
log.Fatal("Failed to create input stream:", err)
}
defer stream.Close()
if err := stream.Connect(nil, pulsego.InputStreamNoRemapChannels); err != nil {
log.Fatal("Failed to connect stream:", err)
}
defer stream.Disconnect()
fmt.Println("✅ Streaming from monitor source... Press Ctrl+C to stop.")
buf := make([]byte, 2048) // 每次读取 2048 字节(1024 个 int16 样本 × 2 ch)
ticker := time.NewTicker(50 * time.Millisecond)
defer ticker.Stop()
for {
select {
case <-ticker.C:
n, err := stream.Read(buf)
if err != nil {
log.Printf("Read error: %v", err)
continue
}
if n > 0 {
// 此处可对 buf[:n] 执行 FFT(如使用 github.com/mjibson/go-dsp/fft)
// 示例:解析为 int16 切片(立体声交错)
// samples := make([]int16, n/2)
// for i := 0; i < n; i += 2 {
// samples[i/2] = int16(buf[i]) | int16(buf[i+1])<<8
// }
fmt.Printf("Captured %d bytes\n", n)
}
}
}
}? 关键注意事项:
- 权限与服务状态:确保 PulseAudio 正在运行(pulseaudio --check),且当前用户属于 audio 组;若使用 systemd user session,避免 --start 冲突。
- Monitor 源动态性:设备名(如 alsa_output.*.monitor)随硬件变化,建议在运行时调用 ctx.GetSourceInfoList() 枚举并筛选含 .monitor 的源。
- FFT 处理建议:采集到的 PCM 数据需按帧分块(如 1024/2048 点),转为 float64 数组后调用 FFT 库(如 github.com/mjibson/go-dsp/fft);注意去直流偏移、加窗(Hann 窗)以提升频谱质量。
- 性能与延迟:PulseAudio 默认缓冲区较大,可通过 stream.SetBufferAttr() 调整 tlength 和 prebuf 降低延迟(需权衡稳定性)。
综上,对于 Linux + Go 的系统级音频可视化需求,pulsego 是最直接、最可控的解决方案。它绕过文件 I/O,直连 PulseAudio monitor 源,为后续 FFT 分析、波形渲染或频谱动画提供了坚实的数据基础。










