1 /************************************************************************
2    io_ncurses.c  -- shows midi events using ncurses or printf
3 
4    Copyright (C) 1994-1996 Nathan I. Laredo
5 
6    This program is modifiable/redistributable under the terms
7    of the GNU General Public Licence.
8 
9    You should have received a copy of the GNU General Public License
10    along with this program; if not, write to the Free Software
11    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
12    Send your comments and all your spare pocket change to
13    laredo@gnu.ai.mit.edu (Nathan Laredo) or to PSC 1, BOX 709, 2401
14    Kelly Drive, Lackland AFB, TX 78236-5128, USA.
15  *************************************************************************/
16 #include "playmidi.h"
17 #ifdef linux
18 #include <ncurses/curses.h>
19 #else
20 #include <ncurses.h>
21 #endif
22 #include "gsvoices.h"
23 #include <sys/time.h>
24 #include <unistd.h>
25 
26 char *metatype[7] =
27 {"Text", "Copyright Notice", "Sequence/Track name",
28  "Instrument Name", "Lyric", "Marker", "Cue Point"};
29 
30 char *sharps[12] =		/* for a sharp key */
31 {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "B#", "B"};
32 char *flats[12] =		/* for a flat key */
33 {"C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Gb", "A", "Bb", "B"};
34 char *majflat[15] =		/* name of major key with 'x' flats */
35 {"C", "F", "Bb", "Eb", "Ab", "Db", "Gb", "Cb", "Fb", "Bbb", "Ebb",
36  "Abb", "Gbb", "Cbb", "Fbb"};	/* only first 8 defined by file format */
37 char *majsharp[15] =		/* name of major key with 'x' sharps */
38 {"C", "G", "D", "A", "E", "B", "F#", "C#", "G#", "D#", "A#",
39  "E#", "B#", "F##", "C##"};	/* only first 8 defined by file format */
40 char *minflat[15] =		/* name of minor key with 'x' flats */
41 {"A", "D", "G", "C", "F", "Bb", "Eb", "Ab", "Db", "Gb", "Cb",
42  "Fb", "Bbb", "Ebb", "Abb"};	/* only first 8 defined by file format */
43 char *minsharp[15] =		/* name of minor key with 'x' sharps */
44 {"A", "E", "B", "F#", "C#", "G#", "D#", "A#", "E#", "B#", "F##",
45  "C##", "G##", "D##", "A##"};	/* only first 8 defined by file format */
46 
47 char *drumset[11] =
48 {
49  "STANDARD    ", "ROOM        ", "POWER       ", "ELECTRONIC  ",
50  "TR-808/909  ", "JAZZ        ", "BRUSH       ", "ORCHESTRA   ",
51  "SFX         ", "User Program", "CM-64/32L   "
52 };
53 
54 #define SET(x) (x == 8 ? 1 : x >= 16 && x <= 23 ? 2 : x == 24 ? 3 : \
55 		x == 25  ? 4 : x == 32 ? 5 : x >= 40 && x <= 47 ? 6 : \
56 		x == 48 ? 7 : x == 56 ? 8 : x >= 96 && x <= 111 ? 9 : \
57 		x == 127 ? 10 : 0)
58 
59 extern int graphics, verbose, perc;
60 extern int format, ntrks, division;
61 extern unsigned long int ticks;
62 extern char *filename, *gmvoice[256];
63 extern float skew;
64 extern void seq_reset();
65 extern struct timeval start_time;
66 
67 struct timeval now_time, want_time;
68 char textbuf[1024], **nn;
69 int i, ytxt, karaoke;
70 
close_show(error)71 void close_show(error)
72 int error;
73 {
74     if (graphics) {
75 	attrset(A_NORMAL);
76 	refresh();
77 	endwin();
78     }
79     exit(error);
80 }
81 
82 #define CHN		(cmd & 0xf)
83 #define NOTE		((int)data[0])
84 #define VEL		((int)data[1])
85 #define OCTAVE		(NOTE / 12)
86 #define OCHAR		(0x30 + OCTAVE)
87 #define XPOS		(14 + ((NOTE/2) % ((COLS - 16) / 4)) * 4)
88 #define YPOS		(CHN + 2)
89 #define NNAME		nn[NOTE % 12]
90 
cdeltat(t1,t2)91 int cdeltat(t1, t2)
92 struct timeval *t1;
93 struct timeval *t2;
94 {
95     int d1, d2;
96 
97     d1 = t1->tv_sec - t2->tv_sec;
98     if((d2 = t1->tv_usec - t2->tv_usec) < 0)
99 	(d2 += 1000000, d1 -= 1);
100     d2 /= 10000;
101     return (d2 + d1 * 100);
102 }
updatestatus()103 int updatestatus()
104 {
105     int ch, d1, d2;
106 
107     want_time.tv_sec = start_time.tv_sec + (ticks / 100);
108     want_time.tv_usec = start_time.tv_usec + (ticks % 100) * 10000;
109     if (want_time.tv_usec > 1000000)
110 	(want_time.tv_usec -= 1000000, want_time.tv_sec++);
111 
112     do {
113 	attrset(A_BOLD);
114 	if ((ch = getch()) != ERR)
115 	    switch (ch) {
116 	    case KEY_RIGHT:
117 		if ((skew -= 0.01) < 0.25)
118 		    skew = 0.25;
119 		if (graphics)
120 		    mvprintw(1, COLS - 6, "%0.2f", skew);
121 		break;
122 	    case KEY_LEFT:
123 		if ((skew += 0.01) > 4)
124 		    skew = 4.0;
125 		if (graphics)
126 		    mvprintw(1, COLS - 6, "%0.2f", skew);
127 		break;
128 	    case KEY_PPAGE:
129 	    case KEY_UP:
130 		seq_reset();
131 		return (ch == KEY_UP ? 0 : -1);
132 		break;
133 	    case 18:
134 	    case 12:
135 		wrefresh(curscr);
136 		break;
137 	    case 'q':
138 	    case 'Q':
139 	    case 3:
140 		close_show(0);
141 		break;
142 	    default:
143 		return 1;	/* skip to next song */
144 		break;
145 	    }
146 	gettimeofday(&now_time, NULL);
147 	d1 = now_time.tv_sec - start_time.tv_sec;
148 	d2 = now_time.tv_usec - start_time.tv_usec;
149 	if (d2 < 0)
150 	    (d2 += 1000000, d1 -= 1);
151 	mvprintw(1, 0, "%02d:%02d.%d", d1 / 60, d1 % 60, d2 / 100000);
152 	refresh();
153 	d1 = cdeltat(&want_time, &now_time);
154 	if (d1 > 15)
155 	    usleep(100000);
156     } while (d1 > 10);
157     return NO_EXIT;
158 }
159 
showevent(cmd,data,length)160 void showevent(cmd, data, length)
161 int cmd;
162 unsigned char *data;
163 int length;
164 {
165     if (cmd < 8 && cmd > 0) {
166 	if (length > COLS)
167 	    length = COLS;
168 	if (cmd != 1 && strncmp(textbuf, data, length - 1) == 0)
169 	     return;	/* ignore repeat messages, "WinJammer Demo" etc. */
170 	if (verbose) {
171 	    printf("%s: ", metatype[cmd - 1]);
172 	    for (i = 0; i < length; i++)
173 		putchar(data[i]);
174 	    putchar('\n');
175 	} else {
176 	    attrset(A_BOLD | COLOR_PAIR(cmd));
177 	    if (!karaoke || *data == '\\' || *data == '/' ||
178 		(*data >= '@' && *data <= 'Z') || *data == '(' ||
179 		karaoke + length > COLS || (cmd != 1 && karaoke)) {
180 		karaoke = 0;
181 		if ((++ytxt) > LINES - 1)
182 		    ytxt = 19;
183 		move(ytxt, 0);
184 		clrtoeol();
185 		if (*data == '\\' || *data == '/')
186 		    (data++, length--);	/* karaoke newlines */
187 		if (*data == '@')	/* karaoke info */
188 		    (data += 2, length -= 2);
189 	    }
190 	    strncpy(textbuf, data, length < COLS - karaoke ? length :
191 		    COLS - karaoke);
192 	    if (length < 1024)
193 		textbuf[length] = 0;
194 	    mvaddstr(ytxt, karaoke, textbuf);
195 	    if (cmd == 1) {
196 		karaoke += strlen(textbuf);
197 		if (karaoke > COLS - 10)
198 		    karaoke = 0;
199 	    } else
200 		karaoke = 0;
201 	}
202     } else if (cmd == key_signature) {
203 	if (graphics || verbose)
204 	    nn = ((NOTE & 0x80) ? flats : sharps);
205 	if (verbose) {
206 	    if (VEL)	/* major key */
207 		printf("Key: %s major\n", (!(NOTE & 0x80) ?
208 			majsharp[NOTE] : majflat[256-NOTE]));
209 	    else	/* minor key */
210 		printf("Key: %s minor\n", (!(NOTE & 0x80) ?
211 			minsharp[NOTE] : minflat[256-NOTE]));
212 	}
213     } else
214 	switch (cmd & 0xf0) {
215 	case MIDI_KEY_PRESSURE:
216 	    if (verbose > 4)
217 		printf("Chn %d Key Pressure %s%c=%d\n",
218 		       1 + (cmd & 0xf), NNAME, OCHAR, VEL);
219 	    break;
220 	case MIDI_NOTEON:
221 	    if (graphics)
222 		if (VEL) {
223 		    attrset(A_BOLD | COLOR_PAIR((CHN % 6 + 1)));
224 		    if (!ISPERC(CHN) || NOTE > 127 ||
225 			gmvoice[NOTE + 128] == NULL)
226 			mvprintw(YPOS, XPOS, "%s%c", NNAME, OCHAR);
227 		    else
228 			mvprintw(YPOS, XPOS, "%c%c%c",
229 				 gmvoice[NOTE + 128][0],
230 				 gmvoice[NOTE + 128][1],
231 				 gmvoice[NOTE + 128][2]);
232 		} else
233 		    mvaddstr(YPOS, XPOS, "   ");
234 	    else if (verbose > 5)
235 		printf("Chn %d Note On %s%c=%d\n",
236 		       1 + (cmd & 0xf), NNAME, OCHAR, VEL);
237 	    break;
238 	case MIDI_NOTEOFF:
239 	    if (graphics)
240 		mvaddstr(YPOS, XPOS, "   ");
241 	    else if (verbose > 5)
242 		printf("Chn %d Note Off %s%c=%d\n",
243 		       1 + (cmd & 0xf), NNAME, OCHAR, VEL);
244 	    break;
245 	case MIDI_CTL_CHANGE:
246 	    if (verbose > 5)
247 		printf("Chn %d Ctl Change %d=%d\n",
248 		       1 + (cmd & 0xf), NOTE, VEL);
249 	    break;
250 	case MIDI_CHN_PRESSURE:
251 	    if (verbose > 5)
252 		printf("Chn %d Pressure=%d\n",
253 		       1 + (cmd & 0xf), NOTE);
254 	    break;
255 	case MIDI_PITCH_BEND:
256 	    {
257 		register val = (VEL << 7) | NOTE;
258 
259 		if (graphics) {
260 		    attrset(A_BOLD);
261 		    if (val > 0x2000)
262 			mvaddch(YPOS, 11, '>');
263 		    else if (val < 0x2000)
264 			mvaddch(YPOS, 11, '<');
265 		    else
266 			mvaddch(YPOS, 11, ' ');
267 		} else if (verbose > 4)
268 		    printf("Chn %d Bender=0x%04x\n",
269 			   1 + CHN, val);
270 	    }
271 	    break;
272 	case MIDI_PGM_CHANGE:
273 	    if (graphics) {
274 		attrset(COLOR_PAIR((CHN % 6 + 1)) | A_BOLD);
275 		if (!ISPERC(CHN))
276 		    mvaddnstr(YPOS, 0, gsvoice[NOTE], 12);
277 		else
278 		    mvaddstr(YPOS, 0, drumset[SET(NOTE)]);
279 	    } else if (verbose > 3)
280 		printf("Chn %d Program=%s %d\n",
281 		       1 + CHN, (ISPERC(CHN) ? drumset[SET(NOTE)]
282 				 : gsvoice[NOTE]), NOTE + 1);
283 	    break;
284 	case 0xf0:
285 	case 0xf7:
286 	    if (verbose > 2) {
287 		printf("Sysex(%2x): ", cmd);
288 		for (i = 0; i < length; i++)
289 		    printf("%02x", data[i]);
290 		putchar('\n');
291 	    }
292 	    break;
293 	default:
294 	    break;
295 	}
296 }
297 
init_show()298 void init_show()
299 {
300     char *tmp;
301 
302     nn = flats;
303     ytxt = 18;
304     karaoke = 0;
305     if (graphics) {
306 	clear();
307 	attrset(A_NORMAL | A_ALTCHARSET);
308 	mvprintw(0, 0, RELEASE " by Nathan Laredo");
309 	mvprintw(0, 40, "Now Playing:");
310 	mvprintw(1, 40, "[P]ause [N]ext [L]ast [O]ptions");
311 	mvaddstr(ytxt, 0, "�����������;");
312 	mvprintw(1, 0, "00:00.0 - 00:00.0, %d track%c", ntrks,
313 		 ntrks > 1 ? 's' : ' ');
314 	for (i = 0; i < 16; i++)
315 	    mvprintw(i + 2, 0, "Channel %2d  �", i + 1);
316 	tmp = strrchr(filename, '/');
317 	strncpy(textbuf, (tmp == NULL ? filename : tmp + 1), COLS - 53);
318 	attrset(A_BOLD);
319 	mvaddstr(0, 53, textbuf);
320 	mvaddch(1, 41, 'P');
321 	mvaddch(1, 49, 'N');
322 	mvaddch(1, 56, 'L');
323 	mvaddch(1, 63, 'O');
324 	mvaddstr(0, 0, RELEASE);
325 	refresh();
326     } else if (verbose) {
327 	printf("** Now Playing \"%s\"\n", filename);
328 	printf("** Format: %d, Tracks: %d, Division: %d\n", format,
329 	       ntrks, division);
330     }
331 }
332 
setup_show(argc,argv)333 void setup_show(argc, argv)
334 int argc;
335 char **argv;
336 {
337     if (graphics) {
338 	initscr();
339 	start_color();
340 	verbose = 0;
341 	init_pair(1, COLOR_RED, COLOR_BLACK);
342 	init_pair(2, COLOR_GREEN, COLOR_BLACK);
343 	init_pair(3, COLOR_YELLOW, COLOR_BLACK);
344 	init_pair(4, COLOR_BLUE, COLOR_BLACK);
345 	init_pair(5, COLOR_MAGENTA, COLOR_BLACK);
346 	init_pair(6, COLOR_CYAN, COLOR_BLACK);
347 	init_pair(7, COLOR_WHITE, COLOR_BLACK);
348 	raw();
349 	noecho();
350 	nodelay(stdscr, TRUE);
351 	keypad(stdscr, TRUE);
352 	attrset(A_NORMAL);
353     }
354 }
355