spatialaudio/python-sounddevice

Can't listen to speaks on MacOS

Opened this issue · 5 comments

Hi,
I'm already using sounddevice to analyse the output of an Application on Linux, it works well, thanks !
I've tried to do the same on MacOS but the app is not listed on the devices (only have mic and speakers), so I chose to analyze the speaker instead:

import sounddevice as sd

devices = sd.query_devices()
print(devices)

speakers = devices[1]
samplerate = 48000 
channels = speakers['max_output_channels']
blocksize = 1024  

s = sd.InputStream(device=speakers, channels=channels, samplerate=samplerate, blocksize=blocksize)
> 0 MacBook Air Microphone, Core Audio (1 in, 0 out)
< 1 MacBook Air Speakers, Core Audio (0 in, 2 out)

Traceback (most recent call last):
  File "/Users/xxx/.pyenv/versions/dev3914/lib/python3.9/site-packages/sounddevice.py", line 2746, in _split
    invalue, outvalue = value
ValueError: too many values to unpack (expected 2)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/xxx/.pyenv/versions/dev3914/lib/python3.9/site-packages/sounddevice.py", line 1429, in __init__
    _StreamBase.__init__(self, kind='input', wrap_callback='array',
  File "/Users/xxx/.pyenv/versions/dev3914/lib/python3.9/site-packages/sounddevice.py", line 825, in __init__
    _get_stream_parameters(kind, device, channels, dtype, latency,
  File "/Users/xxx/.pyenv/versions/dev3914/lib/python3.9/site-packages/sounddevice.py", line 2686, in _get_stream_parameters
    device = _get_device_id(device, kind, raise_on_error=True)
  File "/Users/xxx/.pyenv/versions/dev3914/lib/python3.9/site-packages/sounddevice.py", line 2784, in _get_device_id
    idev, odev = _split(id_or_query_string)
  File "/Users/xxx/.pyenv/versions/dev3914/lib/python3.9/site-packages/sounddevice.py", line 2750, in _split
    raise ValueError('Only single values and pairs are allowed') from e
ValueError: Only single values and pairs are allowed

Did I do something wrong ?

On Linux, I'm doing this and it works like a charm !

def process():
    devices = sd.query_devices()
    for device in devices:
        if device['name'] == 'Firefox':
            loopback_device = device['index']

    duration = 10
    samplerate = 48000  
    channels = 2 
    blocksize = 1024  

    with sd.InputStream(device=loopback_device, channels=channels, samplerate=samplerate, blocksize=blocksize) as stream:
        frames_to_capture = int(duration * samplerate / blocksize)

        for _ in range(frames_to_capture):
            data, overflowed = stream.read(blocksize)
            if overflowed:
                print("Warning: Overflow detected!")
                dataprocess(data)

Thanks !

If your device has 0 inputs, you cannot use it in InputStream.

Also, the device argument expects a number or a string, but you are passing a dictionary (which causes the exception).

The error message seems to be of limited usefulness, so I'm trying to improve it in #554. Can you please check if this would provide a more helpful message in your case?

Indeed, the error is far clearer like that, thank you a lot !

So, it's not possible to achieve the results I have on Linux on macOS ? It's not possible to read data from the speakers or any software ?

On Linux query_devices() returns a lot of devices (including applications) but not on macOS. Any idea why ?

Thanks again for your prompt help !

Thanks for testing #554!

On Linux query_devices() returns a lot of devices (including applications)

Please show the list.

Here's the list on Linux. For example, I see Firefox, which is super convenient !

<  0 HDA NVidia: DELL S2721DGF (hw:0,3), ALSA (0 in, 2 out)
   1 HDA NVidia: PHL 245E1 (hw:0,7), ALSA (0 in, 2 out)
   2 HDA NVidia: HDMI 2 (hw:0,8), ALSA (0 in, 8 out)
   3 HDA NVidia: HDMI 3 (hw:0,9), ALSA (0 in, 8 out)
>  4 Logitech StreamCam: USB Audio (hw:1,0), ALSA (2 in, 0 out)
   5 HD-Audio Generic: ALCS1200A Analog (hw:2,0), ALSA (2 in, 6 out)
   6 HD-Audio Generic: ALCS1200A Digital (hw:2,1), ALSA (0 in, 2 out)
   7 HD-Audio Generic: ALCS1200A Alt Analog (hw:2,2), ALSA (2 in, 0 out)
   8 RODE NT-USB: USB Audio (hw:3,0), ALSA (2 in, 0 out)
   9 hdmi, ALSA (0 in, 2 out)
  10 jack, ALSA (2 in, 2 out)
  11 pipewire, ALSA (64 in, 64 out)
  12 pulse, ALSA (32 in, 32 out)
  13 RODE NT-USB Analog Stereo, JACK Audio Connection Kit (4 in, 2 out)
  14 StreamCam Analog Stereo, JACK Audio Connection Kit (2 in, 0 out)
  15 Starship/Matisse HD Audio Controller Analog Stereo, JACK Audio Connection Kit (2 in, 0 out)
  16 Firefox, JACK Audio Connection Kit (2 in, 0 out)
  17 Starship/Matisse HD Audio Controller Digital Stereo (IEC958), JACK Audio Connection Kit (0 in, 0 out)
  18 TU102 High Definition Audio Controller Digital Stereo (HDMI), JACK Audio Connection Kit (0 in, 0 out)

Thanks for the device list, this is very interesting!

I guess you are using some kind of Pipewire/PulseAudio/JACK plugin that provides the Firefox device?

Anyway, there is nothing that can be done from within the sounddevice module. It gets the device list from the underlying PortAudio library which in turn somehow gets it from the platform-specific back-ends.

There might be some way to get a virtual loopback device on macOS/CoreAudio. If such a thing exists and is running on your system, it would add itself to the list of devices and it should be visible in the sounddevice module.