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