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