DryWetLab
Questo progetto mi ha permesso di unire tre delle mie più grandi passioni: la musica, e in particolare il rock e la chitarra elettrica, la programmazione, in particolare lo sviluppo di simulazioni in real-time, e la matematica, in particolare riguardo l'analisi e l'elaborazione dei segnali.
DryWetLab è un simulatore di pedaliera effetti per chitarra, con i classici accordatori, wha-wha, distorsori, riverberi, eccetera. Come al solito, essendo il mio scopo puramente didattico, è scritto da zero, senza usare alcuna libreria esterna.
Al momento posso applicare gli effetti a un file WAV o utilizzare le vecchie API di Windows (winmm.dll) per l'acquisizione e la riproduzione in tempo reale. Si può anche registrare il risultato, sempre in formato WAV. Certamente sarebbe interessante implementare anche altri sistemi, come l'utilizzo dei driver ASIO per una migliore resa e una minore latenza.
Il nome del progetto si riferisce al gergo che si utilizza solitamente in ambito musicale, dove "dry" sta per il suono "pulito", così come entra nell'effetto, mentre "wet" si riferisce invece al suono processato dall'effetto. Spesso nei pedali è presente un potenziomentro "mix" che come ultima operazione esegue la combinazione lineare tra i due suoni ($\text{output} = \left( 1 - \text{mix} \right) \text{dry} + \text{mix} \, \text{wet}$).
Sono riuscito a conciliare un'altra mia grande passione, la fantascienza, usando come skin per i "pedali" delle immagini generate da un text-to-image model (quindi da una rete neurale artificiale), chiedendo copertine di libri di fantascienza "vintage" dal titolo opportuno ("The Mouth" per il wha-wha, "Square Wave" per il fuzz, eccetera).
Tuner
Il primo pedale presente in ogni pedaliera che si rispetti è l'accordatore. È posizionato sempre per primo perché deve agire sul suono pulito per cercare di individuare la nota fondamentale (la frequenza più grave) del suono in ingresso. Un volta attivato solitamente muta il suono (per non disturbare tutti durante l'accordatura) o eventualmente dirotta il suono lungo una catena differente (solitamente senza effetti e magari udibile solo nei monitor del chitarrista) e inizia a visualizzare la nota più vicina, l'ottava e solitamente i centesimi di nota (quindi da -50 a +50) di differenza tra la nota più vicina e la nota che sta effettivamente rilevando nel segnale in ingresso.
L'algoritmo su cui si basa l'accordatore è il calcolo della correlazione. Due segnali della medesima durata campionati sono due sequenze di numeri che possono essere considerati due vettori $n$-dimensionali. Ne possiamo calcolare il prodotto scalare, cioè sommare i prodotti delle rispettive coordinate, ottenendo un numero che ci fornisce un'indicazione di quanto queste due sequenza siano simili, appunto, correlate. Nel caso di segnali oscillanti ad alta frequenza (suoni) è facile intuire che oltre ad una misura di quanto i due segnali siano simili la correlazione ci da un'indicazione del fatto che i segnali siano o meno in fase. Se i massimi dei due segnali sono temporalmente corrispondenti otterremo un grande contributo positivo nel risultato finale e allo stesso modo se lo sono anche i minimi si otterrà ancora un altro grande contributo positivo (perché meno per meno fa più).
Per prima cosa dobbiamo stabile la frequenza minima e massima che vogliamo poter rilevare, per dimensionare i nostri calcoli. Quella minima, in particolare, corrisponde al periodo massimo (il tempo in cui si compie un'oscillazione completa) e quindi stabilisce la lunghezza del buffer che dobbiamo usare per memorizzare il segnale in ingresso. Potremmo scegliere i $55$ Hz di quello che musicalmente viene considerato come il LA della prima ottava. Data la frequenza minima otteniamo il corrispondente periodo massimo dell'oscillazione, dividendo la frequenza di campionamento per tale frequenza (ad esempio $44100 / 55 \simeq 802$ campioni). Il nostro buffer dovrà essere lungo il doppio, perché il calcolo della correlazione incrociata (cross-correlation) consiste nel calcolare la correlazione tra la prima metà del buffer, cioè il segnale contenuto a partire dall'indice $0$, e il contenuto del buffer a partire dall'indice $k$ per ogni $0 < k < n$. L'ultima correlazione calcolata sarà quindi tra la prima metà del buffer e la seconda metà. La frequenza massima, corrispondente al periodo minimo, determina invece quanti dei primi calcoli possono essere scartati (certamente il primo, che sarà la correlazione del segnale con se stesso, e ad esempio fino a $44100 / 1760 = 25$ campioni, per scartare correlazioni ad altissima frequenza, probabilmente perlopiù rumore). Alla fine ci ritroveremo con $n$ correlazioni e non dovremo fare altro che trovare la maggiore, $k$, per ottenere la corrispondente frequenza ($44100 / k$).
Una volta individuata la frequenza fondamentale non resta che convertirla in nota secondo le convenzioni musicali. Il LA della quarta ottava (A4) corrisponde a $440$ Hz. Le ottave sono logaritmiche (se $440$ Hz è un LA, $220$ Hz è il LA un'ottava sotto e $880$ Hz è il LA un'ottava sopra) e ogni ottava è convenzionalmente divisa in 12 semitoni secondo il cosiddetto temperamento equabile, in qui il rapporto della frequenza dell'$n$-esimo semitono rispetto all'ottava è $2^\frac{n}{12}$ (che da correttamente $1$ per il semitono $0$, l'unisono, e $2$ per il semitono $12$, l'ottava superiore).
Filtri
Filtri passa basso, passa alto, passa banda e passa tutto.
output = coefficient * input + (1 - coefficient) * previousOutput
output = coefficient * (input + previousOutput - previousInput)
output = previousInput + coefficient * (previousOutput - input)
Calcolo della frequenza di taglio.
Wha-wha
Uno dei più caratteristici e riconoscibili effetti per chitarra elettrica è il wha-wha, reso celebre da Jimi Hendrix, ad esempio in Voodoo Child del 1968, ma usato già da chitarristi come Eric Clapton e Frank Zappa. Il nome onomatopeico si riferisce al suono che si ottiene, che in qualche modo imita la voce umana. L'effetto in realtà deriva dal particolare utilizzo della sordina da parte del trombettista Clyde McCoy. Il modello di riferimento, storicamente, ma ancora tutt'oggi, è il Vox, che inizialmente si chiamava proprio Vox Clyde McCoy.
Filtro passa banda a frequenza variabile. Envelope detector.
Fuzz
Il fuzz, come i mitologici Big Muff e Pro Co RAT, uno dei tipi di distorsioni più famose ed "estreme", è anche l'effetto più semplice da riprodurre in digitale. Si tratta semplicemente di amplificare abbondantemente il segnale e "clipparlo", cioè limitarlo in un intervallo, ad esempio +1 e -1. Il risultato è praticametne un'onda quadra. A meno di quello che succede in caso di segnale in ingresso molto basso, si ottiene praticamente lo stesso risultato con if (input > 0) output = 1; else output = -1;.
Dopo l'amplificazione e il clipping è quasi indispensabile un filtro passa basso, perché un onda quadra contiene una gran quantità di armoniche ad alta frequenza molto fastidiose, dovute alla spigolosità dell'onda quadra. Il risultato finale è una sorta di onda a pinna di squalo.
Overdrive
Tangente iperbolica e asimmetria.
Equalizer
Low frequency oscillator
I cosiddetti effetti di modulazione hanno in comune la presenza di un LFO (low frequency oscillator), cioè un generatore di oscillazioni a bassa frequenza. In ambito musicale per "bassa frequenza" si intende una frequenza al di sotto della soglia dell'udibile, quindi diciamo indicativamente tra gli 0 e i 10 Hz. Un LFO è comandato da un controllo di Depth, che regola l'ampiezza dell'oscillazione, e da un controllo Rate che ne regola la frequenza.
Sinusoide, onda triangolare, onda a dente di sega, onda quadra, random.
Tremolo
La più semplice delle modulazioni, oltre che uno dei primi effetti ad essere introdotto, spesso incluso negli amplificatori storici (come i mitici Fender), e il primo effetto a pedale per chitarra mai prodotto (il Model 601 Tremolo Control delle Rowe Industries del 1941). Rumble di Link Wray del 1958.
È molto istruttivo "forzare" l'LFO oltre l'intervallo in cui è utile nel tremolo, andando oltre i $10$ Hz. Dal suono originale si sentirà emergere un suono "nuovo" a bassa frequenza, corrispondente all'oscillazione dell'LFO.
Vibrato
La variazione di intonazione si ottiene con un delay (vedi dopo) che varia tra $1$ e $2$ millisecondi.
Chorus
Il chorus è un vibrato in cui si miscela il suono originale con quello ritardato, creando un effetto "coro", cioè si simula la presenza di due voci con una leggera differenza di intonazione. Il tempo di ritardo oscilla fino ad una decina di millisecondi. Emblematico e riconoscibilissimo in Come As You Are dei Nirvana del 1991.
Flanger
L'implementazione del flanger è quasi identica a quella del chorus, ma con in più un controllo feedback (viene nuovamente memorizzato nel buffer il suono ritardato) e con tempi di ritardo molto più brevi (tra $1$ e $7$ millisecondi). Nonstante ciò il principio di funzionamento è in realtà differente, perché nel chorus si simulano voci leggermente sfasate e leggermente "stonate", mentre nel flanger l'interferenza tra i due segnali genera il caratteristico effetto "jet". Resta il fatto che un flanger con feedback a zero e tempi di ritardo "lunghi" (rispetto a quelli classici del flanger) e a tutti gli effetti un chorus.
Phaser
Il phaser, pur essendo apparentemente molto simile al flanger, si ottiene in modo del tutto diverso. La cancellazione periodica delle frequenze non viene implementata con un delay, ma con dei filtri passa tutto, che modificano la fase senza necessità di un reale delay. MXR Phase 90, il primo pedale prodotto dalla MXR nel 1974, usato da chitarristi come Eddie Van Halen e David Gilmour.
Delay
Il delay simula un eco. I controlli sono i classici dry e wet in percentuale, che regolano la quantità di input che finirà nell'output tale e quale (solitamente il 100%, a meno di situazioni particolari) e il volume con cui viene registrato il suono da riprodurre ritardato. L'implementazione è un semplice buffer circolare la cui dimensione dipende dal tempo di ritardo scelto (time). Un ulteriore controllo, feedback, regola la percentuale del segnale ritardato che viene ulteriormente sommata nel buffer, per avere echi che si ripetono più volte (con feedback a 0% si ha un unico eco, al 50% l'eco si dimezza ad ogni ripezione, al 100% si ripete all'infinito). Ho implementato 3 tipi di delay: digital, in cui il segnale è registrato senza alcuna manipolazione, analog, in cui ho aggiunto un filo di saturazione e un filtro passa basso, e tape, in cui oltre agli effetti della modalità analog viene aggiunto un leggero rumore di fondo e una variazione del tempo di ritardo, per simulare l'imperfezione nella rotazione delle bobine dei vecchi delay a nastro.
Reverb
Un riverbero non è altro che la combinazione di più delay a basso volume e con tempi di ritardo brevi, usati per simulare l'acustica di una stanza.
I delay possono essere in parallelo, cioè agire tutti sul segnale in ingresso per poi sommare i risultati, o in serie, quindi agire uno dopo l'altro sul segnale. Ogni delay deve essere impostato con il "dry" a zero (il segnale pulito sarà sommato in uscita un'unica volta, alla fine) e con tempi di ritardo che siano numeri primi, per non generare fastidiose interferenze tra i vari echi. Al momento ho implementato un unico tipo di riverbero con 4 delay in parallelo, con tempi di ritardo 0.029, 0.037, 0.041 e 0.043, seguiti da 2 delay in serie con tempi di ritardo 0.002 e 0.005. Il controllo size regola la dimensione della stanza, quindi è un fattore moltiplicativo sui tempi di ritardo scelti. Il controllo decay invece regola la quantità di feedback e quindi quanto velocemente decade il riverbero. Il "wet" dei delay è impostato al 50%. Il controllo level regola la quantità di "wet" finale nell'output, mentre il "dry" è sempre restituito tale e quale.
Graph e Recorder
Implementati come fossero effetti a tutti gli... effetti :) alla fine della catena, dopo il riverbero, è presente un effetto Graph, che permette di visualizzare il segnale o la sua trasformata di Fourier, tramite l'algoritmo FFT (Fast Fourier Transform), e un effetto Recorder che permette, quando attivato, di registrare l'output finale della catena su un file WAV.