\articolo{AudioLab16}{Maurizio Ciccione}

\paragrafo{Introduzione}
\noindent Questo articolo è un'introduzione ad \AL, un sistema di
elaborazione audio per \A. Si è preferito, ovunque possibile, evitare
una trattazione matematica degli argomenti, in favore di una trattazione
``informatica''. Gli esempi utilizzano una notazione in pseudo-linguaggio C,
la convenzione dovrebbe essere intuitiva per chiunque conosca questo
linguaggio.

Una certa cautela dovrà essere usata solo nell'interpretazione del
significato dei puntatori. Essendo comune, durante l'elaborazione di segnali
numerici, accedere a valori relativi a istanti di tempo precedenti a quello
corrente, la notazione:

\begin{codestyle}
*pOut = pIn[-N];
\end{codestyle}

\noindent sottintende che \code{pIn} venga gestito in modo da evitare accessi
a memoria non allocata.

\paragrafo{Struttura di un segnale campionato}
\noindent Un segnale audio digitale è una rappresentazione discretizzata,
sia nel tempo che nelle ampiezze, di un segnale analogico. La
discretizzazione nel tempo è realizzata da apposito hardware
``congelando'' l'ampiezza in successivi istanti di tempo (campionamento). La
frequenza alla quale questa operazione viene svolta dipende dall'estensione
in banda del segnale analogico in ingresso.

Frequenze tipiche per segnali acustici sono:
\begin{itemize}
\item{96\,KHz, 48\,KHz: registratori digitali a nastro DAT. È possibile
interfacciare \A con un DAT tramite la scheda Maestro.}
\item{44,1\,KHz: compact disk e DAT. È possibile trasferire il
su \A materiale presente su CD tramite alcuni lettori CDROM.}
\item{32\,KHz: registratori digitali a nastro DAT in modo long play.}
\item{24\,KHz, 22\,KHz: usi video e multimediali.}
\end{itemize}
La discretizzazione nel tempo è realizzata da apposito hardware in grado di
misurare l'ampiezza, in un dato istante di tempo, e di arrotondarne il
valore da un multiplo di una qualche unità di riferimento (quantizzazione).
Il fatto che venga operato un arrotondamento implica che l'accuratezza con
la quale il segnale originale viene rappresentato sia in qualche modo
limitata. La scelta dei multipli utilizzabili influisce quindi sulla qualità
della rappresentazione.

Alcuni metodi di quantizzazione tipici:
\begin{itemize}
\item{16 bit lineare: DAT 48\,KHz, DAT 44,1\,KHz, CD, schede per computer, strumenti musicali;}
\item{12 bit logaritmica: DAT 32\,KHz;}
\item{12 bit lineare: strumenti musicali;}
\item{8 bit logaritmica: schede per computer, telefonia;}
\item{8 bit lineare: schede per computer.}
\end{itemize}

\paragrafo{DSP}
\noindent Si tratta di operare con un algoritmo su una sequenza di campioni
e ottenere un nuova sequenza ``elaborata''. Non consideriamo qui l'analisi
(estrazione di parametri da una sequenza) né la sintesi (generazione di una
sequenza a partire dai parametri). Semplici esempi di analisi in \AL sono la
ricerca del picco di un segnale (ingresso: sequenza; uscita: valore, ed
eventualmente posizione del picco) o il calcolo della risposta in frequenza
di un filtro FIR (ingresso: risposta all'impulso;
uscita: coefficienti ampiezza/fase a varie frequenze). Esempi di sintesi
sono i vari generatori di segnale (ingresso: forma d'onda, frequenza,
durata; uscita: sequenza).

I dati si suppongono risiedere in memoria, in forma di sequenze di parole
con segno (16 bit con segno). Campioni relativi a istanti di tempo
successivi occupano locazioni successive, con indirizzo crescente.

Definiamo \code{pIn} come il puntatore al campione corrente in ingresso e
\code{pOut} come il puntatore al campione corrente in uscita. Un primo
esempio di elaborazione, molto semplice (regolazione di livello) è

\begin{codestyle}
*pOut = A * (*pIn);
\end{codestyle}

\noindent Se \code{A==1} il livello è immutato, \code{A<1} causa
attenuazione, \code{A>1} incremento. Distorsione da overflow è possibile
nel caso vengano accettati valori di \code{A} maggiori di 1.

Per evitare il ricorso ai numeri in virgola mobile si può trasformare
l'istruzione precedente in:

\begin{codestyle}
*pOut = (A1 * (*pIn))/A2;
\end{codestyle}

\noindent \code{A2} è responsabile della granularità del controllo di livello. Più
\code{A2} è elevato, più il controllo è ``fluido''. \code{(A1==A2)} è la condizione
per ottenere guadagno unitario. Un'ulteriore ottimizzazione prevede la
rimozione della divisione, operazione sconveniente su 680x0.

Scegliendo

\begin{codestyle}
A2 == (1<<A3)
\end{codestyle}

\noindent si ottiene:

\begin{codestyle}
*pOut = A1 * ((*pIn)>>A3);
\end{codestyle}

\noindent Un altro semplice esempio: miscelazione di due sorgenti.

\begin{codestyle}
*pOut = A * (*pIn1) + B * (*pIn2);
\end{codestyle}

\noindent Si noti che l'assenza di distorsione da overflow è garantita solo
se \code{(A+B)<=1} (entrambi i segnali subiscono una riduzione della
gamma dinamica sfruttabile).


L'eventuale aggiunta di un controllo master \code{C}

\begin{codestyle}
*pOut = C * (A * (*pIn1) + B * (*pIn2));
\end{codestyle}

\noindent può essere ottimizzata in:

\begin{codestyle}
*pOut = CA * (*pIn1) + CB * (*pIn2);
\end{codestyle}


\paragrafo{Delay}

\noindent Oltre al moltiplicatore (level control) e al sommatore (mixer), il
terzo elemento fondamentale è l'unità di ritardo (delay unit).
\begin{figure}[ht]
\def\epsfsize#1#2{0.5#1}
\begin{center}\tolerance=10000\leavevmode\epsffile[0 0 402 400]{audiolab/delays.ps}\end{center}
\caption{\label{fig:delays}\small Delay}
\end{figure}
\AL utilizza un semplice offset tra puntatori per realizzare un
delay. Per un segnale campionato a \code{Fc} hertz il ritardo pari a
\code{D}
millisecondi corrisponde a un offset, in campioni pari a:

\begin{codestyle}
Delay = (1000*Fc)/D;
\end{codestyle}

\noindent La struttura più semplice è il single-tap delay
(figura~\ref{fig:delays}):

\begin{codestyle}
*pOut = *pIn + A * (*pIn[-Delay]);
\end{codestyle}

\noindent provoca un effetto simile a un eco se il valore di \code{D} è superiore a
50\,ms.

Per ottenere un maggior numero di ripetizioni esistono due alternative. Il
multi-tap delay prevede il collegamento in cascata di più delay unit
(vedi figura~\ref{fig:delays}). È possibile impostare diversi valori di ritardo e livello per
ogni ripetizione. Il peso computazionale cresce con il numero di unit.

\begin{codestyle}
*pOut = *pwIn + A1 * (*pIn[-Delay1])
                A2 * (*pIn[-Delay2]) +
                A3 * (*pIn[-Delay3]) +
                ...;
\end{codestyle}

\noindent Un recursive-delay è l'altra alternativa (figura~\ref{fig:delays}). Permette la
generzione di ripetizioni multiple effettuando una sola moltiplicazione ed
addizione. Diversamente dal multi-tap i valori di ritardo e livello delle
varie ripetizioni non sono tra loro indipendenti.

\begin{codestyle}
*pOut = *pIn + A * (*pOut[-Delay]);
\end{codestyle}


\paragrafo{Flanger}
\noindent Si noti
\begin{figure}[ht]
\def\epsfsize#1#2{0.333333#1}
\begin{center}\tolerance=10000\leavevmode\epsffile[0 0 640 512]{audiolab/flanger.ps}\end{center}
\caption{\small Flanger}
\end{figure}
che la risposta in frequenza di
una single-tap delay esibisce delle bande a intervalli regolari. Maggiori
valori di ritardo generano bande caratterizzate da minore estensione in
frequenza e una più fitta distribuzione (sull'asse delle frequenze) dei
punti di centro banda. Minori valori di ritardo generano bande con maggiore
estensione in frequenza con più rada distribuzione dei punti di centro banda.

Variando il ritardo con continuità è quindi possibile variare il
contenuto armonico del segnale.

\begin{codestyle}
*pOut = (*pIn) + A * (*pInput[f(-Delay)]);
\end{codestyle}

\noindent dove \code{f(x)} è una funzione che si occupa di modulare il valore di
\code{Delay} all'interno dell'intervallo $(\text{\code{Delay}} -
\text{\code{Delta}},\text{\code{Delay}} +
\text{\code{Delta}})$.

\code{Delay} definisce le caratteristiche di estensione e densità delle bande,
quando il sistema è a riposo. \code{Delta} determina il percorso che i punti di
notch seguono sull'asse delle frequenze.

\paragrafo{Filtraggio digitale}
\noindent Gli operatori lineari utilizzabili per filtraggio danno
luogo a due classi di filtri:
\begin{itemize}
\item{filtri a risposta all'impulso finita (FIR);}
\item{filtri a risposta all'impulso infinita (IIR).}
\end{itemize}

\sottoparagrafo{FIR}
I filtri FIR sono caratterizzati da una risposta all'impulso che si
esaurisce dopo un determinato numero di sample-point. Sono rappresentabili
in pseudo-C come:

\begin{codestyle}
x(i) =  c[0]  * x[i]   +
        c[1]  * x[i-1] +
        ...
        c[N-1]* x[i-N+1];
\end{codestyle}

\noindent dove \code{i} è il valore intero incrementato di uno a ogni istante di
clock, \code{y[i]} il valore dell'uscita all'istante \code{i} e
\code{x[i]} il valore dell'ingresso all'istante \code{i}.

Si riconosce immediatamente che si tratta di una media pesata dei più
recenti \code{N} valori presentati all'ingresso. Il vettore \code{c[]}, contenente i
pesi in questione (coefficienti del filtro o tap), definisce il filtro
stesso. È importante notare che la sequenza di valori costituenti il
vettore \code{c[]} è identica alla sequenza di valori della risposta
all'impulso del filtro.

Si immagini in ingresso un segnale così definito:

\begin{codestyle}
x[i] = 1 per i == 0
x[i] = 0 per i != 0.
\end{codestyle}

\noindent L'uscita del filtro risulta essere:

\begin{codestyle}
y[i] = c[i] per i <  N
y[i] = c[i] per i >= N.
\end{codestyle}

\noindent Le implicazioni pratiche sono notevoli. È possibile misurare
sperimentalmente la risposta all'impulso di un sistema fisico lineare e
tempo-invariante e, se questa è di lunghezza finita, realizzare un filtro
che emuli il comportamento del sistema stesso. Questo apre la possibilità di
misurare la risposta all'impulso di sale da concerto, teatri,\ldots  e di
ricrearne il ``sound'' applicandolo a registrazioni realizzate in camera
anecoica.

Altre applicazioni riguardano la ricostruzione di suoni tridimensionali. La
capacità umana di individuare una sorgente sonora nello spazio 3D dipende da
un insieme di fattori tra i quali l'azione di filtraggio che la testa e i
padiglioni auricolari operano sul suono, prima che esso raggiunga la parte
interna dell'orecchio.

Siccome questa azione di filtraggio non viene rilevata dai normali microfoni
(per questioni strutturali) sono stati realizzati rilevatori posizionati in
corrispondenza delle orecchie di teste di manichini (teste artificiali). Le
risposte a impulsi provenienti da determinate posizioni spaziali possono
essere acquisite e memorizzate.

Poiché ogni risposta individua un filtro corrispondente è possibile
costruire delle mappature tra posizioni dello spazio e vettori \code{c[]} da
applicare a registrazioni che vogliano essere posizionate in ambiente 3D.
Putroppo l'estrema variabilità delle caratteristiche dei sistemi biologici
(i potenziali ascoltatori, in questo caso) influisce sulla nitidezza
spaziale della sorgente; ciononostante, gli studi in questo campo stanno dando
risultati incoraggianti.

Un filtro FIR può anche essere realizzato in modo da riprodurre una risposta
in frequenza desiderata (dominio delle frequenze) invece di una risposta
all'impulso desiderata (dominio del tempo). Il metodo più generale consiste
nel campionare una risposta in frequenza tracciata (o rilevata
sperimentalmente) e trasformarla nel dominio del tempo tramite DFT. Altri
metodi, come il McClellan-Parks, generano i coefficienti a partire da
parametri forniti dall'utente (lunghezza,numero di bande, frequenze di
taglio,\ldots ).

I filtri FIR sono attraenti, per utilizzi audio, in quanto è semplice
ottenere risposte con fase lineare, caratteristica benefica per la fedeltà
del segnale. Affinché un FIR abbia risposta con fase lineare è sufficiente
che sia simmetrico, cioè che, considerando il vettore \code{c[]} valga:

\begin{codestyle}
c[0] == c[N-1]
c[1] == c[N-2]
c[2] == c[N-3]
...
\end{codestyle}

\noindent L'operatore DSP \g{FIR Filter} di \AL è in grado di leggere
file di coefficienti FIR e applicarli a registrazioni. Il formato dei file
contenenti i coefficienti è ASCII; questo permette semplici
sperimentazioni costruendo filtri tramite un text editor. Vediamo un esempio.

Lanciato il text-editor si digitino le seguenti linee:

\begin{codestyle}
; Esempio di passa basso
0.1
0.2
0.3
0.2
0.1
\end{codestyle}

\noindent La prima linea è un commento (\code{;} deve essere il primo
carattere). Ogni linea successiva contiene uno e un solo coefficiente. Ogni
coefficiente è rappresentato da un numero reale nell'intervallo
[-1,1]. Si tratta quindi di un FIR a 5 tap.

Si salvi il file (per esempio come \f{RAM:FiltroPB}) quindi, senza
chiudere l'editor, si localizzi l'operatore \g{FIR Filter} in \AL. Agendo
sul gadget \g{Coefficients File/?} si selezioni il file
\f{RAM:FiltroPB}. Se non vi sono stati errori il gadget \g{Taps} dovrebbe ora
mostrare il valore ``5''. Agendo sui gadget \g{Show Response/Impulse} e \g{Show
Response/Frequency} verranno mostrati i grafici di risposta all'impulso e
risposta in frequenza.

\AL monitorizza il file ASCII contenente i coefficienti del filtro per cui,
a questo punto, qualsiasi modifica apportata alle tap viene visualizzata
appena si effettua un'operazione di salvataggio dal text-editor.

\paragrafo{Noise gating}
Alcuni tipi di registrazioni audio (il discorso di uno speaker, le singole
tracce di una registrazione multitraccia) sono caratterizzati dal fatto che
le sezioni conteneti materiale significativo sono intervallate da periodi di
silenzio. Le parti teoricamente mute purtroppo spesso mettono in evidenza
rumore indesiderato causato dall'ambiente nel quale si è svolta la
registrazione e/o dalle caratteristiche del supporto impiegato (nastro
magnetico analogico, ad esempio). Risulta quindi utile la possibilità di
rimuovere le parti indesiderate, ininfluenti per una ricostruzione
significativa del segnale originario.

Il procedimento teorico consiste nel portare in fase di riproduzione il
livello di uscita rapidamente a zero durante le fasi di silenzio, per poi
ripristinarlo al valore originario appena il segnale rappresenta nuovamente
informazioni significative.

Il noise-gate è un operatore che effettua questa operazione
automaticamente. Si tratta di un amplificatore (gate) che può assumere
valori di guadagno pari a zero (il segnale viene completamente soppresso) o
uno (il segnale viene riprodotto senza modifiche).

L'amplificatore viene implementato da un semplice moltiplicatore, già
illustrato in precedenza. Occorre inoltre un segnale di controllo che
piloti, nel tempo, il comportamento del gate, in relazione la struttura
della registrazione.

Si osservi la figura \ref{fig:gate}. Essa
\begin{figure}[ht]
\def\epsfsize#1#2{0.4#1}
\begin{center}\tolerance=10000\leavevmode\epsffile[0 0 564 469]{audiolab/gate.ps}\end{center}
\caption{\label{fig:gate}\small Gate}
\end{figure}
Rappresenta un ipotetico segnale
contenente due zone (caratterizzate da livelli elevati), che si assumeranno
conteneti informazione significativa. Le rimanenti zone, teoricamente
silenziose, mostrano presenza di rumore di fondo. La retta orizzontale
costituisce una ragionevole scelta nel separare le parti contenenti solo
rumore (livelli inferiori alla retta) dalle parti conteneti anche
informazione.

La quota della retta (threshold) è una variabile del problema,
individuabile dall'utente per tentativi. Il valore ideale è quello per cui
la retta si trovi appena sopra il massimo picco raggiunto dal rumore.

Impostato il valore di threshold è semplice ricavare il segnale di
controllo per pilotare il gate. Si tratta di monitorare con continuità il
livello del segnale e forzare il gate a zero (chiusura) quando il livello
stesso scende sotto al valore di threshold, forzare il gate a uno (aperto)
quando il livello supera il valore di threshold. Come stimare il livello del
segnale?

Un'accettabile approssimazione è data dalla somma dei valori assoluti del
sample-point corrente e degli N--1 precedenti. N può essere dimensionato in
modo da operare su un intervallo di qualche decina di millisecondi.

\paragrafo{Reverb}
Come già esposto in precedenza, è possibile riprodurre un riverbero
costruendo un filtro FIR che rappresenti la risposta all'impulso di un dato
ambiente reale. Tuttavia questa tecnica è poco efficiente, in quanto richiede
decine di migliaia di moltiplicazioni e somme per tempi di riverbero
apprezzabili. Le complesse riflessioni che si generano in ambienti naturali
sono schematizzabili in maniera molto semplificata ma ugualmente
soddisfacente a livello percettivo.

\begin{figure}[ht]
\def\epsfsize#1#2{0.5#1}
\begin{center}\tolerance=10000\leavevmode\epsffile[0 0 482 320]{audiolab/reverb.ps}\end{center}
\caption{\small Reverb}
\end{figure}

La risposta all'impulso può essere scomposta in due parti: una piccola serie
di riflessioni primarie (early reflections) distinte seguite da un'amalgama
indistinta di ripetizioni (tail). Un'accettabile simulazione delle early
reflections
\begin{figure}[ht]
\def\epsfsize#1#2{0.5#1}
\begin{center}\tolerance=10000\leavevmode\epsffile[0 0 485 454]{audiolab/earlyreflections.ps}\end{center}
\caption{\small Early reflections}
\end{figure}
comprende da cinque a quindici
riflessioni nell'intervallo (10\,ms,100\,ms). Un delay
multitap con ritardo e guadagno differente per ogni tap rappresenta una
valida soluzione (early reflections processor). Al fine di incrementare la
densità delle riflessioni con limitato costo computazionale, \AL utilizza
un delay ricorsivo per ``ricaricare'' il multitap delay.

\AL dispone di un insieme di valori di ritardo e guadagno per l'early
reflections processor presettati per una stanza di tipo cubico. Il
controllo per modificare la dimensione della stanza corrisponde a un
cambiamento di scala per i valori di ritardo (i rapporti reciproci e i
guadagni sono inalterati). La ``colorazione'' che l'early reflections
processor causa sul segnale (si ricordi la risposta in frequenza della delay
unit) richiede un'accurata taratura dei parametri del multitap, parametri
che per'altro personalizzano il ``sound'' dell'algoritmo. Una soluzione
interessante sarà quella di fornire una biblioteca di set di valori,
acusticamente soddisfacenti, utilizzabili dall'utente.

La generazione della coda (tail) è un processo molto pesante, a livello
computazionale, in quanto è richiesta un'elevata  mole di ripetizioni. In
questo caso l'uso di delay ricorsivi (molto efficienti) è una scelta
obbligata.

\AL utilizza, nella versione attuale, una rete di sei delay unit in
parallelo. Per aumentare la densità delle ripetizioni l'ingresso è prelevato
dall'uscita dell'early reflection processor. L'entità del valore di feedback
delle delay unit è proporzionale a un parametro impostabile dal'utente.
Questo permette di controllare l'estensione, nel tempo, della coda di
riflessioni.

\paragrafo{Pitch shift}
Si tratta di accedere a una registrazione campionata con clock CI e
riprodurla con clock CO. Il cambiamento di intonazione dipende dal rapporto
CI/CO. È un'operazione compiuta, nei sistemi a nastro,
variando la velocità del motore.

Una conseguenza è che le variazioni di intonazione sono indissolubilmente
legate a variazioni nella dimensione temporale. Una registrazione abbassata
di un'ottava (CI/CO=0,5) comporta un
raddoppio della durata nel tempo. L'incremento di un'ottava è legato al
dimezzamento della durata. In molti casi è desiderabile poter variare il
pitch mantenendo invariata la scala dei tempi.

Il principio è quello di mantenere la sincronizzazione tra segnale in
ingresso e segnale in uscita rimuovendo o inserendo dei campioni
periodicamente. Questa operazione introduce delle discontinuità che causano
degenerazione (click) nella qualità dell'output. Per tentare di ridurre
questo effetto nei punti di discontinuità viene attuato un cross-fade. Ciò
significa che, considerando due segmenti A e B si attenua il livello di A sino
a zero e si aumenta da zero il livello di B.

La variazione di velocità CI/CO è implementata tramite sample rate converter.


\autore{Maurizio Ciccione\\
Via Neghelli 9/6\\
I-17021 Alassio SV}{Fidonet: 2:332/206.7}

\citazione{\selectlanguage{english}%
\emph{Benchmark:} An inaccurate measure of computer
   performance.  ``In the computer industry, there are three kinds of
   lies: lies, damn lies, and benchmarks.''  Well-known ones include
   Whetstone, Dhrystone, Rhealstone, the Gabriel LISP
   benchmarks, the SPECmark suite and LINPACK.
}{}
