Històries amb la Wacom
Contingut
Introducció
Allò bàsic és utilitzar la Wacom com a controlador (per ex, com faig amb el Wacom Theremin), i poder generar sons sintètics amb la wacom: és a dir, quan guixo amb la wacom generar un so sintètic similar a un guixot. A partir d'aquí, utilitzar Gimp per a dibuixar i explicar una història, i que hi hagi efectes visuals. D'entrada se m'ocorren dues aplicacions divertides que es tradueixen a espectacles/performances per a escoles o grups:
- per ex, Història de la Ciència, o similar (Història de qualsevol cosa, ..., La Crisi explicada als idiotes, el que sigui...). La idea és explicar una història, un argument, un monòleg,... i anar dibuixant, amb efectes sonors de guixots. Però a més, hi ha una aplicació pel darrera (típicament un client JACK) que sap en quin moment de l'argument estic, i que pot afegir efectes sonors (plugins,..., executar notes midi,...)
- espectacle Teoria Musical. Es tracta d'anar sorprenent l'espectador de forma continuada. Per exemple pinto el pentagrama (seran 5 sons de guixot); pinto una nota, i sona la nota; pinto vàries notes que van sonant...; pinto el signe de Play, Pause, Stop, i quan clico sobre Play va sonant el que he escrit. És necessari un programa que sigui client de JACK, i que sàpiga en quin moment de l'espectacle estic. En un fitxer XML pot haver-hi una llista dels events que van succeint,...
Amb aquestes dues idees es poden desenvolupar espectacles atractius, sorprenents i originals, ben adreçat a escoles i instituts, escoles de música, i també a públic adult. També lliga amb fer monòlegs,...
Per produir el so he de mirar quina és la manera més fàcil i correcta de fer síntesi:
- a pèl amb JACK
- amb LV2: http://ll-plugins.nongnu.org/lv2pftci/. En aquest enllaç s'ensenya com fer un Beep, i la API de LV2 té la classe Synth.
- STK: Synth Toolkit in C++. On faig les proves de STK hi ha l'enllaç a aquell projecte que amb micròfons de contacte es generen efectes de síntesi molt originals: tots els elements (un arbre, una taula,...) passen a ser objectes sonors.
GIMP i 2 monitors
Programació Wacom + GIMP
En un altre lloc s'ha comentat sobre el codi xopen.c i com puc detectar de forma fàcil les comandes que provenen de la wacom. Això servei, entre d'altres coses, per fer la Wacom Theremin.
El problema que es planteja en aquest cas, treballar amb la wacom i GIMP, i programar al mateix temps, és que el codi que tinc de xopen.c no envia per pantalla flux de dades quan estic dibuixant amb el GIMP. Això és bàsic, doncs recordem el que es vol:
- Es vol explicar una història mentre es va dibuixant amb el GIMP. Els events de la wacom controlen una aplicació, que serà un client JACK (JACK Audio i JACK Midi), de manera que es dispararan efectes sonors mentre es va dibuixant.
Ara bé, m'estic trobant que quan dibuixo amb el GIMP, xopen.c no vomita missatges per pantalla. Així doncs, aquí discutiré aquest fet.
prova_mouse.c
Primer de tot, prova_mouse.c funciona perfectament.
$gcc -o prova_mouse prova_mouse.c
prova_mouse.c
#include <stdio.h> #include <stdlib.h> main(){ FILE *fmouse; char b[3]; fmouse = fopen("/dev/input/mice","r"); int xd=0,yd=0; //x/y movement delta int xo=0,yo=0; //x/y overflow (out of range -255 to +255) int lb=0,mb=0,rb=0,hs=0,vs=0; //left/middle/right mousebutton int run=0; while(!run){ fread(b,sizeof(char),3,fmouse); lb=(b[0]&1)>0; rb=(b[0]&2)>0; mb=(b[0]&4)>0; hs=(b[0]&16)>0; vs=(b[0]&32)>0; xo=(b[0]&64)>0; yo=(b[0]&128)>0; xd=b[1]; yd=b[2]; printf("hs=%d,vs=%d,lb=%d rm=%d mb=%d xo=%d yo=%d xd=%d yd=%d\n",hs,vs,lb,rb,mb,xo,yo,xd,yd); } fclose(fmouse); }
que funciona perfectament vol dir que mentre estic dibuixant amb el GIMP, per la consola on corre prova_mouse.c està vomitant els missatges.
xopen_mouse.c
Modifico xopen.c per tal de detectar els events del mouse (i no de la wacom). Tinc dues versions, xopen_mouse.c i xopen_mouse_v2.c. La primera només detecta el moviment del ratolí, i no els botons. I la segona versió, més general, detecta la pressió i el release dels botons.
$ xinput list ... Resolution is 1 Axis 1 : Min_value is -1 Max_value is -1 Resolution is 1 "Logitech USB Optical Mouse" id=4 [XExtensionPointer] Num_buttons is 32
El valor que s'ha de ficar en el programa serà:
#define MOUSENAME "Logitech USB Optical Mouse"
xopen_mouse.c:
$ gcc -o xopen_mouse xopen_mouse.c -lX11 -lXi -lXext
/* Trivial little XINPUT program written to test "raw" access to * XINPUT devices, without gtk wrappers or anything else. * by Philip Brown * Written primarily to test my wacom tablet driver. * http://www.bolthole.com/solaris/drivers/wacom.html * * * Note that it expects hardcoded special XINPUT dev names, * "wacomdev1" and "wacomdev2". * * It will then open those devices, and print out movement info when * you use those devices. * * compile with * cc -o xopen xopen.c -lX11 -lXi -lXext */ #define MOUSENAME "Logitech USB Optical Mouse" #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/Xos.h> #include <X11/extensions/XInput.h> #include <stdio.h> int main (int argc, char **argv) { Display *display; int screen, ndev, i; XDeviceInfo *devinfo; XID mouseID = 0; XDevice *mousedev; XEventClass EClist[10]; int meventtype, numtypes=0; XEventClass meventclass; XEvent event_return; if ((display = XOpenDisplay (NULL)) == NULL) { fprintf (stderr, "failed to connect to X server (%s)\n", XDisplayName (NULL)); return 1; } screen = DefaultScreen (display); if ((devinfo = XListInputDevices (display, &ndev)) == NULL) { fprintf (stderr, "Failed to get list of devices\n"); return 1; } printf ("Number of devices is %d\n", ndev); for (i = 0; i < ndev; i++) { printf ("%d. %s(%d)\n", i, devinfo[i].name, devinfo[i].id); if (strcmp (devinfo[i].name, MOUSENAME) == 0) { mouseID = devinfo[i].id; } } XFreeDeviceList (devinfo); if (mouseID == 0) { puts ("no mouse dev"); exit (0); } printf ("mouseid = %d\n", mouseID); mousedev = XOpenDevice (display, mouseID); printf("mousedev=%x\n",mousedev); numtypes+=1; /* bizzaro macro, to SET arg 2 and 3. Grrr. */ DeviceMotionNotify (mousedev, meventtype, EClist[0]); printf ("DEBUG: motioneventtype is supposedly %d\n", meventtype); XSelectExtensionEvent (display, RootWindow (display, screen), EClist, numtypes); puts ("starting loop"); while (1) { XDeviceMotionEvent *meventptr; XNextEvent (display, &event_return); printf ("Got XEvent of type %d (mevent=%d)\n", event_return.type, meventtype); if (event_return.type == meventtype) { meventptr = (XDeviceMotionEvent *) & event_return; printf ("data is %d,%d,%d (devid %d)\n", meventptr->axis_data[0], meventptr->axis_data[1], meventptr->axis_data[2], meventptr->deviceid); } } XCloseDevice (display, mousedev); }
xopen_mouse_v2.c
$ gcc -o xopen_mouse xopen_mouse_v2.c -lX11 -lXi -lXext
/* Trivial little XINPUT program written to test "raw" access to * XINPUT devices, without gtk wrappers or anything else. * by Philip Brown * Written primarily to test my wacom tablet driver. * http://www.bolthole.com/solaris/drivers/wacom.html * * * Note that it expects hardcoded special XINPUT dev names, * "wacomdev1" and "wacomdev2". * * It will then open those devices, and print out movement info when * you use those devices. * * compile with * cc -o xopen xopen.c -lX11 -lXi -lXext */ //també (button press i button release): http://www-evasion.imag.fr/~Francois.Faure/doc/inventorToolmaker/sgi_html/ch11.html #define MOUSENAME "Logitech USB Optical Mouse" #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/Xos.h> #include <X11/extensions/XInput.h> #include <stdio.h> int main (int argc, char **argv) { Display *display; int screen, ndev, i; XDeviceInfo *devinfo; XID mouseID = 0; XDevice *mousedev; XEventClass EClist[10]; int motionEventType, buttonPressEventType, buttonReleaseEventType, numtypes=0; XEventClass meventclass; XEvent event_return; if ((display = XOpenDisplay (NULL)) == NULL) { fprintf (stderr, "failed to connect to X server (%s)\n", XDisplayName (NULL)); return 1; } screen = DefaultScreen (display); if ((devinfo = XListInputDevices (display, &ndev)) == NULL) { fprintf (stderr, "Failed to get list of devices\n"); return 1; } printf ("Number of devices is %d\n", ndev); for (i = 0; i < ndev; i++) { printf ("%d. %s(%d)\n", i, devinfo[i].name, devinfo[i].id); if (strcmp (devinfo[i].name, MOUSENAME) == 0) { mouseID = devinfo[i].id; } } XFreeDeviceList (devinfo); if (mouseID == 0) { puts ("no mouse dev"); exit (0); } printf ("mouseid = %d\n", mouseID); mousedev = XOpenDevice (display, mouseID); printf("mousedev=%x\n",mousedev); /* bizzaro macro, to SET arg 2 and 3. Grrr. */ DeviceMotionNotify (mousedev, motionEventType, EClist[numtypes]); numtypes+=1; DeviceButtonPress(mousedev, buttonPressEventType, EClist[numtypes]); numtypes+=1; DeviceButtonRelease(mousedev, buttonReleaseEventType, EClist[numtypes]); numtypes+=1; printf ("DEBUG: motioneventtype is supposedly %d\n", motionEventType); printf ("DEBUG: buttonPressEventType is supposedly %d\n", buttonPressEventType); printf ("DEBUG: buttonReleaseEventType is supposedly %d\n", buttonReleaseEventType); XSelectExtensionEvent (display, RootWindow (display, screen), EClist, numtypes); puts ("starting loop"); while (1) { XDeviceMotionEvent *meventptr; XNextEvent (display, &event_return); if (event_return.type == motionEventType) { printf ("Got XEvent of type %d (motionevent=%d)\n", event_return.type, motionEventType); meventptr = (XDeviceMotionEvent *) & event_return; printf ("data is %d,%d,%d (devid %d)\n", meventptr->axis_data[0], meventptr->axis_data[1], meventptr->axis_data[2], meventptr->deviceid); } if (event_return.type == buttonPressEventType) { printf ("Got XEvent of type %d (buttonpressevent=%d)\n", event_return.type, buttonPressEventType); meventptr = (XDeviceButtonEvent *) & event_return; printf ("data is %d,%d,%d (devid %d)\n", meventptr->axis_data[0], meventptr->axis_data[1], meventptr->axis_data[2], meventptr->deviceid); } if (event_return.type == buttonReleaseEventType) { printf ("Got XEvent of type %d (buttonpressevent=%d)\n", event_return.type, buttonReleaseEventType); meventptr = (XDeviceButtonEvent *) & event_return; printf ("data is %d,%d,%d (devid %d)\n", meventptr->axis_data[0], meventptr->axis_data[1], meventptr->axis_data[2], meventptr->deviceid); } } XCloseDevice (display, mousedev); }
Hi ha una diferència important entre les dues versions: la primera no treballa bé amb GIMP (quan dibuixo en el GIMP no vomita res, tot i que quan em moc sobre la finestra principal del GIMP sí que vomita); i en canvi la versió _v2 té el comportament esperat (cal arrencar GIMP com a sudo?). És a dir, he de detectar els events buttonPressEventType i buttonReleaseEventType. I això és precisament el que també he de fer amb la wacom per tal de què treballi bé amb GIMP, detectar aquests dos events: buttonPressEventType i buttonReleaseEventType-
xopen_wacom.c (solució)
Només em centro en el llapis principal, no amb el Erase. Són molts pocs canvis respecte l'anterior. La clau està en què per treballar bé amb el GIMP he de notificar el pressbuton i el releasebutton (no sé per què).
$ gcc -o xopen_wacom xopen_wacom.c -lX11 -lXi -lXext
/* Trivial little XINPUT program written to test "raw" access to * XINPUT devices, without gtk wrappers or anything else. * by Philip Brown * Written primarily to test my wacom tablet driver. * http://www.bolthole.com/solaris/drivers/wacom.html * * * Note that it expects hardcoded special XINPUT dev names, * "wacomdev1" and "wacomdev2". * * It will then open those devices, and print out movement info when * you use those devices. * * compile with * cc -o xopen xopen.c -lX11 -lXi -lXext */ //també (button press i button release): http://www-evasion.imag.fr/~Francois.Faure/doc/inventorToolmaker/sgi_html/ch11.html #define PENNAME "Wacom Intuos2 12x12" #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/Xos.h> #include <X11/extensions/XInput.h> #include <stdio.h> int main (int argc, char **argv) { Display *display; int screen, ndev, i; XDeviceInfo *devinfo; XID tabletID = 0; XDevice *tabletdev; XEventClass EClist[10]; int motionEventType, buttonPressEventType, buttonReleaseEventType, numtypes=0; XEventClass meventclass; XEvent event_return; if ((display = XOpenDisplay (NULL)) == NULL) { fprintf (stderr, "failed to connect to X server (%s)\n", XDisplayName (NULL)); return 1; } screen = DefaultScreen (display); if ((devinfo = XListInputDevices (display, &ndev)) == NULL) { fprintf (stderr, "Failed to get list of devices\n"); return 1; } printf ("Number of devices is %d\n", ndev); for (i = 0; i < ndev; i++) { printf ("%d. %s(%d)\n", i, devinfo[i].name, devinfo[i].id); if (strcmp (devinfo[i].name, PENNAME) == 0) { tabletID = devinfo[i].id; } } XFreeDeviceList (devinfo); if (tabletID == 0) { puts ("no tablet dev"); exit (0); } printf ("tabletid = %d\n", tabletID); tabletdev = XOpenDevice (display, tabletID); printf("tabletdev=%x\n",tabletdev); /* bizzaro macro, to SET arg 2 and 3. Grrr. */ DeviceMotionNotify (tabletdev, motionEventType, EClist[numtypes]); numtypes+=1; DeviceButtonPress(tabletdev, buttonPressEventType, EClist[numtypes]); numtypes+=1; DeviceButtonRelease(tabletdev, buttonReleaseEventType, EClist[numtypes]); numtypes+=1; printf ("DEBUG: motioneventtype is supposedly %d\n", motionEventType); printf ("DEBUG: buttonPressEventType is supposedly %d\n", buttonPressEventType); printf ("DEBUG: buttonReleaseEventType is supposedly %d\n", buttonReleaseEventType); XSelectExtensionEvent (display, RootWindow (display, screen), EClist, numtypes); puts ("starting loop"); while (1) { XDeviceMotionEvent *meventptr; XNextEvent (display, &event_return); if (event_return.type == motionEventType) { printf ("Got XEvent of type %d (motionevent=%d)\n", event_return.type, motionEventType); meventptr = (XDeviceMotionEvent *) & event_return; printf ("data is %d,%d,%d (devid %d)\n", meventptr->axis_data[0], meventptr->axis_data[1], meventptr->axis_data[2], meventptr->deviceid); } if (event_return.type == buttonPressEventType) { printf ("Got XEvent of type %d (buttonpressevent=%d)\n", event_return.type, buttonPressEventType); meventptr = (XDeviceButtonEvent *) & event_return; printf ("data is %d,%d,%d (devid %d)\n", meventptr->axis_data[0], meventptr->axis_data[1], meventptr->axis_data[2], meventptr->deviceid); } if (event_return.type == buttonReleaseEventType) { printf ("Got XEvent of type %d (buttonpressevent=%d)\n", event_return.type, buttonReleaseEventType); meventptr = (XDeviceButtonEvent *) & event_return; printf ("data is %d,%d,%d (devid %d)\n", meventptr->axis_data[0], meventptr->axis_data[1], meventptr->axis_data[2], meventptr->deviceid); } } XCloseDevice (display, tabletdev); }
nota important: arrencar GIMP com a sudo. xopen_wacom no cal. De fet no és tan estrany que funcioni amb buttonpress i buttonrelease, doncs aquests events són importants per a notificar al GIMP que es comenci a dibuixar.
Fer moure el ratolí tot sol
Programàticament podem fer moure el ratolí, disparar tecles del teclat,...
$ sudo apt-get install xautomation $ xte 'mousermove 1 1'
A linux també hi ha xsendkeys (sudo apt-get install lineakd), però no cal si ja tenim xte.
Projectes
- Històries amb Wacom. Projecte mínim gimp + wacom + audio + midi
- Teoria de la música explicada als nens (Històries amb Wacom)+GIMP
creat per Joan Quintana Compte, gener 2012