Sintesi Wavetable con PIC32MX220F032B
Markus Gritsch di Dangerous Prototypes è una di quelle persone che seguo molto volentieri. Lo considero un vero genio, insieme a molti altri e a dire il vero seguo i suoi lavori da quando ha cominciato ad armeggiare con il SID del Commodore64 interfacciandolo ai PIC32… che dire: mai operazione del genere è stata per me più significativa dal momento che mette insieme due passioni, quella per i pimicro e quella per il Commodore64, senza tener conto di quella per la chip-music.
Vi parlo di lui perchè proprio qualche giorno fa ho provato un suo esperimento, dalla semplicità davvero disarmante nonostante il background sia davvero molto complesso e articolato. L’esperimento è in realtà il porting di un firmware scritto in precedenza da un certo ChaN, che prevede la sintesi wavetable su un ATtiny45.
Il concetto di base sta nel creare nella rom del microcontrollore una wavetable, ovvero una tabella, un array, contenente le forme d’onda degli strumenti musicali (più o meno la stessa cosa che accade con i files MIDI sul pc). Questo sistema è stato il precursore alla base dei sintetizzatori digitali. Le musiche in formato MOD sull’AMIGA sono state forse le prime ad utilizzare questo sistema di sintesi sonora. In questo firmware un file MIDI viene successivamente convertito in un array che contiene la durata e il pitch (altezza) delle note che compongono la musica. La conversione del file MIDI viene eseguita con uno script in Python.
Niente paura perchè cerco di spiegarvi come procedere dal momento che anch’io mi sono scontrato con qualcosa di nuovo in questa prova. Tutta la teoria che c’è dietro la potete trovare nella pagina originale del progetto sul sito di ChaN.
Markus Gritsch ha realizzato il porting di questo firmware sul PIC32MX220F032B e la descrizione del suo progetto la si può trovare qui. Avendo a disposizione una MCU capace di girare a 80MHz, Markus dice che è possibile suonare fino a 64 note contemporaneamente. Lo schema da seguire è molto semplice.
L’uscita audio viene prelevata da un pin configurato con il PPS per fungere da uscita PWM: non è necessario nessun altro componente esterno, basta semplicemente collegare il pin ad un amplificatore.
Se avete ORbit16™ vi basta soltanto cambiare il PIC24FJ64GB002 con il PIC32MX220F032B senza fare nessuna altra operazione: vi basterà unicamente caricare il firmware con il pickit3 e collegare l’uscita BP15 (RB15 / RP15) ad un amplificatore, avendo cura di collegare anche GND. Per usare un PIC32 sulla ORbit16 dovete anche rimuovere il jumper JP3.
Qui vi do soltanto alcune “dritte” per fare in modo che vi funzioni tutto al primo colpo dato che pure io all’inizio qualche piccola difficoltà l’ho incontrata.
Indice dei contenuti
Progetto MPLAB – Ottimizzazione
Nel download sul sito di Dangerous Prototypes è incluso il progetto realizzato con MPLAB X, se non usate MPLAB X e create il progetto con MPLAB IDE, dovete avere l’accortezza di impostare ad 1 il livello di ottimizzazione, altrimenti il suono risulterà più cupo e la velocità di esecuzione più lenta. Per impostare il livello di ottimizzazione, dopo aver creato il progetto per PIC32MX220F032B ed aver scelto MPLAB C32 C Compiler, bisogna andare nel menù Project -> Build Options -> Project:
Selezioniamo la scheda “MPLAB PIC32 C Compiler” e dal drop-down menù “Categories” scegliamo “Optimization”:
La finestra all’interno cambia, compare un grafico che illustra come varia il rapporto velocità di esecuzione/dimensioni del codice in base al livello di ottimizzazione, che di default è impostato su zero. Con il Mouse clicchiamo nel pallino all’interno del grafico identificato con il numero 1:
Viene aggiunta l’opzione “-O1” alla riga di comando, possiamo chiudere la finestra.
Word di configurazione
Usando ORbit16™ potete sfruttare la stessa word di configurazione inclusa nel progetto di Markus: qui viene utilizzato l’oscillatore interno e tutto viene impostato per lavorare ad 80MHz. Se volete utilizzare il quarzo esterno ad 8MHz incluso su ORbit16™, potete cancellare tutte le direttive #pragma ed includere la word di configurazione scaricabile dalla pagina dei downloads di ORbit16™. In entrambi i casi il codice funziona lo stesso.
Errore “for loop initial declarations are only allowed in C99 mode”
Di default il compilatore non permette la definizione di una variabile all’interno del ciclo for. Il codice di Markus difatti alla riga 85 è:
for ( uint8_t osc = 0; osc < OSCILLATOR_COUNT; ++osc ) { |
viene in pratica dichiarata la variabile osc come unsigned char (uint8_t) direttamente all’interno del ciclo. Basta che modificate in questo senso:
uint8_t osc; for ( osc = 0; osc < OSCILLATOR_COUNT; ++osc ) { |
La stessa cosa ora dovete fare a riga 124 che dovrà ora apparire così:
for ( osc = 0; osc < OSCILLATOR_COUNT; ++osc ) { |
Aggiungere nuove musiche
La cosa non è tanto semplice per vari fattori:
- non tutti i files MIDI danno buoni risultati (stiamo utilizzando la stessa wavetable per tutti i midi, anche se gli script inclusi permettono di ricrearsela, ma personalmente non ho provato). Markus consiglia di editare i midi per eliminare suoni troppo bassi
- molti MIDI hanno dimensioni troppo elevate e non possono essere contenuti nella ROM del PIC
- è necessario utilizzare lo script in python fornito a meno che non siete bravi e vi create voi un programma apposito
Ad ogni modo do qui delle informazioni perchè possiate cimentarvi da soli: impossibile non è, io ci sono riuscito anche se un po’ di tempo ce l’ho perso. Ma chi non risica non rosica, dopotutto settorezero non è nato primariamente per gli amanti della “pappa bella e pronta”. Per eseguire script in Python è necessario installare l’interprete (http://www.python.it/download/). Lo script da far partire si trova nella cartella “dds” del download originale di Markus e si chiama “midi2h.py”. Per eseguirlo basta digitare da riga di comando (supponendo di trovarci già nella cartella in cui è installato Python a meno che non l’abbiamo incluso nel PATH):
python c:\percorso\al\file\midi2h.py |
Nel file midi2h.py, apribile con un semplice editor di testo, a riga 4 troviamo:
4 | m.open( 'still_alive.mid' ) |
che, si capisce, serve a processare il file mid relativo. Lo script vi darà errore perchè l’interprete python cercherà il file nello stessa cartella di dove risiede lui. Basta che cambiate il percorso nel file. L’output sarà però a video, il che non è molto utile ai nostri scopi. Tra i commenti dell’articolo di Markus ce n’è uno mio in cui ho incluso lo script che ho modificato per fare in modo che i valori vengano restituiti in un file H (ho messo il link al commento nella sezione downloads). Se leggete il commento vedete che alcuni midi richiedono di essere adattati nella durata delle note, la riga finale che produce la stampa del tempo/pitch deve quindi essere adattata ogni volta, riguardo al tempo, dividendo per 2, 3 o 4 (o non dividendo proprio):
int( time )/4 |
questo accade perchè stiamo utilizzando la stessa wavetable per tutti i midi, altrimenti bisognerebbe convertire ogni volta anche la wavetable.
I “numeri” che lo script tira fuori vanno sostituiti nel file tune_still_alive.h, all’interno della struttura event_t. Per quello che dicevo prima riguardo alla memoria del PIC32 che stiamo usando, non potete inserire un numero di note superiore a circa 2200 (? non ricordo di preciso, ho fatto delle prove ma non ho segnato il valore! Pardon!) altrimenti il compilatore restituisce un errore di memoria esaurita. Fate inoltre caso al fatto che la struttura event_t è dichiarata come una coppia di valori uint16, uint8 (coppia durata/pitch) per cui se uno dei valori di durata supera 65535, il compilatore ovviamente restituisce errore. Utilizzando la divisione nello script, comunque, questa evenienza non dovrebbe verificarsi anche perchè ora vi ho avvisato.
Allego a fine articolo 3 musiche che ho già convertito. Per utilizzarle basta che cambiate l’include a riga43 mettendo ad esempio:
#include "tune_rondo.h" |
e cambiare il nome della struttura nel file H della musica da “tune” a “tune_still_alive”. Lo so, il tutto è forse un tantino laborioso ma tenete conto di due cose:
- Ne vale la pena, c’è tanto da imparare
- I problemi li ho già affrontati io e vi ho esposto le soluzioni
In questo video c’è la prova che ho fatto con “tune_rondo.h”, potete rendervi conto che la musica in uscita non è la solita “ciofeca” che si fa con i buzzer o con le suonerie RTTL:
Download
- Pagina del progetto di Markus Gritsch
- mio commento con lo script Python modificato per stampare su file
- Wavetable Tunes (1127 download)