1 /*
2     TiMidity++ -- MIDI to WAVE converter and player
3     Copyright (C) 1999-2004 Masanao Izumo <iz@onicos.co.jp>
4     Copyright (C) 1995 Tuukka Toivonen <tt@cgs.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 
20     vt_100_c.c - written by Masanao Izumo <iz@onicos.co.jp>
21 */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif /* HAVE_CONFIG_H */
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <ctype.h>
30 #include <sys/types.h>
31 #ifndef NO_STRING_H
32 #include <string.h>
33 #else
34 #include <strings.h>
35 #endif
36 
37 #ifdef HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif /* HAVE_UNISTD_H */
40 
41 #ifdef HAVE_SYS_TIME_H
42 #include <sys/time.h>
43 #endif /* HAVE_SYS_TIME_H */
44 
45 #ifdef __W32__
46 #include <windows.h>
47 #endif
48 
49 #include "timidity.h"
50 #include "common.h"
51 #include "instrum.h"
52 #include "playmidi.h"
53 #include "readmidi.h"
54 #include "output.h"
55 #include "controls.h"
56 #include "miditrace.h"
57 #include "vt100.h"
58 #include "timer.h"
59 #include "bitset.h"
60 #include "aq.h"
61 
62 #define SCRMODE_OUT_THRESHOLD 10.0
63 #define CHECK_NOTE_SLEEP_TIME 5.0
64 #define INDICATOR_UPDATE_TIME 0.2
65 
66 static struct
67 {
68     int prog;
69     int disp_cnt;
70     double last_note_on;
71     char *comm;
72 } instr_comment[MAX_CHANNELS];
73 
74 enum indicator_mode_t
75 {
76     INDICATOR_DEFAULT,
77     INDICATOR_LYRIC
78 };
79 
80 static int indicator_width = 78;
81 static char *comment_indicator_buffer = NULL;
82 static char *current_indicator_message = NULL;
83 static char *indicator_msgptr = NULL;
84 static int current_indicator_chan = 0;
85 static int next_indicator_chan = -1;
86 static double indicator_last_update;
87 static int indicator_mode = INDICATOR_DEFAULT;
88 static Bitset channel_program_flags[MAX_CHANNELS];
89 
90 static void update_indicator(void);
91 static void reset_indicator(void);
92 static void indicator_chan_update(int ch);
93 static void indicator_set_prog(int ch, int val, char *comm);
94 static void display_lyric(char *lyric, int sep);
95 static void display_title(char *title);
96 static void init_lyric(char *lang);
97 static char *vt100_getline(void);
98 
99 #define LYRIC_WORD_NOSEP	0
100 #define LYRIC_WORD_SEP		' '
101 
102 static void ctl_refresh(void);
103 static void ctl_total_time(int tt);
104 static void ctl_master_volume(int mv);
105 static void ctl_file_name(char *name);
106 static void ctl_current_time(int ct, int nv);
107 static const char note_name_char[12] =
108 {
109     'c', 'C', 'd', 'D', 'e', 'f', 'F', 'g', 'G', 'a', 'A', 'b'
110 };
111 
112 static void ctl_note(int status, int ch, int note, int vel);
113 static void ctl_program(int ch, int val, void *vp);
114 static void ctl_volume(int channel, int val);
115 static void ctl_expression(int channel, int val);
116 static void ctl_panning(int channel, int val);
117 static void ctl_sustain(int channel, int val);
118 static void ctl_pitch_bend(int channel, int val);
119 static void ctl_lyric(uint16 lyricid);
120 
121 static void ctl_reset(void);
122 static int ctl_open(int using_stdin, int using_stdout);
123 static void ctl_close(void);
124 static int ctl_read(int32 *valp);
125 static int ctl_write(char *valp, int32 size);
126 static int cmsg(int type, int verbosity_level, char *fmt, ...);
127 static void ctl_event(CtlEvent *e);
128 
129 /**********************************************/
130 /* export the interface functions */
131 
132 #define ctl vt100_control_mode
133 
134 ControlMode ctl=
135 {
136     "vt100 interface", 'T',
137     "vt100",
138     1,0,0,
139     0,
140     ctl_open,
141     ctl_close,
142     dumb_pass_playing_list,
143     ctl_read,
144     ctl_write,
145     cmsg,
146     ctl_event
147 };
148 
149 static uint32 cuepoint = 0;
150 static int cuepoint_pending = 0;
151 
152 static int selected_channel = -1;
153 static int lyric_row = 6;
154 static int title_row = 6;
155 static int msg_row = 6;
156 
ctl_refresh(void)157 static void ctl_refresh(void)
158 {
159     if(ctl.opened)
160 	vt100_refresh();
161 }
162 
ctl_total_time(int tt)163 static void ctl_total_time(int tt)
164 {
165     int mins, secs=tt/play_mode->rate;
166     mins=secs/60;
167     secs-=mins*60;
168 
169     vt100_move(4, 6+6+3);
170     vt100_set_attr(VT100_ATTR_BOLD);
171     printf("%3d:%02d  ", mins, secs);
172     vt100_reset_attr();
173     ctl_current_time(0, 0);
174 }
175 
ctl_master_volume(int mv)176 static void ctl_master_volume(int mv)
177 {
178     vt100_move(4, VT100_COLS-5);
179     vt100_set_attr(VT100_ATTR_BOLD);
180     printf("%03d %%", mv);
181     vt100_reset_attr();
182     ctl_refresh();
183 }
184 
ctl_file_name(char * name)185 static void ctl_file_name(char *name)
186 {
187     int i;
188 
189     vt100_move(3, 6);
190     vt100_clrtoeol();
191     vt100_set_attr(VT100_ATTR_BOLD);
192     fputs(name, stdout);
193     vt100_reset_attr();
194 
195     if(ctl.trace_playing)
196     {
197 	memset(instr_comment, 0, sizeof(instr_comment));
198 	for(i = 0; i < MAX_CHANNELS; i++)
199 	    instr_comment[i].disp_cnt = 1;
200 	indicator_msgptr = NULL;
201 	for(i = 0; i < indicator_width; i++)
202 	    comment_indicator_buffer[i] = ' ';
203     }
204     ctl_refresh();
205 }
206 
ctl_current_time(int secs,int v)207 static void ctl_current_time(int secs, int v)
208 {
209     int mins, bold_flag = 0;
210     static int last_voices = -1, last_secs = -1;
211 
212     if(last_secs != secs)
213     {
214 	last_secs=secs;
215 	mins=secs/60;
216 	secs-=mins*60;
217 	vt100_move(4, 6);
218 	vt100_set_attr(VT100_ATTR_BOLD);
219 	printf("%3d:%02d", mins, secs);
220 	bold_flag = 1;
221     }
222 
223     if(!ctl.trace_playing || midi_trace.flush_flag)
224     {
225 	if(bold_flag)
226 	    vt100_reset_attr();
227 	return;
228     }
229 
230     vt100_move(4, 47);
231     if(!bold_flag)
232 	vt100_set_attr(VT100_ATTR_BOLD);
233     printf("%3d", v);
234     vt100_reset_attr();
235 
236     if(last_voices != voices)
237     {
238 	last_voices = voices;
239 	vt100_move(4, 52);
240 	printf("%3d", voices);
241     }
242 }
243 
ctl_note(int status,int ch,int note,int vel)244 static void ctl_note(int status, int ch, int note, int vel)
245 {
246     int xl, n, c;
247     unsigned int onoff, check, prev_check;
248     Bitset *bitset;
249 
250     if(ch >= 16)
251 	return;
252 
253     if (!ctl.trace_playing || midi_trace.flush_flag)
254 	return;
255 
256     n = note_name_char[note % 12];
257     c = (VT100_COLS - 24) / 12 * 12;
258     if(c <= 0)
259 	c = 1;
260     xl=note % c;
261     vt100_move(8 + ch, xl + 3);
262     switch(status)
263     {
264       case VOICE_DIE:
265 	putc(',', stdout);
266 	onoff = 0;
267 	break;
268       case VOICE_FREE:
269 	putc('.', stdout);
270 	onoff = 0;
271 	break;
272       case VOICE_ON:
273 	vt100_set_attr(VT100_ATTR_REVERSE);
274 	putc(n, stdout);
275 	vt100_reset_attr();
276 	indicator_chan_update(ch);
277 	onoff = 1;
278 	break;
279       case VOICE_SUSTAINED:
280 	vt100_set_attr(VT100_ATTR_BOLD);
281 	putc(n, stdout);
282 	vt100_reset_attr();
283 	onoff = 0;
284 	break;
285       case VOICE_OFF:
286 	putc(n, stdout);
287 	onoff = 0;
288 	break;
289     }
290 
291     bitset = channel_program_flags + ch;
292     prev_check = has_bitset(bitset);
293     if(prev_check == onoff)
294     {
295 	/* Not change program mark */
296 	onoff <<= (8 * sizeof(onoff) - 1);
297 	set_bitset(bitset, &onoff, note, 1);
298 	return;
299     }
300     onoff <<= (8 * sizeof(onoff) - 1);
301     set_bitset(bitset, &onoff, note, 1);
302     check = has_bitset(bitset);
303 
304     if(prev_check ^ check)
305     {
306 	vt100_move(8 + ch, VT100_COLS - 21);
307 	if(check)
308 	{
309 	    vt100_set_attr(VT100_ATTR_BOLD);
310 	    putc('*', stdout);
311 	    vt100_reset_attr();
312 	}
313 	else
314 	{
315 	    putc(' ', stdout);
316 	}
317     }
318 }
319 
ctl_program(int ch,int val,void * comm)320 static void ctl_program(int ch, int val, void *comm)
321 {
322     int pr;
323     if(ch >= 16)
324 	return;
325     if (!ctl.trace_playing || midi_trace.flush_flag)
326 	return;
327     if(channel[ch].special_sample)
328 	pr = val = channel[ch].special_sample;
329     else
330 	pr = val + progbase;
331     vt100_move(8+ch, VT100_COLS-21);
332     if (ISDRUMCHANNEL(ch))
333     {
334 	vt100_set_attr(VT100_ATTR_BOLD);
335 	printf(" %03d", pr);
336 	vt100_reset_attr();
337     }
338     else
339 	printf(" %03d", pr);
340 
341   if(comm != NULL)
342       indicator_set_prog(ch, val, (char *)comm);
343 }
344 
ctl_volume(int ch,int val)345 static void ctl_volume(int ch, int val)
346 {
347     if(ch >= 16)
348 	return;
349     if (!ctl.trace_playing || midi_trace.flush_flag)
350 	return;
351     vt100_move(8 + ch, VT100_COLS - 16);
352     printf("%3d", (val * 100) / 127);
353 }
354 
ctl_expression(int ch,int val)355 static void ctl_expression(int ch, int val)
356 {
357     if(ch >= 16)
358 	return;
359     if (!ctl.trace_playing || midi_trace.flush_flag)
360 	return;
361     vt100_move(8 + ch, VT100_COLS - 12);
362     printf("%3d", (val * 100) / 127);
363 }
364 
ctl_panning(int ch,int val)365 static void ctl_panning(int ch, int val)
366 {
367     if(ch >= 16)
368 	return;
369     if (!ctl.trace_playing || midi_trace.flush_flag)
370 	return;
371     vt100_move(8 + ch, VT100_COLS - 8);
372     if (val==NO_PANNING)
373 	fputs("   ", stdout);
374     else if (val<5)
375 	fputs(" L ", stdout);
376     else if (val>123)
377 	fputs(" R ", stdout);
378     else if (val>60 && val<68)
379 	fputs(" C ", stdout);
380     else
381     {
382 	val = (100*(val-64))/64; /* piss on curses */
383 	if (val<0)
384 	{
385 	    putc('-', stdout);
386 	    val=-val;
387 	}
388 	else
389 	    putc('+', stdout);
390 	printf("%02d", val);
391     }
392 }
393 
ctl_sustain(int ch,int val)394 static void ctl_sustain(int ch, int val)
395 {
396     if(ch >= 16)
397 	return;
398     if (!ctl.trace_playing || midi_trace.flush_flag)
399 	return;
400     vt100_move(8 + ch, VT100_COLS - 4);
401     if (val) putc('S', stdout);
402     else putc(' ', stdout);
403 }
404 
ctl_pitch_bend(int ch,int val)405 static void ctl_pitch_bend(int ch, int val)
406 {
407     if(ch >= 16)
408 	return;
409     if (!ctl.trace_playing || midi_trace.flush_flag)
410 	return;
411     vt100_move(8+ch, VT100_COLS-2);
412     if (val==-1) putc('=', stdout);
413     else if (val>0x2000) putc('+', stdout);
414     else if (val<0x2000) putc('-', stdout);
415     else putc(' ', stdout);
416 }
417 
418 /*ARGSUSED*/
ctl_lyric(uint16 lyricid)419 static void ctl_lyric(uint16 lyricid)
420 {
421     char *lyric;
422 
423     lyric = event2string(lyricid);
424     if(lyric != NULL)
425     {
426         /* EAW -- if not a true KAR lyric, ignore \r, treat \n as \r */
427         if (*lyric != ME_KARAOKE_LYRIC) {
428             while (strchr(lyric, '\r')) {
429             	*(strchr(lyric, '\r')) = ' ';
430             }
431             while (strchr(lyric, '\n')) {
432                 *(strchr(lyric, '\n')) = '\r';
433             }
434         }
435 
436 	if(*lyric == ME_KARAOKE_LYRIC)
437 	{
438 	    if(lyric[1] == '/')
439 	    {
440 		display_lyric("\n", LYRIC_WORD_NOSEP);
441 		display_lyric(lyric + 2, LYRIC_WORD_NOSEP);
442 	    }
443 	    else if(lyric[1] == '\\')
444 	    {
445 		display_lyric("\r", LYRIC_WORD_NOSEP);
446 		display_lyric(lyric + 2, LYRIC_WORD_NOSEP);
447 	    }
448 	    else if(lyric[1] == '@' && lyric[2] == 'T')
449 	    {
450 		if(ctl.trace_playing)
451 		{
452 		    display_lyric("\n", LYRIC_WORD_NOSEP);
453 		    display_lyric(lyric + 3, LYRIC_WORD_SEP);
454 		}
455 		else
456 		    display_title(lyric + 3);
457 	    }
458 	    else if(lyric[1] == '@' && lyric[2] == 'L')
459 	    {
460 		init_lyric(lyric + 3);
461 	    }
462 	    else
463 		display_lyric(lyric + 1, LYRIC_WORD_NOSEP);
464 	}
465 	else
466 	{
467 	    if(*lyric == ME_CHORUS_TEXT || *lyric == ME_INSERT_TEXT)
468 		display_lyric("\r", LYRIC_WORD_SEP);
469 	    display_lyric(lyric + 1, LYRIC_WORD_SEP);
470 	}
471     }
472 }
473 
ctl_reset(void)474 static void ctl_reset(void)
475 {
476     int i,j,c;
477     char *title;
478 
479     if (!ctl.trace_playing)
480 	return;
481     c = (VT100_COLS - 24) / 12 * 12;
482     if(c <= 0)
483 	c = 1;
484     for (i=0; i<16; i++)
485     {
486 	vt100_move(8+i, 3);
487 	for (j=0; j<c; j++)
488 	    putc('.', stdout);
489 	if(ISDRUMCHANNEL(i))
490 	    ctl_program(i, channel[i].bank, channel_instrum_name(i));
491 	else
492 	    ctl_program(i, channel[i].program, channel_instrum_name(i));
493 	ctl_volume(i, channel[i].volume);
494 	ctl_expression(i, channel[i].expression);
495 	ctl_panning(i, channel[i].panning);
496 	ctl_sustain(i, channel[i].sustain);
497 	if(channel[i].pitchbend == 0x2000 && channel[i].mod.val > 0)
498 	    ctl_pitch_bend(i, -1);
499 	else
500 	    ctl_pitch_bend(i, channel[i].pitchbend);
501 	clear_bitset(channel_program_flags + i, 0, 128);
502     }
503 
504     reset_indicator();
505     display_lyric(NULL, LYRIC_WORD_NOSEP);
506     if((title = get_midi_title(NULL)) != NULL)
507 	display_lyric(title, LYRIC_WORD_NOSEP);
508 
509     ctl_refresh();
510 }
511 
512 /***********************************************************************/
513 
514 /*ARGSUSED*/
ctl_open(int using_stdin,int using_stdout)515 static int ctl_open(int using_stdin, int using_stdout)
516 {
517     int i;
518 
519     vt100_init_screen();
520     ctl.opened=1;
521 
522     vt100_move(0, 0);
523     fprintf(stdout, "TiMidity++ %s%s" NLS,
524     		(strcmp(timidity_version, "current")) ? "v" : "",
525     		timidity_version);
526     vt100_move(0, VT100_COLS-45);
527     fputs("(C) 1995 Tuukka Toivonen <tt@cgs.fi>", stdout);
528     vt100_move(1,0);
529     fputs("vt100 Interface mode - Written by Masanao Izumo <mo@goice.co.jp>", stdout);
530 
531     vt100_move(3,0);
532     fputs("File:", stdout);
533     vt100_move(4,0);
534     if (ctl.trace_playing)
535     {
536 	fputs("Time:", stdout);
537 	vt100_move(4,6+6+1);
538 	putc('/', stdout);
539 	vt100_move(4,40);
540 	printf("Voices:    /%3d", voices);
541     }
542     else
543     {
544 	fputs("Time:", stdout);
545 	vt100_move(4,6+6+1);
546 	putc('/', stdout);
547     }
548     vt100_move(4,VT100_COLS-20);
549     fputs("Master volume:", stdout);
550     vt100_move(5,0);
551     for (i=0; i<VT100_COLS; i++)
552 	putc('_', stdout);
553     if (ctl.trace_playing)
554     {
555 	int o;
556 
557 	vt100_move(6,0);
558 	fputs("Ch ", stdout);
559 	o = (VT100_COLS - 24) / 12;
560 	for(i = 0; i < o; i++)
561 	{
562 	    int j, c;
563 	    for(j = 0; j < 12; j++)
564 	    {
565 		c = note_name_char[j];
566 		if(islower(c))
567 		    putc(c, stdout);
568 		else
569 		    putc(' ', stdout);
570 	    }
571 	}
572 	vt100_move(6,VT100_COLS-20);
573 	fputs("Prg Vol Exp Pan S B", stdout);
574 	vt100_move(7,0);
575 	for (i=0; i<VT100_COLS; i++)
576 	    putc('-', stdout);
577 	for (i=0; i<16; i++)
578 	{
579 	    vt100_move(8+i, 0);
580 	    printf("%02d ", i+1);
581 	    init_bitset(channel_program_flags + i, 128);
582 	}
583 
584 	set_trace_loop_hook(update_indicator);
585 	indicator_width = VT100_COLS - 2;
586 	if(indicator_width < 40)
587 	    indicator_width = 40;
588 	lyric_row = 2;
589 	msg_row = 2;
590     }
591     memset(comment_indicator_buffer =
592 	(char *)safe_malloc(indicator_width), 0, indicator_width);
593     memset(current_indicator_message =
594 	(char *)safe_malloc(indicator_width), 0, indicator_width);
595     ctl_refresh();
596 
597     return 0;
598 }
599 
ctl_close(void)600 static void ctl_close(void)
601 {
602     if (ctl.opened)
603     {
604 	ctl.opened = 0;
605 	vt100_move(24, 0);
606 	vt100_refresh();
607     }
608 }
609 
char_count(const char * s,int c)610 static int char_count(const char *s, int c)
611 {
612     int n;
613 
614     n = 0;
615     while(*s == c)
616     {
617 	n++;
618 	s++;
619     }
620     if('0' <= *s && *s <= '9')
621 	n = (n - 1) + atoi(s);
622     return n;
623 }
624 
move_select_channel(int diff)625 static void move_select_channel(int diff)
626 {
627     if(selected_channel != -1)
628     {
629 	/* erase the mark */
630 	vt100_move(8 + selected_channel, 0);
631 	printf("%02d", selected_channel + 1);
632     }
633     selected_channel += diff;
634     while(selected_channel < 0)
635 	selected_channel += 17;
636     while(selected_channel >= 16)
637 	selected_channel -= 17;
638 
639     if(selected_channel != -1)
640     {
641 	vt100_move(8 + selected_channel, 0);
642 	vt100_set_attr(VT100_ATTR_BOLD);
643 	printf("%02d", selected_channel + 1);
644 	vt100_reset_attr();
645 	if(instr_comment[selected_channel].comm != NULL)
646 	{
647 	    if(indicator_mode != INDICATOR_DEFAULT)
648 		reset_indicator();
649 	    next_indicator_chan = selected_channel;
650 	}
651     }
652 }
653 
ctl_read(int32 * valp)654 static int ctl_read(int32 *valp)
655 {
656     char *cmd;
657 
658 	if (cuepoint_pending) {
659 		*valp = cuepoint;
660 		cuepoint_pending = 0;
661 		return RC_FORWARD;
662 	}
663     if((cmd = vt100_getline()) == NULL)
664 	return RC_NONE;
665     switch(cmd[0])
666 	{
667 	  case 'q':
668 	    trace_flush();
669 	    return RC_QUIT;
670 	  case 'V':
671 	    *valp = 10 * char_count(cmd, cmd[0]);
672 	    return RC_CHANGE_VOLUME;
673 	  case 'v':
674 	    *valp =- 10 * char_count(cmd, cmd[0]);
675 	    return RC_CHANGE_VOLUME;
676 #if 0
677 	  case '1':
678 	  case '2':
679 	  case '3':
680 	    *valp=cmd[0] - '2';
681 	    return RC_CHANGE_REV_EFFB;
682 	  case '4':
683 	  case '5':
684 	  case '6':
685 	    *valp = cmd[0] - '5';
686 	    return RC_CHANGE_REV_TIME;
687 #endif
688 	  case 's':
689 	    return RC_TOGGLE_PAUSE;
690 	  case 'n':
691 	    return RC_NEXT;
692 	  case 'p':
693 	    return RC_REALLY_PREVIOUS;
694 	  case 'r':
695 	    return RC_RESTART;
696 	  case 'f':
697 	    *valp=play_mode->rate * char_count(cmd, cmd[0]);
698 	    return RC_FORWARD;
699 	  case 'b':
700 	    *valp=play_mode->rate * char_count(cmd, cmd[0]);
701 	    return RC_BACK;
702 	  case '+':
703 	    *valp = char_count(cmd, cmd[0]);
704 	    return RC_KEYUP;
705 	  case '-':
706 	    *valp = -char_count(cmd, cmd[0]);
707 	    return RC_KEYDOWN;
708 	  case '>':
709 	    *valp = char_count(cmd, cmd[0]);
710 	    return RC_SPEEDUP;
711 	  case '<':
712 	    *valp = char_count(cmd, cmd[0]);
713 	    return RC_SPEEDDOWN;
714 	  case 'O':
715 	    *valp = char_count(cmd, cmd[0]);
716 	    return RC_VOICEINCR;
717 	  case 'o':
718 	    *valp = char_count(cmd, cmd[0]);
719 	    return RC_VOICEDECR;
720 	  case 'c':
721 	    *valp = char_count(cmd, cmd[0]);
722 	    move_select_channel(*valp);
723 	    break;
724 	  case 'C':
725 	    *valp = char_count(cmd, cmd[0]);
726 	    move_select_channel(-*valp);
727 	    break;
728 	  case 'd':
729 	    if(selected_channel != -1)
730 	    {
731 		*valp = selected_channel;
732 		return RC_TOGGLE_DRUMCHAN;
733 	    }
734 	    break;
735 	  case 'g':
736 	    return RC_TOGGLE_SNDSPEC;
737 	}
738 
739     if(cmd[0] == '\033' && cmd[1] == '[')
740 	{
741 	    switch(cmd[2])
742 	    {
743 	      case 'A':
744 		*valp=10;
745 		return RC_CHANGE_VOLUME;
746 	      case 'B':
747 		*valp=-10;
748 		return RC_CHANGE_VOLUME;
749 	      case 'C':
750 		*valp=play_mode->rate;
751 		return RC_FORWARD;
752 	      case 'D':
753 		*valp=play_mode->rate;
754 		return RC_BACK;
755 	    }
756 	    return RC_NONE;
757 	}
758     return RC_NONE;
759 }
760 
ctl_write(char * valp,int32 size)761 static int ctl_write(char *valp, int32 size)
762 {
763 	static int warned = 0;
764 
765 	if (!warned) {
766 		fprintf(stderr, "Warning: using stdout with vt100 interface "
767 				"will not\ngive the desired effect.\n");
768 		warned = 1;
769 	}
770 	return write(STDOUT_FILENO, valp, size);
771 }
772 
cmsg(int type,int verbosity_level,char * fmt,...)773 static int cmsg(int type, int verbosity_level, char *fmt, ...)
774 {
775     va_list ap;
776     if ((type==CMSG_TEXT || type==CMSG_INFO || type==CMSG_WARNING) &&
777 	ctl.verbosity<verbosity_level)
778 	return 0;
779     va_start(ap, fmt);
780     if (!ctl.opened)
781     {
782 	vfprintf(stderr, fmt, ap);
783 	fputs(NLS, stderr);
784     }
785     else
786     {
787 	char *buff;
788 	int i;
789 	MBlockList pool;
790 
791 	init_mblock(&pool);
792 	buff = (char *)new_segment(&pool, MIN_MBLOCK_SIZE);
793 	vsnprintf(buff, MIN_MBLOCK_SIZE, fmt, ap);
794 	for(i = 0; i < VT100_COLS - 1 && buff[i]; i++)
795 	    if(buff[i] == '\n' || buff[i] == '\r' || buff[i] == '\t')
796 		buff[i] = ' ';
797 	buff[i] = '\0';
798 	if(!ctl.trace_playing){
799 	  msg_row++;
800 	  if(msg_row == VT100_ROWS)
801 	  {
802 	    int i;
803 	    msg_row = 6;
804 	    for(i = 6; i <= VT100_ROWS; i++)
805 	    {
806 	      vt100_move(i, 0);
807 	      vt100_clrtoeol();
808 	    }
809 	  }
810 	}
811 	vt100_move(msg_row,0);
812 	vt100_clrtoeol();
813 
814 	switch(type)
815 	{
816 	  case CMSG_WARNING:
817 	  case CMSG_ERROR:
818 	  case CMSG_FATAL:
819 	    vt100_set_attr(VT100_ATTR_REVERSE);
820 	    fputs(buff, stdout);
821 	    vt100_reset_attr();
822 	    break;
823 	  default:
824 	    fputs(buff, stdout);
825 	    break;
826 	}
827 	ctl_refresh();
828 	if(type == CMSG_ERROR || type == CMSG_FATAL)
829 	    sleep(2);
830 	reuse_mblock(&pool);
831     }
832 
833     va_end(ap);
834     return 0;
835 }
836 
837 #if !defined(__W32__) || defined(__CYGWIN32__)
838 /* UNIX */
vt100_getline(void)839 static char *vt100_getline(void)
840 {
841     static char cmd[VT100_COLS];
842     fd_set fds;
843     int cnt;
844     struct timeval timeout;
845 
846     FD_ZERO(&fds);
847     FD_SET(0, &fds);
848     timeout.tv_sec = timeout.tv_usec = 0;
849     if((cnt = select(1, &fds, NULL, NULL, &timeout)) < 0)
850     {
851 	perror("select");
852 	return NULL;
853     }
854 
855     if(cnt > 0 && FD_ISSET(0, &fds) != 0)
856     {
857 	if(fgets(cmd, sizeof(cmd), stdin) == NULL)
858 	{
859 	    rewind(stdin);
860 	    return NULL;
861 	}
862 	return cmd;
863     }
864 
865     return NULL;
866 }
867 #else
868 /* Windows */
869 
870 /* Define VT100_CBREAK_MODE if you want to emulate like ncurses mode */
871 /* #define VT100_CBREAK_MODE */
872 
873 #include <conio.h>
vt100_getline(void)874 static char *vt100_getline(void)
875 {
876     static char cmd[VT100_COLS];
877     static int cmdlen = 0;
878     int c;
879 
880     if(kbhit())
881     {
882 	c = getch();
883 	if(c == 'q' || c == 3 || c == 4)
884 	    return "q";
885 	if(c == '\r')
886 	    c = '\n';
887 
888 #ifdef VT100_CBREAK_MODE
889 	cmd[0] = c;
890 	cmd[1] = '\0';
891 	return cmd;
892 #else
893 	if(cmdlen < sizeof(cmd) - 1)
894 	    cmd[cmdlen++] = (char)c;
895 	if(c == '\n')
896 	{
897 	    cmd[cmdlen] = '\0';
898 	    cmdlen = 0;
899 	    return cmd;
900 	}
901 #endif /* VT100_CBREAK_MODE */
902     }
903     return NULL;
904 }
905 #endif
906 
907 
908 /* Indicator */
909 
reset_indicator(void)910 static void reset_indicator(void)
911 {
912     int i;
913 
914     memset(comment_indicator_buffer, ' ', indicator_width - 1);
915     comment_indicator_buffer[indicator_width - 1] = '\0';
916 
917     next_indicator_chan = -1;
918     indicator_last_update = get_current_calender_time();
919     indicator_mode = INDICATOR_DEFAULT;
920     indicator_msgptr = NULL;
921 
922     for(i = 0; i < MAX_CHANNELS; i++)
923     {
924 	instr_comment[i].last_note_on = 0.0;
925 	instr_comment[i].comm = channel_instrum_name(i);
926     }
927 }
928 
update_indicator(void)929 static void update_indicator(void)
930 {
931     double t;
932     int i;
933     char c;
934 
935     t = get_current_calender_time();
936     if(indicator_mode != INDICATOR_DEFAULT)
937     {
938 	int save_chan;
939 	if(indicator_last_update + SCRMODE_OUT_THRESHOLD > t)
940 	    return;
941 	save_chan = next_indicator_chan;
942 	reset_indicator();
943 	next_indicator_chan = save_chan;
944     }
945     else
946     {
947 	if(indicator_last_update + INDICATOR_UPDATE_TIME > t)
948 	    return;
949     }
950     indicator_last_update = t;
951 
952     if(indicator_msgptr != NULL && *indicator_msgptr == '\0')
953 	indicator_msgptr = NULL;
954 
955     if(indicator_msgptr == NULL)
956     {
957 	if(next_indicator_chan >= 0 &&
958 	   instr_comment[next_indicator_chan].comm != NULL &&
959 	   *instr_comment[next_indicator_chan].comm)
960 	{
961 	    current_indicator_chan = next_indicator_chan;
962 	}
963 	else
964 	{
965 	    int prog;
966 
967 	    prog = instr_comment[current_indicator_chan].prog;
968 	    for(i = 0; i < MAX_CHANNELS; i++)
969 	    {
970 		current_indicator_chan++;
971 		if(current_indicator_chan == MAX_CHANNELS)
972 		    current_indicator_chan = 0;
973 
974 
975 		if(instr_comment[current_indicator_chan].comm != NULL &&
976 		   *instr_comment[current_indicator_chan].comm &&
977 		   instr_comment[current_indicator_chan].prog != prog &&
978 		   (instr_comment[current_indicator_chan].last_note_on + CHECK_NOTE_SLEEP_TIME > t ||
979 		    instr_comment[current_indicator_chan].disp_cnt == 0))
980 		    break;
981 	    }
982 
983 	    if(i == MAX_CHANNELS)
984 		return;
985 	}
986 	next_indicator_chan = -1;
987 
988 	if(instr_comment[current_indicator_chan].comm == NULL ||
989 	   *instr_comment[current_indicator_chan].comm == '\0')
990 	    return;
991 
992 	snprintf(current_indicator_message, indicator_width, "%03d:%s   ",
993 		instr_comment[current_indicator_chan].prog,
994 		instr_comment[current_indicator_chan].comm);
995 	instr_comment[current_indicator_chan].disp_cnt++;
996 	indicator_msgptr = current_indicator_message;
997     }
998 
999     c = *indicator_msgptr++;
1000 
1001     for(i = 0; i < indicator_width - 2; i++)
1002 	comment_indicator_buffer[i] = comment_indicator_buffer[i + 1];
1003     comment_indicator_buffer[indicator_width - 2] = c;
1004     vt100_move(msg_row, 0);
1005     fputs(comment_indicator_buffer, stdout);
1006     ctl_refresh();
1007 }
1008 
indicator_chan_update(int ch)1009 static void indicator_chan_update(int ch)
1010 {
1011     double t;
1012 
1013     t = get_current_calender_time();
1014     if(next_indicator_chan == -1 &&
1015        instr_comment[ch].last_note_on + CHECK_NOTE_SLEEP_TIME < t)
1016 	next_indicator_chan = ch;
1017     instr_comment[ch].last_note_on = t;
1018     instr_comment[ch].disp_cnt = 0;
1019     if(instr_comment[ch].comm == NULL)
1020     {
1021 	if((instr_comment[ch].comm = default_instrument_name) == NULL)
1022 	{
1023 	    if(!ISDRUMCHANNEL(ch))
1024 		instr_comment[ch].comm = "<GrandPiano>";
1025 	    else
1026 		instr_comment[ch].comm = "<Drum>";
1027 	}
1028     }
1029 }
1030 
indicator_set_prog(int ch,int val,char * comm)1031 static void indicator_set_prog(int ch, int val, char *comm)
1032 {
1033     instr_comment[ch].comm = comm;
1034     instr_comment[ch].prog = val;
1035     instr_comment[ch].last_note_on = 0.0;
1036 }
1037 
display_lyric(char * lyric,int sep)1038 static void display_lyric(char *lyric, int sep)
1039 {
1040     char *p;
1041     int len, idlen, sepoffset;
1042     static int crflag = 0;
1043 
1044     if(lyric == NULL)
1045     {
1046 	indicator_last_update = get_current_calender_time();
1047 	crflag = 0;
1048 	return;
1049     }
1050 
1051     if(indicator_mode != INDICATOR_LYRIC || crflag)
1052     {
1053 	memset(comment_indicator_buffer, 0, indicator_width);
1054 	vt100_move(lyric_row, 0);
1055 	vt100_clrtoeol();
1056 	ctl_refresh();
1057 	indicator_mode = INDICATOR_LYRIC;
1058 	crflag = 0;
1059     }
1060 
1061     if(*lyric == '\0')
1062     {
1063 	indicator_last_update = get_current_calender_time();
1064 	return;
1065     }
1066     else if(*lyric == '\n')
1067     {
1068 	if(!ctl.trace_playing)
1069 	{
1070 	    crflag = 1;
1071 	    lyric_row++;
1072 	    vt100_move(lyric_row, 0);
1073 	    return;
1074 	}
1075 	else
1076 	    lyric = " / ";
1077     }
1078 
1079     if(strchr(lyric, '\r') != NULL)
1080     {
1081 	crflag = 1;
1082 	if(!ctl.trace_playing)
1083 	{
1084 	    int i;
1085 	    for(i = title_row+1; i <= lyric_row; i++)
1086 	    {
1087 		vt100_move(i, 0);
1088 		vt100_clrtoeol();
1089 	    }
1090 	    lyric_row = title_row+1;
1091 	}
1092 	if(lyric[0] == '\r' && lyric[1] == '\0')
1093 	{
1094 	    indicator_last_update = get_current_calender_time();
1095 	    return;
1096 	}
1097     }
1098 
1099     idlen = strlen(comment_indicator_buffer);
1100     len = strlen(lyric);
1101 
1102     if(sep)
1103     {
1104 	while(idlen > 0 && comment_indicator_buffer[idlen - 1] == ' ')
1105 	    comment_indicator_buffer[--idlen] = '\0';
1106 	while(len > 0 && lyric[len - 1] == ' ')
1107 	    len--;
1108     }
1109 
1110     if(len == 0)
1111     {
1112 	/* update time stamp */
1113 	indicator_last_update = get_current_calender_time();
1114 	reuse_mblock(&tmpbuffer);
1115 	return;
1116     }
1117 
1118     sepoffset = (sep != 0);
1119 
1120     if(len >= indicator_width - 2)
1121     {
1122 	memcpy(comment_indicator_buffer, lyric, indicator_width - 1);
1123 	comment_indicator_buffer[indicator_width - 1] = '\0';
1124     }
1125     else if(idlen == 0)
1126     {
1127 	memcpy(comment_indicator_buffer, lyric, len);
1128 	comment_indicator_buffer[len] = '\0';
1129     }
1130     else if(len + idlen + 2 < indicator_width)
1131     {
1132 	if(sep)
1133 	    comment_indicator_buffer[idlen] = sep;
1134 	memcpy(comment_indicator_buffer + idlen + sepoffset, lyric, len);
1135 	comment_indicator_buffer[idlen + sepoffset + len] = '\0';
1136     }
1137     else
1138     {
1139 	int spaces;
1140 	p = comment_indicator_buffer;
1141 	spaces = indicator_width - idlen - 2;
1142 
1143 	while(spaces < len)
1144 	{
1145 	    char *q;
1146 
1147 	    /* skip one word */
1148 	    if((q = strchr(p, ' ')) == NULL)
1149 	    {
1150 		p = NULL;
1151 		break;
1152 	    }
1153 
1154 	    do q++; while(*q == ' ');
1155 	    spaces += (q - p);
1156 	    p = q;
1157 	}
1158 
1159 	if(p == NULL)
1160 	{
1161 	    vt100_move(lyric_row, 0);
1162 	    vt100_clrtoeol();
1163 	    memcpy(comment_indicator_buffer, lyric, len);
1164 	    comment_indicator_buffer[len] = '\0';
1165 	}
1166 	else
1167 	{
1168 	    int d, l, r, i, j;
1169 
1170 	    d = (p - comment_indicator_buffer);
1171 	    l = strlen(p);
1172 	    r = len - (indicator_width - 2 - l - d);
1173 
1174 	    j = d - r;
1175 	    for(i = 0; i < j; i++)
1176 		comment_indicator_buffer[i] = ' ';
1177 	    for(i = 0; i < l; i++)
1178 		comment_indicator_buffer[j + i] =
1179 		    comment_indicator_buffer[d + i];
1180 	    if(sep)
1181 		comment_indicator_buffer[j + i] = sep;
1182 	    memcpy(comment_indicator_buffer + j + i + sepoffset, lyric, len);
1183 	    comment_indicator_buffer[j + i + sepoffset + len] = '\0';
1184 	}
1185     }
1186 
1187     vt100_move(lyric_row, 0);
1188     fputs(comment_indicator_buffer, stdout);
1189     ctl_refresh();
1190     reuse_mblock(&tmpbuffer);
1191     indicator_last_update = get_current_calender_time();
1192 }
1193 
display_title(char * title)1194 static void display_title(char *title)
1195 {
1196     vt100_move(title_row, 0);
1197     printf("Title:");
1198     vt100_move(title_row++, 7);
1199     vt100_set_attr(VT100_ATTR_BOLD);
1200     printf("%s", title);
1201     vt100_reset_attr();
1202     lyric_row = title_row + 1;
1203 }
1204 
init_lyric(char * lang)1205 static void init_lyric(char *lang)
1206 {
1207     int i;
1208 
1209     if(ctl.trace_playing)
1210 	return;
1211 
1212     msg_row = 6;
1213     for(i = 6; i <= VT100_ROWS; i++)
1214     {
1215 	vt100_move(i, 0);
1216 	vt100_clrtoeol();
1217     }
1218 }
1219 
ctl_event(CtlEvent * e)1220 static void ctl_event(CtlEvent *e)
1221 {
1222     switch(e->type)
1223     {
1224       case CTLE_NOW_LOADING:
1225 	ctl_file_name((char *)e->v1);
1226 	break;
1227       case CTLE_LOADING_DONE:
1228 	break;
1229       case CTLE_PLAY_START:
1230 	ctl_total_time((int)e->v1);
1231 	break;
1232       case CTLE_PLAY_END:
1233 	break;
1234 	case CTLE_CUEPOINT:
1235 		cuepoint = e->v1;
1236 		cuepoint_pending = 1;
1237 		break;
1238       case CTLE_TEMPO:
1239 	break;
1240       case CTLE_METRONOME:
1241 	update_indicator();
1242 	break;
1243       case CTLE_CURRENT_TIME:
1244 	ctl_current_time((int)e->v1, (int)e->v2);
1245 	break;
1246       case CTLE_NOTE:
1247 	ctl_note((int)e->v1, (int)e->v2, (int)e->v3, (int)e->v4);
1248 	break;
1249       case CTLE_MASTER_VOLUME:
1250 	ctl_master_volume((int)e->v1);
1251 	break;
1252       case CTLE_PROGRAM:
1253 	ctl_program((int)e->v1, (int)e->v2, (char *)e->v3);
1254 	break;
1255       case CTLE_VOLUME:
1256 	ctl_volume((int)e->v1, (int)e->v2);
1257 	break;
1258       case CTLE_EXPRESSION:
1259 	ctl_expression((int)e->v1, (int)e->v2);
1260 	break;
1261       case CTLE_PANNING:
1262 	ctl_panning((int)e->v1, (int)e->v2);
1263 	break;
1264       case CTLE_SUSTAIN:
1265 	ctl_sustain((int)e->v1, (int)e->v2);
1266 	break;
1267       case CTLE_PITCH_BEND:
1268 	ctl_pitch_bend((int)e->v1, (int)e->v2);
1269 	break;
1270       case CTLE_MOD_WHEEL:
1271 	ctl_pitch_bend((int)e->v1, e->v2 ? -1 : 0x2000);
1272 	break;
1273       case CTLE_CHORUS_EFFECT:
1274 	break;
1275       case CTLE_REVERB_EFFECT:
1276 	break;
1277       case CTLE_LYRIC:
1278 	ctl_lyric((int)e->v1);
1279 	break;
1280       case CTLE_REFRESH:
1281 	ctl_refresh();
1282 	break;
1283       case CTLE_RESET:
1284 	ctl_reset();
1285 	break;
1286     }
1287 }
1288 
1289 /*
1290  * interface_<id>_loader();
1291  */
interface_T_loader(void)1292 ControlMode *interface_T_loader(void)
1293 {
1294     return &ctl;
1295 }
1296