diff options
-rw-r--r-- | include/seq.h | 14 | ||||
-rw-r--r-- | src/seq/seq.c | 112 | ||||
-rw-r--r-- | test/playmidi1.c | 135 |
3 files changed, 201 insertions, 60 deletions
diff --git a/include/seq.h b/include/seq.h index 934445f1..7e358060 100644 --- a/include/seq.h +++ b/include/seq.h @@ -49,6 +49,9 @@ int snd_seq_set_queue_client(snd_seq_t *handle, int q, snd_seq_queue_client_t *q int snd_seq_create_queue(snd_seq_t *seq, snd_seq_queue_info_t *info); int snd_seq_alloc_named_queue(snd_seq_t *seq, char *name); int snd_seq_alloc_queue(snd_seq_t *handle); +#ifdef SND_SEQ_SYNC_SUPPORT +int snd_seq_alloc_sync_queue(snd_seq_t *seq, char *name); +#endif int snd_seq_free_queue(snd_seq_t *handle, int q); int snd_seq_get_queue_info(snd_seq_t *seq, int q, snd_seq_queue_info_t *info); int snd_seq_set_queue_info(snd_seq_t *seq, int q, snd_seq_queue_info_t *info); @@ -57,6 +60,17 @@ int snd_seq_get_client_pool(snd_seq_t *handle, snd_seq_client_pool_t * info); int snd_seq_set_client_pool(snd_seq_t *handle, snd_seq_client_pool_t * info); int snd_seq_query_next_client(snd_seq_t *handle, snd_seq_client_info_t * info); int snd_seq_query_next_port(snd_seq_t *handle, snd_seq_port_info_t * info); +#ifdef SND_SEQ_SYNC_SUPPORT +int snd_seq_add_sync_master(snd_seq_t *seq, int queue, snd_seq_addr_t *dest, snd_seq_queue_sync_t *info); +int snd_seq_remove_sync_master(snd_seq_t *seq, int queue, snd_seq_addr_t *dest); +int snd_seq_add_sync_std_master(snd_seq_t *seq, int queue, snd_seq_addr_t *dest, int format, int time_format, unsigned char *opt_info); +#define snd_seq_add_sync_master_clock(seq,q,dest) snd_seq_add_sync_std_master(seq, q, dest, SND_SEQ_SYNC_FMT_MIDI_CLOCK, 0, 0) +#define snd_seq_add_sync_master_mtc(seq,q,dest,tfmt) snd_seq_add_sync_std_master(seq, q, dest, SND_SEQ_SYNC_FMT_MTC, tfmt, 0) + +int snd_seq_set_sync_slave(snd_seq_t *seq, int queue, snd_seq_addr_t *src, snd_seq_queue_sync_t *info); +int snd_seq_reset_sync_slave(snd_seq_t *seq, int queue, snd_seq_addr_t *src); + +#endif /* event routines */ snd_seq_event_t *snd_seq_create_event(void); diff --git a/src/seq/seq.c b/src/seq/seq.c index 6fe5373d..771f18b3 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -504,6 +504,23 @@ int snd_seq_alloc_queue(snd_seq_t *seq) return snd_seq_alloc_named_queue(seq, NULL); } +#ifdef SND_SEQ_SYNC_SUPPORT +int snd_seq_alloc_sync_queue(snd_seq_t *seq, char *name) +{ + snd_seq_queue_info_t info; + + if (!seq) + return -EINVAL; + + memset(&info, 0, sizeof(info)); + info.locked = 1; + if (name) + strncpy(info.name, name, sizeof(info.name) - 1); + info.flags = SND_SEQ_QUEUE_FLG_SYNC; + return snd_seq_create_queue(seq, &info); +} +#endif + int snd_seq_free_queue(snd_seq_t *seq, int q) { snd_seq_queue_info_t info; @@ -553,6 +570,101 @@ int snd_seq_get_named_queue(snd_seq_t *seq, char *name) /*----------------------------------------------------------------*/ +#ifdef SND_SEQ_SYNC_SUPPORT +/* + * sync stuff + */ + +int snd_seq_add_sync_master(snd_seq_t *seq, + int queue, + snd_seq_addr_t *dest, + snd_seq_queue_sync_t *info) +{ + snd_seq_port_subscribe_t subs; + + memset(&subs, 0, sizeof(subs)); + subs.convert_time = 1; + if (info->format & SND_SEQ_SYNC_TIME) + subs.realtime = 1; + subs.sync = 1; + subs.sender.client = SND_SEQ_CLIENT_SYSTEM; + subs.sender.port = snd_seq_queue_sync_port(queue); + subs.dest = *dest; + subs.queue = queue; + subs.opt.sync_info = *info; + return snd_seq_subscribe_port(seq, &subs); +} + +int snd_seq_add_sync_std_master(snd_seq_t *seq, + int queue, + snd_seq_addr_t *dest, + int format, int time_format, + unsigned char *optinfo) +{ + snd_seq_queue_sync_t sync_info; + + memset(&sync_info, 0, sizeof(sync_info)); + sync_info.format = format; + sync_info.time_format = time_format; + if (optinfo) + memcpy(sync_info.info, optinfo, sizeof(sync_info.info)); + + return snd_seq_add_sync_master(seq, queue, dest, &sync_info); +} + + +int snd_seq_remove_sync_master(snd_seq_t *seq, int queue, snd_seq_addr_t *dest) +{ + snd_seq_port_subscribe_t subs; + + memset(&subs, 0, sizeof(subs)); + subs.sync = 1; + subs.sender.client = SND_SEQ_CLIENT_SYSTEM; + subs.sender.port = snd_seq_queue_sync_port(queue); + subs.dest = *dest; + subs.queue = queue; + return snd_seq_unsubscribe_port(seq, &subs); +} + + +int snd_seq_set_sync_slave(snd_seq_t *seq, + int queue, + snd_seq_addr_t *src, + snd_seq_queue_sync_t *info) +{ + snd_seq_port_subscribe_t subs; + + memset(&subs, 0, sizeof(subs)); + subs.convert_time = 1; + if (info->format & SND_SEQ_SYNC_TIME) + subs.realtime = 1; + subs.sync = 1; + subs.sender = *src; + subs.dest.client = SND_SEQ_CLIENT_SYSTEM; + subs.dest.port = snd_seq_queue_sync_port(queue); + subs.queue = queue; + subs.opt.sync_info = *info; + return snd_seq_subscribe_port(seq, &subs); +} + +int snd_seq_reset_sync_slave(snd_seq_t *seq, int queue, snd_seq_addr_t *src) +{ + snd_seq_port_subscribe_t subs; + + memset(&subs, 0, sizeof(subs)); + subs.sync = 1; + subs.sender = *src; + subs.dest.client = SND_SEQ_CLIENT_SYSTEM; + subs.dest.port = snd_seq_queue_sync_port(queue); + subs.queue = queue; + return snd_seq_unsubscribe_port(seq, &subs); +} + + +#endif + +/*----------------------------------------------------------------*/ + /* * create an event cell */ diff --git a/test/playmidi1.c b/test/playmidi1.c index 91ab1674..85c76bd9 100644 --- a/test/playmidi1.c +++ b/test/playmidi1.c @@ -48,11 +48,11 @@ #include "../include/asoundlib.h" -/* define this if you want to send real-time time stamps instead of midi ticks to the ALSA sequencer */ -/* #define USE_REALTIME */ +/* send real-time time stamps instead of midi ticks to the ALSA sequencer */ +static int use_realtime = 0; -/* define this if you want to control event buffering by blocking mode */ -#define USE_BLOCKING_MODE +/* control event buffering by blocking mode */ +static int use_blocking_mode = 1; /* default destination queue, client and port numbers */ #define DEST_CLIENT_NUMBER 65 @@ -66,12 +66,13 @@ static FILE *F; static snd_seq_t *seq_handle = NULL; static int ppq = 96; +static int slave_ppq = 96; static double local_secs = 0; static int local_ticks = 0; static int local_tempo = 500000; -static int dest_queue = 0; +static int dest_queue = -1; static int dest_client = DEST_CLIENT_NUMBER; static int dest_port = DEST_PORT_NUMBER; static int my_port = 0; @@ -93,33 +94,25 @@ static inline double tick2time_dbl(int tick) return local_secs + ((double) (tick - local_ticks) * (double) local_tempo * 1.0E-6 / (double) ppq); } -#ifdef USE_REALTIME static void tick2time(snd_seq_real_time_t * tm, int tick) { double secs = tick2time_dbl(tick); tm->tv_sec = secs; tm->tv_nsec = (secs - tm->tv_sec) * 1.0E9; } -#endif -#ifdef USE_BLOCKING_MODE -/* write event - using blocking mode */ -static void write_ev(snd_seq_event_t *ev) -{ - int written; - - written = snd_seq_event_output(seq_handle, ev); - if (written < 0) { - printf("written = %i (%s)\n", written, snd_strerror(written)); - exit(1); - } -} -#else -/* write event - using select syscall */ static void write_ev(snd_seq_event_t *ev) { int rc; + if (use_blocking_mode) { + rc = snd_seq_event_output(seq_handle, ev); + if (rc < 0) { + printf("written = %i (%s)\n", rc, snd_strerror(rc)); + exit(1); + } + return; + } while ((rc = snd_seq_event_output(seq_handle, ev)) < 0) { int seqfd; fd_set fds; @@ -132,7 +125,6 @@ static void write_ev(snd_seq_event_t *ev) } } } -#endif /* read byte */ static int mygetc(void) @@ -173,12 +165,16 @@ static void do_header(int format, int ntracks, int division) exit(1); } if (tempo.ppq != ppq) { + slave_ppq = tempo.ppq; tempo.ppq = ppq; if (snd_seq_set_queue_tempo(seq_handle, dest_queue, &tempo) < 0) { perror("set_queue_tempo"); if (!slave) exit(1); - } + else + printf("different PPQ %d in SMF from queue PPQ %d\n", ppq, slave_ppq); + } else + slave_ppq = ppq; if (verbose >= VERB_INFO) printf("ALSA Timer updated, PPQ = %d\n", tempo.ppq); } @@ -197,13 +193,17 @@ static void do_header(int format, int ntracks, int division) /* fill time */ static void set_event_time(snd_seq_event_t *ev, unsigned int currtime) { -#ifdef USE_REALTIME - snd_seq_real_time_t rtime; - tick2time(&rtime, currtime); - snd_seq_ev_schedule_real(ev, dest_queue, 0, &rtime); -#else - snd_seq_ev_schedule_tick(ev, dest_queue, 0, currtime); -#endif + if (use_realtime) { + snd_seq_real_time_t rtime; + if (ppq != slave_ppq) + currtime = (currtime * slave_ppq) / ppq; + tick2time(&rtime, currtime); + snd_seq_ev_schedule_real(ev, dest_queue, 0, &rtime); + } else { + if (ppq != slave_ppq) + currtime = (currtime * slave_ppq) / ppq; + snd_seq_ev_schedule_tick(ev, dest_queue, 0, currtime); + } } /* fill normal event header */ @@ -357,24 +357,25 @@ static snd_seq_event_t *wait_for_event(void) int left; snd_seq_event_t *input_event; -#ifdef USE_BLOCKING_MODE - /* read event - blocked until any event is read */ - left = snd_seq_event_input(seq_handle, &input_event); -#else - /* read event - using select syscall */ - while ((left = snd_seq_event_input(seq_handle, &input_event)) >= 0 && - input_event == NULL) { - int seqfd; - fd_set fds; - seqfd = snd_seq_file_descriptor(seq_handle); - FD_ZERO(&fds); - FD_SET(seqfd, &fds); - if ((left = select(seqfd + 1, &fds, NULL, NULL, NULL)) < 0) { - printf("select error = %i (%s)\n", left, snd_strerror(left)); - exit(1); + if (use_blocking_mode) { + /* read event - blocked until any event is read */ + left = snd_seq_event_input(seq_handle, &input_event); + } else { + /* read event - using select syscall */ + while ((left = snd_seq_event_input(seq_handle, &input_event)) >= 0 && + input_event == NULL) { + int seqfd; + fd_set fds; + seqfd = snd_seq_file_descriptor(seq_handle); + FD_ZERO(&fds); + FD_SET(seqfd, &fds); + if ((left = select(seqfd + 1, &fds, NULL, NULL, NULL)) < 0) { + printf("select error = %i (%s)\n", left, snd_strerror(left)); + exit(1); + } } } -#endif + if (left < 0) { printf("alsa_sync error!:%s\n", snd_strerror(left)); return NULL; @@ -458,7 +459,10 @@ static void usage(void) fprintf(stderr, " -v: verbose mode\n"); fprintf(stderr, " -a client:port : set destination address (default=%d:%d)\n", DEST_CLIENT_NUMBER, DEST_PORT_NUMBER); + fprintf(stderr, " -q queue: use the specified queue\n"); fprintf(stderr, " -s queue: slave mode (allow external clock synchronisation)\n"); + fprintf(stderr, " -r : play on real-time mode\n"); + fprintf(stderr, " -b : play on non-blocking mode\n"); } /* parse destination address (-a option) */ @@ -476,7 +480,7 @@ int main(int argc, char *argv[]) int tmp; int c; - while ((c = getopt(argc, argv, "s:a:v")) != -1) { + while ((c = getopt(argc, argv, "s:a:q:vrb")) != -1) { switch (c) { case 'v': verbose++; @@ -484,6 +488,13 @@ int main(int argc, char *argv[]) case 'a': parse_address(optarg, &dest_client, &dest_port); break; + case 'q': + dest_queue = atoi(optarg); + if (dest_queue < 0) { + fprintf(stderr, "invalid queue number %d\n", dest_queue); + exit(1); + } + break; case 's': slave = 1; dest_queue = atoi(optarg); @@ -492,6 +503,12 @@ int main(int argc, char *argv[]) exit(1); } break; + case 'r': + use_realtime = 1; + break; + case 'b': + use_blocking_mode = 0; + break; default: usage(); exit(1); @@ -499,11 +516,10 @@ int main(int argc, char *argv[]) } if (verbose >= VERB_INFO) { -#ifdef USE_REALTIME - printf("ALSA MIDI Player, feeding events to real-time queue\n"); -#else - printf("ALSA MIDI Player, feeding events to song queue\n"); -#endif + if (use_realtime) + printf("ALSA MIDI Player, feeding events to real-time queue\n"); + else + printf("ALSA MIDI Player, feeding events to song queue\n"); } /* open sequencer device */ @@ -515,11 +531,7 @@ int main(int argc, char *argv[]) exit(1); } -#ifdef USE_BLOCKING_MODE - tmp = snd_seq_block_mode(seq_handle, 1); -#else - tmp = snd_seq_block_mode(seq_handle, 0); -#endif + tmp = snd_seq_block_mode(seq_handle, use_blocking_mode); if (tmp < 0) { perror("block_mode"); exit(1); @@ -544,8 +556,11 @@ int main(int argc, char *argv[]) } /* setup queue */ - if (slave) { - snd_seq_use_queue(seq_handle, dest_queue, 1); + if (dest_queue >= 0) { + if (snd_seq_use_queue(seq_handle, dest_queue, 1) < 0) { + perror("use queue"); + exit(1); + } } else { dest_queue = snd_seq_alloc_queue(seq_handle); if (dest_queue < 0) { @@ -565,7 +580,7 @@ int main(int argc, char *argv[]) if (slave) { tmp = snd_seq_connect_from(seq_handle, my_port, SND_SEQ_CLIENT_SYSTEM, - SND_SEQ_PORT_SYSTEM_TIMER); + snd_seq_queue_sync_port(dest_queue)); if (tmp < 0) { perror("subscribe"); exit(1); |