Está en la página 1de 264

Universidad ORT Uruguay

Facultad de Ingenierı́a
Carrera de Ingenierı́a en Electrónica

Instrumento virtual

Entregado como requisito para la obtención del


tı́tulo de Ingeniero en Electrónica

Eliel Hojman - 147325


Isaac Ungerovich - 136216
Tutor: Ing. Gustavo Bellora

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

En la década del 20, con la creación de los primeros sintetizadores comenzó la


creación de música mediante señales eléctricas remplazando los clásicos instrumen-
tos. En un principio los sintetizadores no fueron muy populares hasta que en 1970
gracias a los nuevos estilos musicales y al comienzo de la era digital en la música su
popularidad comenzó a crecer a grandes pasos.
En 1982 se crea el formato MIDI, el cual es un estándar de comunicación entre
sintetizadores e instrumentos, este fue un gran avance que permitió incrementar la
popularidad de los sintetizadores.
La gran capacidad de procesamiento de los procesadores y microcontroladores
de la década del 90 y de esta década significó una nueva frontera en el mundo de
la música. Actualmente se puede generar prácticamente cualquier sonido músical
en tiempo real con procesadores extremadamente baratos cuya potencia en décadas
anteriores no se encontraban ni en la imaginación del más optimista de los ingenieros.
La capacidad de los actuales procesadores da lugar a un sin fin de nuevos proyec-
tos e ideas que se hacen viables a un bajo costo. Este proyecto es una de esas, es
un sintetizador que intenta ser un dispositivo que equilibre la gran potencia de los
procesadores de última tecnologı́a con un bajo costo y un uso sencillo pero robusto
y útil.
Se busca crear un dispositivo con una entrada para que se le conecte un ins-
trumento musical y que el dispositivo pueda reproducir las notas musicales que el
usuario toca en el, haciéndolas sonar como las notas de otro instrumento previa-
mente configurado.
Este documento brinda un análisis detallado del estudio realizado, las pruebas
efectuadas, los detalles de la construcción del dispositivo y finalmente una conclusión
del proyecto realizado.

3
Índice

1 Introducción 14

1.1 Conceptos básicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

1.1.1 MIDI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

1.1.2 Instrumento MIDI . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

1.1.3 Instrumentos VSTi . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

1.1.4 Efectos VST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

1.1.5 VST Host . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

1.1.6 Conexión de un dispositivo MIDI a un PC . . . . . . . . . . . . . . 15

1.1.7 Reproducción de un instrumento MIDI conectado al PC . . . . . . 16

1.1.8 Reproducción de un instrumento MIDI con el programa VSTHost


V 1.45 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

1.1.9 Sintetizador MIDI . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

1.2 Objetivo del proyecto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

1.3 Sistemas similares existentes . . . . . . . . . . . . . . . . . . . . . . . . . . 19

1.3.1 Sistemas embebidos similares . . . . . . . . . . . . . . . . . . . . . 19

1.3.2 Generar las funciones de este proyecto con un PC . . . . . . . . . . 20

2 Conceptos avanzados 21

2.1 Instrucciones MIDI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

2.1.1 Canales MIDI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

2.1.2 Modos MIDI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

2.1.3 Instrumentos MIDI . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

2.2 Funcionamiento de los instrumentos VSTi y de los efectos VST . . . . . . . 25

2.2.1 Opciones de proceso . . . . . . . . . . . . . . . . . . . . . . . . . . 25

2.2.2 Implementación de plugins VST y VSTi . . . . . . . . . . . . . . . 25

2.3 Archivos DLL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

2.3.1 Ventajas del uso de librerı́as DLL . . . . . . . . . . . . . . . . . . . 26

2.3.2 Desventajas del uso de librerı́as compartidas DLL . . . . . . . . . . 26

4
2.3.3 Estructura de una DLL de 32 bits . . . . . . . . . . . . . . . . . . . 27

2.3.4 Llamada Estática a un Archivo DLL . . . . . . . . . . . . . . . . . 27

2.3.5 Llamada dinámica a un archivo DLL . . . . . . . . . . . . . . . . . 28

3 Posibles sistemas de implementación 29

3.1 Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

3.2 Utilizar un sistema embebido sin sistema operativo . . . . . . . . . . . . . 29

3.2.1 Interpretación de los comandos MIDI . . . . . . . . . . . . . . . . . 29

3.2.2 Interpretación de Instrumentos VSTi . . . . . . . . . . . . . . . . . 30

3.2.3 Sistemas existentes que interpretan archivos DLL fuera de Windows 30

3.3 Utilizar un Sistema Embebido con Sistema Operativo . . . . . . . . . . . . 31

3.3.1 Sistema Operativo Linux . . . . . . . . . . . . . . . . . . . . . . . . 32

3.3.2 Sistema Operativo Windows . . . . . . . . . . . . . . . . . . . . . . 32

3.3.3 Utilizar sistemas operativos en tiempo real . . . . . . . . . . . . . . 32

3.3.4 Utilizar otros sistemas operativos . . . . . . . . . . . . . . . . . . . 33

4 Análisis de las posibles formas de implementación 34

4.1 Utilizar un sistema embebido sin sistema operativo . . . . . . . . . . . . . 34

4.2 Utilizar un sistema embebido que tenga el sistema operativo Windows . . . 34

4.3 Utilizar un sistema embebido que tenga el sistema operativo Linux . . . . . 34

4.4 Conclusiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

5 Replanteo del proyecto y de su implementación 37

6 Creación de la nota musical 38

6.1 Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

6.2 Componentes de una nota musical . . . . . . . . . . . . . . . . . . . . . . . 38

6.3 Sinusoidales puras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

6.4 Envolvente Global . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

6.5 Envolvente particular a cada frecuencia . . . . . . . . . . . . . . . . . . . . 44

6.5.1 Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

5
6.5.2 Explicación del método para obtener la nota . . . . . . . . . . . . . 45

6.6 Utilización de Samples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

6.7 Limitaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

7 Detección de comandos MIDI 49

8 Prueba previa 50

8.1 Objetivo de la prueba . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

8.2 Construcción del código para la prueba . . . . . . . . . . . . . . . . . . . . 51

8.3 Origen de los datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

8.3.1 Generación de sonidos . . . . . . . . . . . . . . . . . . . . . . . . . 52

8.4 Conversor digital a analógico . . . . . . . . . . . . . . . . . . . . . . . . . . 52

8.5 Análisis de la prueba . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

8.5.1 Operaciones trigonométricas . . . . . . . . . . . . . . . . . . . . . . 53

8.5.2 Multiplicaciones y divisiones . . . . . . . . . . . . . . . . . . . . . . 54

8.5.3 Observación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55

8.5.4 Funcionamiento de la prueba . . . . . . . . . . . . . . . . . . . . . . 55

8.5.5 Conclusiones de la prueba . . . . . . . . . . . . . . . . . . . . . . . 55

9 Selección del sistema embebido 57

9.1 Selección de la arquitectura del sistema embebido . . . . . . . . . . . . . . 57

9.2 Selección del fabricante del DSP . . . . . . . . . . . . . . . . . . . . . . . . 57

9.2.1 Productos adicionales . . . . . . . . . . . . . . . . . . . . . . . . . . 57

9.2.2 Soporte y repuestos en Uruguay . . . . . . . . . . . . . . . . . . . . 58

9.2.3 Soporte en lı́nea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

9.2.4 Presentación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

9.2.5 Experiencia previa . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

9.3 Selección del modelo de DSPic . . . . . . . . . . . . . . . . . . . . . . . . . 60

10 Arquitectura externa del sintetizador 62

6
11 Instrucciones de uso del sintetizador 63

11.1 Reproducción de instrumentos . . . . . . . . . . . . . . . . . . . . . . . . . 63

11.2 Utilización de los efectos digitales . . . . . . . . . . . . . . . . . . . . . . . 63

11.2.1 No aplicar el efecto . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

11.2.2 Efecto eco . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

11.2.3 Efecto trémolo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

11.2.4 Efecto Wah-Wah . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

11.2.5 Efecto limitador ’if’ . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

11.2.6 Efecto limitador ’log’ . . . . . . . . . . . . . . . . . . . . . . . . . . 65

11.2.7 Efecto vibrato . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

11.3 Utilización del efecto analógico . . . . . . . . . . . . . . . . . . . . . . . . 65

11.4 Cambio y creación de instrumentos . . . . . . . . . . . . . . . . . . . . . . 65

12 Arquitectura del Sistema 66

12.1 Análisis de la arquitectura externa . . . . . . . . . . . . . . . . . . . . . . 66

12.2 Entradas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

12.2.1 MIDI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

12.2.2 RS-232 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

12.2.3 Efectos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

12.3 Arquitectura Interna . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67

12.3.1 I 2 C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68

12.3.2 Generación del Sonido . . . . . . . . . . . . . . . . . . . . . . . . . 69

12.3.3 Generación de efectos . . . . . . . . . . . . . . . . . . . . . . . . . . 69

12.3.4 La necesidad de tantos microcontroladores . . . . . . . . . . . . . . 69

12.3.5 Circuito del sumador analógico . . . . . . . . . . . . . . . . . . . . 70

12.3.6 Saturador Analógico . . . . . . . . . . . . . . . . . . . . . . . . . . 72

12.3.7 Circuitos de la caja de efectos . . . . . . . . . . . . . . . . . . . . . 73

12.3.8 Diseño del PCB y de los circuitos de los DSPic . . . . . . . . . . . . 74

13 Entorno de programación de los DSPic 76

7
14 Explicación del código del Maestro 77

14.1 Funcionamiento del I 2 C . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

14.2 Interrupción MIDI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

14.3 Interrupción PC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

14.4 Funcionamiento del Main . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78

14.4.1 Cambios en las perillas . . . . . . . . . . . . . . . . . . . . . . . . . 78

14.4.2 Función agregarNota() . . . . . . . . . . . . . . . . . . . . . . . . . 78

14.4.3 Función quitarNota() . . . . . . . . . . . . . . . . . . . . . . . . . . 79

14.4.4 Función cargarNuevoPrograma() . . . . . . . . . . . . . . . . . . . 79

15 Explicación del código de los esclavos 80

15.1 Funcionamiento de la comunicación I 2 C . . . . . . . . . . . . . . . . . . . 80

15.1.1 Funcionamiento general . . . . . . . . . . . . . . . . . . . . . . . . 80

15.1.2 Funcionamiento detallado . . . . . . . . . . . . . . . . . . . . . . . 80

15.2 Generación de sonidos de notas . . . . . . . . . . . . . . . . . . . . . . . . 82

15.2.1 Explicación general del funcionamiento . . . . . . . . . . . . . . . . 82

15.2.2 Explicación detallada de las variables . . . . . . . . . . . . . . . . . 83

15.3 Funcionamiento del método agregarN ota() . . . . . . . . . . . . . . . . . . 84

15.4 Funcionamiento del método calcularSample() . . . . . . . . . . . . . . . . 84

15.5 Funcionamiento de los efectos . . . . . . . . . . . . . . . . . . . . . . . . . 86

15.5.1 Funcionamiento general de los efectos . . . . . . . . . . . . . . . . . 86

15.5.2 Explicación del método inicarEf ectos . . . . . . . . . . . . . . . . 87

15.5.3 Funcionamiento del efecto Eco . . . . . . . . . . . . . . . . . . . . . 87

15.5.4 Funcionamiento del efecto trémolo . . . . . . . . . . . . . . . . . . . 88

15.5.5 Funcionamiento del efecto wah-wah . . . . . . . . . . . . . . . . . . 89

15.5.6 Funcionamiento del efecto limitador if . . . . . . . . . . . . . . . . 90

15.5.7 Funcionamiento del efecto limitador logarı́tmico . . . . . . . . . . . 90

15.5.8 Explicación del efecto vibrato . . . . . . . . . . . . . . . . . . . . . 91

15.6 Explicación de las funciones actualizarParametrosEfectos y aplicarEfectos . 91

8
15.7 Funcionamiento del método main . . . . . . . . . . . . . . . . . . . . . . . 92

16 Documentación del programa interfaz 93

16.1 Introducción al manejo del programa interfaz . . . . . . . . . . . . . . . . . 93

16.2 Estructura del programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93

16.3 Aspectos técnicos del programa . . . . . . . . . . . . . . . . . . . . . . . . 93

16.3.1 Lenguaje utilizado . . . . . . . . . . . . . . . . . . . . . . . . . . . 93

16.3.2 Caracterı́sticas extras . . . . . . . . . . . . . . . . . . . . . . . . . . 93

17 Manual de instrucciones del programa interfaz de comunicación 95

17.1 Comenzando con el programa . . . . . . . . . . . . . . . . . . . . . . . . . 95

17.2 Ingreso al modo Manejo de Envolventes . . . . . . . . . . . . . . . . . . . . 95

17.3 Crear un instrumento nuevo . . . . . . . . . . . . . . . . . . . . . . . . . . 95

17.4 Abrir instrumentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96

17.5 Uso de la ventana “Manejo de envolventes” . . . . . . . . . . . . . . . . . . 96

17.5.1 Crear una nueva envolvente de forma simple . . . . . . . . . . . . . 97

17.5.2 Crear envolventes mediante extrapolación de otras envolventes . . . 97

17.5.3 Herramientas útiles para la creación de envolventes . . . . . . . . . 98

17.5.4 Guardar envolventes . . . . . . . . . . . . . . . . . . . . . . . . . . 99

17.5.5 Abrir envolventes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99

17.6 Manejo del modo transmisión de instrumentos . . . . . . . . . . . . . . . . 100

17.7 Ventana Manejo del instrumento . . . . . . . . . . . . . . . . . . . . . . . 101

17.7.1 Barra superior . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101

17.7.2 Editar cada envolvente . . . . . . . . . . . . . . . . . . . . . . . . . 102

17.7.3 Edición común a todas las envolventes . . . . . . . . . . . . . . . . 103

17.7.4 Probar las notas generadas . . . . . . . . . . . . . . . . . . . . . . . 104

17.8 Formato de los archivos para enviarle al sintetizador . . . . . . . . . . . . . 105

18 Explicación de los códigos de Scilab 108

18.1 Introducción a los programas de Scilab . . . . . . . . . . . . . . . . . . . . 108

9
18.2 Programa “graficaFacil” . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108

18.3 Programa “acortaSonido” . . . . . . . . . . . . . . . . . . . . . . . . . . . 108

18.4 Programa “fourierFacilDetectaPicos” . . . . . . . . . . . . . . . . . . . . . 109

18.5 Programa “fourierPartes” . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

18.6 Programa “copiaNota” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111

18.7 Programa “copiaSonido” . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111

18.8 Programa “haceGraficas” . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

18.9 Programa “sinusoidalesPuras” . . . . . . . . . . . . . . . . . . . . . . . . . 113

18.10Programa “envolventeComun” . . . . . . . . . . . . . . . . . . . . . . . . . 113

18.11Observación general . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114

18.12Programas que generan efectos . . . . . . . . . . . . . . . . . . . . . . . . . 114

19 Herramienta para reducir imágenes 115

20 Protocolo para Transmitir un Nuevo Instrumento 116

21 Posibles mejoras y complementos 118

21.1 Posibles mejoras en el sintetizador . . . . . . . . . . . . . . . . . . . . . . . 118

21.1.1 Alimentación con una única fuente . . . . . . . . . . . . . . . . . . 118

21.1.2 Aumento de los controles internos . . . . . . . . . . . . . . . . . . . 118

21.1.3 Detección automática de esclavos que están fallando . . . . . . . . . 118

21.1.4 Mejora de la frecuencia de salida . . . . . . . . . . . . . . . . . . . 119

21.1.5 Modificación dinámica de los instrumentos . . . . . . . . . . . . . . 120

21.1.6 Utilizar interfaz USB . . . . . . . . . . . . . . . . . . . . . . . . . . 120

21.1.7 Almacenar múltiples instrumentos virtuales en un solo sintetizador 120

21.1.8 Múltiples instrumentos conectados a un solo sintetizador . . . . . . 121

21.1.9 Ampliar la cantidad de tipos de instrumentos que se pueden generar 121

21.1.10 Opción de grabación . . . . . . . . . . . . . . . . . . . . . . . . . . 121

21.1.11 Reproducir archivos .midi . . . . . . . . . . . . . . . . . . . . . . . 122

21.2 Posibles mejoras en el software de PC . . . . . . . . . . . . . . . . . . . . . 122

10
21.2.1 Mejora en el sistema de generación automática de notas . . . . . . . 122

21.2.2 Mejora en la velocidad de funcionamiento . . . . . . . . . . . . . . 122

21.2.3 Cambiar los parámetros a editar de un instrumento . . . . . . . . . 123

21.2.4 Agregar un conversor de instrumentos VSTi al formato del sintetizador123

21.2.5 Integrar todos los programas y aplicaciones en un solo programa . . 123

22 Explicación del contenido del CD adjunto 124

22.1 Carpeta “Imitaciones Generadas con los Distintos Métodos” . . . . . . . . 124

22.1.1 Carpeta “Imitaciones Generadas Mediante Sinuidales Puras” . . . . 124

22.2 Carpeta “Imitaciones Generadas Mediante Envolvente Global” . . . . . . . 124

22.3 Carpeta “Imitaciones Generadas Mediante Envolvente Individual” . . . . . 124

22.3.1 Carpeta “Imitaciones Generadas Con El Programa CopiaSonido” . 124

22.4 Carpeta “Software Diseñado” . . . . . . . . . . . . . . . . . . . . . . . . . 125

22.4.1 Carpeta “Instrumentos Creados” . . . . . . . . . . . . . . . . . . . 125

22.4.2 Carpeta “Ejemplos de Envolventes” . . . . . . . . . . . . . . . . . . 125

23 Conclusiones 126

Bibliografı́a 127

Anexos 131

Anexo A, teorema de Nyquist-Shannon . . . . . . . . . . . . . . . . . . . . . . . 131

Anexo B, códigos de los DSPic esclavos . . . . . . . . . . . . . . . . . . . . . . . 133

Clase ’CargarProgSlave.c’ . . . . . . . . . . . . . . . . . . . . . . . . . . . 133

Clase ’CargarProgSlave.h’ . . . . . . . . . . . . . . . . . . . . . . . . . . . 135

Clase ’escribirEnMemoriaConst.s’ . . . . . . . . . . . . . . . . . . . . . . . 136

Clase ’dac.c’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137

Clase ’dac.h’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138

Clase ’efectos.c’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139

Clase ’efectos.h’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143

Clase ’call2.s’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144

11
Clase ’I2C.c’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145

Clase ’I2C.h’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148

Clase ’iniciarReloj.c’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149

Clase ’iniciarReloj.h’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150

Clase ’manejoPuertos.c’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151

Clase ’manejoPuertos.h’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152

Clase ’tablas.h’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153

Clase ’Main.c’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154

Anexo C, códigos del DSPic master . . . . . . . . . . . . . . . . . . . . . . . . . 165

Clase ’adc.c’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165

Clase ’adc.h’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166

Clase ’cargarPrograma.c’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167

Clase ’cargarPrograma.h’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169

Clase ’I2C.c’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170

Clase ’I2C.h’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174

Clase ’mapearP.c’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175

Clase ’mapearP.h’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176

Clase ’serie.c’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177

Clase ’serie.h’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180

Clase ’main.c’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181

Anexo D, códigos del software para diseñar instrumentos . . . . . . . . . . . . . 186

Instroducción’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186

Clase ’crearInstrumento.vb’ . . . . . . . . . . . . . . . . . . . . . . . . . . 187

Clase ’crearMascara.vb’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202

Clase ’inicio.vb’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214

Clase ’instrumento.vb’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215

Clase ’mascara.vb’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218

Clase ’metodosComunes.vb’ . . . . . . . . . . . . . . . . . . . . . . . . . . 223

Clase ’nota.vb’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225

12
Clase ’pideEnteros.vb’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228

Clase ’pideUnDouble.vb’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231

Clase ’transmitir.vb’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233

Clase ’ventanaInicial.vb’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242

Anexo E, códigos de scilab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244

Función ’acortaSonido’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244

Función ’copiaNota’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245

Función ’copiaSonido’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246

Función ’fourieFacilDetectaPicos’ . . . . . . . . . . . . . . . . . . . . . . . 247

Función ’fouriePartesPicos’ . . . . . . . . . . . . . . . . . . . . . . . . . . . 248

Función ’graficaFacil’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250

Función ’haceGraficas’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251

Efecto chorus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254

Efecto delay . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255

Efecto eco . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256

Efecto flanger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257

Efecto limitador if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258

Efecto limitador logarı́tmico . . . . . . . . . . . . . . . . . . . . . . . . . . 259

Efecto vibrato . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260

Efecto Wah-Wah . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261

Anexo F, códigos de la prueba previa . . . . . . . . . . . . . . . . . . . . . . . . 262

Clase ’main’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262

Anexo G, códigos del software ”‘reduce Imágenes”’ . . . . . . . . . . . . . . . . 264

Clase ’reduceImagenes.vb’ . . . . . . . . . . . . . . . . . . . . . . . . . . . 264

13
1 Introducción

1.1 Conceptos básicos

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.

1.1.2 Instrumento MIDI

Un instrumento MIDI es un instrumento con la capacidad de generar señales MIDI que


indiquen lo que el usuario está tocando en él. Los instrumentos MIDI por lo general
disponen de tres puertos MIDI llamados MIDI IN, MIDI OUT y MIDI THROW. El
puerto MIDI OUT es una salida que emite los mensajes MIDI correspondientes a lo que
el usuario está tocando en él. El puerto MIDI IN recibe instrucciones MIDI generados por
otro dispositivo. El puerto MIDI THROW es un puerto que sirve para conectar varios
instrumentos en serie porque genera una salida que es igual a lo que se recibe por el
puerto de entrada MIDI IN más los mensajes MIDI correspondientes a lo que el usuario
está tocando en el instrumento. Para conectar varios instrumentos MIDI a un dispositivo
que interprete las instrucciones hay varias formas, las más conocidas son:

a) En paralelo, es decir se conecta cada instrumento al dispositivo interpretador en


una entrada distinta de éste.

b) En serie, se utiliza cuando el dispositivo que interpretará los comandos tiene


menos entradas MIDI que la cantidad de instrumentos que se desea que este maneje.
Supongamos que se quieren conectar los instrumentos A, B y C a un mismo intérprete y
por la misma entrada de éste. Para eso hay que conectar el MIDI OUT del A al MIDI IN
del B, el MIDI THROW del B al MIDI IN del C y finalmente del MIDI THROW del C a
la entrada del intérprete, de esta forma el intérprete recibe las instrucciones generadas por
los tres instrumentos. Este método tiene la desventaja de que genera latencia (retardo)
en la salida de audio, especialmente en el primer instrumento de la serie (en el ejemplo
anterior del ‘A’).

1.1.3 Instrumentos VSTi

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”.

1.1.4 Efectos VST

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.

1.1.5 VST Host

Son programas encargados de interpretar los instrumentos VSTi, permitirle al usuario


asignar a cada entrada MIDI un instrumento VSTi y modificar los valores de dicho ins-
trumento entre otras cosas.

1.1.6 Conexión de un dispositivo MIDI a un PC

Para hacer sonar un instrumento MIDI en un PC se precisa:

a) Un instrumento con salida MIDI, un adaptador de MIDI a USB y un PC con


entrada USB. En este caso se conecta la salida MIDI del instrumento al adaptador y del
adaptador al PC.

La imagen 1 muestra el diagrama de conexión de esta opción.

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.

c) Un instrumento con salida MIDI y un PC con entrada MIDI. En este caso se


conecta directamente el instrumento al PC.

Imagen 1: Imagen de la conexión opción “a”

Imagen 2: Foto de los puertos de conexión de un piano MIDI

1.1.7 Reproducción de un instrumento MIDI conectado al PC

Se abre el VST Host, se abren los instrumentos VSTi que se deseen asociar a cada entrada
MIDI y se los asocia.

1.1.8 Reproducción de un instrumento MIDI con el programa VSTHost V


1.45

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.

3) Seleccionar el instrumento VSTi que se desea asociar a la entrada MIDI, para


eso en el menú “File” seleccionar la opción “New plugin” y seleccionar el instrumento
deseado.

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.

5) Para cambiar el programa de funcionamiento del instrumento, en el menú “Plu-


gin” seleccionar el programa deseado.

1.1.9 Sintetizador MIDI

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.

Un sintetizador MIDI es un dispositivo con entradas MIDI y controles lineales y


rotatorios y con una o más salidas de audio y MIDI. Su principal función es recibir los
comandos MIDI y emitir por su salida de audio el sonido generado por las instrucciones
MIDI recibidas. Los controles sirven para modificar parámetros del sonido generado.

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.

El objetivo es hacer un sistema que se venda con un instrumento VSTi especı́fico


instalado, luego el usuario puede conectarlo a su instrumento MIDI y la salida a un ampli-
ficador y tocar el instrumento. También puede modificar los parámetros del instrumento
mediante los controles.

En el mercado ya existen dispositivos más complejos que el de este proyecto, la idea


es hacer un sistema sencillo de manejar para el usuario “que enchufe el instrumento y
suene directo”. Los dispositivos más complejos que existen en el mercado no sólo que
son caros, sino que también dada la gran cantidad de opciones que estos tienen, terminan
resultando para ciertos usuarios, sobre todo los principiantes y amateurs, un problema
lograr configurarlos y descargarle desde el PC los instrumentos deseados.

Esquemáticamente el conexionado del sistema deberı́a ser de la siguiente manera:

Imagen 3: Esquema del objetivo del proyecto

18
1.3 Sistemas similares existentes

1.3.1 Sistemas embebidos similares

Actualmente en el mercado existe un producto similar al que se busca crear en este


proyecto, el producto se llama “SMPro Audio V-Machine Standalone VST Player”, se
muestra en la imagen 4.

Imagen 4: SMPro Audio V-Machine Standalone VST Player

Su sitio web es: http://pro-audio.musiciansfriend.com/product/SM-Pro-Audio-Vmachine-


Stand-Alone-VST-Player?sku=241884.

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.

La gran cantidad de opciones de manejo y funciones que este dispositivo contiene


lo transforma en un dispositivo ideado para compositores de música y DJ´s. Un usuario
principiante o amateur desea hacer sonar el dispositivo sin tener que hacer grandes configu-
raciones. Tampoco desea tener que estar creando controles virtuales para poder modificar
parámetros de su instrumento virtual. Si a esto se le suma que muchas veces la gran canti-
dad de opciones confunde al usuario inexperto provocando que lo que si quiere configurar
no sepa como, se concluye que la V-Machine no es óptima para el usuario principiante o
amateur que desea “conectar el instrumento y que suene” (Plug and Play).

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.

1.3.2 Generar las funciones de este proyecto con un PC

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.

Si bien comprar un PC para remplazar al dispositivo que se busca realizar es muy


caro, probablemente la mayorı́a de los posibles usuarios de este producto ya tengan ac-
ceso a una, y en el caso de que tal PC no tenga una entrada MIDI, ya vimos que hay
adaptadores MIDI-USB muy económicos. Por lo que el precio no será una ventaja frente
esta otra opció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

2.1 Instrucciones MIDI


3
Como se mencionó anteriormente las instrucciones MIDI son comandos generados por
los instrumentos MIDI que se encargan de indicar las instrucciones de como generar un
sonido en base a lo que el usuario toca en el instrumento. A continuación se indica en
detalle como son estas instrucciones.

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.

A su vez, los mensajes de estado se dividen en dos grupos: mensajes de canal y


mensajes de sistema. Los mensajes de canal son procesados por un dispositivo especı́fico,
mientras que los mensajes de sistema son procesados por todos los equipos.

La tabla 1 contiene todos los mensajes disponibles:

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.

2.1.1 Canales MIDI

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

Tabla 1: Tabla de comandos MIDI

MIDI puede direccionar hasta 16 canales (también llamados voces, o instrumentos);


por ello, al instalar el sistema MIDI será necesario asignar un número de canal para cada
dispositivo.

2.1.2 Modos MIDI

Dentro del sistema MIDI, se decidió crear una serie de diferentes modos de funcionamiento,
cada uno con ciertas caracterı́sticas.

Monofónico: Un instrumento monofónico sólo puede reproducir una nota simultáneamente.


Es decir, para reproducir una nueva nota debe primero dejar de sonar la anterior.
Por ejemplo, los instrumentos de viento son monofónicos, ya que sólo reproducen
un único sonido a la vez.

Polifónico: Un instrumento polifónico puede reproducir varias notas simultáneamente.


Un ejemplo es un piano, que puede formar acordes por medio de hacer sonar dos o
más notas a la vez.

A continuación se muestra la tabla de modos de funcionamiento:

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.

2.1.3 Instrumentos MIDI

La tabla 2 muestra los 128 instrumentos de la especificación estándar de MIDI, también


conocidos como GM o “General Midi”

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)

Tabla 2: Instrumentos MIDI

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.

El código fuente de un instrumento VSTi o de un efecto VST es independiente de


la plataforma de trabajo, pero el compilado depende del sistema operativo en el cual el
VST Host va a ejecutarse.

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.

2.2.1 Opciones de proceso

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.

2.2.2 Implementación de plugins VST y VSTi

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.

Cada una de las clases mencionadas anteriormente tiene su contraparte en la versión


2.3 del SDK en dicha contraparte se le agrega una ‘X’ al final del nombre de la clase.

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.

2.3.1 Ventajas del uso de librerı́as DLL

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.

3. Al reducirse el tamaño del código, se reduce también el tiempo de compilación.

4. Otra consecuencia del segundo punto es el ahorro de espacio en el disco duro.

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.

2.3.2 Desventajas del uso de librerı́as compartidas DLL

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.

2.3.3 Estructura de una DLL de 32 bits

Un archivo DLL consta básicamente de tres partes:

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.

2.3.4 Llamada Estática a un Archivo DLL

Este tipo de llamada consiste en cargar el archivo DLL directamente en el momento


de compilación del programa. Esto tiene como consecuencia que se incluye el código
de la librerı́a dentro del ejecutable. Como la librerı́a forma parte del ejecutable, no
hace falta cargarla más adelante, se carga en seguida que se ejecuta la aplicación. Como
consecuencia las funciones de la DLL pueden ser llamadas dentro del código del ejecutable
como cualquier otra función del programa.

Ventajas de la Llamada Estática a un Archivo DLL :

1. Las librerı́as se cargan automáticamente cuando se carga el programa.


2. El enlace se produce en tiempo de compilación, por lo que el uso del archivo DLL
no enlentece la ejecución del programa.
3. Las funciones del archivo DLL son llamadas como cualquier otra función del pro-
grama.

Desventajas de la Llamada Estática a un Archivo DLL :

1. El programa incluye el código de cada DLL que utiliza.


2. Debe incluirse el código de la DLL en cada ejecutable del programa que la utilice.
3. Como consecuencia de los dos puntos anteriores, el tamaño de los programas au-
menta.
4. Se genera una repetición del código de los archivos DLL.
5. Los archivos DLL permanecen en memoria durante todo el tiempo que se este eje-
cutando el programa que los usa.

27
2.3.5 Llamada dinámica a un archivo DLL

Este método consiste en llamar al archivo DLL durante el tiempo de ejecución en el


momento en que alguna de sus funciones es requerida. El archivo DLL es cargado en
memoria únicamente si el programa lo precisa. Para que este método sea efectivo es
necesario que el programa que requiere el uso de la DLL, luego de utilizarla (en tiempo
de ejecución) libere la memoria RAM eliminando el archivo DLL de ésta.

Ventajas de la llamada dinámica a un archivo DLL :

1. La librerı́a tiene almacenado su código en un archivo DLL y no en el código del


programa que la requiere.

2. No requiere incluir la librerı́a DLL en cada ejecutable que la utilice.

3. Como consecuencia de los primero dos puntos, el tamaño de los ejecutables es menor.

4. Se evitan repeticiones del código del archivo DLL.

5. El código de las librerı́as DLL se puede cargar y descargar de la memoria según si


este es requerido o no.

6. Se aumenta la independencia entre el archivo DLL y el programa que lo requiere.

Desventajas de la llamada dinámica a un archivo DLL :

1. La librerı́a es cargada en tiempo de ejecución, lo que enlentece el programa.

2. Realizar el enlace entre un programa y una librerı́a DLL en tiempo de ejecución


complica su manejo y aumenta las probabilidades de errores.

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).

En esta sección se presentarán y desarrollarán los tipos de sistemas embebidos exis-


tentes que se podrı́an utilizar para desarrollar el sistema.

3.2 Utilizar un sistema embebido sin sistema operativo

La idea de esta posible implementación es hacer el proceso de señales MIDI y de los


archivos VSTi con sistemas embebidos sin la capacidad de cargar un sistema operativo,
sistemas embebidos que directamente se programan en ’C’, ’C++’ o similar.

Esta forma de implementación tiene la ventaja de ser económica y de que no se


necesitarı́a recurrir a sistemas hechos por terceros (VST Host, etc) para poner en marcha
el proyecto, son de menor tamaño que otros sistemas embebidos y están diseñados para
funcionar mucho tiempo sin reiniciarse en condiciones no óptimas (de ruido, relieve, clima,
etc).

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.

3.2.1 Interpretación de los comandos MIDI

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

El objetivo de este proyecto es que el sistema embebido a armar tenga la capacidad de


interpretar instrumentos VSTi, que como se mencionó en su mayorı́a se encuentran en
formato DLL exclusivo de Windows, si vamos a querer utilizar estos mismos archivos
en una plataforma sin sistema operativo se necesitará generar un programa que logre
interpretarlos.

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.

La documentación sobre el funcionamiento interno de un archivo DLL, la estructura


en la cual las funciones de un archivo DLL son almacenadas y sobre el código assembler
de Windows es muy escasa lo que también dificulta la tarea de lograr interpretar los
instrumentos VSTi fuera de Windows.

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.

3.2.3 Sistemas existentes que interpretan archivos DLL fuera de Windows

Se procedió a buscar si ya ha habido un caso en el que se haya logrado ejecutar un archivo


DLL cualquiera fuera de la plataforma Windows, en caso de encontrarlo quizás se podrı́a
realizar lo mismo con los archivos VSTi.

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.

Sistemas que permiten acceso al código binario de un archivo DLL

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.

También existen programas como el txtScani y el exeScope que básicamente son


muy parecidos al FlexHex, solo que estos también tienen la posibilidad de buscar cadenas
de texto y mapas de bits dentro del archivo DLL, son muy comúnmente usados para poder
cambiar de idioma programas, dado que se busca por ejemplo la cadena de texto “File”
y se la remplaza por la cadena “Archivo”.

Otra función de estos programas mencionados es interpretar la cabecera del archivo


DLL permitiendo conocer los encabezados de las funciones que un archivo DLL contiene
y también cuales son las librerı́as que un DLL necesita importar. La mayorı́a de los
instrumentos VSTi utilizan aproximadamente 100 funciones de archivos DLL de Windows,
y necesitan importar 12 librerı́as DLL, entre ellas:

WINMM.dll: Contiene funciones de audio de bajo nivel.

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.

USER32.dll: Contiene funciones para la interfaz con el usuario.

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).

Algunos instrumentos VSTi precisan importar más librerı́as.

3.3 Utilizar un Sistema Embebido con Sistema Operativo

En esta sección se va a analizar la opción de utilizar como el procesador central para


la solución del proyecto un sistema embebido con la capacidad de cargar un sistema
operativo. A continuación se verán las posibles opciones de sistemas operativos.

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.

Como ventajas encontramos que Linux es de libre distribución, lo que disminuirı́a el


costo del nuestro producto, punto que encontramos muy favorable. Como contrapartida
la idea de usar un emulador consumirı́a muchos recursos del sistema y no funcionarı́a tan
bien como correr un Windows embebido directamente.

3.3.2 Sistema Operativo Windows

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.

3.3.3 Utilizar sistemas operativos en tiempo real

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.

A diferencia de la mayorı́a de los otros sistemas operativos, los sistemas operativos


en tiempo real, cambian de tarea por interrupciones de reloj y por evento. Este diseño de
compartición de tiempo gasta más tiempo del procesador en cambios de tarea innecesarios.
Sin embargo, da una mejor ilusión de multitarea.

3.3.4 Utilizar otros sistemas operativos

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.

Dado que no encontremos ninguna información de un sistema operativo especial para


manejar instrumentos musicales, entradas midi, etc, no consideramos como opción válida
utilizar otro sistema operativo. Esto se debe a que ningún otro sistema operativo tiene
las facilidades que tiene Windows para el tipo de aplicaciones que se desea desarrollar
y en caso de renunciar a estas facilidades para utilizar un sistema operativo gratuito
utilizaremos Linux.

33
4 Análisis de las posibles formas de implementación

4.1 Utilizar un sistema embebido sin sistema operativo

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.

La interpretación de instrumentos VSTi requiere la capacidad de interpretar archivos


DLL que, como reflejan los casos mencionados anteriormente, no consideramos que sea
posible hacerlo sin el sistema operativo Windows o su emulador. En caso de optar por
esta opción se deberá renunciar a la posibilidad de reproducir los VSTi.

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.

4.2 Utilizar un sistema embebido que tenga el sistema operativo


Windows

Como ya se mencionó anteriormente, Windows es el sistema ideal para la ejecución de


un VST Host. En el mercado de sistemas embebidos no pasa lo mismo, dado que la
gran mayorı́a están desarrollados para ser cargados con Linux. En Internet hay muchas
compilaciones de Linux para usar en sistemas embebidos, mientras que, dado que Windows
no es gratuito, la cantidad de compilaciones es menor. Si nos concentráramos únicamente
en la confiabilidad del sistema embebido, Linux nos da muchas más garantı́as pero no en
los puntos que nuestro sistema necesita, MIDI y reproducción de archivos DLL.

Hacer el sintetizador en un sistema operativo con sistema operativo “Windows”, es


muy similar a ejecutar las aplicaciones en un PC, razón por la cual no hay garantı́as de
que los retardos que se generan en un PC puedan ser mejorados.

4.3 Utilizar un sistema embebido que tenga el sistema operativo


Linux

Uno de los problemas de la reproducción de instrumentos virtuales desde un PC es la


latencia de estos, es decir el retardo entre el tiempo en que la tecla es oprimida y que el
sonido se genera. Latencias de hasta 5 ms son consideradas buenas, mientras que latencias
de 20ms o más ya son altas.

En el caso de optar por Linux se deberá cargarle también el Wine, y encima de


este correr el VST Host. Todas estas capas se van a ver reflejadas en la latencia del

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.

Un sistema embebido con sistema operativo no es más barato que un PC económico,


que cumple ampliamente los requisitos de los programas Host de VST. La diferencia de
precio se acentúa más teniendo en cuenta que la opción de utilizar un sistema embebido
con Linux esta descartada, por lo que habrı́a que utilizar Windows y pagar la licencia.
De esta forma queda eliminada también la opción de utilizar un sistema embebido con
Windows.

Por estas razones se decidió renunciar a la interpretación de instrumentos VSTi y


desarrollar nuestro dispositivo sin sistema operativo.

Respecto a los instrumentos virtuales, decidimos desarrollar nuestro propio lenguaje


de instrumentos virtuales, junto con un software que permita la conexión con el sinteti-
zador que realicemos y también la creación de instrumentos. Una idea es dejar la puerta
abierta, para que quizás en un futuro si se decide continuar con el proyecto se pueda crear
un software que permite la conversión del formato VSTi al formato que nosotros hayamos
creado.

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.

Se plantea como objetivo adicional hacer un software de PC, capaz de comunicarse


con el sintetizador y con la capacidad de transmitirle nuevos instrumentos. Dicho software
también debe contar con una herramienta de fácil manejo que permita al usuario generar
instrumentos.

37
6 Creación de la nota musical

6.1 Introducción

Una vez que se renunció a la interpretación de instrumentos VSTi, es necesario desar-


rollar un nuevo protocolo para la imitación de instrumentos musicales. En esta sección
buscamos imitar sonidos de distintos instrumentos, para poder determinar la forma en la
cual nuestro sistema embebido imitará instrumentos, poder determinar las caracterı́sticas
fundamentales del sonido de un instrumento y saber los parámetros a considerar en nues-
tro formato de instrumentos virtuales.

6.2 Componentes de una nota musical


6
El sonido, en combinación con el silencio es la materia prima de la música. A lo largo de
la historia el ser humano ha inventado una serie de reglas para ordenarlo hasta construir
un tipo de lenguaje musical.

Podemos diferenciar en un sonido cuatro propiedades básicas:

ˆ Altura — Frecuencia fundamental de onda

ˆ Intensidad — Amplitud de onda

ˆ Timbre — Frecuencias armónicas de la onda

ˆ Duración — Tiempo de vibración

El timbre es lo que caracteriza a un instrumento, la voz propia de este. Es la que


permite distinguir entre la misma nota producida por dos instrumentos distintos, esto se
debe a que cada cuerpo sonoro vibra de una forma distinta.

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.

Caracterı́sticas externas - Envolvente

Las caracterı́sticas externas se combinan para formar la envolvente. Llamaremos


envolvente a evolución temporal en amplitud del sonido generado. Una envolvente por lo
general se descompone en los siguientes cinco parámetros:

ˆ Ataque: Es el tiempo (o porcentaje del tiempo total) de entrada. Lo que tarda en


llegar a su máximo volumen el sonido luego de haber sido ejecutado.

ˆ Mantenimiento: Es el tiempo (o porcentaje del tiempo total) en el cual el sonido


se mantiene en su máximo volúmen.
6
Wikipedia en español: Sonido

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.

En el caso de los instrumentos de viento, como por ejemplo la trompeta, la envolvente


es controlada por el músico, marcando la intensidad del sonido con la fuerza del soplido, al
detenerse el sonido se apaga súbitamente por completo. En los instrumentos de cuerdas
se obtiene un resultado distinto, luego de haber rasgueado la cuerda de guitarra esta
continuará sonando cada vez con una intensidad menor hasta finalmente desaparecer. La
envolvente no sólo se diferencia entre familias de instrumentos, la variación de intensidad
en las notas de un piano también difiere a las de un banjo e incluso a las notas del mismo
piano, una nota grave tendrá un envolvente distinta a una aguda.

Caracterı́sticas internas - Descomposición en frecuencias

Las caracterı́sticas externas son más fáciles e intuitivas de reconocer, detectar la


intensidad del sonido no es algo que a nuestro oı́do le cueste hacer, pero identificar la
frecuencia de la nota que se esta escuchando es algo que incluso a los oı́dos entrenados les
puede resultar una tarea complicada. Para lograr este objetivo se utilizará la transformada
de Fourier.

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.

Imagen 5: Gráfica de la transformada de Fourier del sonido de una guitarra

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.

Otra observación es que contrario a la intuición, los armónicos de mayor frecuencia


y más alejados de la frecuencia fundamental no tienen necesariamente menor magnitud
que los armónicos más cercanos, en la gráfica se observa como, por ejemplo, el sexto pico
es de mayor magnitud que el quinto e incluso que el cuarto.

Como primera prueba se simulará el sonido mediante la sumatoria de ondas si-


nusoidales de las mismas frecuencias que las principales del sonido que se desea imitar
y multiplicando por un parámetro cada sinusoidal tal que las frecuencias que son más
influyentes en el sonido original también lo sean en la imitación.

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.

La tabla 3 muestra en su segunda columna las frecuencias en las que se encuentran


los “picos” más relevantes del sonido a imitar. En la tercer columna muestra la magnitud
de su influencia normalizada, es decir que le asigna el valor 1 a la frecuencia fundamental
y si, por ejemplo, otra frecuencia tiene la mitad de la altura de la fundamental en su FFT
(fast Fourier transform, transformada rápida de Fourier) se le asigna el número 0,5.

El procedimiento de generación del sonido consiste en generar un sonido en Scilab


que sea la suma de ’n’ funciones sinusoidales cada una con una de las ’n’ frecuencias que
se desea representar multiplicada por la magnitud que su frecuencia tiene asociada.

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

Tabla 3: Tabla de armónicos

Imagen 7: Gráfica de la transformada de Fourier del sonido generado

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.

También se probó incrementar el número de armónicos a incluir en la generación


del sonido, se llegó a incluir hasta 100 armónicos, y aún ası́ el sonido no llegó a ser una
buena copia del sonido original. Esta forma de imitar sonidos no sólo no genera buenas
imitaciones, si no que también las señales que genera son señales periódicas, estas señales
pueden ser en su principio similares a las que se desea imitar pero la periodicidad no les

42
permite ‘apagarse’ como hacen las señales que deseamos imitar.

6.4 Envolvente Global

Luego de haber obtenido la composición interna de la nota, se procedió a agregarle una


envolvente global en la cuál la variación de intensidad sea para todas las frecuencias la
misma. En la imagen 8 se muestra la gráfica de la amplitud del sonido original junto con
las lı́neas que luego se modelarán para hallar la envolvente.

Imagen 8: Gráfica de la amplitud de un sonido de guitarra

Para evitar grandes aproximaciones, se modelo la envolvente con seis distintas rectas.

El ataque es representado por la recta OA. El Decaimiento por la AB. El Sosten-


imiento por las rectas CD y DE, y por último las dos rectas DE y EF representarán la
relajación.

Punto Magnitud (normalizada) Tiempo (s)


A 1 0,062
B 0,49 0,21
C 0,41 0,40
D 0,45 0,50
E 0,27 0,63
F 0 4,9
Tabla 4: Tabla de Puntos

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

Para generar automaticamente las imitaciones se utilizó la aplicación de Scilab “en-


volventeComun”. Algunas imitaciones realizadas se encuentran en el cd adjunto.

El sonido obtenido anteriormente si se asemeja bastante a una cuerda de guitarra,


pero todavı́a no es suficientemente parecido. La nota generada, a pesar de tener una
envolvente, suena de forma monótona, la variación en la amplitud no fue suficiente para
darle el realismo que se necesita.

6.5 Envolvente particular a cada frecuencia

6.5.1 Introducción

Al obtener este no tan satisfactorio resultado se decidió probar realizar la transformada


de Fourier en lugar de para toda la duración de la nota, para pequeños pedazos, de esta
forma se puede observar como es que varı́an las magnitudes de las frecuencias a lo largo del
tiempo. Más adelante se nos mencionó que este procedimiento se utiliza de forma regular
para determinar los cambios de frecuencia que tiene una señal a lo largo del tiempo y se

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

6.5.2 Explicación del método para obtener la nota

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.

El resultado de este tipo de imitación fue considerando satisfactorio y será éste el


que va a ser utilizado en nuestro dispositivo.

Imagen 11: Gráfica de las envolventes para las frecuencias más influyentes

6.6 Utilización de Samples

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.

Se considera implementar, o por lo menos dejar la ventana abierta para que en un


futuro se pueda implementar, la opción de editar las notas “en vivo”. Esto significa que
una vez que ya se ha cargado el programa con el instrumento virtual, se pueda editar cierta
nota especı́fica en el momento, variando sus armónicos y algunas de sus envolventes, esto
serı́a imposible en caso de que se utilizarán los samples.

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

Imagen 13: Descomposición en frecuencias de instrumentos de viento y cuerda

En la imagen 13 se observa que, en primer lugar que al tener menos armónicos y


picos más pronunciados los instrumentos de viento son más fáciles de imitar que los de
cuerda, por otro lado en el caso del violı́n se nota claramente y en el del oboe también
pero no de forma tan acentuada que se ha formado una base de frecuencias alrededor de
las frecuencias armónicas. Estos sonidos son más difı́ciles de imitar en nuestro sistema, y
será la importancia que tengan estas frecuencias circundantes las que determinarán lo fiel
que será la imitación realizada por el sintetizador.

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.

Como se mencionó anteriormente, el puerto MIDI transmite sus datos en serie a


una velocidad de 31250 bits/seg y su protocolo para la transmisión de datos es igual
al protocolo de transmisión por el puerto serie. Por esta razón para comunicar un mi-
crocontrolador con un instrumento MIDI solo es necesario un MAX232 al igual que para
comunicarlo con un PC. El circuito utilizado es el indicado en la hoja de datos del MAX232
como circuito recomendado de uso.

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

8.1 Objetivo de la prueba

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.

Imagen 14: Circuito JDM

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.

El objetivo de esta prueba fue diseñar un sistema con la capacidad de interpretar


comandos MIDI de activación y desactivación de notas, junto con la intensidad con la que

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.

8.2 Construcción del código para la prueba

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.

En el código se puede observar como el programa primero ejecuta una configuración


inicial, en la cual configura el puerto serie, los puertos de salida y los timers. Luego ingresa
a un loop que es el encargado de traducir los comandos MIDI a salidas del puerto B. La
forma de actualizarse en caso de un cambio de nota es interruptiva, cada vez que se recibe
por el puerto serie un comando MIDI se almacena y cuando se recibieron suficientes como
para completar una instrucción MIDI, se interpreta y se actualizan los valores de nota y
de velocidad.

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.

8.3 Origen de los datos

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.

8.4 Conversor digital a analógico

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.

En la imagen 15 se muestra el circuito del conversor. El voltaje ‘V0’ corresponde al


bit menos significativo del puerto B, el voltaje ‘V1’ al segundo menos significativo y ası́
sucesivamente. La zona del circuito enmarcada en rojo es un conversor digital a analógico
simple, el circuito por debajo de esta zona es el que se encarga de que el conversor este
combinado con un restador.

52
Imagen 15: Circuito del conversor digital a analógico

8.5 Análisis de la prueba

8.5.1 Operaciones trigonométricas

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.

Como consecuencia decidimos que el resultado de las funciones trigonométricas sea


expresado como short int, es decir asociar a cada valor de las funciones trigonométricas
un número entero entre el -127 y el 127. Aproximamos los resultados de las funciones
seno y coseno como el entero que más se parezca a el verdadero valor del seno o el coseno
multiplicado por 127.

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.

8.5.2 Multiplicaciones y divisiones

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

Para el correcto funcionamiento del sistema de prueba se deberı́a haberle agregado un


filtro analógico a la salida del conversor analógico digital, la razón de esto se detalla en la
sección de fundamentos teóricos, en la explicación del teorema de Nyquist-Shannon. Dado
que esto es solo una prueba inicial no se consideró necesario dado que de todas formas
se logra escuchar la salida del PIC, y las conclusiones de la prueba se pueden obtener sin
tener el sonido de la salida refinado al cien por ciento.

8.5.4 Funcionamiento de la prueba

Dado la frecuencia de funcionamiento, y el tiempo que le toma al microcontrolador realizar


las operaciones, la máxima frecuencia con la cual se pueden obtener un valor digital pronto
para ser colocado en el puerto B del PIC es de 1,1 Khz. Si se fuerza una frecuencia de
salida mayor, el PIC no llegará a realizar las cuentas necesarias, entregando el mismo
valor que en el caso anterior. Con esta frecuencia de salida de acuerdo al teorema de
Nyquist-Shannon que se ve en el anexo A se podrán generar sonidos de hasta 550 Hz.

Si se quisiera aumentar la frecuencia de salida, una de las formas posibles serı́a


conectarle al PIC un cristal externo de 20 Mhz, esto nos permitirı́a generar sonidos de
hasta 2750 Hz. Como se mencionó anteriormente el objetivo de esta prueba no es generar
un sistema útil sino probar las capacidades que tiene un PIC de generar sonidos, razón
por la cual no consideramos necesario conectar el cristal.

8.5.5 Conclusiones de la prueba

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.

No existen microcontroladores de la gama del usado que soporten trabajar a 160


Mhz, y aunque existiesen no serı́an suficientes para reproducir el sonido que esperamos,
porque las señales generadas constarı́an sólo de 2 armónicos. Por esto es necesario cambiar
la familia del microprocesador. Es necesario pasar a una gama de microprocesadores que
le tome menos ciclos de reloj realizar cada instrucción, para ası́ poder trabajar a menos
frecuencia realizando la misma cantidad de operaciones por segundo, el objetivo idea serı́a

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.

En nuestra prueba el microcontrolador calcula una salida, la mantiene constante por


un tiempo, luego calcula otra, la mantiene constante por un tiempo y ası́ sucesivamente.
Como se enuncia en el teorema de Nyquist-Shannon, para poder transformar una señal
digital en analógica, la forma ideal es que la señal de salida se trasmita con impulsos
(deltas de Dyrac), no con pulsos rectangulares, por eso consideramos que es importante
trabajar con un microcontrolador con la capacidad de generar salidas como impulsos. Si
bien se puede generar señales muy similares al impulso con cualquier microcontrolador,
si este no tiene la capacidad de generarlas por hardware es necesario un gasto extra de
tiempo de procesamiento, que es un recurso limitado.

Como se mencionó anteriormente, otra caracterı́stica fundamental que debe tener el


microcontrolador es la capacidad de realizar multiplicaciones y divisiones en menos ciclos
de reloj que el usado para la prueba previa.

Si bien para el sistema final se va a disponer de sistemas con capacidad mucho


mayor, la complejidad de la generación de los sonidos también va a ser mayor. Por
esta razón consideramos que un microcontrolador solo va a poder reproducir una nota
simultáneamente. Va a ser necesario combinar varios microcontroladores para generar
sonidos polifónicos.

56
9 Selección del sistema embebido

9.1 Selección de la arquitectura del sistema embebido

Como se mencionó anteriormente, nuestras prioridades en la búsqueda del sistema embe-


bido son:

(a). Que tenga la capacidad de realizar multiplicaciones en un solo siclo de reloj.

(b). Que tenga la capacidad de trabajar con 16 bits.

(c). Que tenga salida analógica que genere impulsos.

(d). Que tenga puerto serie.

(e). Que pueda hacer operaciones en pocos ciclos de reloj

Encontramos que existen sistemas embebidos diseñados para el manejo de sonidos


que cumplen con estos requisitos, dichos procesadores se llaman DSP (digital signal pro-
cessor). Como estos procesadores cumplen todos los requisitos que establecimos que nues-
tro sistema debe tener y a su vez están optimizados para el manejo de sonidos, decidimos
utilizarlos. Los DSP son un tipo de procesador, no una marca ni un modelo en particular,
el siguiente paso consiste en elegir el modelo y marca deseado.

9.2 Selección del fabricante del DSP

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.

9.2.1 Productos adicionales

Cuando se selecciona y compra un DSP, no solo se compra el procesador, también es


necesario tener un hardware y un software para programarlo. En el caso del DSPic, en
la universidad ORT disponemos de un programador (PICKit 2) que permite programar
todos los modelos de DSPic. Esto nos fue muy útil a la hora de decidirnos por estos DSP,
dado que nos permitió hacer algunas pruebas antes de decidirnos.

Cuando se elige un microprocesador también hay que tener en cuenta el software


para programarlo, si existe algún compilador ‘C’ o similar o si es necesario codificar en
assembler. En este caso la lı́nea DSPic también tiene ventajas dado que el programador

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.

Otras herramientas básicas a la hora de seleccionar un microcontrolador son un


simulador y un depurador (debugger ). Facilita mucho la tarea de encontrar fallas en un
código poder simularlo en la PC con un total control de las variables. La versión gratuita
del MPLAB también cuenta con un simulador de DSPic.

Cuando simulamos programas generados en la prueba previa, la simulación se ejecuta


indicando en que linea del código assembler se encuentra, lo cual no es útil si el código
fue escrito en código ‘C’ y traducido a assembler por un compilador. El simulador de
MPLAB para DSPic indica que linea del código ‘C’ se esta ejecutando en cada momento.

Muchas veces en el simulador un código funciona bien pero en la implementación


real no, esto puede deberse a las caracterı́sticas del DSP que el simulador no simula bien,
a que el DSP no esta recibiendo bien los datos de entrada o a cualquier otra razón. Para
esos y otros casos es muy útil tener un depurador que permita tener un control de las
variables del DSP mientras este ejecuta una aplicación. El PicKit 2 brinda la posibilidad
de depurar toda la lı́nea de procesadores DSP.

Otro aspecto a tener en cuenta de la lı́nea DSPic es que no precisan complejos


circuitos adicionales para comenzar a funcionar. Los circuitos necesarios son básicos y se
encuentran en las hojas de datos.

9.2.2 Soporte y repuestos en Uruguay

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.

En Argentina si hay en venta microprocesadores de la gama DSPic, por lo que no


serán muy complicados de conseguir en caso de necesitar de forma urgente.

9.2.3 Soporte en lı́nea

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.

Nos parece fundamental que el soporte en lı́nea de la empresa de microcontroladores


sea bueno. Un requisito para poder utilizar óptimamente el servicio en lı́nea es que el
hardware y software para programar el DSP sean del mismo fabricante que el DSP, de
esta forma, al enviarle dudas al servicio en lı́nea estos están familiarizados con el lenguaje
de programación y su hardware. Este requisito la lı́nea DSPic lo cumple dado que todas

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.

Imagen 16: Foto del encapsulado SPDIP

59
9.2.5 Experiencia previa

El trabajo en el campo de la electrónica y de los procesadores, muestra que no siempre


es fácil utilizar las funciones de un procesador. En nuestro caso, tanto en el trabajo
realizado en esta tésis como en trabajos anteriores realizados con microcontroladores, ex-
perimentamos que en la mayorı́a de los casos utilizar una función de un sistema embebido
no es sencillo. Utilizar por primera vez una función que nunca utilizamos de un sistema
embebido en la mayorı́a de los casos consume mucho tiempo.

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).

9.3 Selección del modelo de DSPic

Una vez determinado que el microprocesador a utilizar es de la gama DSPic, el siguiente


paso es determinar cual modelo usaremos. Los procesadores DSPic tienen dos familias,
los DSPic33f y los DSPic30f, decidimos utilizar la gama DSPic33f dado que es la más
moderna. Ambas familias están orientadas al proceso de señales digitales, pero la familia
DSPic33f es la más potente.

Dentro de la familia DSPIC33f actualmente hay 92 modelos. Todos estos modelos


pueden hacer multiplicaciones en un ciclo de reloj, trabajar hasta a 40MIPs (millones de
instrucciones por segundo) y trabajan con registros de 16 bits. Lo que no todos tienen es
encapsulado SPDIP y una salida analógica capaz de generar impulsos. Dentro de los que
tienen estas dos caracterı́sticas mencionadas, decidimos optar por los que tengan menor
cantidad de pines, dado que no precisamos de una gran cantidad de entradas y salidas.
De esta forma quedamos con la gama DSPic33fjXXXgp802, dicha gama tiene 28 patas y
cumple todos los requisitos que buscamos que tenga el microcontrolador. Las letras XXX
representan el tamaño de la memoria interna del DSPic expresado en Kb. Inicialmente
comenzamos con el DSPic33fj64gp802, pero luego pasamos al dspic33fj128gp802.

A continuación se enumeran algunas de las caracterı́sticas del DSPic33fj128GP802:

1. Maneja registros de 16 bis.

2. Multiplicaciones de variables enteras en un solo ciclo de reloj.

3. Pueden realizar hasta 40 millones de instrucciones por segundo.

4. Convertidor digital analógico (DAC).

60
5. 10 entradas analógicas.

6. 2 UART (Universal Asynchronous Receiver Transmitter), con velocidad de hasta 1


Megabit/seg.

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.

Muchas de estas caracterı́sticas no fueron consideradas como caracterı́sticas funda-


mentales, pero todas fueron usadas. Si bien el encapsulado utilizado para realizar este
proyecto fue el SPDIP, en caso de querer comercializar el sintetizador se implementarı́a
con el encapsulado QFN dado que es más barato y pequeño. El reducido tamaño del
encapsulado QFN (6mm. por 6mm.) hace que el tamaño no sea un problema pensando
en comercializar el producto.

61
10 Arquitectura externa del sintetizador

En la imagen 17 se muestra un esquema simplificado de la interfaz del dispositivo, consta


de dos entradas, una MIDI que será utilizada para conectar el instrumento MIDI y la
otra es la entrada por la cual se conectará la computadora para cargarle al sistema el
instrumento virtual que se quiera utilizar.

Luego se encuentran siete controladores utilizados para manejar los efectos y por
último una salida de audio.

Imagen 17: Esquema del Dispositivo

62
11 Instrucciones de uso del sintetizador

11.1 Reproducción de instrumentos

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.

11.2 Utilización de los efectos digitales

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.

Imagen 18: Representación de la caja de efectos

Para seleccionar el primer efecto digital a aplicar se coloca la perilla 1 en la posición


correspondiente al efecto deseado, alrededor de la perilla hay un indicador del efecto que
le corresponde a cada posición. Hay efectos que requieren de un solo parámetro y otros de
dos. El primer parámetro del efecto seleccionado se modifica mediante la rotación de la
perilla 2. En caso de que el efecto tenga un segundo parámetro este se modifica mediante
perilla número 3.

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.

A continuación se explica en detalle a que efecto digital corresponde cada posición


de las perillas 1 y 4 comenzando de la posición más a la izquierda y avanzando de a una
posición hacia la derecha. La explicación de los efectos es a un nivel básico, los efectos
son explicados más a fondo en la explicación del código de funcionamiento de los esclavos.

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.

11.2.2 Efecto eco

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.

11.2.3 Efecto trémolo

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.

El resultado es una variación periódica en el volumen.

11.2.4 Efecto Wah-Wah

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.

11.2.5 Efecto limitador ’if ’

Este es un limitador que depende de un solo parámetro. Si la señal de salida es mayor a


un valor x hace que sea x, si es menor a −x hace que sea −x y para los casos intermedios
mantiene intacta la señal. El valor x depende del parámetro del efecto, si esta al mı́nimo

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).

11.2.6 Efecto limitador ’log’

Este es un limitador que simula un amplificador saturado, distorsiona la salida en sus


valores extremos. Cuanto más cerca este el valor de la salida del valor máximo o mı́nimo
que puede adquirir más distorsionada será. Este efecto tiene un parámetro que regula la
magnitud de la distorsión, si esta al mı́nimo no se genera distorsión y si esta al máximo
la distorsión será máxima.

11.2.7 Efecto vibrato

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.

11.3 Utilización del efecto analógico

El sintetizador también cuenta con la posibilidad de generar un efecto analógico a la salida


de audio, dicho efecto es una saturación. El efecto se maneja mediante la perilla número 7
que sirve para aumentar el volumen de la salida generada, esta diseñado de tal forma que
al subir demasiado el volumen la señal sature al amplificador interno y se genere dicho
efecto. Este efecto es explicado más a fondo en la sección ‘Arquitectura del Sistema’.

11.4 Cambio y creación de instrumentos

Para conectar el sintetizador al PC simplemente enchufe el cable serie del sintetizador al


PC. En algunos PC es necesario reiniciar el sistema para que se pueda realizar la conexión.
El modo de uso del software del sintetizador esta en la sección ’Manual de instrucciones
del programa interfaz de comunicación’.

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.1 Análisis de la arquitectura externa

El esquema general de la arquitectura externa se observa en la imágen 17 en la sección


anterior.

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

La entrada RS-232 permite al dispositivo comunicarse con la computadora a través del


puerto COM. El DSPIC33FJ128GP802 posee la capacidad de manejar dos UART inde-
pendientes. La primera fue configurada para obtener los comandos MIDI, la segunda para
cargarle nuevos instrumentos.

Para la comunicación con el PC se utilizó la configuración 9600 de Baud Rate, 8


Data bits, sin paridad y 1 bit de parada por ser esta la más difundida y una que cualquier
PC puede soportar.

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.

12.3 Arquitectura Interna

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.

La imagen 19 es un esquema de la arquitectura interna del sintetizador. En ella


se observa que ambas entradas están conectadas a un DSPic a través de dos integrados
MAX-232. En el caso de la entrada que se conecta al PC el MAX-232 fue utilizado
para convertir los niveles de voltaje del RS-232 que son de 3 a 15 volts tanto positivo
como negativo (el rango cerca del cero no es un nivel válido), a los niveles de voltaje que
entienden los dispositivos TTL/CMOS que son 0 y 5V. La entrada MIDI tiene un circuito
similar con el mismo objetivo.

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.

Imagen 19: Esquema de la Arquitectura Interna

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.

Una transacción en el bus I 2 C tienen el siguiente formato:

— start — A7 A6 ... A1 — R/W — ACK — ..DATA.. — ACK — stop — idle —

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.

El esclavo direccionado responde enviando un bit de ACK que le indica al maestro


que el esclavo reconoce la solicitud y está en condiciones de comunicarse.

Seguidamente comienza el intercambio de información entre los dispositivos, el ma-


estro le envı́a la dirección del registro interno que se desea leer o escribir y el esclavo
responde con otro bit de ACK. Luego el maestro empezará a leer o escribir bytes de
datos. Todos los bytes deben constar de 8 bits y el número máximo de bytes que pueden
ser enviados en una misma transmisión no esta restringido, siendo el esclavo el que fija
esta cantidad de acuerdo a sus caracterı́sticas. Cada byte leı́do/escrito por el maestro
debe ser reconocido por un bit de ACK por el dispositivo maestro/esclavo.

Cuando la comunicación finaliza el maestro transmite una stop condition (condición


de detención) para liberar las lı́neas del bus. Después de la stop condition es obligatorio
8
Wikipedia en Español: I 2 C

68
para el bus estar en idle (estado ocioso) durante unos microsegundos.

En el caso de nuestro sistema además del byte indicando la posición de memoria


interna que se desea leer o escribir, el maestro enviará o recibirá un único byte más antes
de enviar la stop condition, necesaria para liberar el bus. Esto se debe a que las escrituras
y lecturas que se realizan en nuestro sistema son de solamente un byte.

12.3.2 Generación del Sonido

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.

12.3.3 Generación de efectos

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.

12.3.4 La necesidad de tantos microcontroladores

Se mencionó en la sección de “Selección del Microcontrolador” la necesidades de tener


un alta capacidad de procesamiento, debido a la cantidad de armónicos y envolventes
que se deben procesar, esto sumado a la frecuencia de conversión del DAC impide al
DSPic lograr procesar mas de una nota a la vez. Como consecuencia se necesitarán tantos
esclavos como notas se deseen tocar al mismo tiempo. Para realizar este prototipo se
colocaron diez microcontroladores, para permitir en el caso de que se esté utilizando un
órgano MIDI que cada dedo pueda presionar una nota a la vez.

Una desventaja que esta arquitectura presenta es la cantidad de microcontroladores


que tendrá nuestro sistema. Esto implica un mayor consumo de energı́a, una mayor prob-
abilidad de que ocurra una falla con alguno de ellos y también un costo monetario mayor.
Pero, por otra parte, los DSPics son económicos y más robustos que otros procesadores
con mayor capacidad que son los que deberı́amos utilizar en caso de querer reducir la
cantidad de procesadores en el sistema.

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.

12.3.5 Circuito del sumador analógico

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.

El circuito que precisamos es un circuito que sume diferencias de potencial, es decir


que sume la diferencia de potencial entre las patas de los DSPic.

Lo que realizamos fue una combinación de un circuito sumador con un circuito


restador, si llamamos V1x al voltaje respecto a tierra de una de las patas de la salida
analógica del DSPic ’x’ y V2x a la otra pata, la salida buscada del circuito serı́a:
X
(V1n − V2n )
n

El circuito del sumador analógico se presenta en la imágen 20.

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

Como la corriente entre las entradas de un amplificador operacional es cero, entonces


I8 = I4 + I5 + I6 :

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

Obtenemos ası́ la expresión número 12.3.5:

 
R1 R1
VI 1+3 = (V4 + V5 + V6 ) ∗
R R

Como el voltaje entre las entradas de un amplificador operacional es cero, entonces


VI = VII .

V11 −VII V11 −VI


I1 = R
= R
V12 −VII V12 −VI
I2 = R
= R
V13 −VII V13 −VI
I3 = R
= R

Como la corriente entre las entradas de un amplificador operacional es cero, entonces


I7 = I1 + I2 + I3 :

V11 − VI V12 − VI V13 − VI V11 + V12 + V13 3 ∗ VI


I7 = + + = −
R R R R R
71
VII − VOU T VI − VOU T
I7 = = ⇒ VOU T = VI − R1 ∗ I7
R1 R1
   
V11 + V12 + V13 3 ∗ VI 3R1 R1
VOU T = VI − R1 ∗ − = VI 1 + − (V11 + V12 + V13 ) ∗
R R R R

Remplazando con la expresión 12.3.5, llegamos a que:

 
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.

Al analizar el circuito sumador se considero el amplificador operacional ideal debido


a que al amplificador no se le exige mucha potencia, ni se precisa que tenga una gran
ganancia ni resistencia interna o externa.

12.3.6 Saturador Analógico

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.

Como el voltaje entre las entradas de un amplificador operacional es cero, entonces


VI = VII = 0.

72
Imagen 21: Circuito saturador

Como la intensidad que ingresa a un amplificador operacional es cero, entonces


I1 = I2 .

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.

El oı́do humano no nota la diferencia de un sonido y el mismo sonido invertido, por


eso no importa que el amplificador sea inversor.

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.

12.3.7 Circuitos de la caja de efectos

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.

12.3.8 Diseño del PCB y de los circuitos de los DSPic

En la imágen 23 se muestran los circuitos de los esclavos y en la imágen 22 se muestra el


circuito del maestro. Estos circuitos son los circuitos recomendados en las hojas de datos
para las funciones que utilizamos de cada DSPic.

Imagen 22: Circuito del maestro

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.

Se intentó mantener el tamaño al mı́nimo y evitar las conexiones puenteadas, que


fueron inevitables de suprimir todas debido a que el PCB fue impreso utilizando una única
capa. La separación entre las pistas es de 16 mil (0,41 mm) y su grosor es de 24 mil (0,61
mm). Cada PCB tiene lugar para colocar hasta 3 esclavos.

Imagen 24: Diseño PCB

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’.

ˆ Compilador ANSI con las clases estándar de matemática, memoria y conversión de


datos.

ˆ Genera objetos relocalizables para la reutilización de código.

ˆ Permite intercalar código ‘C’ con assembler.

ˆ Permite localizar código y datos en direcciones absolutas.

ˆ 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.

Dado que para la generación de sonido en tiempo real, el tiempo de ejecución de


los métodos es una prioridad, decidimos utilizar un modo de compilación que al convertir
de lenguaje ‘C’ a assembler prioriza el tiempo de ejecución de los métodos al tamaño en
memoria que ocupa el 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.

14.1 Funcionamiento del I 2 C

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.

14.2 Interrupción MIDI

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

La comunicación con un PC es manejada mediante la UART número 2 del DSPIC, de


forma interruptiva. Cuando se ejecuta la interrupción por haber recibido un mensaje
nuevo desde el PC, primero el software chequea si ya se encuentra en el modo de progra-
mación, si es ası́, va agregando los datos a un buffer de cien posiciones. Una vez que se
encuentra lleno, levanta una bandera indicando que ya se obtuvieron los cien valores a
enviar.

En caso de que no se encuentre en modo de programación, si el dato recibido co-


incide con la condición de arranque para comenzar a programar, se levanta una bandera

77
indicando que se comenzaran a recibir los datos del nuevo instrumento.

14.4 Funcionamiento del Main

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.

Luego de esto, el maestro entra en un bucle infinito donde se mantiene ocupado


verificando si se han realizado cambios en los efectos y si se han levantado las banderas
indicando que se ha recibido un comando MIDI o que el usuario comenzará a cargar
un nuevo instrumento. Si se recibió un comando en el caso de que haya sido Note-On
se ejecuta la función agregarNota(), si fue Note-Off se ejecuta quitarNota(). Si fue la
bandera de cargar un instrumento se corre la función cargarNuevoPrograma().

14.4.1 Cambios en las perillas

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.

Si el cambio pertenece a algunas de las perillas discretas se divide el resultado de


la conversión por 146 para obtener un valor entre cero y siete (son los siete efectos más
la posición de descanso), si pertenece a uno de los presets se divide por 8, porque los
parámetros de los efectos varı́an entre 1 y 128.

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.

14.4.2 Función agregarNota()

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()

Se comienza buscando dentro del array mencionado en agregarNota() a que esclavo se le ha


enviado la nota recibida como parámetro en el comando Note-Off, luego de encontrada se le
envı́a a ese mismo esclavo la información de que reproduzca la nota 100 (el sistema trabaja
con instrumentos de hasta 86 notas), con velocidad (intensidad) cero, el interpretará esto
como que debe concluı́r con la reproducción de la nota.

14.4.4 Función cargarNuevoPrograma()

Aquı́ se detallará la lógica detrás de la función cargarNuevoPrograma() para un contenido


más detallado ver la sección “Protocolo de Transmisión”.

Al comenzar el maestro le notifica a todos los esclavos que se ha comenzado a recibir


un nuevo instrumento. Luego se entra en una estructura del tipo while, la condición para
salir del while es que se hayan grabado todos los valores del instrumento. Dentro del
while el método se mantiene a la espera de que se levante la bandera que indica que se ha
completado el buffer de cien posiciones del que se hablo en la parte “Interrupción PC”.
Una vez completo, se procede a enviar los datos a todos los esclavos en rondas de dos
bytes. La razón de que se envı́an de a dos es porque las posiciones de memoria en el
DSPic almacenan 16 bits, lo que estamos haciendo es enviar el valor del int a grabar en
una posición de memoria pero el I 2 C sólo permite el envı́o de variables del tipo short int
(un byte).

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

15.1 Funcionamiento de la comunicación I 2 C

15.1.1 Funcionamiento general

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.

15.1.2 Funcionamiento detallado

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.

El I 2 C como se explicó previamente tiene la capacidad de direccionar sus mensajes


a enviar poniendo como encabezado del mensaje la dirección del esclavo al que se desea
hablar, luego vendrá una cantidad determinada de data que será recibida únicamente por
el DSPic esclavo, cuando se termine de enviar la información se reseteará el bus y ahı́ el
esclavo se dará cuenta de que los próximos bytes que se reciban no necesariamente están
dirigidos a él.

En nuestro caso se distinguen dos funciones en la comunicación del maestro con el


esclavo I2Cwrite(char addr, char subaddr, char value) y I2Cread(char addr, char subaddr).

Cuando ocurre la primera interrupción por I 2 C debido a la recepción de la dirección


del DSPic esclavo, el método chequea si se desea escribir o leer de la memoria del DSPic.

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

Tabla 6: Distribución de RAMBuffer

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.

En el caso de la función I2Cread, se le suma el valor subaddr al valor de RAMPtr


y se procede enviando al maestro el valor *RAMPtr, luego se vuelve el valor de RAMPtr
a la dirección de memoria de RAM Buf f er[0].

81
15.2 Generación de sonidos de notas

15.2.1 Explicación general del funcionamiento

Como se mencionó, el método de generación de notas musicales que se utilizará será el de


una envolvente particular para cada frecuencia. Se considerarán las 15 frecuencias más
influyentes de cada sonido y cada envolvente será aproximada utilizando 7 rectas.

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.

La matriz cambios es una matriz de 15 por 7, en la cual están almacenados la


cantidad de samples a la cual se dan cada uno de los 6 cambios de coeficiente angular
para cada una de las 15 envolventes, el séptimo elemento de cada fila es un 655350 que
luego se explica porque es necesario.

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.

El vector aumentosSeno es un vector de 15 posiciones, que tiene los datos de la


frecuencia de cada componente de la nota a reproducir.

El cálculo de un sample consiste en aumentar todas las envolventes por el incremento

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.

15.2.2 Explicación detallada de las variables

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.

El método que se encarga del cálculo de cada sample es el método calcularSample(),


utiliza una variable llamada salidaLong que es del tipo long int y almacena ahı́ el dato,
luego presenta como salido los 2 bytes más significativos de este número.

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.

La frecuencia de conversión del DAC esta configurada para trabajar a 30 KHz la


razón de esto se explicará más adelante, pero ahora aprovecharemos para ver algunas
implicancias de esto. Como se mencionó nuestra tabla seno posee 15 mil valores, en caso
de que aumente la variable tiempos de a una unidad, luego de transcurrido 1 segundo que
equivalen a 30 mil samples, habrán transcurrido dos perı́odos del seno, esto significa que
se obtuvo una señal de 2 Hz, si se quisiera obtener una de 440 Hz se deberı́a aumentar la
velocidad 220 veces, esto significa avanzar de a 220 valores.

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

15.3 Funcionamiento del método agregarN ota()

Este es el método encargado de la interpretación de los comandos de generación de notas


recibidos desde el maestro. También es el encargado de inicializar todas las variables para
que la nota este lista para generarse.

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.

15.4 Funcionamiento del método calcularSample()

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.

1. salidaLong = salidaLong + punteroM ascaras[i] ∗ seno[tiempos[i]]. Se agrega a


salidaLong el valor de la envolvente multiplicado por el punto que corresponda de
la función sinusoidal.

2. tiempos[i] = tiempos[i] + aumentosSeno[i]. Se aumenta el valor del elemento cor-


respondiente del vector tiempos, ası́ para el próximo sample se utiliza otro valor de
la tabla seno.

3. Si tiempos[i] es mayor a 15000 (largo de la tabla seno) se le resta 15000. Esto es


por que si por ejemplo tiempos[i] = 16000, la tabla seno no puede llegar a ese valor,
pero como la función seno es periódica entonces el elemento que deberı́a estar en la
posición 16000 de la tabla es igual al elemento que esta en la posición 1000.

4. envols[i] = envols[i] + incrementos[i][indices[i]]. También se actualiza el valor


de la envolvente. Se toma como incremento el incremento que esta en la posición
indices[i], esto es por que en indices[i] se encuentra la cantidad de puntos de cambio
de la envolvente que ya fueron superados por la cantidad de samples calculados.

5. Si la cantidad de ciclos es mayor a cambios[i][indices[i]] se aumenta en uno indices[i].


Esto se debe a que en cambios[i] están todos los puntos de cambio de la nota que
se esta reproduciendo y en cambios[i][indices[i]] esta el próximo punto de cambio.
Dado que el último valor de la tabla cambios es mayor a la cantidad de samples que
se reproducen por nota (el último valor es 655350) se puede asegurar que la tabla
cambios no es desbordada.

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.

Finalmente, el método calcularSample multiplica el valor de salidaLong por la


velocidad con la que la tecla fue oprimida y aumenta la variable ciclos en uno.

Cuando el último comando recibido fue el de noteoff, se disminuyendo gradual pero


rápidamente el valor de la variable velocidad, de esta forma los sonidos no desarparecen
tan bruscamente.

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.

15.5 Funcionamiento de los efectos

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.

15.5.1 Funcionamiento general de los efectos

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 vez que se modifica RAMBuffer en una de las posiciones de la 2 a la 7, sig-


nifica que se modificó alguno de los parámetros de los efectos, por eso la interrupción
de I 2 C levanta la bandera banderaCambioEf ectos que indica que hay que actualizar los
parámetros de los efectos.

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.

15.5.3 Funcionamiento del efecto Eco

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.

Luego de obtener valorEco, lo que hace el método es multiplicarlo por magnitudEco


y almacenar la salida generada en valorLargo. Finalmente devuelve lo apuntado por el

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.

Como consecuencia de la forma de diseño de este efecto, no es posible utilizar como


primer y segundo efecto el eco, si se intenta hacer, la salida generada no será el resultado
de aplicar dos veces este efecto.

15.5.4 Funcionamiento del efecto trémolo

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

Los valores para la función seno que se utilizan en la máscara se obtienen de la


tabla seno que también se utiliza para obtener los senos para generar los sonidos de las
notas. El valor que se toma de esta tabla es el valor seno[punteroT remolo]. La variable
punteroTremolo se inicia en cero y luego de calcular cada sample se llama al método
aumentarPunteroTremolo que lo que hace es sumarle a la variable punteroTremolo el
número f recuenciaT remolo, en caso de que punteroTremolo sea mayor o igual a 15000
(largo de la tabla de seno) hace la operación punteroTremolo=punteroTremolo-15000.

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.

15.5.5 Funcionamiento del efecto wah-wah

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.

La fórmula del filtro queda de la siguiente forma:


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.

Es posible que para cierta combinación de valores, el dividendo de la expresión de


y(n) supere los 32bits (largo de la variable valorLargo), por eso decidimos antes de pasar
los valores por el filtro dividirlos todos entre 4 y luego multiplicar el resultado final por 4.

15.5.6 Funcionamiento del efecto limitador if

Este es un efecto de saturación que simplemente pregunta si el sample es mayor a max,


si es devuelve max, si es menor a menos max devuelve menos max y si esta entre max y
menos max devuelve el valor del sample.

15.5.7 Funcionamiento del efecto limitador logarı́tmico

Este es un efecto de saturación que intenta imitar la saturación de un amplificador. Si


se dispone de una señal x(t) que esta entre -1 y 1, la ecuación de este limitador serı́a:
y(t) = x(t) − A ∗ (x(t))3 , siendo A un parámetro entre 0 y 1/3. Dado que nuestra señal de
sonido esta entre -32768 y 32767, para aplicar el efecto serı́a necesario normalizar la señal
X(t) X(t)3
por lo que la ecuación del limitador quedarı́a y1 (t) = 32768 − A 32768 3 , siendo X(t) nuestra

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
.

Como no queremos manejar fracciones creamos un parámetro llamado limiteLog que


vale 32768*A, de esta forma tenemos que:

X(t)3
y3 (t) = X(t) − limiteLog
327683
.

La función que aplica el limitador logarı́tmico es la función int limitadorLog(int


valorActual), donde valorActual es el valor del último sample, y devuelve y3 siendo X(t)
valorActual.
3
X(t)
Lo primero que hace el método es calcular el valor de limiteLog 32768 3 , para eso

aplica la siguiente operación: valorLargo=X(t)*X(t), luego hace que valorLargo sea lo

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.

15.5.8 Explicación del efecto vibrato

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.

15.6 Explicación de las funciones actualizarParametrosEfectos


y aplicarEfectos

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.

Número Efecto asociado


0 No aplicar efecto
1 Trémolo
2 Wah-Wah
3 Limitador if
4 Limitador log
5 Eco
6 Vibrato

Tabla 7: Número asociado a cada efecto

91
15.7 Funcionamiento del método main

Este es el método principal, es el que se ejecuta al iniciarse cada esclavo. Comienza


iniciando todas las herramientas del pic que van a ser utilizadas e inicializando todas las
variables. También inicializa el puntero salida que es un puntero a una variable del tipo
int para que apunte a los dos bytes más significativos de salidaLong, de esta forma lo
apuntado por salida son los datos que hay que emitir por la salida analógica.

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

16.1 Introducción al manejo 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.

16.2 Estructura del programa

El programa tiene tres modos básicos: manejo de envolventes, transmisión de instrumentos


y el de generación de instrumentos. El modo de manejo de envolventes, es el que permite
diseñar envolventes para luego poder aplicárselas a las frecuencias de una nota. Este
modo también permite guardar y abrir las envolventes. El modo trasmisión permite
trasmitirle al dispositivo descripciones de instrumentos previamente generadas ya sea con
nuestro programa o con cualquier otro siempre y cuando estas descripciones estén en el
formato correspondiente, dicho formato se describe más adelante. El modo generación
de instrumentos permite generar instrumentos en un formato que fácilmente se puede
transmitir al sintetizador. Este modo también permite guardar los instrumentos, abrirlos
y escuchar los sonidos generados.

16.3 Aspectos técnicos del programa

16.3.1 Lenguaje utilizado

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.

16.3.2 Caracterı́sticas extras

En la programación del software se le agregaron caracterı́sticas a algunas clases que no


se usan como por ejemplo una foto asociada a los instrumentos. Dichas caracterı́sticas

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

17.1 Comenzando con el programa

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.

El programa comienza mostrando una ventana llamada “Ventana inicial” desde la


cual se puede acceder a todas las funciones del programa. Dicha ventana se muestra en
la imagen 26.

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.

Imagen 26: Screenshot de “Ventana inicial”

17.2 Ingreso al modo Manejo de Envolventes

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.

Esta ventana se explicará más adelante.

17.3 Crear un instrumento nuevo

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.

17.4 Abrir instrumentos

Para abrir un instrumento, haga clic en el botón “Abrir instrumento” de la ventana


principal, luego se abrirá una ventana que permitirá seleccionar el instrumento que se
desea abrir. Los instrumentos que se pueden abrir son sólo aquellos con extensión “.inst”.
Una vez seleccionado un instrumento válido, se abrirá una ventana con el nombre “Manejo
del instrumento” que es la que permite trabajar en el instrumento.

17.5 Uso de la ventana “Manejo de envolventes”

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.

Imagen 27: Screenshot de “Manejo de envolventes” al iniciar la ventana

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.

En el eje X cada unidad esta expresada en “samples”, es decir muestras de sonido.


Como la frecuencia de salida de sonido del sintetizador es de 30 KHz, un valor de 30000
en el eje de las X corresponde a 1 segundo, el valor 15000 a medio segundo, etc.

Si desea comenzar a diseñar de nuevo la envolvente puede hacer clic en el botón


reiniciar.

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.

17.5.2 Crear envolventes mediante extrapolación de otras envolventes

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.

Una de las formas de extrapolar envolventes es promediando dos que ya se tienen,


para eso en el menú edición haga clic en la opción “Crear extrapolando otras dos envol-
ventes por promedio”, se abrirá una ventana que permite seleccionar la ruta donde están
almacenadas las envolventes que desea extrapolar y el valor del “peso” con el que cada una
será considerada. Si los pesos son iguales se realiza un promedio ponderando igualmente
los valores de una y de otra máscara, cuanto mayor sea el peso de una envolvente respecto
de la otra más será considerada al realizar el promedio. Por ejemplo si una envolvente
tiene valor inicial 1 y la otra 2, y los dos pesos son iguales, entonces el valor inicial de la

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.

Si se dispone de una envolvente que comienza con altos valores al comienzo y al


final valores bajos y de otra que comienza con bajos valores y al final toma altos valores,
la extrapolación por promedio de ambas envolventes generarı́a una envolvente que tenga
valores medianos al principio, en el medio disminuye y al final vuelve a tomar valores
medianos. Estas dos máscaras mencionadas anteriormente corresponden a sonidos que
comienzan altos y se apagan rápido y a sonidos que comienzan bajos, se toman un tiempo
a bajo volumen y luego aumentan el volumen. Muchas veces se busca generar sonidos
intermedios, pero que no sean promedio de las dos máscaras, si no más bien sonidos que
tengan cualidades intermedias de entre los dos, es decir que no se tomen tanto tiempo
como la segunda envolvente para llegar a su máximo, pero que tampoco comiencen con
su máximo.

Para lograr la opción deseada en el ejemplo anterior el procedimiento es muy similar


al de crear extrapolando por promedio, pero en lugar de hacer clic en la opción “Crear
extrapolando por promedio”, hay que seleccionar la opción “Crear extrapolando”. Hay
un ejemplo de dos máscaras poderadas con este tipo de ponderación en el cd adjunto.

En la imagen 29 , se muestra una toma de pantalla de la ventana que le permite


seleccionar los parámetros de la extrapolación.

17.5.3 Herramientas útiles para la creación de envolventes

Cambiar la imagen de fondo del panel

98
Imagen 29: Screenshot de la ventana que permite configurar la extrapolación

Es posible que ya se tenga la gráfica de la envolvente que se desea generar, una de


las herramientas de Scilab (que se explica más adelante) permite generar en base a un
sonido en formato .wav gráficas con cada una de las envolventes de la nota. Para facilitar
la copia de la gráfica, puede hacer clic en la opción “Cambiar imagen de fondo”, dentro
del menú “edición”, esta herramienta permite cambiar el fondo del panel donde se genera
la envolvente por otro. De esta forma se puede poner la gráfica que se desea copiar de
fondo y copiarla más fácil.

Multiplicar los ejes por constantes

Estas herramientas se encuentran en el menú “edición” y sirven para multiplicar


las coordenadas x e y de todos los puntos de una envolvente por una constante numérica
mayor a cero. Al hacer clic una de ellas se abre una ventana en la cual se le permite
ingresar la constante por la que desea multiplicar el eje.

La imagen 30 es una toma de pantalla de esa ventana.

Imagen 30: Screenshot de la ventana para ingresar la escala

17.5.4 Guardar envolventes

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”.

En caso de que ya se haya guardado alguna vez la envolvente, al hacer clic en la


opción “Guardar” en el menú “Archivo” la envolvente se guardará en la última ruta donde
haya sido guardada.

17.5.5 Abrir envolventes

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á.

17.6 Manejo del modo transmisión de instrumentos

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”.

La imagen 31 muestra una toma de pantalla de la ventana de transmisión de ins-


trumentos.

Imagen 31: Screenshot de la ventana de trasmisión de instrumentos

El primer paso a realizar dentro de la “Ventana de transmisión” es seleccionar el


puerto serie a través del cual esta conectado el sintetizador. Para eso haga clic en el
botón “chequear puertos”, esto provoca que se genere la lista de puertos serie que están
disponibles en la PC.

De la lista de puertos serie disponibles seleccione el puerto serie en el cual esta


conectado el sintetizador y luego haga clic en el botón “conectar”. Si el proceso fue
correctamente realizado, el programa y el sintetizador ya se encuentran conectados, si
este es el caso se puede observar una etiqueta que dice “esta conectado con el puerto
X”, siendo X el puerto serie solicitado. En algunos casos el proceso de conexión falla,
esto se debe probablemente a que hay otro programa utilizando el mismo puerto serie, se
recomienda cerrar todos los programas que pueden estar interfiriendo y volver a intentar,
si los problemas persisten se recomienda reiniciar la PC y volver a intentar.

Una vez conectado en el puerto serie correspondiente, para enviar un instrumento


haga clic en el botón “Mandar un instrumento”, se abrirá un dialogo que le permite
seleccionar un instrumento a enviar. El instrumento que desee enviar, debe ser un archivo
con la extensión “.hex”, el cual este organizado según el protocolo de transmisión ya
establecido, dicho protocolo se explica más adelante.

Una transmisión correcta tarda unos 5 minutos aproximadamente, luego de conclu-


ida se informa al usuario si ha terminado correctamente o no. No es necesario pero se
recomienda reiniciar el PC luego de cada transmisión, haya resultado esta correcta o no.

No se deben abrir dos ventanas de transmisión simultáneamente, en caso de hacerlo,


el programa puede experimentar fallas importantes.

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”.

La imagen 32 muestra una toma de pantalla de la ventana de manejo de instrumento.

Imagen 32: Screenshot de la ventana de manejo de instrumento

17.7.1 Barra superior

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”

La opción “abrir” sirve para abrir otro instrumento, se le preguntará primero si


confirma que desea abrir otro instrumento en lugar del que esta trabajando y luego se le
presentará una ventana en la cual podrá seleccionar el instrumento a abrir.

Opciones “Guardar” y “Guardar Como”

Los instrumentos se pueden guardar tanto en formato “.inst” como en formato


“.hex”. El formato “.inst” es el que guarda un instrumento completamente, es decir

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 “Guardar” sirve para guardar el instrumento en la última ruta donde


halla sido guardado, en caso de que nunca halla sido guardado la opción “Guardar” es
equivalente a la opción “Guardar Como”.

Opción “Importar desde hex”

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 “exportar instrumento”

La opción “exportar instrumento”, sirve para guardar el instrumento que se esta


realizando en formato “.hex”, su manejo es igual al de la función “guardar como”.

Opción “salir”

La opción “salir” es para cerrar la ventana.

17.7.2 Editar cada envolvente

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.

Como se describió anteriormente, cada nota tiene quince componentes de frecuencia


cada uno con su respectiva envolvente. Una vez elegida la nota seleccione el panel “Editar
cada envolvente”, dicho panel tiene un color de fondo que varı́a con la nota que se este
tocando y tiene también tres cuadros iguales, en cada uno de ellos se puede trabajar cada
uno de los 15 componentes de la nota. La barra de desplazamiento que se encuentra
debajo de dichos cuadros es la que permite trasladarse para que los cuadros representen
otro de los quince componentes, el número de componente se indica en la parte superior de
cada cuadro, donde dice “frecuencia NºX”, siendo X el armónico que el cuadro representa.
Los cuadros también cambian de color dependiendo de la componente que representan.

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.

Cada envolvente, también tiene un multiplicador, que es un número por el cual va


a ser multiplicado su eje de las Y. La función es que se pueda fácilmente aumentar y
reducir la influencia de una frecuencia en el resultado final de una nota. Para modificar
un multiplicador escriba el valor deseado en el cuadro de texto que se encuentra a la
izquierda del botón “aplicar multiplicador” en el cuadro correspondiente a la componente
deseada y haga clic en el botón “aplicar multiplicador”.

17.7.3 Edición común a todas las envolventes

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.

En este panel se dispone de la posibilidad de asignar todas las envolventes de un


cierto rango de notas extrapolando las envolventes de otras notas. El resultado es que para
todas las notas del rango a extrapolar la envolvente de cada componente es el resultado de
la extrapolación de las envolventes con el mismo número de envolvente pero de las notas
tomadas como guı́a para la extrapolación. Si se desea utilizar esta herramienta ingrese el
número de nota que desea tomar como primer referencia en el cuadro de texto que se ubica

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.

El panel también dispone de la posibilidad de copiar frecuencias que se utiliza de


forma análoga a la opción de copiar envolventes.

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.

En la imagen 33, se muestra una captura de pantalla de la ventana “Manejo de


instrumentos”, con el panel “Edición común a todas las envolventes” abierto.

17.7.4 Probar las notas generadas

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.

El botón “generar sonido” permite generar el sonido de la nota que se encuentra


indicada en el cuadro de texto a la izquierda de dicho botón. Al hacer clic en ese botón
se abre un cuadro que permite indicar donde se desea guardar el archivo “.wav” que
contendrá el sonido que generará la nota.

Para normalizar un rango de notas, es decir modificar los multiplicadores de cada


una de las envolventes para que el sonido de la nota tenga un volumen óptimo, se selecciona
el rango de notas que desea normalizar y se hace clic en el botón “normalizar”.

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.

La imagen 34 muestra una captura de imagen de la ventana “Manejo del instru-


mento”, con el panel “Probar notas generadas” seleccionado.

17.8 Formato de los archivos para enviarle al sintetizador

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.

Como ya se mencionó anteriormente la ventana de “transmisión de instrumentos”,


recibe instrumentos en formato “.hex” y los transmite al sintetizador. Los archivos “.hex”
son archivos con mucha menos información y que ocupan mucho menos espacio que los
archivos “.inst”. La ventaja de los archivos “.hex” respecto a los “.inst” es que son
más fáciles de generar y es posible generarlos a partir de otras formas que con nuestro

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.

El objetivo de esta sección es explicar el formato de los archivos “.hex”.

Para que los archivos “.hex” generados se puedan escuchar correctamente en el


sintetizador, es necesario que los sonidos de todas las notas sean normalizados a uno, es
decir que el máximo valor absoluto que puede alcanzar el sonido de una nota sea uno.

Un archivo “.hex” es un archivo de texto compuesto por 20640 números enteros


separados por comas, sin espacios. En caso de que un número no sea entero es necesario
redondearlo a entero antes de ponerlo en el archivo “.hex”

Al principio del archivo se presenta la información sobre todas las frecuencias.


Primero se presenta de la primer nota la primer frecuencia dividido dos, luego la segunda
frecuencia dividido dos y luego la tercer frecuencia dividido dos y ası́ sucesivamente hasta
llegar a la ultima frecuencia, luego la primer frecuencia de la segunda nota dividido dos
y se procede de esa forma hasta llegar a la última frecuencia de la última nota. En total
son 1290 los valores que debe tener esta parte.

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

18.1 Introducción a los programas 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.

18.2 Programa “graficaFacil”

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.

El encabezado del programa es el siguiente:

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.

18.3 Programa “acortaSonido”

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:

z=acortaSonido(entrada, salida, desde, hasta, canal, sumar).

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.

18.4 Programa “fourierFacilDetectaPicos”

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.

Lo que hace el programa es calcular la descomposición en frecuencias de un sonido


y luego divide en grupos el vector resultado formando los grupos por conjuntos de puntos
consecutivos, después hace una gráfica donde cada grupo es representado por la suma
del módulo de cada uno de sus puntos. Es decir que el resultado final es muy similar a
la descomposición en frecuencias del sonido original pero sumando las magnitudes de los
puntos que están consecutivos. Es muy útil para poder tener noción de la potencia que
tiene un sonido en cada una de sus frecuencias.

La función FFT (Fast Fourier Transform) es la Transformada de Fourier en tiempo


discreto del sonido la cual devuelve sólo un vector de valores, este programa como tiene
acceso a la frecuencia de muestreo del sonido genera un eje X adaptado para que el usuario
pueda visualizar cada valor del resultado final relacionado con su valor en Hz.

Dado que la grafica de la descomposición en frecuencias de un vector real es simétrica


respecto al eje de las Y, para reducir la cantidad de puntos a graficar este programa sólo
grafica uno de los lados, en el resultado final el eje de las X comienza en cero y termina
en Fs/2.

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:

[Picos,largoAudio,Fs] = fourierFacilDetectaPicos(entrada, cantidad, can-


tidadMuestras, numeroPicos, colorGraf )

Donde “entrada” es la ruta donde se encuentra el sonido a analizar en formato


“.wav”. “cantidad” es la cantidad de puntos que se desea que tenga la gráfica de la
descomposición en frecuencias. “cantidadMuestras” es para evitar tomar todos los valores
del sonido a analizar, el programa considera sólo los primeros “cantidadMuestras” valores
del sonido pedido, si se asigna un número negativo a esta variable, el programa considera
el sonido entero. “numeroPicos” es la cantidad de frecuencias más influyentes que se desea
que el programa devuelva. “colorgraf” es el número asociado al color con el que se desea
graficar el resultado final. “Picos” es el vector que contiene las frecuencias más influyentes,
“largoAudio” es la cantidad de muestras del sonido original y “Fs” es la frecuencia de
muestreo del sonido original.

18.5 Programa “fourierPartes”

El objetivo de este programa es analizar como evolucionan ciertas frecuencias de un sonido


en el tiempo. La idea es ver como se van desarrollando en el tiempo las frecuencias
más importantes de un sonido, es decir las envolventes que tienen las frecuencias más
importantes del sonido.

Lo que hace básicamente este programa es analizar para un conjunto de frecuencias


que valor tiene cada una de ellas en la descomposición de frecuencias de un sonido en
distintos intervalos del tiempo. Para eso el programa divide en varias partes el sonido a
analizar y para cada parte analiza la influencia de cada una de las frecuencias a analizar.
Finalmente grafica para cada frecuencia su influencia en cada uno de los intervalos del
sonido. Es una herramienta muy útil para crear las envolventes de los sonidos de los ins-
trumentos. Devuelve por cada frecuencia una ventana con una gráfica de como evoluciona
su influencia en el sonido a analizar en función del número de muestra (por ejemplo, el
valor en el eje de las x igual a la frecuencia de muestro corresponde a un tiempo de un se-
gundo). La gráfica cuyo número de ventana sea menor corresponde a la primer frecuencia
devuelta por el programa, la gráfica con el segundo menor número de ventana corresponde
a la segunda frecuencia devuelta y ası́ sucesivamente. Si bien las frecuencias que se pasan
por parámetros se pasan como un vector de números, el programa analiza no sólo cada
frecuencia, si no que con ella también su entrono, por ejemplo si se pasa como frecuencia
a analizar 440Hz, la frecuencia 439Hz también va a ser incluida en el análisis como parte
de la frecuencia 440Hz.

El encabezado del programa es el siguiente:

coeficientes = fourierPartes(rutaorigen, desde, hasta, largoParticion, fre-


cuencias).

Donde “rutaOrigen” es la ruta donde se encuentra el archivo de audio a analizar en


formato “.wav”. “desde” es desde que número de muestra del sonido se desea analizar,
si esta variable vale uno se analiza desde el principio del sonido. “hasta” es el último
número de muestra a analizar, si se le asigna un valor menor a cero se analiza el sonido

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.

18.6 Programa “copiaNota”

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.

El encabezado del programa es el siguiente:

[y, mi, incrementos, cambios, evolucion]= copiaNota(rutaDestino, fre-


cuencias, evolucion, cadaCuantosEvol, nota, largo, Fs)

Donde “rutaDestino” es la ruta donde se desea almacenar el sonido resultante. “fre-


cuencias” es la lista con las 15 frecuencias más influyentes del sonido. “evolucion” es una
matriz que indica como evoluciona cada frecuencia en el tiempo, es la matriz que devuelve
el programa “fourierPartes”. “cadaCuantosEvol” es un parámetro que indica de que largo
(expresado en número de muestras) fueron los intervalos en los que fue dividido el sonido
en la aplicación “fouriePartes” para generar la tabla “evolucion”. “nota” indica el número
de nota que se desea imitar. “largo” es la cantidad de muestras que se desea que tenga el
sonido de salida. “Fs” es la frecuencia de muestreo del sonido de salida. “y” es el sonido
de salida. “mi” es la lista de los valores iniciales de cada una de las envolventes. “in-
crementos” es cada uno de los incrementos que tiene cada envolvente. “cambios” son las
cantidades de muestras en las que se generan los cambios en las envolventes y “evolucion”
es una copia de la matriz “evolucion” que se recibe por parámetros.

18.7 Programa “copiaSonido”

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.

Como este programa utiliza algunas de las aplicaciones mencionadas anteriormente,


es necesario que estas estén cargadas en Scilab para poder utilizarlo. Si bien la idea es
poder imitar cualquier sonido, cuanto más fácil sea de imitar el sonido que recibe por
parámetros el programa mejor quedará la imitación, para eso es necesario que el sonido
a imitar sea lo más corto posible y si es un instrumento de cuerda mejor. Este programa
todavı́a no esta muy perfeccionado por lo que las imitaciones en varios casos no son
buenas, el principal objetivo de este programa no es imitar sonidos, si no mostrar que
se puede llegar a generar programas que automáticamente generen las tablas para que el
sintetizador pueda imitar sonidos.

El encabezado del programa es el siguiente:

[y, mascarasIniciales, incrementos, cambios, Picos, evolucion, aqum] =

copiaSonido(rutaOrigen, rutaDestinoSonido, rutaDestinoTabla, frecuen-


ciaPic, cantidadSenos, nota).

Donde “rutaOrigen” es la ruta donde se encuentra el sonido a imitar en formato


“.wav”. “rutaDestinoSonido” es la carpeta donde se desea almacenar el sonido generado,
el sonido se guarda con el nombre “nota X.wav” siendo X el parámetro ’nota’. “ru-
taDestinoTabla” es la ruta donde se desea almacenar la tabla generada para pasarle al
sintetizador. “frecuenciaPic” es la frecuencia de muestreo del sintetizador, dicho valor es
30000. “cantidadSenos” es otro parámetro del sintetizador que vale 15000. “nota” es el
número de nota asociado a la imitación que se va a generar. “y” es el vector con todos
los valores del sonido generados. “mascarasIniciales” es el vector con todos los valores
iniciales de las envolventes del sonido generado. “incrementos” es la tabla con los in-
crementos del sonido generado. “cambios” es la matriz con los números de muestras en
los cuales las envolventes cambian sus incrementos. “Picos” son las 15 frecuencias más
influyentes en el sonido original. “evolucion” es la tabla de evolución que devuelve la
aplicación “fourierPartes” y “aqum” es una variable auxiliar de control, en caso de que
la función haya concluido satisfactoriamente (la amplia mayorı́a de los casos) la variable
“aqum” debe finalizar con el valor 20640.

18.8 Programa “haceGraficas”

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.

El encabezado del programa es el siguiente:

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.

18.9 Programa “sinusoidalesPuras”

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.

El encabezado del programa es el siguiente:

sonidoGenerado = sinusoidalesPuras(entrada, puntosFourier, colorGraf,


cantidadMejores, largoSalida, rutaDestino)

Donde “entrada” es la ruta donde se encuentra almacenado el sonido a imitar.


“puntosFourier” es la cantidad de grupos de puntos consecutivos en los que se quiere
dividir la transformada de Fourie del sonido a imitar (es lo mismo que “cantidad” en la
función “fourieFacilDetectaPicos”). “colorGraf” es el número asociado al color con el que
las gráficas van a ser dibujadas. “cantidadMejores” es la cantidad de frecuencias más
influyentes que se van a buscar. “largoSalida” es la cantidad de samples que la salida
tendrá. “rutaDestino” es la ruta donde se desea almacenar la salida generada.

18.10 Programa “envolventeComun”

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.

El encabezado del programa es el siguiente:

salida=envolventeComun(rutaEntrada, rutaDestino, largoSalida, numRec-


tas)

Donde “rutaEntrada” es la ruta donde se alamcena el sonido que se desea imitar.


“rutaDestino” es la ruta donde se desea almacenar la imitación generada. “largoSalida”
es la cantidad de samples que se desea que la imitación tenga. “numRectas” es la cantidad
de rectas con las que se desea generar la envolvente.

18.11 Observación general

La potencia de una señal sinusoidal es proporcional a su amplitud al cuadrado. Por esta


razón, si en los programas que agrupan frecuencias similares (como el programa “fourier-
FacilDetectaPicos”) se agrupan directamente sumando sus módulos se estarı́a cometiendo
un error. Esto se debe a que se representarı́a, por ejemplo, de la misma forma una señal
sinusoidal de amplitud 2A que dos señales sinusoidales de frecuencias similares y amplitud
A cuando la potencia de la señal de amplitud 2A es mayor. Si bien estos programas se
explicaron como si se sumara amplitudes sumando sus módulos, lo que realmente hace el
programa es sumar el cuadrado de todos los módulos y luego calcular la raı́z cuadrada de
la suma. De esta forma, si un punto tiene mayor altura en la gráfica que otro significa
que entre las frecuencias que representa hay más potencia acumulada.

18.12 Programas que generan efectos

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.

Para utilizarla haga doble clic en el archivo “reduce imágenes.exe” contenido en el


cd.

La imágen 35 es una captura de pantalla de dicho programa.

Imagen 35: Screenshot de la ventana del programa “Reduce Imagenes”

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

La idea en la generación del software de PC es ofrecer junto al hardware una posibilidad


para que los usuarios puedan sintetizar sus propios instrumentos de una manera amigable.
Pero la idea del proyecto es que cada uno, si ası́ lo desea, pueda diseñar su propio programa
para la generación de sonidos.

Aquı́ se detalla cuál debe ser el protocolo de transmisión de datos en el momento


de cargar un nuevo instrumento.

Se comenzará enviando el byte 0x32 (32 en hexadecimal, 50 en decimal), esta es


la condición de arranque. Luego se procederá a enviar la información conteniendo los
detalles. Debajo se enlistan los pasos:

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.

En caso de que el dispositivo no responda, significa que ha habido un error durante


la transmisión, lo mejor en este caso es re-iniciar el sistema quitándole la corriente y volver
a intentar.

Codificación de los datos

Los datos a enviar deben ser en el siguiente orden:

1. Armónicos, unsigned int[85][15], tamaño 2580 bytes


2. Máscaras Iniciales, long int[86][15], tamaño 5160 bytes
3. Posición de Cambios, unsigned int[86][15][7] tamaño 18060 bytes
4. Incrementos, int[86][15][7] tamaño 18060 bytes
5. Relleno, 40 bytes

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.

El tamaño total de un instrumento es de 43860 bytes, pero como el maestro tiene


integrado un buffer de cien posiciones que sólo permite transmitir los datos una vez que
se llena se necesitan agregar cuarenta bytes cualesquiera para que los último sesenta sean
procesados.

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 mundo de la música y la interpretación de instrumentos permiten realizar una gran


cantidad de aplicaciones. Obviamente no es posible abarcar todas estas aplicaciones en un
solo proyecto de un año. En el planteo del objetivo del proyecto fue necesario renunciar a
muchas aplicaciones adicionales y accesorios que podrı́a tener el sintetizador por el hecho
de que es imposible abarcar todas las opciones posibles simultáneamente. Sin embargo,
la arquitectura del sintetizador permite la realización de varias de ellas de una forma
relativamente sencilla. A continuación se nombran varias de las aplicaciones y accesorios
que se le podrı́an agregar al proyecto sin realizar cambios fundamentales en la arquitectura
del sistema.

21.1 Posibles mejoras en el sintetizador

21.1.1 Alimentación con una única fuente

Actualmente el sintetizador requiere para su correcto funcionamiento de varias fuentes de


alimentación, en caso de querer comercializar el producto es necesario implementar una
solución para que se pueda alimentar con una única fuente. Lo ideal serı́a que se pueda
alimentar a través de pilas o una baterı́a.

21.1.2 Aumento de los controles internos

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.

21.1.3 Detección automática de esclavos que están fallando

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.

21.1.5 Modificación dinámica de los instrumentos

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.

La arquitectura del sintetizador y el formato de los instrumentos virtuales fueron


diseñado para que este tipo de cambios pueda ser realizado muy fácilmente. Actualmente,
cuando el esclavo debe reproducir una nota lo que hace es copiar los parámetros de dicha
nota de la memoria de programación a ciertas variables en su memoria de trabajo. Para
realizar esta mejora se deberı́a hacer que en vez de copiar tal cual son los parámetros de
la nota a reproducir de la memoria de programación a la memoria de trabajo, se los copie
con modificaciones que dependen de la posición de algunos potenciómetros.

Se podrı́a, por ejemplo, al copiar los parámetros de la memoria de programación a


la memoria de trabajo, multiplicar los incrementos de alguna de las componentes, para
lograr de esa forma hacer que la nota tenga más componentes agudos o graves. También
se podrı́a hacer que se modifique los puntos de cambio para hacer la nota más larga o
más corta.

21.1.6 Utilizar interfaz USB

La interfaz de comunicación entre el sintetizador y el PC es mediante el puerto serie, dicha


interfaz es antigua y no se encuentra en muchas de los nuevos modelos de computadoras.
Una posible solución es utilizar un adaptador USB a serie, pero la solución óptima es que
el sintetizador tenga una interfaz de comunicación USB.

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.

21.1.7 Almacenar múltiples instrumentos virtuales en un solo sintetizador

Tanto cuando se utiliza el PC como sintetizador MIDI o el SMPro Audio V-Machine


Standalone VST Player, cambiar el instrumento virtual que se esta utilizando toma escasos
segundos. En nuestro sistema realizar esto toma varios minutos y requiere de una conexión
a una PC. La idea para solucionar esto serı́a poder guardar datos de varios instrumentos
en el sintetizador.

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.

21.1.8 Múltiples instrumentos conectados a un solo sintetizador

Si se logra realizar la mejora de tener varios instrumentos virtuales en un solo sintetizador,


el paso siguiente serı́a poder conectar varios instrumentos al sintetizador y poder asignarle
a cada uno un instrumento virtual.

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.

21.1.9 Ampliar la cantidad de tipos de instrumentos que se pueden generar

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.

Para resolver esto se deberı́a remover el concepto de envolvente y reproducir el sonido


con una intensidad constante que dependa únicamente del valor de intensidad recibido
por el comando MIDI.

21.1.10 Opción de grabación

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.

21.1.11 Reproducir archivos .midi

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.

21.2 Posibles mejoras en el software de PC

El objetivo central del proyecto no fue el software de interconexión entre el PC y el


sintetizador. Por ende, este tiene una gran cantidad de posibles mejoras a aplicarle, sobre
todo teniendo en cuenta la gran cantidad de aplicaciones relacionadas con el procesamiento
de sonidos que permiten realizar los procesadores de hoy en dı́a.

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.

21.2.1 Mejora en el sistema de generación automática de notas

Como se mencionó anteriormente, la aplicación de Scilab “copiaSonido” puede transformar


sonidos en notas de instrumentos virtuales que pueden ser pasadas al sintetizador, en
varios casos no se obtienen buenos resultados. Esto se debe a que no es una aplicación
fácil de realizar y que al no ser una prioridad de nuestro proyecto, no se le dedicó el
tiempo suficiente. Consideramos que más que un buen imitador de sonidos es un programa
que demuestra que se puede generar programas que realmente puedan imitar sonidos y
transformarlos en notas de instrumentos virtuales.

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.

21.2.2 Mejora en la velocidad de funcionamiento

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.

21.2.3 Cambiar los parámetros a editar de un instrumento

Actualmente en el software “Interfaz de comunicación”, la forma de editar instrumentos


implica un manejo de envolventes. Deducir las envolventes no es algo intuitivo, tampoco es
una forma cómoda de generar instrumentos. Lo que se propone como mejora es agregarle
al software opciones más intuitivas para la generación de instrumentos, opciones como
que el sonido sea más agudo, o más prolongado, más parecido al de una guitarra eléctrica,
agregarle eco, etc. Dichas opciones harı́an de la creación de instrumentos una tarea
accesible a cualquier usuario.

21.2.4 Agregar un conversor de instrumentos VSTi al formato del sinteti-


zador

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.

21.2.5 Integrar todos los programas y aplicaciones en un solo programa

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

22.1 Carpeta “Imitaciones Generadas con los Distintos Métodos”

22.1.1 Carpeta “Imitaciones Generadas Mediante Sinuidales Puras”

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”.

22.2 Carpeta “Imitaciones Generadas Mediante Envolvente Global”

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”.

22.3 Carpeta “Imitaciones Generadas Mediante Envolvente In-


dividual”

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.

22.3.1 Carpeta “Imitaciones Generadas Con El Programa CopiaSonido”

Esta subcarpeta contiene imitaciones generadas mediante el método de la envolvente


individual, pero generadas automaticamente mediante la función de scilab “CopiaSonido”.
En este caso hay sonidos originales con los cuales comparar dado que estos sonidos si se
generaron intentando imitar sonidos especı́ficos. Como se mencionó anteriormente, estas
imitaciones no son perfectas, pero son una buena pauta de que se puede llegar a generar
un programa que imite casi cualquier nota musical mediante el método de la envolvente
individual.

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.

22.4.1 Carpeta “Instrumentos Creados”

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.

22.4.2 Carpeta “Ejemplos de Envolventes”

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

Consideramos que logramos un proceso de estudio y aprendizaje exitoso, en el cual se


presentaron varios problemas que, en algunos casos, fueron resueltos y en otros casos re-
quirieron de una reestructuración de la arquitectura y hasta de un replanteo del objetivo
del proyecto. Se ganó mucha experiencia en el campo de la ingenierı́a, sobre todo en el as-
pecto práctico. Aprendimos a trabajar con procesadores DSP y sobre sistemas operativos
y sus archivos.

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.

En cualquier otro proyecto de la carrera, las decisiones fundamentales no son tomadas


por nosotros, en los obligatorios de programación los alumnos no eligen el lenguaje de pro-
gramación, en los obligatorios de sistemas embebidos el alumno no puede elegir el sistema
embebido a utilizar. Uno de los mayores desafı́os de este proyecto fue enfrentarnos casi
que por primera vez a una situación en la cual tenemos que generar un sistema empezando
desde cero, un sistema en el cual todas las decisiones las tenemos que tomar por nuestra
cuenta.

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

[analog devices] analog devices.2009.DSP


Disponible en internet:
http://www.analog.com/en/embedded-processing-dsp/processors/index.html

[blogElectronica] blogElectronica.2009.Sistemas embebidos Linux


Disponible en internet:
http://www.blogelectronica.com/modulos-embebidos-procesadores-digi-connectcore/

[CoolC/C++] CoolC/C++.2009.programación de archivos dll [online]


Disponible en internet:
http://www.programacionenc.net/modules.php?name=Forums\&file=printview\&t=748\&start=
0

[electrónicos online] [online]


Disponible en internet:
http://www.electronicosonline.com/

[embedded] [online]
Disponible en internet:
http://www.embedded.com

[freescale] freescale.2009.Digital Signal Processors and Controllers


Disponible en internet:
http://www.freescale.com/webapp/sps/site/homepage.jsp?nodeId=012795

[linuxemb] linuxemb.2009.GNU/Linux embebido


Disponible en internet:
http://linuxemb.wikidot.com/

[LinuxJournal] LinuxJournal.2009.A Host For Native Linux VST Plugins ? [online]


Disponible en internet:
http://www.linuxjournal.com/node/1000192

[Microchip] microchip.2009.16-bitPIC24 MCU and DSPic DSC products [online]


Disponible en internet:
http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=2629&param=
en533465

[Microchip]Microchip.2009.MPLAB C Compiler for dsPIC DSCs Disponible en internet:


http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=
en535363

[microcontroller] [online]
Disponible en internet:
http://microcontroller.com

[Microsoft] Microsoft.2009.Windows embedded

127
Disponible en internet:
http://www.microsoft.com/windowsembedded/en-us/default.mspx

[Microsoft] Microsoft partner network.2009.Windows Embedded: descripción general


Disponible en internet:
https://partner.microsoft.com/spain/productossoluciones/psembedded

[Microsoft] MSDN.2009.What is Windows Embedded?


Disponible en internet:
http://msdn.microsoft.com/en-us/windowsembedded/default.aspx

[Musican´s friend] VMachine.2009 [online]


Disponible en internet:
http://pro-audio.musiciansfriend.com/product/SM-Pro-Audio-Vmachine-Stand-Alone-VST-Player?
sku=241884

[National instruments] National instruments.2009.VSTi [online]


Disponible en internet:
http://digital.ni.com/worldwide/latam.nsf/web/all/01E4BFF8EC93532086256B6000669953

[processlibrary] processlibrary.2009 [online]


Disponible en internet:
http://www.processlibrary.com/directory/files

[redeweb] Héctor Palacios Pérez.2009.Linux en los sistemas embebidos:


Disponible en internet:
http://www.redeweb.com/\_txt/articulos/520503.pdf

[Sistemas embebidos] [online]


Disponible en internet:
www.sistemasembebidos.com.ar

[Softonic] Softonic.2009.descargar ExeScope [online]


Disponible en internet:
http://exescope.softonic.com/

[soporte de microchip] microchip.2009.24/7 support


Disponible en internet:
http://support.microchip.com/scripts/slxweb.dll/external?name=webticketcust

[Steinberg] gersic.2009.VST-plug ins documentation [online]


Disponible en internet:
http://www.gersic.com/vstsdk/

[texas instruments] texas instruments.2009.DSP


Disponible en internet:
http://focus.ti.com/dsp/docs/dsphome.tsp?sectionId=46

[The university of Maryland] Department of Computer Science.2009.ARMed solutions


to the DSP war
Disponible en internet:
http://www.cs.umd.edu/class/fall2001/cmsc411/proj01/arm/dsp.html

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/

[VSTHost] hermannseib.2009.VSTHost [online]


Disponible en internet:
http://www.hermannseib.com/english/vsthost.htm

[Wikipedia] WIKIPEDIA.2009.MIDI [online]


Disponible en internet:
es.wikipedia.org/wiki/MIDI

[Wikipedia] WIKIPEDIA.2009.Sintetizador [online]


Disponible en internet:
http://es.wikipedia.org/wiki/Sintetizador

[Wikipedia] WIKIPEDIA.2009.Sistema operativo de tiempo real [online]


Disponible en internet:
http://es.wikipedia.org/wiki/Sistema_operativo_de_tiempo_real

[Wikipedia] WIKIPEDIA.2009.Synthesizer [online]


Disponible en internet:
http://en.wikipedia.org/wiki/Synthesizer

[Wikipedia]Wikipedia.2010.Teorema de muestreo de Nyquist-Shannon Disponible en in-


ternet:
http://es.wikipedia.org/wiki/Teorema_de_muestreo_de_Nyquist-Shannon

[Wikipedia] WIKIPEDIA.2009.VST [online]


Disponible en internet:
http://en.wikipedia.org/wiki/Virtual\_Studio\_Technology

[Your Electronics Open Source] EE Software Development.2009 [online]


Disponible en internet:
http://dev.emcelettronica.com/ee-software-development

Foros de internet que utilizamos

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

Anexo A, teorema de Nyquist-Shannon

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 demuestra que la reconstrucción exacta de una señal continua es posible si


esta tiene un ancho de banda ’B’ (no tiene componentes en frecuencias mayores a ’B’) y
la frecuencia de muestreo es mayor a 2’B’.

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→+∞

h(t)= n si 0 < t < n1 , 0 en otro caso

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”

extern short int recibiDatoProg ;


e x t e r n i n t datoProgL ; // Es e l mismo que e l que e s t a d e f i n i d o en main . c

e x t e r n i n t datoProgH ; // Es e l mismo que e l que e s t a d e f i n i d o en main . c

e x t e r n i n t datoProg ; // Es e l mismo que e l que e s t a d e f i n i d o en main . c

e x t e r n s h o r t i n t terminoPrograma ; // Es e l mismo que e l que e s t a d e f i n i d o


en main . c

e x t e r n c h a r r e c i b i C R C d a t o ; // Es e l mismo que e l que e s t a d e f i n i d o en


main . c

e x t e r n c h a r recibiCRC ; // Es e l mismo que e l que e s t a d e f i n i d o en main . c

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 ] ; // Es e l mismo que e l que e s t a


d e f i n i d o en main . c

char llevoCuenta = 0 ;
int direccionL ;
int direccionH ;
e x t e r n c h a r * CRC L ;

v o i d cargoPrograma ( ) {

f o r ( d i r e c c i o n L =0x8000 ; d i r e c c i o n L <0xE4C8+0x3FE ; d i r e c c i o n L+=0x3FE ) {


// Borra l o s d a t o s d e l i n s t r u m e n t o a n t e r i o r , n e c e s a r i o para poder
s o b r e e s c r i b i r l a memoria .

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) ;

f o r ( d i r e c c i o n L =0x1174 ; d i r e c c i o n L <0x5400 ; d i r e c c i o n L+=0x3FE ) {


MemWriteLatch ( 0 x1 , d i r e c c i o n L , 0 x0 , 0 x0 ) ;
MemCommand(PM ROW ERASE) ;
}
MemWriteLatch ( 0 x1 , 0 x5400 , 0 x0 , 0 x0 ) ;
MemCommand(PM ROW ERASE) ;
d i r e c c i o n L = 0 x8000 ; // Se comienza a e s c r i b i r en l a p o s i c i ó n de memoria
0 x8000

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 ++;
}

i f ( d i r e c c i o n L==POS ARMONICOS L+BUFFER ARM) { //Una vez que s e t e r m i n e


de e s c r i b i r en l a p o s i c i ó n de memoria l o s a r m ó n i c o s s e va a l a de
las envolventes .

d i r e c c i o n L=POS MASK INICIAL L ;


d i r e c c i o n H= POS MASK INICIAL H ;
llevoCuentaTotal = 0;
}
i f ( d i r e c c i o n L==POS MASK INICIAL L+BUFFER MASK) {
d i r e c c i o n L=POS CAMBIOS L ;
d i r e c c i o n H=POS CAMBIOS H ;
llevoCuentaTotal = 0;
}
i f ( d i r e c c i o n L==POS CAMBIOS L+BUFFER CAMB) {
d i r e c c i o n L=POS INCREMENTOS L ;
d i r e c c i o n H=POS INCREMENTOS H ;
llevoCuentaTotal = 0;
}
i f ( d i r e c c i o n L==POS INCREMENTOS L+BUFFER INC) { //La t a b l a de
i n c r e m e n t o s e s l a ú l t i m a en s e r e s c r i t a . Luego f i n a l i z a r á l a
f u n c i ó n y s e v o l v e r á a l main .

RAMBuffer [ 1 5 ] = 0 ;
}
}
}
slaves/CargarProgSlave.c

134
Clase ’CargarProgSlave.h’

#i n c l u d e <p33FJ128GP802 . h>

#d e f i n e PM ROW ERASE 0 x4042


#d e f i n e PM ROW WRITE 0 x4001
#d e f i n e CONFIG WORD WRITE 0 x4003
#d e f i n e PM ERASE SECURE SEG 0x404C
#d e f i n e PM ERASE GRAL SEG 0x404D
#d e f i n e UNA RONDA 50

#d e f i n e POS ARMONICOS L 0 x8000


#d e f i n e POS ARMONICOS H 0 x0
#d e f i n e BUFFER ARM 0xA14

#d e f i n e POS INCREMENTOS L 0 x1174


#d e f i n e POS INCREMENTOS H 0 x1
#d e f i n e BUFFER INC 0x468C

#d e f i n e POS MASK INICIAL L 0x8A14


#d e f i n e POS MASK INICIAL H 0 x0
#d e f i n e BUFFER MASK 0 x1428

#d e f i n e POS CAMBIOS L 0x9E3C


#d e f i n e POS CAMBIOS H 0 x0
#d e f i n e BUFFER CAMB 0x468C

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”

// Este método s i r v e para i n i c i a l i z a r e l DAC a 30Khz , c a n a l d e r e c h o .


void initDac ( void ) {
ACLKCONbits .SELACLK = 0 ;
ACLKCONbits .AOSCMD = 0 ;
ACLKCONbits . ASRCSEL = 0 ;
ACLKCONbits . APSTSCLR = 7 ;
DAC1STATbits .ROEN = 1 ;
DAC1STATbits .RMVOEN = 1 ;
DAC1DFLT = 0 x8000 ;
DAC1CONbits .DACFDIV = val orD ac ;
DAC1CONbits .FORM = 1 ;
DAC1CONbits .AMPON = 1 ;
DAC1CONbits .DACEN = 1 ;
}
slaves/dac.c

137
Clase ’dac.h’

void initDac ( void ) ;


slaves/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 −−−−−−−−−−−−−−−−−−−−−−−−−

extern const i n t seno [ largoTablaSenos ] ;


e x t e r n c o n s t i n t diviWah [ 6 4 ] ;
extern const long int parteMultiplicadores [ 6 4 ] ;
e x t e r n c o n s t i n t fCuadrado [ 6 4 ] ;
extern int l i m i t e I f ;
extern int limiteLog ;
extern int frecuenciaVibrato ;
extern int frecCorte ;
e x t e r n i n t magnitudTremolo ;
extern int frecuenciaTremolo ;
e x t e r n u n s i g n e d i n t magnitudEco ;
extern i n t atrasoEco ;
e x t e r n i n t asmFunction ( i n t , i n t ) ;
extern int valorVibrato ;
i n t p u n t e r o A g r e g a r =0;
i n t punteroGuardar =0;
long int valorLargo ;
i n t * paraDevolver ;
i n t u l t i m o s [ maximoEco ] ;
i n t s a l i d a A n t e s =0;
i n t s a l i d a H a c e 2 =0;
int copiaActual ;
int parametrosEfectos [ 6 ] ;
u n s i g n e d i n t punteroTremolo =0;

//−−−−−−−−−−−−−−−−−−−−−−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++;
}

//−−−−−−−−−−−−−−−−−−− E f e c t o s y s u s métodos a u x i l i a r e s −−−−−−−−−−−−−−−−−−−−

v o i d grabarDato ( i n t dato ) { // Este método guarda l o s ú l t i m o s s a m p l e s


g e n e r a d o s , s i r v e para e l e f e c t o ’ e c o ’ .

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 ;
}

i n t t r e m o l o ( i n t v a l o r A c t u a l ) { // Este método l o que hace e s m u l t i p l i c a r una


s e ñ a l por una onda s i n u i d a l más una c o n s t a n t e .

i n t m u l t i p l i c a d o r =( s e n o [ punteroTremolo ] −512) * magnitudTremolo +32256;


// ’ m u l t i p l i c a d o r ’ e s e l v a l o r de l a onda s i n u i d a l más l a c o n s t a n t e .
// ’ magnitudTremolo ’ e s un número e n t r e 1 y 6 3 .
// s e n o [ x ] e s un número e n t r e −512 y 5 1 2 .
//De e s t a forma ’ m u l t p l i c a d o r ’ pasa a s e r un número e n t r e −32256 y 3 2 2 5 6 .
// S i magnitudTremlo v a l e c e r o , e n t o n c e s m u l t i p l i c a d o r e s una c o n s t a n t e .
// S i magnitudTremolo v a l e 6 3 , dado que ( −512) * 63=32256 , m u l t i p l i c a d o r v a l e
l a s i n u i d a l pura .

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 ’

r e t u r n * p a r a D e v o l v e r ; // Finalmente , s e d e v u e l v e ’ * 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 .
}

v o i d aumentarPunteroTremolo ( ) { // Este método s e e n c a r g a de aumentar e l


v a l o r d e l c u a l s e va a tomar e l s e n o para e l e f e c t o t ré m o l o .

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 ;
}
}

i n t wahwah ( i n t v a l o r A c t u a l ) { // Este método d e v u e l v e pasa l a s e ñ a l por un


f i l t r o p a s a b a j o s con dos p o l o s .
//La f r e c u e n c i a de c o r t e e s Fs /2 s i ’ f r e c C o r t e =63 ’ , 0 s i ’ f r e c C o r t e =0’

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) .

s a l i d a A n t e s=asmFunction ((& v a l o r L a r g o ) , ( ( diviWah [ f r e c C o r t e ] ) ) ) ; //Dado


que e l d i v i s o r no depende de l o s v a l o r e s de y ( n ) , tenemos l o s
d i v i s o r e s guardados en una t a b l a .
//La f u n c i ó n ’ asmFunction ’ , r e a l i z a l a d i v i s i ó n : ’ ValorLargo ’ / ’ diviWah ’

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 .

v o i d v i b r a t o ( ) { // Esta f u n c i ó n l o que hace e s cambiar l a f r e c u e n c i a de


s a l i d a d e l DAC.

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 .

DAC1CONbits .DACFDIV=valorDac −1;


return ;
}
i f ( v a l o r V i b r a t o <40){ // S i v a l o r V i b r a t o <40 , 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 .

DAC1CONbits .DACFDIV=val orD ac +1;


return ;
}
DAC1CONbits .DACFDIV=v al orD ac ;
}
slaves/efectos.c

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
−−−−−−−−−−−−−−−−−−−−−−−−−

unsigned short i n t recibiPunVel ;


unsigned i n t recibiPunEfecto ;
s t r u c t FlagType Flag ;
extern short int recibiDatoProg ;
e x t e r n i n t datoProgL ;
e x t e r n i n t datoProgH ;
e x t e r n i n t datoProg ;
e x t e r n s h o r t i n t terminoPrograma ;
extern char recibiCRC dato ;
e x t e r n c h a r recibiCRC ;

//−−−−−−−−−−−−−−−−−−−−−−−Método para i n c i a r e l I2C−−−−−−−−−−−−−−−−−−−−−−−−

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 .

u n s i g n e d c h a r Temp ; // Se usa para l e c t u r a s temporales futuras .

i f ( ( I2C1STATbits .R W == 0 )&&(I2C1STATbits . D A == 0 ) ) { // Compruebo s i


e s t e n hablando con mi d i r e c c i ó n y s e s o l i c i t o una e s c r i t u r a .

Temp = I2C1RCV ; // Leo e l dato r e c i b i d o .

Flag . AddrFlag = 1 ; // Aviso que e l próximo dato va a s e r l a d i r e c c i ó n


d e n t r o de l a memoria .

}
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 .

i f ( Flag . AddrFlag ) { // Chequeo s i e l dato que r e c i bı́ ( y t o d a vı́ a no l eı́ )


l a d i r e c c i ó n d e n t r o de ’ RAMBuffer [ 2 0 ] ’ donde debo e s c r i b i r .

145
Flag . AddrFlag = 0 ;
Flag . DataFlag = 1 ; // Aviso que e l próximo byte e s e l de d a t o s .

Temp=I2C1RCV ; // Leo e l dato que r e c i bı́ .

RAMPtr = RAMPtr + Temp ; //RAMPtr e r a l a d i r e c c i ó n donde comienza


’ RAMBuffer ’ , ahora e s l a d i r e c c i ó n donde debo e s c r i b i r .

i f (Temp==0){ // S o l o para que s i temp =0 no p r e g u n t e por l o s o t r o s


if .

}
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 .

Temp=I2C1RCV ; // Leo e l dato que r e c i bı́ .

*RAMPtr = ( u n s i g n e d c h a r )Temp ; // Guardo e l dato en l a memoria .

i f ( r e c i b i P u n V e l ) { // S i l o que r e c i bı́ f u e una v e l o c i d a d , a v i s o que ya


e s t o y l i s t o para g e n e r a r un s o n i d o .

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 ó .

datoProgH=RAMBuffer [ 1 0 ] * 0 x100 ; // M u l t i p l i c o e l byte más


s i g n i f i c a t i v o d e l i n t por 0 x100 para c o r r e r l o dos p o s i c i o n e s a
l a d e r e c h a y l e sumo e l menos s i g n i f i c a t i v o

datoProgL=RAMBuffer [ 1 1 ] ; // El r e s u l t a d o l o almaceno en datoProg


que s e r á e l que g r a b a ré en memoria .

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 .

Temp = I2C1RCV ; // Leo e l dato r e c i b i d o .

I2C1TRN = *RAMPtr ; // Leo e l dato de ’ RAMBuffer ’ y l o e n vı́ o vı́ a I2C a l


maestro .

I2C1CONbits . SCLREL = 1 ; // l i b e r o l a l i n e a SCL1

w h i l e ( I2C1STATbits .TBF) ; // Espero que t e r m i n e e l e n vı́ o .

RAMPtr = &RAMBuffer [ 0 ] ; // R e i n i c i o e l p u n t e r o a ’ RAMBuffer ’ .

}
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 ) ;

// e x t e r n u n s i g n e d c h a r RAMBuffer [ 2 5 6 ] ; //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

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”

// Este método c o n f i g u r a e l DSPic para que t r a b a j e con su o s c i l a d o r i n t e r n o


a su máxima v e l o c i d a d .
void InitClock ( ) {
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 ) { } ;
}
slaves/iniciarReloj.c

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”

v o i d mapearPuertos ( ) { // Este método s e e n c a r g a de mapear l o s p u e r t o s


remapeables .
TRISB=1;
AD1PCFGL=0xFFFF ;
builtin write OSCCONL (OSCCON & ˜(1<<6) ) ; // Desbloqueo l o s r e g i s t r o s .
RPINR18bits .U1RXR = 0 ; // Asigno U1RX a RP0 .
RPINR18bits . U1CTSR = 1 ; // Asigno U1CTS a RP1 .
RPOR1bits . RP2R = 3 ; // Asigno U1TX a RP2 .
RPOR1bits . RP3R = 4 ; // Asigno U1RTS a RP3 .
builtin write OSCCONL (OSCCON | (1<<6) ) ; // Bloqueo l o s r e g i s t r o s .
}
slaves/manejoPuertos.c

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) ;

//−−−−−−−−−−−−−−−Métodos en a s s e m b l e r que s e u t i l i z a r á n −−−−−−−−−−−−−−−−

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 ) ;

//−−−−−−−−−−−−−−−Banderas para l a g e n e r a c i ó n de s o n i d o s −−−−−−−−−−−−−−−−

u n s i g n e d s h o r t i n t bandera ; // 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 MIDI completa y e s n e c e s a r i o i n t e r p r e t a r l a .

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 .

//−−−Almacenamiento de d a t o s e x t e r n o s para g e n e r a c i ó n d e l s o n i d o −−−

u n s i g n e d s h o r t i n t RAMBuffer [ 2 0 ] ; //RAMBuffer [ 0 ] e s l a nota que hay que


reproducir .
//RAMBuffer [ 1 ] e s l a i n t e n s i d a d con l a que hay que r e p r o d u c i r l a nota .
//RAMBuffer [ 2 ] e s e l p r i m e r parámtero d e l p r i m e r e f e c t o que s e l e va
a p l i c a r al sonido .
//RAMBuffer [ 3 ] e s e l p r i m e r parámtero d e l segundo e f e c t o que s e l e va
a p l i c a r al sonido .
//RAMBuffer [ 4 ] e s e l segundo parámtero d e l p r i m e r e f e c t o que s e l e va
a p l i c a r al sonido .
//RAMBuffer [ 5 ] e s e l segundo parámtero d e l segundo e f e c t o que s e l e va
a p l i c a r al sonido .
//Cada e f e c t o t i e n e un número a s o c i a d o . RAMBuffer [ 6 ] e s e l número de e f e c t o
que s e va a a p l i c a r p r i m e r o .
//RAMBuffer [ 7 ] e s e l número d e l segundo e f e c t o que s e va a a p l i c a r .
//RAMBuffer [ 1 0 ] e s e l byte más s i g n i f i c a t i v o d e l i n t de data r e c i b i d o
cuando s e c a r g a e l nuevo i n s t r u m e n t o
//RAMBuffer [ 1 1 ] e s e l byte menos s i g n i f i c a t i v o d e l i n t de data r e c i b i d o
cuando s e c a r g a e l nuevo i n s t r u m e n t o
//RAMBuffer [ 1 2 ] e s donde s e g u a r d a r á e l CRC r e c i b i d o por e l maestro .
//RAMBuffer [ 1 3 ] e s donde s e g u a r d a r á e l byte r e s u l t a n t e de c a l c u l a r
n u e s t r o CRC.
//RAMBuffer [ 1 5 ] i n d i c a s i hay que cambiar e l i n s t r u m e n t o .

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 −−−−−−−−−−−−−

long int envols [ 1 5 ] ; // Guarda e l v a l o r de cada una de l a s máscaras .

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 .

i n t i n c r e m e n t o s [ 1 5 ] [ 7 ] ; //Una vez que s e r e c i b e l a s e ñ a l de t o c a r c i e r t a


nota , l o s v a l o r e s de l a t a b l a de i n c r e m e n t o s de e s a nota s e c o p i a n de
l a memoria de programación a e s t a m a t r i z .

u n s i g n e d i n t aumentosSeno [ 1 5 ] ; //Una vez que s e r e c i b e l a s e ñ a l de t o c a r


c i e r t a nota , l o s v a l o r e s de l a t a b l a de aumentosSeno de e s a nota s e
c o p i a n de l a memoria de programación a e s t e v e c t o r .

l o n g i n t cambios [ 1 5 ] [ 7 ] ; //Una vez que s e r e c i b e l a s e ñ a l de t o c a r


c i e r t a nota , l o s v a l o r e s de l a t a b l a de cambios de e s a nota s e c o p i a n
de l a memoria de programación a e s t e v e c t o r .

i n t * punteroMascaras [ 1 5 ] ; // Este v e c t o r t i e n e un p u n t e r o a l byte más


s i g n i f i c a t i v o de cada uno de l o s e l e m e n t o s d e l v e c t o r ’ e n v o l s ’
u n s i g n e d s h o r t i n t posDato ; //Cada i n s t r u c c i ó n MIDI e s t a compuesta por
v a r i o s bytes , e s t a v a r i a b l e i n d i c a l a c a n t i d a d r e c i b i d a de b y t e s de una
i n s t r u c c i ó n MIDI que s e e s t a r e c i b i e n d o .

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 nota ; // Esta v a r i a b l e i n d i c a l a nota que hay que


reproducir .

u n s i g n e d s h o r t i n t estoyEnNoteOff =0; // S i e s t o y en ’ n o t e o f f ’ cada


’ s a m p l e s B a j a r ’ s a m p l e s c a l c u l a d o s b a j o en 1 l a v e l o c 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 .

i n t * s a l i d a ; // Este v e c t o r apunta 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 d e l


número ’ s a l i d a L o n g ’ .

int salidaAUtilizar ; // Esta v a l e l o apuntado por e l p u n t e r o ’ s a l i d a ’

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

//−−−−−−−−−−−−−−V a r i a b l e s para l a r e p r o d u c c i ó n de e f e c t o s −−−−−−−−−−−−−−

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 l i m i t e L o g ; // 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 L o g ’ .

i n t v a l o r V i b r a t 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 que


f r e c u e n c i a u t i l i z a r en e l e f e c t o ’ v i b r a t o ’ .

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 magnitudTremolo ; // 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


magnitud de i n f l u e n c i a d e l e f e c t o ’ t ré m o l o ’ .

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 ’ .

u n s i g n e d i n t magnitudEco ; // 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 magnitud de l a i n f l u e n c i a d e l e f e c t o ’ e c 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 ’ .

//−V a r i a b l e s para l a r e p r o g r a m a c i ó n de l a memoria de programación−−

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 −−−−−−−−−−−−−−

//−−−−−−−−−−−−−−−−−−−−−Métodos para l a r e p r o g r a m a c i ó n de l a memoria de


programación ( para cambiar e l i n s t r u m e n t o )−−−−−−−−−−−−−−−−−−−−−−

//La mayorı́a de l o s métodos para l a reprogramamción s e e n c u e n t r a n en


’ Cargar P r o g S l a v e . c ’

v o i d r eal iza rCR C ( i n t v a l o r ) {


CRC += v a l o r ;
}

//−−−−−−−−−Método para l a i n t e r p r e t a c i ó n de comandos MIDI−−−−−−−−−−

//Cuando e l programa d e t e c t a que s e r e c i b i e r o n s u f i c i e n t e s d a t o s por I2C


como para poder g e n e r a r una nota , l l a m a a e s t e método .
void agregarNota ( ) {
unsigned short i n t i , j ;

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 ’

f o r ( i =0; i <15; i ++){


e n v o l s [ i ]= e n v o l s I n i c i a l e s [ nota ] [ i ] ; // Se c o p i a n en e l v e c t o r
’ e n v o l s ’ , l o s v a l o r e s con l o s c u a l e s comienzan t o d a s l a s máscaras
de l a nota a r e p r o d u c i r .

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 .

aumentosSeno [ i ]= TodosLosAumentosSeno [ nota ] [ i ] ; // Se c o p i a n en e l


v e c t o r ’ aumentosSeno ’ t o d o s l o s v a l o r e s de l a t a b l a
’ TodosLosAumentosSeno ’ , e s t a t a b l a i n d i c a l a f r e c u e n c i a de l a s
componentes de l a nota .

f o r ( j =0; j <7; j ++){


cambios [ i ] [ j ] = 1 0 * ( ( l o n g i n t ) todosLosCambios [ nota ] [ i ] [ j ] ) ; // Se
c o p i a n en l a m a t r i z ’ cambios ’ l o s e l e m e n t o s de l a m a t r i z
’ todosLosCambios ’ 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 a i n f o r m a c i ó n para s a b e r cuando cada
e n v o l v e n t e debe v a r i a r su i n c r e m e n t o .
// Se m u l t i p l i c a por 10 por que para a h o r r a r memoria de programación l o s
cambios s e guardan d i v i d i o s e n t r e 1 0 .

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 ;
}

//−−−−−−−−−−−−Método para c a l c u l a r e l v a l o r de l o s samples−−−−−−−−−−−−−

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 ] ’

i f ( t i e m p o s [ 0 ] > ( l a r g o T a b l a S e n o s −1) ) { // S i e l próximo v a l o r de l a t a b l a


de s e n o s e s mayor a l l a r g o de l a t a b l a s e v u e l v e a comenzar d e s d e
e l p r i n c i p i o de l a t a b l a . Por e j e m p l o s e n o ( 4 1 0 )=s e n o ( 5 0 ) (En g r a d o s )

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;
}

//−−−−−−−−−−−−−C á l c u l o de l a s o t r a s componentes d e l sample−−−−−−−−−−−−−

// Las r e s t a n t e s componentes d e l sample s e c a l c u l a n de forma análoga , e s t a


o p e r a c i ó n s e p o d rı́ a haber r e a l i z a d o con un ’ f o r ’ , p e r o de e s t a forma
consume menos c i c l o s de r e l o j .

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;
}

//−−−−−−−−−−−−−Fin d e l c a l c u l o de l a s componentes r e s t a n t e s −−−−−−−−−−−−

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 .

i f ( estoyEnNoteOff ) { // S i e s t o y en n o t e o f f hay que gradualmente


v a j a r l a i n t e n s i d a d de l a nota .

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 .

i f ( v e l o c i d a d >0){ // S i t o d a vı́ a l a v e l o c i d a d no l l e g ó a l mı́nimo , l a


bajo .

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;
}
}
}
}
}

//−−−−−−−−−−−−−Métodos para l a g e n e r a c i ó n de l o s e f e c t o s −−−−−−−−−−−−−−

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 ;
}

v o i d a p l i c a r 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 e s e l programa


e n c a r g a d o de a p l i c a r l e e l e f e c t o s e l e c c i o n a d o a e l s o n i d o .
//Hay que l l a m a r l o una vez con e l parámetro 6 para a p l i c a r 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 p l i c a r e l segundo .
// Es llamado l u e g o de g e n e r a r cada sample .

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 ;
}

//−−−−−−−−−−−−−−−−−−−−−−−−Método P r i n c i p a l ( Main )−−−−−−−−−−−−−−−−−−−−−−−−


i n t main ( v o i d ) {
//−−−−−−−−−−−−−−−D e c l a r a c i ó n e i n i c i a l i z a c i ó n de v a r i a b l e s −−−−−−−−−−−−−−−
unsigned short i n t i ;
posDato =0;
s a l i d a=&s a l i d a L o n g ;
s a l i d a=s a l i d a +1; // E s t a s dos l i n e a s hacen que e l v e c t o r ’ s a l i d a ’ 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 ’ s a l i d a L o n g ’

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 .

i2c1 init () ; // I n i c i a l i z a c i ó n de l a i n t e r f a z I2C .

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

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 * 6 ; //Aumento e l volumen .


// Este aumento de volumen e s t a p u e s t o aca también pensando en un f u t u r o
para poder cambiar e l número 6 por una v a r i a b l e de una p e r i l l a .

grabarDato ( s a l i d a A U t i l i z a r ) ; // Grabo e l dato en l a memoria de l o s


ú l t i m o s s a m p l e s c a l c u l a d o s . S i r v e para e l e f e c t o e c o .

aumentarPunteroTremolo ( ) ; //Aumento e l p u n t e r o a u x i l i a r que s e usa para


e l e f e c t o de t ré m o l o .

aplicarEfectos (6) ; // A p l i c o l o s e f e c t o s .
aplicarEfectos (7) ;

w h i l e ( DAC1STATbits .RFULL==1){ // Espero a que e l DAC t e r m i n e de


p r o c e s a r e l sample que e s t a p r o c e s a n d o a s i s e l i b e r a un l u g a r en su
c o l a de d a t o s .

}
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 .

i f ( bandera >1){ // S i ’ bandera ’ e s mayor a 1 e s por que s e r e c i b i ó


una i n s t r u c c i ó n m u s i c a l .

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 −−−−−−−−−−−−−

// Este método c o n f i g u r a e l ADC.

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 −−−−−−−−−−−−−−−−−−−−−

v o i d realizoCRC ( s h o r t i n t v a l o r ) { // Simple a l g o r i t m o para c h e q u e a r e l CRC,


suma t o d o s l o s v a l o r e s y l u e g o s e comparará e l byte menos s i g n i f i c a t i v o .
//En l a v e r s i ó n f i n a l no e s t a implementado , p e r o l o c o n s i d e r a m o s ú t i l
para una f u t u r a mejora d e l programa .

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ı́

for ( pic = direccionDesde ;


p i c <( d i r e c c i o n D e s d e+numeroPics ) ; p i c++){ // Se l o s e n vı́ o a t o d o s
l o s p i c s que hay en e l d i s p o s i t i v o

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 ,

U2TXREG = 0 x15 ; // b a j o l a bandera de p r o g r a m a c i ó n A c t i v a y v u e l v o a l


main d e l programa .

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

extern s h o r t i n t dato2 , programacionActiva , p o s B u f f e r , b u f f e r L i s t o ;


extern unsigned short i n t e r r o r ;
extern l o n g i n t CRC;
extern s h o r t i n t * CRC L ;
extern s h o r t i n t CRC L recibido ;
extern c h a r bandera2 ;
extern char j ;
extern int llevoCuenta ;
extern i n t b u f f e r D a t a [ 2 * UNA RONDA+ 1 0 ] ;

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”

v o i d r e t a r d o ( i n t c i c l o s ) { // Este método s o l o hace un r e t a r d o , s e usa para


l a e s p e r a e n t r e t r a n s m i c i o n e s por I2C .

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 () ;
}

v o i d i 2 c s t a r t ( v o i d ) { // Esta f u n c i ó n i n i c i a una c o m u n i c a c i ó n v i a I2C .

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) ;
}

c h a r s e n d i 2 c b y t e ( i n t data ) { // Esta f u n c i ó n e s para mandar un byte por


I2C .
// El p r o t o c o l o de c o m u n i c a c i ó n con memorias vı́ a I2C i m p l i c a i n s t r u c c i o n e s
de más de un byte por vez .
// Por e s o e s t a no e s l a f u n c i ó n que vamos a u t i l i z a r para mandar d a t o s vı́ a
I2C .

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 ;
}

//−−−−−−−−−−−−−−F u n c i o n e s de I2C que s e llaman d e s d e a f u e r a −−−−−−−−−−−−−−

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 ) { // Esta f u n c i ó n graba


una p o s i c i ó n de una memoria vı́ a I2C .
// Esta e s l a f u n c i ó n que u t i l i z a m o s para comunicarnos con l o s e s c l a v o s .
//Lo que hace e s u t i l i z a r l a s f u n c i o n e s a n t e r i o r e s de e s c r i t u r a y l e c t u r a
de bytes , p e r o s i g u e n d o e l p r o t o c o l o .
// Devuelve 0 s i no hay un e r r o r .

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 ;
}

c h a r I2Cread ( c h a r addr , c h a r subaddr ) { // Esta f u n c i ó n l e e una p o s i c i ó n de


una memoria vı́ a I2C .
// Esta e s l a f u n c i ó n que u t i l i z a m o s para l e e r de l o s e s c l a v o s .
//Lo que hace e s u t i l i z a r l a s f u n c i o n e s a n t e r i o r e s de e s c r i t u r a y l e c t u r a
de bytes , p e r o s i g u e n d o e l p r o t o c o l o .
// Devuelve 0 s i no hay un e r r o r .

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 I 2 C p o l l ( c h a r addr ) { // Esta e s l a f u n c i ó n que s e usa para


saber s i c i e r t o e s c l a v o esta conectado .
// Devulve 0 s i e s t a c o n e c t a d o .

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 −−−−−−−−−−−−−−−−−−−−−−−−

v o i d mapearPuertos ( ) { // Este método a s i g n a l o s p u e r t o s s e r i e en l o s


puertos remapeables .

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 .

v o i d InitUART1 ( ) { // Este método c o n f i g u r a e l p u e r t a s e r i e 1 para 31250 8


N 1 , i n t e r r u p t i v o en l a r e c e p c i ó n .

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 ;
}

// El p u e r t o s e r i e 2 e s e l que s e comunica con e l s o f t w a r e de PC.

v o i d InitUART2 ( ) { // Este método c o n f i g u r a e l p u e r t a s e r i e 2 para 9600 8


N 1 , i n t e r r u p t i v o en l a r e c e p c i ó n .

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 −−−−−−−−−−−−−−

void attribute ( ( i n t e r r u p t , n o a u t o p s v ) ) U1RXInterrupt ( v o i d ) {


// I n t e r r u p c i ó n a l r e c i b i r d a t o s por e l p u e r t o s e r i e 1 .

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 ’

U1TXREG=dato ; //Hago un eco , s i r v e s o l o para c o n t r o l a r e l programa .

i f ( dato >127 && dato <=143){ // R e c i bı́ l a s e ñ a l de ’ n o t e o f f ’ ?

posDato =0;
f l a g N o t e O f f =1;
}

e l s e i f ( dato >=144){ // S i dato e s mayor a 144 e s por que r e c i bı́ l a s e ñ a l


de ’ n o t e on ’ .

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 .

void attribute ( ( i n t e r r u p t , n o a u t o p s v ) ) U2RXInterrupt ( v o i d ) {


// I n t e r r u p c i ó n a l r e c i b i r d a t o s por e l p u e r t o s e r i e 2 .

dato2 = U2RXREG; // Guardo e l dato r e c i b i d o en l a v a r i a b l e ’ dato ’ .

i f ( p r o g r a m a c i o n A c t i v a==1){ // Chequeo s i ya e s t o y programando un


instrumento .

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 .

p o s B u f f e r ++; // A c t u a l i z o l a p o s i c i ó n de memoria que c o r r e s p o n d e .

i f ( p o s B u f f e r >=(2*UNA RONDA) ) { // chequeo s i ya r e c i bı́ ’UNA RONDA ’ de


d a t o s ( cada dato ocupa 2 b y t e s ) .

p o s B u f f e r =0; // S i ya r e c i bı́ una ronda de datos , r e i n i c i o l a c u e n t a


y a v i s o que ya r e c i bı́ una ronda de d a t o s .

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 r o g r a m a c i o n A c t i v a =1; // Aviso que e n t r o en e s t a d o de programación .

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 .

i n t r c o n a u x = RCON; // S i e s t e e s e l caso , e n vı́ o v a l o r e s de c o n t r o l


a l PC.

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 .

void attribute (( interrupt , no auto psv ) ) U1TXInterrupt ( v o i d ) {


I F S 0 b i t s . U1TXIF = 0 ;
}

void attribute (( interrupt , no auto psv ) ) U2TXInterrupt ( v o i d ) {


I F S 1 b i t s . U2TXIF = 0 ;
}
master/serie.c

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 −−−−−−−−−−−−−−−−−−−−−−−−

u n s i g n e d s h o r t i n t r e c i b i , posDato , v e l o c i d a d , nota , f u n c i o n A c t u a l , bandera ;


unsigned short i n t destino ;
unsigned i n t datosFuncion [ 2 ] ;
int adc buffer [ numPerillas ] ;
unsigned short i n t a d c i ;
unsigned i n t adc valor ;
unsigned short i n t * adc valor2 ;
unsigned i n t adc bandera ;
unsigned i n t adc dato ;
i n t a d c v a l o r I n t = 0 x1234 ;
unsigned short i n t t a b l a P e r i l l a s [ ] = { 5 , 7 , 6 , 3 , 4 , 2 } ;
i n t b u f f e r D a t a [ 2 * UNA RONDA+ 1 0 ] ;
s h o r t i n t programacionActiva , p o s B u f f e r , b u f f e r L i s t o , dato2 ;
u n s i g n e d s h o r t i n t e r r o r =0;
l o n g i n t CRC;
s h o r t i n t * CRC L ;
s h o r t i n t CRC L recibido ;
c h a r bandera2 =0;
char j , k , encontreNota ;
int llevoCuenta = 0;
u n s i g n e d s h o r t i n t e r r o r 2 =0;
s h o r t i n t flagNoteOn , f l a g N o t e O f f ;
u n s i g n e d s h o r t i n t notasGuardadas [ numeroPics ] ;

//−−−−−−−−−−−−−−−−−−−−−−−−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 ) { } ;
}

//−−−−−−−−−−−−−−−−−−−I n t e r p r e t a c i ó n de comandos MIDI−−−−−−−−−−−−−−−−−−−−

v o i d a g r e g a r N o t a ( ) { // Este método i n t e r p r e t a l o s comandos MIDI .

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 .

aux=ADC1BUF0 ; // Guardo e l dato l eı́ d o .

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 .

i f ( a d c i ==1|| a d c i ==2){ // S i e l dato que l eı́ c o r r e s p o n d e a una


p e r i l l a c o r r e s p o n d i e n t e a l a s e l e c c i ó n de e f e c t o , t e n g o que
mandar un número d e l 0 a l 7 .

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

P u b l i c Sub New( ByVal i n s As i n s t r u m e n t o , ByVal r u t a As S t r i n g , ByVal


yaG As Boolean )
InitializeComponent ()
miInstrumento = i n s
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 ”
LImportando . V i s i b l e = F a l s e
ProgressBar1 . V i s i b l e = False
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
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
yaGuardo = yaG
ultimaRuta = r u t a
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 e d i t a r M a s c a r a ( ByVal numeroMascara As I n t e g e r )


Dim vent As c r e a r M a s c a r a
Try
vent = New c r e a r M a s c a r a (Me, numeroNota − 1 , numeroMascara )
vent . ShowDialog ( )
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 )
Me . Enabled = True
End Try
End Sub

P r i v a t e Sub guardarComo ( ByVal r u t a D e s t i n o As S t r i n g )


Try
Dim f s As New F i l e S t r e a m ( r u t a D e s t i n o , FileMode . C r e a t e )
Dim b f As New BinaryFormatter
b f . S e r i a l i z e ( f s , miInstrumento )
f s . Close ()
yaGuardo = True
ultimaRuta = r u t a D e s t i n o
Catch ex As E x c e p t i o n
MsgBox ( ” i m p o s i b l e g u a r d a r ” )
End Try
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

P r i v a t e Sub importarMascara ( ByVal d e s f a s a j e As I n t e g e r )

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 ”

P r i v a t e Sub P a n e l 2 C l i c k ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles Panel2 . C l i c k
esDeArriba = F a l s e
Dim p o s i c i o n X As I n t e g e r = ( M o u s e P o s i t i o n − Me . L o c a t i o n ) .X
Dim p o s i c i o n Y As I n t e g e r = ( M o u s e P o s i t i o n − Me . L o c a t i o n ) .Y
I f p o s i c i o n X > 6 And p o s i c i o n X < 1025 And p o s i c i o n Y > 49 And
p o s i c i o n Y < 199 Then
Dim numOctava As I n t e g e r = Fix ( 1 + ( ( p o s i c i o n X ) − 6 ) / 1 4 0 )
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 ” +
numOctava . T o S t r i n g
I f p o s i c i o n Y > 48 And p o s i c i o n Y < 137 Then
Dim i As I n t e g e r
For i = 0 To l i s t a X A r r i b a . Count − 1
I f ( p o s i c i o n X − 6 ) >= l i s t a X A r r i b a ( i ) And ( p o s i c i o n X −
6 ) <= ( l i s t a X A r r i b a ( i ) + 1 4 ) Then
esDeArriba = True
teclaActualArriba = i
End I f
Next
End I f
Dim r e c t As New R e c t a n g l e ( 6 + 20 * t e c l a A c t u a l − 1 4 , 1 , 5 0 ,
150)
Panel2 . I n v a l i d a t e ( r e c t )
t e c l a A c t u a l = Fix ( ( ( ( M o u s e P o s i t i o n − Me . L o c a t i o n ) .X) − 6 ) / 2 0 )
r e c t = New R e c t a n g l e ( 6 + 20 * t e c l a A c t u a l − 1 4 , 1 , 5 0 , 1 5 0 )
Panel2 . I n v a l i d a t e ( r e c t )
I f esDeArriba Then
numeroNota = ( numOctava − 1 ) * 12 +
l i s t a N o t a s A r r i b a ( t e c l a A c t u a l A r r i b a − ( numOctava − 1 ) *
5)
Else
numeroNota = ( numOctava − 1 ) * 12 +
l i s t a N o t a s A b a j o ( t e c l a A c t u a l − ( numOctava − 1 ) * 7 )
End I f
I f numeroNota = 87 Then
numeroNota = 86
End I f
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 ” +
numeroNota . T o S t r i n g

190
End I f
rearmarPaneles ()
End Sub

P r i v a t e Sub P a n e l 2 P a i n t ( ByVal s e n d e r As Object , ByVal e As


System . Windows . Forms . PaintEventArgs ) Handles Panel2 . P ai nt
I f esLaPrimerVez Then
l i s t a X A r r i b a = New L i s t ( Of I n t e g e r )
End I f
Dim g r a f i c o As G r ap hi cs = e . G ra ph i cs
Dim punta As New Pen ( C o l o r . Black , 4 )
Dim posX As I n t e g e r = 2
Dim j As I n t e g e r
Dim i As I n t e g e r
For i = 0 To 50
I f t e c l a A c t u a l = i And esDeArriba = F a l s e Then
g r a f i c o . F i l l R e c t a n g l e ( Brushes . Red , posX , 2 , 2 0 , 1 5 0 )
Else
g r a f i c o . F i l l R e c t a n g l e ( Brushes . LightGray , posX , 2 , 2 0 , 1 5 0 )
End I f
g r a f i c o . DrawRectangle ( punta , posX , 2 , 2 0 , 1 5 0 )
posX = posX + 20
Next
posX = 15
Dim numTecla As I n t e g e r = 0
For i = 0 To 6
For j = 0 To 1

I f t e c l a A c t u a l A r r i b a = numTecla And esDeArriba Then


g r a f i c o . F i l l R e c t a n g l e ( Brushes . Red , posX , 2 , 1 4 , 9 0 )
Else
g r a f i c o . F i l l R e c t a n g l e ( Brushes . Black , posX , 2 , 1 4 , 9 0 )
End I f
I f esLaPrimerVez Then
l i s t a X A r r i b a . Add( posX )
End I f
g r a f i c o . DrawRectangle ( punta , posX + 2 , 2 , 1 2 , 8 8 )
posX = posX + 20
numTecla = numTecla + 1
Next
posX = posX + 20
For j = 0 To 2
I f t e c l a A c t u a l A r r i b a = numTecla And esDeArriba Then
g r a f i c o . F i l l R e c t a n g l e ( Brushes . Red , posX , 2 , 1 4 , 9 0 )
Else
g r a f i c o . F i l l R e c t a n g l e ( Brushes . Black , posX , 2 , 1 4 , 9 0 )
End I f
I f esLaPrimerVez Then
l i s t a X A r r i b a . Add( posX )
End I f
g r a f i c o . DrawRectangle ( punta , posX + 2 , 2 , 1 2 , 8 8 )
posX = posX + 20
numTecla = numTecla + 1
Next
posX = posX + 20
Next
esLaPrimerVez = F a l s e
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

P r i v a t e Sub BDeArchivo1 Click ( ByVal s e n d e r As System . Object , ByVal e


As System . EventArgs ) Handles BDeArchivo1 . C l i c k
importarMascara ( −1)
rearmarPaneles ()
End Sub

P r i v a t e Sub I m a g e n F r e c u e n c i a 1 D o u b l e C l i c k ( ByVal s e n d e r As Object ,


ByVal e As System . EventArgs ) Handles Im a ge n F re c ue n c ia 1 . D o u b l e C l i c k
editarMascara ( primerEnvolvente − 1)
End Sub

P r i v a t e Sub c r e a r I n s t r u m e n t o D i s p o s e d ( ByVal s e n d e r As Object , ByVal e


As System . EventArgs ) Handles Me . D i s p o s e d
For Each vent As c r e a r M a s c a r a In l i s t a H i j o s
vent . D i s p o s e ( )
Next
End Sub

P r i v a t e Sub B E d i t a r M a s c a r a 1 C l i c k ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles BEditarMascara1 . C l i c k
editarMascara ( primerEnvolvente − 1)
End Sub

P r i v a t e Sub B E d i t a r M a s c a r a 2 C l i c k ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles BEditarMascara2 . C l i c k
editarMascara ( primerEnvolvente )
End Sub

P r i v a t e Sub B E d i t a r M a s c a r a 3 C l i c k ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles BEditarMascara3 . C l i c k
editarMascara ( primerEnvolvente + 1)
End Sub

P r i v a t e Sub I m a g e n F r e c u e n c i a 2 D o u b l e C l i c k ( ByVal s e n d e r As Object ,


ByVal e As System . EventArgs ) Handles Im a ge n F re c ue n c ia 2 . D o u b l e C l i c k
editarMascara ( primerEnvolvente )
End Sub

P r i v a t e Sub I m a g e n F r e c u e n c i a 3 D o u b l e C l i c k ( ByVal s e n d e r As Object ,


ByVal e As System . EventArgs ) Handles Im a ge n F re c ue n c ia 3 . D o u b l e C l i c k
editarMascara ( primerEnvolvente + 1)
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

P r i v a t e Sub B F r e c u e n c i a 2 C l i c k ( ByVal s e n d e r As System . Object , ByVal e


As System . EventArgs ) Handles BFrecuencia2 . C l i c k
Try
Dim f r e c As Double = ( 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 ( primerEnvolvente ) = 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

P r i v a t e Sub B F r e c u e n c i a 3 C l i c k ( ByVal s e n d e r As System . Object , ByVal e


As System . EventArgs ) Handles BFrecuencia3 . C l i c k
Try
Dim f r e c As Double = ( 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 ( 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

P r i v a t e Sub B I n t e r p o l a r C l i c k ( ByVal s e n d e r As System . Object , ByVal e


As System . EventArgs ) Handles B I n t e r p o l a r . C l i c k
I f NHastaInter . Value − NDesdeInter . Value < 2 Then
MsgBox ( ” v e r i f i q u e que l o s números e s t e n b i e n . . . ” ,
MsgBoxStyle . C r i t i c a l )
Else
Dim i As I n t e g e r
Dim mas As mascara
Dim masDesde As mascara = miInstrumento . n o t a s ( numeroNota −
1 ) . l i s t a M a s c a r a s ( NDesdeInter . Value − 1 )
Dim masHasta As mascara = miInstrumento . n o t a s ( numeroNota −
1 ) . l i s t a M a s c a r a s ( NHastaInter . Value − 1 )
Dim mulDesde As Double = 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 ( NDesdeInter . Value − 1 )
Dim mulHasta As Double = 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 ( NHastaInter . Value − 1 )
Dim pon1 As I n t e g e r = 1
Dim pon2 As I n t e g e r = NHastaInter . Value − NDesdeInter . Value − 1
Dim vent As c r e a r M a s c a r a
I f N I n t e r . Value = 1 Then
For i = NDesdeInter . Value To NHastaInter . Value − 2

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

P r i v a t e Sub BDeArchivo2 Click ( ByVal s e n d e r As System . Object , ByVal e


As System . EventArgs ) Handles BDeArchivo2 . C l i c k
importarMascara ( 0 )
rearmarPaneles ()
End Sub

P r i v a t e Sub BDeArchivo3 Click ( ByVal s e n d e r As System . Object , ByVal e


As System . EventArgs ) Handles BDeArchivo3 . C l i c k
importarMascara ( 1 )
rearmarPaneles ()
End Sub

P r i v a t e Sub GuardarComoToolStripMenuItem Click ( ByVal s e n d e r As


System . Object , ByVal e As System . EventArgs ) Handles
GuardarComoToolStripMenuItem . C l i c k
guardarAntesRuta ( )
End Sub

P r i v a t e Sub B G e n e r a r C l i c k ( ByVal s e n d e r As System . Object , ByVal e As


System . EventArgs ) Handles BGenerar . C l i c k
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 ( ) = ” . wav”
vent . AddExtension = True
vent . F i l t e r = ” a r c h i v o s de s o n i d o ( * . wav ) | * . wav”
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

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 ImportarDesdeHexToolStripMenuItem Click ( ByVal s e n d e r As


System . Object , ByVal e As System . EventArgs ) Handles
ImportarDesdeHexToolStripMenuItem . C l i c k
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 = ” a s s e m b l e r ( * . hex ) | * . hex ”
vent . ShowDialog ( )
I f vent . FileNames . Length > 0 Then
Try
LImportando . V i s i b l e = True
P r o g r e s s B a r 1 . V i s i b l e = True
Dim i n s t r As New i n s t r u m e n t o ( vent . FileName , 1 5 0 0 0 , 3 0 0 0 0 )
Me . miInstrumento = i n s t r
Dim i As I n t e g e r
Dim ventAux 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
ventAux = New c r e a r M a s c a r a (Me, j , i , True , True )
Next
P r o g r e s s B a r 1 . Value = ( j * 100 / 8 5 )
Next
rearmarPaneles ()
Catch ex As E x c e p t i o n
Me . Enabled = True
MsgBox ( ” I m p o s i b l e i m p o r t a r d 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
LImportando . V i s i b l e = F a l s e
ProgressBar1 . V i s i b l e = False
End I f
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

P r i v a t e Sub B E x p l o r a r C l i c k ( ByVal s e n d e r As System . Object , ByVal e As


System . EventArgs ) Handles BExplorar . C l i c k
Dim vent As New F o l d e r B r o w s e r D i a l o g
vent . ShowDialog ( )
TRuta . Text = vent . S e l e c t e d P a t h
End Sub

P r i v a t e Sub B N o r m a l i z a r C l i c k ( ByVal s e n d e r As System . Object , ByVal e


As System . EventArgs ) Handles BNormalizar . C l i c k
I f NNormalizarHasta . Value − NNormalizarDesde . Value < 0 Then
MsgBox ( ” E l e j i s t e mal l o s números . . . ” , MsgBoxStyle . C r i t i c a l )
Else
Dim i As I n t e g e r
For i = NNormalizarDesde . Value To NGenerarHasta . Value
miInstrumento . n o t a s ( i − 1 ) . g e n e r a r S o n i d o ( 6 0 0 0 0 , 3 0 0 0 0 )
Next
End I f
rearmarPaneles ()
End Sub

P r i v a t e Sub B G e n e r a r V a r i o s C l i c k ( ByVal s e n d e r As System . Object , ByVal


e As System . EventArgs ) Handles BGenerarVarios . C l i c k
I f NGenerarHasta . Value − NGenerarDesde . Value < 0 Then
MsgBox ( ” E l e j i s t e mal l o s números . . . ” , MsgBoxStyle . C r i t i c a l )
Else
Dim s a l i o B i e n As Boolean = True
Dim i As I n t e g e r
For i = NGenerarDesde . Value − 1 To NGenerarHasta . Value − 1
I f s a l i o B i e n Then
s a l i o B i e n = s a l i o B i e n And
metodosComunes . grabarWav ( miInstrumento . n o t a s ( i ) . g e n e r a r S o n i d o ( ND
* 3 0 , 3 0 0 0 0 ) , TRuta . Text + ”\ nota ” + ( i +
1 ) . T o S t r i n g + ” . wav” )
End I f
Next

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

P r i v a t e Sub B E x t r a p o l a r N o t a 1 C l i c k ( ByVal s e n d e r As System . Object ,


ByVal e As System . EventArgs ) Handles BExtrapolarNota1 . C l i c k
I f NNotaHasta1 . Value − NNotaDesde1 . Value < 2 Then
MsgBox ( ” Los v a l o r e s i n g r e s a d o s no son c o r r e c t o s . ” ,
MsgBoxStyle . C r i t i c a l )
Else
Dim i As I n t e g e r
Dim j As I n t e g e r
Dim pon1 As I n t e g e r = 1
Dim pon2 As I n t e g e r = NNotaHasta1 . Value − NNotaDesde1 . Value − 1
Dim vent As c r e a r M a s c a r a
For i = NNotaDesde1 . Value To NNotaHasta1 . Value − 2
Dim masDesde As mascara
Dim masHasta As mascara
Dim mulDesde As Double
Dim mulHasta As Double
For j = 0 To 14
masDesde = miInstrumento . n o t a s ( NNotaDesde1 . Value −
1) . l i s t a M a s c a r a s ( j )
masHasta = miInstrumento . n o t a s ( NNotaHasta1 . Value −
1) . l i s t a M a s c a r a s ( j )
mulDesde = miInstrumento . n o t a s ( NNotaDesde1 . Value −
1) . l i s t a C o e f i c i e n t e s ( j )
mulHasta = miInstrumento . n o t a s ( NNotaHasta1 . Value −
1) . l i s t a C o e f i c i e n t e s ( j )
I f mulDesde * pon2 > 0 And mulHasta * pon1 > 0 Then
Dim mas As New mascara ( masDesde , masHasta ,
mulDesde * pon2 , mulHasta * pon1 , ” s i n ” )
miInstrumento . n o t a s ( i ) . l i s t a M a s c a r a s ( j ) = mas
vent = New c r e a r M a s c a r a (Me, i , j , True , True )
End I f
Next

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 B E x t r a p o l a r N o t a 2 C l i c k ( ByVal s e n d e r As System . Object ,


ByVal e As System . EventArgs ) Handles BExtrapolarNota2 . C l i c k
I f NNotaHasta1 . Value − NNotaDesde1 . Value < 2 Then
MsgBox ( ” Los v a l o r e s i n g r e s a d o s no son c o r r e c t o s ” ,
MsgBoxStyle . C r i t i c a l )
Else
Dim i As I n t e g e r
Dim j As I n t e g e r
Dim pon1 As I n t e g e r = 1
Dim pon2 As I n t e g e r = NNotaHasta2 . Value − NNotaDesde2 . Value − 1
Dim vent As c r e a r M a s c a r a
For i = NNotaDesde2 . Value To NNotaHasta2 . Value − 2
Dim masDesde As mascara
Dim masHasta As mascara
Dim mulDesde As Double
Dim mulHasta As Double
For j = 0 To 14
masDesde = miInstrumento . n o t a s ( NNotaDesde2 . Value −
1) . l i s t a M a s c a r a s ( j )
masHasta = miInstrumento . n o t a s ( NNotaHasta2 . Value −
1) . l i s t a M a s c a r a s ( j )
mulDesde = miInstrumento . n o t a s ( NNotaDesde2 . Value −
1) . l i s t a C o e f i c i e n t e s ( j )
mulHasta = miInstrumento . n o t a s ( NNotaHasta2 . Value −
1) . l i s t a C o e f i c i e n t e s ( j )
I f mulDesde * pon2 > 0 And mulHasta * pon1 > 0 Then
Dim mas As New mascara ( masDesde , masHasta ,
mulDesde * pon2 , mulHasta * pon1 , ” s i n ” , F a l s e )
miInstrumento . n o t a s ( i ) . l i s t a M a s c a r a s ( j ) = mas
vent = New c r e a r M a s c a r a (Me, i , j , True , True )
End I f
Next
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 BCambiarNombre Click ( ByVal s e n d e r As System . Object , ByVal


e As System . EventArgs ) Handles BCambiarNombre . C l i c k
Try
miInstrumento . n o t a s ( numeroNota − 1 ) . nombre = TNombre . Text
Catch ex As E x c e p t i o n
MsgBox ( ” E r r o r a l a s i g n a r e l nombre” , MsgBoxStyle . C r i t i c a l )
End Try
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

P r i v a t e Sub GuardarToolStripMenuItem Click ( ByVal s e n d e r As Object ,


ByVal e As System . EventArgs ) Handles GuardarToolStripMenuItem . C l i c k
I f yaGuardo Then
guardarComo ( ultimaRuta )
Else
guardarAntesRuta ( )
End I f
End Sub

P r i v a t e Sub Timer1 Tick ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles Timer1 . Tick
Timer1 . Stop ( )
numeroNota = 1
primerEnvolvente = 1
rearmarPaneles ()
End Sub

P r i v a t e Sub B C o p i a r C l i c k ( ByVal s e n d e r As System . Object , ByVal e As


System . EventArgs ) Handles BCopiarEnv . C l i c k
I f NCopiarDesdeEnv . Value > NCopiarHastaEnv . Value Then
MsgBox ( ” El número de nota d e s d e e l c u a l c o p i a r no puede s e r
mayor a l número h a s t a e l c u a l c o p i a r ” , MsgBoxStyle . C r i t i c a l )
Else
Dim masAux As mascara
Dim notaACopiar As nota =
miInstrumento . n o t a s ( NACopiarEnv . Value − 1 )
Dim vent As c r e a r M a s c a r a
Dim i As I n t e g e r

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

P r i v a t e Sub B C o p i a r F r e c C l i c k ( ByVal s e n d e r As System . Object , ByVal e


As System . EventArgs ) Handles BCopiarFrec . C l i c k
I f NCopiarDesdeFrec . Value > NCopiarHastaFrec . Value Then
MsgBox ( ” El número de nota d e s d e e l c u a l c o p i a r no puede s e r
mayor a l número h a s t a e l c u a l c o p i a r ” , MsgBoxStyle . C r i t i c a l )
Else
Dim FrecAux As Double
Dim notaACopiar As nota =
miInstrumento . n o t a s ( NACopiarFrec . Value − 1 )
Dim i As I n t e g e r
Dim j As I n t e g e r
For i = 0 To 14
FrecAux = notaACopiar . l i s t a F r e c u e n c i a s ( i )
For j = NCopiarDesdeFrec . Value To NCopiarHastaFrec . Value
miInstrumento . n o t a s ( j − 1 ) . l i s t a F r e c u e n c i a s ( i ) =
FrecAux
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 )
rearmarPaneles ()
End Sub

P r i v a t e Sub B E x t r a p o l a r F r e c C l i c k ( ByVal s e n d e r As System . Object , ByVal


e As System . EventArgs ) Handles B E x t r a p o l a r F r e c . C l i c k
I f NExtraFrecDesde . Value > NExtraFrecHasta . Value − 2 Then
MsgBox ( ” El rango de e x t r a p o l a c i ó n no e s v á l i d o , e l v a l o r d e s d e
debe s e r menor a e l v a l o r h a s t a menos 2 ” ,
MsgBoxStyle . C r i t i c a l )
Else
Dim i As I n t e g e r
Dim j As I n t e g e r
Dim notaDesde As nota =
miInstrumento . n o t a s ( NExtraFrecDesde . Value − 1 )
Dim notaHasta As nota =
miInstrumento . n o t a s ( NExtraFrecHasta . Value − 1 )
Dim notaAux As nota
Dim m u l t i p l i c a d o r As Double
Dim d i f e r e n c i a As I n t e g e r = NExtraFrecHasta . Value −
NExtraFrecDesde . Value
For i = NExtraFrecDesde . Value To NExtraFrecHasta . Value − 2
notaAux = miInstrumento . n o t a s ( i )
For j = 0 To 14

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 u b l i c Sub aumentarValoresX ( ByVal c o n s t a n t e As Double )


Dim i As I n t e g e r
For i = 0 To 6
I f l i s t a P u n t o s X ( i ) > 0 Then
listaPuntosX ( i ) = listaPuntosX ( i ) * constante
End I f
Next
Me . p i n t a r P a n e l ( )
End Sub

P u b l i c Sub aumentarValoresY ( ByVal c o n s t a n t e As Double )


Dim i As I n t e g e r
For i = 0 To 6
I f l i s t a P u n t o s Y ( i ) > 0 Then
listaPuntosY ( i ) = listaPuntosY ( i ) * constante
End I f
Next
Me . p i n t a r P a n e l ( )
End Sub

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 guardarComo ( ByVal r u t a D e s t i n o As S t r i n g )


Try
Dim mas As mascara = c r e a r M a s c a r a ( )
Dim f s As New F i l e S t r e a m ( r u t a D e s t i n o , FileMode . C r e a t e )
Dim b f As New BinaryFormatter
b f . S e r i a l i z e ( f s , mas )
f s . Close ()
yaGuardo = True
ultimaRuta = r u t a D e s t i n o
Catch ex As E x c e p t i o n
MsgBox ( ” i m p o s i b l e g u a r d a r ” )
End Try
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

P u b l i c Function a b r i r M a s c a r a ( ByVal unaRuta As S t r i n g ) As mascara


Dim mas As New mascara
Try
Dim f s As New F i l e S t r e a m ( unaRuta , FileMode . Open )
Dim b f As New BinaryFormatter
mas = CType ( b f . D e s e r i a l i z e ( f s ) , mascara )
f s . Close ()
armarTablas ( mas )
Return mas
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 e n v o l v e n t e ” , MsgBoxStyle . C r i t i c a l )
End Try
Return Nothing
End Function

P r i v a t e Sub armarTablas ( ByVal mas As mascara )


listaPuntosX (0) = 0
l i s t a P u n t o s Y ( 0 ) = mas . v a l o r I n i c i a l
Dim i As I n t e g e r
Dim ymax As Double = 0
For i = 0 To mas . cambios . Count − 2
I f mas . cambios ( i ) = 65535 Then
listaPuntosX ( i + 1) = listaPuntosX ( i )

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

P u b l i c Sub a p l i c a r C a m b i o s ( ByVal a c t u a l i z o P a d r e As Boolean )


Me . miVentanaPadre . miInstrumento . n o t a s ( numeroNota ) . l i s t a M a s c a r a s ( numeroMascara )
= crearMascara ( )
I f a c t u a l i z o P a d r e Then
Me . miVentanaPadre . r e a r m a r P a n e l e s ( )
End I f
End Sub

#End Region

#Region ” r e a c c i o n e s ”

P r i v a t e Sub B A p l i c a r C l i c k ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles B Apl ic ar . C l i c k
a p l i c a r C a m b i o s ( True )
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 Object , ByVal


e As System . EventArgs ) Handles AbrirToolStripMenuItem . C l i c k
Dim r e s p u e s t a As MsgBoxResult = MsgBox ( ” Desea d e j a r l a máscara
a c t u a l y a b r i r o t r a ? ” , MsgBoxStyle . YesNo )

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

P r i v a t e Sub GuardarComoToolStripMenuItem Click ( ByVal s e n d e r As


System . Object , ByVal e As System . EventArgs ) Handles
GuardarComoToolStripMenuItem . C l i c k
guardarAntesRuta ( )
End Sub

P r i v a t e Sub Panel1 MouseMove ( ByVal s e n d e r As Object , ByVal e As


System . Windows . Forms . MouseEventArgs ) Handles Panel1 . MouseMove
L ab el 1 . Text = ( ( 2 0 * ( ( M o u s e P o s i t i o n .X − 4 − Panel1 . L o c a t i o n .X −
Panel1 . Width / 20 − Me . L o c a t i o n .X) * XMax) ) / ( 1 9 *
Panel1 . Width ) ) . T o S t r i n g ( ”F0” ) + ” ; ” + ( ( 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” )
End Sub

P r i v a t e Sub P a n e l 1 P a i n t ( ByVal s e n d e r As System . Object , ByVal e As


System . Windows . Forms . PaintEventArgs ) Handles Panel1 . P ai nt
Dim g r a f i c o As G r ap hi cs = e . G ra ph i cs
Dim p1 As P oi nt
Dim p2 As P oi nt
Dim punta As Pen
Dim l i s t a X A P i n t a r As L i s t ( Of Double ) = New L i s t ( Of Double )
Dim l i s t a Y A p i n t a r As L i s t ( Of Double ) = New L i s t ( Of Double )
Dim l i s t a E t i q u e t a s X As L i s t ( Of L ab e l ) = New L i s t ( Of L ab el )
l i s t a E t i q u e t a s X . Add( La b el 5 )
l i s t a E t i q u e t a s X . Add( La b el 6 )
l i s t a E t i q u e t a s X . Add( La b el 7 )
l i s t a E t i q u e t a s X . Add( La b el 8 )
Dim l i s t a E t i q u e t a s Y As L i s t ( Of L ab e l ) = New L i s t ( Of L ab el )
l i s t a E t i q u e t a s Y . Add( La b el 9 )
l i s t a E t i q u e t a s Y . Add( La b el 1 0 )
l i s t a E t i q u e t a s Y . Add( La b el 1 1 )
l i s t a E t i q u e t a s Y . Add( La b el 1 2 )
Dim i As I n t e g e r
For i = 0 To 6
I f l i s t a P u n t o s Y ( i ) <> −1 And l i s t a P u n t o s Y ( i ) < 0 Then
listaPuntosY ( i ) = 0
End I f
Next
For i = 0 To 6
I f l i s t a P u n t o s X ( i ) > −1 Then
l i s t a X A P i n t a r . Add( l i s t a P u n t o s X ( i ) )

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

punta = New Pen ( C o l o r . Red , 3 )


Dim xNuevoPunto1 As Double
Dim yNuevoPunto1 As Double
Dim xNuevoPunto2 As Double
Dim yNuevoPunto2 As Double
For i = 0 To l i s t a X A P i n t a r . Count − 2
xNuevoPunto1 = l i s t a X A P i n t a r ( i ) * Panel1 . Width * 19 / (XMax *
2 0 ) + Panel1 . Width / 20
xNuevoPunto2 = l i s t a X A P i n t a r ( i + 1 ) * Panel1 . Width * 19 /
(XMax * 2 0 ) + Panel1 . Width / 20
yNuevoPunto1 = Panel1 . Height − ( l i s t a Y A p i n t a r ( i ) *
Panel1 . Height * 19 / (YMax * 2 0 ) + Panel1 . Height / 2 0 )
yNuevoPunto2 = Panel1 . Height − ( l i s t a Y A p i n t a r ( i + 1 ) *
Panel1 . Height * 19 / (YMax * 2 0 ) + Panel1 . Height / 2 0 )
Try
p1 = New P o in t ( xNuevoPunto1 , yNuevoPunto1 )
p2 = New P o in t ( xNuevoPunto2 , yNuevoPunto2 )
g r a f i c o . DrawLine ( punta , p1 , p2 )
Catch ex As E x c e p t i o n

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

P r i v a t e Sub R a d i o B u t t o n 1 C l i c k ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles RadioButton1 . C l i c k
xElegido = 1
End Sub

P r i v a t e Sub R a d i o B u t t o n 2 C l i c k ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles RadioButton2 . C l i c k
xElegido = 2
End Sub

P r i v a t e Sub R a d i o B u t t o n 3 C l i c k ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles RadioButton3 . C l i c k
xElegido = 3
End Sub

P r i v a t e Sub R a d i o B u t t o n 4 C l i c k ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles RadioButton4 . C l i c k
xElegido = 4
End Sub

P r i v a t e Sub R a d i o B u t t o n 5 C l i c k ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles RadioButton5 . C l i c k
xElegido = 5
End Sub

P r i v a t e Sub R a d i o B u t t o n 6 C l i c k ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles RadioButton6 . C l i c k
xElegido = 6
End Sub

P r i v a t e Sub P a n e l 1 C l i c k ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles Panel1 . C l i c k
Dim p o s i c i o n X As I n t e g e r = ( ( 2 0 * ( ( M o u s e P o s i t i o n .X − 4 −
Panel1 . L o c a t i o n .X − Panel1 . Width / 20 − Me . L o c a t i o n .X) * XMax) )
/ ( 1 9 * Panel1 . Width ) )
Dim o t r a C o n d i c i o n As Boolean = x E l e g i d o > 5
I f Not o t r a C o n d i c i o n Then
o t r a C o n d i c i o n = 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 ) Or
l i s t a P u n t o s X ( x E l e g i d o + 1 ) = −1

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

P r i v a t e Sub BVI Click ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles BVI . C l i c k
Try
Dim v a l o r I n i c i a l As Double = CDbl ( TextBox1 . Text )
I f ( v a l o r I n i c i a l < 0 ) Or ( v a l o r I n i c i a l > YMax) Then
MsgBox ( ” El v a l o r i n i c i a l debe e s t a r e n t r e 0 y ” +
YMax . T o S t r i n g )
Else
l i s t a P u n t o s X ( 0 ) = Panel1 . Width / 20
listaPuntosY (0) = v a l o r I n i c i a l
xElegido = 0
hayQueAumetarPunto = True
pintarPanel ()
End I f

Catch ex As E x c e p t i o n
End Try
End Sub

P r i v a t e Sub B R e i n i c i a r C l i c k ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles B R e i n i c i a r . C l i c k
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
pintarPanel ()
Next
End Sub

P r i v a t e Sub BXM Click ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles BXM. C l i c k
Dim xM As I n t e g e r
Try
xM = CInt (TXM. Text )
I f xM <= 0 Then
MsgBox ( ” El x máximo debe s e r un e n t e r o mayor a c e r o ” )
Else

208
XMax = xM
pintarPanel ()
End I f
Catch ex As E x c e p t i o n

End Try
End Sub

P r i v a t e Sub BYM Click ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles BYM. C l i c k
Dim yM As Double
Try
yM = CDbl (TYM. Text )
I f yM <= 0 Then
MsgBox ( ” El y máximo debe s e r mayor a c e r o ” )
Else
YMax = yM
pintarPanel ()
End I f
Catch ex As E x c e p t i o n

End Try
End Sub

P r i v a t e Sub c r e a r M a s c a r a D e a c t i v a t e ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles Me . D e a c t i v a t e
I f tengoPadre Then
Me . miVentanaPadre . Enabled = True
End I f
End Sub

P r i v a t e Sub c r e a r M a s c a r a D i s p o s e d ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles Me . D i s p o s e d
I f tengoPadre Then
Me . miVentanaPadre . Enabled = True
End I f
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

P r i v a t e Sub CambiarImágenDeFondoToolStripMenuItem Click ( ByVal s e n d e r


As System . Object , ByVal e As System . EventArgs ) Handles
CambiarImágenDeFondoToolStripMenuItem . C l i c k
Dim vent As New O p e n F i l e D i a l o g
vent . M u l t i s e l e c t = True
vent . F i l t e r = ” a r c h i v o s de
imágenes ( * .BMP; * . JPG ; * . GIF ) | * .BMP; * . JPG ; * . GIF | A l l f i l e s
(*.*) |*.* ”
vent . T i t l e = ” S e l e c c i o n e l a imágen que d e s e a poner de fondo en e l
panel ”
vent . ShowDialog ( )
I f vent . FileNames . Length > 0 Then
Try
Panel1 . BackgroundImage = Image . FromFile ( vent . FileName )
Panel1 . BackgroundImageLayout = ImageLayout . S t r e t c h
Catch ex As E x c e p t i o n

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

P r i v a t e Sub GuardarToolStripMenuItem Click ( ByVal s e n d e r As


System . Object , ByVal e As System . EventArgs ) Handles
GuardarToolStripMenuItem . C l i c k
I f yaGuardo Then
guardarComo ( ultimaRuta )
Else
guardarAntesRuta ( )
End I f
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

P u b l i c Sub New( ByVal tablaX ( ) As I n t e g e r , ByVal tablaY ( ) As I n t e g e r ,


ByVal r u t a As S t r i n g , ByVal nom As S t r i n g )
InitializeComponent ()
Dim i As I n t e g e r
Dim maximoY As Double = 0
For i = 0 To 6
l i s t a P u n t o s X ( i ) = tablaX ( i )
l i s t a P u n t o s Y ( i ) = tablaY ( i )
I f tablaY ( i ) > maximoY Then
maximoY = tablaY ( i )
End I f
Next
Me . XMax = l i s t a P u n t o s X ( 6 )
Me . YMax = maximoY

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

P u b l i c Sub New( ByVal vent As c r e a r I n s t r u m e n t o , ByVal numeroNo As


I n t e g e r , ByVal numeroMas As I n t e g e r )
InitializeComponent ()
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
Me . miVentanaPadre = vent
Me . miVentanaPadre . Enabled = F a l s e
numeroNota = numeroNo
numeroMascara = numeroMas
armarTablas (Me . miVentanaPadre . miInstrumento . n o t a s ( numeroNo ) . l i s t a M a s c a r a s ( numero
listaParaBorrar =
Me . miVentanaPadre . miInstrumento . n o t a s ( numeroNo ) . l i s t a M a s c a r a s ( numeroMas ) . i n c r
tengoPadre = True
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

P u b l i c Sub New( ByVal vent As c r e a r I n s t r u m e n t o , ByVal numeroNo As


I n t e g e r , ByVal numeroMas As I n t e g e r , ByVal c i e r r o E n s e g u i d a As
Boolean , ByVal t e n g o As Boolean )
InitializeComponent ()
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
Me . miVentanaPadre = vent
Me . miVentanaPadre . Enabled = F a l s e
numeroNota = numeroNo
numeroMascara = numeroMas

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

P u b l i c Function e x p o r t a r ( 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 , ByVal volumen As I n t e g e r ) As S t r i n g
Dim p a r a D e v o l v e r As S t r i n g = ” ”
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

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’

< S e r i a l i z a b l e ( )> P u b l i c C l a s s mascara

#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( ByVal nom As S t r i n g )


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 = nom
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 4 5 0 0 0 )
Else
cambios . Add ( 6 5 5 3 5 )
i n c r e m e n t o s . Add( ( − 0 . 2 ) / ( 6 5 5 3 5 0 − 10 * cambios ( 5 ) ) )
End I f
Next
End Sub

P u b l i c Sub New( ByVal mas As mascara )


Dim i As I n t e g e r
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 )
For i = 0 To mas . i n c r e m e n t o s . Count − 1
Me . i n c r e m e n t o s . Add( mas . i n c r e m e n t o s ( i ) )
Next
For i = 0 To mas . cambios . Count − 1
Me . cambios . Add( mas . cambios ( i ) )
Next
Me . v a l o r I n i c i a l = mas . v a l o r I n i c i a l
Me . nombre = mas . nombre
Me . f o t o = c r e a r I m a g e n V a c i a ( )
End Sub

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

P u b l i c Sub New( ByVal l i s t a X ( ) As I n t e g e r , ByVal l i s t a Y ( ) As Double ,


ByVal nom As S t r i n g , ByVal f o t As Image )
Me . nombre = nom
Me . v a l o r I n i c i a l = l i s t a Y ( 0 )
cambios = New L i s t ( Of I n t e g e r )
Dim i As I n t e g e r
cambios . Add ( 0 )
For i = 1 To l i s t a X . Length − 1
I f l i s t a X ( i ) = −1 Then
cambios . Add ( 6 5 5 3 5 )
Else
cambios . Add( CInt ( l i s t a X ( i ) / 1 0 ) )
End I f
Next
cambios . Add ( 6 5 5 3 5 )
i n c r e m e n t o s = New L i s t ( Of Double )
For i = 1 To cambios . Count − 2
I f l i s t a Y ( i ) = −1 Then
l i s t a Y ( i ) = l i s t a Y ( i − 1)
End I f
Try
i n c r e m e n t o s . Add ( ( l i s t a Y ( i ) − l i s t a Y ( i − 1 ) ) / ( 1 0 *
( cambios ( i ) − cambios ( i − 1 ) ) ) )
Catch ex As E x c e p t i o n
i n c r e m e n t o s . Add ( 0 )
End Try
Next
i n c r e m e n t o s . Add ( ( 0 − l i s t a Y ( l i s t a Y . Length − 1 ) ) / ( 6 5 5 3 5 0 − 10 *
cambios ( cambios . Count − 2 ) ) )
Me . f o t o = f o t
cambios . Remove ( 0 )
End Sub

P u b l i c Sub New( ByVal mas1 As mascara , ByVal mas2 As mascara , ByVal


pon1 As Double , ByVal pon2 As Double , ByVal nom As S t r i n g )
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 . nombre = nom
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
Dim puntosY1 ( 7 ) As Double
Dim puntosY2 ( 7 ) As Double
puntosY1 ( 0 ) = mas1 . v a l o r I n i c i a l
puntosY2 ( 0 ) = mas2 . v a l o r I n i c i a l
puntosY1 ( 1 ) = mas1 . v a l o r I n i c i a l + 10 * mas1 . cambios ( 0 ) *
mas1 . i n c r e m e n t o s ( 0 )
puntosY2 ( 1 ) = mas2 . v a l o r I n i c i a l + 10 * mas2 . cambios ( 0 ) *
mas2 . i n c r e m e n t o s ( 0 )

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

P u b l i c Sub New( ByVal mas1 As mascara , ByVal mas2 As mascara , ByVal


pon1 As Double , ByVal pon2 As Double , ByVal nom As S t r i n g , ByVal
noPromedio As Boolean )
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 . nombre = nom
Me . f o t o = c r e a r I m a g e n V a c i a ( )
Me . v a l o r I n i c i a l = ( pon1 * mas1 . v a l o r I n i c i a l + pon2 *
mas2 . v a l o r I n i c i a l ) / ( pon1 + pon2 )
Dim l i s t a C a m b i o s 1 As New L i s t ( Of I n t e g e r )
Dim l i s t a C a m b i o s 2 As New L i s t ( Of I n t e g e r )
Dim aux1 As Double
Dim aux2 As Double
l i s t a C a m b i o s 1 . Add ( 0 )
l i s t a C a m b i o s 2 . Add ( 0 )
Me . cambios . Add ( 0 )
Dim i As I n t e g e r
For i = 0 To mas1 . cambios . Count − 1
l i s t a C a m b i o s 1 . Add( mas1 . cambios ( i ) )
l i s t a C a m b i o s 2 . Add( mas2 . cambios ( i ) )
Me . cambios . Add ( ( pon1 * mas1 . cambios ( i ) + pon2 *
mas2 . cambios ( i ) ) / ( pon1 + pon2 ) )
Next
For i = 0 To l i s t a C a m b i o s 1 . Count − 2
aux1 = ( l i s t a C a m b i o s 1 ( i + 1 ) − l i s t a C a m b i o s 1 ( i ) ) *
mas1 . i n c r e m e n t o s ( i )
aux2 = ( l i s t a C a m b i o s 2 ( i + 1 ) − l i s t a C a m b i o s 2 ( i ) ) *
mas2 . i n c r e m e n t o s ( i )
Me . i n c r e m e n t o s . Add ( ( pon1 * aux1 + pon2 * aux2 ) / ( ( pon1 +
pon2 ) * ( l i s t a C a m b i o s 1 ( i + 1 ) + l i s t a C a m b i o s 2 ( i + 1 ) −
listaCambios1 ( i ) − listaCambios2 ( i ) ) ) )
Next
Me . cambios . Remove ( 0 )
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

P u b l i c Shared Function grabarWav ( ByVal v a l o r e s As L i s t ( Of I n t e g e r ) ,


ByVal r u t a As S t r i n g ) As Boolean
Try
Dim h e a d e r ( 4 3 ) As Byte
Dim ms As New System . IO . MemoryStream ( header , 0 , 4 4 , True )
Dim enc As New System . Text . UTF8Encoding
Dim i As I n t e g e r
Dim tamanio As I n t e g e r = v a l o r e s . Count * 2
Dim l i s t a B y t e s ( 2 * v a l o r e s . Count ) As Byte

ms . Write ( enc . GetBytes ( ”RIFF” ) , 0 , 4 )


ms . Write ( System . B i t C o n v e r t e r . GetBytes ( tamanio + 3 6 ) , 0 , 4 )
ms . Write ( enc . GetBytes ( ”WAVE” ) , 0 , 4 )
ms . Write ( enc . GetBytes ( ” fmt ” ) , 0 , 4 )
ms . Write ( System . B i t C o n v e r t e r . GetBytes ( 1 6 ) , 0 , 4 )
ms . Write ( System . B i t C o n v e r t e r . GetBytes ( 1 ) , 0 , 2 )
ms . Write ( System . B i t C o n v e r t e r . GetBytes ( 1 ) , 0 , 2 )
ms . Write ( System . B i t C o n v e r t e r . GetBytes ( 3 0 0 0 0 ) , 0 , 4 )
ms . Write ( System . B i t C o n v e r t e r . GetBytes ( tamanio ) , 0 , 4 )
ms . Write ( System . B i t C o n v e r t e r . GetBytes ( 2 ) , 0 , 2 )
ms . Write ( System . B i t C o n v e r t e r . GetBytes ( 1 6 ) , 0 , 2 )
ms . Write ( enc . GetBytes ( ” data ” ) , 0 , 4 )
ms . Write ( System . B i t C o n v e r t e r . GetBytes ( tamanio ) , 0 , 4 )
Dim o F i l e S t r e a m As System . IO . F i l e S t r e a m
o F i l e S t r e a m = New System . IO . F i l e S t r e a m ( ruta ,
System . IO . FileMode . C r e a t e )
o F i l e S t r e a m . Write ( header , 0 , h e a d e r . Length )
Dim ms2 As New System . IO . MemoryStream ( l i s t a B y t e s , 0 , tamanio ,
True )
For i = 0 To v a l o r e s . Count − 1
ms2 . Write ( System . B i t C o n v e r t e r . GetBytes ( v a l o r e s ( i ) ) , 0 , 2 )
Next
o F i l e S t r e a m . Write ( l i s t a B y t e s , 0 , l i s t a B y t e s . Length )
oFileStream . Close ()
Return True
Catch ex As E x c e p t i o n
Return F a l s e
End Try
End Function

P u b l i c Shared Function c o p i a L i s t a D o u b l e ( ByVal l i s t a As L i s t ( Of


Double ) ) As L i s t ( Of Double )
Dim p a r a D e v o l v e r As New L i s t ( Of Double )
For Each num As Double In l i s t a
p a r a D e v o l v e r . Add(num)
Next
Return p a r a D e v o l v e r
End Function

P u b l i c Shared Function c o p i a L i s t a M a s c a r a s ( ByVal l i s t a As L i s t ( Of


mascara ) ) As L i s t ( Of mascara )
Dim p a r a D e v o l v e r As New L i s t ( Of mascara )
For Each mas As mascara In l i s t a
p a r a D e v o l v e r . Add( mas )

223
Next
Return p a r a D e v o l v e r
End Function

P u b l i c Shared Function c o p i a L i s t a E n t e r o s ( ByVal l i s t a As L i s t ( Of


I n t e g e r ) ) As L i s t ( Of I n t e g e r )
Dim p a r a D e v o l v e r As New L i s t ( Of I n t e g e r )
For Each num As I n t e g e r In l i s t a
p a r a D e v o l v e r . Add(num)
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’

< S e r i a l i z a b l e ( )> P u b l i c C l a s s nota

#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( ByVal nom As S t r i n g , ByVal l i s t a F r e c As L i s t ( Of


Double ) , ByVal l i s t a M a s c As L i s t ( Of mascara ) , ByVal l i s t a C o e f As
L i s t ( Of Double ) , ByVal f r e c P r i n As Double )
Me . nombre = nom
Me . l i s t a F r e c u e n c i a s = metodosComunes . c o p i a L i s t a D o u b l e ( l i s t a F r e c )
Me . l i s t a M a s c a r a s = metodosComunes . c o p i a L i s t a M a s c a r a s ( l i s t a M a s c )
Me . f r e c u e n c i a P r i n c i p a l = f r e c P r i n
Me . l i s t a C o e f i c i e n t e s = metodosComunes . c o p i a L i s t a D o u b l e ( l i s t a C o e f )
End Sub

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

P u b l i c Sub New( ByVal f r e c P r i n As Double )


Me . nombre = ” S i n nombre a s i g n a d o ”
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 )
Dim i As I n t e g e r
For i = 0 To 14
I f f r e c P r i n * ( i + 1 ) / 3 > 14500 Then
l i s t a F r e c u e n c i a s . Add ( 1 4 5 0 0 )
Else
l i s t a F r e c u e n c i a s . Add( f r e c P r i n * ( i + 1 ) / 3 )
End I f
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 = f r e c P r i n
End Sub

#End Region

#Region ” métodos ”

P u b l i c Function g e n e r a r S o n i d o ( ByVal c a n t i d a d M u e s t r a s As I n t e g e r , ByVal


FsPic As I n t e g e r ) As L i s t ( Of I n t e g e r )
Dim l i s t a V a l o r e s As New L i s t ( Of Double )
Dim p a r a D e v o l v e r As New L i s t ( Of I n t e g e r )
Dim i n d i c e As I n t e g e r = 0

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 ”

P u b l i c Sub New( ByVal ventana As c r e a r M a s c a r a )


InitializeComponent ()
Me . v e n t P a d r e = ventana
Me . ventPadre . Enabled = F a l s e
Me . r e s p u e s t a = 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

#End Region

#Region ” r e a c c i o n e s ”

P r i v a t e Sub B A c e p t a r C l i c k ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles BAceptar . C l i c k
Me . r e s p u e s t a = True
Me . numeroUno = NPeso1 . Value
Me . numeroDos = NPeso2 . Value
Me . r u t a 1 = TRuta1 . Text
Me . r u t a 2 = TRuta2 . Text
Me . D i s p o s e ( )
End Sub

P r i v a t e Sub p i d e E n t e r o s D i s p o s e d ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles Me . D i s p o s e d
Me . ventPadre . Enabled = True
End Sub

P r i v a t e Sub B C a n c e l a r C l i c k ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles BCancelar . C l i c k
Dispose ()
End Sub

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

P r i v a t e Sub BExaminar2 Click ( ByVal s e n d e r As System . Object , ByVal e As


System . EventArgs ) Handles BExaminar2 . 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 segunda e n v o l v e n t e d e s d e l a c u a l
quiere extrapolar ”
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 . TRuta2 . Text = vent . FileName
End I f
End Sub

P r i v a t e Sub p i d e E n t e r o s F o r m C l o s e d ( ByVal s e n d e r As Object , ByVal e As


System . Windows . Forms . FormClosedEventArgs ) Handles Me . FormClosed
Me . ventPadre . Enabled = True
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( ByVal vent As crearMascara , ByVal esEjeX As Boolean )


InitializeComponent ()
ventanaPadre = vent
esX = esEjeX
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

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 ”

P r i v a t e Sub B A c e p t a r C l i c k ( ByVal s e n d e r As System . Object , ByVal e As


System . EventArgs ) Handles BAceptar . C l i c k
Try
Dim num As Double = CDbl ( TValor . Text )
I f esX Then
Me . ventanaPadre . aumentarValoresX (num)
Else
Me . ventanaPadre . aumentarValoresY (num)
End I f
Me . D i s p o s e ( )
Catch ex As E x c e p t i o n
MsgBox ( ” El v a l o r i n s e r t a d o no e s v á l i d o , v e r i f i q u e que s e a un
número mayor a c e r o ” , MsgBoxStyle . C r i t i c a l )

231
End Try
End Sub

P r i v a t e Sub B C a n c e l a r C l i c k ( ByVal s e n d e r As System . Object , ByVal e As


System . EventArgs ) Handles BCancelar . C l i c k
Me . D i s p o s e ( )
End Sub

P r i v a t e Sub pideUnDouble FormClosed ( ByVal s e n d e r As Object , ByVal e As


System . Windows . Forms . FormClosedEventArgs ) Handles Me . FormClosed
Me . ventanaPadre . Enabled = True
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 ”

P r i v a t e Sub B C o n e c t a r C l i c k ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles BConectar . C l i c k
I f e s t o y C o n e c t a d o Then
estoyConectado = False
s e r i a l P o r t . Close ()
readThread . J o i n ( )
End I f
I f ListBox1 . S e l e c t e d I t e m = Nothing Or ListBox1 . S e l e c t e d I n d e x = 0
Then
MsgBox ( ”Debe s e l e c c i o n a r un p u e r t o s e r i e ” ,
MsgBoxStyle . Exclamation )
Else
Try

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

P r i v a t e Sub v e n t a n a P r i n c i p a l D i s p o s e d ( ByVal s e n d e r As Object , ByVal e


As System . EventArgs ) Handles Me . D i s p o s e d
Try
estoyConectado = False
s e r i a l P o r t . Close ()
readThread . J o i n ( )
mandarThread . J o i n ( )
Catch ex As E x c e p t i o n

End Try
End Sub

P r i v a t e Sub BArchivoNumeros Click ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles BArchivoNumeros . C l i c k
Try
Dim e l e g i r A r c h i v o As New O p e n F i l e D i a l o g
e l e g i r A r c h i v o . AddExtension = True
e l e g i r A r c h i v o . F i l t e r = ” i n s t r u m e n t o s en hex ( * . hex ) | * . hex ”
e l e g i r A r c h i v o . ShowDialog ( )
I f e l e g i r A r c h i v o . FileNames . Length > 0 Then
textoAEnviar =
My. Computer . F i l e S y s t e m . ReadAllText ( e l e g i r A r c h i v o . FileName )
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 c u e n t a As I n t e g e r = TestArray . Length
Timer1 . S t a r t ( )
mandarThread = New Thread ( AddressOf mandar )
mandarThread . S t a r t ( )
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 i n s t r u m e n t o ” , MsgBoxStyle . C r i t i c a l )
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

P r i v a t e Sub Timer1 Tick ( ByVal s e n d e r As Object , ByVal e As


System . EventArgs ) Handles Timer1 . Tick
L ab el 4 . V i s i b l e = True
L ab el 4 . Text = ” Hasta ahora han s i d o e n v i a d o s ” +
cantidadDeDatosEnviados . T o S t r i n g + ” d a t o s ”
End Sub

P r i v a t e Sub BCheq Click ( ByVal s e n d e r As System . Object , ByVal e As


System . EventArgs ) Handles BCheq . C l i c k
Dim s As S t r i n g
ListBox1 . Items . C l e a r ( )
I f S e r i a l P o r t . GetPortNames . Length > 0 Then
ListBox1 . Items . Add( ” L i s t a de p u e r t o s d i s p o n i b l e s : ” )
For Each s In S e r i a l P o r t . GetPortNames ( )
ListBox1 . Items . Add( s )
Next s
BConectar . Enabled = True
Else
ListBox1 . Items . Add( ”No hay p u e r t o s s e r i e d i s p o n i b l e s ” )
End I f
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 ”

P r i v a t e Sub BCre arMa scar a Cli ck ( ByVal s e n d e r As System . Object , ByVal e


As System . EventArgs ) Handles BCrearMascara . C l i c k
Dim vent As New c r e a r M a s c a r a
vent . ShowDialog ( )
End Sub

P r i v a t e Sub B C r e a r I n s t r u m e n t o C l i c k ( ByVal s e n d e r As System . Object ,


ByVal e As System . EventArgs ) Handles BCrearInstrumento . C l i c k
Dim i n s As i n s t r u m e n t o = New i n s t r u m e n t o
Dim i As I n t e g e r
Dim ventAux As c r e a r M a s c a r a
Dim j As I n t e g e r
Dim vent As New c r e a r I n s t r u m e n t o ( i n s , ” ” , F a l s e )
For j = 0 To 85
For i = 0 To 14
ventAux = New c r e a r M a s c a r a ( vent , j , i , True , F a l s e )
Next
P r o g r e s s B a r 1 . Value = ( j * 100 / 8 5 )
Next
vent . Enabled = True
vent . ShowDialog ( )
End Sub

P r i v a t e Sub B A b r i r I n s t r u m e n t o C l i c k ( ByVal s e n d e r As System . Object ,


ByVal e As System . EventArgs ) Handles BAbrirInstrumento . C l i c k
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 ( )
I f vent . FileNames . Length > 0 Then
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 ()
Dim vent2 As New c r e a r I n s t r u m e n t o ( i n s , ruta , True )
vent2 . ShowDialog ( )
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 BMandarArchivo Click ( ByVal s e n d e r As System . Object , ByVal


e As System . EventArgs ) Handles BMandarArchivo . C l i c k
Dim vent As New t r a n s m i t i r
vent . ShowDialog ( )

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’

f u n c t i o n z= a c o r t a S o n i d o ( entrada , s a l i d a , desde , hasta , c a n a l , sumar )

[ 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 )

f r e c u e n c i a s=f r e c u e n c i a s * 2 . ˆ ( ( nota −49) / 1 2 ) ;


y=z e r o s ( 1 , l a r g o )
i n d i c e s=o n e s ( 1 , 1 5 ) ;
tam=s i z e ( e v o l u c i o n ) ;
tam ( 1 , 1 )=tam ( 1 , 1 ) −1;
cambios=z e r o s ( tam ( 1 , 2 ) , tam ( 1 , 1 ) ) ;
f o r i =1:tam ( 1 , 2 )
f o r j =1:tam ( 1 , 1 )
cambios ( i , j )=cadaCuantosEvol * j ;
end
end
v a l o r e s=z e r o s ( 1 , l e n g t h ( f r e c u e n c i a s ) ) ;
mi=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 i =1: l e n g t h ( f r e c u e n c i a s )
v a l o r e s ( i )=e v o l u c i o n ( 1 , i ) ;
mi ( i )=e v o l u c i o n ( 1 , i ) ;
end
i n c r e m e n t o s=z e r o s ( tam ( 1 , 2 ) , tam ( 1 , 1 ) −1)
f o r i =1:tam ( 1 , 2 )
f o r j =1:tam ( 1 , 1 )−1
i n c r e m e n t o s ( i , j ) =( e v o l u c i o n ( j +1, i )−e v o l u c i o n ( j , i ) ) / cadaCuantosEvol
end
end
f o r i =1: l e n g t h ( y )
f o r j =1: l e n g t h ( f r e c u e n c i a s )
y ( i )=y ( i )+v a l o r e s ( j ) * s i n ( 2 * %pi * f r e c u e n c i a s ( j ) * i / Fs ) ;
v a l o r e s ( j )=v a l o r e s ( j )+i n c r e m e n t o s ( j , i n d i c e s ( j ) ) ;
i f ( v a l o r e s ( j ) <0)
v a l o r e s ( j ) =0;
end
i f ( i n d i c e s ( j ) <5)
i f ( i==cambios ( j , i n d i c e s ( j ) ) )
i n d i c e s ( j )=i n d i c e s ( j ) +1;
end
end
end
end
maximoy=max( abs ( y ) ) ;
y=y/max( abs ( y ) )
r u t a D e s t i n o=r u t a D e s t i n o+” \ nota ”+s t r i n g ( nota )+” . wav” ;
wavwrite ( y , Fs , r u t a D e s t i n o )
mi=2048 * mi/maximoy ;
i n c r e m e n t o s =2048 * i n c r e m e n t o s /maximoy ;
endfunction
scilab/copiaNota.sci

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’

f u n c t i o n [ P i c o s , la rgoA udi o , Fs]=


F o u r i e r F a c i l ( entrada , c a n t i d a d , c a n t i d a d M u e s t r a s , numeroPicos , c o l o r G r a f )

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 , : ) )

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=−5:5
i f kMaximo+k>0
z ( 1 , kMaximo+k ) =−1;
end
end
P i c o s ( i )=j ( kMaximo ) ;
end
endfunction
scilab/fourieFacilDetectaPicos.sci

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 )

[ f r e c u e n c i a s , l arg oAu dio , Fs]= F o u r i e r F a c i l ( ruta , 1 0 0 0 0 , − 1 , 1 5 , c o l )


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 ( ruta , 1 , − 1 , 5 0 0 , f r e c u e n c i a s )
endfunction
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=−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

f u n c t i o n [ P i c o s , la rgoA udi o , Fs]=


F o u r i e r F a c i l ( entrada , c a n t i d a d , c a n t i d a d M u e s t r a s , numeroPicos , c o l o r G r a f )

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 )

[ y , Fs , b i t s ]= wavread ( ru taE ntra da )


t =1: l e n g t h ( y ) ;
y=y . * (1+ magnitud * c o s ( 2 * %pi * f r e c u e n c i a * t / Fs ) )
y=y/max( abs ( y ) )
wavwrite ( y , Fs , r u t a S a l i d a )
endfunction
scilab/efectosScilab/efectoChorus.sci

254
Efecto delay

f u n c t i o n E f e c t o D e l a y ( rutaEntrada , r u t a S a l i d a , del ay , magnitudes )


[ y , Fs , b i t s ]= wavread ( ru taE ntra da )
c a n t i d a d=round ( Fs * d e l a y ) +1;
f o r j =1: l e n g t h ( magnitudes )
f o r i=j * c a n t i d a d +1: l e n g t h ( y )
y ( i )=y ( i )+magnitudes ( j ) * y ( i −j * c a n t i d a d )
end
end
y=y/max( abs ( y ) )
wavwrite ( y , Fs , r u t a S a l i d a )
endfunction
scilab/efectosScilab/efectoDelay.sci

255
Efecto eco

f u n c t i o n E f e c t o E c o ( rutaEntrada , r u t a S a l i d a , dela y , magnitud )


[ y , Fs , b i t s ]= wavread ( ru taE ntra da )
c a n t i d a d=round ( Fs * d e l a y ) +1;
f o r i=c a n t i d a d +1: l e n g t h ( y )
y ( i )=y ( i )+magnitud * y ( i −c a n t i d a d )
end
y=y/max( abs ( y ) )
wavwrite ( y , Fs , r u t a S a l i d a )
endfunction
scilab/efectosScilab/efectoEco.sci

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 )

[ x , Fs , b i t s ]= wavread ( ru taE ntra da ) ;


x=x/max( abs ( x ) )
f o r i =1: l e n g t h ( x )
i f x ( i )>l i m i t e
x ( i )=l i m i t e ;
end
i f x ( i )<−l i m i t e
x ( i )=−l i m i t e ;
end
end
wavwrite ( x , Fs , r u t a S a l i d a ) ;
endfunction
scilab/efectosScilab/efectoLimitador.sci

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 )

[ x , Fs , b i t s ]= wavread ( ru taE ntra da ) ;


x=x−(x . ˆ 3 ) * c o e f i c i e n t e ;
x=x/max( abs ( x ) ) ;
wavwrite ( x , Fs , r u t a S a l i d a ) ;
endfunction
scilab/efectosScilab/efectoLimitadorLog.sci

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 )

[ y , Fs , b i t s ]= wavread ( ru taE ntra da )


t =1: l e n g t h ( y ) ;
y=y . * (1+ magnitud * c o s ( 2 * %pi * f r e c u e n c i a * t / Fs ) )
y=y/max( abs ( y ) )
wavwrite ( y , Fs , r u t a S a l i d a )
endfunction
scilab/efectosScilab/efectoVibrato.sci

260
Efecto Wah-Wah

f u n c t i o n p o t e n c i a=WahWah( rutaEntrada , r u t a S a l i d a , v a r i a c i o n , minimo , maximo )

[ x , Fs , b i t s ]= wavread ( ru taE ntra da ) ;


t =1: l e n g t h ( x ) ;
f r e c u e n c i a C o r t e=minimo+(maximo−minimo ) * ( 0 . 5 + 0 . 5 * c o s ( 2 * %pi * v a r i a c i o n * t / Fs ) ) ;
f r e c u e n c i a C o r t e =2* %pi * f r e c u e n c i a C o r t e ;
y=z e r o s ( 1 , l e n g t h ( x ) ) ;
f o r i =3: l e n g t h ( y ) ;
y ( i ) =(y ( i −1) * (2+ s q r t ( 2 ) * f r e c u e n c i a C o r t e ( i ) )−y ( i −2)+
( f r e c u e n c i a C o r t e ( i ) ˆ 2 ) * x ( i ) ) /(1+ s q r t ( 2 ) * f r e c u e n c i a C o r t e ( i )+
f r e c u e n c i a C o r t e ( i ) ˆ2)

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’

Public Class reduceImagenes


P r i v a t e Sub B R e d u c i r C l i c k ( ByVal s e n d e r As System . Object , ByVal e As
System . EventArgs ) Handles BReducir . C l i c k
Try
Dim k As I n t e g e r
Dim a b r i r As New O p e n F i l e D i a l o g
a b r i r . M u l t i s e l e c t = True
a b r i r . AddExtension = True
a b r i r . F i l t e r = ”mapas de b i t s ( * . bmp) | * . bmp”
a b r i r . ShowDialog ( )
For k = 0 To a b r i r . FileNames . Length − 1
Dim f o t o As Bitmap = New Bitmap ( a b r i r . FileNames ( k ) )
Dim d e s d e I As I n t e g e r = CInt ( 0 . 1 2 5 * f o t o . Width )
Dim desdeJ As I n t e g e r = CInt ( 0 . 1 2 5 * f o t o . Height )
Dim h a s t a I As I n t e g e r = CInt ( 0 . 8 8 * f o t o . Width )
Dim h a s t a J As I n t e g e r = CInt ( 0 . 8 8 * f o t o . Height )
Dim f o t o 2 As New Bitmap ( h a s t a I − d e s d e I , h a s t a J − desdeJ )
Dim i As I n t e g e r
Dim j As I n t e g e r
For i = d e s d e I To h a s t a I − 1
For j = desdeJ To h a s t a J − 1
f o t o 2 . S e t P i x e l ( i − d e s d e I , j − desdeJ ,
f o t o . GetPixel ( i , j ) )
Next
Next
Dim r u t a As S t r i n g = a b r i r . FileNames ( k )
r u t a = r u t a . R ep l ac e ( ” . ” , ” r e d u c i d o . ” )
f o t o 2 . Save ( r u t a )
Next
MsgBox ( ”La r e d u c c i \ ’ on f i n a l i z \ ’ o 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 )
Catch ex As E x c e p t i o n
MsgBox ( ” E r r o r en l a r e d u c c i \ ’ on” , MsgBoxStyle . C r i t i c a l )
End Try
End Sub
End C l a s s
reduceImagenes/apli/WindowsApplication1/reduceImagenes.vb

264

También podría gustarte