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     motif_ctl.c: written by Vincent Pagel (pagel@loria.fr) 10/4/95
21 
22     A motif interface for TIMIDITY: to prevent X redrawings from
23     interfering with the audio computation, I don't use the XtAppAddWorkProc
24 
25     I create a pipe between the timidity process and a Motif interface
26     process forked from the 1st one
27 */
28 
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif /* HAVE_CONFIG_H */
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <stdarg.h>
36 #ifndef NO_STRING_H
37 #include <string.h>
38 #else
39 #include <strings.h>
40 #endif
41 
42 #include "timidity.h"
43 #include "common.h"
44 #include "instrum.h"
45 #include "playmidi.h"
46 #include "readmidi.h"
47 #include "output.h"
48 #include "controls.h"
49 #include "motif.h"
50 #include "miditrace.h"
51 
52 static void ctl_refresh(void);
53 static void ctl_total_time(int tt);
54 static void ctl_master_volume(int mv);
55 static void ctl_file_name(char *name);
56 static void ctl_current_time(int secs, int v);
57 static void ctl_lyric(int lyricid);
58 static int ctl_open(int using_stdin, int using_stdout);
59 static void ctl_close(void);
60 static int ctl_read(int32 *valp);
61 static int cmsg(int type, int verbosity_level, char *fmt, ...);
62 static int ctl_pass_playing_list(int number_of_files, char *list_of_files[]);
63 static void ctl_event(CtlEvent *e);
64 
65 static int motif_ready = 0;
66 
67 /**********************************************/
68 /* export the interface functions */
69 
70 #define ctl motif_control_mode
71 
72 ControlMode ctl=
73 {
74     "motif interface", 'm',
75     "motif",
76     1,0,0,
77     0,
78     ctl_open,
79     ctl_close,
80     ctl_pass_playing_list,
81     ctl_read,
82     NULL,
83     cmsg,
84     ctl_event
85 };
86 
87 static uint32 cuepoint = 0;
88 static int cuepoint_pending = 0;
89 
90 
91 /***********************************************************************/
92 /* Put controls on the pipe                                            */
93 /***********************************************************************/
cmsg(int type,int verbosity_level,char * fmt,...)94 static int cmsg(int type, int verbosity_level, char *fmt, ...)
95 {
96     char local[255];
97 
98     va_list ap;
99     if ((type==CMSG_TEXT || type==CMSG_INFO || type==CMSG_WARNING) &&
100 	ctl.verbosity<verbosity_level)
101 	return 0;
102 
103     va_start(ap, fmt);
104     if (!motif_ready)
105 	{
106 	    vfprintf(stderr, fmt, ap);
107 	    fprintf(stderr, NLS);
108 	}
109     else
110 	{
111 	    vsnprintf(local, sizeof(local), fmt, ap);
112 	    m_pipe_int_write(CMSG_MESSAGE);
113 	    m_pipe_int_write(type);
114 	    m_pipe_string_write(local);
115 	}
116     va_end(ap);
117     return 0;
118 }
119 
120 
_ctl_refresh(void)121 static void _ctl_refresh(void)
122 {
123     /* m_pipe_int_write(REFRESH_MESSAGE); */
124 }
125 
ctl_refresh(void)126 static void ctl_refresh(void)
127 {
128   if (ctl.trace_playing)
129     _ctl_refresh();
130 }
131 
ctl_total_time(int tt)132 static void ctl_total_time(int tt)
133 {
134   int secs=tt/play_mode->rate;
135 
136   m_pipe_int_write(TOTALTIME_MESSAGE);
137   m_pipe_int_write(secs);
138 }
139 
ctl_master_volume(int mv)140 static void ctl_master_volume(int mv)
141 {
142     m_pipe_int_write(MASTERVOL_MESSAGE);
143     m_pipe_int_write(mv);
144 }
145 
ctl_file_name(char * name)146 static void ctl_file_name(char *name)
147 {
148     m_pipe_int_write(FILENAME_MESSAGE);
149     m_pipe_string_write(name);
150 }
151 
ctl_current_time(int secs,int v)152 static void ctl_current_time(int secs, int v)
153 {
154     m_pipe_int_write(CURTIME_MESSAGE);
155     m_pipe_int_write(secs);
156     m_pipe_int_write(v);
157 }
158 
ctl_lyric(int lyricid)159 static void ctl_lyric(int lyricid)
160 {
161     char *lyric;
162     static char lyric_buf[300];
163 
164     lyric = event2string(lyricid);
165     if(lyric != NULL)
166     {
167 	if(lyric[0] == ME_KARAOKE_LYRIC)
168 	{
169 	    if(!lyric[1])
170 		return;
171 	    if(lyric[1] == '/' || lyric[1] == '\\')
172 	    {
173 		snprintf(lyric_buf, sizeof(lyric_buf), "\n%s", lyric + 2);
174 		m_pipe_int_write(LYRIC_MESSAGE);
175 		m_pipe_string_write(lyric_buf);
176 	    }
177 	    else if(lyric[1] == '@')
178 	    {
179 		if(lyric[2] == 'L')
180 		    snprintf(lyric_buf, sizeof(lyric_buf), "Language: %s\n", lyric + 3);
181 		else if(lyric[2] == 'T')
182 		    snprintf(lyric_buf, sizeof(lyric_buf), "Title: %s\n", lyric + 3);
183 		else
184 		    snprintf(lyric_buf, sizeof(lyric_buf), "%s\n", lyric + 1);
185 		m_pipe_int_write(LYRIC_MESSAGE);
186 		m_pipe_string_write(lyric_buf);
187 	    }
188 	    else
189 	    {
190 		strncpy(lyric_buf, lyric + 1, sizeof(lyric_buf) - 1);
191 		m_pipe_int_write(LYRIC_MESSAGE);
192 		m_pipe_string_write(lyric_buf);
193 	    }
194 	}
195 	else
196 	{
197 	    strncpy(lyric_buf, lyric + 1, sizeof(lyric_buf) - 1);
198 	    m_pipe_int_write(LYRIC_MESSAGE);
199 	    m_pipe_string_write(lyric_buf);
200 	}
201     }
202 }
203 
ctl_event(CtlEvent * e)204 static void ctl_event(CtlEvent *e)
205 {
206     switch(e->type)
207     {
208       case CTLE_NOW_LOADING:
209 	ctl_file_name((char *)e->v1);
210 	break;
211       case CTLE_PLAY_START:
212 	ctl_total_time((int)e->v1);
213 	break;
214 	case CTLE_CUEPOINT:
215 		cuepoint = e->v1;
216 		cuepoint_pending = 1;
217 		break;
218       case CTLE_CURRENT_TIME:
219 	ctl_current_time((int)e->v1, (int)e->v2);
220 	break;
221       case CTLE_MASTER_VOLUME:
222 	ctl_master_volume((int)e->v1);
223 	break;
224       case CTLE_LYRIC:
225 	ctl_lyric((int)e->v1);
226 	break;
227       case CTLE_REFRESH:
228 	ctl_refresh();
229 	break;
230     }
231 }
232 
233 
234 /***********************************************************************/
235 /* OPEN THE CONNECTION                                                */
236 /***********************************************************************/
237 /*ARGSUSED*/
ctl_open(int using_stdin,int using_stdout)238 static int ctl_open(int using_stdin, int using_stdout)
239 {
240   ctl.opened=1;
241 #if 0
242   ctl.trace_playing=1;	/* Default mode with Motif interface */
243 #endif
244 
245   /* The child process won't come back from this call  */
246   m_pipe_open();
247 
248   return 0;
249 }
250 
251 /* Tells the window to disapear */
ctl_close(void)252 static void ctl_close(void)
253 {
254   if (ctl.opened)
255     {
256 	m_pipe_int_write(CLOSE_MESSAGE);
257 	ctl.opened=0;
258 	motif_ready = 0;
259     }
260 }
261 
262 
263 /*
264  * Read information coming from the window in a BLOCKING way
265  */
ctl_blocking_read(int32 * valp)266 static int ctl_blocking_read(int32 *valp)
267 {
268   int command;
269   int new_volume;
270   int new_secs;
271   int i=0, nfiles;
272   char buf[256][256];
273   char **ret, *files[256];
274 
275   m_pipe_int_read(&command);
276 
277   for(;;)    /* Loop after pause sleeping to treat other buttons! */
278       {
279 
280 	  switch(command)
281 	      {
282 	      case MOTIF_CHANGE_VOLUME:
283 		  m_pipe_int_read(&new_volume);
284 		  *valp= new_volume - amplification ;
285 		  return RC_CHANGE_VOLUME;
286 
287 	      case MOTIF_CHANGE_LOCATOR:
288 		  m_pipe_int_read(&new_secs);
289 		  *valp= new_secs * play_mode->rate;
290 		  return RC_JUMP;
291 
292 	      case MOTIF_QUIT:
293 		  return RC_QUIT;
294 
295 	      case MOTIF_PLAY_FILE:
296 		  return RC_LOAD_FILE;
297 
298 	      case MOTIF_NEXT:
299 		  return RC_NEXT;
300 
301 	      case MOTIF_PREV:
302 		  return RC_REALLY_PREVIOUS;
303 
304 	      case MOTIF_RESTART:
305 		  return RC_RESTART;
306 
307 	      case MOTIF_FWD:
308 		  *valp=play_mode->rate;
309 		  return RC_FORWARD;
310 
311 	      case MOTIF_RWD:
312 		  *valp=play_mode->rate;
313 		  return RC_BACK;
314 
315 	      case MOTIF_EXPAND:
316 		  m_pipe_int_read(&nfiles);
317 		  for (i=0;i<nfiles;i++)
318 		  {
319 			m_pipe_string_read(buf[i]);
320 			files[i] = buf[i];
321 		  }
322 		  ret = expand_file_archives(files, &nfiles);
323 		  m_pipe_int_write(FILE_LIST_MESSAGE);
324 		  m_pipe_int_write(nfiles);
325 		  for (i=0;i<nfiles;i++)
326 			m_pipe_string_write(ret[i]);
327 		  if(ret != files)
328 		      free(ret);
329 		  return RC_NONE;
330 
331 		case MOTIF_PAUSE:
332 		  return RC_TOGGLE_PAUSE;
333 
334 		default:
335 		  fprintf(stderr,"UNKNOWN RC_MESSAGE %d" NLS, command);
336 		  return RC_NONE;
337 	      }
338       }
339 }
340 
341 /*
342  * Read information coming from the window in a non blocking way
343  */
ctl_read(int32 * valp)344 static int ctl_read(int32 *valp)
345 {
346   int num;
347 
348 	if (cuepoint_pending) {
349 		*valp = cuepoint;
350 		cuepoint_pending = 0;
351 		return RC_FORWARD;
352 	}
353 
354   /* We don't wan't to lock on reading  */
355   num=m_pipe_read_ready();
356 
357   if (num==0)
358       return RC_NONE;
359 
360   return(ctl_blocking_read(valp));
361 }
362 
ctl_pass_playing_list(int number_of_files,char * list_of_files[])363 static int ctl_pass_playing_list(int number_of_files, char *list_of_files[])
364 {
365     int i=0;
366     char file_to_play[1000];
367     int command;
368     int32 val;
369     int retval;
370 
371     motif_ready = 1;
372 
373     m_pipe_int_write(MASTERVOL_MESSAGE);
374     m_pipe_int_write(amplification);
375 
376     /* Pass the list to the interface */
377     m_pipe_int_write(FILE_LIST_MESSAGE);
378     m_pipe_int_write(number_of_files);
379     for (i=0;i<number_of_files;i++)
380 	m_pipe_string_write(list_of_files[i]);
381 
382     /* Ask the interface for a filename to play -> begin to play automatically */
383     m_pipe_int_write(NEXT_FILE_MESSAGE);
384 
385     command = ctl_blocking_read(&val);
386 
387     /* Main Loop */
388     for (;;)
389 	{
390 	    if (command==RC_LOAD_FILE)
391 		{
392 		    /* Read a LoadFile command */
393 		    m_pipe_string_read(file_to_play);
394 		    command=play_midi_file(file_to_play);
395 		}
396 	    else
397 		{
398 		    if (command==RC_QUIT)
399 			return 0;
400 
401 		    switch(command)
402 			{
403 			case RC_ERROR:
404 			    m_pipe_int_write(ERROR_MESSAGE);
405 			    retval=1;
406 			    break;
407 			case RC_NONE:
408 			    break;
409 			case RC_NEXT:
410 			    m_pipe_int_write(NEXT_FILE_MESSAGE);
411 			    break;
412 			case RC_REALLY_PREVIOUS:
413 			    m_pipe_int_write(PREV_FILE_MESSAGE);
414 			    break;
415 			case RC_TUNE_END:
416 			    m_pipe_int_write(TUNE_END_MESSAGE);
417 			    break;
418 			case RC_CHANGE_VOLUME:
419 				amplification += val;
420 				break;
421 			default:
422 			    fprintf(stderr,
423 				    "PANIC !!! OTHER COMMAND ERROR ?!?! %i"
424 				    NLS, command);
425 			}
426 
427 		    command = ctl_blocking_read(&val);
428 		}
429 	}
430     return retval;
431 }
432 
433 /*
434  * interface_<id>_loader();
435  */
interface_m_loader(void)436 ControlMode *interface_m_loader(void)
437 {
438     return &ctl;
439 }
440