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

📄 arecordmidi.c

📁 ALSA驱动的一些调试测试工具
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * arecordmidi.c - record standard MIDI files from sequencer ports * * Copyright (c) 2004-2005 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 <signal.h>#include <getopt.h>#include <sys/poll.h>#include <alsa/asoundlib.h>#include "aconfig.h"#include "version.h"#define BUFFER_SIZE 4088/* linked list of buffers, stores data as in the .mid file */struct buffer {	struct buffer *next;	unsigned char buf[BUFFER_SIZE];};struct smf_track {	int size;			/* size of entire data */	int cur_buf_size;		/* size of cur_buf */	struct buffer *cur_buf;	snd_seq_tick_time_t last_tick;	/* end of track */	unsigned char last_command;	/* used for running status */	int used;			/* anything record on this track */	struct buffer first_buf;	/* list head */};/* timing/sysex + 16 channels */#define TRACKS_PER_PORT 17/* metronome settings *//* TODO: create options for this */#define METRONOME_CHANNEL 9#define METRONOME_STRONG_NOTE 34#define METRONOME_WEAK_NOTE 33#define METRONOME_VELOCITY 100#define METRONOME_PROGRAM 0static snd_seq_t *seq;static int client;static int port_count;static snd_seq_addr_t *ports;static int queue;static int smpte_timing = 0;static int beats = 120;static int frames;static int ticks = 0;static FILE *file;static int channel_split;static int num_tracks;static struct smf_track *tracks;static volatile sig_atomic_t stop = 0;static int use_metronome = 0;static snd_seq_addr_t metronome_port;static int metronome_weak_note = METRONOME_WEAK_NOTE;static int metronome_strong_note = METRONOME_STRONG_NOTE;static int metronome_velocity = METRONOME_VELOCITY;static int metronome_program = METRONOME_PROGRAM;static int metronome_channel = METRONOME_CHANNEL;static int ts_num = 4; /* time signature: numerator */static int ts_div = 4; /* time signature: denominator */static int ts_dd = 2; /* time signature: denominator as a power of two *//* 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);	/* find out our client's id */	client = snd_seq_client_id(seq);	check_snd("get client id", client);	/* set our client's name */	err = snd_seq_set_client_name(seq, "arecordmidi");	check_snd("set client name", err);}/* 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);}/* parses the metronome port address */static void init_metronome(const char *arg){	int err;	err = snd_seq_parse_address(seq, &metronome_port, arg);	if (err < 0)		fatal("Invalid port %s - %s", arg, snd_strerror(err));	use_metronome = 1;}/* parses time signature specification */static void time_signature(const char *arg){	long x = 0;	char *sep;	x = strtol(arg, &sep, 10);	if (x < 1 || x > 64 || *sep != ':')		fatal("Invalid time signature (%s)", arg);	ts_num = x;	x = strtol(++sep, NULL, 10);	if (x < 1 || x > 64)		fatal("Invalid time signature (%s)", arg);	ts_div = x;	for (ts_dd = 0; x > 1; x /= 2)		++ts_dd;}/* * Metronome implementation */static void metronome_note(unsigned char note, unsigned int tick){	snd_seq_event_t ev;	snd_seq_ev_clear(&ev);	snd_seq_ev_set_note(&ev, metronome_channel, note, metronome_velocity, 1);	snd_seq_ev_schedule_tick(&ev, queue, 0, tick);	snd_seq_ev_set_source(&ev, port_count);	snd_seq_ev_set_subs(&ev);	snd_seq_event_output(seq, &ev);}static void metronome_echo(unsigned int tick){	snd_seq_event_t ev;	snd_seq_ev_clear(&ev);	ev.type = SND_SEQ_EVENT_USR0;	snd_seq_ev_schedule_tick(&ev, queue, 0, tick);	snd_seq_ev_set_source(&ev, port_count);	snd_seq_ev_set_dest(&ev, client, port_count);	snd_seq_event_output(seq, &ev);}static void metronome_pattern(unsigned int tick){	int j, t, duration;	t = tick;	duration = ticks * 4 / ts_div;	for (j = 0; j < ts_num; j++) {		metronome_note(j ? metronome_weak_note : metronome_strong_note, t);		t += duration;	}	metronome_echo(t);	snd_seq_drain_output(seq);}static void metronome_set_program(void){	snd_seq_event_t ev;	snd_seq_ev_clear(&ev);	snd_seq_ev_set_pgmchange(&ev, metronome_channel, metronome_program);	snd_seq_ev_set_source(&ev, port_count);	snd_seq_ev_set_subs(&ev);	snd_seq_event_output(seq, &ev);}static void init_tracks(void){	int i;	/* MIDI RP-019 says we need at least one track per port */	num_tracks = port_count;	/* Allocate one track for each possible channel.	 * Empty tracks won't be written to the file. */	if (channel_split)		num_tracks *= TRACKS_PER_PORT;	tracks = calloc(num_tracks, sizeof(struct smf_track));	check_mem(tracks);	for (i = 0; i < num_tracks; ++i)		tracks[i].cur_buf = &tracks[i].first_buf;}static void create_queue(void){	snd_seq_queue_tempo_t *tempo;	int err;	queue = snd_seq_alloc_named_queue(seq, "arecordmidi");	check_snd("create queue", queue);	snd_seq_queue_tempo_alloca(&tempo);	if (!smpte_timing) {		snd_seq_queue_tempo_set_tempo(tempo, 60000000 / beats);		snd_seq_queue_tempo_set_ppq(tempo, ticks);	} else {		/*		 * ALSA doesn't know about the SMPTE time divisions, so		 * we pretend to have a musical tempo with the equivalent		 * number of ticks/s.		 */		switch (frames) {		case 24:			snd_seq_queue_tempo_set_tempo(tempo, 500000);			snd_seq_queue_tempo_set_ppq(tempo, 12 * ticks);			break;		case 25:			snd_seq_queue_tempo_set_tempo(tempo, 400000);			snd_seq_queue_tempo_set_ppq(tempo, 10 * ticks);			break;		case 29:			snd_seq_queue_tempo_set_tempo(tempo, 100000000);			snd_seq_queue_tempo_set_ppq(tempo, 2997 * ticks);			break;		case 30:			snd_seq_queue_tempo_set_tempo(tempo, 500000);			snd_seq_queue_tempo_set_ppq(tempo, 15 * ticks);			break;		default:			fatal("Invalid SMPTE frames %d", frames);		}	}	err = snd_seq_set_queue_tempo(seq, queue, tempo);	if (err < 0)		fatal("Cannot set queue tempo (%u/%i)",		      snd_seq_queue_tempo_get_tempo(tempo),		      snd_seq_queue_tempo_get_ppq(tempo));}static void create_ports(void){	snd_seq_port_info_t *pinfo;	int i, err;	char name[32];	snd_seq_port_info_alloca(&pinfo);	/* common information for all our ports */	snd_seq_port_info_set_capability(pinfo,					 SND_SEQ_PORT_CAP_WRITE |					 SND_SEQ_PORT_CAP_SUBS_WRITE);	snd_seq_port_info_set_type(pinfo,				   SND_SEQ_PORT_TYPE_MIDI_GENERIC |				   SND_SEQ_PORT_TYPE_APPLICATION);	snd_seq_port_info_set_midi_channels(pinfo, 16);	/* we want to know when the events got delivered to us */	snd_seq_port_info_set_timestamping(pinfo, 1);	snd_seq_port_info_set_timestamp_queue(pinfo, queue);	/* our port number is the same as our port index */	snd_seq_port_info_set_port_specified(pinfo, 1);	for (i = 0; i < port_count; ++i) {		snd_seq_port_info_set_port(pinfo, i);		sprintf(name, "arecordmidi port %i", i);		snd_seq_port_info_set_name(pinfo, name);		err = snd_seq_create_port(seq, pinfo);		check_snd("create port", err);	}	/* create an optional metronome port */	if (use_metronome) {		snd_seq_port_info_set_port(pinfo, port_count);		snd_seq_port_info_set_name(pinfo, "arecordmidi metronome");		snd_seq_port_info_set_capability(pinfo,						 SND_SEQ_PORT_CAP_READ |						 SND_SEQ_PORT_CAP_WRITE);		snd_seq_port_info_set_type(pinfo, SND_SEQ_PORT_TYPE_APPLICATION);		snd_seq_port_info_set_midi_channels(pinfo, 0);		snd_seq_port_info_set_timestamping(pinfo, 0);		err = snd_seq_create_port(seq, pinfo);		check_snd("create metronome port", err);	}}static void connect_ports(void){	int i, err;	for (i = 0; i < port_count; ++i) {		err = snd_seq_connect_from(seq, i, ports[i].client, ports[i].port);		if (err < 0)			fatal("Cannot connect from port %d:%d - %s",			      ports[i].client, ports[i].port, snd_strerror(err));	}	/* subscribe the metronome port */	if (use_metronome) {	        err = snd_seq_connect_to(seq, port_count, metronome_port.client, metronome_port.port);		if (err < 0)	    		fatal("Cannot connect to port %d:%d - %s",			      metronome_port.client, metronome_port.port, snd_strerror(err));	}}/* records a byte to be written to the .mid file */static void add_byte(struct smf_track *track, unsigned char byte){	/* make sure we have enough room in the current buffer */	if (track->cur_buf_size >= BUFFER_SIZE) {		track->cur_buf->next = calloc(1, sizeof(struct buffer));		if (!track->cur_buf->next)			fatal("out of memory");		track->cur_buf = track->cur_buf->next;		track->cur_buf_size = 0;	}	track->cur_buf->buf[track->cur_buf_size++] = byte;	track->size++;}/* record a variable-length quantity */static void var_value(struct smf_track *track, int v){	if (v >= (1 << 28))		add_byte(track, 0x80 | ((v >> 28) & 0x03));	if (v >= (1 << 21))		add_byte(track, 0x80 | ((v >> 21) & 0x7f));	if (v >= (1 << 14))		add_byte(track, 0x80 | ((v >> 14) & 0x7f));	if (v >= (1 << 7))		add_byte(track, 0x80 | ((v >> 7) & 0x7f));	add_byte(track, v & 0x7f);}/* record the delta time from the last event */static void delta_time(struct smf_track *track, const snd_seq_event_t *ev){	int diff = ev->time.tick - track->last_tick;	if (diff < 0)		diff = 0;	var_value(track, diff);	track->last_tick = ev->time.tick;}/* record a status byte (or not if we can use running status) */static void command(struct smf_track *track, unsigned char cmd){	if (cmd != track->last_command)		add_byte(track, cmd);	track->last_command = cmd < 0xf0 ? cmd : 0;}/* put port numbers into all tracks */static void record_port_numbers(void){	int i;	for (i = 0; i < num_tracks; ++i) {		var_value(&tracks[i], 0);		add_byte(&tracks[i], 0xff);		add_byte(&tracks[i], 0x21);		var_value(&tracks[i], 1);		if (channel_split)			add_byte(&tracks[i], i / TRACKS_PER_PORT);		else			add_byte(&tracks[i], i);	}}static void record_event(const snd_seq_event_t *ev){	unsigned int i;	struct smf_track *track;

⌨️ 快捷键说明

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