📄 tk_c.c
字号:
/* TiMidity++ -- MIDI to WAVE converter and player Copyright (C) 1999-2002 Masanao Izumo <mo@goice.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*//*================================================================ * * The Tcl/Tk interface for Timidity * written by Takashi Iwai (iwai@dragon.mm.t.u-tokyo.ac.jp) * * Most of the following codes are derived from both motif_ctl.c * and motif_pipe.c. The communication between Tk program and * timidity is established by a pipe stream as in Motif interface. * On the contrast to motif, the stdin and stdout are assigned * as pipe i/o in Tk interface. * *================================================================*/#ifdef HAVE_CONFIG_H#include "config.h"#endif /* HAVE_CONFIG_H */#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <stdarg.h>#ifndef NO_STRING_H#include <string.h>#else#include <strings.h>#endif#include <sys/types.h>#include <sys/ioctl.h>#include <signal.h>#include <sys/ipc.h>#include <sys/shm.h>#include <sys/sem.h>#include <tcl.h>#include <tk.h>#include <sys/wait.h>#include "timidity.h"#include "common.h"#include "instrum.h"#include "playmidi.h"#include "readmidi.h"#include "output.h"#include "controls.h"#include "miditrace.h"#include "aq.h"#ifndef TKPROGPATH#define TKPROGPATH PKGLIBDIR "/tkmidity.tcl"#endif /* TKPROGPATH */static void ctl_refresh(void);static void ctl_total_time(int tt);static void ctl_master_volume(int mv);static void ctl_file_name(char *name);static void ctl_current_time(int secs, int v);static void ctl_program(int ch, int val);static void ctl_volume(int channel, int val);static void ctl_expression(int channel, int val);static void ctl_panning(int channel, int val);static void ctl_sustain(int channel, int val);static void ctl_pitch_bend(int channel, int val);static void ctl_lyric(int lyricid);static void ctl_reset(void);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_pass_playing_list(int number_of_files, char *list_of_files[]);static int ctl_blocking_read(int32 *valp);static void ctl_note(int status, int ch, int note, int vel);static void ctl_event(CtlEvent *e);static void k_pipe_printf(char *fmt, ...);static void k_pipe_puts(char *str);static int k_pipe_gets(char *str, int maxlen);static void k_pipe_open(void);static void k_pipe_error(char *st);static int k_pipe_read_ready(void);static int AppInit(Tcl_Interp *interp);static int ExitAll(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]);static int TraceCreate(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]);static int TraceUpdate(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]);static int TraceReset(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]);static void trace_volume(int ch, int val);static void trace_panning(int ch, int val);static void trace_prog_init(int ch);static void trace_prog(int ch, int val);static void trace_bank(int ch, int val);static void trace_sustain(int ch, int val);static void get_child(int sig);static void shm_alloc(void);static void shm_free(int sig);static void start_panel(void);#define MAX_TK_MIDI_CHANNELS 16typedef struct { int reset_panel; int multi_part; int32 last_time, cur_time; char v_flags[MAX_TK_MIDI_CHANNELS]; int16 cnote[MAX_TK_MIDI_CHANNELS]; int16 cvel[MAX_TK_MIDI_CHANNELS]; int16 ctotal[MAX_TK_MIDI_CHANNELS]; char c_flags[MAX_TK_MIDI_CHANNELS]; Channel channel[MAX_TK_MIDI_CHANNELS]; int wait_reset;} PanelInfo;/**********************************************/#define ctl tk_control_modeControlMode ctl={ "Tcl/Tk interface", 'k', 1,0,0, 0, ctl_open, ctl_close, ctl_pass_playing_list, ctl_read, cmsg, ctl_event};#define FLAG_NOTE_OFF 1#define FLAG_NOTE_ON 2#define FLAG_BANK 1#define FLAG_PROG 2#define FLAG_PAN 4#define FLAG_SUST 8static VOLATILE PanelInfo *Panel;static VOLATILE int child_killed = 0;static int shmid; /* shared memory id */static int semid; /* semaphore id */static int pipeAppli[2],pipePanel[2]; /* Pipe for communication with Tcl/Tk process */static int fpip_in, fpip_out; /* in and out depends in which process we are */static int child_pid; /* Pid for child process *//***********************************************************************//* semaphore utilities *//***********************************************************************/static void semaphore_P(int sid){ struct sembuf sb; sb.sem_num = 0; sb.sem_op = -1; sb.sem_flg = 0; if(semop(sid, &sb, 1) == -1) perror("semop");}static void semaphore_V(int sid){ struct sembuf sb; sb.sem_num = 0; sb.sem_op = 1; sb.sem_flg = 0; if(semop(sid, &sb, 1) == -1) perror("semop");}/***********************************************************************//* Put controls on the pipe *//***********************************************************************/static int cmsg(int type, int verbosity_level, char *fmt, ...){ char local[2048];#define TOO_LONG 2000 va_list ap; if ((type==CMSG_TEXT || type==CMSG_INFO || type==CMSG_WARNING) && ctl.verbosity<verbosity_level) return 0; va_start(ap, fmt); if (strlen(fmt) > TOO_LONG) fmt[TOO_LONG] = 0; if (!ctl.opened) { vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); } else if (type == CMSG_ERROR) { int32 val; vsnprintf(local, sizeof(local), fmt, ap); k_pipe_printf("CERR %d", type); k_pipe_puts(local); while (ctl_blocking_read(&val) != RC_NEXT) ; } else { vsnprintf(local, sizeof(local), fmt, ap); k_pipe_printf("CMSG %d", type); k_pipe_puts(local); } va_end(ap); return 0;}static void ctl_refresh(void){}static void ctl_total_time(int tt){ int centisecs=tt/(play_mode->rate/100); k_pipe_printf("TIME %d", centisecs); ctl_current_time(0, 0);}static void ctl_master_volume(int mv){ k_pipe_printf("MVOL %d", mv);}static void ctl_file_name(char *name){ k_pipe_printf("FILE %s", name);}static void ctl_current_time(int secs, int v){ Panel->cur_time = secs;}static void ctl_channel_note(int ch, int note, int vel){ if (vel == 0) { if (note == Panel->cnote[ch]) Panel->v_flags[ch] = FLAG_NOTE_OFF; Panel->cvel[ch] = 0; } else if (vel > Panel->cvel[ch]) { Panel->cvel[ch] = vel; Panel->cnote[ch] = note; Panel->ctotal[ch] = vel * Panel->channel[ch].volume * Panel->channel[ch].expression / (127*127); Panel->v_flags[ch] = FLAG_NOTE_ON; }}static void ctl_note(int status, int ch, int note, int vel){ if (!ctl.trace_playing) return; if (ch < 0 || ch >= MAX_TK_MIDI_CHANNELS) return; if (status != VOICE_ON) vel = 0; semaphore_P(semid); ctl_channel_note(ch, note, vel); semaphore_V(semid);}static void ctl_program(int ch, int val){ if(ch >= MAX_TK_MIDI_CHANNELS) return; if (!ctl.trace_playing) return; if (ch < 0 || ch >= MAX_TK_MIDI_CHANNELS) return; if(channel[ch].special_sample) val = channel[ch].special_sample; else val += progbase; semaphore_P(semid); Panel->channel[ch].program = val; Panel->c_flags[ch] |= FLAG_PROG; semaphore_V(semid);}static void ctl_volume(int ch, int val){ if(ch >= MAX_TK_MIDI_CHANNELS) return; if (!ctl.trace_playing) return; semaphore_P(semid); Panel->channel[ch].volume = val; ctl_channel_note(ch, Panel->cnote[ch], Panel->cvel[ch]); semaphore_V(semid);}static void ctl_expression(int ch, int val){ if(ch >= MAX_TK_MIDI_CHANNELS) return; if (!ctl.trace_playing) return; semaphore_P(semid); Panel->channel[ch].expression = val; ctl_channel_note(ch, Panel->cnote[ch], Panel->cvel[ch]); semaphore_V(semid);}static void ctl_panning(int ch, int val){ if(ch >= MAX_TK_MIDI_CHANNELS) return; if (!ctl.trace_playing) return; semaphore_P(semid); Panel->channel[ch].panning = val; Panel->c_flags[ch] |= FLAG_PAN; semaphore_V(semid);}static void ctl_sustain(int ch, int val){ if(ch >= MAX_TK_MIDI_CHANNELS) return; if (!ctl.trace_playing) return; semaphore_P(semid); Panel->channel[ch].sustain = val; Panel->c_flags[ch] |= FLAG_SUST; semaphore_V(semid);}/*ARGSUSED*/static void ctl_pitch_bend(int channel, int val){/* if(ch >= MAX_TK_MIDI_CHANNELS) return; if (!ctl.trace_playing) return; semaphore_P(semid); Panel->channel[ch].pitch_bend = val; Panel->c_flags[ch] |= FLAG_BENDT; semaphore_V(semid);*/}static void ctl_lyric(int lyricid){ char *lyric; lyric = event2string(lyricid); if(lyric != NULL) { if(lyric[0] == ME_KARAOKE_LYRIC) { if(lyric[1] == '/' || lyric[1] == '\\') { k_pipe_printf("CMSG %d", CMSG_TEXT); k_pipe_puts(""); k_pipe_printf("LYRC %d", CMSG_TEXT); k_pipe_printf("%s", lyric + 2); } else if(lyric[1] == '@') { if(lyric[2] == 'L') { k_pipe_printf("CMSG %d", CMSG_TEXT); k_pipe_printf("Language: %s", lyric + 3); } else if(lyric[2] == 'T') { k_pipe_printf("CMSG %d", CMSG_TEXT); k_pipe_printf("Title: %s", lyric + 3); } else { k_pipe_printf("CMSG %d", CMSG_TEXT); k_pipe_printf("%s", lyric + 1); } } else { k_pipe_printf("LYRC %d", CMSG_TEXT); k_pipe_printf("%s", lyric + 1); } } else { k_pipe_printf("CMSG %d", CMSG_TEXT); k_pipe_printf("%s", lyric + 1); } }}static void ctl_reset(void){ int i; if (!ctl.trace_playing) { k_pipe_printf("RSET %d", ctl.trace_playing); return; } Panel->wait_reset = 1; k_pipe_printf("RSET %d", ctl.trace_playing); while(Panel->wait_reset) VOLATILE_TOUCH(Panel->wait_reset); if (!ctl.trace_playing) return; for (i = 0; i < MAX_TK_MIDI_CHANNELS; i++) { if(ISDRUMCHANNEL(i)) ctl_program(i, channel[i].bank); else ctl_program(i, channel[i].program); ctl_volume(i, channel[i].volume); ctl_expression(i, channel[i].expression); ctl_panning(i, channel[i].panning); ctl_sustain(i, channel[i].sustain); if(channel[i].pitchbend == 0x2000 && channel[i].mod.val > 0) ctl_pitch_bend(i, -1); else ctl_pitch_bend(i, channel[i].pitchbend); ctl_channel_note(i, Panel->cnote[i], 0); }}/***********************************************************************//* OPEN THE CONNECTION *//***********************************************************************//*ARGSUSED*/static int ctl_open(int using_stdin, int using_stdout){ shm_alloc(); k_pipe_open(); if (child_pid == 0) start_panel(); signal(SIGCHLD, get_child); signal(SIGTERM, shm_free); signal(SIGINT, shm_free); signal(SIGHUP, shm_free); ctl.opened=1; return 0;}/* Tells the window to disapear */static void ctl_close(void){ if (ctl.opened) { kill(child_pid, SIGTERM); shm_free(100); ctl.opened=0; }}/* * Read information coming from the window in a BLOCKING way *//* commands are: PREV, NEXT, QUIT, STOP, LOAD, JUMP, VOLM */static int ctl_blocking_read(int32 *valp){ char buf[8192], *tok, *arg; int new_volume; int new_centiseconds; char *args[64], **files; int i=0, nfiles; k_pipe_gets(buf, sizeof(buf)-1); tok = strtok(buf, " "); for(;;)/* Loop after pause sleeping to treat other buttons! */ { switch (*tok) { case 'V': if ((arg = strtok(NULL, " ")) != NULL) { new_volume = atoi(arg); *valp= new_volume - amplification ; return RC_CHANGE_VOLUME; } return RC_NONE; case 'J': if ((arg = strtok(NULL, " ")) != NULL) { new_centiseconds = atoi(arg); *valp= new_centiseconds*(play_mode->rate / 100) ; return RC_JUMP; } return RC_NONE; case 'Q': return RC_QUIT; case 'L': return RC_LOAD_FILE; case 'N': return RC_NEXT; case 'P': /*return RC_REALLY_PREVIOUS;*/ return RC_PREVIOUS; case 'R': return RC_RESTART; case 'F': *valp=play_mode->rate; return RC_FORWARD; case 'B': *valp=play_mode->rate; return RC_BACK; case 'X': k_pipe_gets(buf, sizeof(buf)-1); args[i++] = strtok(buf, " "); while ((args[i++] = strtok(NULL, " ")) != NULL); nfiles = --i; files = expand_file_archives(args, &nfiles); k_pipe_printf("ALST %d", nfiles); for (i=0;i<nfiles;i++) k_pipe_puts(files[i]); if(files != args) free(files); return RC_NONE; case 'S': return RC_TOGGLE_PAUSE; default: fprintf(stderr,"UNKNOWN RC_MESSAGE `%s'\n", tok); return RC_NONE; } }}/* * Read information coming from the window in a non blocking way */static int ctl_read(int32 *valp){ int num; /* We don't wan't to lock on reading */ num=k_pipe_read_ready(); if (num==0) return RC_NONE; return(ctl_blocking_read(valp));}static void ctl_pass_playing_list(int number_of_files, char *list_of_files[]){ int i=0; char local[1000]; int command; int32 val; /* Pass the list to the interface */ k_pipe_printf("LIST %d", number_of_files); for (i=0;i<number_of_files;i++) k_pipe_puts(list_of_files[i]); /* Ask the interface for a filename to play -> begin to play automatically */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -