Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Facultad de Ingenierı́a
Carrera de Ingenierı́a en Electrónica
Instrumento virtual
2010
Agradecimientos
Agradecemos enormemente a todas aquellas personas que hicieron posible que llegáramos
a este momento. Agradecemos a los que aportaron a la causa y a los que brindaron su
apoyo emocional para poder sobrellevar esta difı́cil tarea a lo largo de un año. Agradece-
mos a la gente de la Universidad ORT: personal, docentes, tutor y a los compañeros de
la sala de proyectos. A nuestros amigos, y especialmente a nuestras familias. Sin ellos,
hubiera resultado un camino mucho más difı́cil de recorrer. A todos ellos, gracias.
2
Abstract
3
Índice
1 Introducción 14
1.1.1 MIDI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2 Conceptos avanzados 21
4
2.3.3 Estructura de una DLL de 32 bits . . . . . . . . . . . . . . . . . . . 27
3.1 Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.4 Conclusiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
6.1 Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
6.5.1 Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
5
6.5.2 Explicación del método para obtener la nota . . . . . . . . . . . . . 45
6.7 Limitaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
8 Prueba previa 50
8.5.3 Observación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
9.2.4 Presentación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
6
11 Instrucciones de uso del sintetizador 63
12.2 Entradas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
12.2.1 MIDI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
12.2.2 RS-232 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
12.2.3 Efectos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
12.3.1 I 2 C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
7
14 Explicación del código del Maestro 77
14.3 Interrupción PC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
8
15.7 Funcionamiento del método main . . . . . . . . . . . . . . . . . . . . . . . 92
9
18.2 Programa “graficaFacil” . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
10
21.2.1 Mejora en el sistema de generación automática de notas . . . . . . . 122
23 Conclusiones 126
Bibliografı́a 127
Anexos 131
11
Clase ’I2C.c’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
Instroducción’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
12
Clase ’pideEnteros.vb’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
13
1 Introducción
1.1.1 MIDI
1
La sigla MIDI es una abreviatura de Musical Instrument Digital Interface (instrumento
musical de interface digital). Es un protocolo de comunicación que surgió en 1982 como
un acuerdo entre distintos fabricantes de instrumentos musicales electrónicos. Un archivo
MIDI no contiene datos de audio muestreado como lo hace un archivo .mp3 o .wav, si no
que contiene una serie de instrucciones para indicarle al reproductor como reproducir el
sonido. Estas instrucciones se llaman mensajes MIDI, indican que sonido hay que utilizar,
que notas hay que tocar, la intensidad, etc.
Esta tecnologı́a fue desarrollada por Steinberg una compañı́a alemana dedicada al desar-
rollo de software y equipos relacionados al mundo de la música. El objetivo de los VSTi
1
Wikipedia en Español: MIDI
14
es traducir las señales MIDI a sonidos. Un instrumento VSTi en el caso de Windows es un
archivo DLL, se profundizará en este tipo de archivos más adelante. Para poder usar un
instrumento VSTi es necesario una aplicación que soporte esta tecnologı́a, esta aplicación
es llamada “VST Host”. El instrumento VSTi le indica al VST Host como reproducir las
instrucciones MIDI que este recibe.
Un instrumento VSTi puede ser desde un archivo que haga sonar un instrumento
MIDI como una guitarra profesional hasta uno en el cual le corresponde a cada nota del
instrumento MIDI el sonido de un animal.
Para darle mayor control al usuario sobre la salida de audio, la mayorı́a de los instru-
mentos VSTi tienen la posibilidad de que el usuario cambie algunos parámetros de ellos.
Muchos instrumentos VSTi tienen imágenes asociadas al instrumento que representan,
estas imágenes a la vez contienen dibujos de controles rotatorios y otros tipos de controles
para que el usuario pueda modificar el valor de alguno de los parámetros de los sonidos
generados por el instrumento VSTi. La idea detrás de esto es de proveerle al usuario la
misma libertad que tendrı́a con un instrumento real para ajustar algunos parámetros del
sonido.
Los instrumentos VSTi por lo general también cuentan con programas de repro-
ducción, cada programa es una modo distinto de funcionamiento del VSTi, por ejemplo
un instrumento VSTi que simula una guitarra puede tener los programas “Metallica,
Disturbed, Nirvana, etc”.
Los efectos VST son tecnologı́as muy similares a los instrumentos VSTi, pero su objetivo
es aplicarle efectos a sonidos ya existentes, no sólo a sonidos generados por instrumentos
VSTi si no que también a sonidos .wav, .mp3, etc. Los archivos VST por lo general son
programados en lenguaje “C” y también exportados en archivos DLL.
15
b) Un instrumento con salida USB que transmita los datos en formato MIDI y un
PC con entrada USB. En este caso se conecta directamente el instrumento al PC.
Se abre el VST Host, se abren los instrumentos VSTi que se deseen asociar a cada entrada
MIDI y se los asocia.
Para poder escuchar los sonidos generados por instrumento MIDI conectado al PC en el
programa VSTHost v 1.45 se deben seguir los siguientes pasos:
1) Abrir el programa.
2) Seleccionar las entradas y salidas MIDI, para eso en el menú “devices” entrar a la
opción “MIDI” luego seleccionar la entrada MIDI deseada en la sección “MIDI input de-
vices” y la salida deseada en la sección “MIDI output devices”, las salidas posibles pueden
16
ser salidas MIDI, salidas de audio para escuchar los sonidos generados con parlantes más
potentes, el mapeador de Microsoft para reproducción de MIDI, etc.
4) Para modificar los parámetros del instrumento puede hacer clic el botón
que se encuentra cerca del nombre del instrumento virtual, aparecerá una imagen con
distintos controles que le permitirá modificar los parámetros del instrumento. Otra forma
de modificar los parámetros es hacer clic en el botón que se encuentra cerca del nombre
del instrumento virtual, aparecerá una ventana que permite cambiar los parámetros.
2
Los sintetizadores son instrumentos musicales electrónicos diseñados para producir sonidos
generados artificialmente, usando técnicas como sı́ntesis aditiva, substractiva, de modu-
lación de frecuencia, de modelado fı́sico o modulación de fase.
2
Wikipedia en español: Sintetizadores
17
1.2 Objetivo del proyecto
El objetivo del proyecto es realizar un sistema embebido que tenga la capacidad de generar
una salida de audio siguiendo las instrucciones recibidas por el instrumento MIDI que se le
conecte (el sistema debe tener una entrada MIDI). El sistema debe tener una interfaz para
comunicarse con un PC y la capacidad de manejar instrumentos VSTi, para que mediante
este puerto se le transmitan dichos instrumentos. El instrumento MIDI le indicará cual
nota es la que tiene que reproducir. También debe contar con un juego de controles que
modifiquen los parámetros del instrumento VSTi.
18
1.3 Sistemas similares existentes
Este dispositivo es un dispositivo mucho más completo del que se desea realizar
en este proyecto. Tiene la capacidad de cargar varios instrumentos VSTi y efectos VST
en su memoria de 1Gb y hacer distintas combinaciones de estos para generar sonidos
únicos. Para modificar los parámetros de los distintos instrumentos VSTi y efectos VST
tiene la posibilidad de crear controles rotatorios virtuales. El dispositivo consta de una
entrada MIDI que puede contener información de varios instrumentos conectados en serie,
y permite asociarle a cada instrumento un instrumento VSTi distinto. También dispone
de una entrada de audio a la cual se le pueden aplicar efectos VST.
Este proyecto esta ideado especı́ficamente para esos usuarios que quieren “conec-
tar el instrumento y que suene” sin grandes complicaciones, y que se puedan modificar
parámetros sin tener que crear controles virtuales. Simplemente se conecta el instru-
mento al sistema, se conecta también un parlante y se escuchan las señales generadas
19
por el instrumento. Para modificar parámetros del instrumento, no es necesario crear
controles virtuales, si no que hay controles de rotación reales que al girarlos modifican los
parámetros del instrumento.
Si bien se tienen menos funciones que la V-Machine, esto tiene la ventaja de volver
más sencillo el funcionamiento. Al ser más simple será también más económico lo cual es
otra ventaja, considerando que la V-Machine no es un dispositivo barato, cuesta alrededor
de 600 dólares en Estados Unidos, que es más de lo que un músico amateur o principiante
promedio quiere gastar.
Existen muchos programas para PC que permiten realizar el objetivo de este proyecto
y muchas más funciones, dichos programas son los Host VST mencionados en la intro-
ducción.
Una de las desventajas de remplazar el sistema que se desea crear con un PC es que
al igual que la V-Mahcine, la PC no es un dispositivo fácil de manejar. Otra desventaja es
que algunos músicos consideran mal visto llevar PC´s o notebooks a conciertos en vivo. Si
la PC no se usa exclusivamente para tocar el instrumento MIDI también está el problema
de que en cualquiera de los otros momentos en los que se usó hayan ingresado virus al PC
o se haya desconfigurado algo que imposibilite el correcto funcionamiento del Host VST.
La principal desventaja del PC es que no es tan es estable como un sistema embebido, es
decir que se puede trancar en cualquier momento, sobre todo en circunstancias como un
concierto en las que la mesa donde este el PC pude no ser muy estable, etc.
20
2 Conceptos avanzados
El byte MIDI está compuesto por diez bits que se envı́an/reciben a una velocidad de
31250 bits/segundo con una tolerancia de +/- 1% según el estándar. El primero es el bit
de inicio que siempre es 0 y el último el bit de terminación que siempre es 1. Esto con el
fin de que los dispositivos MIDI puedan llevar la cuenta de cuantos bytes se han enviado
o recibido. Los ocho bits restantes contienen los mensajes MIDI. Las instrucciones MIDI
se envı́an en serie. El protocolo de transmisión es el mismo que el del sistema RS-232.
Existen dos tipos de bytes: De estado -status byte- y de información -data byte-.
Se diferencian por el primer bit: si es un 1, es un byte de estado, y si es un 0, es un byte
de datos. Al generar un mensaje MIDI, por norma general, siempre se envı́a un byte de
estado, que puede estar seguido de cierta cantidad de bytes de datos. Por ejemplo, se
puede enviar un primer mensaje de estado “activar nota”, seguido de un byte de datos
informado qué nota es la que se activa. En algunas ocasiones y según el dispositivo MIDI
que se trate, puede ocurrir que se omita el byte status si es idéntico al anterior.
Los primeros bytes, cuyos últimos cuatro bits están marcados como “cccc”, se re-
fieren a mensajes de canal; el resto de bytes son mensajes de sistema.
Los comandos más utilizados son los de activación y desactivación de nota. Dichos
comandos constan de tres mensajes, el primero es el identificatorio de la función (byte de
estado), luego la nota que fue activada/desactivada y finalmente la velocidad con que fue
activada/desactivada.
Como se comentó con anterioridad, MIDI está pensado para comunicar un único contro-
lador con varias unidades generadoras de sonido (cada una de las cuales puede tener uno
o varios instrumentos sintetizados que deseemos utilizar), todo por un mismo medio de
transmisión. Es decir, todos los aparatos conectados a la cadena MIDI reciben todos los
mensajes generados desde el controlador. Ello hace necesario un método para diferenciar
cada uno de los instrumentos. Este método es el denominado canal.
3
Wikipedia en Español: MIDI
21
Byte estado Descripción
1000cccc Desactivación de nota
1001cccc Activación de nota
1010cccc Postpulsación polifónica
1011cccc Cambio de control
1100cccc Cambio de programa
1101cccc Postpulsación monofónica de canal
1110cccc Pitch
11110000 Mensaje exclusivo del fabricante
11110001 Mensaje de trama temporal
11110010 Puntero posición de canción
11110011 Selección de canción
11110100 Indefinido
11110101 Indefinido
11110110 Requerimiento de entonación
11110111 Fin de mensaje exclusivo
11111000 Reloj de temporización
11111001 Indefinido
11111010 Inicio
11111011 Continuación
11111100 Parada
11111101 Indefinido
11111110 Espera activa
11111111 Reseteo del sistema
Dentro del sistema MIDI, se decidió crear una serie de diferentes modos de funcionamiento,
cada uno con ciertas caracterı́sticas.
22
Número Nombre Descripción
1 Omni on polifónico Funcionamiento polifónico sin información de canal
2 Omni on monofónico Funcionamiento monofónico sin información de canal
3 Omni off polifónico Funcionamiento polifónico con información de canal
4 Omni off monofónico Funcionamiento monofónico con información de canal
Los mensajes de canal son los que sus bytes de estado en la tabla de funciones
MIDI terminan con “cccc”. En los casos en los que hay un solo instrumento conectado al
sistema, la información del canal se puede prescindir.
23
0 Piano de cola acústico 43 Contrabajo 86 Melodı́a 7 (quintas)
1 Piano acústico brillante 44 Cuerdas con trémolo 87 Melodı́a 8 (bajo y melodı́as)
2 Piano de cola eléctrico 45 Cuerdas con pizzicato 88 Fondo 1 (nueva era)
3 Piano de cantina 46 Arpa 89 Fondo 2 (cálido)
4 Piano Rhodes 47 Timbales 90 Fondo 3 (polisintetizador)
5 Piano con chorus 48 Conjunto de cuerda 1 91 Fondo 4 (coro)
6 Clavicordio 49 Conjunto de cuerda 2 92 Fondo 5 (de arco)
7 Clavinet 50 Cuerdas sintetizadas 1 93 Fondo 6 (metálico)
8 Celesta 51 Cuerdas sintetizadas 2 94 Fondo 7 (celestial)
9 Carillón 52 Coro Aahs 95 Fondo 8 (escobillas)
10 Caja de música 53 Voz Oohs 96 Efecto 1 (lluvia)
11 Vibráfono 54 Voz sintetizada 97 Efecto 2 (banda sonora)
12 Marimba 55 éxito de orquesta 98 Efecto 3 (cristales)
13 Xilófono 56 Trompeta 99 Efecto 4 (atmósfera)
14 Campanas tubulares 57 Trombón 100 Efecto 5 (brillo)
15 Salterio 58 Tuba 101 Efecto 6 (duendes)
16 órgano Hammond 59 Trompeta con sordina 102 Efecto 7 (ecos)
17 órgano percusivo 60 Corno francés (trompa) 103 Efecto 8 (ciencia ficción)
18 órgano de rock 61 Sección de bronces 104 Sitar
19 órgano de iglesia 62 Bronces sintetizados 1 105 Banjo
20 Armonio 63 Bronces sintetizados 2 106 Shamisen
21 Acordeón 64 Saxo soprano 107 Koto
22 Armónica 65 Saxo alto 108 Kalimba
23 Bandoneón 66 Saxo tenor 109 Gaita
24 Guitarra española 67 Saxo barı́tono 110 Violı́n celta
25 Guitarra acústica 68 Oboe 111 Shanai
26 Guitarra eléctrica (jazz) 69 Corno inglés 112 Campanillas
27 Guitarra eléctrica (limpia) 70 Fagot 113 Agogó
28 Guitarra eléctrica (apagada) 71 Clarinete 114 Cajas metálicas
29 Guitarra saturada (overdrive) 72 Flautı́n 115 Caja de madera
30 Guitarra distorsionada 73 Flauta 116 Caja Taiko
31 Armónicos de guitarra 74 Flauta dulce 117 Timbal melódico
32 Bajo acústico 75 Flauta de pan 118 Caja sintetizada
33 Bajo eléctrico pulsado 76 Cuello de botella 119 Platillo invertido
34 Bajo eléctrico punteado 77 Shakuhachi (flauta japonesa) 120 Trasteo de guitarra
35 Bajo sin trastes 78 Silbato 121 Sonido de respiración
36 Bajo golpeado 1 79 Ocarina 122 Playa
37 Bajo golpeado 2 80 Melodı́a 1 (onda cuadrada) 123 Piada de pájaro
38 Bajo sintetizado 1 81 Melodı́a 2 (diente de sierra) 124 Timbre de teléfono
39 Bajo sintetizado 2 82 Melodı́a 3 (órgano de vapor) 125 Helicóptero
40 Violı́n 83 Melodı́a 4 (siseo órgano) 126 Aplauso
41 Viola 84 Melodı́a 5 (charanga) 127 Disparo de fusil
42 Violoncello 85 Melodı́a 6 (voz)
24
2.2 Funcionamiento de los instrumentos VSTi y de los efectos
VST
4
Los efectos VST y los instrumentos VSTi no son aplicaciones en si mismos, si no que son
plugins, es decir que es necesario un programa anfitrión para poder utilizarlos, en el caso
particular de los VST o VSTi dichos programas son los VST Host.
Por lo general, lo que hacen estos plugins es tomar una sección de audio (o in-
strucción MIDI), procesarla y mandar el resultado al programa anfitrión. Por lo general
los comandos MIDI o el audio a analizar es dividido en secciones por el anfitrión que le
encomienda al plugin procesarlo y después toma los resultados generados y los reproduce.
Desde el punto de vista del VST Host los instrumentos VSTi y los efectos VST
son “cajas negras” con un número arbitrario de entradas y parámetros asociados. No es
necesario conocer el funcionamiento interno de estos para poder utilizarlos.
En Windows los instrumentos VSTi y efectos VST son archivos DLL, en Mac son
archivos Bundle y en Linux y otros sistemas UNIX son librerı́as compartidas.
La idea del proyecto es generar los instrumentos VSTi con el programa “Synth
Maker”, dicho programa dispone de una gran cantidad de herramientas para desarrollar
instrumentos virtuales, pero solo los permite exportar en formato DLL.
Tanto los instrumentos VSTi como los efectos VST para procesar audio deben tener un
método llamado process() y preferentemente también uno llamado processReplacing(). El
primer método toma los datos de entrada, les aplica el algoritmo del proceso y coloca
el resultado en un buffer de salida, mientras que el segundo método tiene la capacidad
de reescribir los datos que ya se encuentran en el buffer de salida. El VST Host es el
encargado de proveer tanto el buffer de entrada como el de salida.
Para implementar plugins VST y VSTi es necesario utilizar las clases base: AudioEffect
y AudioEffectX que se encuentran en el VST sdk versión 1.0. Dichas clases tienen sus
declaraciones en: AudioEffect.hpp y AudioEffect.cpp. También es necesario estar al tanto
de las definiciones de estructura de aeffect.h.
4
SDK Steinberg
25
2.3 Archivos DLL
Los archivos DLL (Dynamic Link Library, librerı́a de enlace dinámico) son archivos que
contienen funciones o recursos (mapas de bits, definiciones de fuentes, etc) que pueden
ser llamados desde cualquier aplicación de Windows.
De hecho, se puede considerar que Windows está construido sobre una gran cantidad
de DLL’s. Si bien, gran parte de las librerı́as de enlace dinámico se guardan en archivos
que tienen extensión DLL, en algunos casos también son guardados en archivos .exe
(ejecutables), DRV (controlador de dispositivo) y FON (fuente de Windows).
Lo que caracteriza a las librerı́as de enlace dinámico con extensión DLL es que se
encuentran disponibles para ser cargadas en cualquier momento que el programa se lo
solicite a Windows. En cambio las otras extensiones asociadas a las librerı́as de enlace
dinámico se cargan porque están referenciadas en archivos de inicialización de Windows.
Estas referencias pueden ser creadas por el propio Windows o por el programa de insta-
lación de alguna aplicación.
1. Una función escrita en una archivo DLL esta disponible para cualquier aplicación
de Windows.
2. Como varias aplicaciones pueden usar un mismo archivo DLL, esto reduce el código
y el tamaño de las aplicaciones.
5. Las DLL´s son independientes de la aplicación, por lo que en caso de fallar son
fácilmente reemplazables.
6. Como las DLL´s son independientes de la aplicación, se puede remplazar una DLL
por una mejor y dicho cambio se verá reflejado en todos los programas que usan el
mismo archivo DLL sin que sea necesario tener acceso y modificar el código de cada
uno de estos.
1. Todos los archivos DLL que se vayan a utilizar deben estar en la carpeta del sistema
antes de ejecutar los programas que los usan.
2. El tiempo de acceso a la DLL por parte de una aplicación es más lento que si
estuvieran las funciones de la DLL en el propio código del programa.
3. En caso de que dos programas utilicen dos DLL con el mismo nombre, pero distintas
funciones se generan inconvenientes para ejecutar dichos programas.
4. Si una DLL es mal sustituida todos los programas que la usan se ven afectados.
26
5. Los archivos DLL están hechos para ser ejecutados en Windows, por lo cual no
existe una forma sencilla de ejecutarlos en otros entornos.
Archivo de Cabecera Contiene todas las declaraciones y/o definiciones que use el archivo
DLL.
Punto de Entrada y Salida del archivo DLL Es la función principal de la DLL, y
es la que se encarga de cargar la DLL (cuando se vaya a usar) y descargarla de la
memoria (cuando se deje de usar). Dicha función se llama DllEntryPoint.
Funciones que Contiene la DLL Son las funciones que contiene el archivo DLL, estas
fueron declaradas por el programador.
27
2.3.5 Llamada dinámica a un archivo DLL
3. Como consecuencia de los primero dos puntos, el tamaño de los ejecutables es menor.
3. El manejo de las funciones de las librerı́as DLL es más complicado que en el caso
de las llamadas estáticas.
28
3 Posibles sistemas de implementación
3.1 Introducción
Una vez definido el objetivo del proyecto y estudiados los conceptos implicados en este,
comenzamos el análisis de los posibles sistemas embebidos para crear el sintetizador.
Buscamos un sistema que sea barato, robusto, que pueda interpretar comandos MIDI y
que pueda manejar archivos DLL (para poder interpretar instrumentos VSTi).
Si bien las ventajas de esta forma son considerables, también hay ciertas desventajas
y obstáculos por sortear. En caso de utilizar un sistema embebido con sistema operativo
se puede utilizar la gran cantidad de códigos y herramientas que ya existen desarrolladas
para la interpretación de comandos MIDI y de instrumentos VSTi. En caso de trabajar
con sistemas embebidos sin sistema operativos muchas de estas herramientas ya desarrol-
ladas se deberı́an re-inventar para poder utilizarlas bajo este entorno. A continuación se
analizan los que consideramos los principales problemas a resolver en caso de optar por
esta opción.
Una vez recibidos los comandos MIDI serı́a necesario desarrollar una biblioteca (o encon-
trar alguna hecha) que pueda interpretar las instrucciones MIDI recibidas. Dicha bib-
lioteca deberı́a incluir solo clases que estén en el lenguaje de compilación del compilador
del sistema embebido.
La mayorı́a de los reproductores que se encuentran con código abierto (open source)
de MIDI utilizan bibliotecas precompiladas de Windows por lo cual no serı́a posible uti-
lizarlos para interpretar instrucciones MIDI. Vemos que puede parecer complicado el de-
sarrollo de una biblioteca capaz de interpretar todos los comandos MIDI, la combinación
de comandos e instrucciones MIDI es tan grande que probablemente una biblioteca hecha
por nosotros no lograrı́a tener un control completo de todas las variables posibles.
29
3.2.2 Interpretación de Instrumentos VSTi
Los archivos DLL también funcionan como pequeños programas que necesariamente
deben ser ejecutados sobre un programa madre. Los plug-in en algunos browsers de
internet son ejemplos claros de este tipo de archivos. Dado que los archivos DLL son
“cajas negras” serı́a necesario encontrar una forma de interpretar los procesos internos
que genera Windows para poder usar esas “cajas negras” y poder generar un código capaz
de reproducirlos.
Los archivos DLL también contienen algunas instrucciones sobre el manejo de memo-
ria que estos precisan, por lo cual para poder interpretar los archivos DLL en un sistema
embebido sin sistema operativo se deberá generar un sistema de manejo de memoria que
simule al de Windows.
Como los DLL fueron pensados para correr únicamente en Windows, muchas de las
funciones que estos necesitan para funcionar son exclusivas de Windows, razón por la cual
si queremos interpretar archivos DLL sin utilizar un sistema operativo se deberá hallar
cuales son estas funciones especı́ficas que nuestro DLL requiere para poder implementarlas.
Otra de las fuentes de información en las que buscamos fue en las páginas de las
fábricas que generan sistemas embebidos, buscamos información en varias de ellas y no en-
contramos en ninguna. En la página de Rabbit compañı́a que fabrica microcontroladores
embebidos, preguntamos al servicio técnico si tienen algún producto con la capacidad de
interpretar funciones de un archivo DLL o si tienen conocimiento de algún código fuente
que permita interpretar dichas funciones y la respuesta fue que no. En las páginas de
los otros fabricantes de sistemas embebidos todas las respuestas nos guiaron a utilizar un
sistema embebido con un sistema operativo.
Wine
Wine es un programa que permite correr en Linux programas diseñados para ser
utilizados sólo en Windows.
30
Este caso tiene en común con el nuestro que también se usa para interpretar archivos
DLL entre otras cosas. La iniciativa de Wine nació hace ya catorce y es un proyecto en
continuo desarrollo, esto nos da una pauta de lo difı́cil que puede ser intentar migrar
programas diseñados para Windows a otro sistema operativo. Al ser Windows y Linux
sistemas operativos tienen librerı́as que son equivalentes, en caso de querer interpretar
archivos DLL sin sistema operativo estas tendrı́an que ser creadas.
Existen ciertos programas que permiten abrir este tipo de archivos de forma binaria
e incluso alterarlo. Un ejemplo de esto es el FlexHex.En el FlexHex se distinguen cuatro
paneles. En el primero se muestra la posición de memoria en la que se esta trabajando, y
en el resto se observa el código en formato Hexa, Ansi y Unicode. Este programa no nos
es útil, dado que no es posible interpretar funciones (las que están incluidas en el archivo
DLL), solo permite manejar su archivo binario.
KERNEL32.dll: Es el núcleo más importante del kernel de Windows. Sirve para ges-
tionar el uso de la memoria, también maneja las interrupciones y asigna el tiempo
de ejecución de cada aplicación.
GDI32.dll: Contiene funciones gráficas para objetos en 2 dimensiones. (no serı́a nece-
sario en nuestro caso).
comdlg32.dll: Contiene los mensajes de diálogo y carteles comunes. (no serı́a necesario
en nuestro caso).
31
3.3.1 Sistema Operativo Linux
Para utilizar el sistema operativo Linux el principal problema serı́a lograr transformar los
instrumentos VSTi, que están dados en formato DLL a librerı́as compartidas para que
puedan ser ejecutadas por Linux. Otro problema que podrı́a surgir es encontrar drivers
de los adaptadores MIDI compatibles con Linux.
Además del problema de obtener una conversión para los archivos DLL, también
serı́a necesario desarrollar un VST Host nosotros, dado que si bien hay varios VST Host
gratuitos para Linux, no pudimos encontrar ninguno que sea de código abierto para hacerle
las modificaciones que precisemos. Desarrollar nosotros un VST Host a parte de tener el
problema del tiempo que consuma también tiene el inconveniente de que seguramente no
podamos generar una versión que funcione correcto en todos los casos y menos que este
tan optimizada como los otros Host Vst que tienen años de desarrollo. Esta es una opción
que debemos descartar.
Como segunda opción existe la posibilidad de utilizar el programa Wine para generar
un emulador de Windows, de esta forma no serı́a necesario convertir los DLL y ya
disponemos de un programa open source para poder modificar.
La gran ventaja de utilizar Windows para este proyecto es que los archivos VSTi en for-
mato DLL están desarrollados y optimizados para funcionar en este entorno. Otra ventaja
es que al ser el sistema operativo más utilizado por los editores de música, Windows es
el sistema operativo con mayor compatibilidad con cualquier hardware que precisemos.
Dentro de Windows también encontramos VST Host gratuitos y con años de desarrollo,
de esta forma podemos basar nuestro software en uno existente y hacerle algunas adapta-
ciones para acoplarlo a nuestros requisitos.
5
Los sistemas operativos en tiempo real, son sistemas que no solo garantizan que una
acción pedida se realizará, si no que también pueden garantizar en cuanto tiempo.
Dada las caracterı́sticas de este tipo de sistema operativo, parecerı́an ser ideales
para el proyecto dado que eligiendo el procesador adecuado y el código adecuado se puede
garantizar que el sonido se va a escuchar bien. Con sistemas operativos que no son en
tiempo real (como Windows y Linux), no es posible garantizar que los comandos MIDI
van a ser procesados con una velocidad suficiente como para que el sonido se escuche bien.
5
Wikipedia en español: Sistemas operativos de tiempo real
32
Este tipo de sistema operativo no tiene una capacidad de procesamiento alta. Su
algoritmo de programación especializado, y su tasa de interrupción del reloj alta pueden
interferir en la capacidad de procesamiento.
Aunque para propósito general un procesador moderno suele ser más rápido, para
programación en tiempo real deben utilizarse procesadores lo más predecibles posible,
sin paginación. Todos estos factores añaden una aleatoriedad que hace que sea difı́cil
demostrar que el sistema es viable, es decir, que cumple con los plazos. Razón por la cual
si bien es posible dar garantı́as sobre los tiempos, estas sean muy difı́ciles de calcular y es
difı́cil que cumplan los requisitos.
Se busco también la posibilidad que existe un sistema operativo o algo similar dedicado
exclusivamente al manejo de instrumentos virtuales y entradas MIDI, pero no se encontró
nada.
33
4 Análisis de las posibles formas de implementación
La idea original de los archivos DLL es que sean simples “cajas negras” hechas para
ser interpretadas por Windows, por lo que cualquier software que encontremos o hagamos
para interpretarlos probablemente tenga algunos errores por que los archivos DLL no están
diseñados para ser usados por ese tipo de software. Otro problema en la interpretación de
los instrumentos VSTi es que aunque logremos convertir a otro formato los archivos DLL
aún ası́ tendrı́amos problemas para poder utilizar todas las otras bibliotecas de Windows
que dichos archivos usan.
En caso de optar por esta opción deberı́amos, por lo menos en un principio, inter-
pretar los comandos de activación y desactivación de nota unicamente.
34
sistema, y este es un punto clave también si se desea ofrecer un producto serio. La mayor
interrogante que encontramos es de si va a ser posible disminuir este tiempo de latencia,
y suponiendo de que si, quizás se logre mediante la instalación de un nuevo hardware
que elevará los costos del producto, perdiendo la ventaja del precio que tiene esta opción
frente a la de utilizar Windows.
Si bien la solución con sistemas con Linux embebido con emulador de Windows
parece la más sencilla de implementar, antes de decidirnos por ella hay que determinar si
el Wine (emulador de Windows para Linux) es capaz de ejecutar correctamente host de
midi diseñados para ser ejecutados en Windows y también es necesario idear una solución
al problema de la latencia.
Para saber si el Wine es capaz de ejecutar host de Midi diseñados para Windows
y que se puede cargar instrumentos VSTi en formato DLL lo que hicimos fue instalar el
Wine en un sistema con Linux y luego cargar en el Wine un host vsti de Windows. El
resultado fue positivo en el sentido de que fue posible reproducir sonidos generados por un
piano MIDI y cargar instrumentos virtuales VSTi en formato DLL, pero con una latencia
aún mayor que la hay cuando se ejecuta directamente en Windows.
4.4 Conclusiones
Se ha llegado a uno de los puntos crı́ticos del proyecto. Por un lado se encuentra la
posibilidad de desarrollar el dispositivo mediante un sistema embebido que soporte el
procesamiento de archivos DLL, para esto como se vio previamente se requerirá de uno
suficientemente potente y complejo para que logre correr sobre el un sistema operativo,
por el otro lado se encuentran los microcontroladores sin sistema operativo. Estos son de
una arquitectura más simple, no tienen los recursos necesarios para soportar un sistema
operativo, esto los convierte en sistemas más robustos que tienen menor posibilidad de
sufrir fallos, prácticamente no necesitan tiempo para inicializar el programa y son mucho
más compactos y baratos.
La decisión por uno de ellos involucra la resignación parcial de algunos de los obje-
tivos que fueron decididos al comienzo. El sistema embebido con capacidad de cargar un
sistema operativo ofrece la posibilidad de reproducir archivos DLL, esto significa una mejor
calidad y una gama más variada de distintos VSTi, la implementación mediante micro-
controladores significarı́a quizás la imposibilidad de una reproducción total de los archivos
VSTi y conformarnos con un sonido menos elaborado, resultado de la “traducción” de
este tipo de archivo a un nuevo, que pueda ser procesado por los controladores.
Dado que el problema de la latencia es aún más grave en Wine que directamente
en Windows y que no disponemos de una solución a ese problema, ni siquiera de indicios
de que este problema pueda ser resuelto, decidimos no utilizar un sistema embebido con
Linux.
Dado que si bien las soluciones que involucran un sistema operativo parecen más
sencillas, como un sistema embebido no tiene las mismas capacidades que un PC, conside-
ramos que la ejecución del código no será tan eficiente. Esta argumento toma más valor,
teniendo en cuenta que los VST Host están pensados para ser ejecutados en una PC. Por
35
estas razones consideramos, que si bien un sistema embebido puede tener ventajas contra
un PC (ser más robusto, resistente), no consideramos que pueda mejorar el fundamental
problema del retardo.
36
5 Replanteo del proyecto y de su implementación
Una vez analizadas todas las opciones viables para la resolución del objetivo, decidimos
hacer el sintetizador con sistemas embebidos sin sistema operativo. Como se mencionó
anteriormente, resignamos la interpretación de instrumentos VSTi. De esta forma, el
proyecto queda fijado como un sintetizador MIDI con la capacidad de interpretación de
comandos MIDI de un único instrumento y que cada nota tenga un sonido particular
determinado por un instrumento virtual. Dicho instrumento no es un instrumento VSTi,
si no un instrumento en un formato determinado por nosotros.
Dicho sintetizador también debe contar con un juego de potenciómetros que sirvan
para manejar efectos sonoros a los sonidos generados por el sintetizador.
37
6 Creación de la nota musical
6.1 Introducción
Para simplificar aún más podemos agrupar las cuatro propiedades anteriores en
grupos de a dos, estos serán las caracterı́sticas internas y las externas. La altura y el
timbre entrarán en la primera categorı́a y el resto en la segunda.
38
Decaimiento: Es el tiempo (o porcentaje del tiempo total) en que tarda la amplitud
en reducirse a la de sostenimiento.
Sostenimiento: Luego del ataque y del decaimiento, la señal se mantiene en un
nivel de sostenimiento, dicho nivel se expresa como porcentaje del máximo volúmen
que adquiere la señal.
Relajación: El tiempo (o porcentaje del tiempo total) que tarda el sonido en perder
toda su amplitud.
Habiendo definido el sonido, podemos denominar a una nota musical como un sonido
cuya frecuencia fundamental es constante. Una nota musical tocada por cualquier instru-
mento se compone de varias ondas sinusoidales una de las cuales (la más predominante)
tiene la frecuencia fundamental y el resto de las ondas son las frecuencias armónicas de
esta. Es la frecuencia fundamental la que determina que nota estamos escuchando, la
nota “La” central del piano posee una frecuencia fundamental de 440 Hz, el “La” anterior
a este su frecuencia vale la mitad, 220 Hz. El intervalo que separa dos sonidos cuyas
frecuencias fundamentales tienen una relación de dos a uno se le llama “octava”, también
se usa el término para referirse al rango de frecuencias entre dos notas separadas por una
relación de dos a uno7 .
Fueron tanto las caracterı́sticas internas como externas las que se intentaron mode-
lar en el entorno del Scilab para luego hallar la manera de trasladar este algoritmo de
generación de notas a un código que el microcontrolador pueda entender. A continuación
se enumeran las formas en las cuales se buscó imitar sonidos de instrumentos.
7
Wikipedia: Octava
39
6.3 Sinusoidales puras
Se tomó un sonido generado por el rasgueo de una única cuerda de guitarra y se propuso
analizarlo con el fin de lograr generar uno similar. Para comenzar se necesitaba hallar
las frecuencias de las ondas que estábamos escuchando, para esto se utilizó la función
de Scilab “FourieFacilDetectaPicos”, lo que hace esta función es calcular Transformada
Rápida de Fourier (FFT) y graficarla, más adelante se explica esta función con más detalle.
Se obtuvo la figura de la gráfica de la imagen 5.
Se observan seis claros picos de los cuales el primero es de mayor magnitud, este
está indicando el valor de la frecuencia fundamental de la nota que en este caso es de
aproximadamente 110 Hz que corresponde a la nota ’La’ en la escala musical.
Esta escala nos hace suponer que nuestro sonido esta compuesto de al parecer de
únicamente sinusoidales perfectas, pero si nos acercamos un poco más al eje de las equis
notaremos como nuestra nota de guitarra es bastante más compleja, estando compuesta
por una gama más amplia de frecuencias. En la imagen 6, se observa la gráfica haciendo
zoom en el eje de las equis.
40
Imagen 6: Gráfica de la transformada de Fourier del sonido de una guitarra
Aquı́ se observan además de los picos vistos en la previa gráfica ciertas “irregular-
idades” en la base de estos. Estas anomalı́as son generadas por el instrumento musical
y son otro parámetro que nos permite diferenciar entre cierta nota en una guitarra y la
misma nota en un piano.
Para comenzar se tomaran únicamente los primeros cinco armónicos, luego se irá
aumentando con el objetivo de hallar cuantos armónicos son necesarios para poder re-
construir fielmente el sonido original.
Para corroborar que nuestro nuevo sonido posee las mismas frecuencias armónicas
que el sonido original con sus respectivas magnitudes se calcula el espectro de Fourier de
este, para el caso de n=10 dicho espectro se muestra en la imagen 7.
41
Armónico Frecuencia Magnitud (Normalizada)
1 109 1
2 218 0,34
3 327 0,22
4 436 0,12
5 545 0,053
6 654 0,075
7 763 0,084
8 872 0,0006
9 981 0,011
10 1090 0,020
11 1199 0,0015
12 1308 0,0043
13 1417 0,0040
14 1526 0,010
Si bien, como se puede observar en la imágen 7, la señal generada tiene una descom-
posición en frecuencias similar a la señal original, el sonido que se generó no es similar
al que se desea imitar. Para generar las imitaciones se utilizó la aplicación de Scilab
“sinusoidalesPuras”. Algunos resultados se encuentran en el cd adjunto.
42
permite ‘apagarse’ como hacen las señales que deseamos imitar.
Para evitar grandes aproximaciones, se modelo la envolvente con seis distintas rectas.
43
Imagen 9: Gráfica de la envolvente generada
A continuación se hallará la ecuación que representa a cada una de las rectas para
poder determinar el valor por el cuál se debe multiplicar a nuestro sonido creado ante-
riormente que estaba basado únicamente en las frecuencias armónicas. Dichas ecuaciones
se encuentran en la tabla 5.
Recta Ecuación
OA y = x/0, 062
AB y = 1 − 0, 51 ∗ (x − 0, 062)/(0, 21 − 0, 062)
BC y = 0, 49 − 0, 08 ∗ (x − 0, 21)/(0, 40 − 0, 21)
CD y = 0, 41 + 0, 04 ∗ (x − 0, 40)/(0, 50 − 0, 40)
DE y = 0, 45 − 0, 18 ∗ (x − 0, 50)/(0, 63 − 0, 50)
EF y = 0, 27 − 0, 27 ∗ (x − 0, 63)/(4, 9 − 0, 63)
Tabla 5: Tabla de Puntos
6.5.1 Introducción
44
lo denomina la Transformada de Fourier de Tiempo Reducido o STFT por sus siglas en
inglés “Short Time Fourier Transform”.
Dividimos el sonido que deseamos imitar en ’n’ partes y luego analizamos la trans-
formada de Fourier en cada una de esas partes. En la imagen 10 se observa las gráficas
de las transformadas para ’n’=10 del sonido de guitarra que se desea imitar. La gráfica
1 corresponde con el primer intervalo, la 2 con el segundo y ası́ sucesivamente. Se puede
observar como la frecuencia del segundo armónico la más predominante al principio, pero
luego es sustituida por la fundamental que es la que se mantendrá con mayor magnitud
a lo largo del tiempo restante. Con el método anterior no nos era posible representar la
situación en la cual la influencia de cada frecuencia de la nota varı́a en el tiempo.
Con esta información podemos concluir que para lograr obtener una nota musical
convincente, que presente las mismas caracterı́sticas que una nota real, además de tener
una cantidad significativa de armónicos se deberá generar para cada uno de ellos una
envolvente particular.
Imagen 10: Gráfica de una transformada de Fourier del sonido de una guitarra, di-
vidiéndolo en 10 partes
El primer paso del método es seleccionar las ’m’ frecuencias más influyentes en el sonido,
en este caso son las mencionadas en la tabla 3.
Luego hay que dividir el sonido en ’n’ partes iguales y luego analizar la transformada
de Fourier de cada uno de esos ’n’ sonidos.
Después hay que generar una envolvente para cada una de las frecuencias más in-
fluyentes. Dichas envolventes son aproximaciones con rectas de la evolución de la influen-
cia de cada una de las frecuencias más influyentes a lo largo del tiempo. En la imagen11 se
muestra la evolución de la influencia de cada una de las frecuencias a lo largo del tiempo.
En la gráfica 1 se muestra la evolución de la frecuencia 109 Hz, en la gráfica 2 la evolución
de la frecuencia 218Hz, y ası́ sucesivamente. El significado de la magnitud del eje de las
’Y’ no es importante, lo importante es saber que es la misma magnitud para todas las
gráficas de la imagen, por lo que si una llega a el valor ’100’ y la otra al ’200’ la segunda
vale el doble en ese instante. El eje de las ’X’ es el tiempo expresado en segundos.
45
Finalmente para generar el sonido hay que generar con cada una de las frecuencias
más influyentes una onda sinusoidal, multiplicarla por la envolvente que le corresponde,
sumar todas las sinusoides y normalizar el sonido resultante.
Imagen 11: Gráfica de las envolventes para las frecuencias más influyentes
Como otra opción se manejo también la posibilidad de utilizar una memoria en dónde se
encuentren grabados todos los sonidos de las notas musicales. El piano MIDI actuarı́a
enviando la información a un procesador de cual nota es la que se está solicitando, y
luego el procesador se encargarı́a de irle solicitando los valores a una memoria externa
para luego a través de un conversor digital analógico (DAC) lograr obtener la salida de
audio.
Con este sistema se le exigirı́a mucho menos al procesador, mientras que el caso
anterior es necesaria una capacidad de procesamiento importante para lograr obtener
el resultado de sumar el valor de todos los armónicos multiplicados por su envolvente
correspondiente en un perı́odo de tiempo menor al de muestreo de la salida. Si se utiliza
46
el sistema de los samples la tarea del procesador se limitarı́a a solicitar la posición de
memoria en la que se encuentra el próximo valor del sample. Este sistema también
permite obtener una completa fidelidad de sonido, siendo la reproducción el resultado
directo de la grabación original.
A pesar de las ventajas que indudablemente tiene este nuevo enfoque, optamos
por imitar las notas musicales asignándole a cada armónico una envolvente. Una de las
razones de esta decisión es que el método de multiplicar cada armónico por su envolvente
permite una mayor flexibilidad al momento de manipular el sonido. Por otro lado, para
que el sistema de los samples tenga una fidelidad muy alta es necesario realizar una
buena grabación de una nota de un instrumento musical real, esto requiere de avanzada
tecnologı́a para suprimir todo ruido que pueda llegar a aparecer además de micrófonos de
alta calidad colocados en habitaciones con buena acústica sonora.
Un objetivo del producto es también lograr que cada consumidor pueda diseñar
sus propios instrumentos. La personalización que se le puede realizar a un sample ya
grabado es mucho menor y más complicada de realizar que la personalización que se le
puede realizar a un sonido obtenido con el sistema de la multiplicación de sinusoidales por
envolventes. Este método permitirı́a diseñar los sonidos de instrumentos que únicamente
se han escuchado algunas notas e incluso de algunos inexistentes.
6.7 Limitaciones
Al descartar los samples como método para la recreación de las notas se tomó una decisión
importante que generó una pérdida de funcionalidad con respecto a los objetivos originales
planteados.
La idea original de reproducir los VSTi permite reproducir casi cualquier sonido,
ya se mencionó previamente que estos pueden variar enormemente habiendo algunos
diseñados para sonar como tambores, ruidos de animales e incluso voces humanas.
Estos tres son claros ejemplos de sonidos que no podrán ser sintetizados por nuestro
dispositivo debido a que al realizar el estudio de Fourier se observa que no es posible
descomponerlos en algunas pocas frecuencias.
Para confirmar que la metodologı́a mediante la cual se intentarı́a recrear los instru-
mentos musicales cubrirá una mayorı́a de estos, se procedió a seleccionar una variedad de
instrumentos pertenecientes a las familias de viento y cuerda con el objetivo de investigar
su descomposición en frecuencias.
47
Imagen 12: Descomposición en frecuencias de sonidos imposibles de sintetizar
48
7 Detección de comandos MIDI
Una vez determinada la forma en la cual los sonidos van a ser reproducidos, el paso si-
guiente antes de comenzar la selección del microcontrolador a utilizar es investigar como
vamos a poder interpretar las señales MIDI. Es necesario saber si vamos a poder inter-
pretar los comandos MIDI con cualquier microcontrolador, si es necesario un microcon-
trolador con alguna capacidad especial o si es necesario utilizar algún tipo de adaptador
o de circuito adicional.
Ahora sabemos que el único requisito que debe tener nuestro microcontrolador para
poder interpretar comandos MIDI es la capacidad de comunicarse vı́a RS232 a una ve-
locidad de 31250 bits/seg. Sabemos que esa velocidad, si bien puede ser problema para
transmitir datos para algunas computadoras, para la mayorı́a de los microcontroladores
es una velocidad accesible.
49
8 Prueba previa
Para determinar si la implementación del sistema con sistemas embebidos sin sistema ope-
rativo es viable, decidimos realizar un sistema similar al pedido pero mucho más reducido
con el objetivo de encontrar los posibles problemas y facilidades que tiene implementar
el sistema requerido de esta forma. La prueba consistió en implementar un sistema que
interprete comandos MIDI y que pueda reproducir una función sinusoidal principal, junto
con dos armónicos en un pic16f628a.
Dado que el objetivo de la prueba no era realizar el sistema final, si no que simple-
mente era realizar unas pruebas preliminares, decidimos trabajar con el pic16f628a por
que si bien es un PIC muy limitado es un PIC con el cual tenemos experiencia de trabajo,
además es fácil de armar circuitos básicos con este y de armarle programadores.
Los códigos de programación para el PIC fueron escritos con el programa ’MikroC’.
Este programa permite escribir códigos en lenguaje ’C’ para el PIC. Transforma los códigos
escritos en lenguaje ’C’ al lenguaje assembler del pic y crea un archivo ’.hex’. Los archivos
’.hex’ son los que contienen los programas compilados listos para ser transmitidos al Pic.
Para que el Pic pueda conectarse con el PC para poder recibir su programación
utilizamos el circuito JDM programer, dicho circuito se muestra en la imagen 14. Sirve
para poder comunicar cualquier pic de las lı́neas 12C5XX, 12C67X, 24CXX, 16C55X,
16C61, 16C62X, 16C71, 16C71X, 16C8X, 16F6X, 16F8X con un PC a través del puerto
serie.
Para transmitir los archivos ’.hex ’ generados al Pic utilizamos el programa ICPROG.
Como herramienta de simulación de los códigos escritos utilizamos el PICSIMULATOR.
50
las notas son tocadas. Cada nota será reproducida como una sinuidal de su frecuencia
principal más un armónico con el doble de esta frecuencia y otro con el triple.
Para hacer más sencilla la prueba decidimos que el PIC trabaje con su oscilador
interno de 4Mhz, al tener un PIC tan limitado junto con una frecuencia de funcionamiento
tan lenta, decidimos que el sistema reproduzca sólo una nota a la vez.
El sistema tiene una variable llamada nota y otra llamada velocidad que representarán los
dos datos que el protocolo MIDI envı́a cada vez que detecta que se ha activado una nota,
nota indicará el número de nota que fue activada (la frecuencia) y velocidad guardará la
información de cual fue la intensidad con la que se presionó. El programa también cuenta
con un array de variables del tipo short int llamado velocidadArmonicos de dos posiciones,
el cual sirve para indicar la magnitud de los armónicos de las notas. Los valores de los
elementos de dicho array son fijados en el momento de programación. El valor de pico
del primer armónico se calcula como velocidad * velocidadArmonicos[0] /255, y el valor
de pico del segundo armónico como picoPrimerArmonico * velocidadArmonicos[1] / 255.
El código de esta prueba esta en el anexo F.
Es necesario que la salida actualice su valor cada un tiempo constante, por ejemplo
a una frecuencia de 44100 Hz, no serı́a correcto actualizar el valor de una salida cuando
recién es calculado, por que esto provocarı́a que la frecuencia de muestreo de la salida
no sea constante. Para resolver ese asunto, la solución fue iniciar un timer, previamente
configurado, que cuente el tiempo que debe transcurrir entre que el sistema genera una
salida y genera la siguiente. Una vez inicializado el timer, se calcula la siguiente salida y
cuando el timer llega a su valor de cuenta se actualiza la salida, se reinicia el timer y se
comienza a calcular la salida siguiente.
Dado que los comandos MIDI tienen una forma de transmisión muy similar a la utilizada
por el puerto serie del PC, para realizar esta prueba enviamos los comandos MIDI desde
un PC, ya que esto nos permite mandar los comandos que especı́ficamente queremos y
también nos permite recibir comandos desde el PIC, lo que es muy útil al momento de
depurar.
51
8.3.1 Generación de sonidos
Uno de los objetivos de la prueba fue probar la velocidad con la cual el PIC realiza las
cuentas y probar distintas formas de hacerlas. Dado que el PIC seleccionado maneja
registros de 8 bits, decidimos que la salida de audio tenga 8 bits de resolución.
Para distribuir estos 256 posibles valores de salidas entre el voltaje mı́nimo y máximo,
decidimos utilizar una escala lineal. Esto se debe a que facilita la conversión de digital
a analógico y por que es el sistema utilizado por la mayorı́a de los sistemas de audio
orientados a la música. Otros tipos de escala como la logarı́tmica son recomendados para
sistemas en los cuales la mayorı́a del tiempo la señal toma valores lejanos de los máximos
posibles, como el caso de la transmisión de voz.
Para que la salida sea lo más simétrica posible, decidimos asociarle al mı́nimo voltaje
de la señal de audio el valor 0, al máximo de la salida de audio el valor 255 y 0 volts en
la señal de audio al valor 128 del PIC. De esta forma el rango de salidas posible es casi
simétrico, es decir que el voltaje máximo es lo más parecido posible al valor absoluto del
mı́nimo voltaje.
El circuito necesario para transformar la señal generada por el Pic en una señal de audio
analógica no es exactamente un conversor digital a analógico. Se precisa un circuito que
asocie una salida digital de 0 con el valor -9v y el valor de 255 con 9v. Un conversor digital
a analógico simple asocia el valor digital 0 con 0v, lo que necesitamos es una combinación
entre un conversor y un restador.
52
Imagen 15: Circuito del conversor digital a analógico
Una vez asociados los voltajes de salida del PIC con los voltajes de la salida de audio, el
siguiente paso es calcular como llegar a las salidas deseadas.
La solución más sencilla es multiplicar la intensidad con la que una nota fue oprimida
por una variable del tipo double que valga entre -1 y 1 correspondiente al coseno de (2 ∗
π ∗ f ∗ t) , siendo f la frecuencia asociada a la nota pedida. Luego repetir el procedimiento
para los armónicos y normalizar la salida. Este fue uno de los intentos realizados, si bien
el intento funcionó, al PIC le toma demasiado tiempo hacer cuentas con variables del tipo
double dado que estas son muy precisas, mucho más de lo necesario teniendo en cuenta
que nuestra salida solo tiene 256 posibles valores.
Las funciones coseno y seno que tiene implementado el microC tienen una precisión
muy grande, dado que devuelven un punto flotante, esta precisión tiene como consecuencia
que el cálculo de estos números tome más tiempo del que tomarı́a si lo calculáramos con
una precisión menor.
53
Otra alternativa a las funciones seno y coseno que dispone el MikroC son las fun-
ciones sen1000 y cos1000. Estas soluciones contemplan el problema del largo tiempo que
toman las funciones seno y coseno antes mencionado. La salida de las funciones sen1000
y cos1000 son el número entero más parecido a 1000 multiplicado por el seno o coseno
deseado. Dado que esta operación tiene menos precisión es mucho más rápida que calcular
senos o cosenos directamente. A continuación se enuncian formas aún más rápidas.
La tercer alternativa pensada para poder calcular senos y cosenos rápidamente fue
utilizar la siguiente ecuación en diferencias: y(n) = 2 ∗ cos(φ) ∗ y(n − 1) − y(n − 2), esta
ecuación tiene como soluciones en régimen cosenos y senos de perı́odo 4 ∗ φ ∗ T , siendo
T el perı́odo de actualización de los datos en la ecuación en diferencias. La amplitud
puede ser modificada al variar y(0) e y(1). Esta solución al igual que las mencionadas
anteriormente funciona. En los casos anteriores el valor pico de las funciones seno y coseno
esta determinado, por lo cual para variarlo debemos hacer una multiplicación, en este caso
el valor pico lo determinamos variando y(0) e y(1) sin necesidad de hacer multiplicaciones
extra. Por esta razón, de los métodos mencionados, este es el método que calcula senos
y cosenos en menor cantidad de tiempo. Este método requiere el cálculo de un coseno
(cos(φ)) que hay que realizarlo de otra forma. Una desventaja de este método es que
el resultado de y(n) depende de los valores calculados anteriormente, por esa razón los
errores cometidos al aproximar resultados de multiplicaciones y sumas (para no tener que
trabajar con variables del tipo double) será acumulativo.
En todos los métodos anteriores no sólo se gasta tiempo calculando los senos y
cosenos, si no que también se gasta tiempo haciendo multiplicaciones y divisiones para
saber de que valor se precisa el seno y el coseno (por que por lo general se desea saber
cos(wt) o cos(wt/Fs), etc). La solución que encontramos fue cargar en la memoria de
programación del PIC un array con 256 valores del perı́odo de un coseno, lo números no
serán variables del tipo ‘double’, sino del entero que mejor aproxime a el verdadero valor
del coseno por 128. El primer valor del array corresponde a 128*coseno(0), el segundo a
128*coseno(2π/256), el tercero a 128*coseno(4π/256) y ası́ sucesivamente hasta llegar a
128*(510π/256). Este método tiene la ventaja de que los cosenos no hay que calcularlos
sino que hay que obtenerlos de una tabla y que en vez de realizar multiplicaciones para
saber de que valor se busca el coseno, se calcula el coseno de una variable t que aumenta
a una razón que depende de la nota que se desea reproducir.
Otra de las operaciones que notamos consumı́a gran cantidad de ciclos de reloj son las
operaciones de multiplicación y división. El gran tiempo tomado se debe en parte a que
este no es un diseño de PIC optimizado para este tipo de operaciones. Una de las con-
clusiones de esta prueba es, justamente, que para el diseño final, uno de los requisitos
del microcontrolador es tener la capacidad de resolver multiplicaciones y divisiones en
menos tiempo. Similarmente que con las operaciones trigonométricas, para realizar mul-
tiplicaciones y divisiones en tiempos aceptables es necesario que estas no sean de variables
double o long sino de la menor cantidad de bits posibles.
Como realizar divisiones toma más tiempo que realizar multiplicaciones y las multi-
plicaciones más rápidas son las de variables del tipo short int e int, decidimos transformar
54
todas las divisiones en multiplicaciones de esas variables. Si una variable de dos bytes se
la divide por 256, nos quedará como resultado el byte más significativo, por lo tanto si
queremos dividir una variable cuyo valor entra en un byte por cuatro, podemos multiplicar
a esta variable por 64 y tomar el byte más significativo (64 es el resultado de dividir 256
por cuatro). Este “truco” es preciso únicamente cuando 256 es múltiplo del divisor.
8.5.3 Observación
Al ser el sistema de la prueba un sistema muy sencillo, los sonidos generados tienen
mucha menos complejidad que la que esperamos que tenga los sonidos del dispositivo
final, aún ası́ notamos que al tipo de microcontrolador utilizado le falta capacidad de
procesamiento para manejar las salidas deseadas. En caso de usar microcontroladores
similares serı́a necesario una frecuencia de trabajo de 160 Mhz aproximadamente para
obtener una frecuencia de salida suficientemente alta como para poder generar salidas en
todo el espectro de frecuencias audibles por el ser humano.
55
alcanzar una frecuencia de salida de datos de 44100 Hz, que se considera una calidad alta
en la grabación de sonido.
También estamos buscando una mayor resolución en el sonido, esto implica registros
de más de 8 bits.
56
9 Selección del sistema embebido
Existen muchos microcontroladores DSP y muchas marcas, las empresas tienen micro-
controladores muy similares. Como la gran mayorı́a de los DSP tienen las capacidades
que consideramos fundamentales que tenga nuestro procesador, decidimos seleccionar el
DSP a utilizar, no solo por comparación de las caracterı́sticas mencionadas en las hojas
de datos si no que también decidimos ponderar otras caracterı́sticas. Estas caracterı́sticas
nos llevaron a elegir la lı́nea de DSP llamada DSPic, producida por la compañı́a Microchip,
a continuación se enumeran algunas de las razones por las que tomamos esa decisión.
57
’MPLAB’ es de libre distribución y tiene una versión que permite programar toda la lı́nea
DSPic en lenguaje ‘C’. Dicha versión es de uso gratuito para estudiantes.
Si bien en Uruguay hay muy poco soporte y repuestos de la lı́nea de DSP, si existe soporte
y repuestos para la compañı́a Microchip en Uruguay. Esto le da a los DSPic una ventaja
frente a los otros DSP, dado que por lo menos nos aseguramos de encontrar accesorios de
la lı́nea Pic, que también sirven para los DSPic (como el Pickit 2). También consideramos
que en caso de precisar algún tipo de soporte técnico para nuestros DSPic vamos a poder
encontrar gente con más experiencia de la que encontrarı́amos con el resto de las marcas.
Dado que es evidente que con cualquier microcontrolador que trabajemos vamos a llegar
a etapas en las cuales vamos a tener algún problema que nos cueste mucho resolver,
consideramos fundamental utilizar un microcontrolador que disponga de mucha ayuda en
internet.
58
las herramientas que utilizamos de programación son de la compañı́a Microchip.
Durante el trabajo con la lı́nea DSPic (tanto en las pruebas previas como luego
de decidir que este va a ser nuestro microcontrolador) nos surgieron algunos problemas,
tanto de programación para hacer más eficientes ciertos códigos como para hacer funcionar
ciertas herramientas del microcontrolador. Cada vez que enviamos estas dudas al servicio
en lı́nea de microchip este no solo respondió rápido si no que también con respuestas
concretas y útiles.
La pagina web de microchip también cuenta con una gran cantidad de ejemplos de
códigos escritos para la lı́nea DSPic con el mismo programador que utilizamos nosotros.
En estos ejemplos se muestra el uso de cada una de las herramientas que el DSPic dispone.
Estos ejemplos nos fueron muy útiles para la elaboración de nuestro código.
Ası́ como es importante tener ayuda desde el servicio técnico de microchip, también
es importante que exista ayuda en los foros. La lı́nea DSPic tiene una enorme cantidad de
ayudas y foros dedicados a todas las lı́neas de procesadores inclusive los DSP. En los foros
encontramos información muy útil, en especial problemas similares a los experimentados
por nosotros y posibles soluciones para estos. Estas páginas nos fueron tan útiles como
la información brindada por microchip.
Los comentarios en internet de la experiencia que tuvo la gente que utilizó esta lı́nea
de DSP son muy alentadores en su mayorı́a.
9.2.4 Presentación
Los DSPic que utilizamos vienen en una versión con encapsulado SPDIP, dicho encap-
sulado permite colocarlo en un sócalo y luego poder modificar la ubicación del sócalo
de forma muy fácil y sin riesgo para la integridad del microcontrolador. Muchos mi-
crocontroladores vienen exclusivamente en presentaciones QFN, SOIC o similar, dichas
presentaciones no están diseñadas para permitir cambiar de posición el microcontrolador
más de una vez. Dichas presentaciones son para soldar en una placa con toda ya armado.
Utilizar microcontroladores en presentación SPDIP fue una gran ventaja para nosotros,
sobre todo en la etapa en la cual desarrollamos el sistema. La imagen 16 es una foto que
ilustra como es el encapsulado SPDIP.
59
9.2.5 Experiencia previa
En otros casos un microprocesador tiene cierta facilidad, pero necesita cierto equipo
para funcionar, o la realiza con alguna limitación. En muchos casos un usuario puede no
notar esto hasta que se encuentra en la situación en la cual pretende utilizar la facilidad.
Por estas razones consideramos muy importante utilizar un procesador con el cual
tengamos experiencia previa de trabajo. Si bien no tenemos experiencia de trabajo con
ningún DSPIC, si la tenemos en procesadores anteriores de la compañı́a Microchip, esto
consideramos que nos puede ser de mucha ayuda, tanto en la interpretación de la hoja de
datos, como en la solución de problemas a la hora de implementar el hardware. También
tenemos experiencia en el manejo del MPLAB y del PicKit 2 (para programar Pics).
60
5. 10 entradas analógicas.
7. Interfaz I 2 C.
8. La versión QFN viene en tamaño de 6mm por 6mm por un precio de U$S 3,72 al
por mayor.
61
10 Arquitectura externa del sintetizador
Luego se encuentran siete controladores utilizados para manejar los efectos y por
último una salida de audio.
62
11 Instrucciones de uso del sintetizador
El primer paso para el uso consta de la conexión del sintetizador a las fuentes de corriente
correspondientes. Actualmente es necesario conectar el sintetizador a varias fuentes de
tensión, cada una con una tensión distinta. Los DSPic se alimentan con 3.5V, los integra-
dos MAX-232 con 5V y por último el amplificador lo hace con ±9V. En caso de querer
comercializar el producto consideramos fundamental lograr que se pueda conectar a una
única fuente.
Luego hay que conectar el instrumento por la entrada MIDI y parlantes por la salida
de audio, con eso basta para la reproducción de instrumentos.
El sintetizador consta con la capacidad de aplicarle a la señal de salida hasta dos efectos
digitales, para controlar los efectos se utiliza la caja de manejo de efectos, dicha caja esta
esquematizada en la imagen 18 con cada una de sus perillas numeradas. Consta de dos
perillas discretas para la selección de efectos (1 y 4), y de otras cinco perillas que son para
el ajuste de los parámetros de los efectos.
El manejo del segundo efecto digital es igual al anterior pero manejando las perillas
4,5 y 6 en lugar de las 1,2 y 3 respectivamente. El sistema no funciona bien si se le aplica
dos veces el mismo efecto a la señal de salida.
63
11.2.1 No aplicar el efecto
Tal cual indica el nombre esta posición es para no aplicarle efectos a la salida generada.
Este efecto consta en sumarle a la señal de salida una señal generada anteriormente. El
primer parámetro del efecto indica el tiempo de retraso que tiene la señal que se le suma
a la de salida. Si este parámetro esta en el mı́nimo a la señal de salida se le suma la señal
salida y si esta en el máximo a la señal de salida se le suma la misma señal pero retrasada
253 milisegundos.
El efecto eco consta de un segundo parámetro que regula la magnitud con la que la
señal retrasada es sumada a la de salida. Si el segundo parámetro esta en el mı́nimo a
la señal de salida se le suma la señal atrasada multiplicada por cero (es como si no se le
sumara) y si esta en el máximo se le suma la señal atrasada multiplicada por 1.
Este efecto lo que hace es multiplicar la señal de salida por una constante ‘C1’ y sumarle
la salida multiplicada por otra constante ‘C2’ y por una señal sinusoidal. Si el primer
parámetro de este efecto esta al mı́nimo, entonces ‘C1’ es 1 y ’C2’ es 0, mientras que si
esta al máximo ‘C1’ es 0 y ’C2’ es 1.
Al igual que el efecto eco, este efecto también hace uso del segundo parámetro,
si esta al mı́nimo la frecuencia de la señal sinusoidal es mı́nima y si esta al máximo es
máxima.
Básicamente este efecto lo que hace es atenuar las altas frecuencias de la señal de salida.
Utiliza solo un parámetro que si esta al máximo no se atenúa ninguna frecuencia, mientras
que si esta al mı́nimo se atenúa todas, en los casos intermedios se atenúan solo a partir
de una frecuencia ‘f’ que depende la posición del valor del parámetro. Musicalmente se
aumenta la resonancia de la nota haciéndola sonar parecida a la voz humana diciendo la
sı́laba wah, de ahı́ el nombre.
64
entonces x vale cero (no se escucha la salida en este caso) y si esta al máximo entonces x
es el máximo valor que puede adquirir la señal de salida (es como no aplicar el efecto).
Este efecto modifica todas las frecuencias de la señal de salida. Utiliza un solo parámetro
que si esta al mı́nimo el sonido suena más grave y si esta al máximo el sonido suena más
agudo.
65
12 Arquitectura del Sistema
Dado que cada microcontrolador solo podrá generar una sola nota a la vez y el objetivo
del proyecto es poder generar sonidos polifónicos, es necesario que el sintetizador conste
de varios DSPic. Hay que diseñar una arquitectura que permita que varios DSP puedan
trabajar en conjunto para obtener ası́ sonidos polifónicos. En esta sección se explica el
diseño de dicha arquitectura.
12.2 Entradas
El sistema consta de dos entradas, una MIDI que se utiliza para la conexión con el
instrumento musical y una RS-232 que sirve para conectarse con un PC.
12.2.1 MIDI
Las conexiones utilizadas para lograr descifrar los comandos MIDI ya fueron mencionados
previamente en la secciones de Prueba Previa y detección de comandos MIDI.
12.2.2 RS-232
12.2.3 Efectos
El manejo de los efectos se realiza mediante la caja de manejo de efectos, cuyo fun-
cionamiento fue detallado en el manual de usuario del sintetizador. La selección del
sistema para la elección de efectos y la configuración de sus parámetros permite tener una
cantidad significativa de efectos evitando la necesidad de sobrecargar al dispositivo de
perillas, volviendo más complejo el uso para el usuario. Cada preset agregado significa un
pin más de nuestro microcontrolador ocupado, por lo que de tener una gran cantidad de
66
potenciómetros en nuestro sistema requiere de otro microcontrolador para poder cubrir
la demanda de entradas analógicas.
En esta sección se verá con mayor detalle cómo es que se procesa la información llegada
por la entrada MIDI hasta obtener el resultado final en la salida de audio.
Toda información proveniente de afuera ya sea un comando MIDI, los datos nece-
sarios para cargar el nuevo instrumento o un cambio en los parámetros de los efectos
llegan únicamente a un DSPic que llamaremos “maestro”. El maestro tiene 10 dspic que
llamaremos “esclavos” entre los cuales deberá distribuir las acciones a realizar. Dependi-
endo de que tipo de información se haya recibido el maestro decidirá si es necesario hacer
un broadcast, es decir enviar la información a todos los pics esclavos o enviarla a un único
pic.
Para efectuar la comunicación entre los pics se utilizó el protocolo I 2 C, esto se debe
a que es un protocolo estándar diseñado para comunicar integrados que se encuentran en
un mismo circuito impreso.
67
12.3.1 I 2C
8
Diseñado originalmente por Phillips en el año 1992, el I 2 C es un bus de comunicación
en serie y su nombre viene de Inter-Integrated Circuit (Circuitos Inter-Integrados). Su
velocidad en modo estándar es de 100Kbits/s aunque permite velocidades de hasta 3.4
Mbits/s. Dado que no tenemos grandes requisitos en la veolcidad de transmisión de datos,
utilizamos como velocidad 100Kbits/s. Es muy usado en la industria de los microcontro-
ladores para comunicar circuitos integrados que residen en un mismo circuito impreso.
I 2 C utiliza dos lı́neas para transmitir la información, una por la que envı́a los datos
(SDA) y la otra carga la señal del reloj (SCL), en el caso de que se comunicaran cir-
cuitos que no comparten la misma masa se deberá agregar un lı́nea más para usar como
referencia.
Los dispositivos conectados al bus I 2 C tiene una dirección única para cada uno y
pueden ser maestros o esclavos. El dispositivo maestro inicia la transferencia de datos y
genera la señal de reloj. El bus del I 2 C es multi-maestro, esto significa que no exige que
el maestro sea siempre el mismo dispositivo, sin embargo en el caso de nuestro sistema el
pic maestro se mantendrá fijo y será el único que comience las transmisiones.
El bus esta libre cuando SDA y SCL están en estado lógico alto. El maestro inicia
la comunicación enviando un patrón conocido llamado start condition, esto alerta a los
dispositivos esclavos poniéndolos a la espera de una transmisión. Luego el maestro envı́a
un byte que contiene los siete bits que componen la dirección del esclavo (A7-A1) y el
octavo bit de menor peso corresponde con la operación deseada, ’1’ lectura y ’0’ escritura.
La dirección enviada es comparada por cada esclavo del bus con su propia dirección,
en caso de coincidir el esclavo prestará atención a la siguiente información enviada por el
maestro, caso contrario el esclavo se mantendrá ocioso hasta que en la próxima transmisión
vuelva a recibir la condición de arranque y verifique si el maestro desea comunicarse con
él.
68
para el bus estar en idle (estado ocioso) durante unos microsegundos.
Una vez que el DSPic maestro recibe una nota MIDI se la envı́a a uno de los esclavos,
cuando recibe la siguiente nota se la envı́a al esclavo en la próxima dirección, una vez que
llega a la última dirección comienza nuevamente desde la primera. Cuando un esclavo
recibe la orden de generar una nota transmite la señal de audio resultante a través de
su salida analógica. Luego, las salidas generadas por todos los esclavos son sumadas
mediante el sumador analógico, después pasan por el saturador analógico y finalmente son
emitidas por la salida de audio. El saturador analógico funciona como un amplificador
cuya ganancia es controlada mediante uno de los potenciómetros de la caja de efectos,
para ganancias altas satura.
Cada esclavo esta encargado de aplicarle los efectos a la nota que esta generando. Cada
vez que el maestro detecta un cambio en una de sus entradas analógicas (las que están
conectadas a los potenciómetros que regulan los efectos) envı́a a todos los esclavos los
nuevos parámetros para el efecto modificado. Dado que la entrada analógica del maestro
tiene una precisión de 10 bits y que los parámetros de los efectos son de 7 bits, si un cambio
en la posición de un potenciómetro no es suficientemente significativo no es transmitido.
Existen dos soluciones para este problema, pero en ambas se deberı́a sacrificar la
69
calidad del sonido generado. En primer lugar se podrı́an reproducir sonidos más simples,
con menor cantidad de armónicos y en vez de una envolvente por armónico agrupar las
frecuencias armónicas de a tres o cuatro para que compartan la misma envolvente. Por
otro lado también existe la posibilidad de disminuir la frecuencia de conversión del DAC,
de esta forma se dispondrı́a de mayor tiempo para realizar las debidas operaciones.
Se consideró que la calidad y fidelidad del sonido son los elementos básicos del
sistema y el sacrificarlos por obtener quizás un producto más económico no tendrı́a sentido.
Para lograr que la salida analógica del DSPic pueda ir desde -3,5V hasta 3,5V sin necesidad
de utilizar fuentes negativas, esta se presenta como la diferencia de potencial entre dos
patas DAC1RN y DAC1RP. De esta forma si la pata DAC1RP que es la considerada
positiva tiene 3,5V y la otra 0v entonces la diferencia de potencial es 3,5V, y en el caso
de que sea al revés se tiene una diferencia de -3,5V.
Esta forma de presentar la salida, si bien tiene sus ventajas, hace que el circuito
sumador necesario no sea exactamente un sumador, dado que un sumador tiene n entradas
y una salida que vale la suma de la diferencia de potencial entre cada una de esas entradas
y tierra.
Para simplificar el análisis, este circuito fue ilustrado solo para 3 DSPic, utilizamos
la nomenclatura V1x y V2x para referirnos a el voltaje respecto a tierra de cada pata de la
salida analógica del DSPic x.
VI
I8 = R1
⇒ VI = I8 ∗ R1
V21 −VI
I4 = R
V22 −VI
I5 = R
V23 −VI
I6 = R
70
Imagen 20: Circuito sumador
V21 − VI V22 − VI V23 − VI
VI = I8 ∗ R1 = (I4 + I5 + I6 ) ∗ R1 =
R + + ∗ R1
R
| {z } | {z } | {z } R
I4 I5 I6
R1 R1 R1
VI = (V21 + V22 + V23 − 3 ∗ VI ) ∗ ⇒ VI 1 + 3 = (V21 + V22 + V23 ) ∗
R R R
R1 R1
VI 1+3 = (V4 + V5 + V6 ) ∗
R R
3R1 R1 R1
VOU T = VI 1+ −(V11 + V12 + V13 )∗ = ((V21 + V22 + V23 ) − (V11 + V12 + V13 ))∗
R R R
La salida del sumador es la buscada, es decir la suma de una de las patas de todos los
microcontroladores menos la suma de la otra de las patas, multiplicado por la constante
R1
R
, dicha constante será asignada de tal forma que la suma de todos los voltajes de salida
no pueda superara el máximo voltaje que el sumador puede entregar.
Dado que a la salida de este circuito se coloca otro circuito con alta resistencia de
entrada (se coloca el saturador analógico que se explica a continuación), no tenemos requi-
sitos respecto a la resistencia de salida del amplificador. Como también los voltajes que
manejamos no son voltajes extremadamente chicos (del orden de los microvoltios) tam-
poco hay requisitos respecto a la ganancia del amplificador operacional. También como
la intensidad de salida del DSPic puede alcanzar los 10 mA, que no es extremadamente
chica, podemos utilizar R = 1k y no exigir que el amplificador operacional tenga una
resistencia de entrada muy grande. Como conclusión, cualquier amplificador operacional
que hay en plaza nos sirve. Se utilizó el amplificador 741.
Como se explicó anteriormente, cada DSPic calcula por si mismo la nota que tiene que
generar y le aplica el efecto correspondiente, luego se suman todas las salidas generadas.
Idealmente habrı́a que sumar todos los sonidos de las notas y luego aplicarle los efectos.
Si el efecto es un efecto lineal, como el eco en donde la suma de los ecos de dos sonidos es
igual al eco de la suma del sonidos, no hay problema con que cada DSPic calcule los efectos
de su nota y luego se sumen los resultados. Pero en el caso de la saturación no ocurre lo
mismo, por eso aparte de las saturaciones digitales, se decidió agregarle al circuito una
saturación analógica, controlada con un potenciómetro.
Esta saturación consiste en pasar la señal generada por un circuito amplificador cuya
ganancia depende del valor de resistencia de un potenciómetro, de tal forma si el valor de
la resistencia se acerca al máximo el amplificador satura. En la imágen 21 se muestra el
circuito del saturador.
72
Imagen 21: Circuito saturador
V
I1 = = I2
R1
0 − VOU T V R2
I2 = = ⇒ VOU T = −V ∗
R2 R1 R1
R2
VOU T = −V ∗ R 1
se cumple mientras que el valor absoluto de VOU T no sea cercano
a VM AX , si se acerca el amplificador comenzará a distorsionar.
Dado que el parlante que se utilizará para reproducir los sonidos tiene alimentación
propia, exige poca corriente del amplificador, por lo que no tenemos requisitos de potencia
a la salida del amplificador operacional. Al igual que para el caso del sumador, tampoco
tenemos requisitos exigentes en los otros aspectos del amplificador operacional,cualquier
amplificador operacional de plaza nos sirve, por lo que también utilizamos el 741. Análogamente
con el caso anterior se consideró para este análisis un amplificador ideal.
La caja de efectos consta de cinco potenciómetros, uno de los cuales es destinado a cambiar
la resistencia R2 del saturador analógico, por eso la caja de efectos consta de dos cables
de salida que tienen entre si la resistencia de dicho potenciómetro.
Dado que las entradas analógica del DSPic detectan voltajes entre 0 y 3,5V, la caja
de efectos debe “traducir” la posición de las perillas relacionadas con los efectos digitales
73
a tensiones de ese rango. Para eso consta de cuatro potenciómetros que cada uno tiene
tres patas, una en un extremo de su resistencia, otra en el otro y una tercer pata en un
punto intermedio de la resistencia que depende de la posición de su perilla. Se conecta a
la primer y segunda pata mencionadas de los potenciómetros 0 y 3,5v respectivamente y
de esta forma en la tercer pata mencionada, dado que se forma un divisor resistivo, hay un
voltaje entre 0 y 3,5V que depende de la posición de la perilla, dicho voltaje es el que se
conecta a la entrada analógica del maestro. De esta forma se conectan los potenciómetros
dedicados a la variación de los parámetros de los efectos.
A diferencia de las perillas para modificar los parámetros de los efectos, las perillas
de selección de efectos deben ser discretas, por esa razón no se puede implementar exac-
tamente el mismo sistema para las perillas de selección de efectos que el que se utiliza
para sus parámetros. Para las perillas de selección de efectos se utilizaron llaves rotativas
multiposición (llaves que tienen una cantidad discreta de posiciones, que cada una cierra
otro circuito) para crear un potenciómetro pero de posiciones discretas, luego se procedió
igual que para las perillas de variación de parámetros de los efectos.
Para el maestro y los circuitos analógicos los circuitos fueron soldados en placas
genéricas, mientras que para los esclavos se utilizó un diseño especı́fico PCB Printed
Circuit Board (Circuito Impreso).
La idea detrás del diseño fue permitir que las placas se pudieran interconectar,
ventaja enorme en el caso de que una placa fallase o que se deseen agregar más esclavos
al sistema. Se conecta uno de los PCB con la salida del circuito maestro y luego se
74
Imagen 23: Circuito de los esclavos
conecta por el otro costado otra placa PCB, a esa otra placa PCB se le conecta otra y ası́
sucesivamente hasta conectar la cantidad deseada.
En la imágen 24 se observan las previsiones en los extremos para que puedan ser
conectados entre otras placas. Por otro lado se tomo la recomendación de evitar el ruteo
en ángulos rectos y se observan únicamente ángulos de 45 grados. Por la cantidad de
conexiones fue inevitable agregar pads para permitir realizar puenteos.
75
13 Entorno de programación de los DSPic
9
Para la programación de los DSPic utilizamos el programa MPLAB (de Microchip) con
el compilador de lenguaje ‘C’ que Microchip provee. Utilizamos la versión gratuita para
estudiantes, esta versión tiene las mismas funciones que la versión profesional, pero no
tiene todas las optimizaciones de códigos que la versión profesional tiene. A continuación
se presentan las caracterı́sticas que nos fueron más útiles del compilador.
Es compatible con todas las herramientas del MPLAB, lo cual permite realizar
simulaciones tanto directamente en el DSPic como por software en el MPLAB viendo
como avanza el programa tanto en el código de assembler como en el código ‘C’.
Tiene varios modos de optimización de códigos, cada modo con una relación par-
ticular entre el tamaño en memoria del código generado y la velocidad de ejecución
del código.
Los 128 kb de memoria interna del DSP que utilizamos están divididos en páginas de
32kb, por defecto para optimizar los tiempos de ejecución el compilador no perite utilizar
más de una página. Como nuestro programa requiere más de 32kb de memoria utilizamos
el comando psv const X attribute ((space(psv))) siendo X un tipo de variable válido
(como int, long int, etc) antes de los nombres de todas las variables que queremos que
se localicen en una página que no sea la que se usa por defecto, de esta forma podemos
utilizar toda la memoria del DSP.
9
Microchip.2009.MPLAB C Compiler for dsPIC DSCs
76
14 Explicación del código del Maestro
El maestro es la puerta de enlace de los esclavos con el mundo de afuera, éste esta encar-
gado de reenviar a través del bus I 2 C toda información pertinente y filtrar el resto.
Desde el punto de vista del maestro, cada esclavo se comporta como una memoria externa
de 20 posiciones. La comunicación entre el maestro y los esclavos se realiza siguiendo el
mismo protocolo de comunicación que se realizarı́a entre un DSPic y varias memorias
externas.
De las 20 posiciones de memoria accesibles por I 2 C que tiene cada esclavo, cada
una tiene su función especı́fica. Cuando el maestro quiere transmitir cierto tipo de dato,
simplemente manda grabar el nuevo valor en la posición de memoria correspondiente al
tipo de dato que se esta enviando. Por ejemplo, los esclavos reconocen que si se les
ordena grabar un valor en la posición 1 de la memoria significa que este dato representa la
velocidad con la que deberán reproducir la nueva nota MIDI. En la sección de explicación
del código del esclavo se detalla que información almacena cada posición de memoria.
Los comandos MIDI son manejados con la UART número 1 del maestro. Cuando el
instrumento MIDI envı́e cualquier información por su salida MIDI-OUT se generará una
interrupción en el DSPic maestro. Como se vio en la explicación de comandos MIDI en
la sección ‘conceptos avanzados’, los comandos Note-Off son los “1000cccc” y los Note-
On los “1001cccc”, entonces cuando se reciba un valor mayor a 127 y menor a 144 se
levantará una bandera llamada flagNoteOff, si se recibe un comando de activación de
Nota se levantará la flagNoteOn, luego se levantará otra bandera para indicar que los
próximos dos datos a recibir serán los parámetros de estos comandos (nota y velocidad).
Luego de recibir estos dos parámetros, se levanta una bandera para indicar que ya
se tiene toda la información que el DSPic necesita para reproducir la nota.
14.3 Interrupción PC
77
indicando que se comenzaran a recibir los datos del nuevo instrumento.
Al comienzo del main se configuran cuales pines del DSPic actuarán de forma digital y
cuales de forma analógica, se setea la velocidad de trabajo en 40MIPs, la configuración
de ambas UARTs, el mapeo de sus puertos y las inicializaciones del I 2 C y del conversor
analógico-digital.
Dentro del loop infinito el DSPic irá verificando una por una sus seis entradas analógicas
para detectar si ha habido un cambio en cualquiera de ellas. Las entradas analógicas del
DSPic tienen una resolución de 10 bits, es decir divide el intervalo de voltajes de entrada
válido en 1024 valores. Para asegurarnos que el cambio que se detectó haya sido realizado
por el usuario y no que sea un tema de sensibilidad del conversor análogico a digital, se
implemento un mecanismo de seguridad que sólo considera que ha habido un cambio si la
variación con respecto al último valor guardado es mayor a 20. El número 20 fue elegido
luego de observar la oscilación del conversor cuando las perillas se mantenı́an quietas.
Luego de realizada la división es necesario informar del cambio a todos los esclavos.
Como I 2 C no permite una función de broadcast, esto se debe realizar uno por uno medi-
ante una estructura del tipo for.
El maestro enviará los dos parámetros para reproducir la nota (que nota es y con que
intensidad fue presionada) al próximo esclavo que corresponda. La selección del esclavo
se realiza en orden a partir del que tiene la dirección de menor valor, luego con el de la
dirección siguiente (todos los esclavos tiene direcciones consecutivas) y ası́ sucesivamente
hasta llegar a la última dirección. Después de enviar una nota al esclavo con la dirección
de I 2 C más alta se vuelve a comenzar con el de dirección más baja. La información de que
nota se ha enviado a cada esclavo se almacena en un array, esta información se utilizará
cuando se reciba un comando de Note-Off.
78
14.4.3 Función quitarNota()
Luego de efectuadas las cincuenta rondas necesarias para enviar los cien valores, el
maestro le envı́a un dato a la PC para indicarle que puede empezar a enviar los siguientes
cien valores. Luego de recibir todos los valores que representan al nuevo instrumento, el
maestro le envı́a un byte de confirmación a la PC para avisarle que la transmisión ha
finalizado con éxito, luego abandona el while para volver al main.
79
15 Explicación del código de los esclavos
Toda la generación de notas comienza con mensajes recibidos desde el maestro, también
la selección de efectos y la configuración de instrumentos, por eso la comunicación I 2 C
consideramos que debe ser la primera en ser explicada.
Los esclavos están programados para responder las cadenas I 2 C siguiendo el proto-
colo diseñado para la comunicación entre un maestro y una memoria externa actuando de
esclava, los DSPic esclavos actúan como el protocolo indica que debe actuar una memo-
ria externa. Decidimos utilizar este protocolo, dado que al ser un protocolo conocido y
probado tiene menos probabilidades de tener errores, además de una mayor eficiencia. El
protocolo es explicado en la sección “Arquitectura Interna del Sistema”.
Cada esclavo tiene una variable llamada RAMBuffer que es un vector de 20 posi-
ciones del tipo short int, también tiene una dirección de I 2 C para identificar cuando un
pedido u orden va dirigido a él. Cuando un esclavo detecta que se están comunicando con
él, si le piden que escriba en cierta posición de memoria escribe en esa posición del vector
RAMBuffer y cuando la instrucción es de lectura de cierta dirección envı́an el valor de la
posición de memoria del vector RAMBuffer requerida. En la tabla 6 se explica para que
se utiliza cada elemento del vector RAMBuffer.
Esta sección explica como hace el sistema para simular ser una memoria externa y que
es lo que hace internamente cuando detecta información de una nueva nota y cambio de
efectos.
Para simular el comportamiento de una memoria utiliza una estructura que se llamó
flag, que es un conjunto de banderas. También hay un puntero llamado RAMPtr que al
iniciar el programa se lo hace apuntar al primer elemento de RAMBuffer. La detección
de mensajes por I 2 C es interruptiva.
80
Byte estado Descripción Rango de Valores
RAMBuffer [0] Nota a reproducir 0 al 85
RAMBuffer [1] Velocidad de la nota 0 al 127
RAMBuffer [2] Parámetro 1 del efecto 1 0 al 127
RAMBuffer [3] Parámetro 1 del efecto 2 0 al 127
RAMBuffer [4] Parámetro 2 del efecto 1 0 al 127
RAMBuffer [5] Parámetro 2 del efecto 2 0 al 127
RAMBuffer [6] Primer efecto a realizar 0 al 7
RAMBuffer [7] Segundo efecto a realizar 0 al 7
RAMBuffer [8] Previsión 0 al 255
RAMBuffer [9] Previsión 0 al 255
RAMBuffer [10] Byte más significativo del dato del nuevo instrumento 0 al 255
RAMBuffer [11] Byte menos significativo del dato del nuevo instrumento 0 al 255
RAMBuffer [12] Se guarda el CRC recibido del maestro 0 al 255
RAMBuffer [13] Se guarda el CRC calculado por el esclavo 0 al 255
RAMBuffer [14] Previsión 0 al 255
RAMBuffer [15] Indica inicio recepción del nuevo instrumento 0 al 1
RAMBuffer [16] Previsión 0 al 255
RAMBuffer [17] Previsión 0 al 255
RAMBuffer [18] Previsión 0 al 255
RAMBuffer [19] Previsión 0 al 255
En caso de que sea escribir se levanta la bandera F lag.AddrF lag, esta nos recuerda
que el próximo dato a recibir indicará cual es la posición de memoria en la cual se deberá
grabar el dato, una vez que llega este char subaddr, se baja la bandera F lag.AddrF lag
y se eleva la Flag.DataFlag, enseguida después se guarda el dato recibido en la variable
T emp, luego realiza RAM P tr = RAM P tr + T emp, de esta forma RAMPtr pasa a valer
la posición de memoria donde hay que guardar el próximo dato a recibir. Previo a recibir
el último dato también se fija dependiendo de cual es la posición de memoria donde se
almacenarán los datos, cual va a ser la acción a hacer luego de almacenarlos. Por ejemplo
si la dirección de memoria donde se almacenarán los datos es la 1, significa que el próximo
dato a transmitir es la velocidad de una nota, con lo que se finalizarı́a la transmisión de una
instrucción de reproducción de nota, por lo que se activarı́a la bandera recibiP unV el para
avisar que cuando se reciba el próximo dato vı́a I 2 C hay que activar la bandera bandera
que indica que hay una instrucción MIDI pronta para procesar. Cuando se reciba el char
value, este será almacenado en *RAMPtr.
81
15.2 Generación de sonidos de notas
Una vez detectado que hay que generar cierta nota, se utilizan las matrices to-
dosLosCambios,todosLosIncrementos y envolsIniciales para obtener los datos para crear
las 15 envolventes y se utiliza la matriz todosLosAumentosSeno para obtener las 15 fre-
cuencias a utilizar para generar la nota. Estas matrices tienen los datos para generar
todas las envolventes y frecuencias de todas las notas, lo primero que hace el programa
al detectar que hay que reproducir la nota X es copiar en la matriz envols los datos de
la matriz envolsIniciales correspondientes a la nota X, en la matriz aumentosSeno todos
los datos de la matriz TodosLosAumentosSeno correspondientes a la nota X, en la matriz
incrementos todos los datos de la matriz todosLosIncrementos correspondientes a la nota
X y en la matriz cambios todos los datos de la matriz todosLosCambios correspondientes
a la nota X.
Dado que es necesario saber cuantos samples se van calculando de una nota para
poder saber si llegó el momento de cambiar el coeficiente angular de una envolvente,
entonces es necesario tener una variable que se llama ciclos que es la que indica la cantidad
de samples calculados de la nota que se esta reproduciendo. Esta variable se inicia en
cero y se incrementa en uno luego de calcular cada sample.
El matriz envolsIniciales tiene los datos de los valores iniciales de cada una de las
envolventes de cada nota. El vector envols es un vector de 15 posiciones que almacena
el valor de cada envolvente, dicho valor es actualizado al generar cada sample. Como la
matriz envolsIniciales es la que tiene los datos de los valores iniciales de cada envolvente,
antes de comenzar a generar los samples, es necesario hacer que el vector envols tome los
valores iniciales de las envolventes.
La matriz incrementos, es una matriz de 15 por 7 que indica cuanto debe ser el
incremento (positivo o negativo) de cada una de las envolventes luego de transcurrido
cada ciclo. A cada envolvente le corresponden 7 incrementos, el primero es con el cual se
incrementa hasta llegar a la cantidad de ciclos que indica la primera posición de la matriz
cambios con respecto a esa envolvente, luego se le aplicará el segundo incremento hasta
llegar a la segunda posición de la matriz cambios y ası́ sucesivamente. De esta forma
quedan determinadas todas las envolventes.
82
0 ∗N
que corresponda y luego multiplicar cada envolvente por sen( 2∗π∗f Fs
). Siendo f0 la
frecuencia correspondiente a la envolvente, N el número de sample que se esta generando
y F s la frecuencia de generación de valores a la salida.
El sonido generado tiene una resolución de 16 bits, pero para mejorar la precisión en la
generación de este lo que se hace es calcularlo como un número de 32 bits y luego devolver
como resultado los 16 bits más significativos.
Dado que la cantidad de samples en los cuales se desea generar un cambio en el coefi-
ciente angular de una envolvente puede ser mayor a 65535, la matriz cambios es una matriz
de variables del tipo long int. Pero, para ahorrar memoria, la matriz todosLosCambios
es una matriz de variables del tipo unsigned int. Los números que deberı́an estar en la
matriz todosLosCambios pueden no entrar en variables del tipo unsigned int, por esa
razón en la matriz todosLosCambios se almacenan los números que deberı́an estar, pero
divididos entre 10. Luego al pasar los datos de la matriz todosLosCambios a la matriz
cambios, se los multiplica por 10.
El vector envols es también de variables del tipo long int, esto se debe a que si
usáramos variables del tipo int, aunque el incremento que hay que aplicar a una en-
volvente fuera el mı́nimo no nulo posible (fuera 1), luego de calcular menos de 70000
samples la envolvente ya superarı́a el máximo valor posible. El valor de cada envolvente
es guardado como una variable del tipo long int, del cual al multiplicar por una sinusoidal
para calcular el valor de cada sample se consideran solo los dos bytes más significativos.
Las envolventes son consideradas variables del tipo long int al aumentarlas ası́, se puede
tener más precisión en los incrementos porque el total de valores que puede tener la en-
volvente es mucho mayor. Pero para calcular samples se consideran las envolventes solo
como variables del tipo unsigned int. El vector punteroM ascaras es un vector de 15
punteros a variables del tipo unsigned int, en el cada puntero en la posición X apunta
a los dos bytes más significativos del elemento en la posición X del vector envols, para
todo X entre 0 y 14.
También se utiliza un vector llamado indices de 15 posiciones del tipo int que se
inicializa todo con ceros. Este vector sirve para indicar hasta cual cambio llego cada
componente de cada nota, es decir que sirve para saber con que incremento hay que au-
mentar cada envolvente. Cuando la cantidad de samples generados es igual a un punto de
cambio de una envolvente, el elemento del vector indices correspondiente a esa envolvente
aumenta en uno.
Para calcular senos se utiliza una tabla llamada seno que es una tabla de variables
del tipo int de 15000 valores. El valor número n de la tabla seno es 511*seno( 2∗n∗π
15000
). A
su vez el método utiliza otro vector llamado tiempos que también es de 15 posiciones que
se inicializan todas en cero. Este vector sirve para saber que valor de la tabla seno es el
próximo que corresponde utilizar para cada envolvente. El vector aumentoSeno es el que
83
indica de a cuanto tiene que aumentar cada posición del vector tiempos luego de calcular
cada sample.
Para hallar la cantidad de saltos que se deben realizar para obtener una determinada
frecuencia podemos utilizar la ecuación:
fdeseada ∗ 15000
Saltos =
30KHz
fdeseada
Saltos =
2
Lo primero que hace el método es verificar si la variable nota vale 100, si ese es
el caso es porque el comando recibido fue el de noteoff, entonces modifica las variables
necesarias para que luego al generar cada sample se sepa que hay que ir disminuyendo la
intensidad de la nota hasta que esta se apague.
Si la variable nota no vale 100, graba en las variables nota y velocidad la nota que
hay que generar y la intensidad con la que debe ser generada respectivamente. Luego
inicializa todas las variables necesarias para la generación de notas, el valor con el que
son inicializadas y cuales son esas variables ya fue explicado.
Este es el método que calcula los samples de la nota a generar. Calcula 200000 samples
por nota, lo cual permite una duración del sonido de más de 6 segundos.
Lo primero que hace el método es verificar que no se hallan calculado más de 200000
samples, para eso verifica que la variable ciclos sea menor a 200000, si es mayor ya no
calcula más. A continuación se anuncia el procedimiento del programa en caso de que la
variable ciclos sea menor a 200000.
El primer paso es iniciar la variable salidaLong en cero. Después hace un proceso que
84
es igual para todas las envolventes, a continuación se explica el proceso para la envolvente
número i.
6. Las envolventes nunca deberı́an ser menores a cero, pero si por algún error de
cálculo pueden ser, por eso el método chequea que esto no pase, si pasa hace que la
envolvente para el próximo sample valga cero.
En esta explicación del proceso hay ciertos cambios en el orden de las acciones,
estos cambios de orden no afectan el funcionamiento del método, pero permiten un mejor
entendimiento de este. Algunas variables auxiliares fueron omitidas para que el método
quede más fácil de entender.
Este proceso se repite 15 veces, una por cada envolvente.También podrı́a implemen-
tarse con una estructura f or para evitar repetir código, pero de la forma actual el tiempo
que toma calcular cada sample se reduce en más de 100 ciclos de reloj. Dado que el ahorro
de tiempo en el cálculo de cada sample es nuestra prioridad, decidimos repetir código a
cambio de mejorar el tiempo de procesamiento.
85
Dado que calcular cada sample toma aproximadamente 1050 ciclos de reloj aprox-
imadamente. Decidimos hacer que la frecuencia de salida del DSPic sea de 30 KHz, lo
que da 1333 ciclos de reloj por sample. De esta forma logramos que todas las samples se
calculen en el tiempo necesario hasta para el peor caso y obtenemos una ecuación muy
simple para calcular los saltos que se deben realizar para obtener las frecuencias deseadas.
Como se explicó en la sección Arquitectura del Sistema, los efectos son controlados por
una serie de potenciómetros que están conectados con el maestro. El maestro cuando
detecta un cambio en alguno de los potenciómetros se los transmite a los esclavos. Ahora
se verá la explicación de como llega la información a los esclavos y de como hacen estos
para generar los efectos en base a esta información.
Luego de generar cada sample, el esclavo llama al método con el siguiente encabezado:
void grabarDato(int dato) pasándole como parámetro el último dato generado y al método
aumentarP unteroT remolo(). Estos métodos se explicarán más adelante cuando se ex-
plique los efectos para los cuales sirven.
Después llama dos veces al método aplicarEfectos (unsigned short int numEfec),
primero con parámetro 6 y luego con parámetro 7. El parámetro representa la posición
de memoria en el vector RAMBuffer donde se encuentra el número asociado al efecto
a realizar. Lo que hace el método es determinar en base al dato que se encuentra en
RAM Buf f er[numEf ec] que efecto hay que aplicar y aplicarlo con los parámetros corre-
spondientes, RAM Buf f er[2] y RAM Buf f er[4] para el primer efecto y RAM Buf f er[3]
con RAM Buf f er[5] para el segundo. Las posiciones de RAMBuffer[] 4 y 5 son sólo en
caso de que el efecto necesite dos parámetros.
Cada efecto tiene una o dos variables, todas son enteros, pero cada una con distinto
rango de valores válidos. El programa tiene una variable interna por cada una de las vari-
ables de cada efecto. Cuando esta activada banderaCambioEf ectos, se llama al método
void actualizarParametrosEfectos (unsigned short int numEfec), primero con parámetro
6 y luego con parámetro 7. El parámetro representa la posición de memoria en el vector
RAMBuffer donde se encuentra el número asociado al efecto a realizar. Lo primero que
hace el método es detectar cual es el efecto a aplicar para saber que variables es necesario
actualizar y luego actualizarlas en base al valor de la posición correspondiente del vector
RAMBuffer.
86
15.5.2 Explicación del método inicarEf ectos
Este método debe ser llamado una vez antes de comenzar a generar los efectos.
En muchos casos es necesario multiplicar una variable x, del tipo int por valores
menores a uno, las pruebas que realizamos nos dejaron como experiencia que la forma
más fácil de hacer esta multiplicación es guardar en una variable del tipo long int la
multiplicación de x por una variable z, del tipo unsigned int o int y luego tomar como
resultado los dos bytes más significativos del resultado de la multiplicación. De esta forma
z
el resultado termina siendo x ∗ ( 65535 ), lo cual es x multiplicada por un número menor a
1.
En la clase efectos hay una variable llamada valorLargo del tipo long int y un
puntero llamado paraDevolver, lo que hace el método iniciarEf ectos es hacer que pa-
raDevolver apunte a los dos bytes más significativos de valorLargo. De esta forma si
se desea multiplicar x por un número menor a 1 se lo multiplica por z, se almacena el
resultado en valorLargo y se toma como resultado lo apuntado por paraDevolver.
El método void grabarDato(int dato) graba el dato que se le pasa como parámetro en
el vector ultimos, que es un vector con capacidad para 7600 enteros del tipo int. El
primer dato lo guarda en la posición 0, el segundo en la 1 y ası́ sucesivamente, luego de
grabar en la posición 7599 vuelve a grabar en la posición 0. Sirve para que el efecto eco
tenga los datos anteriores almacenados. Como a este método se lo utiliza pasándole como
parámetro el último sample calculado, lo que hace es grabar los últimos 7600 samples
calculados en el vector ultimos. La última posición del vector ultimos donde se guardo
un dato se almacena en la variable punteroGuardar.
La idea del efecto eco es que la salida sea la salida que corresponde más una salida
anterior multiplicada por un número menor a 1. Por eso el efecto eco tiene dos parámetros,
uno llamado atrasoEco que es la cantidad de samples que esta atrasada la señal que se le
suma a el sonido generado y otro parámetro llamado magnitudEco que es una variable
del tipo unsigned int, el multiplicador mencionado vale magnitudEco
65535
.
Cuando se desea generar el efecto eco se ejecuta el siguiente comando: salidaAU tilizar =
salidaAU tilizar + eco(), siendo salidaAUtilizar el valor calculado del sample y el método
eco() es el que calcula la salida anterior que hay que sumarle al sample calculado.
Lo primero que hace el método eco es obtener el sample generado atrasoEco sam-
ples atrás. Para eso genera la variable indice que corresponde a la posición del vector
ultimos en la cual se encuentra el sample buscado. Si punteroGuardar es mayor o igual a
atrasoEco, entonces indice vale punteroGuardar menos atrasoEco, si punteroGuardar
es menor a atrasoEco, entonces indice vale 7600 + punteroGuardar − atrasoEco. De esta
forma indice siempre es un número no menor a cero y en ultimos[indice] se encuentra el
sample generado atrasoEco samples antes, este valor es llamado valorEco.
87
vector paraDevolver.
Dado que largo del vector ultimos es de 7600 samples y como Fs es igual a 30 khz,
el máximo atraso posible del eco es de 253 ms.
Lo que hace este efecto es multiplicar el sonido generado por una onda sinusoidal más una
constante. Tiene dos parámetros, uno llamado f recuenciaT emolo que es un número entre
1 y 9, si vale 1 la frecuencia de la sinusoidal es 2Hz y si vale 9 la frecuencia es 18Hz. El
otro parámetro es llamado magnitudT remolo que es un número entre 0 y 63. Si magnitud
trémolo vale 0, entonces la salida se deja tal cual es la entrada, si vale 63 se la multiplica
por una sinusoidal pura y si vale un valor intermedio entre ambos se la multiplica por una
sinusoidal más una constante. En la imagen 25, se puede observar como es la función por
la que es multiplicada el sonido para varios casos de magnitudT remolo.
Imagen 25: Gráfica de los multiplicadores que se aplican a la señal en el efecto trémolo
El método que se utiliza para aplicar este efecto es el método: int tremolo(int
valorActual) que recibe como parámetro el último sample calculado y devuelve ese sample
multiplicado por el valor correspondiente de la máscara.
88
El primer paso es calcular el valor de la máscara, lo que hace es calcular el valor
de la máscara multiplicado por 65535 mediante la siguiente fórmula: intmultiplicador =
(seno[punteroT remolo] − 512) ∗ magnitudT remolo + 32256 (la tabla seno tiene un rango
del -511 al 511). Luego multiplica valorActual por multiplicador y almacena el resultado
en valorLargo, finalmente devuelve el valor apuntado por el vector paraDevolver.
Este efecto lo que hace es pasar el sonido generado por un filtro pasabajos de frecuencia de
corte variable. Dicha frecuencia de corte es modificada al mover uno de los potenciómetros.
El filtro a utilizar es un filtro pasabajos Butterworth digital de segundo orden.
√
y(n−1)∗(2+ 2∗f0 )−y(n−2)+f02 ∗x(n)
La función del filtro es la siguiente: y(n)= √
1+ 2∗f0 +f02
, donde y(n)
es la salida número n, x(n) es la entrada número n y f0 es la frecuencia de corte dividido
la frecuencia de salida, es un número que varı́a entre 0 y 0,5. En este caso la entrada es
el sonido generado y la salida el sonido filtrado.
Para facilitar las cuentas decidimos pasar todos los números a enteros, por lo que en
la expresión de y(n) multiplicamos tanto el divisor como el dividendo por 16384=128*128,
por lo que quedó:
√
y(n − 1) ∗ (32768 + (128 ∗ 2) ∗ (128 ∗ f0 )) − 16384 ∗ y(n − 2) + (128 ∗ f0 )2 ∗ x(n)
y(n) = √
16384 + (128 ∗ 2) ∗ (128 ∗ f0 ) + (128 ∗ f0 )2
Dado que es más fácil manejar números enteros que reales, decidimos utilizar como
parámetro del método la variable frecCorte que vale 128*f0 , de esta forma frecCorte es
un parámetro que si vale cero indica que la frecuencia de corte es 0 y si vale 64 indica que
la frecuencia de corte es 0,5.
√
y(n − 1) ∗ (32768 + (128 ∗ 2) ∗ (frecCorte)) − 16384 ∗ y(n − 2) + (frecCorte)2 ∗ x(n)
y(n) = √
16384 + (128 ∗ 2) ∗ (frecCorte) + (frecCorte)2
Dado que los posibles valores de frecCorte son 64 (del 0 al 63, si se quiere tomar
el valor 64 es lo mismo que no aplicar el filtro), para ahorrar tiempo de procesamiento
se agregó una tabla llamada fCuadrado que indica para cada frecCorte cuanto vale al
cuadrado. Como el dividendo de la función de y(n) no depende de la entrada ni de las
salidas anteriores, se agregó también una tabla llamada diviWah que indica para cada
frecCorte cuanto vale el divisor. La variable y(n-1) también esta multiplicada por una
expresión que solo depende de frecCorte, por lo que también creamos una tabla llamada
parteMultiplicadores que para cada valor de frecCorte devuelve el valor de dicha expresión.
Entonces el filtro queda:
y(n − 1) ∗ (parteM ultiplicadores[f recCorte]) − 16384 ∗ y(n − 2) + (f Cuadrado[f recCorte]) ∗ x(n)
y(n) =
diviW ah[f recCorte]
89
Para implementar esta cuenta decidimos que primero valorLargo tome el valor del
divisor, al dividirlo entre el dividendo notamos que la operación tomaba cerca de 400
ciclos de reloj, lo cual nos pareció incoherente dado que la hoja de datos dice que las
divisiones de este tipo deberı́an tomar 17 ciclos más lo que tome pasar las variables a los
registros correspondientes. Decidimos crear la función asmFunction que esta dentro de la
clase call2.s para que realice esta división, de esta forma la división si tomó los ciclos es-
tipulados. La página web de microchip indica que la versión gratuita para estudiantes del
compilador de lenguaje ‘C’ para DSPic no posee todas las optimizaciones implementadas,
suponemos que esta fue la causa del problema.
señal de sonido. Este limitador esta diseñado para entradas y salidas normalizadas a 1,
como queremos que nuestra salida este normalizada a 32768, entonces hacemos:
!
X(t) X(t)3
y2 (t) = 32768 ∗ −A
32768 327683
.
X(t)3
y3 (t) = X(t) − limiteLog
327683
.
90
2
X(t)
apuntado por el vector paraDevolver, de esta forma valorLargo pasa a valer 65536 . Después
hace la siguiente operación: valorLargo=valorLargo*X(t) y vuelve a hacer que valorLargo
X(t)3
valga lo apuntado por paraDevolver, de esa forma valorLargo pasa a valer 65536 2. La
próxima operación es: valorLargo=valorLargo*limiteLog, de esa forma lo apuntado por
3
paraDevolver vale valorLargo∗X(t)
65536 3 , finalmente devuelve: X(t) menos 8 por lo apuntado por
paraDevolver.
Este efecto lo que hace es modificar las frecuencias de la salida de audio, para eso lo que
hace es modificar la frecuencia de generación de datos de la salida analógica.
Este efecto tiene un parámetro llamado ValorVibrato que es un número del 0 al 127
que depende de la posición del potenciómetro asociado al primer parámetro del efecto.
Para lograr modificar la frecuencia del DAC, lo que hace es fijarse si la variable
ValorVibrato es mayor a 85, si es aumenta la frecuencia de salida de audio, si es menor a
40 disminuye la frecuencia de salida de audio y si esta entre 40 y 85 la mantiene.
Las dos funciones comienzan fijándose en base a la posición de RAMBuffer que se le pase
por parámetro que efecto hay que aplicar. Luego la función aplicarEfectos aplica el efecto
y la función actualizarParametrosEfectos actualiza los parámetros del efecto.
Ambas funciones son llamadas primero con parámetro 6 y luego con 7, esto se debe
a que esas son las posiciones de la memoria RAMBuffer donde se almacenan los números
asociados a los efectos a reproducir. La tabla 7 muestra que número tiene asociado cada
efecto.
91
15.7 Funcionamiento del método main
Una vez realizadas las inicializaciones, el método ingresa en un bucle infinito el cual
primero calcula la salida mediante el método calcularSample, luego graba el dato para
que pueda ser utilizado por el efecto eco y también aumenta el puntero para el efecto
trémolo, después le aplica los efectos a los sonidos generados y espera a que la cola de
espera de la salida analógica (DAC) tenga un lugar libre para insertar el dato que generó.
Finalmente se chequea si es necesario actualizar los datos de los parámetros o si hay una
nota nueva para generar, en ambos casos llama al método necesario para hacer la acción.
92
16 Documentación del programa interfaz
Una vez configurados los pics y todo el hardware es necesario generar un software en PC
con la capacidad de comunicarse con nuestro sistema embebido y de transmitir tablas
con descripciones del funcionamiento de instrumentos musicales. La idea del software es
que maneje el protocolo de comunicación del sintetizador para poder trasmitir instrumen-
tos. Debe permitir que el usuario transmita una tabla de datos que generó indicando la
descripción de un instrumento.
El siguiente objetivo serı́a lograr que el mismo software provea al usuario de una
herramienta relativamente sencilla para la generación de instrumentos virtuales. Dicha
herramienta deberı́a permitirle al usuario generar cada una de las notas del instrumento,
esto significa seleccionar las frecuencias armónicas junto con sus envolventes. Ası́ mismo
también deberı́a permitir al usuario escuchar los sonidos generados antes de transmitirlos
al dispositivo.
Decidimos realizar este programa con el lenguaje Visual Basic simplemente por que este es
el lenguaje que mejor manejamos, es un lenguaje que permite realizar todas las funciones
que precisamos para el programa y existe en internet mucha ayuda disponible.
93
fueron agregadas en caso de que en un futuro se le deseen agregar funciones adicionales
al software.
94
17 Manual de instrucciones del programa interfaz de
comunicación
Para ejecutarlo se hace un doble clic sobre el archivo “Interfaz de comunicación con el
sintetizador.exe”. Si desea copiar el programa, copie el archivo “Interfaz de comunicación
con el sintetizador.exe” junto con el archivo ”nota musical.ico” a la ruta deseada.
Este programa se adapta al idioma local donde se ubique el usuario. Todos los
números deben ser ingresados en el formato en el cual este configurado el PC.
Para ingresar a este modo haga clic en el botón “Manejo de envolventes”, esto abrirá una
ventana llamada “Manejo de envolventes”, allı́ se podrán crear envolventes desde cero, ası́
como guardar y abrir otras envolventes.
Para crear un instrumento nuevo, en la “Ventana inicial”, haga clic en el botón “Crear
instrumento”, al programa le toma aproximadamente un minuto la creación del nuevo
95
instrumento. Se puede observar una barra que indica el estado de la creación del instru-
mento. La creación puede ocasionar que la ventana no responda durante el proceso.
Cada instrumento nuevo generado ya contiene todas sus notas y cada nota sus
envolventes y frecuencias.
Una vez creado el instrumento nuevo se abrirá una ventana con el nombre “Manejo
del instrumento” que es la que permite modificar, abrir y guardar instrumentos. Dicha
ventana se explicará más adelante.
Como se indico anteriormente, esta es la ventana que sirve para la creación y edición de
envolventes, en la imagen 27 se muestra como se ve inicialmente.
96
17.5.1 Crear una nueva envolvente de forma simple
Para crear una envolvente de forma simple primero se debe indicar el valor inicial de
la envolvente en el cuadro correspondiente. Si el valor que se desea ingresar es mayor
al máximo valor de la gráfica en el eje Y, para que este valor sea aceptado, primero es
necesario modificar el máximo valor del eje Y en la gráfica, para eso ingrese el nuevo
máximo valor deseado en el cuadro de texto a la derecha de la etiqueta “YMáximo” y
luego haga clic en confirmarY.
Una vez fijado el valor inicial simplemente haga clic en la gráfica en los siguientes
puntos por donde desee que pase la envolvente que esta creando. Observará que el pro-
grama va formando la gráfica de la envolvente resultante. Si lo desea, como se menciono
antes, es posible modificar los máximos valores de los ejes X e Y. Recuerde que cada
envolvente puede tener como máximo 6 puntos aparte del valor inicial. El programa no
permitirá que a un mismo valor del eje de las X le correspondan dos o mas valores distintos
en el eje de las Y.
Para modificar alguno de los puntos de una envolvente, en el grupo “Valor a mod-
ificar” seleccione el número de punto que desea modificar, siendo el 1 el primer punto
luego del valor inicial y el 6 el último punto, y luego haga clic en la gráfica en el lugar
donde desea reubicar el punto.
Nótese que si el puntero del mouse se ubica sobre la gráfica, en la esquina inferior
derecha de la ventana se indica en que posición exacta de la gráfica se encuentra el puntero.
En la imagen 28 se muestra una toma de pantalla de una envolvente con todos sus
puntos definidos.
Muchas veces se desea generar envolventes similares a otras que ya se tienen. Por esa
razón es que el programa permite generar envolventes que resulten de la extrapolación de
otras dos.
97
Imagen 28: Screenshot de “Manejo de envolventes, con una envolvente ya diseñada”
envolvente resultante será de 1,5, pero si el peso de la primer máscara es mayor, entonces
el valor inicial de la envolvente resultante estará entre 1 y 1,5. Hay un ejemplo de dos
máscaras ponderadas con este tipo de ponderación en el cd adjunto.
98
Imagen 29: Screenshot de la ventana que permite configurar la extrapolación
Para guardar una envolvente generada haga clic en la opción “Guardar Como” en el menú
“Archivo”, luego seleccione la ruta donde desea guardar la envolvente en la ventana que
aparecerá . La extensión de las envolventes será “.masc”.
Para abrir una envolvente simplemente haga clic en la opción “Abrir”, dentro del menú
“Archivo” y luego elija la ruta de la envolvente a abrir en la ventana de selección de
99
archivo que se abrirá.
Para ingresar a este modo, dentro de la ventana inicial ingrese en la opción “Transmitir
Instrumento”, esa acción abrirá una ventana llamada “Ventana de transmisión”.
100
17.7 Ventana Manejo del instrumento
Esta ventana consta de una barra de menú superior y tres paneles. La barra superior
contiene únicamente el menú “Archivo”. Los tres paneles que contiene la ventana “Manejo
del instrumento” son: “Editar cada envolvente”, “Edición común a todas las envolventes”
y “Probar las notas generadas”.
El menú “Archivo” consta de las opciones “Abrir”, ”Guardar Como”, “Guardar”, ”Im-
portar desde hex”, “Exportar Instrumento” y “Salir”. A continuación se explica cada una
de estas opciones.
Opción “Abrir”
101
con todos los nombres y valores de cada componente. El formato “.hex” no guarda
nombres ni imagen es de cada nota, este formato es un formato más sencillo, que sólo
contiene la información que el sintetizador necesita, contiene sólo algunas herramientas
para que el sintetizador pueda deducir como generar la nota. La opción “guardar como”
es la que se usa para guardar instrumentos en el formato “.inst”, al hacer clic en ella se
abrirá una ventana en la cual se le permite seleccionar la ruta donde se desea guardar el
instrumento, si bien se puede guardar con una extensión distinta a “.inst” es recomendable
que se guarde con esta extensión. Si se desea guardar un instrumento para luego volver a
abrirlo con este software es recomendable guardarlo con las opciones “Guardar Como” y
“Guardar”.
La opción “importar desde hex”, es la que se utiliza para poder abrir archivos
“.hex”. La apertura de este tipo de archivo no es instantánea, puede llegar a tomar varios
minutos dependiendo del procesador del PC. Notará que en la esquina inferior derecha
de la pantalla aparecerá una barra indicando el progreso del proceso de importación. Es
posible que la PC no responda durante el proceso de importación.
Opción “salir”
Este es el panel que sirve para editar cada nota por separado sin generalizar ni copiar
elementos de otra nota. En la ventana “manejo de instrumento” se observa un dibujo
de las teclas de un piano en la parte superior, para editar una nota, haga clic sobre la
tecla correspondiente a esa nota en dicho dibujo, obsérvese que el número de nota aparece
debajo del dibujo del piano, junto con un espacio que permite asignarle un nombre a dicha
nota. La tecla seleccionada del dibujo del piano pasa a tener un color rojo.
102
Para modificar la frecuencia de una de las componentes cambie el valor del cuadro
de texto que se encuentra a la derecha del texto “frecuencia NºX” por el valor deseado y
haga clic en el botón “aplicar valor” que se encuentra a la derecha del cuadro de texto.
Para editar la envolvente de una de las componentes, haga doble clic en la gráfica
que aparece debajo de la componente cuya envolvente se desea modificar, o haga clic
en el botón “Editar envolvente” que también se encuentra en el mismo cuadro que la
componente que desea modificar. Se abrirá una ventana de “manejo de envolventes” en la
cual podrá modificar la envolvente. Para aplicar los cambios haga clic en el botón “aplicar
cambios”.
Para asignarle a una componente una envolvente que ya haya sido creada y este
guardada, haga clic en el botón “abrir envolvente de archivo” que se encuentra en el
cuadro que representa la envolvente deseada. Se abrirá una ventana en la cual podrá
seleccionar la envolvente que desea asignarle a la componente.
Este panel también cuenta con la posibilidad de generar envolventes para una com-
ponente en base a la extrapolación de dos envolventes de otras componentes de la misma
nota. Para eso en el cuadro de texto que se ubica a la derecha del texto “Generar por
extrapolación desde la” ingrese el número de componente que tiene la envolvente que
desea tomar como primer referencia para la extrapolación y en el cuadro de texto que se
encuentra a la derecha del texto “hasta la” ingrese la componente que tiene la envolvente
que desea tomar como segunda referencia para la extrapolación. Luego seleccione si la
extrapolación deseada es la número 1 o la 2, la número 1 es la extrapolación por promedio
ya explicada en la explicación de la ventana “Manejo de envolventes” y la número 2 es la
otra extrapolación ya explicada en la explicación de la ventana “Manejo de envolventes”.
Si por ejemplo se toman como referencia las componentes 2 y 10, dichas envolventes no
serán modificadas, pero sı́ todas las componentes intermedias entre ambas, cuanto más
cercana este una componente a la componente 2 más parecida será a esta y cuanto más
cercana este a la componente 10, más parecida será a esta otra.
Este panel es el que sirve para editar envolventes pero, a diferencia del panel anterior, ed-
itarlas en base a las envolventes de otra nota y no editando cada parámetro por separado.
103
a la derecha del texto correspondiente a la extrapolación que desea generar y la segunda
nota que desea tomar como referencia en el cuadro de texto que se ubica a la derecha del
texto “hasta la nota” en la misma lı́nea y luego haga clic en el botón “Extrapolar” que se
encuentra en esa lı́nea. Las extrapolaciones que se realizan son iguales a las explicadas en
la ventana “manejo de envolventes”. La operación de extrapolación de notas puede tardar
varios minutos dependiendo de la velocidad del PC que se este usando y de la cantidad
de notas que se deseen generar. Al finalizar esta operación aparecerá un cartel indicando
el resultado de la operación. Esta extrapolación sólo genera envolventes, no modifica las
frecuencias.
También se puede copiar todas las envolventes de una nota y asignárselas a las
componentes de otro rango de notas. Para eso seleccione el número de nota del que desea
copiar las envolventes en el cuadro de texto que se ubica a la derecha del texto “copiar
las envolventes de la nota” e ingrese los números correspondientes al rango de notas en
el que desea pegar los resultados en los otros dos cuadros de textos que hay en la misma
lı́nea. Luego para aplicar los cambios haga clic en el botón “copiar”. Este proceso puede
tardar varios minutos, aparecerá un cartel indicando el resultado de la operación cuando
ésta finalice.
Dado que por lo general dos notas no tienen las mismas frecuencias, existe también
la opción de generar la lista de frecuencias de un rango de notas por extrapolación
logarı́tmica. El manejo de dicha opción es igual al de la opción de extrapolación de
envolventes, el resultado sólo modifica frecuencias y no envolventes.
Este panel sirve para normalizar notas, para evitar que su volumen sea muy bajo o tan
alto que no se puedan reproducir y también para probar los sonidos generados por las
notas.
También se puede generar los sonidos de un rango de notas, para eso seleccione el
rango de notas en los cuadros de texto correspondientes. A su vez, debe seleccionar la
carpeta donde desea almacenar los sonidos en el cuadro de texto que se ubica a la derecha
del texto “Guardar en la carpeta”, puede usar el botón explorar que abre una ventana
104
Imagen 33: Screenshot de la ventana para la edición común a todas las envolventes
que facilita la selección de la carpeta. También puede modificar la duración de las notas
generadas modificando el valor del cuadro que se ubica a la derecha del texto “duración
en milisegundos”.
Las dos opciones que permiten generar sonidos, previo a la generación normalizan
las notas que van a generar.
Para hacer más fácil la transmisión de instrumentos a nuestro sistema embebido, esta se
puede hacer a varios niveles. Se puede generar un instrumento con el software diseñado
y luego transmitirlo. También se puede generar de otra forma un instrumento y trans-
mitı́rselo al sintetizador mediante la ventana “Transmisión de instrumentos”. Existe a
su vez una tercera forma que consiste en conocer el protocolo de comunicación para la
recepción de instrumentos del sintetizador y transmitirle por cuenta propia el instrumento.
105
Imagen 34: Screenshot de la ventana con “Probar notas generadas” seleccionado
software. De esta forma cualquier otro programador puede diseñar el software que desee
para generar instrumentos “.hex” y mediante la ventana “transmisión de instrumentos”
transmitı́rselos al sintetizador.
Luego se presentan los valores iniciales de cada envolvente de cada nota multiplicados
por 134217728. El orden es el mismo que para las frecuencias, primero el valor inicial de la
primer envolvente de la primer nota, luego el de la segunda envolvente de la primer nota
y ası́ sucesivamente hasta completar los valores iniciales de todas las envolventes de la
106
primer nota, luego se procede con la segunda nota, luego con la tercera y ası́ sucesivamente
hasta llegar a la última nota. En total son 1290 los valores que debe tener esta parte.
Después se presenta los puntos de cambio que son los puntos en los cuales la gráfica
de las envolventes tiene un cambio en la pendiente, cada envolvente tiene seis puntos de
esos (el cero no cuenta). Los cambios se expresan en segundos multiplicados por 3000,
por ejemplo si se desea que un cambio sea a los 2 segundos se utiliza como parámetro el
número 6000. Se presenta primero el primer punto de cambio de la primer envolvente de la
primer frecuencia, luego el segundo punto de cambio de la primer envolvente de la primer
frecuencia, luego el tercero y ası́ sucesivamente hasta completar la primer envolvente,
después de los puntos de cambio de cada envolvente se agrega el número 65535, luego se
procede con la segunda envolvente de la misma forma, luego la tercera y ası́ sucesivamente
hasta terminar la primer nota, después de la primer nota se prosigue con la siguiente de
la misma forma. Es necesario que cada envolvente tenga seis puntos de cambios, si no se
desean tantos se pueden poner en puntos cualesquiera y asignar el mismo incremento a la
envolvente antes y después del cambio. En total son 9030 los valores que debe tener esta
parte.
Finalmente se presentan los incrementos que debe tomar cada envolvente entre dos
puntos de cambio. El incremento es el número que se le suma al valor de cada envolvente
luego de calcular cada valor de salida. A cada envolvente se le suman incrementos 30000
veces (el rate de salida del sintetizador) por segundo. Al principio cada envolvente es
incrementada con el primer valor de incremento que le corresponde, luego del primer
cambio pasa a incrementarse con el segundo hasta el segundo cambio, en ese momento
pasa a incrementarse con el tercero y ası́ sucesivamente. Cada incremento se presenta
multiplicado por 134217728. La cantidad de incrementos que cada envolvente posee es
siete. Se presentan de la siguiente forma, primero el primer incremento de la primer
envolvente de la primer nota, luego el segundo incremento de la primer envolvente de
la primer nota y ası́ sucesivamente hasta completar los siete incrementos de la primer
envolvente de la primer nota, después se sigue con la segunda envolvente de la primer
nota y ası́ sucesivamente hasta la última envolvente de la primer nota, luego se procede
con la segunda nota de la misma forma y luego la tercer nota y ası́ sucesivamente hasta
llegar a la última nota. En total son 9030 los valores que debe tener esta parte.
107
18 Explicación de los códigos de Scilab
El objetivo final de la mayorı́a de los programas que realizamos en Scilab es dar ayudas
al usuario para que este pueda tener una idea de como es la envolvente de cada una de
las frecuencias de una nota musical dada. Estos programas deben tener la capacidad de
buscar las frecuencias más importantes de un sonido y poder graficar su evolución en
el tiempo con tanta precisión como el usuario requiera. De esta forma el usuario puede
basarse en la evolución mostrada por los códigos de Scilab para generar un sonido tan
parecido como el quiera a uno que ya dispone. Para todos los casos suponemos que el
usuario cuenta con un vector con todos los elementos menores a uno en valor absoluto
que representa los valores que toma el sonido que desea imitar.
En muchos casos es necesario graficar el vector de valores del sonido, pero por lo general
este vector tiene más de 100000 elementos razón por la cual al Scilab le toma demasiado
tiempo graficarlo y también toma mucho tiempo el manejo de la gráfica (cambiar extremos,
colores, etc). Esta aplicación es útil cuando se desea graficar el vector de valores del sonido
para tener noción de la potencia de sonido y en que partes el sonido tiene mayor volumen.
Lo que hace esta aplicación es graficar el sonido pedido pero agrupando valores
consecutivos del vector, sumándolos en valor absoluto y representando ese conjunto de
puntos únicamente como un sólo punto que vale la suma de los valores absolutos de
los puntos que representa. Si bien este programa no es útil para realizar un análisis en
frecuencia del sonido, si sirve para tener una idea de como evoluciona el volumen del
sonido a analizar.
z=graficaFacil(entrada,cantidad,colorGraf,canal)
Donde “entrada” es la ruta donde esta guardado el sonido en formato .wav . “can-
tidad” es la cantidad de puntos que la gráfica del sonido va a tener. “color graf” es el
número asociado al color que la gráfica va a tener y “canal” es el canal que se desea
graficar, sirve en sonidos estéreo para saber cual de los dos canales se va graficando. “z”
es un vector que contiene los puntos que fueron graficados.
Esta aplicación sirve para acortar sonidos, es decir hacer que empiecen más tarde o ter-
minen antes, para poder analizar sólo la parte deseada del sonido.
Su encabezado es el siguiente:
108
Donde “entrada” es la ruta donde se encuentra el sonido en formato “.wav” y “salida”
es la ruta donde se desea almacenar el resultado de acortar el sonido. “desde” es desde
que muestra se desea que empiece el sonido resultante, si la muestra es uno empieza
del mismo punto que el original, si es Fs empieza un segundo más tarde, etc. “hasta”
representa hasta que muestra se desea que abarque el sonido final, por convención si se
ingresa como parámetro “hasta” un número menor a cero, el resultado final será hasta
el último valor del sonido de entrada. “canal” indica el canal que desea considerar del
sonido, sirve para cuando el sonido es estéreo y sólo se quiere considerar un canal. En
caso de que el sonido sea estéreo, si la variable “sumar” es mayor a cero entonces se va a
trabajar con el resultado de la suma de los dos canales del sonido. “z” es un vector con
los valores del sonido resultante.
La Transformada de Fourier de un vector tiene como resultado otro vector del mismo
largo, si se considera un vector que representa un sonido que en la mayorı́a de los casos
tiene más de 50000 valores, la transformada también tiene ese largo. Graficar vectores
muy largos es un problema para el Scilab, le toma demasiado tiempo y puede incluso
llegar a trancar la computadora. Este programa esta diseñado para poder visualizar la
descomposición en frecuencias de un sonido sin hacer que el Scilab grafique un vector con
tantos puntos como el sonido original.
Este programa también devuelve un vector con las frecuencias donde más se concen-
tra la potencia del sonido. El tamaño de dicho vector puede ser elegido por parámetro.
Cuando una frecuencia es considerada de las más importantes, las frecuencias de los en-
tornos ya no son devueltas por el programa, por ejemplo si 440Hz es la frecuencia con
mayor potencia del sonido final, esta va a ser devuelta, pero no va a ser devuelta la
frecuencia 439Hz aunque probablemente acumule más potencia que otras que si son de-
vueltas. Esto se debe a que el objetivo no es sólo devolver las frecuencias más influyentes
si no también dar una idea de los “sectores” donde se acumula más potencia.
109
El encabezado del programa es el siguiente:
110
hasta el final. Como se mencionó antes el sonido se divide en varios intervalos, la variable
“largoParticion” indica el largo de cada uno de estos intervalos. “frecuencias” es un vector
con todas las frecuencias a analizar. “coeficientes” es una matriz donde se encuentra la
evolución de cada una de las frecuencias pedidas.
El objetivo de este programa es probar los sonidos a generar, este programa recibe por
parámetros elementos como para poder generar las envolventes de una nota y también
recibe por parámetros las frecuencias de la nota, en base a eso puede generar el sonido de
la misma forma que el sintetizador lo harı́a.
Una vez que ya se tiene un programa que puede analizar un sonido y establecer cuales son
las frecuencias más influyentes y también se tiene un programa que puede en base a una
lista de frecuencias devolver una matriz que expresa la evolución de cada una de estas
frecuencias en el tiempo, se esta muy cerca de generar en base a un sonido cualquiera
parámetros para que el sintetizador pueda copiarlo. Este es el objetivo de esta aplicación,
generar las tablas para pasarle al sintetizador que permitan imitar un sonido cuya ruta
de acceso recibe por parámetro. Este programa también genera el sonido resultante de
la imitación del sonido de la ruta de origen. Dado que a este programa se le pasa por
parámetros sólo un sonido y un instrumento del sintetizador tiene 86 notas, el programa
recibe por parámetro que nota es la que se le esta pasando y luego para las otras 85 notas
generaliza en base al sonido de la nota generada.
Lo que básicamente hace este programa es unir los hilos entre las aplicaciones men-
cionadas anteriormente, es decir ejecuta una, luego le pasa por parámetros las salidas
de esa a la segunda y luego toma los parámetros de salida de ambas aplicaciones para
111
ejecutar una tercera aplicación. También genera las tablas para poder pasarle mediante
la ventana “modo transmisión” los datos al sintetizador. Estas tablas son archivos con
el formato “.hex”, se recomienda guardarlos con esa extensión, luego también se pueden
importar desde la ventana “Manejo de instrumento”. A su vez guarda la imitación del
sonido generada en el disco duro.
Este programa lo que hace es deducir las frecuencias más influyentes de un sonido que
recibe por parámetro, devolver un vector con esas frecuencias y después graficar la evolución
de cada una de esas frecuencias en el tiempo. La ventaja de este programa es que hace
todas las gráficas colocando el eje de las X y el eje de las Y con un pequeño sector de los
valores negativos igual que la ventana “manejo de envolventes”. Las gráficas que genera
este programa son ideales para poner como imagen de fondo en dicha ventana, dado que
sus ejes coinciden. Previamente es necesario generar un mapa de bits o foto que sólo
112
incluya el área de la gráfica, sin márgenes. Nótese que esta función tiene definidas otras
funciones en el mismo archivo, dichas funciones son sólo de uso interno.
frecuencias=haceGraficas(ruta, col).
Donde “ruta” es la ruta de origen del sonido, “col” es un número asociado al color que
se desea que tenga la gráfica y “frecuencias” es el vector con las principales frecuencias del
sonido a analizar. La primer gráfica que genera este programa es un análisis en frecuencia
del sonido, la segunda es la evolución en el tiempo de la primer frecuencia del vector
“frecuencias”, la tercer gráfica es la evolución en el tiempo de la segunda frecuencia del
vector “frecuencias” y ası́ sucesivamente.
Este es el programa que se utilizó para generar las imitaciones de sonido mediante el
método de las sinusoidales puras. El programa comienza calculando la transformada de
Fourier del sonido que se desea imitar y, al igual que la aplicación “fourieFacilDetectaPi-
cos”, agrupa los puntos de la transformada que son consecutivos y los considera como uno
sólo que vale la suma de los módulos de estos. Luego, del conjunto de puntos agrupados
que formó selecciona los ’n’ mayores, estos son los que representan a las ’n’ frecuencias
más influyentes del sonido a imitar. Finalmente genera la señal de salida como la suma
del valor de cada uno de los ’n’ puntos mencionados multiplicado por una sinuidal de la
frecuencia que representan. El programa también grafica la descomposición en frecuencias
de la imitación generada.
Este es el programa que se utilizó para generar las imitaciones de sonido mediante el
método de la “envolvente global”. Lo primero que hace el método es imitar el sonido
deseado con la aplicación “sinuisoidalesPuras”. Luego con la aplicación “graficaFacil”
obtiene ‘n’ puntos de la envolvente del sonido a imitar, utiliza estos puntos para generar
una envolvente tan larga como se desea que sea la imitación generada y finalmente mul-
tiplica la imitación por dicha envolvente. La envolvente se forma uniendo con rectas los
113
‘n’ puntos obtenidos en la función “graficaFacil”. Este programa también grafica en la
ventana número 0 la descomposición en frecuencias de la imitación generada antes de ser
multiplicada por la envolvente y en la ventana 1 grafica la envolvente a utilizar.
También se generaron algunas aplicaciones de Scilab que generan algunos efectos de audio,
dichas aplicaciones están dentro de la carpeta efectos. Estas aplicaciones no son explicadas
dado que son herramientas auxiliares que no tiene una finalidad en sı́ para el uso del
sintetizador, otra razón es que son aplicaciones sencillas de usar donde los parámetros
tienen nombres conocidos. En el cd adjunto hay ejemplos de uso de estos efectos.
114
19 Herramienta para reducir imágenes
La función de scilab “haceGraficas” genera las gráficas con los ejes en la misma posición
que la ventana “manejo de envolventes”, pero para poder sacar provecho de esa coinciden-
cia de los ejes es necesario crear una imágen por cada gráfica que generó el programa, cada
imágen debe incluir sólo la gráfica que le corresponde. El scilab tiene una opción para
exportar gráficas como mapa de bits, pero las exporta con un marco extra, la aplicación
creada en visual studio llamada “reduce imágenes”, se encarga de eliminar el marco de
esas gráficas.
Una vez dentro del programa haga clic en el botón “seleccionar imágenes” y selec-
cione todas las imágenes que desea reducir, las imágenes serán reducidas y guardadas en
la misma carpeta. Luego de terminada la reducción aparece un cartel indicando si esta
fue exitosa o no.
Como la posición del marco que el Scilab le agrega a las gráficas en el momento
de crearlas es fijo, para eliminar dicho marco lo que hace el programa es eliminar de los
bordes de los mapas de bits la cantidad de bits que corresponde al ancho del marco.
115
20 Protocolo para Transmitir un Nuevo Instrumento
1. Enviar un 0x32.
2. Enviar los cien bytes de data y esperar la recepción de un 0x26 antes de continuar
enviando mas data.
3. Luego de que se haya enviado los últimos cien bytes, esperar la respuesta de un 0x15
(21 en decimal) que confirme que el dispositivo ha recibido toda la información.
Este es el mismo orden en el cual los archivos ‘.hex’ deben ser generados. En la
sección “Formato de los archivos para enviarle al sintetizador” se encuentra el orden más
detallado.
Las posiciones de memoria en los DSPic son de dos bytes pero los datos sólo pueden
enviarse de a un byte. En el caso de las variables del tipo int hay que enviar primero
116
el byte más significativo y luego el menos. En el caso de las variables del tipo long int
se deberá enviar primero los 2 bytes menos significativos y luego los más significativos.
Por ejemplo, para enviar el valor 22790307 que en hexadecimal es el 0x15BC0A3 hay que
enviar la siguiente trama: 0xC0 0xA3 0x01 0x5B.
117
21 Posibles mejoras y complementos
El sintetizador, si bien funciona bien, tiene muy pocos controles internos, no se chequean
posibles fallas en una transmisión de comandos MIDI, ni en las comunicaciones por I 2 C.
Dichos chequeos serı́an muy útiles en casos de fallas en el hardware.
Como se menciona en la sección de explicación de los códigos de los DSPic, los DSPic
esclavos funcionan como memorias desde el punto de vista del maestro para el envı́o de
datos vı́a I 2 C. El maestro, que ve a los esclavos como memorias, podrı́a guardar en
cierta posición de memoria cierto dato auxiliar. Cuando recibe un comando MIDI se
lo transmite al esclavo que corresponde y luego lee el dato auxiliar que guardo en el
esclavo que le corresponde recibir la próxima instrucción MIDI, si la lectura se realiza
correctamente, le transmite la próxima instrucción MIDI a ese DSPic. Si la lectura falla,
lee el dato auxiliar de los siguientes DSPic hasta que pueda leer correctamente el dato
auxiliar de uno de esos. De esa forma puede hacer cierto control del funcionamiento de
un esclavo antes de mandarle una instrucción MIDI para reproducir.
118
21.1.4 Mejora de la frecuencia de salida
La frecuencia de salida del sonido del sintetizador es de 30 Khz, esta frecuencia permite
reproducir sonidos de hasta 15 Khz, mientras que el oı́do humano puede llegar a escuchar
sonidos de hasta 20 Khz. El estándar que se usa de audio para reproducir sonidos de hasta
20 Khz es usar una frecuencia de salida de 44,1 Khz. Serı́a una buena mejorar lograr que
el sintetizador llegara a esa frecuencia.
Dado que el sintetizador utiliza los DSPic con mayor velocidad del mercado, no es
posible lograr esa frecuencia de salida utilizando la lı́nea DSPic sin modificar los códigos
de la programación del DSPic.
Lograr 44,1 Khz de frecuencia de salida implica calcular cada sample en hasta 903
instrucciones de reloj. Actualmente al procesador le lleva aproximadamente 1050 instruc-
ciones de reloj calcular cada sample, dicho número varı́a según la nota a reproducir y los
efectos seleccionados.
La forma más sencilla de lograr que se calcule en menos de 903 instrucciones cada
sample es reducir la fidelidad del sonido. Se podrı́a hacer que cada nota tenga solo diez
armónicos, con eso la generación de los sonidos tomarı́a aproximadamente 850 ciclos. Otra
alternativa es renunciar a los efectos que mayor cantidad de ciclos toman, como el efecto
wah-wah y hacer que el sintetizador solo aplique un efecto.
Otra formar de lograr esto es optimizar los códigos de los esclavos. Como se menciona
en la parte de explicación de los códigos en muchos casos dos códigos que cumplen el mismo
objetivo y parecen equivalentes, pueden tomar un número distinto de ciclos de reloj. Se
lograron realizar varias optimizaciones en el código, pero aún quedan algunas más por
hacer.
La versión que utilizamos del programador en lenguaje ‘C’ del MPLAB es la versión
gratuita para estudiantes, dicha versión si bien no tiene limitaciones respecto a la cantidad
de aplicaciones y funciones, no tiene todas las optimizaciones que la versión profesional
dispone, un claro ejemplo de esto es explicado al explicar el efecto “Wah-Wah” con el
tema de las divisiones de números de 4 bytes dividido números de 2 bytes. Consideramos
que con la versión profesional se podrı́an optimizar parte de los códigos para lograr de esa
forma llegar a procesar los samples en menos de 903 instrucciones.
Una tercer alternativa consiste en diversificar las tareas que realiza cada esclavo,
por ejemplo hacer que los esclavos no generen los efectos, si no que cada esclavo genera la
salida (analógica) de la nota que le corresponde, luego todas las salidas son sumadas y un
DSPic lee por su entrada analógica el valor de dicha suma y le procesa los efectos. De esta
forma se lograrı́a tener una frecuencia de salida de 44,1 khz, pero se perderı́a precisión en
el sonido dado que la salida de los Pics tiene una resolución de 16 bits mientras que la
entrada analógica tiene una resolución de 10 bits. También se perderı́a calidad de sonido
en el pasaje de digital a analógica debido al error propio del DSPic y el de los sumadores,
de implementar esta solución la señal pasarı́a de digital a analógica dos veces (una vez al
ser generada por los esclavos y otra después de ser procesados los efectos) por lo que este
error serı́a mayor. De todas formas esta alternativa tiene una gran ventaja que es que el
generador de efectos queda independiente del sintetizador, por lo que se podrı́a conectar
no sólo a este, si no a cualquier otro dispositivo que el usuario desee que emita una señal
119
de sonido analógica. Por ejemplo, se podrı́a conectar el generador de efectos a la salida
de un reproductor de MP3 para poder aplicarle los efectos a la música que este genera.
Una de las mejoras que nos parece más útil para el sintetizador es de darle la capacidad
de modificar con potenciómetros algunos parámetros del instrumento virtual en cualquier
momento.
El modelo de DSPic seleccionado no tiene interfaz USB, pero existen una gran
cantidad de microprocesadores de la lı́nea DSPic y también de lı́neas anteriores como
algunos PICs de la lı́nea 18F que si disponen dicha interfaz. La forma más sencilla de
incorporar la interfaz USB serı́a cambiar el modelo del DSPic maestro por uno que sı́
tenga esta interfaz.
Dado que la memoria de programación de los DSPic que utilizamos para el proyecto
no alcanza para almacenar más de un instrumentos y no existen DSPics con grandes
120
memorias de programación, se deberı́an utilizar memorias externas. El funcionamiento
del programa de los esclavos serı́a muy similar, salvo que los datos se obtendrı́an de la
memoria externa y serı́a necesario un parámetro adicional que indicara el instrumento
deseado. También habrı́a que diseñar algún protocolo para que todos los esclavos puedan
acceder sin colisiones a la memoria externa y ası́ evitar la necesidad de una memoria
externa por cada esclavo.
Dado que el protocolo MIDI provee una forma de identificar instrumentos a través
de los “canales MIDI”, no consideramos que sea difı́cil lograr interconectar varios instru-
mentos al sintetizador.
Para realizar esta mejora serı́a necesario aumentar el número de DSPics a utilizar
para poder mejorar la cantidad de notas simultáneas a reproducir.
Como se mencionó anteriormente, una de las limitaciones del proyecto es que no es posible
recrear de forma correcta los sonidos de instrumentos de viento. Esto se debe a que una
vez que se oprimió una tecla no dispone de forma de alargar o acortar su sonido. En los
instrumentos de viento, el sonido de cada nota tiene un largo variable, dependiente del
tiempo que se este soplando.
Una herramienta de gran utilidad para el sintetizador serı́a que tenga la posibilidad de
grabar los sonidos generados, una posible mejora serı́a hacer que cada DSPic además de
transmitir por su salida analógica cada sample que calcula pueda también guardarlo en
una memoria externa. De esa forma el sonido generado se puede volver a reproducir. Si
bien, el sonido generado se puede grabar conectándole un dispositivo adecuado en la salida
de audio, que cada DSPic grabe el sonido que genera tiene sus ventajas. Una de ellas es
que el sonido se puede grabar con mayor calidad, dado que se graba antes de convertirse en
un valor analógico. Otra de las ventajas es que puede almacenarse la variable salidaLong
que tiene una precisión de 32 bits y una tecera ventaja es que se podrı́a implementar un
sistema para que el sonido pueda ser grabado antes de aplicarle los efectos, para luego
poder modificar la forma en la que estos son aplicados.
121
Una variante de esta mejora propuesta consiste en que el maestro grabe cada función
MIDI que recibe junto con el momento en que fue recibida, de esta forma se puede volver
a reproducir el sonido generado pero con cualquier instrumento virtual que se desee. Los
esclavos no tienen porque conocer si el sonido que están reproduciendo es grabado o esta
siendo generado actualmente.
Los archivos con extensión ’.midi’ son archivos que contienen instrucciones MIDI para
reproducir un sonido, una posible mejora para el sistema consiste en reproducir estos
archivos.
A diferencia de las posibles mejoras del sintetizador, las posibles mejoras del software
del PC, en su mayorı́a requieren de una reestructuración de la arquitectura básica del
software. Más que pequeños agregados en un código, pueden llegar a ser proyectos enteros.
A continuación se mencionan algunas de estas mejoras.
Una mejora que consideramos muy útil que se le podrı́a hacer al proyecto es generar
un programa que realmente pueda transformar casi cualquier sonido en una nota de un
instrumento virtual. Que las imitaciones sean mejores que las de la función “copiaSonido”.
Este programa también podrı́a brindar una mejor interfaz al usuario, mucho más amiga-
ble y fácil de usar. A su vez podrı́a también permitir al usuario seleccionar valores de
parámetros para mejorar la imitación.
Sabemos que en el programa “Interfaz de comunicación” hay métodos que si bien funcio-
nan, no son tan eficientes como podrı́an serlo, la lentitud en la creación e importación de
122
instrumentos es un ejemplo de esto. Serı́a una mejora muy práctica lograr reducir esos
tiempos.
La idea original del proyecto era que se le puedan transmitir instrumentos VSTi al sinte-
tizador. Eso serı́a una gran mejora a nuestro proyecto dado que permitirı́a no solo utilizar
la gran cantidad de instrumentos VSTi existentes, si no que también utilizar cualquiera
de los editores de instrumentos VSTi que existen para crear instrumentos.
Esta mejora la incorporamos dentro de las mejoras de software dado que conside-
ramos más sencillo que el software de PC pueda convertir los instrumentos VSTi a el
formato que interpreta el sintetizador a que hacer que el sintetizador interprete directa-
mente los VSTi. Esto se debe a que el software de PC trabaja en Windows que es el
entorno natural de los instrumentos VSTi en formato DLL.
Actualmente para utilizar ciertas herramientas hay que utilizar la interfaz de conección
entre el PC y el sintetizador, mientras que para otras hay que utilizar Scilab. La mejora
propuesta consiste en unificar todas las herramientas creadas para que puedan ser ejecu-
tadas desde un mismo programa.
123
22 Explicación del contenido del CD adjunto
Esta carpeta contiene algunas de las pruebas de imitación de sonidos generadas mediante
el método de las sinudoidales puras mencionado en la sección “creación de la nota musical”.
Para generar las imitaciones utilizamos la función de Scilab “sinusoidalesPuras.sce”.
Esta carpeta contiene algunas de las pruebas de imitación de sonidos generadas mediante
el método de envolvente global mencionado en la sección “creación de la músical”. Para
generar las imitaciones utilizamos la función de Scilab “envolventeComun”.
Esta carpeta contiene algunas de las pruebas de imitación de sonidos generadas mediante
el método de la envolvente particular para cada frecuencia, mencionado en la sección
“creación de la músical”. Para generar las imitaciones y grabar los sonidos se utilizó el
software diseñado por nosotros.
Como se mencionó anteriormente, una de las ventajas de este método para generar
sonidos contra otros métodos basados en samples es que permite realizar facilmente una
gran cantidad de modificaciones en el sonido generado (como filtrar o modificar ciertas
frecuencias). Esta ventaja fue utilizada para la generación de los sonidos que se encuentran
en esta carpeta, muchos sonidos fueron generados en base a extrapolaciones de otros
generados anteriormente, o filtrando ciertas frecuencias. Como muchos de los sonidos
de esta carpeta no fueron generados buscando imitar un sonido de un instrumento en
particular, si no que fueron generados mediante modificaciones de otros ya generados, en
esta carpeta no hay ningún sonido real, sólo hay sonidos generdos mediante el método de
la envolvente particular de cada frecuencia.
124
22.4 Carpeta “Software Diseñado”
Esta carpeta tiene todo lo referente al software diseñado para la comunicación entre el
PC y el sintetizador. Tiene el archivo ejecutable junto con el ı́cono en la carpeta “archivo
ejecutable”. Tiene también los archivos como para poder modificar el software en la
carpeta “Códigos”. Tiene en la carpeta ’Instrumentos Creados’ ejemplos de instrumentos
y en la carpeta ’envolventes creadas’ ejemplos de envolventes creadas.
Esta carpeta tiene varios ejemplos de instrumentos creados junto con la grabación de
algunas de las notas de los instrumentos. Para la generación de todos los instrumentos, se
generaron en detalle una o dos de sus notas y luego mediante los métodos de extrapolación
y copiado se generaron las restantes notas. Estos instrumentos son un claro ejemplo de
como los métodos de extrapolación y copiado pueden ser útiles para generar notas cercanas
a cierta nota que se tiene generada, pero los sonidos generados para notas lejanas a una
disponible no son tan buenos.
Esta carpeta tiene dos subcarpetas, ambas con ejemplos de extrapolaciones. La subcar-
peta llamada ’ejemplos de extrapolación’ tiene un ejemplo en el cual se extrapola entre
la máscara ‘1 de 15 (inicial).masc’ y la máscara ‘15 de 15 (final).masc’, en la carpeta
estan guardadas todas las máscaras intermedias y capturas de pantalla de sus gráficas.
La carpeta ‘ejemplos de extrapolación por promedio’ es similar a la carpeta ‘ejemplos de
extrapolación’ pero tiene un ejemplo de extrapolación por promedio.
125
23 Conclusiones
Fue necesario superar las dificultades del escaso mercado local para traer del exterior
microcontroladores. Traer algo del exterior, dado el tiempo que le toma llegar, implicó que
debamos planificar con antelación las compras y prever posibles fallas antes de encargar.
Se llevó la experiencia del trabajo de equipo un paso más. Fue necesario un total
entendimiento en el equipo, junto con una buena coordinación de tiempos y esfuerzos
para poder llegar al objetivo. Consideramos que el trabajo fue un éxito, por que si bien
no fue posible cumplir con todos los requisitos originales del proyecto, se logró llegar a un
compromiso acorde a las limitaciones del mercado local y al tiempo destinado del proyecto
en el cual se replantearon los objetivos y se lograron cumplir.
Fue necesario tomar grandes decisiones respecto al proyecto, decisiones que modifi-
caban totalmente el rumbo del proyecto que hoy viendo en retrospectiva las consideramos
acertadas en su mayorı́a.
Consideramos que diseñamos un dispositivos útil, con una arquitectura bien pensada
que permite una gran cantidad de mejoras que de concretarse pueden poner el sintetizador
a la par de cualquier otro sistema del mercado. El nuevo formato de instrumentos vir-
tuales, si bien es más limitado que el formato “.vsti”, también creemos que es un formato
con un buen futuro, que de desarrollarse más puede llegar a ser realmente útil.
126
Bibliografı́a
Referencias bibliográficas
[embedded] [online]
Disponible en internet:
http://www.embedded.com
[microcontroller] [online]
Disponible en internet:
http://microcontroller.com
127
Disponible en internet:
http://www.microsoft.com/windowsembedded/en-us/default.mspx
128
[VST-Guy] VST-Guy.2009.VST-plug ins documentation [online]
Disponible en internet:
http://ygrabit.steinberg.de/~ygrabit/public_html/vstgui/V2.2/doc/
http://sistemasembebidos.com.ar/foro/
http://www.psicofxp.com/forums/programacion.313/77337-como-veo-las-funciones-de-dll.
html
http://www.lawebdelprogramador.com/codigo/enlace.php?idp=1419\&id=13\&texto=
C/Visual+C\#
http://msdn.microsoft.com/es-es/library/31d242h4(VS.80).aspx
http://www.forosdelweb.com
http://www.microchip.com/forums/tt.aspx?forumid=152
129
http://todopic.mforos.com/781922-dspic/
http://www.forosdeelectronica.com/f24/bootloader-codigo-fuente-dspic-827/
130
Anexos
Este teorema trata sobre el muestreo digital de señales 10 y la reconstrucción de una señal
a partir de su muestreo.
El teorema también demuestra que si f(t) es una función con ancho de banda ’B’ y la señal
es muestreada a una frecuencia Fs > 20 B 0 , entonces f(t) se puede recuperar exactamente
a partir de sus muestras, siguiendo la fórmula:
∞
f n n
X
f (t) = ∗g t −
n=−∞
| F
{z s } F s
f (t)muestreada
sen(2πF T )
g(t) =
2πF T
Siendo F la frecuencia de muestreo, g(t) es la función conocida como sinc, dicha función
es una función continua. Dado que el principal uso de este teorema es para generar con
sistemas digitales señales analógicas, generar la función sinc se convierte en un problema
dado que un sistema digital no puede generar funciones continuas. La solución más común
adoptada es que el sistema digital genere la función conocida como impulso. Dicha función
se define como:
lim → h(t)
n→+∞
A diferencia de la señal sinc que es difı́cil de generar para un sistema digital dado que es
una señal analógica y que su valor no se mantiene constante, la señal impulso es más fácil
de generar para estos sistemas dado que sólo consiste en generar un valor alto de tensión
por un intervalo de tiempo muy pequeño. Como la descomposición en frecuencias de la
señal impulso es la función h(f)=1 y la descomposición en frecuencias de la señal sinc es
igual en el intervalo entre -’F’ y ’F’ y en el resto es 0, si se pasa la señal impulso por
un filtro pasabajos de frecuencia de corte ’F’, entonces se obtiene la señal sinc. Se puede
demostrar también que un filtro pasabajos con frecuencia de corte que este entre ’B’ y ’F’
también sirve.
10
El muestreo digital es una de las partes que intervienen en la digitalización de las señales. Consiste
en tomar muestras periódicas de la amplitud de una señal analógica, siendo el intervalo entre las muestras
constante. El ritmo de este muestreo, se denomina frecuencia o tasa de muestreo y determina el número
de muestras que se toman en un intervalo de tiempo [Wikipedia]
131
Como consecuencia, para generar una señal analógica continua de ancho de banda ’B’ a
partir de sus muestras (si la frecuencia de muestreo es mayor a 2’B’) hay que reproducir
las muestras multiplicadas por la función impulso (reproducirlas con la misma frecuencia
que fueron muestreadas) y luego aplicarle un filtro pasa bajos de frecuencia de corte
F recCorte a la señal generada, con B ≤ F recCorte ≤ Frecuencia de muestreo.
132
Anexo B, códigos de los DSPic esclavos
Clase ’CargarProgSlave.c’
#i n c l u d e ” C a r g a r P r o g S l a v e . h”
char llevoCuenta = 0 ;
int direccionL ;
int direccionH ;
e x t e r n c h a r * CRC L ;
v o i d cargoPrograma ( ) {
MemWriteLatch ( 0 x0 , d i r e c c i o n L , 0 x0 , 0 x0 ) ;
MemCommand(PM ROW ERASE) ;
}
MemWriteLatch ( 0 x0 , 0 xE0CA, 0 x0 , 0 x0 ) ;
MemCommand(PM ROW ERASE) ;
d i r e c c i o n H = 0 x0 ;
int llevoCuentaTotal = 0;
w h i l e ( RAMBuffer [ 1 5 ] > 0 ) { // M i e n t r a s no s e e s c r i b a l a p o s i c i ó n f i n a l de
memoria s i g o r e c i b i e n d o d a t o s .
i f ( r e c i b i D a t o P r o g >1){
133
r e c i b i D a t o P r o g =0;
MemWriteLatch ( d i r e c c i o n H , d i r e c c i o n L , 0 x0 , datoProg ) ;
MemCommand(CONFIG WORD WRITE) ;
d i r e c c i o n L = d i r e c c i o n L +2; // Las d i r e c c i o n e s de memoria aumentan de
a dos .
l l e v o C u e n t a = l l e v o C u e n t a +1;
l l e v o C u e n t a T o t a l ++;
}
RAMBuffer [ 1 5 ] = 0 ;
}
}
}
slaves/CargarProgSlave.c
134
Clase ’CargarProgSlave.h’
#i n c l u d e <p33FJ128GP802 . h>
v o i d cargoPrograma ( ) ;
slaves/CargarProgSlave.h
135
Clase ’escribirEnMemoriaConst.s’
; *********************************************************
; MemWriteLatch :
;
;W0 = TBLPAG
;W1 = Wn
;W2 = WordHi
;W3 = WordLo
; no r e t u r n v a l u e s
. g l o b a l MemWriteLatch
MemWriteLatch :
mov W0, TBLPAG
t b l w t l W3, [W1]
t b l w t h W2, [W1]
return
; *********************************************************
; MemReadLatch :
;
;W0 = TBLPAG
;W1 = Wn
; r e t u r n : data i n W1:W0
. g l o b a l MemReadLatch
MemReadLatch :
mov W0,TBLPAG
t b l r d l [W1] ,W0
t b l r d h [W1] ,W1
return
; **********************************************************
; MemCommand :
;
;W0 = NVMCON
; no r e t u r n v a l u e s
. g l o b a l MemCommand
MemCommand :
mov W0,NVMCON
mov #0x55 ,W0; Unlock s e q u e n c e
mov W0,NVMKEY
mov #0xAA,W0
mov W0,NVMKEY
b s e t NVMCON,#15
nop ; Required
nop
Loop : b t s c NVMCON,#15 ; Wait f o r w r i t e end
bra Loop
return
slaves/escribirEnMemoriaConst.s
136
Clase ’dac.c’
#d e f i n e v al orD ac 18
#i n c l u d e <p33FJ128GP802 . h>
#i n c l u d e ” dac . h”
137
Clase ’dac.h’
138
Clase ’efectos.c’
#d e f i n e v al orD ac 18
#d e f i n e l a r g o T a b l a S e n o s 15000
#d e f i n e maximoEco 7600
#i n c l u d e ” e f e c t o s . h”
#i n c l u d e ” p 3 3 f j 1 2 8 g p 8 0 2 . h”
//−−−−−−−−−−−−−−−−−−−−−−−−D e c l a r a c i ó n de v a r i a b l e s −−−−−−−−−−−−−−−−−−−−−−−−−
//−−−−−−−−−−−−−−−−−−−−−−I n i c i a l i z a c i ó n de l o s e f e c t o s −−−−−−−−−−−−−−−−−−−−−−
v o i d i n i c i a r E f e c t o s ( ) { // Este método i n i c i a l i z a l o s e f e c t o s
// Debe s e r llamado una vez a n t e s de comenzar a u t i l i z a r l o s e f e c t o s .
//Lo que hace e s h a c e r que e l v e c t o r ’ p a r a D e v o l v e r ’ apunte a l o s dos b y t e s
más s i g n i f i c a t i v o s de ’ v a l o r L a r g o ’ .
p a r a D e v o l v e r=&v a l o r L a r g o ;
p a r a D e v o l v e r++;
}
u l t i m o s [ punteroGuardar ]= dato ;
punteroGuardar++;
i f ( punteroGuardar==maximoEco ) {
punteroGuardar =0;
}
}
139
i n t e c o ( ) { // Este e s e l e f e c t o ’ e c o ’ .
// Retorna un sample a n t e r i o r ( que tan a n t e r i o r depende de ’ a t r a s o E c o ’ )
m u l t i p l i c a d o por un v a l o r menor a uno que depende de magnitud e c o .
i n t valorEco ;
i n t i n d i c e=punteroGuardar−a t r a s o E c o ; // Primero d e t e r m i n a c u a l f u e e l
sample que s e g e n e r ó ’ a t r a s o E c o ’ s a m p l e s a t r a s . LLama ’ v a l o r E c o ’ a
e s e sample .
i f ( i n d i c e >=0){
v a l o r E c o=u l t i m o s [ i n d i c e ] ;
}
else {
v a l o r E c o=u l t i m o s [ i n d i c e+maximoEco ] ;
}
v a l o r L a r g o =(( l o n g i n t ) magnitudEco ) * ( ( l o n g i n t ) v a l o r E c o ) ; // M u l t i p l i c a
’ v a l o r E c o ’ por ’ magnitudEco ’ que e s un número e n t r e 1 y 6 5 5 3 5 .
// Devuelve ’ * p a r a D e v o l v e r ’ que son l o s dos b y t e s más s i g n i f i c a t i v o s de
v a l o r L a r g o , e s d e c i r que e s como d e v o l v e r
’ v a l o r E c o ’ * ’ magnitudEco ’ / 6 5 5 3 5 .
return * paraDevolver ;
}
v a l o r L a r g o =(( l o n g i n t ) m u l t i p l i c a d o r ) * ( ( l o n g i n t ) v a l o r A c t u a l ) ; // Valor
l a r g o e s un ’ l o n g i n t ’ que v a l e e l p r o d u c t o de ’ m u l t i p l i c a d o r ’ por
’ valorActual ’
punteroTremolo+=f r e c u e n c i a T r e m o l o ;
i f ( punteroTremolo >( l a r g o T a b l a S e n o s −1) ) { // V e r i f i c o que e l v a l o r d e l c u a l
voy a tomar e l s e n o no p a s e e l l a r g o de l a t a b l a de s e n o s .
punteroTremolo=punteroTremolo−l a r g o T a b l a S e n o s ;
}
}
140
c o p i a A c t u a l=v a l o r A c t u a l / 4 ; // Por t o d a s l a s m u l t i p l i c a c i o n e s y
d i v i s i o n e s que s e hacen l o s v a l o r e s i n t e r m e d i o s pueden s e r mayor a l
máximo v a l o r que e l t i p o de v a r i a b l e que l o c o n t i n e n .
// Por e s o a n t e s de a p l i c a r e l f i l t r o d i v i d i m o s e l número a f i l t r a r e n t r e 4
y d e s p ué s l o m u l t i p l i c a m o s por 4 .
// El f i l t r o e s una suma de p r o d u c t o s que l u e g o s e d i v i d e e n t r e un d i v i s o r .
v a l o r L a r g o =(( l o n g i n t ) s a l i d a A n t e s ) * ( ( l o n g
i n t ) ( p a r t e M u l t i p l i c a d o r e s [ f r e c C o r t e ] ) ) −(( l o n g
i n t ) s a l i d a H a c e 2 ) * 16384+( fCuadrado [ f r e c C o r t e ] ) * ( ( l o n g
i n t ) c o p i a A c t u a l ) ; // ’ ValorLargo ’ Primero toma e l v a l o r de l a suma de
l o s productos .
s a l i d a H a c e 2=s a l i d a A n t e s ; // Se a c t u a l i z a e l v a l o r de y ( n−2) .
return 4* s a l i d a A n t e s ;
}
i n t l i m i t a d o r I f ( i n t v a l o r A c t u a l ) { // Este método e s s o l o un i f , s i e l v a l o r
a b s o l u t o de l a s a l i d a s u p e r a c i e r t o v a l o r pasa a v a l e r e s e v a l o r .
i f ( v a l o r A c t u a l >l i m i t e I f ) {
return l i m i t e I f ;
}
i n t menosLimite=(− l i m i t e I f ) ;
i f ( v a l o r A c t u a l <menosLimite ) {
r e t u r n menosLimite ;
}
return valorActual ;
}
i n t l i m i t a d o r L o g ( i n t v a l o r A c t u a l ) { // Esta f u n c i ó n d e v u e l v e
v a l o r A c t u a l −l i m i t e L o g * v a l o r A c t u a l ˆ 3 / 3 2 7 6 8 ˆ 3 .
// l i m i t e L o g e s un número que v a l e de 0 a 1 2 0 0 0 .
//La i d e a e s que e s t e e f e c t o e s como una s a t u r a c i ó n , cuanto mayor e s e l
v a l o r de ’ v a l o r A c t u a l ’ , más s e nota .
v a l o r L a r g o =(( l o n g i n t ) v a l o r A c t u a l ) * ( ( l o n g i n t ) v a l o r A c t u a l ) ;
// ’ v a l o r L a r g o ’ e s ’ v a l o r A c t u a l ’ ˆ 2 .
v a l o r L a r g o =( * p a r a D e v o l v e r ) ; // ’ v a l o r L a r g o ’ pasa a s e r s o l o s u s dos b y t e s
más s i g n i f i c a t i v o s , de e s t a forma e s como s i l o d i v i d i e r a e n t r e 6 5 5 3 6 .
v a l o r L a r g o =( v a l o r L a r g o ) * ( ( l o n g i n t ) v a l o r A c t u a l ) ; // ’ v a l o r L a r g o ’ pasa a
s e r ’ valorLargo ’* valorActual .
v a l o r L a r g o =( * p a r a D e v o l v e r ) ; // ’ v a l o r L a r g o ’ pasa a s e r s o l o s u s dos b y t e s
más s i g n i f i c a t i v o s , de e s t a forma e s como s i l o d i v i d i e r a e n t r e 6 5 5 3 6 .
v a l o r L a r g o =( v a l o r L a r g o ) * ( ( l o n g i n t ) ( l i m i t e L o g ) ) ; // M u l t i p l i c o
v a l o r L a r g o por ’ l i m i t e L o g ’ . En e s t e momento ’ * p a r a D e v o l v e r ’ v a l e
l i m i t e L o g * v a l o r A c t u a l ˆ3/65536ˆ3
141
r e t u r n ( v a l o r A c t u a l −8 * ( * p a r a D e v o l v e r ) ) ; // Devuelvo
v a l o r A c t u a l −8 * ( * p a r a D e v o l v e r )=v a l o r A c t u a l −l i m i t e L o g * v a l o r A c t u a l ˆ 3 / 3 2 7 6 8 ˆ 3 .
i f ( v a l o r V i b r a t o >85){ // S i v a l o r V i b r a t o >85 e n t o n c e s l a f r e c u e n c i a de
s a l i d a d e l DAC aumenta .
142
Clase ’efectos.h’
void i n i c i a r E f e c t o s ( ) ;
v o i d grabarDato ( i n t dato ) ;
i n t eco ( ) ;
v o i d aumentarPunteroTremolo ( ) ;
i n t tremolo ( i n t valorActual ) ;
i n t wahwah ( i n t v a l o r A c t u a l ) ;
int l i m i t a d o r I f ( int valorActual ) ;
int limitadorLog ( int valorActual ) ;
void vibrato ( ) ;
slaves/efectos.h
143
Clase ’call2.s’
. g l o b a l asmFunction
asmFunction :
mov w1 , w3
mov [ w0 ] , w4
i n c w0 , w0
i n c w0 , w0
mov [ w0 ] , w5
r e p e a t #17
d i v . s d w4 , w3 ; d i v i d e w5w4/w3
return
.end
slaves/call2.s
144
Clase ’I2C.c’
#i n c l u d e ” p 3 3 f j 1 2 8 g p 8 0 2 . h”
#i n c l u d e ” I2C . h”
//−−−−−−−−−−−−−−−−−−−−−−−−D e c l a r a c i ó n de v a r i a b l e s
−−−−−−−−−−−−−−−−−−−−−−−−−
void i 2 c 1 i n i t ( void ) {
TRISB=0XFFFF ;
ODCBbits .ODCB5=1;
ODCBbits .ODCB6=1;
ODCBbits .ODCB8=1;
ODCBbits .ODCB9=1;
I2C1ADD = d i r e c c i o n ;
RAMPtr = &RAMBuffer [ 0 ] ;
Flag . AddrFlag = 0 ;
Flag . DataFlag = 0 ;
I E C 1 b i t s . SI2C1IE = 1 ;
}
//−−−−−−−−−−−−−−−−−−−−−−Manejo de i n t e r r u p c i ó n de I2C−−−−−−−−−−−−−−−−−−−−−−
void a t t r i b u t e ( ( i n t e r r u p t , n o a u t o p s v ) ) S I 2 C 1 I n t e r r u p t ( v o i d ) { // Este
método hace r e s p o n d e r a l DSPic como una memoria de 20 p o s i c i o n e s .
// También s e comporta como una memoria de 20 p o s i c i o n e s , guardando en
’ RAMBuffer [ 2 0 ] ’ l o s d a t o s que r e c i b e .
}
e l s e i f ( ( I2C1STATbits .R W == 0 )&&(I2C1STATbits . D A == 1 ) ) { // Chequeo a
v e r s i no f u e una d i r e c c i ó n , p e r o e s una orden de e s c r i t u r a .
145
Flag . AddrFlag = 0 ;
Flag . DataFlag = 1 ; // Aviso que e l próximo byte e s e l de d a t o s .
}
e l s e i f (Temp==1){ // Chequeo s i e s c r i b e n en l a d i r e c c i ó n
c o r r e s p o n d i e n t e a l a ’ v e l o c i d a d ’ de l a nota a t o c a r , s i e s e e s e l
c a s o hay que a v i s a r que cuando r e c i b a l a v e l o c i d a d voy a t e n e r
todo para g e n e r a r una nota .
r e c i b i P u n V e l =1;
}
e l s e i f (Temp<8){ // S i Temp e s t a e n t r e 2 y 7 e s por que e l próximo
dato e s uno de l o s p a r á m e t r o s de uno de l o s e f e c t o s .
r e c i b i P u n E f e c t o =1;
}
i f (Temp==11){ // S i Temp=11 e s por que voy a empezar a r e c i b i r
d a t o s para programar un i n s t r u m e n t o .
recibiDatoProg = 1;
}
i f (Temp==12){ // S i Temp=12 e s por que s e t e r m i n o de programar un
instrumento .
terminoPrograma =1;
}
i f (Temp==16){ // S i Temp=16 e s por que me van a e n v i a r un CRC.
recibiCRC = 1 ;
}
}
e l s e i f ( Flag . DataFlag ) { // Chequeo s i e l dato que r e c i bı́ ( y t o d a vı́ a no
l eı́ ) e s un dato para g u a r d a r en l a memoria .
r e c i b i P u n V e l =0;
bandera =2;
}
i f ( r e c i b i P u n E f e c t o ) { // S i l o que r e c i bı́ f u e un parámetro de un
e f e c t o , a v i s o que hay que a c t u a l i z a r l o s p a r á m e t r o s de l o s
efectos .
r e c i b i P u n E f e c t o =0;
146
banderaCambioEfectos =2;
}
i f ( r e c i b i D a t o P r o g ==1){ // S i l o que r e c i bı́ f u e l a s e ñ a l de que hay
que empezar a programar un i n s t u m e n t o s , me p r e p a r o para empezar y
a v i s ó .
r e c i b i D a t o P r o g =2; // Coloco e l v a l o r d e l r e g i s t r o r e c i b i D a t o P r o g en
2 para i n d i c a r l e a l programa que ya puede g r a b a r una p o s i c i ó n
de memoria .
datoProg=datoProgH+datoProgL ;
}
i f ( recibiCRC==1){ // S i l o que r e c i bı́ f u e un CRC, l o almaceno .
r e c i b i C R C d a t o=Temp ;
}
Flag . AddrFlag = 0 ; // Fin de l a t r a n s m i c i ó n . R e i n i c i o b a n d e r a s .
Flag . DataFlag = 0 ;
RAMPtr = &RAMBuffer [ 0 ] ; // R e i n i c i o e l p u n t e r o a ’ RAMBuffer ’ .
}
}
e l s e i f ( ( I2C1STATbits .R W == 1 )&&(I2C1STATbits . D A == 0 ) ) { // Chequeo s i
l o que r e c i bı́ f u e una orden de e n v i a r d a t o s y f u e a mi d i r e c c i ó n .
}
SI2C1IF = 0 ; // Borro l a bandera de i n t e r r u p c i ó n d e l I2C .
}
slaves/I2C.c
147
Clase ’I2C.h’
#d e f i n e d i r e c c i o n 0 x4e
e x t e r n u n s i g n e d s h o r t i n t bandera ;
e x t e r n u n s i g n e d s h o r t i n t banderaCambioEfectos ;
extern int lim ite ;
extern int multiplicador ;
e x t e r n u n s i g n e d s h o r t i n t RAMBuffer [ 2 0 ] ; //RAM a r e a which w i l l work a s
EEPROM f o r Master I2C d e v i c e
e x t e r n u n s i g n e d s h o r t i n t *RAMPtr ; // P o i n t e r t o RAM memory l o c a t i o n s
void i 2 c 1 i n i t ( void ) ;
void a t t r i b u t e ( ( interrupt , no auto psv ) ) SI2C1Interrupt ( void ) ;
s t r u c t FlagType
{
u n s i g n e d c h a r AddrFlag : 1 ;
u n s i g n e d c h a r DataFlag : 1 ;
};
e x t e r n s t r u c t FlagType Flag ;
// c o n s t i n t a b o r r a r =3;
slaves/I2C.h
148
Clase ’iniciarReloj.c’
#i n c l u d e <p33FJ128GP802 . h>
#i n c l u d e ” i n i c i a r R e l o j . h”
149
Clase ’iniciarReloj.h’
void InitClock ( ) ;
slaves/iniciarReloj.h
150
Clase ’manejoPuertos.c’
#i n c l u d e <p33FJ128GP802 . h>
#i n c l u d e ” manejoPuertos . h”
151
Clase ’manejoPuertos.h’
v o i d mapearPuertos ( ) ;
slaves/manejoPuertos.h
152
Clase ’tablas.h’
Dado que esta clase contiene sólo tablas y algunas de ellas tienen varios miles de elementos,
se decidió colocar solo el encabezado de las tablas.
const i n t seno [ 1 5 0 0 0 ] ;
c o n s t i n t diviWah [ 6 4 ] ;
const long int parteMultiplicadores [ 6 4 ] ;
c o n s t i n t fCuadrado [ 6 4 ] ;
psv const unsigned i n t a t t r i b u t e ( ( s p a c e ( psv ) ) )
TodosLosAumentosSeno [ 8 6 ] [ 1 5 ] ;
psv const int a t t r i b u t e ( ( s p a c e ( psv ) ) )
todosLosIncrementos [ 8 6 ] [ 1 5 ] [ 7 ] ;
psv const unsigned i n t a t t r i b u t e ( ( s p a c e ( psv ) ) )
todosLosCambios [ 8 6 ] [ 1 5 ] [ 7 ] ;
psv const unsigned i n t a t t r i b u t e ( ( s p a c e ( psv ) , a d d r e s s ( 0 xE4C8 ) ) )
espacioLibre [ 5 0 0 ] ;
psv const long int a t t r i b u t e ( ( s p a c e ( psv ) ) ) e n v o l s I n i c i a l e s [ 8 6 ] [ 1 5 ]
slaves/encTablas.h
153
Clase ’Main.c’
#d e f i n e maximoCiclos 200000
#d e f i n e l a r g o T a b l a S e n o s 15000
#d e f i n e s a m p l e s B a j a r 60
#i n c l u d e <p33FJ128GP802 . h>
#i n c l u d e ” dac . h”
#i n c l u d e ” manejoPuertos . h”
#i n c l u d e ” I2C . h”
#i n c l u d e ” t a b l a s . h”
#i n c l u d e ” e f e c t o s . h”
//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−C o n f i g u r a c i ó n −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
FOSCSEL(FNOSC FRC) ;
FOSC(FCKSM CSECMD & OSCIOFNC OFF & POSCMD NONE) ;
FWDT(FWDTEN OFF) ;
e x t e r n i n t MemWriteLatch ( i n t , i n t , i n t , i n t ) ;
e x t e r n i n t MemCommand( i n t ) ;
u n s i g n e d s h o r t i n t banderaCambioEfectos ; // Esta v a r i a b l e i n d i c a s i s e
r e c i b i ó n una i n s t r u c c i ó n de cambio de uno de l o s parámtros de l o s
efectos .
154
u n s i g n e d s h o r t i n t *RAMPtr ; // Es e l p u n t e r o a l á r e a de memoria mencionada .
//−−−−−−−−−−−−−V a r i a b l e s i n t e r n a s de g e n e r a c i ó n d e l s o n i d o −−−−−−−−−−−−−
i n t i n d i c e s [ ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ; // Este v e c t o r i n d i c a en
que v a l o r de l a t a b l a de i n c r e m e n t o s de cada e n v o l v e n t e s e e s t a .
i n t t i e m p o s [ ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ; // Este e s e l v e c t o r que
i n d i c a en que v a l o r d e l s e n o e s t a cada una de l a s componentes de l a
nota .
u n s i g n e d s h o r t i n t v e l o c i d a d ; // Esta v a r i a b l e i n d i c a l a ’ v e l o c i d a d ’ de l a
nota que hay que r e p r o d u c i r , l a ’ v e l o c i d a d ’ e s a l i n t e n s i d a d .
u n s i g n e d s h o r t i n t s a m p l e s P a r a B a j a r ; // Cuenta c u a n t o s s a m p l e s p a s a r o n
d e s d e l a ú l t i m a vez que b a j e l a v e l o c i d a d .
l o n g i n t s a l i d a L o n g ; // Para m e j o r a r l a p r e s i c i ó n en l a s c u e n t a s , l a
s a l i d a e s c a l c u l a d a com un ’ l o n g i n t ’ , e s t a v a r i a b l e e s l a que toma e s e
valor .
// s a l i d a L o n g e s e l r e s u l t a d o de l a suma de cada f r e c u e n c i a por su
envolvente .
l o n g i n t c i c l o s =0; // Esta v a r i a b l e l l e v a e l c o n t e o de l a c a n t i d a d de
m ue s t ra s g e n e r a d a s ya d e l s o n i d o de l a nota que s e e s t a r e p r o d u c i e n d o
155
i n t l i m i t e I f ; // Es una v a r i a b l e a u x i l i a r u t i l i z a d a para s a b e r donde poner
e l lı́ m i t e en e l e f e c t o ’ l i m i t e I f ’ .
i n t f r e c C o r t e ; // Es una v a r i a b l e a u x i l i a r u t i l i z a d a para s a b e r l a
f r e c u e n c i a de c o r t e en e l e f e c t o ’ wah wah ’ .
i n t f r e c u e n c i a T r e m o l o ; // Es una v a r i a b l e a u x i l i a r u t i l i z a d a para s a b e r l a
f r e c u e n c i a en e l e f e c t o ’ t ré m o l o ’ .
i n t a t r a s o E c o ; // Es una v a r i a b l e a u x i l i a r u t i l i z a d a para s a b e r e l a t r a s o
del e f e c t o ’ eco ’ .
i n t datoProg ;
i n t datoProgL ;
i n t datoProgH ;
short int recibiDatoProg ;
s h o r t i n t terminoPrograma ;
c h a r recibiCRC ;
char recibiCRC dato = 0 ;
char llegoFF ;
l o n g i n t CRC;
c h a r * CRC L ;
//−−−−−−−−−−−−−−Fin de l a d e c l a r a c i ó n de v a r i a b l e s g l o b a l e s −−−−−−−−−−−−−−
156
nota=RAMBuffer [ 0 ] ; // Rumbuffer [ 0 ] guarda e l número de nota a
r e p r o d u c i r , por l o c u a l s e l o a s o c i a a l a v a r i a b l e ’ nota ’
i f ( nota ==100){
estoyEnNoteOff =1;
s a m p l e s P a r a B a j a r =0;
}
else {
estoyEnNoteOff =0;
v e l o c i d a d=RAMBuffer [ 1 ] ; // Rumbuffer [ 1 ] guarda l a v e l o c i d a d de nota a
r e p r o d u c i r , por l o c u a l s e l o a s o c i a a l a v a r i a b l e ’ v e l o c i d a d ’
t i e m p o s [ i ] = 0 ; // Se r e i n i c i a e l v a l o r de l a t a b l a de s e n o s donde
comienza cada componente de l a nota .
i n d i c e s [ i ] = 0 ; // Se r e i n i c i a e l v e c t o r i n d i c e s , para que t o d a s l a s
e n v o l v e n t e s comiencen aumentando por su p r i m e r i n c r e m e n t o .
i n c r e m e n t o s [ i ] [ j ]= t o d o s L o s I n c r e m e n t o s [ nota ] [ i ] [ j ] ; // Se c o p i a n en
l a m a t r i z ’ i n c r e m e n t o s ’ l o s e l e m e n t o s de l a m a t r i z
’ t o d o s L o s I n c r e m e n t o s ’ que c o r r e s p o n d e n a l a nota a r e p r o d u c i r ,
d i c h a m a t r i z c o n t i e n e l o s i n c r e m e n t o s que s e l e hacen a cada
e n v o l v e n t e l u e g o de g e n e r a r cada sample .
}
}
c i c l o s =0; // Se r e i n i c i a l a c u e n t a de l o s c i c l o s . Esta c u e n t a s i r v e
para s a b e r cuando l l e g o e l momento de cambiar l o s i n c r e m e n t o s de
cada e n v o l v e n t e .
}
return ;
}
void calcularSample ( ) {
157
i n t i n d ; // Esta v a r i a b l e s i r v e para almacenar con c u a l de s u s
i n c r e m e n t o s e s t a s i e n d o i n c r e m e n t a d a l a componenente que s e e s t a
analizando .
// S o l o e s una c o p i a de l a v a r i a b l e i n d i c e s [ i ] , donde i e s l a e n v o l v e n t e a
a n a l i z a r , s i r v e para a h o r r a r c i c l o s de r e l o j .
//−−−−−−−−−−−−−−−−−−−−−−−−C á l c u l o de l a p r i m e r componente d e l
sample−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
i f ( c i c l o s <maximoCiclos ) {
i n d=i n d i c e s [ 0 ] ; // Se l e a s i g n a a l a v a r i a b l e i n d e l v a l o r d e l p r i m e r
indice .
s a l i d a L o n g =( l o n g i n t ) ( * punteroMascaras [ 0 ] ) * ( l o n g
i n t ) ( s e n o [ t i e m p o s [ 0 ] ] ) ; // s a l i d a L o n g va a v a l e r l a suma de cada
componenente en f r e c u e n c i a por l a e n v o l v e n t e que l e c o r r e s p o n d e .
//Dado que e s t a e s l a p r i m e r componente en f r e c u e n c i a que s e e s t a
a n a l i z a n d o s a l i d a L o n g comienza v a l i e n d o e l v a l o r c o r r e s p o n d i e n t e a e s t a
componente .
// ’ * punteroMascaras [ 0 ] ’ e s un p u n t e r o que apunta a l o s 2 b y t e s más
s i g n i f i c a t i v o s de l a e n v o l v e n t e que s e l e a p l i c a a l a p r i m e r f r e c u e n c i a .
t i e m p o s [ 0 ] = t i e m p o s [ 0 ] + aumentosSeno [ 0 ] ; // Se aumenta e l v a l o r d e l c u a l
s e va a c a l c u l a r e l s e n o en e l próximo sample .
e n v o l s [ 0 ] = e n v o l s [ 0 ] + i n c r e m e n t o s [ 0 ] [ i n d ] ; // Se aumenta e l v a l o r de l a
e n v o l v e n t e que s e va a u s a r para m u l t i p l i c a r a l a componente en
f r e c u e n c i a para e l próximo sample .
//La v a r i a b l e ’ * punteroMascaras [ 0 ] ’ , t i e n e como v a l o r l o s dos b y t e s más
s i g n i f i c a t i v o s de l a v a r i a b l e ’ e n v o l s [ 0 ] ’
t i e m p o s [ 0 ] = t i e m p o s [0] − l a r g o T a b l a S e n o s ;
}
i f ( e n v o l s [ 0 ] < 0 ) { // S i por a l g u n a i m p r e s i c i ó n en l a t a b l a de
i n c r e m e n t o s o por a l g u n a o t r a i m p r e s i c i ó n , e l v a l o r de una
e n v o l v e n t e e s menor a c e r o , s e l o toma como c e r o .
envols [0]=0;
}
i f ( c i c l o s >cambios [ 0 ] [ i n d ] ) { // Se comprueba s i l a c a n t i d a d de s a m p l e s
c a l c u d o s s u p e r a l a c a n t i d a d a l a c u a l hay que m o d i f i c a r e l
i n c r e m e n t o s de l a e n v o l v e n t e .
i n d i c e s [0]+=1;
}
i n d=i n d i c e s [ 1 ] ;
158
s a l i d a L o n g=s a l i d a L o n g +( l o n g i n t ) ( * punteroMascaras [ 1 ] ) * ( l o n g
i n t ) ( seno [ tiempos [ 1 ] ] ) ;
t i e m p o s [ 1 ] = t i e m p o s [ 1 ] + aumentosSeno [ 1 ] ;
envols [1]= envols [1]+ incrementos [ 1 ] [ ind ] ;
i f ( t i e m p o s [ 1 ] > ( l a r g o T a b l a S e n o s −1) ) {
t i e m p o s [ 1 ] = t i e m p o s [1] − l a r g o T a b l a S e n o s ;
}
i f ( c i c l o s >cambios [ 1 ] [ i n d ] ) {
i n d i c e s [1]+=1;
}
i n d=i n d i c e s [ 2 ] ;
s a l i d a L o n g=s a l i d a L o n g +( l o n g i n t ) ( * punteroMascaras [ 2 ] ) * ( l o n g
i n t ) ( seno [ tiempos [ 2 ] ] ) ;
t i e m p o s [ 2 ] = t i e m p o s [ 2 ] + aumentosSeno [ 2 ] ;
envols [2]= envols [2]+ incrementos [ 2 ] [ ind ] ;
i f ( t i e m p o s [ 2 ] > ( l a r g o T a b l a S e n o s −1) ) {
t i e m p o s [ 2 ] = t i e m p o s [2] − l a r g o T a b l a S e n o s ;
}
i f ( c i c l o s >cambios [ 2 ] [ i n d ] ) {
i n d i c e s [2]+=1;
}
i n d=i n d i c e s [ 3 ] ;
s a l i d a L o n g=s a l i d a L o n g +( l o n g i n t ) ( * punteroMascaras [ 3 ] ) * ( l o n g
i n t ) ( seno [ tiempos [ 3 ] ] ) ;
t i e m p o s [ 3 ] = t i e m p o s [ 3 ] + aumentosSeno [ 3 ] ;
envols [3]= envols [3]+ incrementos [ 3 ] [ ind ] ;
i f ( t i e m p o s [ 3 ] > ( l a r g o T a b l a S e n o s −1) ) {
t i e m p o s [ 3 ] = t i e m p o s [3] − l a r g o T a b l a S e n o s ;
}
i f ( c i c l o s >cambios [ 3 ] [ i n d ] ) {
i n d i c e s [3]+=1;
}
i n d=i n d i c e s [ 4 ] ;
s a l i d a L o n g=s a l i d a L o n g +( l o n g i n t ) ( * punteroMascaras [ 4 ] ) * ( l o n g
i n t ) ( seno [ tiempos [ 4 ] ] ) ;
t i e m p o s [ 4 ] = t i e m p o s [ 4 ] + aumentosSeno [ 4 ] ;
envols [4]= envols [4]+ incrementos [ 4 ] [ ind ] ;
i f ( t i e m p o s [ 4 ] > ( l a r g o T a b l a S e n o s −1) ) {
t i e m p o s [ 4 ] = t i e m p o s [4] − l a r g o T a b l a S e n o s ;
}
i f ( c i c l o s >cambios [ 4 ] [ i n d ] ) {
i n d i c e s [4]+=1;
}
i n d=i n d i c e s [ 5 ] ;
s a l i d a L o n g=s a l i d a L o n g +( l o n g i n t ) ( * punteroMascaras [ 5 ] ) * ( l o n g
i n t ) ( seno [ tiempos [ 5 ] ] ) ;
t i e m p o s [ 5 ] = t i e m p o s [ 5 ] + aumentosSeno [ 5 ] ;
envols [5]= envols [5]+ incrementos [ 5 ] [ ind ] ;
i f ( t i e m p o s [ 5 ] > ( l a r g o T a b l a S e n o s −1) ) {
t i e m p o s [ 5 ] = t i e m p o s [5] − l a r g o T a b l a S e n o s ;
}
i f ( c i c l o s >cambios [ 5 ] [ i n d ] ) {
i n d i c e s [5]+=1;
}
i n d=i n d i c e s [ 6 ] ;
s a l i d a L o n g=s a l i d a L o n g +( l o n g i n t ) ( * punteroMascaras [ 6 ] ) * ( l o n g
i n t ) ( seno [ tiempos [ 6 ] ] ) ;
159
t i e m p o s [ 6 ] = t i e m p o s [ 6 ] + aumentosSeno [ 6 ] ;
envols [6]= envols [6]+ incrementos [ 6 ] [ ind ] ;
i f ( t i e m p o s [ 6 ] > ( l a r g o T a b l a S e n o s −1) ) {
t i e m p o s [ 6 ] = t i e m p o s [6] − l a r g o T a b l a S e n o s ;
}
i f ( c i c l o s >cambios [ 6 ] [ i n d ] ) {
i n d i c e s [6]+=1;
}
i n d=i n d i c e s [ 7 ] ;
s a l i d a L o n g=s a l i d a L o n g +( l o n g i n t ) ( * punteroMascaras [ 7 ] ) * ( l o n g
i n t ) ( seno [ tiempos [ 7 ] ] ) ;
t i e m p o s [ 7 ] = t i e m p o s [ 7 ] + aumentosSeno [ 7 ] ;
envols [7]= envols [7]+ incrementos [ 7 ] [ ind ] ;
i f ( t i e m p o s [ 7 ] > ( l a r g o T a b l a S e n o s −1) ) {
t i e m p o s [ 7 ] = t i e m p o s [7] − l a r g o T a b l a S e n o s ;
}
i f ( c i c l o s >cambios [ 7 ] [ i n d ] ) {
i n d i c e s [7]+=1;
}
i n d=i n d i c e s [ 8 ] ;
s a l i d a L o n g=s a l i d a L o n g +( l o n g i n t ) ( * punteroMascaras [ 8 ] ) * ( l o n g
i n t ) ( seno [ tiempos [ 8 ] ] ) ;
t i e m p o s [ 8 ] = t i e m p o s [ 8 ] + aumentosSeno [ 8 ] ;
envols [8]= envols [8]+ incrementos [ 8 ] [ ind ] ;
i f ( t i e m p o s [ 8 ] > ( l a r g o T a b l a S e n o s −1) ) {
t i e m p o s [ 8 ] = t i e m p o s [8] − l a r g o T a b l a S e n o s ;
}
i f ( c i c l o s >cambios [ 8 ] [ i n d ] ) {
i n d i c e s [8]+=1;
}
i n d=i n d i c e s [ 9 ] ;
s a l i d a L o n g=s a l i d a L o n g +( l o n g i n t ) ( * punteroMascaras [ 9 ] ) * ( l o n g
i n t ) ( seno [ tiempos [ 9 ] ] ) ;
t i e m p o s [ 9 ] = t i e m p o s [ 9 ] + aumentosSeno [ 9 ] ;
envols [9]= envols [9]+ incrementos [ 9 ] [ ind ] ;
i f ( t i e m p o s [ 9 ] > ( l a r g o T a b l a S e n o s −1) ) {
t i e m p o s [ 9 ] = t i e m p o s [9] − l a r g o T a b l a S e n o s ;
}
i f ( c i c l o s >cambios [ 9 ] [ i n d ] ) {
i n d i c e s [9]+=1;
}
i n d=i n d i c e s [ 1 0 ] ;
s a l i d a L o n g=s a l i d a L o n g +( l o n g i n t ) ( * punteroMascaras [ 1 0 ] ) * ( l o n g
i n t ) ( seno [ tiempos [ 1 0 ] ] ) ;
t i e m p o s [ 1 0 ] = t i e m p o s [ 1 0 ] + aumentosSeno [ 1 0 ] ;
envols [10]= envols [10]+ incrementos [ 1 0 ] [ ind ] ;
i f ( t i e m p o s [ 1 0 ] > ( l a r g o T a b l a S e n o s −1) ) {
t i e m p o s [ 1 0 ] = t i e m p o s [10] − l a r g o T a b l a S e n o s ;
}
i f ( c i c l o s >cambios [ 1 0 ] [ i n d ] ) {
i n d i c e s [10]+=1;
}
i n d=i n d i c e s [ 1 1 ] ;
s a l i d a L o n g=s a l i d a L o n g +( l o n g i n t ) ( * punteroMascaras [ 1 1 ] ) * ( l o n g
i n t ) ( seno [ tiempos [ 1 1 ] ] ) ;
t i e m p o s [ 1 1 ] = t i e m p o s [ 1 1 ] + aumentosSeno [ 1 1 ] ;
envols [11]= envols [11]+ incrementos [ 1 1 ] [ ind ] ;
i f ( t i e m p o s [ 1 1 ] > ( l a r g o T a b l a S e n o s −1) ) {
160
t i e m p o s [ 1 1 ] = t i e m p o s [11] − l a r g o T a b l a S e n o s ;
}
i f ( c i c l o s >cambios [ 1 1 ] [ i n d ] ) {
i n d i c e s [11]+=1;
}
i n d=i n d i c e s [ 1 2 ] ;
s a l i d a L o n g=s a l i d a L o n g +( l o n g i n t ) ( * punteroMascaras [ 1 2 ] ) * ( l o n g
i n t ) ( seno [ tiempos [ 1 2 ] ] ) ;
t i e m p o s [ 1 2 ] = t i e m p o s [ 1 2 ] + aumentosSeno [ 1 2 ] ;
envols [12]= envols [12]+ incrementos [ 1 2 ] [ ind ] ;
i f ( t i e m p o s [ 1 2 ] > ( l a r g o T a b l a S e n o s −1) ) {
t i e m p o s [ 1 2 ] = t i e m p o s [12] − l a r g o T a b l a S e n o s ;
}
i f ( c i c l o s >cambios [ 1 2 ] [ i n d ] ) {
i n d i c e s [12]+=1;
}
i n d=i n d i c e s [ 1 3 ] ;
s a l i d a L o n g=s a l i d a L o n g +( l o n g i n t ) ( * punteroMascaras [ 1 3 ] ) * ( l o n g
i n t ) ( seno [ tiempos [ 1 3 ] ] ) ;
t i e m p o s [ 1 3 ] = t i e m p o s [ 1 3 ] + aumentosSeno [ 1 3 ] ;
envols [13]= envols [13]+ incrementos [ 1 3 ] [ ind ] ;
i f ( t i e m p o s [ 1 3 ] > ( l a r g o T a b l a S e n o s −1) ) {
t i e m p o s [ 1 3 ] = t i e m p o s [13] − l a r g o T a b l a S e n o s ;
}
i f ( c i c l o s >cambios [ 1 3 ] [ i n d ] ) {
i n d i c e s [13]+=1;
}
i n d=i n d i c e s [ 1 4 ] ;
s a l i d a L o n g=s a l i d a L o n g +( l o n g i n t ) ( * punteroMascaras [ 1 4 ] ) * ( l o n g
i n t ) ( seno [ tiempos [ 1 4 ] ] ) ;
t i e m p o s [ 1 4 ] = t i e m p o s [ 1 4 ] + aumentosSeno [ 1 4 ] ;
envols [14]= envols [14]+ incrementos [ 1 4 ] [ ind ] ;
i f ( t i e m p o s [ 1 4 ] > ( l a r g o T a b l a S e n o s −1) ) {
t i e m p o s [ 1 4 ] = t i e m p o s [14] − l a r g o T a b l a S e n o s ;
}
i f ( c i c l o s >cambios [ 1 4 ] [ i n d ] ) {
i n d i c e s [14]+=1;
}
s a l i d a L o n g=s a l i d a L o n g * v e l o c i d a d ; // El sample r e s u l t a n t e s e l o
m u l t i p l i c a por l a i n t e n s i d a d con l a que l a hay que r e p r o d u c i r l a
nota .
c i c l o s ++; // Se a c t u a l i z a l a c a n t i d a d de s a m p l e s g e n e r a d o s .
s a m p l e s P a r a B a j a r++;
i f ( s a m p l e s P a r a B a j a r==s a m p l e s B a j a r ) { // V e r i f i c o que c o r r e s p o n d e
b a j a r l a i n t e n s i d a d de l a nota .
v e l o c i d a d −−;
161
s a m p l e s P a r a B a j a r =0;
}
e l s e { // S i ya l a b a j e a l mı́nimo , no e s t o y más en n o t e o f f .
estoyEnNoteOff =0;
}
}
}
}
}
v o i d a c t u a l i z a r P a r a m e t r o s E f e c t o s ( u n s i g n e d s h o r t i n t numEfec ) { // Este
método s e e n c a r g a de que l o s p a r á m e t r o s para l a g e n e r a c i ó n de e f e c t o s
esten actualizados .
//Hay que l l a m a r l o una vez con e l parámetro 6 para a c t u a l i z a r l o s
p a r á m e t r o s d e l p r i m e r e f e c t o y o t r a con e l parámetro 7 para a c t u a l i z a r
l o s p a r á m e t r o s d e l segundo .
// Es llamado cada vez que e l programa d e t e c t a un cambio en l o s p a r á m e t r o s
de l o s e f e c t o s .
// D e t e c t a e l e f e c t o que e s t a a c t i v a d o y a s i g n a a e l e f e c t o s e l e c c i o n a d o
s u s p ar á m e t r o s .
i n t i n d i c e E f e c t o=RAMBuffer [ numEfec ] ;
i f ( i n d i c e E f e c t o >2){
i f ( i n d i c e E f e c t o ==3){
l i m i t e I f =256 * RAMBuffer [ numEfec − 4 ] ;
return ;
}
i f ( i n d i c e E f e c t o ==4){
l i m i t e L o g =100 * RAMBuffer [ numEfec − 4 ] ;
return ;
}
i f ( i n d i c e E f e c t o ==5){
magnitudEco =512 * RAMBuffer [ numEfec − 4 ] ;
a t r a s o E c o =50* RAMBuffer [ numEfec − 2 ] ;
return ;
}
v a l o r V i b r a t o=RAMBuffer [ numEfec − 4 ] ;
return ;
}
i f ( i n d i c e E f e c t o ==2){
f r e c C o r t e=RAMBuffer [ numEfec − 4 ] / 2 ;
return ;
}
magnitudTremolo=RAMBuffer [ numEfec − 4 ] / 2 ;
f r e c u e n c i a T r e m o l o=1+RAMBuffer [ numEfec − 2 ] / 1 6 ;
return ;
}
162
// Primero d e t e c t a e l e f e c t o que hay que r e a l i z a r y l u e g o l l a m a a l método
que l o r e a l i z a .
i n t i n d i c e E f e c t o=RAMBuffer [ numEfec ] ;
i f ( i n d i c e E f e c t o >2){
i f ( i n d i c e E f e c t o ==3){
s a l i d a A U t i l i z a r=l i m i t a d o r I f ( s a l i d a A U t i l i z a r ) ;
return ;
}
i f ( i n d i c e E f e c t o ==4){
s a l i d a A U t i l i z a r=l i m i t a d o r L o g ( s a l i d a A U t i l i z a r ) ;
return ;
}
i f ( i n d i c e E f e c t o ==5){
s a l i d a A U t i l i z a r=s a l i d a A U t i l i z a r+e c o ( ) ;
return ;
}
vibrato () ;
return ;
}
i f ( i n d i c e E f e c t o ==2){
s a l i d a A U t i l i z a r=wahwah ( s a l i d a A U t i l i z a r ) ;
return ;
}
i f ( indiceEfecto ){
s a l i d a A U t i l i z a r=t r e m o l o ( s a l i d a A U t i l i z a r ) ;
return ;
}
return ;
}
c i c l o s =0;
RAMBuffer [ 2 ] = 2 ;
f o r ( i =0; i <15; i ++){
punteroMascaras [ i ]=& e n v o l s [ i ] ;
// E s t a s dos l i n e a s hacen que cada v e c t o r de punteroMascaras apunte a l o s
2 b y t e s más s i g n i f i c a t i v o s de su c o r r e s o n d i e n t e en e l v e c t o r ’ e n v o l s ’
punteroMascaras [ i ]++;
}
//−−−−−I n i c i a l i z a c i ó n de u t i l i d e a d e s d e l DSPIC−−−−−−
mapearPuertos ( ) ; // I n i c i a l i z a c i ó n de l o s p u e r t o s .
initDac () ; // I n i c i a l i z a c i ó n d e l d i g i t a l t o a n a l a o g c o n v e r t e r .
163
i n i c i a r E f e c t o s ( ) ; // I n i c i a l i z a c i ó n de l o s e f e c t o s .
//−−−−−−−−−−−−−−−−−−−−−−−−−Bucle p r i n c i p a l −−−−−−−−−−−−−−−−−−−−−−−−−
while (1) {
c a l c u l a r S a m p l e ( ) ; // C a l c u l o e l v a l o r d e l próximo sample
s a l i d a A U t i l i z a r =( * s a l i d a ) ; // Asigno a l a v a r i a b l e s a l i d a A U t i l i z a r l a
s a l i d a que voy a u t i l i z a r
aplicarEfectos (6) ; // A p l i c o l o s e f e c t o s .
aplicarEfectos (7) ;
}
DAC1RDAT = s a l i d a A U t i l i z a r ; // Pongo en l a c o l a de d a t o s d e l DAC e l
ú l t i m o v a l o r c a l c u l a d o .
agregarNota ( ) ;
bandera =0;
posDato =0;
}
i f ( banderaCambioEfectos >1){ // S i ’ banderaCambioEfectos ’ e s mayor
a 1 e s por que s e cambió a l g u n o de l o s p a r á m e t r o s de l o s
e f e c t o s y hay que a c t u a l i z a r l o s .
banderaCambioEfectos =0;
actualizarParametrosEfectos (6) ;
actualizarParametrosEfectos (7) ;
}
i f ( RAMBuffer [ 1 5 ] > 0 ) {
cargoPrograma ( ) ;
}
}
}
slaves/main.c
164
Anexo C, códigos del DSPic master
Clase ’adc.c’
#i n c l u d e <p33FJ128GP802 . h>
#i n c l u d e ” adc . h”
//−−−−−−−−−−−−−C o n f i g u r a c i ó n d e l c o n v e r s o r a n a l ó g i c o d i g i t a l −−−−−−−−−−−−−
v o i d initADC ( v o i d ) {
AD1CON1bits . SSRC = 0 b111 ;
AD1CON1bits .ASAM = 0 ;
AD1CON1bits .SAMP = 0 ;
AD1CON2bits .VCFG = 0 ;
AD1CON2bits . CHPS = 0 ;
AD1CON3bits .SAMC = 7 ;
AD1CON3bits .ADCS = 4 0 ;
AD1CON4bits .DMABL = 0 b10 ;
AD1CHS0bits . CH0SA = 1 1 ;
AD1CON1bits .ADON = 1 ;
}
master/adc.c
165
Clase ’adc.h’
v o i d initADC ( v o i d ) ;
master/adc.h
166
Clase ’cargarPrograma.c’
#i n c l u d e ” CargarPrograma . h”
#i n c l u d e ” I2C . h”
#i n c l u d e <p33FJ128GP802 . h>
//−−−−−−−−−−−−−−−−−−−−−−−−D e c l a r a c i ó n de v a r i a b l e s −−−−−−−−−−−−−−−−−−−−−−−
e x t e r n s h o r t i n t dato2 ;
//−−−−−−−−−−−−−−−−−−−−Fin de d e c l a r a c i ó n de v a r i a b l e s −−−−−−−−−−−−−−−−−−−−−
CRC+=v a l o r ;
}
v o i d cargarNuevoPrograma ( ) { //Una vez que s e e n t r a a l método para c a r g a r
un nuevo i n s t r u m e n t o no s e s a l e h a s t a c o m p l e t a r l a t r a n s m i s i ó n de d a t o s .
u n s i g n e d i n t l l e v o C u e n t a T o t a l = 0 ; // LLeva l a c u e n t a de l a c a n t i d a d
t o t a l de b y t e s e n v i a d o s .
llevoCuenta = 0;
short int pic ;
s h o r t i n t auxc ;
error = 0;
f o r ( p i c = d i r e c c i o n D e s d e ; p i c <( d i r e c c i o n D e s d e+numeroPics ) ; p i c++){
// Les a v i s o a t o d o s l o s p i c s que voy a comenzar a e n v i a r e l nuevo
instrumento .
e r r o r += I 2 C p o l l ( 2 * p i c ) ;
I2Cwrite (2* pic , 1 5 , 1 ) ;
}
w h i l e ( p r o g r a m a c i o n A c t i v a==1){
RCON = 0 x0 ;
i f ( b u f f e r L i s t o ==1){ //Cuando s e r e c i b e n 100 v a l o r e s s e l e v a n t a l a
s e ñ a l de b u f f e r L i s t o y s e p r o c e d e
CRC = 0 ; // a e n v i a r l a i n f o r m a c i ó n a l o s p i c s .
b u f f e r L i s t o =0;
f o r ( auxc =0; auxc <(UNA RONDA* 2 ) ; auxc=auxc +2){ // Recorro l o s c i e n
v a l o r e s que r e c i bı́
e r r o r += I 2 C p o l l ( 2 * p i c ) ; // Los b y t e s s e e n vı́ a n en r o n d a s de a
dos , para que e l i n t r e c i b i d o ya pueden s e r g r a b a d o s en l a
memoria d e l p i c e s c l a v o .
retardo (5000) ;
I 2 C w r i t e ( 2 * p i c , 1 0 , b u f f e r D a t a [ auxc ] ) ;
retardo (5000) ;
e r r o r += I 2 C p o l l ( 2 * p i c ) ;
retardo (5000) ;
I 2 C w r i t e ( 2 * p i c , 1 1 , b u f f e r D a t a [ auxc +1]) ;
167
retardo (40000) ;
}
}
l l e v o C u e n t a T o t a l+=UNA RONDA * 2 ; // L l e v o l a c u e n t a t o t a l de c u a n t o s
d a t o s e n v ié .
dato2 = 0 ;
U2TXREG = S e g u i r E n v i a n d o ; // Le i n d i c o a l a PC que s i g a enviando
datos .
l l e v o C u e n t a ++;
}
i f ( l l e v o C u e n t a T o t a l >=43860){ // Luego de e n v i a r t o d o s l o s datos , l e
a v i s o a l a PC que t e r m i né de c a r g a r e l i n s t r u m e n t o ,
p r o g r a m a c i o n A c t i v a =0;
l l e v o C u e n t a =0;
}
}
}
master/cargarPrograma.c
168
Clase ’cargarPrograma.h’
#d e f i n e SeguirEnviando 0 x26
#d e f i n e EMPEZAR PROGRAMAR 0 x32
#d e f i n e DETENER ENVIO 0 x18
#d e f i n e ERROR CON PIC 0xFF
#d e f i n e GRABACION EXITOSA 0xF0
#d e f i n e FINALIZO TRANSMICION 0xF2
#d e f i n e UNA RONDA 50
#d e f i n e VALOR FINAL 0 x30
#d e f i n e numeroPics 3
#d e f i n e d i r e c c i o n D e s d e 0 x4e
v o i d cargarNuevoPrograma ( ) ;
v o i d realizoCRC ( s h o r t i n t v a l o r ) ;
master/cargarPrograma.h
169
Clase ’I2C.c’
#i n c l u d e <p33FJ128GP802 . h>
#i n c l u d e ” I2C . h”
i n t i =0;
f o r ( i =0; i <c i c l o s ; i ++){
Nop ( ) ;
}
return ;
}
v o i d i n i c i a r I 2 C ( ) { // Este método c o n f i g u r a l o s r e g i s t r o s n e c e s a r i o s d e l
DSPic para poder u t i l i z a r e l I2C .
ODCBbits .ODCB5=1;
ODCBbits .ODCB6=1;
ODCBbits .ODCB8=1;
ODCBbits .ODCB9=1;
i n t temp ;
I2C1BRG = 9 5 ;
I2C1CONbits . I2CEN = 0 ;
I2C1CONbits . DISSLW = 1 ;
I F S 1 b i t s . MI2C1IF = 0 ;
I2C1CONbits . I2CEN = 1 ;
temp = I2CRCV ;
reset i2c bus () ;
}
int x = 0;
i n t j =1;
i n t s e g u i r =1;
I2C1CONbits .ACKDT = 0 ;
retardo (398) ;
I2C1CONbits . SEN = 1 ;
j =0;
w h i l e ( I2C1CONbits . SEN&&s e g u i r ) {
retardo (38) ;
x++;
i f ( x > 20) {
s e g u i r =0;
}
}
retardo (78) ;
return ;
}
v o i d i 2 c r e s t a r t ( v o i d ) { // E s t a s f u n c i o n e s s e usan para r e i n i c i a r
c o n e c c i o n e s de I2C .
i n t j =0;
170
int x = 0;
I2C1CONbits .RSEN = 1 ;
j =1;
w h i l e ( I2C1CONbits .RSEN) {
retardo (38) ;
x++;
i f ( x > 20)
break ;
}
retardo (78) ;
}
void r e s e t i 2 c b u s ( void ) {
int x = 0;
I2C1CONbits .PEN = 1 ;
w h i l e ( I2C1CONbits .PEN) {
retardo (38) ;
x ++;
i f ( x > 2 0 ) break ;
}
I2C1CONbits .RCEN = 0 ;
I F S 1 b i t s . MI2C1IF = 0 ;
I2C1STATbits .IWCOL = 0 ;
I2C1STATbits .BCL = 0 ;
retardo (398) ;
}
int i ;
w h i l e ( I2C1STATbits .TBF) { }
I F S 1 b i t s . MI2C1IF = 0 ;
I2CTRN = data ;
f o r ( i =0; i <500; i ++){
i f ( ! I2C1STATbits .TRSTAT) break ;
retardo (38) ;
}
i f ( i == 5 0 0 ) {
return (1) ;
}
f o r ( i =0; i <10; i ++){
i f ( I2C1STATbits .ACKSTAT == 1 ) {
reset i2c bus () ;
return (0) ;
}
retardo (38) ;
}
retardo (78) ;
return (1) ;
}
171
char i 2 c r e a d ( void ) { // Esta e s l a f u n c i ó n que s e u t i l i z a para l e e r b y t e s
por I2C .
int i = 0;
c h a r data = 0 ;
I2C1CONbits .RCEN = 1 ;
w h i l e ( ! I2C1STATbits .RBF) {
i ++;
i f ( i > 2 0 0 0 ) break ;
}
data = I2CRCV ;
r e t u r n data ;
}
c h a r i 2 c r e a d a c k ( v o i d ) { // Esta f u n c i ó n l e e d a t o s y g e n e r a l o s
’ a c k n o w l e d g e s ’ que e l p r o t o c o l o de c o m u n i c a c i ó n i n d i c a .
int i = 0;
c h a r data = 0 ;
I2C1CONbits .RCEN = 1 ;
w h i l e ( ! I2C1STATbits .RBF) {
i ++;
i f ( i > 2 0 0 0 ) break ;
}
data = I2CRCV ;
I2C1CONbits .ACKEN = 1 ;
retardo (398) ;
r e t u r n data ;
}
i2c start () ;
retardo (398) ;
s e n d i 2 c b y t e ( addr ) ;
retardo (398) ;
s e n d i 2 c b y t e ( subaddr ) ;
retardo (398) ;
send i2c byte ( value ) ;
retardo (398) ;
reset i2c bus () ;
return ;
}
172
c h a r temp ;
i2c start () ;
s e n d i 2 c b y t e ( addr ) ;
s e n d i 2 c b y t e ( subaddr ) ;
retardo (398) ;
i2c restart () ;
s e n d i 2 c b y t e ( addr | 0 x01 ) ;
temp = i 2 c r e a d ( ) ;
reset i2c bus () ;
r e t u r n temp ;
}
u n s i g n e d c h a r temp = 0 ;
i2c start () ;
temp = s e n d i 2 c b y t e ( addr ) ;
reset i2c bus () ;
r e t u r n temp ;
}
master/I2C.c
173
Clase ’I2C.h’
void i n i c i a r I 2 C ( ) ;
void retardo ( i n t c i c l o s ) ;
void i 2 c s t a r t ( void ) ;
void i 2 c r e s t a r t ( void ) ;
void r e s e t i 2 c b u s ( void ) ;
c h a r s e n d i 2 c b y t e ( i n t data ) ;
char i 2 c r e a d ( void ) ;
char i 2 c r e a d a c k ( void ) ;
v o i d I 2 C w r i t e ( c h a r addr , c h a r subaddr , c h a r v a l u e ) ;
c h a r I2Cread ( c h a r addr , c h a r subaddr ) ;
u n s i g n e d c h a r I 2 C p o l l ( c h a r addr ) ;
master/I2C.h
174
Clase ’mapearP.c’
#i n c l u d e <p33FJ128GP802 . h>
#i n c l u d e ”mapearP . h”
//−−−−−−−−−−−−−−−−−−−−−−−−C o n f i g u r a c i ó n de p u e r t o s −−−−−−−−−−−−−−−−−−−−−−−−
TRISB=1;
TRISBbits . TRISB12=1;
TRISBbits . TRISB13=1;
TRISBbits . TRISB14=1;
TRISBbits . TRISB15=1;
TRISBbits . TRISB6 = 1 ;
TRISAbits . TRISA0=1;
TRISAbits . TRISA1=1;
builtin write OSCCONL (OSCCON & ˜(1<<6) ) ;
RPINR18bits .U1RXR = 0 ;
RPINR19bits .U2RXR = 6 ;
RPINR18bits . U1CTSR = 1 ;
RPOR1bits . RP2R = 3 ;
RPOR3bits . RP7R = 5 ;
RPOR1bits . RP3R = 4 ;
builtin write OSCCONL (OSCCON | (1<<6) ) ;
}
master/mapearP.c
175
Clase ’mapearP.h’
v o i d mapearPuertos ( ) ;
master/mapearP.h
176
Clase ’serie.c’
#i n c l u d e <p33FJ128GP802 . h>
#i n c l u d e ” s e r i e . h”
//−−−−−−−−−−−−−−−−D e c l a r a c i ó n de v a r i a b l e s y d e f i n i c i o n e s −−−−−−−−−−−−−−−−
#d e f i n e UNA RONDA 50
#d e f i n e EMPEZAR PROGRAMAR 0 x32
u n s i g n e d s h o r t i n t dato ;
extern unsigned i n t datosFuncion [ ] ;
e x t e r n u n s i g n e d s h o r t i n t bandera ;
extern short int
p o s B u f f e r , b u f f e r L i s t o , programacionActiva , posDato , f u n c i o n A c t u a l , dato2 ;
extern int bufferData [ ] ;
e x t e r n s h o r t i n t flagNoteOn , f l a g N o t e O f f ;
//−−−−−−−−−−−−−−−−−−−C o n f i g u r a c i ó n de l o s p u e r t o s s e r i e −−−−−−−−−−−−−−−−−−−−
// El p u e r t o s e r i e 1 e s e l que r e c i b e l o s comandos MIDI .
U1MODE=8; // =0000000000001000;
U1BRG = 3 1 6 ;
U1STA=0;
IPC3=0;
IPC2 =4096;
I F S 0 b i t s . U1TXIF = 0 ;
I E C 0 b i t s . U1TXIE = 0 ;
I F S 0 b i t s . U1RXIF = 0 ;
I E C 0 b i t s . U1RXIE = 1 ;
U1MODEbits .UARTEN = 1 ;
U1STAbits .UTXEN = 1 ;
}
U2MODE=0;
U2BRG = 2 5 6 ;
U2STA=0;
IPC7 =1024; // =00010000000000
I F S 1 b i t s . U2TXIF = 0 ;
I E C 1 b i t s . U2TXIE = 0 ;
I F S 1 b i t s . U2RXIF = 0 ;
I E C 1 b i t s . U2RXIE = 1 ;
U2MODEbits .UARTEN = 1 ;
U2STAbits .UTXEN = 1 ;
}
//−−−−−−−−−−−−−−C o n f i g u r a c i ó n de i n t e r r u p c i o n e s de r e c e p c i ó n −−−−−−−−−−−−−−
177
dato=U1RXREG; // Guardo e l dato r e c i b i d o en l a v a r i a b l e ’ dato ’
posDato =0;
f l a g N o t e O f f =1;
}
posDato =0;
flagNoteOn =1;
else {
d a t o s F u n c i o n [ posDato ]= dato ; // Almaceno e l dato en l a p o s i c i ó n de
memoria c o r r e s p o n d i e n t e .
posDato++;
}
i f ( posDato >1){ // S i posDato e s mayor a 1 , e s por que ya r e c i bı́ t o d o s
l o s d a t o s n e c e s a r i o s para i n t e r p r e t a r e l comando MIDI .
bandera =10;
posDato =0;
}
I F S 0 b i t s . U1RXIF = 0 ; // Bajo l a bandera de i n t e r r u p c i ó n .
b u f f e r D a t a [ p o s B u f f e r ] = dato2 ; // S i ya e s t o y programando un
i n s t r u m e n t o , guardo en l a p o s i c i ó n de memoria que c o r r e s p o n d e e l
dato .
b u f f e r L i s t o =1;
}
}
178
else {
i f ( dato2==EMPEZAR PROGRAMAR) { // Chequeo s i r e c i bı́ l a s e ñ a l de empezar
a programar .
p o s B u f f e r =0; // R e i n i c i o l a e s c r i t u r a d e l b u f f e r .
}
i f ( dato==0x10 ) { // Chequeo s i a n t e s de s i n e s t a r en p r o g r a m a c i o n A c t i v a
me e n vı́ a n un 0 x10 .
U2TXREG = r c o n a u x ;
U2TXREG = ( r c o n a u x /0 x100 ) ;
}
}
I F S 1 b i t s . U2RXIF = 0 ; // Bajo l a bandera de i n t e r r u p c i ó n .
//−−−−−−−−−−−−−−−−−−−−−−I n t e r r u p c i o n e s de t r a n s m i c i ó n −−−−−−−−−−−−−−−−−−−−−−
// E s t a s i n t e r r u p c i o n e s e s t a n supuestamente d e s a c t i v a d a s , p e r o por l a s
dudas en c a s o de que s e a c t i v e n , bajamos l a bandera .
179
Clase ’serie.h’
v o i d InitUART1 ( ) ;
v o i d InitUART2 ( ) ;
void attribute (( interrupt , no auto psv ) ) U1RXInterrupt ( v o i d ) ;
void attribute (( interrupt , no auto psv ) ) U2RXInterrupt ( v o i d ) ;
void attribute (( interrupt , no auto psv ) ) U1TXInterrupt ( v o i d ) ;
void attribute (( interrupt , no auto psv ) ) U2TXInterrupt ( v o i d ) ;
master/serie.h
180
Clase ’main.c’
#i n c l u d e <p33FJ128GP802 . h>
#i n c l u d e ” I2C . h”
#i n c l u d e ” s e r i e . h”
#i n c l u d e ” CargarPrograma . h”
#i n c l u d e ” adc . h”
#i n c l u d e ”mapearP . h”
#d e f i n e n u m P e r i l l a s 6
//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−C o n f i g u r a c i ó n −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
FOSCSEL(FNOSC FRC) ;
FOSC(FCKSM CSECMD & OSCIOFNC OFF & POSCMD NONE) ;
FWDT(FWDTEN OFF) ;
//−−−−−−−−−−−−−−−−−−−−−−− D e c l a r a c i ó n de v a r i a b l e s −−−−−−−−−−−−−−−−−−−−−−−−
//−−−−−−−−−−−−−−−−−−−−−−−−C o n f i g u r a c i ó n d e l r e l o j −−−−−−−−−−−−−−−−−−−−−−−−−−
v o i d I n i t C l o c k ( ) { // Este método hace que e l DSPic f u n c i o n e con e l
c r i s t a l i n t e r n o a 40 Mips .
PLLFBD = 4 1 ;
CLKDIVbits . PLLPOST = 0 ;
CLKDIVbits . PLLPRE = 0 ;
OSCTUN = 0 ;
RCONbits .SWDTEN = 0 ;
builtin write OSCCONH ( 0 x01 ) ;
builtin write OSCCONL ( 0 x01 ) ;
w h i l e ( OSCCONbits .COSC != 0 b001 ) ;
w h i l e ( OSCCONbits .LOCK != 1 ) { } ;
}
181
// Es llamado cuando e l programa d e t e c t a que s e completo una i n s t r u c c i ó n
MIDI .
nota=d a t o s F u n c i o n [ 0 ] ;
e r r o r 2=e r r o r 2+I 2 C p o l l ( 2 * d e s t i n o ) ; // Envı́o por I2C a q u i e n c o r r e s p o n d e
l a nota .
I 2 C w r i t e ( 2 * d e s t i n o , 0 , nota ) ;
v e l o c i d a d=d a t o s F u n c i o n [ 1 ] ;
I2Cwrite (2* destino , 1 , velocidad ) ; // Envı́o por I2C a q u i e n c o r r e s p o n d e
la velocidad .
e r r o r 2=e r r o r 2+I 2 C p o l l ( 2 * d e s t i n o ) ;
notasGuardadas [ d e s t i n o −d i r e c c i o n D e s d e ]= nota ;
d e s t i n o ++; // F i j o e l d e s t i n o para e l próximo e n vı́ o .
i f ( d e s t i n o >=d i r e c c i o n D e s d e+numeroPics ) {
d e s t i n o=d i r e c c i o n D e s d e ;
}
i f ( e r r o r 2 >0){
U1TXREG = 5 ; // S i hubo un e r r o r a v i s o por p u e r t o s e r i e .
error2 = 0;
}
return ;
}
void quitarNota ( ) {
nota = 1 0 0 ; //La c a n t i d a d de n o t a s que e l s i s t e m a puede r e p r o d u c i r e s de
8 6 , s i s e pasa un 1 0 0 , l o s e s c l a v o s saben que deben apagar su nota
actual .
encontreNota = 0 ;
k=0;
while ( ! encontreNota ) {
i f ( notasGuardadas [ k]== nota ) {
d e s t i n o = d i r e c c i o n D e s d e+k ;
notasGuardadas [ k ] = 1 0 0 ; // Para que l a próxima vez que h a l l a que
apagar l a nota ’ nota ’ no l e e n vı́ e a l mismo e s c l a v o l a s e ñ a l .
e r r o r 2=e r r o r 2+I 2 C p o l l ( 2 * d e s t i n o ) ;
I 2 C w r i t e ( 2 * d e s t i n o , 0 , nota ) ;
I 2 C w r i t e ( 2 * d e s t i n o , 1 , 0 ) ; // Envı́o por I2C a q u i e n c o r r e s p o n d e l a
velocidad .
e r r o r 2=e r r o r 2+I 2 C p o l l ( 2 * d e s t i n o ) ;
encontreNota = 1 ;
}
k++;
i f ( k==numeroPics ) {
encontreNota = 1 ;
}
}
}
//−−−−−−−−−−−−−−−−−−−−−−−−−−−−Método p r i n c i p a l −−−−−−−−−−−−−−−−−−−−−−−−−−−−
i n t main ( v o i d ) {
//−−−−−−− C o n f i g u r a c i ó n de e l e m e n t o s y d e c l a r a c i ó n de v a r i a b l e s −−−−−−−−
182
unsigned i n t r r ;
i n t aux ;
char e r r o r 3 = 0 ;
unsigned short i n t i , j ;
adc i = 0;
AD1PCFGL = 0xE1FC ; // =1110000111111100
InitClock () ;
mapearPuertos ( ) ;
InitUART1 ( ) ;
InitUART2 ( ) ;
posDato =0;
v e l o c i d a d =0;
nota =0;
d e s t i n o=d i r e c c i o n D e s d e ;
initADC ( ) ;
iniciarI2C () ;
i = 1;
adc valor = 0;
adc dato = 0;
a d c v a l o r 2 = &a d c v a l o r ;
adc buffer [ 0 ] = 0;
adc buffer [ 1 ] = 0;
p o s B u f f e r =0;
//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−Bucle p r i n c i p a l −−−−−−−−−−−−−−−−−−−−−−−−−−−−
while (1) {
f o r ( r r =1; r r <10000; r r ++){ // Espera para que l o s c o n v e r s o r e s a n a l ó g i c o
d i g i t a l esten l i s t o s .
aux=r r ;
}
i f ( a d c i ==0){ // Chequeo c u a l e n t r a d a a n a l ó g i c o t o c a a n a l i z a r y l a
analizo .
AD1CHS0bits . CH0SA = 1 2 ;
}
i f ( a d c i ==1){
AD1CHS0bits . CH0SA = 1 1 ;
}
i f ( a d c i ==2){
AD1CHS0bits . CH0SA = 1 0 ;
}
i f ( a d c i ==3){
AD1CHS0bits . CH0SA = 9 ;
}
i f ( a d c i ==4){
AD1CHS0bits . CH0SA = 1 ;
}
i f ( a d c i ==5){
AD1CHS0bits . CH0SA = 0 ;
}
AD1CON1bits .SAMP = 1 ;
w h i l e ( ! AD1CON1bits .DONE) ; // A n á l i s i s de l a e n t r a d a a n a l ó g i c a
correspondiente .
// Espero h a s t a que s e h a l l a l eı́ d o l a e n t r a d a c o r r e s p o n d i e n t e .
183
i f ( ( ( aux−a d c b u f f e r [ a d c i ] ) >20) | | ( ( a d c b u f f e r [ a d c i ]−aux ) >20) ) {
// Chequeo s i e l v a l o r de l a e n t r a d a a n a l ó g i c a e s s i g n i f i c a t i v a m e n t e
d i s t i n t o a l ú l t i m o que e n v i e .
a d c b u f f e r [ a d c i ]= aux ; // S i e s s i g n i f i c a t i v a m e n t e d i s t i n t o , l o voy
a e n v i a r . Primero a c t u a l i z o e l v a l o r que t e n g o guardado .
e r r o r 3 =0; // Esta v a r i a b l e va a d e t e c t a r s i s e g e n e r a un e r r o r en l a
t r a n s m i c i ó n d e l I2C .
j=aux / 1 4 6 ;
i f ( j >1){
j=j −1;
}
}
e l s e { // S i e s una p e r i l l a a s o c i a d a a un e f e c t o , t e n g o que mandar un
número d e l 1 a l 1 2 8 .
j=aux / 8 ;
}
U1TXREG = j ; // Envı́o por p u e r t o s e r i e e l cambio para c o n t r o l .
U1TXREG = a d c i ;
f o r ( i=d i r e c c i o n D e s d e ; i <( d i r e c c i o n D e s d e+numeroPics ) ; i ++){ // Le a v i s o
a t o d o s l o s e s c l a v o s d e l cambio .
e r r o r 3=e r r o r 3+I 2 C p o l l ( 2 * i ) ;
I2Cwrite (2* i , t a b l a P e r i l l a s [ a d c i ] , j ) ;
e r r o r 3=e r r o r 3+I 2 C p o l l ( 2 * i ) ;
i f ( e r r o r 3 >0){
U1TXREG=0x34 ; // S i hubo un e r r o r en l a t r a n s m i c i ó n por I2C a v i s o
por p u e r t o s e r i e .
e r r o r 3 =0;
}
r e t a r d o ( 1 0 0 0 0 ) ; //Hago una e s p e r a e n t r e t r a s n m i c i ó n y t r a n s m i c i ó n
para que s e l i b e r e e l p u e r t o .
}
}
a d c i ++; // A c t u a l i z o l a e n t r a d a a n a l ó g i c a a c h e q u e a r .
i f ( a d c i > n u m P e r i l l a s −1){
adc i = 0;
}
i f ( bandera >0){ // S i s e completó l a r e c e p c i ó n de una i n s t r u c c i ó n MIDI ,
la analizo .
i f ( flagNoteOn==1){
agregarNota ( ) ;
flagNoteOn =0;
}
i f ( f l a g N o t e O f f ==1){
quitarNota () ;
184
f l a g N o t e O f f =0;
}
bandera =0;
posDato =0;
}
i f ( ( programacionActiva >0) ) { // S i hay que r e p r o g r a m a r e l i n s t r u m e n t o ,
comienzo a r e p r o g r a m a r l o .
U2TXREG = 0 x18 ;
cargarNuevoPrograma ( ) ;
}
}
}
master/main.c
185
Anexo D, códigos del software para diseñar instrumentos
Introducción
A continuación se presentan los códigos de las clases del programa creado para diseñar
instrumentos. Decidimos no agregar las clases visuales (clases encargadas del diseño de
las ventanas) debido a que estas son clases generadas automáticamente por Visual Studio
que contienen sólo información sobre el aspecto de la ventana (tamaño y posición de cada
uno de los objetos).
Este fue un programa generado en las últimas etapas del proyecto, comenzó siendo un
pequeño programa que sólo realizaba algunas acciones básicas y terminó siendo lo que es
actualmente. La forma de desarrollo del programa se debió a que al no ser un objetivo
prioritario del proyecto no se lo pudo planificar estructuralmente desde el principio. El
sistema para la creación de este código fue el de lograr un programa que haga ciertas fun-
ciones probarlo y si se llega correctamente al objetivo y hay tiempo, ampliar el programa
para agregarle funciones y ası́ sucesivamente para transformar el programa básico con el
que se comenzó en el actual.
Esta forma de creación del programa fue la que llevó a que si bien el programa funcione, la
estructura no esté perfectamente diseñada. Se llegó a un punto en el cual debimos optar
por reestructurar el programa para que quede con una programación prolija, siguiendo
estándares y bien comentada o seguirle agregando aplicaciones al programa. Decidimos
seguirle agregando aplicaciones al programa por que dado que no es una prioridad del
proyecto consideramos que no es necesario que este esté extremadamente prolijo y con
métodos extremadamente eficientes y fáciles de entender. Mientras que agregarle al pro-
grama más facilidades permite un mejor uso de todas las caracterı́sticas del sintetizador.
186
Clase ’crearInstrumento.vb’
Imports System . IO
Imports System . Runtime . S e r i a l i z a t i o n . F o r m a t t e r s . Binary
Public Class crearInstrumento
#Region ” v a r i a b l e s ”
Dim t e c l a A c t u a l As I n t e g e r = 0
Dim esDeArriba As Boolean = F a l s e
Dim l i s t a X A r r i b a As L i s t ( Of I n t e g e r )
Dim esLaPrimerVez As Boolean = True
Dim t e c l a A c t u a l A r r i b a As I n t e g e r = −1
Dim numeroNota As I n t e g e r = 1
Dim l i s t a N o t a s A b a j o ( ) As I n t e g e r = { 1 , 3 , 5 , 6 , 8 , 1 0 , 12}
Dim l i s t a N o t a s A r r i b a ( ) As I n t e g e r = { 2 , 4 , 7 , 9 , 11}
Dim p r i m e r E n v o l v e n t e As I n t e g e r = 1
Dim l i s t a H i j o s As L i s t ( Of c r e a r M a s c a r a )
Dim ultimaRuta As S t r i n g = ” ”
Dim yaGuardo As Boolean = F a l s e
P r i v a t e m i I n s t r u m e n t o As i n s t r u m e n t o
#End Region
#Region ” p r o p i e d a d e s ”
P u b l i c P r o p e r t y miInstrumento ( ) As i n s t r u m e n t o
Get
Return Me . m i I n s t r u m e n t o
End Get
S e t ( ByVal v a l u e As i n s t r u m e n t o )
Me . m i I n s t r u m e n t o = v a l u e
End S e t
End P r o p e r t y
#End Region
#Region ” c o n s t r u c t o r e s ”
P u b l i c Sub New ( )
InitializeComponent ()
miInstrumento = New i n s t r u m e n t o ( )
Dim i As I n t e g e r
Dim vent As c r e a r M a s c a r a
Dim j As I n t e g e r
For j = 0 To 85
For i = 0 To 14
vent = New c r e a r M a s c a r a (Me, j , i , True , True )
Next
Next
rearmarPaneles ()
TabPage1 . Text = ” E d i t a r cada e n v o l v e n t e ”
TabPage2 . Text = ” E d i c i ó n común a t o d a s l a s e n v o l v e n t e s ”
TabPage3 . Text = ” Probar l a s n o t a s g e n e r a d a s ”
L ab el 2 . Text = ”La o c t a v a s e l e c c i o n a d a e s l a número 1 ”
L ab el 5 . Text = ”La nota s e l e c c i o n a d a e s l a número 1 ”
TNombre . Text = miInstrumento . n o t a s ( 0 ) . nombre
LImportando . V i s i b l e = F a l s e
187
ProgressBar1 . V i s i b l e = False
Try
Dim r u t a I c o n o As S t r i n g =
Path . GetDirectoryName ( A p p l i c a t i o n . ExecutablePath ) +
”\ nota musical . i c o ”
Dim i c o n o As New I c o n ( r u t a I c o n o )
Me . I c o n = i c o n o
Catch ex As E x c e p t i o n
End Try
Timer1 . S t a r t ( )
End Sub
#End Region
#Region ” métodos ”
P u b l i c Sub r e a r m a r P a n e l e s ( )
I ma g e nF r ec u en c ia 1 . Image = miInstrumento . n o t a s ( numeroNota −
1) . l i s t a M a s c a r a s ( primerEnvolvente − 1) . f o t o
I ma g e nF r ec u en c ia 2 . Image = miInstrumento . n o t a s ( numeroNota −
1) . l i s t a M a s c a r a s ( primerEnvolvente ) . f o t o
I ma g e nF r ec u en c ia 3 . Image = miInstrumento . n o t a s ( numeroNota −
1) . l i s t a M a s c a r a s ( primerEnvolvente + 1) . f o t o
P a n e l F r e c u e n c i a 1 . BackColor = C o l o r . FromArgb ( 5 0 + ( p r i m e r E n v o l v e n t e
− 1) * 6 , 50 , 50)
P a n e l F r e c u e n c i a 2 . BackColor = C o l o r . FromArgb ( 5 0 +
( primerEnvolvente ) * 6 , 50 , 50)
P a n e l F r e c u e n c i a 3 . BackColor = C o l o r . FromArgb ( 5 0 + ( p r i m e r E n v o l v e n t e
+ 1) * 6 , 50 , 50)
Panel1 . BackColor = C o l o r . FromArgb ( 8 0 , 103 − numeroNota / 2 , 60 +
numeroNota / 2 )
TFrecuencia1 . Text = miInstrumento . n o t a s ( numeroNota −
188
1 ) . l i s t a F r e c u e n c i a s ( p r i m e r E n v o l v e n t e − 1 ) . T o S t r i n g ( ”F2” )
TFrecuencia2 . Text = miInstrumento . n o t a s ( numeroNota −
1 ) . l i s t a F r e c u e n c i a s ( p r i m e r E n v o l v e n t e ) . T o S t r i n g ( ”F2” )
TFrecuencia3 . Text = miInstrumento . n o t a s ( numeroNota −
1 ) . l i s t a F r e c u e n c i a s ( p r i m e r E n v o l v e n t e + 1 ) . T o S t r i n g ( ”F2” )
T m u l t i p l i c a d o r F r e c u e n c i a 1 . Text = miInstrumento . n o t a s ( numeroNota −
1) . l i s t a C o e f i c i e n t e s ( primerEnvolvente − 1)
T m u l t i p l i c a d o r F r e c u e n c i a 2 . Text = miInstrumento . n o t a s ( numeroNota −
1) . l i s t a C o e f i c i e n t e s ( primerEnvolvente )
T m u l t i p l i c a d o r F r e c u e n c i a 3 . Text = miInstrumento . n o t a s ( numeroNota −
1) . l i s t a C o e f i c i e n t e s ( primerEnvolvente + 1)
TNombre . Text = miInstrumento . n o t a s ( numeroNota − 1 ) . nombre
End Sub
P r i v a t e Sub guardarAntesRuta ( )
Dim vent As New S a v e F i l e D i a l o g
vent . D e f a u l t E x t ( ) = ” . i n s t ”
vent . AddExtension = True
vent . F i l t e r = ” i n s t r u m e n t o s ( * . i n s t ) | * . i n s t ”
vent . ShowDialog ( )
Dim s e g u i r As Boolean = True
I f vent . C h e c k F i l e E x i s t s Then
Dim c a r t e l As MsgBoxResult = MsgBox ( ” Desea s o b r e e s c r i b i r e l
a r c h i v o s e l e c c i o n a d o ” , MsgBoxStyle . YesNo )
I f c a r t e l = MsgBoxResult . No Then
seguir = False
End I f
End I f
I f s e g u i r Then
guardarComo ( vent . FileName )
End I f
End Sub
189
Dim vent As New O p e n F i l e D i a l o g
vent . ShowDialog ( )
Try
Dim f s As New F i l e S t r e a m ( vent . FileName , FileMode . Open )
Dim b f As New BinaryFormatter
Dim mas As mascara = CType ( b f . D e s e r i a l i z e ( f s ) , mascara )
f s . Close ()
miInstrumento . n o t a s ( numeroNota −
1 ) . l i s t a M a s c a r a s ( p r i m e r E n v o l v e n t e + d e s f a s a j e ) = mas
rearmarPaneles ()
Catch ex As E x c e p t i o n
MsgBox ( ”No s e pudo a b r i r e l a r c h i v o e s p e c i f i c a d o ” )
End Try
End Sub
#End Region
#Region ” r e a c c i o n e s ”
190
End I f
rearmarPaneles ()
End Sub
191
P r i v a t e Sub HScrollBar1 ValueChanged ( ByVal s e n d e r As Object , ByVal e
As System . EventArgs ) Handles H S c r o l l B a r 1 . ValueChanged
p r i m e r E n v o l v e n t e = 1 + Fix ( H S c r o l l B a r 1 . Value / 7 )
I f p r i m e r E n v o l v e n t e > 13 Then
p r i m e r E n v o l v e n t e = 13
End I f
E F r e c u e n c i a 1 . Text = ” f r e c u e n c i a Nº ” + p r i m e r E n v o l v e n t e . T o S t r i n g +
” =”
E F r e c u e n c i a 2 . Text = ” f r e c u e n c i a Nº ” + ( p r i m e r E n v o l v e n t e +
1 ) . T o S t r i n g + ” =”
E F r e c u e n c i a 3 . Text = ” f r e c u e n c i a Nº ” + ( p r i m e r E n v o l v e n t e +
2 ) . T o S t r i n g + ” =”
rearmarPaneles ()
End Sub
192
P r i v a t e Sub B F r e c u e n c i a 1 C l i c k ( ByVal s e n d e r As System . Object , ByVal e
As System . EventArgs ) Handles BFrecuencia1 . C l i c k
Try
Dim f r e c As Double = CDbl ( TFrecuencia1 . Text )
miInstrumento . n o t a s ( numeroNota −
1) . l i s t a F r e c u e n c i a s ( primerEnvolvente − 1) = f r e c
Catch ex As E x c e p t i o n
MsgBox ( ”No i n s e r t ó una f r e c u e n c i a v á l i d a ” ,
MsgBoxStyle . C r i t i c a l )
End Try
End Sub
193
mas = New mascara ( masDesde , masHasta , mulDesde * pon2 ,
mulHasta * pon1 , ” s i n ” )
pon1 = pon1 + 1
pon2 = pon2 − 1
miInstrumento . n o t a s ( numeroNota − 1 ) . l i s t a M a s c a r a s ( i ) =
mas
vent = New c r e a r M a s c a r a (Me, numeroNota − 1 , i , True ,
True )
Next
Else
For i = NDesdeInter . Value To NHastaInter . Value − 2
mas = New mascara ( masDesde , masHasta , mulDesde * pon2 ,
mulHasta * pon1 , ” s i n ” , F a l s e )
pon1 = pon1 + 1
pon2 = pon2 − 1
miInstrumento . n o t a s ( numeroNota − 1 ) . l i s t a M a s c a r a s ( i ) =
mas
vent = New c r e a r M a s c a r a (Me, numeroNota − 1 , i , True ,
True )
Next
End I f
rearmarPaneles ()
End I f
End Sub
194
End I f
I f s e g u i r Then
Dim l i s t a V a l o r e s As L i s t ( Of I n t e g e r ) =
Me . miInstrumento . n o t a s ( NNotaAGenerar . Value −
1) . generarSonido (60000 , 30000)
I f Not metodosComunes . grabarWav ( l i s t a V a l o r e s , vent . FileName )
Then
MsgBox ( ” E r r o r a l g r a b a r e l a r c h i v o de s o n i d o , v e r i f i q u e
que . . . ” , MsgBoxStyle . C r i t i c a l )
End I f
End I f
rearmarPaneles ()
End Sub
P r i v a t e Sub E x p o r t a r I n s t r u m e n t o T o o l S t r i p M e n u I t e m C l i c k ( ByVal s e n d e r As
System . Object , ByVal e As System . EventArgs ) Handles
ExportarInstrumentoToolStripMenuItem . C l i c k
Dim vent As New S a v e F i l e D i a l o g
vent . AddExtension = True
vent . F i l t e r = ” a s s e m b l e r ( * . hex ) | * . hex ”
vent . ShowDialog ( )
I f vent . FileNames . Length > 0 Then
Try
Dim t e x t o As S t r i n g = Me . miInstrumento . e x p o r t a r ( 1 5 0 0 0 ,
30000 , 1)
195
My. Computer . F i l e S y s t e m . WriteAllText ( vent . FileName , t e x t o ,
False )
Catch ex As E x c e p t i o n
MsgBox ( ”no s e pudo e x p o r t a r . . . . . ” )
End Try
End I f
End Sub
End I f
rearmarPaneles ()
End Sub
P r i v a t e Sub B A p l i c a r M u l t i p l i c a d o r 1 C l i c k ( ByVal s e n d e r As
System . Object , ByVal e As System . EventArgs ) Handles
BAplicarMultiplicador1 . Click
Try
Dim num As Double = CDbl ( T m u l t i p l i c a d o r F r e c u e n c i a 1 . Text )
miInstrumento . n o t a s ( numeroNota −
1 ) . l i s t a C o e f i c i e n t e s ( p r i m e r E n v o l v e n t e − 1 ) = num
Catch ex As E x c e p t i o n
MsgBox ( ”Debe i n s e r t a r un número” , MsgBoxStyle . C r i t i c a l )
End Try
196
End Sub
P r i v a t e Sub B A p l i c a r M u l t i p l i c a d o r 2 C l i c k ( ByVal s e n d e r As
System . Object , ByVal e As System . EventArgs ) Handles
BAplicarMultiplicador2 . Click
Try
Dim num As Double = CDbl ( T m u l t i p l i c a d o r F r e c u e n c i a 2 . Text )
miInstrumento . n o t a s ( numeroNota −
1 ) . l i s t a C o e f i c i e n t e s ( p r i m e r E n v o l v e n t e ) = num
Catch ex As E x c e p t i o n
MsgBox ( ”Debe i n s e r t a r un número” , MsgBoxStyle . C r i t i c a l )
End Try
End Sub
P r i v a t e Sub B A p l i c a r M u l t i p l i c a d o r 3 C l i c k ( ByVal s e n d e r As
System . Object , ByVal e As System . EventArgs ) Handles
BAplicarMultiplicador3 . Click
Try
Dim num As Double = CDbl ( T m u l t i p l i c a d o r F r e c u e n c i a 3 . Text )
miInstrumento . n o t a s ( numeroNota −
1 ) . l i s t a C o e f i c i e n t e s ( p r i m e r E n v o l v e n t e + 1 ) = num
Catch ex As E x c e p t i o n
MsgBox ( ”Debe i n s e r t a r un número” , MsgBoxStyle . C r i t i c a l )
End Try
End Sub
197
pon1 = pon1 + 1
pon2 = pon2 − 1
Next
MsgBox ( ” Se e x t r a p o l o b i e n ” , MsgBoxStyle . I n f o r m a t i o n )
End I f
End Sub
P r i v a t e Sub A b r i r T o o l S t r i p M e n u I t e m C l i c k ( ByVal s e n d e r As
System . Object , ByVal e As System . EventArgs ) Handles
AbrirToolStripMenuItem . C l i c k
198
Dim r e s p u e s t a As MsgBoxResult = MsgBox ( ” Desea d e j a r e l i n s t r u m e n t o
a c t u a l y a b r i r o t r o ? ” , MsgBoxStyle . YesNo )
I f r e s p u e s t a = MsgBoxResult . Yes Then
Dim vent As New O p e n F i l e D i a l o g
vent . AddExtension = True
vent . F i l t e r = ” i n s t r u m e n t o s ( * . i n s t ) | * . i n s t ”
vent . ShowDialog ( )
Try
Dim i n s As i n s t r u m e n t o
Dim r u t a As S t r i n g = vent . FileName
Dim f s As New F i l e S t r e a m ( ruta , FileMode . Open )
Dim b f As New BinaryFormatter
i n s = CType ( b f . D e s e r i a l i z e ( f s ) , i n s t r u m e n t o )
f s . Close ()
miInstrumento = i n s
yaGuardo = True
ultimaRuta = r u t a
Catch ex As E x c e p t i o n
MsgBox ( ” I m p o s i b l e a b r i r e l a r c h i v o s e l e c c i o n a d o ” ,
MsgBoxStyle . C r i t i c a l )
End Try
End I f
End Sub
P r i v a t e Sub S a l i r T o o l S t r i p M e n u I t e m C l i c k ( ByVal s e n d e r As
System . Object , ByVal e As System . EventArgs ) Handles
SalirToolStripMenuItem . Click
Me . D i s p o s e ( )
End Sub
199
Dim j As I n t e g e r
For i = 0 To 14
masAux = notaACopiar . l i s t a M a s c a r a s ( i )
For j = NCopiarDesdeEnv . Value To NCopiarHastaEnv . Value
miInstrumento . n o t a s ( j − 1 ) . l i s t a M a s c a r a s ( i ) = New
mascara ( masAux )
vent = New c r e a r M a s c a r a (Me, j − 1 , i , True , True )
Next
Next
End I f
MsgBox ( ” El p r o c e s o de c o p i a d o f i n a l i z ó s a t i s f a c t o r i a m e n t e ” ,
MsgBoxStyle . I n f o r m a t i o n )
End Sub
200
multiplicador =
Math . Pow ( ( notaHasta . l i s t a F r e c u e n c i a s ( j ) /
notaDesde . l i s t a F r e c u e n c i a s ( j ) ) , ( 1 + i −
NExtraFrecDesde . Value ) / d i f e r e n c i a )
notaAux . l i s t a F r e c u e n c i a s ( j ) =
notaDesde . l i s t a F r e c u e n c i a s ( j ) * m u l t i p l i c a d o r
Next
Next
End I f
MsgBox ( ” F i n a l i z ó s a t i s f a c t o r i a m e n t e l a e x t r a p o l a c i ó n ” ,
MsgBoxStyle . I n f o r m a t i o n )
End Sub
#End Region
End C l a s s
programaVisual/apli/WindowsApplication1/crearInstrumento.vb
201
Clase ’crearMascara.vb’
Imports System . IO
Imports System . Runtime . S e r i a l i z a t i o n . F o r m a t t e r s . Binary
Public Class crearMascara
#Region ” v a r i a b l e s ”
P r i v a t e l i s t a P u n t o s X ( 6 ) As I n t e g e r
P r i v a t e l i s t a P u n t o s Y ( 6 ) As Double
P r i v a t e XMax As I n t e g e r
P r i v a t e YMax As Double
P r i v a t e x E l e g i d o As I n t e g e r = 0
P r i v a t e v e c e s As I n t e g e r = 0
P r i v a t e u l t i m o B i e n As Boolean = True
P r i v a t e hayQueAumetarPunto As Boolean = F a l s e
P r i v a t e r u t a A c t u a l As S t r i n g
P r i v a t e y a E l i g i o R u t a As Boolean
P r i v a t e nombre As S t r i n g
P r i v a t e miVentanaPadre As c r e a r I n s t r u m e n t o
P r i v a t e numeroNota As I n t e g e r
P r i v a t e numeroMascara As I n t e g e r
Dim hayQueGuardarImagen As Boolean = F a l s e
Dim a n c h o P a n e l O r i g i n a l As I n t e g e r
Dim l a r g o P a n e l O r i g i n a l As I n t e g e r
Dim hayQueDevolverMascara As Boolean = F a l s e
Dim tengoPadre As Boolean = F a l s e
Dim l i s t a P a r a B o r r a r As L i s t ( Of Double )
Dim yaGuardo As Boolean = F a l s e
Dim ultimaRuta As S t r i n g = ” ”
#End Region
#Region ” métodos ”
P r i v a t e Sub p i n t a r P a n e l ( )
u l t i m o B i e n = True
Panel1 . I n v a l i d a t e ( )
202
End Sub
P r i v a t e Sub guardarAntesRuta ( )
Dim vent As New S a v e F i l e D i a l o g
vent . AddExtension = True
vent . D e f a u l t E x t ( ) = ” . masc”
vent . F i l t e r = ” máscaras ( * . masc ) | * . masc”
vent . ShowDialog ( )
Dim s e g u i r As Boolean = True
I f vent . C h e c k F i l e E x i s t s Then
Dim c a r t e l As MsgBoxResult = MsgBox ( ” Desea s o b r e e s c r i b i r e l
a r c h i v o s e l e c c i o n a d o ” , MsgBoxStyle . YesNo )
I f c a r t e l = MsgBoxResult . No Then
seguir = False
End I f
End I f
I f s e g u i r Then
guardarComo ( vent . FileName )
End I f
End Sub
203
listaPuntosY ( i + 1) = listaPuntosY ( i )
Else
l i s t a P u n t o s X ( i + 1 ) = 10 * mas . cambios ( i )
l i s t a P u n t o s Y ( i + 1 ) = l i s t a P u n t o s Y ( i ) + mas . i n c r e m e n t o s ( i )
* ( listaPuntosX ( i + 1) − listaPuntosX ( i ) )
End I f
Next
For Each num As Double In l i s t a P u n t o s Y
I f num > ymax Then
ymax = num
End I f
Next
Me . XMax = 10 * mas . cambios ( mas . cambios . Count − 2 )
Me . YMax = ymax
pintarPanel ()
End Sub
P r i v a t e Function c r e a r M a s c a r a ( ) As mascara
hayQueGuardarImagen = F a l s e
Panel1 . Width = 264
Panel1 . Height = 154
L ab e l1 1 . V i s i b l e = F a l s e
L ab e l1 2 . V i s i b l e = F a l s e
pintarPanel ()
Dim mapaBits As Bitmap = New Bitmap ( Panel1 . Width , Panel1 . Height )
Panel1 . DrawToBitmap ( mapaBits , New R e c t a n g l e ( 0 , 0 , Panel1 . Width ,
Panel1 . Height ) )
Panel1 . Height = l a r g o P a n e l O r i g i n a l
Panel1 . Width = a n c h o P a n e l O r i g i n a l
pintarPanel ()
L ab e l1 1 . V i s i b l e = True
L ab e l1 2 . V i s i b l e = True
Dim mas As New mascara (Me . l i s t a P u n t o s X , Me . l i s t a P u n t o s Y ,
Me . TNombre . Text , mapaBits )
Return mas
End Function
#End Region
#Region ” r e a c c i o n e s ”
204
I f r e s p u e s t a = MsgBoxResult . Yes Then
Try
Dim vent As New O p e n F i l e D i a l o g ( )
vent . AddExtension = True
vent . F i l t e r = ” i n s t r u m e n t o s ( * . masc ) | * . masc”
vent . ShowDialog ( )
I f vent . FileNames . Length > 0 Then
a b r i r M a s c a r a ( vent . FileName )
End I f
Catch ex As E x c e p t i o n
MsgBox ( ” e r r o r a l a b r i r l a máscara ” , MsgBoxStyle . C r i t i c a l )
End Try
End I f
End Sub
205
l i s t a Y A p i n t a r . Add( l i s t a P u n t o s Y ( i ) )
End I f
Next
punta = New Pen ( C o l o r . Black , 1 )
p1 = New P o in t ( Panel1 . Width / 2 0 , 0 )
p2 = New P o in t ( Panel1 . Width / 2 0 , Panel1 . Height )
g r a f i c o . DrawLine ( punta , p1 , p2 )
p1 = New P o in t ( 0 , 19 * Panel1 . Height / 2 0 )
p2 = New P o in t ( Panel1 . Width , 19 * Panel1 . Height / 2 0 )
g r a f i c o . DrawLine ( punta , p1 , p2 )
For i = 1 To 4
p1 = New P oi nt ( Panel1 . Width / 20 + 19 * i * Panel1 . Width /
1 0 0 , Panel1 . Height − Panel1 . Height / 20 + 3 )
p2 = New P oi nt ( Panel1 . Width / 20 + 19 * i * Panel1 . Width /
1 0 0 , Panel1 . Height − Panel1 . Height / 20 − 3 )
g r a f i c o . DrawLine ( punta , p1 , p2 )
p1 = New P oi nt ( Panel1 . Width / 20 + 3 , Panel1 . Height −
Panel1 . Height / 20 − 19 * i * Panel1 . Height / 1 0 0 )
p2 = New P oi nt ( Panel1 . Width / 20 − 3 , Panel1 . Height −
Panel1 . Height / 20 − 19 * i * Panel1 . Height / 1 0 0 )
g r a f i c o . DrawLine ( punta , p1 , p2 )
l i s t a E t i q u e t a s X ( i − 1 ) . Text = ( CDbl (XMax * i /
5 ) ) . T o S t r i n g ( ”F0” )
l i s t a E t i q u e t a s Y ( i − 1 ) . Text = ( CDbl (YMax * i /
5 ) ) . T o S t r i n g ( ”F2” )
Next
End Try
Next
veces = veces + 1
I f u l t i m o B i e n And hayQueAumetarPunto Then
hayQueAumetarPunto = F a l s e
xElegido = xElegido + 1
I f x E l e g i d o > 6 Then
xElegido = 6
End I f
I f x E l e g i d o = 1 Then
RadioButton1 . S e l e c t ( )
206
End I f
I f x E l e g i d o = 2 Then
RadioButton2 . S e l e c t ( )
End I f
I f x E l e g i d o = 3 Then
RadioButton3 . S e l e c t ( )
End I f
I f x E l e g i d o = 4 Then
RadioButton4 . S e l e c t ( )
End I f
I f x E l e g i d o = 5 Then
RadioButton5 . S e l e c t ( )
End I f
I f x E l e g i d o = 6 Then
RadioButton6 . S e l e c t ( )
End I f
End I f
End Sub
207
End I f
I f ( p o s i c i o n X > Panel1 . Width / 2 0 ) And ( p o s i c i o n X >
l i s t a P u n t o s X ( x E l e g i d o − 1 ) ) And ( ( l i s t a P u n t o s X ( x E l e g i d o − 1 ) )
>= 0 ) And o t r a C o n d i c i o n Then
L ab el 1 . Text = ( p o s i c i o n X ) . T o S t r i n g + ” , ” + ( M o u s e P o s i t i o n .Y −
Me . L o c a t i o n .Y − Panel1 . L o c a t i o n .Y − 2 3 ) . T o S t r i n g
listaPuntosX ( xElegido ) = posicionX
l i s t a P u n t o s Y ( x E l e g i d o ) = ( ( 0 . 9 5 − ( ( M o u s e P o s i t i o n .Y − 23 −
Panel1 . L o c a t i o n .Y − Panel1 . Height / 20 − Me . L o c a t i o n .Y) ) /
( 1 9 * Panel1 . Height / 2 0 ) ) * YMax) . T o S t r i n g ( ”F2” )
hayQueAumetarPunto = True
pintarPanel ()
Else
MsgBox ( ” El punto e l e g i d o no f u e c o r r e c t o , v e r i f i q u e que su
coordenada en x s e a mayor a l a d e l punto a n t e r i o r y menor a
la del siguiente ”)
Panel1 . I n v a l i d a t e ( )
ultimoBien = False
End I f
End Sub
Catch ex As E x c e p t i o n
End Try
End Sub
208
XMax = xM
pintarPanel ()
End I f
Catch ex As E x c e p t i o n
End Try
End Sub
End Try
End Sub
P r i v a t e Sub
C r e a r I n t e r p o l a n d o O t r a s D o s M á s c a r a s T o o l S t r i p M e n u I t e m C l i c k ( ByVal
s e n d e r As System . Object , ByVal e As System . EventArgs ) Handles
C r e a r I n t e r p o l a n d o O t r a s D o s M á s c a r a s T o o l S t r i p M e n u I t e m . C l i c k
Dim pideN As New p i d e E n t e r o s (Me)
pideN . ShowDialog ( )
I f pideN . r e s p u e s t a = True Then
Dim mas1 As mascara
Dim mas2 As mascara
Dim m a s c a r a F i n a l As mascara
Try
mas1 = a b r i r M a s c a r a ( pideN . r u t a 1 )
mas2 = a b r i r M a s c a r a ( pideN . r u t a 2 )
MsgBox ( mas1 . v a l o r I n i c i a l )
MsgBox ( mas2 . v a l o r I n i c i a l )
m a s c a r a F i n a l = New mascara ( mas1 , mas2 , pideN . numeroUno ,
pideN . numeroDos , ” s i n ” )
armarTablas ( m a s c a r a F i n a l )
Catch ex As E x c e p t i o n
MsgBox ( ”ERROR AL INTERPOLAR” )
209
End Try
End I f
End Sub
P r i v a t e Sub
CrearExtrapolandoOtrasDosMáscarasV2ToolStripMenuItem Click ( ByVal
s e n d e r As System . Object , ByVal e As System . EventArgs ) Handles
CrearExtrapolandoOtrasDosMáscarasV2ToolStripMenuItem . C l i c k
Dim pideN As New p i d e E n t e r o s (Me)
pideN . ShowDialog ( )
I f pideN . r e s p u e s t a = True Then
Dim mas1 As mascara
Dim mas2 As mascara
Dim m a s c a r a F i n a l As mascara
Try
mas1 = a b r i r M a s c a r a ( pideN . r u t a 1 )
mas2 = a b r i r M a s c a r a ( pideN . r u t a 2 )
m a s c a r a F i n a l = New mascara ( mas1 , mas2 , pideN . numeroUno ,
pideN . numeroDos , ” s i n ” , F a l s e )
armarTablas ( m a s c a r a F i n a l )
Catch ex As E x c e p t i o n
MsgBox ( ”ERROR AL INTERPOLAR” )
End Try
End I f
End Sub
End Try
End I f
End Sub
P r i v a t e Sub
M u l t i p l i c a r E l E j e X P o r U n a C o n s t a n t e T o o l S t r i p M e n u I t e m C l i c k ( ByVal
s e n d e r As System . Object , ByVal e As System . EventArgs ) Handles
MultiplicarElEjeXPorUnaConstanteToolStripMenuItem . C l i c k
Dim vent As New pideUnDouble (Me, True )
vent . ShowDialog ( )
End Sub
P r i v a t e Sub
M u l t i p l i c a r E l E j e Y P o r U n a C o n s t a n t e T o o l S t r i p M e n u I t e m C l i c k ( ByVal
s e n d e r As System . Object , ByVal e As System . EventArgs ) Handles
210
MultiplicarElEjeYPorUnaConstanteToolStripMenuItem . C l i c k
Dim vent As New pideUnDouble (Me, F a l s e )
vent . ShowDialog ( )
End Sub
#End Region
#Region ” c o n s t r u c t o r e s ”
P u b l i c Sub New ( )
InitializeComponent ()
Dim i As I n t e g e r
For i = 0 To 6
l i s t a P u n t o s X ( i ) = −1
l i s t a P u n t o s Y ( i ) = −1
Next
Me . XMax = 60000
Me . YMax = 1
Me . r u t a A c t u a l = ” ”
Me . y a E l i g i o R u t a = F a l s e
Me . nombre = ” ”
a n c h o P a n e l O r i g i n a l = Panel1 . Width
l a r g o P a n e l O r i g i n a l = Panel1 . Height
BA pl ica r . V i s i b l e = F a l s e
Try
Dim r u t a I c o n o As S t r i n g =
Path . GetDirectoryName ( A p p l i c a t i o n . ExecutablePath ) +
”\ nota musical . i c o ”
Dim i c o n o As New I c o n ( r u t a I c o n o )
Me . I c o n = i c o n o
Catch ex As E x c e p t i o n
End Try
End Sub
211
Me . r u t a A c t u a l = r u t a
Me . y a E l i g i o R u t a = True
yaGuardo = True
ultimaRuta = r u t a
Me . nombre = nom
Me . TNombre . Text = nom
a n c h o P a n e l O r i g i n a l = Panel1 . Width
l a r g o P a n e l O r i g i n a l = Panel1 . Height
BA pl ica r . V i s i b l e = F a l s e
Try
Dim r u t a I c o n o As S t r i n g =
Path . GetDirectoryName ( A p p l i c a t i o n . ExecutablePath ) +
”\ nota musical . i c o ”
Dim i c o n o As New I c o n ( r u t a I c o n o )
Me . I c o n = i c o n o
Catch ex As E x c e p t i o n
End Try
End Sub
212
armarTablas (Me . miVentanaPadre . miInstrumento . n o t a s ( numeroNo ) . l i s t a M a s c a r a s ( numero
tengoPadre = t e n g o
aplicarCambios ( False )
Me . D i s p o s e ( )
End Sub
#End Region
End C l a s s
programaVisual/apli/WindowsApplication1/crearMascara.vb
213
Clase ’inicio.vb’
Module i n i c i o
P u b l i c Sub main ( )
’Dim vent As v e n t a n a P r i n c i p a l = New v e n t a n a P r i n c i p a l
Dim vent As v e n t a n a I n i c i a l = New v e n t a n a I n i c i a l
vent . ShowDialog ( )
End Sub
End Module
programaVisual/apli/WindowsApplication1/inicio.vb
214
Clase ’instrumento.vb’
< S e r i a l i z a b l e ( )> P u b l i c C l a s s i n s t r u m e n t o
#Region ” a t r i b u t o s ”
Private f o t o As Image
Private n o t a s As L i s t ( Of nota )
Private nombre As S t r i n g
#End Region
#Region ” p r o p i e d a d e s ”
P u b l i c P r o p e r t y f o t o ( ) As Image
Get
Return Me . f o t o
End Get
S e t ( ByVal v a l u e As Image )
Me . f o t o = v a l u e
End S e t
End P r o p e r t y
P u b l i c P r o p e r t y n o t a s ( ) As L i s t ( Of nota )
Get
Return Me . n o t a s
End Get
S e t ( ByVal v a l u e As L i s t ( Of nota ) )
Me . n o t a s = v a l u e
End S e t
End P r o p e r t y
P u b l i c P r o p e r t y nombre ( ) As S t r i n g
Get
Return Me . nombre
End Get
S e t ( ByVal v a l u e As S t r i n g )
Me . nombre = v a l u e
End S e t
End P r o p e r t y
#End Region
#Region ” c o n s t r u c t o r e s ”
P u b l i c Sub New ( )
Me . f o t o = c r e a r I m a g e n V a c i a ( )
n o t a s = New L i s t ( Of nota )
Dim i As I n t e g e r
For i = 0 To 85
Dim f r e As Double = 27 * Math . Exp ( Math . Log ( 2 ) * i / 1 2 )
n o t a s . Add(New nota ( f r e ) )
Next
Me . nombre = ” S i n nombre a s i g n a d o ”
End Sub
P u b l i c Sub New( ByVal r u t a As S t r i n g , ByVal l a r g o T a b l a S e n o s As I n t e g e r ,
ByVal FsPic As I n t e g e r )
n o t a s = New L i s t ( Of nota )
Dim t e x t o T o t a l As S t r i n g = My. Computer . F i l e S y s t e m . ReadAllText ( r u t a )
Dim s e p a r a d o r As Char = ” , ”
215
Dim s e p a r a d o ( ) As S t r i n g = S p l i t ( t e x t o T o t a l , s e p a r a d o r )
Dim i As I n t e g e r
Dim j As I n t e g e r
Dim k As I n t e g e r
Me . f o t o = c r e a r I m a g e n V a c i a ( )
Me . nombre = ” S i n nombre a s i g n a d o ”
Dim l i s t a M a s c a r a s As New L i s t ( Of mascara )
Dim l i s t a C a m b i o s As L i s t ( Of I n t e g e r )
Dim l i s t a I n c r e m e n t o s As L i s t ( Of Double )
Dim f r e c As L i s t ( Of Double )
Dim l i s t a V a l o r e s I n i c i a l e s ( 1 4 ) As Double
Dim v a l o r I n i c i a l As Double
Dim l i s t a C o e f i c i e n t e s As New L i s t ( Of Double )
For i = 0 To 14
l i s t a C o e f i c i e n t e s . Add ( 1 )
Next
For i = 0 To 85
f r e c = New L i s t ( Of Double )
For j = 0 To 14
f r e c . Add ( ( FsPic * ( CDbl ( s e p a r a d o ( 1 5 * i + j ) ) ) /
( largoTablaSenos ) ) )
l i s t a V a l o r e s I n i c i a l e s ( j ) = CInt ( CDbl ( s e p a r a d o ( 1 5 * i + j +
1290) ) / 65536)
v a l o r I n i c i a l = CDbl ( ( s e p a r a d o ( 1 5 * i + j + 1 2 9 0 ) ) / 6 5 5 3 6 )
l i s t a C a m b i o s = New L i s t ( Of I n t e g e r )
l i s t a I n c r e m e n t o s = New L i s t ( Of Double )
For k = 0 To 6
l i s t a C a m b i o s . Add( CInt ( CDbl ( s e p a r a d o ( 1 0 5 * i + 7 * j +
k + 2580) ) ) )
l i s t a I n c r e m e n t o s . Add ( ( CDbl ( s e p a r a d o ( 1 0 5 * i + 7 * j +
k + 11610) ) / 65536) )
Next
l i s t a M a s c a r a s . Add(New mascara ( ” s i n nombre” ,
listaIncrementos , listaCambios , v a l o r I n i c i a l ) )
Next
Me . n o t a s . Add(New nota ( ” s i n nombre ” + i . ToString , f r e c ,
listaMascaras , l ist aC oef ici en tes , frec (2) ) )
Next
End Sub
#End Region
#Region ” métodos ”
P r i v a t e Function c r e a r I m a g e n V a c i a ( ) As Image
Dim pan As New Panel
pan . BackColor = C o l o r . L i g h t Y e l l o w
Dim p a r a D e v o l v e r As New Bitmap ( 1 , 1 )
pan . DrawToBitmap ( paraDevolver , New R e c t a n g l e ( 0 , 0 , 1 , 1 ) )
Return p a r a D e v o l v e r
End Function
216
Dim aux As I n t 6 4
For i = 0 To 85
For j = 0 To 14
aux = CInt (Me . n o t a s ( i ) . l i s t a F r e c u e n c i a s ( j ) *
l a r g o T a b l a S e n o s / FsPic )
p a r a D e v o l v e r = p a r a D e v o l v e r + aux . T o S t r i n g + ” , ”
Next
Next
For i = 0 To 85
For j = 0 To 14
aux = CInt ( 1 3 4 2 1 7 7 2 8 * volumen *
Me . n o t a s ( i ) . l i s t a M a s c a r a s ( j ) . v a l o r I n i c i a l *
Me . n o t a s ( i ) . l i s t a C o e f i c i e n t e s ( j ) )
p a r a D e v o l v e r = p a r a D e v o l v e r + aux . T o S t r i n g + ” , ”
Next
Next
For i = 0 To 85
For j = 0 To 14
For k = 0 To 6
aux = CInt (Me . n o t a s ( i ) . l i s t a M a s c a r a s ( j ) . cambios ( k ) )
p a r a D e v o l v e r = p a r a D e v o l v e r + aux . T o S t r i n g + ” , ”
Next
Next
Next
For i = 0 To 85
For j = 0 To 14
For k = 0 To 6
aux = CInt ( 1 3 4 2 1 7 7 2 8 *
Me . n o t a s ( i ) . l i s t a M a s c a r a s ( j ) . i n c r e m e n t o s ( k ) *
volumen * Me . n o t a s ( i ) . l i s t a C o e f i c i e n t e s ( j ) )
I f i = 85 And j = 14 And k = 6 Then
p a r a D e v o l v e r = p a r a D e v o l v e r + aux . T o S t r i n g
Else
p a r a D e v o l v e r = p a r a D e v o l v e r + aux . T o S t r i n g + ” , ”
End I f
Next
Next
Next
Return p a r a D e v o l v e r
End Function
#End Region
End C l a s s
programaVisual/apli/WindowsApplication1/instrumento.vb
217
Clase ’mascara.vb’
#Region ” a t r i b u t o s ”
Private i n c r e m e n t o s As L i s t ( Of Double )
Private c a m b i o s As L i s t ( Of I n t e g e r )
Private v a l o r I n i c i a l As Double
Private nombre As S t r i n g
Private f o t o As Image
#End Region
#Region ” p r o p i e d a d e s ”
P u b l i c P r o p e r t y i n c r e m e n t o s ( ) As L i s t ( Of Double )
Get
Return Me . i n c r e m e n t o s
End Get
S e t ( ByVal v a l u e As L i s t ( Of Double ) )
Me . i n c r e m e n t o s = v a l u e
End S e t
End P r o p e r t y
P u b l i c P r o p e r t y cambios ( ) As L i s t ( Of I n t e g e r )
Get
Return Me . c a m b i o s
End Get
S e t ( ByVal v a l u e As L i s t ( Of I n t e g e r ) )
Me . c a m b i o s = v a l u e
End S e t
End P r o p e r t y
P u b l i c P r o p e r t y v a l o r I n i c i a l ( ) As Double
Get
Return Me . v a l o r I n i c i a l
End Get
S e t ( ByVal v a l u e As Double )
Me . v a l o r I n i c i a l = v a l u e
End S e t
End P r o p e r t y
P u b l i c P r o p e r t y nombre ( ) As S t r i n g
Get
Return Me . nombre
End Get
S e t ( ByVal v a l u e As S t r i n g )
Me . nombre = v a l u e
End S e t
End P r o p e r t y
P u b l i c P r o p e r t y f o t o ( ) As Image
Get
Return Me . f o t o
End Get
S e t ( ByVal v a l u e As Image )
Me . f o t o = v a l u e
218
End S e t
End P r o p e r t y
#End Region
#Region ” c o n s t r u c t o r e s ”
P u b l i c Sub New ( )
Me . i n c r e m e n t o s = New L i s t ( Of Double )
Me . cambios = New L i s t ( Of I n t e g e r )
Me . v a l o r I n i c i a l = 1
Me . nombre = ” S i n nombre a s i g n a d o ”
Me . f o t o = c r e a r I m a g e n V a c i a ( )
Dim i As I n t e g e r
For i = 0 To 6
I f i <= 5 Then
cambios . Add( 5 0 0 * i + 5 0 0 )
i n c r e m e n t o s . Add ( ( i − 6 ) / 1 2 0 0 0 0 )
Else
cambios . Add ( 6 5 5 3 5 )
i n c r e m e n t o s . Add( ( − 0 . 1 3 ) / ( 6 5 5 3 5 0 − 10 * cambios ( 5 ) ) )
End I f
Next
End Sub
219
P u b l i c Sub New( ByVal nom As S t r i n g , ByVal l i s t a I n c r e m e n t o s As L i s t ( Of
Double ) , ByVal l i s t a C a m b i o s As L i s t ( Of I n t e g e r ) , ByVal v i As
Integer )
Me . i n c r e m e n t o s = metodosComunes . c o p i a L i s t a D o u b l e ( l i s t a I n c r e m e n t o s )
Me . cambios = metodosComunes . c o p i a L i s t a E n t e r o s ( l i s t a C a m b i o s )
Me . v a l o r I n i c i a l = v i
Me . nombre = nom
Me . f o t o = c r e a r I m a g e n V a c i a ( )
End Sub
220
Dim i n d i c e 1 As I n t e g e r = 0
Dim i n d i c e 2 As I n t e g e r = 0
Dim aux As New L i s t ( Of Double )
Me . v a l o r I n i c i a l = ( pon1 * puntosY1 ( 0 ) + pon2 * puntosY2 ( 0 ) ) /
( pon1 + pon2 )
Me . cambios . Add ( 1 )
For i = 0 To mas1 . cambios . Count − 2
I f mas1 . cambios ( i ) = 65535 Then
Me . cambios . Add( mas2 . cambios ( i ) )
E l s e I f mas2 . cambios ( i ) = 65535 Then
Me . cambios . Add( mas1 . cambios ( i ) )
Else
Me . cambios . Add ( ( pon1 * mas1 . cambios ( i ) + pon2 *
mas2 . cambios ( i ) ) / ( pon1 + pon2 ) )
End I f
Try
puntosY1 ( i + 2 ) = puntosY1 ( i + 1 ) + ( 1 0 * ( mas1 . cambios ( i
+ 1 ) − mas1 . cambios ( i ) ) ) * mas1 . i n c r e m e n t o s ( i + 1 )
Catch ex As E x c e p t i o n
puntosY1 ( i + 2 ) = puntosY1 ( i + 1)
End Try
Try
puntosY2 ( i + 2 ) = puntosY2 ( i + 1 ) + ( 1 0 * ( mas2 . cambios ( i
+ 1 ) − mas2 . cambios ( i ) ) ) * mas2 . i n c r e m e n t o s ( i + 1 )
Catch ex As E x c e p t i o n
puntosY2 ( i + 2 ) = puntosY2 ( i + 1)
End Try
Next
Me . cambios . Add ( 6 5 5 3 5 )
Dim listaCambiosAux1 As New L i s t ( Of I n t e g e r )
Dim listaCambiosAux2 As New L i s t ( Of I n t e g e r )
listaCambiosAux1 . Add ( 0 )
listaCambiosAux2 . Add ( 0 )
For i = 0 To mas1 . cambios . Count − 1
listaCambiosAux1 . Add( mas1 . cambios ( i ) )
listaCambiosAux2 . Add( mas2 . cambios ( i ) )
Next
indice1 = 0
indice2 = 0
aux . Add(Me . v a l o r I n i c i a l )
For i = 0 To Me . cambios . Count − 2
While listaCambiosAux1 ( i n d i c e 1 ) < Me . cambios ( i + 1 )
indice1 = indice1 + 1
End While
While listaCambiosAux2 ( i n d i c e 2 ) < Me . cambios ( i + 1 )
indice2 = indice2 + 1
End While
indice2 = indice2 − 1
indice1 = indice1 − 1
aux . Add ( ( pon1 * ( puntosY1 ( i n d i c e 1 ) + mas1 . i n c r e m e n t o s ( i n d i c e 1 )
* 10 * (Me . cambios ( i + 1 ) − listaCambiosAux1 ( i n d i c e 1 ) ) ) +
pon2 * ( puntosY2 ( i n d i c e 2 ) + mas2 . i n c r e m e n t o s ( i n d i c e 2 ) * 10
* (Me . cambios ( i + 1 ) − listaCambiosAux2 ( i n d i c e 2 ) ) ) ) / ( pon1
+ pon2 ) )
Me . i n c r e m e n t o s . Add ( ( aux ( i + 1 ) − aux ( i ) ) / ( 1 0 * (Me . cambios ( i
+ 1 ) − Me . cambios ( i ) ) ) )
Next
221
Me . cambios . Remove ( 1 )
Dim p a r a B o r r a r As I n t e g e r = 2
I f pon1 = 1 Then
paraBorrar = 3
End I f
End Sub
#End Region
#Region ” métodos ”
P r i v a t e Function c r e a r I m a g e n V a c i a ( ) As Image
Dim p a r a D e v o l v e r As New Bitmap ( 1 , 1 )
Return p a r a D e v o l v e r
End Function
#End Region
End C l a s s
programaVisual/apli/WindowsApplication1/mascara.vb
222
Clase ’metodosComunes.vb’
P u b l i c C l a s s metodosComunes
223
Next
Return p a r a D e v o l v e r
End Function
End C l a s s
programaVisual/apli/WindowsApplication1/metodosComunes.vb
224
Clase ’nota.vb’
#Region ” a t r i b u t o s ”
Private nombre As S t r i n g
Private l i s t a F r e c u e n c i a s As L i s t ( Of Double )
Private l i s t a M a s c a r a s As L i s t ( Of mascara )
Private f r e c u e n c i a P r i n c i p a l As Double
Private l i s t a C o e f i c i e n t e s As L i s t ( Of Double )
#End Region
#Region ” m o d i f i c a d o r e s ”
P u b l i c P r o p e r t y l i s t a C o e f i c i e n t e s ( ) As L i s t ( Of Double )
Get
Return Me . l i s t a C o e f i c i e n t e s
End Get
S e t ( ByVal v a l u e As L i s t ( Of Double ) )
Me . l i s t a C o e f i c i e n t e s = v a l u e
End S e t
End P r o p e r t y
P u b l i c P r o p e r t y nombre ( ) As S t r i n g
Get
Return Me . nombre
End Get
S e t ( ByVal v a l u e As S t r i n g )
Me . nombre = v a l u e
End S e t
End P r o p e r t y
P u b l i c P r o p e r t y l i s t a F r e c u e n c i a s ( ) As L i s t ( Of Double )
Get
Return Me . l i s t a F r e c u e n c i a s
End Get
S e t ( ByVal v a l u e As L i s t ( Of Double ) )
Me . l i s t a F r e c u e n c i a s = v a l u e
End S e t
End P r o p e r t y
P u b l i c P r o p e r t y l i s t a M a s c a r a s ( ) As L i s t ( Of mascara )
Get
Return Me . l i s t a M a s c a r a s
End Get
S e t ( ByVal v a l u e As L i s t ( Of mascara ) )
Me . l i s t a M a s c a r a s = v a l u e
End S e t
End P r o p e r t y
P u b l i c P r o p e r t y f r e c u e n c i a P r i n c i p a l ( ) As Double
Get
Return Me . f r e c u e n c i a P r i n c i p a l
End Get
S e t ( ByVal v a l u e As Double )
Me . f r e c u e n c i a P r i n c i p a l = v a l u e
225
End S e t
End P r o p e r t y
#End Region
#Region ” c o n s t r u c t o r e s ”
P u b l i c Sub New ( )
Me . nombre = ” S i n nombre a s i g n a d o ”
Dim i As I n t e g e r
l i s t a F r e c u e n c i a s = New L i s t ( Of Double )
l i s t a M a s c a r a s = New L i s t ( Of mascara )
l i s t a C o e f i c i e n t e s = New L i s t ( Of Double )
For i = 0 To 14
l i s t a F r e c u e n c i a s . Add( 1 4 7 * ( i + 1 ) )
l i s t a M a s c a r a s . Add(New mascara ( ) )
Me . l i s t a C o e f i c i e n t e s . Add ( 1 )
Next
Me . f r e c u e n c i a P r i n c i p a l = 440
End Sub
#End Region
#Region ” métodos ”
226
Dim l i s t a S e c t o r e s ( ) As I n t e g e r = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0}
Dim v a l o r e s M a s c a r a s ( 1 4 ) As Double
Dim i As I n t e g e r
For i = 0 To 14
valoresMascaras ( i ) = listaMascaras ( i ) . v a l o r I n i c i a l
Next
Dim v a l o r A c t u a l As Double
Dim d i v i s o r As Double = 0
For i n d i c e = 0 To c a n t i d a d M u e s t r a s − 1
valorActual = 0
For i = 0 To 14
valoresMascaras ( i ) = valoresMascaras ( i ) +
Me . l i s t a M a s c a r a s ( i ) . i n c r e m e n t o s ( l i s t a S e c t o r e s ( i ) )
valorActual = valorActual + l i s t a C o e f i c i e n t e s ( i ) *
v a l o r e s M a s c a r a s ( i ) * Math . S i n ( 2 * Math . PI *
l i s t a F r e c u e n c i a s ( i ) * i n d i c e / FsPic )
I f i n d i c e > 10 *
Me . l i s t a M a s c a r a s ( i ) . cambios ( l i s t a S e c t o r e s ( i ) ) Then
listaSectores ( i ) = listaSectores ( i ) + 1
End I f
Next
l i s t a V a l o r e s . Add( v a l o r A c t u a l )
I f Math . Abs ( v a l o r A c t u a l ) > d i v i s o r Then
d i v i s o r = Math . Abs ( v a l o r A c t u a l )
End I f
Next
For i = 0 To 14
Me . l i s t a C o e f i c i e n t e s ( i ) = Me . l i s t a C o e f i c i e n t e s ( i ) / d i v i s o r
Next
For i = 0 To c a n t i d a d M u e s t r a s − 1
p a r a D e v o l v e r . Add( CInt ( 3 2 0 0 0 * ( l i s t a V a l o r e s ( i ) / d i v i s o r ) ) )
Next
Return p a r a D e v o l v e r
End Function
#End Region
End C l a s s
programaVisual/apli/WindowsApplication1/nota.vb
227
Clase ’pideEnteros.vb’
Imports System . IO
Imports System . Runtime . S e r i a l i z a t i o n . F o r m a t t e r s . Binary
Public Class pideEnteros
#Region ” a t r i b u t o s ”
Private v e n t P a d r e As c r e a r M a s c a r a
Private numeroUno As I n t e g e r
Private numeroDos As I n t e g e r
Private r u t a 1 As S t r i n g
Private r u t a 2 As S t r i n g
Private r e s p u e s t a As Boolean
#End Region
#Region ” p r o p i e d a d e s ”
P u b l i c P r o p e r t y ventPadre ( ) As c r e a r M a s c a r a
Get
Return Me . v e n t P a d r e
End Get
S e t ( ByVal v a l u e As c r e a r M a s c a r a )
Me . v e n t P a d r e = v a l u e
End S e t
End P r o p e r t y
P u b l i c P r o p e r t y numeroUno ( ) As I n t e g e r
Get
Return Me . numeroUno
End Get
S e t ( ByVal v a l u e As I n t e g e r )
Me . numeroUno = v a l u e
End S e t
End P r o p e r t y
P u b l i c P r o p e r t y r u t a 1 ( ) As S t r i n g
Get
Return Me . r u t a 1
End Get
S e t ( ByVal v a l u e As S t r i n g )
Me . r u t a 1 = v a l u e
End S e t
End P r o p e r t y
P u b l i c P r o p e r t y r u t a 2 ( ) As S t r i n g
Get
Return Me . r u t a 2
End Get
S e t ( ByVal v a l u e As S t r i n g )
Me . r u t a 2 = v a l u e
End S e t
End P r o p e r t y
P u b l i c P r o p e r t y numeroDos ( ) As I n t e g e r
Get
Return Me . numeroDos
228
End Get
S e t ( ByVal v a l u e As I n t e g e r )
Me . numeroDos = v a l u e
End S e t
End P r o p e r t y
P u b l i c P r o p e r t y r e s p u e s t a ( ) As Boolean
Get
Return Me . r e s p u e s t a
End Get
S e t ( ByVal v a l u e As Boolean )
Me . r e s p u e s t a = v a l u e
End S e t
End P r o p e r t y
#End Region
#Region ” c o n s t r u c t o r ”
End Try
End Sub
#End Region
#Region ” r e a c c i o n e s ”
229
P r i v a t e Sub BExaminar1 Click ( ByVal s e n d e r As System . Object , ByVal e As
System . EventArgs ) Handles BExaminar1 . C l i c k
Dim vent As New O p e n F i l e D i a l o g
vent . T i t l e = ” s e l e c c i o n e l a p r i m e r e n v o l v e n t e d e s d e l a c u a l q u i e r e
extrapolar ”
vent . AddExtension = True
vent . F i l t e r = ” i n s t r u m e n t o s ( * . masc ) | * . masc”
vent . ShowDialog ( )
I f vent . FileName <> Nothing Then
Me . TRuta1 . Text = vent . FileName
End I f
End Sub
#End Region
End C l a s s
programaVisual/apli/WindowsApplication1/pideEnteros.vb
230
Clase ’pideUnDouble.vb’
Imports System . IO
Imports System . Runtime . S e r i a l i z a t i o n . F o r m a t t e r s . Binary
P u b l i c C l a s s pideUnDouble
#Region ” v a r i a b l e s ”
P r i v a t e ventanaPadre As c r e a r M a s c a r a
P r i v a t e esX As Boolean
#End Region
#Region ” c o n s t r u c t o r e s ”
P u b l i c Sub New ( )
InitializeComponent ()
Try
Dim r u t a I c o n o As S t r i n g =
Path . GetDirectoryName ( A p p l i c a t i o n . ExecutablePath ) +
”\ nota musical . i c o ”
Dim i c o n o As New I c o n ( r u t a I c o n o )
Me . I c o n = i c o n o
Catch ex As E x c e p t i o n
End Try
End Sub
#End Region
#Region ” r e a c c i o n e s ”
231
End Try
End Sub
#End Region
End C l a s s
programaVisual/apli/WindowsApplication1/pideUnDouble.vb
232
Clase ’transmitir.vb’
Imports System
Imports System . IO . P o r t s
Imports System . Threading
Imports System . IO
Imports System . Runtime . S e r i a l i z a t i o n . F o r m a t t e r s . Binary
Public Class transmitir
#Region ” v a r i a b l e s ”
Dim s e r i a l P o r t As S e r i a l P o r t
Dim nombrePuerto As S t r i n g
Dim textoAEnviar As S t r i n g
Dim r e c i b i E c o As Boolean = F a l s e
Dim ultimoMandado As Byte
Dim readThread As Thread
Dim mandarThread As Thread
Dim e s t o y C o n e c t a d o As Boolean = F a l s e
Dim b y t e R e c i b i d o As Byte
Dim aqum As I n t e g e r = 0
Dim huboError As Boolean = F a l s e
Dim cantidadDeDatosEnviados As I n t e g e r = 0
#End Region
#Region ” c o n s t r u c t o r ”
P u b l i c Sub New ( )
InitializeComponent ()
ListBox1 . Items . Add( ”Debe c h e q u e a r l o s p u e r t o s ” )
Try
Dim r u t a I c o n o As S t r i n g =
Path . GetDirectoryName ( A p p l i c a t i o n . ExecutablePath ) +
”\ nota musical . i c o ”
Dim i c o n o As New I c o n ( r u t a I c o n o )
Me . I c o n = i c o n o
Catch ex As E x c e p t i o n
End Try
End Sub
#End Region
#Region ” r e a c c i o n e s ”
233
nombrePuerto = ListBox1 . S e l e c t e d I t e m . T o S t r i n g
L ab el 1 . Text = ”Ha e l e g i d o e l p u e r t o s e r i e : ” + nombrePuerto
s e r i a l P o r t = New S e r i a l P o r t ( ) ’ C o n f i g u r a c i ó n d e l p u e r t o
serie
s e r i a l P o r t . PortName = nombrePuerto
s e r i a l P o r t . BaudRate = 9600
s e r i a l P o r t . P a r i t y = P a r i t y . None
s e r i a l P o r t . DataBits = 8
s e r i a l P o r t . StopBits = 1
s e r i a l P o r t . Handshake = Handshake . None
s e r i a l P o r t . ReadTimeout = 500
s e r i a l P o r t . WriteTimeout = 500
s e r i a l P o r t . Open ( )
readThread = New Thread ( AddressOf r e a d )
readThread . S t a r t ( )
e s t o y C o n e c t a d o = True
BArchivoNumeros . Enabled = True
Catch ex As E x c e p t i o n
MsgBox ( ” E r r o r de c o n e c c i ó n con e l p u e r t o s e l e c c i o n a d o ” ,
MsgBoxStyle . C r i t i c a l )
End Try
End I f
End Sub
End Try
End Sub
234
P r i v a t e Sub r e a d ( )
While ( e s t o y C o n e c t a d o )
Try
b y t e R e c i b i d o = s e r i a l P o r t . ReadByte ( )
r e c i b i E c o = True
aqum = aqum + 1
L ab el 5 . V i s i b l e = True
L ab el 5 . Text = ”Ha s i d o r e c i bı́ d o un ” +
b y t e R e c i b i d o . T o S t r i n g + ” , a c t u a l m e n t e van ” +
aqum . T o S t r i n g + ” b y t e s r e c i b i d o s ”
Catch ex As E x c e p t i o n
End Try
End While
End Sub
P r i v a t e Sub mandar ( )
I f e s t o y C o n e c t a d o Then
Dim s e p a r a d o r As Char = ” , ”
Dim TestArray ( ) As S t r i n g = S p l i t ( textoAEnviar , s e p a r a d o r )
Dim i As I n t e g e r
Dim largoTrama As I n t e g e r = 100
Dim byteAEnviar1 As Byte
Dim byteAEnviar2 As Byte
Dim byteAEnviar3 As Byte
Dim byteAEnviar4 As Byte
Dim l i n e a As I n t e g e r = 0
Dim datosMandados As I n t e g e r = 0
Dim aEnviar As Long
Dim r e c i b i 2 6 As Boolean = F a l s e
Dim segundoHasta As I n t e g e r = Now . Second + 2
I f segundoHasta > 59 Then
segundoHasta = segundoHasta − 60
End I f
Dim l i s t a As Byte ( ) = {50}
s e r i a l P o r t . Write ( l i s t a , 0 , 1 )
recibiEco = False
While ( segundoHasta <> Now . Second ) And ( Not r e c i b i E c o )
End While
I f segundoHasta <= Now . Second Then
huboError = True
MsgBox ( ”No r e c i bı́ c o n f i r m a c i ó n de comienzo de
programamción ” )
E l s e I f b y t e R e c i b i d o <> 24 Then
MsgBox ( ” R e c i bı́ e c o p e r o no e r a e l e s p e r a d o ” )
End I f
segundoHasta = Now . Second + 5
I f segundoHasta > 59 Then
segundoHasta = segundoHasta − 60
End I f
While ( segundoHasta <> Now . Second )
End While
I f Not huboError Then
For i = 0 To 1289
I f Not huboError Then
Try
linea = 0
aEnviar = CDbl ( TestArray ( i ) )
235
linea = linea + 1
I f aEnviar < 0 Then
aEnviar = aEnviar + 65536
End I f
linea = linea + 1
byteAEnviar2 = CByte ( Fix ( ( aEnviar ) / 2 5 6 ) )
linea = linea + 1
byteAEnviar1 = CByte ( ( aEnviar − 256 *
CDbl ( byteAEnviar2 ) ) )
linea = linea + 1
l i s t a ( 0 ) = byteAEnviar2
linea = linea + 1
s e r i a l P o r t . Write ( l i s t a , 0 , 1 )
linea = linea + 1
datosMandados = datosMandados + 1
cantidadDeDatosEnviados =
cantidadDeDatosEnviados + 1
cantidadDeDatosEnviados =
cantidadDeDatosEnviados + 1
linea = linea + 1
l i s t a ( 0 ) = byteAEnviar1
recibiEco = False
linea = linea + 1
s e r i a l P o r t . Write ( l i s t a , 0 , 1 )
linea = linea + 1
datosMandados = datosMandados + 1
linea = linea + 1
I f datosMandados = largoTrama Then
segundoHasta = Now . Second + 10
I f segundoHasta > 59 Then
segundoHasta = segundoHasta − 60
End I f
datosMandados = 0
While ( segundoHasta <> Now . Second ) And
( Not r e c i b i E c o )
End While
I f r e c i b i E c o Then
I f b y t e R e c i b i d o <> 38 Then
MsgBox ( ” R e c i bı́ e c o p e r o no e r a e l
esperado ” )
huboError = True
End I f
Else
huboError = True
MsgBox ( ”No r e c i bı́ c o n f i r m a c i ó n de
comienzo de que r e c i b i ó e l s e t de
100 datos , aqum v a l e ” +
aqum . T o S t r i n g )
End I f
End I f
Catch ex As E x c e p t i o n
MsgBox ( ” E r r o r a l e n v i a r l a trama , aqum v a l e ”
+ aqum . T o S t r i n g + ” e l número a i n t e r p r e t a r
v a l e ” + TestArray ( i ) + ” l i n e a v a l e ” +
l i n e a . ToString )
huboError = True
End Try
236
End I f
Next
End I f
I f Not huboError Then
For i = 1290 To 2579
I f Not huboError Then
Try
aEnviar = CDbl ( TestArray ( i ) )
byteAEnviar4 = CByte ( Fix ( ( aEnviar ) / 1 6 7 7 7 2 1 6 ) )
byteAEnviar3 = CByte ( Fix ( ( ( aEnviar − 16777216
* CDbl ( byteAEnviar4 ) ) ) / 6 5 5 3 6 ) )
byteAEnviar2 = CByte ( Fix ( ( aEnviar − 16777216 *
CDbl ( byteAEnviar4 ) − 65536 *
CDbl ( byteAEnviar3 ) ) / 2 5 6 ) )
byteAEnviar1 = CByte ( Fix ( aEnviar − 16777216 *
CDbl ( byteAEnviar4 ) − 65536 *
CDbl ( byteAEnviar3 ) − 256 *
CDbl ( byteAEnviar2 ) ) )
l i s t a ( 0 ) = byteAEnviar2
s e r i a l P o r t . Write ( l i s t a , 0 , 1 )
cantidadDeDatosEnviados =
cantidadDeDatosEnviados + 1
l i s t a ( 0 ) = byteAEnviar1
cantidadDeDatosEnviados =
cantidadDeDatosEnviados + 1
s e r i a l P o r t . Write ( l i s t a , 0 , 1 )
recibiEco = False
datosMandados = datosMandados + 2
I f datosMandados = largoTrama Then
segundoHasta = Now . Second + 10
I f segundoHasta > 59 Then
segundoHasta = segundoHasta − 60
End I f
datosMandados = 0
While ( segundoHasta <> Now . Second ) And
( Not r e c i b i E c o )
End While
I f r e c i b i E c o Then
I f b y t e R e c i b i d o <> 38 Then
MsgBox ( ” R e c i bı́ e c o p e r o no e r a e l
esperado ” )
huboError = True
End I f
Else
huboError = True
MsgBox ( ”No r e c i bı́ c o n f i r m a c i ó n de
comienzo de que r e c i b i ó e l s e t de
100 datos , aqum v a l e ” +
aqum . T o S t r i n g )
End I f
End I f
l i s t a ( 0 ) = byteAEnviar4
s e r i a l P o r t . Write ( l i s t a , 0 , 1 )
cantidadDeDatosEnviados =
cantidadDeDatosEnviados + 1
237
l i s t a ( 0 ) = byteAEnviar3
s e r i a l P o r t . Write ( l i s t a , 0 , 1 )
cantidadDeDatosEnviados =
cantidadDeDatosEnviados + 1
recibiEco = False
datosMandados = datosMandados + 2
I f datosMandados = largoTrama Then
segundoHasta = Now . Second + 10
I f segundoHasta > 59 Then
segundoHasta = segundoHasta − 60
End I f
datosMandados = 0
While ( segundoHasta <> Now . Second ) And
( Not r e c i b i E c o )
End While
I f r e c i b i E c o Then
I f b y t e R e c i b i d o <> 38 Then
MsgBox ( ” R e c i bı́ e c o p e r o no e r a e l
esperado ” )
huboError = True
End I f
Else
huboError = True
MsgBox ( ”No r e c i bı́ c o n f i r m a c i ó n de
comienzo de que r e c i b i ó e l s e t de
100 datos , aqum v a l e ” +
aqum . T o S t r i n g )
End I f
End I f
Catch ex As E x c e p t i o n
MsgBox ( ” E r r o r a l e n v i a r l a trama , aqum v a l e ”
+ aqum . T o S t r i n g + ” e l número a i n t e r p r e t a r
v a l e ” + TestArray ( i ) + ” l i n e a v a l e ” +
l i n e a . T o S t r i n g + ” e s t o y en l a segunda
tabla ”)
huboError = True
End Try
End I f
Next
End I f
I f Not huboError Then
For i = 2580 To 20639
I f Not huboError Then
Try
aEnviar = CDbl ( TestArray ( i ) )
I f aEnviar < 0 Then
aEnviar = aEnviar + 65536
End I f
byteAEnviar2 = CByte ( Fix ( ( aEnviar ) / 2 5 6 ) )
byteAEnviar1 = CByte ( ( aEnviar − 256 *
CDbl ( byteAEnviar2 ) ) )
l i s t a ( 0 ) = byteAEnviar2
s e r i a l P o r t . Write ( l i s t a , 0 , 1 )
datosMandados = datosMandados + 1
cantidadDeDatosEnviados =
238
cantidadDeDatosEnviados + 2
l i s t a ( 0 ) = byteAEnviar1
s e r i a l P o r t . Write ( l i s t a , 0 , 1 )
recibiEco = False
datosMandados = datosMandados + 1
I f datosMandados = largoTrama Then
segundoHasta = Now . Second + 10
I f segundoHasta > 59 Then
segundoHasta = segundoHasta − 60
End I f
datosMandados = 0
While ( segundoHasta <> Now . Second ) And
( Not r e c i b i E c o )
End While
I f r e c i b i E c o Then
I f b y t e R e c i b i d o <> 38 Then
MsgBox ( ” R e c i bı́ e c o p e r o no e r a e l
esperado ” )
huboError = True
End I f
Else
huboError = True
MsgBox ( ”No r e c i bı́ c o n f i r m a c i ó n de
comienzo de que r e c i b i ó e l s e t de
100 datos , aqum v a l e ” +
aqum . T o S t r i n g )
End I f
End I f
Catch ex As E x c e p t i o n
MsgBox ( ” E r r o r a l e n v i a r l a trama , aqum v a l e ”
+ aqum . T o S t r i n g + ” e l número a i n t e r p r e t a r
v a l e ” + TestArray ( i ) + ” l i n e a v a l e ” +
l i n e a . T o S t r i n g + ” e s t o y en l a t e r c e r a
tabla ”)
huboError = True
End Try
End I f
Next
End I f
While datosMandados < largoTrama And datosMandados > 0
datosMandados = datosMandados + 1
l i s t a (0) = 1
recibiEco = False
s e r i a l P o r t . Write ( l i s t a , 0 , 1 )
cantidadDeDatosEnviados = cantidadDeDatosEnviados + 1
End While
segundoHasta = Now . Second + 10
I f segundoHasta > 59 Then
segundoHasta = segundoHasta − 60
End I f
While ( segundoHasta <> Now . Second ) And ( Not r e c i b i E c o )
End While
I f r e c i b i E c o Then
recibiEco = False
239
I f b y t e R e c i b i d o <> 38 Then
MsgBox ( ” R e c i bı́ e c o p e r o no e r a e l e s p e r a d o ” )
huboError = True
End I f
Else
huboError = True
MsgBox ( ”No r e c i bı́ c o n f i r m a c i ó n de comienzo de que r e c i b i ó
e l s e t de 100 datos , aqum v a l e ” + aqum . T o S t r i n g )
End I f
segundoHasta = Now . Second + 10
I f segundoHasta > 59 Then
segundoHasta = segundoHasta − 60
End I f
While ( segundoHasta <> Now . Second ) And ( Not r e c i b i E c o )
End While
I f r e c i b i E c o Then
I f b y t e R e c i b i d o <> 21 Then
MsgBox ( ” R e c i bı́ un dato p e r o no e r a l a c o n f i r m a c i ó n de
f i n a l i a c i ó n de l a programación ” )
huboError = True
End I f
Else
huboError = True
MsgBox ( ”No r e c i bı́ c o n f i r m a c i ó n de f i n a l i z a c i ó n d e l
p r o c e s o , aqum v a l e ” + aqum . T o S t r i n g )
End I f
I f huboError Then
MsgBox ( ” El p r o c e s o s a l i ó mal” )
Else
MsgBox ( ” F e l i c i t a c i o n e s , e l p i c e s t a b i e n grabado ” )
End I f
End I f
mandarThread . J o i n ( )
End Sub
240
#End Region
End C l a s s
programaVisual/apli/WindowsApplication1/transmitir.vb
241
Clase ’ventanaInicial.vb’
Imports System . IO
Imports System . Runtime . S e r i a l i z a t i o n . F o r m a t t e r s . Binary
Public Class ventanaInicial
#Region ” r e a c c i o n e s ”
242
End Sub
#End Region
#Region ” c o n s t r u c t o r e s ”
P u b l i c Sub New ( )
InitializeComponent ()
Try
Dim r u t a I c o n o As S t r i n g =
Path . GetDirectoryName ( A p p l i c a t i o n . ExecutablePath ) +
”\ nota musical . i c o ”
Dim i c o n o As New I c o n ( r u t a I c o n o )
Me . I c o n = i c o n o
Catch ex As E x c e p t i o n
End Try
End Sub
#End Region
End C l a s s
programaVisual/apli/WindowsApplication1/ventanaInicial.vb
243
Anexo E, códigos de scilab
Función ’acortaSonido’
[ y , Fs , b i t s ]= wavread ( e n t r a d a ) ;
i f sumar<=0
y=y ( c a n a l , : ) ;
end
i f sumar>0
y=y ( 1 , : )+y ( 2 , : ) ;
end
i f ( hasta <0)
h a s t a=l e n g t h ( y )
end
l a r g o=hasta−d e s d e ;
z=z e r o s ( 1 , l a r g o ) ;
f o r i=d e s d e +1: d e s d e+l a r g o
z ( i −d e s d e )=y ( i ) ;
end
z=z /max( abs ( z ) ) ;
wavwrite ( z , Fs , s a l i d a ) ;
endfunction
scilab/acortaSonido.sci
244
Función ’copiaNota’
f u n c t i o n [ y , mi , i n c r e m e n t o s , cambios , e v o l u c i o n ]=
copiaNota ( r u t a D e s t i n o , f r e c u e n c i a s , e v o l u c i o n , cadaCuantosEvol , nota , l a r g o , Fs )
245
Función ’copiaSonido’
f u n c t i o n [ y , m a s c a r a s I n i c i a l e s , i n c r e m e n t o s , cambios , P i c o s , e v o l u c i o n , aqum]=
copiaSonido ( rutaOrigen , rutaDestinoSonido , rutaDestinoTabla ,
f r e c u e n c i a P i c , c a n t i d a d S e n o s , nota )
[ P i c o s , l arg oAud io , Fs]= F o u r i e r F a c i l ( r u t a O r i g e n , 5 0 0 0 , − 1 , 1 5 , 1 ) ;
c o e f i c i e n t e s=F o u r i e r P a r t e s ( r u t a O r i g e n ,1 , −1 , l a r g o A u d i o / 8 , P i c o s ) ;
[ y , m a s c a r a s I n i c i a l e s , i n c r e m e n t o s , cambios , e v o l u c i o n ]=
copiaNota ( r u t a D e s t i n o S o n i d o , P i c o s ,
c o e f i c i e n t e s , l a r g o A u d i o / 8 , nota , l arg oAud io , Fs ) ;
[ fd , e r r ]=mopen ( r u t a D e s t i n o T a b l a , ’w ’ ) ;
aqum=0;
f o r i =1:86
f r e c u e n c i a s=P i c o s * 2 . ˆ ( ( ( i −nota ) / 1 2 ) )
f o r j =1: l e n g t h ( f r e c u e n c i a s )
t e x t o=m f p r i n t f
( fd , s t r i n g ( round ( f r e c u e n c i a s ( j ) * c a n t i d a d S e n o s / f r e c u e n c i a P i c ) )+” , ” ) ;
aqum=aqum+1;
end
end
f o r i =1:86
f o r j =1: l e n g t h ( m a s c a r a s I n i c i a l e s )
m f p r i n t f ( fd , s t r i n g ( round ( 6 5 5 3 6 * m a s c a r a s I n i c i a l e s ( j ) ) )+” , ” ) ;
aqum=aqum+1;
end
end
f o r i =1:86
tam=s i z e ( cambios ) ;
f o r j =1:tam ( 1 , 1 )
f o r k=1:tam ( 1 , 2 )−2
m f p r i n t f ( fd , s t r i n g ( round ( cambios ( j , k ) * f r e c u e n c i a P i c / ( 1 0 * Fs ) ) )+” , ” ) ;
aqum=aqum+1;
end
m f p r i n t f ( fd , s t r i n g ( 6 5 5 3 5 )+” , ” )
aqum=aqum+1;
end
end
f o r i =1:86
tam=s i z e ( i n c r e m e n t o s ) ;
f o r j =1:tam ( 1 , 1 )
f o r k=1:tam ( 1 , 2 )
i f i ==86 & j==tam ( 1 , 1 ) & k==tam ( 1 , 2 )
m f p r i n t f ( fd , s t r i n g ( round ( 6 5 5 3 6 * i n c r e m e n t o s ( j , k ) * Fs / ( f r e c u e n c i a P i c ) ) ) ) ;
else
m f p r i n t f ( fd ,
s t r i n g ( round ( 6 5 5 3 6 * i n c r e m e n t o s ( j , k ) * Fs / ( f r e c u e n c i a P i c ) ) )+” , ” ) ;
end
aqum=aqum+1;
end
end
end
mclose ( fd ) ;
endfunction
scilab/copiaSonido.sci
246
Función ’fourieFacilDetectaPicos’
i f cantidadMuestras > 0
[ y , Fs , b i t s ]= wavread ( entrada , c a n t i d a d M u e s t r a s ) ;
end
i f c a n t i d a d M u e s t r a s <0
[ y , Fs , b i t s ]= wavread ( e n t r a d a ) ;
end
y=y ( 1 , : ) ;
l a r g o A u d i o=l e n g t h ( y ) ;
z=z e r o s ( 1 , c a n t i d a d )
f o u= f f t ( y )
f o r i =1: l e n g t h ( f o u ) /2
z (1 ,1+ round ( 2 * i * ( c a n t i d a d −1)/ l e n g t h ( f o u ) ) )=
abs ( z (1 ,1+ round ( 2 * i * ( c a n t i d a d −1)/ l e n g t h ( f o u ) ) ) )+abs ( f o u ( i ) ) ˆ2
end
f o r i =1: l e n g t h ( z )
z ( 1 , i )=s q r t ( z ( 1 , i ) ) ;
end
j =0: c a n t i d a d −1
j=j * Fs / ( 2 * c a n t i d a d )
maximo=max( z ( 1 , : ) )
247
Función ’fouriePartesPicos’
f u n c t i o n c o e f i c i e n t e s=
F o u r i e r P a r t e s ( r u t a o r i g e n , desde , hasta , l a r g o P a r t i c i o n , f r e c u e n c i a s )
[ y , Fs , b i t s ]= wavread ( r u t a o r i g e n ) ;
y=y ( 1 , : ) ;
i f hasta <0
h a s t a=l e n g t h ( y ) −2;
end
[ y , Fs , b i t s ]= wavread ( r u t a o r i g e n , h a s t a +1) ;
y=y ( 1 , : ) ;
c o e f i c i e n t e s=z e r o s ( round ( ( hasta −d e s d e ) / l a r g o P a r t i c i o n ) +1,
length ( frecuencias ) ) ;
aqum=1;
s o n i d o=z e r o s ( 1 , hasta−d e s d e ) ;
f o r i =1: hasta−d e s d e
s o n i d o ( i )=y ( i+d e s d e ) ;
end
f o r i =1: l a r g o P a r t i c i o n : l e n g t h ( s o n i d o )
y=z e r o s ( 1 , l a r g o P a r t i c i o n ) ;
f o r j =1: l a r g o P a r t i c i o n
i f j+i <l e n g t h ( s o n i d o )
y ( j )=s o n i d o ( i+j ) ;
end
end
f o u= f f t ( y )
j =0: l e n g t h ( f o u ) −1;
j=j * Fs / ( l e n g t h ( f o u ) ) ;
p o s i c i o n e s F r e c u e n c i a s=z e r o s ( 1 , l e n g t h ( f r e c u e n c i a s ) ) ;
f o r m=1: l e n g t h ( f r e c u e n c i a s )
f o r n=1: l e n g t h ( f o u )−1
i f j ( n )< =f r e c u e n c i a s (m)
i f j ( n+1)>f r e c u e n c i a s (m)
p o s i c i o n e s F r e c u e n c i a s (m)=n ;
end
end
end
end
f o r m=1: l e n g t h ( f r e c u e n c i a s )
c o e f i c i e n t e s ( aqum ,m) =0;
f o r k=−3:3
i f ( p o s i c i o n e s F r e c u e n c i a s (m)+k>0)
i f ( p o s i c i o n e s F r e c u e n c i a s (m)+k<=l e n g t h ( f o u ) )
c o e f i c i e n t e s ( aqum ,m)=c o e f i c i e n t e s ( aqum ,m)+
abs ( f o u ( p o s i c i o n e s F r e c u e n c i a s (m)+k ) ) ˆ2
end
end
end
c o e f i c i e n t e s ( aqum ,m)=s q r t ( c o e f i c i e n t e s ( aqum ,m) ) ;
end
aqum=aqum+1;
end
s a l i d a =0;
aqum=1;
e j e =0: l e n g t h ( c o e f i c i e n t e s ) / l e n g t h ( f r e c u e n c i a s ) −1;
248
e j e=e j e * l a r g o P a r t i c i o n ;
d i v i s i o n e s=round ( l e n g t h ( f r e c u e n c i a s ) / 4 )
f o r j =1: d i v i s i o n e s
x s e t ( ’ window ’ , j +1)
f o r i =1:4
s u b p l o t ( 5 , 1 , i +1) ;
i f i +( j −1) * d i v i s i o n e s <=l e n g t h ( f r e c u e n c i a s )
p l o t 2 d ( e j e , c o e f i c i e n t e s ( : , i +( j −1) * d i v i s i o n e s ) ) ;
end
end
end
endfunction
scilab/fouriePartesPicos.sci
249
Función ’graficaFacil’
f u n c t i o n z= g r a f i c a F a c i l ( entrada , c a n t i d a d , c o l o r G r a f , c a n a l )
[ y , Fs , b i t s ]= wavread ( e n t r a d a ) ;
y=y ( c a n a l , : ) ;
z=z e r o s ( 1 , c a n t i d a d )
f o u=y ;
f o r i =1: l e n g t h ( y )
z (1 ,1+ round ( i * ( c a n t i d a d −1)/ l e n g t h ( y ) ) )=
z (1 ,1+ round ( i * ( c a n t i d a d −1)/ l e n g t h ( y ) ) )+abs ( f o u ( i ) )
end
j =0: c a n t i d a d −1
j=j * l e n g t h ( y ) / ( c a n t i d a d * Fs )
maximo=max( z ( 1 , : ) )
p l o t 2 d ( j , ( z ( 1 , : ) ) /maximo , c o l o r G r a f )
endfunction
scilab/graficaFacil.sci
250
Función ’haceGraficas’
f u n c t i o n f r e c u e n c i a s= h a c e G r a f i c a s ( ruta , c o l )
[ y , Fs , b i t s ]= wavread ( r u t a o r i g e n ) ;
y=y ( 1 , : ) ;
i f hasta <0
h a s t a=l e n g t h ( y ) −2;
end
[ y , Fs , b i t s ]= wavread ( r u t a o r i g e n , h a s t a +1) ;
y=y ( 1 , : ) ;
c o e f i c i e n t e s=z e r o s ( round ( ( hasta −d e s d e ) / l a r g o P a r t i c i o n ) +1,
length ( frecuencias ) ) ;
aqum=1;
s o n i d o=z e r o s ( 1 , hasta−d e s d e ) ;
f o r i =1: hasta−d e s d e
s o n i d o ( i )=y ( i+d e s d e ) ;
end
f o r i =1: l a r g o P a r t i c i o n : l e n g t h ( s o n i d o )
y=z e r o s ( 1 , l a r g o P a r t i c i o n ) ;
f o r j =1: l a r g o P a r t i c i o n
i f j+i <l e n g t h ( s o n i d o )
y ( j )=s o n i d o ( i+j ) ;
end
end
f o u= f f t ( y )
j =0: l e n g t h ( f o u ) −1;
j=j * Fs / ( l e n g t h ( f o u ) ) ;
p o s i c i o n e s F r e c u e n c i a s=z e r o s ( 1 , l e n g t h ( f r e c u e n c i a s ) ) ;
f o r m=1: l e n g t h ( f r e c u e n c i a s )
f o r n=1: l e n g t h ( f o u )−1
i f j ( n )< =f r e c u e n c i a s (m)
i f j ( n+1)>f r e c u e n c i a s (m)
p o s i c i o n e s F r e c u e n c i a s (m)=n ;
end
end
end
end
f o r m=1: l e n g t h ( f r e c u e n c i a s )
c o e f i c i e n t e s ( aqum ,m) =0;
f o r k=−1:1
i f ( p o s i c i o n e s F r e c u e n c i a s (m)+k>0)
i f ( p o s i c i o n e s F r e c u e n c i a s (m)+k<=l e n g t h ( f o u ) )
c o e f i c i e n t e s ( aqum ,m)=c o e f i c i e n t e s ( aqum ,m)+
abs ( f o u ( p o s i c i o n e s F r e c u e n c i a s (m)+k ) ) ˆ2
end
end
end
c o e f i c i e n t e s ( aqum ,m)=s q r t ( c o e f i c i e n t e s ( aqum ,m) )
end
251
aqum=aqum+1;
end
s a l i d a =0;
aqum=1;
e j e =0: l e n g t h ( c o e f i c i e n t e s ) / l e n g t h ( f r e c u e n c i a s ) −1;
e j e=e j e * l a r g o P a r t i c i o n / Fs ;
d i v i s i o n e s =15
i =1
f o r j =1: d i v i s i o n e s
x s e t ( ’ window ’ , j +1)
i f j<=l e n g t h ( f r e c u e n c i a s )
p l o t 2 d ( e j e , c o e f i c i e n t e s ( : , j ) , c o l , r e c t=
[ −0.05 * max( e j e ) / 0 . 9 5 , − 0 . 0 5 * max( c o e f i c i e n t e s ( : , j ) ) / 0 . 9 5
,max( e j e ) ,max( c o e f i c i e n t e s ( : , j ) ) ] ) ;
end
end
endfunction
i f cantidadMuestras > 0
[ y , Fs , b i t s ]= wavread ( entrada , c a n t i d a d M u e s t r a s ) ;
end
i f c a n t i d a d M u e s t r a s <0
[ y , Fs , b i t s ]= wavread ( e n t r a d a ) ;
end
y=y ( 1 , : ) ;
l a r g o A u d i o=l e n g t h ( y ) ;
z=z e r o s ( 1 , c a n t i d a d )
f o u= f f t ( y )
f o r i =1: l e n g t h ( f o u ) /2
z (1 ,1+ round ( 2 * i * ( c a n t i d a d −1)/ l e n g t h ( f o u ) ) )=
abs ( z (1 ,1+ round ( 2 * i * ( c a n t i d a d −1)/ l e n g t h ( f o u ) ) ) )+abs ( f o u ( i ) ) ˆ2
end
f o r i =1: l e n g t h ( z )
z ( 1 , i )=s q r t ( z ( 1 , i ) )
end
j =0: c a n t i d a d −1
j=j * Fs / ( 2 * c a n t i d a d )
maximo=max( z ( 1 , : ) )
x s e t ( ’ window ’ , 0 )
plot2d ( j , ( z ( 1 , : ) ) / cantidad , colorGraf )
P i c o s=z e r o s ( 1 , numeroPicos ) ;
f o r i =1: numeroPicos
valorMaximo =0;
kMaximo=0;
f o r k=1: l e n g t h ( z ( 1 , : ) )
i f z ( 1 , k )>valorMaximo
valorMaximo=z ( 1 , k ) ;
kMaximo=k ;
end
end
f o r k=−25:25
i f kMaximo+k>0
z ( 1 , kMaximo+k ) =−1;
252
end
end
P i c o s ( i )=j ( kMaximo ) ;
end
endfunction
scilab/haceGraficas.sci
253
Efecto chorus
f u n c t i o n E f e c t o C h o r u s ( rutaEntrada , r u t a S a l i d a , f r e c u e n c i a , magnitud )
254
Efecto delay
255
Efecto eco
256
Efecto flanger
function
u=E f e c t o F l a n g e r ( rutaEntrada , r u t a S a l i d a , r e t r a s o , f r e c u e n c i a , p rof und ida d , a g r e g a r )
[ y , Fs , b i t s ]= wavread ( ru taE ntra da )
y=[y ( 1 , : ) , z e r o s ( 1 , a g r e g a r ) ]
maxRetraso=Fs * r e t r a s o ;
t =1: l e n g t h ( y ) ;
u=t ;
f o r i =1: l e n g t h ( t )
u ( i )=u ( i )−round ( maxRetraso * (1+ c o s ( 2 * %pi * f r e c u e n c i a * t ( i ) / Fs ) ) / 2 ) ;
i f ( u ( i ) <1)
u ( i ) =1;
end
end
f o r i =1: l e n g t h ( y )
y ( i )=y ( i )+p r o f u n d i d a d * y ( u ( i ) ) ;
end
y=y/max( abs ( y ) ) ;
wavwrite ( y , Fs , r u t a S a l i d a )
endfunction
scilab/efectosScilab/efectoFlanger.sci
257
Efecto limitador if
f u n c t i o n l i m i t a d o r ( rutaEntrada , r u t a S a l i d a , l i m i t e )
258
Efecto limitador logarı́tmico
f u n c t i o n l i m i t a d o r F u n c i o n ( rutaEntrada , r u t a S a l i d a , c o e f i c i e n t e )
259
Efecto vibrato
f u n c t i o n E f e c t o V i b r a t o ( rutaEntrada , r u t a S a l i d a , f r e c u e n c i a , magnitud )
260
Efecto Wah-Wah
end
p o t e n c i a =0;
f o r i =1: l e n g t h ( y )
p o t e n c i a=p o t e n c i a+y ( i ) ˆ 2 ;
end
y=y/max( abs ( y ) ) ;
wavwrite ( y , Fs , r u t a S a l i d a ) ;
endfunction
scilab/efectosScilab/efectoWahWah.sci
261
Anexo F, códigos de la prueba previa
Clase ’main’
#d e f i n e c u e n t a 132
#d e f i n e a r m o n i c o s 3
c o n s t s h o r t i n t s e n [ ] // Esta t a b l a no f u e impresa , dado que s o l o e s una
t a b l a de 128 números ( ya f u e e x p l i c a d a ) .
short int i ;
unsigned short i n t funcionActual ;
unsigned short i n t datosFuncion [ 2 ] ;
s h o r t i n t posDato ;
s h o r t i n t bandera ;
u n s i g n e d s h o r t i n t nota ;
unsigned short i n t v e l o c i d a d e s [ armonicos ] ;
s h o r t i n t * aux ;
unsigned i n t t o t a l I n t ;
unsigned short i n t coefArmonicos [ armonicos ] ;
void agregarNota ( ) {
i f ( f u n c i o n A c t u a l >143) {
i f ( d a t o s F u n c i o n [1]==0) {
datosFuncion [0]= datosFuncion [ 1 ] ;
}
nota=d a t o s F u n c i o n [ 0 ] ;
v e l o c i d a d e s [0]= datosFuncion [ 1 ] ;
f o r ( i =1; i <a r m o n i c o s ; i ++){
t o t a l I n t=v e l o c i d a d e s [ i −1] * c o e f A r m o n i c o s [ i − 1 ] ;
v e l o c i d a d e s [ i ]= * aux ;
}
return ;
}
nota =0;
}
v o i d main ( ) {
short int total ;
unsigned short i n t tot ;
unsigned short i n t tiempos [ armonicos ] ;
coefArmonicos [0]=127;
coefArmonicos [1]=127;
posDato =0;
nota =0;
TRISB=128;
OPTION REG=0;
INTCON. F5=0;
TXSTA=36;
RCSTA=144;
SPBRG=25;
PIE1 . RCIE=1;
INTCON. GIE=1;
INTCON. PEIE=1;
TMR0=c u e n t a ;
PORTB=0;
t o t a l I n t =1234;
aux=(& t o t a l I n t ) ;
aux++;
PORTB=0;
f o r ( i =0; i <a r m o n i c o s ; i ++){
262
velocidades [ i ]=0;
}
while (1) {
i f (INTCON. F2==1){
INTCON. F2=0;
TMR0=c u e n t a ;
PORTB++;
t o t a l I n t =32768;
t i e m p o s [ 0 ] = t i e m p o s [ 0 ] + nota ;
t o t a l I n t=t o t a l I n t+v e l o c i d a d e s [ 0 ] * s e n [ t i e m p o s [ 0 ] ] ;
t i e m p o s [ 1 ] = t i e m p o s [ 1 ] + 2 * nota ;
t o t a l I n t=t o t a l I n t+v e l o c i d a d e s [ 1 ] * s e n [ t i e m p o s [ 1 ] ] ;
t i e m p o s [ 2 ] = t i e m p o s [ 2 ] + 3 * nota ;
t o t a l I n t=t o t a l I n t+v e l o c i d a d e s [ 2 ] * s e n [ t i e m p o s [ 2 ] ] ;
PORTA=*aux ;
w h i l e ( PIR1 . TXIF==0){}
TXREG=*aux ;
}
i f ( bandera==2){
agregarNota ( ) ;
bandera =0;
posDato =0;
}
}
}
void i n t e r r u p t ( ) {
i f ( PIR1 . RCIF==1){
u n s i g n e d s h o r t i n t d a t o s R e c i b i d o s=RCREG;
i f ( d a t o s R e c i b i d o s >126) {
f u n c i o n A c t u a l=d a t o s R e c i b i d o s ;
}
else {
d a t o s F u n c i o n [ posDato ]= d a t o s R e c i b i d o s ;
posDato++;
}
i f ( ( posDato==2) | | ( f u n c i o n A c t u a l <144 && posDato==1)) {
bandera =2;
}
}
INTCON. RBIF=0;
}
progPruePrevia/main.c
263
Anexo G, códigos del software ”‘reduce Imágenes”’
Clase ’reduceImagenes.vb’
264