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     The Tcl/Tk interface for Timidity
21     written by Takashi Iwai (iwai@dragon.mm.t.u-tokyo.ac.jp)
22 
23     Most of the following codes are derived from both motif_ctl.c
24     and motif_pipe.c.  The communication between Tk program and
25     timidity is established by a pipe stream as in Motif interface.
26     On the contrast to motif, the stdin and stdout are assigned
27     as pipe i/o in Tk interface.
28 */
29 
30 
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif /* HAVE_CONFIG_H */
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <stdarg.h>
38 #ifndef NO_STRING_H
39 #include <string.h>
40 #else
41 #include <strings.h>
42 #endif
43 #include <sys/types.h>
44 #include <sys/ioctl.h>
45 #include <signal.h>
46 #include <sys/ipc.h>
47 #include <sys/shm.h>
48 #include <sys/sem.h>
49 #include <sys/wait.h>
50 
51 #include "timidity.h"
52 #include "common.h"
53 #include "instrum.h"
54 #include "playmidi.h"
55 #include "readmidi.h"
56 #include "output.h"
57 #include "controls.h"
58 #include "miditrace.h"
59 #include "aq.h"
60 
61 #include <tcl.h>
62 #include <tk.h>
63 
64 #ifndef TKPROGPATH
65 #define TKPROGPATH PKGLIBDIR "/tkmidity.tcl"
66 #endif /* TKPROGPATH */
67 
68 #if (TCL_MAJOR_VERSION < 8)
69 #define Tcl_GetStringResult(interp) (interp->result)
70 #endif
71 
72 static void ctl_refresh(void);
73 static void ctl_total_time(int tt);
74 static void ctl_master_volume(int mv);
75 static void ctl_file_name(char *name);
76 static void ctl_current_time(int secs, int v);
77 static void ctl_program(int ch, int val);
78 static void ctl_volume(int channel, int val);
79 static void ctl_expression(int channel, int val);
80 static void ctl_panning(int channel, int val);
81 static void ctl_sustain(int channel, int val);
82 static void ctl_pitch_bend(int channel, int val);
83 static void ctl_lyric(int lyricid);
84 static void ctl_reset(void);
85 static int ctl_open(int using_stdin, int using_stdout);
86 static void ctl_close(void);
87 static int ctl_read(int32 *valp);
88 static int cmsg(int type, int verbosity_level, char *fmt, ...);
89 static int ctl_pass_playing_list(int number_of_files, char *list_of_files[]);
90 static int ctl_blocking_read(int32 *valp);
91 static void ctl_note(int status, int ch, int note, int vel);
92 static void ctl_event(CtlEvent *e);
93 
94 static void k_pipe_printf(char *fmt, ...);
95 static void k_pipe_puts(char *str);
96 static int k_pipe_gets(char *str, int maxlen);
97 static void k_pipe_open(void);
98 static void k_pipe_error(char *st);
99 static int k_pipe_read_ready(void);
100 
101 static int AppInit(Tcl_Interp *interp);
102 static int ExitAll(ClientData clientData, Tcl_Interp *interp,
103 		       int argc, char *argv[]);
104 static int TraceCreate(ClientData clientData, Tcl_Interp *interp,
105 		       int argc, char *argv[]);
106 static int TraceUpdate(ClientData clientData, Tcl_Interp *interp,
107 		    int argc, char *argv[]);
108 static int TraceReset(ClientData clientData, Tcl_Interp *interp,
109 		    int argc, char *argv[]);
110 static void trace_volume(int ch, int val);
111 static void trace_panning(int ch, int val);
112 static void trace_prog_init(int ch);
113 static void trace_prog(int ch, int val);
114 static void trace_bank(int ch, int val);
115 static void trace_sustain(int ch, int val);
116 
117 static void get_child(int sig);
118 static void shm_alloc(void);
119 static void shm_free(int sig);
120 
121 static void start_panel(void);
122 
123 #define MAX_TK_MIDI_CHANNELS	32
124 
125 typedef struct {
126 	int reset_panel;
127 	int multi_part;
128 
129 	int32 last_time, cur_time;
130 
131 	char v_flags[MAX_TK_MIDI_CHANNELS];
132 	int16 cnote[MAX_TK_MIDI_CHANNELS];
133 	int16 cvel[MAX_TK_MIDI_CHANNELS];
134 	int16 ctotal[MAX_TK_MIDI_CHANNELS];
135 
136 	char c_flags[MAX_TK_MIDI_CHANNELS];
137 	Channel channel[MAX_TK_MIDI_CHANNELS];
138 	int wait_reset;
139 } PanelInfo;
140 
141 
142 /**********************************************/
143 
144 #define ctl tk_control_mode
145 
146 ControlMode ctl=
147 {
148     "Tcl/Tk interface", 'k',
149     "tcltk",
150     1,0,0,
151     0,
152     ctl_open,
153     ctl_close,
154     ctl_pass_playing_list,
155     ctl_read,
156     NULL,
157     cmsg,
158     ctl_event
159 };
160 
161 static uint32 cuepoint = 0;
162 static int cuepoint_pending = 0;
163 
164 #define FLAG_NOTE_OFF	1
165 #define FLAG_NOTE_ON	2
166 
167 #define FLAG_BANK	1
168 #define FLAG_PROG	2
169 #define FLAG_PAN	4
170 #define FLAG_SUST	8
171 
172 static VOLATILE PanelInfo *Panel;
173 static VOLATILE int child_killed = 0;
174 
175 static int shmid;	/* shared memory id */
176 static int semid;	/* semaphore id */
177 
178 static int pipeAppli[2],pipePanel[2]; /* Pipe for communication with Tcl/Tk process   */
179 static int fpip_in, fpip_out;	/* in and out depends in which process we are */
180 static int child_pid;	               /* Pid for child process */
181 
182 /***********************************************************************/
183 /* semaphore utilities                                                 */
184 /***********************************************************************/
semaphore_P(int sid)185 static void semaphore_P(int sid)
186 {
187     struct sembuf sb;
188 
189     sb.sem_num = 0;
190     sb.sem_op  = -1;
191     sb.sem_flg = 0;
192     if(semop(sid, &sb, 1) == -1)
193 	perror("semop");
194 }
195 
semaphore_V(int sid)196 static void semaphore_V(int sid)
197 {
198     struct sembuf sb;
199 
200     sb.sem_num = 0;
201     sb.sem_op  = 1;
202     sb.sem_flg = 0;
203     if(semop(sid, &sb, 1) == -1)
204 	perror("semop");
205 }
206 
207 /***********************************************************************/
208 /* Put controls on the pipe                                            */
209 /***********************************************************************/
210 
cmsg(int type,int verbosity_level,char * fmt,...)211 static int cmsg(int type, int verbosity_level, char *fmt, ...)
212 {
213 	char local[2048];
214 #define TOO_LONG	2000
215 
216 	va_list ap;
217 	if ((type==CMSG_TEXT || type==CMSG_INFO || type==CMSG_WARNING) &&
218 	    ctl.verbosity<verbosity_level)
219 		return 0;
220 
221 	va_start(ap, fmt);
222 	if (strlen(fmt) > TOO_LONG)
223 		fmt[TOO_LONG] = 0;
224 	if (!ctl.opened) {
225 		vfprintf(stderr, fmt, ap);
226 		fprintf(stderr, "\n");
227 	} else if (type == CMSG_ERROR) {
228 		int32 val;
229 		vsnprintf(local, sizeof(local), fmt, ap);
230 		k_pipe_printf("CERR %d", type);
231 		k_pipe_puts(local);
232 		while (ctl_blocking_read(&val) != RC_NEXT)
233 			;
234 	} else {
235 		vsnprintf(local, sizeof(local), fmt, ap);
236 		k_pipe_printf("CMSG %d", type);
237 		k_pipe_puts(local);
238 	}
239 	va_end(ap);
240 	return 0;
241 }
242 
ctl_refresh(void)243 static void ctl_refresh(void)
244 {
245 }
246 
ctl_total_time(int tt)247 static void ctl_total_time(int tt)
248 {
249 	int centisecs=tt/(play_mode->rate/100);
250 	k_pipe_printf("TIME %d", centisecs);
251 	ctl_current_time(0, 0);
252 }
253 
ctl_master_volume(int mv)254 static void ctl_master_volume(int mv)
255 {
256 	k_pipe_printf("MVOL %d", mv);
257 }
258 
ctl_file_name(char * name)259 static void ctl_file_name(char *name)
260 {
261 	k_pipe_printf("FILE %s", name);
262 }
263 
ctl_current_time(int secs,int v)264 static void ctl_current_time(int secs, int v)
265 {
266     Panel->cur_time = secs;
267 }
268 
ctl_channel_note(int ch,int note,int vel)269 static void ctl_channel_note(int ch, int note, int vel)
270 {
271 	if (vel == 0) {
272 		if (note == Panel->cnote[ch])
273 			Panel->v_flags[ch] = FLAG_NOTE_OFF;
274 		Panel->cvel[ch] = 0;
275 	} else if (vel > Panel->cvel[ch]) {
276 		Panel->cvel[ch] = vel;
277 		Panel->cnote[ch] = note;
278 		Panel->ctotal[ch] = vel * Panel->channel[ch].volume *
279 			Panel->channel[ch].expression / (127*127);
280 		Panel->v_flags[ch] = FLAG_NOTE_ON;
281 	}
282 }
283 
ctl_note(int status,int ch,int note,int vel)284 static void ctl_note(int status, int ch, int note, int vel)
285 {
286 	if (!ctl.trace_playing)
287 		return;
288 
289 	if (ch < 0 || ch >= MAX_TK_MIDI_CHANNELS) return;
290 
291 	if (status != VOICE_ON)
292 		vel = 0;
293 	semaphore_P(semid);
294 	ctl_channel_note(ch, note, vel);
295 	semaphore_V(semid);
296 }
297 
ctl_program(int ch,int val)298 static void ctl_program(int ch, int val)
299 {
300         if(ch >= MAX_TK_MIDI_CHANNELS)
301 		return;
302 	if (!ctl.trace_playing)
303 		return;
304 	if (ch < 0 || ch >= MAX_TK_MIDI_CHANNELS) return;
305 	if(channel[ch].special_sample)
306 	    val = channel[ch].special_sample;
307 	else
308 	    val += progbase;
309 
310 	semaphore_P(semid);
311 	Panel->channel[ch].program = val;
312 	Panel->c_flags[ch] |= FLAG_PROG;
313 	semaphore_V(semid);
314 }
315 
ctl_volume(int ch,int val)316 static void ctl_volume(int ch, int val)
317 {
318         if(ch >= MAX_TK_MIDI_CHANNELS)
319 		return;
320 	if (!ctl.trace_playing)
321 		return;
322 	semaphore_P(semid);
323 	Panel->channel[ch].volume = val;
324 	ctl_channel_note(ch, Panel->cnote[ch], Panel->cvel[ch]);
325 	semaphore_V(semid);
326 }
327 
ctl_expression(int ch,int val)328 static void ctl_expression(int ch, int val)
329 {
330         if(ch >= MAX_TK_MIDI_CHANNELS)
331 		return;
332 	if (!ctl.trace_playing)
333 		return;
334 	semaphore_P(semid);
335 	Panel->channel[ch].expression = val;
336 	ctl_channel_note(ch, Panel->cnote[ch], Panel->cvel[ch]);
337 	semaphore_V(semid);
338 }
339 
ctl_panning(int ch,int val)340 static void ctl_panning(int ch, int val)
341 {
342         if(ch >= MAX_TK_MIDI_CHANNELS)
343 		return;
344 	if (!ctl.trace_playing)
345 		return;
346 	semaphore_P(semid);
347 	Panel->channel[ch].panning = val;
348 	Panel->c_flags[ch] |= FLAG_PAN;
349 	semaphore_V(semid);
350 }
351 
ctl_sustain(int ch,int val)352 static void ctl_sustain(int ch, int val)
353 {
354         if(ch >= MAX_TK_MIDI_CHANNELS)
355 		return;
356 	if (!ctl.trace_playing)
357 		return;
358 	semaphore_P(semid);
359 	Panel->channel[ch].sustain = val;
360 	Panel->c_flags[ch] |= FLAG_SUST;
361 	semaphore_V(semid);
362 }
363 
364 /*ARGSUSED*/
ctl_pitch_bend(int channel,int val)365 static void ctl_pitch_bend(int channel, int val)
366 {
367 /*
368         if(ch >= MAX_TK_MIDI_CHANNELS)
369 		return;
370 	if (!ctl.trace_playing)
371 		return;
372 	semaphore_P(semid);
373 	Panel->channel[ch].pitch_bend = val;
374 	Panel->c_flags[ch] |= FLAG_BENDT;
375 	semaphore_V(semid);
376 */
377 }
378 
ctl_lyric(int lyricid)379 static void ctl_lyric(int lyricid)
380 {
381     char *lyric;
382 
383     lyric = event2string(lyricid);
384     if(lyric != NULL)
385     {
386 	if(lyric[0] == ME_KARAOKE_LYRIC)
387 	{
388 	    if(lyric[1] == '/' || lyric[1] == '\\')
389 	    {
390 		k_pipe_printf("CMSG %d", CMSG_TEXT);
391 		k_pipe_puts("");
392 		k_pipe_printf("LYRC %d", CMSG_TEXT);
393 		k_pipe_printf("%s", lyric + 2);
394 	    }
395 	    else if(lyric[1] == '@')
396 	    {
397 		if(lyric[2] == 'L')
398 		{
399 		    k_pipe_printf("CMSG %d", CMSG_TEXT);
400 		    k_pipe_printf("Language: %s", lyric + 3);
401 		}
402 		else if(lyric[2] == 'T')
403 		{
404 		    k_pipe_printf("CMSG %d", CMSG_TEXT);
405 		    k_pipe_printf("Title: %s", lyric + 3);
406 		}
407 		else
408 		{
409 		    k_pipe_printf("CMSG %d", CMSG_TEXT);
410 		    k_pipe_printf("%s", lyric + 1);
411 		}
412 	    }
413 	    else
414 	    {
415 		k_pipe_printf("LYRC %d", CMSG_TEXT);
416 		k_pipe_printf("%s", lyric + 1);
417 	    }
418 	}
419 	else
420 	{
421 	    k_pipe_printf("CMSG %d", CMSG_TEXT);
422 	    k_pipe_printf("%s", lyric + 1);
423 	}
424     }
425 }
426 
ctl_reset(void)427 static void ctl_reset(void)
428 {
429 	int i;
430 
431 	if (!ctl.trace_playing)
432 	{
433 	    k_pipe_printf("RSET %d", ctl.trace_playing);
434 	    return;
435 	}
436 
437 	Panel->wait_reset = 1;
438 	k_pipe_printf("RSET %d", ctl.trace_playing);
439 
440 	while(Panel->wait_reset)
441 	    VOLATILE_TOUCH(Panel->wait_reset);
442 
443 	if (!ctl.trace_playing)
444 		return;
445 	for (i = 0; i < MAX_TK_MIDI_CHANNELS; i++) {
446 		if(ISDRUMCHANNEL(i))
447 		    ctl_program(i, channel[i].bank);
448 		else
449 		    ctl_program(i, channel[i].program);
450 		ctl_volume(i, channel[i].volume);
451 		ctl_expression(i, channel[i].expression);
452 		ctl_panning(i, channel[i].panning);
453 		ctl_sustain(i, channel[i].sustain);
454 		if(channel[i].pitchbend == 0x2000 &&
455 		   channel[i].mod.val > 0)
456 		    ctl_pitch_bend(i, -1);
457 		else
458 		    ctl_pitch_bend(i, channel[i].pitchbend);
459 		ctl_channel_note(i, Panel->cnote[i], 0);
460 	}
461 }
462 
463 /***********************************************************************/
464 /* OPEN THE CONNECTION                                                */
465 /***********************************************************************/
466 /*ARGSUSED*/
ctl_open(int using_stdin,int using_stdout)467 static int ctl_open(int using_stdin, int using_stdout)
468 {
469 	shm_alloc();
470 	k_pipe_open();
471 
472 	if (child_pid == 0)
473 		start_panel();
474 
475 	signal(SIGCHLD, get_child);
476 	signal(SIGTERM, shm_free);
477 	signal(SIGINT, shm_free);
478 	signal(SIGHUP, shm_free);
479 
480 	ctl.opened=1;
481 	return 0;
482 }
483 
484 /* Tells the window to disapear */
ctl_close(void)485 static void ctl_close(void)
486 {
487 	if (ctl.opened) {
488 		kill(child_pid, SIGTERM);
489 		shm_free(100);
490 		ctl.opened=0;
491 	}
492 }
493 
494 
495 /*
496  * Read information coming from the window in a BLOCKING way
497  */
498 
499 /* commands are: PREV, NEXT, QUIT, STOP, LOAD, JUMP, VOLM */
500 
ctl_blocking_read(int32 * valp)501 static int ctl_blocking_read(int32 *valp)
502 {
503 	char buf[8192], *tok, *arg;
504 	int new_volume;
505 	int new_centiseconds;
506 	char *args[64], **files;
507 	int i=0, nfiles;
508 
509 	k_pipe_gets(buf, sizeof(buf)-1);
510 	tok = strtok(buf, " ");
511 
512 	for(;;)/* Loop after pause sleeping to treat other buttons! */
513 	{
514 		switch (*tok) {
515 		case 'V':
516 			if ((arg = strtok(NULL, " ")) != NULL) {
517 				new_volume = atoi(arg);
518 				*valp= new_volume - amplification ;
519 				return RC_CHANGE_VOLUME;
520 			}
521 			return RC_NONE;
522 
523 		case 'J':
524 			if ((arg = strtok(NULL, " ")) != NULL) {
525 				new_centiseconds = atoi(arg);
526 				*valp= new_centiseconds*(play_mode->rate / 100) ;
527 				return RC_JUMP;
528 			}
529 			return RC_NONE;
530 
531 		case 'Q':
532 			return RC_QUIT;
533 
534 		case 'L':
535 			return RC_LOAD_FILE;
536 
537 		case 'N':
538 			return RC_NEXT;
539 
540 		case 'P':
541 			/*return RC_REALLY_PREVIOUS;*/
542 			return RC_PREVIOUS;
543 
544 		case 'R':
545 			return RC_RESTART;
546 
547 		case 'F':
548 			*valp=play_mode->rate;
549 			return RC_FORWARD;
550 
551 		case 'B':
552 			*valp=play_mode->rate;
553 			return RC_BACK;
554 
555 		case 'X':
556 			k_pipe_gets(buf, sizeof(buf)-1);
557 			args[i++] = strtok(buf, " ");
558 			while ((args[i++] = strtok(NULL, " ")) != NULL);
559 			nfiles = --i;
560 			files  = expand_file_archives(args, &nfiles);
561 			k_pipe_printf("ALST %d", nfiles);
562 			for (i=0;i<nfiles;i++)
563 				k_pipe_puts(files[i]);
564 			if(files != args)
565 			    free(files);
566 			return RC_NONE;
567 
568 		case 'S':
569 			return RC_TOGGLE_PAUSE;
570 
571 		default:
572 			fprintf(stderr,"UNKNOWN RC_MESSAGE `%s'\n", tok);
573 			return RC_NONE;
574 		}
575 	}
576 
577 }
578 
579 /*
580  * Read information coming from the window in a non blocking way
581  */
ctl_read(int32 * valp)582 static int ctl_read(int32 *valp)
583 {
584 	int num;
585 
586 	if (cuepoint_pending) {
587 		*valp = cuepoint;
588 		cuepoint_pending = 0;
589 		return RC_FORWARD;
590 	}
591 
592 	/* We don't wan't to lock on reading  */
593 	num=k_pipe_read_ready();
594 
595 	if (num==0)
596 		return RC_NONE;
597 
598 	return(ctl_blocking_read(valp));
599 }
600 
ctl_pass_playing_list(int number_of_files,char * list_of_files[])601 static int ctl_pass_playing_list(int number_of_files, char *list_of_files[])
602 {
603 	int i=0;
604 	char local[1000];
605 	int command;
606 	int32 val;
607 
608 	/* Pass the list to the interface */
609 	k_pipe_printf("LIST %d", number_of_files);
610 	for (i=0;i<number_of_files;i++)
611 		k_pipe_puts(list_of_files[i]);
612 
613 	/* Ask the interface for a filename to play -> begin to play automatically */
614 	/*k_pipe_puts("NEXT");*/
615 	command = ctl_blocking_read(&val);
616 
617 	/* Main Loop */
618 	for (;;)
619 	{
620 		if (command==RC_LOAD_FILE)
621 		{
622 			/* Read a LoadFile command */
623 			k_pipe_gets(local, sizeof(local)-1);
624 			command=play_midi_file(local);
625 		}
626 		else
627 		{
628 			if (command==RC_QUIT) {
629 				/* if really QUIT */
630 				k_pipe_gets(local, sizeof(local)-1);
631 				if (*local == 'Z')
632 					return 0;
633 				/* only stop playing..*/
634 			}
635 			if (command==RC_CHANGE_VOLUME) /* init volume */
636 				amplification += val;
637 
638 			switch(command)
639 			{
640 			case RC_ERROR:
641 				k_pipe_puts("ERRR");
642 				break;
643 			case RC_NEXT:
644 				k_pipe_puts("NEXT");
645 				break;
646 			case RC_REALLY_PREVIOUS:
647 				k_pipe_puts("PREV");
648 				break;
649 			case RC_TUNE_END:
650 				k_pipe_puts("TEND");
651 				break;
652 			case RC_RESTART:
653 				k_pipe_puts("RSTA");
654 				break;
655 			}
656 
657 			command = ctl_blocking_read(&val);
658 		}
659 	}
660 	return 0;
661 }
662 
663 
664 /* open pipe and fork child process */
k_pipe_open(void)665 static void k_pipe_open(void)
666 {
667 	int res;
668 
669 	res = pipe(pipeAppli);
670 	if (res!=0) k_pipe_error("PIPE_APPLI CREATION");
671 
672 	res = pipe(pipePanel);
673 	if (res!=0) k_pipe_error("PIPE_PANEL CREATION");
674 
675 	if ((child_pid = fork()) == 0) {
676 		/*child*/
677 		close(pipePanel[1]);
678 		close(pipeAppli[0]);
679 
680 		/* redirect to stdin/out */
681 		dup2(pipePanel[0], fileno(stdin));
682 		close(pipePanel[0]);
683 		dup2(pipeAppli[1], fileno(stdout));
684 		close(pipeAppli[1]);
685 	} else {
686 		close(pipePanel[0]);
687 		close(pipeAppli[1]);
688 
689 		fpip_in= pipeAppli[0];
690 		fpip_out= pipePanel[1];
691 	}
692 }
693 
694 
695 #if defined(sgi)
696 #include <sys/time.h>
697 #endif
698 
699 #if defined(SOLARIS)
700 #include <sys/filio.h>
701 #endif
702 
k_pipe_read_ready(void)703 static int k_pipe_read_ready(void)
704 {
705 #if defined(sgi)
706     fd_set fds;
707     int cnt;
708     struct timeval timeout;
709 
710     FD_ZERO(&fds);
711     FD_SET(fpip_in, &fds);
712     timeout.tv_sec = timeout.tv_usec = 0;
713 
714     if((cnt = select(fpip_in + 1, &fds, NULL, NULL, &timeout)) < 0)
715     {
716 	perror("select");
717 	return -1;
718     }
719 
720     return cnt > 0 && FD_ISSET(fpip_in, &fds) != 0;
721 #else
722     int num;
723 
724     if(ioctl(fpip_in,FIONREAD,&num) < 0) /* see how many chars in buffer. */
725     {
726 	perror("ioctl: FIONREAD");
727 	return -1;
728     }
729     return num;
730 #endif
731 }
732 
733 
734 /***********************************************************************/
735 /* PIPE COMUNICATION                                                   */
736 /***********************************************************************/
737 
k_pipe_error(char * st)738 static void k_pipe_error(char *st)
739 {
740     fprintf(stderr,"CONNECTION PROBLEM WITH TCL/TK PROCESS IN %s BECAUSE:%s\n",
741 	    st,
742 	    strerror(errno));
743     exit(1);
744 }
745 
746 
k_pipe_printf(char * fmt,...)747 static void k_pipe_printf(char *fmt, ...)
748 {
749 	char buf[256];
750 	va_list ap;
751 	va_start(ap, fmt);
752 	vsnprintf(buf, sizeof(buf), fmt, ap);
753 	k_pipe_puts(buf);
754 }
755 
line_strlen(char * str)756 static int line_strlen(char *str)
757 {
758     int len;
759 
760     len = 0;
761     while(*str && *str != '\r' && *str != '\n') {
762 	str++;
763 	len++;
764     }
765     return len;
766 }
767 
k_pipe_puts(char * str)768 static void k_pipe_puts(char *str)
769 {
770 	int len;
771 	char lf = '\n';
772 	len = line_strlen(str);
773 	ssize_t dummy = write(fpip_out, str, len);
774 	dummy = write(fpip_out, &lf, 1);
775 }
776 
777 
k_pipe_gets(char * str,int maxlen)778 int k_pipe_gets(char *str, int maxlen)
779 {
780 /* blocking reading */
781 	char *p;
782 	int len;
783 
784 	/* at least 5 letters (4+\n) command */
785 	len = 0;
786 	for (p = str; len < maxlen - 1; p++) {
787 		ssize_t dummy = read(fpip_in, p, 1);
788 		dummy += 1;
789 		if (*p == '\n')
790 			break;
791 		len++;
792 	}
793 	*p = 0;
794 	return len;
795 }
796 
797 
798 /*----------------------------------------------------------------
799  * shared memory handling
800  *----------------------------------------------------------------*/
801 
802 /*ARGSUSED*/
get_child(int sig)803 static void get_child(int sig)
804 {
805 	child_killed = 1;
806 }
807 
shm_alloc(void)808 static void shm_alloc(void)
809 {
810 	shmid = shmget(IPC_PRIVATE, sizeof(PanelInfo),
811 		       IPC_CREAT|0600);
812 	if (shmid < 0) {
813 		fprintf(stderr, "can't allocate shared memory\n");
814 		exit(1);
815 	}
816 
817 	semid = semget(IPC_PRIVATE, 1, IPC_CREAT|0600);
818 	if (semid < 0) {
819 	    perror("semget");
820 	    shmctl(shmid, IPC_RMID,NULL);
821 	    exit(1);
822 	}
823 
824 	/* bin semaphore: only call once at first */
825 	semaphore_V(semid);
826 
827 	Panel = (PanelInfo *)shmat(shmid, 0, 0);
828 	Panel->reset_panel = 0;
829 	Panel->multi_part = 0;
830 	Panel->wait_reset = 0;
831 }
832 
833 
shm_free(int sig)834 static void shm_free(int sig)
835 {
836 	int status;
837 #if defined(HAVE_UNION_SEMUN)
838 	union semun dmy;
839 #else /* Solaris 2.x, BSDI, OSF/1, HPUX */
840 	void *dmy;
841 #endif
842 
843 	kill(child_pid, SIGTERM);
844 	while(wait(&status) != child_pid)
845 	    ;
846 	memset(&dmy, 0, sizeof(dmy)); /* Shut compiler warning up :-) */
847 	semctl(semid, 0, IPC_RMID, dmy);
848 	shmctl(shmid, IPC_RMID, NULL);
849 	shmdt((char *)Panel);
850 	if (sig != 100)
851 		exit(0);
852 }
853 
854 /*----------------------------------------------------------------
855  * start Tk window panel
856  *----------------------------------------------------------------*/
857 
start_panel(void)858 static void start_panel(void)
859 {
860 	char *argv[128];
861 	int argc;
862 
863 	argc = 0;
864 	argv[argc++] = "-f";
865 	argv[argc++] = TKPROGPATH;
866 
867 	if (ctl.trace_playing) {
868 		argv[argc++] = "-mode";
869 		argv[argc++] = "trace";
870 	}
871 
872 	/* call Tk main routine */
873 	Tk_Main(argc, argv, AppInit);
874 
875 	exit(0);
876 }
877 
878 
879 /*----------------------------------------------------------------
880  * initialize Tcl application
881  *----------------------------------------------------------------*/
882 
883 static Tcl_Interp *my_interp;
884 
AppInit(Tcl_Interp * interp)885 static int AppInit(Tcl_Interp *interp)
886 {
887 #include "bitmaps/back.xbm"
888 #include "bitmaps/fwrd.xbm"
889 #include "bitmaps/next.xbm"
890 #include "bitmaps/pause.xbm"
891 #include "bitmaps/play.xbm"
892 #include "bitmaps/prev.xbm"
893 #include "bitmaps/quit.xbm"
894 #include "bitmaps/stop.xbm"
895 #include "bitmaps/timidity.xbm"
896 
897 #define DefineBitmap(Bitmap) do { \
898 	Tk_DefineBitmap (interp, Tk_GetUid(#Bitmap), Bitmap##_bits, \
899 			 Bitmap##_width, Bitmap##_height); \
900 	} while(0)
901 
902 	my_interp = interp;
903 
904 	if (Tcl_Init(interp) == TCL_ERROR) {
905 		return TCL_ERROR;
906 	}
907 	if (Tk_Init(interp) == TCL_ERROR) {
908 		return TCL_ERROR;
909 	}
910 
911 	Tcl_CreateCommand(interp, "TraceCreate", (Tcl_CmdProc*) TraceCreate,
912 			  (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
913 	Tcl_CreateCommand(interp, "TraceUpdate", (Tcl_CmdProc*) TraceUpdate,
914 			  (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
915 	Tcl_CreateCommand(interp, "TraceReset", (Tcl_CmdProc*) TraceReset,
916 			  (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
917 	Tcl_CreateCommand(interp, "ExitAll", (Tcl_CmdProc*) ExitAll,
918 			  (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
919 	Tcl_CreateCommand(interp, "TraceUpdate", (Tcl_CmdProc*) TraceUpdate,
920 			  (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
921 
922 	DefineBitmap(back);
923 	DefineBitmap(fwrd);
924 	DefineBitmap(next);
925 	DefineBitmap(pause);
926 	DefineBitmap(play);
927 	DefineBitmap(prev);
928 	DefineBitmap(quit);
929 	DefineBitmap(stop);
930 	DefineBitmap(timidity);
931 
932 	return TCL_OK;
933 #undef DefineBitmap
934 }
935 
936 /*ARGSUSED*/
ExitAll(ClientData clientData,Tcl_Interp * interp,int argc,char * argv[])937 static int ExitAll(ClientData clientData, Tcl_Interp *interp,
938 		   int argc, char *argv[])
939 {
940 	/* window is killed; kill the parent process, too */
941 	kill(getppid(), SIGTERM);
942 	for (;;)
943 		sleep(1000);
944 	return TCL_OK;
945 }
946 
947 /* evaluate Tcl script */
v_eval(char * fmt,...)948 static const char *v_eval(char *fmt, ...)
949 {
950 	char buf[256];
951 	va_list ap;
952 	va_start(ap, fmt);
953 	vsnprintf(buf, sizeof(buf), fmt, ap);
954 	Tcl_Eval(my_interp, buf);
955 	va_end(ap);
956 	return Tcl_GetStringResult(my_interp);
957 }
958 
v_get2(const char * v1,const char * v2)959 static const char *v_get2(const char *v1, const char *v2)
960 {
961 	return Tcl_GetVar2(my_interp, v1, v2, 0);
962 }
963 
964 
965 /*----------------------------------------------------------------
966  * update Tcl timer / trace window
967  *----------------------------------------------------------------*/
968 
969 #define FRAME_WIN	".body.trace"
970 #define CANVAS_WIN	FRAME_WIN ".c"
971 
972 #define BAR_WID 20
973 #define BAR_HGT 130
974 #define WIN_WID (BAR_WID * 16)
975 #define WIN_HGT (BAR_HGT + 11 + 17)
976 #define BAR_HALF_HGT (WIN_HGT / 2 - 11 - 17)
977 
978 
979 /*ARGSUSED*/
TraceCreate(ClientData clientData,Tcl_Interp * interp,int argc,char * argv[])980 static int TraceCreate(ClientData clientData, Tcl_Interp *interp,
981 		       int argc, char *argv[])
982 {
983 	int i;
984 	v_eval("frame %s -bg black", FRAME_WIN);
985 	v_eval("canvas %s -width %d -height %d -bd 0 -bg black "
986 	       "-highlightthickness 0",
987 	       CANVAS_WIN, WIN_WID, WIN_HGT);
988 	v_eval("pack %s -side top -fill x", CANVAS_WIN);
989 	for (i = 0; i < 32; i++) {
990 		char *color;
991 		v_eval("%s create text 0 0 -anchor n -fill white -text 00 "
992 		       "-tags prog%d", CANVAS_WIN, i);
993 		v_eval("%s create poly 0 0 0 0 0 0 -fill yellow -tags pos%d",
994 		       CANVAS_WIN, i);
995 		color = (ISDRUMCHANNEL(i) || i == 25) ? "red" : "green";
996 		v_eval("%s create rect 0 0 0 0 -fill %s -tags bar%d "
997 		       "-outline \"\"", CANVAS_WIN, color, i);
998 	}
999 	v_eval("set Stat(TimerId) -1");
1000 	v_eval("TraceReset");
1001 	return TCL_OK;
1002 }
1003 
trace_bank(int ch,int val)1004 static void trace_bank(int ch, int val)
1005 {
1006 	v_eval("%s itemconfigure bar%d -fill %s",
1007 	       CANVAS_WIN, ch,
1008 	       (val == 128 ? "red" : "green"));
1009 }
1010 
trace_prog(int ch,int val)1011 static void trace_prog(int ch, int val)
1012 {
1013 	v_eval("%s itemconfigure prog%d -text %02X",
1014 	       CANVAS_WIN, ch, val);
1015 }
1016 
trace_sustain(int ch,int val)1017 static void trace_sustain(int ch, int val)
1018 {
1019 	v_eval("%s itemconfigure prog%d -fill %s",
1020 	       CANVAS_WIN, ch,
1021 	       (val == 127 ? "green" : "white"));
1022 }
1023 
trace_prog_init(int ch)1024 static void trace_prog_init(int ch)
1025 {
1026 	int item, yofs, bar, x, y;
1027 
1028 	item = ch;
1029 	yofs = 0;
1030 	bar = Panel->multi_part ? BAR_HALF_HGT : BAR_HGT;
1031 	if (ch >= 16) {
1032 		ch -= 16;
1033 		yofs = WIN_HGT / 2;
1034 		if (!Panel->multi_part)
1035 			yofs = -500;
1036 	}
1037 	x = ch * BAR_WID + BAR_WID/2;
1038 	y = bar + 11 + yofs;
1039 	v_eval("%s coords prog%d %d %d", CANVAS_WIN, item, x, y);
1040 }
1041 
trace_volume(int ch,int val)1042 static void trace_volume(int ch, int val)
1043 {
1044 	int item, bar, yofs, x1, y1, x2, y2;
1045 	item = ch;
1046 	yofs = 0;
1047 	bar = Panel->multi_part ? BAR_HALF_HGT : BAR_HGT;
1048 	if (ch >= 16) {
1049 		yofs = WIN_HGT / 2;
1050 		ch -= 16;
1051 		if (!Panel->multi_part)
1052 			yofs = -500;
1053 	}
1054 	x1 = ch * BAR_WID;
1055 	y1 = bar - 1 + yofs;
1056 	x2 = x1 + BAR_WID - 1;
1057 	y2 = y1 - bar * val / 127;
1058 	v_eval("%s coords bar%d %d %d %d %d", CANVAS_WIN,
1059 	       item, x1, y1, x2, y2);
1060 }
1061 
trace_panning(int ch,int val)1062 static void trace_panning(int ch, int val)
1063 {
1064 	int item, bar, yofs;
1065 	int x, ap, bp;
1066 
1067 	if (val < 0) {
1068 		v_eval("%s coords pos%d -1 0 -1 0 -1 0", CANVAS_WIN, ch);
1069 		return;
1070 	}
1071 
1072 	item = ch;
1073 	yofs = 0;
1074 	bar = Panel->multi_part ? BAR_HALF_HGT : BAR_HGT;
1075 	if (ch >= 16) {
1076 		yofs = WIN_HGT / 2;
1077 		ch -= 16;
1078 		if (!Panel->multi_part)
1079 			yofs = -500;
1080 	}
1081 
1082 	x = BAR_WID * ch;
1083 	ap = BAR_WID * val / 127;
1084 	bp = BAR_WID - ap - 1;
1085 	v_eval("%s coords pos%d %d %d %d %d %d %d", CANVAS_WIN, item,
1086 	       ap + x, bar + 5 + yofs,
1087 	       bp + x, bar + 1 + yofs,
1088 	       bp + x, bar + 9 + yofs);
1089 }
1090 
1091 /*ARGSUSED*/
TraceReset(ClientData clientData,Tcl_Interp * interp,int argc,char * argv[])1092 static int TraceReset(ClientData clientData, Tcl_Interp *interp,
1093 			   int argc, char *argv[])
1094 {
1095 	int i;
1096 
1097 	semaphore_P(semid);
1098 	for (i = 0; i < 32; i++) {
1099 		trace_volume(i, 0);
1100 		trace_panning(i, -1);
1101 		trace_prog_init(i);
1102 		trace_prog(i, 0);
1103 		trace_sustain(i, 0);
1104 		Panel->ctotal[i] = 0;
1105 		Panel->cvel[i] = 0;
1106 		Panel->v_flags[i] = 0;
1107 		Panel->c_flags[i] = 0;
1108 	}
1109 	semaphore_V(semid);
1110 	Panel->wait_reset = 0;
1111 	return TCL_OK;
1112 }
1113 
1114 
1115 
1116 #define DELTA_VEL	32
1117 
update_notes(void)1118 static void update_notes(void)
1119 {
1120 	int i, imax;
1121 	semaphore_P(semid);
1122 	imax = Panel->multi_part ? 32 : 16;
1123 	for (i = 0; i < imax; i++) {
1124 		if (Panel->v_flags[i]) {
1125 			if (Panel->v_flags[i] == FLAG_NOTE_OFF) {
1126 				Panel->ctotal[i] -= DELTA_VEL;
1127 				if (Panel->ctotal[i] <= 0) {
1128 					Panel->ctotal[i] = 0;
1129 					Panel->v_flags[i] = 0;
1130 				}
1131 			} else {
1132 				Panel->v_flags[i] = 0;
1133 			}
1134 			trace_volume(i, Panel->ctotal[i]);
1135 		}
1136 
1137 		if (Panel->c_flags[i]) {
1138 			if (Panel->c_flags[i] & FLAG_PAN)
1139 				trace_panning(i, Panel->channel[i].panning);
1140 			if (Panel->c_flags[i] & FLAG_BANK)
1141 				trace_bank(i, Panel->channel[i].bank);
1142 			if (Panel->c_flags[i] & FLAG_PROG)
1143 				trace_prog(i, Panel->channel[i].program);
1144 			if (Panel->c_flags[i] & FLAG_SUST)
1145 				trace_sustain(i, Panel->channel[i].sustain);
1146 			Panel->c_flags[i] = 0;
1147 		}
1148 	}
1149 	semaphore_V(semid);
1150 }
1151 
1152 /*ARGSUSED*/
TraceUpdate(ClientData clientData,Tcl_Interp * interp,int argc,char * argv[])1153 static int TraceUpdate(ClientData clientData, Tcl_Interp *interp,
1154 		    int argc, char *argv[])
1155 {
1156 	const char *playing = v_get2("Stat", "Playing");
1157 	if (playing && *playing != '0') {
1158 		if (Panel->reset_panel) {
1159 			v_eval("TraceReset");
1160 			Panel->reset_panel = 0;
1161 		}
1162 		if (Panel->last_time != Panel->cur_time) {
1163 			v_eval("SetTime %d", Panel->cur_time);
1164 			Panel->last_time = Panel->cur_time;
1165 		}
1166 		if (ctl.trace_playing)
1167 			update_notes();
1168 	}
1169 	v_eval("set Stat(TimerId) [after 50 TraceUpdate]");
1170 	return TCL_OK;
1171 }
1172 
ctl_event(CtlEvent * e)1173 static void ctl_event(CtlEvent *e)
1174 {
1175     switch(e->type)
1176     {
1177       case CTLE_NOW_LOADING:
1178 	ctl_file_name((char *)e->v1);
1179 	break;
1180       case CTLE_LOADING_DONE:
1181 	break;
1182       case CTLE_PLAY_START:
1183 	ctl_total_time((int)e->v1);
1184 	break;
1185       case CTLE_PLAY_END:
1186 	break;
1187 	case CTLE_CUEPOINT:
1188 		cuepoint = e->v1;
1189 		cuepoint_pending = 1;
1190 		break;
1191       case CTLE_TEMPO:
1192 	break;
1193       case CTLE_METRONOME:
1194 	break;
1195       case CTLE_CURRENT_TIME:
1196 	ctl_current_time((int)e->v1, (int)e->v2);
1197 	break;
1198       case CTLE_NOTE:
1199 	ctl_note((int)e->v1, (int)e->v2, (int)e->v3, (int)e->v4);
1200 	break;
1201       case CTLE_MASTER_VOLUME:
1202 	ctl_master_volume((int)e->v1);
1203 	break;
1204       case CTLE_PROGRAM:
1205 	ctl_program((int)e->v1, (int)e->v2);
1206 	break;
1207       case CTLE_VOLUME:
1208 	ctl_volume((int)e->v1, (int)e->v2);
1209 	break;
1210       case CTLE_EXPRESSION:
1211 	ctl_expression((int)e->v1, (int)e->v2);
1212 	break;
1213       case CTLE_PANNING:
1214 	ctl_panning((int)e->v1, (int)e->v2);
1215 	break;
1216       case CTLE_SUSTAIN:
1217 	ctl_sustain((int)e->v1, (int)e->v2);
1218 	break;
1219       case CTLE_PITCH_BEND:
1220 	ctl_pitch_bend((int)e->v1, (int)e->v2);
1221 	break;
1222       case CTLE_MOD_WHEEL:
1223 	ctl_pitch_bend((int)e->v1, e->v2 ? -1 : 0x2000);
1224 	break;
1225       case CTLE_CHORUS_EFFECT:
1226 	break;
1227       case CTLE_REVERB_EFFECT:
1228 	break;
1229       case CTLE_LYRIC:
1230 	ctl_lyric((int)e->v1);
1231 	break;
1232       case CTLE_REFRESH:
1233 	ctl_refresh();
1234 	break;
1235       case CTLE_RESET:
1236 	ctl_reset();
1237 	break;
1238     }
1239 }
1240 
1241 /*
1242  * interface_<id>_loader();
1243  */
interface_k_loader(void)1244 ControlMode *interface_k_loader(void)
1245 {
1246     return &ctl;
1247 }
1248