aboutsummaryrefslogtreecommitdiff
path: root/seq/aseqnet/aseqnet.c
diff options
context:
space:
mode:
Diffstat (limited to 'seq/aseqnet/aseqnet.c')
-rw-r--r--seq/aseqnet/aseqnet.c609
1 files changed, 609 insertions, 0 deletions
diff --git a/seq/aseqnet/aseqnet.c b/seq/aseqnet/aseqnet.c
new file mode 100644
index 0000000..e071ad9
--- /dev/null
+++ b/seq/aseqnet/aseqnet.c
@@ -0,0 +1,609 @@
+/*
+ * network server/client for ALSA sequencer
+ * ver.0.1
+ *
+ * Copyright (C) 1999-2000 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 <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <locale.h>
+#include <alsa/asoundlib.h>
+#include <getopt.h>
+#include <signal.h>
+#include <assert.h>
+#include "aconfig.h"
+#include "gettext.h"
+
+/*
+ * prototypes
+ */
+static void usage(void);
+static void init_buf(void);
+static void init_pollfds(void);
+static void close_files(void);
+static void init_seq(char *source, char *dest);
+static int get_port(char *service);
+static void sigterm_exit(int sig);
+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 40002
+
+/*
+ * 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 struct pollfd *seqifds = NULL;
+static struct pollfd *seqofds = NULL;
+static struct pollfd *pollfds = NULL;
+static int seqifds_count = 0;
+static int seqofds_count = 0;
+static int pollfds_count = 0;
+static int sockfd, netfd[MAX_CONNECTION] = {[0 ... MAX_CONNECTION-1] = -1};
+static int max_connection;
+static int cur_connected;
+static int seq_port;
+
+static int server_mode;
+static int verbose = 0;
+static int info = 0;
+
+
+/*
+ * main routine
+ */
+
+static const struct option long_option[] = {
+ {"port", 1, NULL, 'p'},
+ {"source", 1, NULL, 's'},
+ {"dest", 1, NULL, 'd'},
+ {"help", 0, NULL, 'h'},
+ {"verbose", 0, NULL, 'v'},
+ {"info", 0, NULL, 'i'},
+ {NULL, 0, NULL, 0},
+};
+
+int main(int argc, char **argv)
+{
+ int c;
+ int port = DEFAULT_PORT;
+ char *source = NULL, *dest = NULL;
+
+#ifdef ENABLE_NLS
+ setlocale(LC_ALL, "");
+ textdomain(PACKAGE);
+#endif
+
+ while ((c = getopt_long(argc, argv, "p:s:d:vi", long_option, NULL)) != -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;
+ case 'v':
+ verbose++;
+ break;
+ case 'i':
+ info++;
+ break;
+ default:
+ usage();
+ exit(1);
+ }
+ }
+
+ signal(SIGINT, sigterm_exit);
+ signal(SIGTERM, sigterm_exit);
+
+ init_buf();
+ init_seq(source, dest);
+
+ if (optind >= argc) {
+ server_mode = 1;
+ max_connection = MAX_CONNECTION;
+ init_pollfds();
+ init_server(port);
+ } else {
+ server_mode = 0;
+ max_connection = 1;
+ init_pollfds();
+ init_client(argv[optind], port);
+ }
+
+ do_loop();
+
+ close_files();
+
+ return 0;
+}
+
+
+/*
+ * print usage
+ */
+static void usage(void)
+{
+ printf(_("aseqnet - network client/server on ALSA sequencer\n"));
+ printf(_(" Copyright (C) 1999 Takashi Iwai\n"));
+ printf(_("usage:\n"));
+ printf(_(" server mode: aseqnet [-options]\n"));
+ printf(_(" client mode: aseqnet [-options] server_host\n"));
+ printf(_("options:\n"));
+ printf(_(" -p,--port # : sepcify TCP port (digit or service name)\n"));
+ printf(_(" -s,--source addr : read from given addr (client:port)\n"));
+ printf(_(" -d,--dest addr : write to given addr (client:port)\n"));
+ printf(_(" -v, --verbose : print verbose messages\n"));
+ printf(_(" -i, --info : print certain received events\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;
+}
+
+/*
+ * allocate and initialize poll array
+ */
+static void init_pollfds(void)
+{
+ pollfds_count = seqifds_count + seqofds_count + 1 + max_connection;
+ pollfds = (struct pollfd *)calloc(pollfds_count, sizeof(struct pollfd));
+ assert(pollfds);
+}
+
+/*
+ * close all files
+ */
+static void close_files(void)
+{
+ int i;
+ if (verbose)
+ fprintf(stderr, _("closing files..\n"));
+ for (i = 0; i < max_connection; i++) {
+ if (netfd[i] >= 0)
+ close(netfd[i]);
+ }
+ if (sockfd >= 0)
+ close(sockfd);
+}
+
+
+/*
+ * initialize sequencer
+ */
+static void init_seq(char *source, char *dest)
+{
+ snd_seq_addr_t addr;
+ int err, counti, counto;
+
+ if (snd_seq_open(&handle, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) {
+ perror("snd_seq_open");
+ exit(1);
+ }
+ if (seqifds)
+ free(seqifds);
+ if (seqofds)
+ free(seqofds);
+ counti = seqifds_count = snd_seq_poll_descriptors_count(handle, POLLIN);
+ assert(counti > 0);
+ counto = seqofds_count = snd_seq_poll_descriptors_count(handle, POLLOUT);
+ assert(counto > 0);
+ seqifds = (struct pollfd *)calloc(counti, sizeof(struct pollfd));
+ assert(seqifds);
+ seqofds = (struct pollfd *)calloc(counto, sizeof(struct pollfd));
+ assert(seqofds);
+ err = snd_seq_poll_descriptors(handle, seqifds, counti, POLLIN);
+ assert(err == counti);
+ err = snd_seq_poll_descriptors(handle, seqofds, counto, POLLOUT);
+ assert(err == counto);
+
+ snd_seq_nonblock(handle, 1);
+
+ /* set client info */
+ if (server_mode)
+ snd_seq_set_client_name(handle, "Net Server");
+ else
+ snd_seq_set_client_name(handle, "Net Client");
+
+ /* create a port */
+ seq_port = snd_seq_create_simple_port(handle, "Network",
+ SND_SEQ_PORT_CAP_READ |
+ SND_SEQ_PORT_CAP_WRITE |
+ SND_SEQ_PORT_CAP_SUBS_READ |
+ SND_SEQ_PORT_CAP_SUBS_WRITE,
+ SND_SEQ_PORT_TYPE_MIDI_GENERIC);
+ if (seq_port < 0) {
+ perror("create seq port");
+ exit(1);
+ }
+ if (verbose)
+ fprintf(stderr, _("sequencer opened: %d:%d\n"),
+ snd_seq_client_id(handle), seq_port);
+
+ /* explicit subscriptions */
+ if (source) {
+ /* read subscription */
+ if (snd_seq_parse_address(handle, &addr, source) < 0) {
+ fprintf(stderr, _("invalid source address %s\n"), source);
+ exit(1);
+ }
+ if (snd_seq_connect_from(handle, seq_port, addr.client, addr.port)) {
+ perror("read subscription");
+ exit(1);
+ }
+ }
+ if (dest) {
+ /* write subscription */
+ if (snd_seq_parse_address(handle, &addr, dest) < 0) {
+ fprintf(stderr, _("invalid destination address %s\n"), dest);
+ exit(1);
+ }
+ if (snd_seq_connect_to(handle, seq_port, addr.client, addr.port)) {
+ 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, _("service '%s' is not found in /etc/services\n"), service);
+ return -1;
+ }
+ return sp->s_port;
+}
+
+/*
+ * signal handler
+ */
+static void sigterm_exit(int sig)
+{
+ close_files();
+ exit(1);
+}
+
+
+/*
+ * initialize network server
+ */
+static void init_server(int port)
+{
+ int i;
+ int curstate = 1;
+ 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) {
+ perror("create socket");
+ exit(1);
+ }
+ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &curstate, sizeof(curstate));
+ /* the return value is ignored.. */
+
+ if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("can't bind");
+ exit(1);
+ }
+
+ if (listen(sockfd, 5) < 0) {
+ perror("can't listen");
+ 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;
+ socklen_t 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) {
+ perror("accept");
+ exit(1);
+ }
+ if (verbose)
+ 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 curstate = 1;
+ int fd;
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){
+ perror("create socket");
+ exit(1);
+ }
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &curstate, sizeof(curstate)) < 0) {
+ perror("setsockopt");
+ 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, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("connect");
+ exit(1);
+ }
+ if (verbose)
+ fprintf(stderr, _("ok.. connected\n"));
+ netfd[0] = fd;
+ cur_connected = 1;
+}
+
+/*
+ * event loop
+ */
+static void do_loop(void)
+{
+ int i, rc, width;
+ int seqifd_ptr, sockfd_ptr = -1, netfd_ptr;
+
+ for (;;) {
+ memset(pollfds, 0, pollfds_count * sizeof(struct pollfd));
+ seqifd_ptr = 0;
+ memcpy(pollfds, seqifds, sizeof(*seqifds)*(width = seqifds_count));
+ if (server_mode) {
+ sockfd_ptr = width;
+ pollfds[width].fd = sockfd;
+ pollfds[width].events = POLLIN;
+ width++;
+ }
+ netfd_ptr = width;
+ for (i = 0; i < max_connection; i++) {
+ if (netfd[i] >= 0) {
+ pollfds[width].fd = netfd[i];
+ pollfds[width].events = POLLIN;
+ width++;
+ }
+ }
+ do {
+ rc = poll(pollfds, width, -1);
+ } while (rc <= 0 && errno == EINTR);
+ if (rc <= 0) {
+ perror("poll");
+ exit(1);
+ }
+ if (server_mode) {
+ if (pollfds[sockfd_ptr].revents & (POLLIN|POLLOUT))
+ start_connection();
+ }
+ for (i = 0; i < seqifds_count; i++)
+ if (pollfds[seqifd_ptr + i].revents & (POLLIN|POLLOUT)) {
+ if (copy_local_to_remote())
+ return;
+ break;
+ }
+ for (i = 0; i < max_connection; i++) {
+ if (netfd[i] < 0)
+ continue;
+ if (pollfds[netfd_ptr + i].revents & (POLLIN|POLLOUT)) {
+ if (copy_remote_to_local(netfd[i])) {
+ netfd[i] = -1;
+ cur_connected--;
+ if (cur_connected <= 0)
+ return;
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * 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;
+}
+
+static void print_event(snd_seq_event_t *ev)
+{
+ switch (ev->type) {
+ case SND_SEQ_EVENT_CONTROLLER:
+ printf(_("Channel %2d: Control event : %5d\n"),
+ ev->data.control.channel, ev->data.control.value);
+ break;
+ case SND_SEQ_EVENT_PITCHBEND:
+ printf(_("Channel %2d: Pitchbender : %5d\n"),
+ ev->data.control.channel, ev->data.control.value);
+ break;
+ case SND_SEQ_EVENT_NOTEON:
+ printf(_("Channel %2d: Note On event : %5d\n"),
+ ev->data.control.channel, ev->data.note.note);
+ break;
+ case SND_SEQ_EVENT_NOTEOFF:
+ printf(_("Channel %2d: Note Off event: %5d\n"),
+ ev->data.control.channel, ev->data.note.note);
+ break;
+ }
+}
+
+#define EVENT_PACKET_SIZE 32
+
+/*
+ * 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_ev_is_variable_type(ev)) {
+ snd_seq_free_event(ev);
+ continue;
+ }
+ if (snd_seq_ev_is_variable(ev)) {
+ int len;
+ len = EVENT_PACKET_SIZE + ev->data.ext.len;
+ buf = get_writebuf(len);
+ memcpy(buf, ev, sizeof(snd_seq_event_t));
+ memcpy(buf + EVENT_PACKET_SIZE, ev->data.ext.ptr, ev->data.ext.len);
+ } else {
+ buf = get_writebuf(EVENT_PACKET_SIZE);
+ memcpy(buf, ev, EVENT_PACKET_SIZE);
+ }
+ if (info)
+ print_event(ev);
+ snd_seq_free_event(ev);
+ }
+ 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) {
+ if (verbose)
+ fprintf(stderr, _("disconnected\n"));
+ return 1;
+ }
+
+ while (count > 0) {
+ ev = (snd_seq_event_t*)buf;
+ buf += EVENT_PACKET_SIZE;
+ count -= EVENT_PACKET_SIZE;
+ if (snd_seq_ev_is_variable(ev) && ev->data.ext.len > 0) {
+ ev->data.ext.ptr = buf;
+ buf += ev->data.ext.len;
+ count -= ev->data.ext.len;
+ }
+ snd_seq_ev_set_direct(ev);
+ snd_seq_ev_set_source(ev, seq_port);
+ snd_seq_ev_set_subs(ev);
+ if (info)
+ print_event(ev);
+ snd_seq_event_output(handle, ev);
+ }
+
+ snd_seq_drain_output(handle);
+ return 0;
+}
+