raspberrypi / linux

Kernel source tree for Raspberry Pi-provided kernel builds. Issues unrelated to the linux kernel should be posted on the community forum at https://forums.raspberrypi.com/
Other
11.17k stars 5.01k forks source link

Audio problem with snd_bcm2835 + PyAudio (sound stuttering) #994

Closed josephernest closed 8 years ago

josephernest commented 9 years ago

When using PyAudio with the RaspberryPi's built-in audio snd_bcm2835, we get this strange behaviour:

python test.py          
# ... after 10 or 15 seconds the sound is stuttering!

This problem doesn't exist when you use an external USB soundcard, a DAC, etc.

How to solve this stuttering problem?


It's very simple to reproduce the error. First you need PyAudio >= 0.2.8 (before, it doesn't support audio out via Callback function). Thus you can't apt-get install pyaudio (obsolete). Install PyAudio with:

sudo apt-get update ; sudo apt-get -y install python-dev portaudio19-dev
git clone http://people.csail.mit.edu/hubert/git/pyaudio.git ; cd pyaudio ; sudo python setup.py install ; cd ..

You need to download 20seconds_sine.wav. Then here is test.py (download link here):

# taken from PyAudio example of playing wav / callback version
# http://people.csail.mit.edu/hubert/pyaudio/docs/#id4

import pyaudio
import wave
import time

wf = wave.open('20seconds_sine.wav', 'rb')

p = pyaudio.PyAudio()

def callback(in_data, frame_count, time_info, status):
    data = wf.readframes(frame_count)
    return (data, pyaudio.paContinue)

stream = p.open(format = pyaudio.paInt16, channels = 2, rate = 44100, frames_per_buffer = 2048, output = True, input = False, output_device_index = 0, stream_callback = callback)

stream.start_stream()

while stream.is_active():
    time.sleep(0.1)

stream.stop_stream()

stream.close()

wf.close()

p.terminate()
LinuxCircle commented 9 years ago

I have the same problem with streaming live radio input through my USB sound card on Pi2, until I realised that having 2 channels (stereo) stream made my Pi2 processor suffered a lot... stuttering for a few seconds before the stream died completely. I tried changing channels = 1, it runs with no problem for half hour. However, listening a mono sound is not as nice. I hope there's an improvement with the PyAudio library for Python 3 to somehow compress the throughput.

josephernest commented 9 years ago

I think the problem doesn't come from PyAudio / Python. It is only a wrapper for PortAudio, that works on thousands of audio devices. As previously said, PyAudio works perfectly on RaspberryPi when we use a USB DAC as audio device.

The problem seems really to come from RaspberryPi's built-in audio snd_bcm2835.

What do you think @LinuxCircle ?

popcornmix commented 9 years ago

I can confirm I see the issue with pyaudio + snd_bcm2835. Not sure where the issue lies currently.

popcornmix commented 9 years ago

I've done some logging on firmware side. 44.1kHz audio with 2channels of 16-bits produces 176.4K/s. We get audio in 8K chunks. That should be 21.5 chunks/s.

For the first 10 seconds we get 21 or 22 chunks each second. Then it drops down to about 16 chunks per second so we start underrunning.

So I think firmware is behaving okay. Possibly alsa driver or portaudio is misbehaving.

It looks like portaudio has some latency options. I wonder if these can be set through PyAudio? http://portaudio.com/docs/v19-doxydocs/structPaDeviceInfo.html

LinuxCircle commented 9 years ago

Is there alternative to pyaudio? All I want is to capture and playback the voice, and then translate speech into text.

On Fri, Sep 4, 2015 at 2:39 AM, popcornmix notifications@github.com wrote:

I've done some logging on firmware side. 44.1kHz audio with 2channels of 16-bits produces 176.4K/s. We get audio in 8K chunks. That should be 21.5 chunks/s.

For the first 10 seconds we get 21 or 22 chunks each second. Then it drops down to about 16 chunks per second so we start underrunning.

So I think firmware is behaving okay. Possibly alsa driver or portaudio is misbehaving.

It looks like portaudio has some latency options. I wonder if these can be set through PyAudio? http://portaudio.com/docs/v19-doxydocs/structPaDeviceInfo.html

— Reply to this email directly or view it on GitHub https://github.com/raspberrypi/linux/issues/994#issuecomment-137506310.

josephernest commented 9 years ago

Thanks @popcornmix for your tests ! I did some tests with various latency, the problem is always the same.

It's annoying because PyAudio is one of the most used Python module that provides writing audio with a callback... Are you sure it's because of PyAudio and not a problem with snd_bcm2835 ?

What improvements could be possible on this audio driver to help better performance?

I find the fact that PyAudio + RaspberryPi doesn't work out of the box is a major problem for the development of audio projects.

For my open-source project www.samplerbox.org, we have to ask people to buy a DAC instead, which is annoying...

yienyien commented 9 years ago

Hi, I had the same problem, and I fix it by

  1. converting the sample rate with alsa with .asoundrc
  2. using callback in pyaudio api
  3. using the new pcm (convert, that convert sample rate) in pyaudio
pcm.convert {
         type plug;
         slave {
               pcm default;
               rate 48000;
         }
}
josephernest commented 9 years ago

Bonjour @yienyien , Can you elaborate a bit more? How did you solve the problem? What do you exactly do in 1. ? What's 3., where do you put this code?

If you could paste a reproducible code somewhere on Github, it would help a lot a lot a lot :)

Thanks in advance @yienyien

josephernest commented 9 years ago

Sorry to ask again @yienyien but if you had details about your solution , it would be wonderful for many audio projects, stuck with PyAudio + RaspPi 's built-in soundcard...

yienyien commented 9 years ago

Ok sorry, I will try to clarify.

  1. Put this alsa configuration file in your home ~/.asoundrc (see http://www.alsa-project.org/main/index.php/Asoundrc for more details)
pcm.convert {
         type plug;
         slave {
               pcm default;
               rate 48000;
         }
}

It will create a new "pcm" device that resamples automagically the input framerate to 48Khz that seems to be the "good" framerate for snd_bcm2835

  1. Use this new device in your python program
import pyaudio
import wave
import sys
import time

def play(device_name, path):

    f = wave.open(path, "rb")

    # Create a pyaudio object                                                                                                             
    audio = pyaudio.PyAudio()

    # Looking for the right device by name                                                                                                
    for index in range(audio.get_device_count()):
        desc = audio.get_device_info_by_index(index)
        if desc["name"] == device_name:
            device = index
            rate = int(desc["defaultSampleRate"])
            break

    # Define a reader callback                                                                                                            
    def callback(in_data, frame_count, time_info, status):
        data = f.readframes(frame_count)
        return (data, pyaudio.paContinue)

    # Create the stream with right device, get framerate, the                                                                             
    # number of channel and the format from the wave file                                                                                 
    stream = audio.open(format = audio.get_format_from_width(f.getsampwidth()),
                        channels = f.getnchannels(),
                        rate = f.getframerate(),
                        output_device_index = device,
                        output = True,
                        stream_callback = callback)

    # Wait the stream end                                                                                                                 
    while stream.is_active():
        time.sleep(0.1)

    # Close all                                                                                                                           
    stream.stop_stream()
    stream.close()
    audio.terminate()

if __name__ == "__main__":
    play("convert", sys.argv[1])

Keep me informed

yienyien commented 9 years ago

@josephernest is it working now ?

josephernest commented 9 years ago

Hi @yienyien . I have added these lines into /etc/asound.conf. Unfortunately, not working (yet)... When listing the audio devices with:

import pyaudio
p = pyaudio.PyAudio()

for i in range(p.get_device_count()):
    dev = p.get_device_info_by_index(i)
    print dev

I get this new error:

Expression 'alsa_snd_pcm_hw_params_set_period_size_near( pcm, hwParams,  
&alsaPeriodFrames, &dir )' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 924

Any idea?

PS : these two things might be interesting:

root@samplerbox:~# aplay -L
null
    Discard all samples (playback) or generate zero samples (capture)
convert
default:CARD=ALSA
    bcm2835 ALSA, bcm2835 ALSA
    Default Audio Device
sysdefault:CARD=ALSA
    bcm2835 ALSA, bcm2835 ALSA
    Default Audio Device
dmix:CARD=ALSA,DEV=0
    bcm2835 ALSA, bcm2835 ALSA
    Direct sample mixing device
dmix:CARD=ALSA,DEV=1
    bcm2835 ALSA, bcm2835 IEC958/HDMI
    Direct sample mixing device
dsnoop:CARD=ALSA,DEV=0
    bcm2835 ALSA, bcm2835 ALSA
    Direct sample snooping device
dsnoop:CARD=ALSA,DEV=1
    bcm2835 ALSA, bcm2835 IEC958/HDMI
    Direct sample snooping device
hw:CARD=ALSA,DEV=0
    bcm2835 ALSA, bcm2835 ALSA
    Direct hardware device without any conversions
hw:CARD=ALSA,DEV=1
    bcm2835 ALSA, bcm2835 IEC958/HDMI
    Direct hardware device without any conversions
plughw:CARD=ALSA,DEV=0
    bcm2835 ALSA, bcm2835 ALSA
    Hardware device with all software conversions
plughw:CARD=ALSA,DEV=1
    bcm2835 ALSA, bcm2835 IEC958/HDMI
    Hardware device with all software conversions
default:CARD=DAC
    USB Audio DAC, USB Audio
    Default Audio Device
sysdefault:CARD=DAC
    USB Audio DAC, USB Audio
    Default Audio Device
front:CARD=DAC,DEV=0
    USB Audio DAC, USB Audio
    Front speakers
surround21:CARD=DAC,DEV=0
    USB Audio DAC, USB Audio
    2.1 Surround output to Front and Subwoofer speakers
surround40:CARD=DAC,DEV=0
    USB Audio DAC, USB Audio
    4.0 Surround output to Front and Rear speakers
surround41:CARD=DAC,DEV=0
    USB Audio DAC, USB Audio
    4.1 Surround output to Front, Rear and Subwoofer speakers
surround50:CARD=DAC,DEV=0
    USB Audio DAC, USB Audio
    5.0 Surround output to Front, Center and Rear speakers
surround51:CARD=DAC,DEV=0
    USB Audio DAC, USB Audio
    5.1 Surround output to Front, Center, Rear and Subwoofer speakers
surround71:CARD=DAC,DEV=0
    USB Audio DAC, USB Audio
    7.1 Surround output to Front, Center, Side, Rear and Woofer speakers
iec958:CARD=DAC,DEV=0
    USB Audio DAC, USB Audio
    IEC958 (S/PDIF) Digital Audio Output
dmix:CARD=DAC,DEV=0
    USB Audio DAC, USB Audio
    Direct sample mixing device
dsnoop:CARD=DAC,DEV=0
    USB Audio DAC, USB Audio
    Direct sample snooping device
hw:CARD=DAC,DEV=0
    USB Audio DAC, USB Audio
    Direct hardware device without any conversions
plughw:CARD=DAC,DEV=0
    USB Audio DAC, USB Audio
    Hardware device with all software conversions

and the result of the previous Python code (list all devices) :

Expression 'alsa_snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &alsaPeriodFrames, &dir )' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 924
{'defaultSampleRate': 44100.0, 'defaultLowOutputLatency': 0.005804988662131519, 'defaultLowInputLatency': -1.0, 'maxInputChannels': 0L, 'structVersion': 2L, 'hostApi': 0L, 'index': 0, 'defaultHighOutputLatency': 0.034829931972789115, 'maxOutputChannels': 2L, 'name': u'bcm2835 ALSA: - (hw:0,0)', 'defaultHighInputLatency': -1.0}
{'defaultSampleRate': 44100.0, 'defaultLowOutputLatency': 0.005804988662131519, 'defaultLowInputLatency': -1.0, 'maxInputChannels': 0L, 'structVersion': 2L, 'hostApi': 0L, 'index': 1, 'defaultHighOutputLatency': 0.034829931972789115, 'maxOutputChannels': 2L, 'name': u'bcm2835 ALSA: IEC958/HDMI (hw:0,1)', 'defaultHighInputLatency': -1.0}
{'defaultSampleRate': 44100.0, 'defaultLowOutputLatency': 0.008684807256235827, 'defaultLowInputLatency': -1.0, 'maxInputChannels': 0L, 'structVersion': 2L, 'hostApi': 0L, 'index': 2, 'defaultHighOutputLatency': 0.034829931972789115, 'maxOutputChannels': 2L, 'name': u'USB Audio DAC: - (hw:1,0)', 'defaultHighInputLatency': -1.0}
{'defaultSampleRate': 44100.0, 'defaultLowOutputLatency': 0.005804988662131519, 'defaultLowInputLatency': -1.0, 'maxInputChannels': 0L, 'structVersion': 2L, 'hostApi': 0L, 'index': 3, 'defaultHighOutputLatency': 0.034829931972789115, 'maxOutputChannels': 128L, 'name': u'sysdefault', 'defaultHighInputLatency': -1.0}
{'defaultSampleRate': 44100.0, 'defaultLowOutputLatency': 0.005804988662131519, 'defaultLowInputLatency': -1.0, 'maxInputChannels': 0L, 'structVersion': 2L, 'hostApi': 0L, 'index': 4, 'defaultHighOutputLatency': 0.034829931972789115, 'maxOutputChannels': 128L, 'name': u'default', 'defaultHighInputLatency': -1.0}
{'defaultSampleRate': 48000.0, 'defaultLowOutputLatency': 0.021333333333333333, 'defaultLowInputLatency': -1.0, 'maxInputChannels': 0L, 'structVersion': 2L, 'hostApi': 0L, 'index': 5, 'defaultHighOutputLatency': 0.021333333333333333, 'maxOutputChannels': 2L, 'name': u'dmix', 'defaultHighInputLatency': -1.0}
josephernest commented 9 years ago

(suite)

I also tried with

pcm.blah {
  type rate
  slave {
    pcm "hw:0,0"
    rate 48000
  }
}

Nearly the same error when listing all the available devices with PyAudio:

Expression 'alsa_snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &alsaPeriodFrames, &dir )' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 934

Have you tried on a RPi, does it work for you @yienyien ? Which distribution?

yienyien commented 9 years ago

I have a raspbian:

> uname -a
Linux raspberrypi 4.1.6-v7+ #810 SMP PREEMPT Tue Aug 18 15:32:12 BST 2015 armv7l GNU/Linux

I have installed PyAudio in a virtualenv with pip, then I have the 0.2.8 in my project (against 0.2.4 by default in raspbian) because the my 0.2.4 does not support the callback style.

>  pip freeze
PIL==1.1.7
PyAudio==0.2.8
RPi.GPIO==0.5.11
angus-sdk-python==0.0.7
argparse==1.2.1
backports.ssl-match-hostname==3.4.0.2
certifi==2015.09.06.2
distribute==0.6.24
futures==3.0.3
mcpi==0.1.1
medusa==0.5.4
meld3==0.6.5
numpy==1.6.2
pexpect==2.4
picamera==1.10
pifacecommon==4.1.2
pifacedigitalio==3.0.4
py==1.4.30
pygame==1.9.1release
pygobject==3.8.2
pyserial==2.5
pytest==2.6.4
reportlab==2.5
requests==2.5.3
requests-futures==0.9.5
six==1.9.0
supervisor==3.0a8
tornado==4.2.1
virtualenv==1.7.1.2
wsgiref==0.1.2

But that do not explain your error because with the default PyAudio (from distrib) your script output is:

ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.front
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
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround40
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround41
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround50
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround51
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround71
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.iec958
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.iec958
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.iec958
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
ALSA lib pcm_dmix.c:957:(snd_pcm_dmix_open) The dmix plugin supports only playback stream
{'defaultSampleRate': 44100.0, 'defaultLowOutputLatency': 0.011609977324263039, 'defaultLowInputLatency': -1.0, 'maxInputChannels': 0, 'structVersion': 2, 'hostApi': 0, 'index': 0, 'defaultHighOutputLatency': 0.046439909297052155, 'maxOutputChannels': 2, 'name': 'bcm2835 ALSA: bcm2835 ALSA (hw:0,0)', 'defaultHighInputLatency': -1.0}
{'defaultSampleRate': 44100.0, 'defaultLowOutputLatency': 0.011609977324263039, 'defaultLowInputLatency': -1.0, 'maxInputChannels': 0, 'structVersion': 2, 'hostApi': 0, 'index': 1, 'defaultHighOutputLatency': 0.046439909297052155, 'maxOutputChannels': 2, 'name': 'bcm2835 ALSA: bcm2835 IEC958/HDMI (hw:0,1)', 'defaultHighInputLatency': -1.0}
{'defaultSampleRate': 44100.0, 'defaultLowOutputLatency': 0.011609977324263039, 'defaultLowInputLatency': -1.0, 'maxInputChannels': 0, 'structVersion': 2, 'hostApi': 0, 'index': 2, 'defaultHighOutputLatency': 0.046439909297052155, 'maxOutputChannels': 128, 'name': 'sysdefault', 'defaultHighInputLatency': -1.0}
{'defaultSampleRate': 44100.0, 'defaultLowOutputLatency': 0.011609977324263039, 'defaultLowInputLatency': -1.0, 'maxInputChannels': 0, 'structVersion': 2, 'hostApi': 0, 'index': 3, 'defaultHighOutputLatency': 0.046439909297052155, 'maxOutputChannels': 128, 'name': 'convert', 'defaultHighInputLatency': -1.0}
{'defaultSampleRate': 44100.0, 'defaultLowOutputLatency': 0.011609977324263039, 'defaultLowInputLatency': -1.0, 'maxInputChannels': 0, 'structVersion': 2, 'hostApi': 0, 'index': 4, 'defaultHighOutputLatency': 0.046439909297052155, 'maxOutputChannels': 128, 'name': 'default', 'defaultHighInputLatency': -1.0}
{'defaultSampleRate': 48000.0, 'defaultLowOutputLatency': 0.042666666666666665, 'defaultLowInputLatency': -1.0, 'maxInputChannels': 0, 'structVersion': 2, 'hostApi': 0, 'index': 5, 'defaultHighOutputLatency': 0.042666666666666665, 'maxOutputChannels': 2, 'name': 'dmix', 'defaultHighInputLatency': -1.0}

The error seems to be come from "portaudio" my version is:

dpkg -l | grep portaudio
ii  libportaudio2:armhf                   19+svn20111121-1                        armhf        Portable audio I/O - shared library
ii  libportaudiocpp0:armhf                19+svn20111121-1                        armhf        Portable audio I/O C++ bindings - shared library
ii  portaudio19-dev                       19+svn20111121-1                        armhf        Portable audio I/O - development files
yienyien commented 9 years ago
> cat /etc/apt/sources.list
deb http://mirrordirector.raspbian.org/raspbian/ wheezy main contrib non-free rpi
# Uncomment line below then 'apt-get update' to enable 'apt-get source'
#deb-src http://archive.raspbian.org/raspbian/ wheezy main contrib non-free rpi
josephernest commented 8 years ago

Hi @yienyien . I tried a lots of time, but unfortunately it doesn't work. This is sad because what you achieved would be really really great for my open source project www.samplerbox.org.

Could I ask you a favor? Could you try this Raspberry Pi distribution (it's a Debian Jessie, optimized for my software SamplerBox) : http://www.samplerbox.org/makeitsoftware and try if you achieve to do your "magic audio output trick" ?

It would be wonderful for the project SamplerBox : it would allow to have good audio quality without requiring an external USB DAC :)

G10DRAS commented 8 years ago

@josephernest, is your problem resolved ?

josephernest commented 8 years ago

No. I havent been able to run successfully the method detailed here.

On Thursday, December 17, 2015, G10DRAS notifications@github.com wrote:

@josephernest https://github.com/josephernest, is your problem resolved ?

— Reply to this email directly or view it on GitHub https://github.com/raspberrypi/linux/issues/994#issuecomment-165406138.

G10DRAS commented 8 years ago

I spent 10 days on the issue tried all the solutions, tips and tricks available on internet, but not able to resolve issue. PyAudio recording and playback was working good previously on my Raspberry Pi 2 with raspbian Wizzy. One day I ran sudo apt-get update && sudo apt-get upgrade and next day problem started on my Pi.

josephernest commented 8 years ago

Yes, me too. I spent too much days on this issue, it's really annoying. If RaspberryPi cannot provide a decent audio output in the next months, I'll sadly have to move to another platform for my project.

On Tue, Dec 22, 2015 at 4:50 AM, G10DRAS notifications@github.com wrote:

I spent 10 days on the issue tried all the solutions, tips and tricks available on internet, but not able to resolve issue. PyAudio recording and playback was working good previously on my Raspberry Pi 2 with raspbian Wizzy. One day I ran sudo apt-get update && sudo apt-get upgrade and next day problem started on my Pi.

— Reply to this email directly or view it on GitHub https://github.com/raspberrypi/linux/issues/994#issuecomment-166498671.

gillhofer commented 8 years ago

I solved the problem by using the array module for storing the data.

See here for more info.

Indigo-Tech commented 8 years ago

Hi guys I see that no one here found the answer for this so far, so after few days of tasting a found the solution for this when opening a new stream you need to set frames_per_buffer to 48000

something like this:

stream = audio.open(format=pyaudio.paInt16, channels=2, rate=RATE_PLAY, output=True, frames_per_buffer=48000)

I hope that will help,

josephernest commented 8 years ago

Thanks.

Yes but then, it means that I should real-time resample all my 44.1khz samples to 48khz. This will be CPU-intensive too.

Isn't there a clean way to open a stream in 44.1 khz without having this issue on RPi?

On Wed, Jul 6, 2016 at 1:45 PM, Indigo-Tech notifications@github.com wrote:

Hi guys I see that no one here found the answer for this so far, so after few days of tasting a found the solution for this when opening a new stream you need to set frames_per_buffer to 48000

something like this:

stream = audio.open(format=pyaudio.paInt16, channels=2, rate=RATE_PLAY, output=True, frames_per_buffer=48000)

I hope that will help,

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/raspberrypi/linux/issues/994#issuecomment-230749331, or mute the thread https://github.com/notifications/unsubscribe/AF4eEwddKOFdVgDY-c4tbQgYd6LhoDo4ks5qS5VkgaJpZM4E0c_k .

Indigo-Tech commented 8 years ago

You can also set it to 44000, and You can try using alsaaudio I moved to it and its working much better for me then the pyaudio.

here are some documentation: http://larsimmisch.github.io/pyalsaaudio/libalsaaudio.html#module-alsaaudio

example for playing a wav file: https://github.com/larsimmisch/pyalsaaudio/blob/master/playwav.py

install it using pip: pip install pyalsaaudio

here how I used it: `device = alsaaudio.PCM() device.setchannels(1) device.setrate(22000) device.setformat(alsaaudio.PCM_FORMAT_S16_LE) device.setperiodsize(320)

data = f.read(320) while data: device.write(data) data = f.read(320)`

when the f is the wav file opened in byte mode.

I needed this to stream the wav file while I read it from url.

Ruffio commented 8 years ago

@josephernest has your issue been resolved? If so, please close this issue. Thanks.

josephernest commented 8 years ago

I'm currently in holidays so I can't check, but in my memories : 44.1khz + RPi + PyAudio (in callback mode) Still doesn't work.

The problem doesn't come from PyAudio/Port audio because this works ok on hundreds of other sound chips.

On Tuesday, August 16, 2016, Rasmus Christiansen notifications@github.com wrote:

@josephernest https://github.com/josephernest has your issue been resolved? If so, please close this issue. Thanks.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/raspberrypi/linux/issues/994#issuecomment-240065718, or mute the thread https://github.com/notifications/unsubscribe-auth/AF4eE5P70j11Ks9tR8XPEYaIuTYzlEDWks5qgZG2gaJpZM4E0c_k .

G10DRAS commented 8 years ago

I tested pyaudio as follows:

Play a wave file (16-bit , Rate: 16000Hz, mono) with

  1. aplay --- worked.
  2. play (sox) --- worked.
  3. pyalsaaudio --- worked.
  4. pyaudio --- Didn't work. Tried pyaudio version 0.2.4, 0.2.8 and 0.2.9 --- same result, sound stuttering

A wave file recorded using pyaudio and play with

  1. aplay --- worked.
  2. play (sox) --- worked.
  3. pyalsaaudio --- worked.
  4. pyaudio --- Didn't work.

I think its an issue with Portaudio as pyaudio is marely a python wrapper for it.

josephernest commented 8 years ago
  1. aplay --- worked.
  2. play (sox) --- worked.
  3. pyalsaaudio --- worked.

Are you sure you used them in 'callback' mode (non blocking asynchronous mode) ?

Because when I use PyAudio to play a WAV file in blocking mode, it works (see 'Play' source code in https://people.csail.mit.edu/hubert/pyaudio/, it works ; the one which doesnt work is 'Play (callback)').

I've used portaudio on many projects and many platforms. It always worked, with a large variety audio chips (onboard chips, external USB soundcards, etc.). So I don't think the problem comes from PortAudio.

According to what I read, the problem seems to come when using sndbcm2835 is working in 44.1 khz, because it seems to be ''natively'' in 48khz.

So currently (august 2016), this issue isn't solved : it's impossible to use sndbcm2835 in 44.1khz in callback mode with pyaudio.

On Thursday, August 18, 2016, G10DRAS notifications@github.com wrote:

Play a wave file (16-bit , Rate: 16000Hz, mono) with

  1. aplay --- worked.
  2. play (sox) --- worked.
  3. pyalsaaudio --- worked.
  4. pyaudio --- Didn't work. Tried pyaudio version 0.2.4, 0.2.8 and 0.2.9 --- same result, sound stuttering

A wave file recorded using pyaudio

  1. aplay --- worked.
  2. play (sox) --- worked.
  3. pyalsaaudio --- worked.
  4. pyaudio --- Didn't work.

I think its an issue with Portaudio as pyaudio is marely a python wrapper for it.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/raspberrypi/linux/issues/994#issuecomment-240625241, or mute the thread https://github.com/notifications/unsubscribe-auth/AF4eE45tz4m9C-aeNk5TTLjJXojTUHhXks5qg-jrgaJpZM4E0c_k .

G10DRAS commented 8 years ago

I have recorded a wave(Signed 16 bit Little Endian, Rate 44100 Hz, Stereo) with pyaudio.

$ play output44100.wav 
 File Size: 1.72M     Bit Rate: 1.41M
  Encoding: Signed PCM    
  Channels: 2 @ 16-bit   
Samplerate: 44100Hz      
Replaygain: off         
  Duration: 00:00:09.75  
In:100%  00:00:09.75 [00:00:00.00] Out:430k  [      |      ]        Clip:0    
Done.
$ aplay output44100.wav 
Playing WAVE 'output44100.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo

Now I start using pyalsaaudio

josephernest commented 8 years ago

No, this is not correct : if you do it properly, it does work to play a .wav in blocking mode with pyaudio (which is just a wrapper to PortAudio).

As you mention, it works with "aplay" (and all the others you mentioned), but these tools probably use blocking mode as well.

So this is definately not a proof that the problem comes from pyaudio.

On Fri, Aug 19, 2016 at 4:37 AM, G10DRAS notifications@github.com wrote:

I have recorded a wave(Signed 16 bit Little Endian, Rate 44100 Hz, Stereo) with pyaudio.

-

But it didn't play with pyaudioin both blocking and non blocking (callback) mode.

Same wave play with play(sox)

$ play output44100.wav File Size: 1.72M Bit Rate: 1.41M Encoding: Signed PCM Channels: 2 @ 16-bit Samplerate: 44100Hz Replaygain: off Duration: 00:00:09.75 In:100% 00:00:09.75 [00:00:00.00] Out:430k [ | ] Clip:0 Done.

  • It play with aplay

$ aplay output44100.wav Playing WAVE 'output44100.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo

-

It play with pyalsaaudio

It play with PySoundFileand sounddevice

Now I start using pyalsaaudio

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/raspberrypi/linux/issues/994#issuecomment-240912927, or mute the thread https://github.com/notifications/unsubscribe-auth/AF4eE53e7rZsnxkkb_hQCi6mhjhGXy2bks5qhRcEgaJpZM4E0c_k .

G10DRAS commented 8 years ago

python-sounddevice like pyaudio, is a python binding for PortAudio library, and playback with it is perfectly fine. So it must be pyaudio thats not working as expected.

josephernest commented 8 years ago

@G10DRAS, interesting! Can you paste code showing how you did the playback with python-sounddevice, in callback mode?

On Mon, Aug 29, 2016 at 11:05 AM, G10DRAS notifications@github.com wrote:

python-sounddevice like pyaudio, is a python binding for PortAudio library, and playback with it is perfectly fine. So it must be pyaudio thats not working as expected.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/raspberrypi/linux/issues/994#issuecomment-243071604, or mute the thread https://github.com/notifications/unsubscribe-auth/AF4eE7Nt2lL0g5M86pVPe5FDxEv4G36Fks5qkqDJgaJpZM4E0c_k .

G10DRAS commented 8 years ago

See the 'blocking=False' in play()

import sounddevice as sd
import soundfile as sf

filename = '20seconds_sine.wav'
device=0
dtype = 'int16'
try:
    data, fs = sf.read(filename, dtype)
    sd.play(data, fs, device=device, blocking=False)
    sd.wait()
except BaseException as e:
    raise SystemExit(str(e))
josephernest commented 8 years ago

This is not a "callback" mode, with a

def audiocallback():
      # process an audio buffer of audio
      # here return a buffer of 1024 samples for example

On Mon, Aug 29, 2016 at 12:52 PM, G10DRAS notifications@github.com wrote:

See the 'blocking=False' in play()

import sounddevice as sd import soundfile as sf

filename = '20seconds_sine.wav' device=0 dtype = 'int16' try: data, fs = sf.read(filename, dtype) sd.play(data, fs, device=device, blocking=False) sd.wait() except BaseException as e: raise SystemExit(str(e))

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/raspberrypi/linux/issues/994#issuecomment-243093062, or mute the thread https://github.com/notifications/unsubscribe-auth/AF4eEzx6N_hVZvU3QvYnmg7j8RjFWrroks5qkrntgaJpZM4E0c_k .

G10DRAS commented 8 years ago

oh you want this....

import sounddevice as sd
import soundfile as sff 

duration = 5
filename = '20seconds_sine.wav'
device = 0
dtype = 'int16'

with sff.SoundFile(filename) as sf:
    def callback(outdata, frame_count, time, status):
        data = sf.read(frame_count, dtype=dtype, out=outdata)
        if (data.size != outdata.size): 
            # zero the part of outdata not written into by sf.read()
            outdata[data.shape[0]:,:].fill(0.0)
            raise sd.CallbackStop()

    with sd.OutputStream(device=device, samplerate=sf.samplerate,
                        channels=sf.channels,
                        dtype=dtype,
                        callback=callback) as ss:
        while (ss.active):
            sd.sleep(duration * 1000)
josephernest commented 8 years ago

It works, you're right! Problem solved after more than one year! This allows a major update for www.samplerbox.org: it now works with RPi's built-in audio chip. Great!

elanals commented 7 years ago

I'm having a problem that I think is very related to this and I haven't been able to solve it. Instead of playing a wav file I want to generate the audio directly in my code. As in the following example:

(source: https://mail.python.org/pipermail/tutor/2012-September/091476.html)

import math
import struct
import pyaudio

def play_tone(frequency, amplitude, duration, fs, stream):
    N = int(fs / frequency)
    T = int(frequency * duration)  # repeat for T cycles
    dt = 1.0 / fs
    # 1 cycle
    tone = (amplitude * math.sin(2 * math.pi * frequency * n * dt)
            for n in xrange(N))
    # todo: get the format from the stream; this assumes Float32
    data = ''.join(struct.pack('f', samp) for samp in tone)
    for n in xrange(T):
        stream.write(data)

fs = 48000
p = pyaudio.PyAudio()
stream = p.open(
    format=pyaudio.paFloat32,
    channels=1,
    rate=fs,
    output=True)

# play the C major scale
scale = [130.8, 146.8, 164.8, 174.6, 195.0, 220.0, 246.9, 261.6]
for tone in scale:
    play_tone(tone, 0.5, 0.75, fs, stream)

# up an octave
for tone in scale[1:]:
    play_tone(2*tone, 0.5, 0.75, fs, stream)

stream.close()
p.terminate()

When I run this on my computer (OS X) it works fine. When I run it on raspberry pi (3, with raspian jessie) I get a stuttery sound output and the following errors:

ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.front ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround21 ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround21 ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround40 ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround41 ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround50 ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround51 ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround71 ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.iec958 ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.iec958 ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.iec958 ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline ALSA lib pulse.c:243:(pulse_connect) PulseAudio: Unable to connect: Connection refused

ALSA lib pulse.c:243:(pulse_connect) PulseAudio: Unable to connect: Connection refused

Cannot connect to server socket err = No such file or directory Cannot connect to server request channel jack server is not running or cannot be started

I am very new to this and have attempted to solve the problem with the suggestions made in this thread, was however not successful and would really appreciate some help.

josephernest commented 7 years ago

The problem was solved by replacing pyaudio by sounddevice module.

elanals commented 7 years ago

I can't get sounddevice to work, when I try to run the code I get the error message: No module named sounddevice My attempts to follow the instructions on this page https://pypi.python.org/pypi/sounddevice/ are leading nowhere. Can you provide me with any help in installing sounddevice?

Indigo-Tech commented 7 years ago

Just use pyalsaaudio,

its simple as:

import alsaaudio
out_stream = alsaaudio.PCM(alsaaudio.PCM_PLAYBACK, alsaaudio.PCM_NORMAL, 'default')
out_stream.setformat(alsaaudio.PCM_FORMAT_S16_LE)
out_stream.setchannels(channels)
out_stream.setrate(rate)
out_stream.setperiodsize(size)

data = file.read(size)

while data:
    out_stream.write(data)
    data = file.read(size)
apt-get install libasound2-dev
pip install pyalsaaudio

if you have dmix use it.

elanals commented 7 years ago

@Indigo-Tech

Could you please specify your example for the creation of a 100Hz sine wave?

Indigo-Tech commented 7 years ago

@elanals

import math
import struct
import alsaaudio

def play_tone(frequency, amplitude, duration, fs, stream):
    N = int(fs / frequency)
    T = int(frequency * duration)  # repeat for T cycles
    dt = 1.0 / fs
    # 1 cycle
    tone = (amplitude * math.sin(2 * math.pi * frequency * n * dt)
            for n in xrange(N))
    # todo: get the format from the stream; this assumes Float32
    data = ''.join(struct.pack('f', samp) for samp in tone)
    for n in xrange(T):
        stream.write(data)

fs = 48000
stream = alsaaudio.PCM(alsaaudio.PCM_PLAYBACK, alsaaudio.PCM_NORMAL, 'default')
stream.setformat(alsaaudio.PCM_FORMAT_FLOAT_LE)
stream.setchannels(1)
stream.setrate(fs)
stream.setperiodsize(1024)

# play the C major scale
scale = [130.8, 146.8, 164.8, 174.6, 195.0, 220.0, 246.9, 261.6]
for tone in scale:
    play_tone(tone, 0.5, 0.75, fs, stream)

# up an octave
for tone in scale[1:]:
    play_tone(2*tone, 0.5, 0.75, fs, stream)

stream.close()
elanals commented 7 years ago

@Indigo-Tech Thank you for that, but the problem still remains that the audio output sounds completely wrong and unclean...

Indigo-Tech commented 7 years ago

@elanals

Are you using the built-in sound device?

elanals commented 7 years ago

@Indigo-Tech yes, I have regular headphones plugged into the headphone jack on the raspberry pi

Indigo-Tech commented 7 years ago

It seems the problem is in the build in sound card, I tested it now with usb external sound card and it works fine but with the built in one sound unclean as you said.

elanals commented 7 years ago

@Indigo-Tech Would something like this work? https://www.conrad.de/de/71-soundkarte-extern-hama-71-surround-externe-kopfhoereranschluesse-871260.html (sorry that the website is German, but that's the shop I would go buy it from to be able to get it as quickly as possible)

Indigo-Tech commented 7 years ago

Yes, it should be fine.

moham96 commented 6 years ago

Wow, three years and this issue is still not resolved!

JamesH65 commented 5 years ago

This issue is closed. Are you saying this particular issue is still a problem, or you have a different problem?