spatialaudio / python-sounddevice

:sound: Play and Record Sound with Python :snake:
https://python-sounddevice.readthedocs.io/
MIT License
980 stars 145 forks source link

MacOS : Cannot allocate write+execute memory for ffi.callback() #397

Open ealpha opened 2 years ago

ealpha commented 2 years ago

python3 play_file.py file.wav

MemoryError: Cannot allocate write+execute memory for ffi.callback(). You might be running on a system that prevents this. For more information, see https://cffi.readthedocs.io/en/latest/using.html#callbacks

mgeier commented 2 years ago

See #270.

I'm open to suggestions how to improve the situation.

gormster commented 2 years ago

I cloned the repo and hacked at it until it worked.

https://github.com/gormster/python-sounddevice/tree/api-mode-experimental

Note that this replaces ABI mode completely with API mode. I'm sure there's a graceful way to handle both.

also, I should probably be overriding build_py not install but I couldn't get that to work. I don't have a ton of experience with setuptools and the documentation is pretty spotty.

mgeier commented 2 years ago

Thanks @gormster, see also #91.

Note that this replaces ABI mode completely with API mode. I'm sure there's a graceful way to handle both.

This would be great, but I'm not so sure that's possible.

See also https://groups.google.com/g/python-cffi/c/oBMFw7R1sFI/m/LJDo6NPJCwAJ

gormster commented 2 years ago

Ok so… I've done a thing where it does (I think) handle this gracefully. (Well, sort of; it uses an environment variable at runtime to select which mode to use. That was the best I could think of without breaking the API.) However, in testing it, I realised the original problem is actually only occurring when using a conda environment and not in my base Python install.

So I tried using it under my conda install and… well, it also seems to work when I build it from source. I'm kind of a novice with conda, I usually use pipenv, but it seems like (especially for M1 Macs) conda is a lot more usable.

And here's the kicker: if I go into a fresh conda environment with numpy installed, then within that environment do python -m pip install sounddevicethat also works fine. Even though that should be identical to conda install. I mean… python -m pip install is literally exactly what conda install does according to the recipe for python-sounddevice. And yet… it doesn't work the same. I don't get it.

In short: I have no real way of testing that this patch fixes this bug at all.

Now, this all said, API mode is clearly superior in cases where a C compiler is available, or for which a wheel can be precompiled, and it sure seems like the way I've done it does switch between API and ABI mode seamlessly… but again, it's tough to test if that's actually what's happening.

gormster commented 2 years ago

@ealpha In case you missed it in that wall of text, here's a potential workaround:

  1. Create a fresh conda environment. Do not install python-sounddevice. (You can install numpy or whatever else you want.)
  2. Use conda activate to enter your new environment
  3. Run python -m pip install sounddevice
HaHeho commented 2 years ago

Not contributing to the actual issue, but if I install via conda conda install python-sounddevice, I end up with the installation from conda-forge, not PyPI. This is due to my conda configuration also considering conda-forge.

Otherwise one would do conda install python-sounddevice -c conda-forge. But in that case one ends up with all packages from conda-forge instead of conda-main. For things like numpy this is not preferable however (strong performance differences depending on system architecture).

So an okay way around that would be (resulting in a version of portaudio from conda-forge, but same version number as conda-main):

conda create -n sd numpy
conda activate sd
conda install python-sounddevice -c conda-forge
mgeier commented 2 years ago

There are at least two versions of the PortAudio library availble for conda:

It is theoretically possible to install portaudio with conda and sounddevice with pip, but I don't think that makes sense.

However, it is possible to not install portaudio with conda and then install sounddevice with pip, as @gormster mentioned above. In this case, the bundled PortAudio library from https://github.com/spatialaudio/portaudio-binaries will be used.

In any case, you can check which library is actually used like this:

import sounddevice as sd
print(sd._libname)

In general, I would recommend conda-forge over the default channel. However, I wasn't aware of this:

For things like numpy this is not preferable however (strong performance differences depending on system architecture).

This seems unfortunate. Why is conda-forge not as good? Isn't that something that should be fixed? I didn't find an issue for that: https://github.com/conda-forge/numpy-feedstock/issues

https://anaconda.org/anaconda/numpy https://anaconda.org/conda-forge/numpy

HaHeho commented 2 years ago

In general, I would recommend conda-forge over the default channel. However, I wasn't aware of this:

For things like numpy this is not preferable however (strong performance differences depending on system architecture).

This seems unfortunate. Why is conda-forge not as good? Isn't that something that should be fixed? I didn't find an issue for that: https://github.com/conda-forge/numpy-feedstock/issues

https://anaconda.org/anaconda/numpy https://anaconda.org/conda-forge/numpy

FYI, the difference is what accelerated linear algebra library Numpy is built against, as documented here https://numpy.org/install/#numpy-packages--accelerated-linear-algebra-libraries. I suppose the conclusion of "strong performance differences" should be taken with a grain of salt and seen in context of what one is intending to do, and on what system architecture.

AjaniStewart commented 1 year ago

@ealpha In case you missed it in that wall of text, here's a potential workaround:

  1. Create a fresh conda environment. Do not install python-sounddevice. (You can install numpy or whatever else you want.)
  2. Use conda activate to enter your new environment
  3. Run python -m pip install sounddevice

This worked for me. Just a heads up for other people, if you conda install other packages that has cffi as a dependency (after python -m pip install sounddevice), it might muck things up. Better to use pip for those packages.

ealpha commented 1 year ago

I still have this problem in macOS 12.6

mberz commented 1 year ago

@ealpha You may want to make sure that you install cffi via pip as well as stated by @AjaniStewart. I can confirm that the workaround works on macOS 12.6 & arm64 if cffi and sounddevice are installed using pip. You can use python -m pip install sounddevice --force-reinstall to do so, beware that this will do a force reinstall of all dependencies though.

petriewong commented 1 year ago

Hi, I had the same issue with my M1 mac mini, macos 13.0 & anaconda environment.

pip uninstall cffi
pip install cffi 

It works for me.