+
21
-

回答

录制系统内部音频(扬声器输出)是 Windows 音频编程中的常见需求,但 PyAudio 本身主要面向麦克风输入设计。以下是几种经过验证的实现方案,从简到繁依次介绍。

方案一:启用"立体声混音"设备(最简便)

Windows 系统内置了一个名为 "立体声混音"(Stereo Mix) 的录制设备,它能捕获扬声器播放的所有声音 (citation:7)。

1. 启用立体声混音设备

默认情况下该设备是禁用的,需手动开启 (citation:7):

右键点击任务栏音量图标 → 打开 声音设置

选择右侧的 "声音控制面板"

切换到 "录制" 选项卡

在列表空白处 右键 → 勾选 "显示禁用的设备"

找到 "立体声混音",右键 → 启用

可选:右键 → "设置为默认设备"

注意:部分声卡驱动(尤其是笔记本电脑)可能不提供此选项 (citation:7)。

2. PyAudio 代码实现

import pyaudio
import wave

def record_system_audio(filename, duration=10, sample_rate=48000, channels=2, chunk=1024):
    """录制系统输出音频(通过立体声混音设备)"""
    p = pyaudio.PyAudio()

    # 查找立体声混音设备
    device_index = None
    for i in range(p.get_device_count()):
        dev = p.get_device_info_by_index(i)
        print(f"  设备 {i}: {dev['name']}  (输入通道: {dev['maxInputChannels']})")
        if "立体声混音" in dev["name"] or "Stereo Mix" in dev["name"]:
            device_index = dev["index"]
            print(f"  --> 找到目标设备: {dev['name']}")
            break

    if device_index is None:
        raise Exception(
            "未找到立体声混音设备。请在 Windows 声音设置中启用该设备:\n"
            "声音控制面板 → 录制选项卡 → 右键显示禁用设备 → 启用立体声混音"
        )

    stream = p.open(
        format=pyaudio.paInt16,
        channels=channels,
        rate=sample_rate,
        input=True,
        input_device_index=device_index,  # 关键:指定立体声混音设备
        frames_per_buffer=chunk
    )

    print("● 开始录制系统音频...")
    frames = []
    for _ in range(0, int(sample_rate / chunk * duration)):
        frames.append(stream.read(chunk))

    print("■ 录制完成")
    stream.stop_stream()
    stream.close()
    p.terminate()

    # 保存为 WAV 文件
    with wave.open(filename, 'wb') as wf:
        wf.setnchannels(channels)
        wf.setsampwidth(p.get_sample_size(pyaudio.paInt16))
        wf.setframerate(sample_rate)
        wf.writeframes(b''.join(frames))

    print(f"✓ 文件已保存: {filename}")

record_system_audio("system_audio.wav", duration=15)

方案二:WASAPI Loopback 模式(最专业)

从 Windows Vista 开始,微软提供了 WASAPI(Windows Audio Session API)Loopback(环回)录制模式,可以直接捕获音频引擎正在播放的系统混合音频,无需额外硬件或虚拟设备 (citation:3)(citation:12)(citation:13)。

工作原理

"在 loopback 模式下,WASAPI 客户端可以捕获 rendering endpoint 设备(通常即声卡)正在播放的音频流。客户端只能为共享模式流(AUDCLNT_SHAREMODE_SHARED)启用 loopback 模式。" (citation:13)

WASAPI 在软件层面实现了环回功能——将音频引擎的输出流复制到应用程序的捕获缓冲区中 (citation:12)。

使用 soundcard 库简化操作

PyAudio 对 WASAPI Loopback 的支持不够直接,推荐使用 Python 的 soundcard 库,它底层封装了 WASAPI:

import soundcard
import numpy as np
import wave

def record_loopback(filename, duration=10, sample_rate=48000):
    """使用 WASAPI Loopback 录制系统音频"""
    # 获取默认扬声器(即环回捕获源)
    speaker = soundcard.default_speaker()
    print(f"当前扬声器: {speaker.name}")

    # 通过扬声器的 loopback 模式创建麦克风对象
    mic = speaker.microphone()
    print(f"环回设备: {mic.name}")
    print(f"● 开始录制 ({duration} 秒)...")

    # 采集音频数据(返回 numpy 数组)
    data = mic.record(samplerate=sample_rate, numframes=sample_rate * duration)
    print("■ 录制完成")

    # 将 float32 转换为 int16 并保存 WAV
    data_int16 = np.int16(data * 32767)
    with wave.open(filename, 'wb') as wf:
        wf.setnchannels(data_int16.shape[1] if len(data_int16.shape) > 1 else 1)
        wf.setsampwidth(2)  # 16-bit = 2 bytes
        wf.setframerate(sample_rate)
        wf.writeframes(data_int16.tobytes())

    print(f"✓ 已保存: {filename}")

record_loopback("loopback_audio.wav", duration=15)

安装依赖:

pip install soundcard numpy

方案三:Virtual Audio Cable 虚拟声卡(通用性最强)

Virtual Audio Cable (VAC) 是一款虚拟声卡驱动程序,可以在系统中创建虚拟音频线路,在不同应用程序间传送音频讯号流 (citation:1)(citation:2)。

工作流程

VAC 的核心思路是:将系统默认播放设备设为虚拟声卡,再用 Audio Repeater 将虚拟声卡的输出转发到真实扬声器,同时用 PyAudio 从虚拟声卡录音 (citation:1):

应用程序音频输出 → VAC 虚拟声卡(录制端口) → PyAudio 采集
                                            ↘ Audio Repeater → 真实扬声器(听到声音)

操作步骤

安装 VAC,系统会出现新的虚拟音频设备 Line 1 (Virtual Audio Cable) (citation:1)

设置默认播放设备为 VAC(此时直接播放听不到声音,声音已转入虚拟线路)(citation:1)

打开 Audio Repeater,将 Wave In 设为 Line 1,Wave Out 设为真实扬声器 (citation:1)

用 PyAudio 从 VAC 录制

import pyaudio
import wave

def record_vac(filename, duration=10, sample_rate=44100, channels=2, chunk=1024):
    """从 Virtual Audio Cable 虚拟声卡录制系统音频"""
    p = pyaudio.PyAudio()

    # 查找 VAC 设备
    device_index = None
    for i in range(p.get_device_count()):
        dev = p.get_device_info_by_index(i)
        if "Virtual Audio Cable" in dev["name"] and dev["maxInputChannels"] > 0:
            device_index = dev["index"]
            print(f"找到 VAC 设备: {dev['name']} (索引 {i})")
            break

    if device_index is None:
        raise Exception("未找到 Virtual Audio Cable 设备,请先安装 VAC")

    stream = p.open(
        format=pyaudio.paInt16,
        channels=channels,
        rate=sample_rate,
        input=True,
        input_device_index=device_index,
        frames_per_buffer=chunk
    )

    print("● 从 VAC 录制中...")
    frames = []
    for _ in range(0, int(sample_rate / chunk * duration)):
        frames.append(stream.read(chunk))

    print("■ 录制完成")
    stream.stop_stream()
    stream.close()
    p.terminate()

    with wave.open(filename, 'wb') as wf:
        wf.setnchannels(channels)
        wf.setsampwidth(p.get_sample_size(pyaudio.paInt16))
        wf.setframerate(sample_rate)
        wf.writeframes(b''.join(frames))

    print(f"✓ 已保存: {filename}")

三种方案对比

是否需要安装额外软件soundcard 库需要安装 VAC 驱动
是否需要系统设置需要手动启用设备需要配置默认设备
是否影响日常使用不影响不影响会影响默认播放设备
音质原始数字音频,无损原始数字音频,无损原始数字音频,无损
DRM 保护内容不可录制不可录制 (citation:13)不可录制
兼容性部分声卡不支持Windows Vista+通用
推荐场景快速录音专业应用开发特殊设备/驱动环境

"传的讯号都是数字,所以不会有讯号污染或衰减的问题" (citation:2),三种方案在音质层面没有本质差异。

补充注意事项

音量调节:使用 VAC 或立体声混音时,调节耳机音量可能会导致系统自动切换默认输出设备。录制时需注意保持输出设备指向 VAC 或立体声混音 (citation:2)

DRM 限制:"WASAPI 不允许 loopback 录制包含 DRM 保护内容的数字流" (citation:12),受保护的流媒体音频无法通过任何方式录制

录音监听:如果需要在录制的同时听到声音,立体声混音方案天然支持;VAC 方案需要配合 Audio Repeater 转发 (citation:1)

网友回复

我知道答案,我要回答