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     xskin interface by Daisuke nagano <breeze_geo@geocities.co.jp>
21 */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif /* HAVE_CONFIG_H */
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <math.h>
30 #include <sys/time.h>
31 #ifndef NO_STRING_H
32 #include <string.h>
33 #else
34 #include <strings.h>
35 #endif
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif /* HAVE_UNISTD_H */
39 
40 #include "timidity.h"
41 #include "common.h"
42 #include "instrum.h"
43 #include "playmidi.h"
44 #include "readmidi.h"
45 #include "output.h"
46 #include "controls.h"
47 #include "miditrace.h"
48 #include "xskin.h"
49 #define MSGWINDOW
50 static void ctl_total_time(int tt);
51 static void ctl_master_volume(int mv);
52 static void ctl_current_time(int secs, int v);
53 static void ctl_lyric(int lyricid);
54 static int ctl_open(int using_stdin, int using_stdout);
55 static void ctl_close(void);
56 static int ctl_read(int32 *valp);
57 static int cmsg(int type, int verbosity_level, char *fmt, ...);
58 static int ctl_pass_playing_list(int number_of_files, char *list_of_files[]);
59 static void ctl_event(CtlEvent *e);
60 static void initialize_exp_hz_table( void );
61 
62 static void xskin_pipe_open(void);
63 void xskin_pipe_write(char *);
64 static int xskin_pipe_ready(void);
65 int xskin_pipe_read(char *,int);
66 
67 static int isspeanaenabled;
68 static unsigned char *speana_buf;
69 static double exp_hz_table[SPE_W+1];
70 static int xskin_ready = 0;
71 
72 #define FFTSIZE 1024 /* same as "soudspec.c" */
73 #define NCOLOR  64   /* same as "soudspec.c" */
74 #define DEFAULT_ZOOM (44100.0/1024.0*4.0) /* tekito---- */
75 
76 #define CTL_LAST_STATUS -1
77 
78 /**********************************************/
79 /* export the interface functions */
80 
81 #define ctl xskin_control_mode
82 
83 ControlMode ctl=
84 {
85     "skin interface", 'i',
86     "skin",
87     1,0,0,
88     0,
89     ctl_open,
90     ctl_close,
91     ctl_pass_playing_list,
92     ctl_read,
93     NULL,
94     cmsg,
95     ctl_event
96 };
97 
98 static uint32 cuepoint = 0;
99 static int cuepoint_pending = 0;
100 
101 static char local_buf[300];
102 static int pipe_in_fd,pipe_out_fd=-1;
103 
104 /***********************************************************************/
105 /* Put controls on the pipe                                            */
106 /***********************************************************************/
cmsg(int type,int verbosity_level,char * fmt,...)107 static int cmsg(int type, int verbosity_level, char *fmt, ...) {
108 
109   va_list ap;
110   if ((type==CMSG_TEXT || type==CMSG_INFO || type==CMSG_WARNING) &&
111       ctl.verbosity<verbosity_level)
112     return 0;
113 
114   va_start(ap, fmt);
115 
116 #ifdef MSGWINDOW
117   if(!xskin_ready)
118 #endif
119   {
120     vfprintf(stderr, fmt, ap);
121     fprintf(stderr, NLS);
122     va_end(ap);
123     return 0;
124   }
125 
126   vsnprintf(local_buf+2,100,fmt,ap);
127   if(pipe_out_fd==-1) {
128       fputs(local_buf + 2, stderr);
129       fputs(NLS, stderr);
130   } else {
131     local_buf[0]='L';
132     local_buf[1]=' ';
133     xskin_pipe_write(local_buf);
134   }
135   va_end(ap);
136   return 0;
137 }
138 
139 /*ARGSUSED*/
ctl_total_time(int tt)140 static void ctl_total_time(int tt) {
141 
142   static int previous_total_time=-1;
143   int sec,min;
144 
145   sec=tt/play_mode->rate;
146   min = sec/60;
147   sec-= min*60;
148 
149   if ( tt!=previous_total_time ) {
150     previous_total_time=tt;
151     sprintf(local_buf,"A %d",min*60+sec);
152     xskin_pipe_write(local_buf);
153   }
154 
155   return;
156 }
157 
158 /*ARGSUSED*/
ctl_master_volume(int mv)159 static void ctl_master_volume(int mv) {
160 
161   static int lastvol = CTL_LAST_STATUS;
162 
163   if ( mv != lastvol ) {
164     if ( mv == CTL_LAST_STATUS ) mv = lastvol;
165     else lastvol = mv;
166 
167     sprintf( local_buf, "V %d", mv );
168     xskin_pipe_write(local_buf);
169   }
170 
171   return;
172 }
173 
ctl_current_time(int sec,int v)174 static void ctl_current_time(int sec, int v) {
175 
176   static int previous_sec=-1;
177   if (sec!=previous_sec) {
178     previous_sec=sec;
179     sprintf(local_buf,"T %02d:%02d",sec/60,sec%60);
180     xskin_pipe_write(local_buf);
181   }
182 }
183 
ctl_lyric(int lyricid)184 static void ctl_lyric(int lyricid)
185 {
186     char *lyric;
187 	static int lyric_col = 2;
188 	static char lyric_buf[300];
189 
190     lyric = event2string(lyricid);
191     if(lyric != NULL)
192     {
193 	if(lyric[0] == ME_KARAOKE_LYRIC)
194 	{
195 	    if(lyric[1] == '/' || lyric[1] == '\\')
196 	    {
197 		lyric_buf[0] = 'L';
198 		lyric_buf[1] = ' ';
199 		snprintf(lyric_buf + 2, sizeof (lyric_buf) - 2, "%s", lyric + 2);
200 		xskin_pipe_write(lyric_buf);
201 		lyric_col = strlen(lyric + 2) + 2;
202 	    }
203 	    else if(lyric[1] == '@')
204 	    {
205 		lyric_buf[0] = 'L';
206 		lyric_buf[1] = ' ';
207 		if(lyric[2] == 'L')
208 		    snprintf(lyric_buf + 2, sizeof (lyric_buf) - 2, "Language: %s", lyric + 3);
209 		else if(lyric[2] == 'T')
210 		    snprintf(lyric_buf + 2, sizeof (lyric_buf) - 2, "Title: %s", lyric + 3);
211 		else
212 		    snprintf(lyric_buf + 2, sizeof (lyric_buf) - 2, "%s", lyric + 1);
213 		xskin_pipe_write(lyric_buf);
214 	    }
215 	    else
216 	    {
217 		lyric_buf[0] = 'L';
218 		lyric_buf[1] = ' ';
219 		snprintf(lyric_buf + lyric_col, sizeof (lyric_buf) - lyric_col, "%s", lyric + 1);
220 		xskin_pipe_write(lyric_buf);
221 		lyric_col += strlen(lyric + 1);
222 	    }
223 	}
224 	else
225 	{
226 	    if(lyric[0] == ME_CHORUS_TEXT || lyric[0] == ME_INSERT_TEXT)
227 		lyric_col = 0;
228 	    snprintf(lyric_buf + lyric_col, sizeof (lyric_buf) - lyric_col, "%s", lyric + 1);
229 	    xskin_pipe_write(lyric_buf);
230 	}
231     }
232 }
233 
ctl_speana_data(double * val,int size)234 static void ctl_speana_data(double *val, int size) {
235 
236   /* 0 <= val[n] <= (AMP*NCOLOR) */
237   /* AMP and NCOLOR are defined in soundspec.c */
238   /* By default, AMP*NCOLOR = 1.0*64 */
239 
240   /* size = FFTSIZE/2 = 512 */
241   /* FFTSIZE is also defined in soundspec.c */
242 
243   /* val[n] is the value of FFTed audio data */
244 
245 #ifdef SUPPORT_SOUNDSPEC
246   int i;
247   int tx,x;
248   double px;
249   double s,a;
250   int n;
251 
252   if ( isspeanaenabled ) {
253 
254     px=0.0;
255     speana_buf[0] = (unsigned char)val[0];
256     for ( i=1 ; i<SPE_W-1 ; i++ ) {
257       s=0.0;
258       n=0;
259       tx=exp_hz_table[i];
260       x=(int)px;
261 
262       do {
263 	a=val[x];
264 	s += a + (tx-x)*(val[x+1]-a);
265 	n++;
266 	x++;
267       } while ( x<tx );
268 
269       s/=n;
270       s*=16;
271       if ( s<0 ) s=0;
272       if ( s>=NCOLOR ) s=NCOLOR-1;
273       speana_buf[i] = (unsigned char)(256*s/NCOLOR);
274       px=tx;
275     }
276     speana_buf[SPE_W-1] = val[FFTSIZE/2-1];
277 
278     xskin_pipe_write( "W" );
279 
280   }
281 #endif /* SUPPORT_SOUNDSPEC */
282 
283   return;
284 }
285 
286 /*ARGSUSED*/
ctl_open(int using_stdin,int using_stdout)287 static int ctl_open(int using_stdin, int using_stdout) {
288   ctl.opened=1;
289   initialize_exp_hz_table();
290 
291   /* The child process won't come back from this call  */
292   xskin_pipe_open();
293 
294   return 0;
295 }
296 
ctl_close(void)297 static void ctl_close(void)
298 {
299   if (ctl.opened) {
300     xskin_pipe_write("Q");
301     ctl.opened=0;
302     pipe_out_fd=-1;
303     xskin_ready = 0;
304   }
305 }
306 
307 static int exitflag=0,randomflag=0,repeatflag=0,selectflag=0;
308 
309 /*ARGSUSED*/
ctl_blocking_read(int32 * valp)310 static int ctl_blocking_read(int32 *valp  /* Now, valp is not used */ ) {
311   xskin_pipe_read(local_buf,sizeof(local_buf));
312   for (;;) {
313     switch (local_buf[0]) {
314     case 'P' : return RC_LOAD_FILE;
315     case 'U' : return RC_TOGGLE_PAUSE;
316     case 'S' : return RC_QUIT;
317     case 'N' : return RC_NEXT;
318     case 'B' : return RC_REALLY_PREVIOUS;
319     case 'R' : repeatflag=atoi(local_buf+2);return RC_NONE;
320     case 'D' : randomflag=atoi(local_buf+2);return RC_QUIT;
321     case 'L' : selectflag=atoi(local_buf+2);return RC_QUIT;
322     case 'V' : *valp     =atoi(local_buf+2);return RC_CHANGE_VOLUME;
323 #ifdef SUPPORT_SOUNDSPEC
324     case 'W' : return RC_TOGGLE_CTL_SPEANA;
325 #endif
326     case 'Q' :
327     default : exitflag=1;return RC_QUIT;
328     }
329   }
330 }
331 
ctl_read(int32 * valp)332 static int ctl_read(int32 *valp) {
333 	if (cuepoint_pending) {
334 		*valp = cuepoint;
335 		cuepoint_pending = 0;
336 		return RC_FORWARD;
337 	}
338   if (xskin_pipe_ready()<=0) return RC_NONE;
339   return ctl_blocking_read(valp);
340 }
341 
shuffle(int n,int * a)342 static void shuffle(int n,int *a) {
343 
344   int i,j,tmp;
345 
346   for (i=0;i<n;i++) {
347     j=int_rand(n);
348     tmp=a[i];
349     a[i]=a[j];
350     a[j]=tmp;
351   }
352 }
353 
ctl_pass_playing_list(int number_of_files,char * list_of_files[])354 static int ctl_pass_playing_list(int number_of_files, char *list_of_files[]) {
355 
356   int current_no,command,i;
357   int32 val;
358   int *file_table;
359   char **titles;
360   char *p;
361 
362   /* Wait prepare 'interface' */
363   xskin_pipe_read(local_buf,sizeof(local_buf));
364   if (strcmp("READY",local_buf))
365     return 0;
366   xskin_ready = 1;
367 
368   /* receive shared memory buffer */
369   xskin_pipe_read(local_buf, sizeof(local_buf));
370   if (strcmp("ERROR",local_buf)) {
371     int shmid;
372     isspeanaenabled=1;
373     shmid = atoi(local_buf);
374     speana_buf = (unsigned char *)shmat(shmid,0,0);
375   } else {
376     isspeanaenabled=0;
377   }
378 
379   /* Make title string */
380   titles=(char **)safe_malloc(number_of_files*sizeof(char *));
381   for (i=0;i<number_of_files;i++) {
382     p=strrchr(list_of_files[i],'/');
383     if (p==NULL) {
384       p=list_of_files[i];
385     } else p++;
386     sprintf(local_buf,"%d. %s",i+1,p);
387     titles[i]=(char *)safe_malloc(strlen(local_buf)+1);
388     strcpy(titles[i],local_buf);
389   }
390 
391   /* Send title string */
392   sprintf(local_buf,"%d",number_of_files);
393   xskin_pipe_write(local_buf);
394   for (i=0;i<number_of_files;i++) xskin_pipe_write(titles[i]);
395 
396   /* Make the table of play sequence */
397   file_table=(int *)safe_malloc(number_of_files*sizeof(int));
398   for (i=0;i<number_of_files;i++) file_table[i]=i;
399 
400   /* Draw the title of the first file */
401   current_no=0;
402   sprintf(local_buf,"F %s",titles[file_table[0]]);
403   xskin_pipe_write(local_buf);
404 
405   command=ctl_blocking_read(&val);
406 
407   /* Main loop */
408   for (;;) {
409     /* Play file */
410     if (command==RC_LOAD_FILE) {
411       sprintf(local_buf,"F %s",titles[file_table[current_no]]);
412       xskin_pipe_write(local_buf);
413       command=play_midi_file(list_of_files[file_table[current_no]]);
414     } else {
415       /* Quit timidity*/
416       if (exitflag) return 0;
417       /* Stop playing */
418       if (command==RC_QUIT) {
419 	sprintf(local_buf,"T 00:00");
420 	xskin_pipe_write(local_buf);
421 	/* Shuffle the table */
422 	if (randomflag) {
423 	  current_no=0;
424 	  if (randomflag==1) {
425 	    shuffle(number_of_files,file_table);
426 	    randomflag=0;
427 	    command=RC_LOAD_FILE;
428 	    continue;
429 	  }
430 	  randomflag=0;
431 	  for (i=0;i<number_of_files;i++) file_table[i]=i;
432 	  sprintf(local_buf,"F %s",titles[file_table[current_no]]);
433 	  xskin_pipe_write(local_buf);
434 	}
435 	/* Play the selected file */
436 	if (selectflag) {
437 	  for (i=0;i<number_of_files;i++)
438 	    if (file_table[i]==selectflag-1) break;
439 	  if (i!=number_of_files) current_no=i;
440 	  selectflag=0;
441 	  command=RC_LOAD_FILE;
442 	  continue;
443 	}
444       /* After the all file played */
445       } else if (command==RC_TUNE_END || command==RC_ERROR) {
446 	if (current_no+1<number_of_files) {
447 	  current_no++;
448 	  command=RC_LOAD_FILE;
449 	  continue;
450 	/* Repeat */
451 	} else if (repeatflag) {
452 	  current_no=0;
453 	  command=RC_LOAD_FILE;
454 	  continue;
455 	/* Off the play button */
456 	} else {
457 	  xskin_pipe_write("O");
458 	}
459       /* Play the next */
460       } else if (command==RC_NEXT) {
461 	if (current_no+1<number_of_files) current_no++;
462 	command=RC_LOAD_FILE;
463 	continue;
464       /* Play the previous */
465       } else if (command==RC_REALLY_PREVIOUS) {
466 	if (current_no>0) current_no--;
467 	command=RC_LOAD_FILE;
468 	continue;
469       }
470 
471       command=ctl_blocking_read(&val);
472     }
473   }
474   return 0;
475 }
476 
477 /* ------ Pipe handlers ----- */
478 
479 extern void xskin_start_interface(int);
480 
xskin_pipe_open(void)481 static void xskin_pipe_open(void) {
482 
483   int cont_inter[2],inter_cont[2];
484 
485   if (pipe(cont_inter)<0 || pipe(inter_cont)<0) exit(1);
486 
487   if (fork()==0) {
488     close(cont_inter[1]);
489     close(inter_cont[0]);
490     pipe_in_fd=cont_inter[0];
491     pipe_out_fd=inter_cont[1];
492     xskin_start_interface(pipe_in_fd);
493   }
494   close(cont_inter[0]);
495   close(inter_cont[1]);
496   pipe_in_fd=inter_cont[0];
497   pipe_out_fd=cont_inter[1];
498 }
499 
xskin_pipe_write(char * buf)500 void xskin_pipe_write(char *buf) {
501   ssize_t dummy = write(pipe_out_fd,buf,strlen(buf));
502   dummy += write(pipe_out_fd,"\n",1);
503 }
504 
xskin_pipe_ready(void)505 static int xskin_pipe_ready(void) {
506 
507   fd_set fds;
508   static struct timeval tv;
509   int cnt;
510 
511   FD_ZERO(&fds);
512   FD_SET(pipe_in_fd,&fds);
513   tv.tv_sec=0;
514   tv.tv_usec=0;
515   if((cnt=select(pipe_in_fd+1,&fds,NULL,NULL,&tv))<0)
516     return -1;
517   return cnt > 0 && FD_ISSET(pipe_in_fd, &fds) != 0;
518 }
519 
xskin_pipe_read(char * buf,int bufsize)520 int xskin_pipe_read(char *buf,int bufsize) {
521 
522   int i;
523 
524   bufsize--;
525   for (i=0;i<bufsize;i++) {
526     ssize_t dummy = read(pipe_in_fd,buf+i,1); ++dummy;
527     if (buf[i]=='\n') break;
528   }
529   buf[i]=0;
530   return 0;
531 }
532 
xskin_pipe_read_direct(int32 * buf,int bufsize)533 int xskin_pipe_read_direct(int32 *buf, int bufsize) {
534 
535   ssize_t dummy = read( pipe_in_fd, buf, bufsize ); ++dummy;
536 
537   return 0;
538 }
539 
ctl_event(CtlEvent * e)540 static void ctl_event(CtlEvent *e)
541 {
542     switch(e->type)
543     {
544     case CTLE_PLAY_START:
545       ctl_total_time((int)e->v1);
546       break;
547 	case CTLE_CUEPOINT:
548 		cuepoint = e->v1;
549 		cuepoint_pending = 1;
550 		break;
551     case CTLE_CURRENT_TIME:
552       ctl_current_time((int)e->v1, (int)e->v2);
553       break;
554     case CTLE_MASTER_VOLUME:
555       ctl_master_volume((int)e->v1);
556       break;
557     case CTLE_LYRIC:
558       ctl_lyric((int)e->v1);
559       break;
560 #ifdef SUPPORT_SOUNDSPEC
561     case CTLE_SPEANA:
562       ctl_speana_data((double *)e->v1, (int)e->v2);
563     break;
564 #endif /* SUPPORT_SOUNDSPEC */
565 
566     }
567 }
568 
569 /*
570  * interface_<id>_loader();
571  */
interface_i_loader(void)572 ControlMode *interface_i_loader(void)
573 {
574     return &ctl;
575 }
576 
initialize_exp_hz_table(void)577 static void initialize_exp_hz_table( void ) {
578   int i;
579   double r, x, w;
580 
581   w = (double)play_mode->rate * 0.5 / DEFAULT_ZOOM;
582   r = exp(log(w) * (1.0/SPE_W));
583   w = (FFTSIZE/2.0) / (w - 1.0);
584 
585   for(i = 0, x = 1.0; i <= SPE_W; i++, x *= r)
586     exp_hz_table[i] = (x - 1.0) * w;
587 
588 }
589