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

📄 aplaymidi.c

📁 ALSA驱动的一些调试测试工具
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * aplaymidi.c - play Standard MIDI Files to sequencer port(s) * * Copyright (c) 2004-2006 Clemens Ladisch <clemens@ladisch.de> * * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License as published by *  the Free Software Foundation; either version 2 of the License, or *  (at your option) any later version. * *  This program is distributed in the hope that it will be useful, *  but WITHOUT ANY WARRANTY; without even the implied warranty of *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *  GNU General Public License for more details. * *  You should have received a copy of the GNU General Public License *  along with this program; if not, write to the Free Software *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA *//* TODO: sequencer queue timer selection */#include <stdio.h>#include <stdlib.h>#include <stdarg.h>#include <string.h>#include <getopt.h>#include <unistd.h>#include <alsa/asoundlib.h>#include "aconfig.h"#include "version.h"#define MIDI_BYTES_PER_SEC 3125/* * A MIDI event after being parsed/loaded from the file. * There could be made a case for using snd_seq_event_t instead. */struct event {	struct event *next;		/* linked list */	unsigned char type;		/* SND_SEQ_EVENT_xxx */	unsigned char port;		/* port index */	unsigned int tick;	union {		unsigned char d[3];	/* channel and data bytes */		int tempo;		unsigned int length;	/* length of sysex data */	} data;	unsigned char sysex[0];};struct track {	struct event *first_event;	/* list of all events in this track */	int end_tick;			/* length of this track */	struct event *current_event;	/* used while loading and playing */};static snd_seq_t *seq;static int client;static int port_count;static snd_seq_addr_t *ports;static int queue;static int end_delay = 2;static const char *file_name;static FILE *file;static int file_offset;		/* current offset in input file */static int num_tracks;static struct track *tracks;static int smpte_timing;/* prints an error message to stderr */static void errormsg(const char *msg, ...){	va_list ap;	va_start(ap, msg);	vfprintf(stderr, msg, ap);	va_end(ap);	fputc('\n', stderr);}/* prints an error message to stderr, and dies */static void fatal(const char *msg, ...){	va_list ap;	va_start(ap, msg);	vfprintf(stderr, msg, ap);	va_end(ap);	fputc('\n', stderr);	exit(EXIT_FAILURE);}/* memory allocation error handling */static void check_mem(void *p){	if (!p)		fatal("Out of memory");}/* error handling for ALSA functions */static void check_snd(const char *operation, int err){	if (err < 0)		fatal("Cannot %s - %s", operation, snd_strerror(err));}static void init_seq(void){	int err;	/* open sequencer */	err = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0);	check_snd("open sequencer", err);	/* set our name (otherwise it's "Client-xxx") */	err = snd_seq_set_client_name(seq, "aplaymidi");	check_snd("set client name", err);	/* find out who we actually are */	client = snd_seq_client_id(seq);	check_snd("get client id", client);}/* parses one or more port addresses from the string */static void parse_ports(const char *arg){	char *buf, *s, *port_name;	int err;	/* make a copy of the string because we're going to modify it */	buf = strdup(arg);	check_mem(buf);	for (port_name = s = buf; s; port_name = s + 1) {		/* Assume that ports are separated by commas.  We don't use		 * spaces because those are valid in client names. */		s = strchr(port_name, ',');		if (s)			*s = '\0';		++port_count;		ports = realloc(ports, port_count * sizeof(snd_seq_addr_t));		check_mem(ports);		err = snd_seq_parse_address(seq, &ports[port_count - 1], port_name);		if (err < 0)			fatal("Invalid port %s - %s", port_name, snd_strerror(err));	}	free(buf);}static void create_source_port(void){	snd_seq_port_info_t *pinfo;	int err;	snd_seq_port_info_alloca(&pinfo);	/* the first created port is 0 anyway, but let's make sure ... */	snd_seq_port_info_set_port(pinfo, 0);	snd_seq_port_info_set_port_specified(pinfo, 1);	snd_seq_port_info_set_name(pinfo, "aplaymidi");	snd_seq_port_info_set_capability(pinfo, 0); /* sic */	snd_seq_port_info_set_type(pinfo,				   SND_SEQ_PORT_TYPE_MIDI_GENERIC |				   SND_SEQ_PORT_TYPE_APPLICATION);	err = snd_seq_create_port(seq, pinfo);	check_snd("create port", err);}static void create_queue(void){	queue = snd_seq_alloc_named_queue(seq, "aplaymidi");	check_snd("create queue", queue);	/* the queue is now locked, which is just fine */}static void connect_ports(void){	int i, err;	/*	 * We send MIDI events with explicit destination addresses, so we don't	 * need any connections to the playback ports.  But we connect to those	 * anyway to force any underlying RawMIDI ports to remain open while	 * we're playing - otherwise, ALSA would reset the port after every	 * event.	 */	for (i = 0; i < port_count; ++i) {		err = snd_seq_connect_to(seq, 0, ports[i].client, ports[i].port);		if (err < 0)			fatal("Cannot connect to port %d:%d - %s",			      ports[i].client, ports[i].port, snd_strerror(err));	}}static int read_byte(void){	++file_offset;	return getc(file);}/* reads a little-endian 32-bit integer */static int read_32_le(void){	int value;	value = read_byte();	value |= read_byte() << 8;	value |= read_byte() << 16;	value |= read_byte() << 24;	return !feof(file) ? value : -1;}/* reads a 4-character identifier */static int read_id(void){	return read_32_le();}#define MAKE_ID(c1, c2, c3, c4) ((c1) | ((c2) << 8) | ((c3) << 16) | ((c4) << 24))/* reads a fixed-size big-endian number */static int read_int(int bytes){	int c, value = 0;	do {		c = read_byte();		if (c == EOF)			return -1;		value = (value << 8) | c;	} while (--bytes);	return value;}/* reads a variable-length number */static int read_var(void){	int value, c;	c = read_byte();	value = c & 0x7f;	if (c & 0x80) {		c = read_byte();		value = (value << 7) | (c & 0x7f);		if (c & 0x80) {			c = read_byte();			value = (value << 7) | (c & 0x7f);			if (c & 0x80) {				c = read_byte();				value = (value << 7) | c;				if (c & 0x80)					return -1;			}		}	}	return !feof(file) ? value : -1;}/* allocates a new event */static struct event *new_event(struct track *track, int sysex_length){	struct event *event;	event = malloc(sizeof(struct event) + sysex_length);	check_mem(event);	event->next = NULL;	/* append at the end of the track's linked list */	if (track->current_event)		track->current_event->next = event;	else		track->first_event = event;	track->current_event = event;	return event;}static void skip(int bytes){	while (bytes > 0)		read_byte(), --bytes;}/* reads one complete track from the file */static int read_track(struct track *track, int track_end){	int tick = 0;	unsigned char last_cmd = 0;	unsigned char port = 0;	/* the current file position is after the track ID and length */	while (file_offset < track_end) {		unsigned char cmd;		struct event *event;		int delta_ticks, len, c;		delta_ticks = read_var();		if (delta_ticks < 0)			break;		tick += delta_ticks;		c = read_byte();		if (c < 0)			break;		if (c & 0x80) {			/* have command */			cmd = c;			if (cmd < 0xf0)				last_cmd = cmd;		} else {			/* running status */			ungetc(c, file);			file_offset--;			cmd = last_cmd;			if (!cmd)				goto _error;		}		switch (cmd >> 4) {			/* maps SMF events to ALSA sequencer events */			static const unsigned char cmd_type[] = {				[0x8] = SND_SEQ_EVENT_NOTEOFF,				[0x9] = SND_SEQ_EVENT_NOTEON,				[0xa] = SND_SEQ_EVENT_KEYPRESS,				[0xb] = SND_SEQ_EVENT_CONTROLLER,				[0xc] = SND_SEQ_EVENT_PGMCHANGE,				[0xd] = SND_SEQ_EVENT_CHANPRESS,				[0xe] = SND_SEQ_EVENT_PITCHBEND			};		case 0x8: /* channel msg with 2 parameter bytes */		case 0x9:		case 0xa:		case 0xb:		case 0xe:			event = new_event(track, 0);			event->type = cmd_type[cmd >> 4];			event->port = port;			event->tick = tick;			event->data.d[0] = cmd & 0x0f;			event->data.d[1] = read_byte() & 0x7f;			event->data.d[2] = read_byte() & 0x7f;			break;		case 0xc: /* channel msg with 1 parameter byte */		case 0xd:			event = new_event(track, 0);			event->type = cmd_type[cmd >> 4];			event->port = port;			event->tick = tick;			event->data.d[0] = cmd & 0x0f;			event->data.d[1] = read_byte() & 0x7f;			break;		case 0xf:			switch (cmd) {			case 0xf0: /* sysex */			case 0xf7: /* continued sysex, or escaped commands */				len = read_var();				if (len < 0)					goto _error;				if (cmd == 0xf0)					++len;				event = new_event(track, len);				event->type = SND_SEQ_EVENT_SYSEX;				event->port = port;				event->tick = tick;				event->data.length = len;				if (cmd == 0xf0) {					event->sysex[0] = 0xf0;					c = 1;				} else {					c = 0;				}				for (; c < len; ++c)					event->sysex[c] = read_byte();				break;			case 0xff: /* meta event */				c = read_byte();				len = read_var();				if (len < 0)					goto _error;				switch (c) {				case 0x21: /* port number */					if (len < 1)						goto _error;					port = read_byte() % port_count;					skip(len - 1);					break;				case 0x2f: /* end of track */					track->end_tick = tick;					skip(track_end - file_offset);					return 1;				case 0x51: /* tempo */					if (len < 3)						goto _error;					if (smpte_timing) {						/* SMPTE timing doesn't change */						skip(len);					} else {						event = new_event(track, 0);						event->type = SND_SEQ_EVENT_TEMPO;						event->port = port;						event->tick = tick;						event->data.tempo = read_byte() << 16;						event->data.tempo |= read_byte() << 8;						event->data.tempo |= read_byte();						skip(len - 3);					}					break;				default: /* ignore all other meta events */					skip(len);					break;				}				break;			default: /* invalid Fx command */				goto _error;			}			break;		default: /* cannot happen */			goto _error;		}	}_error:	errormsg("%s: invalid MIDI data (offset %#x)", file_name, file_offset);	return 0;}/* reads an entire MIDI file */static int read_smf(void){	int header_len, type, time_division, i, err;	snd_seq_queue_tempo_t *queue_tempo;	/* the curren position is immediately after the "MThd" id */	header_len = read_int(4);	if (header_len < 6) {invalid_format:		errormsg("%s: invalid file format", file_name);		return 0;	}	type = read_int(2);	if (type != 0 && type != 1) {		errormsg("%s: type %d format is not supported", file_name, type);		return 0;	}

⌨️ 快捷键说明

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