Record speakers output with PyAudio

偶尔善良 提交于 2019-11-27 14:31:39

In case someone is still stumbling over this like me, I found a PyAudio fork to record the output on windows. https://github.com/intxcc/pyaudio_portaudio

Explanation:

The official PyAudio build isn't able to record the output. BUT with Windows Vista and above, a new API, WASAPI was introduced, which includes the ability to open a stream to an output device in loopback mode. In this mode the stream will behave like an input stream, with the ability to record the outgoing audio stream.

To set the mode, one has to set a special flag (AUDCLNT_STREAMFLAGS_LOOPBACK, https://msdn.microsoft.com/de-de/library/windows/desktop/dd316551(v=vs.85).aspx ). Since this flag is not supported in the official build one needs to edit PortAudio as well as PyAudio, to add loopback support.

New option: "as_loopback":(true|false)

You can't record from an output stream as though it were input. To record, you need to connect PyAudio to an input device, like a microphone. At least that's the normal way to do things.

Try connecting to a microphone first, and see if you get anything. If this works, then try doing something unusual.

As a small speedup to your iterations, rather than recording and looking at the file, it's often easier just to print out the max for a few chunks to make sure you're bringing in data. Usually just watching the numbers scroll by and comparing them to the sound gives a quick estimate of whether things are correctly connected.

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

The speaker is an output stream even if you open it as an input. The hostApi value of the speaker is probably 0. You can check the 'maxInputChannels' and 'maxOutputChannels' of every connected devices and the maxInputChannels for the speaker shall be 0. You can't write to an input stream and you can't read from an output stream.

You can detect the available devices with the following code:

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

After you get all the connected devices you can check the 'hostApi' of each devices. For instance if the speaker index is 5 than:

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

I got to record my speaker output with pyaudio with some configuration and code from pyaudio's documentation.

Code

"""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()

Configuration

First, with pulseaudio running, create a loopback device:

pacmd load-module module-loopback latency_msec=5

Then set the default (fallback) to this loopback device in pavucontrol:

Then you can start the script, wait 5 seconds, and you should have an output.wav.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!