sliceLa possibilità di alterare la velocità di riproduzione di un brano musicale lasciandone inalterata la sua intonazione è una delle possibilità offerte dai software di audio editing e dagli strumenti musicali più evoluti, che da sempre mi affascina e che suscita il mio interesse.

Rallentare una registrazione ad esempio, permette di facilitarne l’ascolto, di comprendere meglio i fraseggi strumentali o le frasi del discorso in essa contenuta oppure, come accade in alcuni strumenti musicali professionali o software per PC, permette di adattare un groove audio pre-registrato alle proprie esigenze e riprodurlo in sincrono con Pattern, sequenze Midi, etc..

Ma come è possibile rallentare o accelerare la riproduzione di un file Audio?

Chiunque abbia usato un registratore a “cassette” (oggi un pezzo da museo) o un lettore di dischi in vinile, sa che quando il disco gira ad una velocità diversa da quella prestabilita, la durata del brano audio aumenta o diminuisce.
Il problema che oltre a variare la velocità di riproduzione (il brano risulta più veloce o più lento), come effetto collaterale, otteniamo che viene alterata anche l’intonazione e che quindi l’intero brano diventi più acuto o più grave rispetto all’originale.
Nel mondo della musica digitale l’effetto di variazione velocità / tempo / pitch può essere facilmente ottenuto mediante interpolazione tra i campioni sonori e quindi elaborare il “ricampionamento” dell’audio ad una velocità differente.
Questa operazione è abbastanza semplice da fare (anche se potremmo discuterne per giorni e giorni sulle tecniche per non perdere qualità sonora durante il ricampionamento);  il vero problema inizia però quando vogliamo modificare la velocità senza cambiare l’intonazione, o modificare l’intonazione, senza cambiarne la velocità.
Queste operazioni sono normalmente conosciute con il nome di “Time stretching” e “Pitch Shifting“;

Dominio del tempo e della Frequenza
Come probabilmente alcuni di voi sapranno, esistono due approcci fondamentali per effettuare l’elaborazione di in un segnale Audio: operare nel dominio del tempo o operare nel dominio della frequenza.
I metodi nel dominio del tempo operano direttamente con i dati acquisiti.
Questo tipo di operazioni risultano piuttosto rapide e non troppo pesanti (da un punto di vista computazionale), in quanto i dati audio vengono manipolati nello stesso formato di acquisizione e di riproduzione.
Operare nel dominio della frequenza significa invece acquisire i dati nel dominio del tempo, convertirli nel dominio della frequenza, elaborarli, riconvertire il risultato nel dominio del tempo e riprodurli per ascoltarne il risultato.

Il vantaggio della trasformazione nel dominio della frequenza sta nel fatto che in questo dominio è possibile effettuare un più sofisticato editing che puo’ generare  un risultato più naturale all’ascolto di un orecchio umano.
In effetti all’interno del nostro orecchio, sono presenti un gran numero di sensori di frequenza, ciascuno dei quali sensibile (piu’ o meno)  ad una (od altre) in particolare.
Tutte le informazioni rilevate da questi sensori, vengono inviate al nostro cervello che le elabora e le ricompone creando la sensazione di frequenza continua a cui da sempre siamo abituati.
Per contro si ha che i metodi nel dominio della frequenza sono molto più complicati da programmare e calcolare rispetto ai metodi nel dominio del tempo, rendendoli così forse poco pratici in applicazione vincolate da risorse di calcolo limitate o da velocità della CPU non troppo spinta.

Un esempio di elaborazione in frequenza è stato fatto nel mio precedente articolo relativo al plug-in per gstremaer.

Quello che ci proponiamo nel presente articolo è di analizzare un metodo per variare il tempo di un frammento di audio operando nel dominio del tempo e non della frequenza.

Il taglia e cuci semplice ma funzionate.

La prima idea che può venire in mente guardando la forma d’onda di un brano audio è quello di “tagliarlo” in tante parti e allontanarle tra loro nel tempo.

timeCut

Bene questo è il vostro primo algoritmo di time streching, molto (si.. molto rudimentale) ma che comunque qualcosa farà.
La cosa appena fatta sembra una cosa stupida ma in effetti questa semplice idea è alla base di molti algoritmi di time streaching ed è la base di funzionamento del famoso software ReCycle della Propellerhead.
E’ un approccio molto funzionale per parti audio percussive con transienti molto  netti e ripidi ma non altrettanto valido per le parti armoniche e complesse.
Anche nei casi più adatti, il problema di questa tecnica stà nel fatto che più rallentiamo il brano, più i “pezzi dell’audio” si staccano tra loro producendo un effetto non troppo naturale e poco piacevole.
Quello che serve è trovare il modo per “allontanare” i frammenti audio mantenendo però una continuità nel flusso audio.

L’algoritmo S.O.L.A. (Synchronous Overlap And Add).
Il “SOLA” è uno degli algoritmi utilizzati e conosciuti utilizzati per il time stretching.

Si basa sulla sovrapposizione sincrona di frammenti di audio ed in effetti ottiene il suo risultato, suddividendo il materiale audio in una serie di pezzettini piuttosto corti  (generalmente di lunghezza compresa tra le  decine e le centinaia di millisecondi ciascuno ) per poi fonderli insieme al fine di ottenere un flusso audio continuo e dal tempo di durata più o meno lungo di quanto fosse in origine.

sola

Anche qui l’idea di base è semplice:
Supponiamo di voler rallentare una brano audio che dura 30 secondi della metà;
Il file che dovremmo ottenere dovrà durare nel tempo esattamente il doppio ,ovvero 60 secondi.
Possiamo pensare di dividere il file originale in tanti “frammenti” ognuno dei quali di una durata dell’ordine di qualche decina di milisecondi ed in fase di produzione del risultato ripetere ogni frammento due volte.
Per evitare discontinuità troppo evidenti nel suono tra le sequenze adiacenti viene parzialmente sovrapposto in modo che l’ampiezza del suono si sposti gradualmente da una sequenza all’altra con continuità.
Anche algoritmi più sofisticati come il TDHS , il WSOLA ed il PSOLA si basano sullo stesso concetto.

 

Dalla teoria alla pratica.
In ogni caso l’attuazione pratica di questo algoritmo non è poi così facile.
Scegliendo i frammenti audio senza sufficiente attenzione al loro contenuto, crea molto spesso un risultato non soddisfacente ed un audio con click e distorsioni evidenti.
In effetti l’algoritmo deve scegliere i frammenti audio in modo tale che le loro forme d’onda adiacenti si raccordino tra loro e che quindi siano il più simile possibile nel periodo di sovrapposizione.
In pratica, il flusso audio viene elaborato e individuato il primo frammento.Poi è necessario selezionare il successivo in modo che la sequenza dei due frammenti sia il più possibile continua ed uniforme.
Questa operazione viene effettuata tramite il calcolo della funzione di cross-correlazione tra la fine del precedente frammento audio e il nuovo frammento che ovviamente si dovrà trovare all’interno di una finestra temporale adiacente;
l’offset in cui le forme d’onda hanno alta affinità dà anche il più alto valore di cross-correlazione.
Le sequenze sono poi unite sovrapponendole per produrre un nuovo flusso audio continuo di durata diversa rispetto al suono originale.

Un po di codice.
Il codice seguente mostra l’implementazione dell’algoritmo appena descritto:

sola0

 

 

 

 

 

sola1

 

 

 

 

 

 

 

 

 

 

 

 

sola2

 

 

 

 

 

 

 

 

 

 

 

 

 

 

N.B.
Codice proveniente dagli esempi di : http://www.surina.net/soundtouch/

Ma come funziona?

La funzione Sola (SAMPLE *output, const SAMPLE *input, int num_in_samples)  accetta come parametri un puntatore ad un buffer che contiene i dati audio di ingresso (input), un puntatore al buffer dove verranno salvati i dati processati (output) ed il numero di campioni da processare.
Al suo interno vediamo che suddivide i dati in ingresso in blocchi e che copierà nel buffer di uscita.
Con la funzione seek_best_overlap

seq_offset = input + seek_best_overlap(prev_offset, input);

individua entro un range prestabilito, il miglior punto da cui iniziare la sovrapposizione dei dati.
Poi esegue successivamente l’overlap dei dati con la funzione:
overlap(output + FLAT_DURATION, prev_offset, seq_offset);

In base al define TIME_SCALE viene definita la quantità di sovrapposizione e quindi di quanto verrà “rallentato“ il file.

La funzione di cross correlazione funziona abbastanza bene per valutare la somiglianza delle forme d’onda ed ha il vantaggio che non è poi troppo complicata da implementare.
Richiede una serie di operazioni di somma e moltiplicazioni che possono essere implementare anche con interi a virgola fissa. Ovviamente l’uso di numeri a virgola mobile (float , double) è auspicabile e migliora ulteriormente la qualità dell’audio risultante anche se peggiora sensibilmente le performance computazionali dell’algoritmo.

Posso utilizzare misure di similarità differenti?

Ovviamente è possibile utilizzare altre “misure di similarità”, che possono aiutare a diminuire l’abbinamento  sequenze di bordi alle battute del suono,  riducendo il manufatto riverberante, ipotizzando che le battute ci siano e possano essere rilevate con una buona consistenza.
Un’altra alternativa è di valutare il dominio della frequenza somiglianza spettrale anziché la somiglianza della forma d’onda per trovare il miglior-matching sovrapposizione offset.

Audio multicanale
Per l’elaborazione di suono stereo, audio surround o altri audio multicanale, è bene notare che la sovrapposizione delle sequenze deve essere fatta esattamente sugli stessi offset per tutti i canali al fine di mantene la sincronia tra i vari stream.
Laddove potrebbe apparire ovvio utilizzare N. blocchi SOLA per elaborare gli N. canali come se fossero canali mono separati, questo approccio non è realizzabile in quanto ogni canale potrebbe finire con un offset diverso alterando di fatto la sincronia ed introducendo problemi di fase e distorsione armonica.

Un programma di prova.
Da questo Link, potete scaricare le sorgenti di un software di test che ho realizzato con QtCreator e le sue relative librerie grafiche.
Si tratta di un piccolo programma di test che carica un file Wav stereo, seleziona il primo canale, lo elabora e salva il file risultate contenente due tracce audio uguali (una per il canale destro l’altro per il sinistro) processate entrambe con l’algoritmo SOLA sopra descritto.
Una volta compilato ed avviato, il programma  si aspetta che selezionate un file Wave cliccando sul pulsante “Open File

Schermata del 2015-03-18 10:46:18

Selezionate la directory di destinazione cliccando sul pulsante “Select Folder“.

Schermata del 2015-03-18 10:46:48

Impostate il fattore di Stretching con il relativo slider ed avviate il processamento con il pulsante”Process File“.

Schermata del 2015-03-18 10:47:03

Se volete sapere come compilare un programma con QtCreator seguite i precedenti post sull’argomento.

Quando il gioco si fa duro i duri iniziano a giocare.
Non è ovviamente un mistero che quanto ci siamo detti sino a questo momento è solo un’introduzione all’argomento e che per realizzare un “qualcosa” di professionale e “performante” è ovviamente necessario mettere in campo molte altre conoscenze e trucchi del mestiere.

L’algoritmo, per la sua natura, introduce a volte delle ripetizioni (flam audio) o in altri casi smussa i transienti e gli attacchi dei suoni, inoltre se si vuole applicarlo in tempo reale ad un flusso audio  continuo è necessario tenere sotto controllo la latenza introdotta dal sistema.

Infatti credo che sia chiaro sino a quando si tratta di elaborare un file wave il processing dei dati è abbastanza semplice; i dati sono tutti disponibili ed è sempre possibile leggerne al bisogno e nella quantità necessaria.
Se al contrario si deve trattare un flusso audio in streaming, sarà necessario bufferizzare i dati ed elaborarli a pacchetti.
Il problema è che con un simile approccio di introduce “latenza” e questa “bestia nera” crea molti problemi a tal punto che potrebbe essere impossibile (o comunque difficile) sincronizzare Audio e Midi ad esempio, o magari potrebbe far diminuire le performance del dispositivo o del software al di sotto di un livello minimo accettabile.

In genere quindi sugli strumenti musicali reali o sui software che devono operare con stringenti vincoli di realtime, vengono utilizzati differenti algoritmi e differenti tecniche di processing che pur partendo da idee simili, operano sul flusso  dati in maniera differente.
La riduzione della latenza ha come effetto collaterale la riduzione della qualità dell’audio risultante ma d’altra parte, qualcosa è necessario sacrificare, l’importante è bilanciare le due grandezze in modo che il risultato ottenuto sia adatto allo scopo.

Di seguito potete ascoltare alcuni risultati attenuti con un mio algoritmo a latenza ZERO  applicato in tempo reale ad un flusso audio ritmico.
I cambi di tempo sono veramente estremi, (in alcuni casi anche +/- quaranta punti di BPM) e sono stati effettuati in “corsa” in tempo reale.
Probabilmente si può ancora fare di meglio ma per il momento mi accontento…

Test Audio Realtime Time stretching

 

 

 

 

 

Lascia un commento

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione / Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione / Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione / Modifica )

Google+ photo

Stai commentando usando il tuo account Google+. Chiudi sessione / Modifica )

Connessione a %s...