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