Added aseqnet...

This commit is contained in:
Jaroslav Kysela 1999-08-23 19:39:14 +00:00
parent 278b684054
commit 33348ba6f4
3 changed files with 559 additions and 1 deletions

View file

@ -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

52
test/README.aseqnet Normal file
View file

@ -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.

505
test/aseqnet.c Normal file
View file

@ -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 <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/asoundlib.h>
#include <unistd.h>
/*
* 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;
}