Use microphone in Raspberry Pi Pico (Micropython) - python

How can i use micropython firmware alongside a Max9814?
I have written the code below but cant hear clear voice in audacity...
from machine import Pin, ADC
import ustruct , time
analog_value = machine.ADC(26)
conversion_factor =3.3/(65536)
samples = []
while True:
reading = analog_value.read_u16()*conversion_factor
samples.append(int(reading)) #print("ADC: ",reading)
time.sleep(0.002)
with open('Voice.bin', 'wb') as output:
for sample in samples:
output.write(struct.pack('<h', sample))

Try changing
conversion_factor = 3.3/(65536)
to
conversion_factor = 3.3/(4096)
This is because, although the ADC result is returned as a 16-bit integer the actual result is only the lower 12 bits - it is a 12-bit ADC!
Using 65536 (16 bits), the resulting audio will seem quiet as it is only capable of reaching 1/16 of the full-scale range of a 16-bit value.
I would also suggest using the Normalise effect in Audacity, bearing in mind the audio will always sound a bit noisy.
A further point to bear in mind is that you sample rate is unlikely to 100% stable by doing the timing using code. If you want hardware-timed audio it is worth learning to use DMA. e.g. https://iosoft.blog/2021/10/26/pico-adc-dma/

Related

Inaccurate real-time audio FFT interpretation with Python

I'm trying to use Python to create a live music visualization. The libraries I'm using are SoundCard (for live audio capture) and Librosa (for short-time Fourier transform).
However I suspect I'm not interpreting the audio data correctly. Looking at the 100Hz-200Hz bin, I get a constant stream of sound even when the song doesn't contain that much bass (or really, any whatsoever). I admit I am a bit in over my head with all the audio processing FFT stuff, since it's not really my expertise and the math beats me most of the time.
This is the function that captures and analyses the audio. lb is set to the speakers and works properly. Fs is set to 48000 and I record 1000 frames in the attempt of keeping 48FPS. fftwindowsize is set to 2048*8 because... I'm not sure. I increased the number until Librosa stopped throwing warnings.
def audioanalysis():
with lb.recorder(samplerate=Fs) as mic:
rawdata = mic.record(numframes=1000)
datalen: int = int(rawdata.size/2)
monodata = numpy.empty(datalen)
for x in range(0, datalen):
monodata[x] = max(rawdata[x][0], rawdata[x][1])
data = numpy.abs(librosa.stft(monodata, n_fft=fftwindowsize, hop_length=1024))
return librosa.amplitude_to_db(data, ref=numpy.max)
And the code for making buckets:
frequencies = librosa.core.fft_frequencies(n_fft=fftwindowsize)
freq_index_ratio = len(frequencies)/frequencies[len(frequencies)-1] / 2
[...]
for i in range(0,buckets):
avg = 0
for j in range (i * bucketsize, (i+1)*bucketsize):
avg += amp(spectrogram=spectrogram, freq=j)
amps[i] = avg/bucketsize
def amp(spectrogram, freq) -> float:
return spectrogram[int(freq*freq_index_ratio)]
Over the course of a song, amps[1] (so 100Hz-200Hz) stays in the -50dB to -30dB range, which isn't really useful or representative of the song playing.
Is my FFT analysis wrong? Is there no way to better interpret short samples of sound?
P.S. I know my Python code isn't excellent. This is my first project in Python :)

Read left channel of wav data into numpy array

I'm using pyaudio to take input from a microphone or read a wav file, and analyze the stream while playing it. I want to only analyze the right channel if the input is stereo. I've been able to extract the data and convert to integers using loops:
levels = []
length = len(data)
if channels == 1:
for i in range(length//2):
volume = abs(struct.unpack('<h', data[i:i+2])[0])
levels.append(volume)
elif channels == 2:
for i in range(length//4):
j = 4 * i + 2
volume = abs(struct.unpack('<h', data[j:j+2])[0])
levels.append(volume)
I think this working correctly, I know it runs without error on a laptop and Raspberry Pi 3, but it appears to consume too much time to run on a Raspberry Pi Zero when simultaneously streaming the output to a speaker. I figure that eliminating the loop and using numpy may help. I assume I need to use np.ndarray to do this, and the first parameter will be (CHUNK,) where CHUNK is my chunk size for analyzing the audio (I'm using 1024). And the format would be '<h', as in the struct code above, I think. But I'm at a loss as to how to code it correctly for each of the two cases (mono and right channel only for stereo). How do I create the numpy arrays for each of the two cases?
You are here reading 16-bit integers from a binary file. It seems that you are first reading the data into data variable with something like data = f.read(), which is here not visible. Then you do:
for i in range(length//2):
volume = abs(struct.unpack('<h', data[i:i+2])[0])
levels.append(volume)
BTW, that code is wrong, it shoud be abs(struct.unpack('<h', data[2*i:2*i+2])[0]), otherwise you are overlapping bytes from different values.
To do the same with numpy, you should just do this (instead of both f.read()and the whole loop):
data = np.fromfile(f, dtype='<i2')
This is over 100 times faster than the manual thing above in my test on 5 MB of data.
In the second case, you have interleaved left-right-left-right values. Again you can read them all (assuming you have enough memory) and then access only one half:
data = np.fromfile(f, dtype='<i2')
left = data[::2]
right = data[1::2]
This processes everything, even though you need just one half, but it is still much much faster.
EDIT: If the data not coming from a file, np.fromfile can be replaced with np.frombuffer. Then you have this:
channel_data = np.frombuffer(data, dtype='<i2')
if channels == 2:
channel_data = channel_data[1::2]
levels = np.abs(channel_data)

PyAudio and Audition CS6 recording different sample values

What shall be evaluated and achieved:
I try to record audio data with a minimum of influence by hard- and especially software. After using Adobe Audition for some time I stumbled across PyAudio and was driven by curiosity as well as the possibility to refresh my Python knowledge.
As the fact displayed in the headline above may have given away I compared the sample values of two wave files (indeed sections of them) and had to find out that both programmes produce different output.
As I am definitely at my wit`s end, I do hope to find someone who could help me.
What has been done so far:
An M-Audio “M-Track Two-Channel USB Interface” has been used to record Audio Data with Audition CS6 and PyAudio simultaneously as the following steps are executed in the given order…
Audition is prepared for recording by opening “Prefrences/ Audio Hardware” and selecting the audio interface, a sample rate of 48 kHz and a latency of 250 ms (this value has been examined thoughout the last years as to be the second lowest I can get without getting the warning for lost samples – if I understood the purpose correctly I just have to worry about loosing samples cause monitoring is not an issue).
A new file with one channel, a sample rate of 48 kHz and a bit depth of 24 bit is opened.
The Python code (displayed below) is started and leads to a countdown being used to change over to Audition and start the recording 10 s before Python starts its.)
Wait until Python prints the “end of programme” message.
Stop and save the data recorded by Audition.
Now data has to be examined:
Both files (one recorded by Audition and Python respectively) are opened in Audition (Multitrack session). As Audition was started and terminated manually the two files have completely different beginning and ending times. Then they are aligned visually so that small extracts (which visually – by waveform shape – contain the same data) can be cut out and saved.
A Python programme has been written opening, reading and displaying the sample values using the default wave module and matplotlib.pyplot respectively (graphs are shown below).
Differences in both waveforms and a big question mark are revealed…
Does anybody have an idea why Audition is showing different sample values and specifically where precisely the mistake (is there any?) hides??
some (interesting) observations
a) When calling the pyaudio.PyAudio().get_default_input_device_info() method the default sample rate is listed as 44,1 kHz even though the default M-Track sample rate is said to be 48 kHz by its specifications (indeed Audition recognizes the 48 kHz by resampling incoming data if another rate was selected). Any ideas why and how to change this?
b) Aligning both files using the beginning of the sequence covered by PyAudio and checking whether they are still “in phase” at the end reveals no – PyAudio is shorter and seems to have lost samples (even though no exception was raised and the “exception on overflow” argument is “True”)
c) Using the “frames_per_buffer” keyword in the stream open method I was unable to align both files, having no idea where Python got its data from.
d) Using the “.get_default_input_device_info()” method and trying different sample rates (22,05 k, 44,1 k, 48 k, 192 k) I always receive True as an output.
Official Specifications M-Track:
bit depth = 24 bit
sample rate = 48 kHz
input via XLR
output via USB
Specifications Computer and Software:
Windows 8.1
I5-3230M # 2,6 GHz
8 GB RAM
Python 3.4.2 with PyAudio 0.2.11 – 32 bit
Audition CS6 Version 5.0.2
Python Code
import pyaudio
import wave
import time
formate = pyaudio.paInt24
channels = 1
framerate = 48000
fileName = 'test ' + '.wav'
chunk = 6144
# output of stream.get_read_available() at different positions
p = pyaudio.PyAudio()
stream = p.open(format=formate,
channels=channels,
rate=framerate,
input=True)
#frames_per_buffer=chunk) # observation c
# COUNTDOWN
for n in range(0, 30):
print(n)
time.sleep(1)
# get data
sampleList = []
for i in range(0, 79):
data = stream.read(chunk, exception_on_overflow = True)
sampleList.append(data)
print('end -', time.strftime('%d.%m.%Y %H:%M:%S', time.gmtime(time.time())))
stream.stop_stream()
stream.close()
p.terminate()
# produce file
file = wave.open(fileName, 'w')
file.setnchannels(channels)
file.setframerate(framerate)
file.setsampwidth(p.get_sample_size(formate))
file.writeframes(b''.join(sampleList))
file.close()
Figure 1: first comparison Audition – PyAudio
image 1
Figure 2: second comparison Audition - Pyaudio
image 2

reading 14-bit data using i2c, python, and raspberry pi

I'm trying to read data from this barometric pressure sensor on a raspberry pi using python & i2c/smbus.
The sensor's data sheet (page 10) says it will output a digital value in the range 0-16383 (2**14). So far it seems like I have to read whole bytes, so I'm not sure how to get a 14 bit value. (I had a link to the data sheet, but SO says I need more reputation before I can add more links to posts.)
This sample uses Adafruit's I2C python library, which is basically a wrapper around SMBus.
import Adafruit_I2C
import time
# sensor returns a 14-bit reading
max_output = 2**14
# per data sheet, max_output == 1.6 bar
max_bar = 1.6
# i2c address specified in data sheet
sensor = Adafruit_I2C.Adafruit_I2C(0x78)
while True:
reading = sensor.readU16(0, little_endian=False)
# reading is sometimes, but not always, greater than 2**14
# this adjustment feels pretty hacky/wrong
while reading > max_output:
reading = reading >> 1
bar = reading / float(max_output) * max_bar
print bar
time.sleep(1)
I compare these readings to the output from my handheld GPS, which includes a barometer. I sometimes get readings which are somewhat close (1030 millibar when the GPS reads 1001 millibar), but the sensor then dips drastically (down to 930 millibar) for a few readings. I have a suspicion that this is due to how I'm reading the data, but no real evidence to back that up.
At this point, I'm not sure what to try next.
Some things I've guessed at, but would appreciate some more-informed help with:
How can I read just the 14 bits that the sensor is outputting?
What endian-ness are the returned values? Assuming big-endian produced values which seemed more sane, but I may be conflating multiple problems here.
How can I tell which register to read from? This isn't mentioned in the data-sheet anywhere. I guessed that register 0 is probably the only one.
You should be masking the output of the sensor, not shifting it. e.g. reading = reading & (max_output-1) should probably do it.
The top two bits are the status bits, so if they are set sometimes they could mean things like: normal mode or stale data indicator.

Checking for parity errors with pyserial

I'm currently writing a small utility in python to monitor the communications on a serial line. This is being used to debug some hardware that is connected via rs232 so being able to see exactly what's going over the line is extremely important. How do I check for parity errors using pyserial?
Specifically I'm wondering if there is a platform independent way of finding the value of the parity bit using pyserial. I'd strongly prefer to not need termios to do this as this is used on some windows machines.
I monitored the parity bit by bit banging with the GPIO4 on my Pi.
Inspiration here
My solution is outputting the parity bit in a second byte and writing all into a file:
import time
import pigpio # http://abyz.me.uk/rpi/pigpio/python.html
RXD=4 # number of GPIO pin
pi = pigpio.pi()
if not pi.connected:
exit(0)
pigpio.exceptions = False # Ignore error if already set as bit bang read.
handle = pi.file_open("/home/pi/Documents/bit_bang_output.txt",pigpio.FILE_WRITE) #assuming that the file /opt/pigpio/access (yes without extension) contains a line /home/pi/Domcuments/* w
pi.bb_serial_read_open(RXD, 9600,9) # Set baud rate and number of data bits here. Reading 9 data bits will read the parity bit.
pigpio.exceptions = True
stop = time.time() + 5.0 # recording 5.0 seconds
while time.time() < stop:
(count, data) = pi.bb_serial_read(RXD)
if count:
#print(data.hex(),end="")
pi.file_write(handle, data.hex())
pi.bb_serial_read_close(RXD)
pi.stop()

Categories