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 ; ad ogni giro completo del punto P in Figura 14.1, pari a 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 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. 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: 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: 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: 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: 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: 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: 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: 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Valgono tutte le considerazioni già illustrate per il comando 16h. Valgono tutte le considerazioni già illustrate per il comando 17h. Valgono tutte le considerazioni già illustrate per il comando 16h. Valgono tutte le considerazioni già illustrate per il comando 17h. Valgono tutte le considerazioni già illustrate per il comando 1Fh. Valgono tutte le considerazioni già illustrate per il comando 1Fh. 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. 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: 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: 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. 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). 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). 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. 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 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. Questo comando permette al DSP di uscire dalla modalità Auto Initialize. Questo comando permette al DSP di uscire dalla modalità Auto Initialize. 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: 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: 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)