Moved aconnect and aseqnet sequencer utilities into the alsa-utils package.

This commit is contained in:
Jaroslav Kysela 2000-01-04 12:39:00 +00:00
parent 9417948722
commit fa88a97078
11 changed files with 1161 additions and 2 deletions

View file

@ -1,5 +1,5 @@
INCLUDES=-I$(top_srcdir)/include
SUBDIRS1=include alsactl alsamixer amixer aplay utils
SUBDIRS1=include alsactl alsamixer amixer aplay seq utils
if COND_XAMIXER2
SUBDIRS2=$(SUBDIRS1) xamixer2
else

View file

@ -51,4 +51,4 @@ if test "x$GTK_LIBS" = "x"; then
fi
AC_OUTPUT(Makefile alsactl/Makefile alsamixer/Makefile amixer/Makefile aplay/Makefile \
include/Makefile utils/Makefile utils/alsa-utils.spec xamixer2/Makefile
gamix/Makefile)
gamix/Makefile seq/Makefile seq/aconnect/Makefile seq/aseqnet/Makefile)

1
seq/Makefile.am Normal file
View file

@ -0,0 +1 @@
SUBDIRS=aconnect aseqnet

7
seq/aconnect/Makefile.am Normal file
View file

@ -0,0 +1,7 @@
INCLUDES = -I$(top_srcdir)/include
LDADD = -lasound
EXTRA_DIST = README.aconnect
bin_PROGRAMS = aconnect
aconnect_SOURCES = aconnect.c
man_MANS = aconnect.1

View file

@ -0,0 +1,50 @@
================================================================
aconnect - control subscriptions
ver.0.1.3
Copyright (C) 1999-2000 Takashi Iwai
================================================================
aconnect is a utility to control subscriptions of two ports as the
third "manager" client.
For example, the following connects two ports, from 64:0 to 65:0.
% aconnect 64:0 65:0
To disconnect the existing subscription, use -d option.
% aconnect -d 64:0 65:0
To see which port is available as input port, run the following
command:
% aconnect -i
client 0: 'System' [group=system] [type=kernel]
0 'Timer ' [group=system]
1 'Announce ' [group=system]
client 64: '0: MIDI Synth' [group=] [type=kernel]
0 'card 0: synth-midi: 0' [group=device]
Similary, to see the output ports, use -o flag.
% aconnect -o
client 64: '0: MIDI Synth' [group=] [type=kernel]
0 'card 0: synth-midi: 0' [group=device]
client 65: 'AWE Wave Table Synth : 0' [group=device] [type=kernel]
0 'Emu8000 port 0 ' [group=device]
1 'Emu8000 port 1 ' [group=device]
2 'Emu8000 port 2 ' [group=device]
3 'Emu8000 port 3 ' [group=device]
Some ports may have permission for its own group.
In such a case, change the group of aconnect to the appropriate one by
using -g option.
The option -l together with -i or -o shows subscribers for each port.
Ports are connected exclusively when the option -e is specified.
For modifying time-stamp with a queue, use -r or -t option followed by
a queue index which updates the time-stamp. Former uses real-time queue,
while the latter uses tick queue. The queue must be used (not necessarily
owned) by the receiver client.

127
seq/aconnect/aconnect.1 Normal file
View file

@ -0,0 +1,127 @@
.TH aconnect 1 "January 1, 2000"
.LO 1
.SH NAME
aconnect \- ALSA sequencer connection manager
.SH SYNOPSIS
.B aconnect
[\-d] [-options] sender receiver
.br
.B aconnect
\-i|-o [-options]
.SH DESCRIPTION
.B aconnect
is a utility to connect and disconnect two existing ports on ALSA sequencer
system.
The ports with the arbitrary subscription permission, such as created
by
.B aseqview(1),
can be connected to any (MIDI) device ports using
.B aconnect.
For example, to connect from port 64:0 to 65:0, run as follows:
.IP "" 4
% aconnect 64:0 65:0
.PP
The connection is one-way, and the whole data to the sender port (64:0)
is redirected to the receiver port (65:0). When another port (e.g. 65:1)
is attached to the same sender port, the data is sent to both receiver
ports.
For disconnection, use
.B \-d
option.
.IP "" 4
% aconnect -d 64:0 65:0
.PP
Another function of
.B aconnect
is to list the present ports
on the given condition.
The input ports, which may become
.I sender
ports, can be listed with
.B \-i
option.
.IP "" 4
% aconnect -i
.br
client 0: 'System' [group=system] [type=kernel]
.in +4
0 'Timer ' [group=system]
.br
1 'Announce ' [group=system]
.in -4
client 64: '0: MIDI Synth' [group=] [type=kernel]
.in +4
0 'card 0: synth-midi: 0' [group=device]
.in -4
.PP
Similary, to see the output ports, use
.B \-o
flag.
.SH OPTIONS
.SS CONNNECTION MANAGEMENT
.TP
.B \-d, --disconnect
Disconnect the given subscription.
.TP
.B \-e, --exclusive
Connect ports with exclusvie mode.
Both sender and receiver ports can be no longer connected by any other ports.
.TP
.B \-r, --real queue
Convert time-stamps of event packets to the current value of the given
.I real-time
queue.
This is option is, however, not so useful, since
the receiver port must use (not necessarily own) the specified queue.
.TP
.B \-t, --tick queue
Like
.B -r
option, but
time-stamps are converted to the current value of the given
.I tick
queue.
.TP
.B \-g, --group name
Specify the group name that
.B aconnect
uses.
Some ports may have special permissions, so that only the same group
may subscribe to them. In such a case,
.B aconnect
can fake the group name
with this option.
.SS LIST PORTS
.TP
.B \-i, --input
List existing input (readable) ports.
This option is exclusive to
.B \-o.
.TP
.B \-o, --output
List existing output (writable) ports.
This option is exclusive to
.B \-i.
.TP
.B \-l, --list
List the current connection status. The connected and connecting ports
from/to each port are listed together.
The suffix flag
.B [ex]
means the connection is exclusive.
The suffix flag
.B [real:#]
and
.B [tick:#]
mean the connection includes real-time and tick conversion on the listed
queue, respectively.
.SH "SEE ALSO"
aseqnet(1), aseqview(1)
.SH AUTHOR
Takashi Iwai <iwai@ww.uni-erlangen.de>.

305
seq/aconnect/aconnect.c Normal file
View file

@ -0,0 +1,305 @@
/*
* connect / disconnect two subscriber ports
* ver.0.1.3
*
* 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 <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <sys/ioctl.h>
#include <sys/asoundlib.h>
static void usage(void)
{
fprintf(stderr, "aconnect - ALSA sequencer connection manager\n");
fprintf(stderr, "Copyright (C) 1999-2000 Takashi Iwai\n");
fprintf(stderr, "Usage:\n");
fprintf(stderr, " * Connection/disconnection betwen two ports\n");
fprintf(stderr, " aconnect [-options] sender receiver\n");
fprintf(stderr, " sender, receiver = client:port pair\n");
fprintf(stderr, " -d,--disconnect disconnect\n");
fprintf(stderr, " -e,--exclusive exclusive connection\n");
fprintf(stderr, " -r,--real # convert real-time-stamp on queue\n");
fprintf(stderr, " -t,--tick # convert tick-time-stamp on queue\n");
fprintf(stderr, " -g,--group name set the group name\n");
fprintf(stderr, " * List connected ports (no subscription action)\n");
fprintf(stderr, " aconnect -i|-o [-options]\n");
fprintf(stderr, " -i,--input list input (readable) ports\n");
fprintf(stderr, " -o,--output list output (writable) ports\n");
fprintf(stderr, " -g,--group name specify the group name\n");
fprintf(stderr, " -l,--list list current connections of each port\n");
}
/*
* parse command line to client:port
*/
static int parse_address(snd_seq_addr_t *addr, char *arg)
{
char *p;
if (! isdigit(*arg))
return -1;
if ((p = strpbrk(arg, ":.")) == NULL)
return -1;
addr->client = atoi(arg);
addr->port = atoi(p + 1);
return 0;
}
/*
* check permission (capability) of specified port
*/
static int check_permission(snd_seq_port_info_t *pinfo, char *group, int perm)
{
if ((pinfo->capability & perm) == perm &&
! (pinfo->capability & SND_SEQ_PORT_CAP_NO_EXPORT))
return 1;
if (*group && strcmp(pinfo->group, group) == 0 &&
(pinfo->cap_group & perm) == perm &&
! (pinfo->cap_group & SND_SEQ_PORT_CAP_NO_EXPORT))
return 1;
return 0;
}
/*
* list subscribers of specified type
*/
static void list_each_subs(snd_seq_t *seq, snd_seq_query_subs_t *subs, int type, char *msg)
{
subs->type = type;
subs->index = 0;
while (snd_seq_query_port_subscribers(seq, subs) >= 0) {
if (subs->index == 0)
printf("\t%s: ", msg);
else
printf(", ");
printf("%d:%d", subs->addr.client, subs->addr.port);
if (subs->exclusive)
printf("[ex]");
if (subs->convert_time)
printf("[%s:%d]",
(subs->realtime ? "real" : "tick"),
subs->queue);
subs->index++;
}
if (subs->index)
printf("\n");
}
/*
* list subscribers
*/
static void list_subscribers(snd_seq_t *seq, int client, int port)
{
snd_seq_query_subs_t subs;
memset(&subs, 0, sizeof(subs));
subs.client = client;
subs.port = port;
list_each_subs(seq, &subs, SND_SEQ_QUERY_SUBS_READ, "Connecting To");
list_each_subs(seq, &subs, SND_SEQ_QUERY_SUBS_WRITE, "Connected From");
}
/*
* list all ports
*/
static void list_ports(snd_seq_t *seq, char *group, int perm, int list_subs)
{
snd_seq_client_info_t cinfo;
snd_seq_port_info_t pinfo;
int client_printed;
cinfo.client = -1;
cinfo.name[0] = 0;
cinfo.group[0] = 0;
while (snd_seq_query_next_client(seq, &cinfo) >= 0) {
/* reset query info */
pinfo.client = cinfo.client;
pinfo.port = -1;
pinfo.name[0] = 0;
strncpy(pinfo.group, group, sizeof(pinfo.group));
client_printed = 0;
while (snd_seq_query_next_port(seq, &pinfo) >= 0) {
if (check_permission(&pinfo, group, perm)) {
if (! client_printed) {
printf("client %d: '%s' [group=%s] [type=%s]\n",
cinfo.client, cinfo.name, cinfo.group,
(cinfo.type == USER_CLIENT ? "user" : "kernel"));
client_printed = 1;
}
printf(" %3d '%-16s' [group=%s]\n", pinfo.port, pinfo.name, pinfo.group);
if (list_subs)
list_subscribers(seq, pinfo.client, pinfo.port);
}
/* reset query names */
pinfo.name[0] = 0;
strncpy(pinfo.group, group, sizeof(pinfo.group));
}
/* reset query names */
cinfo.name[0] = 0;
cinfo.group[0] = 0;
}
}
enum {
SUBSCRIBE, UNSUBSCRIBE, LIST_INPUT, LIST_OUTPUT
};
static struct option long_option[] = {
{"disconnect", 0, NULL, 'd'},
{"input", 0, NULL, 'i'},
{"output", 0, NULL, 'o'},
{"group", 1, NULL, 'g'},
{"real", 1, NULL, 'r'},
{"tick", 1, NULL, 't'},
{"exclusive", 0, NULL, 'e'},
{"list", 0, NULL, 'l'},
{NULL, 0, NULL, 0},
};
int main(int argc, char **argv)
{
int c;
snd_seq_t *seq;
int queue = 0, convert_time = 0, convert_real = 0, exclusive = 0;
int command = SUBSCRIBE;
char *group = "";
int client;
int list_subs = 0;
snd_seq_client_info_t cinfo;
snd_seq_port_subscribe_t subs;
while ((c = getopt_long(argc, argv, "diog:r:t:el", long_option, NULL)) != -1) {
switch (c) {
case 'd':
command = UNSUBSCRIBE;
break;
case 'i':
command = LIST_INPUT;
break;
case 'o':
command = LIST_OUTPUT;
break;
case 'g':
group = optarg;
break;
case 'e':
exclusive = 1;
break;
case 'r':
queue = atoi(optarg);
convert_time = 1;
convert_real = 1;
break;
case 't':
queue = atoi(optarg);
convert_time = 1;
convert_real = 0;
break;
case 'l':
list_subs = 1;
break;
default:
usage();
exit(1);
}
}
if (snd_seq_open(&seq, SND_SEQ_OPEN) < 0) {
fprintf(stderr, "can't open sequencer\n");
return 1;
}
if (command == LIST_INPUT) {
list_ports(seq, group, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, list_subs);
snd_seq_close(seq);
return 0;
} else if (command == LIST_OUTPUT) {
list_ports(seq, group, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, list_subs);
snd_seq_close(seq);
return 0;
}
if (optind + 2 > argc) {
snd_seq_close(seq);
usage();
exit(1);
}
if ((client = snd_seq_client_id(seq)) < 0) {
snd_seq_close(seq);
fprintf(stderr, "can't get client id\n");
return 1;
}
/* set client info */
memset(&cinfo, 0, sizeof(cinfo));
cinfo.client = client;
cinfo.type = USER_CLIENT;
strcpy(cinfo.name, "ALSA Connector");
strncpy(cinfo.group, group, sizeof(cinfo.group) - 1);
if (snd_seq_set_client_info(seq, &cinfo) < 0) {
snd_seq_close(seq);
fprintf(stderr, "can't set client info\n");
return 0;
}
/* set subscription */
memset(&subs, 0, sizeof(subs));
if (parse_address(&subs.sender, argv[optind]) < 0) {
fprintf(stderr, "invalid sender address %s\n", argv[optind]);
return 1;
}
if (parse_address(&subs.dest, argv[optind + 1]) < 0) {
fprintf(stderr, "invalid destination address %s\n", argv[optind + 1]);
return 1;
}
subs.queue = queue;
subs.exclusive = exclusive;
subs.convert_time = convert_time;
subs.realtime = convert_real;
if (command == UNSUBSCRIBE) {
if (snd_seq_get_port_subscription(seq, &subs) < 0) {
snd_seq_close(seq);
fprintf(stderr, "No subscription is found\n");
return 1;
}
if (snd_seq_unsubscribe_port(seq, &subs) < 0) {
snd_seq_close(seq);
fprintf(stderr, "Disconnection failed (%s)\n", snd_strerror(errno));
return 1;
}
} else {
if (snd_seq_get_port_subscription(seq, &subs) == 0) {
snd_seq_close(seq);
fprintf(stderr, "Connection is already subscribed\n");
return 1;
}
if (snd_seq_subscribe_port(seq, &subs) < 0) {
snd_seq_close(seq);
fprintf(stderr, "Connection failed (%s)\n", snd_strerror(errno));
return 1;
}
}
snd_seq_close(seq);
return 0;
}

7
seq/aseqnet/Makefile.am Normal file
View file

@ -0,0 +1,7 @@
INCLUDES = -I$(top_srcdir)/include
LDADD = -lasound
EXTRA_DIST = README.aseqnet
bin_PROGRAMS = aseqnet
aseqnet_SOURCES = aseqnet.c
man_MANS = aseqnet.1

View file

@ -0,0 +1,52 @@
================================================================
ALSA sequencer connectors over network
ver.0.1
Copyright (C) 1999-2000 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.

78
seq/aseqnet/aseqnet.1 Normal file
View file

@ -0,0 +1,78 @@
.TH aseqnet 1 "January 1, 2000"
.LO 1
.SH NAME
aseqnet \- ALSA sequencer connectors over network
.SH SYNOPSIS
.B aseqnet
[remotehost]
.SH DESCRIPTION
.B aseqnet
is an ALSA sequencer client which sends and receives event packets
over network.
Suppose two hosts connected by network,
.I hostA
as a server
and
.I hostB
as a client.
The ALSA sequencer system must be running on both hosts.
For creating the server port, run the following on hostA:
.IP "" 4
hostA% aseqnet
.br
sequencer opened: 128:0
.PP
Then a user client 128 with port 0 was opened on hostA.
(The client number may vary.)
For creating the (network-)client port, run
.B aseqnet
with the hostname of the server:
.IP "" 4
hostB% aseqnet hostA
.br
sequencer opened: 132:0
.PP
Now all events sent to hostA:128:0 are transferred to hostB:132:0, and vice
versa.
.PP
The ports created by
.B aseqnet
can be connected arbitrary to other sequencer ports via
.B aconnect(1).
For example, to connect hostB:132:0 to a MIDI output device 65:0:
.IP "" 4
hostB% aconnect 132:0 65:0
.PP
Then events to hostA:128:0 will be delivered to hostB:65:0.
The following command plays MIDI on
.I hostB.
.IP "" 4
hostA% pmidi -p 128:0 foo.mid
.PP
The multiple clients may exist simultaneously. If
.I hostC
is connected as a client to hostA, events from from hostA are sent
to all connected network clients, i.e. hostB and hostC.
However, only one connection is allowed from a client to a server.
.PP
To disconnect network, stop all clients before server by ctrl-C or
sending signal to them. The server will automatically quit.
.SH OPTIONS
.TP
.B \-p port
Specify the TCP port number or TCP service name.
.TP
.B \-s addr
Subscribe to the given address for read automatically.
.TP
.B \-d addr
Subscribe to the given address for write automatically.
.SH "SEE ALSO"
aconnect(1), pmidi(1)
.SH AUTHOR
Takashi Iwai <iwai@ww.uni-erlangen.de>.

532
seq/aseqnet/aseqnet.c Normal file
View file

@ -0,0 +1,532 @@
/*
* 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 <netdb.h>
#include <sys/asoundlib.h>
#include <getopt.h>
#include <signal.h>
/*
* prototypes
*/
static void usage(void);
static void init_buf(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 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] = {[0 ... MAX_CONNECTION-1] = -1};
static int max_connection;
static int cur_connected;
static int seq_port;
static int server_mode;
/*
* main routine
*/
static struct option long_option[] = {
{"port", 1, NULL, 'p'},
{"source", 1, NULL, 's'},
{"dest", 1, NULL, 'd'},
{"help", 0, NULL, 'h'},
{NULL, 0, NULL, 0},
};
int main(int argc, char **argv)
{
int c;
int port = DEFAULT_PORT;
char *source = NULL, *dest = NULL;
while ((c = getopt_long(argc, argv, "p:s:d:", 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;
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_server(port);
} else {
server_mode = 0;
max_connection = 1;
init_client(argv[optind], port);
}
do_loop();
close_files();
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,--source addr : read from given addr (client:port)\n");
fprintf(stderr, " -d,--dest 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 int parse_addr(snd_seq_addr_t *addr, char *arg)
{
char *p;
if (! isdigit(*arg))
return -1;
if ((p = strpbrk(arg, ":.")) == NULL)
return -1;
addr->client = atoi(arg);
addr->port = atoi(p + 1);
return 0;
}
/*
* close all files
*/
static void close_files(void)
{
int i;
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;
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 */
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);
}
fprintf(stderr, "sequencer opened: %d:%d\n",
snd_seq_client_id(handle), seq_port);
/* explicit subscriptions */
if (source) {
/* read subscription */
if (parse_addr(&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 (parse_addr(&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, "%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;
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;
}
}
}
}
}
/*
* 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 (snd_seq_ev_is_variable(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 (snd_seq_ev_is_variable(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;
}
snd_seq_ev_set_direct(ev);
snd_seq_ev_set_source(ev, seq_port);
snd_seq_ev_set_subs(ev);
snd_seq_event_output(handle, ev);
snd_seq_free_event(ev);
}
snd_seq_flush_output(handle);
return 0;
}