Getting Started with Python 3.0

Moderator: flatmax

Post Reply
dBm
Posts: 4
Joined: Tue Apr 11, 2017 7:09 pm

Getting Started with Python 3.0

Post by dBm » Tue Apr 11, 2017 7:38 pm

I am a new user of the Raspberry Pi, Linux and Python so quite a lot to get my head around. I have finally gotten Python 3.0 to work with alsaaudio by installing the package using the following:

Code: Select all

sudo apt-get install libasound2-dev
sudo -H pip3 install pyalsaaudio
I have gotten the Audio Injector to show up in the list of available cards or pcm devices (not sure which) by adding the following line to the config.txt file:

Code: Select all

dtoverlay=audioinjector-wm8731-audio
I have not commented out the following even though I think for some applications it is suggested:

Code: Select all

dtparam=audio
as I don't think I need to for my case. This is because I think I can switch between audio sources or audio outputs in the code - if only I could get the code to work. By-the-way, I have tried commenting it out too and it made no difference to this problem.

Anyway; when I try to write some Python code I get errors. Being a complete beginner I could be making the most horrendous elementary mistakes here so apologies for being a total idiot but here goes. This is what I have been trying:

Code: Select all

import alsaaudio

# set up the constants
CHANNELS         = 2
RATE                 =alsaaudio.PCM_FORMAT_FLOAT_LE
INPUT_FORMAT = 48000
FRAMESIZE        = 1024

card_info = {}

for device_number, card_name in enumerate(alsaaudio.pcms()):
  card_info[card_name] = "hw:%s,0" % device_number

recorder = alsaaudio.PCM(device=card_info["default:CARD=audioinjectorpi"]. type=alsaaudio.PCM_CAPTURE, mode=alsaaudio.PCM_NORMAL)
# this line (above) is where the error occurs

recorder.setchannels(CHANNELS)
recorder.setrate(RATE)
recorder.setformat(INPUT_FORMAT)
recorder.setperiodsize(FRAMESIZE) 
The error is alsaaudio.ALSAAudioError: No such file or directory [hw:12,0]

What I do know is that "default:CARD=audioinjectorpi" is index 12 so that much makes sense but why the no such file or directory error? What am I doing wrong or what haven't I set up?

flatmax
Posts: 609
Joined: Sat Jul 23, 2016 11:39 pm

Re: Getting Started with Python 3.0

Post by flatmax » Tue Apr 11, 2017 10:10 pm

Hi there,

While I am familiar with python, I have never used pyalsaaudio, or alsa programming with python.

Perhaps though I can point you in the right direction. Your error mentions "hw:12,0" which is ALSA speak for a hardware card number 12 and sub device 0. Now I don't think this device exists, which is most likely why it will not load ...
You can see the available devices by typing the following commands :
aplay -l
aplay -L

You can switch "default:CARD=audioinjectorpi" for the correct device which matches the audioinjector.

Now, I am not sure if leaving two different cards using the same i2s driver will work , which is why it is porbably a good idea to comment out the "dtparam=audio" part of boot/config.txt.

Finally you can see available cards using cat cat /proc/asound/cards

Hope that helps !
Matt
Check out our audiophile quality crossovers : https://bit.ly/2kb1nzZ
Please review the Zero sound card on Amazon USA : https://www.amazon.com/dp/B075V1VNDD
---
Check out our new forum on github : https://github.com/Audio-Injector

dBm
Posts: 4
Joined: Tue Apr 11, 2017 7:09 pm

Re: Getting Started with Python 3.0

Post by dBm » Thu Apr 13, 2017 6:34 am

Matt, Hi. Thanks for the prompt reply. Your suggestions were hugely useful. Pointed me in the right direction.

Yes, there is a problem with alsaaudio. The list of "cards" bears no relation to the actual set of cards. To get the code talking to the right card I had to effectively lie and use the descriptor of the third item in the list rather than the audio injector itself (pulse if you are interested). Having realised the limitations of aslsaaudio I therefore swapped the code over to use pyaudio instead. This modification, I have found, delivers a much more realistic list of cards. Having done this I still could not get the thing working till I discovered that there is a minimum buffer size and hence my delay in replying to your post! Anyway, I now have connection. It works I think. So; the things I have learned are:
  • use pyaudio (import pyaudio) because it gives a good list of cards
  • use a large buffer (not sure how large but thousands worked, tens didn't
  • it appears I didn't need to comment out the "dtparam=audio, you can have multiple cards working at the same time"
I will post some example working code when I have a chance.

dBm
Posts: 4
Joined: Tue Apr 11, 2017 7:09 pm

Re: Getting Started with Python 3.0

Post by dBm » Fri Apr 14, 2017 3:09 am

Because my Pi is not networked then all of this is hand-typed into another computer. So I hope there are not too many mistakes.

Essentially then this is some Python code then that reads the Audio Injector input - or at least seems to - and makes the audio samples available for processing. I think it will work with a variety of cards. Keep in mind that I have been programming for just a few days now so don't expect superb style, concise coding or wonderful workflow. To be honest it is probably terrible...

Code: Select all

import pyaudio
import numpy

p = pyaudio.PyAudio() # instantiate the pyaudio

# now get a list of all the audio cards or devices to verify the device index used below
for i in range(p.get_device_count()):
  device = p.get_device_info_by_index(i)
  print((i, device['name'], device['maxInputChannels'])) # won't surprise that there is also a maxOutputChannels

CHANNELS  = 1                       # or could be 2 for stereo or dual mono
FORMAT     = pyaudio.paInt32 # 8 bits more than needed for the card but makes the conversion easier later
RATE         = 48000                # of course you might like 44100 which is more of a popular home rate or 96000
                                            # or one of the lower rates 
FRAMESIZE = 12000                # don't make this too small it just doesn't gather any samples if you do

# Create an input stream with this instance of pyaudio
recorder = p.open(format = FORMAT,
                        channels = CHANNELS,
                        rate = RATE,
                        input = True, # would be Output = True if this was an output stream
                        input_device_index = 1, # set to 0 if the built-in audio is disabled in config.txt 
                                                            # while set to 1 if it isn't
                                                            # or possibly higher number if more audio devices are attached
                        frames_per_buffer = FRAMESIZE
                        )

# the main loop which will run forever
# and which constantly finds out how many audio samples there are to read
# and then reads them before converting them to floating point numbers
running = True
while running:
  buffer_size = recorder.get_read_available  # how many samples are available?
  if buffer_size> 0  # only process if there are some ready to process
    sound_string = recorder.read(buffer_size)  # read in as many samples as were ready to be read
    sound_data = numpy.fromstring(sound_string, 'Int32')  # then convert to floating point numbers
                                                                                    # this is why we had to use 32 bit numbers above
  # now do something with your floating point data here
I hope that this is useful.

dBm
Posts: 4
Joined: Tue Apr 11, 2017 7:09 pm

Re: Getting Started with Python 3.0

Post by dBm » Fri Apr 14, 2017 3:13 am

I should have just reiterated that this does not use alsaaudio which therefore does not need to be installed. It simply uses pyaudio which you may well find is installed by default (it was on mine).

Peter
Posts: 9
Joined: Sat May 27, 2017 7:23 am

Re: Getting Started with Python 3.0

Post by Peter » Mon May 29, 2017 7:12 am

Hi dBm,

I am a newbi when it comes to programming in Python (3). Let's start off what my intentions are: Create a 2 way audio link using RPI3's. Implementing audio compression (uLAW) and low sampling speeds. But first things first: How to get this running at normal speeds (48 kHz) and normal audio quality?
Your example to get samples (form the line input) has been quite helpful, although I re-wrote it into a Python Class, rather then your application running it as a recording device. So I think that this class is working. i.e. it seems to collect audio samples and converts them into 4 Byte floats. So far so good.
What I have not been able to resolve is how to get the samples to the audio output of the AudioInjector device on the other RPi3 (data is correctly received, as far as I can tell). Using the code as depicted in the example below, I get the following errors (errors are listed beneat the example code):

class AudioInjector:
"""Class which specifies the use of the Audio Injector Souncard HAT"""
def __init__(self):
global stream_mic
global stream_ls
global mic_audio
global p
self.CHUNK = 1024 # CHUNKS of bytes to read each time from mic
self.FORMAT = pyaudio.paInt32
self.CHANNELS = 1
self.RATE = 48000

def rx_audio(self): # Obtain audio from the Audio Injector INPUT (Microphone)
global p
p = pyaudio.PyAudio() # instantiate the pyaudio
# Create an input stream with this instance of pyaudio
stream_mic = p.open(format = self.FORMAT,
channels = self.CHANNELS,
rate = self.RATE,
input = True, # would be Output = True if this was an output stream
input_device_index = 0, # set to 0 if the built-in audio is disabled in config.txt
# while set to 1 if it isn't
# or possibly higher number if more audio devices are attached
frames_per_buffer = self.CHUNK
)


frames = [ ]
data = stream.read(self.CHUNK)
rcvr_audio = numpy.fromstring(data, 'Int32') # then convert to floating point numbers
# this is why we had to use 32 bit numbers above
# now do something with your floating point data here
stream_mic.close()
rcvr_ulaw_audio = audioop.lin2ulaw(rcvr_audio,4)
return rcvr_ulaw_audio

def tx_audio(self,audio_string_mono): # Send audio to the Audio Injector OUTPUT.
global p
p = pyaudio.PyAudio() # instantiate the pyaudio
# Create an output stream with this instance of pyaudio
stream_ls = p.open(format = self.FORMAT,
channels = self.CHANNELS,
rate = self.RATE,
output = True, # would be Output = True if this was an output stream
output_device_index = 0, # set to 0 if the built-in audio is disabled in config.txt
# while set to 1 if it isn't
# or possibly higher number if more audio devices are attached
frames_per_buffer = self.CHUNK
)

stream_ls.write(stream_ls.wav) # >>>>HERE THE CODE EXECUTION FAILS <<<<
stream_ls.close()


Error listing when I try to execute the "tx_audio" part of this class.
Expression 'ret' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 1736
Expression 'AlsaOpen( &alsaApi->baseHostApiRep, params, streamDir, &self->pcm )' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 1904
Expression 'PaAlsaStreamComponent_Initialize( &self->playback, alsaApi, outParams, StreamDirection_Out, NULL != callback )' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 2175
Expression 'PaAlsaStream_Initialize( stream, alsaHostApi, inputParameters, outputParameters, sampleRate, framesPerBuffer, callback, streamFlags, userData )' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 2840

I wonder if you, or someone else can shine some light on this matter: How to send streaming data to the Audio Injector.

Thanks!
Peter

Peter
Posts: 9
Joined: Sat May 27, 2017 7:23 am

Re: Getting Started with Python 3.0

Post by Peter » Tue Jun 06, 2017 6:54 am

Solved! That's to say, I noticed a flaw in my code, where I used input_device_index both for the Microphone as the Speaker initialisation :( . Changing this helped a lot getting the a audio to be send across a LAN circuit. A next issue has appeared though: How to run the Audio Injector at 12 kHz sampling rate (which it is supposed to be able to handle). 8 kHz is too slow (but works). 12 kHz gives an Invalid sample rate error -9997. I'll open a new question for this one.
BR
Peter

adddog
Posts: 1
Joined: Tue Apr 10, 2018 11:31 am

Re: Getting Started with Python 3.0

Post by adddog » Tue Apr 10, 2018 12:25 pm

Hello guys

I'm interested in the ideas here as well; would be great to control GLSL shaders with the sound; but unable see much of anything in terms of sound data.

Borrowing a little from this thread: viewtopic.php?f=5&t=2360 & your code to print devices and their inputs above, I got:

Code: Select all

(0, 'audioinjector-pi-soundcard: - (hw:0,0)', 2)
(1, 'sysdefault', 128)
(2, 'pulse', 32)
(3, 'dmix', 0)
(4, 'default', 32)
(5, 'system', 2)
Here is the python3 code I was hoping would give readout of the RCA input.

Image

Image

The data is not reflective of what I'm hearing on headphones. I was hoping to be able to use the RCA outputs but they don't seem to work?

Here are my alsa settings:

https://i.imgur.com/Lrb0l45.png

/boot/config.text

Code: Select all

#dtparam=audio=on
dtoverlay=audioinjector-wm8731-audio
dtoverlay=i2s-mmap
Did I miss something?

Post Reply

Who is online

Users browsing this forum: No registered users and 4 guests