I segnali audio vengono detto tempo-varianti, ovvero non sono Stazionarietà. Si possono però considerare Quasi-stazionarietà Locale

Stazionarietà

Un Sistema tempo-invariante o sistema stazionario, talvolta abbreviato in TIV (dall’acronimo inglese Time-Invariant System), è un sistema dinamico in cui l’uscita non dipende esplicitamente dal tempo. I sistemi tempo-invarianti sono descritti matematicamente da equazioni autonome e dalla funzione di trasferimento, e sono caratterizzati dal fatto che se un ingresso produce l’uscita allora per ogni ingresso traslato si ha un’uscita traslata dello stesso fattore .

Tra i sistemi stazionari maggiormente studiati ci sono quelli lineari.

Wikipedia

Un’altra definizione di stazionarietà è quella statistica. Se si considerano i momenti statistici di ordine n, la media (primo momento) non varia nel tempo, ma la varianza (secondo momento) si.

Quasi-stazionarietà Locale

Questo tipo di sistema può essere approssimato a stazionario per campioni molto brevi. Pochi segnali nel mondo reale sono stazionari, ma quasi tutti possono essere approssimati a quasi-stazionari.

Analisi “a Blocchi / finestre”

Per avere quasi-stazionarietà si analizza il segnale “a blocchi”. La durata del blocco dipende dal contesto, bisogna controllare che tutti gli elementi del blocco appaiano “simili”. Se si analizza del parlato ad esempio si dovrebbe analizzare ogni fonema singolarmente.

Vedi anche: Framing

Link to original

Framing

Il Framing in ambito dell’elaborazione dei segnali reali prevede l’analisi di un segnale attraverso l’utilizzo di dei brevi (massimo 400ms) intervalli detti frame che possano più o meno approssimare le caratteristiche del segnale in quel determinato intervallo.

Si deve anche considerare la correlazione tra i dati presenti nel frame. L’analisi di più note diverse nello stesso frame, o di più simboli fonetici nello stesso frame porta ad un’approssimazione di diversi processi casuali che non funziona molto bene, quindi l’ideale è avere dati simili all’interno del frame.

Link to original

Descrittori Audio

Valore efficace

Nella teoria dei segnali, il Valore efficace (spesso siglato con eff o con rms - dall’inglese root mean square) di una funzione periodica (e non solo), è il valore che avrebbe un “segnale costante” di pari potenza media:

Ovvero, in regime di corrente elettrica alternata, il valore rms è equivalente al valore della corrente continua costante, in grado di produrre la stessa dissipazione di potenza, in un carico resistivo. In elettrotecnica, il valore efficace viene introdotto nel regime alternato e trifase, per un confronto con la corrente continua, semplificando l’espressione della potenza media ed evitando l’analisi istante per istante del suo andamento. Così, ad esempio, la tensione di rete del fornitore di energia elettrica civile (ma anche in ambito industriale) è dato come valore efficace pari a 240 V (anche Vca o VAC) = 240 volt rms (o Vrms). È quindi spesso più utile per capire l’andamento delle intensità e delle tensioni della corrente elettrica, in un circuito in regime alternato (sia di forma periodica che di forma arbitraria).

Wikipedia

Link to original

Zero Crossings Rate

Lo Zero Crossings Rate (ZCR), o tasso di attraversamento dello zero, rappresenta il numero di volte in cui un segnale attraversa l’asse dello zero in un determinato intervallo di tempo o in un frame di segnale.

Il ZCR è particolarmente utile per distinguere suoni con caratteristiche spettrali diverse:

  1. Differenziazione tra voce e musica: I suoni a più alta frequenza, come il rumore e molti strumenti a percussione, hanno un elevato ZCR, mentre le vocali e altri suoni a bassa frequenza tendono ad avere un basso ZCR.
  2. Segmentazione e classificazione dei suoni: Il ZCR è utile per rilevare e classificare differenti tipi di suoni. Ad esempio, nella classificazione delle fricative nella voce (come “s” e “sh”), che hanno un tasso di attraversamento elevato, rispetto a vocali come “a” o “o”.
  3. Riconoscimento del parlato: In combinazione con altre caratteristiche (come MFCC e energia), il ZCR è usato per segmentare e riconoscere il parlato.
def zcr(x):
	return len(np.nonzero(np.diff(np.sign(x)))[0])/len(x)
Link to original

Trasformata di Fourier

In analisi matematica, la Trasformata di Fourier è una trasformata integrale, cioè un operatore che trasforma una funzione in un’altra funzione mediante un’integrazione. Trova numerose applicazioni nella fisica e nell’ingegneria ed è uno degli strumenti matematici maggiormente utilizzati nell’ambito delle scienze pure e applicate, permettendo di scrivere una funzione dipendente dal tempo come combinazione lineare (eventualmente continua) di funzioni di base esponenziali.

La trasformata di Fourier associa a una funzione i valori dei coefficienti di questi sviluppi lineari, dandone in questo modo una rappresentazione nel dominio delle frequenze che viene spesso chiamata spettro della funzione (la relazione con il concetto di spettro di un operatore può essere compresa se si considera l’operatore di convoluzione con la funzione in esame). A volte si intende per trasformata di Fourier la funzione che risulta dall’applicazione di questo operatore.

Wikipedia

In termini semplici, la trasformata di Fourier permette di passare da una rappresentazione nel dominio del tempo (o spazio) a una rappresentazione nel dominio delle frequenze. Se abbiamo una funzione che descrive un segnale nel tempo, la sua trasformata di Fourier ci darà una descrizione di quel segnale in termini delle sue frequenze componenti.

Si può sintetizzare il suono analogico tramite la trasformata inversa, ovvero la sintesi di Fourier.

L’analisi di Fourier si può presentare in diverse modalità in base al tipo di segnale da analizzare.

Tipi di segnale

Un segnale può essere continuo o discreto nel tempo.
Quando un segnale è continuo nel tempo, la trasformata di Fourier sarà discreta in frequenza, e viceversa.
Quindi tramite le operazioni di Campionamento e Quantizzazione si ottiene una trasformata di Fourier continua nel tempo.

Serie di Fourier

Nel caso di segnali periodici continui si realizza la conversione tempo / frequenza tramite la Serie di Fourier, che descrive un segnale una somma pesata di sinusoidi armoniche, multiple della frequenza principale di un multiplo k intero. Se il coefficiente k non fosse intero il segnale non potrebbe essere periodico.
Il segnale in frequenza è la densità spettrale di potenza, non energia, in quanto l’energia dei segnali periodici è infinita.

Link to original

Discrete Time Fourier Transform

In matematica, la trasformata di Fourier a tempo discreto (DTFT) è una forma di analisi di Fourier applicabile a una sequenza di valori discreti.
La DTFT viene spesso utilizzata per analizzare campioni di una funzione continua. Il termine tempo discreto si riferisce al fatto che la trasformata opera su segnali aperiodici a tempo discreto.

dove indica multipli del periodo di campionamento e la frequenza dello spettro è continua.

Wikipedia

Link to original

Discrete Fourier Transform

L’operazione di campionamento di un segnale periodico a tempo discreto genera un segnale con spettro periodico ripetuto ad intervalli Fs, con Fs Sampling frequency.
Bisogna quindi ritagliare la porzione di spettro fino a mediante l’uso di un filtro Filtro passa banda per rispettare il Teorema di Nyquist ed evitare che si crei Aliasing.

Lo spettro di un segnale campionato è dato dalla Convoluzione dello spettro del segnale con quello di un Treno di impulsi.

Risulta quindi un segnale scalato e ripetuto ogni frequenza .

Più generalmente è possibile trovare la DFT campionando il risultato in frequenza della DTFT lungo un solo periodo in N punti equidistanti (generalmente 1024 o 2048). La precisione della DFT cresce al crescere di N.
La DFT è molto sensibile al fenomeno di Spectral Leakage.

Link to original

DFT e DTFT

Notare che la DFT e la DTFT non sono la stessa cosa.
La DTFT è continua nel dominio della frequenza, il passaggio che collega le due è il campionamento dell’output della DTFT per generare la DFT e potervi lavorare con un computer che lavora a numeri finiti.

Segnali a tempo continuoSegnali a tempo-discreto
Segnali periodiciSerie di FourierDiscrete-time Fourier Transform
Segnali aperiodiciContinuous Fourier TransformDiscrete Fourier Transform
Link to original

Spectral Leakage

Quando si utilizzano segnali aperiodici che vengono periodicizzati estraendone un pezzo ed estendendolo tramite periodicizzazione, questi non sono segnali periodici puri, e la transizione brusca tra un periodo e il successivo crea componenti in frequenza aggiuntive.

Questo effetto, molto presente nella DFT può essere ridotto mediante l’utilizzo di finestrature pesate e Zero Padding.

Link to original

Zero Padding

La tecnica dello zero padding permette di aumentare la densità di campioni fino ad arrivare ad un numero (o multiplo di) di riferimento, senza intaccare in maniera significativa il segnale originale.
Il padding non cambia lo spettro di energia del segnale, ma nei sistemi che richiedono campioni multipli di un numero N, permette di aggiungere “tracce vuote” prima e/o dopo il segnale originale per arrivare ad un numero adeguato di campioni.

Padding

In numpy si può usare il padding tramite la funzione np.pad.
Questa funzione permette di effettuare un padding destro, sinistro o uniforme, e di scegliere con che valore costante “imbottire” il segnale.

Nella frequenza si può notare che l’asse delle x (frequenze) viene scalato in quanto vi è un maggior numero di campioni, ma si può risolvere dividendo per dove 1 e 2 indicano il numero di campioni prima e dopo il padding.

Link to original

STFT

La Short Term Fourier Transform (STFT) è una trasformata di Fourier utilizzata per determinare la frequenza sinusoidale e il contenuto di fase delle sezioni locali di un segnale al variare del tempo. In pratica, la procedura per calcolare gli STFT consiste nel dividere un segnale di tempo più lungo in segmenti più corti di uguale lunghezza e quindi calcolare la trasformata di Fourier separatamente su ciascun segmento più corto. Questo rivela lo spettro di Fourier su ogni segmento più corto. Di solito si tracciano gli spettri che cambiano in funzione del tempo, noto come spettrogramma o grafico a cascata. I display a larghezza di banda completa che coprono l’intera gamma di un SDR utilizzano comunemente trasformate di Fourier veloci (FFT) con punti sui computer desktop.

Wikipedia

Vedi anche: Trasformata di Fourier

Link to original

Spettrogramma

Lo Spettrogramma, nell’ambito dell’analisi audio e della fonetica acustica, è la rappresentazione grafica - fornita da uno spettrografo - dell’intensità di un suono in funzione del tempo e della frequenza o, in altre parole, è la rappresentazione grafica della funzione reale i delle variabili reali t ed f: i(t,f).

Wikipedia

L’asse delle ascisse rappresenta il tempo, le ordinate rappresentano la frequenza. Si usa una codifica a colori o scala di grigi per rappresentare quali frequenze hanno intensità maggiore.

Scala delle ottave

Durata delle armoniche

Già dallo spettrogramma è possibile notare che generalmente le armoniche a frequenze più basse sono quelle che tendono a durare di più nel tempo, mentre quelle a frequenze più alte sono molto volatili.

Link to original

Principio di indeterminazione sonora

Questo principio enuncia che:

Più si aumenta la risoluzione in frequenza, più si riduce la risoluzione nel tempo, e viceversa.

Questo perché la risoluzione nel tempo è pari a , mentre quella nella frequenza è pari a .

Link to original

Esercizio DTMF

Generare e analizzare sequenze DTMF

import numpy as np
import librosa
import matplotlib.pyplot as plt
 
N = 1000 # Numero di campioni
Fs = 8000 # Frequenza di campionamento
 
nummap = {
    '1': (697, 1209),
    '2': (697, 1336),
    '3': (697, 1477),
    '4': (770, 1209),
    '5': (770, 1336),
    '6': (770, 1477),
    '7': (852, 1209),
    '8': (852, 1336),
    '9': (852, 1477),
    '0': (941, 1336),
    '*': (941, 1209),
    '#': (941, 1477),
}
 
frequencies = [697, 770, 852, 941, 1209, 1336, 1477]
 
## UTILS
 
# Funzione per trovare il valore più vicino a k in lst
def closest(lst, k):
    return lst[min(range(len(lst)), key = lambda i: abs(lst[i]-k))]
 
# Funzione per plottare la trasformata di Fourier
def plt_transform(freq, sp):
    plt.grid()
    plt.plot(freq, sp)
    plt.xlim([0, Fs/2])
    plt.show()
 
# Funzione per applicare la trasformata di Fourier in frequenze. Opzionalmente mostra il grafico della trasformata.
def transform(y, Fs=Fs, plot=False):
    sp = np.fft.fft(y) # Trasformata di Fourier
    sp = np.abs(sp) # Metti i valori dell'energia in valore assoluto
    freq = np.fft.fftfreq(len(sp), 1/Fs) # Trova le frequenze associate ai valori della trasformata
    if(plot): plt_transform(freq, sp) # Plotta la trasformata se richiesto
    return sp, freq
 
## CORE
 
# Funzione per generare una cifra DTMF
def num_gen(num, N=1000, Fs=Fs):
    f1, f2 = nummap[num] # Frequenze associate alla cifra
    return .5*librosa.tone(f1, sr=Fs, length=N) + .5*librosa.tone(f2, sr=Fs, length=N) # Genera il tono come somma di sinusoidi
 
# Funzione per trovare le frequenze associate a un tono DTMF
def find_freq(y, Fs=8000):
    sp, freq = transform(y, Fs) # Applica la trasformata di fourier
 
    z = zip(sp, freq)
    zs = sorted(z) # Ordina le frequenze per energia maggiore
 
    # Il codice successivo applica un filtro passa banda soltanto attorno alle frequenze stabilite in frequencies, con una tolleranza TOL.
    # Questo permette di ridurre il rumore esterno e facilitare i calcoli.
    TOL = Fs / len(sp) 
    l = [(sp, freq) for sp, freq in zs if freq > 0 and any(abs(freq - target_freq) <= TOL for target_freq in frequencies)]
    return tuple(sorted((closest(frequencies, l[-1][1]), closest(frequencies, l[-2][1])))) # Associo le frequenze trovate a quelle più vicine che si trovano in frequencies
 
# Funzione per associare il tono DTMF a una cifra
def num_find(f1, f2):
    for key, value in nummap.items(): 
        if value == (f1, f2): 
            return key
 
# Funzione per generare un numero DTMF
def gen_dtmf(str, F=2000, tone_length=1, silence_length=0.1):
    dtmf = np.array([]) # Inizializzo il segnale
    for c in str: # Per ogni cifra devo generare il tono e il silenzio
        dtmf = np.concatenate((dtmf, np.zeros(int(silence_length*Fs)), num_gen(c, F*tone_length), np.zeros(int(silence_length*Fs))), axis=0)
    return dtmf
 
# Funzione per analizzare un segnale DTMF
def analyze_dtmf(data=None, Fs=Fs, single_tone_length=0.3):
    N = len(data)
    ret = ""
    step = round(Fs*single_tone_length) # Lunghezza di un tono
    tones = [data[i:i+step] for i in range(0, len(data), step)] # Separa i toni
    for tone in tones:
        #transform(tone, Fs, plot=True) # Abilita questo per stampare la trasformata di ogni tono trovato
        try:
            freq = find_freq(tone, Fs) # Trova le frequenze associate al tono
        except IndexError:
            continue
        try:
            ret += num_find(freq[0], freq[1]) # Trova la cifra associata alle frequenze
        except TypeError:
            continue
    return ret
 
## SCRIPT
 
# Leggere un numero da un file audio sulla memoria
w, Fs = librosa.load('dtmf-example.wav', sr=None) # Carico il file audio
freq = find_freq(w, Fs) # Trovo le frequenze associate al file audio
 
# Generare tutti i toni e controllare che le frequenze combacino
for key in nummap.keys(): # Per ogni cifra
    w = num_gen(key, N=1000) # Genera il tono della cifra
    freq = find_freq(w, Fs) # Trova le frequenze associate al tono
    print(num_find(freq[0], freq[1]), end=", ") # Stampa la cifra associata alle frequenze
print()
 
input = '3348193454' # Numero da generare
num = gen_dtmf(input, tone_length=.5, silence_length=0.1) # Genera il tono del numero
ipd.Audio(data=num, rate=Fs) # Riproduci il suono su web
output = analyze_dtmf(num, Fs, librosa.get_duration(y=num, sr=Fs)/len(input)) # Analizza il numero generato
print(output)