2 * Implements the Client side of the Client/Server demo.
4 * It waits for the user to type in a line of text, sends the line of
5 * text to the speech server, which returns a stream of bytes (the
6 * synthesized wave samples). This client then plays the stream
7 * of bytes at the local audio device.
9 * You must start the speech server first. You can do this by typing:
13 * at the same directory. To run this client, modify set the speech
14 * server host (and port number if not 5555) at the Makefile, and then type:
18 * In the Makefile, you can also specify the sample rate you want
19 * as the third argument (currently, the server supports only 8kHz and 16kHz).
21 * This C client should run across most UNIX implementations, as it
22 * uses standard UNIX system libraries.
24 * For a complete specification of the protocol between client and server,
25 * consult the document <code>Protocol.txt</code>.
28 #include <arpa/inet.h>
33 #include <netinet/in.h>
37 #include <sys/audio.h>
38 #include <sys/audioio.h>
39 #include <sys/filio.h>
40 #include <sys/socket.h>
43 #include <sys/types.h>
50 #define SERVER_PORT 5555
51 #define SERVER_HOST "sunlabs.east"
53 #define AUDIO_DEVICE_FILE "/dev/audio"
54 #define AUDIO_DEVICE_ENV_VAR "AUDIODEV" // for SunRays
55 #define DEFAULT_SAMPLE_RATE 8000
57 #define ADDRESS_SIZE sizeof(struct sockaddr_in)
58 #define AUDIO_BUFFER_SIZE 1024
59 #define TEXT_INPUT_BUFFER_SIZE 1024
60 #define STR_BUFFER_SIZE 10
62 #define FIRST_SENTENCE "Type in what you want me to say."
65 int connect_speech_server(char* server_host, int server_port);
66 void run_tts_protocol(int sock_fd);
67 int read_line(int sock_fd, char *buffer, int buffer_size);
68 int send_tts_request(int sock_fd, char *tts_text);
69 void receive_play_samples(int sock_fd, int number_samples);
70 int open_audio_device();
72 unsigned char short_to_ulaw(short sample);
73 int is_string_nonempty(char *string, int length);
76 /* our audio device file descriptor */
80 int sample_rate = DEFAULT_SAMPLE_RATE;
85 /* equals 1 if the first byte is received */
86 int first_byte_received = 0;
89 struct timeval start_time;
91 /* the first byte time */
92 struct timeval first_byte_time;
94 /* the first sound time */
95 struct timeval first_sound_time;
101 * It first attempts a connection to the speech server. Then,
102 * it waits for the user to type in a line of text, sends the line of
103 * text to the speech server, which returns a stream of bytes (the
104 * synthesized wave samples). This client then plays the stream
105 * of bytes at the local audio device.
107 * Arguments (optional):
108 * argv[1] : the host name of speech server
109 * argv[2] : the port number where the speech server is listening
110 * argv[3] : the sample rate
111 * argv[4] : show metrics, 1 to show, 0 to not show
113 int main(int args, char *argv[]) {
119 server_port = SERVER_PORT;
120 server_host = SERVER_HOST;
122 /* parse command line arguments for server hostname and port number */
124 server_host = argv[1];
127 server_port = atoi(argv[2]);
130 sample_rate = atoi(argv[3]);
133 metrics = atoi(argv[4]);
136 /* connect to the server */
137 sock_fd = connect_speech_server(server_host, server_port);
139 /* start running the TTS protocol */
140 run_tts_protocol(sock_fd);
150 * Connects to the remote speech server at the given host and port,
151 * and returns the socket file descriptor to the connection.
154 * server_host: the host name of the speech server
155 * server_port: the port on which the speech server is listening
158 * a file descriptor of the socket
160 int connect_speech_server(char* server_host, int server_port) {
163 struct sockaddr_in server = {AF_INET, SERVER_PORT};
166 /* obtain the IP address */
168 hp = gethostbyname(server_host);
170 perror("invalid hostname");
174 /* set the IP address and port */
176 bcopy((char *)hp->h_addr, (char *)&server.sin_addr, hp->h_length);
177 server.sin_port = htons(server_port);
180 /* set up the transport end point */
182 if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
183 perror("socket call failed");
187 /* connect to the server */
189 if (connect(sock_fd, (struct sockaddr *) &server, ADDRESS_SIZE) == -1) {
190 perror("connect call failed");
199 * Runs the TTS protocol.
200 * It waits for the user to type in a line of text, sends the line of
201 * text to the speech server, which returns a stream of bytes (the
202 * synthesized wave samples). This client then plays the stream
203 * of bytes at the local audio device.
206 * sock_fd the socket file descriptor
208 void run_tts_protocol(int sock_fd) {
210 char buffer[STR_BUFFER_SIZE];
211 char input_buffer[TEXT_INPUT_BUFFER_SIZE];
214 /* read the "READY" line from the Server */
216 nread = recv(sock_fd, buffer, 6, 0);
217 buffer[nread] = '\0';
219 if (strcmp(buffer, "READY\n") == 0) {
221 if (send_tts_request(sock_fd, FIRST_SENTENCE) == -1) {
225 input_buffer[0] = '\0';
227 while (fgets(input_buffer, TEXT_INPUT_BUFFER_SIZE, stdin) != NULL) {
228 if (is_string_nonempty(input_buffer, strlen(input_buffer)) &&
229 send_tts_request(sock_fd, input_buffer) == -1) {
232 input_buffer[0] = '\0';
237 send(sock_fd, "DONE\n", 5, 0);
239 /* drain all the audio before returning */
241 ioctl(audio_fd, AUDIO_DRAIN, 0);
243 printf("ALL DONE\n");
248 * Sends a TTS request of the given text to the given socket.
251 * sock_fd : socket file descriptor
252 * tts_text : the text to perform TTS
255 * 0 if everything's fine, -1 if any error occurred
257 int send_tts_request(int sock_fd, char *tts_text) {
261 char tts_text_str[TEXT_INPUT_BUFFER_SIZE];
264 char number_samples_str[STR_BUFFER_SIZE];
268 input_length = strlen(tts_text);
270 if (tts_text[input_length - 1] == '\n') {
271 tts_text[input_length - 1] = '\0';
274 sprintf(tts_text_str, "TTS\n%d\n%s\n", sample_rate, tts_text);
276 text_length = strlen(tts_text_str);
278 /* record the time the request is sent */
280 gettimeofday(&start_time, NULL);
281 first_byte_received = 0;
285 * send "TTS\n<sample_rate>\n<text>\n" (sent together to avoid
286 * repetitive send calls)
288 nsend = send(sock_fd, tts_text_str, text_length, 0);
291 read_line(sock_fd, number_samples_str, STR_BUFFER_SIZE);
292 /* how many samples? */
294 if (strcmp(number_samples_str, "-2") == 0) {
295 printf("TTS Error\n");
299 if (strcmp(number_samples_str, "-1") != 0) {
300 number_samples = atoi(number_samples_str);
302 printf("Receiving : %d samples\n", number_samples);
304 receive_play_samples(sock_fd, number_samples);
307 while (strcmp(number_samples_str, "-1") != 0 &&
308 strcmp(number_samples_str, "-2") != 0);
312 (first_byte_time.tv_sec - start_time.tv_sec)*1000 +
313 (first_byte_time.tv_usec - start_time.tv_usec)/1000;
315 printf("FirstByte : %li ms\n", elapsed_time);
323 * Receive the given number of wave samples and play it to the audio
327 * sock_fd : the socket file descriptor
328 * number_samples : the number of wave samples to receive from the socket
330 void receive_play_samples(int sock_fd, int number_samples) {
336 short socket_buffer[AUDIO_BUFFER_SIZE];
338 bytes_remaining = number_samples;
342 /* read the samples from the socket, and write it to the audio device */
344 while (bytes_remaining > 0) {
346 if (bytes_remaining >= AUDIO_BUFFER_SIZE) {
347 bytes_to_read = AUDIO_BUFFER_SIZE;
350 bytes_to_read = bytes_remaining;
353 if ((nread = read(sock_fd, socket_buffer, bytes_to_read)) == -1) {
354 perror("error reading samples");
357 if (metrics && !first_byte_received) {
358 gettimeofday(&first_byte_time, NULL);
359 first_byte_received = 1;
362 if ((nsend = write(audio_fd, socket_buffer, nread)) == -1) {
363 perror("error playing samples");
366 bytes_remaining -= nread;
374 * Reads a line of input from the given file descriptor, and save it
375 * in the given buffer.
378 * sock_fd : the (socket) file descriptor
379 * buffer : the buffer to save the line read
380 * buffer_size : size of the buffer
383 * The number of characters in the line, not including end of line character.
385 int read_line(int sock_fd, char *buffer, int buffer_size) {
390 for (i = 0; i < (buffer_size-1); i++) {
391 read(sock_fd, &rc, 1);
404 * Returns 1 if the given string contains text, ie, it does not only
405 * contain the space, newline or tab characters.
408 * string : the input string
409 * length : the string length
411 int is_string_nonempty(char *string, int length) {
413 for (i = 0; i < length; i++) {
414 if (string[i] != ' ' && string[i] != '\n' && string[i] != '\t') {
423 * Opens the audio device file, and returns the file descriptor,
424 * or -1 if an error occurred.
427 * The audio device file descriptor.
429 int open_audio_device() {
431 char *audio_device = AUDIO_DEVICE_FILE;
433 if ((audio_fd = open(audio_device, O_WRONLY)) == -1) {
435 /* the device might be a SunRay, so get the $AUDIODEV env var */
436 audio_device = getenv(AUDIO_DEVICE_ENV_VAR);
438 if (audio_device != NULL) {
439 if ((audio_fd = open(audio_device, O_RDWR)) == -1) {
440 perror("Can't open audio device with environment variable");
445 perror("Can't open audio device");
450 if (set_pcm_linear() == FALSE) {
451 perror("fail to set audio device to PCM linear");
460 * Attempts to set the audio format of the audio device to 16-bit
461 * PCM linear, at the given sample rate.
464 * TRUE if the audio format was set successfully
467 int set_pcm_linear() {
471 // AUDIO_INITINFO(&info);
473 ioctl(audio_fd, AUDIO_GETINFO, &info);
475 info.play.encoding = AUDIO_ENCODING_LINEAR;
476 info.play.precision = 16;
477 info.play.channels = 1;
478 info.play.sample_rate = sample_rate;
480 set_status = ioctl(audio_fd, AUDIO_SETINFO, &info);
482 if (set_status == -1) {