1 /************************************************************************ 2 io_xaw.c -- shows midi events in X11 using Xaw widgets 3 4 Copyright (C) 1995-1996 Nathan I. Laredo 5 6 This program is modifiable/redistributable under the terms 7 of the GNU General Public Licence. 8 9 You should have received a copy of the GNU General Public License 10 along with this program; if not, write to the Free Software 11 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 12 Send your comments and all your spare pocket change to 13 laredo@gnu.ai.mit.edu (Nathan Laredo) or to PSC 1, BOX 709, 2401 14 Kelly Drive, Lackland AFB, TX 78236-5128, USA. 15 *************************************************************************/ 16 #include "playmidi.h" 17 #include "bitmaps.h" 18 #include <X11/Intrinsic.h> 19 #include <X11/Shell.h> 20 #include <X11/Xaw/Form.h> 21 #include <X11/Xaw/Label.h> 22 #include <X11/Xaw/Toggle.h> 23 #include <X11/Xaw/Command.h> 24 #include <X11/Xaw/AsciiText.h> 25 #include <X11/Xaw/Scrollbar.h> 26 #include <X11/StringDefs.h> 27 #include <sys/time.h> 28 #include <unistd.h> 29 30 extern int chanmask, play_gus, play_fm, play_ext, gus_dev, sb_dev, ext_dev; 31 extern int graphics, verbose, perc, ntrks; 32 extern int note_vel[16][128]; 33 extern unsigned long int ticks; 34 extern char *filename, *gmvoice[256]; 35 extern float skew; 36 extern void seq_reset(); 37 extern struct chanstate channel[16]; 38 extern void seq_stop_note(int, int, int, int); 39 extern struct timeval start_time; 40 struct timeval want_time, now_time; 41 42 int cdeltat(t1, t2) 43 struct timeval *t1; 44 struct timeval *t2; 45 { 46 int d1, d2; 47 48 d1 = t1->tv_sec - t2->tv_sec; 49 if((d2 = t1->tv_usec - t2->tv_usec) < 0) 50 (d2 += 1000000, d1 -= 1); 51 d2 /= 10000; 52 return (d2 + d1 * 100); 53 } 54 55 XtAppContext context; 56 Widget toplevel, manager, titlebar, status, text, exit_button; 57 Widget scrollbar, skew_label, nextsong, repeatsong, prevsong; 58 Pixmap Exit, Next, Play, Prev; /* button icons */ 59 Widget cbutton[16]; /* program for each channel (also toggles) */ 60 Widget cmeter[16]; /* vu meter for each channel */ 61 #define TEXTSIZE 8192 62 char textbuf[TEXTSIZE]; 63 64 int i, return_do_what; 65 int vu_level[16], vu_delta[16]; 66 67 char *drumset[11] = 68 { 69 "Standard Kit", "Room Kit", "Power Kit", "Electronic Kit", "TR-808 Kit", 70 "Jazz Kit", "Brush Kit", "Orchestra Kit", "Sound FX Kit", 71 "Program Kit", "MT-32 Kit" 72 }; 73 74 #define SET(x) (x == 8 ? 1 : x >= 16 && x <= 23 ? 2 : x == 24 ? 3 : \ 75 x == 25 ? 4 : x == 32 ? 5 : x >= 40 && x <= 47 ? 6 : \ 76 x == 48 ? 7 : x == 56 ? 8 : x >= 96 && x <= 111 ? 9 : \ 77 x == 127 ? 10 : 0) 78 79 void close_show(error) 80 int error; 81 { 82 exit(error); 83 } 84 85 void ExitCallback(w, client_data, call_data) 86 Widget w; 87 XtPointer client_data; 88 XtPointer call_data; 89 { 90 close_show(0); 91 } 92 93 void MaskCallback(w, client_data, call_data) 94 Widget w; 95 XtPointer client_data; 96 XtPointer call_data; 97 { 98 int i, j, k; 99 for (i = 0; i < 16 && cbutton[i] != w; i++); 100 if (i < 0 || i >= 16) 101 return; 102 if (call_data) /* channel should be enabled */ 103 chanmask |= (1 << i); 104 else { /* need to stop all notes in channel */ 105 chanmask &= (~(1 << i)); 106 if (ISGUS(i)) 107 k = gus_dev; 108 else if (ISFM(i)) 109 k = sb_dev; 110 else 111 k = ext_dev; 112 for (j = 0; j < 128; j++) 113 if (note_vel[i][j]) 114 seq_stop_note(k, i, j, note_vel[i][j] = 0); 115 } 116 } 117 118 void SkewCallback(w, client_data, call_data) 119 Widget w; 120 XtPointer client_data; 121 XtPointer call_data; 122 { 123 float position; 124 125 position = *(float *) call_data; 126 skew = (position * 3.75) + 0.25; 127 sprintf(textbuf, "%1.2f", skew); 128 XtVaSetValues(skew_label, XtNlabel, textbuf, NULL); 129 }; 130 131 void SkewIncCallback(w, client_data, call_data) 132 Widget w; 133 XtPointer client_data; 134 XtPointer call_data; 135 { 136 double s_position; 137 int movewhere; 138 139 movewhere = (int) call_data; 140 if (movewhere < 0) { 141 skew += 0.01; 142 if (skew > 4.0) 143 skew = 4.0; 144 } else if (movewhere > 0) { 145 skew -= 0.01; 146 if (skew < 0.25) 147 skew = 0.25; 148 } else 149 return; 150 s_position = (skew - 0.25) / 3.75; /* scale is 0.0 - 1.0 */ 151 XawScrollbarSetThumb(scrollbar, s_position, 0.01); 152 sprintf(textbuf, "%1.2f", skew); 153 XtVaSetValues(skew_label, XtNlabel, textbuf, NULL); 154 }; 155 156 void PrevSongCallback(w, client_data, call_data) 157 Widget w; 158 XtPointer client_data; 159 XtPointer call_data; 160 { 161 if ((skew += 0.01) > 4) 162 seq_reset(); 163 return_do_what = -1; 164 } 165 166 void RepeatCallback(w, client_data, call_data) 167 Widget w; 168 XtPointer client_data; 169 XtPointer call_data; 170 { 171 seq_reset(); 172 return_do_what = 0; 173 } 174 175 void NextSongCallback(w, client_data, call_data) 176 Widget w; 177 XtPointer client_data; 178 XtPointer call_data; 179 { 180 seq_reset(); 181 return_do_what = 1; 182 } 183 184 void updateMeter(chn) 185 int chn; 186 { 187 XtVaSetValues(cmeter[chn], XtNwidth, 5 + vu_level[chn], NULL); 188 } 189 190 XtIntervalId mytimerid; 191 Boolean return_now; 192 int oldsec = 0, oldusec = 0; 193 194 void TimerCallback(w, client_data, timerid) 195 Widget w; 196 XtPointer client_data; 197 XtIntervalId timerid; 198 { 199 int i, sec, usec; 200 201 /* update vu meter values */ 202 for (i = 0; i < 16; i++) 203 if (vu_delta[i]) { /* decay meter */ 204 vu_level[i] -= vu_delta[i]; 205 if (vu_level[i] < 0) 206 vu_level[i] = vu_delta[i] = 0; 207 updateMeter(i); 208 } 209 gettimeofday(&now_time, NULL); 210 sec = now_time.tv_sec - start_time.tv_sec; 211 usec = now_time.tv_usec - start_time.tv_usec; 212 if (usec < 0) 213 (usec += 1000000, sec -= 1); 214 usec /= 100000; /* change to 10ths of a second */ 215 sprintf(textbuf, "%02d:%02d.%d", sec / 60, sec % 60, usec); 216 if (sec > oldsec || usec > oldusec) { 217 XtVaSetValues(status, XtNstring, textbuf, NULL); 218 oldsec = sec; oldusec = usec; 219 } 220 if (return_do_what != NO_EXIT || cdeltat(&want_time, &now_time) < 15) 221 return_now = True; 222 else 223 mytimerid = XtAppAddTimeOut(context, 100, TimerCallback, NULL); 224 } 225 226 XEvent event; 227 228 int updatestatus() 229 { 230 XtInputMask mask; 231 232 return_now = False; 233 want_time.tv_sec = start_time.tv_sec + (ticks / 100); 234 want_time.tv_usec = start_time.tv_usec + (ticks % 100) * 10000; 235 if (want_time.tv_usec > 1000000) 236 (want_time.tv_usec -= 1000000, want_time.tv_sec++); 237 return_do_what = NO_EXIT; 238 TimerCallback(context, NULL, 0); /* start timer */ 239 do { /* toolkit main loop */ 240 if ((mask = XtAppPending(context))) /* non-blocking */ 241 XtAppProcessEvent(context, mask); 242 #ifndef HOG_CPU 243 if (!mask && !return_now) { /* sleep 1/10th of a second */ 244 usleep(750); 245 } 246 #endif 247 } while (!return_now || mask); 248 return return_do_what; 249 } 250 251 252 void AppendText(s) 253 char *s; 254 { 255 XawTextBlock textblk; 256 static XawTextPosition whereami; 257 258 259 if (s == NULL) { /* reset text to empty */ 260 XtVaSetValues(text, XtNstring, "", NULL); 261 whereami = 0; 262 return; 263 } 264 textblk.firstPos = 0; 265 textblk.length = strlen(s); 266 textblk.ptr = s; 267 textblk.format = XawFmt8Bit; 268 XawTextReplace(text, whereami, whereami, &textblk); 269 whereami += textblk.length; 270 } 271 272 #define TEXTBUF (&textbuf[strlen(textbuf)]) 273 274 void showevent(cmd, data, length) 275 int cmd; 276 unsigned char *data; 277 int length; 278 { 279 int chn, note, vel, i; 280 281 chn = (cmd & 0x0f); 282 note = (data[0] & 0x7f); 283 vel = (data[1] & 0x7f); 284 if (cmd < 8 && cmd > 0) { 285 strncpy(textbuf, data, length < TEXTSIZE - 4 ? length : TEXTSIZE - 3); 286 textbuf[(length > TEXTSIZE + 2) ? TEXTSIZE - 2 : length] = '\0'; 287 sprintf(TEXTBUF, "\n"); 288 AppendText(textbuf); 289 if (verbose > 2) 290 printf(textbuf); 291 } else if (cmd & 0x80) 292 switch (cmd & 0xf0) { 293 case MIDI_NOTEON: 294 if (!vel && vu_level[chn]) 295 vu_delta[chn] = 32; 296 else 297 (vu_level[chn] = vel, vu_delta[chn] = 0); 298 updateMeter(chn); 299 break; 300 case MIDI_NOTEOFF: 301 if (vu_level[chn]) 302 vu_delta[chn] = vel / 2; 303 updateMeter(chn); 304 break; 305 case MIDI_PGM_CHANGE: 306 if (!ISPERC(chn)) 307 XtVaSetValues(cbutton[chn], XtNlabel, gmvoice[note], NULL); 308 else 309 XtVaSetValues(cbutton[chn], XtNlabel, drumset[SET(note)], 310 NULL); 311 break; 312 case 0xf0: 313 case 0xf7: 314 sprintf(textbuf, "Sysex(%2x): ", cmd); 315 for (i = 0; i < length && i < (TEXTSIZE / 2) - 16; i++) 316 sprintf(TEXTBUF, "%02x", data[i]); 317 sprintf(TEXTBUF, "\n"); 318 AppendText(textbuf); 319 if (verbose > 2) 320 printf(textbuf); 321 break; 322 default: 323 break; 324 } 325 } 326 327 void init_show() 328 { 329 int i; 330 double s_position; 331 332 AppendText(NULL); /* reset text window */ 333 s_position = (skew - 0.25) / 3.75; /* scale is 0.0 - 1.0 */ 334 sprintf(textbuf, "%1.2f", skew); 335 XawScrollbarSetThumb(scrollbar, s_position, 0.01); 336 XtVaSetValues(skew_label, XtNlabel, textbuf, NULL); 337 XStoreName(XtDisplay(toplevel), XtWindow(toplevel), filename); 338 oldsec = oldusec = 0; 339 for (i = 0; i < 16; i++) { 340 vu_level[i] = vu_delta[i] = 0; 341 updateMeter(i); 342 sprintf(textbuf, "channel%02d", i + 1); 343 XtVaSetValues(cbutton[i], XtNlabel, textbuf, NULL); 344 } 345 XtVaSetValues(status, XtNlabel, "00:00.0" , NULL); 346 } 347 348 void setup_show(argc, argv) 349 int argc; 350 char **argv; 351 { 352 int i; 353 Pixel bg, fg; 354 Display *mydisp; 355 Screen *myscreen; 356 Window mywin; 357 358 graphics++; /* force -r option if not selected */ 359 toplevel = XtVaOpenApplication(&context, "XPlaymidi", NULL, 0, 360 &argc, argv, NULL, sessionShellWidgetClass, XtNminWidth, 425, 361 XtNwidth, 425, XtNminHeight, 479, XtNheight, 500, NULL); 362 manager = XtVaCreateManagedWidget("controls", formWidgetClass, 363 toplevel, XtNdefaultDistance, 2, NULL); 364 titlebar = XtVaCreateManagedWidget("titlebar", labelWidgetClass, 365 manager, XtNtop, XawChainTop, XtNright, XawChainRight, XtNleft, 366 XawChainLeft, XtNborderWidth, 0, XtNfromVert, cbutton[15], 367 XtNlabel, "X" RELEASE " by Nathan Laredo" , NULL); 368 skew_label = XtVaCreateManagedWidget("skew", labelWidgetClass, manager, 369 XtNborderWidth, 0, XtNleft, XawChainLeft, XtNfromVert, titlebar, 370 XtNlabel, "1.00", NULL); 371 scrollbar = XtVaCreateManagedWidget("scrollbar", scrollbarWidgetClass, 372 manager, XtNorientation, XtorientHorizontal, XtNlength, 365, 373 XtNfromVert, titlebar, XtNfromHoriz, skew_label, NULL); 374 text = XtVaCreateManagedWidget("text", asciiTextWidgetClass, manager, 375 XtNbottom, XawChainBottom, XtNleft, XawChainLeft, XtNfromVert, 376 skew_label, XtNeditType, XawtextAppend, XtNheight, 400, 377 XtNwidth, 132, XtNscrollHorizontal, XawtextScrollAlways, 378 XtNscrollVertical, XawtextScrollAlways, XtNtype, XawAsciiString, 379 XtNdisplayCaret, False, NULL); 380 for (i = 0; i < 16; i++) { 381 sprintf(textbuf, "channel%02d", i + 1); 382 cbutton[i] = XtVaCreateManagedWidget(textbuf, toggleWidgetClass, 383 manager, XtNstate, (ISPLAYING(i) ? True : False), 384 XtNwidth, 100, XtNjustify, XtJustifyLeft, XtNfromHoriz, text, 385 XtNresizable, False, XtNresize, False, NULL); 386 if (!i) 387 XtVaSetValues(cbutton[i], XtNfromVert, scrollbar, NULL); 388 else 389 XtVaSetValues(cbutton[i], XtNfromVert, cbutton[i - 1], NULL); 390 XtAddCallback(cbutton[i], XtNcallback, MaskCallback, NULL); 391 sprintf(textbuf, "meter%02d", i + 1); 392 cmeter[i] = XtVaCreateManagedWidget(textbuf, toggleWidgetClass, 393 manager, XtNsensitive, False, XtNfromHoriz, cbutton[i], 394 XtNlabel, "", XtNresize, False, XtNjustify, XtJustifyLeft, 395 XtNwidth, 5, XtNresizable, True, NULL); 396 if (!i) 397 XtVaSetValues(cmeter[i], XtNfromVert, scrollbar, NULL); 398 else 399 XtVaSetValues(cmeter[i], XtNfromVert, cmeter[i - 1], NULL); 400 } 401 exit_button = XtVaCreateManagedWidget("exit", commandWidgetClass, 402 manager, XtNfromHoriz, text, XtNfromVert, cbutton[15], 403 XtNinternalWidth, 1, XtNinternalHeight, 0, XtNborderWidth, 0, NULL); 404 prevsong = XtVaCreateManagedWidget("prevfile", commandWidgetClass, 405 manager, XtNfromHoriz, exit_button, XtNfromVert, cbutton[15], 406 XtNinternalWidth, 1, XtNinternalHeight, 0, XtNborderWidth, 0, NULL); 407 repeatsong = XtVaCreateManagedWidget("repeat", commandWidgetClass, 408 manager, XtNfromHoriz, prevsong, XtNfromVert, cbutton[15], 409 XtNinternalWidth, 1, XtNinternalHeight, 0, XtNborderWidth, 0, NULL); 410 nextsong = XtVaCreateManagedWidget("nextfile", commandWidgetClass, 411 manager, XtNfromHoriz, repeatsong, XtNfromVert, cbutton[15], 412 XtNinternalWidth, 1, XtNinternalHeight, 0, XtNborderWidth, 0, NULL); 413 status = XtVaCreateManagedWidget("status", asciiTextWidgetClass, 414 manager, XtNfromHoriz, nextsong, XtNfromVert, cbutton[15], 415 XtNborderWidth, 0, XtNstring, "--:--.-", XtNeditType, XawtextRead, 416 XtNdisplayCaret, False, XtNuseStringInPlace, True, NULL); 417 XtAddCallback(scrollbar, XtNjumpProc, SkewCallback, NULL); 418 XtAddCallback(scrollbar, XtNscrollProc, SkewIncCallback, NULL); 419 XtAddCallback(exit_button, XtNcallback, ExitCallback, NULL); 420 XtAddCallback(nextsong, XtNcallback, NextSongCallback, NULL); 421 XtAddCallback(repeatsong, XtNcallback, RepeatCallback, NULL); 422 XtAddCallback(prevsong, XtNcallback, PrevSongCallback, NULL); 423 myscreen = XtScreen(toplevel); 424 mydisp = DisplayOfScreen(myscreen); 425 mywin = RootWindowOfScreen(myscreen); 426 fg = WhitePixelOfScreen(myscreen); 427 bg = BlackPixelOfScreen(myscreen); 428 Exit = XCreatePixmapFromBitmapData(mydisp, mywin, Eject_bits, 429 Eject_width, Eject_height, fg, bg, 1); 430 XtVaSetValues(exit_button, XtNbitmap, Exit, NULL); 431 Prev = XCreatePixmapFromBitmapData(mydisp, mywin, Prev_bits, 432 Prev_width, Prev_height, fg, bg, 1); 433 XtVaSetValues(prevsong, XtNbitmap, Prev, NULL); 434 Play = XCreatePixmapFromBitmapData(mydisp, mywin, Play_bits, 435 Play_width, Play_height, fg, bg, 1); 436 XtVaSetValues(repeatsong, XtNbitmap, Play, NULL); 437 Next = XCreatePixmapFromBitmapData(mydisp, mywin, Next_bits, 438 Next_width, Next_height, fg, bg, 1); 439 XtVaSetValues(nextsong, XtNbitmap, Next, NULL); 440 XtRealizeWidget(toplevel); 441 } 442