From ff0a1baf66e5d4c4e93821a9732184e1cb5e7405 Mon Sep 17 00:00:00 2001 From: "Graydon, Tracy" Date: Wed, 28 Nov 2012 19:03:52 -0800 Subject: Initial commit for Tizen --- esdlib.c | 1749 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1749 insertions(+) create mode 100644 esdlib.c (limited to 'esdlib.c') diff --git a/esdlib.c b/esdlib.c new file mode 100644 index 0000000..384f51c --- /dev/null +++ b/esdlib.c @@ -0,0 +1,1749 @@ + +#include "config.h" +#include "esd.h" +#include "genrand.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#ifndef SUN_LEN +#define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \ + + strlen ((ptr)->sun_path)) +#endif + +/*******************************************************************/ +/* prototypes */ +int esd_set_socket_buffers( int sock, int src_format, + int src_rate, int base_rate ); +static void dummy_signal(int signum); + +/* dummy handler */ +static void dummy_signal(int signum) { + signal( signum, dummy_signal); + return; +} + +/* from esd_config.c */ +extern char esd_spawn_options[]; +extern int esd_no_spawn; +extern int esd_spawn_wait_ms; +void esd_config_read(void); + +/*******************************************************************/ +/* alternate implementations */ +#ifndef HAVE_INET_PTON +#ifndef HAVE_INET_ATON +int inet_aton(const char *cp, struct in_addr *inp) +{ + union { + unsigned int n; + char parts[4]; + } u; + int a=0,b=0,c=0,d=0, i; + + i = sscanf(cp, "%d.%d.%d.%d%*s", &a, &b, &c, &d); + + if(i != 4) + return 0; + + u.parts[0] = a; + u.parts[1] = b; + u.parts[2] = c; + u.parts[3] = d; + + inp->s_addr = u.n; + + return 1; +} +#endif +#endif + +static ssize_t +read_timeout (int fd, char *buf, size_t buflen) +{ + struct pollfd pfd[1]; + int flags, rv; + ssize_t n; + + pfd[0].fd = fd; + pfd[0].events = POLLIN; + + do { + pfd[0].revents = 0; + rv = poll (pfd, 1, 100); + } while (rv == -1 && (errno == EINTR || errno == EAGAIN)); + + if (rv < 1 || !(pfd[0].revents & POLLIN)) { + errno = ETIMEDOUT; + return -1; + } + + if ((flags = fcntl (fd, F_GETFL)) == -1) + return -1; + + fcntl (fd, F_SETFL, flags | O_NONBLOCK); + + do { + n = read (fd, buf, buflen); + } while (n == -1 && errno == EINTR); + + if (n == -1) { + rv = errno; + fcntl (fd, F_SETFL, flags); + errno = rv; + return -1; + } + + fcntl (fd, F_SETFL, flags); + + return n; +} + +static ssize_t +write_timeout (int fd, const char *buf, size_t buflen) +{ + struct pollfd pfd[1]; + size_t nwritten = 0; + int flags, rv; + ssize_t n; + + if ((flags = fcntl (fd, F_GETFL)) == -1) + return -1; + + fcntl (fd, F_SETFL, flags | O_NONBLOCK); + + do { + pfd[0].fd = fd; + pfd[0].events = POLLOUT; + + do { + pfd[0].revents = 0; + rv = poll (pfd, 1, 100); + } while (rv == -1 && (errno == EINTR || errno == EAGAIN)); + + if (rv < 1 || (pfd[0].revents & (POLLERR | POLLHUP | POLLOUT)) != POLLOUT) { + fcntl (fd, F_SETFL, flags); + errno = ETIMEDOUT; + return -1; + } + + do { + n = write (fd, buf + nwritten, buflen - nwritten); + } while (n == -1 && errno == EINTR); + + if (n == -1) { + rv = errno; + fcntl (fd, F_SETFL, flags); + errno = rv; + return -1; + } + + nwritten += n; + } while (nwritten < buflen); + + fcntl (fd, F_SETFL, flags); + + return nwritten; +} + +/** + * esd_set_socket_buffers: set buffer lengths on a socket to optimal. + * @sock: ESD socket + * @src_format: data format + * @src_rate: sample rate for this stream + * @base_rate: sample rate that server is running at. + * + * Sets send and receive buffer lengths to optimal length for audio data + * transfer. + * + * Return Value: Size that buffers were set to. + */ +int esd_set_socket_buffers( int sock, int src_format, + int src_rate, int base_rate ) +{ + int buf_size = ESD_BUF_SIZE; + + if ( src_rate > 0 ) buf_size = ( buf_size * base_rate ) / src_rate; + if ( ( src_format & ESD_MASK_BITS ) == ESD_BITS16 ) + buf_size *= 2; + if ( ! ( ( src_format & ESD_MASK_CHAN ) == ESD_MONO ) ) + buf_size *= 2; + + setsockopt( sock, SOL_SOCKET, SO_SNDBUF, &buf_size, sizeof( buf_size ) ); + setsockopt( sock, SOL_SOCKET, SO_RCVBUF, &buf_size, sizeof( buf_size ) ); + return buf_size; +} + +/** + * esd_get_latency: get stream latency + * @esd: ESD socket + * + * Get the stream latency to esound (latency is number of samples + * at 44.1khz stereo 16 bit - you'll have to adjust if your input + * sampling rate is less (in bytes per second)) + * + * Return Value: Latency, in number of samples at 44.1khz stereo 16 bit. + */ +int esd_get_latency(int esd) +{ + int lag = 0; + int proto = ESD_PROTO_LATENCY; + void (*phandler)(int); + +/* this is unavoidable - in case ESD "disappears" (ie the socket conn dies) */ +/* we need to catch SIGPIPE to avoid the default handler giving us */ +/* a bad day - ignore the SIGPIPE, then make sure to catch all errors */ + phandler = signal( SIGPIPE, dummy_signal ); /* for closed esd conns */ + /* send the necessary information */ + if ( write_timeout( esd, (const char *) &proto, sizeof(proto) ) != sizeof(proto) ) { + signal( SIGPIPE, phandler ); + return -1; + } + + /* get the latency back from the server */ + if ( read_timeout( esd, (char *) &lag, sizeof(lag) ) != sizeof(lag) ) { + signal( SIGPIPE, phandler ); + return -1; + } + + /* diagnostic info */ + /* + if ( getenv( "ESDBG" ) ) + printf( "esound getting latency\n" ); + */ + + /* return the sample id to the client */ + signal( SIGPIPE, phandler ); + + lag += ESD_BUF_SIZE * 2; + + return lag; +} + + +/** + * esd_send_auth: send authorization to esd + * @sock: ESD socket + * + * Send the authorization cookie, creating a new one if needed. + * + * Return Value: -1 on error, 0 if authorization was refused by server, + * 1 if authorization was accepted. + **/ +int esd_send_auth( int sock ) +{ + int auth_fd = -1; + int endian = ESD_ENDIAN_KEY; + int reply; + char *auth_filename = NULL; + unsigned char auth_key[ESD_KEY_LEN]; + const char *home = NULL; + int namelen, retval; + void (*phandler)(int); + +/* this is unavoidable - in case ESD "disappears" (ie the socket conn dies) */ +/* we need to catch SIGPIPE to avoid the default handler giving us */ +/* a bad day - ignore the SIGPIPE, then make sure to catch all errors */ + phandler = signal( SIGPIPE, dummy_signal ); /* for closed esd conns */ + + /* assemble the authorization filename */ + home = getenv( "HOME" ); + if ( !home ) { + fprintf( stderr, "HOME environment variable not set?\n" ); + signal( SIGPIPE, phandler ); + return -1; + } + + namelen = strlen(home) + sizeof("/.esd_auth"); + if ((auth_filename = malloc(namelen + 1)) == 0) { + fprintf( stderr, "Memory exhausted\n" ); + signal( SIGPIPE, phandler ); + return -1; + } + + strcpy( auth_filename, home ); + strcat( auth_filename, "/.esd_auth" ); + + retval = 0; + /* open the authorization file */ + if ( -1 == (auth_fd = open( auth_filename, O_RDONLY ) ) ) { + + /* it doesn't exist? create one */ + auth_fd = open( auth_filename, O_RDWR | O_CREAT | O_EXCL, + S_IRUSR | S_IWUSR ); + + if ( -1 == auth_fd ) { + /* coun't even create it? bail */ + perror( auth_filename ); + goto exit_fn; + } + + esound_genrand(auth_key, ESD_KEY_LEN); + write_timeout( auth_fd, (const char *) auth_key, ESD_KEY_LEN); + } else { + /* read the key from the authorization file */ + if ( ESD_KEY_LEN != read_timeout( auth_fd, (char *) auth_key, ESD_KEY_LEN ) ) + goto exit_fd; + } + + /* send the key to the server */ + if ( ESD_KEY_LEN != write_timeout( sock, (const char *) auth_key, ESD_KEY_LEN ) ) + /* send key failed */ + goto exit_fd; + + /* send the key to the server */ + if ( sizeof(endian) != write_timeout( sock, (const char *) &endian, sizeof(endian) ) ) + /* send key failed */ + goto exit_fd; + + /* read auth reply. esd will reply 1 as an int for yes and 0 for no */ + /* then close the connection */ + if ( sizeof(reply) != read_timeout( sock, (char *) &reply, sizeof(reply) ) ) { + /* read ok failed */ + retval = 0; + goto exit_fd; + } + /* we got a reply and it's no - so esd will close the socket now */ + /* on us anyway... time to return invalid auth... */ + if (reply == 0) { + /* auth failed */ + retval = 0; + goto exit_fd; + } + + + /* we've run the gauntlet, everything's ok, proceed as usual */ + /* fsync( sock ); */ + retval = 1; + + exit_fd: + close( auth_fd ); + exit_fn: + free( auth_filename ); + signal( SIGPIPE, phandler ); + return retval; +} + +/** + * esd_lock: disable foreign clients + * @esd: ESD socket + * + * Locks the ESD on the end of the specified socket, so that it will not + * accept connections from untrusted clients - eg, clients which do not + * present the appropriate key. + * + * Counterpart to esd_unlock(). + * + * Return Value: -1 on error, 0 if failed, otherwise success. + */ +int esd_lock( int esd ) { + int proto = ESD_PROTO_LOCK; + int ok = 0; + void (*phandler)(int); + +/* this is unavoidable - in case ESD "disappears" (ie the socket conn dies) */ +/* we need to catch SIGPIPE to avoid the default handler giving us */ +/* a bad day - ignore the SIGPIPE, then make sure to catch all errors */ + phandler = signal( SIGPIPE, dummy_signal ); /* for closed esd conns */ + /* diagnostic info */ + /* + if ( getenv( "ESDBG" ) ) + printf( "esound locking\n" ); + */ + + write_timeout( esd, (const char *) &proto, sizeof(proto) ); + esd_send_auth( esd ); + + if ( read_timeout( esd, (char *) &ok, sizeof(ok) ) != sizeof(ok) ) { + signal( SIGPIPE, phandler ); + return -1; + } + + signal( SIGPIPE, phandler ); + return ok; +} + +/** + * esd_unlock: disable foreign clients + * @esd: ESD socket + * + * Unlocks the ESD on the end of the specified socket, so that it will + * accept connections from untrusted clients - eg, clients which do not + * present the appropriate key. + * + * Counterpart to esd_lock(). + * + * Return Value: -1 on error, 0 if failed, otherwise success. + */ +int esd_unlock( int esd ){ + int proto = ESD_PROTO_UNLOCK; + int ok = 0; + void (*phandler)(int); + +/* this is unavoidable - in case ESD "disappears" (ie the socket conn dies) */ +/* we need to catch SIGPIPE to avoid the default handler giving us */ +/* a bad day - ignore the SIGPIPE, then make sure to catch all errors */ + phandler = signal( SIGPIPE, dummy_signal ); /* for closed esd conns */ + /* diagnostic info */ + /* + if ( getenv( "ESDBG" ) ) + printf( "esound unlocking\n" ); + */ + + write_timeout( esd, (const char *) &proto, sizeof(proto) ); + esd_send_auth( esd ); + + if ( read_timeout( esd, (char *) &ok, sizeof(ok) ) != sizeof(ok) ) { + signal( SIGPIPE, phandler ); + return -1; + } + + signal( SIGPIPE, phandler ); + return ok; +} + +/** + * esd_standby: release audio device + * @esd: ESD socket + * + * Causes the ESD on the end of the specified socket to stop playing sounds + * and release its connection to the audio device so that other processes + * may use it. + * + * Counterpart to esd_resume(). + * + * Return Value: -1 on error, 0 if failed, otherwise success. + */ +int esd_standby( int esd ) +{ + int proto = ESD_PROTO_STANDBY; + int ok = 0; + void (*phandler)(int); + +/* this is unavoidable - in case ESD "disappears" (ie the socket conn dies) */ +/* we need to catch SIGPIPE to avoid the default handler giving us */ +/* a bad day - ignore the SIGPIPE, then make sure to catch all errors */ + phandler = signal( SIGPIPE, dummy_signal ); /* for closed esd conns */ + /* diagnostic info */ + /* + if ( getenv( "ESDBG" ) ) + printf( "esound standing by\n" ); + */ + + write_timeout( esd, (const char *) &proto, sizeof(proto) ); + esd_send_auth( esd ); + + if ( read_timeout( esd, (char *) &ok, sizeof(ok) ) != sizeof(ok) ) { + signal( SIGPIPE, phandler ); + return -1; + } + + signal( SIGPIPE, phandler ); + return ok; +} + +/** + * esd_resume: reclaim audio device + * @esd: ESD socket + * + * Causes the ESD on the end of the specified socket to attempt to reconnect + * to the audio device and start playing sounds again. + * + * Counterpart to esd_standby(). + * + * Return Value: -1 on error, 0 if failed, otherwise success. + */ +int esd_resume( int esd ) +{ + int proto = ESD_PROTO_RESUME; + int ok = 0; + void (*phandler)(int); + +/* this is unavoidable - in case ESD "disappears" (ie the socket conn dies) */ +/* we need to catch SIGPIPE to avoid the default handler giving us */ +/* a bad day - ignore the SIGPIPE, then make sure to catch all errors */ + phandler = signal( SIGPIPE, dummy_signal ); /* for closed esd conns */ + /* diagnostic info */ + /* + if ( getenv( "ESDBG" ) ) + printf( "esound resuming\n" ); + */ + + write_timeout( esd, (const char *) &proto, sizeof(proto) ); + esd_send_auth( esd ); + + if ( read_timeout( esd, (char *) &ok, sizeof(ok) ) != sizeof(ok) ) { + signal( SIGPIPE, phandler ); + return -1; + } + + signal( SIGPIPE, phandler ); + return ok; +} + +static int +connect_timeout (int sockfd, const struct sockaddr *serv_addr, size_t addrlen, int timeout) +{ + struct pollfd pfd[1]; + int flags, rv; + + if ((flags = fcntl (sockfd, F_GETFL)) == -1) + return -1; + + fcntl (sockfd, F_SETFL, flags | O_NONBLOCK); + + if (connect (sockfd, serv_addr, addrlen) == 0) { + fcntl (sockfd, F_SETFL, flags); + return 0; + } + + if (errno != EINPROGRESS) + return -1; + + pfd[0].fd = sockfd; + pfd[0].events = POLLIN | POLLOUT; + + do { + pfd[0].revents = 0; + rv = poll (pfd, 1, timeout); + } while (rv == -1 && errno == EINTR); + + if (pfd[0].revents & (POLLIN | POLLOUT)) { + /* success, we are now connected */ + fcntl (sockfd, F_SETFL, flags); + return 0; + } + + /* took too long / error connecting, either way: epic fail */ + return -1; +} + +/** + * esd_connect_tcpip: make a TCPIP connection to ESD + * @host: specifies hostname and port to connect to as "hostname:port" + * Both parts are optional, the default being to connect to localhost on + * ESD_DEFAULT_PORT. This default is used if host is NULL. + * + * Attempts to make a connection to ESD using TCPIP. + * Similar to esd_connect_unix(). + * + * Return Value: -1 on error, else a socket number connected to ESD. + */ +static int +esd_connect_tcpip(const char *host) +{ + const char *espeaker = NULL; + struct hostent *he; + struct sockaddr_in socket_addr; + int socket_out = -1; + int curstate = 1; + char default_host[] = "127.0.0.1"; + char connect_host[64]; + int port = ESD_DEFAULT_PORT; + +#if defined (ENABLE_IPV6) + if ( have_ipv6() ) { + int cnt, i; + char *loc; + struct addrinfo hints, *result = NULL, *res; + + memset (&hints, 0, sizeof (hints)); + hints.ai_socktype = SOCK_STREAM; + connect_host[0] = '\0'; + + if ( host ) { + char *host_dup; + char *s; + + host_dup = strdup (host); + if (!host_dup) { + fprintf(stderr, "Out of memory\n"); + return -1; + } + s = host_dup; + cnt = 0; + for ( i = 0; i < strlen (s); i++ ) + if ( s[i] == ':' ) + cnt++; + + if (( cnt == 1 ) && ((loc = strchr (s, ':')) != NULL)) { + *loc = '\0'; + strcpy (connect_host, s); + port = atoi (loc + 1); + } + else { + if (( cnt != 0 ) && ((loc = strchr (s, ']')) != NULL )) { + *loc = '\0'; + strcpy ( connect_host, ++s ); + port = atoi ( loc + 2 ); + } + else + strcpy ( connect_host, s ); + } + free (host_dup); + } + if( port == 0 ) + port = ESD_DEFAULT_PORT; + + if ( !strlen ( connect_host ) ) + strcpy ( connect_host, "localhost" ); + + if ( getaddrinfo ( connect_host, NULL, &hints, &result ) != 0 ) { + printf ("Usage:program_name [address][:port]"); + return (-1); + } + + for ( res = result; res ;res = res->ai_next ) { + if ( res->ai_family != AF_INET && res->ai_family != AF_INET6 ) + continue; + + if ( res->ai_family == AF_INET ) + ((struct sockaddr_in *)res->ai_addr)->sin_port = htons(port); + + if ( res->ai_family == AF_INET6 ) + ((struct sockaddr_in6 *)res->ai_addr)->sin6_port = htons(port); + + socket_out = socket ( res->ai_family, SOCK_STREAM, 0 ); + + if ( socket_out < 0 ) { + fprintf (stderr,"Unable to create TCP socket\n"); + goto error_out; + } + + /* this was borrowed blindly from the Tcl socket stuff */ + if ( fcntl( socket_out, F_SETFD, FD_CLOEXEC ) < 0 ) { + fprintf(stderr,"Unable to set socket to non-blocking\n"); + goto error_out; + } + + if ( setsockopt( socket_out, SOL_SOCKET, SO_REUSEADDR, + &curstate, sizeof(curstate) ) < 0 ) { + fprintf(stderr,"Unable to set for a fresh socket\n"); + goto error_out; + } + + if ( connect_timeout ( socket_out, res->ai_addr, res->ai_addrlen, 1000 ) != -1 ) + break; + + close ( socket_out ); + } + + freeaddrinfo ( result ); + + if (!res) /* Couldn't connect to any address */ + return -1; + } + else +#endif + { + unsigned int host_div = 0; + memset (&socket_addr, 0, sizeof (socket_addr)); + memset (&he, 0, sizeof (he)); + /* see if we have a remote speaker to play to */ + espeaker = host; + if ( espeaker && *espeaker ) { + strncpy(connect_host, espeaker, sizeof(connect_host)); + + /* split the espeaker host into host:port */ + host_div = strcspn( connect_host, ":" ); + if(host_div > 0 && host_div < strlen(espeaker)) { + connect_host[ host_div ] = '\0'; + } + else if ( host_div == 0) + strcpy( connect_host, default_host ); + + connect_host[sizeof(connect_host) - 1] = '\0'; + + /* Resolving the host name */ + if ( ( he = gethostbyname( connect_host ) ) == NULL ) { + fprintf( stderr, "Can\'t resolve host name \"%s\"!\n", + connect_host); + return(-1); + } + memcpy( (struct in_addr *) &socket_addr.sin_addr, he->h_addr, + sizeof( struct in_addr ) ); + + /* get port */ + if ( host_div < strlen( espeaker ) ) + port = atoi( espeaker + host_div + 1 ); + if ( !port ) + port = ESD_DEFAULT_PORT; + /* printf( "(remote) host is %s : %d\n", connect_host, port ); */ + } +#ifdef HAVE_INET_PTON + else if( !inet_pton(AF_INET, default_host, &socket_addr.sin_addr ) ) { +#else + else if( !inet_aton( default_host, &socket_addr.sin_addr ) ) { +#endif + fprintf( stderr, "couldn't convert %s to inet address\n", + default_host ); + return -1; + } + + /* create the socket, and set for non-blocking */ + socket_out = socket( AF_INET, SOCK_STREAM, 0 ); + if ( socket_out < 0 ) + { + fprintf(stderr,"Unable to create TCP socket\n"); + goto error_out; + } + + /* this was borrowed blindly from the Tcl socket stuff */ + if ( fcntl( socket_out, F_SETFD, FD_CLOEXEC ) < 0 ) + { + fprintf(stderr,"Unable to set socket to non-blocking\n"); + goto error_out; + } + + if ( setsockopt( socket_out, SOL_SOCKET, SO_REUSEADDR, + &curstate, sizeof(curstate) ) < 0 ) + { + fprintf(stderr,"Unable to set for a fresh socket\n"); + goto error_out; + } + + /* set the connect information */ + socket_addr.sin_family = AF_INET; + socket_addr.sin_port = htons( port ); + + if ( connect_timeout ( socket_out, + (struct sockaddr *) &socket_addr, + sizeof(struct sockaddr_in), 1000 ) < 0 ) + goto error_out; + + } + return socket_out; + + error_out: + if( socket_out >= 0 ) + close( socket_out ); + return -1; +} + +/** + * esd_connect_unix: make a local UNIX socket connection to ESD + * + * Attempts to make a connection to ESD using local UNIX sockets. + * Similar to esd_connect_tcpip(). + * + * Return Value: -1 on error, else a socket number connected to ESD. + */ +static int +esd_connect_unix(void) +{ + struct sockaddr_un socket_unix; + int socket_out = -1; + int curstate = 1; + + /* create the socket, and set for non-blocking */ + socket_out = socket( AF_UNIX, SOCK_STREAM, 0 ); + if ( socket_out < 0 ) + { + fprintf(stderr,"Unable to create socket\n"); + goto error_out; + } + + /* this was borrowed blindly from the Tcl socket stuff */ + if ( fcntl( socket_out, F_SETFD, FD_CLOEXEC ) < 0 ) + { + fprintf(stderr,"Unable to set socket to close-on-exec\n"); + goto error_out; + } + if ( setsockopt( socket_out, SOL_SOCKET, SO_REUSEADDR, + &curstate, sizeof(curstate) ) < 0 ) + { + fprintf(stderr,"Unable to set for a fresh socket\n"); + goto error_out; + } + + /* set the connect information */ + socket_unix.sun_family = AF_UNIX; + strncpy(socket_unix.sun_path, ESD_UNIX_SOCKET_NAME, sizeof(socket_unix.sun_path)); + + if ( connect_timeout ( socket_out, (struct sockaddr *) &socket_unix, SUN_LEN(&socket_unix), 100 ) < 0 ) + goto error_out; + + return socket_out; + + error_out: + if(socket_out >= 0) + close(socket_out); + return -1; +} + +static int is_host_local(const char* host) +{ + char hnbuf[256]=""; + if (!host || !*host) return 1; + + gethostname(hnbuf, sizeof(hnbuf)); + return (!strcasecmp(host,hnbuf) || !strcasecmp(host, "localhost")); +} + +/** + * esd_open_sound: open a connection to ESD and get authorization + * @host: Specifies hostname and port to connect to as "hostname:port" + * Both parts are optional, the default being to connect to localhost on + * ESD_DEFAULT_PORT. This default is used if host is NULL. + * + * Attempts to make a connection to ESD on specified host, or the + * host specified by the $ESPEAKER environment variable if @host is NULL, + * or localhost if $ESPEAKER not set. + * + * Will attempt to connect via UNIX sockets if the host is localhost, and by + * TCPIP otherwise, or if UNIX sockets fail. + * + * If neither of these connection methods succeed, and we are attempting to + * contact the localhost, will attempt to spawn a local copy of ESD (unless + * configured not to in esd.conf), and will then try to connect to that + * using UNIX sockets. + * + * Once a connection is created, we attempt to give ESD the necessary + * authorisation keys to do things - only if this succeeds will the socket + * be given to the caller. + * + * Return Value: -1 on error, else a socket number connected and authorized + * to ESD. + */ +#define min(a,b) ( ( (a)<(b) ) ? (a) : (b) ) +int esd_open_sound( const char *rhost ) +{ + int socket_out = -1; + int len; + char use_unix = 0; + char display_host[ 256 ]; + const char *display; + char *host = NULL; + + if ( rhost ) + host = strdup(rhost); + else { + char *espeaker = getenv("ESPEAKER"); + if ( espeaker ) + host = strdup(espeaker); + } + + display = getenv( "DISPLAY" ); + if ( !(host && *host) && display ) { + /* no espeaker specified, but the app should be directed to a + remote display, so try routing the default port over there + and see if we strike gold */ + len = strcspn( display, ":" ); + if ( len ) { + len = min( len, 255 ); + strncpy( display_host, display, len ); + display_host[ len ] = '\0'; + if ( host ) free(host); + host = strdup(display_host); + } + } + + if ( is_host_local(host) ) { + if ( access( ESD_UNIX_SOCKET_NAME, R_OK | W_OK ) == -1 ) + use_unix = 0; + else + use_unix = 1; + } + if ( use_unix ) + socket_out = esd_connect_unix(); + if ( socket_out >= 0 ) goto finish_connect; + + socket_out = esd_connect_tcpip( host ); + if ( socket_out >= 0 ) goto finish_connect; + +#ifndef __EMX__ /* Still in work */ + /* Connections failed, period. Since nobody gave us a remote esd + to use, let's try spawning one. */ + /* ebm - I think this is an Inherently Bad Idea, but will leave it + alone until I can get a good look at it */ + if( is_host_local(host)) { + int childpid; + fd_set fdset; + struct timeval timeout; + int ret; + int esd_pipe[2]; + + esd_config_read(); + + if (esd_no_spawn) goto finish_connect; + + /* All this hackery so we can stop thrashing around if esd startup fails */ + /* there's something inherently flaky about this, and if + there's no audio device, Bad Things Happen */ + + /* do not bother trying if esd does not exist */ + if (access(SERVERDIR"/esd", X_OK) != 0) + goto finish_connect; + + if (pipe (esd_pipe) < 0) + goto finish_connect; + + childpid = fork(); + if(!childpid) { + char *preload2; + char *lib; + char *cmd; + + /* child process */ + close (esd_pipe[0]); + /* + * pull libesddsp out of LD_PRELOAD if it is there + */ + preload2 = getenv("LD_PRELOAD"); + if (preload2) + while ((lib = strstr(preload2,"libesddsp"))) { + char *preload = preload2; + char *start = preload; + char *end = preload+strcspn(preload," \t\n\0"); + int length; + + while(end < lib) { + start = end+1; + end = start+strcspn(start,"\t\n\0"); + } + length = (preload+strlen(preload))-(end+1); + preload2 = malloc(11+(start-preload)+length); + strcpy(preload2, "LD_PRELOAD="); + strncat(preload2, preload, start-preload); + strncat(preload2, end+1, length); + putenv(preload2); + } + cmd = malloc(strlen(SERVERDIR"/esd -spawnfd 9999999999") + strlen(esd_spawn_options)); + sprintf(cmd, "%s/esd %s -spawnfd %d", SERVERDIR, esd_spawn_options, esd_pipe[1]); + + if(!fork()) { + /* child of child process. Minimal so startup overhead is + * not included in the waiting time + */ + setsid(); + execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); + perror("execl"); + _exit(1); + } else + _exit(0); + + /* children end here */ + } else { + int estat; + + close(esd_pipe[1]); + while ((waitpid (childpid, &estat, 0)== -1) && (errno == EINTR)); + } + + /* Wait for spawning to happen. Time taken is system and load + * dependent, so read from config file. + */ + FD_ZERO (&fdset); + FD_SET (esd_pipe[0], &fdset); + + timeout.tv_sec = (esd_spawn_wait_ms * 1000) / 1000000; + timeout.tv_usec = (esd_spawn_wait_ms * 1000) % 1000000; + + ret = select (esd_pipe[0] + 1, &fdset, NULL, NULL, &timeout); + + if (ret == 1) { + char c; + ret = read_timeout (esd_pipe[0], &c, 1); + + if (ret == 1) { + socket_out = esd_connect_unix(); + if (socket_out < 0) + socket_out = esd_connect_tcpip(host); + } + } + + close (esd_pipe[0]); + } +#endif + finish_connect: + if (socket_out >= 0 + && !esd_send_auth (socket_out)) { + close(socket_out); socket_out = -1; + } + + if ( host ) free(host); + return socket_out; +} + +/** + * esd_play_stream: get socket for playing a stream + * @format: data format for this stream + * @rate: sample rate for this stream + * @host: host to connect to, as for esd_open_sound(). + * @name: name used to identify this stream to ESD. (Use NULL if you + * don't care what name you're given - but its generally more useful to give + * something helpful, such as your process name and id.) + * + * Creates a new connection to ESD, using esd_open_sound(), and sets it up + * for playing a stream of audio data, at sample rate @rate, + * + * Return Value: -1 on error, else an ESD socket number set up so that + * any data sent to the socket will be played by the ESD. + */ +int esd_play_stream( esd_format_t format, int rate, + const char *host, const char *name ) +{ + int sock; + int proto = ESD_PROTO_STREAM_PLAY; + char name_buf[ ESD_NAME_MAX ]; + void (*phandler)(int); + + /* connect to the EsounD server */ + sock = esd_open_sound( host ); + if ( sock < 0 ) + return sock; + + /* prepare the name buffer */ + if ( name ) + strncpy( name_buf, name, ESD_NAME_MAX ); + else + name_buf[ 0 ] = '\0'; + +/* this is unavoidable - in case ESD "disappears" (ie the socket conn dies) */ +/* we need to catch SIGPIPE to avoid the default handler giving us */ +/* a bad day - ignore the SIGPIPE, then make sure to catch all errors */ + phandler = signal( SIGPIPE, dummy_signal ); /* for closed esd conns */ + /* send the audio format information */ + if ( write_timeout( sock, (const char *) &proto, sizeof(proto) ) != sizeof(proto) ) { + signal( SIGPIPE, phandler ); + return -1; + } + if ( write_timeout( sock, (const char *) &format, sizeof(format) ) != sizeof(format) ) { + signal( SIGPIPE, phandler ); + return -1; + } + if( write_timeout( sock, (const char *) &rate, sizeof(rate) ) != sizeof(rate) ) { + signal( SIGPIPE, phandler ); + return -1; + } + if( write_timeout( sock, name_buf, ESD_NAME_MAX ) != ESD_NAME_MAX ) { + signal( SIGPIPE, phandler ); + return -1; + } + + /* Reduce buffers on sockets to the minimum needed */ + esd_set_socket_buffers( sock, format, rate, 44100 ); + + /* flush the socket */ + /* fsync( sock ); */ + + /* diagnostic info */ + /* + if ( getenv( "ESDBG" ) ) + printf( "esound playing stream\n" ); + */ + + signal( SIGPIPE, phandler ); + return sock; +} + +/** + * esd_play_stream_fallback: as esd_play_stream() but connect directly if no ESD + * @format: data format for this stream + * @rate: sample rate for this stream + * @host: host to connect to, as for esd_open_sound(). + * @name: name used to to identify this stream to ESD. + * + * Attempts to create a connection to an ESD, using esd_play_stream(), and if + * this fails falls back to trying to contact the soundcard directly. + * (This will not work unless the soundcard is local.) + * + * Return Value: -1 on error, else a socket number set up so that + * any data sent to the socket will be played, either by an ESD or by an. + */ +int esd_play_stream_fallback( esd_format_t format, int rate, + const char *host, const char *name ) +{ + int socket_out; + + /* try to open a connection to the server */ + if(!host) host = getenv("ESPEAKER"); + socket_out = esd_play_stream( format, rate, host, name ); + if ( socket_out >= 0 ) + return socket_out; + + /* if host is set, this is an error, bail out */ + if ( host ) + return -1; + + /* go for /dev/dsp */ + esd_audio_format = format; + esd_audio_rate = rate; + socket_out = esd_audio_open(); + + /* diagnostic info */ + /* + if ( getenv( "ESDBG" ) ) + printf( "esound playing stream fallback\n" ); + */ + + /* we either got it, or we didn't */ + return socket_out; +} + +/** + * esd_monitor_stream: get socket for monitoring an ESD + * @format: data format for this stream + * @rate: sample rate for this stream + * @host: host to connect to, as for esd_open_sound(). + * @name: name used to identify this stream to ESD. + * + * Creates a new connection to ESD, using esd_open_sound(), and sets it up + * for monitoring the output from the ESD. + * + * Return Value: -1 on error, else an ESD socket number set up so that + * any data played by the ESD will be sent to the socket. + */ +int esd_monitor_stream( esd_format_t format, int rate, + const char *host, const char *name ) +{ + int sock; + int proto = ESD_PROTO_STREAM_MON; + char name_buf[ ESD_NAME_MAX ]; + void (*phandler)(int); + + /* connect to the EsounD server */ + sock = esd_open_sound( host ); + if ( sock < 0 ) + return sock; + + /* prepare the name buffer */ + if ( name ) + strncpy( name_buf, name, ESD_NAME_MAX ); + else + name_buf[ 0 ] = '\0'; + +/* this is unavoidable - in case ESD "disappears" (ie the socket conn dies) */ +/* we need to catch SIGPIPE to avoid the default handler giving us */ +/* a bad day - ignore the SIGPIPE, then make sure to catch all errors */ + phandler = signal( SIGPIPE, dummy_signal ); /* for closed esd conns */ + /* send the audio format information */ + if ( write_timeout( sock, (const char *) &proto, sizeof(proto) ) != sizeof(proto) ) { + signal( SIGPIPE, phandler ); + return -1; + } + if ( write_timeout( sock, (const char *) &format, sizeof(format) ) != sizeof(format) ) { + signal( SIGPIPE, phandler ); + return -1; + } + if( write_timeout( sock, (const char *) &rate, sizeof(rate) ) != sizeof(rate) ) { + signal( SIGPIPE, phandler ); + return -1; + } + if( write_timeout( sock, name_buf, ESD_NAME_MAX ) != ESD_NAME_MAX ) { + signal( SIGPIPE, phandler ); + return -1; + } + + /* Reduce buffers on sockets to the minimum needed */ + esd_set_socket_buffers( sock, format, rate, 44100 ); + + /* flush the socket */ + /* fsync( sock ); */ + + /* diagnostic info */ + /* + if ( getenv( "ESDBG" ) ) + printf( "esound monitoring stream\n" ); + */ + + signal( SIGPIPE, phandler ); + return sock; +} + +/** + * esd_filter_stream: get socket for filtering sound produced by an ESD + * @format: data format for this stream + * @rate: sample rate for this stream + * @host: host to connect to, as for esd_open_sound(). + * @name: name used to identify this stream to ESD. + * + * Creates a new connection to ESD, using esd_open_sound(), and sets it up + * for filtering the output from the ESD. + * + * Reading from the stream will give a block of audio data, which is the + * mixed output of the ESD formatted as specified by the function parameters. + * The filter is free to process this data as it likes, but must then write + * an identically sized block of data back to the stream. The data so + * returned is played by the ESD (possibly after applying more filters to + * it.) + * + * The new filter will be placed at the head of the list of filters ie, it + * will receive data for processing first, and the next filter will receive + * the resultant processed data. + * + * Return Value: -1 on error, else an ESD socket number set up so that + * any data played by the ESD will be sent to the socket. + */ +int esd_filter_stream( esd_format_t format, int rate, + const char *host, const char *name ) +{ + int sock; + int proto = ESD_PROTO_STREAM_FILT; + char name_buf[ ESD_NAME_MAX ]; + void (*phandler)(int); + + /* connect to the EsounD server */ + sock = esd_open_sound( host ); + if ( sock < 0 ) + return sock; + + /* prepare the name buffer */ + if ( name ) + strncpy( name_buf, name, ESD_NAME_MAX ); + else + name_buf[ 0 ] = '\0'; + +/* this is unavoidable - in case ESD "disappears" (ie the socket conn dies) */ +/* we need to catch SIGPIPE to avoid the default handler giving us */ +/* a bad day - ignore the SIGPIPE, then make sure to catch all errors */ + phandler = signal( SIGPIPE, dummy_signal ); /* for closed esd conns */ + /* send the audio format information */ + if ( write_timeout( sock, (const char *) &proto, sizeof(proto) ) != sizeof(proto) ) { + signal( SIGPIPE, phandler ); + return -1; + } + if ( write_timeout( sock, (const char *) &format, sizeof(format) ) != sizeof(format) ) { + signal( SIGPIPE, phandler ); + return -1; + } + if( write_timeout( sock, (const char *) &rate, sizeof(rate) ) != sizeof(rate) ) { + signal( SIGPIPE, phandler ); + return -1; + } + if( write_timeout( sock, name_buf, ESD_NAME_MAX ) != ESD_NAME_MAX ) { + signal( SIGPIPE, phandler ); + return -1; + } + + /* Reduce buffers on sockets to the minimum needed */ + esd_set_socket_buffers( sock, format, rate, 44100 ); + + /* flush the socket */ + /* fsync( sock ); */ + + /* diagnostic info */ + /* + if ( getenv( "ESDBG" ) ) + printf( "esound filterng stream\n" ); + */ + + signal( SIGPIPE, phandler ); + return sock; +} + +/** + * esd_record_stream: get socket for recording via an ESD + * @format: data format for this stream + * @rate: sample rate for this stream + * @host: host to connect to, as for esd_open_sound(). + * @name: name used to identify this stream to ESD. + * + * Creates a new connection to ESD, using esd_open_sound(), and sets it up + * for recording data from the soundcard via the ESD. + * + * Return Value: -1 on error, else a socket number to which the external + * audio data arriving at the appropriate soundcard will be sent. + */ +int esd_record_stream( esd_format_t format, int rate, + const char *host, const char *name ) +{ + int sock; + int proto = ESD_PROTO_STREAM_REC; + char name_buf[ ESD_NAME_MAX ]; + void (*phandler)(int); + + /* connect to the EsounD server */ + sock = esd_open_sound( host ); + if ( sock < 0 ) + return sock; + + /* prepare the name buffer */ + if ( name ) + strncpy( name_buf, name, ESD_NAME_MAX ); + else + name_buf[ 0 ] = '\0'; + +/* this is unavoidable - in case ESD "disappears" (ie the socket conn dies) */ +/* we need to catch SIGPIPE to avoid the default handler giving us */ +/* a bad day - ignore the SIGPIPE, then make sure to catch all errors */ + phandler = signal( SIGPIPE, dummy_signal ); /* for closed esd conns */ + /* send the audio format information */ + if ( write_timeout( sock, (const char *) &proto, sizeof(proto) ) != sizeof(proto) ) { + signal( SIGPIPE, phandler ); + return -1; + } + if ( write_timeout( sock, (const char *) &format, sizeof(format) ) != sizeof(format) ) { + signal( SIGPIPE, phandler ); + return -1; + } + if( write_timeout( sock, (const char *) &rate, sizeof(rate) ) != sizeof(rate) ) { + signal( SIGPIPE, phandler ); + return -1; + } + if( write_timeout( sock, name_buf, ESD_NAME_MAX ) != ESD_NAME_MAX ) { + signal( SIGPIPE, phandler ); + return -1; + } + + /* Reduce buffers on sockets to the minimum needed */ + esd_set_socket_buffers( sock, format, rate, 44100 ); + + /* flush the socket */ + /* fsync( sock ); */ + + /* diagnostic info */ + /* + if ( getenv( "ESDBG" ) ) + printf( "esound recording stream\n" ); + */ + + signal( SIGPIPE, phandler ); + return sock; +} + +/** + * esd_record_stream_fallback: esd_record_stream() but connect direct if no ESD + * @format: data format for this stream + * @rate: sample rate for this stream + * @host: host to connect to, as for esd_open_sound(). + * @name: name used to identify this stream to ESD. + * + * Attempts to create a connection to an ESD, using esd_record_stream(), and if + * this fails falls back to trying to contact the soundcard directly. + * (This will not work unless the soundcard is local.) + * + * Return Value: -1 on error, else a socket number to which the external + * audio data arriving at the appropriate soundcard will be sent. + */ +int esd_record_stream_fallback( esd_format_t format, int rate, + const char *host, const char *name ) +{ + int socket_out; + + /* try to open a connection to the server */ + if (!host) host = getenv("ESPEAKER"); + socket_out = esd_record_stream( format, rate, host, name ); + if ( socket_out >= 0 ) + return socket_out; + + /* if ESPEAKER is set, this is an error, bail out */ + if ( host ) + return -1; + + /* go for /dev/dsp */ + esd_audio_format = format; + esd_audio_rate = rate; + socket_out = esd_audio_open(); + + /* Reduce buffers on sockets to the minimum needed */ + esd_set_socket_buffers( socket_out, format, rate, 44100 ); + + /* diagnostic info */ + /* + if ( getenv( "ESDBG" ) ) + printf( "esound recording stream fallback\n" ); + */ + + /* we either got it, or we didn't */ + return socket_out; +} + +/** + * esd_sample_cache: Cache sample in server + * @esd: server to cache the data in + * @format: data format for this stream + * @rate: sampling rate of the sample + * @size: size of the sample + * @name: name of the sample being cached + * + * Caches a sample in the server. + * + * Return value: ID of the sample cached, or -1 on error. + */ +int esd_sample_cache( int esd, esd_format_t format, const int rate, + const int size, const char *name ) +{ + int id = 0; + int proto = ESD_PROTO_SAMPLE_CACHE; + void (*phandler)(int); + + /* prepare the name buffer */ + char name_buf[ ESD_NAME_MAX ]; + if ( name ) + strncpy( name_buf, name, ESD_NAME_MAX ); + else + name_buf[ 0 ] = '\0'; + /* printf( "caching sample: %s (%d) - %ld bytes\n", + name_buf, esd, size ); */ + +/* this is unavoidable - in case ESD "disappears" (ie the socket conn dies) */ +/* we need to catch SIGPIPE to avoid the default handler giving us */ +/* a bad day - ignore the SIGPIPE, then make sure to catch all errors */ + phandler = signal( SIGPIPE, dummy_signal ); /* for closed esd conns */ + /* send the necessary information */ + if ( write_timeout( esd, (const char *) &proto, sizeof(proto) ) != sizeof(proto) ) { + signal( SIGPIPE, phandler ); + return -1; + } + + if ( write_timeout( esd, (const char *) &format, sizeof(format) ) != sizeof(format) ) { + signal( SIGPIPE, phandler ); + return -1; + } + if ( write_timeout( esd, (const char *) &rate, sizeof(rate) ) != sizeof(rate) ) { + signal( SIGPIPE, phandler ); + return -1; + } + if ( write_timeout( esd, (const char *) &size, sizeof(size) ) != sizeof(size) ) { + signal( SIGPIPE, phandler ); + return -1; + } + if ( write_timeout( esd, name_buf, ESD_NAME_MAX ) != ESD_NAME_MAX ) { + signal( SIGPIPE, phandler ); + return -1; + } + + /* flush the socket */ + /* fsync( esd ); */ + + /* get the sample id back from the server */ + if ( read_timeout( esd, (char *) &id, sizeof(id) ) != sizeof(id) ) { + signal( SIGPIPE, phandler ); + return -1; + } + + /* diagnostic info */ + /* + if ( getenv( "ESDBG" ) ) + printf( "esound caching sample\n" ); + */ + + /* return the sample id to the client */ + signal( SIGPIPE, phandler ); + return id; +} + +/*******************************************************************/ +/* call this after sending the sample data to the server, should */ +/* return the same sample id read previously, <= 0 is error */ +int esd_confirm_sample_cache( int esd ) +{ + int id = 0; + void (*phandler)(int); + +/* this is unavoidable - in case ESD "disappears" (ie the socket conn dies) */ +/* we need to catch SIGPIPE to avoid the default handler giving us */ +/* a bad day - ignore the SIGPIPE, then make sure to catch all errors */ + phandler = signal( SIGPIPE, dummy_signal ); /* for closed esd conns */ + /* get the sample id back from the server */ + if ( read_timeout( esd, (char *) &id, sizeof(id) ) != sizeof(id) ) { + signal( SIGPIPE, phandler ); + return -1; + } + + /* diagnostic info */ + /* + if ( getenv( "ESDBG" ) ) + printf( "esound confirming cached sample\n" ); + */ + + /* return the sample id to the client */ + signal( SIGPIPE, phandler ); + return id; +} + +/*******************************************************************/ +/* get the sample ID for an already-cached sample */ +int esd_sample_getid( int esd, const char *name) +{ + int proto = ESD_PROTO_SAMPLE_GETID; + int id; + char namebuf[ESD_NAME_MAX]; + void (*phandler)(int); + +/* this is unavoidable - in case ESD "disappears" (ie the socket conn dies) */ +/* we need to catch SIGPIPE to avoid the default handler giving us */ +/* a bad day - ignore the SIGPIPE, then make sure to catch all errors */ + phandler = signal( SIGPIPE, dummy_signal ); /* for closed esd conns */ + if ( write_timeout( esd, (const char *) &proto, sizeof(proto) ) != sizeof(proto) ) { + signal( SIGPIPE, phandler ); + return -1; + } + + /* prepare the name buffer */ + if ( name ) + strncpy( namebuf, name, ESD_NAME_MAX ); + else + namebuf[ 0 ] = '\0'; + + if ( write_timeout( esd, namebuf, ESD_NAME_MAX ) != ESD_NAME_MAX ) { + signal( SIGPIPE, phandler ); + return -1; + } + + /* flush the socket */ + /* fsync( esd ); */ + + /* get the sample id back from the server */ + if ( read_timeout( esd, (char *) &id, sizeof(id) ) != sizeof(id) ) { + signal( SIGPIPE, phandler ); + return -1; + } + + /* diagnostic info */ + /* + if ( getenv( "ESDBG" ) ) + printf( "esound getting cached sample id: \'%s\' = %d\n", + name, id ); + */ + + /* return the sample id to the client */ + signal( SIGPIPE, phandler ); + return id; +} + +/*******************************************************************/ +/* remove a sample from the cache in the server */ +int esd_sample_free( int esd, int sample ) +{ + int id; + int proto = ESD_PROTO_SAMPLE_FREE; + void (*phandler)(int); + + /* printf( "freeing sample (%d) - <%d>\n", esd, sample ); */ + +/* this is unavoidable - in case ESD "disappears" (ie the socket conn dies) */ +/* we need to catch SIGPIPE to avoid the default handler giving us */ +/* a bad day - ignore the SIGPIPE, then make sure to catch all errors */ + phandler = signal( SIGPIPE, dummy_signal ); /* for closed esd conns */ + /* send the necessary information */ + if ( write_timeout( esd, (const char *) &proto, sizeof(proto) ) != sizeof(proto) ) { + signal( SIGPIPE, phandler ); + return -1; + } + if ( write_timeout( esd, (const char *) &sample, sizeof(sample) ) != sizeof(sample) ) { + signal( SIGPIPE, phandler ); + return -1; + } + /* fsync( esd ); */ + + /* get the sample id back from the server */ + if ( read_timeout( esd, (char *) &id, sizeof(id) ) != sizeof(id) ) { + signal( SIGPIPE, phandler ); + return -1; + } + + /* diagnostic info */ + /* + if ( getenv( "ESDBG" ) ) + printf( "esound freeing sample\n" ); + */ + + /* return the id to the client (0 = error, 1 = ok) */ + signal( SIGPIPE, phandler ); + return id; +} + +/*******************************************************************/ +/* play a sample */ +int esd_sample_play( int esd, int sample ) +{ + int is_ok; + int proto = ESD_PROTO_SAMPLE_PLAY; + void (*phandler)(int); + + /* printf( "playing sample (%d) - <%d>\n", esd, sample ); */ + +/* this is unavoidable - in case ESD "disappears" (ie the socket conn dies) */ +/* we need to catch SIGPIPE to avoid the default handler giving us */ +/* a bad day - ignore the SIGPIPE, then make sure to catch all errors */ + phandler = signal( SIGPIPE, dummy_signal ); /* for closed esd conns */ + /* send the necessary information */ + if ( write_timeout( esd, (const char *) &proto, sizeof(proto) ) != sizeof(proto) ) { + signal( SIGPIPE, phandler ); + return -1; + } + if ( write_timeout( esd, (const char *) &sample, sizeof(sample) ) != sizeof(sample) ) { + signal( SIGPIPE, phandler ); + return -1; + } +#ifdef __EMX__ /* Some strange troubles without this one */ + fsync( esd ); +#endif + /* get the sample id back from the server */ + if ( read_timeout( esd, (char *) &is_ok, sizeof(is_ok) ) != sizeof(is_ok) ) { + signal( SIGPIPE, phandler ); + return -1; + } + + /* diagnostic info */ + /* + if ( getenv( "ESDBG" ) ) + printf( "esound playing sample\n" ); + */ + + /* return the id to the client (0 = error, 1 = ok) */ + signal( SIGPIPE, phandler ); + return is_ok; +} + + +/*******************************************************************/ +/* loop a previously cached sample in the server */ +int esd_sample_loop( int esd, int sample ) +{ + int is_ok; + int proto = ESD_PROTO_SAMPLE_LOOP; + void (*phandler)(int); + + /* printf( "looping sample (%d) - <%d>\n", esd, sample ); */ + +/* this is unavoidable - in case ESD "disappears" (ie the socket conn dies) */ +/* we need to catch SIGPIPE to avoid the default handler giving us */ +/* a bad day - ignore the SIGPIPE, then make sure to catch all errors */ + phandler = signal( SIGPIPE, dummy_signal ); /* for closed esd conns */ + /* send the necessary information */ + if ( write_timeout( esd, (const char *) &proto, sizeof(proto) ) != sizeof(proto) ) { + signal( SIGPIPE, phandler ); + return -1; + } + if ( write_timeout( esd, (const char *) &sample, sizeof(sample) ) != sizeof(sample) ) { + signal( SIGPIPE, phandler ); + return -1; + } + /* fsync( esd ); */ + + /* get the sample id back from the server */ + if ( read_timeout( esd, (char *) &is_ok, sizeof(is_ok) ) != sizeof(is_ok) ) { + signal( SIGPIPE, phandler ); + return -1; + } + + /* diagnostic info */ + /* + if ( getenv( "ESDBG" ) ) + printf( "esound looping sample\n" ); + */ + + /* return the id to the client (0 = error, 1 = ok) */ + signal( SIGPIPE, phandler ); + return is_ok; +} + +/*******************************************************************/ +/* stop a looping sample in the server */ +int esd_sample_stop( int esd, int sample ) +{ + int is_ok; + int proto = ESD_PROTO_SAMPLE_STOP; + void (*phandler)(int); + + /* printf( "stopping sample (%d) - <%d>\n", esd, sample ); */ + +/* this is unavoidable - in case ESD "disappears" (ie the socket conn dies) */ +/* we need to catch SIGPIPE to avoid the default handler giving us */ +/* a bad day - ignore the SIGPIPE, then make sure to catch all errors */ + phandler = signal( SIGPIPE, dummy_signal ); /* for closed esd conns */ + /* send the necessary information */ + if ( write_timeout( esd, (const char *) &proto, sizeof(proto) ) != sizeof(proto) ) { + signal( SIGPIPE, phandler ); + return -1; + } + if ( write_timeout( esd, (const char *) &sample, sizeof(sample) ) != sizeof(sample) ) { + signal( SIGPIPE, phandler ); + return -1; + } + /* fsync( esd ); */ + + /* get the sample id back from the server */ + if ( read_timeout( esd, (char *) &is_ok, sizeof(is_ok) ) != sizeof(is_ok) ) { + signal( SIGPIPE, phandler ); + return -1; + } + + /* diagnostic info */ + /* + if ( getenv( "ESDBG" ) ) + printf( "esound stopping sample\n" ); + */ + + /* return the id to the client (0 = error, 1 = ok) */ + signal( SIGPIPE, phandler ); + return is_ok; +} + +/*******************************************************************/ +/* stop a looping sample in the server */ +int esd_sample_kill( int esd, int sample ) +{ + int is_ok; + int proto = ESD_PROTO_SAMPLE_KILL; + void (*phandler)(int); + + /* printf( "killing sample (%d) - <%d>\n", esd, sample ); */ + +/* this is unavoidable - in case ESD "disappears" (ie the socket conn dies) */ +/* we need to catch SIGPIPE to avoid the default handler giving us */ +/* a bad day - ignore the SIGPIPE, then make sure to catch all errors */ + phandler = signal( SIGPIPE, dummy_signal ); /* for closed esd conns */ + /* send the necessary information */ + if ( write_timeout( esd, (const char *) &proto, sizeof(proto) ) != sizeof(proto) ) { + signal( SIGPIPE, phandler ); + return -1; + } + if ( write_timeout( esd, (const char *) &sample, sizeof(sample) ) != sizeof(sample) ) { + signal( SIGPIPE, phandler ); + return -1; + } + /* fsync( esd ); */ + + /* get the sample id back from the server */ + if ( read_timeout( esd, (char *) &is_ok, sizeof(is_ok) ) != sizeof(is_ok) ) { + signal( SIGPIPE, phandler ); + return -1; + } + + /* diagnostic info */ + /* + if ( getenv( "ESDBG" ) ) + printf( "esound stopping sample\n" ); + */ + + /* return the id to the client (0 = error, 1 = ok) */ + signal( SIGPIPE, phandler ); + return is_ok; +} + +/*******************************************************************/ +/* closes fd, previously obtained by esd_open */ +int esd_close( int esd ) +{ + /* diagnostic info */ + /* + if ( getenv( "ESDBG" ) ) + printf( "esound closing\n" ); + */ + + return close( esd ); +} + -- cgit v1.2.3