PARTE 8: MODULACIÓN POR ANCHO DE PULSOS

A medida que continuamos buscando una mayor variedad en el repertorio sonoro del Atari, nos encontraremos lidiando con formas de onda cada vez más complejas. El modo de funcionamiento normal del Atari nos proporciona probablemente la forma de onda más simple, una onda cuadrada. En las últimas columnas hemos jugado con el diseño de formas de onda personalizadas, como ondas sinusoidales y de rampa. Ahora vamos a dar un paso más hacia las junglas del wacka-wacka: la modulación dinámica de formas de onda.


La característica de la forma de onda que se modifica más fácilmente es probablemente el ancho del pulso. Tomemos, por ejemplo, una forma de onda de pulso simple. El patrón podría verse así:


10000000

donde un 1 significa que el altavoz está empujado a cierta distancia y un 0 significa que el altavoz está en reposo. Este tipo de forma produce un tono muy fino, parecido a una nariz, porque el altavoz está la mayor parte del tiempo en reposo y sólo ocasionalmente sale. Para alargar el ancho del pulso, simplemente alargaríamos el tiempo en que se expulsó el altavoz:


11110000

Esta forma suena más completa porque el altavoz pasa la misma cantidad de tiempo en cada posición. Cuando hablamos de ancho de pulso, comúnmente nos referimos al porcentaje de la relación entre entrada y salida. Por lo tanto, el patrón de forma de onda que se acaba de describir se denomina pulso de ciclo de trabajo del 50 por ciento. Cincuenta por ciento es el pulso más amplio que podemos obtener, porque un ciclo de trabajo del 75 por ciento (11111100) suena igual que un ciclo de trabajo del 25 por ciento (11000000).


Si esto no tiene ningún sentido, tenga en cuenta que el factor importante en el sonido no es la amplitud en sí sino el cambio de amplitud. Un altavoz en la posición máxima no hace más ruido que un altavoz en reposo: es el movimiento del altavoz lo que crea ruido, no su posición. Ahora regrese y observe las ondas del 75 por ciento y del 25 por ciento y observe que si invierte cada una de las posiciones (convierte 1 en 0 y viceversa) obtiene el mismo patrón. Nuestros oídos no podrán detectar ninguna diferencia.


«Ah», dirá, "¿Pero al ampliar el pulso no cambiamos el tono?" ¡No es tan así! En todos los casos mostrados hasta ahora, el patrón requiere ocho pasos para repetirse: por tanto, la frecuencia seguirá siendo la misma.


Dado que cambiar el ancho del pulso afecta la calidad subjetiva del tono, ahora podemos predecir qué sucedería si, mientras reproducimos la forma de onda, estiramos simultáneamente su forma. Si la forma de onda comenzara como un pulso estrecho (como el primer ejemplo) y termina como una onda cuadrada (el segundo), escucharemos que el tono oscila desde un sonido fino y aflautado hasta un tono con mucho cuerpo. Este es un efecto interesante y útil que recuerda vagamente al sonido de esas bobinas de Tesla de las primeras películas de Frankenstein. (¿Recuerda el pequeño y divertido dispositivo que hacía que un arco eléctrico de alto voltaje subiera y bajara?) Es más, como no estamos haciendo nada extraño con el tono, la modulación de ancho de pulso también es musicalmente útil.


Y, en caso de que no lo haya adivinado, eso es lo que vamos a hacer.


El Listado En Lenguage Ensamblador. El código de máquina del listado 1 configura la misma interrupción controlable por frecuencia que comentamos la última vez. La única diferencia es na inicialización adicional y, por supuesto, lo que sucede dentro de la rutina de interrupción. Lo más seguro que se puede hacer aquí es hacer que el procedimiento de configuración inserte 255 en el registro de frecuencia de audio para que cuando se habilite la interrupción la máquina no se bloquee debido a una frecuencia demasiado alta.


Esto es seguro, pero significa que no tiene elección sobre el tono que suena cuando se llama por primera vez a la rutina establecida. Para corregir esto, se agregó un control de volumen crudo en la ubicación 203 (decimal), y esta ubicación se inicializa en 0 mediante el procedimiento de configuración. Introducir un 15 en este registro hará que la forma de onda se reproduzca a su volumen más alto. Tenga en cuenta que introducir un 0 en este registro detiene el tono pero no detiene la interrupción, ¡y no reemplaza el procedimiento de borrado de la interrupción!


La rutina de sonido utiliza manipulación de bits para obtener el efecto de modulación por ancho de pulsos. A continuación se ofrece una explicación del código Ensamblador, pero también se debe estudiar el listado. Si es un programador ensamblador principiante, preste mucha atención a la forma en que se utiliza el registro de acarreo.


STEP realiza un seguimiento de qué parte de la forma de onda está reproduciendo actualmente la rutina y contiene solo un bit que se mueve perpetuamente hacia la derecha. A este puntero se le aplica un AND con la variable de forma de onda WIDTH para derivar el valor que se enviará al altavoz. Para obtener la onda de rampa, simplemente reemplazamos la amplitud arbitraria «activada» discutida anteriormente con una derivada de la posición del paso de onda de la rutina (STEP nuevamente). Esto significa que la primera vez que se ejecute la rutina, la amplitud de encendido será 8, luego 4, y así sucesivamente. Esto es todo lo que necesitamos para reproducir la forma de onda; el resto se encarga de la modulación.


Luego actualizamos un contador para controlar la tasa de modulación. Cuando el contador llega a 0, lo primero que hacemos es comprobar si la tasa de modulación ha alcanzado su valor máximo, 80$; si no es así, el ritmo se ralentiza. Esta parte se añadió para hacer la modulación más interesante que una velocidad de barrido constante. Cuando se introduce un 1 en MRATE, el ancho del pulso cambia a un ritmo vertiginoso y eventualmente disminuye hasta convertirse en un barrido lento y constante. El ejemplo en BASIC utiliza esto para proporcionar cierta cantidad de expresión. Tenga en cuenta también que si presiona MRATE con un número mayor que $80, la velocidad de barrido simplemente se establece en esa tasa. La demostración BASIC usa esto en la línea 55 para iniciar la música con un cambio muy lento.


Lo único que queda por hacer es cambiar el ancho del pulso. Esto se hace examinando el tercer bit desde la izquierda del byte WIDTH e invirtiendo su estado, luego rotando todos los bits hacia la izquierda para que el nuevo bit entre desde el costado. Esto nos da un desfile de bits: pasan los primeros 3 unos, luego 3 ceros, luego 3 unos nuevamente. Se eligió el ancho máximo de 3 bits para garantizar que siempre hubiera un cambio de estado en algún lugar de la forma de onda, evitando así espacios de silencio en el tono.


Finalmente, si va a reproducir música con modulación por ancho de pulsos, necesita saber qué valores producen una escala igualmente templada. El Listado 2 le permitirá imprimir un cuadro de estos valores. La fórmula para determinar la frecuencia de la interrupción es la misma que la discutida anteriormente, pero tenga en cuenta que la velocidad de llamada de la rutina de sonido y el tono producido por esa rutina son dos cosas diferentes. En este caso, la rutina debe llamarse 6 veces antes de que se complete la forma de onda (3 ceros y luego 3 unos), por lo que la frecuencia de salida se divide por 6.


El Listado 3 ofrece un uso bastante obvio de las rutinas. La única característica inusual es el uso de especificaciones de tono negativo: cada vez que se lee un tono negativo de la tabla, esto se interpreta como una señal para ajustar la velocidad de modulación, lo que a su vez provoca una explosión de actividad tonal. Los valores de frecuencia también se utilizan para determinar el color y la posición de las líneas dibujadas en pantalla. Una vez más, el ejemplo BASIC pretende ser sólo un trampolín, para animarle a utilizar la sustancia real en el listado Ensamblador y experimentar con él.


La próxima vez, cambiaremos un poco el ritmo y echaremos un vistazo a una muy buena alternativa a la codificación en BASIC o ensamblador: el lenguaje C.


TITLE 'PWM 1.0'
;
;Pulse Width Modulation Demo
;*Assembler Listing*
;
;OS equates
IRQEN = $D20E ;IRQ enable
POKMSK = $10 ;shadow for IRQEN
AUDF = $D200 ;frequency register
AUDC = $0201 ;volume register
VTIMR1 = $210 ;timer vector
RANDOM = $D20A ;random # register
;
;Zero page variables
VOLUME = $CB ;master volume
MRATE = VOLUME + 1 ;modulation rate
MODC = MRATE + 1 ;modulation count
WIDTH = MODC + 1 ;pulse width
STEP = WIDTH + 1 ;wave step count
ORG $600
;
;Refer to the July '83 Atari Sound
;column for a description of the method
;used to obtain an interrupt-driven
;sound routine.
;
;Enter here to start sound
SET SEI
LDX #$FF ;ensure no lockup
STX AUDF ;upon start
INX
STX VOLUME ;zero volume
INX
STX STEP ;initialize step
STX WIDTH ;and pulse width
LDA #LOW PWM
STA VTIMR1
LDA #HIGH PWM
STA VTIMR1 + 1
LDA POKMSK
ORA #1
STA POKMSK
STA IRQEN
BNE SET2
;Enter here to kill sound
CLEAR    SEI
LDA POKMSK
AND #$FE
STA POKMSK
STA IRQEN
SET2 CLI
PLA
RTS
;
;Here's the new pulse width modulation
;sound routine:
;
PWM LDA STEP ;get position count
LSR A ;update it
BCC NWRAP ;if bit falls off,
LDA #8 ;replenish it
WRAP STA STEP ;save updated pot
AND WIDTH ;examine waveform
AND VOLUME ;clip bits for vol
ORA #$10 ;set forced output
STA AUDC ;poke speaker
DEC MODC ;check if time
BNE NOMOD ;to modulate
LDA MRATE ;recharge count
STA MODC ;with mod rate
BMI NACCL ;if > $7F, skip
INC MRATE ;slow down mod rate
NACCL CLC ;change pulse width
LDA WIDTH ;examine d2
AND #4 ;… if set.
BNE CCLR ;then carry clear
SEC ;else set carry
CCLR ROL WIDTH ;rotate carry in
NOMOD PLA ;ta-da
RTI
;
END	

Listado 1.


5 REM THIS PROGRAM WILL PRINT
10 REM ALL THE EQUALLY TEMPERED
15 REM NOTES AVAILABLE FROM THE
20 REM PWM ASSEMBLER DEMO
25 DIM N$(25)
30 ? "TURN ON PRINTER, PRESS RETURN"
35 INPUT N$
40 LPRINT "NOTE","VALUE","FREQUENCY"
45 LPRINT 
50 REM NOTE PLACEMENT OF SPACES
55 N$="C C#D D#E F F#G G#A A#B "
60 FOUT=8.1759375:SKEY=1
65 CLOCK=63921
70 AUDF=INT((CLOCK/(FOUT*12))-0.5)
75 IF AUDF>255 THEN 90
80 IF AUDF<23 THEN 110
85 LPRINT N$(SKEY,SKEY+1),AUDF,INT(FOUT*100)/100
90 SKEY=SKEY+2
95 IF SKEY=25 THEN SKEY=1
100 FOUT=FOUT*1.05946309
105 GOTO 70
110 END	

Listado 2.


5 REM ****************
10 REM *   PWM DEMO   *
15 REM *              *
20 REM ****************
25 REM 
30 GOSUB 235
35 REM START UP INTERRUPT
40 Q=USR(ION)
45 RESTORE 
50 A=243:COL=15
55 POKE PWM,255
60 POKE AUDF,ABS(A):POKE VOL,15
65 IF A<0 THEN POKE PWM,20:COL=15
70 SETCOLOR 2,COL,0:IF COL>0 THEN COL=COL-3
75 X=ABS(A)/1.5:Y=Y+8-COL/2
80 IF Y>191 THEN Y=0
85 PLOT 0,0:DRAWTO X,Y:DRAWTO 160,96:DRAWTO 319-X,Y:DRAWTO 319,0
90 PLOT 0,191:DRAWTO X,191-Y:DRAWTO 160,96:DRAWTO 319-X,191-Y:DRAWTO 319,191
95 READ A
100 IF A<>0 THEN 60
105 REM NOTE TABLE
110 DATA 204,172,-144,102,85,72
115 DATA 60,-45,42,-35,37,45,47
120 DATA -33,35,42,45,26,31,-37,45
125 DATA -53,31,37,45,57,57,-42,67
130 DATA 57,67,-85,136,-114,136
135 DATA 172,172,172,172,172,172
140 DATA 172,172,0
145 REM TURN OFF INTERRUPT
150 Q=USR(IOFF)
155 GOTO 155
160 REM MACHINE CODE FOLLOWS
165 DATA 120,162,255,142,0,210,232
170 DATA 134,203,232,134,207,134
175 DATA 206,169,48,141,16,2,169,6
180 DATA 141,17,2,165,16,9,1,133
185 DATA 16,141,14,210,208,10,120
190 DATA 165,16,41,254,133,16,141
195 DATA 14,210,88,104,96,165,207
200 DATA 74,144,2,169,8,133,207,37
205 DATA 206,37,203,9,16,141,1,210
210 DATA 198,205,208,18,165,204,133
215 DATA 205,48,2,230,204,24,165
220 DATA 206,41,4,208,1,56,38,206
225 DATA 104,64
230 DATA -1
235 REM POKE IN MACHINE CODE
240 GRAPHICS 24:COLOR 1
245 SETCOLOR 1,0,13
250 RESTORE 160
255 FOR L=1536 TO 1625
260 READ A
265 POKE L,A
270 S=S+A
275 SETCOLOR 2,0,14-INT(S/736)
280 NEXT L
285 READ A
290 IF A<>-1 OR S<>10309 THEN ? "ERROR IN MACHINE CODE":STOP 
295 REM MACHINE CODE ENTRY POINTS
300 ION=1536:IOFF=1571
305 REM SPECIAL REGISTER EQUATES
310 VOL=203:PWM=204:AUDF=53760
315 RETURN	

Listado 3.


Leer la siguiente y última columna sin título.



Publicado en revista Softline Volumen 3 de Septiembre y Octubre del 1983, páginas 27 y 28.