使用PyAudio录制扬声器输出

28 投票
5 回答
63780 浏览
提问于 2025-04-29 17:54

我想用PyAudio录制电脑扬声器的声音。
我试着修改PyAudio文档中的代码示例,但它没有成功。

从技术上讲,没有错误。我得到了一个文件output.wav,而且可以打开它,但里面没有声音。在Audacity软件中,我只能看到一条直线。

这是怎么回事呢?

import pyaudio
import wave

CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "output.wav"

p = pyaudio.PyAudio()

SPEAKERS = p.get_default_output_device_info()["hostApi"] #The part I have modified

stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK,
                input_host_api_specific_stream_info=SPEAKERS) #The part I have modified

print("* recording")

frames = []

for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    data = stream.read(CHUNK)
    frames.append(data)

print("* done recording")

stream.stop_stream()
stream.close()
p.terminate()

wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()
暂无标签

5 个回答

1

扬声器其实是一个输出设备,即使你把它当作输入设备来打开。扬声器的hostApi值可能是0。你可以查看每个连接设备的'maxInputChannels'和'maxOutputChannels',扬声器的maxInputChannels应该是0。

你不能往输入流里写东西,也不能从输出流里读东西。

你可以用下面的代码来检测可用的设备:

import pyaudio 

# detect devices:
p = pyaudio.PyAudio()
host_info = p.get_host_api_info_by_index(0)    
device_count = host_info.get('deviceCount')
devices = []

# iterate between devices:
for i in range(0, device_count):
    device = p.get_device_info_by_host_api_device_index(0, i)
    devices.append(device['name'])

print devices

在你获取到所有连接的设备后,可以检查每个设备的'hostApi'。比如说,如果扬声器的索引是5,那么:

p.get_device_info_by_host_api_device_index(0, 5)['hostApi']
3

你不能把输出流当成输入流来录音。要录音,你需要把PyAudio连接到一个输入设备,比如麦克风。这是正常的操作方式。

先试着连接麦克风,看看能不能录到声音。如果能录到,那再尝试一些不寻常的操作。

为了加快你的测试速度,通常不需要每次都录音然后查看文件,直接打印出几个数据块的最大值就能确认你是否在接收数据。通常只要看着数字滚动,并把它们和声音进行比较,就能快速判断连接是否正常。

import audioop
mx = audioop.max(data, 2)
print mx
14

我用pyaudio录制了我的扬声器输出,使用了一些配置和代码,这些内容来自pyaudio的文档

代码

"""PyAudio example: Record a few seconds of audio and save to a WAVE file."""

import pyaudio
import wave

CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "output.wav"

p = pyaudio.PyAudio()

stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK)

print("* recording")

frames = []

for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    data = stream.read(CHUNK)
    frames.append(data)

print("* done recording")

stream.stop_stream()
stream.close()
p.terminate()

wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()

配置

首先,在运行pulseaudio的情况下,创建一个回环设备:

pacmd load-module module-loopback latency_msec=5

然后在pavucontrol中将默认(备用)设备设置为这个回环设备:

Pavu control example

接下来,你可以启动脚本,等待5秒钟,然后你应该会得到一个output.wav文件。

18

如果你在Windows平台上创建一个应用程序,你可以使用默认的立体声混音器虚拟设备来录制你电脑的声音输出。

1) 启用立体声混音器。

2) 将PyAudio连接到你的立体声混音器,方法如下:

p = pyaudio.PyAudio()
stream = p.open(format = FORMAT,
                channels = CHANNELS,
                rate = RATE,
                input = True,
                input_device_index = dev_index,
                frames_per_buffer = CHUNK)

这里的dev_index是你立体声混音器的索引。

3) 列出你的设备以获取所需的索引:

for i in range(p.get_device_count()):
    print(p.get_device_info_by_index(i))

另外,你也可以通过设备名称自动获取索引:

for i in range(p.get_device_count()):
    dev = p.get_device_info_by_index(i)
    if (dev['name'] == 'Stereo Mix (Realtek(R) Audio)' and dev['hostApi'] == 0):
        dev_index = dev['index'];
        print('dev_index', dev_index)

4) 继续使用pyAudio,就像从麦克风录音一样:

data = stream.read(CHUNK)
27

如果还有人像我一样在这个问题上碰壁,我找到了一款可以在Windows上录制输出的PyAudio分支

解释一下:

官方的PyAudio版本无法录制输出音频。但是从Windows Vista开始,推出了一种新的API,叫做WASAPI,它可以让你以循环模式打开输出设备的音频流。在这种模式下,这个流就像一个输入流,可以录制出去的音频。

要设置这种模式,需要设置一个特殊的标志(AUDCLNT_STREAMFLAGS_LOOPBACK)。因为这个标志在官方版本中不被支持,所以需要对PortAudio和PyAudio进行一些修改,以添加循环录制的功能。

新的选项:

"as_loopback":(true|false)

撰写回答