0

I am trying to do python audio with python-sounddevice on a macOS 13.2.1 with M1 chip but I can't get it to work. I installed portaudio and libsndfile with brew, then created a conda environment with sounddevice and soundfile packages then ran:

❯ python play_file.py some_audio_file.wav
||PaMacCore (AUHAL)|| AUHAL component not found.Traceback (most recent call last):
  File "play_file.py", line 77, in <module>
    stream = sd.OutputStream(
  File "/opt/homebrew/Caskroom/miniforge/base/envs/tvm/lib/python3.8/site-packages/sounddevice.py", line 1488, in __init__
    _StreamBase.__init__(self, kind='output', wrap_callback='array',
  File "/opt/homebrew/Caskroom/miniforge/base/envs/tvm/lib/python3.8/site-packages/sounddevice.py", line 892, in __init__
    _check(_lib.Pa_OpenStream(self._ptr, iparameters, oparameters,
  File "/opt/homebrew/Caskroom/miniforge/base/envs/tvm/lib/python3.8/site-packages/sounddevice.py", line 2736, in _check
    raise PortAudioError(errormsg, err, hosterror_info)
sounddevice.PortAudioError: <exception str() failed>

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "play_file.py", line 85, in <module>
    parser.exit(type(e).__name__ + ': ' + str(e))
  File "/opt/homebrew/Caskroom/miniforge/base/envs/tvm/lib/python3.8/site-packages/sounddevice.py", line 2220, in __str__
    hostname = query_hostapis(host_api)['name']
  File "/opt/homebrew/Caskroom/miniforge/base/envs/tvm/lib/python3.8/site-packages/sounddevice.py", line 640, in query_hostapis
    raise PortAudioError('Error querying host API {}'.format(index))
sounddevice.PortAudioError: Error querying host API -9979

And play_file.py:

#!/usr/bin/env python3
"""Load an audio file into memory and play its contents.

NumPy and the soundfile module (https://python-soundfile.readthedocs.io/)
must be installed for this to work.

This example program loads the whole file into memory before starting
playback.
To play very long files, you should use play_long_file.py instead.

This example could simply be implemented like this::

    import sounddevice as sd
    import soundfile as sf

    data, fs = sf.read('my-file.wav')
    sd.play(data, fs)
    sd.wait()

... but in this example we show a more low-level implementation
using a callback stream.

"""
import argparse
import threading

import sounddevice as sd
import soundfile as sf


def int_or_str(text):
    """Helper function for argument parsing."""
    try:
        return int(text)
    except ValueError:
        return text


parser = argparse.ArgumentParser(add_help=False)
parser.add_argument(
    '-l', '--list-devices', action='store_true',
    help='show list of audio devices and exit')
args, remaining = parser.parse_known_args()
if args.list_devices:
    print(sd.query_devices())
    parser.exit(0)
parser = argparse.ArgumentParser(
    description=__doc__,
    formatter_class=argparse.RawDescriptionHelpFormatter,
    parents=[parser])
parser.add_argument(
    'filename', metavar='FILENAME',
    help='audio file to be played back')
parser.add_argument(
    '-d', '--device', type=int_or_str,
    help='output device (numeric ID or substring)')
args = parser.parse_args(remaining)

event = threading.Event()

try:
    data, fs = sf.read(args.filename, always_2d=True)

    current_frame = 0

    def callback(outdata, frames, time, status):
        global current_frame
        if status:
            print(status)
        chunksize = min(len(data) - current_frame, frames)
        outdata[:chunksize] = data[current_frame:current_frame + chunksize]
        if chunksize < frames:
            outdata[chunksize:] = 0
            raise sd.CallbackStop()
        current_frame += chunksize

    stream = sd.OutputStream(
        samplerate=fs, device=args.device, channels=data.shape[1],
        callback=callback, finished_callback=event.set)
    with stream:
        event.wait()  # Wait until playback is finished
except KeyboardInterrupt:
    parser.exit('\nInterrupted by user')
except Exception as e:
    parser.exit(type(e).__name__ + ': ' + str(e))

The closest case to mine that I found is this one which hasn't been answered.

Nick Skywalker
  • 1,027
  • 2
  • 10
  • 26
  • Error code -9979 means "host API not found". This should be caught by the `sounddevice` module: https://github.com/spatialaudio/python-sounddevice/pull/459. Can you please try with this PR? Maybe it reveals a more meaningful error message. – Matthias Mar 12 '23 at 18:40
  • After the patch the error looks like: `||PaMacCore (AUHAL)|| AUHAL component not found.PortAudioError: Error opening OutputStream: Unanticipated host error [PaErrorCode -9999]: '' [ error 0]` – Nick Skywalker Mar 12 '23 at 18:51
  • Sadly, the error message is empty (`''`). If you create an issue at https://github.com/spatialaudio/python-sounddevice/issues, we can continue investigating there, but SO isn't very convenient for that. And please provide the value of `sd._libname`. – Matthias Mar 12 '23 at 19:02
  • Thanks I've created an [issue](https://github.com/spatialaudio/python-sounddevice/issues/460) – Nick Skywalker Mar 12 '23 at 21:10

1 Answers1

0

Solved from the github issue.

The python-sounddevice package from conda is shipped with a portaudio library that apparently isn't compatible with my system (macOS 13.2.1 with M1).

  1. I unsinstalled the python-sounddevice package (it uninstalled the portaudio it shipped with it as no other packages depended on it).
  2. install portaudio with brew
  3. install sounddevice with pip
conda uninstall python-sounddevice
brew install portaudio
pip install sounddevice
Nick Skywalker
  • 1,027
  • 2
  • 10
  • 26