1 /*
2
3 TiMidity -- Experimental MIDI to WAVE converter
4 Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20 ncurs_c.c
21
22 */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <stdarg.h>
28
29 #include <ncurses.h>
30
31 #include "config.h"
32 #include "common.h"
33 #include "instrum.h"
34 #include "playmidi.h"
35 #include "output.h"
36 #include "controls.h"
37
38 static void ctl_refresh(void);
39 static void ctl_help_mode(void);
40 static void ctl_total_time(int tt);
41 static void ctl_master_volume(int mv);
42 static void ctl_file_name(char *name);
43 static void ctl_current_time(int ct);
44 static void ctl_note(int v);
45 static void ctl_program(int ch, int val);
46 static void ctl_volume(int channel, int val);
47 static void ctl_expression(int channel, int val);
48 static void ctl_panning(int channel, int val);
49 static void ctl_sustain(int channel, int val);
50 static void ctl_pitch_bend(int channel, int val);
51 static void ctl_reset(void);
52 static int ctl_open(int using_stdin, int using_stdout);
53 static void ctl_close(void);
54 static int ctl_read(int32 *valp);
55 static int cmsg(int type, int verbosity_level, char *fmt, ...);
56
57 /**********************************************/
58 /* export the interface functions */
59
60 #define ctl ncurses_control_mode
61
62 ControlMode ctl=
63 {
64 "ncurses interface", 'n',
65 1,0,0,
66 ctl_open,dumb_pass_playing_list, ctl_close, ctl_read, cmsg,
67 ctl_refresh, ctl_reset, ctl_file_name, ctl_total_time, ctl_current_time,
68 ctl_note,
69 ctl_master_volume, ctl_program, ctl_volume,
70 ctl_expression, ctl_panning, ctl_sustain, ctl_pitch_bend
71 };
72
73
74 /***********************************************************************/
75 /* foreground/background checks disabled since switching to curses */
76 /* static int in_foreground=1; */
77 static int ctl_helpmode=0;
78
79 static WINDOW *dftwin=0, *msgwin=0;
80
_ctl_refresh(void)81 static void _ctl_refresh(void)
82 {
83 wmove(dftwin, 0,0);
84 wrefresh(dftwin);
85 }
86
ctl_refresh(void)87 static void ctl_refresh(void)
88 {
89 if (ctl.trace_playing)
90 _ctl_refresh();
91 }
92
ctl_help_mode(void)93 static void ctl_help_mode(void)
94 {
95 static WINDOW *helpwin;
96 if (ctl_helpmode)
97 {
98 ctl_helpmode=0;
99 delwin(helpwin);
100 touchwin(dftwin);
101 _ctl_refresh();
102 }
103 else
104 {
105 ctl_helpmode=1;
106 /* And here I thought the point of curses was that you could put
107 stuff on windows without having to worry about whether this
108 one is overlapping that or the other way round... */
109 helpwin=newwin(2,COLS,0,0);
110 wattron(helpwin, A_REVERSE);
111 werase(helpwin);
112 waddstr(helpwin,
113 "V/Up=Louder b/Left=Skip back "
114 "n/Next=Next file r/Home=Restart file");
115 wmove(helpwin, 1,0);
116 waddstr(helpwin,
117 "v/Down=Softer f/Right=Skip forward "
118 "p/Prev=Previous file q/End=Quit program");
119 wrefresh(helpwin);
120 }
121 }
122
ctl_total_time(int tt)123 static void ctl_total_time(int tt)
124 {
125 int mins, secs=tt/play_mode->rate;
126 mins=secs/60;
127 secs-=mins*60;
128
129 wmove(dftwin, 4,6+6+3);
130 wattron(dftwin, A_BOLD);
131 wprintw(dftwin, "%3d:%02d", mins, secs);
132 wattroff(dftwin, A_BOLD);
133 _ctl_refresh();
134 }
135
ctl_master_volume(int mv)136 static void ctl_master_volume(int mv)
137 {
138 wmove(dftwin, 4,COLS-5);
139 wattron(dftwin, A_BOLD);
140 wprintw(dftwin, "%03d %%", mv);
141 wattroff(dftwin, A_BOLD);
142 _ctl_refresh();
143 }
144
ctl_file_name(char * name)145 static void ctl_file_name(char *name)
146 {
147 wmove(dftwin, 3,6);
148 wclrtoeol(dftwin );
149 wattron(dftwin, A_BOLD);
150 waddstr(dftwin, name);
151 wattroff(dftwin, A_BOLD);
152 _ctl_refresh();
153 }
154
ctl_current_time(int ct)155 static void ctl_current_time(int ct)
156 {
157 int i,v;
158 int mins, secs=ct/play_mode->rate;
159 if (!ctl.trace_playing)
160 return;
161
162 mins=secs/60;
163 secs-=mins*60;
164 wmove(dftwin, 4,6);
165 wattron(dftwin, A_BOLD);
166 wprintw(dftwin, "%3d:%02d", mins, secs);
167 v=0;
168 i=voices;
169 while (i--)
170 if (voice[i].status!=VOICE_FREE) v++;
171 wmove(dftwin, 4,48);
172 wprintw(dftwin, "%2d", v);
173 wattroff(dftwin, A_BOLD);
174 _ctl_refresh();
175 }
176
ctl_note(int v)177 static void ctl_note(int v)
178 {
179 int xl;
180 if (!ctl.trace_playing)
181 return;
182 xl=voice[v].note%(COLS-24);
183 wmove(dftwin, 8+voice[v].channel,xl+3);
184 switch(voice[v].status)
185 {
186 case VOICE_DIE:
187 waddch(dftwin, ',');
188 break;
189 case VOICE_FREE:
190 waddch(dftwin, '.');
191 break;
192 case VOICE_ON:
193 wattron(dftwin, A_BOLD);
194 waddch(dftwin, '0'+(10*voice[v].velocity)/128);
195 wattroff(dftwin, A_BOLD);
196 break;
197 case VOICE_OFF:
198 case VOICE_SUSTAINED:
199 waddch(dftwin, '0'+(10*voice[v].velocity)/128);
200 break;
201 }
202 }
203
ctl_program(int ch,int val)204 static void ctl_program(int ch, int val)
205 {
206 if (!ctl.trace_playing)
207 return;
208 wmove(dftwin, 8+ch, COLS-20);
209 if (ISDRUMCHANNEL(ch))
210 {
211 wattron(dftwin, A_BOLD);
212 wprintw(dftwin, "%03d", val);
213 wattroff(dftwin, A_BOLD);
214 }
215 else
216 wprintw(dftwin, "%03d", val);
217 }
218
ctl_volume(int channel,int val)219 static void ctl_volume(int channel, int val)
220 {
221 if (!ctl.trace_playing)
222 return;
223 wmove(dftwin, 8+channel, COLS-16);
224 wprintw(dftwin, "%3d", (val*100)/127);
225 }
226
ctl_expression(int channel,int val)227 static void ctl_expression(int channel, int val)
228 {
229 if (!ctl.trace_playing)
230 return;
231 wmove(dftwin, 8+channel, COLS-12);
232 wprintw(dftwin, "%3d", (val*100)/127);
233 }
234
ctl_panning(int channel,int val)235 static void ctl_panning(int channel, int val)
236 {
237 if (!ctl.trace_playing)
238 return;
239 wmove(dftwin, 8+channel, COLS-8);
240 if (val==NO_PANNING)
241 waddstr(dftwin, " ");
242 else if (val<5)
243 waddstr(dftwin, " L ");
244 else if (val>123)
245 waddstr(dftwin, " R ");
246 else if (val>60 && val<68)
247 waddstr(dftwin, " C ");
248 else
249 {
250 /* wprintw(dftwin, "%+02d", (100*(val-64))/64); */
251 val = (100*(val-64))/64; /* piss on curses */
252 if (val<0)
253 {
254 waddch(dftwin, '-');
255 val=-val;
256 }
257 else waddch(dftwin, '+');
258 wprintw(dftwin, "%02d", val);
259 }
260 }
261
ctl_sustain(int channel,int val)262 static void ctl_sustain(int channel, int val)
263 {
264 if (!ctl.trace_playing)
265 return;
266 wmove(dftwin, 8+channel, COLS-4);
267 if (val) waddch(dftwin, 'S');
268 else waddch(dftwin, ' ');
269 }
270
ctl_pitch_bend(int channel,int val)271 static void ctl_pitch_bend(int channel, int val)
272 {
273 if (!ctl.trace_playing)
274 return;
275 wmove(dftwin, 8+channel, COLS-2);
276 if (val>0x2000) waddch(dftwin, '+');
277 else if (val<0x2000) waddch(dftwin, '-');
278 else waddch(dftwin, ' ');
279 }
280
ctl_reset(void)281 static void ctl_reset(void)
282 {
283 int i,j;
284 if (!ctl.trace_playing)
285 return;
286 for (i=0; i<16; i++)
287 {
288 wmove(dftwin, 8+i, 3);
289 for (j=0; j<COLS-24; j++)
290 waddch(dftwin, '.');
291 ctl_program(i, channel[i].program);
292 ctl_volume(i, channel[i].volume);
293 ctl_expression(i, channel[i].expression);
294 ctl_panning(i, channel[i].panning);
295 ctl_sustain(i, channel[i].sustain);
296 ctl_pitch_bend(i, channel[i].pitchbend);
297 }
298 _ctl_refresh();
299 }
300
301 /***********************************************************************/
302
303 /* #define CURSED_REDIR_HACK */
304
305 #ifdef CURSED_REDIR_HACK
306 static SCREEN *oldscr;
307 #endif
308
ctl_open(int using_stdin,int using_stdout)309 static int ctl_open(int using_stdin, int using_stdout)
310 {
311 int i;
312 #ifdef CURSED_REDIR_HACK
313 FILE *infp=stdin, *outfp=stdout;
314 SCREEN *dftscr;
315
316 /* This doesn't work right */
317 if (using_stdin && using_stdout)
318 {
319 infp=outfp=stderr;
320 fflush(stderr);
321 setvbuf(stderr, 0, _IOFBF, BUFSIZ);
322 }
323 else if (using_stdout)
324 {
325 outfp=stderr;
326 fflush(stderr);
327 setvbuf(stderr, 0, _IOFBF, BUFSIZ);
328 }
329 else if (using_stdin)
330 {
331 infp=stdout;
332 fflush(stdout);
333 setvbuf(stdout, 0, _IOFBF, BUFSIZ);
334 }
335
336 dftscr=newterm(0, outfp, infp);
337 if (!dftscr)
338 return -1;
339 oldscr=set_term(dftscr);
340 /* dftwin=stdscr; */
341 #else
342 initscr();
343 #endif
344
345 cbreak();
346 noecho();
347 nonl();
348 nodelay(stdscr, 1);
349 scrollok(stdscr, 0);
350 idlok(stdscr, 1);
351 keypad(stdscr, TRUE);
352 ctl.opened=1;
353
354 if (ctl.trace_playing)
355 dftwin=stdscr;
356 else
357 dftwin=newwin(6,COLS,0,0);
358
359 werase(dftwin);
360 wmove(dftwin, 0,0);
361 waddstr(dftwin, "TiMidity v" TIMID_VERSION);
362 wmove(dftwin, 0,COLS-52);
363 waddstr(dftwin, "(C) 1995 Tuukka Toivonen <toivonen@clinet.fi>");
364 wmove(dftwin, 1,0);
365 waddstr(dftwin, "Press 'h' for help with keys, or 'q' to quit.");
366 wmove(dftwin, 3,0);
367 waddstr(dftwin, "File:");
368 wmove(dftwin, 4,0);
369 if (ctl.trace_playing)
370 {
371 waddstr(dftwin, "Time:");
372 wmove(dftwin, 4,6+6+1);
373 waddch(dftwin, '/');
374 wmove(dftwin, 4,40);
375 wprintw(dftwin, "Voices: / %d", voices);
376 }
377 else
378 {
379 waddstr(dftwin, "Playing time:");
380 }
381 wmove(dftwin, 4,COLS-20);
382 waddstr(dftwin, "Master volume:");
383 wmove(dftwin, 5,0);
384 for (i=0; i<COLS; i++)
385 waddch(dftwin, '_');
386 if (ctl.trace_playing)
387 {
388 wmove(dftwin, 6,0);
389 waddstr(dftwin, "Ch");
390 wmove(dftwin, 6,COLS-20);
391 waddstr(dftwin, "Prg Vol Exp Pan S B");
392 wmove(dftwin, 7,0);
393 for (i=0; i<COLS; i++)
394 waddch(dftwin, '-');
395 for (i=0; i<16; i++)
396 {
397 wmove(dftwin, 8+i, 0);
398 wprintw(dftwin, "%02d", i+1);
399 }
400 }
401 else
402 {
403 msgwin=newwin(LINES-6,COLS,6,0);
404 werase(msgwin);
405 scrollok(msgwin, 1);
406 wrefresh(msgwin);
407 }
408 _ctl_refresh();
409
410 return 0;
411 }
412
ctl_close(void)413 static void ctl_close(void)
414 {
415 if (ctl.opened)
416 {
417 endwin();
418 ctl.opened=0;
419 }
420 }
421
ctl_read(int32 * valp)422 static int ctl_read(int32 *valp)
423 {
424 int c;
425 while ((c=getch())!=ERR)
426 {
427 switch(c)
428 {
429 case 'h':
430 case '?':
431 case KEY_F(1):
432 ctl_help_mode();
433 return RC_NONE;
434
435 case 'V':
436 case KEY_UP:
437 *valp=10;
438 return RC_CHANGE_VOLUME;
439 case 'v':
440 case KEY_DOWN:
441 *valp=-10;
442 return RC_CHANGE_VOLUME;
443 case 'q':
444 case KEY_END:
445 return RC_QUIT;
446 case 'n':
447 case KEY_NPAGE:
448 return RC_NEXT;
449 case 'p':
450 case KEY_PPAGE:
451 return RC_REALLY_PREVIOUS;
452 case 'r':
453 case KEY_HOME:
454 return RC_RESTART;
455
456 case 'f':
457 case KEY_RIGHT:
458 *valp=play_mode->rate;
459 return RC_FORWARD;
460 case 'b':
461 case KEY_LEFT:
462 *valp=play_mode->rate;
463 return RC_BACK;
464 /* case ' ':
465 return RC_TOGGLE_PAUSE; */
466 }
467 }
468 return RC_NONE;
469 }
470
cmsg(int type,int verbosity_level,char * fmt,...)471 static int cmsg(int type, int verbosity_level, char *fmt, ...)
472 {
473 va_list ap;
474 if ((type==CMSG_TEXT || type==CMSG_INFO || type==CMSG_WARNING) &&
475 ctl.verbosity<verbosity_level)
476 return 0;
477 va_start(ap, fmt);
478 if (!ctl.opened)
479 {
480 vfprintf(stderr, fmt, ap);
481 fprintf(stderr, "\n");
482 }
483 else if (ctl.trace_playing)
484 {
485 switch(type)
486 {
487 /* Pretty pointless to only have one line for messages, but... */
488 case CMSG_WARNING:
489 case CMSG_ERROR:
490 case CMSG_FATAL:
491 wmove(dftwin, 2,0);
492 wclrtoeol(dftwin);
493 wattron(dftwin, A_REVERSE);
494 vwprintw(dftwin, fmt, ap);
495 wattroff(dftwin, A_REVERSE);
496 _ctl_refresh();
497 if (type==CMSG_WARNING)
498 sleep(1); /* Don't you just _HATE_ it when programs do this... */
499 else
500 sleep(2);
501 wmove(dftwin, 2,0);
502 wclrtoeol(dftwin);
503 _ctl_refresh();
504 break;
505 }
506 }
507 else
508 {
509 switch(type)
510 {
511 default:
512 vwprintw(msgwin, fmt, ap);
513 wprintw(msgwin, "\n");
514 wrefresh(msgwin);
515 break;
516
517 case CMSG_WARNING:
518 wattron(msgwin, A_BOLD);
519 vwprintw(msgwin, fmt, ap);
520 wprintw(msgwin, "\n");
521 wattroff(msgwin, A_BOLD);
522 wrefresh(msgwin);
523 break;
524
525 case CMSG_ERROR:
526 case CMSG_FATAL:
527 wattron(msgwin, A_REVERSE);
528 vwprintw(msgwin, fmt, ap);
529 wprintw(msgwin, "\n");
530 wattroff(msgwin, A_REVERSE);
531 wrefresh(msgwin);
532 if (type==CMSG_FATAL)
533 sleep(2);
534 break;
535 }
536 }
537
538 va_end(ap);
539 return 0;
540 }
541