Bueno. Ahora veremos un ejemplo concreto de cómo se puede escribir en C todo lo que hemos descrito en las secciones anteriores. Antes de compilar este código, deberemos copiarlo en un fichero llamado modulator.c. Este será nuestro servidor.
/*
* modulator.c * * Lee desde la entrada estándar una señal de audio (digital), a 16 bits * por muestra, usando el endian de la máquina. Modula la señal a * partir de una frecuencia especificada por uno o más clientes que la * especifican a través de un socket TCP. Finalmente escribe la señal * modulada en la salida estándard. * * gse. 2006. * */ /****************************************************************************** * * Ficheros cabecera. * *****************************************************************************/ /* Entrada y salida de streams buffereados. */ #include <stdio.h> /* Biblioteca de funciones estándar (exit(), EXIT_SUCCESS, EXIT_FAILURE, ...) */ #include <stdlib.h> /* Manipulación de cadenas y movimiento de memoria (memset(), ...). */ #include <string.h> /* Biblioteca estándar de funciones relacionadas con el sistema operativo Unix (read(), write(), ...). */ #include <unistd.h> /* Biblioteca estándar de cálculos matemáticos en punto flotante (sin(), rint(), ...).*/ #include <math.h> /* POSIX Threads (pthread_t, pthread_create(), ...). */ #include <pthread.h> /* Sockets. */ /* Más info en: * * http://www.csce.uark.edu/~aapon/courses/os/examples/concurrentserver.c * http://beej.us/guide/bgnet/ */ /* Tipos de datos primitivos del sistema. */ #include <sys/types.h> /* Sockets (socket(), listen(), ...). */ #include <sys/socket.h> /* Direcciones IP, opciones y definiciones. */ #include <netinet/in.h> /* Servicio de resolución de nombres. */ #include <netdb.h> /* Signals (interrupciones) (signal(), SIGINT). */ #include <signal.h> /* signal(), SIGINT */ /****************************************************************************** * * Definiciones. * *****************************************************************************/ /* Número de bytes del buffer de audio. */ #define BUFFER_SIZE 32 /* Número de muestras por segundo (calidad CD). */ #define SAMPLES_PER_SECOND 44100 /* Frecuencia por defecto en Hz. */ #define DEFAULT_FREQ 440 /* Dos canales. */ #define NUMBER_OF_CHANNELS 2 /* Puerto de comunicación con los controladores de parámetros. */ #define PORT 6789 /* Número máximo de clientes que esperan a que la conexión sea establecida. */ #define QLEN 1 /****************************************************************************** * * Variable globales. * *****************************************************************************/ /****************************************************************************** * * Funciones. * *****************************************************************************/ /* Cuerpo principal del programa. */ int main(int argc, char *argv[]) { work(argc, argv); return EXIT_SUCCESS; } /* Función que realiza todo el trabajo. */ work(int argc, char *argv[]) { /* Si pulsamos CRTL+C, el programa acaba ejecutando la función end(). */ void end() { fprintf(stderr,"%s: CTRL+C detected. Exiting ... ",argv[0]); fprintf(stderr,"done\n"); exit(EXIT_SUCCESS); } signal(SIGINT, end); /* Un semáforo para acceder "en exclusión mutua" a una zona de código. */ pthread_mutex_t mut; /* Creamos el semáforo. */ pthread_mutex_init(&mut, NULL); /* Frecuencia de la señal sinusoidal en Hz. */ int freq = DEFAULT_FREQ; /* Hilo que realiza la síntesis de la sinusoide. */ void *modu(void *arg) { /* Angulo de la sinusoide, en radianes. */ double angle_in_radians = 0; /* La sinusoide será generada hasta pulsar CTRL+C. */ for(;;) { int i; /* El buffer de audio. */ short buffer[BUFFER_SIZE]; /* Leemos la entrada estándar. */ int bytes_read; bytes_read = read(0/* stdin */, buffer, BUFFER_SIZE*sizeof(buffer[0])); /* Número de muestras leídas (sumando ambos canales). */ int samples = bytes_read / sizeof(buffer[0]); fprintf(stderr,"%d\n",samples); /* Media del canal izquierdo y derecho. */ double ave_l = 0.0, ave_r = 0.0; for(i=0; i<samples; i+=2) { ave_l += buffer[i]; ave_r += buffer[i+1]; } ave_l /= samples; ave_r /= samples; //fprintf(stderr,"*%f %f\n",ave_l, ave_r); /* Eliminamos la componente continua en cada canal. */ for(i=0; i<samples; i+=2) { buffer[i] -= ave_l; buffer[i+1] -= ave_r; } ave_l = 0; ave_r = 0; for(i=0; i<samples; i+=2) { ave_l += buffer[i]; ave_r += buffer[i+1]; } ave_l /= samples; ave_r /= samples; //fprintf(stderr,"%f %f\n",ave_l, ave_r); /* Modulamos. */ for(i=0; i<samples; i+=2) { /* Calculamos la portadora. */ double portadora = sin(angle_in_radians); /* Multiplicamos el valor de cada canal por el valor de la portadora. */ buffer[i] *= portadora; buffer[i+1] *= portadora; /* Angulo de la siguiente muestra de la portadora. */ angle_in_radians += 2*M_PI*freq/SAMPLES_PER_SECOND; } /* Escribimos buffer en la salida estándar. */ write(1/* stdout */, (void *)buffer, bytes_read); } /* Finalizamos el thread. */ pthread_exit(0); } /* Lanzamos el hilo anterior. */ pthread_t modu_tid; if(pthread_create(&modu_tid, NULL, modu, NULL) == -1) { perror("pthread_create"); exit(EXIT_FAILURE); } /* Creamos el socket TCP de escucha. */ int listen_sd; { /* Obtenemos el número del protocolo. */ struct protoent *ptrp; /* Pointer to a protocol table entry. */ ptrp = getprotobyname("tcp"); if((int)(ptrp) == 0) { perror("getprotobyname"); exit(EXIT_FAILURE); } /* Creamos el socket. */ listen_sd = socket (PF_INET, SOCK_STREAM, ptrp->p_proto); if(listen_sd < 0) { perror("socket"); exit(EXIT_FAILURE); } /* Usaremos el puerto de servicio sin esperar a que éste esté libre. */ int yes = 1; if(setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) { perror("setsockopt"); exit(EXIT_FAILURE); } } /* Asignamos una dirección (dir IP, puerto) al socket de escucha. */ { struct sockaddr_in sad; /* Dirección del servidor. */ memset((char *)&sad,0,sizeof(sad)); /* Borramos la estructura. */ sad.sin_family = AF_INET; /* Usaremos Internet. */ sad.sin_addr.s_addr = INADDR_ANY; /* Cualquiera de las IP asignadas al host vale. */ sad.sin_port = htons((u_short)PORT); /* Asignamos el puerto de escucha. */ if (bind(listen_sd, (struct sockaddr *)&sad, sizeof (sad)) < 0) { perror("bind"); exit(EXIT_FAILURE); } } /* Comenzamos a escuchar. */ if (listen(listen_sd, QLEN) < 0) { perror("listen"); exit(EXIT_FAILURE); } /* Creamos el socket UDP. */ int info_sd; if((info_sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket"); exit(EXIT_FAILURE); } /* Dir IP y puerto del socket. */ struct sockaddr_in info_addr; { /* Usaremos Internet. */ info_addr.sin_family = AF_INET; /* IP multicast destino de los datagramas. */ info_addr.sin_addr.s_addr = inet_addr("224.0.0.1"); /* Puerto destino de los datagramas. */ info_addr.sin_port = htons(PORT); memset(&(info_addr.sin_zero), ’\0’, 8); } /* Lazo del servidor. */ while(1) { fprintf(stderr,"sinusoidal: waiting for connection ...\n"); /* Socket para "servir" a los clientes. */ int serve_sd; /* Esperamos a que un cliente se conecte. */ { struct sockaddr_in cad; /* Client’s address. */ socklen_t alen = sizeof(cad); /* Tamaño de la dirección. */ serve_sd = accept(listen_sd, (struct sockaddr *)&cad, &alen); if(serve_sd<0) { perror("accept"); exit (EXIT_FAILURE); } } /* Hilo que controla a un cliente. */ void *service(void *arg) { /* El socket de conmunicación con el cliente. Nótese que cada cliente posee una variable "sd" diferente. */ int sd = (int)arg; /* Mientras la frecuencia recibida sea >= 0. */ for(;;) { int new_freq; receive_from_client(sd, &new_freq); if(new_freq<0) break; /* Sección crítica. */ pthread_mutex_lock(&mut); freq = new_freq; fprintf(stderr,"freq = %d\n",freq); pthread_mutex_unlock(&mut); /* Fin de la sección crítica. */ inform_to_client(info_sd, info_addr, freq); } /* Cerramos la conexión con el cliente. */ close(sd); /* Finalizamos el thread. */ pthread_exit(0); } /* Lanzamos el hilo. */ pthread_t service_tid; /* Thread identificator. */ if(pthread_create(&service_tid, NULL, service, (void *)serve_sd) == -1) { perror("pthread_create"); exit(EXIT_FAILURE); } } /* Cerramos el socket de escucha (cuando pulsamos CTRL+C). */ close(listen_sd); } /* Envía un paquete UDP con la frecuencia. */ inform_to_client(int sd, struct sockaddr_in addr, int freq) { unsigned char message[2]; //fprintf(stderr,"--> %d\n",freq); message[0] = freq/256;//((freq & 0x000F) >> 0); message[1] = freq%256;//((freq & 0x00F0) >> 8); //message[2] = 3;//((freq & 0x0F00) >> 16); //message[3] = 4;//((freq & 0xF000) >> 24); sendto(sd, message, 2, 0,(struct sockaddr *) &addr, sizeof(addr)); } /* Recibe del cliente la nueva frecuencia. */ receive_from_client(int sd, int *freq) { char message[256]; read_text_line(sd, message); sscanf(message,"%d",freq); } /* Lee una línea de texto ASCII. */ read_text_line(int sd, char *str) { int n; do { n = recv(sd, str, 1, 0); } while (n>0 && *str++ != ’\n’); } |