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