Assembly Avanzato con MASM
Capitolo 14: Suono e musica. Programmazione della scheda audio
In questo capitolo analizzeremo innanzi tutto i concetti teorici che stanno alla base del
suono e della musica; successivamente, metteremo in pratica tali concetti attraverso la
programmazione delle schede audio.
La scheda audio di riferimento sarà la Creative Technology Sound Blaster 16
(SB16) che in passato è diventata un vero e proprio standard nel mondo dei
PC; svariate innovazioni tecnologiche introdotte dalle Sound Blaster si
ritrovano applicate anche nelle moderne schede audio. La scelta della SB16 è
legata anche al fatto che essa è pienamente supportata da VirtualBox e da diversi
altri emulatori di sistemi operativi.
14.1 Suono
Il suono è una perturbazione di un mezzo elastico causata da una variazione di pressione
(vibrazione); un mezzo è elastico se, dopo essere stato perturbato, tende a riportarsi
spontaneamente nella configurazione precedente.
Una vibrazione può essere generata, ad esempio, dalle nostre corde vocali, dalla corda
di una chitarra, da un'esplosione, etc; un altro esempio di vibrazione, che analizzeremo
in dettaglio, è quello illustrato in Figura 14.1.
Come possiamo notare, abbiamo una ruota che gira in senso antiorario a velocità angolare
costante (angoli uguali descritti in tempi uguali); una apposita asta trasforma tale
rotazione in un movimento continuo di avanti-indietro del pistone, perturbando così
l'aria posta all'interno di un cilindro. La pressione dell'aria non è altro che la forza
per unità di superficie esercitata dalle molecole (che compongono l'aria stessa) quando
urtano la parete interna del cilindro; in assenza di perturbazioni, la pressione
dell'aria è quella atmosferica.
Supponiamo che, all'istante di tempo iniziale t=0, l'estremità sinistra dell'asta
si trovi nel punto A (quindi, P=A); si vede chiaramente che quando il punto
P descrive l'arco A-B-C, il pistone viene spinto verso destra. Il movimento
del pistone riduce il volume disponibile immediatamente alla sua destra e ciò provoca un
aumento della pressione; infatti, minore volume significa per le molecole maggiore
probabilità di urti e quindi aumento di pressione. La pressione aumenta anche per il fatto
che le molecole che urtano il pistone in movimento verso destra, rimbalzano a loro volta
verso destra a velocità maggiore; ma velocità maggiore significa maggiore energia cinetica
trasferita nell'urto e quindi pressione più alta.
L'aumento di pressione che si è così verificato immediatamente alla destra del pistone,
prende il nome di compressione; le molecole che si trovano nella zona di
compressione vengono da questa spinte leggermente verso destra e urtano le altre molecole
trasferendo loro energia cinetica. La pressione diminuisce quindi vicino al pistone e
aumenta leggermente verso destra; si verifica così una reazione a catena, in conseguenza
della quale la zona di compressione si propaga verso destra.
Si può notare che la velocità del pistone è zero quando P=A e cresce sino a
raggiungere un massimo quando P=B; in seguito la velocità decresce sino a tornare
a zero quando P=C.
Proseguendo nella sua rotazione, il punto P percorre ora l'arco C-D-A; di
conseguenza, il pistone viene tirato verso sinistra. Il movimento del pistone aumenta
il volume disponibile immediatamente alla sua destra e ciò provoca una diminuzione della
pressione; infatti, maggiore volume significa per le molecole minore probabilità di urti
e quindi diminuzione di pressione. La pressione diminuisce anche per il fatto che le
molecole che urtano il pistone in movimento verso sinistra, rimbalzano a loro volta verso
destra a velocità minore; ma velocità minore significa minore energia cinetica trasferita
nell'urto e quindi pressione più bassa.
La diminuzione di pressione che si è così verificata immediatamente alla destra del
pistone, prende il nome di rarefazione; le molecole che si trovano alla destra
della zona di rarefazione vengono da questa risucchiate leggermente verso sinistra e
urtano le altre molecole trasferendo loro energia cinetica. La pressione aumenta quindi
vicino al pistone e diminuisce leggermente verso destra; si verifica così una reazione
a catena, in conseguenza della quale la zona di rarefazione si propaga verso destra.
Si può notare che la velocità del pistone è zero quando P=C e cresce sino a
raggiungere un massimo quando P=D (uguale in valore assoluto ma contrario come
segno al massimo in P=B); in seguito la velocità decresce sino a tornare a zero
quando P=A.
Ad ogni giro completo della ruota corrisponde la formazione di una nuova coppia
compressione-rarefazione; mentre quindi il pistone costringe le molecole dell'aria ad
una continua oscillazione avanti-indietro, le varie coppie compressione-rarefazione si
propagano verso destra. Bisogna ribadire quindi che a propagarsi verso destra sono le
variazioni di pressione, mentre le molecole oscillano leggermente avanti-indietro
attorno ad una posizione di equilibrio; nel cilindro si viene a creare così la
situazione illustrata in Figura 14.2.
Nella parte alta della Figura 14.2 viene mostrata la propagazione delle aree di
compressione e rarefazione, mentre in basso vediamo un grafico che rappresenta la
variazione della pressione al trascorrere del tempo. Si nota chiaramente che il fenomeno
è di tipo ondulatorio, con le oscillazioni delle molecole che avvengono parallelamente
alla direzione di propagazione; si parla allora di onda meccanica longitudinale.
Se si lancia un sasso in uno stagno in quiete, si nota la formazione dei caratteristici
cerchi concentrici, con le molecole d'acqua che in questo caso oscillano verticalmente
(e quindi in direzione perpendicolare a quella di propagazione); si parla allora di
onda meccanica trasversale. Il termine "meccanica" si riferisce al fatto che
questo tipo di onda per propagarsi ha bisogno di un mezzo solido, liquido o gassoso;
proprio per questo motivo, è impossibile che un suono si propaghi nel vuoto (a
differenza delle onde elettromagnetiche e di quelle gravitazionali, che sono in grado
di propagarsi anche nel vuoto). In ogni caso, qualunque sia il tipo di onda, la Figura
14.2 ci permette di definire una caratteristica generale fondamentale: un'onda
trasporta energia ma non materia.
Una funzione matematica si definisce periodica quando essa assume valori che si
ripetono identicamente ad intervalli regolari di tempo; ciascuno di tali intervalli,
indicato con T, prende il nome di periodo e si misura ovviamente in
secondi.
L'insieme di tutti i valori che la funzione assume in un periodo rappresenta un ciclo
completo o oscillazione completa; il numero f di cicli nell'unità di
tempo prende il nome di frequenza e si misura in secondi-1
(infatti, 1 ciclo sta a T secondi come f cicli stanno a 1
secondo, per cui f=1/T). Al posto di s-1 per f si usa
l'unità di misura hertz (simbolo Hz) in memoria del fisico tedesco
Heinrich Rudolf Hertz, scopritore delle onde radio.
Nel compiere un ciclo completo, in T secondi, un'onda percorre un tratto
λ che prende il nome di lunghezza d'onda e si misura in metri; se
l'onda si propaga a velocità v (metri percorsi ogni secondo), in T secondi
percorrerà quindi λ=v⋅T metri.
Il valore massimo che un'onda assume durante un ciclo completo prende il nome di
ampiezza d'onda; in Figura 14.1, un pistone di diametro maggiore, a parità di
velocità angolare produce un'onda della stessa frequenza ma di ampiezza maggiore e ciò
si traduce in una maggiore energia trasmessa.
Osservando il grafico di Figura 14.2 possiamo notare che l'andamento della pressione nel
tempo è di tipo sinusoidale; indicando allora con α l'angolo orientato
relativo all'arco A-P di Figura 14.1, percorso in senso antiorario, possiamo
considerare la funzione y=sin(α). La funzione y=sin(α) è periodica
con periodo 2π; ad ogni giro completo del punto P in Figura 14.1, pari
a 2π radianti, si ottiene un nuovo ciclo. Il punto P ruota in senso
antiorario a velocità angolare ω costante, descrivendo cioè angoli uguali in
tempi uguali, per cui ogni T secondi viene descritto un giro completo da
2π radianti; tenuto conto che il periodo T è l'inverso della frequenza
f, si ha allora:
ω = α / t = 2π / T = 2πf
Per la lunghezza d'onda abbiamo:
λ = v ⋅ T = v / f
Da ω=α/t ricaviamo α=ω⋅t; inoltre, indicando
con P0 l'ampiezza dell'onda di Figura 14.2 (valore massimo assunto
dalla pressione durante un ciclo), possiamo notare che la pressione oscilla nel tempo
tra i due valori +P0 e -P0. Tenuto conto del fatto
che la funzione sin() è definita in tutto l'insieme ℝ dei numeri
reali e oscilla tra i due valori +1 e -1, possiamo in definitiva
rappresentare l'andamento della pressione in funzione del tempo come:
p(t) = P0⋅sin(α) = P0⋅sin(ωt)
Nel caso particolare in cui la frequenza f dell'onda meccanica longitudinale di
Figura 14.2 sia compresa tra circa 20 Hz e 20000 Hz, si ottiene un suono
udibile dagli esseri umani; infatti, l'orecchio umano è una vera e propria antenna
"accordata" su quelle frequenze che, nel loro insieme, formano il cosiddetto spettro
acustico.
L'orecchio esterno comprende il padiglione auricolare, costituito da cartilagine
rivestita dalla pelle; il suo compito è quello di captare i suoni e convogliarli verso
l'orecchio medio attraverso il canale uditivo, il quale termina con la membrana timpanica
(o timpano).
La membrana timpanica viene fatta vibrare dalle onde sonore e tali vibrazioni perturbano
l'aria presente nell'orecchio medio, il quale ha l'importante compito di far comunicare
correttamente l'orecchio esterno con quello interno. L'orecchio esterno contiene aria,
mentre quello interno contiene un liquido e tali due sostanze si comportano in modo
differente quando vengono perturbate da una vibrazione; senza un adeguato adattamento,
svolto dall'orecchio medio, i suoni captati dall'orecchio esterno non arriverebbero a
destinazione (tecnicamente quindi, l'orecchio medio è un vero e proprio "adattatore di
impedenza").
L'orecchio interno comprende la coclea, composta da diverse sezioni piene di liquido; a
sua volta, la coclea contiene un organo che racchiude migliaia di "cellule ciliate". Il
compito fondamentale delle cellule ciliate è quello di convertire le onde meccaniche in
impulsi elettrici; tali impulsi transitano lungo le fibre del nervo acustico e così
raggiungono il cervello, il quale crea la sensazione del suono.
Lo spettro acustico varia da persona a persona; in particolare, l'estremo superiore si
abbassa sensibilmente con l'avanzare dell'età.
Al crescere dell'ampiezza di un'onda acustica, l'orecchio percepisce un suono sempre più
intenso (in gergo si parla di "volume più alto"); ciò è dovuto alla maggiore energia
trasportata dall'onda stessa.
I suoni di frequenza inferiore a 20 Hz vengono definiti infrasuoni, mentre
quelli con frequenza superiore a 20000 Hz vengono definiti ultrasuoni;
queste definizioni quindi sono relative allo spettro acustico umano.
Lo spettro acustico delle altre specie animali può essere molto diverso da quello umano;
ad esempio, balene, elefanti ed ippopotami sentono anche una gamma di infrasuoni, i cani
sentono gli ultrasuoni sino a 50000 Hz, i gatti possono superare i 60000
Hz, mentre i pipistrelli arrivano anche a 120000 Hz. I fischietti per cani si
basano proprio su questo aspetto; producono un suono di frequenza ben oltre 20000
Hz, udibile dai cani stessi ma non dagli esseri umani.
14.2 Musica
Un suono udibile, rappresentato da un'onda sinusoidale (come quella di Figura 14.2) o
da una sovrapposizione di onde sinusoidali, prende il nome di suono musicale o,
semplicemente, musica; la Figura 14.3 illustra la differenza tra un suono generico
e un suono musicale.
In Figura 14.3 (a) vediamo un'onda sonora prodotta da una porta che sbatte; si può notare
la forma irregolare e l'assenza di periodicità. In Figura 14.3 (b) vediamo un'onda sonora
prodotta dalla corda di una chitarra; in questo caso si nota la forma regolare e il
ripetersi nel tempo di oscillazioni con lunghezza d'onda costante. Nel primo caso
l'orecchio percepisce un suono fastidioso che viene definito rumore; nel secondo
caso l'orecchio percepisce un suono piacevole (a seconda dei gusti) che viene definito
musica.
In entrambi i casi vediamo che l'ampiezza d'onda decresce esponenzialmente; ciò è dovuto
al fatto che sbattendo una porta (una sola volta) o toccando una corda di chitarra (una
sola volta), si produce un semplice impulso di pressione che si smorza nel tempo (a
differenza di quanto accade in Figura 14.1 dove il pistone si muove costantemente avanti
e indietro e "alimenta" quindi in modo continuo l'onda di pressione).
Come è stato appena spiegato, nel caso più semplice un suono musicale è descritto da una
singola onda sinusoidale, come quella di Figura 14.2; si parla allora di suono musicale
puro. Molti strumenti musicali, però, producono forme d'onda complesse che, in
apparenza, non sono neppure sinusoidali; la Figura 14.4 illustra un esempio relativo ad
una tromba.
In Figura 14.4 (b) vediamo appunto la forma d'onda che si produce suonando una tromba;
si nota chiaramente l'andamento periodico ma non sinusoidale. Si dimostra però,
matematicamente, che tale tipo di forma d'onda può essere scomposto in una serie di onde
sinusoidali chiamate armoniche; la Figura 14.4 (a) mostra proprio una serie di
armoniche componenti dell'onda di Figura 14.4 (b). In ogni fissato istante di tempo,
sommando tra loro i valori assunti dalle varie armoniche di Figura 14.4 (a), si ottiene
il valore assunto in quello stesso istante di tempo dall'onda di Figura 14.4 (b); ciò
significa appunto che l'onda di Figura 14.4 (b) è una sovrapposizione delle onde di Figura
14.4 (a).
L'onda più in alto in Figura 14.4 (a), di frequenza uguale all'onda di Figura 14.4 (b), è
quella predominante nel suono prodotto dalla tromba e prende il nome di prima
armonica o armonica fondamentale; la seconda onda ha frequenza doppia e prende
il nome di seconda armonica, mentre la terza onda ha frequenza tripla e prende il
nome di terza armonica. Naturalmente, esistono anche le armoniche successive, che
danno un contributo via via sempre meno significativo al suono finale. Il suono che si
ottiene dalla sovrapposizione di tutte le armoniche prende il nome di timbro e
permette di riconoscere in modo inconfondibile lo strumento musicale da cui proviene
(tromba, violino, pianoforte, chitarra, etc).
Un'altra caratteristica dei suoni musicali è l'intensità, la quale risulta legata
all'ampiezza d'onda; maggiore è l'ampiezza di un'onda, maggiore risulta l'intensità del
relativo suono. Dal punto di vista del cervello tutto ciò si traduce nella percezione di
un suono "più forte", esattamente come quando si aumenta il volume di una radio.
Tornando ora allo spettro acustico umano, esistono delle convenzioni attraverso le quali
si assegnano dei nomi a particolari frequenze comprese tra 20 Hz e 20000
Hz; si ottengono così le note musicali che rendono possibile la scrittura e la
lettura della musica.
Riferendoci alle convenzioni occidentali, i cui dettagli vengono spiegati più avanti,
partiamo dalla frequenza di 32.69 Hz; tale frequenza rappresenta la nota chiamata
DO. Per ottenere la nota successiva, si moltiplica la frequenza della nota
precedente per 12√2 (radice dodicesima di 2).
La tabella in Figura 14.5 illustra le prime 12 note ottenute con questo metodo, i
loro nomi italiani e la corrispondenza con i nomi anglosassoni; il simbolo ♯
si legge diesis, mentre il simbolo ♭ si legge bemolle.
Le 12 note così ottenute prendono il nome di semitoni. Il diesis fa salire
la nota di un semitono, mentre il bemolle fa scendere la nota di un semitono; ad esempio,
FA♯ (46.23 Hz) indica la nota FA (43.63 Hz) aumentata
di un semitono (cioè, moltiplicata per 12√2), mentre
MI♭ (38.86 Hz) indica la nota MI (41.19 Hz) diminuita
di un semitono (cioè, divisa per 12√2).
Se ora moltiplichiamo la frequenza 61.71 Hz del SI per
12√2 otteniamo:
61.71 ⋅ 12√2 = 65.38 Hz
che è esattamente il doppio della frequenza 32.69 Hz del DO!
Proseguendo in questo modo si ottiene il doppio della frequenza del DO♯, il
doppio della frequenza del RE, il doppio della frequenza del RE♯ e
così via. Anziché inventare nuovi simboli, si assegnano allora a questi ulteriori
12 semitoni gli stessi nomi di Figura 14.5; per distinguere questo nuovo gruppo
dal precedente si introduce il concetto di ottava. I 12 semitoni di Figura
14.5 formano l'ottava base, mentre i 12 semitoni successivi formano la
prima ottava; proseguendo in questo modo si ottiene la seconda ottava la
terza ottava e così via.
Il concetto di ottava è legato ad un'altra caratteristica dei suoni musicali,
definita altezza; due note, una di frequenza f e l'altra di frequenza
2f, differiscono tra loro in altezza di una ottava.
Partendo, ad esempio, dal DO a 32.69 Hz dell'ottava base e raddoppiando
la sua frequenza, otteniamo il DO a 65.38 Hz della prima ottava; si dice
allora che siamo saliti in altezza di una ottava (o che il secondo
DO è una ottava più alto del primo). Partendo dal FA♯
a 369.84 Hz della terza ottava e dividendo tre volte per 2 la sua
frequenza, otteniamo il FA♯ a 46.23 Hz dell'ottava base; si dice
allora che siamo scesi in altezza di tre ottave (o che il
secondo FA♯ è tre ottave più basso del primo). Risulta
ovvio che due note che differiscono tra loro per una o più ottave hanno lo stesso nome.
A questo punto possiamo capire il perché della 12√2; per passare
da una nota K a frequenza f dell'ottava n ad una nota K a
frequenza doppia 2f dell'ottava n+1, tenuto conto che in ogni ottava ci sono
12 semitoni, dobbiamo chiaramente moltiplicare 12 volte f per una
quantità incognita x. Si ha quindi:
Per spiegare ora la frequenza iniziale 32.69 Hz di Figura 14.5, bisogna tenere
presente che in base ad una convenzione internazionale, si è deciso di prendere come
nota di riferimento il LA della terza ottava a 440 Hz (per la precisione,
439.84 Hz); di conseguenza, il LA dell'ottava base ha frequenza
54.98 Hz e da qui, scendendo ulteriormente di 9 semitoni, si perviene
appunto a 32.69 Hz. In commercio si trovano dispositivi (come i diapason)
che generano il LA di riferimento a 440 Hz e vengono usati per accordare
vari strumenti musicali.
L'insieme formato da 12 semitoni consecutivi e contigui, forma la cosiddetta
scala cromatica; questa definizione è quindi indipendente dall'ottava di
appartenenza e si riferisce semplicemente alla sequenza generica:
DO, DO♯, RE, RE♯, MI, FA, FA♯, SOL, SOL♯, LA, LA♯, SI
Note con lo stesso nome ma appartenenti ad ottave differenti, vengono percepite dal
cervello umano come suoni simili, anche se l'altezza è diversa; alla base di questo
fenomeno c'è la situazione illustrata in Figura 14.6.
Passando, ad esempio, dal RE dell'ottava base al RE della prima ottava,
vediamo che la frequenza raddoppia e, di conseguenza, la lunghezza d'onda si dimezza;
in sostanza, la lunghezza d'onda del primo RE è un multiplo intero di quella del
secondo RE (e, ovviamente, di quella dei RE delle ottave successive).
Nell'intervallo di tempo impiegato dal primo RE per compiere una oscillazione
completa, il secondo RE compie due oscillazioni complete; in quello stesso
intervallo di tempo, l'orecchio viene raggiunto in entrambi i casi da un numero intero
di lunghezze d'onda e ciò provoca nel cervello la sensazione di due suoni simili,
nonostante l'altezza sia differente.
Se confrontiamo, invece, due note con nomi diversi, le loro lunghezze d'onda non sono
l'una un multiplo intero dell'altra; il cervello in questo caso percepisce due suoni
differenti.
Più la frequenza di un'onda si avvicina all'estremo superiore dello spettro acustico,
più l'orecchio percepisce un suono acuto (o alto); più la frequenza di un'onda si
avvicina all'estremo inferiore dello spettro acustico, più l'orecchio percepisce un
suono grave (o basso). Nel mondo della musica, queste considerazioni portano a
suddividere lo spettro acustico in bassi, medi e alti.
Mentre l'orecchio, in ottime condizioni, riesce a sentire tutte le frequenze tra
20 Hz e 20000 Hz, la situazione cambia quando cerchiamo di emettere
suoni con le nostre corde vocali o con gli strumenti musicali; in questo caso, infatti,
ogni strumento copre in genere solo una determinata porzione dello spettro acustico.
La voce maschile copre dalla seconda ottava in su, mentre quella femminile copre dalla
terza ottava in su; un baritono o un basso riescono a scendere anche alla prima ottava.
La tromba spazia dalla seconda alla quinta ottava, il flauto dalla terza alla quinta,
mentre il controfagotto va dall'ottava base alla terza ottava. Il pianoforte a coda è
in grado di riprodurre una vasta gamma di ottave a partire da quella base; come si
vede in Figura 14.7, in questo strumento la disposizione dei semitoni e delle ottave
è estremamente semplice in quanto segue un perfetto ordine.
I tasti bianchi rappresentano le note principali DO, RE, MI,
FA, SOL, LA, SI, mentre quelli neri rappresentano le
note intermedie e cioè, i diesis dei tasti bianchi alla loro sinistra o i bemolle dei
tasti bianchi alla loro destra; ad esempio, il DO♯ corrisponde anche al
RE♭.
14.3 Gestione del suono con il computer
Se vogliamo gestire il suono con il computer, dobbiamo trovare innanzi tutto il modo
per trasformare il suono stesso in un segnale elettrico; a tale proposito, possiamo
seguire diversi metodi come, ad esempio, prelevare una sorgente audio da un impianto
stereo o catturare la nostra voce attraverso un microfono.
Vista la sua importanza da un punto di vista didattico, consideriamo proprio il caso
del microfono; la Figura 14.8 mostra un esempio di microfono dinamico.
Un'onda sonora giunge alla membrana del microfono e la costringe a vibrare;
tali vibrazioni fanno muovere una bobina (solidale con la membrana stessa)
su cui è avvolto un filo conduttore (solenoide). All'interno della bobina è presente
un magnete fisso, che genera un campo magnetico nel quale è immerso il
solenoide; come si sa dalla Fisica, un filo conduttore che si muove all'interno
di un campo magnetico, diventa sede di una forza elettromotrice indotta il
cui valore è proporzionale alla velocità del movimento.
Si ottiene lo stesso risultato muovendo il magnete e tenendo fisso il solenoide;
l'importante è che ci sia un movimento relativo del magnete rispetto al solenoide o
viceversa.
La conseguenza di tutto ciò è che ai capi della bobina risulta presente una
differenza di potenziale elettrico V le cui variazioni nel tempo rispecchiano
fedelmente l'andamento del suono captato dal microfono; nella parte destra della
Figura 14.8 vediamo appunto il grafico di V in funzione del tempo t.
Abbiamo così trasformato un'onda meccanica in un segnale elettrico; un dispositivo
(come il microfono dinamico) capace di trasformare una grandezza fisica in un'altra
(o una forma di energia in un'altra), viene definito trasduttore.
Il segnale che abbiamo ottenuto in Figura 14.8 varia con continuità nel tempo,
seguendo fedelmente l'andamento dell'onda sonora; in sostanza, l'andamento del
segnale prodotto dalla trasduzione è "analogo" all'andamento dell'onda sonora
e per questo motivo si parla di segnale analogico.
Un dispositivo (come il microfono dinamico) che lavora con grandezze di tipo analogico
viene definito, a sua volta, analogico.
Ora che abbiamo convertito il suono in un segnale elettrico, dobbiamo procedere al
trasferimento del segnale stesso al computer; a tale proposito, abbiamo bisogno di una
periferica di acquisizione.
Nei vecchissimi computer tutta la gestione dell'audio si riduceva ad un altoparlante
interno (di pessima qualità) capace di emettere semplici beep usati come segnali di
avvertimento; a tale proposito, si inviava all'altoparlante stesso un'onda quadra di
frequenza udibile, generata dal Timer 2 dell'unico PIT 8254 presente.
Proprio questa tecnica, veniva sfruttata dai programmi dell'epoca per generare musica;
come abbiamo visto, infatti, nel Capitolo 5, si tratta di simulare grossolanamente le
note musicali inviando all'altoparlante onde quadre di frequenza compresa tra 20
Hz e 20000 Hz.
Nei moderni PC, la gestione del suono viene delegata ad una periferica di
acquisizione denominata scheda audio la cui struttura, per certi versi,
rispecchia l'architettura di Von Neumann per i computer, illustrata nella
sezione Assembly Base; in sostanza, nel caso generale, tale periferica è
dotata di una sezione di input, una unità centrale di elaborazione,
una memoria e una sezione di output.
La sezione di input comprende vari ingressi, come quello per il microfono (mic),
quello per una sorgente audio esterna (line-in), etc; la sezione di output
comprende varie uscite, come quella per le cuffie (headphones), quella per gli
altoparlanti (speakers), etc.
Tutti gli ingressi e le uscite, generalmente, sono di tipo analogico, per cui, se
abbiamo la necessità di effettuare elaborazioni dell'audio (mixaggio, regolazione
di bassi, medi e alti, registrazioni, etc), dobbiamo servirci di un qualche
dispositivo che ci permetta di convertire segnali analogici di ingresso in segnali
digitali e (una volta effettuate le elaborazioni) segnali digitali in segnali
analogici di uscita; una generica scheda audio dovrà quindi disporre almeno di
un convertitore analogico/digitale (ADC), un processore del segnale digitale
(DSP) e un convertitore digitale/analogico (DAC). Queste considerazioni
si possono riassumere in uno schema a blocchi semplificato, come quello mostrato in
Figura 14.9.
Se volessimo effettuare delle elaborazioni su un segnale analogico in ingresso,
avremmo bisogno di apparati piuttosto complessi, costosi e a volte anche ingombranti;
a dispetto di queste caratteristiche, tali apparati in genere sono in grado di
effettuare poche operazioni come, ad esempio, amplificazione, modulazione e filtraggio
del segnale. Appare molto più conveniente allora procedere ad una conversione del
segnale analogico in un segnale digitale e cioè, in una codifica binaria comprensibile
dal computer; una volta ottenuta tale codifica, possiamo effettuare elaborazioni anche
piuttosto complesse, utilizzando un apposito microprocessore dedicato.
Il compito di effettuare la conversione da analogico a digitale, viene svolto
dall'ADC o Analog to Digital Converter; la Figura 14.10 illustra in modo
semplificato il procedimento seguito da tale dispositivo.
Questa tecnica consiste nell'effettuare un cosiddetto campionamento (sampling) del
segnale analogico; in pratica, ad intervalli regolari di tempo si misura l'ampiezza del
segnale analogico ottenendo così una serie di campioni (samples). Assegnando a
ciascun campione un valore in bit, il segnale di Figura 14.10 (a) viene così convertito
in forma digitale; la Figura 14.10 (b) mostra il grafico del risultato finale.
Il numero di campioni rilevato ogni secondo rappresenta la frequenza di campionamento
o sampling rate; come è facile intuire, tale frequenza deve essere adeguatamente alta
per evitare una eccessiva perdita di informazione. In base al teorema di Nyquist -
Shannon, la frequenza di campionamento deve essere almeno pari al doppio della
frequenza più alta assunta dal segnale analogico che vogliamo digitalizzare; nel caso del
suono, come sappiamo, la frequenza più alta è circa 20000 Hz, per cui la frequenza
di campionamento minima (consigliata) deve essere circa 40000 Hz (come vedremo in
seguito, un valore molto usato è 44100 Hz = 44.1 kHz).
Il segnale digitale che abbiamo ottenuto dall'ADC può essere ora inviato al
DSP o Digital Signal Processor; si tratta di un microprocessore che
possiamo considerare come una vera e propria CPU dedicata all'elaborazione di
suoni e musica in formato digitale. La Figura 14.11 mostra lo schema a blocchi di un
DSP modello SHARC della Analog Devices.
Osserviamo subito una importante differenza rispetto alle CPU che seguono
l'architettura di Von Neumann; anziché una singola memoria contenente codice,
dati e stack, i moderni DSP usano una PROGRAM MEMORY per le istruzioni
e una DATA MEMORY per i dati. Questo tipo di organizzazione della memoria
prende il nome di Harvard Architecture (o Dual Memory).
Il Program Sequencer in Figura 14.11 gestisce il flusso del programma da
eseguire; a tale proposito, si serve di un generatore di indirizzi per la memoria di
programma e un generatore di indirizzi per la memoria dati. Una volta stabilito
l'indirizzo a cui accedere, le operazioni di I/O si svolgono attraverso il
data bus; anche in questo caso è presente un data bus per le istruzioni e un
data bus per i dati.
La Instruction Cache, come per le CPU, è una memoria ad accesso rapido
nella quale si caricano le istruzioni eseguite più frequentemente; in particolare,
questa tecnica permette un notevole aumento delle prestazioni quando si tratta di
ripetere più volte l'esecuzione di uno stesso gruppo di istruzioni (loop).
Un'altra analogia con le CPU è la presenza dei Data Registers;
chiaramente, si tratta di registri destinati a contenere i dati da elaborare.
Sotto i Data Registers in Figura 14.11 vediamo le unità del DSP
destinate ad effettuare operazioni matematiche. Il Multiplier effettua
ovviamente moltiplicazioni. La ALU (o Arithmetic and Logic Unit) si
occupa di addizioni, sottrazioni, operazioni logiche, etc. Lo Shifter si
occupa di scorrimenti e rotazioni di bit. Generalmente, i DSP sono in grado di
operare sui numeri interi, applicando ad essi le operazioni tipiche di una CPU;
esistono anche DSP capaci di operare sui numeri in virgola mobile, applicando ad
essi funzioni trigonometriche, esponenziali, etc.
Sotto la DATA MEMORY in Figura 14.11 vediamo il controller che si occupa di
interfacciare il DSP con l'ADC e il DAC e permette anche l'accesso
rapido ai dati presenti su altre periferiche (compresa la RAM del computer),
evitando il coinvolgimento della CPU; questa tecnica prende il nome di
DMA (o Direct Memory Access) e verrà analizzata nel prossimo capitolo.
L'uso dei DSP non è limitato solo alle schede audio; essi trovano largo impiego
anche in altri settori che richiedono l'elaborazione di segnali digitali come, ad
esempio, l'industria aerospaziale, la medicina, la telefonia, etc.
Terminate le elaborazioni da parte del DSP, possiamo inviare il segnale ad una
periferica di uscita; spesso tali periferiche sono di tipo analogico, per cui si rende
necessaria una nuova conversione da digitale ad analogico che, questa volta, viene
effettuata dal DAC o Digital to Analog Converter. Il procedimento
seguito dal DAC porta ad un risultato inverso rispetto a quello visibile in
Figura 14.10; a tale proposito, a parità di frequenza di campionamento, ad ogni valore
binario (campione) del segnale digitale viene assegnata una equivalente tensione
elettrica. Per evitare di ottenere in uscita un segnale analogico che varia a salti
troppo bruschi, viene utilizzata una tecnica chiamata interpolazione; con questo
metodo di filtraggio, si fa in modo che si abbia una variazione graduale tra due
qualunque valori adiacenti di tensione elettrica del segnale analogico in uscita.
Tra le varie periferiche a cui inviare il segnale analogico in uscita, la più diffusa è
rappresentata sicuramente dagli altoparlanti; il funzionamento di un altoparlante (o
speaker) è l'esatto inverso del microfono dinamico visibile in Figura 14.8. Un
segnale analogico raggiunge un filo conduttore avvolto su una bobina la quale, stavolta,
è fissa; la corrente elettrica variabile che percorre il filo produce un campo magnetico
a sua volta variabile, che interagisce con il campo magnetico prodotto da un magnete
mobile interno alla bobina e solidale con una membrana (il cono dell'altoparlante).
Il magnete è costretto ad oscillare parallelamente all'asse della bobina e trascina
così in un movimento vibratorio la membrana; le vibrazioni della membrana producono un
suono il cui andamento segue fedelmente l'andamento del segnale analogico in uscita.
Esistono anche schede audio in grado di gestire ingressi e uscite digitali; a tale
proposito, vengono utilizzati appositi standard come, ad esempio, S/PDIF o
Sony/Philips Digital Interface Format".
14.4 La scheda audio Sound Blaster 16
La Figura 14.12 mostra lo schema a blocchi della Sound Blaster 16, tratto dalla
Hardware Programming Guide ufficiale della Creative Technology; tale
documento è disponibile nella sezione
Documentazione tecnica di supporto al corso assembly dell’
Area Downloads di questo sito (SoundBlaster.pdf).
Analizziamo i principali blocchi di questo schema, partendo dal DSP; la
SB16 è dotata di un DSP capace di gestire suoni digitali rappresentati
da campioni a 8 o 16 bit, mono o stereo.
Sulla destra possiamo notare gli ingressi MIC IN (microfono), CD IN (flusso
audio proveniente dal lettore CD) e LINE IN (sorgente esterna ausiliaria); tali
ingressi giungono al MIXER grazie al quale possono essere miscelati tra loro e può
essere regolato separatamente il loro volume.
Tra le uscite notiamo quella destinata agli SPEAKERS (altoparlanti) attraverso un
apposito amplificatore del segnale analogico; una ulteriore uscita (CD-ROM DRIVE) è
disponibile per pilotare un lettore CD direttamente dalla scheda audio.
Il FM Synthesizer è un chip destinato a sintetizzare (simulare) il suono di
svariati strumenti musicali, partendo da un principio simile a quello illustrato in
Figura 14.4; a tale proposito, viene applicata alle onde sinusoidali una tecnica chiamata
FM o Frequency Modulation (modulazione di frequenza).
La MIDI port è una porta destinata ad interfacciare la scheda audio con vari
strumenti musicali compatibili; ad esempio, una tastiera musicale con uscita MIDI,
può trasferire il suo output alla scheda audio in modo che venga sottoposto a varie
elaborazioni prima di essere inviato agli altoparlanti.
Il BUS Interface è un chip destinato ad interfacciare la SB16 con il
computer; attraverso questo chip possiamo programmare la scheda audio.
Sulle versioni più evolute della SB16 è presente anche l'ASP o Advanced
Signal Processor; si tratta di un chip il cui compito è quello di eseguire operazioni
matematiche ad alta velocità sui suoni digitali.
14.4.1 Installazione e configurazione della scheda audio
Per l'Installazione della scheda audio in un ambiente hardware reale, fare riferimento
alle istruzioni fornite insieme alla scheda stessa; nel caso di VirtualBox, si
tratta semplicemente di selezionare SoundBlaster 16 nella sezione Audio -
Controller audio del menu Impostazioni.
Per la configurazione della scheda, è previsto l'uso di una variabile d'ambiente da
inserire nel file C:\AUTOEXEC.BAT; tale variabile, denominata BLASTER,
nel caso più generale assume la seguente forma:
SET BLASTER=A220 I5 D1 H5 M220 P330
Non deve esserci alcuno spazio a sinistra e a destra del segno =, mentre
deve esserci almeno uno spazio tra un parametro e l'altro.
- A è l'indirizzo base (base address) della porta di I/O
principale della scheda
- I specifica la IRQ da usare per le richieste di interruzione
- D specifica il canale DMA a 8 bit
- H specifica il canale DMA a 16 bit
- M è l'indirizzo base (base address) della porta di I/O
del mixer
- P è l'indirizzo base (base address) della porta di I/O
dell'interfaccia MIDI
In questo esempio, il parametro A specifica 220h come base address
della porta di I/O principale della scheda audio; come vedremo in seguito, si
tratta di un dato importantissimo in quanto tutti gli indirizzi dei vari registri della
SB16 si ottengono sommando uno spiazzamento al base address (che, proprio
per questo motivo, si chiama "indirizzo base").
Onde evitare eventuali conflitti hardware con altre periferiche, il base address
principale è selezionabile tramite due appositi ponticelli (jumpers) posti sulla
stessa scheda audio e denominati IOS0 e IOS1; con la SB16 risultano
disponibili i valori 220h, 240h, 260h e 280h, mediante la
disposizione dei jumpers mostrata in Figura 14.13.
Ovviamente, il valore specificato dal parametro A deve coincidere con l'indirizzo
selezionato tramite i jumpers; l'impostazione predefinita di fabbrica è 220h.
Il parametro I indica quale IRQ usare per le richieste di interruzione da
parte della scheda audio; nell'esempio è stata scelta la IRQ5. Come si vede nella
Figura 3.10 del Capitolo 3, sui vecchi computer la IRQ5 veniva assegnata alla
seconda porta parallela (LPT2); considerando il fatto che le porte parallele non
sono più presenti sui nuovi PC (e che la LPT2 era raramente presente anche
sui vecchi PC), possiamo tranquillamente assegnare la IRQ5 alla scheda
audio.
Onde evitare eventuali conflitti hardware con altre periferiche, la IRQ da usare
è selezionabile tramite due appositi jumpers posti sulla stessa scheda audio e denominati
IS0 e IS1; con la SB16 risultano disponibili i valori 2,
5, 7 e 10, mediante la disposizione dei jumpers mostrata in Figura
14.14.
Ovviamente, il valore specificato dal parametro I deve coincidere con la
IRQ selezionata tramite i jumpers; l'impostazione predefinita di fabbrica è
5.
I parametri D e H indicano i canali DMA usati dalla scheda per i
trasferimenti diretti di dati con la RAM; questi aspetti verranno trattati in
dettaglio nel prossimo capitolo.
Il canale DMA a 8 bit da usare è selezionabile tramite due appositi jumpers
posti sulla stessa scheda audio e denominati DAS0 e DAS1; con la SB16
risultano disponibili i valori 0, 1 e 3, mediante la disposizione dei
jumpers mostrata in Figura 14.15.
Ovviamente, il valore specificato dal parametro D deve coincidere con il canale
DMA a 8 bit selezionato tramite i jumpers; l'impostazione predefinita di
fabbrica è 1.
Il canale DMA a 16 bit da usare è selezionabile tramite due appositi jumpers
posti sulla stessa scheda audio e denominati DBS0 e DBS1; con la SB16
risultano disponibili i valori 5, 6 e 7, mediante la disposizione dei
jumpers mostrata in Figura 14.16.
Ovviamente, il valore specificato dal parametro H deve coincidere con il canale
DMA a 16 bit selezionato tramite i jumpers; l'impostazione predefinita di
fabbrica è 5.
In analogia con il parametro A, i parametri M e P specificano il
base address, rispettivamente, del mixer e dell'interfaccia MIDI; in
assenza del parametro M, come base address del mixer viene usato lo stesso
indirizzo indicato dal parametro A.
Il base address dell'interfaccia MIDI è selezionabile tramite un apposito
jumper posto sulla stessa scheda audio e denominato MSEL; con la SB16
risultano disponibili i valori 330h e 300h, mediante la disposizione del
jumper mostrata in Figura 14.17.
Ovviamente, il valore specificato dal parametro P deve coincidere con l'indirizzo
selezionato tramite il jumper; l'impostazione predefinita di fabbrica è 330h.
Esiste anche un ulteriore parametro denominato T, che indica il tipo di scheda
audio; per la SB16 si ha T=6. Per avere questa informazione, è comunque
consigliabile ricorrere al comando E1h - Get DSP version number (vedere la sezione
14.6); nel caso della SB16, tale comando restituisce un valore del tipo
4.xx.
Nel caso di MS-DOS, l'utente deve provvedere ad inserire la variabile BLASTER
nel file AUTOEXEC.BAT; nel caso di FreeDOS, in tale file è già presente la
riga:
SET BLASTER=A220 I5 D1 H5 P330
Il metodo consigliato per individuare i parametri elencati in precedenza, consiste nel
cercare e scandire la variabile d'ambiente BLASTER; a tale proposito, è necessario
ricordare quanto esposto nel Capitolo 13 della sezione Assembly Base.
Quando un programma eseguibile (COM o EXE) viene caricato in memoria,
risulta disposto in un blocco allineato al paragrafo; si tratta quindi di un'area di
memoria che parte da un indirizzo logico del tipo XXXXh:0000h. I due registri di
segmento DS e ES vengono inizializzati proprio con il valore XXXXh;
inoltre, i primi 256 byte di tale blocco contengono il PSP. All'offset
002Ch del PSP (quindi, all'indirizzo logico XXXXh:002Ch), è presente
un campo da 16 bit denominato EnvironmentSegment; tale campo contiene la
componente Seg dell'indirizzo di memoria da cui parte proprio l'environment
segment del programma e anche tale indirizzo è allineato al paragrafo.
L'environment segment contiene un elenco di stringhe C che sono proprio le
variabili d'ambiente; nel nostro caso, la variabile BLASTER (se presente) risulterà
quindi memorizzata da una definizione del tipo:
db 'BLASTER=A220 I5 D1 H5 M220 P330', 0
In sostanza, il nostro programma deve esplorare l'environment segment alla ricerca
di una stringa che inizi per BLASTER; da tale stringa dobbiamo poi estrarre i vari
parametri.
Supponendo che all'inizio del programma ES contenga il PSP, possiamo accedere
alle variabili d'ambiente con le due semplici istruzioni:
Creiamo una stringa "BLASTER" (da 7 caratteri) e facciamola puntare da
DS:SI, mentre ES:DI sta puntando all'inizio dell'environment segment;
per cercare ora una variabile d'ambiente che inizia per BLASTER, scriviamo un loop
che ad ogni stringa applica le istruzioni:
Se, all'uscita dal ciclo, si ha CX=0, allora è stata trovata una variabile d'ambiente
che inizia per "BLASTER".
A questo punto, scandiamo la stringa appena trovata ed estraiamo i vari parametri; se
ES:DI punta alla fine di "BLASTER=", se AL contiene il parametro da
trovare (A, I, etc) e se CX contiene la lunghezza della stringa (meno
gli 8 caratteri di "BLASTER="), possiamo scrivere:
Tutte queste procedure sono state illustrate nel Capitolo 22 della sezione Assembly
Base; nell'esempio pratico presentato più avanti vengono mostrati maggiori dettagli.
Un metodo alternativo (ma sconsigliato) per individuare i parametri consiste nell'interrogare
direttamente la scheda audio; questo procedimento viene illustrato più avanti.
14.4.2 Mappa degli indirizzi per la Sound Blaster 16
Come abbiamo già visto, per la SB16 sono disponibili i quattro possibili indirizzi
220h, 240h, 260h e 280h per il base address principale;
abbiamo anche visto che su hardware reale, tali indirizzi sono selezionabili tramite jumpers.
Tutte le porte hardware della SB16 risultano accessibili ad indirizzi che si
ottengono sommando un offset al base address principale; la Figura 14.18 illustra
l'elenco completo di tali porte (Base indica il base address principale,
R indica "accesso in lettura", W indica "accesso in scrittura", R/W
indica "accesso in lettura/scrittura").
L'interfaccia MIDI della SB16, come abbiamo visto in precedenza, ha un
base address che può essere 300h o 330h (default); su hardware
reale, tali indirizzi sono selezionabili tramite jumpers. La Figura 14.19 illustra
l'elenco delle porte disponibili per tale interfaccia.
14.5 Programmazione della Sound Blaster 16
La SB16 è programmabile secondo due modalità denominate Direct Mode e
DMA Mode.
In Direct Mode, il programmatore interagisce con la SB16 richiedendo in
modo diretto tutti i servizi di cui ha bisogno (acquisizione di suoni dal microfono, invio
di suoni agli altoparlanti, invio di comandi etc); questa modalità di programmazione è la
più semplice, ma è anche la meno efficiente in quanto è richiesto il continuo intervento
della CPU.
In DMA Mode, il programmatore si limita ad inizializzare la SB16 e il
DMAC (controller per l'accesso diretto alla memoria), lasciando poi che le fasi
successive si svolgano in modo automatico; si tratta quindi di una tecnica vantaggiosa
quando, ad esempio, la scheda audio deve eseguire un brano che occupa una grossa area della
RAM. Questa modalità di programmazione è più complessa rispetto al Direct
Mode, ma è anche più efficiente in quanto tutta la fase successiva all'inizializzazione
si svolge senza l'intervento della CPU.
In questo capitolo vengono analizzati tutti i dettagli relativi al Direct Mode e al
supporto del DMA Mode da parte della SB16; invece, nel capitolo successivo si
analizzano i dettagli relativi alla programmazione del dispositivo DMAC 8237. Proprio
per questo motivo, l'esempio pratico presentato alla fine di questo capitolo si riferisce
alla programmazione della SB16 in Direct Mode; nel capitolo successivo, una
volta acquisite le necessarie conoscenze relative al DMAC 8237, viene presentato un
esempio pratico che mostra come programmare la SB16 in DMA Mode.
14.5.1 Reset del DSP
Prima di iniziare la fase di programmazione, il DSP deve essere resettato; questo
passo è fondamentale in quanto, in seguito al reset, il DSP si inizializza e poi
si porta nel suo stato predefinito.
I passi per la fase di reset sono i seguenti:
- Scrivere il valore 1 nella porta DSP Reset all'indirizzo
Base+6h e attendere almeno 3 microsecondi
- Scrivere il valore 0 nella porta DSP Reset all'indirizzo
Base+6h
- Attendere che il bit in posizione 7 della porta DSP Read Buffer Status,
all'indirizzo Base+Eh, si sia portato a 1 (data available)
- Leggere un byte dalla DSP Read Data Port all'indirizzo Base+Ah e
verificare se il suo valore è AAh
Generalmente, tra le fasi 2 e 3 passano circa 100 microsecondi; se,
dopo tale periodo, si ottiene il valore AAh nella DSP Read Data Port, allora
il reset e l'inizializzazione del DSP sono andati a buon fine.
Supponiamo di conoscere il base address principale della SB16, memorizzato
in una costante denominata SB16BASEADDR; in tal caso, per la fase di reset possiamo
scrivere il codice seguente:
14.5.2 Lettura dal DSP
I dati resi disponibili dal DSP devono essere letti dalla DSP Read Data
Port all'indirizzo Base+Ah; come è stato spiegato in precedenza, prima di
effettuare la lettura bisogna attendere che il bit in posizione 7 della porta
DSP Read Buffer Status si sia portato a 1 (buffer di lettura pieno).
Se il base address principale della SB16 è memorizzato in una costante
denominata SB16BASEADDR, per la fase di lettura possiamo scrivere il codice seguente:
14.5.3 Scrittura nel DSP
I dati e i comandi destinati al DSP devono essere scritti nella porta DSP
Write Command/Data all'indirizzo Base+Ch; come è stato spiegato in precedenza,
prima di effettuare la scrittura bisogna attendere che il bit in posizione 7 della
porta DSP Write Buffer Status si sia portato a 0 (buffer di scrittura vuoto).
Se il base address principale della SB16 è memorizzato in una costante
denominata SB16BASEADDR e il dato o comando da scrivere è memorizzato in una
variabile denominata byteComData, per la fase di scrittura possiamo usare il
codice seguente:
14.5.4 Gestione delle interruzioni con il DSP
Il DSP versione 4.xx della SB16 genera una interruzione hardware per
ciascuno dei seguenti processi:
- 8 bit DMA Mode digitized sound I/O
- 16 bit DMA Mode digitized sound I/O
- Interfaccia MIDI generica (SB-MIDI)
- Interfaccia MPU-401 MIDI UART
Viene utilizzata la stessa linea IRQ (una di quelle elencate in Figura 14.14) per
gestire questi quattro processi. Per sapere quale processo ha generato la IRQ,
dobbiamo consultare il registro n. 82h del mixer, denominato Interrupt Status
Register (vedere più avanti la sezione dedicata alla programmazione del Mixer
chip); la Figura 14.20 illustra la struttura di tale registro.
Il processo che ha generato la IRQ pone a 1 il relativo bit
dell'Interrupt Status Register.
Come si può notare, le due IRQ relative alla 8 bit DMA Mode e alla
SB-MIDI vengono segnalate attraverso lo stesso bit D0 del registro
82h; è compito del programmatore evitare che entrambi i processi vengano eseguiti
contemporaneamente.
In base a quanto abbiamo appena visto, il programmatore è tenuto a predisporre le varie
ISR di cui ha bisogno; ciascuna ISR, una volta chiamata, provvede a testare
i quattro bit di Figura 14.20 per sapere se il processo che ha generato la IRQ è
quello a cui essa è destinata. Se il processo è quello giusto, vengono eseguite le
relative istruzioni; in caso contrario, si chiama la ISR associata all'altro
processo.
Come sappiamo, una ISR relativa ad una interruzione hardware, prima di terminare
deve inviare un EOI al PIC che ha ricevuto la IRQ; nel caso del
DSP della SB16, tale fase deve essere preceduta dall'invio al DSP
stesso di un comando di "acknowledgment" (ACK), attraverso la semplice istruzione
di lettura:
in al, dx
dove il registro DX deve contenere il seguente indirizzo di porta:
- Base + Eh per la 8 bit DMA Mode digitized sound I/O
- Base + Eh per l'interfaccia SB-MIDI
- Base + Fh per la 16 bit DMA Mode digitized sound I/O
- Base MIDI + 0h per l'interfaccia MPU-401 MIDI UART
La porta usata per i primi due tipi di interruzione è la Base+Eh che, come
sappiamo, corrisponde alla porta DSP Read-Buffer Status; ciò è necessario per
garantire la compatibilità con le vecchie versioni del DSP montate sui modelli
di Sound Blaster precedenti alla SB16.
Supponiamo, ad esempio, di voler gestire la 8 bit DMA Mode digitized sound
I/O; la relativa ISR può assumere allora l'aspetto seguente:
14.5.5 Configurazione del DMA e impostazione delle interruzioni
Come è stato già anticipato, tutti i dettagli relativi alla programmazione del DMA
controller (DMAC) verranno illustrati nel capitolo successivo; in questo capitolo
ci occuperemo dei servizi offerti dalla SB16 per l'accesso diretto alla memoria.
Per il momento, è importante sapere che la tecnica del Direct Memory Access
permette ad una periferica (come la scheda audio) di accedere direttamente alla memoria
RAM in I/O evitando di coinvolgere la CPU; a tale proposito, si
ricorre ad un dispositivo come il DMAC, il quale si comporta come un vero e
proprio microprocessore, capace però di gestire esclusivamente trasferimenti di dati.
Analizziamo il caso di una scheda audio che deve eseguire un grosso file musicale
leggendolo dalla RAM; per descrivere il procedimento da seguire, serviamoci dello
schema mostrato in Figura 14.21.
Attraverso la CPU si programma il DMAC fornendo ad esso tutti i
parametri necessari per il trasferimento dati; i parametri principali sono:
- indirizzo sorgente
- indirizzo destinazione
- numero di WORD da trasferire
Sempre attraverso la CPU si programma la scheda audio specificando quale canale
DMA verrà usato e quale IRQ verrà generata dal DSP al termine del
trasferimento dati; come vedremo meglio nel prossimo capitolo, il canale DMA è
la linea DMAREQ/DMACK da utilizzare per dialogare con il DMAC.
Il passo finale consiste nell'inviare al DSP un comando per richiedere l'avvio del
trasferimento dati; a questo punto:
- La scheda audio richiede (DMAREQ) al DMAC l'avvio di un
trasferimento dati
- Il DMAC richiede (BUSREQ) alla CPU la disponibilità
dei bus di sistema (dati, indirizzi e controllo)
- La CPU concede (BUSACK) i bus di sistema al
DMAC
- Il DMAC imposta gli indirizzi sorgente e destinazione e dà il
via libera (DMACK) al trasferimento dati
- Terminato il trasferimento dati, il DMAC rilascia i bus di
sistema, mentre il DSP genera una IRQ
Predisponendo una apposita ISR intercettiamo la IRQ tramite la quale
possiamo decidere se il trasferimento dati è terminato o meno; nel secondo caso,
dobbiamo procedere ad una nuova programmazione della scheda audio e del DMAC
impostando i parametri per il nuovo blocco dati.
Il DSP versione 4.xx della SB16 permette di impostare via
software, sia il canale DMA, sia la linea IRQ da utilizzare per il
trasferimento dati diretto.
Per impostare la linea IRQ dobbiamo servirci del registro n. 80h del
mixer, denominato Interrupt Setup Register (vedere più avanti la sezione dedicata
alla programmazione del Mixer chip); la Figura 14.22 illustra la struttura di
tale registro.
Solo uno dei quattro bit disponibili deve essere posto a 1; gli altri tre
devono valere rigorosamente 0.
Per impostare il canale DMA dobbiamo servirci del registro n. 81h del
mixer, denominato DMA Setup Register (vedere più avanti la sezione dedicata
alla programmazione del Mixer chip); la Figura 14.23 illustra la struttura di
tale registro.
I canali 0, 1 e 3 sono destinati ai trasferimenti di dati a 8
bit; i canali 5, 6 e 7 sono destinati ai trasferimenti di dati a
16 bit. Solo uno dei tre bit D0, D1, D3 e solo uno dei tre
bit D5, D6, D7 deve essere posto a 1; gli altri bit devono
valere rigorosamente 0.
Il DSP versione 4.xx della SB16 supporta anche trasferimenti di dati
a 16 bit attraverso i canali DMA a 8 bit; a tale proposito, bisogna
porre a 0 i tre bit D5, D6, D7 e porre a 1 uno solo
dei tre bit D0, D1, D3.
14.5.6 Rappresentazione del suono digitale nel DSP
La tecnica illustrata in Figura 14.10, permette di trasformare un segnale analogico in un
segnale digitale costituito da una sequenza di numeri binari; nel caso in cui il segnale
analogico di partenza sia di tipo audio, si dice che il segnale digitale così ottenuto è
un suono in formato Pulse Code Modulation o PCM. Il DSP della
SB16 gestisce i suoni digitali in formato PCM a 8 e 16 bit,
mono o stereo.
Ogni campione (sample) a 8 bit contiene un numero intero senza segno,
compreso quindi tra 0 e 255; in questo caso, in riferimento all'onda di
Figura 14.10, il valore 0 indica il volume minimo negativo, 255 indica il
volume massimo positivo, mentre 128 indica volume zero.
La Figura 14.24 illustra la struttura del flusso audio a 8 bit monofonico. Nelle
figure seguenti, left indica il canale audio sinistro (channel 0), mentre
right indica il canale audio destro (channel 1); inoltre, il flusso di
dati è ordinato da sinistra verso destra.
La Figura 14.25 illustra la struttura del flusso audio a 8 bit stereofonico.
Ogni campione (sample) a 16 bit contiene un numero intero con segno,
compreso quindi tra -32768 e +32767; in questo caso, in riferimento
all'onda di Figura 14.10, il valore -32768 indica il volume minimo negativo,
+32767 indica il volume massimo positivo, mentre 0 indica volume zero.
La Figura 14.26 illustra la struttura del flusso audio a 16 bit monofonico.
La Figura 14.27 illustra la struttura del flusso audio a 16 bit stereofonico.
Come si può notare, percorrendo da sinistra verso destra un flusso audio a 16 bit,
si incontra prima il byte meno significativo (o low byte) e poi il byte più
significativo (o high byte) di ogni campione.
14.5.7 Frequenza di trasferimento dei campioni
Abbiamo visto che quando si programma il DSP in DMA Mode, per il blocco
dati da trasferire si imposta, tra l'altro, l'indirizzo sorgente e l'indirizzo di
destinazione; un ulteriore importantissimo parametro da specificare è il transfer
rate o "frequenza di trasferimento" (cioè, quanti campioni al secondo bisogna
trasferire). Il transfer rate non deve essere confuso con il sampling
rate che, come abbiamo visto, è la frequenza con la quale viene campionato il
segnale analogico che l'ADC deve convertire in digitale (campioni letti ogni
secondo).
Per qualunque Sound Blaster, il transfer rate viene impostato specificando
la cosiddetta time constant o "costante di tempo"; tale costante viene calcolata
nel modo seguente:
time_constant = 65536 - (256000000 / (channels * sampling_rate))
dove channels indica il numero di canali audio (1 per i suoni mono e
2 per i suoni stereo).
Solamente il byte più significativo del risultato deve essere fornito al DSP;
per maggiori dettagli su questo ed altri aspetti si veda più avanti la sezione dedicata
al set di comandi del DSP.
Nel caso del DSP versione 4.xx della SB16, è anche possibile
specificare direttamente il sampling rate.
14.5.8 Programmazione del DSP in Direct Mode
In Direct Mode, tutte le operazioni di I/O con il DSP vengono
gestite direttamente dal programmatore attraverso la CPU; in particolare, è
possibile leggere o scrivere singoli campioni audio e comandi. In questa modalità il
DSP lavora solo con suoni digitali in formato PCM a 8 bit
monofonico.
Se si vuole eseguire un file audio in Direct Mode, la tecnica più efficace da
utilizzare è del tutto simile a quella illustrata nella sezione 5.5.3 del
Capitolo 5; si tratta quindi di riprogrammare il Timer 0 del PIT 1, in
modo da farlo lavorare ad una frequenza uguale a quella di campionamento del file
stesso. L'esempio pratico presentato alla fine del capitolo si riferisce proprio alla
esecuzione di un file audio con tale metodo.
14.5.9 Programmazione del DSP in DMA Mode - Single-Cycle
Nella modalità Single-Cycle, il DSP richiede al DMAC un singolo
trasferimento dati; terminata questa operazione, viene generata una IRQ.
Come vedremo nel prossimo capitolo, a causa delle limitazioni del DMAC 8237,
la dimensione del blocco dati da trasferire non può eccedere i 65536 byte
(64 KiB); se il blocco ha dimensioni maggiori, deve essere suddiviso in un
opportuno numero di sotto-blocchi, ciascuno dei quali non può eccedere i 64
KiB. Questa situazione viene gestita sfruttando proprio la IRQ che viene
generata al termine di ogni trasferimento dati; la ISR che intercetta la
IRQ riprogramma il DMAC e il DSP impostando il nuovo sotto-blocco
da trasferire.
In modalità Single-Cycle, il DSP supporta il suono digitale in formato
PCM a 8 e 16 bit e in formato compresso ADPCM (descritto
più avanti).
14.5.10 Programmazione del DSP in DMA Mode - Auto-Initialize
Nella modalità Auto-Initialize, il DSP e il DMAC richiedono una
sola fase di inizializzazione; una volta terminato il trasferimento dati, il DMAC
si riprogramma automaticamente con gli stessi indirizzi sorgente e destinazione e con
la stessa dimensione del blocco dati.
Generalmente, per gestire questa modalità, si utilizza il metodo del
double-buffering; la Figura 14.28 illustra il concetto su cui si basa tale
procedimento.
Innanzi tutto, il programma predispone un buffer di memoria (DMA buffer) che non
deve eccedere la dimensione di 64 KiB; il DMAC viene programmato per un
trasferimento dati di dimensione pari a quella del DMA buffer (supponiamo
64 KiB), mentre il DSP viene programmato per un trasferimento dati di
dimensione pari alla metà esatta del DMA buffer (supponiamo 32 KiB).
Supponiamo ora di voler trasferire al DSP un file audio che si trova nella
RAM del computer; come indirizzo sorgente impostiamo quindi quello del DMA
buffer. Il programma riempie l'intero DMA buffer con i primi 64 KiB
del file audio e poi dà il via al trasferimento dati; a questo punto:
- Il DMAC trasferisce al DSP i primi 32 KiB del buffer (in
verde in Figura 14.28)
- Il DSP genera una prima IRQ
- La ISR che intercetta la IRQ carica una nuova porzione da 32
KiB del file audio nella prima metà del buffer
- Il DMAC trasferisce al DSP i secondi 32 KiB del buffer (in
giallo in Figura 14.28)
- Il DSP genera una seconda IRQ
- La ISR che intercetta la IRQ carica una nuova porzione da 32
KiB del file audio nella seconda metà del buffer
- Il trasferimento dati prosegue dal punto 1
Come si può notare, ogni volta che il DMAC trasferisce al DSP l'intero
contenuto del buffer, si auto-inizializza di nuovo e trova altri 64 KiB da
trasferire.
Per porre fine al trasferimento dati, la ISR può riprogrammare il DSP in
modalità Single-Cycle; in alternativa, la stessa ISR può inviare al
DSP un comando per l'uscita dalla modalità Auto-Initialize (vedere più
avanti la sezione dedicata ai comandi per il DSP).
La tecnica appena illustrata permette di evitare un inconveniente che si presenta in
modalità Single-Cycle; infatti, in tale modalità, ogni volta che termina il
trasferimento di un blocco dati, bisogna riprogrammare il DMAC e il DSP e
ciò (nel caso in cui si stia inviando un file audio agli altoparlanti) produce un tipico
rumore tra un blocco e l'altro.
In modalità Auto-Initialize, il DSP supporta il suono digitale in formato
PCM a 8 e 16 bit e in formato compresso ADPCM.
14.5.11 Programmazione del DSP in DMA Mode - High-Speed
Quando opera nelle modalità illustrate in precedenza, il DSP è anche in grado di
accettare comandi; tale possibilità può però creare problemi quando si devono trasferire
grosse quantità di dati ad alta velocità. Un esempio classico è rappresentato
dall'esecuzione di un CD audio attraverso il DSP; la cosiddetta "qualità CD"
comporta una frequenza di campionamento ad almeno 44.1 kHz (44100 campioni
al secondo), con campioni a 16 bit in stereofonia, pari a 4 byte per
campione (Figura 14.27). Tutto ciò richiede una frequenza di trasferimento (transfer
rate) pari a:
44100 * 4 = 176400 B/s
Per gestire transfer rate ancora più elevati, è necessario impostare il DSP
in modalità High-Speed; in tale modalità il DSP esegue esclusivamente
trasferimenti di dati, mentre è esclusa la possibilità di inviare comandi.
La modalità High-Speed può essere attivata in combinazione con il modo
Single-Cycle o con il modo Auto-Initialize. In modo Single-Cycle,
al termine del singolo trasferimento dati il DSP esce automaticamente dal modo
High-Speed; in modo Auto-Initialize, il DSP richiede un reset per
uscire dal modo High-Speed.
Quando il modo High-Speed è attivo, il DSP supporta solo il suono digitale
in formato PCM a 8 bit, mono o stereo; non è supportato, invece, il formato
compresso ADPCM.
14.5.12 Programmazione del DSP in DMA Mode - ADPCM
Il DSP può usare il DMAC anche per trasferire dati in formato compresso
ADPCM o Adaptive Delta Pulse Code Modulation; tali dati vengono
automaticamente decompressi dallo stesso DSP prima di essere inviati ad un
dispositivo di output (come gli altoparlanti).
Questa tecnica di compressione consiste nel memorizzare il suono digitale come una sequenza
di differenze tra ogni campione e quello precedente, anziché come sequenza di campioni;
considerando il fatto che un segnale audio varia in modo continuo (senza grossi salti),
risulta chiaro che la differenza tra un qualsiasi campione e quello precedente sarà molto
contenuta e quindi potrà essere rappresentata usando pochissimi bit.
In formato ADPCM i campioni sono esclusivamente a 8 bit; i tre formati di
compressione compatibili con il DSP sono 8 bit to 2 bit, 8 bit to 3
bit e 8 bit to 4 bit. Ovviamente, il primo byte è un campione non compresso e
viene usato come riferimento in fase di compressione per calcolare le varie differenze a
partire dal secondo byte; in fase di decompressione, il DSP usa ugualmente il byte
di riferimento per ricostruire i campioni originali.
14.5.13 Programmazione del Mixer Chip
Sulla SB16 è presente un mixer chip CT1745; la programmazione di tale
chip avviene attraverso le seguenti due porte, elencate anche in Figura 14.18:
- Base + 4h: Mixer Chip Register Address Port (write only)
- Base + 5h: Mixer Chip Data Port (read/write)
In pratica, attraverso la porta Base+4h si indica l'indirizzo del registro del
mixer a cui si vuole accedere; attraverso poi la porta Base+5h si effettua la
lettura o la scrittura dei dati.
Nella sezione 14.5.4, ad esempio, abbiamo visto che per sapere quale processo ha
generato una IRQ, dobbiamo leggere il contenuto del registro 82h del mixer
chip, denominato Interrupt Status Register; a tale proposito, abbiamo utilizzato
il seguente codice:
Il CT1745 fornisce il controllo indipendente del volume a 32 livelli per
gli ingressi Master, Voice, MIDI, CD e Line-In; tali
ingressi sono tutti stereofonici e per ciascuno di essi è possibile regolare in modo
indipendente anche il volume del canale destro e del canale sinistro.
L'ingresso Microphone (Mic) è monofonico ed ha un controllo volume a 32
livelli; l'ingresso PC Speaker (altoparlante interno del computer) è monofonico ed ha
un controllo volume a 4 livelli;
Il CT1745 è in grado di miscelare tra loro in uscita le sorgenti PC Speaker,
Voice, MIDI, Mic, CD e Line-In; le sorgenti Mic,
CD e Line-In possono essere escluse via software dal mixaggio, mentre le altre
possono essere escluse solo azzerando il loro volume.
Il CT1745 è in grado di miscelare tra loro in ingresso (in fase di registrazione) le
sorgenti Mic, Line-In e MIDI; anche in questo caso, tali tre sorgenti
possono essere escluse via software dal mixaggio.
La Figura 14.29 illustra l'elenco completo dei registri presenti sul mixer chip
CT1745; L indica il canale sinistro, mentre R indica il canale destro.
Qualunque valore a 8 bit scritto nel registro di indice 00h produce un
reset del mixer.
I registri di indice 04h, 0Ah, 22h, 26h, 28h e
2Eh hanno lo scopo di mantenere la compatibilità con i mixer dei modelli
inferiori di Sound Blaster; per i programmi destinati espressamente alla
SB16, è vivamente raccomandato l'uso dei nuovi registri per il controllo del
volume, da 30h a 3Bh.
I registri 04h, 22h e 26h sono da 4 bit per canale audio,
pari a 24=16 livelli di volume ciascuno; il passaggio da 0 a
15 equivale ad una escursione da -60 dB a 0 dB, a salti di
4 dB, mentre il valore predefinito è 12 (-12 dB).
I registri 28h e 2Eh sono da 4 bit per canale audio, pari a
24=16 livelli di volume ciascuno; il passaggio da 0 a
15 equivale ad una escursione da -60 dB a 0 dB, a salti di
4 dB, mentre il valore predefinito è 0 (-60 dB).
Il registro 0Ah è da 3 bit, pari a 23=8 livelli di
volume; il passaggio da 0 a 8 equivale ad una escursione da -48
dB a 0 dB, a salti di 6 dB, mentre il valore predefinito è 0
(-48 dB).
I registri 30h/31h, 32h/33h e 34h/35h sono da 5 bit per
canale audio, pari a 25=32 livelli di volume ciascuno; il passaggio
da 0 a 31 equivale ad una escursione da -62 dB a 0 dB, a
salti di 2 dB, mentre il valore predefinito è 24 (-14 dB).
I registri 36h/37h, 38h/39h e 3Ah sono da 5 bit per canale
audio, pari a 25=32 livelli di volume ciascuno; il passaggio da
0 a 31 equivale ad una escursione da -62 dB a 0 dB, a salti
di 2 dB, mentre il valore predefinito è 0 (-62 dB).
Il registro 3Bh è da 2 bit, pari a 22=4 livelli di
volume; il passaggio da 0 a 3 equivale ad una escursione da -18
dB a 0 dB, a salti di 6 dB, mentre il valore predefinito è 0
(-18 dB).
Il registro 3Ch permette di stabilire quali sorgenti verranno miscelate in
uscita; un bit posto a 1 abilita la relativa sorgente, mentre 0 la
disabilita. Le impostazioni predefinite sono illustrate in Figura 14.30.
Il registro 3Dh permette di stabilire quali sorgenti verranno miscelate in
ingresso per il canale sinistro del mixer; un bit posto a 1 abilita la relativa
sorgente, mentre 0 la disabilita. Le impostazioni predefinite sono illustrate
in Figura 14.31.
Il registro 3Eh permette di stabilire quali sorgenti verranno miscelate in
ingresso per il canale destro del mixer; un bit posto a 1 abilita la relativa
sorgente, mentre 0 la disabilita. Le impostazioni predefinite sono illustrate
in Figura 14.32.
I registri 3Fh/40h e 41h/42h sono da 2 bit per canale audio, pari
a 22=4 livelli di guadagno ciascuno; il passaggio da 0 a
3 equivale ad una escursione da 0 dB a 18 dB, a salti di 6
dB, mentre il valore predefinito è 0 (0 dB).
Il registro 43h (AGC o Automatic Gain Control) è da 1 bit;
il valore 0 abilita l'AGC, mentre il valore 1 imposta l'AGC
al valore di guadagno fisso 20 dB.
I registri 44h/45h (regolazione alti) e 46h/47h (regolazione bassi) sono
da 4 bit per canale audio, pari a 24=16 livelli di guadagno
ciascuno; il passaggio da 0 a 7 equivale ad una escursione da -14
dB a 0 dB, a salti di 2 dB, mentre il passaggio da 8 a 15
equivale ad una escursione da 0 dB a 14 dB, a salti di 2 dB.
Il valore predefinito è 8 (0 dB).
14.5.14 Programmazione della porta MIDI
La programmazione della porta MIDI non viene trattata in questo capitolo; se si
è interessati a tale argomento, si possono trovare tutti i dettagli nella Hardware
Programming Guide ufficiale della Creative Technology, disponibile nella
sezione Documentazione tecnica di supporto al corso assembly dell’
Area Downloads di questo sito (SoundBlaster.pdf).
14.6 Comandi del DSP
Analizziamo ora i principali comandi che il programmatore può inviare al DSP
versione 4.xx della SB16; come abbiamo visto in precedenza, tali comandi
vanno scritti nel registro DSP Write Command/Data all'indirizzo Base+Ch.
Eventuali parametri richiesti da un comando vanno scritti nello stesso registro DSP
Write Command/Data; eventuali dati restituiti da un comando inviato al DSP
vanno letti dal registro DSP Read Data Port all'indirizzo Base+Ah.
Bisogna anche ricordare che, prima di inviare un comando al DSP, è necessario
assicurarsi che sia a 0 il bit 7 del DSP Write-Buffer Status
all'indirizzo Base+Ch; analogamente, prima di leggere un dato dalla DSP Read
Data Port, è necessario assicurarsi che sia a 1 il bit 7 del DSP
Read-Buffer Status all'indirizzo Base+Eh.
- 10h - 8-bit Direct Mode single byte digitized sound output
Questo comando invia al DSP un campione audio digitale da 1 byte
(formato PCM a 8 bit monofonico); è compito del programma gestire la
frequenza di trasferimento dei campioni.
Dopo aver ricevuto il comando 10h, il DSP resta in attesa di un dato
da 1 byte; se si devono inviare più campioni in sequenza, è necessario
ripetere la procedura appena descritta (invio del comando 10h seguito da un
singolo campione audio) rispettando la corretta temporizzazione.
- 14h - 8-bit Single Cycle DMA Mode digitized sound output
Questo comando invia al DSP una sequenza di campioni audio digitali da 1
byte ciascuno, in Single Cycle DMA Mode.
Dopo aver ricevuto il comando, il DSP resta in attesa di un dato da 1
word che rappresenta il numero di byte meno uno da trasferire; tale dato deve
essere inviato in due parti: prima il byte basso e poi il byte alto.
- 16h - Creative 8-bit to 2-bit ADPCM Single Cycle DMA Mode digitized sound
output
Questo comando invia al DSP un blocco di campioni audio digitali da 1
byte ciascuno, compressi in formato ADPCM 8-bit to 2-bit, in Single Cycle DMA
Mode.
Dopo aver ricevuto il comando, il DSP resta in attesa di un dato da 1
word che rappresenta il numero di byte meno uno contenuti nel blocco da trasferire;
tale dato deve essere inviato in due parti: prima il byte basso e poi il byte alto.
Come sappiamo, il primo byte di un blocco compresso in formato ADPCM rappresenta
un campione audio vero e proprio e viene chiamato reference byte; per trasferire
il primo blocco, contenente quindi il reference byte, è necessario usare il
comando 17h.
- 17h - Creative 8-bit to 2-bit ADPCM Single Cycle DMA Mode digitized sound
output with reference byte
Questo comando è identico al precedente, ma è destinato a trasferire solo il primo blocco,
contenente il reference byte; per trasferire i blocchi successivi, è necessario
usare il comando 16h.
- 1Ch - 8-bit Auto Initialize DMA Mode digitized sound output
Questo comando invia al DSP una sequenza di campioni audio digitali da 1
byte ciascuno, in Auto Initialize DMA Mode; la dimensione del blocco da trasferire
deve essere impostata tramite il comando 48h. Al termine del trasferimento, il
DSP genera una IRQ.
Per uscire dalla modalità Auto Initialize, bisogna riprogrammare il DSP in
modalità Single Cycle; in alternativa, si può inviare un apposito comando
illustrato più avanti.
- 1Fh - Creative 8-bit to 2-bit ADPCM Auto Initialize DMA Mode digitized
sound output with reference byte
Questo comando invia al DSP un blocco di campioni audio digitali da 1
byte ciascuno, compressi in formato ADPCM 8-bit to 2-bit, in Auto Initialize
DMA Mode.
- 20h - 8-bit Direct Mode single byte digitized sound input
Questo comando legge dal DSP un campione audio digitale da 1 byte (formato
PCM a 8 bit monofonico); è compito del programma gestire la frequenza di
trasferimento dei campioni.
Dopo aver ricevuto il comando 20h, il DSP attende che il programma legga
un dato da 1 byte; se si devono leggere più campioni in sequenza, è necessario
ripetere la procedura appena descritta (invio del comando 20h seguito dalla
lettura di un singolo campione audio) rispettando la corretta temporizzazione.
- 24h - 8-bit Single Cycle DMA Mode digitized sound input
Questo comando legge dal DSP una sequenza di campioni audio digitali da 1
byte ciascuno, in Single Cycle DMA Mode.
Dopo aver ricevuto il comando, il DSP resta in attesa di un dato da 1
word che rappresenta il numero di byte meno uno da trasferire; tale dato deve
essere inviato in due parti: prima il byte basso e poi il byte alto.
- 2Ch - 8-bit Auto Initialize DMA Mode digitized sound input
Questo comando legge dal DSP una sequenza di campioni audio digitali da 1
byte ciascuno, in Auto Initialize DMA Mode; la dimensione del blocco da trasferire
deve essere impostata tramite il comando 48h. Al termine del trasferimento, il
DSP genera una IRQ.
- 40h - Set digitized sound transfer Time Constant
Come abbiamo visto nella sezione 14.5.7 la costante di tempo necessaria per
impostare il transfer rate viene calcolata nel modo seguente:
time_constant = 65536 - (256000000 / (channels * sampling_rate))
dove channels indica il numero di canali audio (1 per i suoni mono e
2 per i suoni stereo).
Dopo aver ricevuto il comando 40h, il DSP resta in attesa di un dato da
1 byte che rappresenta il byte più significativo del valore time_constant.
- 41h - Set digitized sound output sampling rate
Dopo aver ricevuto questo comando, il DSP resta in attesa di un dato da 1
word che rappresenta la frequenza di campionamento; tale dato deve essere inviato in due
parti: prima il byte alto e poi il byte basso.
I valori validi per la frequenza di campionamento spaziano da 5000 Hz a
45000 Hz; a differenza di quanto accade per la costante di tempo, la frequenza di
campionamento non deve essere moltiplicata per 2 nel caso di audio stereo.
- 42h - Set digitized sound input sampling rate
Dopo aver ricevuto questo comando, il DSP resta in attesa di un dato da 1
word che rappresenta la frequenza di campionamento; tale dato deve essere inviato in due
parti: prima il byte alto e poi il byte basso.
Valgono tutte le considerazioni illustrate per il comando 41h.
- 48h - Set DSP block transfer size
Questo comando può essere usato solo in Auto Initialize DMA Mode in combinazione
con la modalità High Speed; il suo scopo è quello di impostare le dimensioni del
blocco dati da trasferire. Il DSP genera una IRQ per segnalare la fine del
trasferimento.
Dopo aver ricevuto questo comando, il DSP resta in attesa di un dato da 1
word che rappresenta la dimensione, in byte meno 1, del blocco da trasferire; tale
dato deve essere inviato in due parti: prima il byte basso e poi il byte alto.
- 74h - Creative 8-bit to 4-bit ADPCM Single Cycle DMA Mode digitized
sound output
Valgono tutte le considerazioni già illustrate per il comando 16h.
- 75h - Creative 8-bit to 4-bit ADPCM Single Cycle DMA Mode digitized
sound output with reference byte
Valgono tutte le considerazioni già illustrate per il comando 17h.
- 76h - Creative 8-bit to 3-bit ADPCM Single Cycle DMA Mode digitized
sound output
Valgono tutte le considerazioni già illustrate per il comando 16h.
- 77h - Creative 8-bit to 3-bit ADPCM Single Cycle DMA Mode digitized
sound output with reference byte
Valgono tutte le considerazioni già illustrate per il comando 17h.
- 7Dh - Creative 8-bit to 4-bit ADPCM Auto Initialize DMA Mode digitized
sound output with reference byte
Valgono tutte le considerazioni già illustrate per il comando 1Fh.
- 7Fh - Creative 8-bit to 3-bit ADPCM Auto Initialize DMA Mode digitized
sound output with reference byte
Valgono tutte le considerazioni già illustrate per il comando 1Fh.
- 80h - Pause DAC for a duration
Questo comando ha lo scopo di specificare la durata di una pausa; una volta trascorso il
periodo di tempo stabilito, il DSP genera una IRQ.
Dopo aver ricevuto questo comando, il DSP resta in attesa di un dato da 1
word che rappresenta la durata della pausa, in intervalli di tempo di campionamento meno
1; tale dato deve essere inviato in due parti: prima il byte basso e poi il byte
alto.
- Bxh - Program 16-bit DMA Mode digitized sound I/O
Questo comando imposta varie funzionalità del DSP per la gestione dell'audio
digitale con campioni a 16 bit.
I 4 bit più significativi del comando valgono Bh (1011b), mentre gli
altri 4 possono variare assumendo il significato illustrato in Figura 14.33.
dove:
- D3=0 abilita il DAC, mentre D3=1 abilita l'ADC
- D2=0 abilita il Single Cycle DMA Mode, mentre D2=1 abilita
l'Auto-Initialize DMA Mode
- D1=0 disabilita la coda FIFO, mentre D1=1 la abilita
Dopo aver ricevuto questo comando, il DSP resta in attesa di due parametri; il
primo, da 1 byte, assume il significato illustrato in Figura 14.34.
dove:
- D5=0 imposta la modalità mono, mentre D5=1 imposta la modalità
stereo
- D4=0 indica che i campioni sono numeri a 16 bit senza segno, mentre
D4=1 indica che i campioni sono numeri a 16 bit con segno
Il secondo parametro, da 1 word, indica il numero di campioni meno 1; tale
parametro deve essere inviato in due parti: prima il byte basso e poi il byte alto.
- Cxh - Program 8-bit DMA Mode digitized sound I/O
Questo comando imposta varie funzionalità del DSP per la gestione dell'audio
digitale con campioni a 8 bit.
I 4 bit più significativi del comando valgono Ch (1100b), mentre gli
altri 4 sono organizzati esattamente come in Figura 14.33; i due parametri
richiesti da questo comando hanno lo stesso significato di quelli del comando Bxh
(Figura 14.34).
- D0h - Pause 8-bit DMA Mode digitized sound I/O
Dopo aver ricevuto questo comando, il DSP interrompe temporaneamente l'invio
di richieste al DMAC; può essere usato nelle modalità Single Cycle e
Auto Initialize.
Dopo aver ricevuto questo comando, il DSP connette l'ingresso dell'amplificatore
all'uscita dello Speaker; sulla SB16 questo comando influisce solo sulla
richiesta dello stato dello Speaker (vedere più avanti il comando D8h).
Dopo aver ricevuto questo comando, il DSP disconnette l'ingresso dell'amplificatore
dall'uscita dello Speaker; sulla SB16 questo comando influisce solo sulla
richiesta dello stato dello Speaker (vedere più avanti il comando D8h).
- D4h - Continue 8-bit DMA Mode digitized sound I/O
Dopo aver ricevuto questo comando, il DSP riprende l'invio di richieste al
DMAC precedentemente interrotte dal comando D0h; può essere usato nelle
modalità Single Cycle e Auto Initialize.
- D5h - Pause 16-bit DMA Mode digitized sound I/O
Dopo aver ricevuto questo comando, il DSP interrompe temporaneamente l'invio
di richieste al DMAC; può essere usato nelle modalità Single Cycle e
Auto Initialize.
- D6h - Continue 16-bit DMA Mode digitized sound I/O
Dopo aver ricevuto questo comando, il DSP riprende l'invio di richieste al
DMAC precedentemente interrotte dal comando D5h; può essere usato nelle
modalità Single Cycle e Auto Initialize.
Dopo aver ricevuto questo comando, il DSP risponde con un valore da 1
byte che contiene lo stato dello Speaker; il valore FFh indica che lo
speaker è abilitato, mentre il valore 00h indica che è disabilitato.
- D9h - Exit 16-bit Auto Initialize DMA Mode digitized sound I/O
Questo comando permette al DSP di uscire dalla modalità Auto Initialize.
- DAh - Exit 8-bit Auto Initialize DMA Mode digitized sound I/O
Questo comando permette al DSP di uscire dalla modalità Auto Initialize.
- E1h - Get DSP version number
Dopo aver ricevuto questo comando, il DSP restituisce due byte; il primo contiene
il major number della versione, mentre il secondo contiene il minor number.
Nel caso del DSP della SB16, il primo byte contiene il valore 4.
14.7 Creative Voice File (VOC) format
Oltre alle schede Sound Blaster, la Creative Technology ha reso disponibile
anche un formato di file audio denominato Creative Voice File o VOC; tale
tipo di file audio è riconoscibile dall'estensione .VOC.
I file audio VOC supportano varie funzionalità e, tra l'altro, sono in grado di:
- incorporare testo in formato ASCII
- fornire informazioni sul tipo di compressione usata
- effettuare un loop su una porzione del file audio
- utilizzare frequenze di campionamento multiple nello stesso file audio
Nella loro forma più semplice, i file VOC si prestano ad essere utilizzati anche
a scopo didattico; ad esempio, possiamo ricavare informazioni sul tipo di codifica,
estrarre campioni audio da inviare al DSP, etc.
Gli esempi presentati in questo capitolo e in quello successivo, consistono proprio nella
estrazione di campioni da un file audio e nella loro esecuzione; in questo capitolo
vedremo come eseguire un file VOC monofonico a 8 bit (l'unico supportato)
in Direct Mode, mentre nel capitolo successivo ci occuperemo dell'esecuzione di
un file audio monofonico o stereofonico in DMA Mode.
Un file VOC è diviso in due parti denominate Header Block e Data
Block. L'Header Block contiene importanti informazioni come l'identificatore
del tipo di file, il numero di versione e un puntatore all'inizio del Data Block;
il Data Block contiene campioni audio e altre informazioni ed è suddiviso in
sotto-blocchi di vario tipo.
14.7.1 L'Header Block
I primi 14h byte dell'Header Block, compresi quindi tra gli offset 00h
e 13h, contengono la stringa "Creative Voice File" seguita dal valore
1Ah.
Agli offset 14h e 15h sono presenti il byte basso e il byte alto di una
word che contiene l'offset, all'interno del file, da cui parte il Data Block;
tale offset è calcolato rispetto all'inizio del file e, per le vecchie versioni, vale
sempre 001Ah.
Agli offset 16h e 17h sono presenti il byte basso e il byte alto di una
word che contiene il numero di versione del formato VOC; il byte basso contiene il
minor number, mentre il byte alto contiene il major number. Ad esempio, il
valore 010Ah è da intendere come 01h.0Ah, che in base 10 equivale alla
versione 1.10.
Agli offset 18h e 19h sono presenti il byte basso e il byte alto di una
word che contiene il codice di identificazione del formato VOC; tale valore viene
ottenuto effettuando il complemento a 1 del numero di versione e sommando
1234h al risultato. Ad esempio, se il numero di versione è 010Ah, si
ottiene:
NOT(010Ah) + 1234h = FEF5h + 1234h = (1)1129h = 1129h
(vanno presi solo i primi 4 nibble del risultato).
14.7.2 Il Data Block
Come è stato appena spiegato, il Data Block è suddiviso in vari sotto-blocchi; il
primo byte di ogni sotto-blocco è chiamato Block Type ed è importantissimo in
quanto contiene il tipo di dati contenuto nel sotto-blocco stesso.
I successivi 3 byte formano il Block Length e il loro contenuto codifica
la lunghezza in byte del sotto-blocco; da tale lunghezza risultano esclusi il Block
Type e il Block Length stesso.
Analizziamo ora i vari tipi di sotto-blocco.
Si tratta di un sotto-blocco da un solo byte (il Block Type stesso) che indica il
termine dell'intero Data Block (cioè, non esistono ulteriori sotto-blocchi); di
norma, si trova sempre alla fine di un file VOC.
Si tratta di un sotto-blocco contenente campioni audio digitali; la sua intestazione
occupa 6 byte e assume l'aspetto mostrato in Figura 14.35.
BlockID vale sempre 1.
BlockLen contiene la lunghezza in byte del sotto-blocco; da tale lunghezza
risultano esclusi i 4 byte occupati dai campi BlockID e BlockLen.
Si tratta quindi della lunghezza del blocco contenente i campioni audio digitali, più
i 2 byte dei campi TimeConst e PackMethod.
TimeConst contiene il byte più significativo del risultato che scaturisce dal
calcolo della costante di tempo (vedere la sezione 14.5.7).
PackMethod contiene il metodo di compressione usato per i campioni audio
digitali; il valore di tale campo assume il significato illustrato in Figura 14.36.
L'intestazione è immediatamente seguita dal blocco contenente i campioni audio digitali.
Se un sotto-blocco di tipo 1 è presente da solo, i campioni audio digitali che
esso contiene sono da intendersi in formato monofonico.
Se un sotto-blocco di tipo 1 è preceduto da un sotto-blocco di tipo 8
(vedere più avanti), per conoscere gli attributi dei campioni audio digitali bisogna
fare riferimento solo all'intestazione del sotto-blocco di tipo 8; le informazioni
contenute nell'intestazione del sotto-blocco di tipo 1 devono essere ignorate, ad
eccezione del campo BlockLen che ci permette di ricavare il numero di campioni da
eseguire.
Si tratta di un sotto-blocco che rappresenta la continuazione di un altro sotto-blocco
contenente campioni audio digitali; la sua intestazione occupa 4 byte e assume
l'aspetto mostrato in Figura 14.37.
BlockID vale sempre 2.
BlockLen contiene la lunghezza in byte del sotto-blocco; da tale lunghezza
risultano esclusi i 4 byte occupati dai campi BlockID e BlockLen.
Questo tipo di sotto-blocco viene usato quando la lunghezza di quello precedente non
è rappresentabile con i soli 3 byte (24 bit) del campo BlockLen.
Si tratta di un sotto-blocco che specifica la durata di una eventuale pausa di tempo
prima che inizi l'esecuzione dei campioni audio digitali contenuti nel sotto-blocco
successivo; la sua intestazione occupa 7 byte e assume l'aspetto mostrato in
Figura 14.38.
BlockID vale sempre 3.
BlockLen vale sempre 3 e contiene la lunghezza in byte del sotto-blocco;
da tale lunghezza risultano esclusi i 4 byte occupati dai campi BlockID
e BlockLen. Si tratta quindi dei 3 byte occupati dai due campi
PausePeriod e TimeConst.
PausePeriod contiene la durata della pausa in una unità di misura data
dall'inverso della frequenza di campionamento (periodo); ad esempio, se l'audio digitale
è stato campionato a 20000 Hz, l'unità di misura è pari a 1/20000 secondi.
TimeConst contiene il byte più significativo del risultato che scaturisce dal
calcolo della costante di tempo (vedere la sezione 14.5.7).
Si tratta di un sotto-blocco che specifica un marker (una sorta di segnalibro)
posto tra i campioni audio digitali da eseguire; la sua intestazione occupa 6
byte e assume l'aspetto mostrato in Figura 14.39.
BlockID vale sempre 4.
BlockLen vale sempre 2 e contiene la lunghezza in byte del sotto-blocco;
da tale lunghezza risultano esclusi i 4 byte occupati dai campi BlockID
e BlockLen. Si tratta quindi dei 2 byte occupati dal campo Marker.
Marker contiene un valore compreso tra 1 e FFFEh il cui scopo è
quello di sincronizzare l'esecuzione dei campioni audio digitali con determinati eventi;
la SB16 aggiorna continuamente una propria status word (Figura 14.18) con il
valore letto da questo campo. Un programma può utilizzare tale valore, ad esempio, per
sincronizzare l'esecuzione di un file VOC con una animazione grafica.
Si tratta di un sotto-blocco che permette di inserire una stringa C (zero
finale) all'interno di un file VOC; lo scopo di tale stringa è in genere quello
di fornire informazioni relative al titolo del brano, all'autore, etc.
L'intestazione del sotto-blocco occupa un numero di byte legato alla lunghezza della
stringa e assume l'aspetto mostrato in Figura 14.40.
BlockID vale sempre 5.
BlockLen contiene la lunghezza in byte del sotto-blocco; da tale lunghezza
risultano esclusi i 4 byte occupati dai campi BlockID e BlockLen.
Si tratta in pratica della lunghezza della stringa C, compreso lo 0
finale.
cString contiene una stringa C, terminata quindi da uno 0; la
stringa deve contenere esclusivamente caratteri
ASCII.
Si tratta di un sotto-blocco che rappresenta l'inizio di un loop; tutti i dati audio
digitali compresi tra questo sotto-blocco e il successivo sotto-blocco di tipo 7,
verranno ripetuti un determinato numero di volte.
L'intestazione del sotto-blocco occupa 6 byte e assume l'aspetto mostrato in
Figura 14.41.
BlockID vale sempre 6.
BlockLen contiene la lunghezza in byte del sotto-blocco; da tale lunghezza
risultano esclusi i 4 byte occupati dai campi BlockID e BlockLen.
Si tratta in pratica della lunghezza del campo RepTimes (da 2 byte), per
cui il suo contenuto è sempre 2.
RepTimes contiene il numero di ripetizioni ed è un valore compreso tra 1
e FFFEh; si presti attenzione al fatto che un valore FFFFh per questo
campo produce un loop infinito!.
Si tratta di un sotto-blocco che rappresenta la fine di un loop; chiaramente, questo
tipo di sotto-blocco lavora in coppia con un sotto-blocco di tipo 6.
L'intestazione del sotto-blocco occupa 4 byte e assume l'aspetto mostrato in
Figura 14.42.
BlockID vale sempre 7.
BlockLen contiene la lunghezza in byte del sotto-blocco; da tale lunghezza
risultano esclusi i 4 byte occupati dai campi BlockID e BlockLen.
Non essendoci ulteriori campi nell'intestazione, il suo contenuto è sempre 0.
Si tratta di un sotto-blocco importantissimo in quanto destinato a contenere
campioni audio digitali stereofonici o di tipo High Speed; deve sempre
precedere un sotto-blocco di tipo 1 la cui intestazione deve essere
ignorata, ad eccezione del campo BlockLen!.
L'intestazione del sotto-blocco occupa 8 byte e assume l'aspetto mostrato in
Figura 14.43.
BlockID vale sempre 8.
BlockLen contiene la lunghezza in byte del sotto-blocco; da tale lunghezza
risultano esclusi i 4 byte occupati dai campi BlockID e BlockLen.
Si tratta in pratica della lunghezza dei successivi campi TimeConst,
PackMethod e VoiceMode, per cui il suo contenuto è sempre 4.
TimeConst contiene questa volta l'intero risultato a 16 bit che
scaturisce dal calcolo della costante di tempo (vedere la sezione 14.5.7).
PackMethod contiene il metodo di compressione usato per i campioni digitali;
il valore di tale campo assume lo stesso significato già illustrato in Figura 14.36.
VoiceMode indica se i campioni audio digitali sono in formato monofonico o
stereofonico; il valore 0 indica "mono", mentre il valore 1 indica
"stereo".
L'intestazione di un sotto-blocco di tipo 8 è seguita immediatamente dalla
intestazione di un sotto-blocco di tipo 1. Come è stato già spiegato, in un
caso del genere gli attributi specificati dall'intestazione del successivo
sotto-blocco di tipo 1 devono essere ignorati, ad eccezione del campo
BlockLen che ci permette di calcolare la lunghezza del blocco contenente i
campioni audio digitali.
Si tratta di un sotto-blocco contenente campioni audio digitali, destinato a rimpiazzare
i sotto-blocchi di tipo 1 e 8 nella nuova versione 1.20 o superiore
dei file VOC; la sua intestazione occupa 16 byte e assume l'aspetto
mostrato in Figura 14.44.
BlockID vale sempre 9.
BlockLen contiene la lunghezza in byte del sotto-blocco; da tale lunghezza
risultano esclusi i 4 byte occupati dai campi BlockID e BlockLen.
Si tratta in pratica della lunghezza del blocco contenente i dati audio digitali, più
i 12 byte dei campi successivi.
SamplesSec contiene la frequenza di campionamento del file audio.
SampleBits contiene il numero di bit impiegato per rappresentare ogni campione
compresso (nel caso in cui l'audio sia memorizzato in formato compresso).
Channels contiene il numero di canali audio; il valore 1 indica "mono",
mentre il valore 2 indica "stereo".
Format contiene il formato dei campioni audio digitali; i valori da specificare
in questo campo sono quelli illustrati in Figura 14.45.
Reserved è un campo da 4 byte il cui contenuto è riservato per usi futuri;
lo scopo di tale campo è anche quello di portare la dimensione dell'intestazione a
16 byte, garantendo così una più semplice gestione dell'intestazione stessa da
parte dei programmi.
14.8 Microsoft/IBM Waveform Audio File (WAV) format
Il formato waveform (forma d'onda) per i file audio è stato sviluppato da
Microsoft e IBM ed è destinato esplicitamente alle piattaforme
hardware basate su CPU 80x86 e compatibili; di conseguenza, i dati al suo
interno seguono la convenzione Little Endian (bit disposti in ordine di
peso crescente).
I file audio waveform recano, generalmente, l'estensione WAV; nella
loro forma più semplice, tali file contengono campioni audio in formato PCM
lineare non compresso (segnale audio analogico convertito in digitale e memorizzato
così com'è, senza alcun sistema di compressione).
Il successo notevole ottenuto dal formato WAV è legato principalmente proprio
alla possibilità di memorizzare i campioni audio in formato non compresso; ciò
permette all'utenza professionale di elaborare al computer, in modo semplicissimo,
flussi audio della massima qualità possibile (formato audio senza perdita di
informazione o lossless).
Lo svantaggio del formato non compresso è dato chiaramente dal fatto che i file
WAV possono raggiungere dimensioni gigantesche, poco adatte allo scambio di
dati via Internet; proprio per questo motivo, sono stati introdotti i file
WAV con supporto per la compressione dei campioni audio.
I file WAV sono una variante del formato RIFF, dove le informazioni
vengono memorizzate in blocchi (chunks); ogni blocco è costituito da tre
parti:
- chunk identifier (campo o identificatore del blocco)
- chunk length (dimensione del blocco)
- chunk data (contenuto del blocco)
Analizziamo la struttura interna di un file WAV contenente campioni audio
in formato PCM lineare non compresso; nella forma più semplice che ci
interessa a scopo didattico, il file contiene un unico blocco di campioni audio.
14.8.1 La sezione "Header" di un File WAV
La Figura 14.46 illustra la struttura dell'header di un file WAV, che si trova
all'offset 0 del file stesso.
Il campo ChunkSize contiene la dimensione totale del file WAV meno
8 byte; tale valore, infatti, esclude i 4+4 byte occupati dal campo
ChunkID e dallo stesso campo ChunkSize.
14.8.2 Le sezioni "fmt " e "data" di un File WAV
Se il campo Format contiene la stringa WAVE, l'header di Figura 14.46
risulta immediatamente seguito dalle due sezioni "fmt " e "data".
La Figura 14.47 illustra la struttura della sezione fmt " di un file WAV,
che descrive il formato dei campioni audio; tale sezione segue immediatamente quella
di Figura 14.46 e si trova quindi all'offset 12 (000Ch) del file stesso.
Il campo Subchunk1ID contiene la stringa "fmt " con uno spazio finale
(ASCII 20h) in modo da
occupare 4 byte.
Il campo Subchunk1Size contiene la dimensione in byte della sezione
"fmt " a partire dal successivo offset 0014h; se i campioni audio
sono in formato PCM non compresso, tale campo vale sempre 16. Il
campo Subchunk1Size diventa importante quando i campioni audio sono in
formato compresso (non PCM); in tal caso, infatti, la sezione "fmt "
contiene informazioni extra dopo l'offset 0022h.
Se il campo AudioFormat ha un valore diverso da 1, significa che i
campioni audio sono in formato compresso.
Il campo ByteRate è dato da:
ByteRate = SampleRate * NumChannels * (BitsPerSample / 8)
Il campo BlockAlign rappresenta la dimensione totale in byte di ogni campione
audio mono o stereo; ad esempio, per campioni audio in formato PCM 8-bit
stereo, ogni campione è formato da due parti (canale destro e sinistro) ciascuna
delle quali occupa 8 bit (1 byte), per cui, BlockAlign=2. Il
campo BlockAlign è dato da:
BlockAlign = NumChannels * (BitsPerSample / 8)
A differenza di BlockAlign, il campo BitsPerSample rappresenta la
dimensione in bit della singola parte che forma ogni campione audio mono o stereo;
ad esempio, per campioni audio in formato PCM 8-bit stereo, ogni campione è
formato da due parti (canale destro e sinistro), ciascuna delle quali occupa 8
bit, per cui BlockAlign=8.
Per i file WAV in formato PCM non compresso, all'offset 0024h
(immediatamente successivo alla sezione "fmt "), è presente la sezione
"data" che contiene la dimensione in byte del blocco audio e i dati audio
stessi; la Figura 14.48 illustra la struttura di tale sezione.
Il campo Subchunk2Size occupa 4 byte (32 bit) per cui,
teoricamente, la dimensione massima del blocco audio è pari a
232=4 GiB; può capitare però di imbattersi in codificatori
di file WAV che limitano tale dimensione a 2 GiB.
Se conosciamo il numero totale di campioni audio presenti in un file WAV,
indicando con NumSamples tale numero, il campo Subchunk2Size è
dato da:
Subchunk2Size = NumSamples * NumChannels * (BitsPerSample / 8)
14.9 Esempi pratici
Programmare la SB16 in Direct Mode significa gestire un solo campione
audio alla volta; inoltre, come è stato spiegato in precedenza, i campioni devono
essere esclusivamente in formato PCM monofonico a 8 bit (quindi, nessuna
compressione).
Si potrebbe ritenere allora che, ad esempio, eseguendo un file audio in Direct
Mode, si ottengano risultati piuttosto grossolani; in effetti, se la frequenza di
campionamento è molto bassa, più che un suono si ottiene un ronzio, a causa delle
pause tra un campione e l'altro. Se, però, la frequenza di campionamento è abbastanza
alta, il risultato è più che soddisfacente e, anzi, non si nota alcuna differenza con
il DMA Mode; l'unico vero problema è rappresentato dall'enorme carico di lavoro
che viene riversato sulla CPU.
La situazione appena descritta è legata al fatto che, come era già stato anticipato nella
sezione 14.5.8, la tecnica più efficace per eseguire un file audio in Direct
Mode consiste nel riprogrammare il Timer 0 del PIT 1, in modo da farlo
lavorare ad una frequenza uguale a quella di campionamento del file stesso; ad esempio,
se il file audio è campionato a 44100 Hz, dobbiamo inizializzare il contatore del
timer con il valore:
N = 1193181 / 44100 = 27,0562585034
che possiamo approssimare a 27 (si ricordi che 1193181 è la frequenza
del segnale di clock che giunge ad ogni timer del PIT 1).
Il Timer 0 è inizializzato dal BIOS nel Modo 2 (rate generator)
e ciò significa che, ad ogni impulso del segnale di clock (1193181 impulsi al
secondo), il contatore viene decrementato di 1; quando il conteggio arriva a
zero, viene generata una IRQ0, mentre il contatore si porta nuovamente al
valore N e ricomincia il conto alla rovescia. La CPU riceve la IRQ
dal PIC e le associa una INT 08h; la relativa ISR provvede poi ad
eseguire il compito per cui è stata scritta.
Nel nostro caso, con il contatore inizializzato a 27, vengono generate
44100 richieste di interruzione ogni secondo; la ISR da noi installata,
ad ogni chiamata invia un nuovo campione audio al DSP.
Dalle considerazioni appena illustrate, risulta evidente l'inefficienza di questo
metodo; la CPU, infatti, si trova costretta a sobbarcarsi ben 44100
richieste di interruzione ogni secondo solo dal Timer 0 e in più, bisogna tenere
conto anche delle numerose IRQ provenienti da altre periferiche. Nonostante
ciò, si tratta di una tecnica con un notevole valore didattico in quanto permette di
programmare il DSP nel modo più semplice possibile; proprio per questo motivo,
l'esempio presentato nel seguito si riferisce alla esecuzione di un file VOC
tramite la riprogrammazione del Timer 0 del PIT 1.
14.9.1 Esecuzione di un file VOC in Direct Mode
Seguendo la stessa linea dei precedenti capitoli, anche in questo caso scriviamo innanzi
tutto una apposita libreria contenente una serie di procedure destinate alla gestione
della SB16; tale libreria si occupa anche dell'individuazione dell'environment
string BLASTER e della conseguente estrazione da essa dei relativi parametri.
Nella sezione
Librerie di supporto per il corso assembly dell’
Area Downloads di questo sito,
è presente una libreria, denominata SB16LIB, che può essere linkata
ai programmi destinati alla generazione di eseguibili in formato EXE; all'interno
del pacchetto sb16libexe.zip è presente la libreria vera e propria
SB16LIB.ASM, l'include file SB16LIBN.INC per il NASM e l'include file
SB16LIBM.INC per il MASM.
Per la creazione dell'object file di SB16LIB.ASM è necessario il NASM;
il comando da impartire è:
nasm -f obj sb16lib.asm
L'object file così ottenuto è perfettamente linkabile anche ai programmi
scritti con MASM.
La libreria SB16LIB è ben commentata e il lavoro svolto dalle varie procedure è
piuttosto semplice da capire; si noti che, per l'elaborazione della environment string,
viene fatto largo uso di istruzioni come CMPS, MOVS, SCAS, etc.
Per convertire i parametri (stringhe numeriche) in numeri, viene utilizzato il solito
metodo già illustrato nei precedenti capitoli; un carattere numerico, contenuto nel
registro AL sotto forma di codice
ASCII, si trasforma nella equivalente
cifra con la sottrazione:
sub al, '0'
Lavorando in esadecimale, tale cifra viene memorizzata nel nibble meno significativo
di una apposita variabile; se il parametro è formato da più cifre, la cifra appena
memorizzata deve essere spostata di un nibble (4 bit) a sinistra per fare
posto alla cifra successiva.
La Figura 14.49 illustra il listato del programma SB16TEST che usa la libreria
SB16LIB per suonare un file VOC attraverso il DSP della SB16.
Come si può notare, buona parte del programma si occupa del controllo degli errori;
il nucleo, invece, è formato solo da due piccole parti che sono: il loop principale
(main_music_loop) e la ISR (new_int08h).
Innanzi tutto, visto che il file VOC viene caricato (un blocco alla volta) in
un buffer di memoria, si provvede a restituire al DOS la memoria in eccesso
occupata dall'eseguibile SB16TEST.EXE (vedere il Capitolo 6 sulla memoria base
del computer).
Accedendo all'Environment Segment si verifica se è presente la stringa di
ambiente BLASTER; in caso affermativo si procede all'estrazione dei vari
parametri (è richiesto solo il base address).
Si procede poi al reset del DSP e si verifica se la sua versione è 4.xx;
come sappiamo, questo passo è necessario per sapere se abbiamo a che fare con una
SB16.
Il programma si aspetta che gli venga passato un file VOC come parametro dalla
linea di comando; ad esempio:
sb16test sound1.voc
Viene letta quindi l'intestazione (Header Block) del file per sapere se si
tratta di un VOC valido; in caso affermativo, viene letto il Block Type
del primo Data Block del file per sapere se i successivi campioni audio sono in
formato PCM monofonico a 8 bit (blocco di tipo 1). Si tenga
presente che il programma si aspetta di trovare il primo blocco di tipo 1 e,
eventualmente, esegue solo quello.
Tutte le informazioni relative all'Header Block e al Data Block vengono
mostrate sullo schermo. Si noti che la procedura sb16_testblocktype1 della
libreria SB16LIB provvede anche a convertire il campo blockLen da 3
a 4 byte in modo da renderne più semplice la gestione; la stessa procedura
calcola anche la frequenza di campionamento ricavandola dalla costante di tempo. Dalla
formula:
time_constant = 65536 - (256000000 / (channels * sampling_rate))
si ottiene facilmente:
sampling_rate = 256000000 / (channels * (65536 - time_constant))
Si ricordi che in un Data Block di tipo 1 la costante di tempo è
memorizzata in un campo da 1 byte che rappresenta il MSB; di conseguenza,
il LSB deve essere aggiunto dal programmatore e deve valere 00h.
Se il file VOC ha superato tutte le verifiche, si procede ad allocare un
buffer in RAM da 64 KiB e a riempirlo con il primo blocco di campioni
audio; ogni blocco è formato da FBLOCK_SIZE=32768 byte (valore che può essere
modificato a piacimento, anche se è preferibile un multiplo intero di 2 non
superiore a 65534).
Si noti che, posto EDX=0, EAX=blockLen (lunghezza blocco audio) e
EBX=FBLOCK_SIZE, la divisione:
div eax, ebx
ci dà il quoziente in EAX e il resto in EBX; ovviamente, il quoziente
in EAX rappresenta il numero di blocchi completi da FBLOCK_SIZE byte,
mentre il resto in EDX ci dà la dimensione dell'ultimo blocco da eseguire.
A questo punto non rimane che riprogrammare il Timer 0 del PIT 1 (con
la stessa frequenza di campionamento del file VOC) e installare la nuova
ISR per la INT 08h.
La ISR ad ogni chiamata invia un campione audio al DSP e incrementa di
1 un indice blockIndex interno al buffer di memoria; il loop principale
del programma controlla blockIndex per sapere se è stato raggiunto il limite
superiore blockSize. Il limite superiore è FBLOCK_SIZE per i blocchi
completi e lastblockSize per l'ultimo blocco; quando l'indice raggiunge il
valore blockSize, viene azzerato e si provvede a caricare in memoria il
prossimo blocco da eseguire. In una situazione del genere, il numero di blocchi
numBlocks viene decrementato di 1; il programma termina quando
numBlocks diventa 0, oppure quando l'utente preme un tasto qualunque
(uscita prematura dal loop).
Si ricordi che, leggendo N byte da un file, si provoca automaticamente
l'incremento di N byte della posizione del puntatore al file stesso; in questo
modo si possono effettuare più letture consecutive e contigue senza preoccuparsi di
aggiornare ogni volta la posizione del puntatore.
Sia il loop principale, sia la ISR, sono due aree critiche del programma;
queste due porzioni di codice, infatti, vengono eseguite un numero enorme di volte
ogni secondo. Appare chiaro quindi che le istruzioni presenti al loro interno devono
garantire la massima velocità di esecuzione possibile; la ISR, ad esempio,
provvede anche a visualizzare una progress-bar disegnandola direttamente nella memoria
video proprio per evitare rallentamenti.
L'incremento della progress-bar viene calcolato con la semplice proporzione:
blockLen : x = 80 : 1 ⟹ x = blockLen / 80
In pratica, l'incremento incognito x sta alla lunghezza totale blockLen
del blocco audio come l'incremento di una cella dello schermo sta a 80 celle
(larghezza dello schermo); nel caso, ad esempio, di un blocco audio da 12540236
byte, si ottiene x=156752 (divisione intera) per cui la progress-bar verrà
incrementata di una cella dalla ISR ogni 156752 chiamate!
La Figura 14.50 mostra il programma SB16TEST.EXE in esecuzione sull'emulatore
DOSBox.
14.10 Creazione di file VOC in formato PCM monofonico a 8 bit
I file VOC ormai fanno parte di un'epoca passata, per cui sono molto rari da
trovare pure su Internet; fortunatamente, esiste la possibilità di crearli
in modo estremamente semplice grazie ad appositi programmi di conversione.
Prima di tutto, procuriamoci il programma gratuito sox, disponibile per
Windows, Linux e MacOSX, scaricandolo dal sito ufficiale:
sox Home Page
Se abbiamo a disposizione un file audio sorgente in un formato supportato da
sox, possiamo procedere subito alla sua conversione; ad esempio, per un file
sound1.ogg (formato audio Ogg Vorbis), il comando da impartire da un
terminale (prompt di MS-DOS di Windows) è:
sox sound1.ogg -c 1 -b 8 sound1.voc
Il parametro -c 1 indica un solo canale audio (mono), mentre -b 8
indica che i campioni audio devono essere a 8 bit; generalmente, con tali
parametri sox produce un file VOC con un unico Data Block di
tipo 1.
Se il formato audio sorgente non è supportato da sox, possiamo prima procedere
alla sua conversione in Ogg Vorbis attraverso altri programmi, come il celebre
VLC; vediamo, ad esempio, come ottenere un file VOC a partire da un
file MOD (tipo di file audio molto usato sui computer Amiga).
Procediamo innanzi tutto al download di un file MOD di esempio:
relaxing.mod
Apriamo ora VLC e selezioniamo il menu "Media" - "Converti/Salva..."; nella
finestra che si apre premiamo il pulsante "Aggiungi..." e carichiamo il file sorgente
relaxing.mod. Nel pulsante "Converti/Salva" in basso, selezioniamo "Converti"
e poi, come "Profilo", Audio-Vorbis (OGG); diamo un nome al "File di
destinazione" (ad esempio, relaxing.ogg) e premiamo il pulsante "Avvia" per
dare inizio alla conversione. Infine, usciamo da VLC e creiamo il file
VOC con:
sox relaxing.ogg -c 1 -b 8 relaxing.voc
Vediamo un altro esempio che illustra come ottenere un file VOC a partire dalla
traccia audio di un video su YouTube.
Prima di tutto, procuriamoci il programma gratuito youtube-dl, disponibile per
Windows, Linux e MacOSX, scaricandolo dal sito ufficiale:
youtube-dl Home Page
Con un browser andiamo su YouTube, scegliamo un video e clicchiamoci sopra
con il pulsante destro del mouse selezionando Copia URL video; supponendo
che l'URL sia, ad esempio, https://youtu.be/HEXWRTEbj1I, apriamo un
terminale (prompt di MS-DOS di Windows) e impartiamo il comando:
youtube-dl -F https://youtu.be/HEXWRTEbj1I
Il parametro -F (in maiuscolo) permette di ottenere l'elenco completo dei
formati multimediali disponibili per quel video; in particolare, notiamo la presenza
del formato "audio only":
140 m4a audio only DASH audio 128k, m4a_dash container, mp4a.40.2@128k (44100Hz), 3.65MiB
Si tratta della traccia audio del video, in formato MPEG4 Audio; per
scaricarla con youtube-dl, dobbiamo impartire il comando:
youtube-dl -f 140 https://youtu.be/HEXWRTEbj1I
(notare che stavolta la 'f' è minuscola).
Una volta scaricata la traccia audio, convertiamola in formato Ogg Vorbis
(audio) con VLC; a questo punto, possiamo procedere alla conversione finale
in formato VOC con sox come è stato già illustrato in precedenza.
Il nome del file VOC deve essere lungo al massimo 8 caratteri, più
l'estensione .VOC, come richiesto dal DOS.
Bibliografia
Creative Technology - Sound Blaster Series - Hardware Programming Guide
(disponibile nella sezione
Documentazione tecnica di supporto al corso assembly dell’
Area Downloads di questo sito - SoundBlaster.pdf)