Hi, I've had problems with aseqnet, especially in client mode, using it to send local seq events to a remote aseqnet server.
The first problem was that it called poll() without properly initializing the struct pollid.events to POLLIN, so the poll never returned. Fix for the first problem: --- alsa-utils/seq/aseqnet/aseqnet.c 2002/03/21 21:39:27 1.1 +++ alsa-utils/seq/aseqnet/aseqnet.c 2002/03/21 22:02:13 @@ -420,7 +426,7 @@ for (;;) { memset(pollfds, 0, pollfds_count * sizeof(struct pollfd)); seqifd_ptr = 0; - memcpy(pollfds, seqifds, width = seqifds_count); + memcpy(pollfds, seqifds, sizeof(*seqifds)*(width = seqifds_count)); if (server_mode) { sockfd_ptr = width; pollfds[width].fd = sockfd; At this point to seq input pollfds are copied to the common pollfds struct, the init with memset(..., 0, count * sizeof) is ok, but the cp of the pollfds was only done with the count, without the sizeof, so only the first byte was copied ;-) The second problem was that copy_local_to_remote() was reading the local events in a read loop but the seq was set to blocking mode so the loop has never finished and data was not sent to the remote side. Obviosly, the assumption copy_local_to_remote() assumes that we are in nonblocking mode and that it can loop loop until all data has been read. As this is the only place where local events are read, it' safe to enable nonblocking mode, it works with 1, maybe the 0 was just a thinko: @@ -234,7 +240,7 @@ err = snd_seq_poll_descriptors(handle, seqofds, counto, POLLOUT); assert(err == counto); - snd_seq_nonblock(handle, 0); + snd_seq_nonblock(handle, 1); /* set client info */ if (server_mode) A third, smaller problem was that when the poll was interrupted due to signals (e.g. SIGSTOP/CONT), do_loop() immediately exited witht message. At this place it's ok to check for EINTR and call poll again until a real error occurs and use perror() to show what has happend: @@ -435,9 +441,13 @@ width++; } } - rc = poll(pollfds, width, -1); - if (rc <= 0) + do + rc = poll(pollfds, width, -1); + while (rc <= 0 && errno == EINTR); + if (rc <= 0) { + perror("poll"); exit(1); + } if (server_mode) { if (pollfds[sockfd_ptr].revents & (POLLIN|POLLOUT)) start_connection(); The last problem is that aseqnet uses struct snd_seq_event directly for transferring data over the net but it includes a union with elements that are dependent of the word size. One (obviosly unused) is snd_seq_ev_ipcshm: /** external stored data - IPC shared memory */ typedef struct snd_seq_ev_ipcshm { size_t len; /**< length of data */ key_t ipc; /**< IPC key */ } snd_seq_ev_ipcshm_t; size_t is 8 bytes on 64bit so this causes sizeof(snd_seq_event_t) to be 32 on 64-bit while it's 28 (4 less) on 32bit. I think we could remove snd_seq_ev_ipcshm but snd_seq_ev_ext, which is really used is the real problem: /** external stored data */ typedef struct snd_seq_ev_ext { size_t len; /**< length of data */ void *ptr; /**< pointer to data (note: can be 64-bit) */ } snd_seq_ev_ext_t; While it shouln't be a problem to declare len as int (would limit the external event data to 2GB also on 64bit which shoun't be a problem), the data pointer needs to be 64-bit in the real memory struct. For the network transfer it isn't used, the data is just appended afte the 28 bytes of sturct snd_seq_event. A quick hack to make aseqnet also working between 64 and 32bit machines would be to set the network size of sturct snd_seq_event to 28 octets fixed: @@ -543,8 +577,14 @@ while (count > 0) { ev = (snd_seq_event_t*)buf; - buf += sizeof(snd_seq_event_t); - count -= sizeof(snd_seq_event_t); + if (sizeof(size_t) == 4) { + buf += sizeof(snd_seq_event_t); + count -= sizeof(snd_seq_event_t); + } else { + buf += 28; // Hack alarm: size differs because of + count -= 28; // size_t and void* used in seq_ev_ext + } + // this works only with 64-bit bigendian if ext.len is int: if (snd_seq_ev_is_variable(ev) && ev->data.ext.len > 0) { ev->data.ext.ptr = buf; buf += ev->data.ext.len; If this is done and ext.len(snd_seq_ev_ext_t.len) is declared as int, it works also between 32-bit little endian and 64-bit bigendian. - thanks to the luck that the union which uses snd_seq_ev_ext_t is last in struct snd_seq_event and len is first in snd_seq_ev_ext_t, the len should be read correctly... Maybe it's better to have a #define SEQ_EVENT_SETSIZE or so to define a fixed number of octets which are used then event data is transferred over the net or stored on disk. aseqnet-fixes.diff with the patche above is attached. Also attached aseqnet.c.dif which also adds an -i/--info option to aseqnet which prints some of the reveived events. It's just a minor thing but a handy debugging helper. All the best, Bernd
--- aseqnet.c 2002/03/21 21:39:27 1.1 +++ aseqnet.c 2002/03/22 00:18:55 @@ -234,7 +240,7 @@ err = snd_seq_poll_descriptors(handle, seqofds, counto, POLLOUT); assert(err == counto); - snd_seq_nonblock(handle, 0); + snd_seq_nonblock(handle, 1); /* set client info */ if (server_mode) @@ -420,7 +426,7 @@ for (;;) { memset(pollfds, 0, pollfds_count * sizeof(struct pollfd)); seqifd_ptr = 0; - memcpy(pollfds, seqifds, width = seqifds_count); + memcpy(pollfds, seqifds, sizeof(*seqifds)*(width = seqifds_count)); if (server_mode) { sockfd_ptr = width; pollfds[width].fd = sockfd; @@ -435,9 +441,13 @@ width++; } } - rc = poll(pollfds, width, -1); - if (rc <= 0) + do + rc = poll(pollfds, width, -1); + while (rc <= 0 && errno == EINTR); + if (rc <= 0) { + perror("poll"); exit(1); + } if (server_mode) { if (pollfds[sockfd_ptr].revents & (POLLIN|POLLOUT)) start_connection(); @@ -543,8 +577,14 @@ while (count > 0) { ev = (snd_seq_event_t*)buf; - buf += sizeof(snd_seq_event_t); - count -= sizeof(snd_seq_event_t); + if (sizeof(size_t) == 4) { + buf += sizeof(snd_seq_event_t); + count -= sizeof(snd_seq_event_t); + } else { + buf += 28; // Hack alarm: size differs because of + count -= 28; // size_t and void* used in seq_ev_ext + } + // this works only with 64-bit bigendian if ext.len is int: if (snd_seq_ev_is_variable(ev) && ev->data.ext.len > 0) { ev->data.ext.ptr = buf; buf += ev->data.ext.len;
--- aseqnet.c 2002/03/21 21:39:27 1.1 +++ aseqnet.c 2002/03/22 00:18:55 @@ -72,6 +72,7 @@ static int server_mode; static int verbose = 0; +static int info = 0; /* @@ -84,6 +85,7 @@ {"dest", 1, NULL, 'd'}, {"help", 0, NULL, 'h'}, {"verbose", 0, NULL, 'v'}, + {"info", 0, NULL, 'i'}, {NULL, 0, NULL, 0}, }; @@ -93,7 +95,7 @@ int port = DEFAULT_PORT; char *source = NULL, *dest = NULL; - while ((c = getopt_long(argc, argv, "p:s:d:v", long_option, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "p:s:d:vi", long_option, NULL)) != -1) { switch (c) { case 'p': if (isdigit(*optarg)) @@ -110,6 +112,9 @@ case 'v': verbose++; break; + case 'i': + info++; + break; default: usage(); exit(1); @@ -157,6 +162,7 @@ fprintf(stderr, " -s,--source addr : read from given addr (client:port)\n"); fprintf(stderr, " -d,--dest addr : write to given addr (client:port)\n"); fprintf(stderr, " -v, --verbose : print verbose messages\n"); + fprintf(stderr, " -i, --info : print certain received events\n"); } @@ -234,7 +240,7 @@ err = snd_seq_poll_descriptors(handle, seqofds, counto, POLLOUT); assert(err == counto); - snd_seq_nonblock(handle, 0); + snd_seq_nonblock(handle, 1); /* set client info */ if (server_mode) @@ -420,7 +426,7 @@ for (;;) { memset(pollfds, 0, pollfds_count * sizeof(struct pollfd)); seqifd_ptr = 0; - memcpy(pollfds, seqifds, width = seqifds_count); + memcpy(pollfds, seqifds, sizeof(*seqifds)*(width = seqifds_count)); if (server_mode) { sockfd_ptr = width; pollfds[width].fd = sockfd; @@ -435,9 +441,13 @@ width++; } } - rc = poll(pollfds, width, -1); - if (rc <= 0) + do + rc = poll(pollfds, width, -1); + while (rc <= 0 && errno == EINTR); + if (rc <= 0) { + perror("poll"); exit(1); + } if (server_mode) { if (pollfds[sockfd_ptr].revents & (POLLIN|POLLOUT)) start_connection(); @@ -492,6 +502,28 @@ return buf; } +static void print_event(snd_seq_event_t *ev) +{ + switch (ev->type) { + case SND_SEQ_EVENT_CONTROLLER: + printf("Channel %2d: Control event : %5d\n", + ev->data.control.channel, ev->data.control.value); + break; + case SND_SEQ_EVENT_PITCHBEND: + printf("Channel %2d: Pitchbender : %5d\n", + ev->data.control.channel, ev->data.control.value); + break; + case SND_SEQ_EVENT_NOTEON: + printf("Channel %2d: Note On event : %5d\n", + ev->data.control.channel, ev->data.note.note); + break; + case SND_SEQ_EVENT_NOTEOFF: + printf("Channel %2d: Note Off event: %5d\n", + ev->data.control.channel, ev->data.note.note); + break; + } +} + /* * copy events from sequencer to port(s) */ @@ -517,6 +549,8 @@ buf = get_writebuf(sizeof(snd_seq_event_t)); memcpy(buf, ev, sizeof(snd_seq_event_t)); } + if (info) + print_event(ev); snd_seq_free_event(ev); } flush_writebuf(); @@ -543,8 +577,14 @@ while (count > 0) { ev = (snd_seq_event_t*)buf; - buf += sizeof(snd_seq_event_t); - count -= sizeof(snd_seq_event_t); + if (sizeof(size_t) == 4) { + buf += sizeof(snd_seq_event_t); + count -= sizeof(snd_seq_event_t); + } else { + buf += 28; // Hack alarm: size differs becaus of + count -= 28; // size_t and void* used in seq_ev_ext + } + // this works only with 64-bit bigendian if ext.len is int: if (snd_seq_ev_is_variable(ev) && ev->data.ext.len > 0) { ev->data.ext.ptr = buf; buf += ev->data.ext.len; @@ -553,6 +593,8 @@ snd_seq_ev_set_direct(ev); snd_seq_ev_set_source(ev, seq_port); snd_seq_ev_set_subs(ev); + if (info) + print_event(ev); snd_seq_event_output(handle, ev); }