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