Ho già parlato del Raspberry Pi nell’articolo “Raspberry Pi: Introduzione e primi passi”, puoi trovarlo qui.
Prima di tutto: cosa è un analizzatore di spettro?
Un analizzatore di spettro è uno strumento che permette di eseguire l’analisi dello spettro audio dei segnali posti al suo ingresso.
Lo spettro sonoro è un grafico che si utilizza nell’analisi di un suono, dove vi sono riportati i livelli sonori in funzione della frequenza.
Questo grafico riporta sull’asse delle ascisse (x) la frequenza in Hertz, mentre sulle ordinate (y) l’ampiezza espressa in decibel.
I picchi sul grafico mostrano quali sono le frequenze che più contribuiscono al segnale totale.
Il punto più alto del grafico ci dà la frequenza fondamentale del suono, i picchi via via più bassi sono le armoniche.
Spiegato molto molto semplicemente, il nostro programma dovrà acquisire dei dati audio, dalla musica in riproduzione nel nostro player, applicare ai dati audio una trasformata di Fourier veloce (FFT) ed estrarre i livelli di ampiezza media per gli intervalli di frequenza specificati.
I dati sulla frequenza cosi ottenuti, vengono formattati e visualizzati nella matice LED.
Adesso, vediamo nel dettaglio di cosa abbiamo bisogno per realizzare un analizzatore di spettro audio con il Raspberry Pi.
Cosa ci serve (Hardware)
- Ovviamente un Raspberry Pi;
- Una matrice LED 8×8 montata su modulo con controller MAX 7219;
- Cavetti jumper;
- Casse audio.
Cosa ci serve (Software)
- Python DEV, Python Imaging e Python SMBUS;
- Libreria audio ALSA di Python;
- Libreria MAX7219 di Python;
- VLC media player installato sul Raspberry Pi.
Schema e collegamenti
Per prima cosa colleghiamo la matrice ai GPIO del Raspberry Pi seguendo questo schema:
Fatti tutti i collegamenti passiamo alla scrittura del nostro programma.
Programma python
Possiamo scrivere il nostro programma direttamente sull’IDE di Python, o anche in un normale editor di testo.
Per prima cosa importiamo le librerie.
#!/usr/bin/env python # -*- coding: utf-8 -*- import alsaaudio as aa import wave from struct import unpack, calcsize import numpy as np import re import argparse from random import randint from luma.led_matrix.device import max7219 from luma.core.interface.serial import spi, noop from luma.core.render import canvas from luma.core.virtual import viewport from luma.core.legacy import text, show_message from luma.core.legacy.font import proportional, CP437_FONT, TINY_FONT, SINCLAIR_FONT, LCD_FONT
Creiamo e inizializziamo la matrice LED come device.
serial = spi(port=0, device=0, gpio=noop()) device = max7219(serial, cascaded=1, block_orientation=0, rotate=0) print("Created device")
Definiamo tre vettori.
matrix = [0,0,0,0,0,0,0,0] power = [] weighting = [2,8,8,16,16,32,32,64]
- Nel vettore matrix verrano inseriti i valori di ampiezza corrispondenti alle frequenze dell’audio campionato.
- In power verranno inseriti i valori della trasformata di Fourier.
- Mentre in weighting sono contenuti i valori di ponderazione.
Adesso passiamo alle impostazioni audio.
sample_rate = 44100 chunk = 640 print "##############################" print "# Waiting for a song to play #" print "##############################" input = aa.PCM(aa.PCM_CAPTURE, aa.PCM_NORMAL, 'hw:Loopback,1,0') output = aa.PCM(aa.PCM_PLAYBACK, aa.PCM_NORMAL, 'plughw:0,0') input.setchannels(2) input.setrate(sample_rate) input.setformat(aa.PCM_FORMAT_S16_LE) #2 byte per Canale input.setperiodsize(chunk) output.setchannels(2) output.setrate(sample_rate) output.setformat(aa.PCM_FORMAT_S16_LE) output.setperiodsize(chunk)
- sample_rate è la frequenza di campionamento.
- chunk è un multiplo di 8 e serve per determinare la lunghezza dei frame audio da acquisire, per volta.
In questo caso la lunghezza dei frame è data da chunk * 2 (numero di canali) * 4Byte (del formato PCM_FORMAT_S16_LE).
Il resto sono le impostazioni di ALSA.
Adesso creiamo le funzioni.
def piff(val): return int(2*chunk*val/sample_rate) def calculate_levels(data, chunk,sample_rate): global matrix data = unpack("%dH"%(len(data)/2),data) # len(data)=chunk*(2 byte [perché formato audio 16 bit] * n_canali) data = np.array(data, dtype='h') fourier = np.fft.rfft(data) fourier = np.delete(fourier,len(fourier)-1) power = np.abs(fourier) matrix[0] = int(np.mean(power[piff(0) :piff(156):1])) matrix[1] = int(np.mean(power[piff(156) :piff(313):1])) matrix[2] = int(np.mean(power[piff(313) :piff(625):1])) matrix[3] = int(np.mean(power[piff(625) :piff(1250):1])) matrix[4] = int(np.mean(power[piff(1250) :piff(2500):1])) matrix[5] = int(np.mean(power[piff(2500) :piff(5000):1])) matrix[6] = int(np.mean(power[piff(5000) :piff(10000):1])) matrix[7] = int(np.mean(power[piff(10000):piff(20000):1])) matrix = np.divide(np.multiply(matrix,weighting),1000000) matrix = matrix.clip(0,8) return matrix
- piff() ritorna un indice del vettore power corrispondente a una determinata frequenza (passata tramite val). calculate_level invece restituisce l’elenco delle ampiezze medie di frequenza da visualizzare.
- unpack converte i dati audio grezzi in un formato compatibile per creare un vettore NumPy.
In questo punto del codice viene applicata la trasformata di fourier ai dati audio e calcolata l’ampiezza media per ogni intervallo di frequenza (in Hz).
I valori ottenuti vengono ridimensionati per poterli visulizzare nella matrice LED.
Tutto questo è fatto direttamente da NumPy.
E infine:
while True: l,data = input.read() if data!='': try: matrix=calculate_levels(data, chunk,sample_rate) with canvas(device) as draw: for y in range (0,8): for x in range(0, matrix[y]): draw.point((x,y),fill="white") except(): print "Program Closed" break
Il file audio viene letto e per ogni frame calcolato il livello di ampiezza delle frequenze e visualizzato nella matrice LED.
Avvio dell’analizzatore di spettro
Fatti tutti i collegamenti e scritto il nostro programma passiamo subito all’esecuzione.
Per prima cosa aprimao il terminale e digitiamo sudo modprobe snd-aloop per creare una scheda audio virtuale.
quindi ancora da terminale digitiamo sudo python <NOMEPROGRAMMA>.py (importante: bisogna essere posizionati nella cartella dove abbiamo salvato il nostro programma).
Se non ci sono errori, apprira:
“##############################”
“# Waiting for a song to play #”
“##############################”
Adesso apriamo VLC, ma prima di avviare la riproduzione andiamo su Audio->Dispositivo Audio e scegliamo uno tra quelli Loopback.
Se tutto sarà andato a buon fine, avviata la riproduzione, nella matice LED vedremo comparire lo spettro audio della nostra canzone!
E questo è come realizzare un analizzatore di spettro audio con il Raspberry Pi.
Conclusioni
Questi sono alcuni link da cui abbiamo preso delle idee sulla realizzazione del codice:
- https://www.rototron.info/raspberry-pi-spectrum-analyzer/
- http://julip.co/2012/05/arduino-python-soundlight-spectrum/
Questi invece sono i link per scaricare le librerie necessarie, sicuramente Python DEV, Python Imaging e Python SMBUS saranno già installate, comunque sono facilissime da trovare su internet: