/* scale.c : play a scale through "keyboard bell" of an X display. * * Written by Alan Iwi, 20 December '97, mainly because I'm trying to * learn some C, but who knows, *somebody* out there might find it useful... * * Tested on i386 Linux platform. Public domain. No warranties etc. * * Compile with, e.g.: "gcc -o scale -L/usr/X11/lib -lX11 -lm scale.c" */ #include #include #include #include #include #include #include #define BASE 440. /* default starting freq in Hz */ void usage(void); void play(int,int,int); void set_bell(int,int); void stophandle(int); void susphandle(int); XKeyboardState initkbstate; Display *display; int main(int argc, char **argv) { typedef struct { int nup; int ndown; int *up; int top; int *down; } Scale; int ptrn1[7]={0,2,4,5,7,9,11}; int ptrn2[7]={0,2,3,5,7,8,11}; int ptrn3[7]={0,2,3,5,7,9,11}; int ptrn4[7]={0,2,3,5,7,8,10}; int ptrn5[3]={0,4,7}; int ptrn6[3]={0,3,7}; int ptrn7[12]={0,1,2,3,4,5,6,7,8,9,10,11}; Scale major={7,7,ptrn1,12,ptrn1}; Scale hminor={7,7,ptrn2,12,ptrn2}; Scale mminor={7,7,ptrn3,12,ptrn4}; Scale arpeg={3,3,ptrn5,12,ptrn5}; Scale marpeg={3,3,ptrn6,12,ptrn6}; Scale chrom={12,12,ptrn7,12,ptrn7}; Scale *scale; char *dispname; int duration,volume,octave,octaves,note,transpose,uponly; extern int opterr; extern char* optarg; char opt; const char optstr[]="aAcd:D:hmo:t:uv:"; /* set some default values */ uponly=transpose=volume=0; duration=200; dispname=getenv("DISPLAY"); scale=&major; octaves=1; /* parse arguments */ opterr=0; while ((opt=getopt(argc,argv,optstr))!=EOF) { if (opt=='?') usage(); switch(opt) { case 'a': scale=&arpeg; break; case 'A': scale=&marpeg; break; case 'c': scale=&chrom; break; case 'h': scale=&hminor; break; case 'm': scale=&mminor; break; case 'd': dispname=(char *)malloc(strlen(optarg)+1); strcpy(dispname,optarg); break; case 'D': if (sscanf(optarg,"%d",&duration)!=1) usage(); if (duration<=0) usage(); break; case 'o': if (sscanf(optarg,"%d",&octaves)!=1) usage(); if (octaves<0) usage(); break; case 't': if (sscanf(optarg,"%d",&transpose)!=1) usage(); break; case 'u': uponly=1; break; case 'v': if (sscanf(optarg,"%d",&volume)!=1) usage(); if (abs(volume)>100) usage(); break; } } /* open display */ if (!(display=XOpenDisplay(dispname))) { fprintf(stderr,"Can't open display\n"); return(-1); } /* get current bell info, save for later */ XGetKeyboardControl(display,&initkbstate); /* now trap some signals, so we can try to leave bell parameters in original state */ signal(SIGHUP,stophandle); signal(SIGINT,stophandle); signal(SIGTERM,stophandle); signal(SIGTSTP,susphandle); /* play scale */ for (octave=0;octavenup;note++) play(transpose+scale->top*octave+scale->up[note],duration,volume); play(transpose+scale->top*octaves,duration,volume); if (!uponly) for (octave=octaves-1;octave>=0;octave--) for (note=scale->ndown-1;note>=0;note--) play(transpose+scale->top*octave+scale->down[note],duration,volume); /* put bell settings back as before */ set_bell(initkbstate.bell_pitch,initkbstate.bell_duration); /* close display */ XCloseDisplay(display); return(0); } void play(int pitch,int duration,int volume) { set_bell((int)(0.5+pow(2.,(float)(pitch)/12.)*BASE),duration); XBell(display,volume); XFlush(display); usleep(duration*1000L); } void set_bell(int pitch,int duration) { XKeyboardControl changes; changes.bell_duration=duration; changes.bell_pitch=pitch; XChangeKeyboardControl(display,KBBellDuration|KBBellPitch,&changes); } #define USEPRT(msg) fprintf(stderr,msg"\n") void usage() { USEPRT("Usage: scale "); USEPRT(" -a play major arpeggio "); USEPRT(" -A play minor arpeggio "); USEPRT(" -c play chromatic scale "); USEPRT(" -d display display name of X server on which to play scale "); USEPRT(" -D duration duration of each note in milliseconds "); USEPRT(" -h play harmonic minor scale "); USEPRT(" -m play melodic minor scale "); USEPRT(" -o octaves number of octaves to play "); USEPRT(" -t transpose number of semitones to transpose up starting note"); USEPRT(" -u up only: only play the ascending part "); USEPRT(" -v volume volume enhancement, from +100 to -100 "); USEPRT("By default, plays scale of A major. "); USEPRT(" "); exit(1); } void stophandle(int signo) { signal(signo,SIG_IGN); set_bell(initkbstate.bell_pitch,initkbstate.bell_duration); XCloseDisplay(display); exit(signo+128); } void susphandle(int signo) { signal(signo,SIG_IGN); set_bell(initkbstate.bell_pitch,initkbstate.bell_duration); XFlush(display); signal(signo,SIG_DFL); raise(signo); signal(signo,susphandle); }