9.9 El código de modulator.c

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’);  
}