How to record audio with python sounddevice on WSL?

357 Views Asked by At

I am on WSL, and when trying to record audio using sounddevice, I get an error saying no device was found: sounddevice.PortAudioError: Error querying device -1.

I followed sounddevice's instructions and installed libportaudio2, CFFI, and numpy.

I also followed PortAudio's instructions and compiled PortAudio.

As my device is a USB microphone, I also followed this to attach the device to WSL, apparently successfully as when I run usbipd wsl list in Windows Powershell I get:

BUSID  VID:PID    DEVICE                                                        STATE
1-2    239d:f218  USB Input Device                                              Not attached
1-3    216e:2462  Camera                                                        Not attached
1-6    046d:0a38  Logi USB Headset H340, USB Input Device                       Attached - WSL
1-10   12s1:8543  Bluetooth Adapter                                             Not attached

and when I run sudo usbip port in the WSL bash I get:

Port 00: <Port in Use> at Full Speed(12Mbps)
       Logitech, Inc. : Headset H340 (046d:0a38)
       1-1 -> usbip://172.25.96.1:3240/1-6
           -> remote bus/dev 001/006

And yet, when my python script calls sounddevice.rec I get the following:

Traceback (most recent call last):
  File "/home/user/project/scripts/recorder.py", line 223, in <module>
    main()
  File "/home/user/project/scripts/recorder.py", line 203, in main
    recording = record(args.sample_rate, args.duration)
  File "/home/user/project/scripts/recorder.py", line 54, in record
    recording = sd.rec(int(duration * sample_rate), samplerate=sample_rate, channels=2)
  File "/home/user/project/venv/lib/python3.9/site-packages/sounddevice.py", line 276, in rec
    ctx.start_stream(InputStream, samplerate, ctx.input_channels,
  File "/home/user/project/venv/lib/python3.9/site-packages/sounddevice.py", line 2582, in start_stream
    self.stream = StreamClass(samplerate=samplerate,
  File "/home/user/project/venv/lib/python3.9/site-packages/sounddevice.py", line 1421, in __init__
    _StreamBase.__init__(self, kind='input', wrap_callback='array',
  File "/home/user/project/venv/lib/python3.9/site-packages/sounddevice.py", line 817, in __init__
    _get_stream_parameters(kind, device, channels, dtype, latency,
  File "/home/user/project/venv/lib/python3.9/site-packages/sounddevice.py", line 2660, in _get_stream_parameters
    info = query_devices(device)
  File "/home/user/project/venv/lib/python3.9/site-packages/sounddevice.py", line 569, in query_devices
    raise PortAudioError(f'Error querying device {device}')
sounddevice.PortAudioError: Error querying device -1

Also, running python3 -m sounddevice in the terminal or sd.query_devices() in the console return nothing.

What am I missing here? Is there a way to make it work?

1

There are 1 best solutions below

0
my-name-is-g On

I use the code at the bottom.
and if you have a problem with pyaudio installation, try to fix it ERROR: Could not build wheels for pyaudio, which is required to install pyproject.toml-based projects.

def record_audio_py() -> str:
    chunk = 1024  # Record in chunks of 1024 samples
    sample_format = pyaudio.paInt16  # 16 bits per sample
    channels = 1
    fs = 44100  # Record at 44100 samples per second
    seconds = 5
    filename = "output.wav"

    p = pyaudio.PyAudio()  # Create an interface to PortAudio

    print('Recording')

    stream = p.open(format=sample_format,
                    channels=channels,
                    rate=fs,
                    frames_per_buffer=chunk,
                    input=True)

    frames = []  # Initialize array to store frames

    # Store data in chunks for 5 seconds
    for i in range(0, int(fs / chunk * seconds)):
        data = stream.read(chunk)
        frames.append(data)

    # Stop and close the stream
    stream.stop_stream()
    stream.close()
    # Terminate the PortAudio interface
    p.terminate()

    print('Finished recording')

    # Save the recorded data as a WAV file
    wf = wave.open(filename, 'wb')
    wf.setnchannels(channels)
    wf.setsampwidth(p.get_sample_size(sample_format))
    wf.setframerate(fs)
    wf.writeframes(b''.join(frames))
    wf.close()

    return filename

and to play the file:

def play_file(filename):

    # Set chunk size of 1024 samples per data frame
    chunk = 1024

    # Open the sound file
    wf = wave.open(filename, 'rb')

    # Create an interface to PortAudio
    p = pyaudio.PyAudio()

    # Open a .Stream object to write the WAV file to
    # 'output = True' indicates that the sound will be played rather than recorded
    stream = p.open(format = p.get_format_from_width(wf.getsampwidth()),
                    channels = wf.getnchannels(),
                    rate = wf.getframerate(),
                    output = True)

    # Read data in chunks
    data = wf.readframes(chunk)

    # Play the sound by writing the audio data to the stream
    while data != '':
        stream.write(data)
        data = wf.readframes(chunk)

    # Close and terminate the stream
    stream.close()
    p.terminate()