1 /************************************************************************
2    io_gtk.c  -- shows midi events in X11/Gnome using Gtk widgets
3 
4    Copyright (C) 1995-1996 Nathan I. Laredo
5    Copyright (C) 1997 Elliot Lee
6 
7    This program is modifiable/redistributable under the terms
8    of the GNU General Public Licence.
9 
10    You should have received a copy of the GNU General Public License
11    along with this program; if not, write to the Free Software
12    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
13    Send your comments and all your spare pocket change to
14    laredo@gnu.ai.mit.edu (Nathan Laredo) or to PSC 1, BOX 709, 2401
15    Kelly Drive, Lackland AFB, TX 78236-5128, USA.
16  *************************************************************************/
17 #include "playmidi.h"
18 #include "bitmaps.h"
19 #include <gtk/gtk.h>
20 #include <sys/time.h>
21 #include <unistd.h>
22 
23 extern int chanmask, play_gus, play_fm, play_ext, gus_dev, sb_dev, ext_dev;
24 extern int graphics, verbose, perc, ntrks;
25 extern int note_vel[16][128];
26 extern unsigned long int ticks;
27 extern char *filename, *gmvoice[256];
28 extern float skew;
29 extern void seq_reset();
30 extern struct chanstate channel[16];
31 extern void seq_stop_note(int, int, int, int);
32 extern struct timeval start_time;
33 struct timeval want_time, now_time;
34 
35 typedef unsigned char Boolean;
36 #define True 1
37 #define False 0
38 
39 /* Gives us t2 usecs - t1 usecs */
cdeltat(struct timeval * t1,struct timeval * t2)40 int cdeltat(struct timeval *t1, struct timeval *t2)
41 {
42   long long d1 = (t1->tv_sec * 10000) + t1->tv_usec;
43   long long d2 = (t2->tv_sec * 10000) + t2->tv_usec;
44   return (int)(d1 - d2);
45 }
46 
cdeltat_old(t1,t2)47 int cdeltat_old(t1, t2)
48 struct timeval *t1;
49 struct timeval *t2;
50 {
51     int d1, d2;
52 
53     d1 = t1->tv_sec - t2->tv_sec;
54     if((d2 = t1->tv_usec - t2->tv_usec) < 0)
55 	(d2 += 1000000, d1 -= 1);
56     d2 /= 10000;
57     return (d2 + d1 * 100);
58 }
59 
60 GtkWindow *mainwin;
61 GtkVBox *dispbox;
62 GtkHBox *mwbox, *btnbox;
63 GtkButton *exit_button, *nextsong, *prevsong, *repeatsong;
64 GtkToggleButton *cbutton[16];
65 GtkProgressBar *cmeter[16];
66 GtkHScale *scrollbar;
67 GtkAdjustment *sbadj;
68 GtkText *miditext;
69 GtkLabel *lbl_timer;
70 gint timer_tag, quittimer_tag;
71 #define TEXTSIZE 8192
72 char textbuf[TEXTSIZE];
73 
74 int i, return_do_what;
75 int vu_level[16], vu_delta[16];
76 
77 char *drumset[11] =
78 {
79  "Standard Kit", "Room Kit", "Power Kit", "Electronic Kit", "TR-808 Kit",
80     "Jazz Kit", "Brush Kit", "Orchestra Kit", "Sound FX Kit",
81     "Program Kit", "MT-32 Kit"
82 };
83 
84 #define SET(x) (x == 8 ? 1 : x >= 16 && x <= 23 ? 2 : x == 24 ? 3 : \
85 		x == 25  ? 4 : x == 32 ? 5 : x >= 40 && x <= 47 ? 6 : \
86 		x == 48 ? 7 : x == 56 ? 8 : x >= 96 && x <= 111 ? 9 : \
87 		x == 127 ? 10 : 0)
88 
close_show(int error)89 void close_show(int error)
90 {
91   gtk_exit(error);
92 }
93 
ExitCallback(GtkWidget * w)94 void ExitCallback(GtkWidget *w)
95 {
96   close_show(0);
97 }
98 
MaskCallback(GtkWidget * w)99 void MaskCallback(GtkWidget *w)
100 {
101     int i, j, k;
102     for (i = 0; i < 16 && cbutton[i] != GTK_TOGGLE_BUTTON(w); i++);
103     if (i < 0 || i >= 16)
104 	return;
105     if (GTK_TOGGLE_BUTTON(w)->active)		/* channel should be enabled */
106 	chanmask |= (1 << i);
107     else {			/* need to stop all notes in channel */
108 	chanmask &= (~(1 << i));
109 	if (ISGUS(i))
110 	    k = gus_dev;
111 	else if (ISFM(i))
112 	    k = sb_dev;
113 	else
114 	    k = ext_dev;
115 	for (j = 0; j < 128; j++)
116 	    if (note_vel[i][j])
117 		seq_stop_note(k, i, j, note_vel[i][j] = 0);
118     }
119 }
120 
SkewCallback(GtkScale * w)121 void SkewCallback(GtkScale *w)
122 {
123   skew = (sbadj->value * 3.75) + 0.25;
124 }
125 
playCallback(GtkButton * w)126 void playCallback(GtkButton *w)
127 {
128   seq_reset();
129   if(w == nextsong)
130     return_do_what = 1;
131   else if(w == prevsong)
132     return_do_what = -1;
133   else if(w == repeatsong)
134     return_do_what = 0;
135   else
136     g_print("Unknown button pressed\n");
137 }
138 
updateMeter(int chn)139 void updateMeter(int chn)
140 {
141   gtk_progress_bar_update(cmeter[chn], (5 + vu_level[chn]) / 200.0);
142 }
143 
144 Boolean return_now;
145 int oldsec = 0, oldusec = 0;
146 
TimerCallback(gpointer data)147 void TimerCallback(gpointer data)
148 {
149     int i, sec, usec;
150     gettimeofday(&now_time, NULL);
151     if(start_time.tv_sec && start_time.tv_usec) {
152       sec = now_time.tv_sec - start_time.tv_sec;
153       usec = now_time.tv_usec - start_time.tv_usec;
154     } else
155       sec = usec = 0;
156 
157     if (usec < 0)
158 	(usec += 1000000, sec -= 1);
159     /* update vu meter values */
160     for (i = 0; i < 16; i++)
161 	if (vu_delta[i]) {	/* decay meter */
162 	    vu_level[i] -= vu_delta[i];
163 	    if (vu_level[i] < 0)
164 		vu_level[i] = vu_delta[i] = 0;
165 	    if(sec > oldsec)
166 	      updateMeter(i);
167 	}
168     usec /= 100000;	/* change to 10ths of a second */
169     if(sec > oldsec) {
170       sprintf(textbuf, "%02d:%02d.%d", sec / 60, sec % 60, usec);
171       gtk_label_set(lbl_timer, textbuf);
172       gtk_widget_queue_draw(lbl_timer);
173       oldsec = sec; oldusec = usec;
174     }
175     if(return_do_what != NO_EXIT || cdeltat(&want_time, &now_time) < 100) {
176       return_now = True;
177       gtk_main_quit();
178     }
179 }
180 
quittimer(gpointer data)181 void quittimer(gpointer data)
182 {
183   gtk_timeout_remove(quittimer_tag);
184   return_now = True;
185   gettimeofday(&now_time, NULL);
186   g_print("quittimer with %d\n", cdeltat(&want_time, &now_time));
187   gtk_main_quit();
188 }
189 
updatestatus(void)190 int updatestatus(void)
191 {
192   return_now = False;
193   want_time.tv_sec = start_time.tv_sec + (ticks / 100);
194   want_time.tv_usec = start_time.tv_usec + (ticks % 100) * 10000;
195   if (want_time.tv_usec > 1000000)
196     (want_time.tv_usec -= 1000000, want_time.tv_sec++);
197   return_do_what = NO_EXIT;
198   /*
199   gettimeofday(&now_time, NULL);
200   if(cdeltat(&want_time, &now_time) < 100)
201     return return_do_what;
202   quittimer_tag = gtk_timeout_add((cdeltat(&want_time, &now_time)/1000) - 5,
203 				  quittimer, NULL);
204   g_print("Timeout in %d ms\n", (cdeltat(&want_time, &now_time)/1000) - 5);
205   */
206   gtk_main();
207 
208   return return_do_what;
209 }
210 
AppendText(char * s)211 void AppendText(char *s)
212 {
213   return;
214 }
215 
216 #define TEXTBUF		(&textbuf[strlen(textbuf)])
217 
showevent(int cmd,unsigned char * data,int length)218 void showevent(int cmd, unsigned char *data, int length)
219 {
220     int chn, note, vel, i;
221 
222     chn = (cmd & 0x0f);
223     note = (data[0] & 0x7f);
224     vel = (data[1] & 0x7f);
225     if (cmd < 8 && cmd > 0) {
226 	strncpy(textbuf, data, length < TEXTSIZE - 4 ? length : TEXTSIZE - 3);
227 	textbuf[(length > TEXTSIZE + 2) ? TEXTSIZE - 2 : length] = '\0';
228 	sprintf(TEXTBUF, "\n");
229 	AppendText(textbuf);
230 	if (verbose > 2)
231 	    printf(textbuf);
232     } else if (cmd & 0x80)
233 	switch (cmd & 0xf0) {
234 	case MIDI_NOTEON:
235 	    if (!vel && vu_level[chn])
236 		vu_delta[chn] = 32;
237 	    else
238 		(vu_level[chn] = vel, vu_delta[chn] = 0);
239 	    updateMeter(chn);
240 	    break;
241 	case MIDI_NOTEOFF:
242 	    if (vu_level[chn])
243 		vu_delta[chn] = vel / 2;
244 	    updateMeter(chn);
245 	    break;
246 	case MIDI_PGM_CHANGE:
247 	    if (!ISPERC(chn))
248 	      gtk_label_set(GTK_BUTTON(cbutton[chn])->child, gmvoice[note]);
249 	    else
250 	      gtk_label_set(GTK_BUTTON(cbutton[chn])->child, drumset[SET(note)]);
251 	    break;
252 	case 0xf0:
253 	case 0xf7:
254 	    sprintf(textbuf, "Sysex(%2x): ", cmd);
255 	    for (i = 0; i < length && i < (TEXTSIZE / 2) - 16; i++)
256 		sprintf(TEXTBUF, "%02x", data[i]);
257 	    sprintf(TEXTBUF, "\n");
258 	    AppendText(textbuf);
259 	    if (verbose > 2)
260 		printf(textbuf);
261 	    break;
262 	default:
263 	    break;
264 	}
265 }
266 
init_show(void)267 void init_show(void)
268 {
269   int i;
270   sbadj->value = 1.0;
271   gtk_widget_queue_draw(GTK_WIDGET(scrollbar));
272   for(i = 0; i < 16; i++) {
273     vu_level[i] = vu_delta[i] = 0;
274     updateMeter(i);
275     sprintf(textbuf, "channel%02d", i + 1);
276     gtk_label_set(GTK_BUTTON(cbutton[i])->child, textbuf);
277   }
278   gtk_label_set(lbl_timer, "00:00.0");
279   gtk_window_set_title(mainwin, filename);
280 }
281 
nuttin(void)282 void nuttin(void) {}
283 
284 #define SW(x) gtk_widget_show(GTK_WIDGET(x))
285 void
setup_show(int argc,char ** argv)286 setup_show(int argc, char **argv)
287 {
288   int i;
289   gtk_init(&argc, &argv);
290   mainwin = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
291   graphics++;
292   mwbox = GTK_HBOX(gtk_hbox_new(1, 5));
293   SW(mwbox);
294   btnbox = GTK_HBOX(gtk_hbox_new(1, 5));
295   SW(btnbox);
296   dispbox = GTK_VBOX(gtk_vbox_new(1, 5));
297   SW(dispbox);
298   gtk_box_pack_start_defaults(GTK_WIDGET(mwbox), dispbox);
299   gtk_box_pack_start_defaults(GTK_WIDGET(dispbox), btnbox);
300   gtk_window_set_title(mainwin,
301 		       "Gtk" RELEASE " by Nathan Laredo (Gtk by Elliot Lee)");
302   sbadj = gtk_adjustment_new(1.0, 0.0, 2.0, 0.05, 1.0, 2.0); /* XXX ??? */
303   scrollbar = GTK_HSCALE(gtk_hscale_new(sbadj));
304   gtk_scale_set_draw_value(GTK_WIDGET(scrollbar), TRUE);
305   gtk_scale_set_digits(GTK_WIDGET(scrollbar), 1);
306   gtk_signal_connect(GTK_OBJECT(sbadj),
307 		     "changed", (GtkSignalFunc)SkewCallback, NULL);
308   SW(scrollbar);
309   gtk_box_pack_start_defaults(GTK_WIDGET(dispbox), scrollbar);
310   for(i = 0; i < 16; i++) {
311     GtkWidget *tmp;
312     sprintf(textbuf, "channel%02d", i + 1);
313     cbutton[i] = GTK_TOGGLE_BUTTON(gtk_toggle_button_new_with_label(textbuf));
314     gtk_toggle_button_set_state(cbutton[i], TRUE);
315     gtk_signal_connect(GTK_OBJECT(cbutton[i]),
316 		       "toggled", (GtkSignalFunc)MaskCallback,
317 		       NULL);
318     gtk_widget_set_usize(cbutton[i], 80, 10);
319     SW(cbutton[i]);
320     sprintf(textbuf, "meter%02d", i + 1);
321     cmeter[i] = GTK_PROGRESS_BAR(gtk_progress_bar_new());
322     gtk_widget_set_usize(cmeter[i], 50, 10);
323     SW(cmeter[i]);
324     tmp = gtk_hbox_new(1, 5);
325     SW(tmp);
326     gtk_box_pack_start_defaults(tmp, cbutton[i]);
327     gtk_box_pack_start_defaults(tmp, cmeter[i]);
328     gtk_box_pack_start_defaults(dispbox, tmp);
329   }
330   lbl_timer = gtk_label_new("00:00.0");
331   SW(lbl_timer);
332   gtk_box_pack_start_defaults(dispbox, lbl_timer);
333   exit_button = GTK_BUTTON(gtk_button_new_with_label("Exit"));
334   SW(exit_button);
335   gtk_signal_connect(exit_button, "clicked", (GtkSignalFunc)ExitCallback,
336 		     0);
337   prevsong = GTK_BUTTON(gtk_button_new_with_label("<<"));
338   SW(prevsong);
339   gtk_signal_connect(prevsong, "clicked", (GtkSignalFunc)playCallback,
340 		     NULL);
341   repeatsong = GTK_BUTTON(gtk_button_new_with_label(" > "));
342   SW(repeatsong);
343   gtk_signal_connect(repeatsong, "clicked", (GtkSignalFunc)playCallback,
344 		     NULL);
345   nextsong = GTK_BUTTON(gtk_button_new_with_label(">>"));
346   SW(nextsong);
347   gtk_signal_connect(nextsong, "clicked", (GtkSignalFunc)playCallback,
348 		     NULL);
349   gtk_box_pack_start_defaults(btnbox, exit_button);
350   gtk_box_pack_start_defaults(btnbox, nextsong);
351   gtk_box_pack_start_defaults(btnbox, prevsong);
352   gtk_box_pack_start_defaults(btnbox, repeatsong);
353   /*  timer_tag = gtk_timeout_add(100, (GtkFunction)TimerCallback, NULL); */
354   gtk_idle_add((GtkFunction)TimerCallback, NULL);
355   gtk_container_add(mainwin, mwbox);
356   gtk_main_iteration();
357   SW(mainwin);
358 }
359 #undef SW
360