mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-12-22 22:46:31 +01:00
Moved aconnect and aseqnet sequencer utilities into the alsa-utils package.
This commit is contained in:
parent
9417948722
commit
fa88a97078
11 changed files with 1161 additions and 2 deletions
|
@ -1,5 +1,5 @@
|
||||||
INCLUDES=-I$(top_srcdir)/include
|
INCLUDES=-I$(top_srcdir)/include
|
||||||
SUBDIRS1=include alsactl alsamixer amixer aplay utils
|
SUBDIRS1=include alsactl alsamixer amixer aplay seq utils
|
||||||
if COND_XAMIXER2
|
if COND_XAMIXER2
|
||||||
SUBDIRS2=$(SUBDIRS1) xamixer2
|
SUBDIRS2=$(SUBDIRS1) xamixer2
|
||||||
else
|
else
|
||||||
|
|
|
@ -51,4 +51,4 @@ if test "x$GTK_LIBS" = "x"; then
|
||||||
fi
|
fi
|
||||||
AC_OUTPUT(Makefile alsactl/Makefile alsamixer/Makefile amixer/Makefile aplay/Makefile \
|
AC_OUTPUT(Makefile alsactl/Makefile alsamixer/Makefile amixer/Makefile aplay/Makefile \
|
||||||
include/Makefile utils/Makefile utils/alsa-utils.spec xamixer2/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
1
seq/Makefile.am
Normal file
|
@ -0,0 +1 @@
|
||||||
|
SUBDIRS=aconnect aseqnet
|
7
seq/aconnect/Makefile.am
Normal file
7
seq/aconnect/Makefile.am
Normal 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
|
50
seq/aconnect/README.aconnect
Normal file
50
seq/aconnect/README.aconnect
Normal 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
127
seq/aconnect/aconnect.1
Normal 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
305
seq/aconnect/aconnect.c
Normal 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
7
seq/aseqnet/Makefile.am
Normal 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
|
52
seq/aseqnet/README.aseqnet
Normal file
52
seq/aseqnet/README.aseqnet
Normal 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
78
seq/aseqnet/aseqnet.1
Normal 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
532
seq/aseqnet/aseqnet.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue