pkg://awemidi-0.4.2b-1.src.rpm:98839/awemidi-0.4.2b.tgz
info downloads
awemidi-0.4.2b/ 40755 765 12 0 6416450043 11456 5 ustar iwai wheel awemidi-0.4.2b/atmidi.c 100644 765 12 32275 6410033215 13207 0 ustar iwai wheel /*================================================================
* Simple Athena widget control program
*
* Copyright (C) 1996,1997 Takashi Iwai
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*================================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/AsciiText.h>
#include "config.h"
#include "util.h"
#include "midievent.h"
#include "channel.h"
#include "controls.h"
#include "ext_c.h"
#include "BITMAPS/play.xbm"
#include "BITMAPS/pause.xbm"
#include "BITMAPS/prev.xbm"
#include "BITMAPS/back.xbm"
#include "BITMAPS/stop.xbm"
#include "BITMAPS/fwrd.xbm"
#include "BITMAPS/next.xbm"
#include "BITMAPS/quit.xbm"
/*----------------------------------------------------------------*/
static XtAppContext apps;
static Widget toplevel, topform, title, buttons;
static Widget title_index, title_name, title_time;
static Widget btn_play, btn_pause, btn_prev, btn_back, btn_stop, btn_fwrd, btn_next, btn_quit;
static Widget textwin;
/*----------------------------------------------------------------*/
static int number_of_files;
static char **filename;
static int curfile;
static int total_time, cur_time;
static int curstate;
enum { STAT_CLEAN, STAT_PLAYING, STAT_PAUSE };
static FILE *p_input;
/*----------------------------------------------------------------*/
static void create_window(Widget parent);
static Pixmap MakeBitmap(char *bits, int width, int height);
static void PlayCB(void);
static void NextCB(void);
static void PrevCB(void);
static void FwrdCB(void);
static void BackCB(void);
static void StopCB(void);
static void QuitCB(void);
static void PauseCB(void);
static void InputCB(void);
static void PauseCB(void);
static void TimerCB(void);
static void init_line(void);
static void append_line(void);
static void popup_msg(void);
static void do_play(void);
static void set_index(void);
static void set_title(char *title);
static void set_curtime(int sec);
static void clear_status(void);
static void playing_status(void);
static void paused_status(void);
static void exit_all(void);
/*----------------------------------------------------------------*/
static XtActionsRec actions[] = {
"play", (XtActionProc)PlayCB,
"stop", (XtActionProc)StopCB,
"pause", (XtActionProc)PauseCB,
"next", (XtActionProc)NextCB,
"prev", (XtActionProc)PrevCB,
"forward", (XtActionProc)FwrdCB,
"backward", (XtActionProc)BackCB,
"quit", (XtActionProc)QuitCB,
};
/*----------------------------------------------------------------*/
#define DELTA_TIME 50
void main(int argc, char **argv)
{
toplevel = XtAppInitialize(&apps, "Atmidi", NULL, 0, &argc, argv,
NULL, NULL, 0);
if (argc < 2) {
fprintf(stderr, "no midi file is given\n");
exit(1);
}
number_of_files = argc - 1;
filename = argv + 1;
curfile = -1;
if (! ext_control_init(TRUE, TRUE, TRUE, FALSE, 0, NULL))
exit(0);
p_input = fdopen(pipe_in, "r");
setvbuf(p_input, NULL, _IONBF, 0);
create_window(toplevel);
XtAppAddActions(apps, actions, XtNumber(actions));
XtAppAddInput(apps, pipe_in, (XtPointer)XtInputReadMask,
(XtInputCallbackProc)InputCB, NULL);
XtRealizeWidget(toplevel);
XtAppAddTimeOut(apps, DELTA_TIME, (XtTimerCallbackProc)TimerCB, NULL);
/* start automatically */
PlayCB();
XtAppMainLoop(apps);
exit_all();
}
static void exit_all(void)
{
ext_control_end();
exit(0);
}
/*----------------------------------------------------------------
* create widgets
*----------------------------------------------------------------*/
#define CREATE_BUTTON(name,parent,left,bitmap) CreateButton(name, parent, left, MakeBitmap(bitmap##_bits, bitmap##_width, bitmap##_height))
static Widget CreateButton(char *name, Widget parent, Widget left, Pixmap bitmap)
{
return XtVaCreateManagedWidget(name, commandWidgetClass, parent,
XtNjustify, XtJustifyLeft,
XtNresize, False,
XtNfromHoriz, left,
XtNbitmap, bitmap,
NULL);
}
static void create_window(Widget parent)
{
topform = XtVaCreateManagedWidget
("topform", formWidgetClass, parent,
NULL);
title = XtVaCreateManagedWidget
("title", formWidgetClass, topform,
XtNleft, XawChainLeft,
XtNright, XawChainRight,
XtNtop, XawChainTop,
NULL);
title_index = XtVaCreateManagedWidget
("name", labelWidgetClass, title,
XtNlabel, "000/000",
XtNtop, XawChainTop,
XtNbottom, XawChainBottom,
XtNleft, XawChainLeft,
NULL);
title_name = XtVaCreateManagedWidget
("name", labelWidgetClass, title,
XtNtop, XawChainTop,
XtNbottom, XawChainBottom,
XtNfromHoriz, title_index,
NULL);
title_time = XtVaCreateManagedWidget
("time", labelWidgetClass, title,
XtNwidth, 64,
XtNtop, XawChainTop,
XtNbottom, XawChainBottom,
XtNfromHoriz, title_name,
XtNright, XawChainRight,
NULL);
buttons = XtVaCreateManagedWidget
("buttons", formWidgetClass, topform,
XtNleft, XawChainLeft,
XtNright, XawChainRight,
XtNfromVert, title,
NULL);
btn_play = CREATE_BUTTON("play", buttons, NULL, play);
btn_pause = CREATE_BUTTON("pause", buttons, btn_play, pause);
btn_prev = CREATE_BUTTON("prev", buttons, btn_pause, prev);
btn_back = CREATE_BUTTON("back", buttons, btn_prev, back);
btn_stop = CREATE_BUTTON("stop", buttons, btn_back, stop);
btn_fwrd = CREATE_BUTTON("fwrd", buttons, btn_stop, fwrd);
btn_next = CREATE_BUTTON("next", buttons, btn_fwrd, next);
btn_quit = CREATE_BUTTON("quit", buttons, btn_next, quit);
XtAddCallback(btn_play, XtNcallback, (XtCallbackProc)PlayCB, NULL);
XtAddCallback(btn_pause, XtNcallback, (XtCallbackProc)PauseCB, NULL);
XtAddCallback(btn_prev, XtNcallback, (XtCallbackProc)PrevCB, NULL);
XtAddCallback(btn_back, XtNcallback, (XtCallbackProc)BackCB, NULL);
XtAddCallback(btn_stop, XtNcallback, (XtCallbackProc)StopCB, NULL);
XtAddCallback(btn_fwrd, XtNcallback, (XtCallbackProc)FwrdCB, NULL);
XtAddCallback(btn_next, XtNcallback, (XtCallbackProc)NextCB, NULL);
XtAddCallback(btn_quit, XtNcallback, (XtCallbackProc)QuitCB, NULL);
textwin = XtVaCreateManagedWidget
("text", asciiTextWidgetClass, topform,
XtNfromVert, buttons,
XtNleft, XawChainLeft,
XtNeditType, XawtextAppend,
XtNwidth, 250,
XtNheight, 200,
XtNscrollHorizontal, XawtextScrollAlways,
XtNscrollVertical, XawtextScrollAlways,
XtNtype, XawAsciiString,
XtNdisplayCaret, False,
NULL);
init_line();
clear_status();
}
static Pixmap MakeBitmap(char *bits, int width, int height)
{
Display *disp;
Screen *scr;
Window win;
Pixel bg, fg;
scr = XtScreen(toplevel);
disp = DisplayOfScreen(scr);
win = RootWindowOfScreen(scr);
fg = WhitePixelOfScreen(scr);
bg = BlackPixelOfScreen(scr);
return XCreatePixmapFromBitmapData(disp, win, bits, width, height, fg, bg, 1);
}
/*----------------------------------------------------------------
* callback actions
*----------------------------------------------------------------*/
static void PlayCB()
{
if (curstate != STAT_PLAYING)
ext_control_next();
}
static void NextCB()
{
if (curstate == STAT_PLAYING)
ext_control_next();
}
static void PrevCB()
{
if (curstate == STAT_PLAYING)
ext_control_prev();
}
static void FwrdCB()
{
if (curstate == STAT_PLAYING)
ext_control_fwrd();
}
static void BackCB()
{
if (curstate == STAT_PLAYING)
ext_control_back();
}
static void StopCB()
{
if (curstate == STAT_PLAYING) {
ext_control_stop();
clear_status();
}
}
static void QuitCB()
{
exit_all();
}
static void PauseCB()
{
if (curstate == STAT_PLAYING) {
paused_status();
ext_control_pause();
} else if (curstate == STAT_PAUSE) {
playing_status();
ext_control_pause();
}
}
/*----------------------------------------------------------------
* pipe input from player
*----------------------------------------------------------------*/
static void skip_line(void)
{
int c;
while ((c = getc(p_input)) != '\n')
;
}
static void InputCB(void)
{
int val, len;
char local[100], *cmd, *arg;
if (fgets(local, sizeof(local), p_input) == NULL)
return;
if (*local == 0) return;
len = strlen(local);
if (local[len-1] != '\n')
skip_line();
else
local[len-1] = 0;
local[4] = 0;
cmd = local;
arg = local + 5;
if (strcmp(cmd, "TIME") == 0) {
total_time = atoi(arg) / 100;
set_curtime(0);
} else if (strcmp(cmd, "FILE") == 0) {
set_title(arg);
} else if (strcmp(cmd, "TITL") == 0) {
/*set_title(arg);*/
;
} else if (strcmp(cmd, "PREV") == 0) {
if (curfile > 0)
curfile--;
do_play();
} else if (strcmp(cmd, "NEXT") == 0) {
curfile++;
if (curfile >= number_of_files) {
ext_control_stop();
clear_status();
} else
do_play();
} else if (strcmp(cmd, "CMSG") == 0) {
append_line();
} else if (strcmp(cmd, "CERR") == 0) {
popup_msg();
ext_control_next();
}
}
/*----------------------------------------------------------------
* display text message from player
*----------------------------------------------------------------*/
#define MAX_TEXT_LINES 500
#define DISPLAY_LINES 12
static XawTextPosition textpos, linepos[DISPLAY_LINES];
static int curline, lineidx;
static void init_line(void)
{
int i;
XtVaSetValues(textwin, XtNstring, "", NULL);
textpos = 0;
curline = 0;
lineidx = 0;
for (i = 0; i < DISPLAY_LINES; i++)
linepos[i] = 0;
}
static void append_line(void)
{
XawTextBlock blk;
char buf[1000];
if (curline >= MAX_TEXT_LINES)
init_line();
linepos[lineidx++] = textpos;
if (lineidx >= DISPLAY_LINES) lineidx = 0;
for (;;) {
if (fgets(buf, sizeof(buf), p_input) == NULL)
return;
blk.firstPos = 0;
blk.length = strlen(buf);
blk.ptr = buf;
blk.format = XawFmt8Bit;
XawTextReplace(textwin, textpos, textpos, &blk);
textpos += blk.length;
if (blk.length > 0 && buf[blk.length-1] == '\n')
break;
}
curline++;
if (curline >= DISPLAY_LINES) {
XtVaSetValues(textwin, XtNdisplayPosition, linepos[lineidx], NULL);
}
}
/*----------------------------------------------------------------
* popup error message
*----------------------------------------------------------------*/
static void popup_msg(void)
{
int c;
while ((c = getc(p_input)) != '\n')
putc(c, stderr);
putc('\n', stderr);
/* skip to next song */
ext_control_next();
}
/*----------------------------------------------------------------
* tell a midi file name to player
*----------------------------------------------------------------*/
static void do_play(void)
{
playing_status();
ext_control_load(filename[curfile]);
set_index();
}
/*----------------------------------------------------------------
* status change
*----------------------------------------------------------------*/
/* display index number */
static void set_index(void)
{
char local[20];
sprintf(local, "%03d/%03d", curfile+1, number_of_files);
XtVaSetValues(title_index, XtNlabel, local, NULL);
}
/* display title */
static void set_title(char *title)
{
XtVaSetValues(title_name, XtNlabel, title, NULL);
}
/* display total & current time */
static void set_curtime(int sec)
{
char local[20];
sprintf(local, "%02d:%02d/%02d:%02d",
sec / 60, sec % 60, total_time / 60, total_time % 60);
XtVaSetValues(title_time, XtNlabel, local, NULL);
cur_time = sec;
}
/* reset all status */
static void clear_status(void)
{
curfile = -1;
total_time = 0;
set_curtime(0);
set_title("------------------");
XtVaSetValues(title_index, XtNlabel, "000/000", NULL);
XtSetSensitive(btn_play, True);
XtSetSensitive(btn_prev, False);
XtSetSensitive(btn_next, False);
XtSetSensitive(btn_pause, False);
XtSetSensitive(btn_fwrd, False);
XtSetSensitive(btn_back, False);
curstate = STAT_CLEAN;
}
/* change to playing mode */
static void playing_status(void)
{
if (curstate == STAT_PLAYING)
return;
XtSetSensitive(btn_play, False);
XtSetSensitive(btn_prev, True);
XtSetSensitive(btn_next, True);
XtSetSensitive(btn_pause, True);
XtSetSensitive(btn_fwrd, True);
XtSetSensitive(btn_back, True);
curstate = STAT_PLAYING;
}
/* change to pause mode */
static void paused_status(void)
{
if (curstate == STAT_PAUSE)
return;
XtSetSensitive(btn_play, False);
XtSetSensitive(btn_prev, False);
XtSetSensitive(btn_next, False);
XtSetSensitive(btn_pause, True);
XtSetSensitive(btn_fwrd, False);
XtSetSensitive(btn_back, False);
curstate = STAT_PAUSE;
}
/*----------------------------------------------------------------
* timer callback procedure
*----------------------------------------------------------------*/
static void TimerCB()
{
int sec;
/* redisplay the current time */
sec = extpanel->curcs / 100;
if (sec != cur_time)
set_curtime(sec);
/* reset timer callback again */
XtAppAddTimeOut(apps, DELTA_TIME, (XtTimerCallbackProc)TimerCB, NULL);
}
awemidi-0.4.2b/dumb_c.c 100644 765 12 11131 6371333675 13200 0 ustar iwai wheel /*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include "util.h"
#include "midievent.h"
#include "controls.h"
void dumb_pass_playing_list(int number_of_files, char *list_of_files[]);
static void ctl_update(MidiInfo *mp);
static void ctl_total_time(int tt);
static void ctl_file_name(char *name);
static void ctl_title(char *name);
static void ctl_current_time(int ct);
static void ctl_note(int ch, int note, int vel);
static void ctl_program(int ch, int val);
static void ctl_bank(int ch, int val);
static void ctl_control_change(int ch, int type, int val);
static void ctl_pitch_bend(int channel, int val);
static void ctl_pitch_sense(int channel, int val);
static void ctl_system_change(int type, int mode);
static void ctl_effects_change(int ch, int type, int mode);
static void ctl_main_vol(int atten);
static void ctl_reset(MidiInfo *mp);
static int ctl_open(int using_stdin, int using_stdout);
static void ctl_close(void);
static int ctl_read(MidiInfo *mp, int *valp);
static int cmsg(int type, int verbosity_level, char *fmt, ...);
/**********************************/
/* export the interface functions */
#define ctl dumb_control_mode
ControlMode ctl=
{
"dumb interface", 'd',
TRUE, /* need_args */
FALSE, /* need_sync */
FALSE, /* fuzzy_prev */
0,0,0,0, /* verbose, trace, open, repeat */
ctl_open,dumb_pass_playing_list, ctl_close, ctl_read, ctl_read, cmsg,
ctl_update, ctl_reset, ctl_file_name, ctl_title, ctl_total_time, ctl_current_time,
ctl_note, ctl_program,ctl_bank, ctl_control_change,
ctl_pitch_bend, ctl_pitch_sense,
ctl_system_change, ctl_effects_change, ctl_main_vol,
};
static FILE *infp=stdin, *outfp=stdout; /* infp isn't actually used yet */
static int ctl_open(int using_stdin, int using_stdout)
{
if (using_stdin && using_stdout)
infp=outfp=stderr;
else if (using_stdout)
outfp=stderr;
else if (using_stdin)
infp=stdout;
ctl.opened=1;
return 0;
}
static void ctl_close(void)
{
fflush(outfp);
ctl.opened=0;
}
static int ctl_read(MidiInfo *mp, int *valp)
{
return RC_NONE;
}
static int cmsg(int type, int verbosity_level, char *fmt, ...)
{
va_list ap;
switch (type) {
case CMSG_INFO:
case CMSG_TEXT:
if (ctl.verbosity == 1 && verbosity_level == -1)
return 0;
if (ctl.verbosity < verbosity_level + 3)
return 0;
break;
default:
if (ctl.verbosity < verbosity_level) return 0;
break;
}
va_start(ap, fmt);
if (!ctl.opened)
{
if (type == CMSG_TEXT) /* not printf */
fputs(fmt, stderr);
else
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
}
else
{
if (type == CMSG_TEXT) /* not printf */
fputs(fmt, outfp);
else
vfprintf(outfp, fmt, ap);
fprintf(outfp, "\n");
}
va_end(ap);
return 0;
}
static void ctl_update(MidiInfo *mp) {}
static void ctl_total_time(int tt)
{
tt /= 100;
if (ctl.verbosity > 0)
fprintf(outfp, "Total Time %d:%02d\n", tt/60, tt%60);
}
static void ctl_file_name(char *name)
{
if (ctl.verbosity > 0)
fprintf(outfp, "Playing %s\n", name);
}
static void ctl_title(char *name)
{
if (ctl.verbosity > 0)
fprintf(outfp, "Title: %s\n", name);
}
static void ctl_current_time(int ct) {}
static void ctl_note(int ch, int note, int vel) {}
static void ctl_program(int ch, int val) {}
static void ctl_bank(int ch, int bank) {}
static void ctl_control_change(int channel, int type, int val) {}
static void ctl_system_change(int type, int mode) {}
static void ctl_effects_change(int channel, int type, int mode) {}
static void ctl_main_vol(int atten) {}
static void ctl_pitch_bend(int channel, int val) {}
static void ctl_pitch_sense(int channel, int val) {}
static void ctl_reset(MidiInfo *mp) {}
#undef ctl
void dumb_pass_playing_list(int number_of_files, char *list_of_files[])
{
int i;
static MidiInfo minfo;
if (ctl->playing_mode & PLAY_SHUFFLE) {
srandom(getpid());
for (i = 0; i < number_of_files; i++) {
int j = random() % (number_of_files - i);
if (j) {
char *tmp = list_of_files[i];
list_of_files[i] = list_of_files[i + j];
list_of_files[i + j] = tmp;
}
}
}
i = 0;
for (;;)
{
copy_midi_info(&minfo, &glinfo, list_of_files[i]);
switch(play_midi_file(&minfo))
{
case RC_REALLY_PREVIOUS:
if (i>0)
i--;
break;
default: /* An error or something */
case RC_NEXT:
if (i<number_of_files-1)
{
i++;
break;
}
else if (ctl->playing_mode & PLAY_REPEAT)
i = 0;
/* else fall through */
case RC_QUIT:
case RC_KILL:
ctl_close();
return;
}
}
}
awemidi-0.4.2b/channel.h 100644 765 12 1563 6375616027 13353 0 ustar iwai wheel /*
*/
#ifndef CHANNEL_H_DEF
#define CHANNEL_H_DEF
#ifdef linux
#include <sys/soundcard.h>
#elif defined(__FreeBSD__)
#include <machine/soundcard.h>
#endif
#define MAX_MIDI_CHANNELS 32
typedef struct _ChannelStat {
int bank, defbank; /* bank, default bank */
int preset, defpreset; /* preset */
/* parameters */
int pitchsense; /* usually 0x100 (= 2 semitones) */
int pitchbend; /* 0 - 0x3fff (0x2000 = no bend) */
int chan_press; /* channel pressure */
char controls[127];
/* tuning */
int finetune, coarsetune;
int volscale; /* volume sensitivity */
int bankmode; /* XG bank mode; bank MSB */
} ChannelStat;
#ifndef CTL_REVERB_DEPTH
#define CTL_REVERB_DEPTH CTL_EXT_EFF_DEPTH
#endif
extern int channel_drums; /* drum flag bits */
extern ChannelStat channels[MAX_MIDI_CHANNELS];
#define CHAN_ISDRUM(ch) (channel_drums & DRUMBIT(ch))
#endif /* CHANNEL_H_DEF */
awemidi-0.4.2b/midi.c 100644 765 12 52176 6413671030 12672 0 ustar iwai wheel /*================================================================
* midi.c
* midi player program for AWE32 sound driver
*
* Copyright (C) 1996,1997 Takashi Iwai
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*================================================================*/
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include "channel.h"
#include "seq.h"
#include "midievent.h"
#include "util.h"
#include "sighandle.h"
#include "controls.h"
#include "options.h"
#include "config.h"
/*#define DONT_TRUST_SYSTEM_SLEEP*/
/*
* prototypes
*/
#ifdef RCFILE
static void parse_default_options(void);
static int parse_rcfile(char *path, char *base);
#endif
static void parse_options(int argc, char **argv);
static int wait_pending(MidiInfo *mp);
static int send_seq_events(MidiInfo *mp);
void print_usage(void);
static void clean_up(int sig);
static int search_event(MidiInfo *mp, int delta);
static void jump_to(MidiInfo *mp, int csec, int output_lyric);
static void jump_to_start(MidiInfo *mp);
static void reset_status(MidiInfo *mp);
static void find_start_pos(MidiInfo *mp);
static void push_clear(void);
static int pop_event(MidiInfo *mp);
static int check_same_csec(MidiEvent *ev, MidiInfo *mp);
static int do_control(MidiInfo *mp);
/* Sanity check */
#if !(defined(INCLUDE_TK_MODE) || defined(INCLUDE_NCURSES_MODE) || defined (INCLUDE_DUMB_MODE) || defined(INCLUDE_PIPE_MODE))
#error No control interfaces defined!
#endif
#ifdef INCLUDE_TK_MODE
extern ControlMode tk_control_mode;
extern char *tk_display, *tk_geometry, *tk_effect;
#endif
#ifdef INCLUDE_NCURSES_MODE
extern ControlMode ncurses_control_mode;
#endif
#ifdef INCLUDE_DUMB_MODE
extern ControlMode dumb_control_mode;
#endif
#ifdef INCLUDE_PIPE_MODE
extern ControlMode pipe_control_mode;
extern int pipe_in, pipe_out, gen_shmid;
#endif
ControlMode *ctl_list[] = {
#ifdef INCLUDE_TK_MODE
&tk_control_mode,
#endif
#ifdef INCLUDE_NCURSES_MODE
&ncurses_control_mode,
#endif
#ifdef INCLUDE_DUMB_MODE
&dumb_control_mode,
#endif
#ifdef INCLUDE_PIPE_MODE
&pipe_control_mode,
#endif
};
ControlMode *ctl;
MidiInfo glinfo;
int verbose = 0, debug = 0;
static int playing_mode = 0;
void main(int argc, char **argv)
{
memcpy(&glinfo, &gl_default, sizeof(glinfo));
if (!getenv("DISPLAY"))
#ifdef INCLUDE_NCURSES_MODE
ctl = &ncurses_control_mode;
#elif defined(INCLUDE_DUMB_MODE)
ctl = &dumb_control_mode;
#elif defined(INCLUDE_PIPE_MODE)
ctl = &pipe_control_mode;
#else
{
printf("Can't run in text console!\n"
"Please recompile with ncurses or dumb interface support.\n\n");
print_usage();
exit(1);
}
#endif
else
ctl = ctl_list[0];
#ifdef RCFILE
parse_default_options();
#endif
parse_options(argc, argv);
if (optind >= argc && ctl->need_args) {
print_usage();
exit(1);
}
ctl->verbosity = verbose;
ctl->playing_mode = playing_mode;
if (argc > optind && *argv[optind] == '-')
ctl->open(1, 0);
else
ctl->open(0, 0);
add_signal(SIGTERM, clean_up, 1);
add_signal(SIGINT, clean_up, 1);
add_signal(SIGQUIT, clean_up, 1);
ctl->pass_playing_list(argc - optind, argv + optind);
ctl->close();
exit(0);
}
/*----------------------------------------------------------------
* parse options
*----------------------------------------------------------------*/
#ifdef RCFILE
static void parse_default_options(void)
{
char *p, rcfile[256];
rcfile[0] = 0;
if ((p = getenv("HOME")) != NULL && *p) {
sprintf(rcfile, "%s/%s", p, RCFILE);
if (access(rcfile, R_OK) != 0)
rcfile[0] = 0;
}
if (! *rcfile) {
#ifdef SYSTEM_RCFILE
strcpy(rcfile, SYSTEM_RCFILE);
if (access(rcfile, R_OK) != 0)
return;
#else
return;
#endif
}
parse_rcfile(rcfile, NULL);
}
#define MAX_ARGC 100
static int parse_rcfile(char *path, char *base)
{
FILE *fp;
char line[256];
if ((fp = fopen(path, "r")) == NULL)
return 0;
while (fgets(line, sizeof(line), fp)) {
char *argv[MAX_ARGC];
int argc;
argv[0] = strtoken(line);
if (argv[0] == NULL || !*argv[0] || *argv[0] == '#')
continue;
for (argc = 1; argc < MAX_ARGC; argc++) {
argv[argc] = strtoken(NULL);
if (!argv[argc])
break;
}
parse_options(argc, argv);
}
fclose (fp);
return 1;
}
#endif /* RCFILE */
static awe_option_args long_options[] = {
{"help", 1, 0, OPT_HELP},
{"interface", 1, 0, OPT_INTERFACE},
{"trace", 2, 0, OPT_TRACE},
{"mode", 1, 0, OPT_MODE},
{"drum", 1, 0, OPT_DRUM},
{"drumflag", 1, 0, OPT_DRUMFLAG},
{"chorus", 1, 0, OPT_CHORUS},
{"reverb", 1, 0, OPT_REVERB},
{"volume", 1, 0, OPT_VOLUME},
{"volscale", 1, 0, OPT_VOLSCALE},
{"acceptall", 2, 0, OPT_ACCEPTALL},
{"realpan", 2, 0, OPT_REALTIME_PAN},
{"tracks", 1, 0, OPT_TRACKS},
{"multipart", 1, 0, OPT_MULTIPART},
{"mt32", 1, 0, OPT_MT32},
{"dynamic", 1, 0, OPT_DYNAMIC},
{"xgload", 1, 0, OPT_XGLOAD},
{"xgmap", 2, 0, OPT_XGMAP},
{"samecsec", 2, 0, OPT_SAMECSEC},
{"gsmacro", 2, 0, OPT_GSMACRO},
{"xgmacro", 2, 0, OPT_XGMACRO},
{"verbose", 2, 0, OPT_VERBOSE},
{"autoskip", 2, 0, OPT_AUTOSKIP},
{"parsetitle", 2, 0, OPT_PARSETITLE},
{"chorusdepth", 1, 0, OPT_CHORUSDEPTH},
{"reverbdepth", 1, 0, OPT_REVERBDEPTH},
{"tuning", 2, 0, OPT_TUNING},
{"chnprior", 2, 0, OPT_CHN_PRIOR},
{"usefx", 2, 0, OPT_USEFX},
{"fx_cutoff", 1, 0, OPT_FX_CUTOFF},
{"fx_resonance", 1, 0, OPT_FX_RESONANCE},
{"fx_attack", 1, 0, OPT_FX_ATTACK},
{"fx_release", 1, 0, OPT_FX_RELEASE},
{"fx_vibrate", 1, 0, OPT_FX_VIBRATE},
{"fx_vibdepth", 1, 0, OPT_FX_VIBDEPTH},
{"fx_vibdelay", 1, 0, OPT_FX_VIBDELAY},
{"convert", 1, 0, OPT_CONVERT},
{"seqbuf", 1, 0, OPT_SEQBUF},
{"seqecho", 1, 0, OPT_SEQECHO},
#ifdef INCLUDE_TK_MODE
{"effect", 1, 0, OPT_EFFECT},
{"display", 1, 0, OPT_DISPLAY},
{"geometry", 1, 0, OPT_GEOMETRY},
#endif
#ifdef INCLUDE_PIPE_MODE
{"shmid", 1, 0, OPT_SHMID},
{"pipein", 1, 0, OPT_PIPEIN},
{"pipeout", 1, 0, OPT_PIPEOUT},
#endif
{0, 0, 0, 0},
};
static int option_index;
#define BASE_OPTION_FLAGS "hvc:r:D:i:V:S:sPOT:CGXM:tm:L:Y"
#ifdef INCLUDE_TK_MODE
#define OPTION_FLAGS BASE_OPTION_FLAGS "d:g:"
#else
#define OPTION_FLAGS BASE_OPTION_FLAGS
#endif
static void parse_options(int argc, char **argv)
{
int cp, i, mode;
char *p;
while ((cp = awe_getopt(argc, argv, OPTION_FLAGS,
long_options, &option_index)) != -1) {
switch (cp) {
#ifdef INCLUDE_PIPE_MODE
case OPT_PIPEIN:
pipe_in = atoi(optarg); break;
case OPT_PIPEOUT:
pipe_out = atoi(optarg); break;
case OPT_SHMID:
gen_shmid = atoi(optarg); break;
#endif
case OPT_XGLOAD:
if (glinfo.xgload) free(glinfo.xgload);
if (strcmp(optarg, "none") == 0)
glinfo.xgload = NULL;
else
glinfo.xgload = safe_strdup(optarg);
break;
case OPT_DYNAMIC:
if (glinfo.dynamicload) free(glinfo.dynamicload);
if (optarg) {
if (strcmp(optarg, "none") == 0)
glinfo.dynamicload = NULL;
else
glinfo.dynamicload = safe_strdup(optarg);
}
break;
case OPT_INTERFACE:
mode = optarg[0];
for (i = 0; i < numberof(ctl_list); i++) {
if (ctl_list[i]->id_character == mode) {
ctl = ctl_list[i];
break;
}
}
if (i >= numberof(ctl_list)) {
printf("illegal interface option\n");
print_usage();
exit(1);
}
break;
case OPT_TRACE:
if (optarg) {
if (bool_val(optarg))
BITON(playing_mode, PLAY_TRACE);
else
BITOFF(playing_mode, ~PLAY_TRACE);
} else
BITSWT(playing_mode, PLAY_TRACE);
break;
case OPT_MODE:
for (p = optarg; *p; p++) {
switch (*p) {
case 't':
BITSWT(playing_mode, PLAY_TRACE); break;
case 's':
BITOFF(playing_mode, PLAY_NORMAL);
BITON(playing_mode, PLAY_SHUFFLE); break;
case 'n':
BITOFF(playing_mode, PLAY_SHUFFLE);
BITON(playing_mode, PLAY_NORMAL); break;
case 'p':
BITSWT(playing_mode, PLAY_AUTOSTART); break;
case 'q':
BITON(playing_mode, PLAY_AUTOEXIT);
BITOFF(playing_mode, PLAY_REPEAT); break;
case 'r':
BITON(playing_mode, PLAY_REPEAT);
BITOFF(playing_mode, PLAY_AUTOEXIT); break;
default:
fprintf(stderr, "illegal mode option '%c'\n", *p);
break;
}
}
break;
#ifdef INCLUDE_TK_MODE
case OPT_EFFECT:
tk_effect = optarg;
break;
case OPT_GEOMETRY:
tk_geometry = optarg;
break;
case OPT_DISPLAY:
tk_display = optarg;
break;
#endif
default:
general_options(OPTION_FLAGS, cp);
break;
}
}
}
void print_usage(void)
{
int i;
printf("drvmidi ver.%s MIDI player for AWE sound driver\n", VERSION_STR);
printf(" copyright (c) 1996,1997 by Takashi Iwai\n");
printf("usage: drvmidi [-options] midifile[.gz] ...\n");
printf("options are:\n");
printf(" -i, --interface=type: available interfaces are\n");
for (i = 0; i < numberof(ctl_list); i++) {
printf(" %c : %s", ctl_list[i]->id_character,
ctl_list[i]->id_name);
if (ctl == ctl_list[i])
printf(" (default)");
printf("\n");
}
print_general_options(stdout, OPTION_FLAGS, long_options);
printf(" -t, --trace[=bool]: turn on tracing mode (equivalent with -mt)\n");
printf(" -m, --mode=modifiers: set playing mode\n");
printf(" n: normal play, s: shffule play, r: repeat play, ");
#if defined(INCLUDE_TK_MODE) || defined(INCLUDE_NCURSES_MODE)
printf("t: trace on");
#endif
printf("\n");
#ifdef INCLUDE_TK_MODE
printf(" p: auto start, q: auto exit\n");
printf(" --effect=config-file: set user defined chorus/reverb configuration\n");
printf(" -d, --display=xdisp: set window display\n");
printf(" -g, --geometry=xgeom: set window geometry\n");
#endif
}
/*----------------------------------------------------------------
* open / close sequencer
*----------------------------------------------------------------*/
int midi_open(MidiInfo *mp)
{
if (! seq_opened()) {
int no_block, do_echo;
no_block = (mp->seq_buffered && ctl->need_sync);
do_echo = (no_block && mp->use_echoback);
seq_blocking_mode(!no_block);
if (seq_init(do_echo)) {
ctl->cmsg(CMSG_ERROR, -1, "can't open sequencer device");
return 1;
}
}
mp->master_volume = 100;
seq_set_chorus(mp->chorus);
seq_set_reverb(mp->reverb);
seq_change_volume(mp->volume_base);
seq_equalizer(mp->bass_level, mp->treble_level);
seq_set_drumchannels(mp->drumflag);
seq_channel_priority(mp->chn_prior);
seq_set_realtime_pan(mp->realtime_pan);
if (mp->midi_mode == MODE_XG && mp->xg_mapping)
seq_set_def_drum(64);
else
seq_set_def_drum(0);
seqbuf_dump();
ctl->system_change(SY_CHORUS_MODE, mp->chorus);
ctl->system_change(SY_REVERB_MODE, mp->reverb);
ctl->master_volume(mp->volume_base);
return 0;
}
void midi_close(MidiInfo *mp)
{
if (seq_opened()) {
seq_terminate_all();
seq_end();
}
}
static void clean_up(int sig)
{
/*ctl->cmsg(CMSG_INFO, 0, "\nterminating..");*/
if (seq_opened()) {
seq_terminate_all();
seq_end();
}
ctl->close();
}
/*================================================================*/
/* search the appropriate event for skip forward/back and returns its time */
static int search_event(MidiInfo *mp, int delta)
{
int i, target;
MidiEvent *ev;
ev = mp->list + mp->curev;
target = mp->curcs + delta;
if (delta > 0) {
for (i = mp->curev; i <= mp->endidx; i++, ev++) {
if (ev->csec >= target)
break;
}
return ev->csec;
} else {
for (i = mp->curev; i > 0; i--, ev--) {
if (ev->csec <= target)
break;
}
return ev->csec;
}
}
/* store the channel status by the specified time, and jump there */
static void jump_to(MidiInfo *mp, int csec, int from_start)
{
int i;
MidiEvent *ev;
int type;
seq_terminate_all();
push_clear();
type = (from_start ? EV_STARTUP : EV_SETSTAT);
ev = mp->list;
if (!from_start && mp->curcs < csec) {
i = mp->curev;
ev += i;
} else {
i = 0;
channel_init(mp);
}
for (; i <= mp->endidx; i++, ev++) {
if (ev->csec >= csec)
break;
do_midi_event(ev, mp, type);
}
if (i > mp->endidx)
i = mp->endidx;
mp->curev = mp->prevev = i;
mp->updatecs = mp->prevcs = mp->curcs = mp->list[i].csec;
ctl->reset(mp);
ctl->current_time(mp->curcs);
channel_set(mp);
seq_clear(mp->curcs);
mp->echocs = mp->curcs;
}
/* jump to start event or the first note-on event */
static void jump_to_start(MidiInfo *mp)
{
if (mp->startcs > 0)
jump_to(mp, mp->startcs, TRUE);
else
reset_status(mp);
}
/* terminate sounds and reset all status */
static void reset_status(MidiInfo *mp)
{
seq_terminate_all();
channel_init(mp);
push_clear();
mp->curev = mp->prevev = 0;
mp->updatecs = mp->prevcs = mp->curcs = 0;
ctl->reset(mp);
ctl->current_time(0);
channel_set(mp);
seq_clear(0);
mp->echocs = 0;
}
#define TOO_LONG_DELTA 4000
/* find start position */
static void find_start_pos(MidiInfo *mp)
{
MidiEvent *ev = mp->list;
int i = 0;
mp->startcs = 0;
if (mp->skip_blank_head) {
for (i = 0; i < mp->nlists; i++) {
if (ev[i].type == ME_NOTEON) {
mp->startcs = ev[i].csec;
break;
}
}
}
mp->endidx = mp->nlists - 1;
for (i++; i < mp->nlists; i++) {
if (ev[i].csec - ev[i-1].csec >= TOO_LONG_DELTA) {
mp->endidx = i - 1;
break;
}
}
}
/*----------------------------------------------------------------*/
/* play a midi file */
int play_midi_file(MidiInfo *mp)
{
int piped;
MidiEvent *ev;
FILE *fp;
char *p;
int cmd;
if ((fp = CmpOpenFile(mp->filename, &piped)) == NULL) {
ctl->cmsg(CMSG_ERROR, -1, "can't open MIDI file %s",
mp->filename);
return RC_NEXT;
}
ctl->cmsg(CMSG_INFO, 0, "reading midi %s:", mp->filename);
if ((p = strrchr(mp->filename, '/')) != NULL)
ctl->file_name(p + 1);
else
ctl->file_name(mp->filename);
/* read index file before reading midi file */
find_midi_index(mp, TRUE);
ev = ReadMidiFile(fp, mp);
CmpCloseFile(fp, piped);
if (ev == NULL)
return RC_ERROR;
else if (mp->nlists == 0) {
FreeMidiFile(mp);
return RC_ERROR;
}
/* read index file after reading midi file */
find_midi_index(mp, FALSE);
if (mp->midi_mode == MODE_XG && mp->xgload) {
if (! mp->dynamic_loaded) {
if (midi_open(mp)) return RC_NONE;
ctl->cmsg(CMSG_INFO, 1, "loading xg map %s", mp->xgload);
preload_sample(mp, mp->xgload);
}
} else if (mp->dynamicload) {
if (! mp->dynamic_loaded) {
if (midi_open(mp)) return RC_NONE;
ctl->cmsg(CMSG_INFO, 1, "loading default font %s", mp->dynamicload);
preload_sample(mp, mp->dynamicload);
}
}
if (midi_open(mp))
return RC_NONE;
find_start_pos(mp);
ctl->total_time(mp->list[mp->endidx].csec);
for (;;) {
jump_to_start(mp);
while ((cmd = send_seq_events(mp)) == RC_NONE)
;
if (cmd != RC_TUNE_END || !ctl->repeated)
break;
}
reset_status(mp);
FreeMidiFile(mp); /* free event list */
if (cmd == RC_TUNE_END)
return RC_NEXT;
if (cmd == RC_QUIT || cmd == RC_KILL)
midi_close(mp);
return cmd;
}
/*----------------------------------------------------------------
* trick to avoid send on/off at the same time
*----------------------------------------------------------------*/
/* temporary event-list handlers */
#define MAX_PUSHLIST 32
static int npushed;
static int ev_pushed[MAX_PUSHLIST];
static void push_clear(void)
{
npushed = 0;
}
static int pop_event(MidiInfo *mp)
{
int i;
if (npushed <= 0)
return RC_NONE;
seq_wait(1);
mp->prevcs++;
for (i = 0; i < npushed; i++) {
do_midi_event(mp->list + ev_pushed[i], mp, EV_PLAY);
}
npushed = 0;
return RC_NONE;
}
/*----------------------------------------------------------------*/
/* check the note-on event was done in the same csec */
static int check_same_csec(MidiEvent *ev, MidiInfo *mp)
{
int i;
if (! mp->check_same_csec) return FALSE;
if (ev->type != ME_NOTEOFF &&
(ev->type != ME_NOTEON || VelOf(ev) != 0))
return FALSE;
if (npushed >= MAX_PUSHLIST)
return FALSE;
/* search backward the note-on event;
* if found in the same csec, do this event later.
*/
for (i = mp->curev - 1; i >= 0; i--) {
if (mp->list[i].csec < ev->csec)
break;
if (mp->list[i].type == ME_NOTEON &&
mp->list[i].channel == ev->channel &&
KeyOf(mp->list + i) == KeyOf(ev) &&
VelOf(mp->list + i) != 0) {
ev_pushed[npushed++] = mp->curev;
return TRUE;
}
}
return FALSE;
}
/*----------------------------------------------------------------*/
/* send wait and do the event */
static int process_event(MidiInfo *mp)
{
int rc;
MidiEvent *ev;
if (seq_pending())
return RC_NONE;
ev = mp->list + mp->curev;
if (ev->csec > mp->prevcs) {
pop_event(mp);
if (ev->csec > mp->prevcs)
seq_wait(ev->csec - mp->prevcs);
if (ctl->need_sync && ev->csec >= mp->echocs + 100) {
seq_echo(ev->csec);
mp->echocs = ev->csec;
}
mp->prevcs = ev->csec;
}
/* check note on/off events */
if (! check_same_csec(ev, mp)) {
/* send a seq event */
if ((rc = do_midi_event(ev, mp, EV_PLAY)) != RC_NONE)
return rc;
}
if (mp->curev >= mp->endidx) {
pop_event(mp);
seq_echo(ev->csec+1);
return RC_TUNE_END;
}
mp->curev++;
return RC_NONE;
}
/*----------------------------------------------------------------
* get input message from control panel
*----------------------------------------------------------------*/
static int do_control(MidiInfo *mp)
{
int cmd, val;
val = 0;
cmd = ctl->read(mp, &val);
if (cmd == RC_NONE) return RC_NONE;
switch (cmd) {
case RC_PAUSE:
seq_terminate_all();
for (;;) {
val = 1;
cmd = ctl->blocking_read(mp, &val);
if (cmd == RC_CONTINUE)
break;
else if (cmd == RC_QUIT || cmd == RC_KILL)
return cmd;
}
/* no break; continued */
case RC_CONTINUE:
jump_to(mp, mp->curcs, FALSE);
return RC_JUMP;
case RC_CHANGE_CHORUS:
mp->chorus = glinfo.chorus = val;
seq_set_chorus(val);
return RC_NONE;
case RC_CHANGE_REVERB:
mp->reverb = glinfo.reverb = val;
seq_set_reverb(val);
return RC_NONE;
case RC_CHANGE_VOLUME:
mp->volume_base = glinfo.volume_base = val;
seq_change_volume(mp->volume_base * mp->master_volume / 127);
ctl->master_volume(val);
return RC_NONE;
case RC_CHANGE_BASS:
mp->bass_level = glinfo.bass_level = val;
seq_equalizer(mp->bass_level, mp->treble_level);
return RC_NONE;
case RC_CHANGE_TREBLE:
mp->treble_level = glinfo.treble_level = val;
seq_equalizer(mp->bass_level, mp->treble_level);
return RC_NONE;
case RC_BASE_CHANGE:
mp->base_offset += val;
jump_to(mp, mp->curcs, FALSE);
return RC_JUMP;
case RC_RESTART:
jump_to_start(mp);
return RC_JUMP;
case RC_JUMP:
jump_to(mp, val, FALSE);
return RC_JUMP;
case RC_SEARCH:
jump_to(mp, search_event(mp, val), FALSE);
return RC_JUMP;
case RC_PREVIOUS:
if (!ctl->fuzzy_prev || mp->curcs > mp->startcs + 300) {
jump_to_start(mp);
return RC_JUMP;
}
cmd = RC_REALLY_PREVIOUS;
/* continue */
case RC_KILL:
case RC_NEXT:
case RC_REALLY_PREVIOUS:
case RC_LOAD_FILE:
case RC_QUIT:
seq_terminate_all();
return cmd;
}
return RC_NONE;
}
/*----------------------------------------------------------------
* play event list
*----------------------------------------------------------------*/
#define BUFFER_THRESHOLD 200
#define TIME_TO_UPDATE(mp) (seq_lasttime() > (mp)->curcs + BUFFER_THRESHOLD)
/* send seq events */
static int send_seq_events(MidiInfo *mp)
{
int rc;
do {
if (seq_pending()) {
rc = wait_pending(mp);
continue;
}
if ((rc = process_event(mp)) != RC_NONE)
break;
if (ctl->need_sync &&
(!mp->seq_buffered || TIME_TO_UPDATE(mp))) {
seqbuf_dump();
rc = wait_pending(mp);
}
} while (rc == RC_NONE);
if (rc == RC_TUNE_END) {
seqbuf_dump();
if (ctl->need_sync) {
while ((rc = wait_pending(mp)) == RC_NONE)
;
} else
seq_wait_time(mp->list[mp->endidx].csec);
}
return rc;
}
#define SLEEP_DELTA 5
#define UPDATE_DELTA 10
/* update interface and control status */
static void update_status(MidiInfo *mp)
{
/* update interface status */
for (; mp->prevev < mp->curev; mp->prevev++) {
if (mp->list[mp->prevev].csec > mp->curcs)
break;
do_midi_event(mp->list + mp->prevev, mp, EV_CONTROL);
}
/* update control */
if (mp->curcs >= mp->updatecs + UPDATE_DELTA) {
ctl->update(mp);
mp->updatecs = mp->curcs;
}
/* set current time */
ctl->current_time(mp->curcs);
}
#ifdef DONT_TRUST_SYSTEM_SLEEP
#define CHECK_DELTA_TIME() 1
#else
#define CHECK_DELTA_TIME() (seq_lasttime() - seq_curtime() <= SLEEP_DELTA*2)
#endif
/* wait and update controls */
static int wait_pending(MidiInfo *mp)
{
int rc;
do {
if (seq_timer_started())
update_status(mp);
/* input from control */
if ((rc = do_control(mp)) == RC_JUMP)
break;
else if (rc != RC_NONE)
return rc;
if (! mp->seq_buffered) {
if (CHECK_DELTA_TIME()) {
mp->curcs = seq_wait_time(seq_lasttime());
if (mp->curcs >= mp->list[mp->endidx].csec)
return RC_TUNE_END;
return RC_NONE;
}
}
mp->curcs = seq_sleep(SLEEP_DELTA);
if (mp->curcs > mp->list[mp->endidx].csec)
return RC_TUNE_END;
if (mp->seq_buffered && !TIME_TO_UPDATE(mp))
break;
} while (seq_pending());
return RC_NONE;
}
awemidi-0.4.2b/event.c 100644 765 12 35460 6414502220 13061 0 ustar iwai wheel /*================================================================
* event.c
* MIDI event processing routine
*================================================================*/
#include <stdio.h>
#include "channel.h"
#include "midievent.h"
#include "controls.h"
#include "util.h"
#include "seq.h"
#include "awe_effect.h"
/*----------------------------------------------------------------*/
static void channel_reset(MidiInfo *mp, int ch, int init);
static void Nothing(MidiEvent *ev, MidiInfo *minfo, int mode);
static void NoteOn(MidiEvent *ev, MidiInfo *minfo, int mode);
static void NoteOff(MidiEvent *ev, MidiInfo *minfo, int mode);
static void KeyPressure(MidiEvent *ev, MidiInfo *minfo, int mode);
static void ChanPressure(MidiEvent *ev, MidiInfo *minfo, int mode);
static void PitchSens(MidiEvent *ev, MidiInfo *minfo, int mode);
static void PitchWheel(MidiEvent *ev, MidiInfo *minfo, int mode);
static void Program(MidiEvent *ev, MidiInfo *minfo, int mode);
static void ControlChange(MidiEvent *ev, MidiInfo *minfo, int mode);
static void ResetControl(MidiEvent *ev, MidiInfo *minfo, int mode);
static void AllNotesOff(MidiEvent *ev, MidiInfo *minfo, int mode);
static void AllSoundsOff(MidiEvent *ev, MidiInfo *minfo, int mode);
static void ToneBank(MidiEvent *ev, MidiInfo *minfo, int mode);
static void PrintLyric(MidiEvent *ev, MidiInfo *minfo, int mode);
static void Tempo(MidiEvent *ev, MidiInfo *minfo, int mode);
static void NRPNEvent(MidiEvent *ev, MidiInfo *minfo, int mode);
static void GSNRPNEvent(MidiEvent *ev, MidiInfo *minfo, int mode);
static void FineTune(MidiEvent *ev, MidiInfo *minfo, int mode);
static void CoarseTune(MidiEvent *ev, MidiInfo *minfo, int mode);
static void MasterVolume(MidiEvent *ev, MidiInfo *minfo, int mode);
/*----------------------------------------------------------------*/
/* global variables for channel status */
int channel_drums;
ChannelStat channels[MAX_MIDI_CHANNELS];
/*----------------------------------------------------------------*/
/* MT-32 emulation translate table (copied from playmidi) */
static int mt32pgm[128] =
{
0, 1, 2, 4, 4, 5, 5, 3, 16, 16, 16, 16, 19,
19, 19, 21, 6, 6, 6, 7, 7, 7, 8, 8, 62, 57,
63, 58, 38, 38, 39, 39, 88, 33, 52, 35, 97, 100, 38,
39, 14, 102, 68, 103, 44, 92, 46, 80, 48, 49, 51, 45,
40, 40, 42, 42, 43, 46, 46, 24, 25, 28, 27, 104, 32,
32, 34, 33, 36, 37, 39, 35, 79, 73, 76, 72, 74, 75,
64, 65, 66, 67, 71, 71, 69, 70, 60, 22, 56, 59, 57,
63, 60, 60, 58, 61, 61, 11, 11, 99, 100, 9, 14, 13,
12, 107, 106, 77, 78, 78, 76, 111, 47, 117, 127, 115, 118,
116, 118, 126, 121, 121, 55, 124, 120, 125, 126, 127
};
/*----------------------------------------------------------------*/
typedef void (*EventFunc)(MidiEvent *ev, MidiInfo *minfo, int mode);
static EventFunc midi_events[] = {
Nothing, /* ME_NONE */
NoteOn, /* ME_NOTEON */
NoteOff, /* ME_NOTEOFF */
KeyPressure, /* ME_KEYPRESSURE */
ControlChange, /* ME_CONTROL */
Program, /* ME_PROGRAM */
ChanPressure, /* ME_CHNPRESSURE */
PitchWheel, /* ME_PITCHWHEEL */
Tempo, /* ME_TEMPO */
PitchSens, /* ME_PITCH_SENS */
ToneBank, /* ME_TONE_BANK */
AllSoundsOff, /* ME_ALL_SOUNDS_OFF */
ResetControl, /* ME_RESET_CONTROLLERS */
AllNotesOff, /* ME_ALL_NOTES_OFF */
PrintLyric, /* ME_LYRIC */
MasterVolume, /* ME_MASETER_VOLUME */
FineTune, /* ME_FINETUNE */
CoarseTune, /* ME_COARSETUNE */
NRPNEvent, /* ME_AWE_FX */
GSNRPNEvent, /* ME_GS_FX */
};
/*
* process one midi event
*/
int do_midi_event(MidiEvent *ev, MidiInfo *minfo, int mode)
{
if (ev->type == ME_EOT)
return RC_TUNE_END;
else if (ev->type < ME_EOT)
midi_events[ev->type](ev, minfo, mode);
return RC_NONE;
}
/*
* initialize all midi channels (not call sequencer controls)
*/
void channel_init(MidiInfo *mp)
{
int i;
mp->master_volume = 100;
channel_drums = mp->drumflag;
for (i = 0; i < MAX_MIDI_CHANNELS; i++) {
if (CHAN_ISDRUM(i)) {
channels[i].defbank = 128;
if (mp->midi_mode == MODE_MT32)
channels[i].defpreset = 127;
else
channels[i].defpreset = 0;
channels[i].bankmode = 127;
} else {
channels[i].defpreset = 0;
if (mp->midi_mode == MODE_MT32)
channels[i].defbank = 127;
else
channels[i].defbank = 0;
channels[i].bankmode = 0;
}
if (!(mp->chn_volflag & DRUMBIT(i)))
channels[i].volscale = 100;
channel_reset(mp, i, TRUE);
}
}
/*
* reset channel status to default (not call sequencer)
*/
static void channel_reset(MidiInfo *mp, int i, int init)
{
ChannelStat *ch = &channels[i];
if (init) {
ch->preset = ch->defpreset;
ch->bank = ch->defbank;
ch->controls[CTL_MAIN_VOLUME] = 100;
ch->pitchsense = 2 * 128;
ch->controls[CTL_CHORUS_DEPTH] = 0;
ch->controls[CTL_REVERB_DEPTH] = 0;
ch->controls[CTL_PAN] = 63;
ch->controls[CTL_CHORUS_DEPTH] = mp->chorusdepth;
ch->controls[CTL_REVERB_DEPTH] = mp->reverbdepth;
}
ch->controls[CTL_MODWHEEL] = 0;
ch->controls[CTL_EXPRESSION] = 127;
ch->controls[CTL_SUSTAIN] = 0;
ch->controls[CTL_SOSTENUTO] = 0;
ch->pitchbend = 0x2000;
ch->chan_press = 0;
ch->finetune = 0;
ch->coarsetune = 0;
}
/*
* send all the channel status to sequencer device
*/
#define CONTROL(v,type) seq_control(v, type, channels[v].controls[type])
void channel_set(MidiInfo *mp)
{
int i;
seq_set_drumchannels(channel_drums);
for (i = 0; i < MAX_MIDI_CHANNELS; i++) {
ChannelStat *ch = &channels[i];
seq_reset_channel(i);
seq_set_bank(i, ch->bank);
if (mp->midi_mode == MODE_XG && mp->xg_mapping &&
CHAN_ISDRUM(i))
seq_set_program(i, ch->preset + 64);
else
seq_set_program(i, ch->preset);
CONTROL(i, CTL_MODWHEEL);
CONTROL(i, CTL_PAN);
CONTROL(i, CTL_MAIN_VOLUME);
CONTROL(i, CTL_EXPRESSION);
CONTROL(i, CTL_SUSTAIN);
CONTROL(i, CTL_SOSTENUTO);
if (ch->controls[CTL_CHORUS_DEPTH] >= 0)
CONTROL(i, CTL_CHORUS_DEPTH);
if (ch->controls[CTL_REVERB_DEPTH] >= 0)
CONTROL(i, CTL_REVERB_DEPTH);
seq_pitchsense(i, ch->pitchsense);
seq_pitchbend(i, ch->pitchbend);
seq_chan_pressure(i, ch->chan_press);
seq_detune(i, ch->coarsetune, ch->finetune);
}
}
/*----------------------------------------------------------------*/
static void Nothing(MidiEvent *ev, MidiInfo *minfo, int mode) {}
/* note on */
static void NoteOn(MidiEvent *ev, MidiInfo *minfo, int mode)
{
int key, vol;
if (mode == EV_PRELOAD) {
/* only for check of dynamic loading of drum sounds */
if (CHAN_ISDRUM(ev->channel) ||
channels[ev->channel].bankmode == 126) {
int preset = channels[ev->channel].preset;
if (minfo->midi_mode == MODE_XG && minfo->xg_mapping &&
CHAN_ISDRUM(ev->channel))
preset += 64;
add_preload(ev->channel, preset,
channels[ev->channel].bank, KeyOf(ev));
}
return;
} else if (mode != EV_PLAY && mode != EV_CONTROL)
return;
if (VelOf(ev) == 0) {
NoteOff(ev, minfo, mode);
return;
}
vol = VelOf(ev);
#ifndef NO_VOLSCALE
vol = (vol * minfo->volscale) / 100;
vol = (vol * channels[ev->channel].volscale) / 100;
if (vol <= 0) vol = 0;
else if (vol > 127) vol = 127;
#endif
key = KeyOf(ev);
if (!CHAN_ISDRUM(ev->channel)) {
key += minfo->base_offset;
if (key < 0 || key >= 127) return;
}
if (mode == EV_PLAY)
seq_start_note(ev->channel, key, vol);
else if (mode == EV_CONTROL)
ctl->note(ev->channel, key, vol);
}
/* note off */
static void NoteOff(MidiEvent *ev, MidiInfo *minfo, int mode)
{
int key;
if (mode != EV_PLAY && mode != EV_CONTROL) return;
key = KeyOf(ev);
if (!CHAN_ISDRUM(ev->channel)) {
key += minfo->base_offset;
if (key < 0 || key >= 127) return;
}
if (mode == EV_PLAY)
seq_stop_note(ev->channel, key, VelOf(ev));
else if (mode == EV_CONTROL)
ctl->note(ev->channel, key, 0);
}
/* key pressure change (aftertouch) */
static void KeyPressure(MidiEvent *ev, MidiInfo *minfo, int mode)
{
int vol, key;
if (mode != EV_PLAY && mode != EV_CONTROL) return;
vol = VelOf(ev);
#ifndef NO_VOLSCALE
vol = (vol * minfo->volscale) / 100;
vol = (vol * channels[ev->channel].volscale) / 100;
if (vol <= 0) vol = 0;
else if (vol > 127) vol = 127;
#endif
key = KeyOf(ev);
if (!CHAN_ISDRUM(ev->channel)) {
key += minfo->base_offset;
if (key < 0 || key >= 127) return;
}
if (mode == EV_PLAY)
seq_aftertouch(ev->channel, key, vol);
else if (mode == EV_CONTROL)
ctl->note(ev->channel, key, vol);
}
/* pitch sense change (RPN control) */
static void PitchSens(MidiEvent *ev, MidiInfo *minfo, int mode)
{
channels[ev->channel].pitchsense = ev->p.val;
if (mode == EV_PLAY)
seq_pitchsense(ev->channel, ev->p.val);
else if (mode == EV_CONTROL)
ctl->pitch_sense(ev->channel, ev->p.val);
}
/* pitch wheel change */
static void PitchWheel(MidiEvent *ev, MidiInfo *minfo, int mode)
{
channels[ev->channel].pitchbend = ev->p.val;
if (mode == EV_PLAY)
seq_pitchbend(ev->channel, ev->p.val);
else if (mode == EV_CONTROL)
ctl->pitch_bend(ev->channel, ev->p.val);
}
/* channel pressure change */
static void ChanPressure(MidiEvent *ev, MidiInfo *minfo, int mode)
{
#if 0
int vol;
vol = ev->p.val;
#ifndef NO_VOLSCALE
vol = (vol * minfo->volscale) / 100;
vol = (vol * channels[ev->channel].volscale) / 100;
if (vol <= 0) vol = 0;
else if (vol > 127) vol = 127;
#endif
channels[ev->channel].chan_press = vol;
if (mode == EV_PLAY)
seq_chan_pressure(ev->channel, vol);
#endif
}
/* program change */
static void Program(MidiEvent *ev, MidiInfo *minfo, int mode)
{
ChannelStat *ch = &channels[ev->channel];
int preset;
if (minfo->midi_mode == MODE_MT32_INT)
ch->preset = mt32pgm[ev->p.val];
else
ch->preset = ev->p.val;
if (minfo->midi_mode == MODE_XG && minfo->xg_mapping &&
CHAN_ISDRUM(ev->channel))
preset = ch->preset + 64;
else
preset = ch->preset;
if (mode == EV_PRELOAD) {
if (! CHAN_ISDRUM(ev->channel) && ch->bankmode != 126)
add_preload(ev->channel, preset, ch->bank, -1);
return;
}
else if (mode == EV_PLAY)
seq_set_program(ev->channel, preset);
else if (mode == EV_CONTROL)
ctl->program(ev->channel, ch->preset);
}
/* midi control change */
static void ControlChange(MidiEvent *ev, MidiInfo *minfo, int mode)
{
channels[ev->channel].controls[CtrlOf(ev)] = CValOf(ev);
if (minfo->midi_mode == MODE_XG) {
/* not implemented in driver */
int i;
if (! minfo->use_fx) return;
for (i = 0; i < num_xg_effects; i++) {
if (CtrlOf(ev) == xg_effects[i].control) {
int cval = xg_effects[i].convert(minfo, CValOf(ev));
if (mode == EV_PLAY)
seq_add_effect(ev->channel, xg_effects[i].awe_effect, cval);
return;
}
}
}
if (CtrlOf(ev) == CTL_SOFT_PEDAL) {
/* not implemented in driver */
if (! minfo->use_fx) return;
seq_soft_pedal(ev->channel, CValOf(ev));
return;
}
if (mode == EV_PLAY)
CONTROL(ev->channel, CtrlOf(ev));
else if (mode == EV_CONTROL)
ctl->control_change(ev->channel, CtrlOf(ev), CValOf(ev));
}
/* reset controllers */
static void ResetControl(MidiEvent *ev, MidiInfo *minfo, int mode)
{
seq_reset_control(ev->channel);
}
/* all notes off; send note-off to all channels */
static void AllNotesOff(MidiEvent *ev, MidiInfo *minfo, int mode)
{
if (mode == EV_PLAY && minfo->accept_all_off)
seq_note_off_all();
}
/* all sounds off; terminate all sounds immediately */
static void AllSoundsOff(MidiEvent *ev, MidiInfo *minfo, int mode)
{
if (mode == EV_PLAY && minfo->accept_all_off)
seq_sound_off_all();
}
/* tone bank change */
static void ToneBank(MidiEvent *ev, MidiInfo *minfo, int mode)
{
ChannelStat *ch = &channels[ev->channel];
if (minfo->midi_mode == MODE_XG && IsMSB(ev)) {
ch->bankmode = BankOf(ev);
/* XG MSB value; not normal bank selection */
switch (BankOf(ev)) {
case 127: /* remap to drum channel */
if (! CHAN_ISDRUM(ev->channel)) {
BITON(channel_drums, DRUMBIT(ev->channel));
ch->bank = 128;
if (mode == EV_PLAY)
seq_set_drumchannels(channel_drums);
}
break;
default: /* remap to normal channel */
if (CHAN_ISDRUM(ev->channel)) {
BITOFF(channel_drums, DRUMBIT(ev->channel));
if (mode == EV_PLAY)
seq_set_drumchannels(channel_drums);
}
ch->bank = BankOf(ev);
if (mode == EV_PLAY && ch->bankmode != 0)
seq_set_bank(ev->channel, ch->bank);
break;
}
return;
} else if (minfo->midi_mode == MODE_GS && !IsMSB(ev))
return; /* ignore LSB bank in GS mode (used for mapping) */
/* normal bank controls; accept both MSB and LSB */
if (! CHAN_ISDRUM(ev->channel)) {
if (ch->bankmode == 0) {
if (minfo->midi_mode == MODE_XG &&
(BankOf(ev) == 64 || BankOf(ev) == 126))
channels[ev->channel].bank = 0;
else
channels[ev->channel].bank = BankOf(ev);
if (mode == EV_PLAY)
seq_set_bank(ev->channel, BankOf(ev));
}
}
if (mode == EV_CONTROL)
ctl->bank(ev->channel, channels[ev->channel].bank);
}
/* print a text (SYSEX) */
static void PrintLyric(MidiEvent *ev, MidiInfo *minfo, int mode)
{
if (mode != EV_CONTROL && mode != EV_STARTUP) return;
if (ev->p.text)
ctl->cmsg(CMSG_TEXT, 0, ev->p.text);
}
/* tempo change */
static void Tempo(MidiEvent *ev, MidiInfo *minfo, int mode)
{
minfo->tempo = ev->p.val;
}
/* sysex master volume change */
static void MasterVolume(MidiEvent *ev, MidiInfo *minfo, int mode)
{
minfo->master_volume = ev->p.val * 100 / 127;
if (mode == EV_PLAY)
seq_change_volume(minfo->volume_base * minfo->master_volume / 127);
}
/* fine tune (RPN control) */
static void FineTune(MidiEvent *ev, MidiInfo *minfo, int mode)
{
if (!minfo->do_tuning) return;
channels[ev->channel].finetune = ev->p.val - 8192;
if (mode == EV_PLAY)
seq_detune(ev->channel, channels[ev->channel].coarsetune,
channels[ev->channel].finetune);
}
/* coarse tune (RPN control) */
static void CoarseTune(MidiEvent *ev, MidiInfo *minfo, int mode)
{
if (!minfo->do_tuning) return;
channels[ev->channel].coarsetune = ev->p.val - 8192;
if (mode == EV_PLAY)
seq_detune(ev->channel, channels[ev->channel].coarsetune,
channels[ev->channel].finetune);
}
/* AWE32 NRPN events */
static void NRPNEvent(MidiEvent *ev, MidiInfo *minfo, int mode)
{
int type, val, cval;
if (! minfo->use_fx)
return;
type = ev->p.par[0];
val = ev->p.par[1] - 8192;
if (type < 0 || type >= num_awe_effects) {
ctl->cmsg(CMSG_INFO, 2, "[nrpn unknown ch=%d type=%d val=%d]",
ev->channel, type, val);
return;
}
cval = awe_effects[type].convert(val);
ctl->cmsg(CMSG_INFO, 2, "[nrpn ch=%d %s: %d -> %x]",
ev->channel, awe_effects[type].name, val, (unsigned short)cval);
if (mode == EV_PLAY)
seq_send_effect(ev->channel, awe_effects[type].awe_effect, cval);
else if (mode == EV_CONTROL)
ctl->effects_change(ev->channel, type, val);
}
/* SC88 NRPN events */
static void GSNRPNEvent(MidiEvent *ev, MidiInfo *minfo, int mode)
{
int i, type, val, cval;
if (! minfo->use_fx)
return;
type = ev->p.par[0];
val = ev->p.par[1];
for (i = 0; i < num_gs_effects; i++) {
if (gs_effects[i].control == ev->p.par[0]) {
cval = gs_effects[i].convert(minfo, val);
if (mode == EV_PLAY)
seq_add_effect(ev->channel, gs_effects[i].awe_effect, cval);
break;
}
}
}
awemidi-0.4.2b/glinfo.c 100644 765 12 3247 6413667746 13224 0 ustar iwai wheel /*----------------------------------------------------------------
* glinfo.c -- default settings of midi info record
*----------------------------------------------------------------*/
#include <stdio.h>
#include <string.h>
#include "config.h"
/* default settings */
MidiInfo gl_default = {
/* midi info: format, tracks, divisions */
0, 0, 0,
/* variable: startcs, endcs, prevcs, curcs */
0, 0, 0, 0,
/* variable: updatecs, echocs, prevev, curev, tempo */
0, 0, 0, 0, 0,
/* midi data: nlists, list */
0, NULL,
/* configuration data */
/* controls: chorus, reverb, chorusdepth, reverbdepth */
DEF_CHORUS_MODE, DEF_REVERB_MODE, DEF_CHORUS_DEPTH, DEF_REVERB_DEPTH,
/* controls: volume, master, bass, treble */
DEF_VOLUME, 100, DEF_BASS, DEF_TREBLE,
/* controls: alloff, realpan, multi, checkcs */
DEF_ALLOFF, DEF_REALTIME_PAN, DEF_MULTIPART, DEF_CHECKCS,
/* controls: midi_mode, gsmacro, xgmacro, chhprior, do_tuning */
MODE_GM, DEF_GSMACRO, DEF_XGMACRO, DEF_XGMAP, DEF_CHN_PRIOR, TRUE,
/* controls: drumflag, tracks */
DEF_DRUMFLAG, DEF_TRACKS,
/* controls: volscale, skipblank */
DEF_VOLSCALE, DEF_SKIPBLANK,
/* controls: chn_volflag, base_offset, parse_title */
0, 0, DEF_PARSETITLE,
/* controls: seq_buffered, use_echoback */
DEF_SEQ_BUFFERED, DEF_USE_ECHOBACK,
/* controls: use_effects */
DEF_USEFX,
/* effect sense: */
{
DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE,
DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY
},
/* name, dynamicload, xgload, sample_cleared, loaded */
NULL, NULL, NULL, FALSE, FALSE,
};
void copy_midi_info(MidiInfo *dst, MidiInfo *src, char *fname)
{
memcpy(dst, src, sizeof(MidiInfo));
dst->filename = fname;
}
awemidi-0.4.2b/midievent.h 100644 765 12 6372 6413671272 13726 0 ustar iwai wheel /*----------------------------------------------------------------
* midievent.h
* read midi file and parse events
*----------------------------------------------------------------*/
#ifndef MIDIEVENT_H_DEF
#define MIDIEVENT_H_DEF
typedef struct _MidiEvent {
int time; /* tempo counter */
int csec; /* realtime csec counter */
unsigned char channel, type;
union {
short par[2]; /* RPN events */
int val; /* others */
char *text; /* text message */
} p;
} MidiEvent;
#define KeyOf(ev) ((ev)->p.par[0])
#define VelOf(ev) ((ev)->p.par[1])
#define CtrlOf(ev) ((ev)->p.par[0])
#define CValOf(ev) ((ev)->p.par[1])
#define BankOf(ev) ((ev)->p.par[0])
#define IsMSB(ev) ((ev)->p.par[1])
#define FX_CUTOFF 0
#define FX_RESONANCE 1
#define FX_ATTACK 2
#define FX_RELEASE 3
#define FX_VIBRATE 4
#define FX_VIBDEPTH 5
#define FX_VIBDELAY 6
#define FX_NUMS 7
typedef struct _MidiInfo {
int format;
int tracks;
int divisions;
/* variable data */
int startcs, endidx; /* start csec, end index */
int prevcs, curcs; /* prev/current csec */
int updatecs, echocs; /* timer for control update & echo back */
int prevev, curev; /* event counter */
int tempo; /* current tempo */
/* midi data */
int nlists;
MidiEvent *list;
/* other control parameters */
int chorus, reverb;
int chorusdepth, reverbdepth;
int volume_base, master_volume;
int bass_level, treble_level;
int accept_all_off;
int realtime_pan;
int multi_part;
int check_same_csec;
int midi_mode; /* GM=0, MT32=1, MT32(conv)=2, GS=3, XG=4, CMF=5/6 */
int check_gs_macro;
int check_xg_macro, xg_mapping;
int chn_prior;
int do_tuning;
unsigned int drumflag;
int track_nums;
int volscale;
int skip_blank_head;
int chn_volflag;
int base_offset; /* in semitones */
int parse_title;
int seq_buffered, use_echoback;
int use_fx;
int fx_sense[FX_NUMS];
char *filename;
char *dynamicload;
char *xgload;
int sample_cleared;
int dynamic_loaded;
} MidiInfo;
enum {
MODE_GM = 0, MODE_MT32, MODE_MT32_INT, MODE_GS, MODE_XG,
MODE_CMF, MODE_CMF2
};
#define DRUMBIT(ch) (1 << (ch)) /* ch: 0 - 31 */
#define MidiTempo(a,b,c) ((int)(a) * 65536 + (int)(b) * 256 + (int)(c))
/* Midi events */
enum {
ME_NONE,
ME_NOTEON, ME_NOTEOFF, ME_KEYPRESSURE, ME_CONTROL,
ME_PROGRAM, ME_CHNPRESSURE, ME_PITCHWHEEL,
ME_TEMPO, ME_PITCH_SENS, ME_TONE_BANK,
ME_ALL_SOUNDS_OFF, ME_RESET_CONTROLLERS, ME_ALL_NOTES_OFF,
ME_LYRIC,
ME_MASTER_VOLUME,
ME_FINETUNE, ME_COARSETUNE,
ME_AWE_FX, ME_GS_FX,
ME_EOT,
};
/* default values */
#define MIDI_DEFAULT_TEMPO 500000
enum {
EV_PLAY=0, EV_CONTROL, EV_SETSTAT, EV_STARTUP, EV_PRELOAD,
};
enum {
SY_CHORUS_MODE, SY_REVERB_MODE,
};
/* prototypes */
MidiEvent *ReadMidiFile(FILE *fp, MidiInfo *infop);
void FreeMidiFile(MidiInfo *infop);
int do_midi_event(MidiEvent *ev, MidiInfo *minfo, int mode);
extern MidiInfo glinfo, gl_default;
int play_midi_file(MidiInfo *minfo);
int midi_open(MidiInfo *mp);
void midi_close(MidiInfo *mp);
void preload_sample(MidiInfo *mp, char *fontfile);
void add_preload(int chn, int preset, int bank, int keynote);
void load_sub_sf(MidiInfo *mp, char *args);
int find_midi_index(MidiInfo *mp, int before_reading);
void copy_midi_info(MidiInfo *dst, MidiInfo *src, char *fname);
void channel_init(MidiInfo *mp);
void channel_set(MidiInfo *mp);
#endif
awemidi-0.4.2b/README 100644 765 12 56027 6416447756 12506 0 ustar iwai wheel ================================================================
AWE32 MIDI Player for Linux / FreeBSD
version 0.4.2b; Oct. 7, 1997
================================================================
* GENERAL NOTES
This is a midi player program which supports AWE32 sound driver v0.4.2
by Takashi Iwai. Before using this program, you have to load sound
samples onto the driver. Please read the document in utility package
awesfx-0.4.2.tar.gz. All AWE32 driver and utilities can be downloaded
from:
http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/
* USAGE
drvmidi [options] midifile[.gz] ...
You can give the compressed or gzipped files on command line.
These files are expanded automatically inside the program.
The new drvmidi accepts not only normal MIDI files, but also RIFF
combined MIDI files and CMF files.
If drvmidi finds a file which has the same file name with the
extension ".id", drvmidi reads this file as an option file. You can
specify its own settings without the command line options. See
"option file format" section for the syntax of the option file. The
option file is editable via File/Edit Options menu in tkmidi.
Ver.0.4.2 can play XG MIDI with GS soundfont samples. You need to
load XG SFX and drum maps for this emulation together with --xgmap
option to play properly XG sounds.
* CONTROL PROGRAMS
- Tkmidi
Tkmidi provides an external control to drvmidi in Tcl/Tk window.
This works just as Tcl/Tk interface of drvmidi except tkmidi uses
the general pipe interface to communicate with drvmidi.
All the long command line options starting double minus (--) are
passed to drvmidi, but short options are unavailable.
Tkmidi accepts the directory as command line arguments. It search the
files with .mid extension under the directory, and add them to the
file list. For example, to play all midi files under the current
directory, you can start tkmidi as follows:
% tkmidi .
- Atmidi
Atmidi is another external control program which employs Athena Widget
sets. This is an example program, and supports only limited
functions.
* TKMIDI AND TCL/TK INTERFACE MENUES
- File Menu
Open File: open a MIDI file from the file selection window.
Not available during play midi files.
Clear List: clear all MIDI songs selected in the list.
Not available during play midi files.
Save Config: save the current configuration in user config
file ~/.tkawe. The play style, display style, volume
parameters, chorus/reverb modes are saved. This file is
read when starting drvmidi.
Edit Options: edit option files in the selection list, or the
system default options. A file selection dialog appear
when this menu is selected, then choose a midi file from
the file list. Most of playing options can be set via
option editor window. If the corresponding midi option
file doesn't exist, a new option file is created, which
name starts from period and has ".id" extension. For
example, when "foo.mid" is edited, an option file
".foo.id" will be created on the current directory. The
system default option can be editted by selecting "Edit
Default" in file selection dialog. The options are saved
on the system resource file, ~/.drvmidirc.
- Mode Menu
Repeat: play the first file again repeatedly after all files
are played.
Shuffle: shuffle play mode. The order of the playing files is
shuffled and selected randomly.
Auto Start: start playing midi files automatically when the
program runs.
Auto Exit: exit the program automatically when finishing all
the files.
Confirm Quit: pop up a dialog to confirm when the program is
going to quit.
- Display Menu
This menu contains six toggle button items, File List, Time,
Messages, Volume, Buttons, and Trace. Each item selects to
display or remove the corresponding window. The current
display style can be saved by "Save Config".
File List: File list window displays all the MIDI files.
You can start or change the file currently playing by
clicking the file name in it.
Time: Time window displays the current time and total time,
and a scale bar. You can control the current time
directly by dragging the thumb in the scale bar.
Messages: Message window displays messages in the MIDI file.
To clear the window, push "clear" button under the message
window.
Volume: Volume window controls total and equalizer volumes.
The total volume can be changed by dragging the upper
scale bar from 0 to 200%. Other two lower scale bars
control bass and treble volumes. The volume status will
be saved by "Save Config" in File menu.
Buttons: Button window displays operational buttons to play,
stop, previous, backward, pause, forward, next, and quit.
Each button will be activated or disabled according the
playing status.
Trace: Trace window dnisplay volume bars of each MIDI
channel. The red bar means the drum channel, and green
bar means the normal one. The yellow arrows under volume
bars indicate the panning positions, and the lower numbers
indicate the preset number in hex.
- Chorus & Reverb Menues:
Change the current chorus and reverb modes immediately.
These modes may be changed according to MIDI sysex messages.
- Key Bindings
[Return] play
[c] stop
[Space] [s] pause / continue
[q] quit
[n] next song
[p] restart or previous song
[r] restart
[Right] forward two seconds
[Left] backward two seconds
[Up] [v] volume up 5%
[Down] [V] volume down 5%
[u] increase the base key
[d] decrease the base key
* NCURSES INTERFACE
- Key bindings
[h] [?] [F1] display help window
[Space] pause / continue
[q] quit
[Up] [V] volume up 10 %
[Down] [v] volume down 10 %
[n] next song
[p] previous song
[r] restart
[Right] [f] forward two seconds
[Left] [b] backward two seconds
[c] [C] change chorus mode
[e] [E] change reverb mode
[u] increase the base key
[d] decrease the base key
* DYNAMIC SOUNDFONT LOADING
The dynamic soundfont loading is helpful for users who have only
limited DRAM on the soundcard. If the dynamic loading is enabled,
drvmidi loads the specified soundfont or virtual bank file
automatically when it receives each MIDI file.
Since only necessary samples are loaded, the required memory size of
soundcard is reduced. For example, 2MB GS font can be used on 512KB
RAM in most of MIDI files.
In the case of XG midi file, specify the font file via --xgload
option. The specified file should contain proper XG mappings, and
GS font file as default font. Usually, this option should be used
together with --xgmap option.
In other cases, the font file specified in --dynamic option is used as
the dynamic font file. In both cases, the specified file can be a
soundfont file or a virtual bank file. The virtual bank file can
define each preset from various soundfont files, and preset mappings.
See the description in awesfx utility for more details.
* OPTIONS
-i, --interface=type
Specifies the interface mode. "k" for Tcl/Tk interface, and
"d" for dumb interface. "n" means ncurses interface.
The default is Tcl/Tk interface.
-D, --drum=channel
Used to change drum channels. If the argument is larger
than zero, this channel is treated as a drum channel.
If less than zero, minus of this value is treated as a normal
channel.
ex)
% drvmidi --drum=-10 --drum=16 foo.mid
will set channel 10 as a normal channel, and channel 16 as
a drum one.
As default, drvmidi uses only channel 10 (and 26) as drum
channels.
--drumflag=val
Set all drum channels as 32bit flags. For example, the argument
0x02008200 will set channel 10, 16, and 26 as drum channels.
-c, --chorus=mode
-r, --reverb=mode
They supply the chorus and reverb modes. The AWE32 card
has eight chorus modes and eight reverb modes as default.
The chorus/reverb mode specified in this option may be changed
by sysex control in MIDI file.
The system default modes are:
chorus mode
0: chorus 1
1: chorus 2
2: chorus 3
3: chorus 4
4: feedback
5: flanger
6: short delay
7: short delay 2
reverb mode
0: room 1
1: room 2
2: room 3
3: hall 1
4: hall 2
5: plate
6: delay
7: panning delay
The other modes can be defined via setfx utility program.
--chorusdepth=val
--reverbdepth=val
Define the default chorus/reverb depth, respectively.
The arguments are MIDI control values from 0 to 127.
These values are not used when chorus and reverb values are
defined in each MIDI file.
-V, --volume=percent
Set the master volume in percent unit. Default is 100.
-S, --volscale=percent
Set volume scaling factor. The argument is in percent.
It still accepts a float number from 0 to 5.0 for
compatibility with older version. For example, if a number
0.8 is given, the MIDI volume will be output in 80% of the
normal scale.
Default factor is 100.
-O, --acceptall[=bool]
Toggle to accept or ignore "all notes off" and "all sounds off"
events. Some MIDI file contains these events. Either of
"on", "off", "yes", "no", "true", or "false" can be specified
as an optional boolean argument.
Default is ON.
-P, --realpan[=bool]
Toggle to enable or disable realtime panning change.
Emu8000 chip often causes a click noise when changing the panning
position in real time during playing a voice.
Default is ON.
--multipart[=bool]
Toggle to enable or disable multi MIDI parts.
If this option is enabled, the the tracks later than the given
track number are treates as MIDI part B.
Note that even if this option is disabled, multi mode is
activated when GS multi mode sysex is received (see SYSEX
MACROS section).
Default is OFF.
-T, --tracks=num
Specify the track number of multi part. For example, to deal
with tracks from 0 to 16 as part A, and from 17 to 32 as part
B, specify '--tracks=16'. The value -1 means to disable multi
MIDI parts (equivalent with --multipart=off).
The default value is 16.
-M, --mt32=mode
Set MT32 program mode. The argument must be from 0 to 2.
When mode is 0, no conversion is done (GM/GS mode). When 1 is
given, bank number 127 is automatically set. If you have a
proper MT-32 set, you should specify 1. Otherwise, when the
argument is 2, each preset number is converted internally to
the corresponding GM preset number.
Default is 0.
-C, ---samecsec[=bool]
Toggle to enable or disable the note-off event check.
The sequencer device can handle events only in 1/100 sec unit,
so often both the note on and off events occur at the same time.
To prevent this problem, drvmidi checks the time of each note
off event, and send it after 1/100 sec later if necessary.
Default is ON.
-G, --gsmacro[=bool]
Toggle to enable or disable GS sysex controls.
When GS part check is on, drvmidi accepts chorus mode, reverb
mode, and GS part change messages.
Default is ON.
-X, --xgmacro[=bool]
Toggle to enable or disable XG bank controls.
Default is ON.
--xgmap=bool
Control XG SFX and drum mapping. If this option is enabled,
bank #64 and #126 are used for XG SFX sounds. XG drumsets are
shifted from #64 to #126 to avoid conflicts with GS sounds.
To support this mapping, you need to load XG map file on AWE
drvier. The --xgload option below is helpful for dynamic
loading of this map file.
--xgload=file
Set the font file to load automatically when drvmidi receives
a XG midi file. This just behaves as --dynamic option, but
invoked only when the MIDI file is XG.
If "none" is specified as the argument, this function is
suppressed.
-s, --autoskip[=bool]
Toggle auto skip mode. If this option is enabled, drvmidi skips
to the first note on event immediately after starting.
Default is OFF.
--chnprior[=bool]
Enable or disable MIDI channel proprity mode.
If this is enabled, the drum channels have the highest priority,
and the lower channels have higher priority. A new voice is
allocated from the channel with lower priority. This
mechanism is available only on GS or XG mode.
Default is OFF.
-L, --dynamic=fontfile
Load the specified soundfont file automatically.
If this option is enabled, drvmidi invokes sfxload program to
load soundfont (or virtual bank) file on the driver.
If "none" is specified as the argument, this function is
suppressed.
-v, --verbose[=level]
Increase or set the verbosity level.
--parsetitle[=bool]
Enable or disable to display the first text event as a title.
Default is ON.
-m, --mode=modifiers
Set playing mode. Some modes are valid only on Tcl/Tk
interface. The following mode identifiers may be available:
t Turn tracing mode on. It shows realtime bar graph and
program number of each channel in the trace window.
s Shuffle play mode. The order of the playing files is
shuffled and selected randomly.
n Normal play mode. The songs are played in the order
of specified command line arguments.
p Start play automatically.
q Exit drvmidi automatically after finishing all songs.
r Loop the playing songs.
-t, --trace
Turns tracing mode on. Equivalent with -mt.
-d, --display=xdisp
Set window display. Valid only on Tcl/Tk interface.
-g, --geometry=xgeom
Set window geometry. Valid only on Tcl/Tk interface.
--effect=file
Set user defined chorus/reverb configurations. Valid only on
Tcl/Tk interface.
--convert=ext/converter
Define new file conversion rule for the specified file
extension. The argument gives both file extension and the
converter program separated by slash letter. The converter
program must read the source file from stdin and output a MIDI
format to stdout. For example, to convert .rcp file to midi
format via rcptomid program automatically, set the option as
follows:
--convert=rcp/rcptomid
Also, to accept the gzipped file, too, define the following
with addition to the above one:
--convert="rcp.gz/zcat %s | rcptomid"
The %s is replaced with the corresponding source file name.
--usefx=bool
Enable or disable effect controls. This feature is still
experimental. When this option is enabled, drvmidi emulates
SC88 NRPN (TVF/EG parameters) and AWE32 NRPN controls in GM/GS
mode, and accepts hermonic (cc#71), brightness (cc#74), attack
and release time (cc#72 and #73) in XG mode.
--fx_cutoff=int
Change TVF cut-off parameter sensitivity. Valid only when
usefx option is set. Default value is 170.
--fx_resonance=int
Change TVF resonance parameter sensitivity. Valid only when
usefx option is set. Default value is 6.
--fx_attack=int
Change EG attack time sensitivity. Valid only when usefx
option is set. Default value is 50.
--fx_release=int
Change EG release time sensitivity. Valid only when usefx
option is set. Default value is 50.
--fx_vibrate=int
Change vibrato rate sensitivity. Valid only when usefx
option is set. Default value is 30.
--fx_vibdeepth=int
Change vibrato depth sensitivity. Valid only when usefx
options is set. Default value is 4.
--fx_vibdelay=int
Change vibrato delay sensitivity. Valid only when usefx
options is set. Default value is 1500.
--seqbuf=bool
Enable or disable the buffered output to sequencer device.
See event handling section for more details.
Default is ON.
--seqecho=bool
Enable or disable the timer synchronization via echo back from
sequencer device. See event handling section for more details.
Default is ON.
* OPTION FILE FORMAT
title string
Set title of this midi file. Displayed on behalf of the file
name.
parsetitle bool
Enable or suppress to parse the title string from MIDI file
automatically.
chorus mode
reverb mode
Set chorus and reverb modes, respectively. The arguments are
identical with the command line options. See --chorus and
--reverb options, respectively.
chorusdepth value
reverbdepth value
Set default chorus and reverb depth, respectively. The
arguments are identical with the command line options.
volume percent
Total volume in percent unit (see --volume option).
drum channel
Set drum a channel status. Equivalent with --drum option.
drumflag bits
Set drum channels. The argument is a digit number indicating
drum channels (See --drumflag option).
acceptall bool
Accpet or disable all notes/sounds off events (see
--acceptall option).
multipart bool
Enable or disable the multi MIDI part (see --multipart option).
tracks num
Set track number of multi part (see --tracks option).
samecsec bool
Enable or suppress the note off event check (see --samecsec
option).
gsmacro bool
Enable or suppress the GS sysex messages (see --gsmacro
option).
xgmacro bool
Enable or suppress the XG bank controls (see --xgmacro option)
xgmap bool
Enable or suppress the XG SFX & drum mapping (see --xgmap
option).
xgload fontfile
Use the specified file for XG dynamic loading (see --xgload
option).
subsf arguments..
Load optional SoundFont file automatically.
The arguments are passed to sfxload internally together with
"-x -b1" options. For example, to load sfx.sf2 for sample.mid,
set "subsf sfx.sf2" in sample.id file.
Note that this commmand will not work together with dynamic
loading.
volscale scale
Set volume scaling parameter (see --volscale option).
mt32 mode
Set MT32 program mode. The argument is from 0 to 2.
See --mt32 option for each meaning.
dynamic fontfile
Use the specified font file for dynamic loading (see --dynamic
option).
chnprior bool
Enable or disable MIDI channel proprity mode (see --chnprior
option).
chnvolume channel scale
Set volume scale for each specified channel. The scale factor
is given as same way as in volscale command.
offset value
Change the base key. The argument is an integer number.
If the number -1 is given, all notes (except drums) will be
played in one semitone lower, and if 12 is given, the sounds
become one octave higher than normal output.
usefx bool
Enable or disable effect change controls. Equivalent with
--usefx option.
Note that this feature is still experimental.
fx_* value
Set effect change parameters. See the corresponding command
line option. Valid only when usefx option is enabled.
* SYSTEM RESOURCE FILE:
You can set default option arguments in the system resource file.
There're two files loaded as default.
$HOME/.drvmidirc
/etc/drvmidirc
The syntax is as follows:
default options..
The first argument is arbitrary so far, but should be "default" for
future extensions. The remaining arguments are identical with command
line options described above.
The resource file can be changed via File/Edit Options menu in tkmidi.
Althogh drvmidi accepts both short and long options in the resource
file, tkmidi can edit only long options.
* EVENT HANDLING
As default, drvmidi once writes music events on a buffer for up to two
seconds forward, then sends them to the sequencer device. This
prevents a pausing even when some heavy process (like garbage
collection in emacs) is working unlike the previous version (ver.0.4.0
or older).
Since the events are sent to the device asynchronously, the program
need to know the playing actual time by the other means. Then, drvmidi
checks the system timer to know the current time and update the status
like volume bars. Also, sends an echo back event once per second to
the sequencer, and synchronize the internal timer when receiving the
signal from sequencer device.
It is known that on some FreeBSD systems this function doesn't work
correctly. In such a case, try the option --autoskip (I have no idea
why this solves the bug). Also, you should try to disable --seqecho
or --seqbuf option.
* SUPPORTED MIDI CONTROLS
#0 bank select (MSB)
#1 modulation wheel
#6 data entry (MSB)
#7 main volume
#10 panning position
#11 expression volume
#32 bank select (LSB)
#38 data entry (LSB)
#64 sustain
#66 sostenuto
#67 soft pedal (experimental)
#71 hermonic content (XG only)
#72 attack time (XG only)
#73 release time (XG only)
#74 brightness (XG only)
#91 reverb effects send
#93 chorus effects send
#98 NRPN LSB
#99 NRPN MSB
#100 RPN LSB
#101 RPN MSB
#120 all sounds off
#121 reset controllers
#123 all notes off
* SYSEX MACROS
GM on
F0 7E 7F 09 01 F7
GS reset
F0 41 10 42 12 40 00 00 00 cs F7
GS reverb mode
F0 41 10 42 12 40 01 30 XX cs F7
where XX denotes the reverb mode above (from 0 to 7).
GS chorus mode
F0 41 10 42 12 40 01 38 XX cs F7
where XX denotes the chorus mode above (from 0 to 7).
GS drum part change
F0 41 10 42 12 40 1X 15 YY cs F7
where X denotes the channel and YY denotes a drum flag.
GS master volume
F0 41 10 42 12 40 00 04 XX cs F7
where XX denotes the volume (from 0 to 7).
GS multi mode
F0 41 10 42 12 00 00 7F XX cs F7
where XX=0: double module mode, XX=1: single module mode.
XG on
F0 43 10 4C 00 00 7E 00 F7
* RPN CONTROLS
- 0000 : pitch bend sensitivity (MSB/LSB). 1 semitone per 128.
- 0001 : fine tuning (MSB/LSB). in 100/8192 cent unit, and
no tuning when MSB/LSB=8192.
- 0002 : coarse tuning (MSB/LSB). 1 semitone per 128, and no
tuning when MSB/LSB=8192.
- 7F7F : lock-in. values are ignored.
* NRPN CONTROLS
GS (SC88) NRPN Controls
NRPN MSB/LSB, Data Entry (MSB only, center=64)
- NRPN 1/8: Vibrato rate
- NRPN 1/9: Vibrato depth
- NRPN 1/10: Vibrato delay
- NRPN 1/32: TVF cutoff
- NRPN 1/33: TVF resonance
- NRPN 1/99: EG attak time
- NRPN 1/100: EG decay time
- NRPN 1/102: EG release time
AWE32 NRPN Controls
NRPN MSB/LSB, Data Entry MSB/LSB (center=8192)
- NRPN 127/0: LFO1 delay
- NRPN 127/1: LFO1 freq
- NRPN 127/2: LFO2 delay
- NRPN 127/3: LFO2 freq
- NRPN 127/4: EG1 delay
- NRPN 127/5: EG1 attack
- NRPN 127/6: EG1 hold
- NRPN 127/7: EG1 decay
- NRPN 127/8: EG1 sustain
- NRPN 127/9: EG1 release
- NRPN 127/10: EG2 delay
- NRPN 127/11: EG2 attack
- NRPN 127/12: EG2 hold
- NRPN 127/13: EG2 decay
- NRPN 127/14: EG2 sustain
- NRPN 127/15: EG2 release
- NRPN 127/16: initial pitch
- NRPN 127/17: LFO1 pitch
- NRPN 127/18: LFO2 pitch
- NRPN 127/19: EG1 pitch
- NRPN 127/20: LFO1 volume
- NRPN 127/21: Cutoff
- NRPN 127/22: Resonance
- NRPN 127/23: LFO1 cutoff
- NRPN 127/24: EG1 cutoff
- NRPN 127/25: chorus
- NRPN 127/26: reverb
* COPYRIGHT
Copyright (C) 1996,1997 Takashi Iwai
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., 675 Mass Ave, Cambridge, MA 02139, USA.
Takashi Iwai <iwai@dragon.mm.t.u-tokyo.ac.jp>
awemidi-0.4.2b/misc.tcl 100644 765 12 15054 6410160255 13234 0 ustar iwai wheel #----------------------------------------------------------------
# Miscellaneous procedures
# Copyright (c) 1996,1997 Takashi Iwai
#----------------------------------------------------------------
#----------------------------------------------------------------
# tk easy programming
#----------------------------------------------------------------
#
# grab a window
#
set tk_priv(grablist) {}
proc push-grab {w} {
global tk_priv
set prevw [grab current $w]
lappend tk_priv(grablist) $prevw
grab $w
}
proc pop-grab {} {
global tk_priv
set w [lrange $tk_priv(grablist) end end]
set tk_priv(grablist) [lreplace $tk_priv(grablist) end end]
if {[winfo exists $w]} {
grab $w
}
}
#
# sec to time string
#
proc sec2time {sec} {
if {$sec >= 0} {
return [format "%02d:%02d" [expr $sec / 60] [expr $sec % 60]]
} else {
set sec [expr -$sec]
return [format "-%02d:%02d" [expr $sec / 60] [expr $sec % 60]]
}
}
#
# numeric binding:
# only numerical key and some controls are available for input.
#
proc numeric-bind {w} {
bind $w <Any-Key> {
if {"%A" != "" && [regexp "\[0-9\]+" %A]} {
%W insert insert %A
tk_entrySeeCaret %W
} elseif {"%K" == "Return"} {
global tk_priv
focus none
}
}
bind $w <Key-Delete> {tk_entryBackspace %W; tk_entrySeeCaret %W}
bind $w <Key-BackSpace> {tk_entryBackspace %W; tk_entrySeeCaret %W}
bind $w <Control-Key-h> {tk_entryBackspace %W; tk_entrySeeCaret %W}
bind $w <Control-Key-d> {%W delete sel.first sel.last; tk_entrySeeCaret %W}
bind $w <Control-Key-u> {%W delete 0 end}
}
#
# make a listbox
#
proc my-listbox {w title size {dohoriz 1} {multiple 0}} {
frame $w
label $w.label -text $title -relief flat
pack $w.label -side top -fill x -anchor w
regexp "(\[0-9\]+)x(\[0-9\])" $size foo width height
if {$multiple} {
set mode multiple
} else {
set mode browse
}
return [make-listbox $w $width $height $dohoriz $mode]
}
proc make-listbox {w width height {dohoriz 0} {mode browse}} {
scrollbar $w.yscr -command "$w.list yview"
pack $w.yscr -side right -fill y
set lopt [list -width $width -height $height]
lappend lopt -exportselection 0 -selectmode $mode
if {$dohoriz} {
scrollbar $w.xscr -command "$w.list xview" -orient horizontal
pack $w.xscr -side bottom -fill x
eval listbox $w.list -relief sunken -setgrid yes $lopt\
[list -yscroll "$w.yscr set"]\
[list -xscroll "$w.xscr set"]
} else {
eval listbox $w.list -relief sunken -setgrid yes $lopt\
[list -yscroll "$w.yscr set"]
}
pack $w.list -side top -fill both -expand yes
return $w.list
}
#----------------------------------------------------------------
# dialog pop-up
#----------------------------------------------------------------
proc my-dialog {w title defbtn canbtn buttons} {
toplevel $w -class Dialog
wm title $w $title
wm iconname $w $title
label $w.title -text $title -relief raised -bd 1
pack $w.title -side top -fill x
frame $w.f -relief raised -bd 1
pack $w.f -side top -fill both
frame $w.buttons -relief raised -bd 1
pack $w.buttons -side bottom -fill both
set i 0
foreach but $buttons {
button $w.buttons.c$i -text [lindex $but 0] -command [lindex $but 1]
if {$defbtn != "" && $i == $defbtn} {
frame $w.buttons.default -relief sunken -bd 1
raise $w.buttons.c$i $w.buttons.default
pack $w.buttons.default -side left -expand 1\
-padx 3m -pady 2m
pack $w.buttons.c$i -in $w.buttons.default -padx 2m -pady 2m \
-ipadx 2m -ipady 1m
bind $w <Return> "$w.buttons.c$i flash; $w.buttons.c$i invoke"
} else {
pack $w.buttons.c$i -side left -expand 1 \
-padx 3m -pady 3m -ipadx 2m -ipady 1m
if {$canbtn != "" && $i == $canbtn} {
bind $w <Key-Escape> "$w.buttons.c$i flash; $w.buttons.c$i invoke"
}
}
incr i
}
return $w.f
}
#----------------------------------------------------------------
# warning/question dialog
#----------------------------------------------------------------
proc my-message-dialog {w title text bitmap defbtn canbtn args} {
#puts stderr $text
return [eval tk_dialog [list $w $title $text $bitmap $defbtn] $args]
}
proc warning {message} {
my-message-dialog .warning "Warning" $message warning 0 0 { OK }
}
proc error {message} {
my-message-dialog .error "Error" $message error 0 0 { OK }
}
proc information {message} {
my-message-dialog .info "Information" $message info 0 0 { OK }
}
proc question {message {defrc 1}} {
global tk_priv
if {$defrc} {
set defbtn 0
set canbtn 1
} else {
set defbtn 1
set canbtn 0
}
return [expr ![my-message-dialog .yesno "Question" $message question\
$defbtn $canbtn "Yes" "No"]]
}
#----------------------------------------------------------------
# pseudo random routine without TclX
#----------------------------------------------------------------
set pseudo_random [catch {random 1}]
if {$pseudo_random} {
set pseudo_random_next -1
proc random {max} {
global pseudo_random pseudo_random_next
set pseudo_random_next [expr $pseudo_random_next * 1103515245 + 12345]
return [expr ($pseudo_random_next/65536) % $max]
# Or, use bash's random routine instead...
# return [expr [exec bash -c {echo $RANDOM}] % $max]
}
}
proc init-random {num} {
global pseudo_random pseudo_random_next
if {$pseudo_random} {
set pseudo_random_next $num
} else {
random seed $num
}
}
#----------------------------------------------------------------
# convert string to list
#----------------------------------------------------------------
proc isspace {c} {
if {[lsearch {" " "\t" "\n" "\r"} $c] != -1} {
return 1
} else {
return 0
}
}
#
# split a string to token list
#
proc splittoken {str} {
set in_escape 0
set in_space 1
set quote ""
set vlist {}
set token ""
foreach c [split $str {}] {
if {$in_escape} {
append token $c
set in_escape 0
} elseif {$c == $quote} {
set quote ""
} else {
if {$quote == "" && [isspace $c]} {
if {! $in_space} {
lappend vlist $token
set token ""
}
set in_space 1
} else {
if {$c == "\"" || $c == "'"} {
set quote $c
} elseif {$c == "\\"} {
set in_escape 1
} else {
append token $c
}
set in_space 0
}
}
}
if {! $in_space} {
lappend vlist $token
}
return $vlist
}
#
# add escape letter before special letters
#
proc escstring {str} {
set v ""
foreach i [split $str {}] {
if {$i == "\"" || $i == "'" || $i == "\\"} {
append v "\\"
}
append v $i
}
return $v
}
awemidi-0.4.2b/seq.c 100644 765 12 24667 6414513562 12552 0 ustar iwai wheel /*================================================================
* sequencer device handler
*================================================================*/
#include <stdio.h>
#include <math.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <math.h>
#include <fcntl.h>
#ifdef linux
#include <sys/soundcard.h>
#include <linux/awe_voice.h>
#elif defined(__FreeBSD__)
#include <machine/soundcard.h>
#include <awe_voice.h>
#endif
#include "seq.h"
#include "midievent.h"
#include "channel.h"
#include "controls.h"
/*----------------------------------------------------------------
* common variables
*----------------------------------------------------------------*/
int seqfd; /* sequencer file descpriptor */
int awe_dev; /* awedrv device number */
#define SEQBUF_LEN 2048
#define SEQUENCER_DEV "/dev/sequencer"
/* sequencer event buffer;
* allocate enough size than the defined size to avoid dropping events
*/
unsigned char _seqbuf[SEQBUF_LEN * 2];
int _seqbuflen = SEQBUF_LEN;
int _seqbufptr = 0;
static int echo_sync = 0; /* use echo back for timer sync */
static int writeptr = 0; /* pending position */
static int seqblock = 0; /* block if seqbuf is full */
static int pending = 0; /* events are pending? */
static int opened = 0; /* seq device is opened? */
static int ticks; /* timer resolution in Hz */
/* check the seq device is writable now */
static int seq_writable(int wait)
{
fd_set fds;
struct timeval tv;
int rc;
FD_ZERO(&fds);
FD_SET(seqfd, &fds);
if (wait < 0) {
rc = select(seqfd + 1, NULL, &fds, NULL, NULL);
} else {
/* no timeout */
tv.tv_sec = 0;
tv.tv_usec = wait;
rc = select(seqfd + 1, NULL, &fds, NULL, &tv);
}
if (rc > 0 && FD_ISSET(seqfd, &fds))
return 1;
return 0;
}
static void do_write(int do_anyway)
{
int i, size;
size = _seqbufptr - writeptr;
if ((i = write(seqfd, _seqbuf + writeptr, size)) < 0) {
if (! do_anyway) {
fprintf(stderr, "can't write to sequencer.");
exit(1);
}
} else if (i < size) {
writeptr += i;
pending = 1;
} else {
_seqbufptr = 0;
writeptr = 0;
pending = 0;
}
}
/* dump the buffer to seq device.
* if the device is blocked, set pending flag and returns.
* if writable, dump as much as possible
*/
void seqbuf_dump()
{
if (_seqbufptr) {
if (seqblock)
do_write(0);
else {
if (_seqbufptr >= SEQBUF_LEN*2 - 16) {
/* the reserved buffer is almost full;
write all data as emergency */
while (pending)
do_write(1);
} else if (! seq_writable(0))
pending = 1;
else
do_write(0);
}
}
}
/* set blocking mode */
void seq_blocking_mode(int mode)
{
seqblock = mode;
}
/* the buffered events are pending? */
int seq_pending(void)
{
if (pending)
seqbuf_dump();
return pending;
}
/* sequencer device is already opened? */
int seq_opened(void)
{
return opened;
}
/* initialize sequencer device */
int seq_init(int do_echoback)
{
int i;
int nrsynths;
struct synth_info card_info;
if (opened)
return 0;
echo_sync = 0;
/* use echo back synchronization mechanism */
if (do_echoback && (seqfd = open(SEQUENCER_DEV, O_RDWR)) >= 0)
echo_sync = 1;
else if ((seqfd = open(SEQUENCER_DEV, O_WRONLY)) < 0) {
if (ctl == NULL) {
perror("sequencer " SEQUENCER_DEV);
exit(1);
}
return 1;
}
/* check AWE driver */
if (ioctl(seqfd, SNDCTL_SEQ_NRSYNTHS, &nrsynths) == -1) {
fprintf(stderr, "there is no soundcard\n");
if (ctl) ctl->close();
exit(1);
}
awe_dev = -1;
for (i = 0; i < nrsynths; i++) {
card_info.device = i;
if (ioctl(seqfd, SNDCTL_SYNTH_INFO, &card_info) == -1) {
fprintf(stderr, "cannot get info on soundcard\n");
perror(SEQUENCER_DEV);
if (ctl) ctl->close();
exit(1);
}
if (card_info.synth_type == SYNTH_TYPE_SAMPLE
&& card_info.synth_subtype == SAMPLE_TYPE_AWE32)
awe_dev = i;
}
if (awe_dev < 0) {
fprintf(stderr, "No AWE synth device is found\n");
if (ctl) ctl->close();
exit(1);
}
ticks = 0; /* get the default ticks */
if (ioctl(seqfd, SNDCTL_SEQ_CTRLRATE, &ticks) < 0) {
fprintf(stderr, "can't get ticks from sequencer\n");
if (ctl) ctl->close();
exit(1);
}
if (ticks == 0) {
fprintf(stderr, "illegal ticks value\n");
if (ctl) ctl->close();
exit(1);
}
/* set channel mode on */
AWE_SET_CHANNEL_MODE(awe_dev, AWE_PLAY_MULTI);
seqbuf_dump();
opened = 1;
return 0;
}
/* close the seq device */
void seq_end()
{
_seqbufptr = writeptr = 0;
pending = 0;
if (opened)
close(seqfd);
opened = 0;
}
/*----------------------------------------------------------------
* timer events
*----------------------------------------------------------------*/
static int start_csec, cur_csec; /* base offset of timer */
static struct timeval start_time; /* timeval at start_csec */
static int timer_started; /* echo back is called? */
/* set timer offset */
static void seq_set_timer(int csec)
{
gettimeofday(&start_time, NULL);
start_csec = csec;
}
int seq_timer_started(void)
{
return timer_started;
}
/* send timer clear event */
void seq_clear(int csec)
{
SEQ_START_TIMER();
seq_echo(csec);
timer_started = !echo_sync;
seq_set_timer(csec);
cur_csec = csec;
}
/* get the current time from timer */
int seq_curtime()
{
int d1, d2;
struct timeval now_time;
gettimeofday(&now_time, NULL);
d1 = now_time.tv_sec - start_time.tv_sec;
d2 = now_time.tv_usec - start_time.tv_usec;
while (d2 < 0) {
d2 += 1000000;
d1--;
}
d2 /= 10000;
return (d2 + d1 * 100 + start_csec);
}
/* send wait event for time centi seconds; not actually sleeping */
void seq_wait(int time)
{
if (ticks != 100)
time = time * 100 / ticks;
SEQ_DELTA_TIME(time);
cur_csec += time;
}
/* get the last time on seq buffer */
int seq_lasttime()
{
return cur_csec;
}
/* sleep for csec, or get an echo back from seq device */
int seq_sleep(int csec)
{
if (echo_sync) {
/* check echo back from seq device */
int rc;
fd_set fds;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = csec * 10000;
FD_ZERO(&fds);
FD_SET(seqfd, &fds);
rc = select(seqfd + 1, &fds, NULL, NULL, &tv);
if (rc > 0 && FD_ISSET(seqfd, &fds)) {
int msg;
if (read(seqfd, &msg, 4) != 4 ||
(msg & 0xff) != SEQ_ECHO)
return seq_curtime();
msg >>= 8;
timer_started = 1;
seq_set_timer(msg);
/*fprintf(stderr, "catch %d\n", msg);*/
return msg;
}
} else {
/* just sleep for a while */
usleep(csec * 10000);
}
return seq_curtime();
}
/* send echo back time to the seq device */
void seq_echo(int csec)
{
if (echo_sync) {
SEQ_ECHO_BACK(csec);
}
}
/* sync to the last event */
int seq_wait_time(int endcs)
{
seqbuf_dump();
ioctl(seqfd, SNDCTL_SEQ_SYNC);
if (!seqblock && timer_started) {
int csec = seq_curtime();
while (csec < endcs)
csec = seq_sleep(endcs - csec);
}
return endcs;
}
/*----------------------------------------------------------------
* terminate sounds immediately via ioctl.
* all events in the seq device and buffer are cleared.
*----------------------------------------------------------------*/
void seq_terminate_all(void)
{
ioctl(seqfd, SNDCTL_SEQ_RESET);
_seqbufptr = writeptr = 0;
pending = 0;
}
/*----------------------------------------------------------------
* normal play event handlers; put on seq buffer
*----------------------------------------------------------------*/
void seq_set_bank(int v, int bank)
{
SEQ_CONTROL(awe_dev, v, CTL_BANK_SELECT, bank);
}
void seq_note_off_all(void)
{
/*SEQ_CONTROL(awe_dev, 0, 123, 0);*/
AWE_NOTEOFF_ALL(awe_dev);
}
void seq_sound_off_all(void)
{
/*SEQ_CONTROL(awe_dev, 0, 120, 0);*/
AWE_RELEASE_ALL(awe_dev);
}
void seq_control(int v, int type, int val)
{
SEQ_CONTROL(awe_dev, v, type, val);
}
void seq_set_program(int v, int pgm)
{
SEQ_SET_PATCH(awe_dev, v, pgm);
}
void seq_start_note(int v, int note, int vel)
{
SEQ_START_NOTE(awe_dev, v, note, vel);
}
void seq_stop_note(int v, int note, int vel)
{
SEQ_STOP_NOTE(awe_dev, v, note, vel);
}
/* val : 128 = 1 semitone */
void seq_pitchsense(int v, int val)
{
val = val * 100 / 128;
SEQ_BENDER_RANGE(awe_dev, v, val);
}
void seq_pitchbend(int v, int val)
{
SEQ_BENDER(awe_dev, v, val);
}
void seq_aftertouch(int v, int note, int vel)
{
AWE_KEY_PRESSURE(awe_dev, v, note, vel);
}
void seq_chan_pressure(int v, int val)
{
AWE_CHN_PRESSURE(awe_dev, v, val);
}
/* tuning:
* coarse = -8192 to 8192 (100 cent per 128)
* fine = -8192 to 8192 (max=100cent)
*/
void seq_detune(int v, int coarse, int fine)
{
/* 4096 = 1200 cents in AWE parameter */
int val;
val = coarse * 4096 / (12 * 128);
val += fine / 24;
if (val) {
AWE_SEND_EFFECT(awe_dev, v, AWE_FX_INIT_PITCH, val);
} else {
AWE_UNSET_EFFECT(awe_dev, v, AWE_FX_INIT_PITCH);
}
}
/* set effect */
void seq_send_effect(int v, int type, int val)
{
AWE_SEND_EFFECT(awe_dev, v, type, val);
}
/* send effect */
void seq_add_effect(int v, int type, int val)
{
AWE_ADD_EFFECT(awe_dev, v, type, val);
}
/* send soft pedal on/off: emulation by effect control */
void seq_soft_pedal(int v, int val)
{
if (val == 127) {
AWE_ADD_EFFECT(awe_dev, v, AWE_FX_CUTOFF, -160);
} else {
AWE_UNSET_EFFECT(awe_dev, v, AWE_FX_CUTOFF);
}
}
/*----------------------------------------------------------------
* send the control events; put on seq buffer
*----------------------------------------------------------------*/
void seq_reset_channel(int v)
{
AWE_RESET_CHANNEL(awe_dev, v);
}
void seq_reset_control(int v)
{
AWE_RESET_CONTROL(awe_dev, v);
}
void seq_set_realtime_pan(int mode)
{
AWE_REALTIME_PAN(awe_dev, mode);
}
void seq_set_def_drum(int val)
{
AWE_MISC_MODE(awe_dev, AWE_MD_DEF_DRUM, val);
}
void seq_set_drumchannels(int channels)
{
AWE_DRUM_CHANNELS(awe_dev, channels);
}
void seq_channel_priority(int mode)
{
AWE_MISC_MODE(awe_dev, AWE_MD_CHN_PRIOR, mode);
}
/*----------------------------------------------------------------
* immediately change the values; sent direcly to the seq device
*----------------------------------------------------------------*/
void seq_remove_samples(void)
{
AWE_REMOVE_LAST_SAMPLES(seqfd, awe_dev);
}
void seq_set_reverb(int mode)
{
_AWE_CMD_NOW(seqfd, awe_dev, 0, _AWE_REVERB_MODE, mode, 0)
}
void seq_set_chorus(int mode)
{
_AWE_CMD_NOW(seqfd, awe_dev, 0, _AWE_CHORUS_MODE, mode, 0)
}
void seq_equalizer(int bass, int treble)
{
_AWE_CMD_NOW(seqfd, awe_dev, 0, _AWE_EQUALIZER, bass, treble)
}
/* change volume in percent (0-100) */
void seq_change_volume(int vol)
{
int atten;
if (vol <= 0)
atten = 0xff;
else
atten = (int)(-log10((double)vol / 100.0) * 20 * 8 / 3);
_AWE_CMD_NOW(seqfd, awe_dev, 0, _AWE_INITIAL_VOLUME, atten, 1)
}
awemidi-0.4.2b/readmidi.c 100644 765 12 62577 6416446117 13545 0 ustar iwai wheel /*----------------------------------------------------------------
* read midi file and parse events
*----------------------------------------------------------------
* based on the midi parser routine in TiMidity by Tuukka Toivonen.
*----------------------------------------------------------------*/
#include <stdio.h>
#include <string.h>
#include "midievent.h"
#include "util.h"
#include "controls.h"
#include "channel.h"
/*----------------------------------------------------------------*/
/* midi event as linked list */
typedef struct {
MidiEvent event;
void *next;
} MidiEventList;
static MidiEventList *evlist, *curev; /* event linked list */
static int event_count; /* number of events allocated */
static int at; /* current tick */
static int default_tempo; /* default tempo (for CTMF) */
static int channel_shifted; /* channel offset for multi midi part */
static MidiInfo *minfop; /* midi header information */
static int text_printed; /* first text event is found? */
#define MID_OK 0
#define MID_ERR -1 /* recoverable error */
#define MID_END -2 /* end of track */
#define MID_EOF -3 /* reach at EOF */
#define MID_FATAL -4 /* fatal error */
/*----------------------------------------------------------------
* prototypes
*----------------------------------------------------------------*/
static int skip(FILE *fp, size_t len);
static int readc(unsigned char *p, FILE *fp);
static int reads(unsigned char *s, int len, FILE *fp);
static int READLONG(FILE *fp);
static short READSHORT(FILE *fp);
static short READSHORT_LE(FILE *fp);
static int getvl(FILE *fp);
static MidiEventList *alloc_midi_event(int type, int ch);
static void init_midi_event(int append_mode);
static MidiEventList *append_to_list(MidiEventList *new);
static MidiEventList *midi_dbl_event(int type, int ch, int pa, int pb);
static MidiEventList *midi_val_event(int type, int ch, int val);
static MidiEventList *midi_text_event(int type, int ch, char *str);
static int print_message(int type, int len, FILE *fp);
static int nrpn_event(FILE *fp);
static int rpn_event(FILE *fp);
static int system_exclusive(FILE *fp);
static int meta_event(FILE *fp);
static int control_change(FILE *fp, unsigned char type, unsigned char val);
static int read_track(FILE *fp, int append);
static void free_midi_list(void);
static void convert_midi_list(MidiInfo *header);
static int read_header(FILE *fp, MidiInfo *header);
#ifdef CONVERT_JAPANESE
#include "kanji.h"
#endif
/*----------------------------------------------------------------
* file handling functions
*----------------------------------------------------------------*/
/* current file position in bytes */
static int filepos;
/* not use fseek due to piped input */
static int skip(FILE *fp, size_t len)
{
size_t c;
char tmp[1024];
while (len > 0) {
c = len;
if (c > 1024) c = 1024;
len -= c;
filepos += c;
if (c != fread(tmp, 1, c, fp)) {
if (ctl)
ctl->cmsg(CMSG_WARNING, 0,
"Corrupted midi file (pos:%d)", filepos);
return MID_EOF;
}
}
return MID_OK;
}
/* read one character */
static int readc(unsigned char *p, FILE *fp)
{
filepos++;
if (fread(p, 1, 1, fp) == 1)
return 0;
else {
if (ctl)
ctl->cmsg(CMSG_WARNING, 0,
"Corrupted midi file (pos:%d)", filepos);
return MID_EOF;
}
}
/* read some bytes */
static int reads(unsigned char *s, int len, FILE *fp)
{
filepos += len;
if (fread(s, 1, len, fp) == len)
return 0;
else {
if (ctl)
ctl->cmsg(CMSG_WARNING, 0,
"Corrupted midi file (pos:%d)", filepos);
return -1;
}
}
/* read 4bytes */
#define READID(str,fp) reads(str,4,fp)
/* Instrument files are little-endian, MIDI files big-endian, so we
need to do some conversions. */
#define XCHG_SHORT(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
#ifdef __i486__
# define XCHG_LONG(x) \
({ int __value; \
asm ("bswap %1; movl %1,%0" : "=g" (__value) : "r" (x)); \
__value; })
#else
# define XCHG_LONG(x) ((((x)&0xFF)<<24) | \
(((x)&0xFF00)<<8) | \
(((x)&0xFF0000)>>8) | \
(((x)>>24)&0xFF))
#endif
/*#if ENDIAN==LITTLE_ENDIAN*/
#define BE_LONG(x) XCHG_LONG(x)
#define BE_SHORT(x) XCHG_SHORT(x)
#define LE_LONG(x) (x)
#define LE_SHORT(x) (x)
/*
#else
#define BE_LONG(x) (x)
#define BE_SHORT(x) (x)
#define LE_LONG(x) XCHG_LONG(x)
#define LE_SHORT(x) XCHG_SHORT(x)
#endif
*/
/* big endian 4bytes integer */
static int READLONG(FILE *fp)
{
int v;
reads((char*)&v, 4, fp);
return BE_LONG(v);
}
/* big endian 2bytes integer */
static short READSHORT(FILE *fp)
{
short v;
reads((char*)&v, 2, fp);
return BE_SHORT(v);
}
/* little endian 2bytes integer */
static short READSHORT_LE(FILE *fp)
{
short v;
reads((char*)&v, 2, fp);
return LE_SHORT(v);
}
/* Read variable-length number (7 bits per byte, MSB first) */
static int getvl(FILE *fp)
{
int i = 0;
int l = 0;
unsigned char c;
for (;;) {
if (readc(&c, fp) != MID_OK)
return MID_EOF;
l += (c & 0x7f);
if (!(c & 0x80)) {
if (l < 0) {
if (ctl) ctl->cmsg(CMSG_WARNING, -1, "#too deep getvl (pos:%d)", filepos);
return MID_ERR;
}
return l;
}
l <<= 7;
i++;
}
}
/*----------------------------------------------------------------
* generate a event list record
*----------------------------------------------------------------*/
/* allocate midi event record */
static MidiEventList *alloc_midi_event(int type, int ch)
{
MidiEventList *new=safe_malloc(sizeof(MidiEventList));
new->event.time = at;
new->event.type = type;
new->event.channel = (unsigned char)ch;
new->next = 0;
return new;
}
/* initialize start time and midi record pointer */
static void init_midi_event(int append_mode)
{
curev = evlist;
if (append_mode && curev) {
/* find the last event in the list */
for (; curev->next; curev = curev->next)
;
at = curev->event.time;
} else
at = 0;
}
/* append midi event record */
static MidiEventList *append_to_list(MidiEventList *new)
{
MidiEventList *next;
if (channel_shifted)
new->event.channel += channel_shifted;
while ((next = curev->next) != NULL &&
(next->event.time <= new->event.time))
curev = next;
new->next = next;
curev->next = new;
event_count++; /* increment the event counter */
curev = new;
return new;
}
/* get a midi event record with two parameters */
static MidiEventList *midi_dbl_event(int type, int ch, int pa, int pb)
{
MidiEventList *p = append_to_list(alloc_midi_event(type, ch));
p->event.p.par[0] = pa;
p->event.p.par[1] = pb;
return p;
}
/* get a midi event record with one integer parameter */
static MidiEventList *midi_val_event(int type, int ch, int val)
{
MidiEventList *p = append_to_list(alloc_midi_event(type, ch));
p->event.p.val = val;
return p;
}
/* get a midi event record with a text string */
static MidiEventList *midi_text_event(int type, int ch, char *str)
{
MidiEventList *p = append_to_list(alloc_midi_event(type, ch));
p->event.p.text = str;
return p;
}
/*----------------------------------------------------------------
* midi status variables
*----------------------------------------------------------------*/
/* currently processing status and channel */
static unsigned char laststatus, lastchan;
/* RPN & NRPN */
static unsigned char nrpn[MAX_MIDI_CHANNELS]; /* current event is NRPN? */
static int msb_bit; /* current event is msb for RPN/NRPN */
/* RPN & NRPN indeces */
static unsigned char rpn_msb[MAX_MIDI_CHANNELS], rpn_lsb[MAX_MIDI_CHANNELS];
/* RPN & NRPN values */
static int rpn_val[MAX_MIDI_CHANNELS];
static void clear_rpn(void)
{
int i;
for (i = 0; i < MAX_MIDI_CHANNELS; i++) {
nrpn[i] = 0;
rpn_msb[i] = 127;
rpn_lsb[i] = 127;
rpn_val[i] = 0;
}
msb_bit = 0;
}
/*----------------------------------------------------------------*/
/*
* output message
*/
static int print_message(int type, int len, FILE *fp)
{
static char *label[]={
"Text event: ", "Text: ", "Copyright: ", "Track name: ",
"Instrument: ", "Lyric: ", "Marker: ", "Cue point: ",
};
char *s, *p;
int slen;
if (type < 0 || type > 7) type = 0;
slen = strlen(label[type]);
s = (char*)safe_malloc(len + slen + 1);
strcpy(s, label[type]);
for (p = s + slen; len > 0; len--) {
readc(p, fp);
/* convert to UNIX text format: CR+LF -> LF; just ignore CR */
if (*p != 0x0d) p++;
}
*p = 0;
midi_text_event(ME_LYRIC, lastchan, s);
if (minfop->parse_title && !text_printed &&
(type == 1 || type == 3 || type == 5 || type == 6)) {
text_printed = 1;
if (ctl && ctl->title)
ctl->title(s + slen); /* skip "Text: " header */
}
return MID_OK;
}
/*
* NRPN events: accept as AWE32/SC88 specific controls
*/
static int nrpn_event(FILE *fp)
{
if (rpn_msb[lastchan] == 127 && rpn_lsb[lastchan] <= 26) {
if (! msb_bit) /* both MSB/LSB necessary */
midi_dbl_event(ME_AWE_FX, lastchan,
rpn_lsb[lastchan], rpn_val[lastchan]);
} else if (rpn_msb[lastchan] == 1) {
if (msb_bit) /* only MSB is valid */
midi_dbl_event(ME_GS_FX, lastchan,
rpn_lsb[lastchan], rpn_val[lastchan]/128);
}
return MID_OK;
}
/*
* RPN events
*/
static int rpn_event(FILE *fp)
{
int type;
type = (rpn_msb[lastchan]<<8) | rpn_lsb[lastchan];
switch (type) {
case 0x0000: /* Pitch bend sensitivity */
/* MSB only / 1 semitone per 128 */
if (msb_bit)
midi_val_event(ME_PITCH_SENS, lastchan, rpn_val[lastchan]);
break;
case 0x0001: /* fine tuning: */
/* MSB/LSB, 8192=center, 100/8192 cent step */
midi_val_event(ME_FINETUNE, lastchan, rpn_val[lastchan]);
break;
case 0x0002: /* coarse tuning */
/* MSB only / 8192=center, 1 semitone per 128 */
if (msb_bit)
midi_val_event(ME_COARSETUNE, lastchan, rpn_val[lastchan]);
break;
case 0x7F7F: /* "lock-in" RPN */
break;
default:
if (ctl)
ctl->cmsg(CMSG_INFO, 2, "unsupported RPN %xh (%d) %d\n",
type, lastchan, rpn_val[lastchan]);
break;
}
return MID_OK;
}
/*
* parse system exclusive message
* GM/GS/XG macros are accepted
*/
static int system_exclusive(FILE *fp)
{
/* GM on */
static unsigned char gm_on_macro[] = {
0x7e,0x7f,0x09,0x01,
};
/* XG on */
static unsigned char xg_on_macro[] = {
0x43,0x10,0x4c,0x00,0x00,0x7e,0x00,
};
/* GS prefix
* drum channel: XX=0x1?(channel), YY=0x15, ZZ=on/off
* reverb mode: XX=0x01, YY=0x30, ZZ=0-7
* chorus mode: XX=0x01, YY=0x38, ZZ=0-7
*/
static unsigned char gs_pfx_macro[] = {
0x41,0x10,0x42,0x12,0x40,/*XX,YY,ZZ*/
};
/* SC88 system mode set
* single module mode: XX=1
* double module mode: XX=0
*/
static unsigned char gs_mode_macro[] = {
0x41,0x10,0x42,0x12,0x00,0x00,0x7F,/*ZZ*/
};
/* SC88 display macro: XX=01:bitmap, 00:text
*/
static unsigned char gs_disp_macro[] = {
0x41,0x10,0x45,0x12,0x10,/*XX,00*/
};
unsigned char buf[1024];
int len;
int i; char tmp[256];
if ((len = getvl(fp)) < 0) return len;
if (len > sizeof(buf)) {
if (ctl)
ctl->cmsg(CMSG_WARNING, 0, "too long sysex!");
return skip(fp, len);
}
reads(buf, len, fp);
/* GM on */
if (memcmp(buf, gm_on_macro, sizeof(gm_on_macro)) == 0) {
if (ctl) ctl->cmsg(CMSG_INFO, 1, "SYSEX: GM on");
if (minfop->midi_mode != MODE_GS &&
minfop->midi_mode != MODE_XG)
minfop->midi_mode = MODE_GM;
}
/* GS macros */
else if (minfop->check_gs_macro &&
memcmp(buf, gs_pfx_macro, sizeof(gs_pfx_macro)) == 0) {
if (minfop->midi_mode != MODE_GS &&
minfop->midi_mode != MODE_XG) {
minfop->midi_mode = MODE_GS;
if (ctl) ctl->cmsg(CMSG_INFO, 1, "SYSEX: GS on");
}
if (buf[5] == 0x00 && buf[6] == 0x7f && buf[7] == 0x00) {
/* GS reset */
if (ctl) ctl->cmsg(CMSG_INFO, 1, "SYSEX: GS reset");
} else if ((buf[5] & 0xf0) == 0x10 && buf[6] == 0x15) {
/* drum pattern */
int p = buf[5] & 0x0f;
if (p == 0) p = 9;
else if (p < 10) p--;
if (buf[7] == 0)
BITOFF(minfop->drumflag, DRUMBIT(p));
else
BITON(minfop->drumflag, DRUMBIT(p));
if (ctl) ctl->cmsg(CMSG_INFO, 1, "SYSEX: drum part %d %s",
p, (buf[7] ? "on" : "off"));
} else if ((buf[5] & 0xf0) == 0x10 && buf[6] == 0x21) {
/* program */
int p = buf[5] & 0x0f;
if (p == 0) p = 9;
else if (p < 10) p--;
if (!(minfop->drumflag & DRUMBIT(lastchan)))
midi_val_event(ME_PROGRAM, p, buf[7]);
} else if (buf[5] == 0x01 && buf[6] == 0x30) {
/* reverb mode */
if (ctl) ctl->cmsg(CMSG_INFO, 1, "SYSEX: reverb_mode = %d", buf[7]);
minfop->reverb = buf[7];
} else if (buf[5] == 0x01 && buf[6] == 0x38) {
/* chorus mode */
if (ctl) ctl->cmsg(CMSG_INFO, 1, "SYSEX: chorus_mode = %d", buf[7]);
minfop->chorus = buf[7];
} else if (buf[5] == 0x00 && buf[6] == 0x04) {
/* master volume */
midi_val_event(ME_MASTER_VOLUME, 0, buf[7]);
}
}
/* GS multi mode */
else if (minfop->check_gs_macro &&
memcmp(buf, gs_mode_macro, sizeof(gs_mode_macro)) == 0) {
/* single module mode */
if (buf[7]) {
if (ctl) ctl->cmsg(CMSG_INFO, 1, "SYSEX: single module mode");
minfop->multi_part = FALSE;
} else {
if (ctl) ctl->cmsg(CMSG_INFO, 1, "SYSEX: double module mode");
minfop->multi_part = TRUE;
}
}
/* LCD macro */
else if (minfop->check_gs_macro &&
memcmp(buf, gs_disp_macro, sizeof(gs_disp_macro)) == 0) {
if (buf[5] == 0x00) {
char *p = (char*)safe_malloc(len - 8);
memcpy(p, buf + 7, len - 9);
buf[len-9] = 0;
midi_text_event(ME_LYRIC, lastchan, p);
}
}
/* XG on */
else if (minfop->check_xg_macro &&
memcmp(buf, xg_on_macro, sizeof(xg_on_macro)) == 0) {
if (ctl) ctl->cmsg(CMSG_INFO, 1, "SYSEX: XG on");
minfop->midi_mode = MODE_XG;
}
/* print sysex bytes on control interface */
if (ctl) {
strcpy(tmp, "SYSEX:");
for (i = 0; i < len; i++) {
sprintf(tmp+i*3+6," %02x", buf[i]);
if (i >= 50) {
sprintf(tmp+(i+1)*3+6,"...");
break;
}
}
ctl->cmsg(CMSG_INFO, 2, tmp);
}
return MID_OK;
}
/*
* parse meta event
* support only "end of track" and "tempo"
*/
static int meta_event(FILE *fp)
{
unsigned char type;
unsigned char a, b, c;
int rc, len;
if ((rc = readc(&type, fp)) != MID_OK) return rc;
if ((len = getvl(fp)) < 0) return len;
if (type > 0 && type < 16) {
return print_message(type, len, fp);
} else {
switch(type) {
case 0x2F: /* End of Track */
return MID_END;
case 0x51: /* Tempo */
if ((rc = readc(&a, fp)) != MID_OK) return rc;
if ((rc = readc(&b, fp)) != MID_OK) return rc;
if ((rc = readc(&c, fp)) != MID_OK) return rc;
midi_val_event(ME_TEMPO, 0, MidiTempo(a,b,c));
break;
default:
/*Warning("unknown meta event: type%d, len%d", type, len);*/
return skip(fp, len);
}
}
return MID_OK;
}
/*
* parse control change message
*/
static int control_change(FILE *fp, unsigned char type, unsigned char val)
{
unsigned char control;
/* controls #31 - #64 are LSB of #0 - #31 */
msb_bit = 1;
if (type >= 0x20 && type < 0x40) {
msb_bit = 0;
type -= 0x20;
}
control = ME_NONE;
switch (type) {
/* bank change */
case CTL_BANK_SELECT:
midi_dbl_event(ME_TONE_BANK, lastchan, val, msb_bit);
return MID_OK;
/* special controls */
case 120: control = ME_ALL_SOUNDS_OFF; break;
case 121: control = ME_RESET_CONTROLLERS; break;
case 123: control = ME_ALL_NOTES_OFF; break;
/* set RPN/NRPN parameter */
case CTL_REGIST_PARM_NUM_MSB: nrpn[lastchan]=0; rpn_msb[lastchan]=val;
return MID_OK;
case CTL_REGIST_PARM_NUM_LSB: nrpn[lastchan]=0; rpn_lsb[lastchan]=val;
return MID_OK;
case CTL_NONREG_PARM_NUM_MSB: nrpn[lastchan]=1; rpn_msb[lastchan]=val;
return MID_OK;
case CTL_NONREG_PARM_NUM_LSB: nrpn[lastchan]=1; rpn_lsb[lastchan]=val;
return MID_OK;
/* send RPN/NRPN entry */
case CTL_DATA_ENTRY:
if (msb_bit)
rpn_val[lastchan] = val * 128;
else
rpn_val[lastchan] |= val;
if (nrpn[lastchan])
return nrpn_event(fp);
else
return rpn_event(fp);
break;
/* increase/decrease data entry */
case CTL_DATA_INCREMENT:
rpn_val[lastchan]++; return rpn_event(fp);
case CTL_DATA_DECREMENT:
rpn_val[lastchan]--; return rpn_event(fp);
/* CTMF specific controls */
case 102: break; /* marker */
case 103: /* rythm mode on/off */
if (minfop->midi_mode == MODE_CMF && val) {
/* rythm mode; 12-16 are drums */
minfop->drumflag = 0xf800;
minfop->midi_mode = MODE_CMF2;
return MID_OK;
}
break;
case 104: /* pitch increase */
if (minfop->midi_mode == MODE_CMF ||
minfop->midi_mode == MODE_CMF2) {
int shift = 0x2000 + val * 32;
midi_val_event(ME_PITCHWHEEL, lastchan, shift);
return MID_OK;
}
break;
case 105: /* pitch decrease */
if (minfop->midi_mode == MODE_CMF ||
minfop->midi_mode == MODE_CMF2) {
int shift = 0x2000 - val * 32;
midi_val_event(ME_PITCHWHEEL, lastchan, shift);
return MID_OK;
}
break;
/* other normal control messages */
default:
control = ME_CONTROL; break;
}
if (control == ME_CONTROL)
midi_dbl_event(control, lastchan, type, val);
else if (control != ME_NONE)
midi_val_event(control, lastchan, val);
else if (ctl)
ctl->cmsg(CMSG_INFO, 2, "unsupported control %d (%d) %d",
type, lastchan, val);
return MID_OK;
}
/*----------------------------------------------------------------*/
/* Read a midi track into the linked list, either merging with any previous
tracks or appending to them. */
static int read_track(FILE *fp, int append)
{
int rc;
int tracklen, endpos;
init_midi_event(append);
tracklen = 0;
endpos = filepos;
/* Check the formalities */
/* read 4bytes header and track length */
if (! (minfop->midi_mode == MODE_CMF||minfop->midi_mode == MODE_CMF2)) {
char tmp[4];
READID(tmp, fp);
if (memcmp(tmp, "MTrk", 4)) {
if (ctl)
ctl->cmsg(CMSG_ERROR, -1, "No MTrk entry (filepos: %d)", filepos);
return MID_FATAL;
}
tracklen = READLONG(fp); /* length */
endpos = filepos + tracklen;
}
rc = MID_OK;
while (rc == MID_OK && (tracklen == 0 || filepos < endpos)) {
unsigned char me, a, b;
int len;
/* read timer tick */
if ((len = getvl(fp)) < 0) {
rc = len;
break;
}
at += len; /* increase the time */
if ((rc = readc(&me, fp)) != MID_OK)
break;
if (me == 0xF0 || me == 0xF7)
/* SysEx event */
rc = system_exclusive(fp);
else if (me == 0xFF)
/* Meta event */
rc = meta_event(fp);
else {
a = me;
if (a & 0x80) { /* status byte */
lastchan = a & 0x0F;
laststatus = (a>>4) & 0x07;
if ((rc = readc(&a, fp)) != MID_OK) break;
a &= 0x7F;
}
switch (laststatus) {
case 0: /* Note off */
if ((rc = readc(&b, fp)) != MID_OK) break;
b &= 0x7F;
midi_dbl_event(ME_NOTEOFF, lastchan, a, b);
break;
case 1: /* Note on */
if ((rc = readc(&b, fp)) != MID_OK) break;
b &= 0x7F;
if (minfop->midi_mode == MODE_CMF2 &&
lastchan >= 11) {
switch (lastchan) {
case 11: a = 36; break; /* bassdrum */
case 12: a = 38; break; /* snare */
case 13: a = 47; break; /* tom */
case 14: a = 49; break; /* cymbal */
case 15: a = 44; break; /* hi-hat */
}
}
midi_dbl_event(ME_NOTEON, lastchan, a, b);
break;
case 2: /* Key Pressure */
if ((rc = readc(&b, fp)) != MID_OK) break;
b &= 0x7F;
midi_dbl_event(ME_KEYPRESSURE, lastchan, a, b);
break;
case 3: /* Control change */
if ((rc = readc(&b, fp)) != MID_OK) break;
b &= 0x7F;
rc = control_change(fp, a, b);
break;
case 4: /* Program change */
if (minfop->midi_mode == MODE_CMF2 &&
lastchan >= 11)
break;
midi_val_event(ME_PROGRAM, lastchan, a);
break;
case 5: /* Channel pressure */
midi_val_event(ME_CHNPRESSURE, lastchan, a);
break;
case 6: /* Pitch wheel */
if ((rc = readc(&b, fp)) != MID_OK) break;
b &= 0x7F;
midi_val_event(ME_PITCHWHEEL, lastchan,
(int)b * 128 + a);
break;
default:
if (ctl)
ctl->cmsg(CMSG_WARNING, 0, "unknown status 0x%02X, channel 0x%02X",
laststatus, lastchan);
break;
}
}
}
if (rc == MID_FATAL || rc == MID_EOF)
return rc;
/* skip the remainging track bytes */
if (rc != MID_END && tracklen > 0 && filepos < endpos) {
ctl->cmsg(CMSG_WARNING, 0, "#skip %d bytes", endpos-filepos);
return skip(fp, endpos - filepos);
}
return MID_OK;
}
/* Free the linked event list from memory. */
static void free_midi_list(void)
{
MidiEventList *meep, *next;
for (meep = evlist; meep; meep = next) {
next = meep->next;
safe_free(meep);
meep = next;
}
evlist=0;
}
/* merge linked list to an array together with calculating the csec event
time */
static void convert_midi_list(MidiInfo *header)
{
MidiEventList *meep;
MidiEvent *evdata;
int tempo;
int prevt;
int i;
double curt, factor;
evdata = (MidiEvent*)safe_malloc(sizeof(MidiEvent) * event_count);
header->nlists = event_count;
header->list = evdata;
header->tempo = default_tempo;
tempo = default_tempo;
prevt = 0;
curt = 0;
i = 0;
factor = (double)tempo / 10000.0 / header->divisions;
for (meep = evlist->next; meep; meep = meep->next) {
/* delta time in 1/100 second */
double dtime = (double)(meep->event.time - prevt) * factor;
curt += dtime;
prevt = meep->event.time;
if (meep->event.type == ME_TEMPO) {
tempo = meep->event.p.val;
factor = (double)tempo / 10000.0 / header->divisions;
}
evdata[i] = meep->event;
/* reduce from 32 to 16 channels if necessary */
if (evdata[i].channel >= 16 && !header->multi_part)
evdata[i].channel -= 16;
evdata[i].csec = (int)curt;
i++;
if (i >= event_count)
break;
}
}
/* read header info of a MIDI or CMF file */
static int read_header(FILE *fp, MidiInfo *header)
{
int len, divisions;
short format, tracks;
char tmp[4];
default_tempo = MIDI_DEFAULT_TEMPO;
/* read 4bytes id */
READID(tmp, fp);
/* check RIFF format */
if (memcmp(tmp, "RIFF", 4) == 0) {
len = READLONG(fp);
READID(tmp, fp);
if (memcmp(tmp, "RMID", 4) != 0) {
if (ctl)
ctl->cmsg(CMSG_ERROR, -1, "not a MIDI file");
return MID_FATAL;
}
for (;;) {
READID(tmp, fp);
if (memcmp(tmp, "data", 4) == 0) {
len = READLONG(fp);
READID(tmp, fp);
break;
}
if (feof(fp)) {
if (ctl)
ctl->cmsg(CMSG_ERROR, -1, "not a MIDI file");
return MID_FATAL;
}
}
}
if (memcmp(tmp, "CTMF", 4) == 0) { /* creative CMF file */
short Version_Id;
int offs1,offs2,offs3,offs4,len1,len2;
char *s;
if ((Version_Id=READSHORT(fp)) > 0x0101) {
if (ctl)
ctl->cmsg(CMSG_ERROR, -1, "Unknown version Id of CTMF file: 0x%x\n",Version_Id);
return MID_FATAL;
}
offs4=READSHORT_LE(fp); /* skip 2 bytes -- offset of instrument block; */
/* we should use this block */
len = READSHORT_LE(fp); /* offset of music block */
READSHORT_LE(fp); /* skip 2 bytes -- ticks per quarter note */
divisions = READSHORT_LE(fp); /* ticks per sec */
offs1=READSHORT_LE(fp); /* Music title */
offs2=READSHORT_LE(fp); /* Composer name */
offs3=READSHORT_LE(fp); /* Remarks */
#if 0
if (offs1>offs2 || offs2>offs3 || offs3>offs4 || offs4>len || (offs1 && offs1<40) || len<40) {
if (ctl) {
ctl->cmsg(CMSG_ERROR, -1, "illegal CTMF file");
}
return MID_FATAL;
}
#endif
if (ctl && (offs1 || offs2 || offs3)) {
len2=(offs1)? offs1: (offs2)? offs2: offs3;
skip(fp,len2-20); /* skip the rest of the header */
s = (char*)safe_malloc(len1=((offs4) ? offs4 : len)-len2);
fread(s,1,len1,fp);
if (offs1) {
ctl->cmsg(CMSG_INFO, -1, "Title: %s", s);
ctl->file_name(s);
}
if (offs2)
ctl->cmsg(CMSG_INFO, -1, "Composer: %s", s+offs2-len2);
if (offs3)
ctl->cmsg(CMSG_INFO, -1, "Remarks: %s", s+offs3-len2);
safe_free(s);
skip(fp, len-offs4);
} else
skip(fp, len-20);
default_tempo = 1000000;
format = 0; /* always format 0 */
tracks = 1;
minfop->midi_mode = MODE_CMF;
}
else { /* normal MIDI file */
len = READLONG(fp);
if (memcmp(tmp, "MThd", 4) || len < 6) {
if (ctl)
ctl->cmsg(CMSG_ERROR, -1, "not a MIDI file");
return MID_FATAL;
}
format = READSHORT(fp);
tracks = READSHORT(fp);
divisions = READSHORT(fp);
if (divisions < 0)
divisions = -(divisions/256) * (divisions & 0xFF);
if (len > 6)
skip(fp, len-6); /* skip the excess */
}
if (format < 0 || format >2) {
if (ctl)
ctl->cmsg(CMSG_ERROR, -1, "unknown MIDI file format %d", format);
return MID_FATAL;
}
header->format = format;
header->tracks = tracks;
header->divisions = divisions;
if (ctl)
ctl->cmsg(CMSG_INFO, 1, "format=%d, tracks=%d, divs=%d",
format, tracks, divisions);
return MID_OK;
}
/* read a midi file and generate the event array */
MidiEvent *ReadMidiFile(FILE *fp, MidiInfo *header)
{
int i, rc;
int do_append;
event_count = 0;
at = 0;
evlist = NULL;
minfop = header;
filepos = 0;
text_printed = 0;
clear_rpn();
if (read_header(fp, header) != MID_OK)
return NULL;
/* Put a do-nothing event first in the list for easier processing */
evlist = (MidiEventList*)safe_malloc(sizeof(MidiEventList));
evlist->event.time = 0;
evlist->event.type = ME_NONE;
evlist->next = 0;
channel_shifted = 0;
switch(header->format) {
case 0:
if (read_track(fp, FALSE) == MID_FATAL) {
free_midi_list();
return 0;
}
break;
case 1:
case 2:
if (header->format == 2)
do_append = TRUE;
else
do_append = FALSE;
for (i = 0; i < header->tracks; i++) {
/* check multi MIDI part:
* normally track 0 is used for controls,
* track 1-16 for channel 1-16,
* track 17-32 for channel 17-32 */
if (i > header->track_nums)
channel_shifted = 16;
if ((rc = read_track(fp, do_append)) != MID_OK) {
if (rc == MID_EOF)
break;
else if (rc == MID_FATAL) {
free_midi_list();
return 0;
}
}
}
break;
}
convert_midi_list(header);
free_midi_list();
return header->list;
}
/* free event array */
void FreeMidiFile(MidiInfo *header)
{
int i;
for (i = 0; i < header->nlists; i++) {
if (header->list[i].type == ME_LYRIC)
safe_free(header->list[i].p.text);
}
safe_free(header->list);
}
awemidi-0.4.2b/controls.h 100644 765 12 5124 6371333536 13600 0 ustar iwai wheel /*
controls.h
This file is brought from TiMidity 0.2i, and modified for tkawe.
*/
/* Return values for ControlMode.read */
#ifndef CONTROLS_H_DEF
#define RC_ERROR -1
#define RC_NONE 0
#define RC_QUIT 1
#define RC_NEXT 2
#define RC_PREVIOUS 3 /* Restart this song at beginning, or the previous
song if we're less than a second into this one. */
#define RC_MOVE 4
#define RC_SEARCH RC_MOVE
#define RC_FORWARD RC_MOVE
#define RC_BACK RC_MOVE
#define RC_JUMP 6
/*#define RC_TOGGLE_PAUSE 7*/ /* Pause/continue */
#define RC_RESTART 8 /* Restart song at beginning */
#define RC_PAUSE 9 /* Really pause playing */
#define RC_CONTINUE 10 /* Continue if paused */
#define RC_REALLY_PREVIOUS 11 /* Really go to the previous song */
#define RC_CHANGE_VOLUME 12
#define RC_LOAD_FILE 13 /* Load a new midifile */
#define RC_TUNE_END 14 /* The tune is over, play it again sam? */
#define RC_CHANGE_CHORUS 15
#define RC_CHANGE_REVERB 16
/*#define RC_RESUME 17*/
#define RC_CHANGE_BASS 18
#define RC_CHANGE_TREBLE 19
#define RC_BASE_CHANGE 20
#define RC_KILL 99
#define CMSG_INFO 0
#define CMSG_WARNING 1
#define CMSG_ERROR 2
#define CMSG_FATAL 3
#define CMSG_TRACE 4
#define CMSG_TIME 5
#define CMSG_TOTAL 6
#define CMSG_FILE 7
#define CMSG_TEXT 8
#define VERB_NORMAL 0
#define VERB_VERBOSE 1
#define VERB_NOISY 2
#define VERB_DEBUG 3
#define VERB_DEBUG_SILLY 4
#define PLAY_REPEAT 1
#define PLAY_SHUFFLE 2
#define PLAY_AUTOSTART 4
#define PLAY_AUTOEXIT 8
#define PLAY_TRACE 16
#define PLAY_NORMAL 32
typedef struct {
char *id_name, id_character;
int need_args, need_sync, fuzzy_prev;
int verbosity, playing_mode, opened, repeated;
int (*open)(int using_stdin, int using_stdout);
void (*pass_playing_list)(int number_of_files, char *list_of_files[]);
void (*close)(void);
int (*read)(MidiInfo *mp, int *valp);
int (*blocking_read)(MidiInfo *mp, int *valp);
int (*cmsg)(int type, int verbosity_level, char *fmt, ...);
void (*update)(MidiInfo *mp);
void (*reset)(MidiInfo *mp);
void (*file_name)(char *name);
void (*title)(char *name);
void (*total_time)(int tt);
void (*current_time)(int ct);
void (*note)(int ch, int note, int vel);
void (*program)(int channel, int val); /* val<0 means drum set -val */
void (*bank)(int channel, int val);
void (*control_change)(int channel, int type, int val);
void (*pitch_bend)(int channel, int val);
void (*pitch_sense)(int channel, int val);
void (*system_change)(int type, int mode);
void (*effects_change)(int channel, int type, int value);
void (*master_volume)(int atten);
} ControlMode;
extern ControlMode *ctl_list[], *ctl;
#endif
awemidi-0.4.2b/tkpanel.tcl 100644 765 12 60156 6411775245 13756 0 ustar iwai wheel #!/usr/local/bin/wishx -f
#
# Tk control panel routine
# Copyright (c) 1996,1997 Takashi Iwai
#
#----------------
# initialize global variables
#
proc InitGlobal {} {
global Stat
# input/output pipe
set Stat(input) stdin
set Stat(output) stdout
# time table
set Stat(MaxSecs) 0
set Stat(LastSec) 0
set Stat(TotalTimeStr) "/ 0:00"
# message lines
set Stat(CurMsgs) 0
set Stat(MaxMsgs) 500
# current status
set Stat(Playing) 0
set Stat(Paused) 0
set Stat(Blinking) 0
set Stat(Buttons) -1
# MIDI file list
set Stat(CurIdx) -1
set Stat(MaxFiles) 0
set Stat(FileList) {}
set Stat(ShuffleList) {}
set Stat(CurFile) "--------"
set Stat(NextFile) "--------"
global Config
# playing mode
set Config(RepeatPlay) 0
set Config(ShufflePlay) 0
set Config(AutoStart) 0
set Config(AutoExit) 0
set Config(ConfirmExit) 1
set Stat(Tracing) 1
# display configuration
set Config(Disp:file) 1
set Config(Disp:time) 0
set Config(Disp:text) 0
set Config(Disp:volume) 0
set Config(Disp:button) 1
set Config(Disp:trace) 1
set Config(Mode:chorus) 2
set Config(Mode:reverb) 4
set Config(CurFileMode) 0
# current volume
set Config(CurVol) 100
set Config(CurBass) 5
set Config(CurTreble) 9
set Stat(ChorusList) {
{0 "Chorus 1"}
{1 "Chorus 2"}
{2 "Chorus 3"}
{3 "Chorus 4"}
{4 "Feedback"}
{5 "Flanger"}
{6 "Short Delay"}
{7 "Short Delay 2"}
}
set Stat(ReverbList) {
{0 "Room 1"}
{1 "Room 2"}
{2 "Room 3"}
{3 "Hall 1"}
{4 "Hall 2"}
{5 "Plate"}
{6 "Delay"}
{7 "Panning Delay"}
}
set Config(UserChorus) {}
set Config(UserReverb) {}
wm title . "TkAWEMidi"
wm iconname . "TkAWEMidi"
#wm iconbitmap . @$bitmap_path/tkawe.xbm
}
#----------------
# read a message from stdin
#
proc HandleInput {} {
global Stat Config
set mlist [gets $Stat(input)]
set msg [string range $mlist 0 3]
set args [string range $mlist 5 end]
if {$msg == "TIME"} {
# set total time
set csecs [expr $args / 100]
set Stat(TotalTimeStr) [sec2time $csecs]
set Stat(MaxSecs) $csecs
set tics [expr $csecs / 8]
set tics [expr (($tics + 4) / 5) * 5]
.body.time.scale configure -tickinterval $tics -to $csecs
SetTime 0
} elseif {$msg == "VOLM"} {
# set master volume
SetVolume $args
} elseif {$msg == "FILE"} {
# set playing file
set Stat(CurFile) $args
wm title . "TkAWEMidi: $Stat(CurFile)"
wm iconname . "TkAWEMidi: $Stat(CurFile)"
.body.curfile.title configure -text $Stat(CurFile)
if {$Config(CurFileMode) == 0} {
.body.curfile.time configure -text "00:00"
} else {
.body.curfile.time configure -text "-$Stat(TotalTimeStr)"
}
AppendMsg "------"
} elseif {$msg == "TITL"} {
# set title name
set title $args
catch {set title [kanji conversion [kanji code $title] EUC $title]}
.body.curfile.title configure -text $title
} elseif {$msg == "LIST"} {
# set file list
.body.file.list delete 0 end
set Stat(MaxFiles) $args
set Stat(FileList) {}
for {set i 0} {$i < $Stat(MaxFiles)} {incr i} {
set file [gets stdin]
.body.file.list insert end $file
lappend Stat(FileList) $file
}
# MakeShuffleList
set Stat(CurIdx) -1
SelectNumber
} elseif {$msg == "PREV"} {
# previous file
set Stat(CurIdx) [expr $Stat(CurIdx) - 1]
if {$Stat(CurIdx) < 0} {set Stat(CurIdx) 0}
SelectNumber
} elseif {$msg == "NEXT"} {
# next file
incr Stat(CurIdx)
if {$Stat(CurIdx) >= $Stat(MaxFiles)} {
if {$Config(RepeatPlay)} {
set Stat(CurIdx) 0
} elseif {$Config(AutoExit)} {
QuitCmd
} else {
StopCmd
}
}
SelectNumber
} elseif {$msg == "CMSG"} {
# put message
set str [gets stdin]
AppendMsg $str
} elseif {$msg == "CERR"} {
error [format "%s: %s" $Stat(NextFile) [gets stdin]]
WriteMsg "NEXT"
} elseif {$msg == "CHRS"} {
set Config(Mode:chorus) $args
} elseif {$msg == "EVRB"} {
set Config(Mode:reverb) $args
}
}
#----------------
# make shuffled list
#
proc MakeShuffleList {} {
global Stat
set tmplist {}
for {set i 0} {$i < $Stat(MaxFiles)} {incr i} {
lappend tmplist $i
}
set Stat(ShuffleList) {}
set len $Stat(MaxFiles)
while {$len > 0} {
set pos [random $len]
lappend Stat(ShuffleList) [lindex $tmplist $pos]
set tmplist [lreplace $tmplist $pos $pos]
set len [expr $len - 1]
}
}
#
# append a string to message buffer
#
proc AppendMsg {str} {
global Stat
incr Stat(CurMsgs)
if {$Stat(CurMsgs) >= $Stat(MaxMsgs)} { ClearMsg }
.body.text.buf insert end $str\n
.body.text.buf yview -pickplace end
}
#
# clear message buffer
#
proc ClearMsg {} {
global Stat
.body.text.buf delete 0.0 end
.body.text.buf yview 0
set Stat(CurMsgs) 0
}
#----------------
# select the file in listbox and load it
#
proc SelectNumber {} {
global Stat Config
.body.file.list select clear 0 end
set idx -1
if {$Stat(CurIdx) >= 0 && $Stat(CurIdx) < [llength $Stat(FileList)]} {
if {$Config(ShufflePlay)} {
if {$Stat(ShuffleList) == {}} {
MakeShuffleList
}
set idx [lindex $Stat(ShuffleList) $Stat(CurIdx)]
} else {
set idx $Stat(CurIdx)
}
set thefile [lindex $Stat(FileList) $idx]
set Stat(NextFile) $thefile
}
if {$idx >= 0 && $thefile != "-" && ![file exists $thefile]} {
warning "Can't open file \"$thefile\"."
set idx -1
}
if {$idx >= 0} {
.body.file.list select set $idx
.body.file.list see $idx
LoadCmd $idx
set Stat(Playing) 1
.body.curfile.index configure -text [format "%03d/%03d"\
[expr $idx + 1] $Stat(MaxFiles)]
} else {
SetTime 0
.body.curfile.title configure -text "--------"
.body.curfile.time configure -text "00:00"
.body.curfile.index configure -text "000/000"
set Stat(Playing) 0
set Stat(Paused) 0
}
DispButtonPlay
}
#
# update current time
#
proc SetTime {val} {
global Stat Config
if {$Stat(CurIdx) == -1} {
return
}
set Stat(LastSec) $val
if {$Config(CurFileMode) == 0} {
set curt [sec2time $val]
.body.curfile.time configure -text "$curt"
} else {
set curt [sec2time [expr $val - $Stat(MaxSecs)]]
.body.curfile.time configure -text "$curt"
}
set curt [sec2time $val]
.body.time.label configure\
-text "$curt / $Stat(TotalTimeStr)"
.body.time.scale set $val
ToggleButtonLump
}
#
# colorize buttons with each state
#
proc ToggleButtonLump {} {
global Stat
if {$Stat(Playing)} {
if {$Stat(Blinking)} {
set color green
set Stat(Blinking) 0
} else {
set color red
set Stat(Blinking) 1
}
.body.button.play configure -fg $color -activeforeground $color
}
}
#
# enable/disable button & menu status
#
proc DispButtonPlay {} {
global Stat
ToggleButtonLump
if {$Stat(Playing) && $Stat(Paused)} {
.body.button.pause configure -fg red -activeforeground red
} else {
.body.button.pause configure -fg black -activeforeground black
}
if {$Stat(Playing) && $Stat(Paused)} {
if {$Stat(Buttons) != 2} {
.body.time.scale configure -state disabled
.body.button.play configure -state disabled
.body.button.next configure -state disabled
.body.button.prev configure -state disabled
.body.button.pause configure -state normal
.body.button.fwrd configure -state disabled
.body.button.back configure -state disabled
set Stat(Buttons) 2
}
} elseif {$Stat(Playing)} {
if {$Stat(Buttons) != 1} {
.body.time.scale configure -state normal
.body.button.play configure -state normal
.body.button.next configure -state normal
.body.button.prev configure -state normal
.body.button.pause configure -state normal
.body.button.fwrd configure -state normal
.body.button.back configure -state normal
.body.button.stop configure -state normal
set Stat(Buttons) 1
}
} else {
if {$Stat(Buttons) != 0} {
.body.time.scale configure -state disabled
.body.button.play configure -fg black -activeforeground black
.body.button.play configure -state normal
.body.button.next configure -state disabled
.body.button.prev configure -state disabled
.body.button.pause configure -state disabled
.body.button.fwrd configure -state disabled
.body.button.back configure -state disabled
.body.button.stop configure -state disabled
set Stat(Buttons) 0
}
}
if {$Stat(Playing)} {
set w .menu.file.m
$w entryconfigure 1 -state disabled
$w entryconfigure 2 -state disabled
#$w entryconfigure 3 -state disabled
set w .menu.mode.m
$w entryconfigure 2 -state disabled
} else {
set w .menu.file.m
$w entryconfigure 1 -state normal
$w entryconfigure 2 -state normal
#$w entryconfigure 3 -state normal
set w .menu.mode.m
$w entryconfigure 2 -state normal
}
}
#
# update current volume
#
proc SetVolume {val} {
global Config
set Config(CurVol) $val
#.body.volume.label configure -text [format "Volume: %d%%" $val]
.body.volume.scale set $val
}
#----------------
# write message
# messages are: PREV, NEXT, ZAPP, QUIT, FWRD, BACK, RSTA, STOP
# LOAD\n<filename>, JUMP <time>
# VOLM <%>, VBAS <val>, VTRB <val>
#
proc WriteMsg {str} {
global Stat
puts $Stat(output) $str
flush $Stat(output)
}
#----------------
# callback commands
#
#
# jump to specified time
#
proc JumpCmd {val} {
global Stat
if {$val != $Stat(LastSec)} {
WriteMsg [format "JUMP %d" [expr $val * 100]]
}
}
#
# change volume amplitude
#
proc VolumeCmd {val {force 0}} {
global Config
if {$val < 0} {set val 0}
if {$val > 200} {set val 200}
if {$force != 0 || $val != $Config(CurVol)} {
WriteMsg [format "VOLM %d" $val]
}
}
#
proc SetChorus {} {
global Config
WriteMsg [format "CHRS %d" $Config(Mode:chorus)]
}
proc SetReverb {} {
global Config
WriteMsg [format "EVRB %d" $Config(Mode:reverb)]
}
#
# load the specified file
#
proc LoadCmd {idx} {
global Stat Config
WriteMsg "LOAD"
WriteMsg [lindex $Stat(FileList) $idx]
AppendMsg ""
VolumeCmd $Config(CurVol) 1
BassCmd $Config(CurBass)
TrebleCmd $Config(CurTreble)
}
#
# play the first file
#
proc PlayCmd {} {
global Stat
MakeShuffleList
if {$Stat(Playing) == 0} {
WriteMsg "NEXT"
}
}
#
# pause music
#
proc PauseCmd {} {
global Stat
if {$Stat(Playing)} {
if {$Stat(Paused)} {
set Stat(Paused) 0
} else {
set Stat(Paused) 1
}
DispButtonPlay
WriteMsg "STOP"
}
}
#
# stop playing
#
proc StopCmd {} {
global Stat Config
if {$Stat(Playing)} {
WriteMsg "QUIT"
SetTime 0
set Stat(CurIdx) -1
SelectNumber
if {$Stat(Tracing)} {
TraceReset
}
wm title . "TkAWEMidi"
wm iconname . "TkAWEMidi"
}
}
#
# quit TkAWEMidi
#
proc QuitCmd {} {
global Config Stat
if {$Config(AutoExit) || !$Config(ConfirmExit)} {
ReallyQuit
return
}
set oldpause $Stat(Paused)
if {!$oldpause} {PauseCmd}
if {[question "Really Quit TkAWEMidi?" 0]} {
ReallyQuit
return
}
if {!$oldpause} {PauseCmd}
}
#
# play previous file
#
proc PrevCmd {} {
global Stat
if {$Stat(Playing)} {
WriteMsg "PREV"
}
}
#
# play next file
#
proc NextCmd {} {
global Stat
if {$Stat(Playing)} {
WriteMsg "NEXT"
}
}
#
# restart play
#
proc RestartCmd {} {
global Stat
if {$Stat(Playing)} {
WriteMsg "RSTA"
}
}
#
# forward/backward 2 secs
#
proc ForwardCmd {} {
global Stat
if {$Stat(Playing)} {
WriteMsg "FWRD"
}
}
proc BackwardCmd {} {
global Stat
if {$Stat(Playing)} {
WriteMsg "BACK"
}
}
#
# volume up/down
#
proc VolUpCmd {} {
global Stat Config
if {$Stat(Playing)} {
VolumeCmd [expr $Config(CurVol) + 5]
}
}
proc VolDownCmd {} {
global Stat Config
if {$Stat(Playing)} {
VolumeCmd [expr $Config(CurVol) - 5]
}
}
proc BassCmd {val} {
WriteMsg [format "VBAS %d" $val]
}
proc TrebleCmd {val} {
WriteMsg [format "VTRB %d" $val]
}
proc BaseUpCmd {} {
WriteMsg [format "OFST 1"]
}
proc BaseDownCmd {} {
WriteMsg [format "OFST -1"]
}
#----------------
# display configured tables
#
proc DispTables {} {
global Config
set allitems {file time text volume button trace}
foreach i $allitems {
pack forget .body.$i
if {$Config(Disp:$i)} {
pack .body.$i -side top -fill x
}
}
}
#
# save configuration and playing mode
#
proc SaveConfig {} {
global Config ConfigFile
set fd [open $ConfigFile w]
if {$fd != ""} {
puts $fd "global Config"
foreach i [array names Config] {
if {[llength $Config($i)] > 1} {
puts $fd "set Config($i) {$Config($i)}"
} else {
puts $fd "set Config($i) $Config($i)"
}
}
close $fd
}
}
#
# load configuration file
#
proc LoadConfig {} {
global ConfigFile Stat
catch {source $ConfigFile}
}
#
# from command line
#
proc ParseCmdLine {} {
global Config Stat argc argv
set Stat(FileList) {}
set Stat(MaxFiles) 0
for {set i 0} {$i < $argc} {incr i} {
set v [lindex $argv $i]
if {$v == "-effect"} {
incr i
SetUserEffects [lindex $argv $i]
} elseif {$v == "-mode"} {
incr i
set mode [lindex $argv $i]
if {$mode == "shuffle"} {
set Config(ShufflePlay) 1
} elseif {$mode == "normal"} {
set Config(ShufflePlay) 0
} elseif {$mode == "autostart"} {
set Config(AutoStart) 1
} elseif {$mode == "autoexit"} {
set Config(AutoExit) 1
} elseif {$mode == "repeat"} {
set Config(RepeatPlay) 1
}
} elseif {![regexp "^-" $v dummy]} {
if {[regexp "^\(.*\)/" $v dummy root]} {
set v $root
}
if {[file isdirectory $v]} {
if {$v == "."} {
set pattern "*.mid*"
} else {
set pattern "$v/*.mid*"
}
foreach f [lsort [glob -nocomplain $pattern]] {
if {[file isfile $f]} {
lappend Stat(FileList) $f
incr Stat(MaxFiles)
}
}
} elseif {[file isfile $v]} {
lappend Stat(FileList) $v
incr Stat(MaxFiles)
}
}
}
}
#
# read a config file for user defined chorus/reverb modes
#
proc SetUserEffects {file} {
global Stat Config
set fp [open $file r]
if {$fp == ""} {return}
set Config(UserChorus) {}
set Config(UserReverb) {}
gets $fp line
while {![eof $fp]} {
if {[regexp "^\(\[crCR\]\)\[a-zA-Z\]*:\(\[0-9\]+\):\(\[^:\]+\):"\
$line dummy type mode name]} {
if {$type == "c" || $type == "C"} {
lappend Config(UserChorus) [list $mode $name]
} else {
lappend Config(UserReverb) [list $mode $name]
}
}
if {[regexp "\\$" $line]} {
while {1} {
gets $fp line
if {[eof $fp]} {close $fp; return}
if {![regexp "\\$" $line]} {break}
}
} else {
gets $fp line
}
}
close $fp
}
#
# selection callback of the playing file from listbox
#
proc SelectList {lw pos} {
global Config Stat
set idx [$lw nearest $pos]
if {$idx >= 0 && $idx < $Stat(MaxFiles)} {
if {$Config(ShufflePlay)} {
set found [lsearch -exact $Stat(ShuffleList) $idx]
if {$found != -1} {
set Stat(CurIdx) $found
}
} else {
set Stat(CurIdx) $idx
}
if {$Stat(Playing) && $Stat(Paused)} {
PauseCmd
}
set Stat(Playing) 1
SelectNumber
}
}
#
# open file browser and append some files
#
proc OpenFiles {} {
global Stat
if {$Stat(Playing)} {
return
}
set files [filebrowser .browser "" "*.mid*"]
if {$files != ""} {
foreach i $files {
if {[lsearch -exact $Stat(FileList) $i] == -1} {
.body.file.list insert end $i
lappend Stat(FileList) $i
incr Stat(MaxFiles)
}
}
MakeShuffleList
}
}
#
# clear file list
#
proc CloseFiles {} {
global Stat
if {$Stat(Playing) || $Stat(MaxFiles) == 0} {
return
}
if {[question "Really Clear List?" 0]} {
StopCmd
.body.file.list delete 0 end
set Stat(MaxFiles) 0
set Stat(FileList) {}
set Stat(SuffleList) {}
}
}
#
# toggle time display mode
#
proc ToggleTimeDispMode {} {
global Config Stat
if {$Config(CurFileMode) == 0} {
set Config(CurFileMode) 1
} else {
set Config(CurFileMode) 0
}
SetTime $Stat(LastSec)
}
#
# select and edit options for each midi file
#
proc EditIndexInList {} {
global Config Stat
set top .selediti
set w [my-dialog $top "Midi File Selection" 0 0 [list\
[list "Close" "destroy $top"]\
[list "Edit Default" "EditIndexFile .editor {}"]]]
set lw [make-listbox $w 36 10 0 single]
foreach i $Stat(FileList) {
$lw insert end $i
}
bind $lw <ButtonRelease-1> {DoEditIndex %W %y}
tkwait visibility $top
tkwait window $top
}
proc DoEditIndex {w y} {
global Config Stat
set idx [$w nearest $y]
if {$idx >= 0 && $idx < $Stat(MaxFiles)} {
EditIndexFile .editor [lindex $Stat(FileList) $idx]
}
}
#----------------
# create main window
#
proc CreateWindow {} {
global Config Stat
# menu bar
frame .menu -relief raised -bd 1
pack .menu -side top -expand 1 -fill x
# File menu
menubutton .menu.file -text "File" -menu .menu.file.m\
-underline 0
menu .menu.file.m
.menu.file.m add command -label "Open Files" -underline 0\
-command OpenFiles
.menu.file.m add command -label "Clear List" -underline 0\
-command CloseFiles
.menu.file.m add command -label "Edit Options" -underline 0\
-command EditIndexInList
.menu.file.m add command -label "Save Config" -underline 0\
-command SaveConfig
.menu.file.m add command -label "About" -underline 0\
-command {
information "TkAWEMidi -- Tcl/Tk interface for AWE MIDI player\n written by T.IWAI"
}
.menu.file.m add command -label "Quit" -underline 0\
-command QuitCmd
# Mode menu
menubutton .menu.mode -text "Mode" -menu .menu.mode.m\
-underline 0
menu .menu.mode.m
.menu.mode.m add check -label "Repeat" -underline 0\
-variable Config(RepeatPlay)
.menu.mode.m add check -label "Shuffle" -underline 0\
-variable Config(ShufflePlay) -command {
if {$Config(ShufflePlay)} {MakeShuffleList}
}
.menu.mode.m add check -label "Auto Start" -underline 5\
-variable Config(AutoStart)
.menu.mode.m add check -label "Auto Exit" -underline 5\
-variable Config(AutoExit)
.menu.mode.m add check -label "Confirm Quit" -underline 0\
-variable Config(ConfirmExit)
# Display menu
menubutton .menu.disp -text "Display" -menu .menu.disp.m\
-underline 0
menu .menu.disp.m
.menu.disp.m add check -label "File List" -underline 0\
-variable Config(Disp:file) -command "DispTables"
.menu.disp.m add check -label "Time" -underline 0\
-variable Config(Disp:time) -command "DispTables"
.menu.disp.m add check -label "Messages" -underline 0\
-variable Config(Disp:text) -command "DispTables"
.menu.disp.m add check -label "Volume" -underline 0\
-variable Config(Disp:volume) -command "DispTables"
.menu.disp.m add check -label "Buttons" -underline 0\
-variable Config(Disp:button) -command "DispTables"
if {$Stat(Tracing)} {
.menu.disp.m add check -label "Trace" -underline 1\
-variable Config(Disp:trace) -command "DispTables"
}
# chorus and reverb mode menu
menubutton .menu.c -text "Chorus" -menu .menu.c.m\
-underline 0
menu .menu.c.m
foreach i $Stat(ChorusList) {
.menu.c.m add radio -label [lindex $i 1]\
-variable Config(Mode:chorus) -value [lindex $i 0]\
-command "SetChorus"
}
foreach i $Config(UserChorus) {
.menu.c.m add radio -label [lindex $i 1]\
-variable Config(Mode:chorus) -value [lindex $i 0]\
-command "SetChorus"
}
menubutton .menu.r -text "Reverb" -menu .menu.r.m\
-underline 0
menu .menu.r.m
foreach i $Stat(ReverbList) {
.menu.r.m add radio -label [lindex $i 1]\
-variable Config(Mode:reverb) -value [lindex $i 0]\
-command "SetReverb"
}
foreach i $Config(UserReverb) {
.menu.r.m add radio -label [lindex $i 1]\
-variable Config(Mode:reverb) -value [lindex $i 0]\
-command "SetReverb"
}
pack .menu.file .menu.mode .menu.disp .menu.c .menu.r -side left
# display body
frame .body -relief flat
pack .body -side top -expand 1 -fill both
# current playing file
frame .body.curfile -relief flat
label .body.curfile.index -text "000/000" -relief flat -anchor w
label .body.curfile.title -text "--------" -relief flat -width 23\
-anchor w
button .body.curfile.time -text "00:00" -relief ridge -width 5\
-command "ToggleTimeDispMode"
pack .body.curfile.index .body.curfile.title -side left -expand 1
pack .body.curfile.time -side right
pack .body.curfile -side top -fill x -expand 1
# playing files list
frame .body.file -relief raised -bd 1
make-listbox .body.file 36 10 0 single
bind .body.file.list <Button-1> {SelectList %W %y}
foreach i $Stat(FileList) {
.body.file.list insert end $i
}
# time label and scale
frame .body.time -relief raised -bd 1
label .body.time.label -text "0:00 / 0:00"
pack .body.time.label -side top
scale .body.time.scale -orient horizontal -length 280\
-from 0 -to 100 -tickinterval 10
bind .body.time.scale <ButtonRelease-1> {JumpCmd [%W get]}
pack .body.time.scale -side bottom -expand 1 -fill x
# text browser
frame .body.text -relief raised -bd 1
scrollbar .body.text.bar -relief sunken\
-command ".body.text.buf yview"
pack .body.text.bar -side right -fill y
text .body.text.buf -width 36 -height 12 -relief sunken -bd 2\
-wrap word -yscroll ".body.text.bar set"
bind .body.text.buf <Button-1> { }
bind .body.text.buf <Any-Key> { }
pack .body.text.buf -side top -expand 1 -fill both
button .body.text.clear -text "Clear"\
-command ClearMsg
pack .body.text.clear -side bottom
# volume label and scale
frame .body.volume -relief raised -bd 1
#label .body.volume.label -text "Volume:"
#pack .body.volume.label -side top
scale .body.volume.scale -orient horizontal -length 280\
-from 0 -to 200 -tickinterval 25\
-showvalue true -label "Volume"
bind .body.volume.scale <ButtonRelease-1> {VolumeCmd [%W get]}
pack .body.volume.scale -side top -expand 1 -fill x
frame .body.volume.mixer -relief flat -bd 0
scale .body.volume.mixer.bass -orient horizontal -length 135\
-from 0 -to 11 -showvalue false -label "Bass"\
-variable Config(CurBass)\
-command BassCmd
#bind .body.volume.mixer.bass <ButtonRelease-1> {BassCmd [%W get]}
scale .body.volume.mixer.treble -orient horizontal -length 135\
-from 0 -to 11 -showvalue false -label "Treble"\
-variable Config(CurTreble)\
-command TrebleCmd
#bind .body.volume.mixer.treble <ButtonRelease-1> {TrebleCmd [%W get]}
pack .body.volume.mixer.bass .body.volume.mixer.treble -side left -expand 1 -expand 1
pack .body.volume.mixer -side bottom -expand 1 -fill x
# buttons
global bitmap_path
frame .body.button -relief raised -bd 1
button .body.button.play -bitmap @$bitmap_path/play.xbm\
-command "PlayCmd"
button .body.button.stop -bitmap @$bitmap_path/stop.xbm\
-command "StopCmd"
button .body.button.prev -bitmap @$bitmap_path/prev.xbm\
-command "PrevCmd"
button .body.button.back -bitmap @$bitmap_path/back.xbm\
-command "BackwardCmd"
button .body.button.fwrd -bitmap @$bitmap_path/fwrd.xbm\
-command "ForwardCmd"
button .body.button.next -bitmap @$bitmap_path/next.xbm\
-command "NextCmd"
button .body.button.pause -bitmap @$bitmap_path/pause.xbm\
-command "PauseCmd"
button .body.button.quit -bitmap @$bitmap_path/quit.xbm\
-command "QuitCmd"
pack .body.button.play .body.button.pause\
.body.button.prev .body.button.back\
.body.button.stop\
.body.button.fwrd .body.button.next\
.body.button.quit\
-side left -ipadx 4 -pady 2 -fill x
if {$Stat(Tracing)} {
# trace display
TraceCreate
}
TraceUpdate
# pack all items
DispTables
focus .
tk_menuBar .menu .menu.file .menu.mode .menu.disp
bind . <Key-Right> "ForwardCmd"
bind . <Key-n> "NextCmd"
bind . <Key-Left> "BackwardCmd"
bind . <Key-p> "PrevCmd"
bind . <Key-r> "RestartCmd"
bind . <Key-Down> "VolDownCmd"
bind . <Key-v> "VolDownCmd"
bind . <Key-Up> "VolUpCmd"
bind . <Key-V> "VolUpCmd"
bind . <Key-s> "PauseCmd"
bind . <Key-space> "PauseCmd"
bind . <Return> "PlayCmd"
bind . <Key-c> "StopCmd"
bind . <Key-q> "QuitCmd"
bind . <Key-u> "BaseUpCmd"
bind . <Key-d> "BaseDownCmd"
SetVolume $Config(CurVol)
}
awemidi-0.4.2b/ext_c.c 100644 765 12 43625 6413671576 13070 0 ustar iwai wheel /*================================================================
* General Pipe and Tcl/Tk interface mode
*================================================================*/
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#ifdef INCLUDE_TK_MODE
#include "trace.h"
#endif
#include "util.h"
#include "sighandle.h"
#include "channel.h"
#include "midievent.h"
#include "controls.h"
#include "seq.h"
#include "ext_c.h"
#include "options.h"
static void ctl_update(MidiInfo *mp);
static void ctl_total_time(int tt);
static void ctl_file_name(char *name);
static void ctl_title(char *name);
static void ctl_current_time(int ct);
static void ctl_note(int ch, int note, int vel);
static void ctl_program(int ch, int val);
static void ctl_control_change(int channel, int type, int val);
static void ctl_pitch_bend(int channel, int val);
static void ctl_bank(int ch, int val);
static void ctl_system_change(int type, int mode);
static void ctl_effects_change(int type, int ch, int val);
static void ctl_master_vol(int atten);
static void ctl_reset(MidiInfo *mp);
static void ctl_pitch_sense(int ch, int val);
static int ctl_read(MidiInfo *mp, int *valp);
static int cmsg(int type, int verbosity_level, char *fmt, ...);
static int ctl_blocking_read(MidiInfo *mp, int *valp);
static void pipe_printf(char *fmt, ...);
static void pipe_puts(char *str);
static int pipe_gets(char *str, int maxlen);
static int pipe_read_ready();
static void start_midi(void);
#ifdef INCLUDE_TK_MODE
static int tk_open(int using_stdin, int using_stdout);
static void tk_close(void);
static void tk_pass_playing_list(int number_of_files, char *list_of_files[]);
#endif
#ifdef INCLUDE_PIPE_MODE
static int pipe_open(int using_stdin, int using_stdout);
static void pipe_close(void);
static void pipe_pass_playing_list(int number_of_files, char *list_of_files[]);
#endif
/*----------------------------------------------------------------
* shared parameters
*----------------------------------------------------------------*/
PanelInfo *extpanel;
int gen_shmid = -1;
int pipe_in = -1, pipe_out = -1;
/*----------------------------------------------------------------
* control interface
*----------------------------------------------------------------*/
#ifdef INCLUDE_PIPE_MODE
ControlMode pipe_control_mode =
{
"general pipe interface", 'p',
FALSE, /* need_args */
TRUE, /* need_sync */
TRUE, /* fancy_prev */
0, 0, 0, 0, /* verbose, trace, open, repeat */
pipe_open, pipe_pass_playing_list, pipe_close,
ctl_read, ctl_blocking_read, cmsg,
ctl_update, ctl_reset, ctl_file_name, ctl_title, ctl_total_time,
ctl_current_time, ctl_note, ctl_program, ctl_bank, ctl_control_change,
ctl_pitch_bend, ctl_pitch_sense, ctl_system_change,
ctl_effects_change, ctl_master_vol,
};
#endif
#ifdef INCLUDE_TK_MODE
ControlMode tk_control_mode =
{
"Tcl/Tk interface", 'k',
FALSE, /* need_args */
TRUE, /* need_sync */
TRUE, /* fancy_prev */
0, 0, 0, 0, /* verbose, trace, open, repeat */
tk_open, tk_pass_playing_list, tk_close,
ctl_read, ctl_blocking_read, cmsg,
ctl_update, ctl_reset, ctl_file_name, ctl_title, ctl_total_time,
ctl_current_time, ctl_note, ctl_program, ctl_bank, ctl_control_change,
ctl_pitch_bend, ctl_pitch_sense, ctl_system_change,
ctl_effects_change, ctl_master_vol,
};
#endif
/*----------------------------------------------------------------
* controls via pipe
*----------------------------------------------------------------*/
static int cmsg(int type, int verbosity_level, char *fmt, ...)
{
char local[1000];
#define TOO_LONG 980
va_list ap;
if ((type==CMSG_TEXT || type==CMSG_INFO || type==CMSG_WARNING) &&
ctl->verbosity<verbosity_level)
return 0;
va_start(ap, fmt);
if (! ctl->opened || pipe_out < 0) {
if (type == CMSG_ERROR) {
vfprintf(stderr, fmt, ap);
putc('\n', stderr);
}
} else if (type == CMSG_ERROR) {
if (strlen(fmt) > TOO_LONG)
fmt[TOO_LONG] = 0;
vsprintf(local, fmt, ap);
pipe_printf("CERR %d", type);
pipe_puts(local);
} else {
if (type == CMSG_TEXT) {
char *p;
while ((p = strtok(fmt, "\n")) != NULL) {
pipe_printf("CMSG %d %d", type, strlen(p));
pipe_puts(p);
fmt = NULL;
}
} else {
if (strlen(fmt) > TOO_LONG)
fmt[TOO_LONG] = 0;
vsprintf(local, fmt, ap);
pipe_printf("CMSG %d %d", type, strlen(local));
pipe_puts(local);
}
}
va_end(ap);
return 0;
}
static void ctl_update(MidiInfo *mp) {}
static void ctl_total_time(int tt)
{
pipe_printf("TIME %d", tt);
}
static void ctl_file_name(char *name)
{
char tmp[32+1], *p;
sprintf(tmp, "%-32.32s", name);
if ((p = strchr(tmp, '\n')) != NULL) *p = 0;
pipe_printf("FILE %s", tmp);
}
static void ctl_title(char *name)
{
char tmp[32+1], *p;
sprintf(tmp, "%-32.32s", name);
if ((p = strchr(tmp, '\n')) != NULL) *p = 0;
pipe_printf("TITL %s", tmp);
}
static void ctl_system_change(int type, int mode)
{
switch (type) {
case SY_CHORUS_MODE:
pipe_printf("CHRS %d", mode);
break;
case SY_REVERB_MODE:
pipe_printf("EVRB %d", mode);
break;
}
}
static void ctl_master_vol(int vol)
{
pipe_printf("VOLM %d", vol);
}
static void ctl_effects_change(int ch, int type, int value)
{
}
/*----------------------------------------------------------------
* controls vis shared memory
*----------------------------------------------------------------*/
static void ctl_pitch_sense(int ch, int val)
{
if (!(ctl->playing_mode & PLAY_TRACE))
return;
extpanel->channels[ch].pitchsense = val;
}
static void ctl_pitch_bend(int ch, int val)
{
if (!(ctl->playing_mode & PLAY_TRACE))
return;
extpanel->channels[ch].pitchbend = val;
}
static void ctl_current_time(int ct)
{
if (extpanel)
extpanel->curcs = ct;
}
static int cnote[MAX_MIDI_CHANNELS];
static int cvel[MAX_MIDI_CHANNELS];
static void ctl_note(int ch, int note, int vel)
{
if (!(ctl->playing_mode & PLAY_TRACE))
return;
if (vel == 0) {
if (note == cnote[ch]) {
cvel[ch] = 0;
extpanel->vel[ch] = 0;
}
} else if (vel >= cvel[ch]) {
cvel[ch] = vel;
cnote[ch] = note;
extpanel->vel[ch] = vel * extpanel->channels[ch].controls[CTL_MAIN_VOLUME] *
extpanel->channels[ch].controls[CTL_EXPRESSION] / (127*127);
if (extpanel->vel[ch] > extpanel->maxvel[ch])
extpanel->maxvel[ch] = extpanel->vel[ch];
}
}
static void ctl_program(int ch, int val)
{
if (!(ctl->playing_mode & PLAY_TRACE))
return;
extpanel->channels[ch].preset = val;
}
static void ctl_bank(int ch, int val)
{
if (!(ctl->playing_mode & PLAY_TRACE))
return;
extpanel->channels[ch].bank = val;
}
static void ctl_control_change(int ch, int type, int val)
{
if (!(ctl->playing_mode & PLAY_TRACE))
return;
extpanel->channels[ch].controls[type] = val;
if (type == CTL_MAIN_VOLUME || type == CTL_EXPRESSION)
ctl_note(ch, cnote[ch], cvel[ch]);
}
/*----------------------------------------------------------------
* reset controls
*----------------------------------------------------------------*/
static void ctl_reset(MidiInfo *mp)
{
int i;
if (!(ctl->playing_mode & PLAY_TRACE))
return;
if (extpanel->multi_part != mp->multi_part) {
extpanel->multi_part = mp->multi_part;
extpanel->reset_panel = 1;
}
for (i = 0; i < MAX_MIDI_CHANNELS; i++) {
ChannelStat *cp = &channels[i];
ctl_program(i, cp->preset);
ctl_bank(i, cp->bank);
ctl_pitch_sense(i, cp->pitchsense);
ctl_pitch_bend(i, cp->pitchbend);
ctl_control_change(i, CTL_SUSTAIN, cp->controls[CTL_SUSTAIN]);
ctl_control_change(i, CTL_PAN, cp->controls[CTL_PAN]);
ctl_control_change(i, CTL_MAIN_VOLUME, cp->controls[CTL_MAIN_VOLUME]);
ctl_control_change(i, CTL_EXPRESSION, cp->controls[CTL_EXPRESSION]);
cvel[i] = 0;
extpanel->vel[i] = extpanel->maxvel[i] = 0;
}
}
#ifdef INCLUDE_PIPE_MODE
/*================================================================
* open/close general pipe interface
*================================================================*/
static int pipe_open(int using_stdin, int using_stdout)
{
if (ctl->playing_mode & PLAY_TRACE) {
if (gen_shmid < 0)
ctl->playing_mode &= ~PLAY_TRACE;
else {
extpanel = (PanelInfo *)shmat(gen_shmid, 0, 0);
extpanel->reset_panel = 0;
extpanel->multi_part = 0;
}
}
ctl->opened = 1;
return 0;
}
static void pipe_close(void)
{
if (ctl->opened) {
ctl->opened = 0;
}
}
#endif
#ifdef INCLUDE_TK_MODE
/*================================================================
* open/close Tcl/Tk interface
*================================================================*/
char *tk_display = NULL;
char *tk_geometry = NULL;
char *tk_effect = NULL;
static int child_pid = -1;
static void shm_alloc(void);
static void shm_free(void);
static void child_killed(void);
static void start_panel(void);
static int AppInit(Tcl_Interp *interp);
static int StartPlayer(ClientData clientData, Tcl_Interp *interp,
int argc, char *argv[]);
static int ReallyQuit(ClientData clientData, Tcl_Interp *interp,
int argc, char *argv[]);
static int InitCmdLine(ClientData clientData, Tcl_Interp *interp,
int argc, char *argv[]);
static int ExitAll(ClientData clientData, Tcl_Interp *interp,
int argc, char *argv[]);
static void child_killed(void)
{
int st;
waitpid(child_pid, &st, WNOHANG);
if (! WIFEXITED(st)) {
reset_signal(SIGCHLD);
return;
}
child_pid = -1;
shm_free();
exit(0);
}
static void shm_alloc(void)
{
gen_shmid = shmget(IPC_PRIVATE, sizeof(PanelInfo),
IPC_CREAT|0600);
if (gen_shmid < 0) {
fprintf(stderr, "can't allocate shared memory\n");
exit(1);
}
extpanel = (PanelInfo *)shmat(gen_shmid, 0, 0);
extpanel->reset_panel = 0;
extpanel->multi_part = 0;
}
static void shm_free(void)
{
int st;
if (child_pid != -1) {
int st;
signal(SIGCHLD, SIG_DFL);
kill(child_pid, SIGTERM);
waitpid(child_pid, &st, 0);
}
if (gen_shmid >= 0) {
shmctl(gen_shmid, IPC_RMID,NULL);
shmdt((char *)extpanel);
gen_shmid = -1;
}
}
static int tk_open(int using_stdin, int using_stdout)
{
int pipeAppli[2], pipePanel[2];
int rc;
if (pipe(pipeAppli) || pipe(pipePanel)) {
fprintf(stderr, "can't open pipe\n");
return 1;
}
shm_alloc();
if ((child_pid = fork()) == 0) {
/*child*/
close(pipePanel[1]);
close(pipeAppli[0]);
/* redirect to stdin/out */
dup2(pipePanel[0], fileno(stdin));
close(pipePanel[0]);
dup2(pipeAppli[1], fileno(stdout));
close(pipeAppli[1]);
start_panel();
} else {
close(pipePanel[0]);
close(pipeAppli[1]);
pipe_in = pipeAppli[0];
pipe_out = pipePanel[1];
}
add_signal(SIGCHLD, child_killed, 0);
add_signal(SIGTERM, shm_free, 1);
add_signal(SIGINT, shm_free, 1);
ctl->opened = 1;
return 0;
}
static void tk_close(void)
{
if (ctl->opened) {
ctl->opened = 0;
shm_free();
}
}
static void start_panel(void)
{
char *argv[128];
int argc;
argc = 0;
argv[argc++] = "-f";
argv[argc++] = TKPROGPATH;
if (tk_display) {
argv[argc++] = "-display";
argv[argc++] = tk_display;
}
if (tk_geometry) {
argv[argc++] = "-geometry";
argv[argc++] = tk_geometry;
}
/* call Tk main routine */
Tk_Main(argc, argv, AppInit);
exit(0);
}
Tcl_Interp *my_interp;
static int AppInit(Tcl_Interp *interp)
{
my_interp = interp;
if (Tcl_Init(interp) == TCL_ERROR) {
return TCL_ERROR;
}
if (Tk_Init(interp) == TCL_ERROR) {
return TCL_ERROR;
}
Tcl_CreateCommand(interp, "ReallyQuit", ReallyQuit,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, "StartPlayer", StartPlayer,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, "InitCmdLine", InitCmdLine,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, "TraceCreate", TraceCreate,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, "TraceUpdate", TraceUpdate,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, "TraceReset", TraceReset,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, "ExitAll", ExitAll,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
return TCL_OK;
}
static int ReallyQuit(ClientData clientData, Tcl_Interp *interp,
int argc, char *argv[])
{
v_eval("WriteMsg \"ZAPP\"");
return TCL_OK;
}
static int StartPlayer(ClientData clientData, Tcl_Interp *interp,
int argc, char *argv[])
{
return TCL_OK;
}
static int ExitAll(ClientData clientData, Tcl_Interp *interp,
int argc, char *argv[])
{
/* window is killed; kill the parent process, too */
kill(getppid(), SIGTERM);
for (;;)
sleep(1000);
}
static int InitCmdLine(ClientData clientData, Tcl_Interp *interp,
int argc, char *argv[])
{
if (tk_effect)
v_eval("SetUserEffects %s", tk_effect);
if (ctl->playing_mode & PLAY_SHUFFLE)
v_eval("set Config(ShufflePlay) 1");
else
v_eval("set Config(ShufflePlay) 0");
if (ctl->playing_mode & PLAY_TRACE) {
v_eval("set Stat(Tracing) 1");
v_eval("set Config(Disp:trace) 1");
} else {
v_eval("set Stat(Tracing) 0");
v_eval("set Config(Disp:trace) 0");
}
if (ctl->playing_mode & PLAY_AUTOSTART)
v_eval("set Config(AutoStart) 1");
if (ctl->playing_mode & PLAY_AUTOEXIT)
v_eval("set Config(AutoExit) 1");
if (ctl->playing_mode & PLAY_REPEAT)
v_eval("set Config(RepeatPlay) 1");
return TCL_OK;
}
#endif
/*----------------------------------------------------------------
* Read information coming from the window in a BLOCKING way
*----------------------------------------------------------------*/
/* commands are: PREV, NEXT, QUIT, STOP, LOAD, JUMP, VOLM */
static int ctl_blocking_read(MidiInfo *mp, int *valp)
{
char buf[256], *arg;
int rc;
rc = pipe_gets(buf, sizeof(buf)-1);
arg = buf + 5;
switch (*buf) {
case 'J': /* jump */
*valp = atoi(arg);
return RC_JUMP;
case 'M': /* move */
*valp = atoi(arg);
return RC_MOVE;
case 'Z': /* kill */
return RC_KILL;
case 'Q': /* stop */
return RC_QUIT;
case 'L': /* load file; the file name appears in next line */
return RC_LOAD_FILE;
case 'N': /* next */
return RC_NEXT;
case 'P': /* previous */
return RC_PREVIOUS;
case 'R': /* restart */
return RC_RESTART;
case 'F': /* forward */
*valp = 200;
return RC_MOVE;
case 'B': /* backward */
*valp = -200;
return RC_MOVE;
case 'C': /* chorus mode */
*valp = atoi(arg);
return RC_CHANGE_CHORUS;
case 'E': /* reverb mode */
*valp = atoi(arg);
return RC_CHANGE_REVERB;
case 'V': /* volume */
*valp = atoi(arg);
switch (buf[1]) {
case 'O':
return RC_CHANGE_VOLUME;
case 'B':
return RC_CHANGE_BASS;
case 'T':
return RC_CHANGE_TREBLE;
}
return RC_NONE;
case 'S': /* pause/continue */
return *valp ? RC_CONTINUE : RC_PAUSE;
case 'O': /* key offset */
*valp = atoi(arg);
return RC_BASE_CHANGE;
case 'A': /* auto repeat mode */
ctl->repeated = atoi(arg);
return RC_NONE;
case 'D': /* drum channels */
channel_drums = atoi(arg);
return RC_NONE;
case 'X':
if (mp)
parse_index_line(mp, arg, TRUE);
break;
}
return RC_NONE;
}
/*
* Read information coming from the window in a non blocking way
*/
static int ctl_read(MidiInfo *mp, int *valp)
{
if (pipe_in >= 0 && pipe_read_ready() > 0)
return ctl_blocking_read(mp, valp);
else
return RC_NONE;
}
/*----------------------------------------------------------------
* pass file lists and start playing
*----------------------------------------------------------------*/
#ifdef INCLUDE_PIPE_MODE
static void pipe_pass_playing_list(int number_of_files, char *list_of_files[])
{
int i;
/* Pass the list to the interface */
if (ctl->playing_mode & PLAY_SHUFFLE) {
srandom(getpid());
for (i = 0; i < number_of_files; i++) {
int j = random() % (number_of_files - i);
if (j) {
char *tmp = list_of_files[i];
list_of_files[i] = list_of_files[i + j];
list_of_files[i + j] = tmp;
}
}
}
if (pipe_in < 0) {
MidiInfo minfo;
int command;
for (i = 0; i < number_of_files; i++) {
memcpy(&minfo, &glinfo, sizeof(minfo));
minfo.filename = list_of_files[i];
if ((command = play_midi_file(&minfo)) == RC_KILL)
break;
}
return;
}
start_midi();
}
#endif
#ifdef INCLUDE_TK_MODE
static void tk_pass_playing_list(int number_of_files, char *list_of_files[])
{
int i;
/* Pass the list to the interface */
if (number_of_files > 0 && list_of_files != NULL) {
pipe_printf("LIST %d", number_of_files);
for (i = 0; i <number_of_files; i++)
pipe_puts(list_of_files[i]);
}
start_midi();
}
#endif
/* start playing */
static void start_midi(void)
{
int command;
int val;
char local[1000];
MidiInfo minfo;
/* read the first command */
memcpy(&minfo, &glinfo, sizeof(minfo));
command = ctl_blocking_read(&minfo, &val);
/* Main Loop */
while (command != RC_KILL) {
if (command == RC_LOAD_FILE) {
/* Read a LoadFile command */
pipe_gets(local, sizeof(local)-1);
memcpy(&minfo, &glinfo, sizeof(minfo));
minfo.filename = local;
command = play_midi_file(&minfo);
} else {
switch (command) {
case RC_QUIT:
midi_close(&minfo);
break;
case RC_NEXT:
case RC_TUNE_END:
pipe_puts("NEXT");
break;
case RC_REALLY_PREVIOUS:
pipe_puts("PREV");
break;
}
command = ctl_blocking_read(&minfo, &val);
}
}
}
/*================================================================
* pipe comunication
*================================================================*/
static int pipe_read_ready()
{
int num;
ioctl(pipe_in, FIONREAD, &num); /* see how many chars in buffer. */
return num;
}
static void pipe_printf(char *fmt, ...)
{
char buf[1024];
va_list ap;
va_start(ap, fmt);
if (pipe_out < 0) return;
vsprintf(buf, fmt, ap);
pipe_puts(buf);
}
static void pipe_puts(char *str)
{
int len;
char lf = '\n';
if (pipe_out < 0) return;
len = strlen(str);
write(pipe_out, str, len);
write(pipe_out, &lf, 1);
}
int pipe_gets(char *str, int maxlen)
{
/* blocking reading */
char *p;
int len;
/* at least 5 letters (4+\n) command */
len = 0;
for (p = str; ; p++) {
read(pipe_in, p, 1);
if ((*p == '\n') || (len >= maxlen))
break;
len++;
}
*p = 0;
return len;
}
awemidi-0.4.2b/seq.h 100644 765 12 3150 6413671221 12513 0 ustar iwai wheel /*
*/
#ifndef SEQ_H_DEF
#define SEQ_H_DEF
extern void seqbuf_dump();
/* open / close sequencer device */
int seq_init(int do_echoback);
void seq_end(void);
int seq_opened(void);
void seq_blocking_mode(int mode);
int seq_pending(void);
void seq_check_pending(void);
/* timer controls */
void seq_clear(int csec);
int seq_timer_started(void);
int seq_lasttime(void);
int seq_curtime(void);
void seq_wait(int time);
void seq_echo(int csec);
int seq_sleep(int csec);
int seq_wait_time(int csec);
/* immediately reset voices */
void seq_terminate_all(void);
/* send seq events */
void seq_set_bank(int v, int bank);
void seq_set_program(int v, int pgm);
void seq_start_note(int v, int note, int vel);
void seq_stop_note(int v, int note, int vel);
void seq_control(int v, int type, int val);
void seq_aftertouch(int v, int note, int vel);
void seq_chan_pressure(int v, int val);
void seq_pitchsense(int v, int val);
void seq_pitchbend(int v, int val);
void seq_send_effect(int v, int type, int val);
void seq_add_effect(int v, int type, int val);
void seq_soft_pedal(int v, int val);
void seq_note_off_all(void);
void seq_sound_off_all(void);
/* send status as buffered */
void seq_reset_channel(int v);
void seq_reset_control(int v);
void seq_set_drumchannels(int channels);
void seq_channel_priority(int mode);
void seq_detune(int v, int coarse, int fine);
void seq_set_realtime_pan(int mode);
void seq_set_def_drum(int val);
/* immediately change status */
void seq_set_chorus(int mode);
void seq_set_reverb(int mode);
void seq_change_volume(int vol);
void seq_equalizer(int bass, int treble);
void seq_remove_samples(void);
#endif
awemidi-0.4.2b/INSTALL 100644 765 12 3566 6410023263 12610 0 ustar iwai wheel *** Installation of drvmidi ***
0. Requirements
- AWE32 driver v0.4.2 or newer
- Tcl7.5/Tk4.1 libraries (optional)
- Ncurses library (optional)
- AWESFX package v0.4.2 or newer (optional)
AWE32 driver and utilities can be downloaded from:
http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/
1. Edit Imakefile (if necessary).
* Select control interfaces
- Select control interfaces to be built in drvmidi (from line
12 to 15). Currently all interfaces except Tk interface are
selected. Comment out unnecessary interfaces (do NOT set
value to 0). At least one interface should be defined.
* AWE library (optional)
- If you want to use AWElib for dynamic loading, uncomment
the definition in line 30. Otherwise, sfxload program is
used to load the samples.
* Ncurses interface options (optional)
- if you don't have ncurses library, comment out
the line `INTERFACE_NCURSES' at the beginning of Imakefile.
* Tkmidi program
- to build the external Tcl/Tk interface (tkmidi), comment out
and define 'BUILD_TKMIDI' in Imakefile (line 22).
* Tcl/Tk options (for both tkmidi and tk interface)
- both tkmidi and tk interface refer to a directory to store
scripts and bitmap files specified in TCLDIR (line 46).
- if the program name of wish is different, change TCLSH.
- if you want to use the old type file browser, rename the
file "browser.tcl.old" to "browser.tcl".
* Atmidi program
- to build another external interface, comment out and define
'BUILID_ATMIDI' in Imakefile (line 23). This program
requires Athena Widget set.
2. Edit config.h (if any).
Miscellaneous configurations are defined in config.h
You can change the default parameter of command line options.
See the comment of each definition.
3. Make Makefile via xmkmf
% xmkmf
4. Make, and install programs
% make all
% make install
5. That's all. Enjoy listening!
awemidi-0.4.2b/miditext.c 100644 765 12 16032 6404555234 13575 0 ustar iwai wheel /*================================================================
* miditext:
* visualize MIDI event sequences to stdout
*
* usage: miditext [-options] midifile
*
* Copyright (C) 1996,1997 Takashi Iwai
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*================================================================*/
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "midievent.h"
#include "util.h"
#include "controls.h"
#include "awe_effect.h"
#include "channel.h"
#include "options.h"
static int cmsg(int type, int verbosity_level, char *fmt, ...);
static ControlMode dummy_ctl =
{
"dumb interface", 'd',
FALSE,
FALSE,
FALSE,
0,0,0,0,
NULL,NULL,NULL,NULL,NULL,cmsg,
NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,
};
ControlMode *ctl = &dummy_ctl;
MidiInfo glinfo;
int verbose = 0, debug = 0;
int seqfd, awe_dev;
#define OPTION_FLAGS "hvG:T:"
#define OPT_CHANNEL OPT_LOCAL
static awe_option_args long_options[] = {
{"help", 1, 0, OPT_HELP},
{"verbose", 2, 0, OPT_VERBOSE},
{"gsmacro", 2, 0, OPT_GSMACRO},
{"