使用Python 3捕获192 kHz音频

12
我需要使用Python 3捕获192 kHz的音频来进行生物声学实验。我有所需硬件:一个Sound Devices USBPre 2声卡,一个具有良好频率响应曲线(高达100 kHz)的麦克风,并且我已经在我的操作系统(ubuntu 13.04)上启用了从该声卡采样以达到192 kHz的功能。
我尝试使用PyAudio进行录制。它似乎可以工作,并会给我一个采样率为192 kHz的wav文件。然而,当我查看频谱时,没有24 kHz以上的功率,这表明PyAudio实际上并没有捕获192 kHz,而是48 kHz。但是,当我使用JACK的输入进行录制时,使用Audacity可以得到带有高达96 kHz功率的漂亮录音。因此,我认为PyAudio实际上并没有以192 kHz采样声音,尽管它应该能够做到。 如何解决这个问题?
我能够无错误地启动JACK:
/usr/bin/jackd -R -dalsa -Chw:1,0 -n3 -o1 -p2048 -r192000

jackd 0.122.0
Copyright 2001-2009 Paul Davis, Stephane Letz, Jack O'Quinn, Torben Hohn and others.
jackd comes with ABSOLUTELY NO WARRANTY
This is free software, and you are welcome to redistribute it
under certain conditions; see the file COPYING for details

JACK compiled with System V SHM support.
loading driver ..
apparent rate = 192000
creating alsa driver ... -|hw:1,0|2048|3|192000|0|1|nomon|swmeter|-|32bit
control device hw:0
configuring for 192000Hz, period = 2048 frames (10.7 ms), buffer = 3 periods
ALSA: final selected sample format for capture: 24bit little-endian
ALSA: use 3 periods for capture

初始化PyAudio(据我所知,没有任何真正的错误):

p = pyaudio.PyAudio()
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
bt_audio_service_open: connect() failed: Connection refused (111)
bt_audio_service_open: connect() failed: Connection refused (111)
bt_audio_service_open: connect() failed: Connection refused (111)
bt_audio_service_open: connect() failed: Connection refused (111)
ALSA lib pcm_dmix.c:957:(snd_pcm_dmix_open) The dmix plugin supports only playback stream

打开一个PyAudio流:

stream = p.open(format=pyaudio.paInt32,
                channels=1,rate=192000,
                input=True,
                frames_per_buffer=2048)

如果有人想要验证我的解释:PyAudio无法捕获192 kHz的声音,但Audacity可以。以下是声谱图图像:

使用PyAudio捕获的声音的频谱图 使用PyAudio捕获的声音的频谱图

使用Audacity捕获的声音的频谱图 使用Audacity捕获的声音的频谱图

如何使用PyAudio以192000个采样/s录制声音? 欢迎提供其他使用Python 3捕获声音的建议。


当我使用 p.open(..., input_device_index=dev_idx) 打开时,我会收到以下错误 OSError: [Errno Invalid number of channels] -9998,类似于尝试 p.is_format_supported(...) 时收到的错误:ValueError: ('Invalid number of channels', -9998) - user2936487
刚刚在尝试在OS X上让PyAudio工作时,我也遇到了这个问题。还有[Errno Input overflowed] -9981。通过循环尝试不同的采样率、通道数和设备索引,我成功地探测到了正确的配置(现在它可以工作了)。我会尝试优化代码并提供一个“答案”,希望对你有所帮助。 - Lukas Graf
这只是一种猜测,但是......您是否尝试过将格式更改为pyaudio.paFloat32而不是pyaudio.paInt32?(我仍在清理我的代码) - Lukas Graf
尝试过了,但是不行。同样的错误:OSError: [Errno Invalid number of channels] -9998 - user2936487
让我们在聊天中继续这个讨论:http://chat.stackoverflow.com/rooms/40284/discussion-between-lukas-graf-and-user2936487 - Lukas Graf
显示剩余3条评论
1个回答

6
这并不是一个确定性的答案,而是帮助您自行追踪问题的尝试。
在尝试在OS X上使用PyAudio重现您的问题时,我总是遇到[Errno Input overflowed] -9981(像其他 一样,似乎是这样)。即使p.is_format_supported()报告正常,也会出现这些错误。因此,我编写了一个脚本,只尝试使用所有可能的录音设置进行记录。
这个脚本以防御性的方式探测所有列表的排列组合,包括设备采样率格式通道,并将结果保存到根据记录设置命名的文件中。
import os
import pyaudio
import sys

# === These parameters will be permuted ===========
DEVICES = [0, 1, 2]
RATES = [44100, 48000, 192000]
FORMATS = ['Float32', 'Int32', 'Int24', 'Int16', 'Int8', 'UInt8']
CHANNELS = [1, 2]
# =================================================

CHUNK = 1024
COLUMNS = (('filename', 30),
           ('result', 9),
           ('dev', 5),
           ('rate', 8),
           ('format', 9),
           ('channels', 10),
           ('chunk', 7),
           ('reason', 0))
STATUS_MSG = "Recording... "

pa = pyaudio.PyAudio()


def get_format(format):
    fmt = getattr(pyaudio, 'pa%s' % format)
    return fmt


def record(filename=None,
           duration=5,
           dev=0,
           rate=44100,
           format='Float32',
           channels=2,
           chunk=1024,):
    """Record `duration` seconds of audio from the device with index `dev`.
    Store the result in a file named according to recording settings.
    """
    if filename is None:
        filename = "dev{dev}-{rate}-{format}-{channels}ch.raw".format(**locals())
    result = 'FAILURE'
    reason = ''

    outfile = open(filename, 'w')
    print STATUS_MSG,
    sys.stdout.flush()

    try:
        stream = pa.open(input_device_index=dev,
                         rate=rate,
                         format=get_format(format),
                         channels=channels,
                         frames_per_buffer=chunk,
                         input=True,
                         )

        try:
            for i in range(0, rate / (chunk) * duration):
                a = stream.read(chunk)
                outfile.write(a)
            result = 'SUCCESS'
        # Catch exceptions when trying to read from stream
        except Exception, e:
            reason = "'%s'" % e
    # Catch exceptions when trying to even open the stream
    except Exception, e:
        reason = "'%s'" % e

    outfile.close()

    # Don't leave files behind for unsuccessful attempts
    if result == 'FAILURE':
        os.remove(filename)
        filename = ''

    info = {}
    for col_name, width in COLUMNS:
        info[col_name] = str(locals()[col_name]).ljust(width)

    msg = "{filename}{result}{dev}{rate}{format}{channels}{chunk}{reason}"
    print msg.format(**info)

def main():
    # Build the header line
    header = 'STATUS'.ljust(len(STATUS_MSG) + 1)
    for col_name, width in COLUMNS:
        header += col_name.upper().ljust(width)
    print header
    print "=" * len(header)

    # Record samples for all permutations of our parameter lists
    for dev in DEVICES:
        for rate in RATES:
            for format in FORMATS:
                for channels in CHANNELS:
                    record(duration=2,
                           dev=dev,
                           rate=rate,
                           format=format,
                           channels=channels,
                           chunk=CHUNK)

if __name__ == '__main__':
    main()

示例输出(简化版):

STATUS        FILENAME                      RESULT   DEV  RATE    FORMAT   CHANNELS  CHUNK  REASON
==================================================================================================
Recording...  dev0-44100-Float32-1ch.raw    SUCCESS  0    44100   Float32  1         1024
Recording...  dev0-44100-Float32-2ch.raw    SUCCESS  0    44100   Float32  2         1024
Recording...  dev0-44100-Int16-1ch.raw      SUCCESS  0    44100   Int16    1         1024
Recording...  dev0-44100-Int16-2ch.raw      SUCCESS  0    44100   Int16    2         1024
Recording...                                FAILURE  0    192000  Float32  1         1024   '[Errno Input overflowed] -9981'
Recording...                                FAILURE  0    192000  Float32  2         1024   '[Errno Input overflowed] -9981'
Recording...                                FAILURE  0    192000  Int16    1         1024   '[Errno Input overflowed] -9981'
Recording...                                FAILURE  0    192000  Int16    2         1024   '[Errno Input overflowed] -9981'
Recording...  dev1-44100-Float32-1ch.raw    SUCCESS  1    44100   Float32  1         1024
Recording...  dev1-44100-Float32-2ch.raw    SUCCESS  1    44100   Float32  2         1024
Recording...  dev1-44100-Int16-1ch.raw      SUCCESS  1    44100   Int16    1         1024
Recording...  dev1-44100-Int16-2ch.raw      SUCCESS  1    44100   Int16    2         1024
Recording...                                FAILURE  1    192000  Float32  1         1024   '[Errno Input overflowed] -9981'
Recording...                                FAILURE  1    192000  Float32  2         1024   '[Errno Input overflowed] -9981'
Recording...                                FAILURE  1    192000  Int16    1         1024   '[Errno Input overflowed] -9981'
Recording...                                FAILURE  1    192000  Int16    2         1024   '[Errno Input overflowed] -9981'
Recording...                                FAILURE  2    44100   Float32  1         1024   '[Errno Invalid number of channels] -9998'
Recording...                                FAILURE  2    44100   Float32  2         1024   '[Errno Invalid number of channels] -9998'
Recording...                                FAILURE  2    44100   Int16    1         1024   '[Errno Invalid number of channels] -9998'
Recording...                                FAILURE  2    44100   Int16    2         1024   '[Errno Invalid number of channels] -9998'
Recording...                                FAILURE  2    192000  Float32  1         1024   '[Errno Invalid number of channels] -9998'
Recording...                                FAILURE  2    192000  Float32  2         1024   '[Errno Invalid number of channels] -9998'
Recording...                                FAILURE  2    192000  Int16    1         1024   '[Errno Invalid number of channels] -9998'
Recording...                                FAILURE  2    192000  Int16    2         1024   '[Errno Invalid number of channels] -9998'

我尝试了你的脚本并进行了一些小修改(将DEVICES添加了11)。今天pa.get_device_info_by_index(11)看起来更好(输出的部分):... 'defaultSampleRate': 192000.0, 'hostApi': 2, 'index': 11, 'maxInputChannels': 2, 'maxOutputChannels': 1,...pa.get_host_api_info_by_index(2)给出了这个结果:{'defaultInputDevice': 11, 'defaultOutputDevice': 11, 'deviceCount': 1, 'index': 2, 'name': 'JACK Audio Connection Kit', 'structVersion': 1, 'type': 12} - user2936487
我甚至可以打开一个流:stream = pa.open(input_device_index=11,rate=192000,format=4,channels=1,frames_per_buffer=2048,input=True),并偶尔读取一块数据:stream.read(1024),但大部分时间它会导致Python崩溃:python3: malloc.c:2369: sysmalloc: ... Aborted (core dumped) - user2936487
打开流似乎只需要找到正确的输入设备索引(可能会变化),然后将参数值与启动JACK时返回的值匹配(速率= 192000,格式= paInt24)。但是,由于读取块时出现的崩溃问题,我已经放弃了。我可能会改用Audacity录制。非常感谢Lukas提供的所有帮助。 - user2936487
@user2936487 不用谢,感谢您的反馈! - Lukas Graf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接