#include #include float Tempo = 200., atof(); int midi, Annoy=1; int GlitchGap = 1; Error(s){ MidiError("%s: ", av0), perror(s), exit(1); } RandomNote() /* ** Play a random note with random velocity for a random time: ** turn the note on, pause a bit, then turn it off. */ { int p; #define rnd(a,b) (random()%(b-a)+a) /* return # in range a...b */ NoteOn(midi, 0, p=rnd(dx7_MIN,dx7_MAX), velocity()); fsleep((float)(rnd(10,100))/Tempo); NoteOff(midi, 0, p); } #d MAXdown 20 int Down[MAXdown]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; int Ndown=0; #d Pitch(c) c[1] #d Velocity(c) c[2] EmptyDown(){ Int i; loop(i,MAXdown) if (!Down[i]) return i; return MAXdown - 1; } PitchDown(p){ Int i; loop(i,MAXdown) if (Down[i] == p) return i; return MAXdown - 1; } RandomDown(){ Int i, limit=0; if (Ndown <= 0) return -1; do i = random()%MAXdown; while (!Down[i] && ++limit < 200); return Down[i]; } next(n){ ++n; return n=0? n : MAXdown-1; } UpSweep(){ static int start = 0; Int i; i = start; do { if (Down[i]) return start = next(i), Down[i]; i = next(i); } while (i != start); return -1; } DownSweep(){ static int start = 0; Int i; i = start; do { if (Down[i]) return start = prev(i), Down[i]; i = prev(i); } while (i != start); return -1; } int Select = 0; PickDown(){ switch (Select){ case 1: return UpSweep(); case 2: return DownSweep(); } return RandomDown(); } SetNote(c) unsigned char *c; { if ((*c & M_CMD_MASK) != SX_CMD) *c &= M_CMD_MASK; if (*c == CH_KEY_ON) { if (Velocity(c)) { ++Ndown; Down[EmptyDown()] = Pitch(c); } else { --Ndown; Down[PitchDown(Pitch(c))] = 0; } } } Flip(n) { return (random()%n) == n/2; } int GlitchFreq = 2000; int GlitchVel = 50; int GlitchVelInc = 2; int GlitchDur = 15; velocity(){ static int pv=10; if (GlitchVel == -2){ if (pv > 99 || pv < 1) GlitchVelInc = -GlitchVelInc; return pv += GlitchVelInc; } else if (GlitchVel == -1) return rnd(20,99); else return GlitchVel; } duration(){ if (GlitchDur>0) return GlitchDur; return rnd(4,100); } Glitch(){ /* play a glitch */ int n; if (!Annoy) return; if (Ndown <= 0){ /* no notes down; play a random note, maybe */ if (Flip(GlitchFreq)) RandomNote(); return ; } /* pick a held note and play a glitch nearby */ n = PickDown(); if (n == -1) return; n = GlitchGap? rnd((n-GlitchGap), (n+GlitchGap)) : n; NoteOn(midi, 0, n, velocity()); fsleep((float)duration()/Tempo); NoteOff(midi, 0, n); } #d sp(c) (c==' ' || c=='\t' || c == '\n') DoCommand(f) FILE *f; /* ** Read command from 'f' and do it. */ { char s[1024], *p = s; if (!fgets(s,sizeof s,f)) return; while sp(*p) ++p; switch (*p){ Case 'e': GlitchGap = atoi(p+1); Case 'g': GlitchFreq = atoi(p+1); Case 'a': Annoy = (Annoy? 0 : 1); Case 't': Tempo = atof(p+1); Case 'v': GlitchVel = atoi(p+1); Case 'i': GlitchVelInc = atoi(p+1); Case 'd': GlitchDur = atoi(p+1); Case 's': Select = atoi(p+1); Case 'q': exit(0); } } main(ac, av) char *av[]; { Int i, n = 100; FILE *Midi, *f = stdin, *popen(); MpuCmd *m = Alloc(MpuCmd); int F, fromstdin=0; srandom(42); /* 42 is the most random number... */ if ((midi = open(MidiDevice, 2)) == -1) Error(MidiDevice); for_each_argument{ Case 'n': n = atoi(argument); /* number of notes to play */ Case 'r': srandom(atoi(argument)); /* use argument as seed */ Case 'a': Annoy = 0; Case 't': Tempo = atof(argument); Case 'g': GlitchFreq = atoi(argument); Case 'e': GlitchGap = atoi(argument); Case 'v': GlitchVel = atoi(argument); Case 'i': GlitchVelInc = atoi(argument); Case 'd': GlitchDur = atoi(argument); Case 's': Select = atoi(argument); Case 'S': fromstdin = 1; Default : MidiError("use: %s [-n #notes] [-r seed] [-t tempo] [-a]\n",av0), exit(1); } #d mp(x) MpuSet(MPU_/**/x) mp(RESET), mp(BENDER_ON), mp(MIDI_THRU_OFF); if (Annoy) { mp(START_RECORD); if (!fromstdin) f = popen("marteauslide","r"); Midi = fdopen(midi,"r+"); setbuf(Midi,NULL); } setbuf(f,0); F = fileno(f); MpuFlush(midi); if (Annoy) for (;;) { if (iwait(F,0)) DoCommand(f); while (iwait(midi,0) && GetMpuCmd(Midi,m)) SetNote(m->mpu_cmd); Glitch(); } else { printf("Instant Boulez...\n"); loop(i,n) RandomNote(); printf("(Applause...)\n"); } close(midi); dx7_reset(-1); exit(0); }