LINGUAGGI
Ovvero il suono tra le dita di Mr. Lambda Disporre nel dispositivo audio di Amiga per capire come questo straordinario audio device lavori, e quindi sfruttare la capacità manipolatoria del Forth per creare Ecco l'occasione per trasformare bn incontro in un avvenimento determinante. E noi non vogliamo perderla. Come avrete già capito inizieremo subito Forth ... issimo, ma siate sempre pronti al peggio. In ogni senso. Il Forth, infatti, è un linguaggio che offre, come pochi, ampie capacità di manipolazione e interattività, ma richiede pure, come pochi, spiccate doti di creatività. Qualsiasi cosa si voglia, bisogna essere innanzitutto capaci di progettarsela e realizzarsela da sè. Il programma che accompagna questo articolo è una valida dimostrazione di ciò. Insomma, la programmazione all'insegna dell'inventività e del bricolage. Ma credeteci, si può arrivare davvero molto lontano. E intanto incominceremo dal suono, Il metodo della riproduzione del suono utilizzato da Amiga è simile a quello utilizzato dai Compact Disc che molti di voi ormai possiederanno. Anche se l'audio di Amiga non può essere paragonato con la fedeltà dei CD, la qualità del suono che può produrre risulta coxunque impressionante. Ciò che limita le prestazioni di Amiga, se lo paragoniamo con il Compact Disc, è la frequenza di campionamento e il numero di bit a disposizione per campione. Infatti un disco digitale ha una risposta in frequenza che raggiunge i 20 kilohertz, mentre l'Amiga è limitato teoricamente a circa 14 kilohertz, e praticamente consente una riproduzione corretta fino a 7,5 kilohertz cioè alla metà della cifra teorica precedente. Inoltre, come si diceva, un Compact Disc utilizza più bit per rappresentare ciascun punto della forma d'onda campionata e quindi ha un più ampio spettro dinamico. Vediamo ora come l'Amiga produca effettivamente un suono. Innanzituto il suono che deve venire riprodotto deve essere definito come una serie di numeri che rappresentano il livello di voltaggio della forma d'onda campionata a determinati intervalli. I diversi voltaggi rappresentati da quei numeri devono quindi essere filtrati, amplificati, e inviati a uno o più altoparlanti per produrre finalmente il suono desiderato. In questa maniera è possibile creare qualsiasi suono concepibile e inconcepibile all'interno, naturalmente, dei limiti imposti dalla frequenza a cui la forma d'onda può venire campionata, che ne determinerà la risposta in frequenza, con il numero di bit disponibili per ciascun campione, che ne determinerà la quantità di rumore nel segnale e la dinamica, e, infine, con la memoria che abbiamo a disposizione. Naturalmente per una più dettagliata comprensione della produzione del suono in Amiga vi suggeriamo di consultare la sezione riguardante l'audio nell'HARDWARE REFERENCE MANUAL. Come tutto è iniziato ... Innanzitutto precisiamo che per la scrittura e il collaudo del programma che vi andiamo a presentare abbiamo utilizzato il Multi-Forth della Creative Solutions perché più facilmente reperibile e più completo. Le versioni più recenti di questo prodotto, tra le altre meraviglie, supportano anche un driver audio, rendendo quindi il tutto ancor più appetibile. Ma il Multi-Forth in nostro possesso, che è di gran lunga una versione più antica, per l'audio di Amiga non supporta alcunché. Ci siamo decisi a presentarvi questo programma innanzitutto perché rappresenta comunque un'ottimo esempio di applicazione del Forth alla soluzione di un problema fondamentale come quello del suono in Amiga e ne traccia in modo chiaro i passaggi e le possibilità permettendovi i più ampi sviluppi e miglioramenti, ma soprattutto perché non sarete certamente in molti a possedere le più recenti versioni del Multi- Forth e morirete, come noi, dalla voglia di poter controllare il suono di Amiga subito. E veniamo ai fatti. Dobbiamo incominciare con l'ammettere che realizzare il programma non è stato semplice e immediato come ci aspettavamo, ma eccovi il resoconto della nostra avventura. Come ormai sapete, il nostro Multi-Forth non supportava alcunché per l'audio e non c'era alcun esempio nel manuale che ci potesse assistere. Insomma, possiamo dichiarare che non una parola del vocabolario a disposizione ci permetteva di fare alcunché con il suono! Ma questo è Forth! E se qualcosa vi è necessario, dovete costruirvela, diciamo bene? Quasi subito fummo in grado di capire che avevamo bisogno di una funzione chiamata nei manuali BeginlO. I manuali indicavano anche che la funzione si aspettava come argomento l'indirizzo di una struttura IORequest. Ma nulla di tutto questo esisteva nel nostro Multi-Forth. E a nulla sono servite le ricerche dettagliate nel nostro manuale Multi-Forth: non c'era nulla, neanche qualcosa che potesse richiamare indirettamente questo concetto. La cosa che più ci ispirava era la funzione CallIO, ma questa non era chiaramente documentata, anzi non era documentata affatto, se non per una breve menzione nel glossario del manuale. Prima di incazzarci definitivamente con il nostro Multi-Forth, abbiamo considerato il fatto che non c'era alcuna documentazione sulla funzione Begin10 nemmeno negli AMIGA REFERENCE MANUAL. Il solo riferimento che avevamo potuto trovare era nella appendice 6 dello EXEC MANUAL. Esso consisteva semplicemente in questa macro in linguaggio assembler: _BeginIO: move.l 4(sp),a1
Ci siamo procurati una copia di Metascope - il debugger simbolico della Metadign - e un compilatore C, abbiamo compilato un programma demo che riguardava l'audio di Amiga, e quindi abbiamo incominciato a curiosare. Quasi immediatamente abbiamo individuato la famigarata funzione BeginlO e l'abbiamo disassemblata. Eccola: _BeginIO: movea.l 4(a7),a1
Una delle difficoltà che si possono incontrare nella traduzione di routine in Forth risiede nel differente modo in cui C e Forth passano i parametri. Perché tutto funzioni a dovere in Multi-Forth, la prima istruzione deve infatti essere: movea.l (a7)+,a1 che muove l'indirizzo della struttura IORequest dallo stack dati del Multi-Forth (A7 è lo stack pointer) nel registro Al. E ancora lo RTS deve essere sostituito da: move.w (a2)+,d3
che è la macro per il NEXT in Multi-Forth. Nel listato che accompagna questo articolo voi troverete un'altra definizione CODE per la funzione NewList. Naturalmente è possibile scrivere questafunzione anche direttamente in Forth, ma non ci siamo potuti trattenere dalla tentazione di continuare il lavoro iniziato con BeginlO! Per evitarvi la fatica di caricare l'assembler vi proponiamo una versione delle due word che inserisce i valori in codice macchina esadecimale direttamente nel vostro dizionario. Ma a questo punto, era anche necessario implementare la chiamata OpenDevice della libreria Exec (che, per fortuna, è documentata nella nostra versione di Multi-Forth) per poter fornire a BeginlO un puntatore al device node. L'implementazione delle altre chiamate Exec è stata più semplice, e non si sono incontrati grossi problemi. Un altro lavoro preparatorio riguardava la definizione delle strutture dati richieste dal device audio. Vediamone dapprima la versione in C e la versione in Assembler che ne danno i manuali e quindi la traduzione che ne abbiamo fatto in Forth. Vi facciamo presente che le recenti versioni del Multi-Forth includono tutte le strutture disponibili presentate dai AMIGA REFERENCE MANUAL, ma la versione in nostro in C: struct Message
E ora vediamone la versione in Assembler: STRUCTUREMN,LN_SIZE
E finalmente presentiamo la nostra versione
structure Message
E ora interessiamoci della struttura IORequest, che, come potrete vedere, presenta delle particolarità. Innanzitutto presentiamo le due strutture utilizzabili in C: struct IORequest
struct IOStdReq
E ora vediamo come queste strutture vengano tradotte in Assembler, considerando semplicemente la struttura IOquest un sottoinsieme della struttura IOStdReq: STRUCTURE IO,MN_SIZE
Per adeguare queste strutture al Forth si adotta il medesimo
concetto visto nella precedente traduzione Assembler anche se la sintassi
chiaramente ne differisce:
Consideriamo brevemente il costituirsi di questa struttura
e la soluzione programmativa che le permette di operare correttamente.
La word STRUCTURE crea una nuova word, fondamentalmente una costante, e
lascia l'indirizzo del campo parametri (parameter field) di quella word
e uno zero nello stack. Lo zero è un valore fittizio in cui viene
accumulata la dimensione della struttura. Quando STRUCTURE. END viene incontrata
la dimensione accumulata della struttura viene memorizzata all'indirizzo
del campo parametri della word. Come risultato di queste operazioni, quando
viene eseguito il nome della struttura, ne viene restituita la dimensione.
Dal momento che il nome della struttura ritorna la sua dimensione, l'inserzione
di ' IORequest + ' alla fine della struttura IORequest aggiunge I'offset
appropriato perché + ioActual venga eseguita correttamente.
Ecco la versione in sintassi Assembler:
STRUCTURE Audio,lO-SIZE
E quella in Multi-Forth:
structure IOAudio
Tutte le costanti che sono comandi del device e che trovate
nel listato sono spiegate dettagliatamente negli AMIGA REFERENCE MANUAL.
0" nome" priorità CreatePort Oppure privata, e cioè senza nome e
0 priorità CreatePort Nel caso dell'audio device noi abbiamo utilizzato una porta
privata e senza nome dal momento che nessun altro task nel sistema aveva
necessità di sapere della sua esistenza.
Ed è diventato suono Il listato che vi presentiamo vi permette di generare semplici forme d'onda all'interno di un limitato ambito di frequenze. Vi forniamo le tavole dei dati per I'onda sinusoidale (Sine), I'onda quadra (Square), I'onda triangolare (Triangle) e I'onda a dente di sega (Sawtooth) che possono produrre buoni risultati tra i 1200 e i 2400 Hz. Anche frequenze tra 600 e 1200 Hz possono essere riprodotte con buoni risultati anche se ciò non è strettamente in accordo con quanto enunciato nel manuale dello Hardware. Le frequenze al di fuori di questi limiti produranno suoni distintamente distorti. Ciascuna tavola di dati audio consiste di dodici campioni che descrivono un ciclo completo. Le frequenze più basse richiederebbero unatavola di dati più lunga, e le frequenze più alte meno campioni. Per creare un suono continuo basta che puntiate il DMA audio alla tavola dei dati del- I'onda e gli comunichiate quante volte desiderate ripeta i dati. Un ciclo è sufficiente per generare un suono continuo. Il DMA si riposiziona automaticamente all'inizio dell'array dei dati ogni voltache raggiunge la sua fine e ripete questa operazione fino a quando ha riletto I'array per il numero di volte che avete specificato. E adesso vi diamo alcune spiegazioni che riguardano la word Hz. Il manuale dello Hardware indica un metodo per determinare adeguatamente la frequenza di campionamento necessaria a produrre una data frequenza da un precostituito numero di campioni che noi chiaramente non abbiamo seguito. Questo metodo è basato sul periodo del timer audio e il periodo della frequenza desiderata, ma è molto più semplice utilizzare le frequenze per eseguire i calcoli necessari in modo da evitare reciproci o calcoli con virgola mobile. Ecco l'equazione: Frequenza_di_Campionamento = AudioClock / (Frequenza_Desiderata * Campioni) Dove Frequenza_di_campionamento è il numero di tick che il clock
dell'audio deve aspettare prima di prelevare un campione, la frequenza
di AudioClock è 3,579546 Mhz, e Campioni è il numero di campioni
che devono essere riprodotti in un periodo della forma d'onda. La word
Hz calcola il periodo richiesto per generare una determinata frequenza
da una tavola di dodici campioni e memorizza quel valore in una variabile
chiamata Period per servirsene successivamente. Ora parleremo delle altre
word descrivendo l'azione della word Wave. La prima cosa che Wave fa è
tentare di aprire unA port audio. La word AudioPort? ci restituisce l'indirizzo
della port allocata dalla word CreatePort, se, naturalmente questa ha successo,
e il flag O se questa non ha successo. Se il tentativo di creare una message
port non ha successo Wave termina con il messaggio "La port del device
audio non è stata aperta."
La priorità richiesta viene memorizzata nel campo +InPpri della
struttura IOAudio. A proposito di + InPpri permetteteci un commento. Probabilmente
a molti di voi sarà sembrato un errore o un refuso, dal momento
che si riferisce a LN_PRI della relativa struttura in Assembler oppure
a In_Pri della relativa struttura in C, e invece è proprio così.
Ma tant'è. E dal momento che cosi appare nella nostra versione di
Multi-Forth, noi siamo stati costretti ad accettarlo proprio così,
nudo e crudo, e sbagliato. Quindi per non perdere il controllo del vostro
sistema nervoso per un errore che vi sembra sciocco e impossibile, date
un'occhiata alla word relativa che è presente nella vostra versione.
Bene. E ora continuiamo. Si diceva della word Wave e delle sue successive
inizializzazioni. Dopo la priorità, l'indirizzo di ChannelMask viene
memorizzato nel campo +ioData, che richiederà per noi al sistema
tutti quattro i canali. La mask per la richiesta dei canali consiste in
un solo byte, ecosì + ioalength viene settato a uno.
1200 Hz Sine Wave 2000 Hz Square Wave e cosi via. Naturalmente, una volta che la frequenza sia stata specificata
utilizzando Hz, non è più necessario ripetere il comando
se intendiamo generare un altro tipo di forma d'onda della stessa frequenza.
In altre parole, una volta che il comando 2000 Hz viene eseguito, Triangle
Wave oppure Sawtooth Wave genereranno le forme d'onda appropriate alla
frequenza 2 kHz. E ancora, dopo che siano stati specificati la frequenza
e i dati della forma d'onda, Wave può venire utilizzato nuovamente
per rigenerare lo stesso tono.
|