Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Capture 192 kHz audio using Python 3

I need to capture 192 kHz audio for some bioacoustics experiments using Python 3. I have the hardware, a Sound Devices USBPre 2 sound card, a microphone with good frequency response curve up to 100 kHz, and I have enabled my os (ubuntu 13.04) to sample from this card at 192 kHz.

I have tried recording with PyAudio. It appears to work, and will give me a wav file with a sampling rate of 192 kHz. However, when I look at the spectra there is no power above 24 kHz, suggesting that PyAudio doesn't really capture at 192 kHz, but rather at 48 kHz. However, when I record using Audacity with input from JACK, I get a nice recording with power up to 96kHz. So, I have the impression that PyAudio doesn't actually sample the sound at 192 kHz even though it should be able to. How to fix this?

I start JACK without errors:

/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

Initialize PyAudio (without any real errors (as far as I can tell)):

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

Open a PyAudio stream:

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

I have images of the spectrograms, in case someone wants to verify my interpretation that PyAudio doesn't capture at 192 kHz (but Audacity does):

Spectrogram of sound captured usig PyAudio Spectrogram of sound captured usig PyAudio

Spectrogram of sound captured usig Audacity Spectrogram of sound captured usig Audacity

How can I record sound at 192 000 samples/s using PyAudio? Suggestions of other ways to capture sound using Python 3 are also welcome.

like image 676
user2936487 Avatar asked Oct 30 '13 19:10

user2936487


People also ask

How do I record live audio in Python?

Recording AudioThe python-sounddevice and pyaudio libraries provide ways to record audio with Python. python-sounddevice records to NumPy arrays and pyaudio records to bytes objects. Both of these can be stored as WAV files using the scipy and wave libraries, respectively.

How do you use PyAudio?

To use PyAudio, first instantiate PyAudio using pyaudio. PyAudio() (1), which sets up the portaudio system. To record or play audio, open a stream on the desired device with the desired audio parameters using pyaudio.

Can Python play sounds?

Play sound on Python is easy. There are several modules that can play a sound file (. wav). These solutions are cross platform (Windows, Mac, Linux).


1 Answers

This is not meant to be a conclusive answer, but rather an attempt to help you maybe track down the issue yourself.

When trying to reproduce your issue with PyAudio on OS X, I always kept running into [Errno Input overflowed] -9981 (like several other people, it seems). Also configurations that p.is_format_supported() reported as OK caused these errors. So I whipped up a script that just tries to record with all the possible permutations of the recording settings.

This script probes all permutations of lists of devices, sample rates, formats and channels, in a defensive way, and saves the results to files named according to the recording settings.

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

Sample output (simplified):

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'
like image 112
Lukas Graf Avatar answered Oct 08 '22 17:10

Lukas Graf