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     emacs_c.c
21     Emacs control mode - written by Masanao Izumo <mo@goice.co.jp>
22 */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif /* HAVE_CONFIG_H */
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdarg.h>
30 #include <sys/types.h>
31 #include <sys/ioctl.h>
32 #ifndef NO_STRING_H
33 #include <string.h>
34 #else
35 #include <strings.h>
36 #endif
37 
38 #include "timidity.h"
39 #include "common.h"
40 #include "output.h"
41 #include "controls.h"
42 #include "instrum.h"
43 #include "playmidi.h"
44 #include "miditrace.h"
45 
46 /*
47  * miditrace functions
48  *
49  * ctl_current_time
50  * ctl_note
51  * ctl_program
52  * ctl_volume
53  * ctl_expression
54  * ctl_panning
55  * ctl_sustain
56  * ctl_pitch_bend
57  */
58 
59 /*
60  * commands:
61  * LIST CMSG TIME MVOL DRUMS FILE CURT NOTE PROG VOL EXP PAN SUS PIT RESET
62  */
63 
64 static void ctl_refresh(void);
65 static void ctl_total_time(int tt);
66 static void ctl_master_volume(int mv);
67 static void ctl_file_name(char *name);
68 static void ctl_current_time(int ct, int nv);
69 static void ctl_note(int status, int ch, int note, int vel);
70 static void ctl_program(int ch, int val);
71 static void ctl_volume(int channel, int val);
72 static void ctl_expression(int channel, int val);
73 static void ctl_panning(int channel, int val);
74 static void ctl_sustain(int channel, int val);
75 static void ctl_pitch_bend(int channel, int val);
76 static void ctl_reset(void);
77 static int ctl_open(int using_stdin, int using_stdout);
78 static void ctl_close(void);
79 static int ctl_read(int32 *valp);
80 static int cmsg(int type, int verbosity_level, char *fmt, ...);
81 static int ctl_pass_playing_list(int number_of_files, char *list_of_files[]);
82 static void ctl_event(CtlEvent *e);
83 static int read_ready(void);
84 static int emacs_type = 0; /* 0:emacs, 1:mule, 2:??
85 			      Note that this variable not used yet.
86 			      */
87 enum emacs_type_t
88 {
89     ETYPE_OF_EMACS,
90     ETYPE_OF_MULE,
91     ETYPE_OF_OTHER
92 };
93 
94 /**********************************/
95 /* export the interface functions */
96 
97 #define ctl emacs_control_mode
98 
99 ControlMode ctl=
100 {
101     "Emacs interface (invoked from `M-x timidity')", 'e',
102     "emacs",
103     1, 0, 0,
104     0,
105     ctl_open,
106     ctl_close,
107     ctl_pass_playing_list,
108     ctl_read,
109     NULL,
110     cmsg,
111     ctl_event
112 };
113 
114 static uint32 cuepoint = 0;
115 static int cuepoint_pending = 0;
116 
117 static FILE *outfp;
118 
quote_string_out(char * str)119 static void quote_string_out(char *str)
120 {
121     char *s;
122 
123     s = NULL;
124     if(emacs_type == ETYPE_OF_MULE)
125     {
126 	int len;
127 
128 	len = SAFE_CONVERT_LENGTH(strlen(str));
129 	s = (char *)new_segment(&tmpbuffer, len);
130 	code_convert(str, s, len, NULL, "EUC");
131 	str = s;
132     }
133 
134     while(*str)
135     {
136 	if(*str == '\\' || *str == '\"')
137 	    putc('\\', outfp);
138 	putc(*str, outfp);
139 	str++;
140     }
141 
142     if(s != NULL)
143 	reuse_mblock(&tmpbuffer);
144 }
145 
146 /*ARGSUSED*/
ctl_open(int using_stdin,int using_stdout)147 static int ctl_open(int using_stdin, int using_stdout)
148 {
149     if(using_stdout)
150 	outfp = stderr;
151     else
152 	outfp = stdout;
153     ctl.opened = 1;
154     output_text_code = "NOCNV";
155     fprintf(outfp, "(timidity-VERSION \"");
156     quote_string_out(timidity_version);
157     fprintf(outfp, "\")\n");
158     ctl_refresh();
159     return 0;
160 }
161 
ctl_close(void)162 static void ctl_close(void)
163 {
164     fflush(outfp);
165     ctl.opened = 0;
166 }
167 
ctl_read(int32 * valp)168 static int ctl_read(int32 *valp)
169 {
170     char cmd[BUFSIZ];
171     int n;
172 
173 	if (cuepoint_pending) {
174 		*valp = cuepoint;
175 		cuepoint_pending = 0;
176 		return RC_FORWARD;
177 	}
178     if(read_ready() <= 0)
179 	return RC_NONE;
180     if(fgets(cmd, sizeof(cmd), stdin) == NULL)
181 	return RC_QUIT; /* Emacs may down */
182     n = atoi(cmd + 1);
183     switch(cmd[0])
184     {
185       case 'L':
186 	return RC_LOAD_FILE;
187       case 'V':
188 	*valp = 10 * n;
189 	return RC_CHANGE_VOLUME;
190       case 'v':
191 	*valp = -10 * n;
192 	return RC_CHANGE_VOLUME;
193       case '1':
194       case '2':
195       case '3':
196 	*valp = cmd[0] - '2';
197 	return RC_CHANGE_REV_EFFB;
198       case '4':
199       case '5':
200       case '6':
201 	*valp = cmd[0] - '5';
202 	return RC_CHANGE_REV_TIME;
203       case 'Q':
204 	return RC_QUIT;
205       case 'r':
206 	return RC_RESTART;
207       case 'f':
208 	*valp = play_mode->rate * n;
209 	return RC_FORWARD;
210       case 'b':
211 	*valp = play_mode->rate * n;
212 	return RC_BACK;
213       case ' ':
214 	return RC_TOGGLE_PAUSE;
215       case '+':
216 	*valp = n;
217 	return RC_KEYUP;
218       case '-':
219 	*valp = -n;
220 	return RC_KEYDOWN;
221       case '>':
222 	*valp = n;
223 	return RC_SPEEDUP;
224       case '<':
225 	*valp = n;
226 	return RC_SPEEDDOWN;
227       case 'O':
228 	*valp = n;
229 	return RC_VOICEINCR;
230       case 'o':
231 	*valp = n;
232 	return RC_VOICEDECR;
233       case 'd':
234 	*valp = n;
235 	return RC_TOGGLE_DRUMCHAN;
236       case 'g':
237 	return RC_TOGGLE_SNDSPEC;
238     }
239 
240     return RC_NONE;
241 }
242 
chomp(char * s)243 static char *chomp(char *s)
244 {
245     int len = strlen(s);
246 
247     if(len < 2)
248     {
249 	if(len == 0)
250 	    return s;
251 	if(s[0] == '\n' || s[0] == '\r')
252 	    s[0] = '\0';
253 	return s;
254     }
255     if(s[len - 1] == '\n')
256 	s[--len] = '\0';
257     if(s[len - 1] == '\r')
258 	s[--len] = '\0';
259     return s;
260 }
261 
ctl_pass_playing_list(int argc,char * argv[])262 static int ctl_pass_playing_list(int argc, char *argv[])
263 {
264     int i;
265     char cmd[BUFSIZ];
266 
267     if(argc > 0)
268     {
269 	if(!strcmp(argv[0], "emacs"))
270 	{
271 	    emacs_type = ETYPE_OF_EMACS;
272 	    argc--; argv++;
273 	}
274 	else if(!strcmp(argv[0], "mule"))
275 	{
276 	    emacs_type = ETYPE_OF_MULE;
277 	    argc--; argv++;
278 	}
279 	else
280 	    emacs_type = ETYPE_OF_OTHER;
281     }
282 
283     if(argc > 0 && !strcmp(argv[0], "debug"))
284     {
285 	for(i = 1; i < argc; i++)
286 	    play_midi_file(argv[i]);
287 	return 0;
288     }
289 
290     /* Main Loop */
291     for(;;)
292     {
293 	int rc;
294 
295 	if(fgets(cmd, sizeof(cmd), stdin) == NULL)
296 	    return 0; /* Emacs may down */
297 	chomp(cmd);
298 	if(!strncmp(cmd, "PLAY", 4))
299 	{
300 	    rc = play_midi_file(cmd + 5);
301 	    switch(rc)
302 	    {
303 	      case RC_TUNE_END:
304 	      case RC_NEXT:
305 		fprintf(outfp, "(timidity-NEXT)\n");
306 		ctl_refresh();
307 		break;
308 	      case RC_QUIT:
309 		return 0;
310 	    } /* skipping others command */
311 	}
312 	else if(!strncmp(cmd, "QUIT", 4))
313 	    return 0;
314 	else
315 	    continue; /* skipping unknown command */
316     }
317     /*NOTREACHED*/
318 }
319 
cmsg(int type,int verbosity_level,char * fmt,...)320 static int cmsg(int type, int verbosity_level, char *fmt, ...)
321 {
322     va_list ap;
323     char buff[BUFSIZ];
324 
325     if((type==CMSG_TEXT || type==CMSG_INFO || type==CMSG_WARNING) &&
326        ctl.verbosity < verbosity_level)
327 	return 0;
328     va_start(ap, fmt);
329 
330     vsprintf(buff, fmt, ap);
331     fprintf(outfp, "(timidity-CMSG %d \"", type);
332     quote_string_out(buff);
333     fprintf(outfp, "\")\n");
334     va_end(ap);
335     ctl_refresh();
336     return 0;
337 }
338 
ctl_refresh(void)339 static void ctl_refresh(void)
340 {
341     fflush(stdout);
342 }
343 
ctl_total_time(int tt)344 static void ctl_total_time(int tt)
345 {
346     int secs;
347     secs = tt/play_mode->rate;
348     fprintf(outfp, "(timidity-TIME %d)\n", secs);
349     ctl_refresh();
350 }
351 
ctl_master_volume(int mv)352 static void ctl_master_volume(int mv)
353 {
354     fprintf(outfp, "(timidity-MVOL %d)\n", mv);
355     ctl_refresh();
356 }
357 
ctl_file_name(char * name)358 static void ctl_file_name(char *name)
359 {
360     fprintf(outfp, "(timidity-FILE \"");
361     quote_string_out(name);
362     fprintf(outfp, "\")\n");
363     ctl_refresh();
364 }
365 
ctl_current_time(int secs,int v)366 static void ctl_current_time(int secs, int v)
367 {
368     fprintf(outfp, "(timidity-CURT %d %d)\n", secs, v);
369     ctl_refresh();
370 }
371 
status_number(int s)372 static int status_number(int s)
373 {
374     switch(s)
375     {
376       case VOICE_FREE:
377 	return 0;
378       case VOICE_ON:
379 	return 1;
380       case VOICE_SUSTAINED:
381 	return 2;
382       case VOICE_OFF:
383 	return 3;
384       case VOICE_DIE:
385 	return 4;
386     }
387     /* dmy */
388     return 3;
389 }
390 
ctl_note(int status,int ch,int note,int vel)391 static void ctl_note(int status, int ch, int note, int vel)
392 {
393     if(ch >= 16)
394 	return;
395     if(midi_trace.flush_flag)
396 	return;
397     fprintf(outfp, "(timidity-NOTE %d %d %d)\n", ch, note,
398 	    status_number(status));
399     ctl_refresh();
400 }
401 
ctl_program(int ch,int val)402 static void ctl_program(int ch, int val)
403 {
404     if(ch >= 16)
405 	return;
406     if(midi_trace.flush_flag)
407 	return;
408     if(channel[ch].special_sample)
409 	val = channel[ch].special_sample;
410     else
411 	val += progbase;
412     fprintf(outfp, "(timidity-PROG %d %d)\n", ch, val);
413     ctl_refresh();
414 }
415 
ctl_volume(int ch,int val)416 static void ctl_volume(int ch, int val)
417 {
418     if(ch >= 16)
419 	return;
420     if(midi_trace.flush_flag)
421 	return;
422     fprintf(outfp, "(timidity-VOL %d %d)\n", ch, (val*100)/127);
423     ctl_refresh();
424 }
425 
ctl_expression(int ch,int val)426 static void ctl_expression(int ch, int val)
427 {
428     if(ch >= 16)
429 	return;
430     if(midi_trace.flush_flag)
431 	return;
432     fprintf(outfp, "(timidity-EXP %d %d)\n", ch, (val*100)/127);
433     ctl_refresh();
434 }
435 
ctl_panning(int ch,int val)436 static void ctl_panning(int ch, int val)
437 {
438     if(ch >= 16)
439 	return;
440     if(midi_trace.flush_flag)
441 	return;
442     fprintf(outfp, "(timidity-PAN %d %d)\n", ch, val);
443     ctl_refresh();
444 }
445 
ctl_sustain(int ch,int val)446 static void ctl_sustain(int ch, int val)
447 {
448     if(ch >= 16)
449 	return;
450     if(midi_trace.flush_flag)
451 	return;
452     fprintf(outfp, "(timidity-SUS %d %d)\n", ch, val);
453     ctl_refresh();
454 }
455 
ctl_pitch_bend(int ch,int val)456 static void ctl_pitch_bend(int ch, int val)
457 {
458     if(ch >= 16)
459 	return;
460     if(midi_trace.flush_flag)
461 	return;
462     fprintf(outfp, "(timidity-PIT %d %d)\n", ch, val);
463     ctl_refresh();
464 }
465 
ctl_reset(void)466 static void ctl_reset(void)
467 {
468     int i;
469     uint32 drums;
470 
471     /* Note that Emacs is 24 bit integer. */
472     drums = 0;
473     for(i = 0; i < 16; i++)
474 	if(ISDRUMCHANNEL(i))
475 	    drums |= (1u << i);
476     fprintf(outfp, "(timidity-DRUMS %lu)\n", (unsigned long)drums);
477 
478     fprintf(outfp, "(timidity-RESET)\n");
479     for(i = 0; i < 16; i++)
480     {
481 	if(ISDRUMCHANNEL(i))
482 	    ctl_program(i, channel[i].bank);
483 	else
484 	    ctl_program(i, channel[i].program);
485 	ctl_volume(i, channel[i].volume);
486 	ctl_expression(i, channel[i].expression);
487 	ctl_panning(i, channel[i].panning);
488 	ctl_sustain(i, channel[i].sustain);
489 	ctl_pitch_bend(i, channel[i].pitchbend);
490     }
491     ctl_refresh();
492 }
493 
494 
495 #if defined(sgi)
496 #include <sys/time.h>
497 #include <bstring.h>
498 #endif
499 
500 #if defined(SOLARIS) || defined(__FreeBSD__)
501 #include <unistd.h>
502 #include <sys/filio.h>
503 #endif
504 
read_ready(void)505 static int read_ready(void)
506 {
507 #if defined(sgi)
508     fd_set fds;
509     int cnt;
510     struct timeval timeout;
511     int fd;
512 
513     fd = fileno(stdin);
514 
515     FD_ZERO(&fds);
516     FD_SET(fd, &fds);
517     timeout.tv_sec = timeout.tv_usec = 0;
518 
519     if((cnt = select(fd + 1, &fds, NULL, NULL, &timeout)) < 0)
520     {
521 	fprintf(outfp, "(error \"select system call is failed\")\n");
522 	ctl_refresh();
523 	return -1;
524     }
525 
526     return cnt > 0 && FD_ISSET(fd, &fds) != 0;
527 #else
528     int num;
529     int fd;
530 
531     fd = fileno(stdin);
532     if(ioctl(fd, FIONREAD, &num) < 0) /* see how many chars in buffer. */
533     {
534 	fprintf(outfp, "(error \"ioctl system call is failed\")\n");
535 	ctl_refresh();
536 	return -1;
537     }
538     return num;
539 #endif
540 }
541 
ctl_event(CtlEvent * e)542 static void ctl_event(CtlEvent *e)
543 {
544     switch(e->type)
545     {
546       case CTLE_NOW_LOADING:
547 	ctl_file_name((char *)e->v1);
548 	break;
549       case CTLE_LOADING_DONE:
550 	break;
551       case CTLE_PLAY_START:
552 	ctl_total_time((int)e->v1);
553 	break;
554       case CTLE_PLAY_END:
555 	break;
556 	case CTLE_CUEPOINT:
557 		cuepoint = e->v1;
558 		cuepoint_pending = 1;
559 		break;
560       case CTLE_TEMPO:
561 	break;
562       case CTLE_METRONOME:
563 	break;
564       case CTLE_CURRENT_TIME:
565 	ctl_current_time((int)e->v1, (int)e->v2);
566 	break;
567       case CTLE_NOTE:
568 	ctl_note((int)e->v1, (int)e->v2, (int)e->v3, (int)e->v4);
569 	break;
570       case CTLE_MASTER_VOLUME:
571 	ctl_master_volume((int)e->v1);
572 	break;
573       case CTLE_PROGRAM:
574 	ctl_program((int)e->v1, (int)e->v2);
575 	break;
576       case CTLE_VOLUME:
577 	ctl_volume((int)e->v1, (int)e->v2);
578 	break;
579       case CTLE_EXPRESSION:
580 	ctl_expression((int)e->v1, (int)e->v2);
581 	break;
582       case CTLE_PANNING:
583 	ctl_panning((int)e->v1, (int)e->v2);
584 	break;
585       case CTLE_SUSTAIN:
586 	ctl_sustain((int)e->v1, (int)e->v2);
587 	break;
588       case CTLE_PITCH_BEND:
589 	ctl_pitch_bend((int)e->v1, (int)e->v2);
590 	break;
591       case CTLE_MOD_WHEEL:
592 	ctl_pitch_bend((int)e->v1, e->v2 ? -1 : 0x2000);
593 	break;
594       case CTLE_CHORUS_EFFECT:
595 	break;
596       case CTLE_REVERB_EFFECT:
597 	break;
598       case CTLE_LYRIC:
599 	default_ctl_lyric((int)e->v1);
600 	break;
601       case CTLE_REFRESH:
602 	ctl_refresh();
603 	break;
604       case CTLE_RESET:
605 	ctl_reset();
606 	break;
607     }
608 }
609 
610 /*
611  * interface_<id>_loader();
612  */
interface_e_loader(void)613 ControlMode *interface_e_loader(void)
614 {
615     return &ctl;
616 }
617