📄 alsaseq_c.c
字号:
/* TiMidity++ -- MIDI to WAVE converter and player Copyright (C) 1999-2004 Masanao Izumo <iz@onicos.co.jp> Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi> 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 alsaseq_c.c - ALSA sequencer server interface Copyright (c) 2000 Takashi Iwai <tiwai@suse.de> This interface provides an ALSA sequencer client which receives events and plays it in real-time. On this mode, TiMidity works as a software (quasi-)real-time MIDI synth engine. See doc/C/README.alsaseq for more details.*/#ifdef HAVE_CONFIG_H#include "config.h"#endif /* HAVE_CONFIG_H */#include <stdio.h>#include <stdlib.h>#include <stdarg.h>#include <unistd.h>#include <sched.h>#include <sys/types.h>#include <sys/time.h>#ifndef NO_STRING_H#include <string.h>#else#include <strings.h>#endif#include <math.h>#include <signal.h>#if HAVE_ALSA_ASOUNDLIB_H#include <alsa/asoundlib.h>#else#include <sys/asoundlib.h>#endif#include "timidity.h"#include "common.h"#include "controls.h"#include "instrum.h"#include "playmidi.h"#include "readmidi.h"#include "recache.h"#include "output.h"#include "aq.h"#include "timer.h"void readmidi_read_init(void);#define MAX_PORTS 16#define TICKTIME_HZ 100struct seq_context { snd_seq_t *handle; /* The snd_seq handle to /dev/snd/seq */ int client; /* The client associated with this context */ int num_ports; /* number of ports */ int port[MAX_PORTS]; /* created sequencer ports */ int fd; /* The file descriptor */ int used; /* number of current connection */ int active; /* */ int queue; snd_seq_queue_status_t *q_status;};static struct seq_context alsactx;#if SND_LIB_MAJOR > 0 || SND_LIB_MINOR >= 6/* !! this is a dirty hack. not sure to work in future !! */static int snd_seq_file_descriptor(snd_seq_t *handle){ int pfds = snd_seq_poll_descriptors_count(handle, POLLIN); if (pfds > 0) { struct pollfd pfd; if (snd_seq_poll_descriptors(handle, &pfd, 1, POLLIN) >= 0) return pfd.fd; } return -ENXIO;}static int alsa_seq_open(snd_seq_t **seqp){ return snd_seq_open(seqp, "hw", SND_SEQ_OPEN_DUPLEX, 0);}static int alsa_create_port(snd_seq_t *seq, int index){ char name[32]; int port; sprintf(name, "TiMidity port %d", index); port = snd_seq_create_simple_port(seq, name, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_MIDI_GENERIC); if (port < 0) { fprintf(stderr, "error in snd_seq_create_simple_port\n"); return -1; } return port;}#elsestatic int alsa_seq_open(snd_seq_t **seqp){ return snd_seq_open(seqp, SND_SEQ_OPEN_IN);}static int alsa_create_port(snd_seq_t *seq, int index){ snd_seq_port_info_t pinfo; memset(&pinfo, 0, sizeof(pinfo)); sprintf(pinfo.name, "TiMidity port %d", index); pinfo.capability = SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE; pinfo.type = SND_SEQ_PORT_TYPE_MIDI_GENERIC; strcpy(pinfo.group, SND_SEQ_GROUP_DEVICE); if (snd_seq_create_port(alsactx.handle, &pinfo) < 0) { fprintf(stderr, "error in snd_seq_create_simple_port\n"); return -1; } return pinfo.port;}#endifstatic void alsa_set_timestamping(struct seq_context *ctxp, int port){#if HAVE_SND_SEQ_PORT_INFO_SET_TIMESTAMPING int q = 0; snd_seq_port_info_t *pinfo; if (ctxp->queue < 0) { q = snd_seq_alloc_queue(ctxp->handle); ctxp->queue = q; if (q < 0) return; if (snd_seq_queue_status_malloc(&ctxp->q_status) < 0) { fprintf(stderr, "no memory!\n"); exit(1); } } snd_seq_port_info_alloca(&pinfo); if (snd_seq_get_port_info(ctxp->handle, port, pinfo) < 0) return; snd_seq_port_info_set_timestamping(pinfo, 1); snd_seq_port_info_set_timestamp_real(pinfo, 1); snd_seq_port_info_set_timestamp_queue(pinfo, q); if (snd_seq_set_port_info(ctxp->handle, port, pinfo) < 0) return;#endif}static int ctl_open(int using_stdin, int using_stdout);static void ctl_close(void);static int ctl_read(int32 *valp);static int cmsg(int type, int verbosity_level, char *fmt, ...);static void ctl_event(CtlEvent *e);static void ctl_pass_playing_list(int n, char *args[]);/**********************************//* export the interface functions */#define ctl alsaseq_control_modeControlMode ctl={ "ALSA sequencer interface", 'A', 1,0,0, 0, ctl_open, ctl_close, ctl_pass_playing_list, ctl_read, cmsg, ctl_event};/* options */int opt_realtime_priority = 0;int opt_sequencer_ports = 4;static int buffer_time_advance;static long buffer_time_offset;static long start_time_base;static long cur_time_offset;static long last_queue_offset;static double rate_frac, rate_frac_nsec;static FILE *outfp;/*ARGSUSED*/static int ctl_open(int using_stdin, int using_stdout){ ctl.opened = 1; ctl.flags &= ~(CTLF_LIST_RANDOM|CTLF_LIST_SORT); if (using_stdout) outfp = stderr; else outfp = stdout; return 0;}static void ctl_close(void){ if (!ctl.opened) return;}static int ctl_read(int32 *valp){ return RC_NONE;}static int cmsg(int type, int verbosity_level, char *fmt, ...){ va_list ap; if((type==CMSG_TEXT || type==CMSG_INFO || type==CMSG_WARNING) && ctl.verbosity < verbosity_level) return 0; if(outfp == NULL) outfp = stderr; va_start(ap, fmt); vfprintf(outfp, fmt, ap); fputs(NLS, outfp); fflush(outfp); va_end(ap); return 0;}static void ctl_event(CtlEvent *e){}static RETSIGTYPE sig_timeout(int sig){ signal(SIGALRM, sig_timeout); /* For SysV base */ /* Expect EINTR */}static void doit(struct seq_context *ctxp);static int do_sequencer(struct seq_context *ctxp);static int start_sequencer(struct seq_context *ctxp);static void stop_sequencer(struct seq_context *ctxp);static void server_reset(void);/* reset all when SIGHUP is received */static RETSIGTYPE sig_reset(int sig){ if (alsactx.active) { stop_sequencer(&alsactx); server_reset(); } signal(SIGHUP, sig_reset);}/* * set the process to realtime privs */static int set_realtime_priority(void){ struct sched_param schp; int max_prio; if (opt_realtime_priority <= 0) return 0; memset(&schp, 0, sizeof(schp)); max_prio = sched_get_priority_max(SCHED_FIFO); if (max_prio < opt_realtime_priority) opt_realtime_priority = max_prio; schp.sched_priority = opt_realtime_priority; if (sched_setscheduler(0, SCHED_FIFO, &schp) != 0) { printf("can't set sched_setscheduler - using normal priority\n"); return -1; } /* drop root priv. */ if (! geteuid() && getuid() != geteuid()) { if (setuid(getuid())) perror("dropping root priv"); } printf("set SCHED_FIFO(%d)\n", opt_realtime_priority); return 0;}static void ctl_pass_playing_list(int n, char *args[]){ double btime; int i, j;#ifdef SIGPIPE signal(SIGPIPE, SIG_IGN); /* Handle broken pipe */#endif /* SIGPIPE */ printf("TiMidity starting in ALSA server mode\n"); set_realtime_priority(); if (alsa_seq_open(&alsactx.handle) < 0) { fprintf(stderr, "error in snd_seq_open\n"); return; } alsactx.queue = -1; alsactx.client = snd_seq_client_id(alsactx.handle); alsactx.fd = snd_seq_file_descriptor(alsactx.handle); snd_seq_set_client_name(alsactx.handle, "TiMidity"); snd_seq_set_client_pool_input(alsactx.handle, 1000); /* enough? */ if (opt_sequencer_ports < 1) alsactx.num_ports = 1; else if (opt_sequencer_ports > MAX_PORTS) alsactx.num_ports = MAX_PORTS; else alsactx.num_ports = opt_sequencer_ports; printf("Opening sequencer port:"); for (i = 0; i < alsactx.num_ports; i++) { int port; port = alsa_create_port(alsactx.handle, i); if (port < 0) return; alsactx.port[i] = port; alsa_set_timestamping(&alsactx, port); printf(" %d:%d", alsactx.client, alsactx.port[i]); } printf("\n"); alsactx.used = 0; alsactx.active = 0; opt_realtime_playing = 1; /* Enable loading patch while playing */ allocate_cache_size = 0; /* Don't use pre-calclated samples */ current_keysig = (opt_init_keysig == 8) ? 0 : opt_init_keysig; note_key_offset = key_adjust; if (IS_STREAM_TRACE) { /* set the audio queue size as minimum as possible, since * we don't have to use audio queue.. */ play_mode->acntl(PM_REQ_GETFRAGSIZ, &buffer_time_advance); if (!(play_mode->encoding & PE_MONO)) buffer_time_advance >>= 1; if (play_mode->encoding & PE_16BIT) buffer_time_advance >>= 1; btime = (double)buffer_time_advance / play_mode->rate; btime *= 1.01; /* to be sure */ aq_set_soft_queue(btime, 0.0); } else { buffer_time_advance = 0; } rate_frac = (double)play_mode->rate / 1000000.0; rate_frac_nsec = (double)play_mode->rate / 1000000000.0; alarm(0); signal(SIGALRM, sig_timeout); signal(SIGINT, safe_exit); signal(SIGTERM, safe_exit); signal(SIGHUP, sig_reset); i = current_keysig + ((current_keysig < 8) ? 7 : -9), j = 0; while (i != 7) i += (i < 7) ? 5 : -7, j++; j += note_key_offset, j -= floor(j / 12.0) * 12; current_freq_table = j; play_mode->close_output(); if (ctl.flags & CTLF_DAEMONIZE) { int pid = fork(); FILE *pidf; switch (pid) { case 0: // child is the daemon break; case -1: // error status return exit(7); default: // no error, doing well if ((pidf = fopen( "/var/run/timidity.pid", "w" )) != NULL )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -