From 33348ba6f40ee965edfc6cdfb70444d0d53e7e9f Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 23 Aug 1999 19:39:14 +0000 Subject: [PATCH] Added aseqnet... --- test/Makefile.am | 3 +- test/README.aseqnet | 52 +++++ test/aseqnet.c | 505 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 559 insertions(+), 1 deletion(-) create mode 100644 test/README.aseqnet create mode 100644 test/aseqnet.c diff --git a/test/Makefile.am b/test/Makefile.am index 08df9b83..b22c1348 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,5 +1,5 @@ check_PROGRAMS=control mixer switches pause pcm latency seq \ - playmidi1 timer loopback aconnect + playmidi1 timer loopback aconnect aseqnet control_LDADD=../src/libasound.la mixer_LDADD=../src/libasound.la @@ -12,6 +12,7 @@ playmidi1_LDADD=../src/libasound.la timer_LDADD=../src/libasound.la loopback_LDADD=../src/libasound.la aconnect_LDADD=../src/libasound.la +aseqnet_LDADD=../src/libasound.la INCLUDES=-I$(top_srcdir)/include CFLAGS=-static -Wall -pipe -g diff --git a/test/README.aseqnet b/test/README.aseqnet new file mode 100644 index 00000000..7e005715 --- /dev/null +++ b/test/README.aseqnet @@ -0,0 +1,52 @@ +================================================================ + ALSA sequencer connectors + ver.0.1 + Copyright (C) 1999 Takashi Iwai +================================================================ + +* ASEQNET + +aseqnet is a sequencer client which sends/receives events over +network. Suppose two hosts (hostA and hostB) connected by network. +You need to run ALSA system on both hosts. Then, start aseqnet as a +server on hostA: + + hostA% aseqnet + sequencer opened: 128:0 + +A user client 128 with port 0 was opened. (The client number may +vary.) At next, start client on hostB. The argument is the hostname +where server is running. + + hostB% aseqnet hostA + sequencer opened: 132:0 + +Now events sent to hostA:128:0 is transferred to hostB:132:0, and vice +versa. + +You can connect these ports arbitrary to other sequencer ports. +For example, connect hostB:132:0 to a MIDI output device 65:0. The +aconnect utility can be used for this: + + hostB% aconnect 132:0 65:0 + +Events to hostA:128:0 will be delivered indirectly to hostB:65:0. +You'll hear MIDI sounds as following: + + hostA% pmidi -p 128:0 foo.mid + +The multiple clients may exist simultaneously. If hostC is connected +as a client to hostA, events from from hostA are sent to all connected +network clients, hostB and hostC. However, only one connection is +allowed from a client to a server. + +To disconnect network, stop all clients before server by ctrl-C or +sending signal to them. The server will automatically quit. + +The available options are: + + -p port : specify the TCP port number or TCP service name. + Default value is 9009 (I don't know it's allowed..) + -s addr : explicit read-subscription to the given address + (client:addr). + -d addr : explicit write-subscription to the given address. diff --git a/test/aseqnet.c b/test/aseqnet.c new file mode 100644 index 00000000..101981a5 --- /dev/null +++ b/test/aseqnet.c @@ -0,0 +1,505 @@ +/* + * network server/client for ALSA sequencer + * ver.0.1 + * + * Copyright (C) 1999 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * prototypes + */ +static void usage(void); +static void init_buf(void); +static void init_seq(char *source, char *dest); +static int get_port(char *service); +static void init_server(int port); +static void init_client(char *server, int port); +static void do_loop(void); +static int copy_local_to_remote(void); +static int copy_remote_to_local(int fd); + +/* + * default TCP port number + */ +#define DEFAULT_PORT 9009 + +/* + * local input buffer + */ +static char *readbuf; +static int max_rdlen; +static char *writebuf; +static int cur_wrlen, max_wrlen; + +#define MAX_BUF_EVENTS 200 +#define MAX_CONNECTION 10 + +static snd_seq_t *handle; +static int seqfd, sockfd, netfd[MAX_CONNECTION] = {[1 ... MAX_CONNECTION] = -1}; +static int max_connection; +static int cur_connected; +static int seq_port; + +static int server_mode; + + +/* + * main routine + */ + +int main(int argc, char **argv) +{ + int c, i; + int port = DEFAULT_PORT; + char *source = NULL, *dest = NULL; + + while ((c = getopt(argc, argv, "p:s:d:")) != -1) { + switch (c) { + case 'p': + if (isdigit(*optarg)) + port = atoi(optarg); + else + port = get_port(optarg); + break; + case 's': + source = optarg; + break; + case 'd': + dest = optarg; + break; + default: + usage(); + exit(1); + } + } + + init_buf(); + init_seq(source, dest); + + if (optind >= argc) { + server_mode = 1; + max_connection = MAX_CONNECTION; + init_server(port); + } else { + server_mode = 0; + max_connection = 1; + init_client(argv[optind], port); + } + + do_loop(); + + for (i = 0; i < max_connection; i++) { + if (netfd[i] >= 0) + close(netfd[i]); + } + if (sockfd >= 0) + close(sockfd); + + return 0; +} + + +/* + * print usage + */ +static void usage(void) +{ + fprintf(stderr, "aseqnet - network client/server on ALSA sequencer\n"); + fprintf(stderr, " Copyright (C) 1999 Takashi Iwai\n"); + fprintf(stderr, "usage:\n"); + fprintf(stderr, " server mode: aseqnet [-options]\n"); + fprintf(stderr, " client mode: aseqnet [-options] server_host\n"); + fprintf(stderr, "options:\n"); + fprintf(stderr, " -p port : sepcify TCP port (digit or service name)\n"); + fprintf(stderr, " -s addr : read from given addr (client:port)\n"); + fprintf(stderr, " -d addr : write to given addr (client:port)\n"); +} + + +/* + * allocate and initialize buffers + */ +static void init_buf(void) +{ + max_wrlen = MAX_BUF_EVENTS * sizeof(snd_seq_event_t); + max_rdlen = MAX_BUF_EVENTS * sizeof(snd_seq_event_t); + writebuf = malloc(max_wrlen); + readbuf = malloc(max_rdlen); + if (writebuf == NULL || readbuf == NULL) { + fprintf(stderr, "can't malloc\n"); + exit(1); + } + memset(writebuf, 0, max_wrlen); + memset(readbuf, 0, max_rdlen); + cur_wrlen = 0; +} + +/* + * parse client:port argument + */ +static void parse_addr(snd_seq_addr_t *addr, char *arg) +{ + char *p; + + addr->client = atoi(arg); + if ((p = strchr(arg, ':')) != NULL) + addr->port = atoi(p + 1); + else + addr->port = 0; +} + +/* + * initialize sequencer + */ +static void init_seq(char *source, char *dest) +{ + snd_seq_client_info_t cinfo; + snd_seq_port_info_t pinfo; + snd_seq_port_subscribe_t subs; + + if (snd_seq_open(&handle, SND_SEQ_OPEN) < 0) { + perror("snd_seq_open"); + exit(1); + } + seqfd = snd_seq_file_descriptor(handle); + snd_seq_block_mode(handle, 0); + + /* set client info */ + memset(&cinfo, 0, sizeof(cinfo)); + if (server_mode) + strcpy(cinfo.name, "Net Server"); + else + strcpy(cinfo.name, "Net Client"); + if (snd_seq_set_client_info(handle, &cinfo) < 0) { + perror("set client info"); + exit(1); + } + + /* create a port */ + memset(&pinfo, 0, sizeof(pinfo)); + pinfo.capability = SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_WRITE| + SND_SEQ_PORT_CAP_SUBS_READ|SND_SEQ_PORT_CAP_SUBS_WRITE; + strcpy(pinfo.name, "Network"); + pinfo.port = 0; + if (snd_seq_create_port(handle, &pinfo) < 0) { + perror("create seq port"); + exit(1); + } + seq_port = pinfo.port; + fprintf(stderr, "sequencer opened: %d:%d\n", pinfo.client, pinfo.port); + + /* explicit subscriptions */ + memset(&subs, 0, sizeof(subs)); + if (source) { + /* read subscription */ + parse_addr(&subs.sender, source); + subs.dest.client = pinfo.client; + subs.dest.port = pinfo.port; + subs.sender.queue = subs.dest.queue = 0; + if (snd_seq_subscribe_port(handle, &subs)) { + perror("read subscription"); + exit(1); + } + } + if (dest) { + /* write subscription */ + parse_addr(&subs.dest, dest); + subs.sender.client = pinfo.client; + subs.sender.port = pinfo.port; + subs.sender.queue = subs.dest.queue = 0; + if (snd_seq_subscribe_port(handle, &subs)) { + perror("write subscription"); + exit(1); + } + } +} + + +/* + * convert from string to TCP port number + */ +static int get_port(char *service) +{ + struct servent *sp; + + if ((sp = getservbyname(service, "tcp")) == NULL){ + fprintf(stderr, "%s is not found in /etc/services\n", service); + return -1; + } + return sp->s_port; +} + +/* + * initialize network server + */ +static void init_server(int port) +{ + int i; + struct sockaddr_in addr; + + memset(&addr, 0, sizeof(addr)); + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons(port); + + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) { + fprintf(stderr, "can't create a socket\n"); + exit(1); + } + + if (bind(sockfd, &addr, sizeof(addr)) < 0) { + fprintf(stderr, "can't bind address\n"); + exit(1); + } + + if (listen(sockfd, 5) < 0) { + fprintf(stderr, "can't listen on socket\n"); + exit(1); + } + + cur_connected = 0; + for (i = 0; i < max_connection; i++) + netfd[i] = -1; +} + +/* + * start connection on server + */ +static void start_connection(void) +{ + struct sockaddr_in addr; + int i; + int addr_len; + + for (i = 0; i < max_connection; i++) { + if (netfd[i] < 0) + break; + } + if (i >= max_connection) { + fprintf(stderr, "too many connections!\n"); + exit(1); + } + memset(&addr, 0, sizeof(addr)); + addr_len = sizeof(addr); + netfd[i] = accept(sockfd, (struct sockaddr *)&addr, &addr_len); + if (netfd[i] < 0) { + fprintf(stderr, "can't accept\n"); + exit(1); + } + fprintf(stderr, "accepted[%d]\n", netfd[i]); + cur_connected++; +} + +/* + * initialize network client + */ +static void init_client(char *server, int port) +{ + struct sockaddr_in addr; + struct hostent *host; + int fd; + + if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){ + fprintf(stderr, "can't create socket\n"); + exit(1); + } + if ((host = gethostbyname(server)) == NULL){ + fprintf(stderr,"can't get address %s\n", server); + exit(1); + } + addr.sin_port = htons(port); + addr.sin_family = AF_INET; + memcpy(&addr.sin_addr, host->h_addr, host->h_length); + if (connect(fd, &addr, sizeof(addr)) < 0) { + fprintf(stderr,"can't connect\n"); + exit(1); + } + fprintf(stderr, "ok.. connected\n"); + netfd[0] = fd; + cur_connected = 1; +} + +/* + * set file descriptor + */ +static void set_fd(int fd, fd_set *p, int *width) +{ + FD_SET(fd, p); + if (fd >= *width) + *width = fd + 1; +} + +/* + * event loop + */ +static void do_loop(void) +{ + fd_set rfd; + int i, rc, width; + + for (;;) { + FD_ZERO(&rfd); + width = 0; + set_fd(seqfd, &rfd, &width); + if (server_mode) + set_fd(sockfd, &rfd, &width); + for (i = 0; i < max_connection; i++) { + if (netfd[i] >= 0) + set_fd(netfd[i], &rfd, &width); + } + rc = select(width, &rfd, NULL, NULL, NULL); + if (rc <= 0) + exit(1); + if (server_mode) { + if (FD_ISSET(sockfd, &rfd)) + start_connection(); + } + if (FD_ISSET(seqfd, &rfd)) { + if (copy_local_to_remote()) + break; + } + for (i = 0; i < max_connection; i++) { + if (netfd[i] < 0) + continue; + if (FD_ISSET(netfd[i], &rfd)) { + if (copy_remote_to_local(netfd[i])) { + netfd[i] = -1; + cur_connected--; + if (cur_connected <= 0) + return; + } + } + } + } +} + + +/* + * is variable length event? + */ +#define is_varlen(ev) (((ev)->flags & SND_SEQ_EVENT_LENGTH_MASK) == SND_SEQ_EVENT_LENGTH_VARIABLE) + +/* + * flush write buffer - send data to the socket + */ +static void flush_writebuf(void) +{ + if (cur_wrlen) { + int i; + for (i = 0; i < max_connection; i++) { + if (netfd[i] >= 0) + write(netfd[i], writebuf, cur_wrlen); + } + cur_wrlen = 0; + } +} + +/* + * get space from write buffer + */ +static char *get_writebuf(int len) +{ + char *buf; + if (cur_wrlen + len >= max_wrlen) + flush_writebuf(); + buf = writebuf + cur_wrlen; + cur_wrlen += len; + return buf; +} + +/* + * copy events from sequencer to port(s) + */ +static int copy_local_to_remote(void) +{ + int rc; + snd_seq_event_t *ev; + char *buf; + + while ((rc = snd_seq_event_input(handle, &ev)) >= 0 && ev) { + if (ev->type >= SND_SEQ_EVENT_CLIENT_START) { + snd_seq_free_event(ev); + continue; + } + if (is_varlen(ev)) { + int len; + len = sizeof(snd_seq_event_t) + ev->data.ext.len; + buf = get_writebuf(len); + memcpy(buf, ev, sizeof(snd_seq_event_t)); + memcpy(buf + sizeof(snd_seq_event_t), ev->data.ext.ptr, ev->data.ext.len); + } else { + buf = get_writebuf(sizeof(snd_seq_event_t)); + memcpy(buf, ev, sizeof(snd_seq_event_t)); + } + } + flush_writebuf(); + return 0; +} + +/* + * copy events from a port to sequencer + */ +static int copy_remote_to_local(int fd) +{ + int count; + char *buf; + snd_seq_event_t *ev; + + count = read(fd, readbuf, MAX_BUF_EVENTS * sizeof(snd_seq_event_t)); + buf = readbuf; + + if (count == 0) { + fprintf(stderr, "disconnected\n"); + return 1; + } + + while (count > 0) { + ev = snd_seq_create_event(); + if (ev == NULL) { + fprintf(stderr, "can't malloc\n"); + exit(1); + } + memcpy(ev, buf, sizeof(snd_seq_event_t)); + buf += sizeof(snd_seq_event_t); + count -= sizeof(snd_seq_event_t); + if (is_varlen(ev) && ev->data.ext.len > 0) { + ev->data.ext.ptr = malloc(ev->data.ext.len); + if (ev->data.ext.ptr == NULL) { + fprintf(stderr, "can't malloc\n"); + exit(1); + } + memcpy(ev->data.ext.ptr, buf, ev->data.ext.len); + buf += ev->data.ext.len; + count -= ev->data.ext.len; + } + ev->source.port = seq_port; + ev->dest.queue = SND_SEQ_ADDRESS_SUBSCRIBERS; + snd_seq_event_output(handle, ev); + snd_seq_free_event(ev); + } + + snd_seq_flush_output(handle); + return 0; +} + -- 2.47.1