From 291f0bb7beed4dca5a240c04a263d2b786adb06e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 8 Jul 2005 10:57:34 +0000 Subject: initial checkin: a port of takashi iwai's "aconnect" to pure-data svn path=/trunk/externals/iem/aconnect/; revision=3303 --- aconnect.c | 406 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 406 insertions(+) create mode 100644 aconnect.c (limited to 'aconnect.c') diff --git a/aconnect.c b/aconnect.c new file mode 100644 index 0000000..bcb2b91 --- /dev/null +++ b/aconnect.c @@ -0,0 +1,406 @@ +/****************************************************** + * + * aconnect - ALSA sequencer connection manager + * Copyright (C) 1999-2000 Takashi Iwai + * + * + * + * ported from alsa-1.0.9a to pd by: + * copyleft (c) 2005 IOhannes m zmölnig + * + * forum::für::umläute + * + * institute of electronic music and acoustics (iem) + * + * + ****************************************************** + * + * license: GNU General Public License v.2 + * + ******************************************************/ + +#include "m_pd.h" + +/* aconnect :: connect/disconnect 2 ALSA sequencer subscriber ports */ + +static t_class *aconnect_class; + +typedef struct _aconnect +{ + t_object x_obj; + t_outlet*x_error; +} t_aconnect; + + +#ifdef HAVE_ALSA + +#include + +#define LIST_INPUT 1 +#define LIST_OUTPUT 2 +#define perm_ok(pinfo,bits) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits)) + +static snd_seq_t* ac_seq; + + + +static int check_permission(snd_seq_port_info_t *pinfo, int perm) +{ + if (perm) { + if (perm & LIST_INPUT) { + if (perm_ok(pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ)) + goto __ok; + } + if (perm & LIST_OUTPUT) { + if (perm_ok(pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE)) + goto __ok; + } + return 0; + } + __ok: + if (snd_seq_port_info_get_capability(pinfo) & SND_SEQ_PORT_CAP_NO_EXPORT) + return 0; + return 1; +} + +/* + * search all ports + */ +typedef void (*action_func_t)(t_aconnect *x, + snd_seq_t *seq, snd_seq_client_info_t *cinfo, snd_seq_port_info_t *pinfo); + +static void do_search_port(t_aconnect *x, snd_seq_t *seq, int perm, action_func_t do_action) +{ + snd_seq_client_info_t *cinfo; + snd_seq_port_info_t *pinfo; + int count; + + snd_seq_client_info_alloca(&cinfo); + snd_seq_port_info_alloca(&pinfo); + snd_seq_client_info_set_client(cinfo, -1); + while (snd_seq_query_next_client(seq, cinfo) >= 0) { + /* reset query info */ + snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo)); + snd_seq_port_info_set_port(pinfo, -1); + count = 0; + while (snd_seq_query_next_port(seq, pinfo) >= 0) { + if (check_permission(pinfo, perm)) { + do_action(x, seq, cinfo, pinfo); + count++; + } + } + } +} + +static void print_port(t_aconnect *x, t_symbol*s, snd_seq_t *seq, snd_seq_client_info_t *cinfo, + snd_seq_port_info_t *pinfo) +{ + t_atom ap[4]; + int client_id =snd_seq_client_info_get_client(cinfo); + int port_id =snd_seq_port_info_get_port(pinfo); + char *client_name=(char*)snd_seq_client_info_get_name(cinfo); + char *port_name =(char*)snd_seq_port_info_get_name(pinfo); + + SETFLOAT (ap+0, client_id); + SETSYMBOL(ap+1, gensym(client_name)); + SETFLOAT (ap+2, port_id); + SETSYMBOL(ap+3, gensym(port_name)); + + outlet_anything(x->x_obj.ob_outlet, s, 4, ap); +} +static void print_output(t_aconnect *x, snd_seq_t *seq, snd_seq_client_info_t *cinfo, + snd_seq_port_info_t *pinfo) +{ + print_port(x, gensym("output"), seq, cinfo, pinfo); +} + +static void print_input(t_aconnect *x, snd_seq_t *seq, snd_seq_client_info_t *cinfo, + snd_seq_port_info_t *pinfo) +{ + print_port(x, gensym("input"), seq, cinfo, pinfo); +} + +static void print_connections(t_aconnect *x, snd_seq_t *seq, snd_seq_client_info_t *cinfo, + snd_seq_port_info_t *pinfo) +{ + int count = 0; + int sender_id =snd_seq_client_info_get_client(cinfo); + int sender_port =snd_seq_port_info_get_port(pinfo); + int receiver_id =-1; + int receiver_port =-1; + + t_atom ap[4]; + + SETFLOAT (ap+0, sender_id); + SETFLOAT (ap+1, sender_port); + + snd_seq_query_subscribe_t *subs; + snd_seq_query_subscribe_alloca(&subs); + snd_seq_query_subscribe_set_root(subs, snd_seq_port_info_get_addr(pinfo)); + + snd_seq_query_subscribe_set_type(subs, SND_SEQ_QUERY_SUBS_READ); + snd_seq_query_subscribe_set_index(subs, 0); + while (snd_seq_query_port_subscribers(seq, subs) >= 0) { + const snd_seq_addr_t *addr = snd_seq_query_subscribe_get_addr(subs); + receiver_id =addr->client; + receiver_port =addr->port; + + SETFLOAT (ap+2, receiver_id); + SETFLOAT (ap+3, receiver_port); + + outlet_list(x->x_obj.ob_outlet, 0, 4, ap); + + snd_seq_query_subscribe_set_index(subs, snd_seq_query_subscribe_get_index(subs) + 1); + } +} + + +/* + * remove all (exported) connections + */ +static void remove_connection(t_aconnect *x, snd_seq_t *seq, snd_seq_client_info_t *cinfo, + snd_seq_port_info_t *pinfo) +{ + snd_seq_query_subscribe_t *query; + + snd_seq_query_subscribe_alloca(&query); + snd_seq_query_subscribe_set_root(query, snd_seq_port_info_get_addr(pinfo)); + + snd_seq_query_subscribe_set_type(query, SND_SEQ_QUERY_SUBS_READ); + snd_seq_query_subscribe_set_index(query, 0); + for (; snd_seq_query_port_subscribers(seq, query) >= 0; + snd_seq_query_subscribe_set_index(query, snd_seq_query_subscribe_get_index(query) + 1)) { + snd_seq_port_info_t *port; + snd_seq_port_subscribe_t *subs; + const snd_seq_addr_t *sender = snd_seq_query_subscribe_get_root(query); + const snd_seq_addr_t *dest = snd_seq_query_subscribe_get_addr(query); + snd_seq_port_info_alloca(&port); + if (snd_seq_get_any_port_info(seq, dest->client, dest->port, port) < 0) + continue; + if (!(snd_seq_port_info_get_capability(port) & SND_SEQ_PORT_CAP_SUBS_WRITE)) + continue; + if (snd_seq_port_info_get_capability(port) & SND_SEQ_PORT_CAP_NO_EXPORT) + continue; + snd_seq_port_subscribe_alloca(&subs); + snd_seq_port_subscribe_set_queue(subs, snd_seq_query_subscribe_get_queue(query)); + snd_seq_port_subscribe_set_sender(subs, sender); + snd_seq_port_subscribe_set_dest(subs, dest); + snd_seq_unsubscribe_port(seq, subs); + } + + snd_seq_query_subscribe_set_type(query, SND_SEQ_QUERY_SUBS_WRITE); + snd_seq_query_subscribe_set_index(query, 0); + for (; snd_seq_query_port_subscribers(seq, query) >= 0; + snd_seq_query_subscribe_set_index(query, snd_seq_query_subscribe_get_index(query) + 1)) { + snd_seq_port_info_t *port; + snd_seq_port_subscribe_t *subs; + const snd_seq_addr_t *dest = snd_seq_query_subscribe_get_root(query); + const snd_seq_addr_t *sender = snd_seq_query_subscribe_get_addr(query); + snd_seq_port_info_alloca(&port); + if (snd_seq_get_any_port_info(seq, sender->client, sender->port, port) < 0) + continue; + if (!(snd_seq_port_info_get_capability(port) & SND_SEQ_PORT_CAP_SUBS_READ)) + continue; + if (snd_seq_port_info_get_capability(port) & SND_SEQ_PORT_CAP_NO_EXPORT) + continue; + snd_seq_port_subscribe_alloca(&subs); + snd_seq_port_subscribe_set_queue(subs, snd_seq_query_subscribe_get_queue(query)); + snd_seq_port_subscribe_set_sender(subs, sender); + snd_seq_port_subscribe_set_dest(subs, dest); + snd_seq_unsubscribe_port(seq, subs); + } +} + +static void remove_all_connections(t_aconnect *x, snd_seq_t *seq) +{ + do_search_port(x, seq, 0, remove_connection); +} + + +/* + * list input and output clients in a format like + * "output <#ports> " + * "input <#ports> " + */ +static void aconnect_listdevices(t_aconnect *x, t_symbol *s) +{ + snd_seq_t *seq; + if (snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) { + error("aconnect: can't open sequencer"); + outlet_float(x->x_error, (float)(-2)); + return; + } + + if(&s_==s || gensym("input")==s) + do_search_port(x, seq, LIST_INPUT, print_input); + + if(&s_==s || gensym("output")==s) + do_search_port(x, seq, LIST_OUTPUT, print_output); + + snd_seq_close(seq); + outlet_float(x->x_error, 0.); +} + + +/* + * list connections in the form + * "list " + */ +static void aconnect_bang(t_aconnect *x) +{ + snd_seq_t *seq; + if (snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) { + error("aconnect: can't open sequencer"); + outlet_float(x->x_error, (float)(-2)); + return; + } + + do_search_port(x, seq, LIST_OUTPUT, print_connections); + + snd_seq_close(seq); + outlet_float(x->x_error, 0.); +} + +static int aconnect_subscribe(snd_seq_t*seq, int subscribe, + int sender_id, int sender_port, int dest_id, int dest_port){ + snd_seq_port_subscribe_t *subs; + snd_seq_addr_t sender, dest; + int queue = 0, convert_time = 0, convert_real = 0, exclusive = 0; + int err=0; + + sender.client=sender_id; + sender.port =sender_port; + dest.client =dest_id; + dest.port =dest_port; + + /* set the information into "subs" */ + snd_seq_port_subscribe_alloca(&subs); + snd_seq_port_subscribe_set_sender(subs, &sender); + snd_seq_port_subscribe_set_dest(subs, &dest); + + snd_seq_port_subscribe_set_queue(subs, queue); + snd_seq_port_subscribe_set_exclusive(subs, exclusive); + snd_seq_port_subscribe_set_time_update(subs, convert_time); + snd_seq_port_subscribe_set_time_real(subs, convert_real); + + /* do the subscription/unsubscription */ + if(subscribe){ + if (snd_seq_get_port_subscription(seq, subs) == 0) { + post("Connection is already subscribed"); + err=1; + } else if (snd_seq_subscribe_port(seq, subs) < 0) { + error("aconnect: Connection failed (%s)", snd_strerror(errno)); + err=-1; + } + } else { + if (snd_seq_get_port_subscription(seq, subs) < 0) { + post("aconnect: no subscription is found"); + err=1; + } else if (snd_seq_unsubscribe_port(seq, subs) < 0) { + error("aconnect: Disonnection failed (%s)", snd_strerror(errno)); + err=-1; + } + } + return err; +} + +/* a list like "connect " + * clients can be both numeric or symbolic + * port>=0; + * if port==-1; then all ports are connected (TODO) + * if numeric client == -1 then all clients are connected (TODO) + */ +static void aconnect_connect(t_aconnect *x, t_symbol *s, int argc, t_atom *argv) +{ + snd_seq_t *seq; + int sender_id, sender_port, dest_id, dest_port; + int err=0; + + if(argc!=4){ + pd_error(x, "aconnect: invalid connection string!"); + return; + } + + if (snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) { + error("aconnect: can't open sequencer"); + outlet_float(x->x_error, (float)(-2)); + return; + } + + /* get sender and dest */ + sender_id =atom_getint(argv); + sender_port=atom_getint(argv+1); + dest_id =atom_getint(argv+2); + dest_port=atom_getint(argv+3); + + err=aconnect_subscribe(seq, 1, sender_id, sender_port, dest_id, dest_port); + + snd_seq_close(seq); + outlet_float(x->x_error, (float)err); +} +/* a list like "disconnect " + * see aconnect_connect() + */ +static void aconnect_disconnect(t_aconnect *x, t_symbol *s, int argc, t_atom *argv) +{ + snd_seq_t *seq; + int err; + int sender_id, sender_port, dest_id, dest_port; + + if(argc!=4){ + pd_error(x, "aconnect: invalid connection string!"); + return; + } + + if (snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) { + error("aconnect: can't open sequencer"); + outlet_float(x->x_error, (float)(-2)); + return; + } + + /* get sender and dest */ + sender_id =atom_getint(argv); + sender_port=atom_getint(argv+1); + dest_id =atom_getint(argv+2); + dest_port=atom_getint(argv+3); + + err=aconnect_subscribe(seq, 0, sender_id, sender_port, dest_id, dest_port); + outlet_float(x->x_error, (float)err); + + snd_seq_close(seq); +} +#endif /* ALSA */ + +static void *aconnect_new(void) +{ + t_aconnect *x = (t_aconnect *)pd_new(aconnect_class); + outlet_new(&x->x_obj, 0); + x->x_error=outlet_new(&x->x_obj, 0); + +#ifndef HAVE_ALSA + error("aconnect: compiled without ALSA-suppor !!"); + error("aconnect: no functionality enabled!"); +#endif /* !ALSA */ + + return (x); +} + + +void aconnect_setup(void) +{ + post("aconnect: ALSA sequencer connection manager"); + post(" Copyright (C) 1999-2000 Takashi Iwai"); + post(" ported to pure-data by IOhannes m zmölnig 2005"); + post(" institute of electronic music and acoustics (iem)"); + post(" published under the GNU General Public License version 2"); + + aconnect_class = class_new(gensym("aconnect"), (t_newmethod)aconnect_new, 0, + sizeof(t_aconnect), 0, 0); +#ifdef HAVE_ALSA + class_addmethod(aconnect_class, (t_method)aconnect_connect,gensym("connect"), A_GIMME, 0); + class_addmethod(aconnect_class, (t_method)aconnect_disconnect,gensym("disconnect"), A_GIMME, 0); + class_addmethod(aconnect_class, (t_method)aconnect_listdevices,gensym("devices"), A_DEFSYM, 0); + class_addbang(aconnect_class, (t_method)aconnect_bang); +#endif /* ALSA */ +} + -- cgit v1.2.1