📄 server_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 server_c.c - TiMidity server written by Masanao Izumo <iz@onicos.co.jp> Mon Apr 5 1999: Initial created. Launch TiMidity server: (example) % timidity -ir 7777 Protcol note: The protocol is based on OSS interface. TiMidity server has 2 TCP/IP connection. They are control port and data port. Control port: ASCII text protocol like FTP control port. See command_help for control protocol in this source code. Data port: One way Binary stream data from client to server. The format of stream is same as OSS sequencer stream. TODO: Protocol specification to be documented.*/#ifdef HAVE_CONFIG_H#include "config.h"#endif /* HAVE_CONFIG_H */#include <stdio.h>#include <stdlib.h>#include <stdarg.h>#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <sys/time.h>#include <netinet/in.h>#ifndef NO_STRING_H#include <string.h>#else#include <strings.h>#endif#include <signal.h>#ifdef HAVE_SYS_SOUNDCARD_H#include <sys/soundcard.h>#else#include "server_defs.h"#endif /* HAVE_SYS_SOUNDCARD_H */#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"/* #define DEBUG_DUMP_SEQ 1 */#define MIDI_COMMAND_PER_SEC 100#define DEFAULT_LOW_TIMEAT 0.4#define DEFAULT_HIGH_TIMEAT 0.6#define DONT_STOP_AUDIO 1#define DEFAULT_TIMEBASE 100 /* HZ? */#define MAXTICKDIFF 150#define SIG_TIMEOUT_SEC 3static int cmd_help(int argc, char **argv);static int cmd_open(int argc, char **argv);static int cmd_close(int argc, char **argv);static int cmd_timebase(int argc, char **argv);static int cmd_reset(int argc, char **argv);static int cmd_patch(int argc, char **argv);static int cmd_quit(int argc, char **argv);static int cmd_queue(int argc, char **argv);static int cmd_maxqueue(int argc, char **argv);static int cmd_time(int argc, char **argv);static int cmd_nop(int argc, char **argv);static int cmd_autoreduce(int argc, char **argv);static int cmd_setbuf(int argc, char **argv);struct{ char *cmd, *help; int minarg, maxarg; int (* proc)(int argc, char **argv); /* argv[0] is command name * argv[1..argc-1] is the arg. * return: 0=success, -1=fatal-error, * 1=connection-closed */} cmd_table[] ={ {"HELP", "HELP\tDisplay this message", 1, 1, cmd_help}, {"OPEN", "OPEN {lsb|msb}\n" "\tOpen data connection\n" "\tlsb: The byte order of data stream is LSB\n" "\tmsb: The byte order of data stream is MSB", 2, 2, cmd_open}, {"CLOSE", "CLOSE\tShutdown current data connection", 1, 1, cmd_close}, {"TIMEBASE", "TIMEBASE [timebase]\n\tSet time base", 1, 2, cmd_timebase}, {"RESET", "RESET\tInitialize all of MIDI status", 1, 1, cmd_reset}, {"PATCH", "PATCH {drumset|bank} <bank-no> <prog-no>\n\tLoad specified patch", 4, 4, cmd_patch}, {"QUIT", "QUIT\tClose control connection", 1, 1, cmd_quit}, {"QUEUE", "QUEUE\tTiMidity tells the time of audio buffer queue in second", 1, 1, cmd_queue}, {"MAXQUEUE", "MAXQUEUE\n" "\tTiMidity tells the maxmum time of audio buffer queue in second", 1, 1, cmd_maxqueue}, {"TIME", "TIME\tTiMidity tells the current playing time in second", 1, 1, cmd_time}, {"NOP", "NOP\tDo nothing", 1, 1, cmd_nop}, {"AUTOREDUCE", "AUTOREDUCE {on|off} [msec]\n\tEnable/Disable auto voice reduction", 2, 3, cmd_autoreduce}, {"SETBUF", "SETBUF low hi\n\tSpecify low/hi sec of buffer queue", 3, 3, cmd_setbuf}, {NULL, NULL, 0, 0, NULL} /* terminate */};/*TEMPO [<tempo>]\n\ Change the tempo. If the argument is omitted, TiMidity tells the\n\ current tempo.\n\KEYSHIFT <{+|-}offs>\n\ Change the base key. (0 to reset)\n\SPEED <{+|-}offs>\n\ Change the play speed. (0 to reset)\n\MODE {gs|xg|gm}\n\ Specify default MIDI system mode\n\SYNTH [gus|awe]\n\ Specify Synth type emulation. (no argument to default)\n\*/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 server_control_modeControlMode ctl={ "remote interface", 'r', 1,0,0, 0, ctl_open, ctl_close, ctl_pass_playing_list, ctl_read, cmsg, ctl_event};struct fd_read_buffer{ char buff[BUFSIZ]; /* count: beginning of read pointer * size: end of read pointer * fd: file descripter for input */ int count, size, fd;};static int fdgets(char *buff, size_t buff_size, struct fd_read_buffer *p);static int fdputs(char *s, int fd);static uint32 data2long(uint8* data);static uint16 data2short(uint8* data);static int do_control_command_nonblock(void);static struct fd_read_buffer control_fd_buffer;static int data_fd = -1, control_fd = -1;static int data_port, control_port;static int is_lsb_data = 1;static int curr_timebase = DEFAULT_TIMEBASE;static int32 sample_correction;static int32 sample_increment;static int32 sample_correction;static int32 sample_cum;static int32 curr_event_samples, event_time_offset;static int32 curr_tick, tick_offs;static double start_time;static int tmr_running;static int is_system_prefix = 0;static struct sockaddr_in control_client;static double low_time_at = 0.3;static double high_time_at = 0.5;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; if(data_fd != -1) { close(data_fd); data_fd = -1; } if(control_fd != -1) { close(control_fd); control_fd = -1; }}/*ARGSUSED*/static int ctl_read(int32 *valp){ if(data_fd != -1) do_control_command_nonblock(); 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 int pasv_open(int *port){ int sfd; struct sockaddr_in server; if((sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); return -1; } memset(&server, 0, sizeof(server)); server.sin_port = htons(*port); server.sin_family = AF_INET; server.sin_addr.s_addr = htonl(INADDR_ANY);#ifdef SO_REUSEADDR { int on = 1; setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (caddr_t)&on, sizeof(on)); }#endif /* SO_REUSEADDR */ ctl.cmsg(CMSG_INFO, VERB_DEBUG, "Bind TCP/IP port=%d", *port); if(bind(sfd, (struct sockaddr *)&server, sizeof(server)) < 0) { perror("bind"); close(sfd); return -1; } if(*port == 0) { int len = sizeof(server); if(getsockname(sfd, (struct sockaddr *)&server, &len) < 0) { perror("getsockname"); close(sfd); return -1; } *port = ntohs(server.sin_port); } /* Set it up to wait for connections. */ if(listen(sfd, 1) < 0) { perror("listen"); close(sfd); return -1; } return sfd;}static RETSIGTYPE sig_timeout(int sig){ signal(SIGALRM, sig_timeout); /* For SysV base */ /* Expect EINTR */}static void doit(void);static int send_status(int status, char *message, ...);static void compute_sample_increment(void);static void server_reset(void);static void ctl_pass_playing_list(int n, char *args[]){ int sock; if(n != 2 && n != 1) { fprintf(stderr, "Usage: timidity -ir control-port [data-port]\n"); return; }#ifdef SIGPIPE signal(SIGPIPE, SIG_IGN); /* Handle broken pipe */#endif /* SIGPIPE */ control_port = atoi(args[0]); if(n == 2) data_port = atoi(args[1]); else data_port = 0; if (control_port) { sock = pasv_open(&control_port); if(sock == -1) return; } opt_realtime_playing = 1; /* Enable loading patch while playing */ allocate_cache_size = 0; /* Don't use pre-calclated samples *//* aq_set_soft_queue(-1.0, 0.0); */ alarm(0); signal(SIGALRM, sig_timeout); play_mode->close_output(); while(1) { int addrlen; addrlen = sizeof(control_client); memset(&control_client, 0, addrlen); if (control_port) { if((control_fd = accept(sock, (struct sockaddr *)&control_client, &addrlen)) < 0) { if(errno == EINTR) continue; perror("accept"); close(sock); return; } } else control_fd = 0; if(play_mode->open_output() < 0) { ctl.cmsg(CMSG_FATAL, VERB_NORMAL, "Couldn't open %s (`%c')", play_mode->id_name, play_mode->id_character); send_status(510, "Couldn't open %s (`%c')", play_mode->id_name, play_mode->id_character); if (control_port) { close(control_fd); control_fd = 1; } continue; } server_reset(); ctl.cmsg(CMSG_INFO, VERB_NOISY, "Connected"); doit(); ctl.cmsg(CMSG_INFO, VERB_NOISY, "Connection closed"); play_mode->close_output(); if(control_fd != -1 && control_port) { close(control_fd); control_fd = -1; } if(data_fd != -1) { close(data_fd); data_fd = -1; } free_instruments(0); free_global_mblock(); if (!control_port) break; }}#define MAX_GETCMD_PARAMS 8/* return: * 0: success * 1: error *-1: fatal error (will be close the connection) */static int control_getcmd(char **params, int *nparams){ static char buff[BUFSIZ]; int n; /* read line */ n = fdgets(buff, sizeof(buff), &control_fd_buffer); if(n == -1) { perror("read"); return -1; } if(n == 0) return 1; if((params[0] = strtok(buff, " \t\r\n\240")) == NULL) return 0; *nparams = 0; while(params[*nparams] && *nparams < MAX_GETCMD_PARAMS) params[++(*nparams)] = strtok(NULL," \t\r\n\240"); return 0;}static int send_status(int status, char *message, ...){ va_list ap; char buff[BUFSIZ]; va_start(ap, message); sprintf(buff, "%03d ", status); vsnprintf(buff + 4, sizeof(buff) - 5, message, ap); va_end(ap); strncat(buff, "\n", BUFSIZ - strlen(buff) - 1); buff[BUFSIZ-1] = '\0'; /* force terminate */ if(write(control_fd, buff, strlen(buff)) == -1) return -1; return 0;}static void seq_play_event(MidiEvent *ev){ if(tmr_running) ev->time = curr_event_samples; else { if(IS_STREAM_TRACE) { event_time_offset += play_mode->rate / MIDI_COMMAND_PER_SEC; ev->time = curr_event_samples; } else { double past_time = get_current_calender_time() - start_time; if(play_mode->flag & PF_PCM_STREAM) past_time += high_time_at; ev->time = (int32)(past_time * play_mode->rate); } } ev->time += event_time_offset; play_event(ev);}static void tmr_reset(void){ curr_event_samples = event_time_offset = sample_cum = 0; playmidi_tmr_reset(); curr_timebase = DEFAULT_TIMEBASE; curr_tick = tick_offs = 0; start_time = get_current_calender_time();}static void compute_sample_increment(void){ double a; a = (double)current_play_tempo * (double)play_mode->rate * (65536.0/500000.0) / (double)curr_timebase, sample_correction = (int32)(a) & 0xFFFF;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -