1/* 2 * aseqdump.c - show the events received at an ALSA sequencer port 3 * 4 * Copyright (c) 2005 Clemens Ladisch <clemens@ladisch.de> 5 * 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22#include "aconfig.h" 23#include <stdio.h> 24#include <stdlib.h> 25#include <stdarg.h> 26#include <string.h> 27#include <signal.h> 28#include <getopt.h> 29#include <poll.h> 30#include <alsa/asoundlib.h> 31#include "version.h" 32#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION 33#include <alsa/ump_msg.h> 34#endif 35 36enum { 37 VIEW_RAW, VIEW_NORMALIZED, VIEW_PERCENT 38}; 39 40static snd_seq_t *seq; 41static int port_count; 42static snd_seq_addr_t *ports; 43static volatile sig_atomic_t stop = 0; 44#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION 45static int ump_version; 46#else 47#define ump_version 0 48#endif 49static int view_mode = VIEW_RAW; 50 51/* prints an error message to stderr, and dies */ 52static void fatal(const char *msg, ...) 53{ 54 va_list ap; 55 56 va_start(ap, msg); 57 vfprintf(stderr, msg, ap); 58 va_end(ap); 59 fputc('\n', stderr); 60 exit(EXIT_FAILURE); 61} 62 63/* memory allocation error handling */ 64static void check_mem(void *p) 65{ 66 if (!p) 67 fatal("Out of memory"); 68} 69 70/* error handling for ALSA functions */ 71static void check_snd(const char *operation, int err) 72{ 73 if (err < 0) 74 fatal("Cannot %s - %s", operation, snd_strerror(err)); 75} 76 77static void init_seq(void) 78{ 79 int err; 80 81 /* open sequencer */ 82 err = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0); 83 check_snd("open sequencer", err); 84 85 /* set our client's name */ 86 err = snd_seq_set_client_name(seq, "aseqdump"); 87 check_snd("set client name", err); 88} 89 90/* parses one or more port addresses from the string */ 91static void parse_ports(const char *arg) 92{ 93 char *buf, *s, *port_name; 94 int err; 95 96 /* make a copy of the string because we're going to modify it */ 97 buf = strdup(arg); 98 check_mem(buf); 99 100 for (port_name = s = buf; s; port_name = s + 1) { 101 /* Assume that ports are separated by commas. We don't use 102 * spaces because those are valid in client names. */ 103 s = strchr(port_name, ','); 104 if (s) 105 *s = '\0'; 106 107 ++port_count; 108 ports = realloc(ports, port_count * sizeof(snd_seq_addr_t)); 109 check_mem(ports); 110 111 err = snd_seq_parse_address(seq, &ports[port_count - 1], port_name); 112 if (err < 0) 113 fatal("Invalid port %s - %s", port_name, snd_strerror(err)); 114 } 115 116 free(buf); 117} 118 119static void create_port(void) 120{ 121 int err; 122 123 err = snd_seq_create_simple_port(seq, "aseqdump", 124 SND_SEQ_PORT_CAP_WRITE | 125 SND_SEQ_PORT_CAP_SUBS_WRITE, 126 SND_SEQ_PORT_TYPE_MIDI_GENERIC | 127 SND_SEQ_PORT_TYPE_APPLICATION); 128 check_snd("create port", err); 129} 130 131static void connect_ports(void) 132{ 133 int i, err; 134 135 for (i = 0; i < port_count; ++i) { 136 err = snd_seq_connect_from(seq, 0, ports[i].client, ports[i].port); 137 if (err < 0) 138 fatal("Cannot connect from port %d:%d - %s", 139 ports[i].client, ports[i].port, snd_strerror(err)); 140 } 141} 142 143static int channel_number(unsigned char c) 144{ 145 if (view_mode != VIEW_RAW) 146 return c + 1; 147 else 148 return c; 149} 150 151static const char *midi1_data(unsigned int v) 152{ 153 static char tmp[32]; 154 155 if (view_mode == VIEW_PERCENT) { 156 if (v <= 64) 157 snprintf(tmp, sizeof(tmp), "%.2f%%", 158 ((double)v * 50.0) / 64); 159 else 160 snprintf(tmp, sizeof(tmp), "%.2f%%", 161 ((double)(v - 64) * 50.0) / 63 + 50.0); 162 return tmp; 163 } 164 165 sprintf(tmp, "%d", v); 166 return tmp; 167} 168 169static const char *midi1_pitchbend(int v) 170{ 171 static char tmp[32]; 172 173 if (view_mode == VIEW_PERCENT) { 174 if (v < 0) 175 snprintf(tmp, sizeof(tmp), "%.2f%%", 176 ((double)v * 100.0) / 8192); 177 else 178 snprintf(tmp, sizeof(tmp), "%.2f%%", 179 ((double)v * 100.0) / 8191); 180 return tmp; 181 } 182 183 sprintf(tmp, "%d", v); 184 return tmp; 185} 186 187static void dump_event(const snd_seq_event_t *ev) 188{ 189 printf("%3d:%-3d ", ev->source.client, ev->source.port); 190 191 switch (ev->type) { 192 case SND_SEQ_EVENT_NOTEON: 193 if (ev->data.note.velocity) 194 printf("Note on %2d, note %d, velocity %s\n", 195 channel_number(ev->data.note.channel), 196 ev->data.note.note, 197 midi1_data(ev->data.note.velocity)); 198 else 199 printf("Note off %2d, note %d\n", 200 channel_number(ev->data.note.channel), 201 ev->data.note.note); 202 break; 203 case SND_SEQ_EVENT_NOTEOFF: 204 printf("Note off %2d, note %d, velocity %s\n", 205 channel_number(ev->data.note.channel), 206 ev->data.note.note, 207 midi1_data(ev->data.note.velocity)); 208 break; 209 case SND_SEQ_EVENT_KEYPRESS: 210 printf("Polyphonic aftertouch %2d, note %d, value %s\n", 211 channel_number(ev->data.note.channel), 212 ev->data.note.note, 213 midi1_data(ev->data.note.velocity)); 214 break; 215 case SND_SEQ_EVENT_CONTROLLER: 216 printf("Control change %2d, controller %d, value %d\n", 217 channel_number(ev->data.control.channel), 218 ev->data.control.param, ev->data.control.value); 219 break; 220 case SND_SEQ_EVENT_PGMCHANGE: 221 printf("Program change %2d, program %d\n", 222 channel_number(ev->data.control.channel), 223 ev->data.control.value); 224 break; 225 case SND_SEQ_EVENT_CHANPRESS: 226 printf("Channel aftertouch %2d, value %s\n", 227 channel_number(ev->data.control.channel), 228 midi1_data(ev->data.control.value)); 229 break; 230 case SND_SEQ_EVENT_PITCHBEND: 231 printf("Pitch bend %2d, value %s\n", 232 channel_number(ev->data.control.channel), 233 midi1_pitchbend(ev->data.control.value)); 234 break; 235 case SND_SEQ_EVENT_CONTROL14: 236 printf("Control change %2d, controller %d, value %5d\n", 237 channel_number(ev->data.control.channel), 238 ev->data.control.param, ev->data.control.value); 239 break; 240 case SND_SEQ_EVENT_NONREGPARAM: 241 printf("Non-reg. parameter %2d, parameter %d, value %d\n", 242 channel_number(ev->data.control.channel), 243 ev->data.control.param, ev->data.control.value); 244 break; 245 case SND_SEQ_EVENT_REGPARAM: 246 printf("Reg. parameter %2d, parameter %d, value %d\n", 247 channel_number(ev->data.control.channel), 248 ev->data.control.param, ev->data.control.value); 249 break; 250 case SND_SEQ_EVENT_SONGPOS: 251 printf("Song position pointer value %d\n", 252 ev->data.control.value); 253 break; 254 case SND_SEQ_EVENT_SONGSEL: 255 printf("Song select value %d\n", 256 ev->data.control.value); 257 break; 258 case SND_SEQ_EVENT_QFRAME: 259 printf("MTC quarter frame %02xh\n", 260 ev->data.control.value); 261 break; 262 case SND_SEQ_EVENT_TIMESIGN: 263 // XXX how is this encoded? 264 printf("SMF time signature (%#010x)\n", 265 ev->data.control.value); 266 break; 267 case SND_SEQ_EVENT_KEYSIGN: 268 // XXX how is this encoded? 269 printf("SMF key signature (%#010x)\n", 270 ev->data.control.value); 271 break; 272 case SND_SEQ_EVENT_START: 273 if (ev->source.client == SND_SEQ_CLIENT_SYSTEM && 274 ev->source.port == SND_SEQ_PORT_SYSTEM_TIMER) 275 printf("Queue start queue %d\n", 276 ev->data.queue.queue); 277 else 278 printf("Start\n"); 279 break; 280 case SND_SEQ_EVENT_CONTINUE: 281 if (ev->source.client == SND_SEQ_CLIENT_SYSTEM && 282 ev->source.port == SND_SEQ_PORT_SYSTEM_TIMER) 283 printf("Queue continue queue %d\n", 284 ev->data.queue.queue); 285 else 286 printf("Continue\n"); 287 break; 288 case SND_SEQ_EVENT_STOP: 289 if (ev->source.client == SND_SEQ_CLIENT_SYSTEM && 290 ev->source.port == SND_SEQ_PORT_SYSTEM_TIMER) 291 printf("Queue stop queue %d\n", 292 ev->data.queue.queue); 293 else 294 printf("Stop\n"); 295 break; 296 case SND_SEQ_EVENT_SETPOS_TICK: 297 printf("Set tick queue pos. queue %d\n", ev->data.queue.queue); 298 break; 299 case SND_SEQ_EVENT_SETPOS_TIME: 300 printf("Set rt queue pos. queue %d\n", ev->data.queue.queue); 301 break; 302 case SND_SEQ_EVENT_TEMPO: 303 printf("Set queue tempo queue %d\n", ev->data.queue.queue); 304 break; 305 case SND_SEQ_EVENT_CLOCK: 306 printf("Clock\n"); 307 break; 308 case SND_SEQ_EVENT_TICK: 309 printf("Tick\n"); 310 break; 311 case SND_SEQ_EVENT_QUEUE_SKEW: 312 printf("Queue timer skew queue %d\n", ev->data.queue.queue); 313 break; 314 case SND_SEQ_EVENT_TUNE_REQUEST: 315 printf("Tune request\n"); 316 break; 317 case SND_SEQ_EVENT_RESET: 318 printf("Reset\n"); 319 break; 320 case SND_SEQ_EVENT_SENSING: 321 printf("Active Sensing\n"); 322 break; 323 case SND_SEQ_EVENT_CLIENT_START: 324 printf("Client start client %d\n", 325 ev->data.addr.client); 326 break; 327 case SND_SEQ_EVENT_CLIENT_EXIT: 328 printf("Client exit client %d\n", 329 ev->data.addr.client); 330 break; 331 case SND_SEQ_EVENT_CLIENT_CHANGE: 332 printf("Client changed client %d\n", 333 ev->data.addr.client); 334 break; 335 case SND_SEQ_EVENT_PORT_START: 336 printf("Port start %d:%d\n", 337 ev->data.addr.client, ev->data.addr.port); 338 break; 339 case SND_SEQ_EVENT_PORT_EXIT: 340 printf("Port exit %d:%d\n", 341 ev->data.addr.client, ev->data.addr.port); 342 break; 343 case SND_SEQ_EVENT_PORT_CHANGE: 344 printf("Port changed %d:%d\n", 345 ev->data.addr.client, ev->data.addr.port); 346 break; 347 case SND_SEQ_EVENT_PORT_SUBSCRIBED: 348 printf("Port subscribed %d:%d -> %d:%d\n", 349 ev->data.connect.sender.client, ev->data.connect.sender.port, 350 ev->data.connect.dest.client, ev->data.connect.dest.port); 351 break; 352 case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: 353 printf("Port unsubscribed %d:%d -> %d:%d\n", 354 ev->data.connect.sender.client, ev->data.connect.sender.port, 355 ev->data.connect.dest.client, ev->data.connect.dest.port); 356 break; 357 case SND_SEQ_EVENT_SYSEX: 358 { 359 unsigned int i; 360 printf("System exclusive "); 361 for (i = 0; i < ev->data.ext.len; ++i) 362 printf(" %02X", ((unsigned char*)ev->data.ext.ptr)[i]); 363 printf("\n"); 364 } 365 break; 366 default: 367 printf("Event type %d\n", ev->type); 368 } 369} 370 371#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION 372static int group_number(unsigned char c) 373{ 374 if (view_mode != VIEW_RAW) 375 return c + 1; 376 else 377 return c; 378} 379 380static const char *pitchbend_value(uint8_t msb, uint8_t lsb) 381{ 382 int pb = (msb << 7) | lsb; 383 384 return midi1_pitchbend(pb - 8192); 385} 386 387static void dump_ump_midi1_event(const unsigned int *ump) 388{ 389 const snd_ump_msg_midi1_t *m = (const snd_ump_msg_midi1_t *)ump; 390 unsigned char group = group_number(m->hdr.group); 391 unsigned char status = m->hdr.status; 392 unsigned char channel = channel_number(m->hdr.channel); 393 394 printf("Group %2d, ", group); 395 switch (status) { 396 case SND_UMP_MSG_NOTE_OFF: 397 printf("Note off %2d, note %d, velocity %s", 398 channel, m->note_off.note, 399 midi1_data(m->note_off.velocity)); 400 break; 401 case SND_UMP_MSG_NOTE_ON: 402 printf("Note on %2d, note %d, velocity %s", 403 channel, m->note_off.note, 404 midi1_data(m->note_off.velocity)); 405 break; 406 case SND_UMP_MSG_POLY_PRESSURE: 407 printf("Poly pressure %2d, note %d, value %s", 408 channel, m->poly_pressure.note, 409 midi1_data(m->poly_pressure.data)); 410 break; 411 case SND_UMP_MSG_CONTROL_CHANGE: 412 printf("Control change %2d, controller %d, value %d", 413 channel, m->control_change.index, m->control_change.data); 414 break; 415 case SND_UMP_MSG_PROGRAM_CHANGE: 416 printf("Program change %2d, program %d", 417 channel, m->program_change.program); 418 break; 419 case SND_UMP_MSG_CHANNEL_PRESSURE: 420 printf("Channel pressure %2d, value %s", 421 channel, midi1_data(m->channel_pressure.data)); 422 break; 423 case SND_UMP_MSG_PITCHBEND: 424 printf("Pitchbend %2d, value %s", 425 channel, pitchbend_value(m->pitchbend.data_msb, 426 m->pitchbend.data_lsb)); 427 break; 428 default: 429 printf("UMP MIDI1 event: status = %d, channel = %d, 0x%08x", 430 status, channel, *ump); 431 break; 432 } 433 printf("\n"); 434} 435 436static const char *midi2_velocity(unsigned int v) 437{ 438 static char tmp[32]; 439 440 if (view_mode == VIEW_NORMALIZED) { 441 if (v <= 0x8000) 442 snprintf(tmp, sizeof(tmp), "%.2f", 443 ((double)v * 64.0) / 0x8000); 444 else 445 snprintf(tmp, sizeof(tmp), ".2%f", 446 ((double)(v - 0x8000) * 63.0) / 0x7fff + 64.0); 447 return tmp; 448 } else if (view_mode == VIEW_PERCENT) { 449 snprintf(tmp, sizeof(tmp), "%.2f%%", ((double)v * 100.0) / 0xffff); 450 return tmp; 451 } 452 453 sprintf(tmp, "0x%x", v); 454 return tmp; 455} 456 457static const char *midi2_data(unsigned int v) 458{ 459 static char tmp[32]; 460 461 if (view_mode == VIEW_NORMALIZED) { 462 if (!v) 463 return "0"; 464 else if (v == 0xffffffffU) 465 return "127"; 466 if (v <= 0x80000000) 467 snprintf(tmp, sizeof(tmp), "%.2f", 468 ((double)v * 64.0) / 0x80000000U); 469 else 470 snprintf(tmp, sizeof(tmp), "%.2f", 471 ((double)(v - 0x80000000U) * 63.0) / 0x7fffffffU + 64.0); 472 return tmp; 473 } else if (view_mode == VIEW_PERCENT) { 474 snprintf(tmp, sizeof(tmp), "%.2f%%", ((double)v * 100.0) / 0xffffffffU); 475 return tmp; 476 } 477 478 sprintf(tmp, "0x%x", v); 479 return tmp; 480} 481 482static const char *midi2_pitchbend(unsigned int v) 483{ 484 static char tmp[32]; 485 486 if (view_mode == VIEW_NORMALIZED) { 487 if (!v) 488 return "-8192"; 489 else if (v == 0xffffffffU) 490 return "8191"; 491 if (v <= 0x80000000) 492 snprintf(tmp, sizeof(tmp), "%.2f", 493 ((int)(v ^ 0x80000000U) * 8192.0) / 0x80000000U); 494 else 495 snprintf(tmp, sizeof(tmp), "%.2f", 496 ((double)(v - 0x80000000U) * 8191.0) / 0x7fffffffU + 8192.0); 497 return tmp; 498 } else if (view_mode == VIEW_PERCENT) { 499 snprintf(tmp, sizeof(tmp), "%.2f%%", ((int)(v ^ 0x80000000U) * 100.0) / 0xffffffffU); 500 return tmp; 501 } 502 503 sprintf(tmp, "0x%x", v); 504 return tmp; 505} 506 507static void dump_ump_midi2_event(const unsigned int *ump) 508{ 509 const snd_ump_msg_midi2_t *m = (const snd_ump_msg_midi2_t *)ump; 510 unsigned char group = group_number(m->hdr.group); 511 unsigned char status = m->hdr.status; 512 unsigned char channel = channel_number(m->hdr.channel); 513 514 printf("Group %2d, ", group); 515 switch (status) { 516 case SND_UMP_MSG_PER_NOTE_RCC: 517 printf("Per-note RCC %2d, note %u, index %u, value 0x%x", 518 channel, m->per_note_rcc.note, 519 m->per_note_rcc.index, m->per_note_rcc.data); 520 break; 521 case SND_UMP_MSG_PER_NOTE_ACC: 522 printf("Per-note ACC %2d, note %u, index %u, value 0x%x", 523 channel, m->per_note_acc.note, 524 m->per_note_acc.index, m->per_note_acc.data); 525 break; 526 case SND_UMP_MSG_RPN: 527 printf("RPN %2d, bank %u:%u, value 0x%x", 528 channel, m->rpn.bank, m->rpn.index, m->rpn.data); 529 break; 530 case SND_UMP_MSG_NRPN: 531 printf("NRPN %2d, bank %u:%u, value 0x%x", 532 channel, m->rpn.bank, m->rpn.index, m->rpn.data); 533 break; 534 case SND_UMP_MSG_RELATIVE_RPN: 535 printf("relative RPN %2d, bank %u:%u, value 0x%x", 536 channel, m->rpn.bank, m->rpn.index, m->rpn.data); 537 break; 538 case SND_UMP_MSG_RELATIVE_NRPN: 539 printf("relative NRP %2d, bank %u:%u, value 0x%x", 540 channel, m->rpn.bank, m->rpn.index, m->rpn.data); 541 break; 542 case SND_UMP_MSG_PER_NOTE_PITCHBEND: 543 printf("Per-note pitchbend %2d, note %d, value %s", 544 channel, m->per_note_pitchbend.note, 545 midi2_pitchbend(m->per_note_pitchbend.data)); 546 break; 547 case SND_UMP_MSG_NOTE_OFF: 548 printf("Note off %2d, note %d, velocity %s, attr type = %d, data = 0x%x", 549 channel, m->note_off.note, 550 midi2_velocity(m->note_off.velocity), 551 m->note_off.attr_type, m->note_off.attr_data); 552 break; 553 case SND_UMP_MSG_NOTE_ON: 554 printf("Note on %2d, note %d, velocity %s, attr type = %d, data = 0x%x", 555 channel, m->note_off.note, 556 midi2_velocity(m->note_off.velocity), 557 m->note_off.attr_type, m->note_off.attr_data); 558 break; 559 case SND_UMP_MSG_POLY_PRESSURE: 560 printf("Poly pressure %2d, note %d, value %s", 561 channel, m->poly_pressure.note, 562 midi2_data(m->poly_pressure.data)); 563 break; 564 case SND_UMP_MSG_CONTROL_CHANGE: 565 printf("Control change %2d, controller %d, value 0x%x", 566 channel, m->control_change.index, m->control_change.data); 567 break; 568 case SND_UMP_MSG_PROGRAM_CHANGE: 569 printf("Program change %2d, program %d", 570 channel, m->program_change.program); 571 if (m->program_change.bank_valid) 572 printf(", Bank select %d:%d", 573 m->program_change.bank_msb, 574 m->program_change.bank_lsb); 575 break; 576 case SND_UMP_MSG_CHANNEL_PRESSURE: 577 printf("Channel pressure %2d, value %s", 578 channel, 579 midi2_data(m->channel_pressure.data)); 580 break; 581 case SND_UMP_MSG_PITCHBEND: 582 printf("Channel pressure %2d, value %s", 583 channel, 584 midi2_pitchbend(m->channel_pressure.data)); 585 break; 586 case SND_UMP_MSG_PER_NOTE_MGMT: 587 printf("Per-note management %2d, value 0x%x", 588 channel, m->per_note_mgmt.flags); 589 break; 590 default: 591 printf("UMP MIDI2 event: status = %d, channel = %d, 0x%08x", 592 status, channel, *ump); 593 break; 594 } 595 printf("\n"); 596} 597 598static void dump_ump_event(const snd_seq_ump_event_t *ev) 599{ 600 if (!snd_seq_ev_is_ump(ev)) { 601 dump_event((const snd_seq_event_t *)ev); 602 return; 603 } 604 605 printf("%3d:%-3d ", ev->source.client, ev->source.port); 606 607 switch (snd_ump_msg_type(ev->ump)) { 608 case SND_UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE: 609 dump_ump_midi1_event(ev->ump); 610 break; 611 case SND_UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE: 612 dump_ump_midi2_event(ev->ump); 613 break; 614 default: 615 printf("UMP event: type = %d, group = %d, status = %d, 0x%08x\n", 616 snd_ump_msg_type(ev->ump), 617 snd_ump_msg_group(ev->ump), 618 snd_ump_msg_status(ev->ump), 619 *ev->ump); 620 break; 621 } 622} 623#endif /* HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION */ 624 625static void list_ports(void) 626{ 627 snd_seq_client_info_t *cinfo; 628 snd_seq_port_info_t *pinfo; 629 630 snd_seq_client_info_alloca(&cinfo); 631 snd_seq_port_info_alloca(&pinfo); 632 633 puts(" Port Client name Port name"); 634 635 snd_seq_client_info_set_client(cinfo, -1); 636 while (snd_seq_query_next_client(seq, cinfo) >= 0) { 637 int client = snd_seq_client_info_get_client(cinfo); 638 639 snd_seq_port_info_set_client(pinfo, client); 640 snd_seq_port_info_set_port(pinfo, -1); 641 while (snd_seq_query_next_port(seq, pinfo) >= 0) { 642 /* we need both READ and SUBS_READ */ 643 if ((snd_seq_port_info_get_capability(pinfo) 644 & (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ)) 645 != (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ)) 646 continue; 647 printf("%3d:%-3d %-32.32s %s\n", 648 snd_seq_port_info_get_client(pinfo), 649 snd_seq_port_info_get_port(pinfo), 650 snd_seq_client_info_get_name(cinfo), 651 snd_seq_port_info_get_name(pinfo)); 652 } 653 } 654} 655 656static void help(const char *argv0) 657{ 658 printf("Usage: %s [options]\n" 659 "\nAvailable options:\n" 660 " -h,--help this help\n" 661 " -V,--version show version\n" 662 " -l,--list list input ports\n" 663 " -N,--normalized-view show normalized values\n" 664 " -P,--percent-view show percent values\n" 665 " -R,--raw-view show raw values (default)\n" 666#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION 667 " -u,--ump=version set client MIDI version (0=legacy, 1= UMP MIDI 1.0, 2=UMP MIDI2.0)\n" 668 " -r,--raw do not convert UMP and legacy events\n" 669#endif 670 " -p,--port=client:port,... source port(s)\n", 671 argv0); 672} 673 674static void version(void) 675{ 676 puts("aseqdump version " SND_UTIL_VERSION_STR); 677} 678 679static void sighandler(int sig ATTRIBUTE_UNUSED) 680{ 681 stop = 1; 682} 683 684int main(int argc, char *argv[]) 685{ 686 static const char short_options[] = "hVlp:NPR" 687#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION 688 "u:r" 689#endif 690 ; 691 static const struct option long_options[] = { 692 {"help", 0, NULL, 'h'}, 693 {"version", 0, NULL, 'V'}, 694 {"list", 0, NULL, 'l'}, 695 {"port", 1, NULL, 'p'}, 696 {"normalized-view", 0, NULL, 'N'}, 697 {"percent-view", 0, NULL, 'P'}, 698 {"raw-view", 0, NULL, 'R'}, 699#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION 700 {"ump", 1, NULL, 'u'}, 701 {"raw", 0, NULL, 'r'}, 702#endif 703 {0} 704 }; 705 706 int do_list = 0; 707 struct pollfd *pfds; 708 int npfds; 709 int c, err; 710 711 init_seq(); 712 713 while ((c = getopt_long(argc, argv, short_options, 714 long_options, NULL)) != -1) { 715 switch (c) { 716 case 'h': 717 help(argv[0]); 718 return 0; 719 case 'V': 720 version(); 721 return 0; 722 case 'l': 723 do_list = 1; 724 break; 725 case 'p': 726 parse_ports(optarg); 727 break; 728 case 'R': 729 view_mode = VIEW_RAW; 730 break; 731 case 'P': 732 view_mode = VIEW_PERCENT; 733 break; 734 case 'N': 735 view_mode = VIEW_NORMALIZED; 736 break; 737#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION 738 case 'u': 739 ump_version = atoi(optarg); 740 snd_seq_set_client_midi_version(seq, ump_version); 741 break; 742 case 'r': 743 snd_seq_set_client_ump_conversion(seq, 0); 744 break; 745#endif 746 default: 747 help(argv[0]); 748 return 1; 749 } 750 } 751 if (optind < argc) { 752 help(argv[0]); 753 return 1; 754 } 755 756 if (do_list) { 757 list_ports(); 758 return 0; 759 } 760 761 create_port(); 762 connect_ports(); 763 764 err = snd_seq_nonblock(seq, 1); 765 check_snd("set nonblock mode", err); 766 767 if (port_count > 0) 768 printf("Waiting for data."); 769 else 770 printf("Waiting for data at port %d:0.", 771 snd_seq_client_id(seq)); 772 printf(" Press Ctrl+C to end.\n"); 773 printf("Source %sEvent Ch Data\n", 774 ump_version ? "Group " : ""); 775 776 signal(SIGINT, sighandler); 777 signal(SIGTERM, sighandler); 778 779 npfds = snd_seq_poll_descriptors_count(seq, POLLIN); 780 pfds = alloca(sizeof(*pfds) * npfds); 781 for (;;) { 782 snd_seq_poll_descriptors(seq, pfds, npfds, POLLIN); 783 if (poll(pfds, npfds, -1) < 0) 784 break; 785 for (;;) { 786 snd_seq_event_t *event; 787#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION 788 snd_seq_ump_event_t *ump_ev; 789 if (ump_version > 0) { 790 err = snd_seq_ump_event_input(seq, &ump_ev); 791 if (err < 0) 792 break; 793 if (ump_ev) 794 dump_ump_event(ump_ev); 795 continue; 796 } 797#endif 798 799 err = snd_seq_event_input(seq, &event); 800 if (err < 0) 801 break; 802 if (event) 803 dump_event(event); 804 } 805 fflush(stdout); 806 if (stop) 807 break; 808 } 809 810 snd_seq_close(seq); 811 return 0; 812} 813