En esta prctica vamos a utilizar los pipes de Unix para transmitir datos entre las aplicaciones arecord y aplay, realizando un procesamiento en tiempo real de la seal de audio transferida. En esta fase de procesamiento realizaremos una modulacin de la seal de audio.
Un modulador de seales es un sistema al que entra una seal (la seal a modular, la seal de datos original, la seal de audio) y del que sale otra seal (la seal modulada, la nueva seal de datos, la seal de audio modulada). Adems, normalmente se le especifica la frecuencia de la seal moduladora como un parmetro de entrada (frecuencia de la seal moduladora o portadora). Para ello vamos a utilizar los pipes de Unix para leer de la tarjeta de sonido una seal, modularla y a continuacin reproducirla. Este sistema queda construido escribiendo:
Ntese que sin el flag -t raw modularamos tambin la cabecera WAV, con el consiguiente deterioro de la misma.
Tambin veremos en esta prctica de qu manera es posible sintetizar una seal digital de audio sencilla mediante un programa que escriba dicha seal sobre la salida estndar. En concreto, generaremos una seal sinusoidal peridica de una determinada frecuencia. Este tipo de seales son tiles para hacer comprobaciones, como veremos, acerca del buen funcionamiento de otros programas de tratamiento de audio.
Bien. Como ya hemos dicho, vamos a generar los datos escribindolos sobre la salida estndar. De esta manera y usando un pipe, podremos escuchar nuestro generador se seales sinusoidales escribiendo:
El programa dc genera a la salida estndar un valor constante (1000 por defecto). Si modulamos esta seal obtenenos la sinusoide tal y como se explica a continuacin.
Las seales de audio, como el resto de seales unidimensionales y dependientes del tiempo, pueden representarse tanto en el dominio del tiempo como en el dominio de la frecuencia. En el primero, una muestra de audio indica, para el instante de tiempo que referencia la muestra, la amplitud del sonido. En el segundo, un coeficiente especifica, para todo el tiempo que comprende la ventana de anlisis, la amplitud de la sinusoide de frecuencia igual al ndice del coeficiente, que dicho sonido contiene.
En el caso de las seales de audio, el dominio del tiempo y el dominio de la frecuencia son equivalentes y por tanto, una determinada operacin que se realiza en uno de los dominios siempre tiene una equivalente en el otro.
Un ejemplo de sto lo encontramos en el filtrado de seales. Cuando filtramos, en el dominio de la frecuencia lo que realizamos (atenuando unas determinadas frecuencias con respecto a otras) es multiplicar el espectro de la seal de audio (la que queremos filtrar) con el espectro de la funcin de transferencia de filtro (la funcin que resulta de hacer pasar a travs del filtro la funcin impulso unitario). Como sabemos, por el teorema de convolucin, multiplicar los espectros de dos seales es equivalente a convolucionar las transformadas inversas de dichos espectros en el dominio del tiempo, es decir, para filtrar una seal en el dominio del tiempo podemos convolucionar dicha seal con la respuesta del filtro a la funcin impulso unitario.
Por definicin, modular una seal es desplazar su espectro en el dominio de la frecuencia. Para conseguir dicho desplazamiento en ω0 Hz, desde el punto de vista de la teora de seales, podemos convolucionar el espectro (por tanto, en el dominio de la frecuencia) con la funcin impulso unitario de frecuencia ω0 Hz. Sea dicha funcin δ(ω0).
Aplicando de nuevo el teorema de convolucin, dicha convolucin sera equivalente a multiplicar la seal por la transformada inversa de la funcin impulso unitario δ(ω0). Por definicin, la funcin cuyo espectro es una funcin impulso, es una funcin exponencial compleja en el dominio del tiempo. Como esta funcin no existe realmente (fsicamente) podemos simularla utilizando una sinusoide que, aunque presenta dos impulsos, es perfectamente vlida al no poderse representar fsicamente las frecuencias negativas.
Por tanto, para desplazar el espectro de una seal de audio debemos multiplicar cada una de las muestras por una funcin sinusoidal de una determinada frecuencia. La cantidad de desplazamiento que sufra el espectro va a depender de la frecuencia de la funcin sinusoidal, que por cierto, se suele llamar en el contexto de la modulacin de seales seal moduladora y seal portadora en el contexto de la transmisin de seales.
Los threads (hilos o hebras en castellano) se utilizan para ejecutar procesos concurrentes. Cuando se implementa un programa interactivo como el que nos atae, en el que hay un proceso que no cesa (el que modula) y que depende de un parmetro de entrada que puede cambiar a lo largo del tiempo (la frecuencia de la seal moduladora), los hilos son la solucin. Usaremos un segundo hilo para determinar este parmetro de entrada.
En todo programa escrito en C existe un thread por defecto, el que ejecuta secuencialmente todo el cdigo escrito en el programa. Cuando se desea utilizar otro thread ms hay que declararlo de forma explcita. Para ello debemos crear una estructura del tipo pthread_t que se declara en el fichero cabecera pthread.h. A continuacin, lanzaremos el nuevo hilo materializado a travs de una determinada funcin. Veremos un ejemplo concreto ms adelante en el cdigo que acompaa a esta prctica.
Existen muchas formas de indicar el valor de los parmetros de entrada a un programa. La forma ms simple es hacerlo a travs de la lista de argumentos a la funcin main(). Por desgracia, esta forma no permite la modificacin de dichos parmetros una vez que la ejecucin ya ha comenzado.
Otra forma de introducir los parmetros es a travs de la entrada estndar, que por defecto, si no se usan los pipes, es el teclado del terminal. Esta opcin requiere, como es lgico, el uso de un hilo dedicado. Sin embargo, en esta prctica preferiremos utilizar la entrada y la salida estndar para transferir otros datos (como las seales de audio).
La alternativa que vamos a escoger es la de utilizar un programa externo que se comunica con modulator a travs de un socket. Dicho programa se llamar ModulatorControl y estar escrito en Java. El lenguaje de programacin Java es muy adecuado para disear interfaces de usuario grficos lo que nos ayudar a controlar la frecuencia de la seal con comodidad.
Por otra parte, los sockets permiten que los programas modulator y ModulatorControl se ejecuten de forma independiente en hosts (ordenadores) distintos. As, podemos estar modulando la seal en un ordenador sin teclado ni pantalla (aunque s con tarjeta de sonido, como es lgico) y controlando la frecuencia de la seal portadora desde otro ordenador con un terminal grfico.
En la jerga usada en redes de datos, un servidor representa generalmente una aplicacin de computadora que atiende a las peticiones realizadas por una o varias aplicaciones clientes. En esta prctica vamos a intentar que uno o varios clientes controlen de forma simultanea la frecuencia de la portadora que genera el servidor.
Para realizar la comunicacin desde los clientes al servidor vamos a usar el TCP que es ms fiable que el UCP y adems permite conectarnos al servidor usando programas estndar como el telnet. Cuando se usa el TCP antes de transmitir nada es neceario realizar una conexin, luego transmitiremos y finalmente es necesario realizar una desconexin. El tiempo que puede transcurrir desde que se realiza la conexin hasta que se produce la desconexin es, a priori, indeterminado. Esto significa que si un cliente se conecta al servidor y ste no puede atender a otro cliente mientras el primero no ha cerrado su conexin, el segundo cliente tardar un tiempo indefinido en ser atendido. Para evitar este problema se utiliza un tipo de servidor que se conoce como concurrente. Un servidor concurrente permite que muchos clientes permanezcan conectados el tiempo que sea necesario, realizndose los ciclos de conexin-transmisin-desconexin de una forma totalmente asncrona. La mayora de los servidores de datos que existen y que usan el TCP (como los servidores Web) son servidores concurrentes. Para atender concurrentemente a cada cliente utilizaremos un thread para cada uno de ellos.
Para enviar la frecuencia desde el servidor a los clientes vamos a usar el UDP porque permite realizar transmisiones multicast. En una transmisin de este tipo una nica fuente puede enviar un nico paquete de datos a muchos destinos usando la infraestructura existente en la red de transmisin. As, por muchos clientes que existan el servidor no se conviertir en el cuello de botella del sistema.
El fujo de datos desde los clientes al servidor se realiza usando el TCP y por tanto hay que establecer una conexin por cada cliente. Dichas conexiones permanecern abiertas todo el tiempo que deseen los clientes. Cuando se reciba una frecuencia se enviar por el canal multicast dicha frecuencia a todos los clientes que escuchan en dicho canal. El servidor no sabe cuntos clientes existen (aunque podra hacer una suposicin razonable contando el nmero de conexiones TCP abiertas). Las frecuencias se codificarn en ASCII, para que pueda usarse el programa telnet como cliente. Para indicar el final de una conexin el cliente enviar una frecuencia negativa (una frecuencia imposible) al servidor.
A continuacin aparece un resumen de la interaccin cliente-servidor en seudo-cdigo:
Nota: la recepcin es bloqueante, es decir, mientras no se reciben los datos esperados la ejecucin est detenida.
Bueno. Ahora veremos un ejemplo concreto de cmo se puede escribir en C todo lo que hemos descrito en las secciones anteriores. Antes de compilar este cdigo, deberemos copiarlo en un fichero llamado modulator.c. Este ser nuestro servidor.
En esta prctica se usan tres programas distintos (dc, modulator y ModulatorControl) y cada uno de ellos tiene un Makefile distinto. A continuacin se muestran dichos ficheros:
Para ejecutar correctamente esta aplicacion debemos lanzar modulator en el host que posee la tarjeta de sonido. El/los cliente/s que controla/n la frecuencia de la portandora puede/n ejecutarse tambin es ese host o en cualquier otro host de Internet (siempre que el trfico multicast est permitido).
Como se puede ver en el cdigo de modulator.c, el socket que hemos creado para controlar la frecuencia acepta una cadena de caracteres que representan un nmero entero. Puesto que dicha cadena est en formato ASCII, podemos usar el programa estndar telnet para controlar la frecuencia de la portadora. Para hacer esto escribiremos:
donde PORT es el puerto de escucha, definido en el fichero modulator.c. Tras establecer la conexin podremos cambiar la frecuencia tecleando una nueva (y pulsando la tecla Enter). La otra forma de controlar la frecuencia consiste en usar ModulatorControl. Para ejecutar este programa escribieremos:
en el directorio que contiene las clases compiladas (los ficheros .class).
Por supuesto, es necesita una mquina virtual de Java instalada en cada host cliente.
Bueno, estos son algunos ejemplos interesantes: