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     slang_c.c, Riccardo Facchetti (riccardo@cdc8g5.cdc.polimi.it)
21 
22       based on ncurses_ctl.c
23       slang library is more efficient than ncurses one.
24 
25     04/04/1995
26       - Initial, working version.
27 
28     15/04/1995
29       - Works with no-trace playing too; not the best way, but
30         it is the only way: slang 0.99.1 don't have a window management interface
31         and I don't want write one for it! :)
32         The problem is that I have set the no-scroll slang option so
33         when there are too much messages, the last ones are not displayed at
34         all. Tipically the last messages are warning for instruments not found
35         so this is no real problem (I hope :)
36       - Get the real size of screen we are running on
37 
38     TiMidity++ release: Masanao Izumo <iz@onicos.co.jp>
39 */
40 
41 #ifdef HAVE_CONFIG_H
42 #include "config.h"
43 #endif /* HAVE_CONFIG_H */
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <unistd.h>
47 #include <stdarg.h>
48 #include <termios.h>
49 #include <sys/ioctl.h>
50 #ifndef NO_STRING_H
51 #include <string.h>
52 #else
53 #include <strings.h>
54 #endif
55 
56 #ifdef HAVE_SLANG_SLANG_H
57 #include <slang/slang.h>
58 #else
59 #include <slang.h>
60 #endif
61 
62 #include "timidity.h"
63 #include "common.h"
64 #include "instrum.h"
65 #include "playmidi.h"
66 #include "readmidi.h"
67 #include "output.h"
68 #include "controls.h"
69 #include "miditrace.h"
70 #include "timer.h"
71 
72 /*
73  * For the set_color pairs (so called 'objects')
74  * see the ctl_open()
75  */
76 #define SLsmg_normal()                SLsmg_set_color(20)
77 #define SLsmg_bold()                  SLsmg_set_color(21)
78 #define SLsmg_reverse()               SLsmg_set_color(22)
79 
80 #define SCRMODE_OUT_THRESHOLD 10.0
81 #define CHECK_NOTE_SLEEP_TIME 5.0
82 #define INDICATOR_UPDATE_TIME 0.2
83 
84 static struct
85 {
86     int prog;
87     int disp_cnt;
88     double last_note_on;
89     char *comm;
90 } instr_comment[MAX_CHANNELS];
91 
92 enum indicator_mode_t
93 {
94     INDICATOR_DEFAULT,
95     INDICATOR_LYRIC
96 };
97 
98 static int indicator_width = 78;
99 static char *comment_indicator_buffer = NULL;
100 static char *current_indicator_message = NULL;
101 static char *indicator_msgptr = NULL;
102 static int current_indicator_chan = 0;
103 static int next_indicator_chan = -1;
104 static double indicator_last_update;
105 static int indicator_mode = INDICATOR_DEFAULT;
106 
107 static void update_indicator(void);
108 static void reset_indicator(void);
109 static void display_lyric(char *lyric, int sep);
110 static void display_title(char *title);
111 static void init_lyric(char *lang);
112 
113 #define LYRIC_WORD_NOSEP	0
114 #define LYRIC_WORD_SEP		' '
115 
116 static void ctl_refresh(void);
117 static void ctl_help_mode(void);
118 static void ctl_total_time(int tt);
119 static void ctl_master_volume(int mv);
120 static void ctl_file_name(char *name);
121 static void ctl_current_time(int secs, int v);
122 static void ctl_note(int status, int ch, int note, int vel);
123 static void ctl_program(int ch, int val);
124 static void ctl_volume(int channel, int val);
125 static void ctl_expression(int channel, int val);
126 static void ctl_panning(int channel, int val);
127 static void ctl_sustain(int channel, int val);
128 static void ctl_pitch_bend(int channel, int val);
129 static void ctl_reset(void);
130 static int ctl_open(int using_stdin, int using_stdout);
131 static void ctl_close(void);
132 static int ctl_read(int32 *valp);
133 static int ctl_write(char *valp, int32 size);
134 static void ctl_lyric(int valp);
135 static int cmsg(int type, int verbosity_level, char *fmt, ...);
136 static void ctl_event(CtlEvent *e);
137 
138 /**********************************************/
139 /* export the interface functions */
140 
141 #define ctl slang_control_mode
142 
143 ControlMode ctl=
144 {
145     "slang interface", 's',
146     "slang",
147     1,0,0,
148     0,
149     ctl_open,
150     ctl_close,
151     dumb_pass_playing_list,
152     ctl_read,
153     ctl_write,
154     cmsg,
155     ctl_event
156 };
157 
158 static uint32 cuepoint = 0;
159 static int cuepoint_pending = 0;
160 
161 /***********************************************************************/
162 /* foreground/background checks disabled since switching to curses */
163 /* static int in_foreground=1; */
164 static int ctl_helpmode=0;
165 static int lyric_row = 6;
166 static int title_row = 6;
167 static int msg_row = 0;
168 
_ctl_refresh(void)169 static void _ctl_refresh(void)
170 {
171   SLsmg_gotorc(0,0);
172   SLsmg_refresh();
173 }
174 
SLsmg_printfrc(int r,int c,char * fmt,...)175 static void SLsmg_printfrc( int r, int c, char *fmt, ...) {
176   char p[1000];
177   va_list ap;
178 
179   SLsmg_gotorc( r, c);
180   va_start(ap, fmt);
181   vsnprintf(p, sizeof(p), fmt, ap);
182   va_end(ap);
183 
184   SLsmg_write_string (p);
185 }
186 
ctl_head(void)187 static void ctl_head(void)
188 {
189   SLsmg_printfrc(0, 0, "TiMidity++ %s%s",
190   		(strcmp(timidity_version, "current")) ? "v" : "", timidity_version);
191   SLsmg_printfrc(0,SLtt_Screen_Cols-45, "(C) 1995 Tuukka Toivonen <toivonen@clinet.fi>");
192   SLsmg_printfrc(1,0, "Press 'h' for help with keys, or 'q' to quit.");
193 }
ctl_refresh(void)194 static void ctl_refresh(void)
195 {
196   if (ctl.trace_playing)
197     _ctl_refresh();
198 }
199 
ctl_help_mode(void)200 static void ctl_help_mode(void)
201 {
202   if (ctl_helpmode)
203     {
204       ctl_helpmode=0;
205 
206 /*
207  * Clear the head zone and reprint head message.
208  */
209       SLsmg_gotorc(0,0);
210       SLsmg_erase_eol();
211       SLsmg_gotorc(1,0);
212       SLsmg_erase_eol();
213         ctl_head();
214       _ctl_refresh();
215     }
216   else
217     {
218       ctl_helpmode=1;
219         SLsmg_reverse();
220       SLsmg_gotorc(0,0);
221       SLsmg_erase_eol();
222       SLsmg_write_string(
223             "V=Louder    b=Skip back      "
224             "n=Next file      r=Restart file");
225 
226       SLsmg_gotorc(1,0);
227       SLsmg_erase_eol();
228       SLsmg_write_string(
229             "v=Softer    f=Skip forward   "
230             "p=Previous file  q=Quit program");
231         SLsmg_normal();
232       _ctl_refresh();
233     }
234 }
235 
ctl_total_time(int tt)236 static void ctl_total_time(int tt)
237 {
238   int mins, secs=tt/play_mode->rate;
239   mins=secs/60;
240   secs-=mins*60;
241 
242   SLsmg_gotorc(4,6+6+3);
243   SLsmg_bold();
244   SLsmg_printf("%3d:%02d", mins, secs);
245   SLsmg_normal();
246   _ctl_refresh();
247 }
248 
ctl_master_volume(int mv)249 static void ctl_master_volume(int mv)
250 {
251   SLsmg_gotorc(4,SLtt_Screen_Cols-5);
252   SLsmg_bold();
253   SLsmg_printf("%03d %%", mv);
254   SLsmg_normal();
255   _ctl_refresh();
256 }
257 
ctl_file_name(char * name)258 static void ctl_file_name(char *name)
259 {
260   SLsmg_gotorc(3,6);
261   SLsmg_erase_eol();
262   SLsmg_bold();
263   SLsmg_write_string(name);
264   SLsmg_normal();
265   _ctl_refresh();
266 }
267 
ctl_current_time(int secs,int v)268 static void ctl_current_time(int secs, int v)
269 {
270   int mins;
271   static int last_voices=-1,last_secs=-1;
272 
273   if(1/*last_secs!=secs*/)
274   {
275     last_secs=secs;
276     mins=secs/60;
277     secs-=mins*60;
278     SLsmg_gotorc(4,6);
279     SLsmg_bold();
280     SLsmg_printf("%3d:%02d", mins, secs);
281     _ctl_refresh();
282   }
283   if(!ctl.trace_playing||midi_trace.flush_flag)
284   {
285     SLsmg_normal();
286     return;
287   }
288 
289   if(last_voices!=voices)
290   {
291     last_voices=voices;
292     SLsmg_gotorc(4,48);
293     SLsmg_printf("%2d", v);
294     SLsmg_normal();
295     _ctl_refresh();
296   }
297 }
298 
ctl_note(int status,int channel,int note,int velocity)299 static void ctl_note(int status, int channel, int note, int velocity)
300 {
301   int xl;
302   if(channel >= 16)
303       return;
304   if (!ctl.trace_playing)
305     return;
306   xl=note%(SLtt_Screen_Cols-24);
307   SLsmg_gotorc(8+channel,xl+3);
308   switch(status)
309     {
310     case VOICE_DIE:
311       SLsmg_write_char(',');
312       break;
313     case VOICE_FREE:
314       SLsmg_write_char('.');
315       break;
316     case VOICE_ON:
317         SLsmg_bold();
318       SLsmg_write_char('0'+(10*velocity)/128);
319       SLsmg_normal();
320       break;
321     case VOICE_OFF:
322     case VOICE_SUSTAINED:
323       SLsmg_write_char('0'+(10*velocity)/128);
324       break;
325     }
326 }
327 
ctl_program(int ch,int val)328 static void ctl_program(int ch, int val)
329 {
330   if(ch >= 16)
331     return;
332   if (!ctl.trace_playing)
333     return;
334   if(channel[ch].special_sample)
335       val = channel[ch].special_sample;
336   else
337       val += progbase;
338   SLsmg_gotorc(8+ch, SLtt_Screen_Cols-20);
339   if (ISDRUMCHANNEL(ch))
340     {
341         SLsmg_bold();
342       SLsmg_printf("%03d", val);
343         SLsmg_normal();
344     }
345   else
346     SLsmg_printf("%03d", val);
347 }
348 
ctl_volume(int ch,int val)349 static void ctl_volume(int ch, int val)
350 {
351   if(ch >= 16)
352     return;
353   if (!ctl.trace_playing)
354     return;
355   SLsmg_gotorc(8+ch, SLtt_Screen_Cols-16);
356   SLsmg_printf("%3d", (val*100)/127);
357 }
358 
ctl_expression(int ch,int val)359 static void ctl_expression(int ch, int val)
360 {
361   if(ch >= 16)
362     return;
363   if (!ctl.trace_playing)
364     return;
365   SLsmg_gotorc(8+ch, SLtt_Screen_Cols-12);
366   SLsmg_printf("%3d", (val*100)/127);
367 }
368 
ctl_panning(int ch,int val)369 static void ctl_panning(int ch, int val)
370 {
371   if(ch >= 16)
372     return;
373   if (!ctl.trace_playing)
374     return;
375   SLsmg_gotorc(8+ch, SLtt_Screen_Cols-8);
376   if (val==NO_PANNING)
377     SLsmg_write_string("   ");
378   else if (val<5)
379     SLsmg_write_string(" L ");
380   else if (val>123)
381     SLsmg_write_string(" R ");
382   else if (val>60 && val<68)
383     SLsmg_write_string(" C ");
384   else
385     {
386       /* wprintw(dftwin, "%+02d", (100*(val-64))/64); */
387       val = (100*(val-64))/64; /* piss on curses */
388       if (val<0)
389       {
390         SLsmg_write_char('-');
391         val=-val;
392       }
393       else SLsmg_write_char('+');
394       SLsmg_printf("%02d", val);
395     }
396 }
397 
ctl_sustain(int ch,int val)398 static void ctl_sustain(int ch, int val)
399 {
400   if(ch >= 16)
401     return;
402   if (!ctl.trace_playing)
403     return;
404   SLsmg_gotorc(8+ch, SLtt_Screen_Cols-4);
405   if (val) SLsmg_write_char('S');
406   else SLsmg_write_char(' ');
407 }
408 
ctl_pitch_bend(int ch,int val)409 static void ctl_pitch_bend(int ch, int val)
410 {
411   if(ch >= 16)
412     return;
413   if (!ctl.trace_playing)
414     return;
415   SLsmg_gotorc(8+ch, SLtt_Screen_Cols-2);
416   if (val==-1) SLsmg_write_char('=');
417   else if (val>0x2000) SLsmg_write_char('+');
418   else if (val<0x2000) SLsmg_write_char('-');
419   else SLsmg_write_char(' ');
420 }
421 
ctl_reset(void)422 static void ctl_reset(void)
423 {
424   int i,j;
425   if (!ctl.trace_playing)
426     return;
427   for (i=0; i<16; i++)
428     {
429       SLsmg_gotorc(8+i, 3);
430       for (j=0; j<SLtt_Screen_Cols-24; j++)
431 	  SLsmg_write_char('.');
432       if(ISDRUMCHANNEL(i))
433 	  ctl_program(i, channel[i].bank);
434       else
435 	  ctl_program(i, channel[i].program);
436       ctl_volume(i, channel[i].volume);
437       ctl_expression(i, channel[i].expression);
438       ctl_panning(i, channel[i].panning);
439       ctl_sustain(i, channel[i].sustain);
440       if(channel[i].pitchbend == 0x2000 && channel[i].mod.val > 0)
441 	  ctl_pitch_bend(i, -1);
442       else
443 	  ctl_pitch_bend(i, channel[i].pitchbend);
444     }
445   _ctl_refresh();
446 }
447 
448 /***********************************************************************/
449 
450 /*ARGSUSED*/
ctl_open(int using_stdin,int using_stdout)451 static int ctl_open(int using_stdin, int using_stdout)
452 {
453 #ifdef TIOCGWINSZ
454   struct winsize size;
455 #endif
456   int i;
457   int save_lines, save_cols;
458 
459   SLtt_get_terminfo();
460 /*
461  * Save the terminfo values for lines and cols
462  * then detect the real values.
463  */
464   save_lines = SLtt_Screen_Rows;
465   save_cols = SLtt_Screen_Cols;
466 #ifdef TIOCGWINSZ
467   if (!ioctl(0, TIOCGWINSZ, &size)) {
468     SLtt_Screen_Cols=size.ws_col;
469     SLtt_Screen_Rows=size.ws_row;
470   } else
471 #endif
472   {
473     SLtt_Screen_Cols=atoi(getenv("COLUMNS"));
474     SLtt_Screen_Rows=atoi(getenv("LINES"));
475   }
476   if (!SLtt_Screen_Cols || !SLtt_Screen_Rows) {
477     SLtt_Screen_Rows = save_lines;
478       SLtt_Screen_Cols = save_cols;
479   }
480   SLang_init_tty(7, 0, 0);
481   SLsmg_init_smg();
482   SLtt_set_color (20, "Normal", "lightgray", "black");
483   SLtt_set_color (21, "HighLight", "white", "black");
484   SLtt_set_color (22, "Reverse", "black", "white");
485   SLtt_Use_Ansi_Colors = 1;
486   SLtt_Term_Cannot_Scroll = 1;
487 
488   ctl.opened=1;
489 
490   SLsmg_cls();
491 
492   ctl_head();
493 
494   SLsmg_printfrc(3,0, "File:");
495   if (ctl.trace_playing)
496     {
497       SLsmg_printfrc(4,0, "Time:");
498       SLsmg_gotorc(4,6+6+1);
499       SLsmg_write_char('/');
500       SLsmg_gotorc(4,40);
501       SLsmg_printf("Voices:    / %d", voices);
502     }
503   else
504     {
505       SLsmg_printfrc(4,0, "Time:");
506       SLsmg_printfrc(4,13, "/");
507     }
508   SLsmg_printfrc(4,SLtt_Screen_Cols-20, "Master volume:");
509   SLsmg_gotorc(5,0);
510   for (i=0; i<SLtt_Screen_Cols; i++)
511     SLsmg_write_char('_');
512   if (ctl.trace_playing)
513     {
514       SLsmg_printfrc(6,0, "Ch");
515       SLsmg_printfrc(6,SLtt_Screen_Cols-20, "Prg Vol Exp Pan S B");
516       SLsmg_gotorc(7,0);
517       for (i=0; i<SLtt_Screen_Cols; i++)
518       SLsmg_write_char('-');
519       for (i=0; i<16; i++)
520       {
521         SLsmg_printfrc(8+i, 0, "%02d", i+1);
522       }
523       set_trace_loop_hook(update_indicator);
524       indicator_width=SLtt_Screen_Cols-2;
525       if(indicator_width<40)
526 	indicator_width=40;
527       lyric_row=2;
528     }
529   else
530     msg_row = 6;
531   memset(comment_indicator_buffer =
532     (char *)safe_malloc(indicator_width), 0, indicator_width);
533   memset(current_indicator_message =
534     (char *)safe_malloc(indicator_width), 0, indicator_width);
535   _ctl_refresh();
536 
537   return 0;
538 }
539 
ctl_close(void)540 static void ctl_close(void)
541 {
542   if (ctl.opened)
543     {
544         SLsmg_normal();
545         SLsmg_gotorc(SLtt_Screen_Rows - 1, 0);
546         SLsmg_refresh();
547         SLsmg_reset_smg();
548         SLang_reset_tty();
549       ctl.opened=0;
550     }
551 }
552 
ctl_read(int32 * valp)553 static int ctl_read(int32 *valp)
554 {
555   int c;
556 
557 	if (cuepoint_pending) {
558 		*valp = cuepoint;
559 		cuepoint_pending = 0;
560 		return RC_FORWARD;
561 	}
562 
563   if (!SLang_input_pending(0))
564     return RC_NONE;
565 
566   c=SLang_getkey();
567     switch(c)
568       {
569       case 'h':
570       case '?':
571         ctl_help_mode();
572         return RC_NONE;
573 
574       case 'V':
575         *valp=10;
576         return RC_CHANGE_VOLUME;
577       case 'v':
578         *valp=-10;
579         return RC_CHANGE_VOLUME;
580       case 'q':
581         return RC_QUIT;
582       case 'n':
583         return RC_NEXT;
584       case 'p':
585         return RC_REALLY_PREVIOUS;
586       case 'r':
587         return RC_RESTART;
588 
589       case 'f':
590         *valp=play_mode->rate;
591         return RC_FORWARD;
592       case 'b':
593         *valp=play_mode->rate;
594         return RC_BACK;
595       case 's':
596 	return RC_TOGGLE_PAUSE;
597       }
598   return RC_NONE;
599 }
600 
ctl_write(char * valp,int32 size)601 static int ctl_write(char *valp, int32 size)
602 {
603   static int warned = 0;
604   if (!warned) {
605     fprintf(stderr, "Warning: using stdout with slang interface will not\n"
606 		    "give the desired effect.\n");
607     warned = 1;
608   }
609   return write(STDOUT_FILENO, valp, size);
610 }
611 
612 /*ARGSUSED*/
ctl_lyric(int lyricid)613 static void ctl_lyric(int lyricid)
614 {
615     char *lyric;
616 
617     lyric = event2string(lyricid);
618     if(lyric != NULL)
619     {
620 	if(*lyric == ME_KARAOKE_LYRIC)
621 	{
622 	    if(lyric[1] == '/')
623 	    {
624 		display_lyric("\n", LYRIC_WORD_NOSEP);
625 		display_lyric(lyric + 2, LYRIC_WORD_NOSEP);
626 	    }
627 	    else if(lyric[1] == '\\')
628 	    {
629 		display_lyric("\r", LYRIC_WORD_NOSEP);
630 		display_lyric(lyric + 2, LYRIC_WORD_NOSEP);
631 	    }
632 	    else if(lyric[1] == '@' && lyric[2] == 'T')
633 	    {
634 		if(ctl.trace_playing)
635 		{
636 		    display_lyric("\n", LYRIC_WORD_NOSEP);
637 		    display_lyric(lyric + 3, LYRIC_WORD_SEP);
638 		}
639 		else
640 		    display_title(lyric + 3);
641 	    }
642 	    else if(lyric[1] == '@' && lyric[2] == 'L')
643 	    {
644 		init_lyric(lyric + 3);
645 	    }
646 	    else
647 		display_lyric(lyric + 1, LYRIC_WORD_NOSEP);
648 	}
649 	else
650 	{
651 	    if(*lyric == ME_CHORUS_TEXT || *lyric == ME_INSERT_TEXT)
652 		display_lyric("\r", LYRIC_WORD_SEP);
653 	    display_lyric(lyric + 1, LYRIC_WORD_SEP);
654 	}
655     }
656 }
657 
cmsg(int type,int verbosity_level,char * fmt,...)658 static int cmsg(int type, int verbosity_level, char *fmt, ...)
659 {
660   va_list ap;
661   char p[1000];
662   if ((type==CMSG_TEXT || type==CMSG_INFO || type==CMSG_WARNING) &&
663       ctl.verbosity<verbosity_level)
664     return 0;
665   va_start(ap, fmt);
666   if (!ctl.opened)
667     {
668       vfprintf(stderr, fmt, ap);
669       fprintf(stderr, "\n");
670     }
671   else if (ctl.trace_playing)
672     {
673       switch(type)
674       {
675         /* Pretty pointless to only have one line for messages, but... */
676       case CMSG_WARNING:
677       case CMSG_ERROR:
678       case CMSG_FATAL:
679         SLsmg_gotorc(2,0);
680       SLsmg_erase_eol();
681       SLsmg_bold();
682         vsnprintf(p, sizeof(p), fmt, ap);
683         SLsmg_write_string(p);
684       SLsmg_normal();
685         _ctl_refresh();
686         if (type==CMSG_WARNING)
687           sleep(1); /* Don't you just _HATE_ it when programs do this... */
688         else
689           sleep(2);
690         SLsmg_gotorc(2,0);
691       SLsmg_erase_eol();
692         _ctl_refresh();
693         break;
694       }
695     }
696   else
697     {
698       SLsmg_gotorc(msg_row++,0);
699       if(msg_row==SLtt_Screen_Rows){
700 	int i;
701         msg_row=6;
702 	for(i=6;i<=SLtt_Screen_Rows;i++){
703           SLsmg_gotorc(i,0);
704           SLsmg_erase_eol();
705 	}
706       }
707       switch(type)
708       {
709       default:
710         vsnprintf(p, sizeof(p), fmt, ap);
711         SLsmg_write_string(p);
712         _ctl_refresh();
713         break;
714 
715       case CMSG_WARNING:
716       SLsmg_bold();
717         vsnprintf(p, sizeof(p), fmt, ap);
718         SLsmg_write_string(p);
719       SLsmg_normal();
720         _ctl_refresh();
721         break;
722 
723       case CMSG_ERROR:
724       case CMSG_FATAL:
725       SLsmg_bold();
726         vsnprintf(p, sizeof(p), fmt, ap);
727         SLsmg_write_string(p);
728       SLsmg_normal();
729         _ctl_refresh();
730         if (type==CMSG_FATAL)
731           sleep(2);
732         break;
733       }
734     }
735 
736   va_end(ap);
737   return 0;
738 }
739 
740 /* Indicator */
741 
reset_indicator(void)742 static void reset_indicator(void)
743 {
744     int i;
745 
746     memset(comment_indicator_buffer, ' ', indicator_width - 1);
747     comment_indicator_buffer[indicator_width - 1] = '\0';
748 
749     next_indicator_chan = -1;
750     indicator_last_update = get_current_calender_time();
751     indicator_mode = INDICATOR_DEFAULT;
752     indicator_msgptr = NULL;
753 
754     for(i = 0; i < MAX_CHANNELS; i++)
755     {
756 	instr_comment[i].last_note_on = 0.0;
757 	instr_comment[i].comm = channel_instrum_name(i);
758     }
759 }
760 
update_indicator(void)761 static void update_indicator(void)
762 {
763     double t;
764     int i;
765     char c;
766 
767     t = get_current_calender_time();
768     if(indicator_mode != INDICATOR_DEFAULT)
769     {
770 	int save_chan;
771 	if(indicator_last_update + SCRMODE_OUT_THRESHOLD > t)
772 	    return;
773 	save_chan = next_indicator_chan;
774 	reset_indicator();
775 	next_indicator_chan = save_chan;
776     }
777     else
778     {
779 	if(indicator_last_update + INDICATOR_UPDATE_TIME > t)
780 	    return;
781     }
782     indicator_last_update = t;
783 
784     if(indicator_msgptr != NULL && *indicator_msgptr == '\0')
785 	indicator_msgptr = NULL;
786 
787     if(indicator_msgptr == NULL)
788     {
789 	if(next_indicator_chan >= 0 &&
790 	   instr_comment[next_indicator_chan].comm != NULL &&
791 	   *instr_comment[next_indicator_chan].comm)
792 	{
793 	    current_indicator_chan = next_indicator_chan;
794 	}
795 	else
796 	{
797 	    int prog;
798 
799 	    prog = instr_comment[current_indicator_chan].prog;
800 	    for(i = 0; i < MAX_CHANNELS; i++)
801 	    {
802 		current_indicator_chan++;
803 		if(current_indicator_chan == MAX_CHANNELS)
804 		    current_indicator_chan = 0;
805 
806 
807 		if(instr_comment[current_indicator_chan].comm != NULL &&
808 		   *instr_comment[current_indicator_chan].comm &&
809 		   instr_comment[current_indicator_chan].prog != prog &&
810 		   (instr_comment[current_indicator_chan].last_note_on + CHECK_NOTE_SLEEP_TIME > t ||
811 		    instr_comment[current_indicator_chan].disp_cnt == 0))
812 		    break;
813 	    }
814 
815 	    if(i == MAX_CHANNELS)
816 		return;
817 	}
818 	next_indicator_chan = -1;
819 
820 	if(instr_comment[current_indicator_chan].comm == NULL ||
821 	   *instr_comment[current_indicator_chan].comm == '\0')
822 	    return;
823 
824 	snprintf(current_indicator_message, indicator_width, "%03d:%s   ",
825 		instr_comment[current_indicator_chan].prog,
826 		instr_comment[current_indicator_chan].comm);
827 	instr_comment[current_indicator_chan].disp_cnt++;
828 	indicator_msgptr = current_indicator_message;
829     }
830 
831     c = *indicator_msgptr++;
832 
833     for(i = 0; i < indicator_width - 2; i++)
834 	comment_indicator_buffer[i] = comment_indicator_buffer[i + 1];
835     comment_indicator_buffer[indicator_width - 2] = c;
836     SLsmg_printfrc(2,0,comment_indicator_buffer);
837     ctl_refresh();
838 }
839 
display_lyric(char * lyric,int sep)840 static void display_lyric(char *lyric, int sep)
841 {
842     char *p;
843     int len, idlen, sepoffset;
844     static int crflag = 0;
845 
846     if(lyric == NULL)
847     {
848 	indicator_last_update = get_current_calender_time();
849 	crflag = 0;
850 	return;
851     }
852 
853     if(indicator_mode != INDICATOR_LYRIC || crflag)
854     {
855 	memset(comment_indicator_buffer, 0, indicator_width);
856 	SLsmg_gotorc(lyric_row,0);
857         SLsmg_erase_eol();
858 	ctl_refresh();
859 	indicator_mode = INDICATOR_LYRIC;
860 	crflag = 0;
861     }
862 
863     if(*lyric == '\0')
864     {
865 	indicator_last_update = get_current_calender_time();
866 	return;
867     }
868     else if(*lyric == '\n')
869     {
870 	if(!ctl.trace_playing)
871 	{
872 	    crflag = 1;
873 	    lyric_row++;
874 	    SLsmg_gotorc(0,lyric_row);
875 	    return;
876 	}
877 	else
878 	    lyric = " / ";
879     }
880 
881     if(strchr(lyric, '\r') != NULL)
882     {
883 	crflag = 1;
884 	if(!ctl.trace_playing)
885 	{
886 	    int i;
887 	    for(i = title_row+1; i <= lyric_row; i++)
888 	    {
889 		SLsmg_gotorc(i,0);
890 		SLsmg_erase_eol();
891 	    }
892 	    lyric_row = title_row+1;
893 	}
894 	if(lyric[0] == '\r' && lyric[1] == '\0')
895 	{
896 	    indicator_last_update = get_current_calender_time();
897 	    return;
898 	}
899     }
900 
901     idlen = strlen(comment_indicator_buffer);
902     len = strlen(lyric);
903 
904     if(sep)
905     {
906 	while(idlen > 0 && comment_indicator_buffer[idlen - 1] == ' ')
907 	    comment_indicator_buffer[--idlen] = '\0';
908 	while(len > 0 && lyric[len - 1] == ' ')
909 	    len--;
910     }
911 
912     if(len == 0)
913     {
914 	/* update time stamp */
915 	indicator_last_update = get_current_calender_time();
916 	reuse_mblock(&tmpbuffer);
917 	return;
918     }
919 
920     sepoffset = (sep != 0);
921 
922     if(len >= indicator_width - 2)
923     {
924 	memcpy(comment_indicator_buffer, lyric, indicator_width - 1);
925 	comment_indicator_buffer[indicator_width - 1] = '\0';
926     }
927     else if(idlen == 0)
928     {
929 	memcpy(comment_indicator_buffer, lyric, len);
930 	comment_indicator_buffer[len] = '\0';
931     }
932     else if(len + idlen + 2 < indicator_width)
933     {
934 	if(sep)
935 	    comment_indicator_buffer[idlen] = sep;
936 	memcpy(comment_indicator_buffer + idlen + sepoffset, lyric, len);
937 	comment_indicator_buffer[idlen + sepoffset + len] = '\0';
938     }
939     else
940     {
941 	int spaces;
942 	p = comment_indicator_buffer;
943 	spaces = indicator_width - idlen - 2;
944 
945 	while(spaces < len)
946 	{
947 	    char *q;
948 
949 	    /* skip one word */
950 	    if((q = strchr(p, ' ')) == NULL)
951 	    {
952 		p = NULL;
953 		break;
954 	    }
955 
956 	    do q++; while(*q == ' ');
957 	    spaces += (q - p);
958 	    p = q;
959 	}
960 
961 	if(p == NULL)
962 	{
963 	    SLsmg_gotorc(lyric_row,0);
964 	    SLsmg_erase_eol();
965 	    memcpy(comment_indicator_buffer, lyric, len);
966 	    comment_indicator_buffer[len] = '\0';
967 	}
968 	else
969 	{
970 	    int d, l, r, i, j;
971 
972 	    d = (p - comment_indicator_buffer);
973 	    l = strlen(p);
974 	    r = len - (indicator_width - 2 - l - d);
975 
976 	    j = d - r;
977 	    for(i = 0; i < j; i++)
978 		comment_indicator_buffer[i] = ' ';
979 	    for(i = 0; i < l; i++)
980 		comment_indicator_buffer[j + i] =
981 		    comment_indicator_buffer[d + i];
982 	    if(sep)
983 		comment_indicator_buffer[j + i] = sep;
984 	    memcpy(comment_indicator_buffer + j + i + sepoffset, lyric, len);
985 	    comment_indicator_buffer[j + i + sepoffset + len] = '\0';
986 	}
987     }
988 
989     SLsmg_printfrc(lyric_row,0,"%s",comment_indicator_buffer);
990     ctl_refresh();
991     reuse_mblock(&tmpbuffer);
992     indicator_last_update = get_current_calender_time();
993 }
994 
display_title(char * title)995 static void display_title(char *title)
996 {
997     SLsmg_printfrc(title_row,0,"Title:");
998     SLsmg_bold();
999     SLsmg_printfrc(title_row++,7,"%s", title);
1000     SLsmg_normal();
1001     lyric_row = title_row + 1;
1002 }
1003 
init_lyric(char * lang)1004 static void init_lyric(char *lang)
1005 {
1006     int i;
1007 
1008     if(ctl.trace_playing)
1009       return;
1010     for(i=6;i<=SLtt_Screen_Rows;i++){
1011       SLsmg_gotorc(i,0);
1012       SLsmg_erase_eol();
1013     }
1014 }
1015 
ctl_event(CtlEvent * e)1016 static void ctl_event(CtlEvent *e)
1017 {
1018     switch(e->type)
1019     {
1020       case CTLE_NOW_LOADING:
1021 	ctl_file_name((char *)e->v1);
1022 	break;
1023       case CTLE_LOADING_DONE:
1024 	break;
1025       case CTLE_PLAY_START:
1026 	ctl_total_time((int)e->v1);
1027 	break;
1028       case CTLE_PLAY_END:
1029 	break;
1030 	case CTLE_CUEPOINT:
1031 		cuepoint = e->v1;
1032 		cuepoint_pending = 1;
1033 		break;
1034       case CTLE_TEMPO:
1035 	break;
1036       case CTLE_METRONOME:
1037 	/* update_indicator(); */
1038 	break;
1039       case CTLE_CURRENT_TIME:
1040 	ctl_current_time((int)e->v1, (int)e->v2);
1041 	break;
1042       case CTLE_NOTE:
1043 	ctl_note((int)e->v1, (int)e->v2, (int)e->v3, (int)e->v4);
1044 	break;
1045       case CTLE_MASTER_VOLUME:
1046 	ctl_master_volume((int)e->v1);
1047 	break;
1048       case CTLE_PROGRAM:
1049 	ctl_program((int)e->v1, (int)e->v2);
1050 	break;
1051       case CTLE_VOLUME:
1052 	ctl_volume((int)e->v1, (int)e->v2);
1053 	break;
1054       case CTLE_EXPRESSION:
1055 	ctl_expression((int)e->v1, (int)e->v2);
1056 	break;
1057       case CTLE_PANNING:
1058 	ctl_panning((int)e->v1, (int)e->v2);
1059 	break;
1060       case CTLE_SUSTAIN:
1061 	ctl_sustain((int)e->v1, (int)e->v2);
1062 	break;
1063       case CTLE_PITCH_BEND:
1064 	ctl_pitch_bend((int)e->v1, (int)e->v2);
1065 	break;
1066       case CTLE_MOD_WHEEL:
1067 	ctl_pitch_bend((int)e->v1, e->v2 ? -1 : 0x2000);
1068 	break;
1069       case CTLE_CHORUS_EFFECT:
1070 	break;
1071       case CTLE_REVERB_EFFECT:
1072 	break;
1073       case CTLE_LYRIC:
1074 	ctl_lyric((int)e->v1);
1075 	break;
1076       case CTLE_REFRESH:
1077 	ctl_refresh();
1078 	break;
1079       case CTLE_RESET:
1080 	ctl_reset();
1081 	break;
1082     }
1083 }
1084 
1085 /*
1086  * interface_<id>_loader();
1087  */
interface_s_loader(void)1088 ControlMode *interface_s_loader(void)
1089 {
1090     return &ctl;
1091 }
1092