/**
* @file adin_tcpip.c
*
*
* @brief ネットワーク入力:adinnet クライアントからの音声入力
*
* 入力ソースとして Julius の adinnet クライアントを使用する低レベル関数です.
*
* adinnet は ネットワーク上で音声データを送信する Julius 独自の方式です.
* この入力が選ばれた場合,Julius はadinnetサーバとなり,起動時に
* クライアントの接続を待ちます.サンプルのadinnetクライアントとして,
* adintool が Julius に付属していますので参考にしてください.
*
* Linux, Windows, その他サポートされているほとんどの OS で動作します.
*
* 他にネットワーク上で音声データをやりとりする方法として, Linux では
* EsounD を使う方法があります.adin_esd.c をご覧ください.
*
* @attention サーバ側とクライアント側でサンプリングレート
* の設定を一致させる必要があります.接続時に両者間でチェックは行われません.
*
* @bug マシンバイトオーダーの異なるマシン同士で正しく通信できません.
*
*
* @brief Audio input from adinnet client
*
* Low level I/O functions for audio input from adinnet client.
* The adinnet server/client is a scheme to transfer speech data via tcp/ip
* network with segmentation information, implemented for Julius.
* When this input is selected, Julius becomes adinnet server and
* wait for a client to connect.
* The sample client "adintool" is also included in Julius package.
*
* This input works on Linux, Windows and most OS where Julius can run.
*
* "EsounD" is an another method of obtaining audio input from network
* on Linux. See adin_esd.c for details.
*
* @attention The sampling rate setting on both side (server and client)
* should be the same. They will not be checked when connected.
*
* @bug Does not work between different machine byte order.
*
*
* @author Akinobu LEE
* @date Mon Feb 14 14:55:03 2005
*
* $Revision: 1.7 $
*
*/
/*
* Copyright (c) 1991-2012 Kawahara Lab., Kyoto University
* Copyright (c) 2000-2005 Shikano Lab., Nara Institute of Science and Technology
* Copyright (c) 2005-2012 Julius project team, Nagoya Institute of Technology
* All rights reserved
*/
#include
#include
#include
static int adinnet_sd = -1; ///< Listen socket for adinserv
static int adinnet_asd = -1; ///< Accept socket for adinserv
#ifdef FORK_ADINNET
static pid_t child; /* child process ID (0 if myself is child) */
#endif
/**
* Initialize as adinnet server: prepare to become server.
*
* @param freq [in] required sampling frequency
* @param port_str [in] port number in string
*
* @return TRUE on success, FALSE on failure.
*/
boolean
adin_tcpip_standby(int freq, void *port_str)
{
int port;
port = atoi((char *)port_str);
if ((adinnet_sd = ready_as_server(port)) < 0) {
jlog("Error: adin_tcpip: cannot ready for server\n");
return FALSE;
}
jlog("Stat: adin_tcpip: ready for server\n");
return TRUE;
}
/**
* Wait for connection from adinnet client and begin audio input stream.
*
* @param pathname [in] path name to open or NULL for default
*
* @return TRUE on success, FALSE on failure.
*/
boolean
adin_tcpip_begin(char *pathname)
{
#ifdef FORK_ADINNET
/***********************************/
/*** server infinite loop here!! ***/
/***********************************/
for (;;) {
/* wait connection */
jlog("Stat: adin_tcpip: waiting connection...\n");
if ((adinnet_asd = accept_from(adinnet_sd)) < 0) {
return FALSE;
}
jlog("Stat: adin_tcpip: connected\n");
/* fork self */
child = fork();
if (child < 0) { /* error */
jlog("Error: adin_tcpip: fork failed\n");
return FALSE;
}
/* child thread should handle this request */
if (child == 0) { /* child thread */
break; /* proceed */
} else { /* parent thread */
jlog("Stat: adin_tcpip: forked process [%d] handles this request\n", child);
}
}
#else /* ~FORK_ADINNET */
jlog("Stat: adin_tcpip: waiting connection...\n");
if ((adinnet_asd = accept_from(adinnet_sd)) < 0) {
return FALSE;
}
jlog("Stat: adin_tcpip: connected\n");
#endif /* FORK_ADINNET */
return TRUE;
}
/**
* @brief End recording.
*
* If last input segment was segmented by end of connection, close socket
* and wait for next connection. Otherwise, it means that the last input
* segment was segmented by either end-of-segment signal
* from client side or speech detection function in server side, so just
* wait for a next input in the current socket.
*
* @return TRUE on success, FALSE on failure.
*/
boolean
adin_tcpip_end()
{
/* end of connection */
close_socket(adinnet_asd);
#ifdef FORK_ADINNET
/* terminate this child process here */
jlog("Stat: adin_tcpip: connection end, child process now exit\n");
exit(0);
#else
/* wait for the next connection */
jlog("Stat: adin_tcpip: connection end\n");
#endif
return TRUE;
}
/**
* Try to read @a sampnum samples and returns actual sample num recorded.
* This function will not block.
*
* If data of zero length has been received, it is treated as a end-of-segment
* marker from the client. If data below zero length has been received,
* it means the client has finished the overall input stream transmission and
* want to disconnect.
*
* @param buf [out] samples obtained in this function
* @param sampnum [in] wanted number of samples to be read
*
* @return actural number of read samples, -1 if EOF, -2 if error.
*/
int
adin_tcpip_read(SP16 *buf, int sampnum)
{
int cnt, ret;
fd_set rfds;
struct timeval tv;
int status;
/* check if some commands are waiting in queue */
FD_ZERO(&rfds);
FD_SET(adinnet_asd, &rfds);
tv.tv_sec = 0;
tv.tv_usec = 10000; /* 10msec */
status = select(adinnet_asd+1, &rfds, NULL, NULL, &tv);
if (status < 0) { /* error */
jlog("Error: adin_tcpip: failed to poll socket\n");
return -2; /* error return */
}
if (status > 0) { /* there are some data */
/* read one data segment, leave rest even if any for avoid blocking */
ret = rd(adinnet_asd, (char *)buf, &cnt, sampnum * sizeof(SP16));
if (ret == 0) {
/* end of segment mark */
return -3;
}
if (ret < 0) {
/* end of input, mark */
return -1;
}
} else { /* time out, no data */
cnt = 0;
}
cnt /= sizeof(SP16);
#ifdef WORDS_BIGENDIAN
swap_sample_bytes(buf, cnt);
#endif
return cnt;
}
/**
* Tell the adinnet client to pause transfer.
*
* @return TRUE on success, FALSE on failure.
*/
boolean
adin_tcpip_send_pause()
{
int count;
char com;
/* send stop command to adinnet client */
com = '0';
count = wt(adinnet_asd, &com, 1);
if (count < 0) jlog("Warning: adin_tcpip: cannot send pause command to client\n");
jlog("Stat: adin_tcpip: sent pause command to client\n");
return TRUE;
}
/**
* Tell the adinnet client to resume the paused transfer.
*
* @return TRUE on success, FALSE on failure.
*/
boolean
adin_tcpip_send_resume()
{
int count;
char com;
int cnt, ret;
fd_set rfds;
struct timeval tv;
int status;
static char *tmpbuf = NULL;
/* check if some commands are waiting in queue */
count = 0;
do {
FD_ZERO(&rfds);
FD_SET(adinnet_asd, &rfds);
tv.tv_sec = 0;
tv.tv_usec = 0;
status = select(adinnet_asd+1, &rfds, NULL, NULL, &tv);
if (status < 0) { /* error */
jlog("Error: adin_tcpip: failed to poll socket\n");
return FALSE; /* error return */
}
if (status > 0) { /* there are some data */
if (tmpbuf == NULL) tmpbuf = (char *)mymalloc(MAXSPEECHLEN);
ret = rd(adinnet_asd, tmpbuf, &cnt, MAXSPEECHLEN * sizeof(SP16));
}
if (cnt > 0) count += cnt;
} while (status != 0);
if (count > 0) {
jlog("Stat: %d samples transfered while pause are flushed\n", count);
}
/* send resume command to adinnet client */
com = '1';
count = wt(adinnet_asd, &com, 1);
if (count < 0) jlog("Warning: adin_tcpip: cannot send resume command to client\n");
jlog("Stat: adin_tcpip: sent resume command to client\n");
/* flush current buffer */
return TRUE;
}
/**
* Tell the adinnet client to terminate transfer.
*
* @return TRUE on success, FALSE on failure.
*/
boolean
adin_tcpip_send_terminate()
{
int count;
char com;
/* send terminate command to adinnet client */
com = '2';
count = wt(adinnet_asd, &com, 1);
if (count < 0) jlog("Warning: adin_tcpip: cannot send terminate command to client\n");
jlog("Stat: adin_tcpip: sent terminate command to client\n");
return TRUE;
}
/**
*
* Function to return current input source device name
*
* @return string of current input device name.
*
*/
char *
adin_tcpip_input_name()
{
return("network socket");
}