Desarrollo de aplicaciones de audio en C++: un enfoque

Transcripción

Desarrollo de aplicaciones de audio en C++: un enfoque
UNIVERSIDAD NACIONAL DE EDUCACIÓN A DISTANCIA
ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA INFORMÁTICA
Proyecto de Fin de Carrera de Ingeniero Informático
Desarrollo de aplicaciones de audio en C++: un
enfoque práctico
Carlos Jiménez de Parga Bernal - Quirós
Dirigido por: D. Antonio Jiménez de Parga Bernal - Quirós
Supervisado por: D. José Luis Fernández Marrón
Curso: 2010 - 2011 (21 de diciembre de 2010)
Desarrollo de aplicaciones de audio en C++: un enfoque práctico
Proyecto de Fin de Carrera de modalidad específica
Realizado por: Carlos Jiménez de Parga Bernal - Quirós
Dirigido por: D. Antonio Jiménez de Parga Bernal - Quirós
Supervisado por: D. José Luis Fernández Marrón
Tribunal calificador:
a
Presidente: D./D . .......................................................................................................................
a
Secretario: D./D . ........................................................................................................................
a
Vocal: D./D . ...............................................................................................................................
Fecha de lectura y defensa: ........................................................................................................
Calificación................................................................................................................................
1
1. Resumen: Estudio sobre el panorama actual del desarrollo de aplicaciones de audio de alto
rendimiento en entornos multiplataforma escritos en lenguaje C++. Abordado desde la
perspectiva teórica de los fundamentos físicos del sonido, la tecnología electrónica, los
sintetizadores musicales, software libre para el desarrollo de aplicaciones y enfoque teórico y
práctico sobre los principios del tratamiento digital de señales.
2. Lista de palabras clave: Onda, Sonido, Fourier, Armónicos, Espectro, Instrumentos
musicales, Sintetizador, Teorema de Nyquist, Aliasing, Decibelios, Síntesis musical, DirectX,
DirectSound, COM, OpenAL, Buffer, MIDI, Sistema Exclusivo, Canal MIDI, Esquemático MIDI,
DirectMusic, DirectMIDI, Formatos de sonido, Audiere, PCM, WAV, AIFF, MP3, XM, MOD,
Procesador Digital de Señal, FFT, Transormada Rápida de Fourier, Filtros, FIR, IIR,
Envolventes.
3. Traducción del título: Audio application development in C++: a practical approach
4. Traducción del resumen: Study on currently available multi-platform libraries in C++ for
high-performance audio application development. Theoretical approach to the physics of sound,
audio synthesizer hardware, open-source software for audio applications and signal processing
principles.
5. Traducción de las palabras clave: Waveform, Sound, Fourier, Armonics, Spectrum,
Musical instruments, Synthesizer, Nyquist’s theorem, Aliasing, Decibels, Music synthesis,
DirectX, DirectSound, OpenAL, Buffer, MIDI, Exclusive System, MIDI Channel, MIDI schematic,
DirectMusic, DirectMIDI, Sound formats, Audiere, PCM, WAV, AIFF, MP3, XM, MOD, Digital
Signal Processor, FFT, Fast Fourier Transform, Filters, FIR, IIR, Envelopes.
2
6. Índice
3
1. Resumen...........................................................................................................................
2
2. Lista de palabras clave.....................................................................................................
2
3. Traducción del título.........................................................................................................
2
4. Traducción del resumen...................................................................................................
2
5. Traducción de las palabras clave.....................................................................................
2
6. Índice................................................................................................................................
3
7. Listas de figuras...............................................................................................................
4
8. Cuerpo..............................................................................................................................
5
8.1 Capítulo 1...................................................................................................................
5
8.1.1
Contexto........................................................................................................
5
8.1.2
Trabajos anteriores........................................................................................
9
8.1.3
Aportaciones y conclusiones.........................................................................
9
8.2 Capítulo 2...................................................................................................................
10
8.2.1
Contexto........................................................................................................
10
8.2.2
Validación mediante un prototipo..................................................................
11
8.2.3
Trabajos anteriores........................................................................................ 12
8.2.4
Aportaciones y conclusiones.........................................................................
12
8.3 Capítulo 3...................................................................................................................
12
8.3.1
Contexto........................................................................................................
12
8.3.2
Validación mediante un prototipo..................................................................
13
8.3.3
Trabajos anteriores........................................................................................ 13
8.3.4
Aportaciones y conclusiones.........................................................................
13
8.4 Capítulo 4...................................................................................................................
14
8.4.1
Contexto......................................................................................................... 14
8.4.2
Trabajos anteriores........................................................................................ 15
8.4.3
Aportaciones y conclusiones.........................................................................
15
8.5 Capítulo 5...................................................................................................................
16
8.5.1
Contexto......................................................................................................... 16
8.5.2
Desarrollo del subproyecto DirectMIDI..........................................................
17
8.5.3
Trabajos anteriores........................................................................................ 18
8.5.4
Aportaciones y conclusiones.........................................................................
18
8.6 Capítulo 6...................................................................................................................
18
8.6.1
Contexto........................................................................................................
18
8.6.2
Validación mediante un prototipo..................................................................
19
8.6.3
Aportaciones y conclusiones.........................................................................
20
8.7 Capítulo 7...................................................................................................................
20
8.7.1
Contexto........................................................................................................
20
8.7.2
Validación mediante un prototipo..................................................................
22
8.7.3
Trabajos anteriores........................................................................................ 22
8.7.4
Aportaciones y conclusiones.........................................................................
22
9. Listado de referencias y Bibliografía.................................................................................
23
10. Listado de siglas, abreviaturas y acrónimos.....................................................................
28
11. Anexos.............................................................................................................................
29
7. Listas de figuras
Figura 8.1 – Umbrales de tolerancia al sonido......................................................................
6
Figura 8.2 – Formas de onda básicas...................................................................................
7
Figura 8.3 – Envolvente de volumen.....................................................................................
7
Figura 8.4 – Muestreo de señal.............................................................................................
8
Figura 8.5 – Arquitectura básica de DirectX..........................................................................
10
Figura 8.6 – Ciclo de vida en cascada..................................................................................
11
Figura 8.7 – Logotipo de MIDI...............................................................................................
14
Figura 8.8 – Logotipo del proyecto DirectMIDI......................................................................
17
4
8. Cuerpo
8.1 Capítulo 1
8.1.1 Contexto
En este capítulo se abordan varios conceptos de la teoría básica del sonido. Se
comienza introduciendo al lector en los conceptos de generación de la onda y
cómo ésta es transmitida por el espacio. Para explicar estos conceptos se
exponen diversos ejemplos ilustrativos. Posteriormente, se aborda en el
capítulo los tipos de onda que se dan en la naturaleza.
Otro aspecto que se aborda en este capítulo son los conceptos de las
magnitudes de la onda: periodo, longitud de onda, frecuencia y amplitud,
aspectos éstos fundamentales en la física de la onda. Tan importante como los
conceptos de magnitudes es la ecuación matemática que describe la onda, de
la cual se realiza un desarrollo teórico.
A continuación se explica uno de los conceptos fundamentales que serán de
gran utilidad para poder seguir el tratado y que son de importancia relevante en
el capítulo 7 de Tratamiento Digital de Señales. Estos son la representación de
la señal en el dominio del tiempo y en el dominio de la frecuencia. Dentro de
este apartado también se realiza una introducción al concepto de espectro.
Se explica también en este capítulo el Teorema de Fourier que demuestra que
cualquier función periódica continua, con un número finito de máximos y
mínimos, puede desarrollarse en una serie trigonométrica uniformemente
convergente, llamada serie de Fourier.
Las conceptos de ondas armónicas y el ruido con sus respectivas
representaciones en el dominio y en la frecuencia son un tema también tratado
en este capítulo.
Posteriormente se pasa a introducir la teoría de la propagación del sonido a
través de diferentes medios físicos y sus características. Se describen las
velocidades que alcanza el sonido en el agua, la madera y el acero.
5
En el tercer apartado del primer punto del capítulo se menciona, sin llegar a lo
exhaustivo, el funcionamiento del oído humano, sus principales órganos
componentes y cómo se relacionan estos con el cerebro para producir la
sensación acústica. Por tanto, se explican cada una de estas partes que son: el
oído externo, el oído medio y el oído interno. Como ejemplo ilustrativo se
expone un diagrama de bloques que relaciona cada parte del oído con un
sistema digital de audio.
Dentro de este apartado se definen a grandes rasgos las principales cualidades
del sonido que son: intensidad, tono, timbre y duración.
Es conocido por todos el rango de frecuencias que abarca el espectro audible,
esto es, un rango que va desde los 20 a los 20000 Hz. Como ejemplo práctico
se representa mediante una tabla los diferentes rangos de frecuencia de las
diferentes octavas musicales.
Se incluye en este apartado un gráfico con los umbrales de la tolerancia
humana de los sonidos en decibelios; como se muestra en la siguiente figura:
Figura 8.1 Umbrales de tolerancia al sonido
Por tanto, y para detallar más este apartado se ha querido añadir una tabla con
los diferentes niveles de presión sonora.
6
Dentro de un nuevo apartado se explica los conceptos teóricos de la
generación del sonido analógico y digital. Para explicar la idea de generación
de sonido analógico se ha hecho alusión a los tres tipos básicos de
instrumentos: de percusión, de viento y de cuerda. Ya dentro de este mismo
apartado se explica la teoría de la síntesis analógica de audio y la electrónica
que lo hace posible. Los componentes electrónicos fundamentales para la
generación analógica son:
Oscilador: es un circuito electrónico capaz de generar una onda periódica a una
determinada frecuencia. Por ejemplo estos dos tipos de onda:
Figura 8.2 Formas de onda básicas
Generador de envolvente: es un circuito que genera una señal que permite
controlar un parámetro dentro de un sistema de síntesis. La señal envolvente
se suele usar para modular la amplitud de la señal armónica, y de esta manera
simular las variaciones de amplitud de un instrumento de cuerda. Otros tipos de
envolvente se utilizan para modular el tono o pitch de la señal.
Figura 8.3 Envolvente de volumen
Amplificador de ganancia controlada: es un sistema que utilizan los
sintetizadores analógicos cuya ganancia puede ser controlada por una tensión
auxiliar.
7
Filtro: es un circuito selectivo en frecuencia que deja pasar unas mientras
atenúa otras. Este componente es fundamental para la síntesis analógica. Los
tipos básicos son: paso-bajo, paso-alto, paso-banda y elimina-banda.
Tan importante como la síntesis analógica es la síntesis digital, por eso se hace
un repaso en este apartado a las técnicas que comenzaron a desarrollarse a
finales del siglo XX para la generación digital de sonido. En este capítulo se
explican:
La síntesis aditiva: Consistente en generar un sonido periódico, más complejo,
resultante de la suma de ondas sinusoidales más elementales de frecuencia
múltiplo de una frecuencia base.
Modulación de frecuencia o síntesis FM: Una de las formas de poder realizar
síntesis de sonido es por medio de la modulación, que consiste en variar la
frecuencia de la señal portadora en función de la señal moduladora, generando
una señal modulada.
Síntesis por tabla de onda: Consiste en reproducir pequeños fragmentos de
sonidos digitalizados y almacenados en memoria escaládolos según la nota
deseada.
En el último apartado de este capítulo se abordan los conceptos de
representación del sonido. Dentro del mismo se explica en detalle el concepto
de Conversión analógico / digital, para lo cual es necesario entender el
Teorema de Nyquist, que indica que para muestrear una señal es necesario
hacerlo al menos al doble de la frecuencia máxima de la señal analógica de
entrada. En el siguiente gráfico se muestra una señal analógica muestreada
para su representación digital:
Figura 8.4 Muestreo de señal
8
Dentro de este apartado se explica, igualmente, el proceso de conversión de
una señal analógica en otra digital, de relevante importancia para el tratamiento
digital de señales. Junto con el concepto de muestreo, se explican también los
conceptos de cuantización y codificación.
Antes de abordar el siguiente tema se hace un breve apunte sobre las formas
de representación del sonido muestreado que son: PCM, PAM y PDM.
No se podría terminar este apartado sobre la conversión analógico-digital sin
explicar el concepto de aliasing, que es un efecto que puede ocurrir durante la
conversión y que se produce cuando la señal que se muestra tiene
componentes de frecuencia por encima de fs/2 o frecuencia de Nyquist.
Por último, para concluir el capítulo se explica el concepto de conversión
digital-analógica.
8.1.2 Trabajos anteriores
Algunos trabajos anteriores que versan sobre los temas previamente expuestos
son: John G. Proakis & Dimitris G. Manolakis – Tratamiento Digital de Señales,
libros de Física de preuniversitario y Técnicas de Grabación modernas de la
editorial Omega.
8.1.3 Aportaciones y conclusiones
En este capítulo se ha pretendido dar a conocer al lector las nociones básicas
de teoría del sonido, sin ánimo de ser demasiado teórico ni demasiado básico;
tratando de abordar los conceptos complejos de una manera sencilla y
entendible para el lector y/o estudiante.
9
8.2 Capítulo 2
8.2.1 Contexto
Este capítulo se centra principalmente en la programación en DirectX con
Visual C++ para la generación de audio con DirectSound.
El capítulo comienza con una introducción a la filosofía de DirectX, desarrollada
principalmente para los juegos en plataformas Windows que siempre han
necesitado de altas prestaciones. Se explica conjuntamente la arquitectura de
DirectX que es esencialmente una arquitectura software por capas, como se
muestra en la figura 8.5:
DirectX
HAL
HEL
Hardware
Figura 8.5 Arquitectura básica de DirectX
Posteriormente se describen los componentes del paquete SDK de DirectX,
para pasar finalmente a una breve introducción a la filosofía COM (Component
Object Model – Modelo de Objetos Componentes), que junto a DCOM
conforma uno de los pilares fundamentales de la arquitectura de componentes
de Windows.
Después de la breve introducción a COM se entra definitivamente, y ya en
profundidad, en la programación en DirectSound. Se explica al lector los
fundamentos de la arquitectura DirectSound para posteriormente desarrollar los
distintos formatos de sonido soportados por la librería. Para explicar los
fundamentos de programación en DirectSound se ha procedido a desglosar los
contenidos en las siguientes partes:
10
Creación del objeto DirectSound
Establecimiento del nivel cooperativo
Creación del buffer primario
Introducción a los buffers secundarios
Pasos para crear los buffers secundarios
Archivos de sonido
Descripción del buffer
Creación del buffer secundario
Liberación de las interfaces COM
Por último, y debido al interés por el sonido en tres dimensiones se explica
cómo hacer que la aplicación soporte buffers en 3D.
8.2.2 Validación mediante un prototipo
Para aportar un ejemplo de aplicación en DirectX / DirectSound, se ha
desarrollado una aplicación en el entorno Visual C++ 2005, en modo consola,
llamada dsound.exe que se podrá encontrar en el directorio de binarios del CDROM del proyecto. Para escribir esta aplicación Win32 se ha utilizado un
esquema de ciclo de vida en cascada, puesto que los requisitos de los usuarios
están bastante claros y la tecnología es madura:
Figura 8.6 Ciclo de vida en cascada
11
8.2.3 Trabajos anteriores
Existen otros trabajos anteriores sobre la programación de DirectSound,
especialmente en la Web, pero fundamentalmente el libro de referencia para la
programación en DirectX / DirectSound es el publicado por la editorial de
Microsoft: a fondo DirectX.
8.2.4 Aportaciones y conclusiones
Se ha procurado explicar los conceptos complejos de la arquitectura COM sin
entrar en demasiado detalle, pues es un tema complicado para desarrolladores
que están acostumbrados a ser clientes invocadores de tales objetos. Se ha
pretendido, sobre todo, dar una visión de la potencia y el alcance mundial que
DirectSound ha tenido durante su larga historia hasta el DirectX10. En las
versiones actuales de DirectX aún se soportan las API’s de DirectSound con
las antiguas interfaces; no obstante se ha creado una nueva arquitectura para
sistemas de 64 bits y nuevas funciones de la API que han dejado un poco
desplazadas a las antiguas de DirectSound.
8.3 Capítulo 3
8.3.1 Contexto
En este capítulo se aborda la programación en la librería OpenAL. Esta librería
es una versión multiplataforma de DirectSound de la compañía Creative Labs.
Para iniciar el capítulo se introduce al lector en la arquitectura de OpenAL,
mucho más sencilla y directa que la de su homólogo DirectX.
Una vez explicado el funcionamiento de la arquitectura, se comienza a exponer
al lector los fundamentos básicos para la programación en OpenAL. Para ello
se ha dividido el apartado en las siguientes secciones a fin de seguir una
metodología más pedagógica. Los apartados sobre la programación OpenAL
son los siguientes:
Inicialización
12
Trabajo con buffers
Reproducción de sonido
Posicionamiento 3D
Salida de la aplicación
Gestión de errores
Por último se comentan las principales ventajas de OpenAL como librería
multiplataforma
de
alto
rendimiento
y muy utilizada
actualmente
en
videojuegos, junto con la ventaja adicional de que no es necesario la
instalación de ningún plug-in para su ejecución. Además de esto, se comenta
también que su programación y su diseño es mucho más versátil que el de
DirectSound.
8.3.2 Validación mediante un prototipo
Se ha desarrollado una aplicación con Visual C++ 2010 sobre Win32 y
OpenAL, en modo consola, para demostrar las utilidades de esta API, así como
ilustrar al lector con un ejemplo compuesto de varias fases de cómo escribir
una aplicación con los elementos básicos de OpenAL.
La aplicación de demostración se puede encontrar en el CD-ROM del proyecto
en la carpeta de binarios, con el nombre openal.exe.
Por último comentar que se ha utilizado un esquema de ciclo de vida en
cascada como el que se ilustra en la figura 8.6.
8.3.3 Trabajos anteriores
No hay constancia de otros trabajos en castellano sobre la librería de OpenAL.
Para el desarrollo del proyecto se han utilizado los manuales oficiales que
están publicados en la página principal de Creative Labs.
8.3.4 Aportaciones y conclusiones
Se ha explicado al lector la programación en OpenAL de una forma
metodológica y pedagógica que sea fácilmente comprensible, de esta forma,
13
se han evitado construir aplicaciones complejas con interfaces gráficas de
usuario para no desviar la atención del lector hacia aspectos diferentes de la
API de audio.
8.4 Capítulo 4
8.4.1 Contexto
En este capítulo sobre tecnología MIDI se exponen todos los aspectos teóricos
sobre la interfaz para la interconexión de instrumentos musicales digitales.
En el principio del capítulo se presenta al lector en una breve historia de MIDI,
así como el origen y la concepción de este sistema innovador que sigue
perdurando en la actualidad.
Figura 8.7 Logotipo de MIDI
Posteriormente se explican algunos conceptos de la utilización de la tecnología
y los entornos más usuales donde suele aplicarse.
Como sección de interés para lectores especializados en diseño electrónico se
explica con detalle todo el hardware en torno al sistema MIDI. Para ello se
ilustran detalles sobre los conectores, cables MIDI, pines, y esquemáticos. Se
exponen de esta forma las ideas básicas de los diferentes puertos MIDI: MIDI
in, MIDI out y MIDI thru.
Además de la configuración explicada anteriormente se comentan los
diferentes modos de interconexión de sistemas, por medio de USB, FireWire y
conexiones de red mLAN.
No podía faltar en un capítulo sobre MIDI información sobre los diferentes
mensajes que componen la especificación mantenida por la MMA. Por tanto se
comenta de manera detallada este significado de los campos de los diferentes
14
mensajes midi. Para ello se explican los fundamentos de los canales MIDI y los
mensajes de canal: note on, note off, poly aftertouch, channel aftertouch, etc.
Además de los mensajes de canal se explican los mensajes de sistema
exclusivo, necesarios para controlar los dispositivos y que permiten el uso de
un lenguaje ajeno al protocolo musical, y que al ser propietario de cada sistema
se requiere saber su especificación según cada fabricante.
Para entender esta terminología se explican en el capítulo los fundamentos de
transmisión y recepción SYX.
Al finalizar el capítulo se explican los fundamentos del tiempo MIDI y su
sincronización.
8.4.2 Trabajos anteriores
Hay mucha bibliografía y referencias en la Web tanto en inglés como en
castellano, debido a la repercusión y popularidad del tema. Principalmente, se
ha recurrido a fuentes en Internet, especialmente a la Web de la MMA y una
página
Web
bastante
afamada
entre
los
desarrolladores
llamada
peculiarmente: MIDI Technical Fanatic's Brainwashing Center
8.4.3 Aportaciones y conclusiones
Se ha pretendido en este capítulo introducir al lector en los fundamentos y la
terminología MIDI. Se ha intentado ser breve en los conceptos menos
relevantes y más exhaustivo en los conceptos orientados a la programación de
sistemas en esta tecnología. El enfoque principalmente teórico de este capítulo
ha servido como puente para introducir al lector en el siguiente capítulo que
versará sobre programación MIDI con la librería LGPL DirectMIDI.
15
8.5 Capítulo 5
8.5.1 Contexto
Una vez explicados los conceptos básicos sobre MIDI se introduce al lector en
los fundamentos de programación de estos sistemas. Para entender los
entresijos técnicos de la programación en MIDI se ha centrado el capítulo en la
librería de software libre DirectMIDI. Esta librería está basada en DirectX y
actualmente se encuentra disponible para sistemas Windows de 32 bits. Este
1
software utiliza una licencia GNU LGPL que, según Richard Stallman , es un
método para licenciar software de tal forma que su uso y modificación
permanezcan siempre libres y queden en la comunidad, permitiendo su uso
libremente por otros desarrolladores sin coste alguno.
Para comenzar la explicación sobre el uso de la librería se introduce
primeramente al lector en los fundamentos de las interfaces COM más
importantes de DirectMusic que permitirán la implementación de las
funcionalidades.
Después se explica cómo obtener el software del repositorio de software libre
2
SourceForge .
Posteriormente se ilustra mediante gráficos la arquitectura de objetos que
constituyen la librería y sus objetivos dentro del sistema de clases.
Para mostrar el uso de la librería en C++ se han seguido los siguientes pasos
en la metodología:
Configuración del entorno de desarrollo
Creación de las primeras líneas de código
Preparación de la captura de música
Inicialización de objetos
Comienzo de la captura de música
Aumento del límite de instrumentos
1
es un programador estadounidense y figura relevante del movimiento por el software libre en
el mundo. Ver proyecto GNU.
2
http://www.sourceforge.net
16
o
DLS de alto nivel
o
DLS de bajo nivel
Terminación la aplicación
Por último se explica cómo manejar excepciones para el tratamiento de errores
de la librería.
8.5.2 Desarrollo del subproyecto DirectMIDI
3
El proyecto DirectMIDI se comenzó en septiembre de 2002 con la idea de
incluirlo en un futuro proyecto de fin de carrera universitario. Inicialmente se
orientó a su publicación en la revista electrónica para programadores en
4
plataformas Windows CodeProject en la que obtuvo una gran aceptación y
valoración. Posteriormente se licenció como software libre y se publicó en el
repositorio SourceForge.
El proyecto ha sido desarrollado en Visual C++ .NET 7.1 utilizando la API
Win32 y DirectX. Para su desarrollo se ha utilizado un esquema de ciclo de
vida en espiral, puesto que es un proyecto complejo, novedoso y los cambios
en los requisitos son frecuentes.
Se puede encontrar una copia de la librería en el directorio de fuentes del
capítulo y un ejemplo de programación en el directorio de binarios, llamado
Example_mid.exe. Para ejecutar este programa es necesario tener un
ordenador con un puerto de entrada MIDI.
Figura 8.8 Logotipo del proyecto DirectMIDI
3
4
Página Web del proyecto: http://directmidi.sourceforge.net
http://www.codeproject.com
17
8.5.3 Trabajos anteriores
Existe un libro sobre programación MIDI en Windows que ha sido un referente
en toda la década llamado: Maximum MIDI music applications in C++.
8.5.4 Aportaciones y conclusiones
Las aportaciones de este capítulo tienen una doble vertiente: por un lado se ha
intentado explicar lo más cómodamente posible la programación con la librería
de manera que sea amigable y entretenida para el programador de
aplicaciones musicales; por otro lado, se ha de tener en cuenta que el
desarrollo de la librería DirectMIDI, que transcurrió entre los años 2002 a 2004,
fue orientado a obtener una librería MIDI con resultados eficientes y que
pudiera ser utilizada en el campo profesional y de investigación. Una de las
características que se resaltaron de la librería fue la baja latencia del timer MIDI
que usa DierectMusic y que permite aplicaciones con un mejor rendimiento.
Actualmente la librería se encuentra en estado beta y disponible desde DirectX
9. Se pretender publicar en breve una versión para plataformas de 64 bits.
8.6 Capítulo 6
8.6.1 Contexto
En este capítulo se aborda el tema fundamental para el programador de
aplicaciones de audio, que es el problema de añadir capacidades de
reproducción y manipulación de ficheros de audio conocidos.
Para comenzar, se introduce una sección sobre el estado actual de las librerías
de reproducción de audio. Posteriormente se presenta al lector la librería de
5
software libre Audiere , un potente software en varias plataformas que permite
la reproducción de diversos formatos de audio: Ogg Vorbis, MP3, WAV, MOD,
XM.
5
http://audiere.sourceforge.net/
18
La librería, que está implementada y orientada a la programación en C++,
accede al hardware de audio de varias plataformas con diferentes tecnologías:
DirectSound y WinMM en Windows y OSS en Linux.
Para iniciar al lector en la programación en Audiere se comienza explicando los
fundamentos de la API, así como la jerarquía de clases que componen el
sistema. Por tanto para llegar a reproducir un fichero de audio son necesarios
los siguientes pasos que se explican en el capítulo:
La clase AudioDevice
La clase OutputStream
Reproducción y efectos
Por último se explica cómo acceder al buffer PCM para obtener las muestras.
En este caso se ilustra un ejemplo que invierte un fichero de música MP3.
Para finalizar el capítulo se describen las características de los principales
formatos de audio. Los formatos que se explican en este capítulo son:
WAV
AIFF
MPEG:MP3
General Midi MID
XM / MOD
8.6.2 Validación mediante un prototipo
En este capítulo se han desarrollado dos prototipos de prueba para ilustrar los
ejemplos explicados. El primero de ellos llamado audiere.exe demuestra las
capacidades de la librería usando las funciones play, setPan (para cambiar el
balance), SetPitchShift (para cambiar la frecuencia de reproducción de las
muestras) y stop.
La segunda aplicación desarrollada se llama invert.exe cuya finalidad es leer un
fichero de audio y reescribir las muestras al revés (en backward).
19
Para desarrollar estos proyectos se ha seguido un ciclo de vida en cascada
puesto que la tecnología es madura y los requisitos son conocidos desde el
principio.
8.6.3 Aportaciones y conclusiones
La idea fundamental de este capítulo es iniciar al programador de aplicaciones
de audio en el uso de librerías de código fuente abierto y software libre para su
inclusión en sus programas. Con la librería que se expone en el capítulo se
pretende facilitar en gran medida el desarrollo de aplicaciones para reproducir
archivos de audio que de lo contrario conllevarían un período de desarrollo muy
largo y un aumento del coste.
Otro aspecto que se ha intentado exponer son los diferentes formatos de audio
existentes en la actualidad y que están más extendidos en el mundo del
desarrollo de software de audio digital.
8.7 Capítulo 7
8.7.1 Contexto
El capítulo final del tratado se orienta a los fundamentos del Tratamiento Digital
de Señal. Este tema es transcendente por su amplia difusión en el entorno
académico y profesional y porque su repercusión en el desarrollo de
aplicaciones tanto de imagen como de audio ha sido muy importante en estos
últimos años.
El capítulo comienza explicando los aspectos más relevantes de la historia del
tratamiento digital de señales y sus ámbitos de aplicación. Posteriormente, se
comienza a introducir al lector en los algoritmos básicos utilizados en el audio
que se clasifican en lineales y no lineales. A continuación se pasa a explicar los
fundamentos de los dos tipos de filtros existentes: los filtros IIR y FIR, que son
los acrónimos de Infinite Impulse Response y Finite Impulse Response
respectivamente. Para explicar estos conceptos se detallan con gráficos sus
20
principales estructuras y se presentan sus ecuaciones. Dentro del apartado de
algoritmos se explica también en qué consiste la técnica de envolvente de
volumen, así como el retardo y los efectos basados en el mismo: coros, eco,
reverberación, phaser y flanger.
Posteriormente, y dentro del apartado de algoritmos, se explican los
fundamentos de la técnica de Compresión del rango dinámico.
Una de las herramientas más utilizadas actualmente en los proyectos de audio
es la Transformada Rápida de Fourier o FFT, la cual se explica detalladamente
a lo largo del capítulo.
Para finalizar, se introduce al lector en la programación C++ de aplicaciones de
TDS utilizando la librería gratuita para fines educacionales Mitov. En concreto
se explica detalladamente al lector cómo se desarrolla una aplicación en Visual
C++ con esta librería desde cero.
Para conseguir este propósito se utilizará la librería GUI MFC (Microsoft
Foundation Classes), que tiene la ventaja de ser una API primitiva y no
necesitar ninguna versión de sistema operativo Windows avanzado, ni tan
siquiera la instalación de .NET.
En primer lugar se explica al lector cómo utilizar las librerías SignalLab y
AudioLab para generar una forma de onda básica por pantalla y poder
reproducir un fichero WAV.
Después de explicar estos conceptos se presenta un ejemplo de aplicación
real de uso de la librería para el análisis de señales de audio. Para conseguir
este propósito se explica al lector el diagrama de bloques que usará la
aplicación, así como el código fuente necesario para instanciar los objetos
Mitov que requiere la aplicación.
El ejemplo que se propone es una aplicación C++ para el análisis de una forma
de onda en formato WAV, aplicación de filtros paso banda, paso bajo y paso
alto y ecualización de 10 bandas. Por último la salida de la señal se visualizará
en tres tipos de presentación diferentes: espectrograma (waterfall), dominio del
tiempo y dominio de la frecuencia (FFT).
21
8.7.2 Validación mediante un prototipo
Como se ha mencionado en el apartado anterior, se ha desarrollado una
aplicación en Visual C++ que refleja la tendencia actual del uso de las técnicas
fundamentales del Tratamiento Digital de Señal.
Para desarrollar la aplicación propuesta se ha servido de la librería gratuita
para fines no comerciales Mitov.
Para la ingeniería del proyecto se ha utilizado un ciclo de vida en cascada
(figura 8.6), puesto que los requisitos fueron indicados por el Director y eran
claros desde el principio.
La aplicación se puede encontrar en el CD-ROM del proyecto dentro del
directorio de binarios con el nombre tds.exe.
8.7.3 Trabajos anteriores
Se han escrito muchos tratados sobre el Tratamiento Digital de Señal, aunque
la mayor parte de la bibliografía se encuentra en lengua inglesa. En la Web hay
multitud de referencias sobre este tema tanto desde el punto de vista
comercial, académico como de aficionado. No obstante, el principal libro de
referencia anteriormente escrito y utilizado por la mayoría de la Universidades
es el de John G. Proakis & Dimitris G. Manolakis – Tratamiento Digital de
Señales.
8.7.4 Aportaciones y conclusiones
La idea de este capítulo es introducir al lector en los conceptos de Tratamiento
de Señales, con el ánimo de hacer una lectura amena de un tema
eminentemente complejo. Aunque este capítulo es de dificultosa lectura se ha
intentado centrase en el aspecto más pragmático del mismo, huyendo de un
riguroso formalismo matemático y centrándose en los aspectos que conciernen
a las aplicaciones informáticas de sonido en la actualidad.
22
9. Listado de referencias y Bibliografía
9.1 Capítulo 1
Libros
1. [Alonso et al] Diseño y desarrollo Multimedia, Sistemas, Imagen, Sonido y
Vídeo, Ra-ma, 2002.
2. [Candel et al] Física COU, Anaya, 1990.
3. [Cuenca et al] Tecnología Básica del Sonido I, Paraninfo, 2006.
4. [Miles et al] Técnicas de Grabación modernas, Omega, 2007.
5. [Sánchez] Apuntes Redes 3º IT Informática de Sistemas, 1997.
La Web
6. http://www.mrfizzix.com/instruments/
7. http://www.ehu.es/acustica/espanol/fisiologia1/siaues/siaues.html
8. [Herrera] http://sam.atlantes.org/
9. [Wikipedia] http://es.wikipedia.org/wiki/Cualidades_del_sonido
10. [Wikipedia] http://es.wikipedia.org/wiki/O%C3%ADdo
11. [Wikipedia] http://es.wikipedia.org/wiki/Octava
23
12. [Wikipedia] http://en.wikipedia.org/wiki/Frequency_modulation_synthesis
13. [Wikipedia]
http://es.wikipedia.org/wiki/S%C3%ADntesis_mediante_tabla_de_ondas
9.2 Capítulo 2
Libros
14. [Bradley et al] A fondo DirectX, Microsoft Press, 1998.
15. [Colouris et al] Sistemas Distribuidos: conceptos y diseño, Pearson, 2001.
16. [Gordon] Programación COM y COM+ , Anaya Multimedia, 2000.
La Web
17. [Gimeno]
http://usuarios.multimania.es/andromeda_studios/paginas/tutoriales/articulo02.
htm
18. [MSDN] http://msdn2.microsoft.com/es-es/default.aspx
9.3 Capítulo 3
Manuales
19. [Creative Labs] OpenAL Programmer's Guide - OpenAL Versions 1.0 and 1.1
24
La Web
20. [Wikipedia] http://en.wikipedia.org/wiki/OpenAL
9.4 Capítulo 4
Libros
21. [Alonso et al] Diseño y desarrollo Multimedia, Sistemas, Imagen, Sonido y
Vídeo, Ra-ma, 2002.
22. [Miles et al] Técnicas de Grabación modernas, Omega, 2007.
La Web
23. [MIDI Manufacturers Association] http://www.midi.org/
24. http://home.roadrunner.com/~jgglatt/
25. [Wikipedia] http://es.wikipedia.org/wiki/MIDI
9.5 Capítulo 5
La Web
26. [Jiménez de Parga] http://directmidi.sourceforge.net/
27. [MSDN] http://msdn2.microsoft.com/es-es/default.aspx
25
9.6 Capítulo 6
Libros
28. [Alonso et al] Diseño y desarrollo Multimedia, Sistemas, Imagen, Sonido y
Vídeo, Ra-ma, 2002.
La Web
29. [Arribas]
http://www.lpi.tel.uva.es/~nacho/docencia/ing_ond_1/trabajos_01_02/formatos_audi
o_digital/html/midiformat.htm
30. [Austin et al] http://audiere.sourceforge.net/documentation.php
31. [MSDN] http://msdn2.microsoft.com/es-es/default.aspx
32. [Wikipedia] http://es.wikipedia.org/wiki/Waveform_Audio_Format
33. [Wikipedia] http://es.wikipedia.org/wiki/AIFF
34. [Wikipedia] http://es.wikipedia.org/wiki/MP3
35. [Wikipedia] http://es.wikipedia.org/wiki/General_MIDI
36. [Wikipedia] http://en.wikipedia.org/wiki/MOD_(file_format)
37. [Wikipedia] http://en.wikipedia.org/wiki/XM_(file_format)
26
9.7 Capítulo 7
Libros
38. [Schildt] Programación con MFC 6.0, Osborne McGraw-Hill, 1999.
La Web
39. [MSDN] http://msdn2.microsoft.com/es-es/default.aspx
40. [Mitov documentation] http://www.mitov.com
41. [Wikipedia] http://en.wikipedia.org/wiki/Digital_audio_editor
42. [Wikipedia] http://en.wikipedia.org/wiki/Sound_effect
43. [Wikipedia] http://paws.kettering.edu/~drussell/demos.html
44. [Wikipedia] http://en.wikipedia.org/wiki/Cooley–Tukey_FFT_algorithm
45. [Wikipedia] http://en.wikipedia.org/wiki/Butterfly_diagram
27
10. Listado de siglas, abreviaturas y acrónimos
Hz: Hertzios
VCA: Voltaje Controlled Amplifier (Amplificador controlador por tensión)
FM: Frequency Modulation (Modulación de frecuencia)
FFT: Fast Fourier Transform
IIR: Infinite Impulse Response (Respuesta infinita al impulso)
FIR: Finite Impulse Response (Respuesta finita al impulso)
PCM: Pulse Code Modulation (Modulación por pulsos codificados)
PAM: Pulse Amplitude Modulation (Modulación por amplitud de pulsos)
PDM: Pulse Density Modulation (Modulación de señal por densidad de
impulsos)
SDK: Software Development Kit
COM: Component Object Model (Modelo de objetos componentes)
DCOM: Modelo de objetos componentes distribuidos
CD-ROM: Compact Disk Read-Only Memory
API: Application Programming Interface (Interfaz de programación de
aplicaciones)
MIDI: Musical Instrument Digital Interface (Interfaz digital de instrumentos
musicales)
mLAN: Music Local Area Network
LGPL: Lesser General Public License
GNU: GNU no es Unix
DLS: Downloadable Sounds
MP3: Mpeg I audio layer III
WAV: Waveform audio file format
OSS: Open Sound System
AIFF: Audio Interchange File Format
MID: General MIDI format
MOD: Amiga music Module file
28
XM: Extended Module
GUI: Graphical User Interface
11. Anexos
A continuación se adjunta el tratado teórico correspondiente al proyecto de fin de carrera.
Esta obra está bajo una licencia Reconocimiento 3.0 España de Creative Commons.
Para ver una copia de esta licencia, visite http://creativecommons.org/licenses/by-sa/3.0/es/
o envie una carta a Creative Commons, 171 Second Street, Suite 300, San Francisco,
California 94105, USA.
29
Desarrollo de
Aplicaciones de
Audio en C++
Un enfoque práctico
Carlos Jiménez de Parga Bernal - Quirós
A mis padres Antonio y Carolina, por comprenderme y
acompañarme estos 12 años que he dedicado al estudio de esta
ciencia con tan gran potencial creativo como es la Informática.
En recuerdo de las personas que nos dejaron en el camino,
especialmente a mi tío Pepe y mi abuelo Juan, el cual me enseñó
a apreciar la música.
Prefacio
Es evidente el auge en capacidad de cómputo de los sistemas con
microprocesador en la actualidad. En un breve período de tiempo, hemos
pasado de procesadores de 8 bits con una capacidad computacional baja, a
procesadores de 64 bits con un alta frecuencia de reloj y gran capacidad de
cálculo. Así mismo, y paralelamente a este desarrollo en microprocesadores
para ordenadores personales, ha surgido una industria de fabricación de
dispositivos externos que permiten realizar tareas de E/S impensables hace
veinte años, en especial, los dispositivos de procesamiento de sonido como son:
tarjetas de audio, DSP’s (procesadores digitales de señal) y módulos hardware
de síntesis de sonido, basados en los últimos avances de la electrónica de física
acústica, software y multiprocesadores.
El sonido digital se ha convertido en objeto de estudio y forma parte de
nuestra cultura de hoy en día. Como ejemplos caben destacar: la música, donde
ya no es difícil escuchar temas o composiciones donde se inserte un sonido
digital, los videojuegos, donde las últimas tecnologías se abren paso para
producir efectos más realistas que introducen al jugador en una atmósfera de
gran verosimilitud, el cine, con efectos acústicos en tres dimensiones que
realzan las escenas, y un amplio rango de investigaciones que van desde la
compresión de audio y reconocimiento de voz, hasta la Inteligencia Artificial.
En lo que respecta a la programación de sonido profesional en
computadores personales, especialmente en Windows, Linux y Mac OS, se han
desarrollado componentes software accesibles a través del API del sistema
operativo, que permiten acceder a funciones de bajo nivel de manejo de puertos
MIDI y realizar E/S de formas de onda PCM a través de los canales de audio de
las tarjetas; abstrayendo la complejidad de la programación del hardware.
Estas API’s son esenciales para comprender el modelo de programación de
aplicaciones de audio profesionales, donde la latencia debe ser lo más baja
posible y poder conseguir así las máximas garantías de rendimiento y
fiabilidad.
Tan importante como conocer el software de abstracción de las
funcionalidades básicas de una tarjeta, es la teoría elemental del sonido, como
son: su comportamiento físico, sus parámetros, conversión A/D y D/A, la
percepción humana de las vibraciones acústicas en el espacio y los teoremas
fundamentales de física.
Es lógico que en un mundo donde la señal de sonido es computable y
analizable, sea posible realizar un tratamiento digital de señal para poder
manipularla, filtrarla, mezclarla y conseguir efectos en tiempo real sobre las
muestras capturadas por algún dispositivo externo. Esto, por lo tanto, es un
tema trascendental a nivel actual y profesional.
iii
Índice
Dedicatoria....................................................................................................................
Prefacio.........................................................................................................................
Índice............................................................................................................................
i
iii
v
PARTE I: Conceptos preliminares...........................................................................
1. Teoría del sonido..........................................................................................
1.1 Física de la onda...................................................................................
1.1.1 El concepto de onda..................................................................
1.1.2 Magnitudes de la onda..............................................................
1.1.3 Ecuación matemática de la onda...............................................
1.2 El sonido como forma de onda.............................................................
1.2.1 Dominio del tiempo y frecuencia..............................................
1.2.2 Teorema de Fourier...................................................................
1.2.3 Ondas armónicas y ruido...........................................................
1.2.4 Propagación del sonido.............................................................
1.3 Percepción humana del sonido.............................................................
1.3.1 Funcionamiento del oído...........................................................
1.3.2 Cualidades del sonido...............................................................
1.3.3 Espectro audible e intensidad sonora (Decibelios)...................
1.4 Generación de sonido analógico y digital.............................................
1.4.1 Instrumentos musicales convencionales...................................
1.4.2 Síntesis analógica......................................................................
1.4.3 Técnicas de síntesis digital........................................................
1.5 Representación del sonido....................................................................
1.5.1 Conversión Analógico / Digital...................................................
1.5.1.1 Teorema de Nyquist...................................................
1.5.1.2 Proceso de conversión................................................
1.5.1.3 El problema del aliasing.............................................
1.5.2 Conversión Digital / Analógico...................................................
Bibliografía y referencias...........................................................................
1
3
3
3
4
6
8
8
10
12
13
15
15
17
18
20
20
23
29
34
34
34
34
37
38
40
PARTE II: Programación..........................................................................................
2. DirectX y la API DirectSound......................................................................
2.1 ¿Qué es DirectX?..................................................................................
2.1.1 Introducción..............................................................................
2.1.2 Filosofía de DirectX..................................................................
2.1.3 Arquitectura de DirectX............................................................
2.1.4 Componentes de DirectX..........................................................
2.1.5 Programación en DirectX..........................................................
2.1.6 Tecnología COM.......................................................................
2.2 Programación en DirectSound..............................................................
2.2.1 Introducción..............................................................................
2.2.2 Arquitectura de DirectSound.....................................................
2.2.3 Formato de sonido soportado por DirectSound........................
2.2.4 Creación del objeto DirectSound..............................................
43
45
45
45
46
46
47
47
48
52
52
52
53
54
v
2.2.5 Establecer el nivel cooperativo.................................................
2.2.6 Crear el buffer primario............................................................
2.2.7 Introducción a los buffers secundarios......................................
2.2.8 Pasos para crear los buffers secundarios..................................
2.2.9 Archivos de sonido...................................................................
2.2.10 Describir el buffer....................................................................
2.2.11 Creación del buffer secundario................................................
2.2.12 Sonido en tres dimensiones......................................................
2.2.13 Bloqueo del buffer....................................................................
2.2.14 Reproducir y posicionar el buffer en 3D..................................
2.2.15 Liberar las interfaces................................................................
Bibliografía y referencias.................................................................................
55
56
57
57
58
58
59
60
61
63
64
65
3. OpenAL...........................................................................................................
3.1 Introducción a OpenAL........................................................................
3.2 Arquitectura de OpenAL......................................................................
3.3 Programación con OpenAL..................................................................
3.3.1 Inicialización.............................................................................
3.3.2 Trabajar con buffers..................................................................
3.3.3 Reproducir sonido.....................................................................
3.3.4 Posicionamiento 3D..................................................................
3.3.5 Salida de la aplicación...............................................................
3.3.6 Gestión de errores.....................................................................
3.4 Ventajas de OpenAL............................................................................
Bibliografía y referencias.................................................................................
67
67
67
69
69
71
73
74
76
77
77
78
PARTE III: MIDI.......................................................................................................
4. Introducción al MIDI..................................................................................
4.1 ¿Qué es MIDI?......................................................................................
4.1.1 Un poco de historia...................................................................
4.1.2 Conceptos básicos.....................................................................
4.1.3 Electrónica MIDI......................................................................
4.1.3.1 Interconexión de sistemas...........................................
4.1.3.2 Esquemáticos...............................................................
4.2 Especificación MIDI.............................................................................
4.2.1 Introducción..............................................................................
4.2.2 Canales MIDI............................................................................
4.3 Mensajes MIDI.....................................................................................
4.3.1 Mensajes de canal.....................................................................
4.3.2 Mensajes de sistema..................................................................
4.3.3 Mensajes de sistema exclusivo..................................................
4.3.4 Mensajes de modo....................................................................
4.3.5 Mensajes de tiempo real...........................................................
4.4 Sincronización y tiempo MIDI.............................................................
Bibliografía y referencias...........................................................................
79
81
81
81
82
84
84
87
88
88
88
90
90
93
94
95
96
98
99
5. Programación MIDI....................................................................................
5.1 Introducción..........................................................................................
5.2 La API DirectMusic de DirectX...........................................................
5.2.1 Características principales........................................................
101
101
102
102
vi
5.2.2 Principales interfaces COM......................................................
5.3 Desarrollando aplicaciones con la librería DirectMIDI........................
5.3.1 Introducción..............................................................................
5.3.2 Esquema de la arquitectura DirectMIDI...................................
5.3.3 Comenzando la aplicación........................................................
5.3.3.1 Primer paso: configurar el entorno de desarrollo........
5.3.3.2 Segundo paso: las primeras líneas de código..............
5.3.3.3 Tercer paso: preparando la captura de música............
5.3.3.4 Cuarto paso: inicializando objetos..............................
5.3.3.5 Quinto paso: comenzar la captura de música..............
5.3.3.6 Sexto paso: aumentando el límite de instrumentos.....
5.3.3.7 Séptimo paso: terminar la aplicación..........................
5.3.4 Manejo de excepciones.............................................................
Bibliografía y referencias...........................................................................
103
104
104
106
107
107
107
108
109
111
112
118
119
120
PARTE IV: Reproducción de formatos de sonido..................................................
6. Reproducción de formatos de sonido..........................................................
6.1 Introducción..........................................................................................
6.2 La librería Audiere................................................................................
6.2.1 Introducción..............................................................................
6.2.2 Clase AudioDevice...................................................................
6.2.3 Clase OutputStream..................................................................
6.2.4 Reproducción y efectos.............................................................
6.2.5 Acceso a los datos PCM de un buffer.......................................
6.3 Formatos de sonido conocidos..............................................................
6.3.1 Formato WAV..........................................................................
6.3.2 Formato AIFF...........................................................................
6.3.3 Formatos MPEG: MP3.............................................................
6.3.4 Formato General MIDI.............................................................
6.3.5 Formato MOD / XM.................................................................
Bibliografía y referencias..........................................................................
121
123
123
124
124
126
127
128
129
133
133
133
134
136
141
143
PARTE V: Tratamiento Digital de Señal.................................................................
7. Tratamiento Digital de Señal......................................................................
7.1 Procesado digital de señal aplicado al audio........................................
7.2 Algoritmos básicos para audio..............................................................
7.2.1 Filtros........................................................................................
7.2.2 Envolvente de volumen............................................................
7.2.3 Retardo......................................................................................
7.2.4 Compresión...............................................................................
7.3 Transformada rápida de Fourier (FFT).................................................
7.4 Librerías C++........................................................................................
7.4.1 Modelos de librerías..................................................................
7.4.2 Mitov.........................................................................................
7.4.3 Principales librerías de Mitov...................................................
7.4.4 Conceptos básicos de las librerías SignalLab y AudioLab.......
7.5 Desarrollo de una aplicación de procesado de señal.............................
7.5.1 Diagrama de bloques................................................................
7.5.2 Declaración de objetos de librería............................................
7.5.3 Inicialización e interconexión de objetos..................................
145
147
147
148
148
150
150
152
153
158
158
158
158
159
163
163
164
165
vii
7.5.4 Comportamiento de los filtros y el ecualizador........................ 167
7.5.5 Aspecto final de la aplicación................................................... 170
Bibliografía y referencias............................................................................................. 171
APÉNDICE.................................................................................................................
A1. Mensajes de la especificación MIDI 1.0.................................................
A2. Lista de mensajes de controladores MIDI 1.0.......................................
A3. Lista de instrumentos GM1 (General MIDI 1.0)..................................
A4. Tabla de frecuencias de notas musicales en Hertzios...........................
viii
173
173
176
179
181
Parte I
Conceptos preliminares
C A P Í T U L O 1 • Teoría del sonido
1
Teoría del sonido
1.1 Física de la onda
En nuestro entorno físico habitual estamos expuestos a perturbaciones ondulatorias
transmitidas por distintos medios: líquidos, sólidos, gases o incluso el vacío. La
naturaleza de la perturbación es diferente según el medio, y también la manera de
percibirla.
Un ejemplo cotidiano es el de la luz que nos viene del sol a través del espacio en
forma de radiación electromagnética.
En la figura 1.1 se muestra otro ejemplo de propagación de una onda cuando
sacudimos el extremo de una cuerda, la perturbación producida viaja por ella alcanzado
todos sus puntos.
Figura 1.1 Ejemplo de onda mecánica
1.1.1 El concepto de onda
En los ejemplos anteriores se puede apreciar una característica común: una
perturbación generada en un punto de inicio que se propaga en el espacio hasta alcanzar
otro punto.
En estas situaciones anteriores ha sido necesario aportar energía, por lo tanto al generar
la perturbación se transmite energía.
3
C A P Í T U L O 1 • Teoría del sonido
Así pues, podemos definir una onda como el fenómeno de transmisión de una
perturbación de alguna propiedad de un medio en el espacio transportando energía. El
medio en el que se transmite puede ser de diferente naturaleza: aire, agua o un trozo de
metal, por ejemplo. Es de tener en cuenta que este transporte de energía por el medio
nunca va acompañado de transporte de materia.
Dentro de los tipos de onda y dependiendo del tipo de clasificación que apliquemos
podemos considerar los siguientes:
•
Ondas materiales: en este tipo de ondas la perturbación se produce en un medio
elástico (aire, agua, metal) y se transmiten gracias a la elasticidad del medio, es
por ello que la velocidad de propagación dependa de sus características elásticas.
En esta clasificación cabe destacar los ejemplos anteriormente citados de la onda
producida al tirar una piedra al agua y la cuerda.
•
Ondas electromagnéticas: Son un caso especial de ondas, en las que no es
necesario un medio material, sino que pueden transmitirse por el vacío. Las
ondas electromagnéticas están producidas por las oscilaciones de un campo
eléctrico en relación con un campo magnético asociado. Dentro de este
arquetipo podemos encuadrar los ejemplos de las ondas de radio, las de TV, o
las de un router Wi-Fi.
Figura 1.2 Otro ejemplo de onda, al tirar un objeto al agua
1.1.2 Magnitudes de la onda
Una vez vistos los ejemplos de ondas más característicos de nuestro entorno
cotidiano y explicado su concepto, es hora de definir las magnitudes que caracterizan
los tipos de ondas vistos previamente. Las magnitudes principales que describen una
onda son las siguientes:
4
•
Periodo: Es el tiempo que transcurre entre dos pulsos sucesivos de la onda, se
representa con la letra T y se mide en segundos.
•
Longitud de onda: Se define como la distancia entre dos pulsos sucesivos en un
medio determinado. La longitud de onda depende de la velocidad de
C A P Í T U L O 1 • Teoría del sonido
propagación de la perturbación en el medio. Se escribe con la letra griega λ
(lambda) y se representa en metros.
•
Frecuencia: Esta magnitud es fundamental en el estudio de las ondas y también
está relacionada con el periodo, puesto que se define como la inversa del mismo.
Para hacernos una idea que resulte fácil, podemos entenderla como el número de
oscilaciones que se producen en un segundo. Su medida es el Hertzio (Hz o s-1),
en honor al físico alemán Heinrich Rudolf Hertz, aunque también se emplean
múltiplos como el Kilohertzio (KHz) y el Megahertzio(MHz). Se expresa con la
fórmula:
f =
•
1
T
Amplitud: Se considera como la distancia máxima que separa un punto de la
posición de equilibrio. (La distancia superior o inferior a la línea central de la
forma de onda de la figura 1.3). Cuanto más grande sea la distancia o
desplazamiento de esa línea central, más intenso será el nivel de la señal
eléctrica, la variación de presión o el desplazamiento físico dentro del medio.
Amplitud
Amplitud (A)
Amplitud (A)
Tiempo
Periodo (T)
Figura 1.3 Parámetros de una onda senoidal
5
C A P Í T U L O 1 • Teoría del sonido
1.1.3 Ecuación matemática de la onda
El movimiento ondulatorio supone la transmisión de una perturbación de un
punto a otro sin transporte neto de materia. Por lo tanto, y como el objetivo es conocer
dicho movimiento (sólo queda satisfecho cuando en cada momento se pueda conocer la
perturbación que afecta a cada punto del medio por el que se propaga), ¿cómo
podríamos establecer una ecuación matemática que defina el valor de dicha
perturbación en función del tiempo?
Tomando como ejemplo el caso de la cuerda y suponiendo que la propagación
de la onda armónica es a una determinada velocidad (v) y que se mueve en dirección
positiva del eje X como se muestra en la figura 1.4:
y
v
x
Figura 1.4 Pulso inicial
Transcurrido un tiempo t, el pulso habrá recorrido una distancia:
d=v·t
Quedando en la siguiente posición:
y
d
x
Figura 1.5 Desplazamiento del pulso
Por lo tanto, si la función:
6
C A P Í T U L O 1 • Teoría del sonido
y = f(x)
representa el pulso inicial, la función
y = f(x – d)
representará el pulso d, ya que la forma de la curva no varía. Se puede observar que para
valores de x incrementados cierto valor d se obtiene el mismo resultado en la función.
Sustituyendo d por su valor, resulta la función:
y = f(x- vt)
que corresponde a una onda que se propaga con velocidad v hacia la derecha a lo largo
del eje X.
Si se considera un tren de ondas armónico propagándose por la cuerda, la
ecuación que describe la posición de cada punto de la cuerda en el instante inicial es de
la siguiente forma:
y = A · sen kx
siendo A y k constantes.
Si admitimos que a medida que transcurre el tiempo el tren de ondas se propaga con
velocidad v hacia la derecha sin deformarse, la ecuación que describe el movimiento es
de la siguiente forma:
y(x,t) = A · sen k(x – vt)
Figura 1.6 Onda inicial y propagada
Al término k(x-vt) se le denomina fase. En ocasiones hay que añadir a esta fórmula la
constante δ, que recibe el nombre de fase inicial. De ese modo,
7
C A P Í T U L O 1 • Teoría del sonido
y(x,t) = A · sen[k(x – vt) + δ]
sería entonces la ecuación que encierra toda la información acerca del movimiento
ondulatorio.
1.2 El sonido como forma de onda
El sonido es por tanto un fenómeno físico y como se ha visto anteriormente, es
un movimiento ondulatorio en un medio elástico que generalmente es el aire, debido a
cambios rápidos de presión generados por el movimiento vibratorio en el medio.
Podríamos, entonces, clasificar el sonido como un tipo de onda que produce cierta
sensación en el oído como consecuencia del movimiento ondulatorio en el aire, agua u
otros medios.
1.2.1 Dominio del tiempo y frecuencia
Una señal puede verse como la variación en el tiempo de una cantidad tal como
voltaje o corriente. Como ejemplo consideramos la señal de voltaje sinusoidal dada por:
v(t) = V · cos (2πFt + θ)
V es el voltaje de pico, F es la frecuencia y θ es la fase relativa. Estos parámetros se
indican en la figura 1.7. La onda es periódica con periodo T = 1/F, ya que:
v(t) = v(t + T)
V(t)
V
Amplitud (A)
Tiempo
T = 1/F
Figura 1.7 Onda senoidal en el dominio del tiempo
Esta descripción se denomina representación de la señal en el dominio del
tiempo: la señal se ve como una función del tiempo. Esta es la forma más común de
8
C A P Í T U L O 1 • Teoría del sonido
representar las señales y de observarlas en las aplicaciones de edición de sonido
comunes.
Sin embargo, existe otra manera de representación de una señal. Consideremos la forma
de onda de la figura 1.7. Esta señal sinusoidal viene descrita por la amplitud V, la
frecuencia F y la fase θ. Estos tres parámetros junto con el conocimiento de que la señal
es sinusoidal son suficientes para dibujar la forma de onda del voltaje; la tripleta (V, F,
θ) especifica completamente la señal. Si consideramos despreciable la fase, es decir, θ
=0, trataremos entonces con la amplitud V y la frecuencia F; ahora el par (V,F)
especifica completamente la señal. En esta interpretación F y V pueden tomar cualquier
valor positivo y la señal puede representarse gráficamente como muestra la figura 1.8.
(V,F)
V
Componente espectral
0
F = 1/T
Figura 1.8 Representación espectral de una onda senoidal
Esta es la representación en el dominio de la frecuencia. Con este punto de vista,
una señal cosenoidal viene representada por una flecha vertical localizada en algún
punto F del eje de frecuencias, y la altura de la flecha corresponde a la amplitud V.
La representación en el dominio de la frecuencia es muy útil, ya que las señales
más complejas pueden ser consideradas como una superposición (suma) de
componentes sinusoidales con diferentes amplitudes, frecuencias y fases. Para las
señales armónicas las frecuencias están relacionadas por números enteros. Si T es el
período, entonces se dice que la forma de onda tiene una frecuencia fundamental de 1/T.
La siguiente frecuencia más cercana contenida en la forma de onda es 2/T, denominada
segundo armónico, y después viene el tercer armónico a la frecuencia 3/T, y así
sucesivamente. Generalmente los distintos armónicos tienen amplitudes y fases
diferentes. Para obtener una apreciación general del contenido en armónicos de una
forma de onda compleja, es conveniente representar la señal gráficamente en el dominio
de la frecuencia, como se ilustra en la figura 1.9.
9
C A P Í T U L O 1 • Teoría del sonido
f
0
1/T
2/T
3/T
4/T
Figura 1.9 Representación de las componentes espectrales de una onda armónica
La altura de las flechas representa la fuerza de los distintos armónicos. Esta
representación en el dominio de la frecuencia se denomina frecuentemente espectro de
la señal; los distintos componentes en frecuencia que los constituyen se denominan
componentes espectrales o líneas espectrales.
1.2.2 Teorema de Fourier
Gracias al teorema de Fourier, desarrollado por el matemático francés Fourier
(1807-1822) y completado por el matemático alemán Dirichlet (1829), es posible
demostrar que toda función periódica continua, con un número finito de máximos y
mínimos en cualquier período, puede desarrollarse en una única serie trigonométrica
uniformemente convergente a dicha función, llamada serie de Fourier.
Supongamos que una señal periódica se puede representar como:
x(t) = A0 + A1cos(2π(t/T) + θ1) + A2cos(2π(t/T) + θ2) + … + Ancos(2π(t/T) + θn)
o más concisamente como:
∞
x(t ) = A0 + ∑ An cos[2π (nt / T ) + θn ]
n =1
Esto se conoce como la representación mediante series de Fourier de x(t). Nótese
la relación con la representación en el dominio de la frecuencia: Las amplitudes An dan
las alturas de las distintas líneas espectrales.
Una representación alternativa se obtiene a partir de la relación trigonométrica cos(A
+B), podemos entonces representar x(t) de la siguiente forma:
∞
∞
n =1
n =1
x(t ) = A0 + ∑ an cos(2πnt / T ) + ∑ bnsen(2πnt / T )
donde an = Ancos(θn) y bn = -An sen(θn).
10
C A P Í T U L O 1 • Teoría del sonido
Otra representación hace uso de la relación:
cos A =
e ja + e − ja
2
donde, definiendo C0 = A0, x(t) puede expresarse en términos de una única serie sobre
todos los números enteros:
x(t ) =
∞
∑ Cne −( j 2π / T )
n = −∞
los términos an y bn reciben el nombre de coeficientes de Fourier y pueden obtenerse
evaluando las integrales:
a0 =
1T
∫ x(t )dt
T0
an =
2 T
∫ x(t ) cos(2πnt / T )dt
T 0
bn =
2 T
∫ x(t ) sen(2πnt / T )dt
T 0
que se obtienen extrapolando lo sumatorios, es decir usando el concepto de integral
definida, como suma de infinitos diferenciales de función entre dos límites que
representan un intervalo.
El coeficiente a0 corresponde al valor medio de la función en el período T.
11
C A P Í T U L O 1 • Teoría del sonido
1.2.3 Ondas armónicas y ruido
Desde el punto de vista subjetivo, los sonidos que percibimos se pueden dividir
en dos grandes grupos: sonidos armónicos y ruidos o sonidos no armónicos.
Las ondas armónicas se caracterizan por tener componentes que se repiten
periódicamente y que dan lugar a una serie de rayas espectrales en la transformada de
Fourier, conocidas como armónicos. Este es el tipo de sonido que se genera como
consecuencia de la vibración de un material elástico a la frecuencia de resonancia. Este
sería el caso del sonido de una cuerda de piano.
El ruido en cambio no tiene componentes armónicas y por tanto da lugar a una
transformada de Fourier con componentes en todas las frecuencias. Según la
distribución de amplitud de estas componentes se puede hablar de ruido blanco y ruido
coloreado. El ruido blanco tiene igual amplitud en todas las frecuencias, mientras que el
ruido coloreado tiene mayor peso de unas frecuencias que de otras. Este es el tipo de
sonido que se genera como consecuencia de una interacción mecánica a una velocidad
superior de la característica del medio, lo cual da lugar a una onda de choque. Este sería
del caso del sonido de percusión.
12
C A P Í T U L O 1 • Teoría del sonido
Figura 1.10 Ondas armónicas y ruido con sus respectivas transformadas
1.2.4 Propagación del sonido
El efecto que producen la vibración de las partículas desplazadas como forma de
onda en el aire y que percibe el oído es lo que denominamos sonido. Como
mencionamos antes, el sonido es una onda mecánica, y aunque se puede propagar por
otros medios, es necesario que se convierta en una perturbación acústica para poder ser
percibida por el oído. Como ejemplo ilustrativo, imagine el lector los primeros modelos
de teléfono en el que se conectaban dos vasos de plástico por medio de una cuerda
tensa. Al comenzar a hablar uno de los interlocutores, el sonido se transforma en una
vibración mecánica y consigue hacer vibrar el envase situado en el extremo opuesto.
El sonido se propaga por los diferentes medios de forma longitudinal, es decir, el
movimiento de las partículas ocurre en la dirección misma en la que la onda se propaga.
Si estudiamos el caso de la propagación del sonido en el aire, éste se transmite como
una onda de presión debido a la compresión y expansión de las partículas de oxígeno.
Un ejemplo de este fenómeno podemos verlo ilustrado en la figura 1.11.
13
C A P Í T U L O 1 • Teoría del sonido
Figura 1.11 Propagación de una onda sonora
No debe confundirse la propagación del sonido con la propagación de una
perturbación mecánica que da lugar a una señal acústica. Esto ocurre en medios
diferentes al aire. Así pues, cuando por ejemplo golpeamos un metal alargado que
produce sonido en un extremo y en el otro es la transformación acústica de la vibración
mecánica, aquí es importante saber que no se transmite el sonido por el metal, sino que
es una perturbación mecánica a través del metal.
Como vimos en el apartado 1.1.1, tanto si se trata de una onda material, como si
ésta es electromagnética, la perturbación se propaga de un punto a otro del espacio con
una velocidad que depende de las características del medio. En ambos casos se produce
un transporte de energía.
La velocidad del sonido depende de las características del medio físico en las
que se transmite la propagación. La velocidad del sonido es mayor en elementos sólidos
que en elementos líquidos, y en los líquidos mayor que en los gaseosos. La velocidad
del sonido depende de la siguiente fórmula:
v=
B
ρ
Siendo B el factor de compresión del medio y ρ la densidad.
En general la velocidad del sonido dependerá de la densidad del material y su
elasticidad. Así, la velocidad de propagación de la onda es menor cuanto mayor es la
densidad, por ello, la velocidad en el agua es menor que en la madera, como podemos
ver en la tabla 1.1. No obstante, también influiría aquí el factor de compresión, que
explicaría entonces por qué la velocidad de una onda es mayor en el acero que en el
agua, a pesar de tener mayor densidad.
Material
Velocidad
Agua (20 ºC)
1.482 m/s
Madera
3.900 m/s
Acero Inoxidable
5790 m/s
Tabla 1.1 Velocidad de propagación del sonido en diferentes medios
14
C A P Í T U L O 1 • Teoría del sonido
La velocidad del sonido en el aire (a una temperatura de 20º) es de 344 m/s. Existe
una ecuación propuesta por Newton y posteriormente modificada por Laplace que nos
permite obtener la velocidad del sonido en el aire teniendo en cuenta la variable de la
temperatura:
V =
331.3
t
1+
273.15
1.3 Percepción humana del sonido
El sonido no sería una realidad si no fuera por la capacidad de captación por los
sentidos del ser humano, en este caso el órgano encargado de transformar las
vibraciones sonoras en el medio en respuestas nerviosas interpretables por el cerebro es
el oído. Este será el objeto de estudio del presente apartado.
1.3.1 Funcionamiento del oído
Una de las funciones principales del oído es convertir las ondas sonoras en
vibraciones que estimulen las células nerviosas, para ello el oído tiene tres partes
claramente identificadas. Estas partes están interconectadas y son el oído externo, el
medio y el interno. Cada parte tiene funciones específicas dentro de la secuencia de
procesamiento del sonido.
Figura 1.12 Anatomía del oído humano
15
C A P Í T U L O 1 • Teoría del sonido
Empecemos con cada una de estas partes:
•
Oído externo: Es la parte más externa e incluye las siguientes partes:
o Pabellón auricular: Es la parte exterior del oído, es un cartílago plano y
elástico que tiene forma del extremo de una trompeta y está cubierta de
piel gruesa. Las partes principales del pabellón auricular son: el hélix que
es el borde exterior, el antihélix que es la eminencia central del pabellón
que termina en una elevación llamada antítrago o parte central, en la
parte inferior.
o Conducto auditivo externo: Es una especie de tubo que mide unos 2,5 cm
de longitud situado en el hueso temporal; está compuesto por folículos
pilosos, glándulas sebáceas que producen cerumen y glándulas de ovillo
que son las responsables de dar el color a la cera.
•
o Membrana timpánica: Esta membrana vibra cuando es golpeada por
ondas sonoras transmitidas por el aire y es la responsable de convertir las
mismas en impulsos nerviosos que llegan al cerebro.
Oído medio:
o Trompa de Eustaquio: Conecta el oído medio con la faringe e iguala la
presión entre las dos bandas de la membrana timpánica.
o Martillo, Yunque y Estribo: Se encargan de transmitir al oído interno las
vibraciones sonoras que llegan por el aire. Transforman las ondas
sonoras en vibraciones mecánicas. Las vibraciones captadas por la
membrana timpánica hacen que se mueva el martillo, que desplaza al
yunque y al estribo, que es el hueso conectado a la membrana oval,
recibiendo las vibraciones aumentadas en 5 dB.
•
o Ventana oval: Es la responsable de convertir los estímulos recibidos por
los huesos anteriores del medio aéreo a un medio líquido
Oído interno:
o Cóclea: También llamado caracol. Como su mismo nombre indica, tiene
forma de tubo espiral. Se encuentra lleno de líquido y posee la membrana
de Reissner y la membrana basilar, donde reside el órgano de Corti,
formado por células ciliadas que vibran a determinadas frecuencias.
o Canales semicirculares: Son tres tubos de forma semicircular, uno de
ellos se encuentra en posición horizontal y los otros dos en posición
vertical.
o Nervios auditivos: Son los responsables de transmitir la información
sonora al cerebro.
En la figura 1.13 puede observarse la relación de las funciones del oído con un
procesamiento computerizado del mismo:
16
C A P Í T U L O 1 • Teoría del sonido
Oído externo
Oído medio
Oído interno
Pabellón auricular +
Conducto auditivo +
Membrana timpánica
Martillo +
Yunque +
Estribo
Vestíbulo+
Cóclea
Preamplificador
procesador
Micrófono de
alta calidad
Analizador de frecuencias
Figura 1.13 Modelo equivalente del oído humano
1.3.2 Cualidades del sonido
En apartados anteriores hemos visto los parámetros del sonido desde el punto de
vista físico. Sin embargo, desde el punto de vista de la percepción auditiva, hay otros
parámetros que resultan relevantes:
•
Intensidad: Se define como la energía por unidad de área. Normalmente es la
medida de la intensidad del sonido en el aire en la posición donde se encuentra
el oyente. El oído humano responde de manera logarítmica a la intensidad del
sonido y se suele medir en decibelios, concepto que se estudiará en la siguiente
sección. La unidad básica son los vatios/m2 o vatios/ cm2.
•
Tono: Es la respuesta del oído a la frecuencia del sonido. Con el tono se percibe
si los sonidos son graves, agudos o medios. La mayoría de los sonidos que
escuchamos están compuestos de varias frecuencias, por tanto, el tono que se
percibe depende de la frecuencia dominante.
•
Timbre: Se determina principalmente por el contenido armónico o las
características dinámicas del sonido como son: el vibrato, la componente de
ataque etc. El timbre permite distinguir sonidos, como por ejemplo, diferenciar
una misma nota producida por dos instrumentos musicales diferentes.
•
Duración: Es la duración de la vibración del sonido.
17
C A P Í T U L O 1 • Teoría del sonido
1.3.3 Espectro audible e intensidad sonora (Decibelios)
El espectro audible del ser humano se encuentra en el rango de frecuencias desde
los 20 a los 20000 Hertzios (Hz). Los sonidos por debajo de los 20 Hz se les denomina
infrasonidos o subsónicos, y respectivamente, a los que tienen una frecuencia superior a
los 20000 Hz se les llama ultrasónicos.
A las frecuencias se las puede clasificar según su tonalidad, por lo tanto, a
mayor frecuencia mayor tonalidad. Así existen tonos graves, medios y altos. Los
sonidos graves caen dentro del rango de 20 a 300 Hz, los medios de 300 a 2000 Hz y los
agudos de 2000 a 20000Hz.
En cuestión de frecuencia el oído también funciona en escala logarítmica, y esta
es la base de la armonía musical. Dos notas musicales se consideran armónicas cuando
la frecuencia de una es un múltiplo de la otra. Una octava es el intervalo entre dos notas
cuya frecuencia fundamental tiene una relación de dos a uno. Dentro de una octava hay
siete notas básicas, siendo la octava nota del doble de frecuencia que la primera, de ahí
su nombre. El oído nos hace percibir como equiespaciadas las notas dentro de una
octava, a pesar de que la distancia en frecuencia es diferente según la octava que se
considere.
El espectro audible se puede subdividir en octavas. En la siguiente tabla se
puede observar una relación entre el número de octava y su frecuencia:
1ª octava
16 – 32 Hz
2ª octava
32 - 64 Hz
3ª octava
64- 125 Hz
4ª octava
125 – 250 hz
5ª octava
250 – 500 Hz
6ª octava
500 – 1000 Hz
7ª octava
1000 – 2000 Hz
8ª octava
2000 – 4000 Hz
9ª octava
4000 – 8000 Hz
10ª octava
8000 – 16000 Hz
11ª octava
16000 – 32000 Hz
Tabla 1.2 Rango de frecuencias de las octavas musicales
La primera y la última octava son prácticamente inaudibles.
También la capacidad vocal se puede medir en octavas, siendo el rango medio
de una persona de 3 octavas.
Respecto a la intensidad sonora, depende de la amplitud de oscilación, de la
potencia de la fuente y de la forma en que ha sido transmitida, es decir, el medio físico.
La intensidad sonora se mide en decibelios (dB), que es la unidad utilizada para
medir el nivel presión sonora (SPL). El término decibelio significa 1/10 de un belio,
unidad de medida de transmisión telefónica que recibió el nombre gracias a Alexander
Graham Bell, inventor del teléfono.
18
C A P Í T U L O 1 • Teoría del sonido
La sensación sonora de intensidad se agudiza para sonidos débiles, y su
sensibilidad disminuye para sonidos fuertes, es decir, los decibelios siguen una relación
exponencial, que es la que tiene el oído humano.
En la figura 1.14 se presentan los umbrales de la tolerancia de los sonidos en
decibelios:
Figura 1.14 Sensibilidad del oído humano
La referencia de los 0 dB SPL es el umbral de audición, que es la menor cantidad de
sonido en el aire perceptible por el oído humano emitida a 1000 Hz.
En la siguiente ecuación se muestra la relación del valor del nivel de intensidad sonora:
 I 
Nivel = 10·log 
 Iref 
que, por lo tanto, se puede expresar de la siguiente forma:
 P 
Nivel = 10·log

 Pr ef 
Como la referencia es 2 · 10 -5 Pa, resulta que los decibelios SPL valen medidos en Pa:
 P 
NivelSPL = 20·log
-5 
 2 · 10 
19
C A P Í T U L O 1 • Teoría del sonido
En la siguiente tabla se muestra una correlación de distintos niveles de SPL de presión
sonora con sonidos del medio:
Decibelios
Caso real
Despegue de un avión
120
Umbral de dolor
110
Martillo neumático
90
Interior de una oficina
60
Conversación normal
50
Habitación en silencio
40
Al aire libre en silencio
20
Umbral inferior de audición
0
Tabla 1.3 Niveles sonoros de diversos eventos
1.4 Generación de sonido analógico y digital
El sonido es resultado de vibraciones producidas en alguna fuente de tipo
material (como un piano) o una fuente electrónica (como un sintetizador o una guitarra
eléctrica).
Ambas son diferentes en la forma de generación de la forma de onda, pero aún así,
comparten características comunes que comentaremos a continuación.
1.4.1 Instrumentos musicales convencionales
En este apartado tratamos los instrumentos fabricados con materiales como la
madera o el metal. Vamos a tratar la física de los instrumentos de percusión, los de
viento y los de cuerda como ejemplos más significativos.
Instrumentos de percusión:
El ejemplo más característico es el tambor. La peculiaridad principal de este
instrumento es que el sonido se genera golpeando un mazo o baquetas contra una
membrana, llamada comúnmente parche, que cubre la abertura de una caja de
resonancia generalmente cilíndrica.
Los instrumentos de percusión se basan principalmente en la vibración y en la
resonancia. Cuando se golpea la membrana se produce una deformación de la misma
que genera una vibración del aire que hay dentro del mismo. El cuerpo del tambor
empieza a resonar, junto con la membrana, produciendo un sonido alto y grave
procedente de su estructura.
La frecuencia de resonancia de la caja del tambor se define según la ecuación:
20
C A P Í T U L O 1 • Teoría del sonido
0.764  F 
· 
f =
D µ
1/ 2
donde F es la tensión de la membrana por unidad de circunferencia en N/m (newtons
por metro), D es el diámetro de la membrana y µ es la densidad de área de la membrana
medida en kg/m2.
Para los xilofones, marimbas u otros instrumentos que incorporen platos
vibrantes, se usa la siguiente ecuación para obtener la frecuencia de resonancia:
f =
1.03·kv
L2
donde L es la longitud de la barra, v es la velocidad del sonido en el objeto y k es una
constante que depende del objeto.
Instrumentos de viento:
Los instrumentos de viento generan sonido por medio de vibraciones producidas
en el interior de tubos. Los cambios de frecuencia del sonido se producen como
consecuencia de acortar o alargar la longitud de los tubos, cerrando y abriendo pequeños
orificios creados en los mismos. Existen tres categorías de instrumentos de viento, en
atención al material constructivo y a la forma de generar la vibración: metal (ej.
trompeta), madera (ej. flauta) y órganos.
La ecuación para determinar la longitud de onda en un instrumento de viento
donde el tubo está abierto por ambos extremos es:
λ=
2L
n
donde n ≤ 1 y L es la longitud del tubo.
y la ecuación para un instrumento de viento abierto por sólo por un extremo es:
λ=
4L
(2n + 1)
donde n ≤ 0 y L es la longitud del tubo.
21
C A P Í T U L O 1 • Teoría del sonido
Instrumentos de cuerda:
Los instrumentos de cuerda generan el sonido por vibración de una cuerda
tensada, que resuena en una caja de resonancia. Estos instrumentos están construidos de
diferentes materiales para producir diferentes tonalidades.
En todos los instrumentos de cuerda, la cuerda está fijada en los extremos del
instrumento y es golpeada, frotada o pinzada para producir una onda sonora que es
amplificada posteriormente en el cuerpo del instrumento. De la forma de actuar sobre la
cuerda se deriva la clasificación de estos instrumentos: de cuerda frotada (ej. violín), de
cuerda pulsada (ej. guitarra) o de cuerda percutida (ej. piano).
A continuación se deduce la frecuencia de vibración de una cuerda tensada. La
velocidad de una onda viajando por una cuerda vibrando, y la tensión de la cuerda están
relacionados por la siguiente ecuación:
v = λf


 T 

v=
m




  L  
1/ 2
= λf
donde T es la tensión en Newtons, m es la masa de la cuerda y L la longitud de la
misma. Por lo tanto:
1/ 2

 


 T  
 

m



   
  L   
f =
λ
Esta frecuencia es conocida como la frecuencia fundamental y tiene una longitud
de onda igual a dos veces la longitud de la cuerda, debido a esto los nodos o puntos cero
de desplazamiento de la onda son los finales de la cuerda del instrumento (figura 1.15).
Debido a esto incluiría longitudes de onda que son iguales a la longitud de la cuerda,
(primer armónico), 2/3 de la longitud de la cuerda (segundo armónico), 2/4 de la
longitud de la cuerda (tercer armónico), y así sucesivamente. Es importante conocer que
el sonido de un instrumento de cuerda será diferente atendiendo al cuerpo del
instrumento que hará que resuene a diferentes frecuencias.
22
C A P Í T U L O 1 • Teoría del sonido
Figura 1.15 Vibración de una cuerda
1.4.2 Síntesis analógica
En esta forma de síntesis entendemos la generación de sonido sin electrónica
digital, conectando varios módulos en cascada para obtener el sonido final. El elemento
principal en un sintetizador, ya sea analógico o digital, es el oscilador, que es un
sistema que genera una onda periódica con una determinada forma (cuadrada,
sinusoidal, triangular, diente de sierra, etc.).
Se puede clasificar los tipos de síntesis analógica en los siguientes tipos:
•
•
•
•
Síntesis aditiva: Consiste en generar el sonido mediante sucesivos
enriquecimientos del espectro de la onda, es decir, se van añadiendo armónicos
hasta conseguir la forma de onda deseada.
Síntesis sustractiva: Se parte de una forma de onda muy rica en armónicos, por
ejemplo del resultado de una síntesis no lineal, y desde ahí vamos eliminando
armónicos hasta quedarnos con una forma de onda más básica y adecuada a
nuestras necesidades.
Síntesis por modulación de amplitud (AM): Consiste en utilizar una señal
portadora a una determinada frecuencia e ir modulando su amplitud mediante
otra señal, por lo general, múltiplo de la primera. La forma de onda resultante se
le denomina señal moduladora. Por ende, se consiguen formas de ondas con
diferentes timbres partiendo de formas de onda más primitivas.
Síntesis por modulación en anillo (RM): En este caso se trata de una modulación
en amplitud, pero se multiplica la señal portadora por la moduladora, generando
de esta forma sonidos más peculiares que los de síntesis por modulación de
amplitud (AM).
Las técnicas citadas anteriormente se las califica de técnicas de síntesis lineal,
pues existe una relación lineal entre el espectro de las frecuencias iniciales
(moduladora y portadora, por ejemplo) y el espectro de salida.
La síntesis analógica está basada en módulos, todo en síntesis analógica es
modular.
En la figura 1.16 tenemos el diagrama de bloques de un sintetizador analógico:
23
C A P Í T U L O 1 • Teoría del sonido
Figura 1.16 Diagrama de bloques de un sintetizador analógico
A continuación se describen con más detenimiento cada una de sus partes:
Oscilador
Un oscilador es un circuito electrónico capaz de generar una forma de onda
periódica a una determinada frecuencia. Existen osciladores controlados por tensión
(cuya frecuencia de oscilación es controlada por el voltaje de un circuito) y osciladores
digitales (que son generados de por circuitería digital). En este caso nos centraremos en
los primeros.
Las formas de onda más utilizadas en música electrónica han sido las cuadradas y las de
diente de sierra, como se ve en la siguiente figura:
Figura 1.17 Onda de diente de sierra (izquierda) y onda cuadrada (derecha)
También se utilizan otros tipos de onda como la triangular y la onda de ruido
(onda que no posee tonalidad, es aleatoria, no posee periodo y se le denomina ruido
blanco).
Las ondas triangulares son apropiadas para generar timbres parecidos a los instrumentos
de viento, mientras que la aleatoria es más apropiada para los instrumentos de
percusión.
Figura 1.18 Onda triangular (izquierda) y aleatoria (derecha)
Para crear un generador de onda triangular con circuitería analógica se utiliza el
circuito que se describe en la figura 1.19. El condensador C se carga mediante una
fuente de corriente constante, con lo cual la tensión en sus terminales crece linealmente.
24
C A P Í T U L O 1 • Teoría del sonido
Dicha tensión se aplica a un amplificador operacional que funciona como comparador.
Cuando la tensión del condensador supera la tensión de referencia Vref el comparador
activa el transistor, el cual descarga el condensador y el ciclo vuelve a empezar. Si la
señal triangular se aplica a un segundo comparador con una tensión de referencia
variable, se consigue una onda de ancho de pulso variable (PWM).
En la siguiente figura podemos ver una implementación analógica para un oscilador de
diente de sierra y cuadrada:
Figura 1.19 Generador de formas de onda básicas (diente de sierra y cuadrada)
Generadores de envolvente
Un generador de envolvente es un circuito que genera una señal que permite
controlar un parámetro dentro de un sistema de síntesis. La señal envolvente se suele
usar para modular la amplitud de la señal armónica, y de esta manera simular las
variaciones de amplitud de un instrumento de cuerda. Otros tipos de envolvente se
utilizan para modular el tono o pitch de la señal.
Normalmente, los generadores de envolvente, son del tipo ADSR (Attack, decay,
sustain, release) o traducido al español (ataque, decaimiento, sostenido, liberación)
como se puede ver en la siguiente figura:
25
C A P Í T U L O 1 • Teoría del sonido
Figura 1.20 Diagrama de una envolvente de volumen
En la siguiente figura se puede ver un circuito utilizado para generador de
envolvente tipo ADSR
Figura 1.21 Esquema eléctrico de un generador de envolvente de volumen
Amplificador
Los amplificadores que utilizan los sistemas analógicos son del tipo VCA o
amplificadores controlados por tensión que tienen la peculiaridad que su ganancia puede
ser controlada por un voltaje adicional. El VCA es el complemento indispensable para
producir una envolvente de amplitud.
Para implementar un VCA se suelen usar amplificadores operacionales de
transconductancia, aunque también se puede implementar con componentes discretos.
En la siguiente figura se observa la circuitería para implementar un VCA.
26
C A P Í T U L O 1 • Teoría del sonido
Figura 1.22 Amplificador de amplitud controlada
Filtro
Los filtros son elementos indispensables en la síntesis analógica; aunque estos
conceptos se tratarán más en profundidad en el capítulo 7.
Hay varios tipos de filtro como veremos a continuación:
Filtro paso-bajo
Filtro paso-banda
Figura 1.23 Tipos de filtro
Filtro paso-alto
Filtro elimina-banda
27
C A P Í T U L O 1 • Teoría del sonido
El filtro más utilizado en cuestión es el paso-bajo, pues es el más fácil de
implementar. Tiene dos características fundamentales: la frecuencia de corte y la
pendiente de filtrado. Algunas implementaciones de filtro paso-bajo incorporan un filtro
resonante para mejorar la pendiente de corte, un cuyo caso hay un parámetro más que
controlar.
La frecuencia de corte debe ser ajustable mediante un voltaje de control, la de
resonancia también, aunque en este esquema no se varía con un voltaje sino con una
resistencia, mientras que la pendiente de filtrado viene determinada por el número de
polos del circuito. Valores típicos para la pendiente
2 polos: 12 dB/octava
3 polos: 18 dB/octava
4 polos: 24 dB/octava
5 polos: 30 dB/octava
…
Vemos que los decibelios/octava se van incrementando de 6 en 6 a partir de 12
dB/octava: cada polo introduce un incremento de 6 dB/octava en la pendiente. A mayor
pendiente, más selectivo es el filtro.
Valores comunes para filtros es de 12 dB/octava (2 polos) y 24 dB/octava (4 polos).
Respecto a la implementación del circuito se puede decir que la configuración se
le denomina Filtro de Estado Variable (State Variable Filter) ya que permite la
configuración independiente tanto de la frecuencia de corte como de la resonancia o
factor de amortiguamiento.
Figura 1.24 Esquema eléctrico de un filtro sintonizable
La frecuencia de corte se controla mediante un voltaje, mientras que la resonancia se
controla con un simple potenciómetro.
28
C A P Í T U L O 1 • Teoría del sonido
1.4.3 Técnicas de síntesis digital
Dentro de la síntesis digital consideramos la generación de sonido utilizando un
sistema electrónico digital, que puede ser un circuito dedicado o un sistema procesador
controlado por software. Hay que destacar que muchas de las técnicas utilizadas en la
síntesis analógica son utilizadas también en la síntesis digital.
La síntesis digital tiene varias ventajas sobre la analógica:
•
•
•
Se puede reutilizar el mismo circuito para varios sonidos, simplemente
cambiando la configuración.
Es menos sensible al ruido acoplado, porque todo el procesamiento es digital.
Se pueden hacer filtros y efectos difíciles de fabricar en circuitería analógica.
A continuación destacamos las técnicas más comunes de síntesis digital:
Síntesis aditiva
Como vimos en la sección anterior, la síntesis aditiva consiste en generar un
sonido periódico más complejo resultante de la suma de ondas sinusoidales más
elementales de frecuencia múltiplo de una frecuencia base.
Para obtener una forma de onda rica, son necesarios muchos armónicos.
Actualmente esto es fácilmente realizable vía software, pero en los inicios plasmarlo
con circuitería digital (hardware) requería un gran número de osciladores y sumadores y
por lo tanto, encarecía en exceso el sistema.
En la siguiente figura se puede observar un ejemplo típico de síntesis aditiva
digital por medio de la suma de dos formas de onda básicas:
Figura 1.25 Síntesis aditiva de formas de onda
La onda resultante mantiene la frecuencia del componente más grave, pero con el timbre
alterado.
29
C A P Í T U L O 1 • Teoría del sonido
Modulación de frecuencia (FM)
Una de las formas de poder realizar síntesis de sonido es por medio de la
modulación, que consiste en variar un parámetro de la señal portadora con respecto a la
señal moduladora, generando una señal modulada. Para la síntesis FM se hace oscilar la
frecuencia de la onda portadora. Para esta síntesis sólo se utilizan de dos a seis
osciladores, mientras que en la síntesis aditiva se requiere un oscilador para cada onda.
Por ende, la síntesis FM es más rica en calidad que la síntesis aditiva, ya que puede
generar ondas complejas que contengan múltiples frecuencias con tan sólo dos
osciladores.
La historia de este modelo de síntesis tiene su origen en el famoso compositor John
Chowning y en el sintetizador DX7 fabricado por la casa Yamaha:
Figura 1.26 Sintetizador Yamaha DX7
Yamaha compró los derechos del algoritmo de síntesis FM para construir su modelo de
sintetizador que puede apreciarse en la imagen anterior.
En el funcionamiento de la síntesis FM, la distribución de los armónicos de una
onda sinusoidal simple modulada por otra señal sinusoidal puede ser representada con
las funciones de Bessel.
La síntesis FM es una forma de síntesis no lineal. Comienza con un oscilador
generando una onda portadora con la frecuencia Fc y otra con una señal moduladora,
con una frecuencia Fm que se aplica para cambiar o modular la frecuencia de la
portadora.
Si la amplitud del modulador es 0, la frecuencia de salida de la señal portadora
es simplemente Fc. De otra forma, la amplitud de la señal moduladora causa que la señal
de la portadora oscile por encima o por debajo de Fc. En otras palabras, cuanto más
fuerte es la señal moduladora, más cambia la frecuencia de la portadora. Como ejemplo
ilustrativo, supongamos que Fc es 1000 Hz. Si la amplitud de la modulación es 100 Hz
se consigue que la señal de la portadora oscile entre 900 Hz y 1100 Hz, esto es, 100 Hz
en cada dirección. Esto es lo que se conoce como “desviación”.
A la misma vez, la frecuencia de la señal moduladora causa bandas laterales
(side bands) que aparecen en frecuencias por encima y por debajo de la frecuencia
portadora. Por lo tanto, para cada componente de frecuencia en la señal moduladora,
aparece una banda lateral superior por encima de Fc y una banda inferior por debajo de
Fc. Una señal compleja moduladora (conteniendo más armónicos que una señal
30
C A P Í T U L O 1 • Teoría del sonido
sinusoidal) creará bandas laterales correspondientes a cada una de sus componentes
sinusoidales.
La desviación es responsable de la distribución espectral de potencia de la señal
de salida. Cuando la desviación es cero, toda la potencia se concentra en la frecuencia
portadora. A medida que aumenta la desviación la potencia se reparte entre las
frecuencias que rodean a la portadora.
La relación entre la desviación y la frecuencia moduladora se denomina índice
de modulación (I = d / fm). Esta relación controla la riqueza espectral del sonido. Si se
actúa simultáneamente sobre la profundidad de modulación y sobre el espectro de la
señal moduladora se consigue una gran riqueza armónica en la señal modulada. Esta es
la potencia de la síntesis FM.
β=1
β=5
β=25
Figura 1.27 Representación temporal de una señal modulada en FM, en función del
índice de modulación.
Puede consultarse más información sobre el algoritmo FM en la siguiente URL:
http://the-all.org/tx81z/fm_overview.html
y
http://www.rfcafe.com/references/electrical/frequency-modulation.htm
31
C A P Í T U L O 1 • Teoría del sonido
Síntesis por tabla de ondas
Este es el sistema utilizado por la mayoría de los sintetizadores digitales
modernos. Parten de pequeños fragmentos de sonidos digitalizados y almacenados en
memoria. Esta síntesis también llamada de tabla de ondas o Wavetable se utiliza cuando
no recurre directamente a un muestreo del sonido a emitir. Un sintetizador General
MIDI (GM) deberá contener suficientes fragmentos para representar los 128
instrumentos y los 59 sonidos de percusión.
Las técnicas de síntesis por tabla de onda más utilizadas son las siguientes:
•
Generación en bucle: es la técnica más primitiva y se basa en la repetición
(looping) de muestras de sonido pregrabadas para producir una señal original.
Para ello se dividen y almacenan las dos partes de la mayoría de los sonidos
producidos por instrumentos: la sección de “ataque” que se reproduce entera y
la de sostenimiento que se reproduce en bucle cada vez con menor intensidad
siguiendo un patrón preestablecido. Finalmente el sonido se completa con una
sección de liberación donde se termina de bajar la amplitud hasta 0 de manera
más rápida imitando el desvanecimiento natural del sonido.
Este sistema tiene cierta complejidad si se intentan utilizar efectos como coro o
reverberación. Por otro lado, es necesario un tratamiento adicional de la señal
para mejorar la relación señal/ruido o preparar la señal para su edición.
32
•
Desplazamiento de tonos: con esta técnica se puede lograr que a partir de una
muestra de sonido que representa una nota determinada se puedan generar otras
notas del mismo instrumento. Para conseguir esto se aplican diferentes filtros a
la señal original que junto con un aumento o reducción de la frecuencia de
reproducción de la señal da lugar a la nueva nota. Este sistema es más fácil de
implementar por software y es el que utilizan muchos VST (Instrumentos
Virtuales Digitales) comerciales, como el de la figura 1.28.
•
Interpolación: esta técnica intenta eliminar el problema de almacenamiento en
memoria, reduciendo la memoria destinada al muestreo de sonidos. En este caso
los sonidos pueden estar grabados a una frecuencia relativamente baja. De esta
forma cuando la señal sea enviada al conversor D/A (digital-analógico) el
sintetizador puede encontrarse con que debe enviarle posiciones de memoria
que son decimales (como, por ejemplo, enviarle la posición 4,5 de la memoria).
Este problema se resuelve mediante la interpolación de los valores adyacentes.
Con este método se puede reconstruir mejor la señal original y aumentar
considerablemente la calidad del sonido.
•
Diezmado: es el efecto contrario a la interpolación. La señal es muestreada a
varias veces por encima de la frecuencia necesaria para poder afinar mucho
mejor los problemas de cambio de tono al editar la misma o realizar bucles con
ella. El único problema de este método es el excesivo espacio de memoria que
requiere.
C A P Í T U L O 1 • Teoría del sonido
Los sintetizadores actuales tienen una memoria ROM de hasta 64MB para el
almacenamiento de muestras y las tarjetas de sonidos 4 y 8MB respectivamente. En el
caso de los sintetizadores hay ciertas garantías de que la calidad del sonido será
aceptablemente buena.
Figura 1.28 Software VST con síntesis de tabla de ondas con desplazamiento de tonos.
33
C A P Í T U L O 1 • Teoría del sonido
1.5 Representación del sonido
1.5.1 Conversión analógico / digital
Debe de ser posible representar el sonido que se encuentra en el ambiente o
procedente de líneas de entrada (line-in) en un formato numérico inteligible para el ser
humano y sobre todo procesable por computadora, de esta manera, es posible capturar
sonido del medio con micrófonos y transformar inicialmente esas vibraciones en el aire
en impulsos electromagnéticos (señal analógica) y finalmente, a través de un proceso
electrónico de conversión, obtener una señal o secuencia de información digital
procesable por software. A continuación tratamos el proceso de conversión de analógico
a digital.
1.5.1.1 Teorema de Nyquist
Para obtener muestras de una señal de entrada analógica se utiliza el teorema de
Nyquist-Shannon, el cual postula que la frecuencia de muestreo debe ser dos veces
superior a la frecuencia máxima de la señal a muestrear.
Frecuencia muestreo = 2 x Frecuencia máxima señal analógica de entrada
En la siguiente figura se puede observar el proceso de muestreo:
Figura 1.29 Señal analógica muestreada
En la práctica se suele elegir una frecuencia de muestreo ligeramente superior a
la frecuencia máxima de la señal analógica. Así por ejemplo, si queremos representar
una señal con una frecuencia 20000 Hz con calidad de CD tendremos que capturar la
señal con una frecuencia de muestreo de 44100 Hz o 44,1 Khz.
1.5.1.2 Proceso de conversión
Para poder realizar la conversión analógico digital se hace preciso la utilización
de un tipo de circuito especial llamado ADC (Analog-to-Digital Converter o Conversor
Analógico Digital). Dicho circuito realiza los siguientes procesos:
Muestreo de la señal analógica:
El primer paso para convertir una señal analógica en digital es muestrearla
(sampling), es decir, tomar valores sucesivos de la amplitud de la señal a intervalos
regulares de tiempo. Con este proceso la señal continua pasa a ser discreta en el tiempo,
si bien no en amplitud. Como hemos explicado anteriormente estas muestras son
34
C A P Í T U L O 1 • Teoría del sonido
tomadas con una frecuencia determinada a la que se denomina frecuencia de muestreo y
por lo tanto a mayor frecuencia de muestreo mayor calidad y fidelidad tendrá la señal
digital resultante.
Durante el proceso de muestreo se asignan valores numéricos equivalentes a la tensión
la amplitud de la señal, con la finalidad de realizar a continuación el proceso de
cuantización.
Voltios
8
7
6
5
4
3
2
1
0
Tiempo
Figura 1.30 Principio básico del muestreo
Cuanto mayor sea el número de muestras tomadas, mayor será también el ancho
de banda necesario para transmitir una señal digital, requiriendo también un espacio
mucho mayor para almacenarla en un CD o un DVD.
Cuantización:
Posteriormente al proceso de muestreo se realiza el proceso de cuantización de
la señal analógica de entrada.
Para conseguir este propósito, los valores continuos de señal se convierten en una
secuencia de valores numéricos decimales discretos que corresponden a los diferentes
niveles de voltaje de la señal de entrada.
Voltios
8
7
6
5
4
3
2
1
0
Tiempo
Figura 1.31 Cuantización de amplitud
35
C A P Í T U L O 1 • Teoría del sonido
Codificación
La codificación permite asignarle valores numéricos binarios equivalentes a los
valores de tensiones o voltajes que conforman la señal eléctrica analógica original,
como puede verse en la siguiente figura:
Voltios
8
7
6
5
4
3
2
1
0
Tiempo
001 010 001 011 010 100 101 100 101 011
Figura 1.32 Codificación de las muestras (PCM)
En el proceso de codificación es importante saber cuántos bits asignamos a esos
valores de amplitud de la señal muestreada y cuantificada. Así si utilizamos valores de n
bits para representar la señal podremos codificar un total 2n – 1 valores de amplitud
diferentes. Por ejemplo, en un byte (8 bits) podemos representar un total de 28 – 1 = 256
-1 valores diferentes de amplitud, el problema es que la calidad del sonido no será tan
aceptable. Por esto, en la mayoría de las aplicaciones reales se utiliza un tamaño de
palabra de 16 bits, pudiendo por lo tanto representar un total de 216 – 1 = 65536 – 1
valores diferentes, obteniendo una señal resultante de mayor calidad. En la siguiente
tabla se muestra una relación de formatos de frecuencias de muestreo y bits para
representar los formatos de sonido más conocidos:
Tipo formato
Frecuencia muestreo
Resolución
Calidad CD
44100Hz
16bits
Calidad Radio
22Khz
8bits
Calidad teléfono
11Khz
8bits
Tabla 1.4 Niveles de muestreo según la calidad y ancho de banda
Dentro del proceso de codificación podemos considerar diferentes tipos de formato,
entre ellos destacan:
PCM
El formato PCM (en inglés Pulse Code Modulation) es un formato o
procedimiento de modulación para transformar la señal analógica muestreada y
cuantificada en una formato de bits. Este formato es ampliamente utilizado, así podemos
verlo aplicado a los teléfonos digitales, el audio digital en ordenadores (ampliamente
tratado en este estudio) y el formato Red Book de los CD de audio.
36
C A P Í T U L O 1 • Teoría del sonido
PAM
El formato PAM o modulación por amplitud de pulsos (Pulse AmplitudModulation) es la más sencilla de las modulaciones digitales. Consiste en cambiar la
amplitud de la señal (de frecuencia fija) en función del símbolo a transmitir.
Dichas amplitudes pueden ser reales o complejas. Si representamos las
amplitudes en el plano complejo tenemos lo que se llaman constelaciones de señal. En
función del número de símbolos o amplitudes posibles se llama a la modulación NPAM. Así podemos tener 2PAM, 4PAM, 260PAM. De la correcta elección de los
puntos de la constelación (amplitudes) depende la inmunidad a ruido (distancia entre
puntos).
Figura 1.33 Diferentes formatos de codificación
1.5.1.3 El problema del aliasing
Un riesgo que puede existir durante la conversión analógico-digital es el
aliasing, que se produce cuando la señal que se muestrea tiene componentes de
frecuencia por encima de fs/2 o frecuencia de Nyquist.
Para evitar este problema se utiliza un filtro paso-bajo, que elimina todas las frecuencias
que están por encima de fs/2. En la práctica como los filtros no son perfectos hay que
intentar que la frecuencia de Nyquist esté un poco por encima de la máxima frecuencia
que se desea muestrear.
La consecuencia del aliasing es que las frecuencias que están por encima de la
frecuencia de Nyquist aparecen desplazadas en frecuencia dentro del espectro
muestreado, como se muestra en la figura 1.34.
37
C A P Í T U L O 1 • Teoría del sonido
Figura 1.34 Efecto del aliasing
1.5.2 Conversión digital / analógico
El objeto de este tipo de convertidor es transformar una señal discreta en tiempo
y frecuencia en una señal continua, en otras palabras, convertir una secuencia de
códigos binarios en una señal eléctrica analógica.
Como se vio en la sección anterior, la representación espectral de una señal
muestreada es un conjunto de réplicas del espectro de la señal original, que se repiten en
múltiplos de la frecuencia de muestreo. Al convertir la señal del dominio digital al
analógico, estas réplicas aparecerán a menos que se disponga un filtro paso bajo a la
salida del conversor D/A. Este filtro paso bajo es necesariamente analógico y puede
resultar caro y complicado si la frecuencia máxima de la señal está muy cerca de la
frecuencia de Nyquist.
Sin embargo existen técnicas digitales que simplifican la reconstrucción de la
señal y el diseño de dicho filtro, a cambio de una mayor complejidad en la parte digital.
La técnica más comúnmente utilizada en los conversores D/A de audio y vídeo
es el oversampling. Esta técnica consiste en aumentar la frecuencia de muestreo de la
señal, manteniendo intacta la representación espectral. Como consecuencia, la
frecuencia máxima de la señal se aleja de la frecuencia de Nyquist, permitiendo el uso
de filtros analógicos más sencillos.
Para aumentar la frecuencia de muestreo de la señal se ha de interpolar con
muestras de valor cero. Un filtro interpolador digital transforma estas muestras nulas en
valores intermedios entre las muestras originales.
A pesar del aumento de complejidad en el dominio digital, el resultado merece la
pena desde un punto de vista práctico, pues el área de silicio ocupada por el filtro digital
es mucho menor que el espacio de un filtro analógico de orden elevado, con la ventaja
38
C A P Í T U L O 1 • Teoría del sonido
adicional de que el filtrado digital no está sujeto a las tolerancias de los componentes
analógicos.
filtro analógico
banda imagen
t
0
fs/2
fs
f
interpolación
t
filtrado digital
filtro analógico
banda imagen
t
0
f’s/2
f’s
f
Figura 1.35 Comparación entre la conversión D/A básica (arriba) y la conversión con
oversampling (abajo)
El desarrollo del oversampling ha evolucionado paralelamente al de los
convertidores D/A sigma-delta, también conocidos como one bit stream. Estos
convertidores no reconstruyen la señal analógica desde cero para cada muestra, sino que
suman o restan la diferencia necesaria para que la señal alcance el nivel deseado. Esta
diferencia se muestrea con un único bit de resolución, de ahí el nombre one bit stream.
Esta simplificación se compensa con una mayor frecuencia de muestreo, pues
para poder reproducir grandes variaciones de la señal en saltos de 1 LSB se requiere que
su valor se actualice mucho más rápido que la frecuencia original de muestreo. Sin
embargo las etapas analógicas del convertidor D/A son más sencillas, lo cual reduce su
consumo.
39
C A P Í T U L O 1 • Teoría del sonido
Bibliografía y referencias
Libros
Física COU – A.Candel, J.Satoca, J.B. Soler, F.Tejerina, J.J. Tent. Editorial Anaya,
1990.
Apuntes Redes 3º IT Informática de Sistemas – Universidad de Murcia, 1997.
Técnicas de Grabación modernas – David Miles Huber, Robert E. Runstein, 6ª edición.
Editorial Omega, 2007.
Diseño y desarrollo Multimedia, Sistemas, Imagen, Sonido y Vídeo – Manuel-Alonso
Castro Gil, Antonio Colmenar Santos, Pablo Losada de Dios, Juan Peire Arroba.
Editorial Ra-ma, 2002.
Tecnología Básica del Sonido I – Ignasi Cuenca David, Eduard Gómez Juan. Editorial
Paraninfo, 2006.
La Web
Wikipedia:
Concepto de sonido:
http://es.wikipedia.org/wiki/Cualidades_del_sonido
Funcionamiento del oído:
http://es.wikipedia.org/wiki/O%C3%ADdo
Otros URL:
The Physic of Instruments
http://www.mrfizzix.com/instruments/
Sistema auditivo humano:
http://www.ehu.es/acustica/espanol/fisiologia1/siaues/siaues.html
Síntesis analógica musical:
http://sam.atlantes.org/ – Avelino Herrera Morales
Técnicas de síntesis digital:
http://en.wikipedia.org/wiki/Frequency_modulation_synthesis
http://es.wikipedia.org/wiki/S%C3%ADntesis_mediante_tabla_de_ondas
40
C A P Í T U L O 1 • Teoría del sonido
http://es.wikipedia.org/wiki/Octava
http://www.analog.com/en/content/0,2886,761%255F795%255F91588%255F0,00.html
Lista de figuras obtenidas de Internet
Figura 1.10
Figura 1.28
http://www.virtins.com
http://www.renoise.com/
41
Parte II
Programación
C A P Í T U L O 2 • DirectX y la API DirectSound
2
DirectX y la API DirectSound
2.1 ¿Qué es DirectX?
2.1.1 Introducción
En los inicios del desarrollo de aplicaciones multimedia en Windows era muy difícil
poder escribir código eficiente que pudiera controlar con éxito los efectos de vídeo y
audio en este sistema operativo. Aunque el objetivo de Windows era aislar al
programador del hardware, lo cual era origen de numerosas dificultades de operatividad
en sistemas MS-DOS, sin embargo este aislamiento no gustó al principio a los
desarrolladores multimedia pues seguían con el hábito de trabajar directamente con el
hardware para poder así sacar el máximo rendimiento a las aplicaciones.
Paulatinamente, fueron aceptándose las dificultades que implicaba programar
aplicaciones en MS-DOS: falta de uniformidad en la plataforma, imposibilidad para
manejar una gama amplia de dispositivos hardware de entrada/salida multimedia,
dificultad para acceder al conocimiento específico de cada arquitectura, hardware y
recursos software, sistema operativo mono-usurario y mono-tarea, etc.
DirectX ha permitido abrir la puerta a las funciones de Windows (sin pedir al
programador que sacrifique el rendimiento) y poder abstraer de esta manera la
complejidad del acceso al hardware. El objetivo de los creadores de DirectX era hacer la
plataforma Windows un entorno atractivo para programar aplicaciones multimedia. Así,
desde la aparición de esta tecnología han surgido juegos con movimientos suaves,
animaciones cristalinas y sonido de alta calidad.
Los desarrolladores consiguieron una nueva plataforma muy rica, de gran rendimiento y
con gran operatividad; y los fabricantes de hardware dispondrían de un mercado más
amplio para sus productos. Se había conseguido al fin la independencia del dispositivo
que facilita el desarrollo de aplicaciones multimedia a gran escala.
DirectX consta de unas librerías en código binario que abstraen las funciones de acceso
al hardware y al mismo tiempo consigue un alto rendimiento. Se distribuye como un
programa que se instala junto a las librerías del sistema y se proporciona adicionalmente
y dirigido a profesionales del sector del desarrollo multimedia un SDK (Software
Development Kit) que incluye las librerías estáticas y código fuente para poder crear
aplicaciones DirectX. La versión actual de DirectX es la 11 que está disponible
solamente para Windows Vista y Windows 7.
45
C A P Í T U L O 2 • DirectX y la API DirectSound
2.1.2 Filosofía de DirectX
Los diseñadores de DirectX se plantearon los siguientes objetivos:
•
•
•
Rapidez
Disminuir la latencia
Ser humilde
Rapidez: Este es el objetivo más importante de todos. En primer lugar DirectX debe
ser muy rápido y por lo tanto se debe utilizar la asistencia hardware en todo momento y
sólo en casos de su ausencia utilizar la emulación por software.
Disminuir la latencia: El sistema operativo Windows permite la abstracción de casi
todas las funciones. Normalmente, dicha abstracción representa un beneficio que libera
al creador de software de los detalles específicos del entorno sobre el que se ejecuta la
aplicación. Pero también, puede implicar un retardo al solicitar un servicio. Este retardo
se le denomina latencia y debe ser disminuida al máximo.
Ser humilde: Al desarrollar un librería software se intenta transmitir una idea
personal de cómo hacer las cosas, no obstante, los estándares pueden facilitar las cosas
considerablemente. El objetivo del diseño de DirectX es que fuera ampliamente
aceptado y no irrumpir en las partes más importantes del diseño de la aplicación o en el
proceso de ingeniería del software.
2.1.3 Arquitectura de DirectX
La arquitectura está basada en un “equipo de controladores”: dos controladores, la
capa de abstracción del hardware (HAL) y la capa de emulación de hardware (HEL)
colaboran para ejecutar las peticiones de servicio realizadas por DirectX. Cuando se
crea un objeto DirectX para un determinado dispositivo, DirectX consulta el hardware
para una determinada posibilidad (por ejemplo, un coprocesador gráfico que puede
realizar operaciones de escalado) y éste utilizará el hardware que proporcione dicha
función. Cuando no haya asistencia de hardware se recurrirá a la capa HEL.
En la figura 2.1 puede verse dicha distribución:
DirectX
HAL
HEL
Hardware
Figura 2.1 Arquitectura HAL/HEL de DirectX
46
C A P Í T U L O 2 • DirectX y la API DirectSound
2.1.4 Componentes de DirectX
Los diferentes componentes de DirectX se ofrecen al programador a través de
diferentes APIs (Application program interface). La versión 9 de DirectX (utilizada en
este estudio) consta de los siguientes componentes:
•
DirectDraw: permite la animación suave de sprites y gráficos usando
intercambio de páginas de vídeo, acceso a coprocesadores gráficos
especializados (GPU’s) y administración de memoria de vídeo. Sirve de base
para otros componentes como Direct3D y DirectShow.
•
Direct3D: proporciona funciones 3D en tiempo real utilizando el hardware de la
GPU.
•
DirectSound: tratado en este capítulo. Proporciona sonido estéreo y 3D con
mezcla de sonido por hardware así como administración de la memoria de la
tarjeta de sonido.
•
DirectMusic: proporciona funciones de alto nivel para manejar funciones MIDI
con baja latencia y una mayor abstracción de los objetos de sonido. Este
componente utiliza las funciones de DirectSound.
•
DirectShow: facilita el desarrollo de aplicaciones de vídeo con uso de filtros y
codecs de compresión de audio y vídeo.
•
DirectPlay: incluye servicios transparentes de mensajería independientes del
medio para crear juegos en red local (LAN) e Internet, de forma que puedan
cooperar diferentes jugadores.
•
DirectInput: proporciona entrada de baja latencia desde una amplia variedad de
dispositivos de entrada y permite el funcionamiento de dispositivos de salida,
incluyendo periféricos activos de juego (joysticks, volantes, etc…) .
2.1.5 Programación en DirectX
Para programar en DirectX podemos utilizar cualquier lenguaje existente en
plataforma Windows: Visual Basic, C# y Visual C++. En este estudio abordaremos el
desarrollo de aplicaciones en DirectSound utilizando este último.
Para el correcto funcionamiento de las aplicaciones en DirectX es fundamental conocer
a fondo la API de Windows (API Win32), es decir, es necesario para el programador
tener conocimientos consistentes sobre los entresijos de la programación en este sistema
operativo comercial. En caso de que el lector no posea estos conocimientos, se le remite
a la lectura del libro de Programación para Windows 95 de Charles Petzold.
Es fundamental para poder programar con DirectX tener una ligera noción sobre la
tecnología COM (Modelo de objetos componentes) ya que es la base de la filosofía
DirectX y, como consecuencia, será explicado en el siguiente apartado.
47
C A P Í T U L O 2 • DirectX y la API DirectSound
2.1.6 Tecnología COM
La tecnología COM es ampliamente utilizada en Windows, de hecho, es un estándar
creado por Microsoft, que junto con el middleware DCOM forma la base para la
tecnología de objetos distribuidos.
COM es un modelo binario que puede ser utilizado independientemente de los lenguajes
de programación que puedan soportarlo. CORBA y Java RMI son otros de los modelos
que compiten con COM.
Un componente es un objeto que incluye unas características mínimas, para que puedan
ser utilizados tanto en un programa como en la etapa de diseño del mismo. Los
componentes COM pueden utilizarse en múltiples lenguajes, de ahí que estén más
extendidos. Además, las distintas partes del propio sistema operativo están realizadas
con COM. La dificultad de esta tecnología estriba en la complejidad para su desarrollo.
El origen de COM está en Microsoft, en sus primeros sistemas operativos, pues
encontraron el problema de insertar gráficos de unas aplicaciones en otras. En 1991
desarrollaron un protocolo mediante el cual en un documento podrían insertarse objetos
mantenidos por programas distintos en los que se estaba editando. El protocolo se
llamaba OLE 1.0 y se basaba en el paso de mensajes y el uso de memoria global
compartida. El resultado fue realmente malo, no sólo por la fragilidad del sistema (con
la caída de una de las aplicaciones caía el sistema), sino por la complejidad de la
realización de componentes para los programadores.
La siguiente versión de OLE (llamada COM) se rescribió desde cero por Microsoft y
DEC en un principio. Al final DEC abandonó el proyecto y Microsoft continuó con él.
Las nuevas versiones que realizó Microsoft ampliaron el modelo para poder usar los
componentes desde distintos ordenadores ( DCOM o Distributed COM ).
El objetivo de la tecnología COM es utilizar la programación orientada a objetos
que posteriormente se encontrarán a nivel de programas binarios y que por lo tanto, para
poder usarlos no es necesario tener su código fuente, lo que fomenta la transparencia y
la abstracción.
Se pueden utilizar las librerías de enlace dinámico o DLL’s para albergar estos
componentes.
La característica de un objeto COM es que puede ejecutarse fuera del ámbito de un
proceso, es decir, puede estar en ejecución en otros procesos de la misma máquina o de
otra máquina diferente.
El modelo COM sigue la arquitectura cliente/servidor. El objeto COM en sí es el
servidor, y es usado por un programa que hace de cliente. Existen varias formas de
realizar la comunicación. Podemos encontrar un componente COM como parte de un
ejecutable, o en una DLL o incluso en otra máquina (DCOM).
Los servidores COM pueden ser escritos en variedad de lenguajes y en sistemas
operativos distintos, mientras que, por ejemplo, los objetos escritos en C++ son siempre
para C++.
Es normal que un servidor tenga varios objetos COM, aunque se presenta un problema a
la hora de identificar qué componente se quiere usar cuando reside en un lugar distinto
al cliente. La forma de conseguir la unicidad del objeto COM es con una secuencia de
128 bits que lo identifique unívocamente y que la probabilidad de conflicto sea
prácticamente nula. Esta secuencia se llama GUID (Identificador único global). Los
GUIDs utilizados para identificar objetos COM se denominan CLSID (Identificador de
clase).
48
C A P Í T U L O 2 • DirectX y la API DirectSound
La programación en COM está básicamente orientada a objetos. Pero en este punto
es necesario definir la noción de Interfaz. Así, para COM una interfaz es un conjunto de
declaraciones de funciones que puede o no implementar un objeto. La definición de
Interfaz es: conjunto de funciones que se ponen a disposición del público. Suelen tener
relación entre sí. Las interfaces también tienen un GUID (denominado IID, o
identificador de interfaz ). Un mismo objeto implementa normalmente varias interfaces.
En la siguiente figura se puede observar un ejemplo de objeto COM.
CLSID_IObjeto1
Objeto1
Interfaz1
Interfaz2
- metodo1
- metodo1
- metodo2
- metodo2
- metodo3
- metodo3
IID_IInterfaz1
IID_IInterfaz2
Figura 2.2 Objeto COM
Uno de los componentes principales de modelo COM son las interfaces, que,
aunque este concepto no existe en programación C++, sí existe en otros lenguajes como
Java. Las interfaces en C++ se implementan como clases abstractas. Toda interfaz en
COM deriva de la interfaz IUnknown. Por lo tanto, una interfaz es una clase que
contiene un conjunto de punteros a funciones que se encuentran en la tabla de métodos
virtuales VMT que genera el compilador.
El cliente de un objeto COM no accederá directamente al objeto, sino que lo hará a
través de sus interfaces. Lo único que poseerá el cliente será un puntero a la interfaz que
implementará los métodos que llevan a cabo la lógica de negocio.
Interfaz IUnknown: Como se ha mencionado anteriormente todas las interfaces
derivan de IUnknown. Si nos fijamos en el gráfico 2.3, podemos observar que para
acceder al resto de las interfaces se entra por la interfaz de arriba:
49
C A P Í T U L O 2 • DirectX y la API DirectSound
IUnknown
Interfaz 2
Objeto COM
Interfaz 3
Figura 2.3
Se utiliza un lenguaje independiente de otros lenguajes de programación y de la
plataforma llamado IDL (Interface Definition Language) para definir las interfaces y sus
argumentos. A partir de este lenguaje sería posible obtener las declaraciones en distintos
lenguajes de programación para que puedan ser usadas. Por consiguiente, la interfaz
IUnknown quedaría representada en IDL de la siguiente manera:
[ local, object
huid(00000000-0000-0000-C000-000000000046)
pointer_default(unique)
]
interface IUnknown
{
HRESULT QueryInterface (
[in]REFIID riid,
[out,iid_is(riid)] void **ppvObject);
ULONG AddRef();
ULONG Release();
}
En la definición de la intefaz IUnknown podemos apreciar tres métodos especiales:
1. QueryInterface(): Este método se utiliza para la invocación de interfaces, es
decir, el objeto COM se puede ver como una caja negra que encapsula un
conjunto de interfaces y métodos. Para acceder a la implementación de una
determinada interfaz recurriremos a la utilización de este método, que nos
devolverá un puntero a dicha interfaz. Su funcionamiento es siempre similar: el
primero es el IID que el identificador de interfaz que se quiere invocar, y el
segundo parámetro un doble puntero void a esa interfaz. Por ejemplo:
50
C A P Í T U L O 2 • DirectX y la API DirectSound
CoCreateInstante(CLSID_Objeto1, NULL, CLSCTX_ALL,
IID_IUnknown, (void **)&Interfaz1);
Interfaz1->QueryInterface(IID_Interfaz2, (void **)
&Interfaz2);
2. AddRef() y Release(): Permiten controlar el ciclo de vida del objeto COM,
incrementando o disminuyendo el contador de referencias a dicho objeto, de
forma que cuando el contador de referencias llegue a cero se liberará el objeto de
la memoria.
51
C A P Í T U L O 2 • DirectX y la API DirectSound
2.2 Programación en DirectSound1
2.2.1 Introducción
DirectSound proporciona las dos características requeridas por los desarrolladores
de aplicaciones de sonido profesionales: velocidad y control. Entre sus características
principales destacan:
•
•
•
•
•
•
Uso automático de aceleración por hardware cuando se encuentra disponible.
Mezcla ilimitada de sonidos.
Reproducción de baja latencia.
Efectos de sonido con posicionamiento 3D fácilmente integrables en
aplicaciones Direct3D u OpenGL.
Conversión automática de los formatos de onda de entrada (incluso de varios
formatos) para que concuerden con el formato de salida.
Acceso a las funcionalidades de la tarjeta de sonido sin acceder al hardware, a
través de la API.
2.2.2 Arquitectura de DirectSound
La estructura de base de la arquitectura DirectSound reside en la utilización de
buffers secundarios de sonido, que representan un único sonido, ya sea estático (sonidos
cortos fácilmente almacenables en bloques de memoria) o canalizados (los datos de
audio se transfieren en bloques al dispositivo de salida).
Un aspecto fundamental en la programación en DirectSound es que el tipo de datos de
forma de onda almacenados en los buffers será, en todo caso, formato PCM.
Apoyándose en los buffers primarios, DirectSound mezcla los sonidos almacenados en
sus respectivos buffers secundarios a alta velocidad. Además realiza otras operaciones
adicionales como conversiones de formato (por ejemplo, cambios de frecuencia de
muestreo de 44 a 22 kHz) y aplicación de efectos especiales, como posicionamiento 3D
del sonido o efectos de Echo, Delay o Chorus. Finalmente, desde el buffer primario los
datos se envían al dispositivo de salida.
DirectSound coloca de manera automática tantos buffers como le es posible en la
memoria del hardware siempre que buffers y funciones de mezcla estén disponibles por
hardware. Si no, DirectSound mezcla por software los buffers que residen en la
memoria y los canaliza hacia el mezclador por hardware junto con los sonidos
procedentes de los buffers por hardware. El proceso se ilustra en la figura 2.4:
1
En este capítulo se utilizará el compilador Microsoft Visual C++ 2005.
52
C A P Í T U L O 2 • DirectX y la API DirectSound
Buffers por software
Mezclador por software
Buffer primario
Dispositivo
Buffers por hardware (si
disponibles)
Mezclador por hardware (si
disponible)
Figura 2.4 Arquitectura DirectSound
2.2.3 Formato de sonido soportado por DirectSound
El formato de sonido de una forma de onda en DirectSound debe tener las siguientes
características:
•
•
•
•
El número de canales (1 = mono, 2 = estéreo, etc.)
La frecuencia de muestreo (en Hertzios). El formato depende de la frecuencia y
del número de canales, por lo que para el sonido estéreo habrá el doble de
muestras por cada segundo de sonido. Las muestras de sonido estéreo están
intercaladas, yendo en primer lugar el canal izquierdo. Las frecuencias de
muestreo que admite el hardware de los PC son 11.025, 22.050 y 22.100 Hz.
La resolución de la muestra en número de bits: 8 ó 16.
El tag de formato, que identifica cómo deben interpretarse los datos de las
muestras. En DirectSound deben ajustarse al tag WAVE_FORMAT_PCM.
La información del formato se contiene en
WAVEFORMATEX, que tiene la siguiente composición:
Campo
WORD wFormatTag
WORD nChannels
DWORD nSamplesPerSec
DWORD nAvgBytesPerSec
WORD nBlockAlign
una
estructura
de
tipo
Descripción
Tipo de formato. Debe ser
WAVE_FORMAT_PCM
Número de canales 1(mono), 2 (estéreo)
Frecuencia de muestreo(en hertzios):
Normalmente 11.025, 22.050, 44.100
Velocidad media de transferencia
(nSamplesPerSec * nBlockAlign)
Bytes por muestra (nChannels *
nBitsPerSample / 8)
53
C A P Í T U L O 2 • DirectX y la API DirectSound
WORD wBitsPerSample
WORD cbSize
Número de bits por muestra (8 ó 16)
Número de bytes de información
adicional: para propósito del usuario.
Siempre es 0
Tabla 2.1
2.2.4 Creación del objeto DirectSound
Para la creación del objeto DirectSound podemos
DirectSoundCreate que se describe en la siguiente tabla:
Parámetro
LPGUID lpGUID
LPDIRECTSOUND *ppDS
LPUNKNOWN pUnkOuter
utilizar
la función
Descripción
Puntero al GUID del dispositivo, o NULL
para el dispositivo por defecto
Dirección del puntero que se iniciará
mediante la llamada
Debe ser NULL
Tabla 2.2
No obstante, antes de crear el objeto DirectSound se precisa iniciar la librería de
COM mediante la llamada a la función Win32 CoInitialize(NULL):
#include "stdafx.h"
#include
#include
#include
#include
#include
#include
#include
<windows.h>
<mmsystem.h>
<dsound.h>
<SDKsound.h>
<math.h>
<string>
<sstream>
#include "directsound.h"
int _tmain(int argc, _TCHAR* argv[])
{
// Inicializa COM
CoInitialize(NULL);
IDirectSound8* pDS = NULL;
HRESULT
hr;
const LPCWSTR tituloVentana = (LPCWSTR) L"Directsound - Capítulo
2";
SetConsoleTitle(tituloVentana);
// Crea IDirectSound a partir del dispositivo primario
if( FAILED( hr = DirectSoundCreate8( NULL, &pDS, NULL ) ) )
printf("Error al crear DirectSound (codigo numero=%d)\n",hr);
54
C A P Í T U L O 2 • DirectX y la API DirectSound
En las primeras líneas se incluyen los ficheros cabecera básicos para poder acceder a
las definiciones de las funciones y clases.
Después del punto de entrada main(), iniciamos COM y establecemos un título para
la ventana de consola de aplicación Win32. Finalmente se invoca una referencia a la
interfaz COM IDirectSound8 con el dispositivo de sonido por defecto.
Para manejar información sobre errores en DirectX se utiliza con frecuencia una
variable del tipo HRESULT que contiene un código de estado con respecto a la llamada
a la función COM, que puede luego verificarse con las macros FAILED (para detectar
algún error en la llamada) y SUCCEEDED (la función devolvió S_OK y funcionó
correctamente).
2.2.5 Establecer el nivel cooperativo
Posteriormente a la creación del objeto DirectSound necesitamos establecer el nivel
cooperativo mediante la llamada SetCooperativeLevel. Esta función tiene el efecto de
asociar DirectSound a una ventana y determinar cómo su aplicación va a compartir con
otros el dispositivo de sonido.
El prototipo de la función es:
HRESULT IDirectSound::SetCooperativeLevel(HWND hwnd, DWORD dwLevel)
El primer parámetro es un handle de ventana y si todas las ventanas hijas de la
aplicación derivan de la misma ventana principal, se debería pasar el handle de la
ventana de nivel superior a dichos métodos. El segundo parámetro permite fijar el valor
del nivel cooperativo como se muestra en la tabla 2.3 en orden ascendente de prioridad:
dwLevel
DDSCL_NORMAL
Ventajas
Mejor cooperación con
otras aplicaciones.
DDSCL_PRIORITY
Permite modificar el
formato primario.
DDSCL_EXCLUSIVE
Garantiza el uso exclusivo
del dispositivo, por lo que
las aplicaciones de fondo
permanecen mudas.
DDSCL_WRITEPRIMARY Garantiza el acceso al
buffer primario para
realizar una mezcla
personalizada.
Desventajas
No se puede modificar el
formato primario, por lo
que se está restringiendo al
formato de salida por
defecto de DirectSound.
Podría terminar
modificando la salida de
otra aplicación.
Podría no ser apropiado
silenciar todas las
aplicaciones de fondo.
Requiere un controlador de
DirectSound; no se pueden
reproducir buffers
secundarios; las
aplicaciones restantes
pierden sus buffers.
Tabla 2.3
55
C A P Í T U L O 2 • DirectX y la API DirectSound
Casi todas las aplicaciones utilizan un nivel cooperativo DDSCL_NORMAL o
DDSCL_PRIORITY. El nivel DDSCL_WRITEPRIMARY sólo es necesario para
aplicaciones altamente especializadas que necesitan realizar sus propias operaciones de
mezclado.
En el fragmento de código del ejemplo se muestra el funcionamiento de esta función:
// Establece el nivel cooperativo de DirectSound
if (FAILED( hr = pDS>SetCooperativeLevel(FindWindow(NULL,tituloVentana),DSSCL_PRIORITY) )
)
printf("Error en la funcion SetCooperativeLevel (codigo
numero %d)\n", hr );
2.2.6 Crear el buffer primario
Por defecto el buffer primario tiene una frecuencia de 22.050 Hz, 2 canales y 8 bits
por muestra. En el buffer primario es donde se mezclan todos los sonidos, con
independencia de sus formatos antes de reproducirse.
En el ejemplo se ha establecido un formato primario de 16 bits, estéreo y 44.100 Hz;
obviamente, DirectSound canalizará los datos al buffer primario con más rapidez si no
tiene que convertirlos.
Es significativo advertir que DirectSound está optimizado para trabajar con muestras
de 16 bits, y no se produce pérdida alguna de rendimiento por cambiar el formato del
buffer primario a uno de 16 bits.
En el código del ejemplo siguiente se muestra cómo obtener un buffer primario:
// Crea el buffer primario
LPDIRECTSOUNDBUFFER pDSBPrimary = NULL;
// Obtiene el buffer primario
DSBUFFERDESC dsbd;
ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
dsbd.dwSize
= sizeof(DSBUFFERDESC);
dsbd.dwFlags
= DSBCAPS_PRIMARYBUFFER ;
dsbd.dwBufferBytes = 0;
dsbd.lpwfxFormat
= NULL;
if( FAILED( hr = pDS->CreateSoundBuffer( &dsbd, &pDSBPrimary, NULL
) ) )
printf("Error al crear el buffer primario (codigo error =
%d)", hr );
// Establece el formato del buffer primario
WAVEFORMATEX wfx;
ZeroMemory( &wfx, sizeof(WAVEFORMATEX) );
wfx.wFormatTag
= (WORD) WAVE_FORMAT_PCM;
wfx.nChannels
= 2;
wfx.nSamplesPerSec = 44100;
wfx.wBitsPerSample = 16;
wfx.nBlockAlign
= (WORD) (wfx.wBitsPerSample / 8 *
wfx.nChannels);
wfx.nAvgBytesPerSec = (DWORD) (wfx.nSamplesPerSec *
wfx.nBlockAlign);
56
C A P Í T U L O 2 • DirectX y la API DirectSound
if( FAILED( hr = pDSBPrimary->SetFormat(&wfx) ) )
printf("Error al poner formato al buffer primario (codigo
error =%d)", hr );
2.2.7 Introducción a los buffers secundarios
El objeto básico del sonido es el buffer y se representa mediante la interfaz
IDirectSoundBuffer. Los buffers secundarios son circulares. Si un buffer se está
reproduciendo de manera continua, tan pronto como DirectSound llega al final vuelve
de nuevo al principio. Si se están canalizando datos hacia el buffer, es responsabilidad
del programador enlazar los datos de esta forma.
DirectSound mantiene un par de punteros por cada buffer de sonido: la posición actual
de reproducción y la posición actual de escritura. La más necesaria es la posición actual
de reproducción, o cursor de reproducción, que es el desplazamiento del siguiente byte
de datos que se va a enviar al mezclador. Respecto a la posición actual de escritura,
DirectSound no está escribiendo en dicha posición y, en muchos casos, ni siquiera lo
hace la aplicación. Lo que en realidad representa la posición de escritura es el primer
punto del buffer, más allá de la posición de reproducción, en el que es seguro escribir.
Los diferentes tipos de buffers secundarios se dividen en estáticos y canalizados. Un
buffer secundario estático es aquel que se usará para un único sonido corto que se
emplee con frecuencia. Un buffer canalizado está destinado a un sonido que no cabría
en un buffer de un tamaño razonable y del que se debe copiar una parte determinada a
medida que se reproduce. La única diferencia entre estos dos tipos de buffers estriba en
la optimización. Se pueden canalizar datos hacia un buffer estático o usar un buffer
canalizado para un sonido estático, pero ninguno de ellos funcionaría a la perfección.
Por lo tanto, mediante el uso de las constantes de la API: DSBCAPS_STATIC,
DSBCAPS_LOCHARDWARE y DSBCASP_LOCSOFTWARE se pueden definir las
diferentes combinaciones de tipos de buffers.
2.2.8 Pasos para crear los buffers secundarios
Los pasos implicados en la creación de buffers secundarios son los siguientes:
1. Iniciar una estructura del tipo WAVEFORMATEX que describa el formato del
sonido.
2. Iniciar una estructura del tipo DSBUFFERDESC con los parámetros del buffer,
incluyendo un puntero a su estructura WAVEFORMATEX.
3. Llamar al método CreateSoundBuffer para crear el buffer.
4. Bloquear el buffer, entero o sólo una parte.
5. Copiar datos al buffer(normalmente provenientes de un fichero de audio o
generados con una función matemática).
6. Desbloquear el buffer.
7. Fijar la posición de reproducción.
8. Reproducir el buffer.
9. Si se trata de un buffer canalizado, repetir los pasos 4, 5 y 6.
57
C A P Í T U L O 2 • DirectX y la API DirectSound
2.2.9 Archivos de sonido
Para trabajar con sonidos en DirectSound es necesario recurrir a alguna fuente de
sonido almacenado en formato PCM en memoria secundaria. Este es el caso de los
ficheros WAV (originarios de Microsoft) y muy utilizados en Windows. Estos archivos
están en formato RIFF (Resource Interchange File Format), formato que almacena
cabeceras y datos en bloques de longitud variable que siempre comienzan con un código
de cuatro letras, por ejemplo: “wave” o “data”. Los archivos de onda incluyen un bloque
“fmt” que se corresponde con una estructura del tipo WAVEFORMATEX. El bloque
“data”, dando por su puesto que el formato es PCM, es tan sólo una colección de
muestra de 8 ó 16 bits.
En el ejemplo que se incluye con el estudio se ha recurrido a la clase CWaveFile
definida en el fichero del SDK de DirectX como SDKSound.cpp y SDKSound.h que
deben de incluirse en el proyecto para poder trabajar con ficheros WAV.
2.2.10 Describir el buffer
Para describir el buffer se utiliza la estructura DSBUFFERDESC que
inicializaremos con algunos de los siguientes flags:
Flag
DBSCAPS_CTRL3D
Notas
Se utiliza para posicionamiento en 3D del
sonido, si y sólo si se crea una interfaz
IDirectSound3DBuffer. No puede
combinarse con DSBCAPS_CTRLPAN
DSBCAPS_CTRLALL
Establece todos los controles
DSBCAPS_CTRLDEFAULT
Establece controles por defecto
DSBCAPS_CTRLFREQUENCY
Cambia la frecuencia de reproducción
DSBCAPS_CTRLPAN
Cambia el pan (balance)
DSBCAPS_POSITIONNOTIFY
Informa o notifica sobre el
posicionamiento
DSBCAPS_CTRLVOLUME
El buffer va a sufrir cambios de volumen
DSBCAPS_LOCHARDWARE
Buffer canalizado por hardware
DSBCAPS_LOCSOFTWARE
Buffer canalizado por software
DSBCAPS_STATIC
Buffer estático
DSBCAPS_PRIMARYBUFFER
El buffer es primario
DSBCAPS_MUTE3DATMAXDISTANCE Hace que los buffers 3D sean más
eficientes reduciendo el número de
cálculos necesarios.
Tabla 2.4
58
C A P Í T U L O 2 • DirectX y la API DirectSound
2.2.11 Creación del buffer secundario
Para crear el buffer secundario utilizaremos el método:
HRESULT IDirectSound::CreateSoundBuffer
Con los siguientes parámetros:
Parámetro
LPCDBUFFERDESC lpcDSBufferDesc
LPLDIRECTSOUNDBUFFER
lplpDirectSoundBuffer
IUnknown FAR *pUnkOuter
Descripción
Puntero a la estructura de descripción del
buffer.
Recibe el puntero a la interfaz
Debe ser NULL
Tabla 2.5
A continuación se muestra el código que lo hace posible:
/ Crea un buffer secundario con sonido
LPDIRECTSOUNDBUFFER*
LPDIRECTSOUND3DBUFFER*
DWORD
CWaveFile*
const int
apDSBuffer
apDS3DBuffer
dwDSBufferSize
pWaveFile
NUM_BUFFERS
=
=
=
=
=
NULL;
NULL;
NULL;
NULL;
3;
// Arrays de buffers secundarios y 3D respectivamente
apDSBuffer
= new LPDIRECTSOUNDBUFFER[NUM_BUFFERS];
apDS3DBuffer = new LPDIRECTSOUND3DBUFFER[NUM_BUFFERS];
if( apDSBuffer == NULL )
printf("Falta memoria para buffers secundarios");
if( apDS3DBuffer == NULL )
printf("Falta memoria para buffers 3D");
for(int i = 0;i<NUM_BUFFERS; i++ )
{
pWaveFile = new CWaveFile();
if( pWaveFile == NULL )
printf("Falta memoria para objeto CWaveFile");
std::wstring fichero,indice;
std::wostringstream iss;
iss << i+1;
fichero = L"..\\media\\sonido" + iss.str() + L".wav";
// Abre fichero WAV/PCM para leer cabecera
pWaveFile->Open((LPWSTR)fichero.c_str(), NULL,
WAVEFILE_READ );
if( pWaveFile->GetSize() == 0 ) // Fichero inexistente o
vacío
printf("Error al leer fichero WAV");
59
C A P Í T U L O 2 • DirectX y la API DirectSound
// El buffer secundario debe tener el mismo tamaño que el
fichero WAV
dwDSBufferSize = pWaveFile->GetSize();
// Crea el buffer secundario con las propiedades deseadas
ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
dsbd.dwSize
= sizeof(DSBUFFERDESC);
dsbd.dwFlags
= DSBCAPS_STATIC |
DSBCAPS_CTRLFREQUENCY |DSBCAPS_CTRLVOLUME | DSBCAPS_CTRL3D;
dsbd.dwBufferBytes
= dwDSBufferSize;
dsbd.guid3DAlgorithm = GUID_NULL;
dsbd.lpwfxFormat
= pWaveFile->m_pwfx;
// Crea el buffer secundario DirectSound
if (FAILED(hr = pDS->CreateSoundBuffer( &dsbd,
&apDSBuffer[i], NULL )))
printf("Error al crear buffer secundario (codigo
error =%d)", hr );
2.2.12 Sonido en tres dimensiones
Una de las posibilidades de DirectSound es el posicionamiento 3D en tiempo real
del sonido. Éste coloca los sonidos en el espacio siguiendo el mismo sistema de
coordenadas que usa Direct3D y OpenGL. Para poder adaptar una aplicación
DirectSound a DirectSound 3D es tan sencillo como añadir las dos siguientes interfaces:
•
•
IDirectSound3DBuffer: controla las propiedades 3D de fuentes de sonido
individuales, es decir, buffer secundarios.
IDirectSound3DListener: se emplea para colocar un oyente virtual y para
manipular las propiedades generales del entorno del sonido 3D.
DirectSound puede proporcionar el efecto sonoro de la posición y el movimiento de
una fuente acústica de las siguientes formas:
•
•
•
•
•
Atenuando la amplitud dependiendo de la distancia que haya desde la fuente del
sonido hasta el oyente.
Atenuando la amplitud en uno u otro lado, o realizando un desplazamiento, para
indicar la orientación hacia la izquierda o la derecha de la fuente de sonido.
Truncando un sonido que se encuentre justo delante del oyente, de acuerdo con
la orientación de sus oídos y el efecto de bloqueo de sonido de su cabeza.
Aplicando un breve retardo a la reproducción del sonido en un lado o en otro
para reflejar la mayor distancia recorrida por las ondas sonoras originadas en el
lado opuesto de la cabeza. (A este efecto se le denomina ITD, lo que significa
retardo “interaural” o diferencia de tiempo “interaural”).
Incrementando o disminuyendo la frecuencia de una fuente de sonido para crear
un efecto Doppler, simulando el efecto de fuentes sonoras que se mueven.
Las coordenadas que utiliza DirectSound 3D son las cartesianas X, Y y Z. Las
posiciones se miden desde el origen de coordenadas (0.0, 0.0, 0.0). Para representar las
coordenadas 3D se utiliza el tipo de datos D3DVALUE. En Direct3D, una diferencia de
60
C A P Í T U L O 2 • DirectX y la API DirectSound
1.0 entre dos puntos situados a lo largo de un vector no se corresponde con una
determinada distancia del mundo real; es responsabilidad del diseñador establecer una
escala. Por defecto, 1.0 equivale a 1 metro. Por lo tanto, un buffer con una posición (2.0, 0.0, 8.0) estará situado un metro a la izquierda y 8 metros por delante de la posición
por defecto del oyente.
Para obtener una interfaz IDirectSound3D, es necesario invocarla a través de la interfaz
IDirectSoundBuffer con el método COM QueryInterface.
De esta forma quedaría el código:
// Obtiene interfaces 3D
if (FAILED(hr = apDSBuffer[i]->QueryInterface(
IID_IDirectSound3DBuffer, (VOID**) &apDS3DBuffer[i])))
printf("Error al obtener buffer 3D (codigo error
=%d)", hr );
y obtiene tres buffers 3D.
A partir de aquí es necesario fijar las propiedades del buffer 3D; para ello se utiliza
la estructura DS3DBUFFER donde se asignarán los flags que describen las
características del buffer. En esta estructura se puede definir cuándo un sonido debe
situarse en el espacio de forma relativa al oyente o de manera absoluta, dependiendo del
modo del buffer.
Los siguientes flags representan los tres modelos mediante los que se puede configurar
un buffer 3D:
•
•
•
DS3DMODE_HEARDRELATIVE: el buffer se coloca en una posición relativa
a la del oyente. Es el modo que se usará en el ejemplo.
DS3DMODE_NORMAL: el buffer se coloca de forma relativa al espacio y no
se desplaza a medida que lo hace el oyente. La mayoría de los sonidos 3D
funcionan con este método; ésta es la configuración por defecto.
DS3DMODE_DISABLE: esta configuración se puede utilizar para ahorrar
tiempo de CPU al desactivar el proceso 3D de un buffer de sonido.
// Propiedades del buffer 3D
DS3DBUFFER ds3db;
ZeroMemory(&ds3db,sizeof(DS3DBUFFER));
ds3db.dwSize = sizeof(DS3DBUFFER);
apDS3DBuffer[i]->GetAllParameters( &ds3db);
ds3db.dwMode = DS3DMODE_HEADRELATIVE;
apDS3DBuffer[i]->SetAllParameters(&ds3db, DS3D_IMMEDIATE );
2.2.13 Bloqueo del buffer
Ya se ha visto cómo se crea el buffer secundario y se establecen sus propiedades. A
continuación se explicará cómo bloquearlo antes de desplazar datos desde el archivo de
sonido al buffer. De esta forma nos aseguramos de que el buffer permanece en una
situación estable y al mismo tiempo, se obtiene su dirección de memoria. En la tabla 2.6
se muestran los parámetros del método Lock.
61
C A P Í T U L O 2 • DirectX y la API DirectSound
Parámetro
DWORD dwWriteCursor
DWORD dwWriteBytes
DWORD lplpvAudioPtr1
LPDWORD lpdwAudioBytes1
LPVOID lplpAudioPtr2
LPDWORD lpdwAudioBytes2
DWORD dwFlags
Descripción
Desplazamiento a la zona en la que
comenzará la parte bloqueada.
Número de bytes a bloquear.
Recibe la dirección de inicio de bloqueo.
Recibe el tamaño de bloqueo, o la primera
porción si el bloqueo es circular.
Recibe la dirección de la segunda porción
del bloqueo; si no lo es, recibe NULL.
Recibe el número de bytes de la segunda
porción del bloqueo si éste es circular; si
no lo es, recibe 0.
DSBLOCK_FROMWRITECURSOR o
DSBLOCK_ENTIREBUFFER
Tabla 2.6
En el fragmento de código siguiente se muestra el ejemplo del bloqueo del buffer antes
de la reproducción.
VOID*
pDSLockedBuffer
= NULL; // Puntero a la memoria bloqueada
DWORD
dwDSLockedBufferSize = 0;
// Tamaño de la zona bloqueada
DWORD
dwWavDataRead
= 0;
// Cantidad de datos del fichero
WAV a leer
// Bloquea el buffer
if( FAILED( hr = apDSBuffer[i]->Lock( 0,dwDSBufferSize,
&pDSLockedBuffer,&dwDSLockedBufferSize,
NULL, NULL, 0L ) ) )
printf("Error de bloqueo del buffer (codigo
error=%d)", hr );
// Empieza a leer desde el inicio del fichero
pWaveFile->ResetFile();
if( FAILED( hr = pWaveFile->Read( (BYTE*) pDSLockedBuffer,
dwDSLockedBufferSize,&dwWavDataRead ) ) )
printf("Error leyendo el fichero de audio (codigo error=
%d)", hr );
Por último, es necesario desbloquear el buffer. En la tabla 2.7 se muestran los
parámetros del método Unlock.
Parámetro
LPVOID lpvAudioPtr1
DWORD dwAudioBytes1
LPVOID lpvAudioPtr2
DWORD dwAudioBytes2
62
Descripción
Dirección de inicio del bloqueo.
Número de bytes en la primera porción
del bloqueo.
Dirección de inicio del buffer si se
produjo desbordamiento del buffer, si no
es así, NULL.
Número de bytes en la parte del buffer que
C A P Í T U L O 2 • DirectX y la API DirectSound
produjo el desbordamiento.
Tabla 2.7
if ( FAILED( apDSBuffer[i]->Unlock( pDSLockedBuffer,
dwDSLockedBufferSize, NULL, 0 ) ) )
printf("Error al desbloquear buffer (codigo error
=%d)", hr );
SAFE_DELETE( pWaveFile ) // Libera el objeto CWaveFile
}
2.2.14 Reproducir y posicionar el buffer en 3D
Reproducir el buffer consiste en fijar la posición actual de reproducción al principio
del buffer y llamar al método Play. En la tabla 2.8 se muestran sus parámetros:
HRESULT IDirectSoundBuffer::Play
Parámetros
DWORD dwReserved1
DWORD dwReserved2
DWORD dwFlags
Descripción
Debe ser 0.
Debe ser 0.
0 o DBSPLAY_LOOPING.
Tabla 2.8
case '1':
if (FAILED (hr = apDSBuffer[0]->Play( 0, 0, 0)))
printf("Error al reproducir buffer 1 (codigo error =%d)",
hr );
break;
Es indispensable controlar la gestión de una pérdida del buffer. Si el error que
devuelve Play es DSERR_BUFFERLOST es porque un buffer mediante hardware
puede perder su espacio de memoria debido a que otra aplicación tome el control de
dicho buffer. Cuando esto sucede todas las llamadas a Lock o Play que se realicen sobre
dicho buffer generarán un error hasta que la aplicación recupere el espacio de memoria
del buffer mediante una llamada al método Restore.
Por último, si deseamos posicionar el buffer en 3D es necesario llamar al método:
IDirectSoundBuffer3D::SetPosition
al cual se debe pasar la posición 3D en formato D3DVALUE de las coordenadas x,y,z
y una constante que debe ser:
•
•
DS3D_DEFERRED: Los parámetros no se hacen efectivos hasta que no se
llama al método: IDirectSound3DListener8::CommitDeferredSettings.
DS3D_INMEDIATE: Los parámetros son aplicados inmediatamente.
En el siguiente ejemplo podemos ver una aplicación de posicionamiento 3D:
63
C A P Í T U L O 2 • DirectX y la API DirectSound
case '4':
for( double alfa=0.0; alfa < circulo ; alfa += 0.1 )
{
D3DVALUE x = D3DVALUE(distancia*cos(alfa));
D3DVALUE y = D3DVALUE(distancia*sin(alfa));
if ( FAILED( hr = apDS3DBuffer[0]>SetPosition(x,y,0,DS3D_IMMEDIATE) ) )
printf("Error al posicionar 3D (codigo error =%d)",
hr );
Sleep(100);
}
break;
2.2.15 Liberar las interfaces
En último lugar, una vez acabado el procesamiento de sonido y con vistas a salir de
la aplicación es necesario liberar las referencias a las interfaces COM. Para ello se debe
llamar a la macro definida en directsound.h como SAFE_RELEASE. En el siguiente
fragmento de código podemos ver su utilización:
// Libera interfaces y arrays
SAFE_RELEASE(pDSBPrimary);
SAFE_RELEASE(apDSBuffer[0]);
SAFE_RELEASE(apDSBuffer[1]);
SAFE_RELEASE(apDSBuffer[2]);
SAFE_RELEASE(apDS3DBuffer[0]);
SAFE_RELEASE(apDS3DBuffer[1]);
SAFE_RELEASE(apDS3DBuffer[2]);
SAFE_RELEASE(pDS);
SAFE_DELETE(apDSBuffer);
SAFE_DELETE(apDS3DBuffer);
64
C A P Í T U L O 2 • DirectX y la API DirectSound
Bibliografía y referencias
Libros
A fondo DirectX – Bradley Bargen, Peter Donnelly. Microsoft Press, 1998.
Programación COM y COM+ – Alan Gordon . Anaya Multimedia, 2001.
Sistemas Distribuidos: conceptos y diseño – Colouris, Dolimore, Kindberg. Pearson
Addison Wesley, 2001
La Web
MSDN (Microsoft Developer Network):
http://msdn2.microsoft.com/es-es/default.aspx
Francisco Ángel Gimeno Doménech – Andrómeda Studios
http://usuarios.multimania.es/andromeda_studios/paginas/tutoriales/articulo02.htm
65
C A P Í T U L O 3 • OpenAL
3
OpenAL
3.1 Introducción a OpenAL
OpenAL son las siglas de Open Audio Library, una librería de código fuente abierto
con una API multiplataforma. El diseño fue pensado para un alto rendimiento en el
posicionamiento 3D multi-canal y se distribuye como librería adicional que se instala en
los directorios del sistema. El estilo de OpenAL es muy parecido al de su semejante
OpenGL, por lo que existen diversas analogías en la convención de llamadas a las
funciones.
La historia de OpenAL se remonta a la compañía de software Loki cuando
intentaban poder realizar de una forma fácil la traslación de código de juegos
programados en Windows sobre Linux. El proyecto fue distribuido con licencia pública
como software libre y mantenido por esta comunidad. Más tarde fue la compañía de
Creative Labs la que continuó el mantenimiento del proyecto, con apoyo de Apple y la
comunidad de software libre.
Figura 3.1 Logotipo de OpenAL
3.2 Arquitectura de OpenAL
La arquitectura de OpenAL se basa en la Arquitecture Review Board o ARB que es
el modelo usado por OpenGL.
La estructura básica consta de 3 elementos principales: objetos source, buffers y
listeners. Un objeto source contiene un puntero a un buffer, la velocidad, la posición, la
dirección y la intensidad del sonido. Un objeto listener contiene la posición, la
velocidad, la dirección y la ganancia global aplicada a todos los sonidos. Así mismo, los
buffers son los responsables de albergar formas de onda de audio con codificación
PCM, con resolución de 8 ó 16 bits y formato monoaural o estéreo. El motor de
renderizado lleva a cabo todos los cálculos necesarios para obtener la atenuación de
distancia, efecto Doppler, etc.
El resultado neto de una aplicación desarrollada con esta arquitectura es que el usuario
final aprecia que el sonido es muy natural y que el posicionamiento 3D está
perfectamente acompasado con el movimiento por un escenario virtual. Desde la
67
C A P Í T U L O 3 • OpenAL
perspectiva del programador de aplicaciones requiere de muy poco tiempo de
codificación y de mantenimiento para cumplir los requisitos.
De la misma forma que OpenGL, OpenAL consta de dos secciones de la API: el núcleo
basado en las llamadas a las funciones y la API ALC que es usada para manejar
contextos de renderización, uso de recursos y bloqueo multiplataforma. Además,
imitando a su predecesor, OpenAL incluye una librería de alto nivel para
programadores, llamada ALUT, con convenciones de llamadas muy parecidas a la
GLUT de OpenGL cuyo cometido es simplificar algunas tareas complicadas como es la
lectura de un fichero WAV.
Dispositivo nº1
Listener
Contexto nº1
Source 1
Source 2
Source 3
Source 4
Buffer 1
Buffer 2
Buffer 3
Buffer 4
Figura 3.2 Arquitectura de OpenAL
Con el objetivo de proveer funcionalidad de cara al futuro, OpenAL utiliza un
mecanismo de extensiones por el cual los fabricantes pueden incluir sus propias
extensiones en distribuciones de OpenAL, con el propósito de exponer funciones
adicionales en su hardware propietario. Las extensiones siguen la base ARB
(Arquitecture Review Board), asegurando que va a existir retrocompatibilidad con las
extensiones anteriores. Las extensiones ARB pueden ser agregadas al núcleo de la API
tras un cierto período de tiempo.
Entre las plataformas en las que se encuentra implementado OpenAL destacan las
siguientes:
•
•
•
•
•
68
Mac OSX
GNU/Linux
BSD
Solaris
IRIX
C A P Í T U L O 3 • OpenAL
•
•
Windows PC
XBox 360
3.3 Programación con OpenAL1
3.3.1 Inicialización
El primer paso para inicializar OpenAL es abrir el dispositivo. Una vez abierto con
éxito, procederemos abrir un contexto sobre dicho dispositivo. A partir de entonces es
cuando los objetos fundamentales de OpenAL pueden ser manejados. La función para
abrir el dispositivo es la que se muestra a continuación:
alcOpenDevice
Parámetros
conts ALCchar *devicename
Descripción
Un string describiendo el dispositivo
Tabla 3.1
Si el string que contiene el nombre del dispositivo es null, se obtendrá el dispositivo
por defecto. En caso de que el dispositivo no pueda ser abierto o se produzca un error
devolverá la constante literal null.
Después de iniciar el dispositivo es necesario crear el contexto, y para esto
llamamos a la función:
alcCreateContext
Parámetros
ALCdevice *device
ALCint *attrlist
Descripción
Puntero al dispositivo
Atributos:
ALC_FREQUENCY
ALC_MONO_SOURCES
ALC_REFRESH
ALC_STEREO_SOURCES
ALC_SYNC
Tabla 3.2
La función devolverá un puntero al nuevo contexto o null si ocurre algún error.
Una vez creado el contexto, procederemos a informar al sistema sobre cuál va a ser
el contexto con el que se activará la aplicación, para lo que se requiere llamar a la
siguiente función:
1
En este capítulo utilizaremos el compilador Microsoft Visual C++ .NET 2010 que es el más avanzado
en Windows 64 bits hasta la fecha de publicación de este tratado.
69
C A P Í T U L O 3 • OpenAL
alcMakeContextCurrent
Parámetros
ALContext *context
Descripción
Puntero al contexto
Tabla 3.3
En caso de error, la función devolverá la constante booleana ALC_FALSE, y en caso de
éxito devolverá ALC_TRUE.
Con estas llamadas tan simples podemos activar un dispositivo y un contexto de
renderización para reproducir buffers de sonidos y activar sources y listeners.
Por último, dentro de la sección de inicialización, existe la posibilidad de indagar en
OpenAL la presencia de extensiones hardware, mediante la llamada a la función:
alIsExtensionPresent
Parámetros
ALCdevice *device
const ALCchar *extname
Descripción
Puntero al dispositivo del cual interesa
averiguar extensión
Cadena indicando la extensión
Tabla 3.4
A continuación se muestra el código del ejemplo que consigue dichas tareas:
#include
#include
#include
#include
#include
<conio.h>
<windows.h>
<math.h>
<string>
<sstream>
#include "al.h"
#include "alc.h"
#include "AL/alut.h"
const int NUM_BUFFERS = 4;
const int NUM_SOURCES = 4;
void _tmain(int argc, _TCHAR* argv[])
{
ALCdevice* Device;
ALCcontext* Context;
ALenum
error;
// Inicialización
Device = alcOpenDevice(NULL); // Selecciona el dispositivo por
defecto
if (Device)
alcMakeContextCurrent(Context =
alcCreateContext(Device,NULL));
// Comprueba que existe la extensión EAX 2.0
ALboolean bEAX = alIsExtensionPresent("EAX2.0");
if (bEAX) printf("EAX2.0 presente en el sistema...\n");
alGetError(); // Limpia el código de error
70
C A P Í T U L O 3 • OpenAL
3.3.2 Trabajar con buffers
Depués de iniciar la aplicación es preciso generar el primer elemento básico de la
arquitectura de OpenAL: el buffer. Con este objeto podremos almacenar formas de
ondas provenientes de ficheros WAV y reproducirlas más tarde.
La función que lleva a cabo este cometido es:
alGenBuffers
Parámetros
Descripción
ALsizei n
Número de buffers a generar
ALuint *buffers
Puntero a un array de valores ALuint que
almacenará el nombre de los nuevos
buffers.
Tabla 3.5
Una vez creado el buffer es posible almacenar en él datos en formato PCM
provenientes de un fichero WAV. Para realizar esta operación se utiliza una función de
alto nivel definida en la librería externa “alut.h” que permite leer ficheros en formato
WAV.
alutLoadWAVFile
Parámetros
ALbyte* strFile
ALenum* format
ALvoid *data
ALsizei *size
ALsizei *freq
ALboolean *loop
Descripción
Nombre del fichero
Formato del fichero
Datos en PCM
Tamaño del fichero
Frecuencia de sampleo
Bucle de reproducción del fichero
Tabla 3.6
Por último, y una vez leídos los datos PCM en un array, se procederá a copiarlos
dentro del objeto buffer con la siguiente función:
alBufferData
Parámetros
ALuint buffer
ALenum format
const ALvoid *data
ALsizei size
ALsizei freq
Descripción
Nombre del buffer a llenar con datos
Puede ser uno de los siguientes:
AL_FORMAT_MONO8
AL_FORMAT_MONO16
AL_FORMAT_STEREO8
AL_FORMAT_STEREO16
Puntero a los datos de audio
El tamaño de los datos en bytes
Frecuencia de muestreo
Tabla 3.7
71
C A P Í T U L O 3 • OpenAL
Esta función también puede leer datos en formatos diferentes a PCM si se utilizan
las extensiones apropiadas.
Con todo esto, el código necesario para implementar la generación y carga de datos en
el buffer es como se muestra en el siguiente ejemplo:
// Genera los buffers
ALuint
buffer[NUM_BUFFERS];
alGenBuffers(NUM_BUFFERS, buffer);
printf("Generando buffers...\n");
if ((error = alGetError()) != AL_NO_ERROR)
{
printf("alGenBuffers :", error);
terminaAplicacion();
return;
}
// Carga los ficheros WAV
ALsizei size,freq;
ALenum format;
ALvoid *data;
ALboolean loop;
printf("Leyendo ficheros de audio...\n");
for(int i = 0; i < NUM_BUFFERS; i++)
{
std::string fichero,indice;
std::ostringstream iss;
iss << i+1;
fichero = "..\\media\\sonido" + iss.str() + ".wav";
alutLoadWAVFile((ALbyte*)fichero.c_str(), &format, &data,
&size, &freq, &loop);
if ((error = alGetError()) != AL_NO_ERROR)
{
printf("alutLoadWAVFile: %d", error);
// Delete Buffers
alDeleteBuffers(NUM_BUFFERS, buffer);
terminaAplicacion();
return;
}
// Copia a los respectivos buffers los datos
printf("Copiando a buffer...\n");
alBufferData(buffer[i],format,data,size,freq);
if ((error = alGetError()) != AL_NO_ERROR)
{
printf("alBufferData buffer 0 : %d", error);
alDeleteBuffers(NUM_BUFFERS, buffer);
terminaAplicacion();
return;
}
// Descarga el fichero WAV
alutUnloadWAV(format,data,size,freq);
if ((error = alGetError()) != AL_NO_ERROR)
{
printf("alutUnloadWAV : %d", error);
alDeleteBuffers(NUM_BUFFERS, buffer);
terminaAplicacion();
return;
}
}
72
C A P Í T U L O 3 • OpenAL
3.3.3 Reproducir sonido
Como es habitual en toda aplicación de sonido, debe exisitir algún objeto que
controle la reproducción de audio. En OpenAL tenemos el objeto source que permite
llevar a cabo esta operación:
alGenSources
Parámetros
ALsizei n
ALuint *sources
Descripción
Número de sources a generar
Puntero a un array de valores ALuint con
los nombres de los sources
Tabla 3.8
Con el fin de reproducir el sonido debemos adjuntar los buffers, creados en la sección
anterior, a los sources mediante la llamada a la siguiente función:
alSourcei
Parámetros
ALuint source
ALenum param
ALint value
Descripción
Nombre del source al que hay que
establecer los atributos
Uno de los siguientes atributos:
AL_SOURCE_RELATIVE
AL_CONE_INNER_ANGLE
AL_CONE_OUTER_ANGLE
AL_LOOPING
AL_BUFFER
AL_SOURCE_STATE
Valor a establecer
Tabla 3.9
En el siguiente fragmento de código se ilustra la manera de llevarlo a cabo:
// Genera las fuentes
ALuint
source[NUM_SOURCES];
alGenSources(NUM_SOURCES,source);
if ((error = alGetError()) != AL_NO_ERROR)
{
printf("alGenSources 1 : %d", error);
alDeleteSources(NUM_SOURCES, source);
alDeleteBuffers(NUM_BUFFERS, buffer);
terminaAplicacion();
return;
}
// Adjunta los buffers a las fuentes
for(int i=0;i < NUM_SOURCES; i++)
{
alSourcei(source[i], AL_BUFFER, buffer[i]);
if ((error = alGetError()) != AL_NO_ERROR)
{
printf("alSourcei AL_BUFFER 0 : %d", error);
73
C A P Í T U L O 3 • OpenAL
alDeleteSources(NUM_SOURCES, source);
alDeleteBuffers(NUM_BUFFERS, buffer);
terminaAplicacion();
return;
}
}
Ahora ya es posible reproducir un source mediante la función:
alSourcePlay
Parámetros
Descripción
El nombre de la fuente a reproducir
Tabla 3.10
ALuint source
while( condicion )
{
switch( _getch() )
{
// Reproduce buffer 1
case '1':
alSourcePlay(source[0]);
Con estas funciones adicionales es posible también:
•
Detener el sonido
alSourceStop(source[0]);
•
Rebobinar
alSourceRewind(source[0]);
•
o pausarlo…
alSourcePause(source[0]);
3.3.4 Posicionamiento 3D
Esta es una de las características más avanzadas de OpenAL, ya que permimte
utilizar diferentes configuraciones para manipular el sonido en tres dimensiones. Uno de
los elementos fundamentales para el posicionamiento 3D es el listener, que se podría
asemejar a los oídos del sujeto que se encuentra sumergido en el espacio virtual.
Normalmente, los listeners suelen establecerse en la posición de la cámara, de manera
que sensación acústica concuerde con la percepción visual. Así mismo, es posible
aplicar un gran número de parámetros sobre el listener para controlar la orientación,
posición y la velocidad. Este objeto debe ser único en la aplicación, lógicamente, ya que
el sonido procesado mediante el mismo es luego dirigido a los canales estéreo de los
altavoces.
74
C A P Í T U L O 3 • OpenAL
Respecto al posicionamiento 3D de un sonido en OpenAL es posible llevarlo a cabo
llamando a la siguiente función:
alSourcefv
Parámetros
ALuint source
ALenum param
ALfloat *values
Descripción
Source al que hay que establecer los
atributos
Atributo a establecer. Puede ser uno de los
siguientes:
AL_POSITION
AL_VELOCITY
AL_DIRECTION
Un puntero a un array de valores a
establecer
Tabla 3.11
En este caso se utilizará un vector de 3 elementos para almacenar las
componentes X,Y y Z de la posición, velocidad o dirección del sonido 3D, como se
muestra en el ejemplo programado:
ALfloat source4Pos[3]; // Almacena la posicion del buffer 4
Por último, para realizar el posiconamiento 3D del sonido utilizaremos el siguiente
código:
for(float alfa=0.0f; alfa < circulo ; alfa += 0.1f )
{
ALfloat x = distancia*cos(alfa);
ALfloat y = distancia*sin(alfa);
source4Pos[0] = x;
source4Pos[1] = y;
source4Pos[2] = 0.0f;
alSourcefv(source[3], AL_POSITION, source4Pos);
if ((error = alGetError()) != AL_NO_ERROR)
{
printf("Error al posicionar 3D buffer4 :", error);
alDeleteSources(NUM_SOURCES, source);
alDeleteBuffers(NUM_BUFFERS, buffer);
terminaAplicacion();
return;
}
Sleep(100);
}
alSourcei(source[3], AL_LOOPING, AL_FALSE);
75
C A P Í T U L O 3 • OpenAL
Figura 3.3 Descripción del efecto de giro de 360º creado sobre el oyente en el ejemplo
anterior
3.3.5 Salida de la aplicación
Para terminar la aplicación de forma controlada necesitamos obtener un puntero al
contexto actual, obtener el dispositivo activado dentro de él y posteriormente establecer
el contexto actual como null, destruir el contexto y cerrar el dispositivo. A continuación
se muestra la secuencia del código que lo realiza:
// Termina la aplicación
// Libera el contexto y el dispositivo
void terminaAplicacion()
{
ALCcontext* Context
= alcGetCurrentContext();
ALCdevice* Device
= alcGetContextsDevice(Context);
alcMakeContextCurrent(NULL);
alcDestroyContext(Context);
alcCloseDevice(Device);
alutExit();
}
También es importante liberar los buffers y los sources creados para manejar audio.
Las funciones alDeleteBuffers y alDeleteSources son las responsables de destruir los
objetos buffer y source que se hayan creado durante la ejecución de la aplicación. De
esta manera se liberará el espacio de memoria utilizado para dicho propósito en el
sistema. El siguiente fragmento de código ha sido utilizado en el ejemplo para liberar
los objetos sources y buffers en el momento de romper el flujo de control para salir de la
aplicación:
alDeleteSources(NUM_SOURCES, source);
alDeleteBuffers(NUM_BUFFERS, buffer);
terminaAplicacion();
76
C A P Í T U L O 3 • OpenAL
Ya sólo faltaría, por último, salir de la aplicación OpenAL mediante a la llamada:
aluExit()
3.3.6 Gestión de errores
Cuando se produce una situación anómala en la llamada a cualquier función de
OpenAL se genera un estado de error en la librería que informa con un código el tipo de
error que se ha producido. Evidentemente, este tipo de gestión de errores se realiza
mediante consulta utilizando la función:
ALenum alGetError(ALvoid)
La consiguiente llamada a la función producirá que se elimine el estado de error de la
memoria.
El uso de esta función puede verse en el siguiente ejemplo:
ALuint
buffer[NUM_BUFFERS];
alGenBuffers(NUM_BUFFERS, buffer);
printf("Generando buffers...\n");
if ((error = alGetError()) != AL_NO_ERROR)
{
printf("alGenBuffers :", error);
terminaAplicacion();
return;
}
3.4 Ventajas de OpenAL
La primera gran ventaja de OpenAL con respecto a otras librerías de audio como
DirectSound es la portabilidad. Se puede utilizar en gran cantidad de plataformas, desde
Windows hasta Linux, con lo que los desarrolladores tienen un gran abanico de sistemas
que pueden cubrir utilizando el mismo código sin necesidad de cambiar nada. Las
razones son estratégicas a la hora de abordar aplicaciones multiplataforma, cada vez
más de moda y que repercute en aspectos tales como la Ingeniería de Software
(reutilización de código, diseño, pruebas, mantenimiento, evaluación, etc.) así como
económicas.
La segunda gran ventaja es la simplicidad de la arquitectura y del código. No es
necesario tecnología de programación compleja, como es el caso de COM en DirectX,
no hay que complicar lo evidente con funciones adicionales como es el caso de la
“pérdida del buffer” en DirectSound. La arquitectura es fácilmente comprensible: las
entidades utilizadas son las básicas de cualquier aplicación de sonido.
La últimas grandes ventajas son la eficiencia y las prestaciones. OpenAL
proporciona una baja latencia y un rendimiento óptimo de CPU a la hora de mezclar
varios buffers de sonido y realizar cálculos de posicionamiento 3D espacial con muchas
fuentes de sonido.
77
C A P Í T U L O 3 • OpenAL
Como conclusión se puede destacar que OpenAL se presenta como una gran alternativa
a otras librerías de sonido por sus múltiples ventajas a la hora del desarrollo de software
de audio.
Bibliografía y referencias
referencias
Manuales
OpenAL Programmer's Guide - OpenAL Versions 1.0 and 1.1
La Web
Wikipedia:
http://en.wikipedia.org/wiki/OpenAL
78
Parte III
MIDI
C A P Í T U L O 4 • Introducción a MIDI
4
Introducción a MIDI
4.1 ¿Qué es MIDI?
4.1.1 Un poco de historia
Al comienzo del desarrollo de la tecnología musical, todos los sintetizadores
eran monofónicos, es decir, sólo eran capaces de reproducir una sola nota al mismo
tiempo.
A finales de la década de los 70 hizo su aparición el sintetizador digital y trajo consigo
el problema de la incompatibilidad entre sistemas de fabricados por cada compañía. De
este modo se hizo necesario la creación de un lenguaje común que abstrajera las
características técnicas de cada marca y permitiera la comunicación entre sistemas.
En 1982 , Dave Smith, de la empresa Sequential, propuso poner de acuerdo a las
grandes compañías para crear un protocolo de comunicación entre sistemas que fuera
respetado por el convenio o norma. El fundamento de su idea era poder interconectar los
instrumentos digitales para hacer sonar a más de un aparato a la vez, creando así un
efecto de polifonía musical.
El estándar MIDI fue propuesto inicialmente en un documento dirigido a la
Audio Engineering Scoiety. La primera especificación MIDI se publicó en agosto de
1983.
El primer sintetizador capaz de soportar la especificación fue el Prophet 600 de
Sequential (figura 4.1). En 1996 todos los instrumentos que ostentaban el logotipo
respetaban el estándar.
MIDI es el acrónimo de “Interfaz Digital de Instrumentos Musicales”, y
actualmente el estándar es ampliamente utilizado en la industrial musical; aunque es
también muy utilizado en otros muchos sistemas, no sólo instrumentos musicales. Así,
por ejemplo, podemos encontrar la tecnología MIDI implementada en sistemas para
controlar la secuencia de luces de un espectáculo en directo o controlar aparatos de
edición de sonido, entre otras muchas aplicaciones.
81
C A P Í T U L O 4 • Introducción a MIDI
Figura 4.1 Prophet 600 de Sequential
4.1.2 Conceptos básicos
La información MIDI define diversos tipos de datos como números que pueden
corresponder a notas particulares, números de programas de sonidos o valores de
controladores. Gracias a esta simplicidad, los datos pueden ser interpretados de diversas
maneras y utilizados con fines diferentes a la música. El protocolo incluye
especificaciones complementarias de hardware y software. Así, es posible la
interconexión de una gran número heterogéneo de sistemas software y hardware, tal
como se muestra en la figura 4.2.
Permite, por ejemplo, reproducir y componer música en el formato MIDI. Los
archivos almacenados en disco se caracterizan por su calidad de muestreo y su poco
espacio en bytes requerido. Un fichero de una canción puede oscilar entorno a los 17
Kilobytes. Aunque este estándar de fichero no es comúnmente utilizado en Internet.
Figura 4.2 Interconexión de sistemas MIDI
82
C A P Í T U L O 4 • Introducción a MIDI
Cabe aclarar que MIDI no transmite señales de audio, sino datos de eventos y
mensajes controladores que se pueden interpretar de manera arbitraria, de acuerdo con
la programación del dispositivo que los recibe. Es decir, MIDI es una especie de
“partitura” que contiene las instrucciones en valores numéricos, o más específicamente
en binario, sobre cuándo generar cada nota de sonido y las características que debe
tener; posteriormente el sistema MIDI transformará en música las secuencia de
instrucciones.
Los aparatos MIDI se pueden clasificar en tres grandes categorías:
Controladores: generan los mensajes MIDI (activación de nota, desactivación
de nota). El controlador más familiar para los músicos tiene forma de teclado de
piano, que es el más utilizado; aunque también se puede encontrar en guitarras
eléctricas, órganos de tubos, clarinetes, incuso gaitas.
Figura 4.3 Controlador MIDI
Unidades generadoras de sonido: también conocidas como módulos de sonido.
Suelen ser unidades centrales de procesamiento de audio que reciben datos
MIDI y generan sonido.
Figura 4.4 Módulo MIDI
Secuenciadores: son sistemas software o hardware dedicados a reproducir un
fichero con especificación General MIDI 1.0 (GM1) o General MIDI 2.0 (GM2).
Figura 4.5 Secuenciador MIDI
83
C A P Í T U L O 4 • Introducción a MIDI
4.1.3 Electrónica MIDI
4.1.3.1 Interconexión de sistemas
MIDI no ha sido indiferente a los nuevos avances en sistemas de comunicación
electrónica de datos. Así, las nuevas tecnologías en comunicaciones han abierto un gran
abanico de posibilidades al estándar, del cual se ha dejado influenciar.
El sistema MIDI utiliza un total de 16 canales que se pueden comunicar entre aparatos o
instrumentos a través de cada cable. Los tipos de conexión actuales son los siguientes:
Conectores y cables MIDI:
El MIDI comunica de manera digital los datos de una interpretación musical a
través de un cable estándar MIDI como una serie de mensajes que se transmiten a una
velocidad de 31,25 kbaudios (bits/segundo).
Figura 4.6 Cable MIDI
El sistema de funcionamiento es tipo simplex, es decir, sólo puede transmitir
señales en un sentido. La dirección que toman las señales es siempre desde un
dispositivo maestro hacia un dispositivo esclavo. El primero genera la información y el
segundo la recibe.
Un cable estándar MIDI consiste en un cable mutipolar trenzado blindado
terminado en un conector DIN de cinco pines. Los pines 4 y 5 se utilizan para conducir
los datos y el pin 2 para conectar el blindaje del cable a la toma de tierra. Los pines 1 y
3 no se utilizan actualmente, pero están reservados para futuros propósitos. El cable
trenzado y el blindaje metálico se utilizan para reducir las interferencias
electromagnéticas del exterior.
La especificación del cable MIDI obliga a una longitud máxima del cable de 15
metros para evitar la degradación de señal y la interferencia externa; aunque las
longitudes típicas son de 1,3 y 6 metros de largo. En la figura 4.6 pueden apreciarse los
conectores MIDI estándar.
En los sistemas MIDI se utilizan tres tipos de puerto:
84
C A P Í T U L O 4 • Introducción a MIDI
•
•
•
MIDI in: es la entrada de datos al dispositivo.
MIDI out: salida de datos hacia otro dispositivo.
MIDI thru: Puente de datos. Transmite una copia exacta de los datos que llegan
por MIDI in a otro instrumento o aparato MIDI que continúa la cadena de datos
MIDI conectada.
Figura 4.7: Puertos MIDI
Algunos dispositivos no tiene puerto thru, por lo que deben emularlo por software.
Sólo hay dos formas de conectar un sistema MIDI a otro. Son las siguientes:
•
•
Conectar el MIDI out al MIDI in del otro sistema.
Conectar el puerto MIDI thru de un sistema al puerto MIDI in del otro sistema.
Una forma típica de conectar dispositivos MIDI es un una red margarita o daisy
chain. Este método transmite MIDI de un aparato al siguiente puenteando los datos
recibidos por MIDI in de un aparato, directamente a otro aparato por MIDI thru, donde
la cadena continúa en el siguiente aparato. En la figura 4.8 se muestra un ejemplo de
esta disposición.
Figura 4.8: Conexión en margarita (daisy –chain)
Esta conexión permite al dispositivo 1 conectarse al dispositivo 2, que repite los
datos hacia el dispositivo 3 y así sucesivamente.
85
C A P Í T U L O 4 • Introducción a MIDI
Conexiones USB y FireWire:
Actualmente los sistemas MIDI pueden conectarse también a través de
conectores USB del tipo 1 y 2 y el protocolo IEEE (FireWare). Gracias a estos
dispositivos de alta velocidad es posible conectar, respetando el protocolo MIDI 1.0,
aparatos de diferentes compañías. La ventaja de estos conectores es la facilidad de
auto-detección del dispositivo, la rápida y cómoda conexión.
Conexiones de red mLAN:
Este innovador sistema fue creado por Yamaha y resolvía el problema de la
innecesaria parafernalia de cables para la conexión MIDI. Este sistema permite que el
audio multicanal y los datos sean transferidos a través del cable estándar IEEE FireWare
1394.
mLAN, soporta un total de 100 canales de datos de audio digital y hasta 256 puertos
MIDI, es decir, un total de 16 x 256 conexiones o canales en tiempo real. Además tiene
la posibilidad de comunicación fullduplex a velocidades de 100, 200 ó 400 Mbps.
Las ventajes principales de este sistema son la velocidad de conexión, el ancho de banda
para audio y canales MIDI, y la fácil conexión automática entre sistemas MIDI sin
necesidad de configuración.
Figura 4.9 Sistema mLAN
86
C A P Í T U L O 4 • Introducción a MIDI
4.1.3.2 Esquemáticos
En esta sección se ilustra los esquemáticos electrónicos para configurar las
conexiones IN, OUT y THRU a la circuitería MIDI basada en un integrado UART para
comunicación serie.
Figura 4.10 Esquemático MIDI
Como puede observarse en la figura 4.10, MIDI es un interfaz serie asíncrono.
La frecuencia de baudios es 31.25 Kbaudios (+/- 1%). Hay 1 bit de comienzo, 8 bits de
datos y 1 bit de parada (en total 10 bits), lo cual hace un periodo de 320 microsegundos
por byte serie.
La intensidad de corriente en el bucle del circuito MIDI es de 5mA. Para evitar
daños en el aparato en caso de una diferencia de tensión excesiva con el transmisor, la
entrada está optoaislada. Los optoacopladores Sharp PC-900 y HP6N138 garantizan una
respuesta binaria bien diferenciada para evitar errores de datos. Los flancos de subida y
bajada de los optoacopladores deben estar por debajo de 2 microsegundos.
La transmisión MIDI-thru podría no ser realizada correctamente debido al
tiempo de retardo (causado por el tiempo de respuesta del optoacoplador) entre los
flancos de subida y bajada de la onda cuadrada. Estos errores de temporización tenderán
a incrementarse de forma progresiva cuantos más dispositivos estén conectados en
estructura de margarita (daisy-chain) a otros conectores MIDI-thru. Por lo tanto, esto
limita el número de dispositivos que pueden encadenarse en esta distribución.
87
C A P Í T U L O 4 • Introducción a MIDI
4.2 Especificación MIDI
4.2.1 Introducción
De acuerdo a la especificación MIDI, los mensajes están compuestos de varios bytes
que se transmiten en serie y contienen un conjunto de instrucciones o de datos de
control a uno o a varios sistemas. Los dos bytes fundamentales del sistema MIDI son:
•
•
Byte de estado.
Byte de datos.
El byte de estado se utiliza para indicar el tipo de mensaje y el canal dentro del
sistema al que va dirigido. En los bytes de datos se codifican en binario los valores
numéricos que indican los parámetros para ese canal. Por ejemplo: la nota pulsada y la
velocidad de pulsación. Figura 4.11.
Para identificarlo, el MSB (bit más significativo) del byte de estado es siempre 1, en
contrapartida, el MSB del byte de datos siempre empieza en 0.
Byte de estado
Nº de estado/canal
(1001 0100)
(note on/canal 5)
Byte de datos 1
Nº de nota
(0100 0000)
(64)
Byte de datos 2
Velocidad de pulsación
(0101 1001)
(89)
Figura 4.11 Mensaje MIDI
4.2.2 Canales MIDI
Con el uso de canales MIDI, un mensaje generado por un sistema puede dirigirse
a otro sistema por medio de la identificación de canales. Esto se consigue utilizando
cuatro bits dentro del byte de estado. Como son cuatro bits en el campo de estado,
tenemos un total de 24 = 16 canales MIDI direccionables. Con el uso de canales
podemos asignar un instrumento a cada canal y enviarlo a través del sistema.
En la figura 4.12, puede verse esta situación representada dentro del círculo en rojo.
Tenemos dos dispositivos: el GPO Studio 2 y la tarjeta SB Creative Live, en los cuales
hemos asignados dos instrumentos de percusión sobre el mismo canal: Tambourine y
Splash Cymbal 2 que sonarán simultáneamente cuando reciban datos sobre el canal 10,
aunque estén en aparatos diferentes.
88
C A P Í T U L O 4 • Introducción a MIDI
Figura 4.12 Asignación a canales
Utilizando como ejemplo la figura 4.12 se podría crear una composición corta
utilizando un sintetizador como estación de trabajo conectado a un secuenciador (un
software capaz de grabar, editar y reproducir datos MIDI), un módulo de sonido y un
sampler. De esta manera es posible que el ordenador envíe mensajes MIDI al módulo de
sonido, al sintetizador o al sampler, controlando por medio de cada canal los
instrumentos en cada dispositivo antes mencionado.
89
C A P Í T U L O 4 • Introducción a MIDI
4.3 Mensajes MIDI
En el apéndice A1 puede consultarse la tabla resumida de los diferentes tipos de
mensajes MIDI. A continuación se detallan ampliamente cada uno de estos tipos.
4.3.1 Mensajes de canal
Son utilizados para transmitir datos de interpretación en tiempo real, puesto que son
generados cuando un instrumento MIDI es tocado en directo. Los mensajes de canal
contienen un número de canal en su byte de estado. A continuación se explican los siete
tipos de mensajes:
Note on: indica que una nota particular debería ser reproducida. Esencialmente,
significa que la nota comienza a sonar, aunque algunos patches podrían tener un
periodo de ataque VCA largo que necesite aumentar lentamente el sonido. En
cualquier caso, este mensaje indica que una nota particular comienza a sonar (al
menos que la velocidad de pulsación de la nota sea 0). Si el dipositivo el
multitímbrico, cada nota se reproduce en un canal diferente.
El rango del byte de estado abarca los valores hexadecimales 0x90 a 0x9F. El
primer byte de datos contiene el número de la nota. Hay 128 posibles notas en
un sistema MIDI, numeradas del 0 al 127. El valor 60 corresponde a la nota DO
central del teclado. El segundo byte de datos es la velocidad, también en el rango
[0,127]. Indica con qué fuerza se ha pulsado la tecla. Con frecuencia la
velocidad se usa para adaptar el tiempo de ataque del VCA.
Un mensaje MIDI con la velocidad 0 es considerado como un mensaje de Note
off.
Note off: indica que una nota en concreto ha sido finalizada. Esto significa que
la nota debe de parar la reproducción, aunque algunos patches podrían tener un
tiempo de liberación (release time) que necesite disminuir lentamente el sonido.
Algunas veces, el pedal de sostenimiento puede estar presionado, en ese caso la
liberación de la nota es pospuesta hasta que se levante el pedal. En cualquier
evento, este mensaje puede causar que el VCA se posicione dentro de la etapa de
liberación, o si el pedal está presionado, indica que una nota debería ser liberada
cuando el pedal de sostenimiento esté desactivado. En un dispositivo
multitímbrico cada note off debería activarse por cada canal.
El mensaje contiene un byte de estado con el rango de valores: 0x80 a 0x8F,
mientras que el primer byte de datos contiene la tecla a liberar y el segundo byte
la velocidad en la que se ha dejado levantar la tecla.
90
Aftertouch: cuando una determinada nota está reproduciéndose puede serle
aplicada una presión. Muchos instrumentos MIDI tiene electrónica sensible a la
presión que pueden detectar cómo de fuerte presiona un músico la tecla. El
músico puede variar esta presión, incluso cuando continúa presionando la tecla.
El mensaje de aftertouch expresa la cantidad de presión en un determinado
punto de una tecla. Puesto que el músico pude variar continuamente la presión,
los dispositivos que generan aftertouch normalmente envían varios mensajes
C A P Í T U L O 4 • Introducción a MIDI
durante el proceso. Una vez recibido el mensaje, muchos dispositivos lo usan
para variar el VCA de la nota y/o el nivel de sostenido de la envolvente VCF, o
la cantidad a ser aplicada al control LFO.
El byte de estado está comprendido en el rango de valores: 0xA0 a 0xAF.
El primer byte de datos es el número de nota a aplicar la presión. El segundo
byte de datos es la cantidad de presión, en un rango discreto de [0,127] valores.
Channel preassure: este mensaje expresa la cantidad general (media) de
presión en las teclas en un determinado punto. Al igual que en el mensaje de
aftertouch, los músicos pueden variar continuamente la presión, generándose
múltiples mensajes de este tipo. El comportamiento al recibir el mensaje de este
efecto es el mismo que en el aftertouch. Pero, ¿cuál es la diferencia entre el
mensaje de aftertouch y channel presassure? La diferencia principal recae en
que el mensaje aftertouch es para teclas individuales, es decir, un mensaje
aftertouch solamente afecta a la nota cuyo número está en el mensaje. Cada tecla
que se presiona genera su propio mensaje aftertouch. Si presionamos una tecla
más fuerte que otra, esa tecla sola generará mensajes aftertouch con valores más
altos que la otra tecla. El resultado neto es que algunos efectos se aplicarán a una
tecla más que a otra. En contrapartida, el mensaje channel preassure es enviado
para el teclado entero. Así, si presionamos una tecla más fuerte que otra, el
sintetizador promediará la diferencia entre las dos presiones, y luego
simplemente produce el efecto de presionar las dos teclas con la misma presión
exacta. Un sistema MIDI usa normalmente uno de los dos efectos
separadamente.
Muchos controladores MIDI no generan aftertouch ya que requiere un sensor de
presión individual por cada tecla, y esto encarece el diseño y el producto. Por
esta razón, algunos dispositivos sólo implementan el mensaje de channel
preassure.
Program change: se utiliza para cambiar un programa determinado1. La
mayoría de los módulos de sonido tienen una gran variedad de instrumentos.
Cada uno de estos instrumentos está contenido en un programa. Así, cambiando
el programa cambia el instrumento que suena cuando una nota es presionada.
Obviamente, los mensajes MIDI pueden cambiar el programa. En MIDI hay un
total de 128 posibles programas, numerados en el rango [0,127] (véase apéndice
A3). Si el dispositivo es multitímbrico, normalmente puede reproducir 16 partes
a la vez en cada canal.
Existen algunos sistemas MIDI que no tienen instrumentos, por ejemplo, una
unidad de Reverb. En este caso se utiliza para seleccionar el Preset a utilizar.
Otro ejemplo sería el de una caja de ritmos, en el que el mensaje se utilizaría
para seleccionar un determinado patrón rítmico.
En este mensaje el byte de estado comprende los valores del 0xC0 al 0xCF,
mientas que sólo transporta un byte de datos, que contiene el programa a
establecer.
1
Alguna terminología se refiere al programa como patch, intrumento, preset, etc.
91
C A P Í T U L O 4 • Introducción a MIDI
Control change: establece un valor de controlador concreto. Un controlador es
cualquier botón, slider, switch etc. que normalmente implementa alguna función
más que reproducir o detener notas. Hay un total de 128 posibles controladores
en un dispositivo MIDI y están numerados en el rango discreto [0,127] (véase
apéndice A2). Alguno de estos números de controlador están asignados a un
control hardware particular en el dispositivo MIDI. Por ejemplo, el controlador 1
es asignado a la rueda de modulación o mudulation wheel. Otros números de
controlador son libremente asignados para ser arbitrariamente interpretados por
el sistema. Por ejemplo, una caja de ritmos podría tener un control slider para
manejar el Tempo el cual asigna libremente valores arbitrarios. Cuando la caja
de ritmos recibe un mensaje de controlador con ese número de controlador ya
puede ajustar el Tempo. Hay que recalcar en este apartado que un dispositivo
MIDI no necesita el controlador físico para responder a los mensajes de
controladores.
El byte de estado del mensaje de cambio de control cae en el rango: 0xB0 a
0xBF, y el primer byte de datos es el número de controlador en el rango [0,127].
El segundo byte especifica el valor a establecer para el controlador determinado.
Pitch Wheel: denominado también rueda de modulación. Este mensaje es usado
para desplazar la altura tonal de un nota. La rueda de modulación es medida por
el décimo cuarto bit. El valor central es 0x2000.
Respecto al byte de estado, está dentro del rango 0xE0 a 0xEF, mientras que los
dos byte de datos deberían combinarse para formar un valor de 14 bits. Los bits
0 a 6 del segundo byte son realmente los bits del 7 al 13 del valor de 14 bits. De
esta forma tendríamos el siguiente procedimiento en C++ para realizar la
combinación:
unsigned short CombineBytes(unsigned char First, unsigned char
Second)
{
unsigned short _14bit;
_14bit = (unsigned short)Second;
_14bit <<= 7;
_14bit |= (unsigned short)First;
return(_14bit);
}
92
C A P Í T U L O 4 • Introducción a MIDI
4.3.2 Mensajes de sistema
Estos mensajes son transmitidos globalmente a cada dispositivo dentro de la red
MIDI, puesto que no se utiliza en ellos el número de canal. Cualquier dispositivo
responderá a los mensajes del sistema independientemente del canal. A continuación se
enumeran los diferentes mensajes de sistema MIDI:
System exclusive: sirve para comunicar con el dispositivo información
independiente del protocolo MIDI. En la sección siguiente (4.3.3) se examinará
este mensaje con más detenimiento.
Song Position Pointer: algunos dispositivo maestros que controlan la secuencia
de reproducción envían este mensaje para forzar a un dispositivo esclavo a poner
en la cola una cierta posición de la secuencia. Este mensaje no comienza
inmediatamente la reproducción, simplemente pone al dispositivo preparado
para la reproducción en una particular posición en la secuencia.
El byte de estado tiene el valor 0xF2 y los bytes de datos, al igual que en el caso
de la rueda de modulación, tienen que ser combinados en un valor de 14 bits. El
valor de 14 bits es el MIDI Beat en el que comenzar la secuencia. Se asume que
la secuencia comienza en el MIDI Beat 0. Cada MIDI Beat utiliza 6 pulsos
MIDI. Es decir, cada MIDI Beat es un deciseisavo de nota.
Ejemplo: Si el valor del Song Position Pointer es 8, el secuenciador debería
poner en la cola al tercer cuarto de nota de la secuencia:
8 MIDI Beats x 6 pulsos de reloj MIDI por Beat = 48 pulsos MIDI
ya que hay 24 pulsos en ¼ de nota.
Song select: los dispositivos maestros MIDI envían este mensaje para comenzar
el “playback” de una canción.
El byte de estado es 0xF3, y el byte de datos es una valor comprendido entre 0 y
127 que indica el número de secuencia o canción.
Tune request: mensaje que también se traduce como petición de tono. Este
mensaje se utiliza para solicitar al dispositivo un cambio de calibración tonal.
Con frecuencia se implementa en módulos de sonido con circuitos osciladores
analógicos.
El byte de estado es el valor 0xF6 y no contiene byte de datos.
End of system exclusive: indica el final de un mensaje de sistema exclusivo.
93
C A P Í T U L O 4 • Introducción a MIDI
4.3.3 Mensajes de sistema exclusivo
Estos mensajes permiten a los fabricantes y programadores de sistemas MIDI
comunicar información específica para la configuración del dispositivo.
El principal uso del mensaje es para enviar una gran cantidad de datos en formato no
estructurado MIDI, como volcados de la memoria de patches, datos del secuenciador o
datos de formas de onda. Por ejemplo, un mensaje SysEx podría se utilizado para
establecer el nivel de retroalimentación para un operador en un sintetizador de
modelado físico de Roland .
El formato de transmisión de un mensaje de sistema exclusivo (figura 4.13),
como se define en el estándar MIDI, incluye un byte de estado (arranque) de sistema
exclusivo (0xF0) y un byte de parada EOX (0xF7).
0xF0 0x43 0x25 ... ... ... ... ... ... 0xF7
Figura 4.13 Estructura de mensaje de sistema exclusivo
El byte más importante después del 0xF0 (SOX) debería ser el identificador de
fabricante, así, por ejemplo para la marca Korg tendríamos el byte 0x42 y para la
empresa Kurzweil el byte 0x07.
A continuación se muestran algunos ejemplos de utilización de los mensajes de
sistema exclusivo:
•
•
•
•
Transmisión de datos de sonido (patches) entre sintetizadores con tabla de
ondas.
Copia de seguridad de los sonidos actuales almacenados en la EPROM del
sintetizador. Este proceso se lleva a cabo haciendo un volcado (dump) de los
datos del sintetizador a un ordenador para ser almacenados en un disco u otro
tipo de memoria secundaria.
Obtención de sonidos de la Web. Internet permite acceder a un gran banco de
muestras de sonido (patches) de sistema exclusivo. Tan sólo es necesario
descargar el fichero en formato .syx compatible con el modelo de sintetizador y
proceder a su envío desde el ordenador a través de un puerto MIDI.
Control y edición MIDI en tiempo real basada en sistema exclusivo. Los editores
de sonido proporcionados por los fabricantes del dispositivo permiten alterar y
enviar cambios de los parámetros digitales a la configuración del sistema. Así,
es posible cambiar las variables del sonido, la secuenciación, los osciladores,
etc. En la imagen 4.14 puede verse un editor de sonidos para el sintetizador
Korg X5D.
Es necesario recalcar que los datos de sistema exclusivo tomados de la red o de
disco suelen codificarse utilizando varios estilos de formato de archivo de sistema
exclusivo (no hay por lo tanto estandarización). Debido a esto, los volcados de
memoria se codifican utilizando utilidades software de sistema exclusivo estándar,
fácilmente disponibles para Mac o PC (figura 4.14).
También es posible encontrar datos de sistema exclusivo en ficheros MIDI estándar.
Este sistema almacena los datos en una pista o track del archivo MIDI.
94
C A P Í T U L O 4 • Introducción a MIDI
Figura 4.14 Editor de sonidos para envío por sistema exclusivo
4.3.4 Mensajes de modo
Este tipo de mensajes se utilizan para enviar al dispositivo receptor parámetros de
configuración MIDI para recibir los mensajes de canal. Como se verá a continuación, el
modo 3 es el más utilizado, ya que es el más potente, en detrimento del uso actual de los
otros modos.
•
•
•
•
Modo 1 – omni on/poly: Es el más simple de todos. Con este mensaje si un
dispositivo envía un mensaje (note on) por el canal 7 y otro por el canal 10, el
receptor en modo Omni ignorará los canales por los que lleguen, reproduciendo
las dos notas a la vez. Omni on indica que no se consideran los canales y Poly
que puede reproducir más de una nota a la vez.
Modo 2 – omni on/mono: Es idéntico al modo 1, excepto que sólo permite una
nota a la misma vez.
Modo 3 – omni off/poly: Es el modo más potente de los cuatro y el más
comúnmente usado. Omni off indica que sí considera los canales en que recibe,
por lo tanto, selecciona lo que reproduce. Poly indica que es posible reproducir
más de una nota a la vez.
Modo 4 – omni off/mono: Este modo es básicamente una versión monofónica del
modo 3. Esta variante sólo permite una nota a la vez en cada canal.
95
C A P Í T U L O 4 • Introducción a MIDI
4.3.5 Mensajes de tiempo real
Los mensajes de tiempo real, que consisten en un solo byte de estado, están
comprendidos en el rango [0xF8,0xFF]. Estos mensajes están principalmente
relacionados con la temporización y sincronización. MIDI permite que los mensajes de
tiempo real sean enviados en cualquier momento, incluso intercalados con otros
mensajes MIDI. Por ejemplo, un mensaje en tiempo real podría ser enviado entre los
dos bytes de datos de un mensaje note on. Un dispositivo debería siempre estar
preparado para manejar cada situación; procesando el byte de tiempo real, y
subsiguientemente continuar procesando el mensaje anteriormente interrumpido.
A continuación se enumeran los mensajes de tiempo real utilizados en MIDI:
MIDI Clock: algunos dispositivos maestros que controlan la reproducción de la
secuencia envían este mensaje para mantener al dispositivo esclavo en sincronía
con el maestro. Un mensaje de tiempo MIDI se envía a intervalos regulares
(basados en el Tempo del maestro) para realizar esta tarea.
El mensaje de reloj de tiempo contiene el byte de estado 0xF8, mientras que no
contiene ningún byte de datos.
En la sección 4.4 comentaremos con más detalle los aspectos relacionados con el
reloj y la sincronización MIDI.
MIDI Tick: como en el caso anterior, algunos dispositivos maestros envían este
mensaje para mantener las sincronía con el receptor. Este mensaje es enviado a
intervalos regulares de uno cada 10 ms.
El byte de estado contiene el valor 0xF9 y no tiene datos.
MIDI Start: indica al dispositivo esclavo que debe comenzar la reproducción de
una secuencia o una canción. Comienza siempre en el MIDI Beat 0.
El byte de estado es 0xFA y no contiene datos.
MIDI Stop: es el opuesto al anterior. Indica al dispositivo esclavo que detenga
la secuencia o la canción.
El byte de estado es 0xFC y no tiene datos.
MIDI Continue: para continuar la reproducción de la secuencia o canción por
donde se había detenido anteriormente, o puesto en cola con el mensaje Song
Position Pointer.
El byte de estado es 0xFB y no contiene datos.
Reset: Reinicia el dispositivo a la configuración por defecto como si se hubiera
encendido de nuevo.
El byte de estado es 0xFF y no tiene bytes de datos.
96
C A P Í T U L O 4 • Introducción a MIDI
Active Sense: Este mensaje se envía cada 300 ms si no ha habido actividad en el
bus MIDI. Esto permite indicar a los dispositivos que hay una buena conexión
entre ellos.
El byte de estado es 0xFE y tampoco contiene datos.
En la figura 4.15 puede verse el algoritmo del mensaje Active Sense. Se asume
que los dispositivos tienen un timer hardware que se incrementa cada
milisegundo. Una variable llamada timeout se utiliza para incrementar los
milisegundos transcurridos. Otra variable llamada flag se activa cuando el
dispositivo recibe un mensaje homónimo de otros dispositivo, y
consecuentemente espera a recibir más mensajes Active Sense de ese
dispositivo.
In t e r r u p c ió n
d e l t im e r
Interru pció n
d e recepció n
M ID I
F la g = 0
?
R ecib e
d atos
F la g = 1
In c r e m e n t a
t im e o u t
Inicia la
variable d e
tim eou t
S A L ID A
t im e o u t < = 3 0 0 m s
no 0xF E
?
?
t im e o u t > 3 0 0 m s
0xFE
Establece
el flag
S AL ID A
a algu na o tra
o peración
D e s a c t iv a
t o d a s la s
n o ta s
S A L ID A
In ic ia e l
f la g
S A L ID A
Figura 4.15 Algoritmo Active Sense
97
C A P Í T U L O 4 • Introducción a MIDI
4.4 Sincronización y tiempo MIDI
Hay 24 pulsos MIDI en cada cuarto de nota (un cuarto de nota equivale a una
negra), 12 pulsos MIDI en un octavo de nota, 6 pulsos en un dieciseisavo, etc. Por lo
tanto, cuando un dispositivo esclavo cuenta 24 pulsos de reloj MIDI, sabe que ha
transcurrido ¼ de nota. Obviamente, la frecuencia a la que el reloj maestro se
incrementa depende del Tempo. Por ejemplo, para un tempo de 120 BPM, es decir, 120
cuartos de nota en cada minuto, el maestro envía un pulso cada 20833 microsegundos.
Sabiendo que hay 106 microsegundos en un segundo y que un minuto contiene 60 x 106
microsegundos. En un tempo de 120 BPM, hay 120 cuartos de nota por minuto. Como
hay 24 pulsos MIDI en un cuarto de nota, en consecuencia, debería haber 24 x 120
pulsos MIDI por minuto. Por lo tanto, cada pulso MIDI es enviado a una frecuencia de
60 x 106 / (24 x 120) microsegundos.
El código de tiempo MIDI o MIDI time code (MTC) incorpora el formato de
tiempo absoluto: horas, minutos, segundos y cuadros, al flujo de datos MIDI. Es uno de
los más recientes códigos de tiempo utilizados en MIDI.
Con el mensaje Song Position Pointer se pueden sincronizar las secuencias o canciones
entre dispositivos MIDI con suficiente exactitud, aunque surgen problemas cuando hay
que sincronizarlos, por ejemplo, con grabadoras de vídeo o procesadores de sonido. El
código de tiempo utilizado internacionalmente para estos aparatos es el SMPTE que es
el acrónimo de Society of Motion Pictures & Television Engineers y utiliza, al igual que
MTC el tiempo absoluto. El formato SMPTE es muy eficaz y permite sincronizar con
mucha eficacia aparatos muy heterogéneos. Es posible convertir el formato MTC a
SMPTE y al contrario.
Figura 4.16 Secuenciador MIDI Rosegarden
98
C A P Í T U L O 4 • Introducción a MIDI
Bibliografía y referencias
Libros
Técnicas de Grabación modernas – David Miles Huber, Robert E. Runstein, 6ª edición.
Editorial Omega, 2007.
Diseño y desarrollo Multimedia, Sistemas, Imagen, Sonido y Vídeo – Manuel-Alonso
Castro Gil, Antonio Colmenar Santos, Pablo Losada de Dios, Juan Peire Arroba.
Editorial Ra-ma, 2002.
La Web
MIDI Manufacturers Association:
http://www.midi.org/
MIDI specification:
http://home.roadrunner.com/~jgglatt/
Wikipedia:
http://es.wikipedia.org/wiki/MIDI
Lista de figuras obtenidas de Internet
http://www.amazona.de/media/articles/article_images/article_697/1_prop
het600.jpg
Figura 4.2 http://www.nopianonoproblem.com/files/other/MIDI_SetupOptions_Larg
e.jpg
Figura 4.3 http://www.etcetera.co.uk/products/images/EMU300big.jpg
Figura 4.4 http://www.generalmanual.com/img/0902/roland-jv-2080-synthesizermodule.jpg
Figura 4.8 http://www.fortunecity.com/emachines/e11/86/graphics/midi/MIDI5.gif
Figura 4.9 http://www.savedbytechnology.com/2003/yamaha_my16-mlan.jpg
Figura 4.14 Software del sintetizador Korg X5D
http://www.les-stooges.org/pascal/midiswing/index.php
Portada
Figura 4.1
99
C A P Í T U L O 5 • Programación MIDI
5
Programación MIDI
5.1 Introducción
Una vez examinados los conceptos básicos sobre la tecnología MIDI es momento de
programar el sistema para modificar en tiempo real eventos y mensajes. De esta forma,
es posible realizar aplicaciones que se ajusten a las demandas actuales de edición de
música, así como programas de control de dispositivos a través de mensajes MIDI.
La programación MIDI se ha vuelto fundamental en la programación de sistemas, pues
se utiliza en un amplio rango de aplicaciones, desde investigación hasta videojuegos.
Actualmente se hace necesaria alguna herramienta que permita a los ingenieros y
programadores desarrollar software musical y de acceso a las funciones básicas de los
dispositivos MIDI. De esta manera, se consigue una abstracción del software de bajo
nivel, proveyendo una arquitectura orientada a objetos y estratificada que permita el
diseño de software robusto y complejo compuesto por una gran cantidad de
componentes.
En los principios de los años 90, desarrollar una aplicación de acceso a MIDI sobre
una plataforma de 16 bits como era Atari (muy utilizado en la industria en aquella
época), suponía desarrollar una aplicación basada en una ingeniería de software no
orientada a objetos, pero estructurada y modular, implementada en un lenguaje como C
donde el acceso al hardware MIDI del computador se hacía a través de funciones de
envió de bytes a puertos, y en su extremo, programando directamente en ensamblador.
Posteriormente, con la aparición de los sistemas operativos modernos de 16 y 32 bits
como Windows, Linux y Mac/OS, han provisto una API (Interfaz de Programación de
Aplicaciones) al servicio de los programadores que, además de abstraer la complejidad
del acceso al hardware (manejado por las capas de E/S y la máquina virtual del sistema
operativo) proporciona un conjunto de servicios para acceder a todas las funciones que
MIDI posee. La ventaja de este enfoque moderno permite desarrollar software MIDI
que funcionará con cualquier fabricante de hardware sin la necesidad de modificar el
código fuente del programa.
Actualmente en Windows existe la posibilidad de desarrollar aplicaciones MIDI
utilizando dos APIs diferentes: la Windows Multmedia API y la API DirectMusic de
DirectX. En este estudio nos centraremos en esta última.
Otras librerías MIDI de alto nivel profesionales para C++ existentes en Internet son:
•
•
•
•
CLAM: C++ library for audio music. Desarrollada en la Universidad Pompeu
Fabra de Barcelona por el Grupo de Tecnología Musical.
The Synthesis Toolkit in C++: Desarrollado por la Universidad de Standford.
Maximum MIDI music applications in C++. Libro sobre MIDI que contiene una
librería sobre la API Windows Multimedia.
DirectMIDI: Librería de software libre que se explicará en este capítulo.
101
C A P Í T U L O 5 • Programación MIDI
5.2 La API DirectMusic de DirectX
Como se explicó en el capítulo 2, DirectMusic es un componente más de la librería
de alto rendimiento para multimedia DirectX, junto con DirectSound y DirectShow.
DirectMusic permite que los efectos de sonido y la música sean compuestos y
reproducidos con un control flexibe e interactivo.
Arquitectónicamente, DirectMusic es un conjunto de objetos de alto nivel, construidos
sobre DirectSound que permite la reproducción de sonido y música sin la necesidad de
utilizar las funciones de bajo nivel de DirectSound. DirectMusic trabaja con datos de
música basados en mensajes. La música puede ser sintetizada vía hardware directamente
con los puertos que proporciona el fabricante o con el Sintetizador software de
Microsoft.
La historia de la API de DirectMusic se remonta al año 1996 cuando fue lanzado por
primera vez como un componente ActiveX, llamado Iteractive Music Architecture
(IMA). Fue introducido inicialmente como parte de la versión 6.1 de DirectX en febrero
de 1999 e incluido en todos los sistemas operativos Windows 98 segunda edición.
5.2.1 Características principales
DirectMusic provee un sistema completo para la implementación de soundtracks
(pistas) utilizando las ventajas de la aceleración hardware, sonidos descargables
(downloadable sounds) y los Objetos Media DirectX (DMOs), posicionamiento
avanzado de efectos en 3D, entre otras muchas más características.
En DirectMusic, la música es generada en tiempo real y no de forma estática,
proporcionado funcionalidades para la reproducción con variaciones y responder a
eventos flexibles de programas vía MIDI. Entre las características avanzadas caben
destacar:
•
•
•
•
•
•
•
•
102
Carga y reproducción de sonidos desde ficheros o recursos MIDI, WAV y
ficheros propietarios.
Monitorización del tiempo de los eventos musicales con alta precisión. Permite
además la captura del tiempo de los datos MIDI en el momento de su recepción
utilizando un reloj de referencia del sistema de alta resolución.
Permite que la música y los efectos sean rápidos y dinámicamente cambiados en
tiempo real en respuesta a eventos del usuario. En este aspecto, resuelve los
problemas de rendimiento de la API Multimedia básica de Windows(función
midiout).
Reproduce múltiples fuentes de sonido a la vez.
Envía eventos de tempo, cambios de programa (patch) y otros eventos MIDI de
forma programada.
Utilización de los sonidos descargables (Downloadable sounds), un estándar de
La Asociación de Fabricantes MIDI (MMA), permitiendo a los desarrolladores
la salida de datos de audio de tabla de ondas sobre hardware de audio no
equipado con síntesis de tabla de ondas.
En los ordenadores sin un hardware sintetizador de tabla de ondas, el
Sintetizador Software de Microsoft permite emular un hardware de tabla de
ondas, consiguiendo que las aplicaciones tengan un resultado homogéneo en
todos los sistemas.
Posicionamiento del sonido en un espacio tridimensional.
C A P Í T U L O 5 • Programación MIDI
•
•
•
•
Posibilidad de aplicar efectos de sonidos como Chorus, Reverb, Delay, Flanger,
Echo, Distortion, Gargle, etc.
Uso de más de 16 canales MIDI. Por medio de la utilización de los objetos
performance es posible reproducir hasta 216 ó 65536 canales MIDI.
Con la utilización de los audiopaths los efectos de espacialización pueden ser
aplicados individualmente a cada sonido.
Captura de datos MIDI, permitiendo la redirección del flujo de un puerto de
entrada a otro de salida.
5.2.2 Principales interfaces COM
Como pudimos ver en el capítulo 2, DirectX está basado en tecnología COM
(Modelo de objetos componentes) que se describió detalladamente en la sección 2.1.6.
DirectMusic está compuesto de varias interfaces COM que abstraen la funcionalidad de
las aplicaciones musicales MIDI. Las principales interfaces son las siguientes:
•
IDirectMusic8: provee métodos para manejar buffers, puertos y el reloj
maestro. Sólo debería haber una instancia de esta interfaz por aplicación.
•
IReferenceClock: esta interfaz estándar proporciona acceso al reloj maestro que
es un timer hardware en modo kernel con una alta resolución y es usado para
sincronizar todo el playback de audio en el sistema. El método
IReferenceClock::GetTime devuelve el tiempo como un entero de 64 bits
(definido como un tipo REFERENCE_TIME) en incrementos de 100ηs o
nanosegundos.
•
IDirectMusicPort8: proporciona acceso al objeto DirectMusicPort, que
representa un dispositivo que recibe y envía datos MIDI, por ejemplo, el puerto
de entrada de un MPU-401, el puerto de salida de un MPU-401 o el sintetizador
software de Microsoft.
•
IDirectMusicThru8: permite la redirección de mensajes de un puerto de
entrada a otros puertos de salida. El método IDirectMusicThru8::ThruChannel
es utilizado para establecer o romper la conexión thru desde un canal de entrada
en un puerto MIDI a otro canal de salida de otro puerto MIDI.
•
IDirectMusicBuffer8: representa un buffer que contiene datos (normalmente
en forma de mensajes MIDI) para ser secuenciados por un puerto. El buffer
contiene una pequeña cantidad de datos(normalmente menos de 200ms). Este
buffer es creado con al menos 32 bytes de datos MIDI estándar.
•
IDirectMusicLoader8: se usa para la carga de objetos DirectMusic tales como
segmentos, ficheros MIDI, ficheros WAV y DLS.
•
IDirectMusicCollection8: maneja un conjunto de instrumentos de un fichero
DLS y contiene métodos para descargarlos a un puerto de un sintetizador.
103
C A P Í T U L O 5 • Programación MIDI
•
IDirectMusicIntrument8: esta interfaz representa un instrumento individual de
una colección DLS que es descargado posteriormente a un sintetizador usando el
método IDirectMusicPort8::DownloadInstrument.
•
IDirectMusicDownloadedInstrument8: es usada para identificar un
instrumento descargado en un sintetizador. El puntero a la interfaz es usado más
tarde para liberar el instrumento de la memoria del sintetizador a través de una
llamada al método IDirectMusicPort8::UnloadInstrument.
•
IDirectMusicPortDownload8: permite a una aplicación comunicarse
directamente con un puerto que soporte descarga DLS para descargar bloques de
memoria directamente al puerto.
•
IDirectMusicDownload8: representa un bloque contiguo de memoria utilizado
para la descarga a un puerto DLS.
5.3 Desarrollando aplicaciones con la librería DirectMIDI
5.3.1 Introducción
La librería DirectMIDI es una colección de clases C++ basadas en DirectMusic con
la intención de mejorar el desarrollo software bajo la tecnología de audio y MIDI. La
capa software está diseñada con una precisa orientación a objetos para facilitar la
construcción de aplicaciones e integración en la arquitectura. El framework
proporciona, además, un sistema de prevención de errores y su facilidad de uso hace que
DirectMIDI sea la herramienta ideal para desarrolladores que están buscando una
librería de audio y MIDI estable, amigable, segura y en el estado del arte.
El proyecto se encuentra en fase beta aunque hay una versión estable desde la 2.3.
Desde la versión 2.1 se considera además software libre, liberada bajo los términos de
GNU(General Public License) y publicada como artículo en la revista electrónica sobre
programación CodeProject.
El diseñador y programador de la librería es el autor de este proyecto, que dedicó dos
años a su desarrollo y evaluación íntegros.
Actualmente se puede acceder a los ficheros fuente desde el repositorio de software
libre de SourceForge, en la URL: http://directmidi.sourceforge.net
104
C A P Í T U L O 5 • Programación MIDI
Figura 5.1 Página principal de la librería DirectMIDI
Figura 5.2 Aplicación de muestra de la librería DirectMIDI
105
C A P Í T U L O 5 • Programación MIDI
5.3.2 Esquema de la arquitectura DirectMIDI
El núcleo principal de la librería está basado en sus diez clases relacionadas que
definen los diferentes objetos involucrados en una aplicación basada en MIDI y que
encapsulan el código para llevarlo a cabo.
El siguiente diagrama de colaboración muestra los objetos creados por una aplicación
que usa DirectMIDI:
Exceptions
CMasterClock
CInputPort
CReceiver
CDirectMusic
CDLSLoader
CCollection1..i
COutputPort
CInstrument1..j
Initialize
DownLoad / UnLoad
DirectMusic
main
objects
MIDI ports
External MIDI - Synthesizer with
SysEx Events DLS support
CDMusicException
CSampleInstrument
1..n
Exception
handling
Figura 5.3 Diagrama de colaboración de objetos
Como podemos observar, hay un objeto del tipo CDirectMusic que encapsula la
inicialización COM de una aplicación Win32. Este objeto es el responsable de iniciar
los objetos que representan los puertos MIDI que se dividen en dos categorías: puertos
de entrada para mensajes de entrada MIDI tales como mensajes de sistema exclusivo o
mensajes MIDI 1.0 y puerto de salida MIDI para enviar mensajes en formato sistema
exclusivo o MIDI 1.0. Hay un objeto adicional llamado CMasterClock que proporciona
una enumeración y selección de un timer hardware como reloj maestro.
Hay otros tres objetos relacionados con el objeto COutputPort directa e indirectamente,
este es el caso del objeto CDLSLoader que es el responsable de cargar ficheros DLS
para almacenarlos en un objeto CCollection. Este objeto representa un conjunto de
instrumentos en formato DLS 1.0 ó 2.0 y permite extraer sus instrumentos a mejores
contenedores para ellos, llamados objetos CInstrument. Estos son los responsables de
mantener una instancia de un objeto particular para un mejor control y organización.
Una vez se hayan seleccionado todos los instrumentos de la colecciones, se puede
proceder a descargarlos a un programa MIDI específico en un sintetizador para
reproducirlos.
Además del objeto CInstrument, hay otro objeto similar proporcionado por la librería
DirectMIDI que permite almacenar formas de onda en formato WAV o en formato
PCM generadas de forma programática. Este objeto, llamado CSampleInstrument,
106
C A P Í T U L O 5 • Programación MIDI
proporciona funciones de ayuda para ajustar envolventes, LFOs (Osciladores de baja
frecuencia) y regiones (zonas activas del teclado) antes de descargarlos al puerto de
salida.
Finalmente, el objeto CDMusicException maneja todas las excepciones producidas en la
aplicación y muestra información detallada sobre el problema que generó el error.
5.3.3 Comenzando la aplicación
5.3.3.1 Primer paso: Configurar el entorno de desarrollo
Es posible comenzar la aplicación en muchos tipos diferentes de proyectos con
Visual Studio y la librería DirectMIDI, tales como una aplicación MFC, Win32 básica o
Win32 en modo consola1.
Para poder compilar una aplicación DirectMIDI necesitamos añadir al proyecto los
siguientes ficheros cabecera: CDirectMidi.h, CDirectBase.h y CMidiPart.h. Por último
es necesario añadir también al proyecto todos los ficheros .cpp que aparecen dentro del
directorio directmidi.
También se requiere tener instalada una versión actualizada del SDK de DirectX, en
este caso se ha utilizado la versión 9.0c. Finalmente necesitamos añadir al IDE de
Visual C++ las rutas de las dependencias (.lib) y ficheros cabecera (.h) del SDK de
DirectX, que normalmente se encuentran localizados en los directorios lib que incluye
el paquete de instalación.
5.3.3.2 Segundo paso: Las primeras líneas de código
El compilador debería saber cual es el código externo que se está utilizando en el
fichero .cpp actual de trabajo. Por ello se debe utilizar la directiva #include para indicar
al compilador qué otros recursos fuente son necesarios para la compilación. Las
cabeceras (includes) requeridas se muestran en el siguiente código:
// Cabeceras ANSI I/0
#include <conio.h>
#include <iostream>
#include <math.h>
// La librería DirectMIDI
#include ".\\DirectMidi\\CDirectMidi.h"
// Inclusión de
#pragma comment
#pragma comment
#pragma comment
#pragma comment
la librerías necesaria en línea
(lib,"dxguid.lib") // Definiciones GUID
(lib,"winmm.lib")
(lib,"dsound.lib")
(lib,"dxerr9.lib")
using namespace std;
using namespace directmidi;
// Tamaño máximo para los datos de System Exclusivo
1
Preferiblemente se utilizará el IDE Microsoft Visual C++ .NET 7.1.
107
C A P Í T U L O 5 • Programación MIDI
const int SYSTEM_EXCLUSIVE_MEM = 48000;
// Define PI
const double PI = 3.1415926;
La directiva #pragma comment indica al linkador que cree un fichero obj con las
librerías necesarias. Las últimas líneas de código indicadas anteriormente contienen
constantes simbólicas necesarias para el ejemplo.
5.3.3.3 Tercer paso: preparando la captura de música
La clase CInputPort es la responsable de manejar eventos de entrada MIDI. Estos
eventos MIDI son capturados por un thread que invoca a dos métodos virtuales puros
sobrecargados de la clase CReceiver; dependiendo del tipo de dato MIDI llegado al
puerto. Estos dos diferentes tipos de datos pueden ser: datos MIDI no estructurados (en
forma de Sistema Exclusivo) y datos MIDI estructurados (mensajes MIDI Standard
1.0).
Para sobrecargar estas funciones virtuales puras, es necesario derivar una clase de
CReceiver como se muestra en el siguiente fragmento de código:
// Clase heredada de CReceiver
class CDMReceiver:public CReceiver
{
public:
// Funciones sobrecargadas
void RecvMidiMsg(REFERENCE_TIME rt,DWORD dwChannel,DWORD
dwBytesRead,BYTE *lpBuffer);
void RecvMidiMsg(REFERENCE_TIME rt,DWORD dwChannel,DWORD dwMsg);
};
Una vez realizada esta operación, podemos implementar parte del código de
procesamiento de eventos:
// Función sobrecargada para la captura de datos en Sistema Exclusivo
void CDMReceiver::RecvMidiMsg(REFERENCE_TIME lprt,DWORD
dwChannel,DWORD dwBytesRead,BYTE *lpBuffer)
{
DWORD dwBytecount;
// Imprime el buffer recibido
for (dwBytecount = 0;dwBytecount < dwBytesRead;dwBytecount++)
{
cout.width(2);
cout.precision(2);
cout.fill('0');
cout << hex <<static_cast<int>(lpBuffer[dwBytecount])<< "
";
if ((dwBytecount % 20) == 0) cout << endl;
if (lpBuffer[dwBytecount] == END_SYS_EX)
cout << "\nMemoria del sistema volcada" << endl;
108
C A P Í T U L O 5 • Programación MIDI
}
}
// Función sobrecargada para la captura de datos MIDI estructurados
void CDMReceiver::RecvMidiMsg(REFERENCE_TIME lprt,DWORD
dwChannel,DWORD dwMsg)
{
unsigned char Command,Channel,Note,Velocity;
// Extrae parámetros MIDI del mensaje
CInputPort::DecodeMidiMsg(dwMsg,&Command,&Channel,&Note,&Velocit
y);
if (Command == NOTE_ON) //Channel #0 Note-On
{
cout << " Received on channel " <<
static_cast<int>(Channel) <<
" Note " << static_cast<int>(Note) << " with velocity " <<
static_cast<int>(Velocity) << endl;
}
}
La primera función lee los datos del buffer en formato Sistema Exclusivo, imprime
los valores en base numérica hexadecimal y detecta cuándo el sintetizador alcanza el
final del volcado de datos (el byte 0xF7 de fin de datos exclusivos). Es importante saber
que no todos los datos SysEx son recibidos en una única llamada RecvMidiMsg, se
pueden producir múltiples llamadas consecutivas a esta función miembro.
La segunda función sobregcargada gestiona mensajes estándar MIDI, tales como
note on o cambio de programa, en formato de doble palabra. Si se necesita parsear el
mensaje en partes se debe usar el método estático CInputPort::DecodeMidiMsg para
extraer cada byte componente MIDI.
5.3.3.4 Cuarto paso: inicilizando objetos
En este paso se declaran los principales objetos que serán usados a lo largo de la
aplicación. Son lo siguientes:
int main(int argc, char* argv[])
{
CDirectMusic CDMusic;
CInputPort
CInPort;
CDMReceiver Receiver;
COutputPort COutPort;
CDLSLoader
CLoader;
CCollection CCollectionA,CCollectionB;
CInstrument CInstrument1,CInstrument2;
CSampleInstrument CSample1,CSample2;
La primera línea declara un objeto del tipo CDirectMusic que es el responsable de
instanciar e inicilizar DirectMusic y será el último objeto en ser destruido. El siguiente
objeto es CInputPort que maneja los puertos de entrada. El tercero es el objeto
109
C A P Í T U L O 5 • Programación MIDI
CDMReceiver que es una clase derivada de CReciever y que implementa las funciones
sobrecargadas vistas en el aparatado anterior. El objeto COutputPort es el responsable
de enviar datos al dispositivo y de descargar datos en el puerto. Los últimos objetos
gestionan los sonidos descargables que serán comentados en el siguiente paso.
Ahora es posible comenzar a llamar a los métodos y activar todo el sistema MIDI. A
continuación se explica como conseguir este propósito:
try
{
// Inicializa DirectMusic
CDMusic.Initialize();
// Inicializa los puertos dado un objeto gestor de DirectMusic
COutPort.Initialize(CDMusic);
CInPort.Initialize(CDMusic);
El siguiente fragmento de código muestra cómo activar los puertos de entrada y salida:
// Etructura de información para el puerto
INFOPORT PortInfo;
DWORD dwPortCount = 0;
//Selección del sintetizador software
do
COutPort.GetPortInfo(++dwPortCount,&PortInfo);
while (!(PortInfo.dwFlags & DMUS_PC_SOFTWARESYNTH));
COutPort.SetPortParams(0,0,1,SET_REVERB | SET_CHORUS,44100);
COutPort.ActivatePort(&PortInfo,32);
// Activación del puerto de salida a partir de la estructura de
información
cout << "\nPuerto de salida seleccionado: " <<
PortInfo.szPortDescription << endl;
// Activación del puerto de entrada, selecciona el primero (por
defecto)
CInPort.GetPortInfo(1,&PortInfo);
CInPort.ActivatePort(&PortInfo,SYSTEM_EXCLUSIVE_MEM);
// Activa el objeto receptor
CInPort.SetReceiver(Receiver);
Las primeras líneas enumeran todos los puertos de salida y seleccionan el primer
sintetizador software existente en el sistema, dado un número desde 1 hasta el valor de
COutputPort::GetNumPorts en el primer parámetro de COutputPort::GetPortInfo.
110
C A P Í T U L O 5 • Programación MIDI
Antes de llamar a COutputPort::ActivatePort necesitamos llamar al método
COutputPort::SetPortParams para indicar el tipo de características que se necesitan en
el puerto de salida (si se pasa el valor cero como parámetro, se asumirá la configuración
por defecto para ese parámetro). Posteriormente se llamará a la función
COutputPort::ActivatePort pasando un puntero a una estructura INFOPORT para
activar el puerto de salida, usando los parámetros del número de grupo de canales y una
frecuencia de muestreo pasada en la llamada a COutputPort::SetPortParams. El
parámetro de canal de grupo es el número de grupos de canales MIDI para ser utilizados
en el puerto software, cada grupo de canal es un conjunto de 16 canales MIDI.
Uno de los parámetros configurables más importantes en el método
COutputPort::SetPortParams es el parámetro de la frecuencia de muestreo, que es la
frecuencia en Hertzios que se necesita para establecer la calidad del sonido en el puerto
de salida. En este caso se asumen 44100 Hz como frecuencia de muestreo.
En las últimas tres líneas se selecciona el puerto de entrada MIDI para la captura de
mensajes, realizando operaciones similares. En este caso no se lleva a cabo ninguna
enumeración, se limita a seleccionar el primer puerto de entrada por defecto. Es
importante advertir que hay un segundo parámetro en el método
CInputPort::ActivatePort que indica el tamaño de memoria máximo reservado para
albergar datos en formato de sistema exclusivo. En este caso sólo se reservan 46.8
Kilobytes. Si se deja este parámetro opcional, el valor por defecto será 32 bytes,
suficiente espacio para recibir datos MIDI estructurados estándar.
Finalmente, se establece un objeto receiver por medio de una llamada al método
CInputPort::SetReceiver. Si cerramos la llave de sentencia principal y se ejecuta la
aplicación se obtendrá la siguiente salida:
Figura 5.4
5.3.3.5 Quinto paso: comenzar la captura de música
Capturar datos musicales desde un teclado externo es muy simple tan pronto como
se haya inicializado el puerto de entrada. Si reservamos espacio para recibir datos en
sistema exclusivo en la llamada a CInputPort::ActivatePort, ahora la aplicación estará
preparada para manejar todos los eventos de entrada generados por un teclado
controlador o un sintetizador. El siguiente código explica como realizar la captura:
//Activa la recepción de mensajes de entrada
CInPort.ActivateNotification();
cout << "Notificacion activada" << endl;
// Redirige mensajes del canal global 0 al canal de destino
global 0
CInPort.SetThru(0,0,0,COutPort);
Como podemos observar, la primera línea de código activa la notificación de todos
los mensajes de entrada MIDI usando un manejador de eventos que invoca a su
respectiva función miembro virtual ya sobrecargada en la primera parte de la aplicación.
111
C A P Í T U L O 5 • Programación MIDI
Otra característica de DirectMIDI es la redirección. Usando la redirección (MIDI thru)
se pueden pasar mensajes MIDI desde un puerto de entrada activado a otro puerto de
salida especificando un canal de grupo y el canal global de origen y de destino donde
los mensajes serán redireccionados.
La siguiente salida muestra un volcado de datos en sistema exclusivo y una captura de
datos MIDI estándar estructurados:
Figura 5.5
5.3.3.6 Sexto paso: aumentando el límite de instrumentos
DirectMIDI soporta la carga de múltiples sonidos almacenados en ficheros DLS
(Downloadable sound files). Esta tecnología es el estándar de los fabricantes MIDI para
un formato de fuentes de sonido en el estado del arte de la tecnología multimedia. El
actual formato DLS2 especifica todas las definiciones de los instrumentos: muestras,
LFOs, filtros paso bajo, bucles y generadores de evolvente que serán descargados y
renderizados en un puerto que soporte estos requisitos.
DirectMIDI soporta dos tipos de operaciones DLS: DLS de alto nivel y DLS de bajo
nivel. Ambas prestaciones se comentan a continuación.
El DLS de alto nivel es la forma de manejar instrumentos de formas de onda que
pueden ser almacenados en formatos DLS 1.0 y DLS 2.0. Estos ficheros pueden ser
creados por una aplicación como DirectMusic Producer que permite configurar
visualmente un amplio rango de parámetros antes mencionados. El DLS de bajo nivel
permite descarga directa de bytes de datos en formato DLS 1.0 a un puerto sintetizador,
proporcionando parámetros de articulación y regiones para el instrumento antes de la
decarga.
DLS de alto nivel:
Utilizar ficheros DLS (.dls) en una aplicación DirectMIDI resulta flexible y sencillo.
Para este propósito debemos declarar un objeto del tipo CDLSLoader con el fin de
realizar la carga y la descarga de ficheros a la librería. Además de este objeto, es
necesario declarar otro objeto del tipo CCollection que es un contenedor que permite
albergar objetos del tipo Cinstrument, que son la instancia última de mantener una
referencia al instrumento DLS. El código mostrado a continuación explica como llevar a
cabo esta tarea:
// Inicializa el objeto loader
CLoader.Initialize();
// Carga el primer fichero DLS
CLoader.LoadDLS(_T(".\\media\\sample.dls"),CCollectionA);
// Carga la colección GM/GS por defecto del sintetizador software
112
C A P Í T U L O 5 • Programación MIDI
CLoader.LoadDLS(NULL,CCollectionB);
// Estructura de información de un instrumento
INSTRUMENTINFO InstInfo;
DWORD dwInstIndex = 0;
// Enumera los instrumentos en la colección B
while (CCollectionB.EnumInstrument(dwInstIndex++,&InstInfo) == S_OK)
{
cout << "Nombre del instrumento: " << InstInfo.szInstName
<< endl;
cout << "Patch en coleccion: " <<
InstInfo.dwPatchInCollection << endl;
cout << "----------------------------------------" << endl;
}
// Obtiene el instrumento con índice 214 de la colección B
CCollectionB.GetInstrument(CInstrument1,214);
// Lo asigna al programa MIDI 0
CInstrument1.SetPatch(0);
cout << "\nInstrumento seleccionado: " << CInstrument1.m_strInstName
<< endl;
cout << "Patch en la coleccion fuente " <<
CInstrument1.m_dwPatchInCollection <<
" al programa destino MIDI: " <<
CInstrument1.m_dwPatchInMidi << endl;
// Obtien el instrumento con indice 0 de la coleccion A
CCollectionA.GetInstrument(CInstrument2,0);
// Lo asigna al programa MIDI 1
CInstrument2.SetPatch(1);
cout << "\nInstrumento seleccionado: " << CInstrument2.m_strInstName
<< endl;
cout << "Patch en la coleccion fuente " <<
CInstrument2.m_dwPatchInCollection <<
" al programa destino MIDI: " <<
CInstrument2.m_dwPatchInMidi << endl;
// Establece el rango de notas
CInstrument1.SetNoteRange(0,127);
CInstrument2.SetNoteRange(0,127);
// Descarga los instrumentos a los puertos de salida
COutPort.DownloadInstrument(CInstrument1);
COutPort.DownloadInstrument(CInstrument2);
113
C A P Í T U L O 5 • Programación MIDI
cout << "Tocando el instrumento 1:" << CInstrument1.m_strInstName <<
endl;
cout << "Presione una tecla para reproducir el segundo instrumento..."
<< endl;
COutPort.SendMidiMsg(COutputPort::EncodeMidiMsg(PATCH_CHANGE,0,0,0),0)
;
COutPort.SendMidiMsg(COutputPort::EncodeMidiMsg(NOTE_ON,0,40,127),0);
getch();
COutPort.SendMidiMsg(COutputPort::EncodeMidiMsg(NOTE_OFF,0,40,0),0);
COutPort.SendMidiMsg(COutputPort::EncodeMidiMsg(PATCH_CHANGE,0,1,0),0)
;
COutPort.SendMidiMsg(COutputPort::EncodeMidiMsg(NOTE_ON,0,60,127),0);
cout << "Tocando el instrumento 2:" << CInstrument2.m_strInstName <<
endl;
getch();
COutPort.SendMidiMsg(COutputPort::EncodeMidiMsg(NOTE_OFF,0,60,0),0);
La primera línea inicializa el objeto Loader que llama internamente a la función
Win32 CoCreateInstance e instancia el objeto COM en el espacio del proceso. Una vez
iniciado el objeto COM IDirectMusicLoader podemos proceder a la carga del fichero
DLS usando el método CDLSLoader::LoadDLS, que toma una cadena de texto
terminada en null representando un fichero, y una referencia a un objeto CCollection de
destino donde los instrumentos definidos en el fichero serán almacenados en memoria.
Cuando la cadena de texto proporcionada es null, DirectMIDI cargará el el conjunto
estándar GM/GS definido en la EPROM del sintetizador.
Para averiguar qué instrumentos residen en el objeto CCollection debemos llamar al
método CCollectionEnumInstrument que toma una variable contador indicando el
índice deseado del instrumento en la colección y un puntero a una estructura
INSTRUMENTINFO que recibirá la información del instrumento en cuestión, es decir,
el nombre del instrumento y el patch en la colección.
Es posible también obtener una referencia particular a un instrumento llamando a la
función sobrecargada CCollection::GetInstrument y proporcionando una referencia a un
objeto CInstrument con el índice en la colección. Este método inciará las propiedades
del objeto CInstrument con los datos del instrumento.
El método CInstrument::SetNoteRange activa la región del teclado que debe responder
a un evento note-on. Finalmente, es necesario proporcionar un programa MIDI destino
para el instrumento en el sintetizador, llamando a la función miembro
COutputPort::DownloadInstrument y pasando una referencia al objeto CInstrument.
114
C A P Í T U L O 5 • Programación MIDI
La siguiente imagen muestra la salida del programa que se ha comentado previamente:
Figura 5.6
DLS de bajo nivel:
DirectMIDI 2.3 permite a una aplicación comunicarse directamente con un puerto
que soporta descarga DLS. Hay dos alternativas para descargar datos al puerto: la
primera es cargar un fichero de forma de onda PCM en formato WAV que contenga los
datos de la muestra, y la segunda es generar la forma de onda en memoria usando
funciones matemáticas. En el primer caso, es necesario cargar el fichero WAV mediante
el método estático CDLSLoader::LoadWaveFile y proporcionar los siguientes tres
parámetros: un puntero a una cadena terminada en null con la ruta del fichero en disco,
una referencia a un objeto CSampleInstrument y un flag indicando el acceso deseado al
fichero. Si el flag es la constante DM_LOAD_FROM_FILE, el fichero es siempre leído
de disco cuando sea necesario; esto es útil para ficheros muy grandes. Si el flag es
DM_USE_MEMORY, el fichero permanece almacenado en memoria dinámica
aumentando la velocidad de acceso.
Respecto a la segunda alternativa antes comentada, puede ser utilizada mediante
el método CSampleInstrument::SetWaveForm, pasando un puntero a un buffer de bytes
y proporcionando una estructura WAVEFORMATEX conteniendo el formato de la
forma de onda. La forma de onda, tanto si se ha leído de un fichero WAV como si se ha
generado matemáticamente, debemos almacenarla en un objeto CSampleInstrument.
El código a continuación explica estas características:
// Carga el fichero WAV
CDLSLoader::LoadWaveFile(_T(".\\media\\starbreeze.wav"),CSample1,DM_US
E_MEMORY);
// Lo asigna a un patch
CSample1.SetPatch(2);
// Establece un loop contínuo
CSample1.SetLoop(TRUE);
// Establece parámetros WAV adicionales
CSample1.SetWaveParams(0,0,68,F_WSMP_NO_TRUNCATION);
REGION region;
ARTICPARAMS articparams;
115
C A P Í T U L O 5 • Programación MIDI
// Inicializa estructuras
ZeroMemory(&region,sizeof(REGION));
ZeroMemory(&articparams,sizeof(ARTICPARAMS));
// Establece los parámetros de región
region.RangeKey.usHigh = 127;
region.RangeKey.usLow = 0;
region.RangeVelocity.usHigh = 127;
// Ajusta LFO
articparams.LFO.tcDelay = TimeCents(10.0);
articparams.LFO.pcFrequency = PitchCents(5.0);
// Establece el envolvente de Pitch
articparams.PitchEG.tcAttack
articparams.PitchEG.tcDecay
articparams.PitchEG.ptSustain
articparams.PitchEG.tcRelease
=
=
=
=
TimeCents(0.0);
TimeCents(0.0);
PercentUnits(0.0);
TimeCents(0.0);
// Establece la envolvente de volumen
articparams.VolEG.tcAttack
articparams.VolEG.tcDecay
articparams.VolEG.ptSustain
articparams.VolEG.tcRelease
=
=
=
=
TimeCents(1.275);
TimeCents(0.0);
PercentUnits(100.0);
TimeCents(10.157);
// Establece los parámetros de instrumentos
CSample1.SetRegion(&region);
CSample1.SetArticulationParams(&articparams);
// Reserva memoria para los sampes
COutPort.AllocateMemory(CSample1);
// Descarga el instrumento al puerto
COutPort.DownloadInstrument(CSample1);
COutPort.SendMidiMsg(COutputPort::EncodeMidiMsg(PATCH_CHANGE,0,2,0),0)
;
cout << "Preparado para reproducir el fichero WAV" << endl;
getch();
// Asigna patch 3
CSample2.SetPatch(3);
// Establece parámetros adicionales
CSample2.SetWaveParams(0,0,68,F_WSMP_NO_TRUNCATION);
116
C A P Í T U L O 5 • Programación MIDI
// Establece los parámetros de los instrumentos
CSample2.SetLoop(TRUE);
CSample2.SetRegion(&region);
CSample2.SetArticulationParams(&articparams);
// Genera los datos de la forma de onda
// Muestras por segundo
DWORD nSamplesPerSec = 44100;
// La duración de la muestra
double nTimeSec = 1.5;
// Numero de muestras
DWORD nSamples = static_cast<DWORD>(nTimeSec * nSamplesPerSec);
// Frecuencia digital de la forma de onda
double Frequency = 1000.0/nSamplesPerSec;
// Reserva memoria para la forma de onda
WORD *pRawData = new WORD[nSamples];
// Genera la forma de onda
for(DWORD ni = 0;ni < nSamples;ni++)
pRawData[ni]
=
static_cast<WORD>(30000*sin(2.0*PI*Frequency*ni) +
5000*sin(6.0*PI*Frequency*ni) +
1000*sin(10.0*PI*Frequency*ni));
// Formato de la forma de onda
WAVEFORMATEX pwfex = {WAVE_FORMAT_PCM,1,44100,44100,2,16,0};
// Establece el formato de la forma de onda
CSample2.SetWaveForm((BYTE*)pRawData,&pwfex,nSamples*2);
// Reserva memoria para la interfaz
COutPort.AllocateMemory(CSample2);
//Descarga instruemento al puerto
COutPort.DownloadInstrument(CSample2);
COutPort.SendMidiMsg(COutputPort::EncodeMidiMsg(PATCH_CHANGE,0,3,0),0)
;
La imagen 5.7 muestra la forma de onda generada en el ejemplo anterior:
117
C A P Í T U L O 5 • Programación MIDI
Figura 5.7
5.3.3.7 Séptimo paso: terminar la aplicación
El último paso de esta secuencia es cerrar la aplicación de una forma apropiada y
limpia. Un ejemplo de esta función de limpieza y terminación de notificaciones puede
verse en el código siguiente:
// Finaliza la aplicaicón
// Rompe la redirección
CInPort.BreakThru(0,0,0);
// Finaliza la notificación
CInPort.TerminateNotification();
cout << "\n\nNotificacion terminada" << endl;
cout << "Descargando instrumentos" << endl;
// Descarga las colecciones del objeto Loader
CLoader.UnloadCollection(CCollectionA);
CLoader.UnloadCollection(CCollectionB);
// Descarga los instrumentos del puerto
cout << "Unloading instruments" << endl;
COutPort.UnloadInstrument(CInstrument1);
COutPort.UnloadInstrument(CInstrument2);
// Descarga las muestras de los puertos
COutPort.UnloadInstrument(CSample1);
COutPort.UnloadInstrument(CSample2);
// Libera la memoria reservada
COutPort.DeallocateMemory(CSample1);
COutPort.DeallocateMemory(CSample2);
delete [] pRawData;
cout << "Aplicación terminada...OK" << endl;
118
C A P Í T U L O 5 • Programación MIDI
getch();
}
catch (CDMusicException& DMExcp)
{
cout << "\n" << DMExcp.GetErrorDescription() << "\n" << endl;
getch();
}
return 0;
}
Si se activó la notificación en el puerto de entrada MIDI para recibir mensajes de
entrada MIDI, es responsabilidad del programador llamar al método
CInputPort::TerminateNotification para finalizar la recepción de eventos por este
puerto. También es necesario llamar al método CInputPort::BreakThru si se estableció
un conexión Thru entre dos puertos. Es importante liberar la memoria reservada por las
colecciones de instrumentos. Para conseguir esto, debemos llamar al método
CDLSLoader::UnloadCollection y liberar la interfaces DirectMusic internas llamando a
COutputPort::DeallocateMemory. Esta operación hay que repetirla con los
instrumentos descargados en el sintetizador mediante la llamada al método
COutputPort::UnloadInstrument.
Aunque la librería DirectMIDI liberará la memoria automáticamente, mediante el
sistema de recolección de basura implementado en los destructores de las clases, es
importante que el programador también se ocupe de estas cuestiones.
5.3.4 Manejo de excepciones
DirectMIDI contiene la clase CDMusicException que maneja todas las posibles
situaciones anómalas que ocurren durante la ejecución de la aplicación, permitiendo
liberar al programador de comprobar constantemente los valores de retorno de las
funciones DirectX mediante la macro FAILED.
Básicamente el objeto provee tres importantes propiedades para informar sobre el error
que son: m_hrCode que informa sobre el valor HRESULT de COM en DirectX,
m_strMethod que proporciona la descripción del método donde la llamada a la función
falló, y por último, m_nLine que devuelve el número de línea del código fuente del
módulo donde se produjo el error.
Además de estas tres propiedades, hay un método adicional para facilitar la
descripción textual del error del método, CDMusicException::GetErrorDescription que
retorna una cadena LPCTSTR conteniendo una descripción detallada del error una vez
que la excepción a sido capturada.
Para terminar, en la siguiente imagen se muestra un ejemplo que informa de un
error durante la ejecución:
Figura 5.8 Información de una excepción
119
C A P Í T U L O 5 • Programación MIDI
Bibliografía y referencias
La Web
DirectMIDI wrapper class library
http://directmidi.sourceforge.net/
MSDN
http://msdn2.microsoft.com/es-es/default.aspx
120
Parte IV
Reproducción de
formatos de sonido
C A P Í T U L O 6 • Reproducción de formatos de sonido
6
Reproducción de formatos
de sonido
6.1 Introducción
Para desarrollar aplicaciones profesionales y complejas se hace necesario el uso
de abstracción y modularidad de las funciones de bajo nivel ya vistas en los capítulos 2
y 3 de la parte 2. Evidentemente, si requerimos implementar funcionalidades como
mezclar gran cantidad de sonidos o reproducir ficheros de audio conocidos como el
formato mp3, ogg, MIDI, WAV, etc. se nos presenta el problema de necesitar
encapsular y abstraer la complejidad de las funciones COM de DirectX o de la cantidad
de funciones de OpenAL, puesto que si se implementa en la misma capa que la lógica
de negocio de nuestra aplicación, el resultado será desastroso. En el caso de desarrollar
una aplicación de reproducción de ficheros mp3, sería muy confuso tener que mezclar el
código de procesamiento y decodificación MPEG con las funciones de DirectSound.
Esto también sería aplicable a un videojuego, donde el código y los objetos que
gestionan el motor del juego no podrían entremezclarse con código de bajo nivel para
reproducir un efecto o poner una música de fondo.
La solución en este caso tendría dos alternativas:
1. Desarrollar por nuestra cuenta una capa software donde se implementara el
código de acceso a los dispositivos de audio(DirectSound, OpenAL…), y,
adicionalmente otra capa superior a ésta donde se implementaría la lógica más
cercana al usuario como: reproducción de formatos de sonido conocidos y
objetos de alto nivel para gestionar streams, sonidos cortos, mezclas, efectos,
etc. o
2. Utilizar una librería de software de terceros donde ya se nos provea de estas
funcionalidades antes descritas. La ventaja de este enfoque es la eficiencia de la
reutilización y el ahorro del tiempo de desarrollo, puesto que esta parte puede
ser subcontratada a terceras personas. La desventaja, sin embargo, de este
enfoque es el coste de la adquisición de la licencia para la distribución al
público.
Afortunadamente, en el entorno del software libre existen soluciones muy
aceptables y eficientes que resuelven este problema.
En este capítulo se explicará la utilización de una librería GNU de software
libre para resolver todos los problemas de una aplicación de audio profesional.
Esta librería se llama Audiere que se explicará a continuación.
123
C A P Í T U L O 6 • Reproducción de formatos de sonido
6.2 La librería Audiere
6.2.1 Introducción
Audiere es una librería software de audio con una API de alto nivel desarrollada
por Chad Austin (jefe de desarrollo), Matt Campbell, Jacky Chong, Theo Reed, Richard
SCAF y Ben Scoot como contribuidores.
Entre las características más relevantes caben destacar:
•
•
•
•
•
•
•
API fácil e intuitiva.
Permite la reproducción de audio en streaming y almacenado dinámicamente en
buffers.
Efectos de cambio de volumen, pan y velocidad de reproducción.
Posibilidad de generación de formas de onda básicas como ruido blanco y ondas
cuadradas.
Enumeración en tiempo de ejecución de dispositivos de audio y formatos de
ficheros de audio soportados.
Manejo de ficheros por streams.
Portabilidad a lenguajes como Python, Delphi, Java y XPCOM (JavaScript en
Mozilla).
Los formatos de audio que soporta son:
•
•
•
•
•
•
Ogg Vorbis (OGG).
MP3 (MPEG Layer-3)
FLAC
WAV descomprimido
AIFF
MOD, S3M, XM e IT.
Las tecnologías software de salida de audio que soporta Audiere son:
•
•
•
•
DirectSound
WinMM (Windows Multimedia API)
OSS en Linux y Cygwin
SGI AL en IRIX
Audiere es código fuente abierto y publicado bajo la licencia LGPL. Esto significa
que es posible usar esta librería en productos comerciales siempre y cuando no se
modifique el código fuente. En el caso de modificar el código fuente y liberar una nueva
librería, ésta debe ser publicada bajo los términos de LGPL también.
Audiere es una librería portable que ha sido testeada en sistemas como Windows,
Linux i386, Cygwin e IRIX con al menos tres grandes compiladores. La arquitectura de
Audiere es independiente de la codificación big-endian y little-endian.
124
C A P Í T U L O 6 • Reproducción de formatos de sonido
La librería puede descargarse del repositorio de software libre SourceForge en la
URL: http://audiere.sourceforge.net/home.php y en la sección download podemos
descargar la librería completa cuya última versión es la 1.9.4.
Una vez instalada la librería podemos acceder al programa de prueba en el
directorio audiere-1.9.4-win32\bin y ejecutar la aplicación wxPlayer.exe. Veremos una
interfaz de usuario como la siguiente:
Figura 6.1 Aplicación de ejemplo de la librería
Donde podremos ejecutar las funcionalidades básicas de la librería. En el
ejemplo de la imagen 6.1 se están reproduciendo dos ficheros mp3 mezclados en tiempo
real: uno en modo stream y otro en un buffer. Lógicamente el fichero mp3 cargado en
forma de stream requiere menos tiempo de carga y memoria que el almacenado en el
buffer puesto que lo reproduce bajo demanda en tiempo de ejecución.
125
C A P Í T U L O 6 • Reproducción de formatos de sonido
6.2.2 Clase AudioDevice
Comenzaremos a programar una aplicación real de audio con la librería Audiere
importando en primer lugar la cabecera audiere.h, como se muestra en el código
siguiente:
include
#include
#include
#include
<windows.h>
<conio.h>
<iostream>
"audiere.h"
using namespace audiere;
using namespace std;
int main(int argc, char* argv[])
{
// Crea el dispositivo defecto para salida de audio
AudioDevicePtr device(OpenDevice());
if (!device) {
cout << "Error al crear dispositivo" << endl;
return 1;
}
Como podemos observar en el código anterior, además de incluir la cabecera
audiere.h y usar su espacio de nombres(namespace), se ha creado el objeto AudioDevice
que maneja la gestión del dispositivo de audio de salida.
La función OpenDevice permite inicilializar la librería, detectar y abrir el dispositivo de
sonido por defecto. La clase AudioDevice deriva de la clase RefCounted como se ilustra
en la siguiente figura:
Figura 6.2
Esta clase base (RefCounted) permite contar referencias de instancias de la clase,
así cuando el contador de referencias llega a cero se libera completamente la memoria
que ocupaba el objeto AudioDevice.
El objeto AudioDevice representa un dispositivo en el sistema que es capaz de abrir y
mezclar varias fuentes de sonido. En Windows, DirectSound sería un ejemplo de un
dispositivo.
126
C A P Í T U L O 6 • Reproducción de formatos de sonido
6.2.3 Clase OutputStream
La clase OutputStream representa una conexión a un dispositivo de audio. De
esta forma múltiples streams de salida son mezclados por un dispositivo de salida para
producir una forma de onda final que es lo que el usuario oye.
Cada objeto OutputStream puede ser reproducido o detenido independientemente de
otros objetos OutputStream. Además se les puede establecer parámetros como volumen,
pan y efectos también de forma independiente.
Figura 6.3
Como en el caso anterior, esta clase también hereda de la clase RefCounted para la
gestión de memoria.
Sus métodos públicos disponibles al desarrollador son los que a continuación se
muestran:
virtual
virtual
virtual
virtual
virtual
virtual
virtual
virtual
virtual
virtual
virtual
virtual
virtual
virtual
virtual
virtual
void
void
bool
void
void
bool
void
float
void
float
void
float
bool
int
void
int
play ()=0
stop ()=0
isPlaying ()=0
reset ()=0
setRepeat (bool repeat)=0
getRepeat ()=0
setVolume (float volume)=0
getVolume ()=0
setPan (float pan)=0
getPan ()=0
setPitchShift (float shift)=0
getPitchShift ()=0
isSeekable ()=0
getLength ()=0
setPosition (int position)=0
getPosition ()=0
Como podemos apreciar, este objeto contiene los métodos principales para
manejar las funcionalidades básicas de un sonido: cambiar el volumen, pan, reproducir,
pausar, obtener la posición de la secuencia, cambiar la frecuencia de reproducción, etc.
Si a continuación requerimos cargar un fichero en un objeto OutputStream para
su reproducción tendremos que recurrir a la función global OpenSound que detectará
automáticamente el tipo de formato y lo cargará en el objeto como stream o buffer
según se le indique en el último parámetro mediante los flags true o false.
En el código siguiente se muestra esta situación:
127
C A P Í T U L O 6 • Reproducción de formatos de sonido
// Carga el fichero de sonido 3
OutputStreamPtr sound3(OpenSound(device, "..\\media\\sonido3.wav",
false));
if (!sound3) {
cout << "Error al cargar sonido3" << endl;
return 1;
}
// Carga la música
OutputStreamPtr stream(OpenSound(device, "..\\media\\OMD Electricity.mp3", true));
if (!stream) {
cout << "Error al cargar musica" << endl;
return 1;
}
6.2.4 Reproducción y efectos
Por último, una vez que se hayan cargado los ficheros en los objetos
OutputStream, tan sólo queda ahora reproducir el sonido y aplicar efectos como pan,
volumen y cambio de la velocidad de reproducción.
Como se vio en la lista de métodos públicos de la clase OutputStream, tenemos todas las
funciones necesarias para llevarlo a cabo, mediante, por ejemplo, el método play,
setPan, setVolume, setRepeat, etc.
En el código que se muestra en la siguientes líneas podemos apreciar el uso de estos
métodos:
while( condicion )
{
switch( _getch() )
{
// Reproduce buffer 1
case '1':
sound1->play();
break;
// Reproduce buffer 2
case '2':
sound2->play();
break;
// Reproduce buffer 3
case '3':
sound3->play();
break;
// Reproduce musica
case '4':
stream->play();
break;
// Efecto pan de la musica
case '5':
for(float i= -1.0;i < 1.0;i+=0.1)
{
stream->setPan(i); // Izquierda->Derecha
Sleep(100);
}
128
C A P Í T U L O 6 • Reproducción de formatos de sonido
for(float i= 1.0;i > -1.0;i-=0.1)
{
stream->setPan(i); // Derecha->Izquierda
Sleep(100);
}
stream->setPan(0); // Centro
break;
// Frecuencia de reproduccion a 2x
case '6':
stream->setPitchShift(2.0);
break;
// Frecuencia de reproduccion a 0.5x
case '7':
stream->setPitchShift(0.5);
break;
// Frecuencia de reproduccion
case '8':
stream->setPitchShift(1.0);
break;
// Pausa musica
case '9':
stream->stop();
break;
// Fin de la aplicacion
case '0':
condicion = false;
}
}
Es fácil realizar estas operaciones. Como puede intuirse, todos los sonidos que
se reproducen con el método play se mezclan sobre el mismo dispositivo, permitiendo
utilizar un número indeterminado de fuentes de sonido. Esto puede ser útil para un
videojuego donde varios efectos de sonido se deben mezclar con una banda sonora de
fondo. El método setPan toma valores desde -1.0 (balance a canal izquierdo) a 1.0
(balance a canal derecho), estando en el 0.0 el balance central. Por último, el método
setPitchShift cosigue cambiar la velocidad de reproducción con valores de 2.0
(reproduce a doble velocidad), 0.5 (reproduce a la mitad de la velocidad) y 1.0
(reproduce normal).
Es de destacar que esta librería tiene una pólitica de gestión de memoria muy
avanzada y automática por lo que puede terminarse la apliacación sin necesidad de
liberar los recursos manualmente.
6.2.5 Acceso a los datos PCM de un buffer
Uno de los aspectos más importantes de una librería de audio es poder acceder a
los datos de audio PCM contenidos en un buffer de sonido. Para poder realizar esta
operación con Audiere es necesario cargar el sonido en un objeto SampleSurce que
además de ser la fuente para datos PCM permite posicionar el sonido. En la siguiente
imagen se muestra la estructura de esta clase:
129
C A P Í T U L O 6 • Reproducción de formatos de sonido
Figura 6.4
Previo a la obtención de los datos PCM necesitamos utilizar la clase
SampleBuffer que es un contenedor de muestras de sonido de sólo lectura que permite
abrir streams de muestras e iterar sobre ellos. Este objeto es usado en la situación donde
un sonido muy largo se carga una sola vez en memoria y se redirige en stream varias
veces hacia el dispositivo. Para obtener el objeto que hemos mencionado anteriormente
llamaremos a la función global CreateSampleBuffer.
En la siguiente imagen se muestra la estructura de esta clase estándar:
Figura 6.5
Por lo tanto el código para llevar esto a cabo es el siguiente:
#include
#include
#include
#include
<windows.h>
<conio.h>
<iostream>
"audiere.h"
using namespace audiere;
using namespace std;
int main(int argc, char* argv[])
{
AudioDevicePtr device(OpenDevice());
if (!device) {
cout << "Error abriendo dispositivo" << endl;
return 1;
}
// Cargamos el fichero de sonido mp3 en un objeto SampleSource
SampleSourcePtr sampleSource = OpenSampleSource("..\\media\\OMD
- Electricity.mp3");
if (!sampleSource)
{
cout << "Error al cargar fichero" << endl;
return 1;
}
// Se obtiene un buffer de datos a partir del objeto
SampleSource
130
C A P Í T U L O 6 • Reproducción de formatos de sonido
SampleBufferPtr sampleBuffer1 =
CreateSampleBuffer(sampleSource);
if (!sampleBuffer1)
{
cout << "Error al crear objeto sampleBuffer" << endl;
return 1;
}
Con las funciones miembro getFormat y getLength podemos obtener
información del formato de la forma de onda y la longitud en frames.
Con el objetivo de reservar memoria dinámica para albergar los datos de la forma de
onda se hace preciso calcular el tamaño de la muestra en bytes. Para ello se recurrirá a la
siguiente función:
Tamaño buffer(bytes) = número de canales × número de frames × bits por muestra
Finalmente se recurre a la función miembro SampleBuffer::getSamples para
obtener un puntero a los bytes de la muestra.
Con todo esto se puede ya reservar memoria y almacenar los datos PCM en ella
mediante el siguiente código de ejemplo:
int channel_count,sample_rate,frame_count;
SampleFormat sample_format;
// Se obtienen los datos de formato del buffer de audio
sampleBuffer1->getFormat(channel_count,sample_rate,sample_format);
// Se obtiene el número de frames
frame_count = sampleBuffer1->getLength();
// Se calcula el tamaño de muestra en bytes
DWORD sampleSize = channel_count * frame_count *
GetSampleSize(sample_format);
cout << "El tamano de la muestra a invertir es: " << sampleSize << "
bytes" << endl;
// Puntero al array dinámico donde almacenar los datos
WORD *pRawData = new WORD[sampleSize / 2];
// Volcado de datos al array
CopyMemory(pRawData,sampleBuffer1->getSamples(),sampleSize);
El código que se muestra a continuación es un ejemplo de inversión de una
forma de onda, en este caso de una canción, para reproducir en reverse o backward. Con
esta finalidad se ha creado un array auxiliar en memoria dinámica donde se realizará el
proceso de inversión, que como se muestra en la figura siguiente, consiste en
intercambiar el orden de los últimos bytes para ser los primeros en el array de forma
invertida. El motor reproducirá la muestra palabra por palabra, ya que el formato es de
16 bits por muestra.
131
C A P Í T U L O 6 • Reproducción de formatos de sonido
Array origen en bytes
1
2
3
4
5
6
7
8
9
10 11 12 13 14 15 16 17 18 19 20
Array destino en bytes
19 20 17 18 15 16 13 14 11 12
9
10
7
8
5
6
3
4
1
2
Figura 6.6
Así, por lo tanto, el algoritmo que lleva a cabo esta inversión es el siguiente:
// Array auxiliar para la inversión
WORD *pRawAux = new WORD[sampleSize / 2];
// Indice al penúltimo byte de datos
int t = sampleSize / 2 - 1;
// Proceso de inversión
for(int i = 0;i < sampleSize / 2;i++)
{
pRawAux[i] = pRawData[t];
t--;
}
Ya sólo falta crear un nuevo objeto SampleBuffer a partir del nuevo array con los
datos invertidos y de ahí obtener un objeto SampleSource mediante el método
SampleBuffer::openStream. Por último se pasa este objeto a la función global ya
comentada, OpenSound, y se provee un puntero al objeto SampleBuffer que contiene la
muestra invertida. Esta función nos devolverá definitivamente un puntero a un objeto
OutputStream cuyo destino final es poder ser reproducido.
El código que se muestra a continuación ilustra estas operaciones:
// Crear un nuevo objeto SampleBuffer para albergar datos
SampleBufferPtr sampleBuffer2 =
CreateSampleBuffer(pRawAux,frame_count,channel_count,sample_rate,sampl
e_format);
if (!sampleBuffer2)
{
cout << "Error al crear objeto SampleBuffer" << endl;
return 1;
}
// Se obtiene un puntero al objeto SampleSource
SampleSourcePtr sampleSource2 = sampleBuffer2->openStream();
// Se libera la memoria de los arrays de datos
delete [] pRawData;
delete [] pRawAux;
// Se crea un objeto de salido de sonido stream
OutputStreamPtr sound2(OpenSound(device,sampleSource2,false));
if (!sound2)
{
cout << "Error al crear el objeto OutputStream" << endl;
return 1;
132
C A P Í T U L O 6 • Reproducción de formatos de sonido
}
// Finalmente se reproducen los datos invertidos
cout << "Reproduciendo sonido invertido. Pulse una tecla para
finalizar..." << endl;
sound2->play();
_getch();
return 0;
}
6.3 Formatos de sonido conocidos
6.3.1 Formato WAV
El formato WAV o WAVE cuyo nombre proviene de waveform audio file
format, es un formato de audio sin compresión desarrollado por Microsoft e IBM. Este
formato admite los modos mono y estéreo a diversos bits y velocidades de muestreo. Se
encuentra normalmente en los sistemas PC con la extensión .wav y es el principal de los
sistemas operativos Windows.
El formato WAV deriva del formato RIFF (Resource Interchange File Format)
y es relativamente parecido al los formatos AIFF e IFF utilizados en sistemas Mac OS.
Una de las ventajas que proporciona el formato es que puede soportar cualquier
tipo de códec; aunque principalmente se utiliza con el formato PCM sin comprimir.
Una de las limitaciones de WAV es que sólo se puede grabar un archivo de hasta 4
gigabytes, debido a una limitación en la cabecera del fichero donde se indica la longitud
del mismo con un número de 32 bits.
La estructura de este fichero es muy simple, con una cabecera compuesta por 4
parámetros: duración temporal, frecuencia de muestreo, resolución y grabación mono o
estéreo.
6.3.2 Formato AIFF
Este formato de audio fue desarrollado por Apple en 1998 y está basado en el
formato IFF. Es le acrónimo de Apple Interchange File Format y es uno de los formatos
de sonido más compatibles.
Los datos de audio en estándar AIFF no están comprimidos, almacenándose los datos en
big-endian y codificación PCM. Este formato tiene una variante con compresión que se
le denomina AIFF-C o AIFC, con soporte para una gran variedad de códecs.
El estándar AIFF es uno de los formatos líderes, junto a WAV, usados a nivel
profesional para aplicaciones de audio, ya que, a diferencia del formato MP3, está
comprimido sin ninguna pérdida lo que facilita un rápido procesamiento de señal en
detrimento del aumento de espacio en memoria secundaria.
Una de las ventajas de este estándar es poder dar soporte a bucles en notas
musicales para su uso en samplers.
El formato se puede encontrar con extensiones .aiff o .aif, y para las variantes
comprimidas con extensión .aifc, aunque las anteriores también admiten compresión.
133
C A P Í T U L O 6 • Reproducción de formatos de sonido
6.3.3 Formatos MPEG: MP3
El formato MPEG-1 Audio Layer III o MPEG-2 Audio Leyer III, más
comúnmente conocido MP3, es un formato de compresión de audio digital patentado
que usa un algoritmo con pérdida para obtener tamaños de ficheros relativamente
pequeños.
MP3 fue desarrollado por el Moving Picture Experts Group (MPEG) que es una
organización encargada de desarrollar normativas para compresión de vídeo y audio
digital. MP3 forma parte del estándar MPEG-1 y del posterior y más extendido MPEG2. El estándar está recogido en las normativas ISO/IEC 11172-3 e ISO/IEC 13818-3.
Estos tipos de ficheros que suelen encontrarse en ordenadores personales y
reproductores de audio, tiene la extensión .mp3.
Historia
Este formato fue desarrollado principalmente por Karlheinz BrandenBurg,
director de tecnologías del Instituto Fraunhofer IIS. Las primeras patentes fueron
registradas en 1986 y varias más en 1991. Pero no fue hasta julio de 1995 cuando
Brandenburg usó por primera vez la extensión .mp3 para los archivos de su ordenador.
A partir de esta fecha el formato MP3 se convirtió en muy popular y fue el
sistema utilizado para streaming de audio y compresión de alta calidad, aunque en los
equipos de alta fidelidad era patente la pérdida de calidad acústica. Con un fichero MP3
con una compresión de 128kbits/s se conseguía un tamaño unas 11 veces menor que su
homónimo en CD.
Con el boom de Internet a principios del milenio el formato tuvo una gran
difusión, ya que hizo posible el intercambio de ficheros musicales. De todos es sabido el
problema actual con las copias piratas y la violación de los derechos de autor derivadas
del desarrollo de software P2P. Actualmente MP3 goza de un gran éxito y su utilización
está siendo cada vez más frecuente.
Especificaciones técnicas
El sistema de codificación MP3 utiliza un algoritmo con pérdidas. Sin embargo,
las pérdidas corresponden a frecuencias que el oído humano no puede captar. El
algoritmo elimina toda la información que no se necesita. Para ello utiliza un sistema
denominado codificación de subbandas, proceso el cual la señal se descompone en
subbandas a través del banco de filtros híbrido. Las subbandas obtenidas, se comparan
con el original mediante un modelo psicoacústico que discrimina las bandas que no son
importantes. Por último las subbandas son cuantizadas y codificadas y el resultado final
se comprime con un algoritmo tipo Huffman LZW.
En la capa III de MPEG-1, se encuentra el llamado banco de filtros híbrido. Esta
mejora de la resolución frecuencial empeora la resolución temporal introduciendo
problemas de pre-eco que son predichos y corregidos.
Este sistema utilizado en la capa III es el llamado banco de filtros híbrido
polifase/MDCT. Se encarga de realizar el mapeado de la señal en el dominio del tiempo
134
C A P Í T U L O 6 • Reproducción de formatos de sonido
al de la frecuencia tanto para el codificador como para los filtros de reconstrucción del
decodificador. Las muestras de salida del banco están cuantizadas y proporcionan una
resolución en frecuencia variable, 6x32 o 18x32 subbandas, ajustándose mucho mejor a
las bandas críticas de las diferentes frecuencias.
La capa III tiene tres modos de bloque de funcionamiento: dos modos donde las 32
salidas del banco de filtros pueden pasar a través de las ventanas y las transformadas
MDCT y un modo de bloque mixto donde las dos bandas de frecuencia más baja usan
bloques largos y las 30 bandas superiores usan bloques cortos. Para la capa III de
MPEG-1 se especifican cuatro tipos de ventana:
1.
2.
3.
4.
Normal
Transición de ventana larga a corta (Start)
3 ventanas cortas (Short)
Transición de ventana corta a larga (Stop)
La compresión de basa en la reducción del margen dinámico irrelevante, es decir, en
la incapacidad del sistema auditivo para detectar los errores de cuantización en
condiciones de enmascaramiento. Este estándar divide la señal en bandas de frecuencia
que se aproximan a las bandas críticas, y luego cuantifica cada subbanda en función del
umbral de detección del ruido dentro de esa banda. El modelo psicoacústico es una
modificación del empleado en esquema II, y utiliza un método denominado predicción
polinómica. Analiza la señal de audio y calcula la cantidad de ruido que se puede
introducir en función de la frecuencia, es decir, calcula “la cantidad de
enmascaramiento” o umbral de enmascaramiento en función de la frecuencia.
El codificador utiliza esta información para decidir la mejor manera de gastar los
bits disponibles. El estándar provee dos modelos piscoacústicos el I y el II. El modelo I
es mucho menos complejo que el II y simplifica considerablemente los cálculos.
Las pruebas acústicas, realizadas con personas con oído muy agudo, se han utilizado
para comprobar si la codificación es suficientemente buena. En la tabla 6.1 pueden
observarse las diferentes características de distintas fuentes de sonido.
Capa
1
2
3
Compresión
4:1
6:1 a 8:1
10:1 a 12:1
Transferencia
384 kbits/s
256 a 192 kbits/s
128 a 112 kbits/s
Tabla 6.1 Características de las distintas fuentes de sonido
135
C A P Í T U L O 6 • Reproducción de formatos de sonido
En la tabla 6.2 se muestra las características de distintas fuentes de sonido.
Calidad
Teléfono
Radio AM
Radio FM
CD
DAT
Muestreo
8 Khz
11,025 Khz
22,050 Khz
44,1 Khz
48 Khz
Resolución
8
8
16
16
16
Modo
Mono
Mono
Estéreo
Estéreo
Estéreo
Tasa transferencia
64 kbps
88 kbps
705,6 kbps
1441,2 kbps
1536 kbps
Tabla 6.2 Características de distintas fuentes de sonido
Así por ejemplo para el oído no experimentado, con 128 kbps o hasta 192 kbps
es aceptable. En personas que escuchan mucha música o que tienen experiencia en la
parte auditiva, desde 192 a 256 kbps debe ser recomendable. En la música que circula
por Internet las tasas suelen ser de 128 y 192 kbps.
Para la codificación y la cuantización la solución que propone el estándar en cuanto
a la repartición de bits o ruido, se hace en un ciclo de iteración que consiste en un ciclo
interno y otro externo:
Ciclo interno: realiza la cuantización no-uniforme de acuerdo con el sistema de
punto flotante. El ciclo escoge un determinado intervalo de cuantización y, a los
datos cuantizados, se les aplica codificación Huffman en el siguiente bloque. El
ciclo termina cuando los valores cuantizados que han sido codificados con
Huffman usan menor o igual número de bits que la máxima cantidad de bits
permitida.
Ciclo externo: este ciclo se encarga de verificar si el factor de escala para cada
subbanda tiene más distorsión de la permitida (ruido en la señal codificada),
comparando cada banda del factor de escala con los datos previamente
calculados en el análisis psicoacústico.
Por último falta empaquetar los datos. Para ello se toman muestras cuantificadas del
banco de filtros, junto a los datos de asignación de bits/ruido y almacena el audio
codificado y datos adicionales en tramas. Cada trama contiene hasta 1152 muestras de
audio y está formada por un encabezado, junto con un chequeo de detección de errores
CRC y datos auxiliares.
6.3.4 Formato General MIDI
General MIDI conocido con el acrónimo GM exige que todos los instrumentos
sean compatibles con el mismo estándar GM.
El estándar especifica qué sonidos se crean con el ordenador y cómo van a ser
enviados al procesador para que emita los sonidos, por tanto es la especificación GM la
que define el formato MIDI en una tarjeta de sonido.
136
C A P Í T U L O 6 • Reproducción de formatos de sonido
Los ficheros GM contienen las partituras de las composiciones musicales en
formato MIDI. La extensión con la que se encuentra el formato suele es .mid, aunque
existe una variante llamada .kar que contiene la lírica de las canciones para ser
interpretadas por sistemas de “karaoke”.
El conjunto GM consta de un total de 128 instrumentos clasificados por tipos
como se muestra en el apéndice A3. Sin embargo, para la especificación GM2 se
definen un total de 256 instrumentos y 9 kits de batería GS (General Sound).
Estos ficheros ocupan muy poco espacio físico y las composiciones suelen ser
reproducidas a 16 bits a 44,1 kHz en estéreo y efectos. Para reproducir un fichero MIDI
no requiere los recursos que necesita un fichero de audio convencional, como por
ejemplo un MP3 o un XM. La MMA (Asociación Internacional de fabricantes MIDI)
publicó una normativa en cuanto a la estandarización de los archivos MIDI o SMF que
consisten en la siguiente estructura:
Cabecera:
id
tamano
formato
deltatime
Identificador que contiene los caracteres MThh
Tamaño de la longitud del fichero sin contar id ni
tamano
0 – Pista única
1 – Multipista, síncrono (todas las pistas se ejecutan
simultáneamente)
2 – Multipista asíncrono (las pistas se ejecutan de
manera secuencial)
Número de intervalos contenidos en ¼ de nota.
Especifica tiempo de reproducción.
Si quisiéramos definir una estructura en C++ para albergar estos datos
podríamos recurrir a una clase estándar como la del siguiente ejemplo:
class MIDIhdr{
private:
char id[4];
long tamano;
unsigned formato;
unsigned numero_pistas;
unsigned deltatime;
public:
void setid(const char *id);
char *getid();
etc.
};
137
C A P Í T U L O 6 • Reproducción de formatos de sonido
Segmentos de pista:
Cada segmento de pista está compuesto por una cabecera, seguida de una
serie de eventos MIDI. Entre estos eventos se encuentran los comandos, que son
los datos enviados y recibidos por los puertos MIDI.
Cadena de cuatro caracteres que identifica la cabecera del
track. Es MTrk.
Indica el tamaño de la pista sin indicar la cabecera, es
decir, el número de comandos que contiene.
id
tamano
En C++ podemos implementar simplemente con la clase estándar:
class Miditrk {
private:
char
long
public:
void
char
etc.
id[4];
tamano;
setid(const char *id);
*getid();
};
Notas musicales:
En MIDI están comprendidas con un numero decimal positivo entre 0 y 127.
Son las siguientes:
Octava
0
1
2
3
4
5
6
7
8
9
10
Do
0
12
24
36
48
60
72
84
96
108
120
Do#
1
13
25
37
49
61
73
85
97
109
121
Re
2
14
26
38
50
62
74
86
98
110
122
Re#
3
15
27
39
51
63
75
87
99
111
123
Mi
4
16
28
40
52
64
76
88
100
112
124
Fa
5
17
29
41
53
65
77
89
101
113
125
Fa#
6
18
30
42
54
66
78
90
102
114
126
Sol
7
19
31
43
55
67
79
91
103
115
127
Sol#
8
20
32
44
56
68
80
92
104
116
La
9
21
33
45
57
69
81
93
105
117
La#
10
22
34
46
58
70
82
94
106
118
Si
11
23
35
47
59
71
83
95
107
119
Valores de longitud variable:
La finalidad de utilizar comandos con datos de longitud variable es
representar un amplio rango de valores evitando que valores pequeños ocupen
más espacio físico de lo necesitado.
Estos valores se forman como una secuencia de bytes, del que sólo cuentan los
siete bits menos significativos. El MSB debe ser siempre 1 si quedan más bytes
para representar el valor y 0 para el último byte.
138
C A P Í T U L O 6 • Reproducción de formatos de sonido
Eventos:
Están formados por los comandos de voz y los comandos de modo. Los
primeros constan de un byte de estado seguido de datos dependientes del byte de
estado. Para los 16 posibles canales de GM-1 se utilizan los 4 bits más bajos del
byte de estado. Así, por ejemplo, para indicar que una nota ha sido pulsada o
liberada se utilizaría los siguientes bytes de estado:
Byte de estado
0x8....
Significado
Liberación de nota
0x9....
Pulsación de nota
Datos
1 byte de nota + 1 byte de
velocidad pulsación
1 byte de nota + 1 byte de
velocidad pulsación
Los comandos de modo constan sólo de un byte que indica el estado pero no
requieren ningún canal determinado. El comando más utilizado tiene como
función silenciar las notas en todos los canales.
Mensajes de sistema exclusivo:
Como se comentó en el capítulo 4, los mensajes de sistema exclusivo son
datos no estructurados proporcionados por cada fabricante de sistema MIDI que
tienen una nomenclatura determinada.
Los mensajes de sistema exclusivo tienen el siguiente formato de arranque:
0xF0 + longitud + mensaje no estructurado
Se pueden enviar desde un solo bloque hasta varios. Para el envío de un
mensaje de múltiples bloques se hace necesario indicar el final del stream
indicándolo con el byte 0xF7.
Eventos META:
Los eventos Meta no se consideran mensajes MIDI sino otro tipo de
información que se asocia al fichero. Para desarrollar un componente de lectura
de un fichero MIDI no es necesario conocer todos los eventos Meta. Por su
extensión se ha eludido indicar en esta sección los diferentes eventos. Si el lector
está interesado en conocer los diferentes eventos, se le anima a consultar el sitio
Web de la MMA.
Para reproducir un fichero MIDI en Audiere es necesario en primer lugar utilizar
un nuevo tipo de dispositivo, llamado MIDIDevice que gestiona el objeto de control
del puerto MIDI. En segundo lugar, para abrir el puerto MIDI se invocará a la
función OpenMIDIDevice con el parámetro NULL, puesto que actualmente no se
encuentra implementado en esta librería la elección de otros tipos de puertos.
Debido a esto, sólo será posible seleccionar un puerto MIDI por defecto. En
último lugar y con el fin de reproducir el fichero MIDI, se recurrirá al objeto
MIDIStream que albergará el contenido del archivo previamente leído y almacenado
139
C A P Í T U L O 6 • Reproducción de formatos de sonido
con el método MIDIDevice::openStream, pasándole como parámetro un cadena con
la identificación del archivo.
En la siguientes líneas de código podemos apreciar la utilización de estas clases.
// Dispositivo MIDI
MIDIDevicePtr midi(OpenMIDIDevice(NULL));
if (!midi) {
cout << "Error al crear dispositivo MIDI" << endl;
return 1;
}
MIDIStreamPtr midStream(midi->openStream("../media/SONG_001.mid"));
if (!midStream) {
cout << "Error al cargar fichero MIDI" << endl;
return 1;
}
Por último invocaremos al método MIDIStream::play para reproducir el contenido del
fichero MIDI, como se muestra en el ejemplo:
// Reproduce fichero MIDI
case 'x':
midStream->play();
break;
140
C A P Í T U L O 6 • Reproducción de formatos de sonido
6.3.5 Formato MOD / XM
El formato MOD tiene su origen en los ordenadores Amiga donde los programas
de edición musical, llamados trackers, solían almacenarlos en ficheros con extensión
.mod.
La primera versión del formato fue creado por Karsten Obarski para su uso en el
editor Ultimate Soundtracker; tracker publicado para el ordenador Amiga en 1987.
Desde entonces, el formato ha sido soportado por cientos de reproductores de audio y
docenas de trackers.
En las primeras versiones del formato MOD se utilizaban sólo 4 canales para la
reproducción simultánea, correspondiendo a las capacidades del chipset de audio
original de Amiga, y un límite de 15 instrumentos.
El formato fue diseñado para ser directamente reproducido en el Amiga sin
procesamiento adicional por hardware. Por ejemplo, las muestras de sonido eran
almacenadas en formato PCM de 8 bits de resolución que se enviaban posteriormente al
DAC del sistema de audio. Tampoco los datos de las “patterns” u hojas de notas
estaban empaquetados. La gran innovación de este sistema era el poco uso de CPU que
requería para la reproducción de ficheros de audio. Esto fue utilizado para reproducir
música de fondo en los videojuegos de la época.
El fichero MOD tuvo su máximo auge en la llamada Demoscene (movimiento
cultural para el arte infográfico en tiempo real) donde dos “demosceners”, llamados
Mahoney y Kaktus, desarrollaron un papel relevante en el diseño del formato.
Consideraciones técnicas:
Un “pattern” es normalmente representado en una interfaz de usuario como una
tabla con una columna por canal, así si tenemos 4 columnas; una para cada canal, cada
columna tiene un total de 64 filas o “rows”.
Una celda en la tabla pude causar un cambio en el canal cuando el tiempo de la
fila (row) se ha alcanzado. Por ejemplo:
Comienzo de la reproducción de una nueva nota, a un determinado volumen
y efectos.
Cambiar el volumen o efecto especial de la nota.
Cambiar el flujo del “pattern” (saltar a una determinada posición dentro del
“pattern” o hacer un loop en el mismo).
Etc.
Respecto al Timing, el mínimo tiempo per frame es 0.02 segundos, o un retrazo
vertical (intervalo VSync), ya que el software original usaba la sincronización VSync del
monitor at 50 Hz para sistemas PAL ó 60 Hz para sistemas NTSC.
La frecuencia a la que un “pattern” es reproducido está definida por la
configuración de velocidad. Cada fila o “row” en el “pattern” dura un retrazo vertical
(0.02 segundos). La configuración de velocidad varía entonces entre 1 a 255. Con este
141
C A P Í T U L O 6 • Reproducción de formatos de sonido
factor se consigue que el tempo de la melodía o canción se reproduzca más deprisa o
más lentamente.
En versiones posteriores del formato, el retrazo vertical fue reemplazado con un
periodo de tiempo ajustable en el rango [0.01,0.078] segundos. Lamentablemente,
algunas de las funcionalidades antiguas fallaron, puesto que el nuevo comando de
establecimiento de la velocidad tenía un código idéntico al antiguo. Por ende, los
valores entre [1, 31] eran interpretados como configuraciones antiguas, pero los demás
valores fueron considerados modificaciones al período de tiempo ajustable. Como
consecuencia, los valores comprendidos entre [32, 255] usados en algunas ficheros
fallaban en las nuevas versiones de los trackers.
Formato XM:
Una variante del formato MOD que surgió posteriormente a su desarrollo fue el
formato XM, acrónimo de “extended module”, e introducido por los “demosceners” que
desarrollaron el secuenciador “Fast Tracker 2”. Su extensión es .xm.
Entre las características técnicas introducidas por este formato, caben destacar:
instrumentos multi-muestra con efectos de volumen, envolventes de panning y
compresión de “patterns”. También aumentó el número de canales y de comandos
utilizados por el antiguo fichero MOD, añadió soporte para muestra PCM de 16 – bits y
ofreció una tabla de frecuencias alternativas para “portamentos”.
El formato XM se utiliza actualmente en muchas composiciones para ordenador, sobre
todo en “demos” y videojuegos.
Figura 6.6 Imagen del mítico editor de MOD / XM FastTracker II
142
C A P Í T U L O 6 • Reproducción de formatos de sonido
Bibliografía y referencias
Libros
Diseño y desarrollo Multimedia, Sistemas, Imagen, Sonido y Vídeo – Manuel-Alonso
Castro Gil, Antonio Colmenar Santos, Pablo Losada de Dios, Juan Peire Arroba.
Editorial Ra-ma, 2002.
La Web
Wikipedia:
WAV
http://es.wikipedia.org/wiki/Waveform_Audio_Format
AIFF
http://es.wikipedia.org/wiki/AIFF
MP3
http://es.wikipedia.org/wiki/MP3
GM
http://es.wikipedia.org/wiki/General_MIDI
MOD
http://en.wikipedia.org/wiki/MOD_(file_format)
XM
http://en.wikipedia.org/wiki/XM_(file_format)
GM
http://www.lpi.tel.uva.es/~nacho/docencia/ing_ond_1/trabajos_01_02/formatos_audio_
digital/html/midiformat.htm – Juan Ignacio Arribas
Audiere documentation:
http://audiere.sourceforge.net/documentation.php
MSDN
http://msdn2.microsoft.com/es-es/default.aspx
143
Parte V
Tratamiento Digital de
Señal
C A P Í T U L O 7 • Tratamiento Digital de Señal
7
Tratamiento Digital de Señal
7.1 Procesado digital de señal aplicado al audio
El procesado digital de señal ha supuesto un gran avance en el campo del audio,
aunque su desarrollo ha sido relativamente lento por la gran cantidad de potencia de
cálculo que se requiere.
La primera aplicación comercial del procesado digital data de principios de los 80 en los
reproductores de CD, que incorporaban filtros FIR para mejorar la reconstrucción de la
señal analógica. En esta época todavía resultaba demasiado caro cualquier otro tipo de
procesado digital, de manera que los soportes digitales convivían con amplificadores y
ecualizadores analógicos. Incluso en los estudios de grabación se empleaba una amplia
variedad de equipos analógicos por la escasez de equipos y aplicaciones capaces de
procesar el audio digital.
A principios de los 90 hizo su aparición el DSP (Digital Signal Processor), un
procesador especializado para operaciones matemáticas en serie sobre grandes
cantidades de datos. El DSP permitió llevar a la práctica toda la teoría de procesado
digital que hasta entonces sólo podía ser probada en simulaciones, pero no en tiempo
real. Además el DSP era programable, lo cual permitía cambiar sobre la marcha el tipo
de procesado que se hacía sobre el audio. Hasta entonces la única manera económica de
disponer de un procesado digital era mediante circuitos programados sobre hardware,
como era el caso de los reproductores de CD.
Al principio el DSP era caro, ya que debía tener suficiente potencia para realizar por
software lo que otros circuitos hacían por hardware. Durante algunos años el procesado
digital de audio dependía de costosas tarjetas DSP que se añadían a los ordenadores para
realizar los cálculos que eran demasiado pesados para el procesador. Estas tarjetas eran
además propietarias, de manera que el hardware y la aplicación estaban estrechamente
relacionados.
A mediados de los 90 comenzaron a aparecer procesadores de uso general con
extensiones multimedia, es decir, con un DSP integrado. Progresivamente el juego de
instrucciones multimedia se fue popularizando e integrándose en las aplicaciones, y
comenzaron a aparecer los primeros programas de procesado de audio para todos los
públicos.
La consagración del procesado digital como norma general para el audio se produjo con
la popularización del formato MP3. La capa III de MPEG es una especificación para la
compresión de audio por transformada, que en lugar de almacenar la onda de audio en el
tiempo se almacena su transformada en frecuencia, con ciertos recortes para reducir la
tasa binaria. A partir de este momento el procesado digital saltó de los ordenadores a los
dispositivos portátiles, resultando cada vez más barato y más eficiente.
147
C A P Í T U L O 7 • Tratamiento Digital de Señal
7.2 Algoritmos básicos para audio
En esta sección se comentarán algunos algoritmos básicos en el procesado de audio.
Existen muchos más efectos, pero casi todos se basan en combinaciones de los que aquí
se mencionan.
Los efectos de audio se pueden clasificar en dos grupos básicos:
•
Lineales: el audio resultante es una combinación de las muestras presentes o
pasadas del audio inicial multiplicadas por coeficientes que son independientes
de la amplitud. Esto no significa que los coeficientes tengan que ser constantes,
sino que en un instante de tiempo dado se cumplan las propiedades de aditividad
y homogeneidad respecto de la señal de entrada. En esta categoría se encuadran
el filtrado, la ecualización, el mezclado, el retardo y la envolvente.
•
No lineales: el audio resultante depende del audio inicial con una relación que
no es una constante, sino una función del mismo. En esta categoría se encuadran
la compresión o expansión del rango dinámico, la distorsión y el recorte
(clipping).
Como veremos a continuación, muchos de los efectos que simulan procesos del
mundo real están modificados en alguno de sus parámetros por funciones que varían
con el tiempo.
7.2.1 Filtros
Un filtro es un procesado de sonido que tiene como resultado la reducción o
amplificación de la señal en alguna banda de frecuencia. El filtrado se consigue
mediante la suma de la señal original con diversas muestras retardadas y multiplicadas
por ciertos coeficientes.
Existen dos categoría básicas para la realización de filtros digitales:
• IIR (Infinite Impulse Response), o filtro recursivo: en este tipo de filtro la
señal de salida depende de la señal de entrada con diversos retardos y de la señal
de salida retardada, todas ellas multiplicadas por ciertos coeficientes. Al
depender la señal de salida de las muestras retardadas de la misma, se produce
un bucle de realimentación que mantiene la señal activa de manera infinita. En la
práctica, los coeficientes y la cuantización de los datos hacen que la señal
realimentada acabe extinguiéndose. También es posible que la señal de salida
oscile o se amplifique, pero estos no son efectos deseables en un filtro. Un filtro
IIR responde a la fórmula general:
y[n] = b0·x[n] + b1·x[n-1] + b2·x[n-2] + ... – a1·y[n-1] – a2·y[n-2] – ...
•
148
FIR (Finite Impulse Response), o filtro no recursivo: En este tipo de filtro la
señal de salida depende únicamente de las muestras de la señal original con
diversos retardos y multiplicadas por ciertos coeficientes. Por tanto, una vez
extinguida la señal de entrada, la señal de salida desaparecerá una vez
transcurrido el retardo del filtro. Una característica del filtro FIR es que la
C A P Í T U L O 7 • Tratamiento Digital de Señal
respuesta impulsional se corresponde con los valores de los coeficientes del
filtro, lo cual simplifica bastante la programación de una respuesta en frecuencia
determinada. Un filtro FIR responde a la fórmula general:
y[n] = b0·x[n] + b1·x[n-1] + b2·x[n-2] + ...
Para ambas categorías existen varias formas de construir el filtro según cómo se
desee optimizar el hardware asociado. Los elementos de diseño básicos son el retardo
(que es un registro), el multiplicador y el sumador. La estructura del filtro es muy
importante para optimizar el uso de recursos y la precisión de las operaciones. Para una
función de transferencia dada, existen diversas implementaciones posibles. Las
estructuras básicas son:
•
Forma directa I: esta estructura requiere el doble de celdas de retardo que el
orden del filtro.
Figura 7.1
•
Forma directa II o canónica: esta estructura requiere el mismo número de
celdas de retardo que el orden del filtro.
Figura 7.2
En el caso de un filtro FIR, la sección de coeficientes ai no existiría. Dicha sección
representa la parte recursiva del filtro, y es responsable de los polos de la función de
149
C A P Í T U L O 7 • Tratamiento Digital de Señal
transferencia. La sección de coeficientes bi representa los ceros de la función de
transferencia. En la cercanía de los polos, la función de transferencia aumenta mucho de
valor, pudiendo causar problemas de desbordamiento en los sumadores. En la cercanía
de los ceros, la función de transferencia se acerca a cero, pudiendo causar problemas de
cuantización. Es por ello que la elección de la estructura del filtro debe hacerse
cuidadosamente según la función que se desee implementar.
La ventaja de los filtros FIR es que se pueden realizar estructuras muy largas puesto
que la señal no se amplifica durante el camino. No obstante, para lograr una respuesta
abrupta se puede requerir una gran cantidad de etapas. Los coeficientes del filtro FIR
equivalen a las muestras de la respuesta impulsional, o dicho de otra manera, a la
transformada inversa de su respuesta en frecuencia, lo cual simplifica su realización.
Los filtros IIR pueden presentar grandes resonancias a lo largo de la estructura. En
consecuencia, se suele recurrir a encadenar filtros IIR de orden bajo para asegurar que la
señal no diverja demasiado. El diseño del filtro IIR es más complejo, ya que hay que
combinar ceros y polos para lograr la respuesta deseada. Una manera típica de diseñar
filtros IIR es usando las funciones de transferencia de filtros analógicos como
referencia.
7.2.2 Envolvente de volumen
Una envolvente de volumen es una modulación de la amplitud de la señal de forma
variable en el tiempo. Equivale a multiplicar el valor de cada muestra por el valor de la
función envolvente en el correspondiente instante de tiempo.
Las envolventes se emplean de manera sistemática en la síntesis de sonido para generar
sonidos más reales.
A continuación se describen algunos tipos de envolventes:
•
•
•
Fundido (fade): el fundido se suele utilizar al inicio de una pieza musical (fadein) o al final (fade-out) para lograr una transición suave de la música al silencio
y viceversa. En estos casos se aplica una envolvente exponencial para que,
combinada con la respuesta del oído al volumen, el oyente tenga una impresión
lineal.
Trémolo: el trémolo se utiliza para simular instrumentos musicales en la síntesis
de sonido, y suele ser una envolvente sinusoidal de frecuencia inferior a la
audible.
Envolvente de nota musical: modula la amplitud de la nota en 4 fases: ataque,
decaimiento, sostenido y liberación. Simula los distintos estados de vibración
por los que pasa un instrumento musical durante la generación de una nota. En el
caso de un instrumento de cuerda hay 4 fases, pero en otros puede haber menos.
7.2.3 Retardo
El retardo consiste en generar una copia retrasada del sonido original con el objeto
de mezclarla con éste. La señal retardada se manipula y se mezcla con el sonido original
para generar el efecto deseado.
El retardo es importante en muchos efectos musicales, entre ellos los filtros, pero
también otros más exóticos como el flanger y phaser.
150
C A P Í T U L O 7 • Tratamiento Digital de Señal
Hay varios tipos de efectos que se basan en el retardo de la señal:
•
•
•
Coros: consiste en mezclar una o más versiones retardadas del sonido con el
audio original. El retardo debe ser superior a 5 ms para ser percibido por el
oyente y para evitar la interferencia destructiva con la onda original. Las
versiones retardadas se pueden manipular para que se diferencien mejor del
sonido original. El efecto resultante se asemeja a un coro de voces o
instrumentos interpretando una misma pieza musical.
Eco: consiste en mezclar una o más versiones retardadas del sonido con el audio
original. El retardo debe ser superior a 50 ms para producir este efecto. Se
pueden añadir réplicas con distintos retardos para simular múltiples reflexiones
del sonido en la distancia.
Reverberación: se suele distinguir el eco de la reverberación por el número de
réplicas y el retardo entre las mismas. El eco tiene pocas réplicas que son
distinguibles en el tiempo por el oyente. En cambio, la reverberación está
compuesta de muchas réplicas que llegan en un intervalo reducido de tiempo (<1
ms). La reverberación representa mejor el efecto que se produce al propagarse el
sonido en un recinto cerrado. Para simular el efecto de la absorción del sonido
por los materiales las réplicas deben estar afectadas por una envolvente de
volumen. El tiempo que tarda en extinguirse el eco viene dado por la ecuación
de Sabine:
T=
V
c ⋅S⋅a
T: tiempo hasta que el eco se atenúa 60 dB (s)
c: velocidad del sonido en el aire (340 m/s)
V: volumen del recinto (m3)
S: superficie del recinto (suelo, techo y paredes) (m2)
a: coeficiente de absorción de los materiales
Figura 7.3. Nivel sonoro para un recinto con paredes desnudas (rojo) y el mismo
recinto con moqueta (negro).
• Phaser: consiste en mezclar el sonido original con una versión del mismo con
desfase constante, es decir, una copia exacta donde todas las frecuencias han
sufrido el mismo desfase. No se trata de un simple retardo temporal, sino de un
filtrado de fase lineal. El resultado es un sonido de aspecto sintético,
especialmente cuando se usa sobre voces.
• Flanger: consiste en mezclar la señal original con una versión que tiene un
retardo variable controlado por otra señal. El resultado de mezclar una señal con
otra versión retardada es que se produce un filtrado en peine debido a la
151
C A P Í T U L O 7 • Tratamiento Digital de Señal
interferencia destructiva en frecuencias equiespaciadas, por lo que el espectro
resultante presenta picos y valles que van cambiando de posición según la señal
moduladora. Para que se aprecie la variación espectral, el sonido original debe
ser rico en armónicos, de manera que siempre quede algo de señal después del
filtrado en peine.
7.2.4 Compresión
La compresión es un efecto no lineal que reduce el rango dinámico de una señal de
audio, es decir, disminuye la diferencia entre la amplitud máxima y la mínima. Este
efecto se consigue aplicando una función para la que la relación entre el valor de salida
y el de entrada sea menor cuanto mayor sea la amplitud del segundo. Este efecto se
utiliza mucho en producción para acomodar sonidos tenues e intensos sobre un registro
con rango dinámico limitado sin que se pierdan los primeros ni se saturen los segundos.
Este efecto se usaría para convertir una grabación de una con 20 bits a un formato CD
de 16 bits.
En la siguiente figura podemos observar la relación entre el nivel de entrada de la
señal de audio, el nivel de salida, y la reducción de ganancia como consecuencia de la
compresión.
Figura 7.4. Función de transferencia de un compresor de rango dinámico.
152
C A P Í T U L O 7 • Tratamiento Digital de Señal
7.3 Transformada rápida de Fourier (FFT)
Comenzaremos explicando la forma analítica de la transformada de Fourier de una
señal discreta en el tiempo (muestreada), también conocida como DTFT (Discrete Time
Fourier Transform). Existen numerosas analogías entre la transformada de una señal
continua y una discreta, aunque también hay diferencias esenciales que a continuación
describiremos.
La transformada de Fourier para una señal discreta x[n] se define como:
X(ω) =
n =∞
∑ x[n]e
− jωn
n = −∞
Donde x[n] es una señal discreta infinita en el tiempo y X(ω) es una función
continua que se repite periódicamente con un intervalo 2π. Comúnmente se utiliza el
intervalo [-π, π] para referirse al rango de frecuencia de la señal discreta. Obsérvese que
el rango de frecuencia es adimensional, es decir, no está referido a ninguna unidad de
medida de frecuencia ya que la señal muestreada en el tiempo también es adimensional,
pues al muestrearla se convierte en una sucesión de valores sin escala temporal. Por
tanto, se habla de ω como la frecuencia normalizada en radianes, aunque también se
puede sustituir ω por 2πf, en cuyo caso la frecuencia normalizada representa la relación
f/fs (fs = frecuencia de muestreo) y su intervalo es de [-0.5, 0.5].
Esta periodicidad es la principal diferencia con la transformada continua de Fourier,
que existe de -∞ a ∞. En este punto es donde debemos enlazar con el capítulo 1 de
muestreo de señal. La frecuencia π representa la frecuencia de Nyquist de la señal
discreta. Recuérdese que el espectro de una señal muestreada se repite periódicamente y
que toda componente frecuencial que se encuentre por encima de la frecuencia de
Nyquist aparece alienada como una frecuencia dentro del rango permitido (figura 7.5).
La explicación de este efecto de “aliasing” la tenemos en la propia definición de la
transformada.
En Tratamiento Digital de Señal la notación, e-jωn también se puede representar
como cos(ωn) + j·sen(ωn), donde j es la unidad imaginaria, también representada como
i.
Figura 7.5. Muestreo de una señal y alisasing.
153
C A P Í T U L O 7 • Tratamiento Digital de Señal
Si x[n] es una señal real, como es habitualmente el caso en el muestreo de audio, la
transformada X(ω) es una función donde la parte negativa es la conjugada de la parte
positiva, es decir X*(-ω) = X(ω). Si tenemos en cuenta que la transformada se puede
representar en términos de amplitud y fase, una señal real produciría una transformada
cuyo módulo es simétrico respecto a cero mientras que la fase tiene signo distinto a un
lado y otro del cero. Por este motivo, en muchos casos tan sólo se representa la
magnitud de la transformada entre 0 y π.
En un sistema de procesado de señal las señales en el tiempo no son infinitas y
tampoco se puede representar numéricamente una transformada que es una función
continua. Para este caso existe una nueva aproximación de la transformada que es la
transformada discreta de Fourier o DFT (Discrete Fourier Transform) donde tanto la
señal en el tiempo como la señal transformada son discretas y limitadas en el tiempo.
La transformada discreta de Fourier de una señal discreta x[n] sobre un número de
muestras N se define como:
N −1
X[k ] = ∑ x[n]e
−j
2 πkn
N
n =0
La transformada discreta es una aproximación de la transformada continua bajo
ciertas condiciones. Si la señal temporal que se analiza es un ciclo exacto de una señal
periódica entonces la transformada discreta es una versión muestreada de la
transformada continua. Sin embargo, esto no es normalmente así en la señales de audio
muestreadas, y por tanto la transformada de una porción de audio dará como resultado
una aproximación de su contenido en frecuencia con ciertos artefactos. Estos artefactos
son consecuencia del enventanado.
Si tomamos una porción de una señal senoidal muestreada y calculamos su
transformada, muy probablemente lo que veremos no será una raya a la frecuencia
normalizada de la senoidal, sino un lóbulo principal más o menos ancho acompañado de
varios lóbulos laterales de amplitud decreciente a ambos lados del primero, como se
muestra en la figura 7.6. Lo que ha ocurrido es que transformada se ha convolucionado
con transformada de la función rectangular, que es la “máscara” que hemos utilizado
para seleccionar un fragmento de la señal senoidal, que por definición es infinita. Tan
sólo si el fragmento seleccionado se corresponde exactamente a un ciclo de la señal
senoidal la transformada discreta de una señal finita coincidirá completamente con la
transformada continua de una señal infinita.
154
C A P Í T U L O 7 • Tratamiento Digital de Señal
Figura 7.6. Transformada discreta de una porción de señal sinusoidal.
Esta “máscara” utilizada para seleccionar una porción de la señal temporal se
denomina ventana, y la forma de la misma determina la resolución con la cual se podrán
distinguir las componentes espectrales de la señal temporal. Hay muchos tipos de
ventanas según se quiera mejorar la resolución espectral o el rango dinámico del
espectro. Las ventanas más comunes para mejorar la resolución espectral (para
distinguir mejor las componentes espectrales) son las de von Hann, Hamming,
Blackman y coseno alzado. Las ventanas más comunes para mejorar el rango dinámico
de la transformada son las de Blackman. A diferencia de la ventana rectangular, todas
estas ventanas
El uso de una ventana es importante cuando se realiza la transformada de una señal
de audio al vuelo, ya que se debe seleccionar una porción de audio suficientemente
larga para representar todas las componentes espectrales pero suficientemente corta para
generar una carga computacional asequible y un refresco del espectro razonable.
El aspecto computacional es importante, ya que la transformada exige un gran
número de sumas, multiplicaciones y operaciones trigonométricas que requieren
búsquedas en tablas. Para reducir esta carga computacional está la transformada rápida
de Fourier or FFT (Fast Fourier Transform) que explota ciertas propiedades de la
transformada para reducir el número de operaciones. Así, mientras que la DFT requiere
N2 multiplicaciones complejas, algoritmos como el de Cooley-Tukey requieren tan sólo
(N/2) log2 N. La restricción de este algoritmo es que sólo puede hacerse sobre un
número de muestras que sea potencia de 2, aunque existen otros algoritmos para
descomponer muestras de tamaño arbitrario en secciones donde se pueden aplicar los
algoritmos rápidos.
El algoritmo de mariposa o de Cooley-Tukey consiste en calcular la FFT como
combinación de otras FFTs de la mitad de longitud (figura 7.7), prosiguiendo
recursivamente hasta llegar a la transformada elemental de 2 muestras (figura 7.8). Esta
transformada de dos muestras se calcula de forma muy simple como sumas y restas de
las muestras de entrada. Al combinar las FFTs se utilizan unos multiplicadores que
corresponden a valores de la exponencial e-jωn evaluada en posiciones fijas.
155
C A P Í T U L O 7 • Tratamiento Digital de Señal
Figura 7.7. Descomposición de la transformada en transformadas menores mediante
diezmado y operaciones en mariposa.
Figura 7.8. FFT básica de 2 puntos, también conocida como operación mariposa.
La esencia del algoritmo Cooley-Tukey es separar la FFT en muestras pares e
impares, de manera que el sumatorio inicial
N −1
X[k ] = ∑ x[n]e
−j
2 πkn
N
n =0
se transforma en
X[k ] =
N / 2 −1
∑
x[2n]e
−j
2 πk ( 2 n )
N
+
N / 2 −1
∑
n =0
x[2n + 1]e
−j
2 πk ( 2 n +1)
N
n =0
Donde el primer sumatorio corresponde a las muestras pares y el segundo a las
impares. Si extraemos el término e
reescribirse como:
X[k ] =
N / 2 −1
∑ x[2n]e
−j
−j
2 πk
N
2 πk ( 2 n )
N
del segundo sumatorio, la expresión puede
+e
−j
2 πk N / 2 −1
N
∑ x[2n + 1]e
n =0
−j
2 πk ( 2 n )
N
n =0
Obsérvese que la primera parte es sencillamente la DFT de las muestras pares,
mientras que la segunda parte es la DFT de las muestras impares multiplicada por un
coeficiente. Es decir:
−j
2 πk
N
X[k ] = DFT(pares) + e
DFT(impares)
Dicho coeficiente se denomina factor de alternancia y habitualmente se representa
como WNk . Si la transformada tuviera sólo dos muestras (N=2), dicho término valdría 1
156
C A P Í T U L O 7 • Tratamiento Digital de Señal
para k=0 y –1 para k=1. En consecuencia se reduce enormemente el número de
operaciones con números complejos a cambio de una mayor complejidad en los cruces
de muestras mediante operaciones en mariposa, lo cual no es un problema para un
algoritmo recursivo.
Un problema adicional cuando se calcula una DFT al vuelo para representar
dinámicamente el espectrograma de un sonido es que al tomar bloques consecutivos de
muestras existen discontinuidades que pueden causar grandes diferencias en el espectro
resultante. Una forma de evitar este problema es tomar secciones de audio solapadas y
aplicar un enventanado, de manera que el espectro resultante refleje mejor las
variaciones del contenido musical. Este método se conoce como Transformada de
Fourier a Corto Plazo (Discrete-Time STFT). Esta transformada se parece a la DFT pero
se incluye un término de enventanado w[n].
N −1
X[k , m] = ∑ w[n − m] ⋅ x[n]e
−j
2 πkn
N
n =0
157
C A P Í T U L O 7 • Tratamiento Digital de Señal
7.4 Librerías C++
7.4.1 Modelos de librerías
Comenzaremos esta sección de programación introduciendo los elementos software
básicos que implementarán los conceptos previamente explicados.
En el desarrollo de aplicaciones en C++ para Tratamiento Digital de señal se pueden
utilizar las librerías industriales de las compañías Intel o AMD, o en su caso las librerías
de código fuente abierto “Spuc”, que es el acrónimo de Signal Processing Using C++
A DSP Library.
Puesto que algunas librerías enunciadas anteriormente son comerciales o
excesivamente complejas, en este apartado proponemos una librería para Visual C++
que simplifica en gran medida el desarrollo de aplicaciones de Tratamiento Digital de
Señales. Esta librería es de la compañía Mitov, que ofrece una serie de componentes en
varios lenguajes dentro de la plataforma Win32 y .NET para el desarrollo de
aplicaciones de manejo de señal de alto rendimiento.
7.4.2 Mitov
El software de Mitov está especializado en el desarrollo de procesado rápido de
señal para audio y video. Las librerías están orientadas a su utilización en plataformas
Windows bajo los compiladores de Delphi, Borland C++ Builder, Visual C++ con MFC
y .NET.
En el presente capítulo se va a explicar cómo utilizar los componentes básicos de la
librería dentro del entorno de desarrollo de Microsoft Visual C++.1
El paquete software podemos localizarlo a través de la URL http://www.mitov.com.
Una vez descargado, se puede leer en la documentación de la licencia que el
producto software es de pago para aplicaciones con salida comercial, mientras que las
aplicaciones utilizadas con fines educativos no implican el pago de ninguna cantidad.
7.4.3 Principales librerías de Mitov
Los principales componentes incluidos en la “suite” de la compañía Mitov son los
que a continuación se enumeran:
•
•
•
•
1
VideoLab: es un conjunto de objetos para la captura, reproducción, procesado,
manipulaciones geométricas, mezclado, análisis y visualización. Ofrece, entre
otras posibilidades, manipulaciones rápidas y complejas de video.
AudioLab: permite la captura, reproducción, procesado, mezclado, análisis y
visualización de audio. Generalmente utiliza como formato de entrada la
extensión WAV.
SignalLab: conjunto de componentes para el procesamiento rápido de señal
(DSP). Contiene objetos para la visualización de la señal por medio de
osciloscopios.
VisionLab: es una colección de objetos orientados a la visión básica por
computador. El componente permite el desarrollo de aplicaciones de detección
Se compilará con el IDE Microsoft Visual C++ 2005 8.0
158
C A P Í T U L O 7 • Tratamiento Digital de Señal
•
de movimiento para la industria de la seguridad. Incluye algoritmos para la
detección de contornos, rostros y objetos.
PlotLab: componente para el desarrollo de aplicaciones que requieren el uso de
gráficas y representación estadística de datos.
Éstas son a grandes rasgos los principales componentes o librerías de la “suite”
del software de Mitov. Las librerías concernientes a este capítulo serán AudioLab y
SignalLab, para la reproducción de audio y el tratamiento digital de señales
respectivamente.
7.4.4 Conceptos básicos de las librerías SingalLab y AudioLab
Como se ha comentado anteriormente, la librería SignalLab permite la manipulación
de señal por medio de varios objetos fundamentales. Gracias a ellos es posible
programar aplicaciones de señal en Visual C++ con apoyo de las librerías de MFC
(Microsoft Foundation Classes).
Comenzaremos explicando esta primera librería básica. Con el fin de desarrollar una
aplicación con SingalLab o AudioLab es imprescindible, en primera instancia, crear un
proyecto del tipo MFC, aplicación de diálogo, en un entorno de desarrollo de Microsoft.
La peculiaridad de los objetos de visualización de gráficas (Scopes), es que se requiere
enlazar el identificador de un componente tipo Label, sobre una aplicación basada en un
cuadro de Diálogo, con un objeto del tipo CTSLScope. De esta manera se obtendría el
siguiente código que se muestra en el ejemplo:
// SignalGeneratorDlg.h : fichero cabecera
//
#pragma once
#include "afxwin.h"
#include <CSLScope.h> // Ficheros cabecera de la librería
#include <CSLSignalGen.h>
// CSignalGeneratorDlg dialog
class CSignalGeneratorDlg : public CDialog
{
// Construcción
public:
CSignalGeneratorDlg(CWnd* pParent = NULL); // Constructor
// estándar
// Datos del diálogo
enum { IDD = IDD_SIGNALGENERATOR_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV
// para intercambio de datos dinámico
// Implementación
protected:
CTSLScope SLScope; // Objeto para visualización
CTSLSginalGen SLSignalGen; // Objeto para generación de señal
159
C A P Í T U L O 7 • Tratamiento Digital de Señal
protected:
HICON m_hIcon;
Finalmente, se requiere enlazar el handler de ventana del control Label con el
objeto SLScope, dentro del método ::OnInitDialog() de punto de entrada de la
aplicación, tal como se muestra en el siguiente fragmento de código fuente:
VCL_InitControls( m_hWnd );
SLScope.Open( m_Scope.m_hWnd ); // Aquí enlazamos el handler
SLSignalGen.OutputPin.Connect( SLScope.InputPins[ 0 ] );
VCL_Loaded();
return TRUE; // return TRUE unless you set the focus to a
control
}
El resultado se ilustra en la siguiente figura:
Figura 7.9 Generación de una forma de onda básica
Como se puede observar, con muy pocas líneas de código hemos conseguido una
pequeña y básica aplicación que genera una señal elemental sinusoidal y la representa
en un osciloscopio virtual.
Otra cuestión clave es la interconectividad entre los objetos de señal, es decir, entre
el objeto de generación de señal básica y el objeto del osciloscopio. Para conseguir este
propósito se recurre a las propiedades de los objetos OutputPin e InputPin, que son las
responsables de conectar la salida de generación de onda con la entrada del
osciloscopio. De esta manera, y como se explica en el código fuente se lleva a cabo de
la siguiente forma:
SLSignalGen.OutputPin.Connect( SLScope.InputPins[ 0 ] );
160
C A P Í T U L O 7 • Tratamiento Digital de Señal
Figura 7.10 Diagrama de interconexión entre los objetos
Con esto conseguimos conectar la salida del generador de señal con una de las
entradas del osciloscopio, puesto que es posible conectar varias entradas para la
visualización de señal, con sólo cambiar el índice del array de la propiedad, en el modo
que se muestra en el siguiente fragmento de código:
SLSignalGen1.OutputPin.Connect( SLScope.InputPins[ 0 ] );
SLSignalGen2.OutputPin.Connect( SLScope.InputPins[ 1 ] );
SLSignalGen3.OutputPin.Connect( SLScope.InputPins[ 2 ] );
.
.
.
Explicaremos ahora el funcionamiento trivial del componente AudioLab. Es
importante saber que son imprescindibles dos objetos básicos para la lectura de un
fichero de sonido y la salida de audio por el altavoz del sistema. Etos son
CTALWavePlayer y CTALAudioOut respectivamente.
El primero de ellos permite la lectura de un fichero en formato WAV que
posteriormente será redirigirlo a un objeto de SignalLab o al propio objeto de salida de
audio. El funcionamiento de estos objetos es muy simple y sigue la misma filosofía que
se ha explicado en el ejemplo de la generación de una señal de sonido básica. En este
caso, la interconexión para la lectura de un fichero y su reproducción por uno de los
canales de salida de audio del sistema, se realizaría de la siguiente manera que se
muestra a continuación:
{
// Construcción
public:
CAudioPlayerDlg(CWnd* pParent = NULL); // Constructor
estándar
// Datos del diálogo
enum { IDD = IDD_AUDIOPLAYER_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV
// para intercambio dinámico de datos
// Implementación
protected:
CTALWavePlayer ALWavePlayer; // Objeto reproductor de sonido
CTALAudioOut ALAudioOut; // Objeto de salida de audio
protected:
HICON m_hIcon;
// Funciones miembro
virtual BOOL OnInitDialog();
161
C A P Í T U L O 7 • Tratamiento Digital de Señal
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
};
Como podemos observar, en primer lugar se declaran los objetos CTALWavePlayer
y CTALAudioOut.
Por último se interconectan en uno de los métodos de la aplicación basada en
diálogos; pero en este caso en concreto, se utiliza el método OnInitDialog como punto
de entrada a la aplicación. En el código que se muestra a continuación podemos apreciar
el funcionamiento de los respectivos objetos de AudioLab:
BOOL CAudioPlayerDlg::OnInitDialog()
{
CDialog::OnInitDialog();
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING,
IDM_ABOUTBOX,
strAboutMenu);
}
}
SetIcon(m_hIcon, TRUE); // Establece icono grande
SetIcon(m_hIcon, FALSE); //Establece icono pequeño
// Lectura del fichero WAV
ALWavePlayer.FileName = "C:\\Program Files\\LabPacks\\Visual
C++\\Demos\\AVIFiles\\Demo.wav";
// Redirección al canal de salida de audio
ALWavePlayer.OutputPin.Connect( ALAudioOut.InputPin );
VCL_Loaded(); // Inicio de los controles Mitov
return TRUE;
}
Obviamente, en la primera línea en negrita indicamos el fichero de sonido WAV2
que necesitamos leer, y por último, interconectamos la salida del objeto reproductor con
la entrada del objeto de salida de audio en el sistema.
2
En este tratado únicamente se utilizará el formato WAV por su calidad; aunque es posible utilizar otros
formatos en Mitov como WMA.
162
C A P Í T U L O 7 • Tratamiento Digital de Señal
En la siguiente figura se ilustra dicha interconexión:
Figura 7.11 Diagrama de conexión entre los objetos
Para concluir y con el fin de que funcionen correctamente todas las librerías dentro
de la aplicación Visual C++ no debemos olvidar llamar a la función VCL_Loaded() que
se responsabilizará de iniciar y arrancar todo el sistema de objetos SignalLab y
AudioLab.
7.5 Desarrollo de una aplicación de procesado de señal
7.5.1 Diagrama de bloques
El propósito de este apartado es explicar cómo se desarrolla una aplicación completa
de procesado digital de audio en un entorno conocido como es Visual C++.
Con el fin de exponer un ejemplo práctico del mundo real, se ha pretendido
implementar un programa que realiza varias operaciones elementales con un objetivo
obviamente instructivo de tratamiento de señal.
La aplicación consiste en una ventana de diálogo que importa un fichero en formato
WAV y realiza cuatro operaciones básicas interconectadas de señal: filtrado paso bajo,
filtrado paso banda, filtrado paso alto y ecualización de diez bandas. La salida de esta
secuencia será conectada a su vez, a tres osciloscopios. Estos osciloscopios son:
representación tiempo frecuencia, onda y espectro FFT. En la imagen que se muestra
debajo de esta líneas podemos apreciar la interconexión en el diagrama de bloques:
Tiempo
frecuencia
in
out
Reproductor
ON / OFF
in
Filtro paso
bajo
out
ON / OFF
in
Filtro paso
banda
out
ON / OFF
in
out
Filtro paso alto
in
Onda
in
Espectro
(FFT)
in
Altavoz del
sistema
out
in
Ecualizador
ON / OFF
Figura 7.12 Diagrama de bloques de la aplicación
163
C A P Í T U L O 7 • Tratamiento Digital de Señal
7.5.2 Declaración de objetos de librería
Para comenzar a implementar la aplicación, es requisito imprescindible declarar los
objetos que se van a utilizar a lo largo de la aplicación de ejemplo. Con este fin, hemos
de utilizar los siguientes objetos que se declararán inicialmente en el fichero de cabecera
de la clase del diálogo principal:
// tdsDlg.h : Fichero cabecera
//
#pragma once
// Incluye cabeceras de los ficheros de librería
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
<CSLScope.h>
<CALWavePlayer.h>
<CALAudioOut.h>
<CALSpectrum.h>
<CSLWaterfall.h>
<CALLowPass.h>
<CALBandPass.h>
<CALHighPass.h>
<CALGraphicEqualizer.h>
<CSLFourier.h>
"afxwin.h"
"afxcmn.h"
// CtdsDlg dialog
class CtdsDlg : public CDialog
{
// Construcción
public:
CtdsDlg(CWnd* pParent = NULL);
// Constructor estándar
// Datos del diálogo
enum { IDD = IDD_TDS_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX);
// para intercambio dinámico de datos
// DDX/DDV
// Implementación
protected:
CTSLWaterfall SLScope; // Scope Waterfall
CTSLScope SLScope2;
// Scope en tiempo
CTSLScope SLScope3;
// Scope para Fourier
// Objetos de SignalLab y AudioLab
CTSLFourier SLFourier;
// Objeto
CTALLowPass SLLowPass;
// Objeto
CTALBandPass SLBandPass; // Objeto
CTALHighPass SLHighPass; // Objeto
CTALGraphicEqualizer SLEqualizer;
CTALWavePlayer ALWavePlayer;
CTALAudioOut ALAudioOut; // Objeto
CTALSpectrum ALSpectrum; // Objeto
164
que realiza FFT
filtro paso bajo
filtro paso banda
filtro paso alto
// Objeto ecualizador
// Objeto reproductor
salida de audio
espectro
C A P Í T U L O 7 • Tratamiento Digital de Señal
Como podemos observar se declararán los objetos dentro de la clase que hereda
de CDialog en la parte protected de la misma.
7.5.3 Inicialización e interconexión de objetos
Con la idea de poder comenzar la aplicación con unos valores iniciales por defecto,
llamaremos a los constructores y métodos de inicialización de los objetos que se han
declarado en la sección anterior. En el siguiente fragmento de código podemos observar
la inicialización tanto de los objetos de librería, como de los controles GUI de la
aplicación visual. Para conseguir esto sobrecargamos el método OnInitDialog de MFC
donde comenzamos a iniciar objetos:
// Manejador de mensaje OnInitDialog
BOOL CtdsDlg::OnInitDialog()
{
CDialog::OnInitDialog();
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX,
strAboutMenu);
}
}
SetIcon(m_hIcon, TRUE);
SetIcon(m_hIcon, FALSE);
// Establece icono grande
// Establece icono pequeño
VCL_InitControls(m_hWnd); // Inicia librería
SLScope.Open(m_Scope.m_hWnd); // Conecta scopes con handlers de
controles
SLScope2.Open(m_Scope2.m_hWnd);
SLScope3.Open(m_Scope3.m_hWnd);
SLScope3.Visible = false; // Fourier no es visible al principio
// Cambia títulos de los scopes
SLScope.Title.Text = "Tiempo frecuencia";
SLScope2.Title.Text = "Onda";
SLScope3.Title.Text = "Espectro (FFT)";
// Conecta el Wave player con el filtro paso bajo
ALWavePlayer.OutputPin.Connect(SLLowPass.InputPin);
// Desactiva filtros
165
C A P Í T U L O 7 • Tratamiento Digital de Señal
SLLowPass.Enabled
SLBandPass.Enabled
SLHighPass.Enabled
SLEqualizer.Enabled
=
=
=
=
false;
false;
false;
false;
// Inicia frecuencias de los filtros
SLLowPass.Frequency = 5000;
SLBandPass.FreqHigh = 11000;
SLBandPass.FreqLow
= 9000;
SLHighPass.Frequency = 5000;
SLEqualizer.Channels.Add(10);
// Establece coeficientes y frecuencias en el ecualizador
SLEqualizer.Channels[0].Coefficient = 1.0;
SLEqualizer.Channels[0].Frequency
= 31.25;
SLEqualizer.Channels[1].Coefficient = 1.0;
SLEqualizer.Channels[1].Frequency
= 62.5;
SLEqualizer.Channels[2].Coefficient = 1.0;
SLEqualizer.Channels[2].Frequency
= 125.0;
SLEqualizer.Channels[3].Coefficient = 1.0;
SLEqualizer.Channels[3].Frequency
= 250.0;
SLEqualizer.Channels[4].Coefficient = 1.0;
SLEqualizer.Channels[4].Frequency
= 500.0;
SLEqualizer.Channels[5].Coefficient = 1.0;
SLEqualizer.Channels[5].Frequency
= 1000.0;
SLEqualizer.Channels[6].Coefficient = 1.0;
SLEqualizer.Channels[6].Frequency
= 2000.0;
SLEqualizer.Channels[7].Coefficient = 1.0;
SLEqualizer.Channels[7].Frequency
= 4000.0;
SLEqualizer.Channels[8].Coefficient = 1.0;
SLEqualizer.Channels[8].Frequency
= 8000.0;
SLEqualizer.Channels[9].Coefficient = 1.0;
SLEqualizer.Channels[9].Frequency
= 16000.0;
SLScope3.Channels.Clear();
SLScope3.Channels.Add();
// Conecta filtro paso bajo con filtro paso banda
SLLowPass.OutputPin.Connect(SLBandPass.InputPin);
// Conecta filtro paso banda con filtro paso alto
SLBandPass.OutputPin.Connect(SLHighPass.InputPin);
// Conecta filtro paso alto con ecualizador
SLHighPass.OutputPin.Connect(SLEqualizer.InputPin);
166
C A P Í T U L O 7 • Tratamiento Digital de Señal
// Conecta ecualizador con scopes
SLEqualizer.OutputPin.Connect(SLFourier.InputPin);
SLFourier.SpectrumOutputPin.Connect(SLScope3.InputPins[0]);
SLEqualizer.OutputPin.Connect(ALAudioOut.InputPin);
SLEqualizer.OutputPin.Connect(SLScope2.InputPins[0]);
SLEqualizer.OutputPin.Connect(ALSpectrum.InputPin);
ALSpectrum.OutputPins[0].Connect(SLScope.InputPin );
// Inicia controles de librería
VCL_Loaded();
IniciaSliders();
IniciaVariables();
setIconos();
SLScope.Levels.Axis.Autoscale = false;
return TRUE;
control
}
// return TRUE
unless you set the focus to a
En las primeras líneas se detalla cómo interconectar los controles Label con los
objetos CTSLScopes a través de sus handlers de ventana. A continuación se conecta el
reproductor de audio con el objeto filtro paso bajo.
Para comenzar la aplicación de una manera clara y comprensible se ha preferido
desactivar por defecto los objetos de los filtros y el ecualizador, de esta manera se
puede observar en el código cómo se establece a false las propiedades Enable de dichos
objetos. A continuación se inician las frecuencias de corte de los filtros, así como los
coeficientes y frecuencias del ecualizador. Como última acción, se procede a
inerconectar los objetos de procesado de señal tal como se ilustró en el digrama de
bloques de la figura 7.12
7.5.4 Comportamiento de los filtros y el ecualizador
La última parte de la aplicación es implementar el comportamiento de los filtros y el
ecualizador. Para implementar los filtros, se ha capturado el mensaje Win32 de
desplazamiento de sliders horizontales con el fin de calcular sus propiedades básicas, de
esta manera tenemos el siguiente código del método sobrecargado:
// Manejador de mensajes de los sliders horizontales
afx_msg void CtdsDlg::OnHScroll(UINT SBCode,UINT nPos,CScrollBar *SB)
{
// Mensaje del slider paso bajo
if (SB==(CScrollBar *)&m_SliderBajo)
SLLowPass.Frequency = m_SliderBajo.GetPos();
// Mensaje del slider paso banda
else if ((SB==(CScrollBar *)&m_SliderFreqBanda) ||
(SB==(CScrollBar *)&m_SliderAnchoBanda))
{
167
C A P Í T U L O 7 • Tratamiento Digital de Señal
double freqlow = (m_SliderFreqBanda.GetPos() m_SliderAnchoBanda.GetPos())/2;
if (freqlow < 20.0) freqlow = 20.0;
SLBandPass.FreqLow = freqlow;
double freqhigh = (m_SliderFreqBanda.GetPos() +
m_SliderAnchoBanda.GetPos())/2;
if (freqhigh > 20000.0) freqhigh = 20000.0;
SLBandPass.FreqHigh = freqhigh;
// Mensaje del slider paso alto
} else if (SB==(CScrollBar *)&m_SliderFreqAlto)
{
SLHighPass.Frequency = m_SliderFreqAlto.GetPos();
}
}
Como se aprecia en el código, tanto para el objeto filtro paso bajo como el filtro
paso alto, nos hemos limitado a aplicar la frecuencia de corte a partir de los valores del
slider. De esta manera el filtro paso bajo obtiene unos valores comprendidos entre los
30Hz y los 10 KHz, y el el filtro paso alto unos valores comprendidos entre 30 Hz y los
15 Khz para sus sliders. Sin embargo, para el filtro paso banda se le ha aplicado la
siguiente ecuación que expresa las frecuencias de corte:
Frecuenciaalta = frecuenciacentral +
anchobanda
2
Frecuenciabaja = frecuenciacentral −
anchobanda
2
Ya sólo resta implementar el comportamiento de ecualizador. Para este propósito se
ha capturado el mensaje de movimiento vertical de los sliders, puesto que en sistemas
hardware reales típicamente se diseñan en esta posición y por este motivo se han elegido
controles GUI que lo imiten. En el siguiente fragmento de código que se expone a
continuación se puede observar cómo se obtienen los valores de los coeficientes para el
ecualizador:
// Manejador de mensajes de los sliders verticales (ecualizador)
afx_msg void CtdsDlg::OnVScroll(UINT SBCode,UINT nPos,CScrollBar *SB)
{
if (SB==(CScrollBar *)&m_Slider1)
SLEqualizer.Channels[0].Coefficient
m_Slider1.GetPos()) / 100.0;
else if (SB==(CScrollBar *)&m_Slider2)
SLEqualizer.Channels[1].Coefficient
m_Slider2.GetPos()) / 100.0;
else if (SB==(CScrollBar *)&m_Slider3)
SLEqualizer.Channels[2].Coefficient
m_Slider3.GetPos()) / 100.0;
else if (SB==(CScrollBar *)&m_Slider4)
SLEqualizer.Channels[3].Coefficient
m_Slider4.GetPos()) / 100.0;
168
= (200.0 -
= (200.0 -
= (200.0 -
= (200.0 -
C A P Í T U L O 7 • Tratamiento Digital de Señal
else if (SB==(CScrollBar *)&m_Slider5)
SLEqualizer.Channels[4].Coefficient
m_Slider5.GetPos()) / 100.0;
else if (SB==(CScrollBar *)&m_Slider6)
SLEqualizer.Channels[5].Coefficient
m_Slider6.GetPos()) / 100.0;
else if (SB==(CScrollBar *)&m_Slider7)
SLEqualizer.Channels[6].Coefficient
m_Slider7.GetPos()) / 100.0;
else if (SB==(CScrollBar *)&m_Slider8)
SLEqualizer.Channels[7].Coefficient
m_Slider8.GetPos()) / 100.0;
else if (SB==(CScrollBar *)&m_Slider9)
SLEqualizer.Channels[8].Coefficient
m_Slider9.GetPos()) / 100.0;
else if (SB==(CScrollBar *)&m_Slider10)
SLEqualizer.Channels[9].Coefficient
m_Slider10.GetPos()) / 100.0;
}
= (200.0 -
= (200.0 -
= (200.0 -
= (200.0 -
= (200.0 -
= (200.0 -
De esta forma se calculan los coeficientes para cada canal del ecualizador con la
siguiente ecuación:
Coeficientecanal =
200 − decibeliosbanda
100
169
C A P Í T U L O 7 • Tratamiento Digital de Señal
7.5.5 Aspecto final de la aplicación
Una vez escrito el código explicado anteriormente y ejecutada la aplicación
obtendremos la siguiente interfaz de usuario:
Figura 7.13 GUI de la aplicación de ejemplo
En la parte izquierda podemos ver el osciloscopio para la respuesta en tiempo
frecuencia, como una representación en vista de planta de la distribución de las
frecuencias y sus amplitudes, mientras que en la parte derecha se visualiza la
representación FFT de la misma señal donde se aprecia claramente en qué rango del
espectro se aglutinan las principales frecuencias.
Además de la visualización de estos controles gráficos, se permite la posibilidad de
aplicar un efecto de filtro paso bajo, cuyo objetivo es atenuar las frecuencias altas,
mientras que el filtro paso alto atenúa las frecuencias bajas. El filtro paso banda se
encarga de concentrar las frecuencias de la señal en una parte del espectro, por medio
del control Frecuencia, mientras que el control Ancho es el responsable de ampliar o
reducir el rango de frecuencias filtradas dentro del espectro.
Por último, el ecualizador es el responsable de aumentar o disminuir la
intensidad en amplitud de las bandas de frecuencia seleccionadas. Si arrancamos la
aplicación y activamos el objeto ecualizador podemos apreciar bien cómo se cambia la
intensidad de las diferentes bandas al tiempo que movemos verticalmente los sliders.
170
C A P Í T U L O 7 • Tratamiento Digital de Señal
Bibliografía y referencias
referencias
Libros
Programación con MFC 6.0 – Herbert Schildt. Editorial Osborne McGraw-Hill, 1999.
La Web
Mitov documentation
http://www.mitov.com
MSDN
http://msdn2.microsoft.com/es-es/default.aspx
Wikipedia:
Efectos de audio:
http://en.wikipedia.org/wiki/Digital_audio_editor
http://en.wikipedia.org/wiki/Sound_effect
http://paws.kettering.edu/~drussell/demos.html
FFT:
http://en.wikipedia.org/wiki/Cooley–Tukey_FFT_algorithm
http://en.wikipedia.org/wiki/Butterfly_diagram
171
APÉNDICE
A
Apéndice
A1. Mensajes de la especificación MIDI 1.0
Byte estado
D7----D0
Byte(s) datos
D7----D0
Descripción
Mensajes de canal
1000cccc
0nnnnnnn
0vvvvvvv
1001cccc
0nnnnnnn
0vvvvvvv
1010cccc
0nnnnnnn
0vvvvvvv
1011cccc
0nnnnnnn
0vvvvvvv
1100cccc
0ppppppp
1101nnnn
0ccccccc
1110nnnn
0lllllll
0mmmmmmm
Evento note-off.
Este evento es enviado cuando finaliza la nota.
(nnnnnnn) es el número de la nota.
(vvvvvvv) es la velocidad.
Evento note-on.
Este mensaje se envía cuando comienza una nota
(nnnnnnn) es el número de la nota.
Presión sobre la tecla (Aftertouch).
Este mensaje se envía para informar de la velocidad
de presión sobre la tecla.
(nnnnnnn) número de la nota.
(vvvvvvv) velocidad.
Cambio de control.
Este mensaje es enviado cuando se cambia un valor
de controlador.
Véase apéndice A2.
(ccccccc) número de controlador.
(vvvvvvv) nuevo valor.
Cambio de programa
Indica un cambio de programa
(ppppppp) es el nuevo número de programa.
Presión del canal (After-touch).
Este mensaje es enviado cuando la presión del canal
cambia. Algunos teclados no soportan esta
característica.
(ccccccc) es el número de canal.
Cambio de la rueda de modulación.
Este mensaje es enviado para indicar un cambio en
la rueda de modulación. La rueda de modulación es
medido por el décimo cuarto bit. El valor central es
2000H.
(llllll) son los 7 bits menos significativos.
(mmmmmm) son los 7 bits más significativos.
173
APÉNDICE
1011nnnn
Mensajes de modo de canal
0ccccccc
Mensajes de modo de canal
0vvvvvvv
Es el mismo código que el Cambio de
control(arriba), pero implementa un Modo de
control usando unos números de controlador
reservados. Los números son:
Control Local
Cuando el Control Local está off, todos los
dispositivos de un canal dado responderán
solamente a los datos recibidos sobre MIDI.
c = 122, v = 0: Control Local Off
c = 122, v = 127: Control Local On
Todas las notas desactivadas
Cuando llega este mensaje todos los osciladores se
desactivan.
c = 123, v = 0: Todas las notas Off
c = 124, v = 0: Modo Omni Off
11110000
11110001
11110010
11110011
11110100
11110101
11110110
11110111
174
c = 125, v = 0: Modo Omni On
c = 126, v = M: Modo mono On (Poly Off) donde
M es el número de canales
(Omni Off) o 0 (Omni On)
c = 127, v = 0: Modo Poly On (Mono Off)
(Note: Estos cuatro mensajes también causan todas
las notas desactivadas)
Mensajes de Sistema
0iiiiiii
Sistema exclusivo.
0ddddddd
Estos mensajes son específicos del dispositivo e
..
independientes del protocolo MIDI. Los bits (iiiiiii)
..
es la indentificación de ID del fabricante.
0ddddddd
Si el dispositivo reconoce el ID lee los siguientes
11110111
mensajes (ddddddd). En otro caso el mensaje será
ignorado. (Nota: Los mensajes de tiempo-real
solamente pueden ser entrelazados con los de
Sistema Exclusivo)
No definido
0lllllll
Puntero a la posición de la canción.
0mmmmmmm
Es un registro interno de 14 bits que almacena el
número de pulsos MIDI.
0sssssss
Selección de secuecia.
La selección de la canción especifica que secuencia
o canción debe ser reproducida..
No definido
No definido
Petición de tono.
Fin de sistema exclusivo
APÉNDICE
11111000
11111001
11111010
11111011
11111100
11111101
11111110
11111111
Mensajes de tiempo-real
Reloj de tiempo.
Oscila 24 veces por cuarto de nota cuando la
sincronización es requerida.
No definido
Comienzo
Comienza la reproducción de la secuencia. (Este
mensaje vendrá seguido de mensajes de tiempo del
reloj).
Continua.
Continua en el punto donde la secuencia paró.
Stop
Para la secuencia actual
No definido
Activa Sensing. El uso de este mensaje es opcional.
Cuando es inicialmente enviado, el receptor
esperará recibir otro mensaje Active Sensing cada
300ms (máximo), o será asumido que la conexión
ha sido terminada. En la terminación, el receptor
desactivará todas las voces y volverá al modo
normal de operación (Sensing no activo)
Reset.
Reinicia todos los receptores en el sistema al estado
activado. Este mensaje debería ser usado con
cuidado y preferiblemente bajo control manual.
Particularmente, no debería ser enviado con el
sistema encendido.
175
APÉNDICE
A2. Lista de mensajes de controladores MIDI 1.0
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
176
Bank select
Modulation wheel
Breath control
Indefinido
Foot controller
Portamento time
Data Entry
Channel Volume
Balance
Indefinido
Pan
Expression Controller
Effect control 1
Effect control 2
Indefinido
Indefinido
General Purpose Controller
General Purpose Controller
General Purpose Controller
General Purpose Controller
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
Bank Select
Modulation wheel
Breath control
Indefinido
Foot controller
Portamento time
Data entry
Channel Volume
Balance
Indefinido
Pan
Expression Controller
Effect control 1
Effect control 2
Indefinido
Indefinido
General Purpose Controller
General Purpose Controller
General Purpose Controller
General Purpose Controller
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
#1
#2
#3
#4
#1
#2
#3
#4
APÉNDICE
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
Damper pedal on/off (Sustain)
Portamento on/off
Sustenuto on/off
Soft pedal on/off
Legato Footswitch
Hold 2
Sound Controller 1 (Sound Variation)
Sound Controller 2 (Timbre)
Sound Controller 3 (Release Time)
Sound Controller 4 (Attack Time)
Sound Controller 5 (Brightness)
Sound Controller 6
Sound Controller 7
Sound Controller 8
Sound Controller 9
Sound Controller 10
General Purpose Controller #5
General Purpose Controller #6
General Purpose Controller #7
General Purpose Controller #8
Portamento Control
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
Effects 1 Depth
Effects 2 Depth
Effects 3 Depth
Effects 4 Depth
Effects 5 Depth
Data entry +1
Data entry -1
Non-Registered Parameter Number LSB
Non-Registered Parameter Number MSB
Registered Parameter Number LSB
Registered Parameter Number MSB
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
Indefinido
177
APÉNDICE
116
117
118
119
120
121
122
123
124
125
126
127
178
Indefinido
Indefinido
Indefinido
Indefinido
All Sound Off
Reset All Controllers
Local control on/off
All notes off
Omni mode off (+ all notes off)
Omni mode on (+ all notes off)
Poly mode on/off (+ all notes off)
Poly mode on (incl mono=off +all notes off)
APÉNDICE
A3. Lista de instrumentos GM (General MIDI)
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
00 - Piano de cola
acústico
01 - Piano
acústico
brillante
02 - Piano de cola
eléctrico
03 - Piano de
cantina
04 - Piano Rhodes
05 - Piano con
"chorus"
06 - Clavicordio
07 - Clavinet
08 - Celesta
09 - Carillón
10 - Caja de
música
11 - Vibráfono
12 - Marimba
13 - Xilófono
14 - Campanas
tubulares
15 - Salterio
16 - Órgano
Hammond
17 - Órgano
percusivo
18 - Órgano de
rock
19 - Órgano de
iglesia
20 - Armonio
21 - Acordeón
22 - Armónica
23 - Bandoneón
24 - Guitarra
española
25 - Guitarra
acústica
26 - Guitarra
eléctrica
(jazz)
27 - Guitarra
eléctrica
(limpia)
28 - Guitarra
eléctrica
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
32 - Bajo acústico
33 - Bajo eléctrico
pulsado
34 - Bajo eléctrico
punteado
35 - Bajo sin
trastes
36 - Bajo
golpeado 1
37 - Bajo
golpeado 2
38 - Bajo
sintetizado 1
39 - Bajo
sintetizado 2
40 - Violín
41 - Viola
42 - Violoncello
43 - Contrabajo
44 - Cuerdas con
trémolo
45 - Cuerdas con
pizzicato
46 - Arpa
47 - Timbales
48 - Conjunto de
cuerda 1
49 - Conjunto de
cuerda 2
50 - Cuerdas
sintetizadas
1
51 - Cuerdas
sintetizadas
2
52 - Coro Aahs
53 - Voz Oohs
54 - Voz
sintetizada
55 - Éxito de
orquesta
56 - Trompeta
57 - Trombón
58 - Tuba
59 - Trompeta con
sordina
60 - Corno
francés
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
64 - Saxo soprano
65 - Saxo alto
66 - Saxo tenor
67 - Saxo barítono
68 - Oboe
69 - Corno inglés
70 - Fagot
71 - Clarinete
72 - Flautín
73 - Flauta
74 - Flauta dulce
75 - Flauta de pan
76 - Cuello de
botella
77 - Shakuhachi
(flauta
japonesa)
78 - Silbato
79 - Ocarina
80 - Melodía 1
(onda
cuadrada)
81 - Melodía 2
(diente de
sierra)
82 - Melodía 3
(órgano de
vapor)
83 - Melodía 4
(siseo
órgano)
84 - Melodía 5
(charanga)
85 - Melodía 6
(voz)
86 - Melodía 7
(quintas)
87 - Melodía 8
(bajo y
melodías)
88 - Fondo 1
(nueva era)
89 - Fondo 2
(cálido)
90 - Fondo 3
(polisintetiz
ador)
91 - Fondo 4
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
96 - Efecto 1
(lluvia)
97 - Efecto 2
(banda
sonora)
98 - Efecto 3
(cristales)
99 - Efecto 4
(atmósfera)
100 Efecto 5
(brillo)
101 Efecto 6
(duendes)
102 Efecto 7
(ecos)
103 Efecto 8
(ciencia
ficción)
104 Sitar
105 Banjo
106 Shamisen
107 Koto
108 Kalimba
109 Gaita
110 Violín celta
111 Shanai
112 Campanillas
113 Agogó
114 Cajas
metálicas
115 Caja de
madera
116 Caja Taiko
117 Timbal
melódico
118 Caja
sintetizada
119 Platillo
invertido
120 Trasteo de
guitarra
121 Sonido de
respiración
122 Playa
123 Piada de
pájaro
124 Timbre de
teléfono
179
APÉNDICE
(apagada)
29 - Guitarra
saturada
(overdrive)
• 30 - Guitarra
distorsionad
a
• 31 - Armónicos
de guitarra
•
180
(trompa)
61 - Sección de
metal
• 62 - Metales
sintetizados
1
• 63 - Metales
sintetizados
2
•
(coro)
92 - Fondo 5 (de
arco)
• 93 - Fondo 6
(metálico)
• 94 - Fondo 7
(celestial)
• 95 - Fondo 8
(escobillas)
•
•
•
•
125 Helicóptero
126 Aplauso
127 Disparo de
fusil
APÉNDICE
A4. Tabla de frecuencias de notas musicales en Hertzios
Do 1: 65,406
Do# 1: 69,296
Re 1: 73,416
Re# 1: 77,782
Mi 1: 82,407
Fa 1: 87,307
Fa# 1: 92,499
Sol 1: 97,999
Sol#1: 103,826
La 1: 110
La# 1: 116,541
Si 1: 123,471
Do 2: 130,813
Do# 2: 138,591
Re 2: 146,832
Re# 2: 155,563
Mi 2: 164,814
Fa 2: 174,614
Fa# 2: 184,997
Sol 2: 195,998
Sol#2: 207,652
La 2: 220
La# 2: 233,082
Si 2: 246,942
Do 3: 261,626
Do# 3: 277,183
Re 3: 293,665
Re# 3: 311,127
Mi 3: 329,628
Fa 3: 349,228
Fa# 3: 369,994
Sol 3: 391,995
Sol#3: 415,305
La 3: 440
La# 3: 466,164
Si 3: 493,883
Do 4: 523,251
Do# 4: 554,365
Re 4: 587,33
Re# 4: 622,254
Mi 4: 659,255
Fa 4: 698,456
Fa# 4: 739,989
Sol 4: 783,991
Sol#4: 830,609
La 4: 880
La# 4: 932,328
Si 4: 987,767
Do 5: 1046,502
Do# 5: 1108,731
Re 5: 1174,659
Re# 5: 1244,508
Mi 5: 1318,51
Fa 5: 1396,913
Fa# 5: 1479,978
Sol 5: 1567,982
Sol#5: 1661,219
La 5: 1760
La# 5: 1864,655
Si 5: 1975,533
Do 6: 2093,005
Do# 6: 2217,461
Re 6: 2349,318
Re# 6: 2489,016
Mi 6: 2637,02
Fa 6: 2793,826
Fa# 6: 2959,955
Sol 6: 3135,963
Sol#6: 3322,438
La 6: 3520
La# 6: 3729,31
Si 6: 3951,066
Do 7: 4186,009
Do# 7: 4434,922
Re 7: 4698,636
Re# 7: 4978,032
Mi 7: 5274,041
Fa 7: 5587,652
Fa# 7: 5919,911
Sol 7: 6271,927
Sol#7: 6644,875
La 7: 7040
La# 7: 7458,62
Si 7: 7902,133
Do 8: 8372,018
Do# 8: 8869,844
Re 8: 9397,273
Re# 8: 9956,063
Mi 8: 10548,082
Fa 8: 11175,303
Fa# 8: 11839,822
Sol 8: 12543,854
Sol#8: 13289,75
La 8: 14080
La# 8: 14917,24
Si 8: 15804,266
181

Documentos relacionados