⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 aplaymidi.c

📁 ALSA驱动的一些调试测试工具
💻 C
📖 第 1 页 / 共 2 页
字号:
	num_tracks = read_int(2);	if (num_tracks < 1 || num_tracks > 1000) {		errormsg("%s: invalid number of tracks (%d)", file_name, num_tracks);		num_tracks = 0;		return 0;	}	tracks = calloc(num_tracks, sizeof(struct track));	if (!tracks) {		errormsg("out of memory");		num_tracks = 0;		return 0;	}	time_division = read_int(2);	if (time_division < 0)		goto invalid_format;	/* interpret and set tempo */	snd_seq_queue_tempo_alloca(&queue_tempo);	smpte_timing = !!(time_division & 0x8000);	if (!smpte_timing) {		/* time_division is ticks per quarter */		snd_seq_queue_tempo_set_tempo(queue_tempo, 500000); /* default: 120 bpm */		snd_seq_queue_tempo_set_ppq(queue_tempo, time_division);	} else {		/* upper byte is negative frames per second */		i = 0x80 - ((time_division >> 8) & 0x7f);		/* lower byte is ticks per frame */		time_division &= 0xff;		/* now pretend that we have quarter-note based timing */		switch (i) {		case 24:			snd_seq_queue_tempo_set_tempo(queue_tempo, 500000);			snd_seq_queue_tempo_set_ppq(queue_tempo, 12 * time_division);			break;		case 25:			snd_seq_queue_tempo_set_tempo(queue_tempo, 400000);			snd_seq_queue_tempo_set_ppq(queue_tempo, 10 * time_division);			break;		case 29: /* 30 drop-frame */			snd_seq_queue_tempo_set_tempo(queue_tempo, 100000000);			snd_seq_queue_tempo_set_ppq(queue_tempo, 2997 * time_division);			break;		case 30:			snd_seq_queue_tempo_set_tempo(queue_tempo, 500000);			snd_seq_queue_tempo_set_ppq(queue_tempo, 15 * time_division);			break;		default:			errormsg("%s: invalid number of SMPTE frames per second (%d)",				 file_name, i);			return 0;		}	}	err = snd_seq_set_queue_tempo(seq, queue, queue_tempo);	if (err < 0) {		errormsg("Cannot set queue tempo (%u/%i)",			 snd_seq_queue_tempo_get_tempo(queue_tempo),			 snd_seq_queue_tempo_get_ppq(queue_tempo));		return 0;	}	/* read tracks */	for (i = 0; i < num_tracks; ++i) {		int len;		/* search for MTrk chunk */		for (;;) {			int id = read_id();			len = read_int(4);			if (feof(file)) {				errormsg("%s: unexpected end of file", file_name);				return 0;			}			if (len < 0 || len >= 0x10000000) {				errormsg("%s: invalid chunk length %d", file_name, len);				return 0;			}			if (id == MAKE_ID('M', 'T', 'r', 'k'))				break;			skip(len);		}		if (!read_track(&tracks[i], file_offset + len))			return 0;	}	return 1;}static int read_riff(void){	/* skip file length */	read_byte();	read_byte();	read_byte();	read_byte();	/* check file type ("RMID" = RIFF MIDI) */	if (read_id() != MAKE_ID('R', 'M', 'I', 'D')) {invalid_format:		errormsg("%s: invalid file format", file_name);		return 0;	}	/* search for "data" chunk */	for (;;) {		int id = read_id();		int len = read_32_le();		if (feof(file)) {data_not_found:			errormsg("%s: data chunk not found", file_name);			return 0;		}		if (id == MAKE_ID('d', 'a', 't', 'a'))			break;		if (len < 0)			goto data_not_found;		skip((len + 1) & ~1);	}	/* the "data" chunk must contain data in SMF format */	if (read_id() != MAKE_ID('M', 'T', 'h', 'd'))		goto invalid_format;	return read_smf();}static void cleanup_file_data(void){	int i;	struct event *event;	for (i = 0; i < num_tracks; ++i) {		event = tracks[i].first_event;		while (event) {			struct event *next = event->next;			free(event);			event = next;		}	}	num_tracks = 0;	free(tracks);	tracks = NULL;}static void handle_big_sysex(snd_seq_event_t *ev){	unsigned int length;	ssize_t event_size;	int err;	length = ev->data.ext.len;	if (length > MIDI_BYTES_PER_SEC)		ev->data.ext.len = MIDI_BYTES_PER_SEC;	event_size = snd_seq_event_length(ev);	if (event_size + 1 > snd_seq_get_output_buffer_size(seq)) {		err = snd_seq_drain_output(seq);		check_snd("drain output", err);		err = snd_seq_set_output_buffer_size(seq, event_size + 1);		check_snd("set output buffer size", err);	}	while (length > MIDI_BYTES_PER_SEC) {		err = snd_seq_event_output(seq, ev);		check_snd("output event", err);		err = snd_seq_drain_output(seq);		check_snd("drain output", err);		err = snd_seq_sync_output_queue(seq);		check_snd("sync output", err);		if (sleep(1))			fatal("aborted");		ev->data.ext.ptr += MIDI_BYTES_PER_SEC;		length -= MIDI_BYTES_PER_SEC;	}	ev->data.ext.len = length;}static void play_midi(void){	snd_seq_event_t ev;	int i, max_tick, err;	/* calculate length of the entire file */	max_tick = -1;	for (i = 0; i < num_tracks; ++i) {		if (tracks[i].end_tick > max_tick)			max_tick = tracks[i].end_tick;	}	/* initialize current position in each track */	for (i = 0; i < num_tracks; ++i)		tracks[i].current_event = tracks[i].first_event;	/* common settings for all our events */	snd_seq_ev_clear(&ev);	ev.queue = queue;	ev.source.port = 0;	ev.flags = SND_SEQ_TIME_STAMP_TICK;	err = snd_seq_start_queue(seq, queue, NULL);	check_snd("start queue", err);	/* The queue won't be started until the START_QUEUE event is	 * actually drained to the kernel, which is exactly what we want. */	for (;;) {		struct event* event = NULL;		struct track* event_track = NULL;		int i, min_tick = max_tick + 1;		/* search next event */		for (i = 0; i < num_tracks; ++i) {			struct track *track = &tracks[i];			struct event *e2 = track->current_event;			if (e2 && e2->tick < min_tick) {				min_tick = e2->tick;				event = e2;				event_track = track;			}		}		if (!event)			break; /* end of song reached */		/* advance pointer to next event */		event_track->current_event = event->next;		/* output the event */		ev.type = event->type;		ev.time.tick = event->tick;		ev.dest = ports[event->port];		switch (ev.type) {		case SND_SEQ_EVENT_NOTEON:		case SND_SEQ_EVENT_NOTEOFF:		case SND_SEQ_EVENT_KEYPRESS:			snd_seq_ev_set_fixed(&ev);			ev.data.note.channel = event->data.d[0];			ev.data.note.note = event->data.d[1];			ev.data.note.velocity = event->data.d[2];			break;		case SND_SEQ_EVENT_CONTROLLER:			snd_seq_ev_set_fixed(&ev);			ev.data.control.channel = event->data.d[0];			ev.data.control.param = event->data.d[1];			ev.data.control.value = event->data.d[2];			break;		case SND_SEQ_EVENT_PGMCHANGE:		case SND_SEQ_EVENT_CHANPRESS:			snd_seq_ev_set_fixed(&ev);			ev.data.control.channel = event->data.d[0];			ev.data.control.value = event->data.d[1];			break;		case SND_SEQ_EVENT_PITCHBEND:			snd_seq_ev_set_fixed(&ev);			ev.data.control.channel = event->data.d[0];			ev.data.control.value =				((event->data.d[1]) |				 ((event->data.d[2]) << 7)) - 0x2000;			break;		case SND_SEQ_EVENT_SYSEX:			snd_seq_ev_set_variable(&ev, event->data.length,						event->sysex);			handle_big_sysex(&ev);			break;		case SND_SEQ_EVENT_TEMPO:			snd_seq_ev_set_fixed(&ev);			ev.dest.client = SND_SEQ_CLIENT_SYSTEM;			ev.dest.port = SND_SEQ_PORT_SYSTEM_TIMER;			ev.data.queue.queue = queue;			ev.data.queue.param.value = event->data.tempo;			break;		default:			fatal("Invalid event type %d!", ev.type);		}		/* this blocks when the output pool has been filled */		err = snd_seq_event_output(seq, &ev);		check_snd("output event", err);	}	/* schedule queue stop at end of song */	snd_seq_ev_set_fixed(&ev);	ev.type = SND_SEQ_EVENT_STOP;	ev.time.tick = max_tick;	ev.dest.client = SND_SEQ_CLIENT_SYSTEM;	ev.dest.port = SND_SEQ_PORT_SYSTEM_TIMER;	ev.data.queue.queue = queue;	err = snd_seq_event_output(seq, &ev);	check_snd("output event", err);	/* make sure that the sequencer sees all our events */	err = snd_seq_drain_output(seq);	check_snd("drain output", err);	/*	 * There are three possibilities how to wait until all events have	 * been played:	 * 1) send an event back to us (like pmidi does), and wait for it;	 * 2) wait for the EVENT_STOP notification for our queue which is sent	 *    by the system timer port (this would require a subscription);	 * 3) wait until the output pool is empty.	 * The last is the simplest.	 */	err = snd_seq_sync_output_queue(seq);	check_snd("sync output", err);	/* give the last notes time to die away */	if (end_delay > 0)		sleep(end_delay);}static void play_file(void){	int ok;	if (!strcmp(file_name, "-"))		file = stdin;	else		file = fopen(file_name, "rb");	if (!file) {		errormsg("Cannot open %s - %s", file_name, strerror(errno));		return;	}	file_offset = 0;	ok = 0;	switch (read_id()) {	case MAKE_ID('M', 'T', 'h', 'd'):		ok = read_smf();		break;	case MAKE_ID('R', 'I', 'F', 'F'):		ok = read_riff();		break;	default:		errormsg("%s is not a Standard MIDI File", file_name);		break;	}	if (file != stdin)		fclose(file);	if (ok)		play_midi();	cleanup_file_data();}static void list_ports(void){	snd_seq_client_info_t *cinfo;	snd_seq_port_info_t *pinfo;	snd_seq_client_info_alloca(&cinfo);	snd_seq_port_info_alloca(&pinfo);	puts(" Port    Client name                      Port name");	snd_seq_client_info_set_client(cinfo, -1);	while (snd_seq_query_next_client(seq, cinfo) >= 0) {		int client = snd_seq_client_info_get_client(cinfo);		snd_seq_port_info_set_client(pinfo, client);		snd_seq_port_info_set_port(pinfo, -1);		while (snd_seq_query_next_port(seq, pinfo) >= 0) {			/* port must understand MIDI messages */			if (!(snd_seq_port_info_get_type(pinfo)			      & SND_SEQ_PORT_TYPE_MIDI_GENERIC))				continue;			/* we need both WRITE and SUBS_WRITE */			if ((snd_seq_port_info_get_capability(pinfo)			     & (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE))			    != (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE))				continue;			printf("%3d:%-3d  %-32.32s %s\n",			       snd_seq_port_info_get_client(pinfo),			       snd_seq_port_info_get_port(pinfo),			       snd_seq_client_info_get_name(cinfo),			       snd_seq_port_info_get_name(pinfo));		}	}}static void usage(const char *argv0){	printf(		"Usage: %s -p client:port[,...] [-d delay] midifile ...\n"		"-h, --help                  this help\n"		"-V, --version               print current version\n"		"-l, --list                  list all possible output ports\n"		"-p, --port=client:port,...  set port(s) to play to\n"		"-d, --delay=seconds         delay after song ends\n",		argv0);}static void version(void){	puts("aplaymidi version " SND_UTIL_VERSION_STR);}int main(int argc, char *argv[]){	static const char short_options[] = "hVlp:d:";	static const struct option long_options[] = {		{"help", 0, NULL, 'h'},		{"version", 0, NULL, 'V'},		{"list", 0, NULL, 'l'},		{"port", 1, NULL, 'p'},		{"delay", 1, NULL, 'd'},		{}	};	int c;	int do_list = 0;	init_seq();	while ((c = getopt_long(argc, argv, short_options,				long_options, NULL)) != -1) {		switch (c) {		case 'h':			usage(argv[0]);			return 0;		case 'V':			version();			return 0;		case 'l':			do_list = 1;			break;		case 'p':			parse_ports(optarg);			break;		case 'd':			end_delay = atoi(optarg);			break;		default:			usage(argv[0]);			return 1;		}	}	if (do_list) {		list_ports();	} else {		if (port_count < 1) {			/* use env var for compatibility with pmidi */			const char *ports_str = getenv("ALSA_OUTPUT_PORTS");			if (ports_str)				parse_ports(ports_str);			if (port_count < 1) {				errormsg("Please specify at least one port with --port.");				return 1;			}		}		if (optind >= argc) {			errormsg("Please specify a file to play.");			return 1;		}		create_source_port();		create_queue();		connect_ports();		for (; optind < argc; ++optind) {			file_name = argv[optind];			play_file();		}	}	snd_seq_close(seq);	return 0;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -